github.com/janelia-flyem/dvid@v1.0.0/datatype/roi/roi.go (about)

     1  /*
     2  	Package roi implements DVID support for Region-Of-Interest operations.
     3  */
     4  package roi
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"encoding/gob"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"math"
    14  	"net/http"
    15  	"reflect"
    16  	"sort"
    17  	"strconv"
    18  	"strings"
    19  	"sync"
    20  
    21  	"github.com/janelia-flyem/dvid/datastore"
    22  	"github.com/janelia-flyem/dvid/dvid"
    23  	"github.com/janelia-flyem/dvid/server"
    24  	"github.com/janelia-flyem/dvid/storage"
    25  )
    26  
    27  const (
    28  	Version  = "0.1"
    29  	RepoURL  = "github.com/janelia-flyem/dvid/datatype/roi"
    30  	TypeName = "roi"
    31  
    32  	DefaultBlockSize = 32
    33  )
    34  
    35  const HelpMessage = `
    36  API for 'roi' datatype (github.com/janelia-flyem/dvid/datatype/roi)
    37  ===================================================================
    38  
    39  Note: UUIDs referenced below are strings that may either be a unique prefix of a
    40  hexadecimal UUID string (e.g., 3FA22) or a branch leaf specification that adds
    41  a colon (":") followed by the case-dependent branch name.  In the case of a
    42  branch leaf specification, the unique UUID prefix just identifies the repo of
    43  the branch, and the UUID referenced is really the leaf of the branch name.
    44  For example, if we have a DAG with root A -> B -> C where C is the current
    45  HEAD or leaf of the "master" (default) branch, then asking for "B:master" is
    46  the same as asking for "C".  If we add another version so A -> B -> C -> D, then
    47  references to "B:master" now return the data from "D".
    48  
    49  Command-line:
    50  
    51  $ dvid repo <UUID> new roi <data name> <settings...>
    52  
    53  	Adds newly named roi data to repo with specified UUID.
    54  
    55  	Example:
    56  
    57  	$ dvid repo 3f8c new roi medulla
    58  
    59      Arguments:
    60  
    61      UUID           Hexadecimal string with enough characters to uniquely identify a version node.
    62      data name      Name of data to create, e.g., "medulla"
    63      settings       Configuration settings in "key=value" format separated by spaces.
    64  
    65      Configuration Settings (case-insensitive keys)
    66  
    67      Versioned      "true" or "false" (default)
    68      BlockSize      Size in pixels  (default: %d)
    69  	
    70      ------------------
    71  
    72  HTTP API (Level 2 REST):
    73  
    74  Note that browsers support HTTP PUT and DELETE via javascript but only GET/POST are
    75  included in HTML specs.  For ease of use in constructing clients, HTTP POST is used
    76  to create or modify resources in an idempotent fashion.
    77  
    78  GET  <api URL>/node/<UUID>/<data name>/help
    79  
    80  	Returns data-specific help message.
    81  
    82  
    83  GET  <api URL>/node/<UUID>/<data name>/info
    84  POST <api URL>/node/<UUID>/<data name>/info
    85  
    86      Retrieves or puts data properties.
    87  
    88      Example: 
    89  
    90      GET <api URL>/node/3f8c/stuff/info
    91  
    92      Returns JSON with configuration settings.
    93  
    94      Arguments:
    95  
    96      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
    97      data name     Name of roi data.
    98  
    99  
   100  GET  <api URL>/node/<UUID>/<data name>/roi
   101  POST <api URL>/node/<UUID>/<data name>/roi
   102  DEL  <api URL>/node/<UUID>/<data name>/roi 
   103  
   104      Performs operations on an ROI depending on the HTTP verb.
   105  
   106      Example: 
   107  
   108      GET <api URL>/node/3f8c/medulla/roi
   109  
   110      Returns the data associated with the "medulla" ROI at version 3f8c.
   111      If an ROI is currently being created asynchronously, e.g., during an imageblk
   112      foreground command, then a HTTP status code 206 (Partial Content) is returned
   113      until the ROI is completely stored (HTTP status code 200).
   114  
   115      The "Content-type" of the HTTP response (and usually the request) are
   116      "application/json" for arbitrary binary data.  Returns a list of 4-tuples:
   117  
   118    	"[[0, 0, 0, 1], [0, 2, 3, 5], [0, 2, 8, 9], [1, 2, 3, 4]]"
   119  
   120  	Each element is expressed as [z, y, x0, x1], which represents blocks with the block coordinates
   121  	(x0, y, z) to (x1, y, z).  Each block is a chunking of voxel space using the BlockSize for 
   122  	the ROI.
   123  
   124      Arguments:
   125  
   126      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   127      data name     Name of ROI data to save/modify or get.
   128  
   129  GET <api URL>/node/<UUID>/<data name>/mask/0_1_2/<size>/<offset>
   130  
   131  	Returns a binary volume in ZYX order (increasing X is contiguous in array) same as format of
   132  	the nD voxels GET request.  The returned payload is marked as "octet-stream".
   133  
   134  	The request must have size and offset arguments (both must be given if included) similar
   135  	to the nD voxels GET request.  Currently, only the 3d GET is implemented, although in the
   136  	future this endpoint will parallel voxel GET request.
   137  
   138  	Example:
   139  
   140  	GET <api URL>/node/3f8c/myroi/mask/0_1_2/512_512_256/100_200_300
   141  
   142  	Returns a binary volume with non-zero elements for voxels within ROI.  The binary volume
   143  	has size 512 x 512 x 256 voxels and an offset of (100, 200, 300).
   144  
   145  
   146  POST <api URL>/node/<UUID>/<data name>/ptquery
   147  
   148  	Determines whether a list of 3d points (voxel coordinates) in JSON format sent by POST is within 
   149  	the ROI.  Returns a list of true/false answers for each point in the same sequence as the POSTed 
   150  	list.  The send format is:
   151  
   152  	[[x0, y0, z0], [x1, y1, z1], ...]
   153  
   154      The "Content-type" of the HTTP response (and usually the request) are
   155      "application/json" for arbitrary binary data.  Example:
   156  
   157    	Sent: "[[0, 100, 910], [0, 121, 900]]"
   158  
   159    	Returned: "[false, true]"
   160  
   161  
   162  GET <api URL>/node/<UUID>/<data name>/partition?batchsize=8
   163  
   164  	Returns JSON of subvolumes that are batchsize^3 blocks in volume and cover the ROI.
   165  
   166      Query-string Options:
   167  
   168      batchsize	Number of blocks along each axis to batch to make one subvolume (default = 8)
   169      optimized   If "true" or "on", partioning returns non-fixed sized subvolumes where the coverage
   170                    is better in terms of subvolumes having more active blocks.
   171  
   172  TODO (API endpoints that are planned in near future)
   173  
   174  GET  <api URL>/node/<UUID>/<data name>/erode/<element size>
   175  
   176      Returns a ROI that has been eroded with a cubic structuring element of the given size.
   177  
   178      Example: 
   179  
   180      GET <api URL>/node/3f8c/medulla/erode/1
   181  
   182      This returns JSON for an ROI that has been eroded by 1 block.
   183  
   184  `
   185  
   186  func init() {
   187  	datastore.Register(NewType())
   188  
   189  	// Need to register types that will be used to fulfill interfaces.
   190  	gob.Register(&Type{})
   191  	gob.Register(&Data{})
   192  }
   193  
   194  // Type embeds the datastore's Type to create a unique type for keyvalue functions.
   195  type Type struct {
   196  	datastore.Type
   197  }
   198  
   199  // NewType returns a pointer to a new keyvalue Type with default values set.
   200  func NewType() *Type {
   201  	dtype := new(Type)
   202  	dtype.Type = datastore.Type{
   203  		Name:    TypeName,
   204  		URL:     RepoURL,
   205  		Version: Version,
   206  		Requirements: &storage.Requirements{
   207  			Batcher: true,
   208  		},
   209  	}
   210  	return dtype
   211  }
   212  
   213  // --- TypeService interface ---
   214  
   215  // NewData returns a pointer to new ROI data with default values.
   216  func (dtype *Type) NewDataService(uuid dvid.UUID, id dvid.InstanceID, name dvid.InstanceName, c dvid.Config) (datastore.DataService, error) {
   217  	basedata, err := datastore.NewDataService(dtype, uuid, id, name, c)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	s, found, err := c.GetString("BlockSize")
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  	var blockSize dvid.Point3d
   226  	if found {
   227  		pt, err := dvid.StringToPoint(s, ",")
   228  		if err != nil {
   229  			return nil, err
   230  		}
   231  		if pt.NumDims() != 3 {
   232  			return nil, fmt.Errorf("BlockSize must be 3d, not %dd", pt.NumDims())
   233  		}
   234  		blockSize, _ = pt.(dvid.Point3d)
   235  	} else {
   236  		blockSize = dvid.Point3d{DefaultBlockSize, DefaultBlockSize, DefaultBlockSize}
   237  	}
   238  	d := &Data{
   239  		Data:       basedata,
   240  		Properties: Properties{blockSize, math.MaxInt32, math.MinInt32},
   241  	}
   242  	return d, nil
   243  }
   244  
   245  func (dtype *Type) Help() string {
   246  	return fmt.Sprintf(HelpMessage, DefaultBlockSize)
   247  }
   248  
   249  // Properties are additional properties for keyvalue data instances beyond those
   250  // in standard datastore.Data.   These will be persisted to metadata storage.
   251  type Properties struct {
   252  	BlockSize dvid.Point3d
   253  
   254  	// Minimum Block Coord Z for ROI
   255  	MinZ int32
   256  
   257  	// Maximum Block Coord Z for ROI
   258  	MaxZ int32
   259  }
   260  
   261  // Immutable is an ROI fixed to a particular version that you can check
   262  // voxel coordinates against.
   263  type Immutable struct {
   264  	version   dvid.VersionID
   265  	blockSize dvid.Point3d
   266  	blocks    map[dvid.IZYXString]struct{}
   267  }
   268  
   269  func (i Immutable) VoxelWithin(p dvid.Point3d) bool {
   270  	izyx := p.ToBlockIZYXString(i.blockSize)
   271  	_, found := i.blocks[izyx]
   272  	return found
   273  }
   274  
   275  // ImmutableBySpec returns an Immutable ROI (or nil if not available) given
   276  // a name and uuid using string format "<roiname>,<uuid>"
   277  func ImmutableBySpec(spec string) (*Immutable, error) {
   278  	d, v, found, err := DataBySpec(spec)
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  	if !found {
   283  		return nil, nil
   284  	}
   285  
   286  	// Read the ROI from this version.
   287  	spans, err := d.GetSpans(v)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  
   292  	// Setup the immutable.
   293  	im := Immutable{
   294  		version:   v,
   295  		blockSize: d.BlockSize,
   296  		blocks:    make(map[dvid.IZYXString]struct{}),
   297  	}
   298  	for _, span := range spans {
   299  		z, y, x0, x1 := span[0], span[1], span[2], span[3]
   300  		for x := x0; x <= x1; x++ {
   301  			c := dvid.ChunkPoint3d{x, y, z}
   302  			izyx := c.ToIZYXString()
   303  			im.blocks[izyx] = struct{}{}
   304  		}
   305  	}
   306  	return &im, nil
   307  }
   308  
   309  // Data embeds the datastore's Data and extends it with keyvalue properties (none for now).
   310  type Data struct {
   311  	*datastore.Data
   312  	datastore.Updater
   313  	Properties
   314  
   315  	sync.RWMutex
   316  }
   317  
   318  // IsMutationRequest overrides the default behavior to specify POST /ptquery as an immutable
   319  // request.
   320  func (d *Data) IsMutationRequest(action, endpoint string) bool {
   321  	lc := strings.ToLower(action)
   322  	if endpoint == "ptquery" && lc == "post" {
   323  		return false
   324  	}
   325  	return d.Data.IsMutationRequest(action, endpoint) // default for rest.
   326  }
   327  
   328  // DescribeTKeyClass returns a string explanation of what a particular TKeyClass
   329  // is used for.  Implements the datastore.TKeyClassDescriber interface.
   330  func (d *Data) DescribeTKeyClass(tkc storage.TKeyClass) string {
   331  	return "ROI block + span key"
   332  }
   333  
   334  // CopyPropertiesFrom copies the data instance-specific properties from a given
   335  // data instance into the receiver's properties.  Fulfills the datastore.PropertyCopier interface.
   336  func (d *Data) CopyPropertiesFrom(src datastore.DataService, fs storage.FilterSpec) error {
   337  	d2, ok := src.(*Data)
   338  	if !ok {
   339  		return fmt.Errorf("unable to copy properties from non-roi data %q", src.DataName())
   340  	}
   341  	d.Properties.BlockSize = d2.Properties.BlockSize
   342  
   343  	// TODO -- Handle mutable data that could be potentially altered by filter.
   344  	d.Properties.MinZ = d2.Properties.MinZ
   345  	d.Properties.MaxZ = d2.Properties.MaxZ
   346  
   347  	return nil
   348  }
   349  
   350  // DataBySpec returns a ROI Data based on a string specification of the form
   351  // "<roiname>,<uuid>". If the given string is not parsable, the "found" return value is false.
   352  func DataBySpec(spec string) (d *Data, v dvid.VersionID, found bool, err error) {
   353  	roispec := strings.Split(spec, ",")
   354  	if len(roispec) != 2 {
   355  		err = fmt.Errorf("Expect ROI filters to have format %q, but got %q", "roi:<roiname>,<uuid>", spec)
   356  		return
   357  	}
   358  	roiName := dvid.InstanceName(roispec[0])
   359  	_, v, err = datastore.MatchingUUID(roispec[1])
   360  	if err != nil {
   361  		return
   362  	}
   363  	var data datastore.DataService
   364  	data, err = datastore.GetDataByVersionName(v, roiName)
   365  	if err != nil {
   366  		return
   367  	}
   368  	var ok bool
   369  	d, ok = data.(*Data)
   370  	if !ok {
   371  		err = fmt.Errorf("Data instance %q is not ROI instance", roiName)
   372  		return
   373  	}
   374  	found = true
   375  	return
   376  }
   377  
   378  // DataByFilter returns a ROI Data based on a string specification of the form
   379  // "roi:<roiname>,<uuid>". If the given string is not parsable, the "found" return value is false.
   380  func DataByFilter(spec storage.FilterSpec) (d *Data, v dvid.VersionID, found bool, err error) {
   381  	filterval, found := spec.GetFilterSpec("roi")
   382  	if !found {
   383  		return
   384  	}
   385  	return DataBySpec(filterval)
   386  }
   387  
   388  // Equals returns false if any version of the ROI is different
   389  func (d *Data) Equals(d2 *Data) bool {
   390  	if !d.Data.Equals(d2.Data) || !reflect.DeepEqual(d.Properties, d2.Properties) {
   391  		return false
   392  	}
   393  	return true
   394  }
   395  
   396  // GetByUUIDName returns a pointer to ROI data given a version (UUID) and data name.
   397  func GetByUUIDName(uuid dvid.UUID, name dvid.InstanceName) (*Data, error) {
   398  	source, err := datastore.GetDataByUUIDName(uuid, name)
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  	data, ok := source.(*Data)
   403  	if !ok {
   404  		return nil, fmt.Errorf("Instance '%s' is not a ROI datatype!", name)
   405  	}
   406  	return data, nil
   407  }
   408  
   409  func (d *Data) MarshalJSON() ([]byte, error) {
   410  	return json.Marshal(struct {
   411  		Base     *datastore.Data
   412  		Extended Properties
   413  	}{
   414  		d.Data,
   415  		d.Properties,
   416  	})
   417  }
   418  
   419  func (d *Data) GobDecode(b []byte) error {
   420  	buf := bytes.NewBuffer(b)
   421  	dec := gob.NewDecoder(buf)
   422  	if err := dec.Decode(&(d.Data)); err != nil {
   423  		return err
   424  	}
   425  	if err := dec.Decode(&(d.Properties)); err != nil {
   426  		return err
   427  	}
   428  	return nil
   429  }
   430  
   431  func (d *Data) GobEncode() ([]byte, error) {
   432  	var buf bytes.Buffer
   433  	enc := gob.NewEncoder(&buf)
   434  	if err := enc.Encode(d.Data); err != nil {
   435  		return nil, err
   436  	}
   437  	if err := enc.Encode(d.Properties); err != nil {
   438  		return nil, err
   439  	}
   440  	return buf.Bytes(), nil
   441  }
   442  
   443  type ZRange struct {
   444  	MinZ, MaxZ int32
   445  }
   446  
   447  const (
   448  	// keyUnknown should never be used and is a check for corrupt or incorrectly set keys
   449  	keyUnknown storage.TKeyClass = iota
   450  
   451  	// keyROI are keys for ROI RLEs
   452  	keyROI = 90
   453  )
   454  
   455  var (
   456  	minIndexRLE = indexRLE{dvid.MinIndexZYX, 0}
   457  	maxIndexRLE = indexRLE{dvid.MaxIndexZYX, math.MaxUint32}
   458  )
   459  
   460  // indexRLE is the key component for block indices included in an ROI.
   461  // Because we use dvid.IndexZYX for index byte slices, we know
   462  // the key ordering will be Z, then Y, then X0 (and then X1).
   463  type indexRLE struct {
   464  	start dvid.IndexZYX
   465  	span  uint32 // the span along X
   466  }
   467  
   468  func (i *indexRLE) Bytes() []byte {
   469  	buf := new(bytes.Buffer)
   470  	_, err := buf.Write(i.start.Bytes())
   471  	if err != nil {
   472  		dvid.Errorf("Error in roi.go, indexRLE.Bytes(): %v\n", err)
   473  	}
   474  	binary.Write(buf, binary.BigEndian, i.span)
   475  	return buf.Bytes()
   476  }
   477  
   478  func (i *indexRLE) IndexFromBytes(b []byte) error {
   479  	if len(b) != 16 {
   480  		return fmt.Errorf("Illegal byte length (%d) for ROI RLE Index", len(b))
   481  	}
   482  	if err := i.start.IndexFromBytes(b[0:12]); err != nil {
   483  		return err
   484  	}
   485  	i.span = binary.BigEndian.Uint32(b[12:])
   486  	return nil
   487  }
   488  
   489  func minIndexByBlockZ(z int32) indexRLE {
   490  	return indexRLE{dvid.IndexZYX{math.MinInt32, math.MinInt32, z}, 0}
   491  }
   492  
   493  func maxIndexByBlockZ(z int32) indexRLE {
   494  	return indexRLE{dvid.IndexZYX{math.MaxInt32, math.MaxInt32, z}, math.MaxUint32}
   495  }
   496  
   497  // Returns all (z, y, x0, x1) Spans in sorted order: z, then y, then x0.
   498  func getSpans(ctx *datastore.VersionedCtx, minIndex, maxIndex indexRLE) ([]dvid.Span, error) {
   499  	db, err := ctx.GetOrderedKeyValueDB()
   500  	if err != nil {
   501  		return nil, err
   502  	}
   503  	spans := []dvid.Span{}
   504  
   505  	var f storage.ChunkFunc = func(chunk *storage.Chunk) error {
   506  		ibytes, err := chunk.K.ClassBytes(keyROI)
   507  		if err != nil {
   508  			return err
   509  		}
   510  		index := new(indexRLE)
   511  		if err = index.IndexFromBytes(ibytes); err != nil {
   512  			return fmt.Errorf("Unable to get indexRLE out of []byte encoding: %v\n", err)
   513  		}
   514  		z := index.start.Value(2)
   515  		y := index.start.Value(1)
   516  		x0 := index.start.Value(0)
   517  		x1 := x0 + int32(index.span) - 1
   518  		spans = append(spans, dvid.Span{z, y, x0, x1})
   519  		return nil
   520  	}
   521  	mintk := storage.NewTKey(keyROI, minIndex.Bytes())
   522  	maxtk := storage.NewTKey(keyROI, maxIndex.Bytes())
   523  	err = db.ProcessRange(ctx, mintk, maxtk, &storage.ChunkOp{}, f)
   524  	return spans, err
   525  }
   526  
   527  // VoxelBoundsInside returns true if the given voxel extents intersects the spans.
   528  func VoxelBoundsInside(e dvid.Extents3d, blocksize dvid.Point3d, spans []dvid.Span) (bool, error) {
   529  	// Convert the voxel bounds to block coordinates
   530  	emin := e.MinPoint.Chunk(blocksize).(dvid.ChunkPoint3d)
   531  	emax := e.MaxPoint.Chunk(blocksize).(dvid.ChunkPoint3d)
   532  
   533  	// Iterate through the spans to see if there's intersection between
   534  	// the extents and span.
   535  	for _, span := range spans {
   536  		bz, by, bx0, bx1 := span[0], span[1], span[2], span[3]
   537  		if bz > emax[2] {
   538  			return false, nil
   539  		}
   540  		if bz < emin[2] || by < emin[1] || bx1 < emin[0] {
   541  			continue
   542  		}
   543  		if by > emax[1] || bx0 > emax[0] {
   544  			continue
   545  		}
   546  		return true, nil
   547  	}
   548  	return false, nil
   549  }
   550  
   551  // GetSpans returns all (z, y, x0, x1) Spans in sorted order: z, then y, then x0.
   552  func GetSpans(ctx *datastore.VersionedCtx) ([]dvid.Span, error) {
   553  	return getSpans(ctx, minIndexRLE, maxIndexRLE)
   554  }
   555  
   556  // GetSpans returns all (z, y, x0, x1) Spans in sorted order: z, then y, then x0.
   557  func (d *Data) GetSpans(v dvid.VersionID) ([]dvid.Span, error) {
   558  	ctx := datastore.NewVersionedCtx(d, v)
   559  	return getSpans(ctx, minIndexRLE, maxIndexRLE)
   560  }
   561  
   562  // Get returns a JSON-encoded byte slice of the ROI in the form of 4-Spans,
   563  // where each Span is [z, y, xstart, xend]
   564  func Get(ctx *datastore.VersionedCtx) ([]byte, error) {
   565  	spans, err := GetSpans(ctx)
   566  	if err != nil {
   567  		return nil, err
   568  	}
   569  	jsonBytes, err := json.Marshal(spans)
   570  	if err != nil {
   571  		return nil, err
   572  	}
   573  	return jsonBytes, nil
   574  }
   575  
   576  // Delete removes an ROI.
   577  func (d *Data) Delete(ctx storage.VersionedCtx) error {
   578  	db, err := datastore.GetOrderedKeyValueDB(d)
   579  	if err != nil {
   580  		return err
   581  	}
   582  
   583  	// We only want one PUT on given version for given data to prevent interleaved PUTs.
   584  	putMutex := ctx.Mutex()
   585  	putMutex.Lock()
   586  	defer putMutex.Unlock()
   587  
   588  	d.MinZ = math.MaxInt32
   589  	d.MaxZ = math.MinInt32
   590  	if err := datastore.SaveDataByVersion(ctx.VersionID(), d); err != nil {
   591  		return fmt.Errorf("error in trying to save repo on roi extent change: %v", err)
   592  	}
   593  
   594  	// Delete all spans for this ROI for just this version by tombstoning.
   595  	begIndex := indexRLE{
   596  		start: dvid.MinIndexZYX,
   597  		span:  0,
   598  	}
   599  	begTk := storage.NewTKey(keyROI, begIndex.Bytes())
   600  	endIndex := indexRLE{
   601  		start: dvid.MaxIndexZYX,
   602  		span:  math.MaxUint32,
   603  	}
   604  	endTk := storage.NewTKey(keyROI, endIndex.Bytes())
   605  	return db.DeleteRange(ctx, begTk, endTk)
   606  }
   607  
   608  // PutSpans saves a slice of spans representing an ROI into the datastore.
   609  // If the init parameter is true, all previous spans of this ROI are deleted before
   610  // writing these spans.
   611  func (d *Data) PutSpans(versionID dvid.VersionID, spans []dvid.Span, init bool) error {
   612  	ctx := datastore.NewVersionedCtx(d, versionID)
   613  	db, err := datastore.GetOrderedKeyValueDB(d)
   614  	if err != nil {
   615  		return err
   616  	}
   617  	d.StartUpdate()
   618  	defer d.StopUpdate()
   619  
   620  	d.Lock()
   621  	defer d.Unlock()
   622  
   623  	// Delete the old key/values
   624  	if init {
   625  		if err := d.Delete(ctx); err != nil {
   626  			return err
   627  		}
   628  	}
   629  
   630  	// Make sure our small data store can do batching.
   631  	batcher, ok := db.(storage.KeyValueBatcher)
   632  	if !ok {
   633  		return fmt.Errorf("Unable to store ROI: small data store can't do batching!")
   634  	}
   635  
   636  	// We only want one PUT on given version for given data to prevent interleaved PUTs.
   637  	putMutex := ctx.Mutex()
   638  	putMutex.Lock()
   639  
   640  	// Save new extents after finished.
   641  	defer func() {
   642  		err := datastore.SaveDataByVersion(ctx.VersionID(), d)
   643  		if err != nil {
   644  			dvid.Errorf("Error in trying to save repo on roi extent change: %v\n", err)
   645  		}
   646  		putMutex.Unlock()
   647  	}()
   648  
   649  	// Put the new key/values
   650  	const BATCH_SIZE = 10000
   651  	batch := batcher.NewBatch(ctx)
   652  	for i, span := range spans {
   653  		if span[0] < d.MinZ {
   654  			d.MinZ = span[0]
   655  		}
   656  		if span[0] > d.MaxZ {
   657  			d.MaxZ = span[0]
   658  		}
   659  		if span[3] < span[2] {
   660  			return fmt.Errorf("Got weird span %v.  span[3] (X1) < span[2] (X0)", span)
   661  		}
   662  		index := indexRLE{
   663  			start: dvid.IndexZYX{span[2], span[1], span[0]},
   664  			span:  uint32(span[3] - span[2] + 1),
   665  		}
   666  		tk := storage.NewTKey(keyROI, index.Bytes())
   667  		batch.Put(tk, dvid.EmptyValue())
   668  		if (i+1)%BATCH_SIZE == 0 {
   669  			if err := batch.Commit(); err != nil {
   670  				return fmt.Errorf("Error on batch PUT at span %d: %v\n", i, err)
   671  			}
   672  			batch = batcher.NewBatch(ctx)
   673  		}
   674  	}
   675  	if len(spans)%BATCH_SIZE != 0 {
   676  		if err := batch.Commit(); err != nil {
   677  			return fmt.Errorf("Error on last batch PUT: %v\n", err)
   678  		}
   679  	}
   680  	return nil
   681  }
   682  
   683  // PutJSON saves JSON-encoded data representing an ROI into the datastore.
   684  func (d *Data) PutJSON(v dvid.VersionID, jsonBytes []byte) error {
   685  	spans := []dvid.Span{}
   686  	err := json.Unmarshal(jsonBytes, &spans)
   687  	if err != nil {
   688  		return fmt.Errorf("Error trying to parse POSTed JSON: %v", err)
   689  	}
   690  	if err := d.PutSpans(v, spans, true); err != nil {
   691  		return err
   692  	}
   693  	return nil
   694  }
   695  
   696  // Returns the voxel range normalized to begVoxel offset and constrained by block span.
   697  func voxelRange(blockSize, begBlock, endBlock, begVoxel, endVoxel int32) (int32, int32) {
   698  	v0 := begBlock * blockSize
   699  	if v0 < begVoxel {
   700  		v0 = begVoxel
   701  	}
   702  	v1 := (endBlock+1)*blockSize - 1
   703  	if v1 > endVoxel {
   704  		v1 = endVoxel
   705  	}
   706  	v0 -= begVoxel
   707  	v1 -= begVoxel
   708  	return v0, v1
   709  }
   710  
   711  // GetMask returns a binary volume of subvol size where each element is 1 if inside the ROI
   712  // and 0 if outside the ROI.
   713  func (d *Data) GetMask(ctx *datastore.VersionedCtx, subvol *dvid.Subvolume) ([]byte, error) {
   714  	pt0 := subvol.StartPoint()
   715  	pt1 := subvol.EndPoint()
   716  	minBlockZ := pt0.Value(2) / d.BlockSize[2]
   717  	maxBlockZ := pt1.Value(2) / d.BlockSize[2]
   718  	minBlockY := pt0.Value(1) / d.BlockSize[1]
   719  	maxBlockY := pt1.Value(1) / d.BlockSize[1]
   720  	minBlockX := pt0.Value(0) / d.BlockSize[0]
   721  	maxBlockX := pt1.Value(0) / d.BlockSize[0]
   722  
   723  	minIndex := minIndexByBlockZ(minBlockZ)
   724  	maxIndex := maxIndexByBlockZ(maxBlockZ)
   725  
   726  	spans, err := getSpans(ctx, minIndex, maxIndex)
   727  	if err != nil {
   728  		return nil, err
   729  	}
   730  
   731  	// Allocate the mask volume.
   732  	data := make([]uint8, subvol.NumVoxels())
   733  	size := subvol.Size()
   734  	nx := size.Value(0)
   735  	nxy := size.Value(1) * nx
   736  
   737  	// Fill the mask volume
   738  	for _, span := range spans {
   739  		// Handle out of range blocks
   740  		if span[0] < minBlockZ {
   741  			continue
   742  		}
   743  		if span[0] > maxBlockZ {
   744  			break
   745  		}
   746  		if span[1] < minBlockY || span[1] > maxBlockY {
   747  			continue
   748  		}
   749  		if span[3] < minBlockX || span[2] > maxBlockX {
   750  			continue
   751  		}
   752  
   753  		// Get the voxel range for this span, including limits based on subvolume.
   754  		x0, x1 := voxelRange(d.BlockSize[0], span[2], span[3], pt0.Value(0), pt1.Value(0))
   755  		y0, y1 := voxelRange(d.BlockSize[1], span[1], span[1], pt0.Value(1), pt1.Value(1))
   756  		z0, z1 := voxelRange(d.BlockSize[2], span[0], span[0], pt0.Value(2), pt1.Value(2))
   757  
   758  		// Write the mask
   759  		for z := z0; z <= z1; z++ {
   760  			for y := y0; y <= y1; y++ {
   761  				i := z*nxy + y*nx + x0
   762  				for x := x0; x <= x1; x++ {
   763  					data[i] = 1
   764  					i++
   765  				}
   766  			}
   767  		}
   768  	}
   769  	return data, nil
   770  }
   771  
   772  // Returns the current span index and whether given point is included in span.
   773  func seekSpan(pt dvid.ChunkPoint3d, spans []dvid.Span, curSpanI int) (int, bool) {
   774  	numSpans := len(spans)
   775  	if curSpanI >= numSpans {
   776  		return curSpanI, false
   777  	}
   778  
   779  	// Keep going through spans until we are equal to or past the chunk point.
   780  	for {
   781  		curSpan := spans[curSpanI]
   782  		if curSpan.LessChunkPoint3d(pt) {
   783  			curSpanI++
   784  		} else {
   785  			if curSpan.Includes(pt) {
   786  				return curSpanI, true
   787  			} else {
   788  				return curSpanI, false
   789  			}
   790  		}
   791  		if curSpanI >= numSpans {
   792  			return curSpanI, false
   793  		}
   794  	}
   795  }
   796  
   797  // PointQuery checks if a JSON-encoded list of voxel points are within an ROI.
   798  // It returns a JSON list of bools, each corresponding to the original list of points.
   799  func (d *Data) PointQuery(ctx *datastore.VersionedCtx, jsonBytes []byte) ([]byte, error) {
   800  	list, err := dvid.ListChunkPoint3dFromVoxels(jsonBytes, d.BlockSize)
   801  	if err != nil {
   802  		return nil, err
   803  	}
   804  	sort.Sort((*dvid.ByZYX)(list))
   805  
   806  	// Get the ROI.  The spans are ordered in z, y, then x0.
   807  	spans, err := GetSpans(ctx)
   808  	if err != nil {
   809  		return nil, err
   810  	}
   811  
   812  	// Iterate through each query point, using the ordering to make the search more efficient.
   813  	inclusions := make([]bool, len(list.Points))
   814  	var included bool
   815  	curSpan := 0
   816  	for i, pt := range list.Points {
   817  		origIndex := list.Indices[i]
   818  		curSpan, included = seekSpan(pt, spans, curSpan)
   819  		inclusions[origIndex] = included
   820  	}
   821  
   822  	// Convert to JSON
   823  	inclusionsJSON, err := json.Marshal(inclusions)
   824  	if err != nil {
   825  		return nil, err
   826  	}
   827  	return inclusionsJSON, nil
   828  }
   829  
   830  type subvolumesT struct {
   831  	NumTotalBlocks  uint64
   832  	NumActiveBlocks uint64
   833  	NumSubvolumes   int32
   834  	ROI             dvid.ChunkExtents3d
   835  	Subvolumes      []subvolumeT
   836  }
   837  
   838  type subvolumeT struct {
   839  	dvid.Extents3d
   840  	dvid.ChunkExtents3d
   841  	TotalBlocks  uint64
   842  	ActiveBlocks uint64
   843  }
   844  
   845  type layerT struct {
   846  	activeBlocks []*indexRLE
   847  	minX, maxX   int32
   848  	minY, maxY   int32
   849  	minZ, maxZ   int32
   850  }
   851  
   852  func (d *Data) newLayer(z0, z1 int32) *layerT {
   853  	return &layerT{
   854  		[]*indexRLE{},
   855  		math.MaxInt32, math.MinInt32,
   856  		math.MaxInt32, math.MinInt32,
   857  		z0, z1,
   858  	}
   859  }
   860  
   861  func (layer *layerT) extend(rle *indexRLE) {
   862  	layer.activeBlocks = append(layer.activeBlocks, rle)
   863  
   864  	y := rle.start.Value(1)
   865  	x0 := rle.start.Value(0)
   866  	x1 := x0 + int32(rle.span) - 1
   867  
   868  	if layer.minX > x0 {
   869  		layer.minX = x0
   870  	}
   871  	if layer.maxX < x1 {
   872  		layer.maxX = x1
   873  	}
   874  	if layer.minY > y {
   875  		layer.minY = y
   876  	}
   877  	if layer.maxY < y {
   878  		layer.maxY = y
   879  	}
   880  }
   881  
   882  func getPadding(x0, x1, batchsize int32) (leftPad, rightPad int32) {
   883  	var padding int32
   884  	overage := (x1 - x0 + 1) % batchsize
   885  	if overage == 0 {
   886  		padding = 0
   887  	} else {
   888  		padding = batchsize - overage
   889  	}
   890  	leftPad = padding / 2
   891  	rightPad = padding - leftPad
   892  	return
   893  }
   894  
   895  // For a slice of RLEs, return the min and max block Y coordinate
   896  func getYRange(blocks []*indexRLE) (minY, maxY int32, found bool) {
   897  	minY = math.MaxInt32
   898  	maxY = math.MinInt32
   899  	for _, rle := range blocks {
   900  		if rle.start[1] < minY {
   901  			minY = rle.start[1]
   902  		}
   903  		if rle.start[1] > maxY {
   904  			maxY = rle.start[1]
   905  		}
   906  		found = true
   907  	}
   908  	return
   909  }
   910  
   911  // Return range of x for all spans within the given range of y
   912  func getXRange(blocks []*indexRLE, minY, maxY int32) (minX, maxX int32, actives []*indexRLE) {
   913  	minX = math.MaxInt32
   914  	maxX = math.MinInt32
   915  	actives = []*indexRLE{}
   916  	for i, rle := range blocks {
   917  		if rle.start[1] >= minY && rle.start[1] <= maxY {
   918  			if rle.start[0] < minX {
   919  				minX = rle.start[0]
   920  			}
   921  			x1 := rle.start[0] + int32(rle.span) - 1
   922  			if x1 > maxX {
   923  				maxX = x1
   924  			}
   925  			actives = append(actives, blocks[i])
   926  		}
   927  	}
   928  	return
   929  }
   930  
   931  func findActives(blocks []*indexRLE, minX, maxX int32) uint64 {
   932  	var numActive uint64
   933  	for _, rle := range blocks {
   934  		spanBeg := rle.start[0]
   935  		if spanBeg > maxX {
   936  			continue
   937  		}
   938  		spanEnd := spanBeg + int32(rle.span) - 1
   939  		if spanEnd < minX {
   940  			continue
   941  		}
   942  		x0 := dvid.MaxInt32(minX, spanBeg)
   943  		x1 := dvid.MinInt32(maxX, spanEnd)
   944  		numActive += uint64(x1 - x0 + 1)
   945  	}
   946  	return numActive
   947  }
   948  
   949  func findXHoles(blocks []*indexRLE, minX, maxX int32) (bestBeg, bestEnd int32, found bool) {
   950  	nx := maxX - minX + 1
   951  	used := make([]bool, nx, nx)
   952  
   953  	for _, rle := range blocks {
   954  		spanBeg := rle.start[0]
   955  		if spanBeg > maxX {
   956  			continue
   957  		}
   958  		spanEnd := spanBeg + int32(rle.span) - 1
   959  		if spanEnd < minX {
   960  			continue
   961  		}
   962  		x0 := dvid.MaxInt32(minX, spanBeg)
   963  		x1 := dvid.MinInt32(maxX, spanEnd)
   964  
   965  		for x := x0; x <= x1; x++ {
   966  			i := x - minX
   967  			used[i] = true
   968  		}
   969  	}
   970  
   971  	// See if there are holes.
   972  	var holeSize, bestSize int32
   973  	var holeBeg, holeEnd int32
   974  	var inHole bool
   975  	for x := minX; x <= maxX; x++ {
   976  		i := x - minX
   977  		if !used[i] {
   978  			if inHole {
   979  				holeEnd = x
   980  				holeSize++
   981  			} else {
   982  				inHole = true
   983  				holeBeg = x
   984  				holeEnd = x
   985  				holeSize = 1
   986  			}
   987  		} else {
   988  			inHole = false
   989  		}
   990  		if holeSize > bestSize {
   991  			bestSize = holeSize
   992  			bestBeg = holeBeg
   993  			bestEnd = holeEnd
   994  		}
   995  	}
   996  	if bestSize > 0 {
   997  		found = true
   998  	}
   999  	return
  1000  }
  1001  
  1002  func totalBlocks(minCorner, maxCorner dvid.ChunkPoint3d) uint64 {
  1003  	dx := uint64(maxCorner[0] - minCorner[0] + 1)
  1004  	dy := uint64(maxCorner[1] - minCorner[1] + 1)
  1005  	dz := uint64(maxCorner[2] - minCorner[2] + 1)
  1006  	return dx * dy * dz
  1007  }
  1008  
  1009  // Adds subvolumes based on given extents for a layer.
  1010  func (d *Data) addSubvolumes(layer *layerT, subvolumes *subvolumesT, batchsize int32, merge bool) {
  1011  	// mergeThreshold := batchsize * batchsize * batchsize / 10
  1012  	minY, maxY, found := getYRange(layer.activeBlocks)
  1013  	if !found {
  1014  		return
  1015  	}
  1016  	subvolumes.ROI.ExtendDim(1, minY)
  1017  	subvolumes.ROI.ExtendDim(1, maxY)
  1018  	dy := maxY - minY + 1
  1019  	yleft := dy % batchsize
  1020  
  1021  	begY := minY
  1022  	for {
  1023  		if begY > maxY {
  1024  			break
  1025  		}
  1026  		endY := begY + batchsize - 1
  1027  		if yleft > 0 {
  1028  			endY++
  1029  			yleft--
  1030  		}
  1031  		minX, maxX, actives := getXRange(layer.activeBlocks, begY, endY)
  1032  		if len(actives) > 0 {
  1033  			subvolumes.ROI.ExtendDim(0, minX)
  1034  			subvolumes.ROI.ExtendDim(0, maxX)
  1035  
  1036  			dx := maxX - minX + 1
  1037  			xleft := dx % batchsize
  1038  
  1039  			// Create subvolumes along this row.
  1040  			begX := minX
  1041  			for {
  1042  				if begX > maxX {
  1043  					break
  1044  				}
  1045  				endX := begX + batchsize - 1
  1046  				if xleft > 0 {
  1047  					endX++
  1048  					xleft--
  1049  				}
  1050  				minCorner := dvid.ChunkPoint3d{begX, begY, layer.minZ}
  1051  				maxCorner := dvid.ChunkPoint3d{endX, endY, layer.maxZ}
  1052  				holeBeg, holeEnd, found := findXHoles(actives, begX, endX)
  1053  				var numActive, numTotal uint64
  1054  				if found && merge {
  1055  					// MinCorner stays same since we are extended in X
  1056  					if holeBeg-1 >= begX {
  1057  						lastI := len(subvolumes.Subvolumes) - 1
  1058  						subvolume := subvolumes.Subvolumes[lastI]
  1059  						lastCorner := dvid.ChunkPoint3d{holeBeg - 1, endY, layer.maxZ}
  1060  						subvolume.MaxPoint = lastCorner.MinPoint(d.BlockSize).(dvid.Point3d)
  1061  						subvolume.MaxChunk = lastCorner
  1062  						numTotal = totalBlocks(minCorner, lastCorner)
  1063  						numActive = findActives(actives, begX, holeBeg-1)
  1064  						subvolume.TotalBlocks += numTotal
  1065  						subvolume.ActiveBlocks += numActive
  1066  						subvolumes.Subvolumes[lastI] = subvolume
  1067  					}
  1068  					begX = holeEnd + 1
  1069  				} else {
  1070  					numTotal = totalBlocks(minCorner, maxCorner)
  1071  					numActive = findActives(actives, begX, endX)
  1072  					subvolume := subvolumeT{
  1073  						Extents3d: dvid.Extents3d{
  1074  							minCorner.MinPoint(d.BlockSize).(dvid.Point3d),
  1075  							maxCorner.MaxPoint(d.BlockSize).(dvid.Point3d),
  1076  						},
  1077  						ChunkExtents3d: dvid.ChunkExtents3d{minCorner, maxCorner},
  1078  						TotalBlocks:    numTotal,
  1079  						ActiveBlocks:   numActive,
  1080  					}
  1081  					subvolumes.Subvolumes = append(subvolumes.Subvolumes, subvolume)
  1082  					begX = endX + 1
  1083  				}
  1084  				subvolumes.NumActiveBlocks += numActive
  1085  				subvolumes.NumTotalBlocks += numTotal
  1086  			}
  1087  		}
  1088  		begY = endY + 1
  1089  	}
  1090  }
  1091  
  1092  // Partition returns JSON of differently sized subvolumes that attempt to distribute
  1093  // the number of active blocks per subvolume.
  1094  func (d *Data) Partition(ctx storage.Context, batchsize int32) ([]byte, error) {
  1095  	// Partition Z as perfectly as we can.
  1096  	dz := d.MaxZ - d.MinZ + 1
  1097  	zleft := dz % batchsize
  1098  
  1099  	// Adjust Z range
  1100  	layerBegZ := d.MinZ
  1101  	layerEndZ := layerBegZ + batchsize - 1
  1102  
  1103  	// Iterate through blocks in ascending Z, calculating active extents and subvolume coverage.
  1104  	// Keep track of current layer = batchsize of blocks in Z.
  1105  	var subvolumes subvolumesT
  1106  	subvolumes.Subvolumes = []subvolumeT{}
  1107  	subvolumes.ROI.MinChunk[2] = d.MinZ
  1108  	subvolumes.ROI.MaxChunk[2] = d.MaxZ
  1109  
  1110  	layer := d.newLayer(layerBegZ, layerEndZ)
  1111  
  1112  	db, err := datastore.GetOrderedKeyValueDB(d)
  1113  	if err != nil {
  1114  		return nil, err
  1115  	}
  1116  	merge := true
  1117  	var f storage.ChunkFunc = func(chunk *storage.Chunk) error {
  1118  		ibytes, err := chunk.K.ClassBytes(keyROI)
  1119  		if err != nil {
  1120  			return err
  1121  		}
  1122  		index := new(indexRLE)
  1123  		if err = index.IndexFromBytes(ibytes); err != nil {
  1124  			return fmt.Errorf("Unable to get indexRLE out of []byte encoding: %v\n", err)
  1125  		}
  1126  
  1127  		// If we are in new layer, process last one.
  1128  		z := index.start.Value(2)
  1129  		if z > layerEndZ {
  1130  			// Process last layer
  1131  			dvid.Debugf("Computing subvolumes in layer with Z %d -> %d (dz %d)\n",
  1132  				layer.minZ, layer.maxZ, layer.maxZ-layer.minZ+1)
  1133  			d.addSubvolumes(layer, &subvolumes, batchsize, merge)
  1134  
  1135  			// Init variables for next layer
  1136  			layerBegZ = layerEndZ + 1
  1137  			layerEndZ += batchsize
  1138  			if zleft > 0 {
  1139  				layerEndZ++
  1140  				zleft--
  1141  			}
  1142  			layer = d.newLayer(layerBegZ, layerEndZ)
  1143  		}
  1144  
  1145  		// Check this block against current layer extents
  1146  		layer.extend(index)
  1147  		return nil
  1148  	}
  1149  	mintk := storage.MinTKey(keyROI)
  1150  	maxtk := storage.MaxTKey(keyROI)
  1151  	err = db.ProcessRange(ctx, mintk, maxtk, &storage.ChunkOp{}, f)
  1152  	if err != nil {
  1153  		return nil, err
  1154  	}
  1155  
  1156  	// Process last incomplete layer if there is one.
  1157  	if len(layer.activeBlocks) > 0 {
  1158  		dvid.Debugf("Computing subvolumes for final layer Z %d -> %d (dz %d)\n",
  1159  			layer.minZ, layer.maxZ, layer.maxZ-layer.minZ+1)
  1160  		d.addSubvolumes(layer, &subvolumes, batchsize, merge)
  1161  	}
  1162  	subvolumes.NumSubvolumes = int32(len(subvolumes.Subvolumes))
  1163  
  1164  	// Encode as JSON
  1165  	jsonBytes, err := json.MarshalIndent(subvolumes, "", "    ")
  1166  	if err != nil {
  1167  		return nil, err
  1168  	}
  1169  	return jsonBytes, err
  1170  }
  1171  
  1172  // Adds subvolumes using simple algorithm centers fixed-sized subvolumes across active blocks.
  1173  func (d *Data) addSubvolumesGrid(layer *layerT, subvolumes *subvolumesT, batchsize int32) {
  1174  	minY, maxY, found := getYRange(layer.activeBlocks)
  1175  	if !found {
  1176  		return
  1177  	}
  1178  	subvolumes.ROI.ExtendDim(1, minY)
  1179  	subvolumes.ROI.ExtendDim(1, maxY)
  1180  	dy := maxY - minY + 1
  1181  	yleft := dy % batchsize
  1182  
  1183  	addYtoBeg := yleft / 2
  1184  	addYtoEnd := yleft - addYtoBeg
  1185  	begY := minY - addYtoBeg
  1186  	endY := maxY + addYtoEnd
  1187  
  1188  	// Iterate through Y, block by block, and determine the X range.  Then center subvolumes
  1189  	// along that X.
  1190  	for y0 := begY; y0 <= endY; y0 += batchsize {
  1191  		y1 := y0 + batchsize - 1
  1192  		minX, maxX, actives := getXRange(layer.activeBlocks, y0, y1)
  1193  		if len(actives) == 0 {
  1194  			continue
  1195  		}
  1196  		subvolumes.ROI.ExtendDim(0, minX)
  1197  		subvolumes.ROI.ExtendDim(0, maxX)
  1198  
  1199  		// For this row of subvolumes along X, position them to encompass the X range.
  1200  		dx := maxX - minX + 1
  1201  		xleft := dx % batchsize
  1202  
  1203  		addXtoBeg := xleft / 2
  1204  		addXtoEnd := xleft - addXtoBeg
  1205  		begX := minX - addXtoBeg
  1206  		endX := maxX + addXtoEnd
  1207  
  1208  		// Create the subvolumes along X
  1209  		for x0 := begX; x0 <= endX; x0 += batchsize {
  1210  			x1 := x0 + batchsize - 1
  1211  			minCorner := dvid.ChunkPoint3d{x0, y0, layer.minZ}
  1212  			maxCorner := dvid.ChunkPoint3d{x1, y1, layer.maxZ}
  1213  
  1214  			numTotal := totalBlocks(minCorner, maxCorner)
  1215  			numActive := findActives(actives, x0, x1)
  1216  			if numActive > 0 {
  1217  				subvolume := subvolumeT{
  1218  					Extents3d: dvid.Extents3d{
  1219  						minCorner.MinPoint(d.BlockSize).(dvid.Point3d),
  1220  						maxCorner.MaxPoint(d.BlockSize).(dvid.Point3d),
  1221  					},
  1222  					ChunkExtents3d: dvid.ChunkExtents3d{minCorner, maxCorner},
  1223  					TotalBlocks:    numTotal,
  1224  					ActiveBlocks:   numActive,
  1225  				}
  1226  				subvolumes.Subvolumes = append(subvolumes.Subvolumes, subvolume)
  1227  			}
  1228  			subvolumes.NumActiveBlocks += numActive
  1229  			subvolumes.NumTotalBlocks += numTotal
  1230  		}
  1231  	}
  1232  }
  1233  
  1234  // SimplePartition returns JSON of identically sized subvolumes arranged over ROI
  1235  func (d *Data) SimplePartition(ctx storage.Context, batchsize int32) ([]byte, error) {
  1236  	// Partition Z as perfectly as we can.
  1237  	dz := d.MaxZ - d.MinZ + 1
  1238  	zleft := dz % batchsize
  1239  
  1240  	// Adjust Z range
  1241  	addZtoTop := zleft / 2
  1242  	layerBegZ := d.MinZ - addZtoTop
  1243  	layerEndZ := layerBegZ + batchsize - 1
  1244  
  1245  	// Iterate through blocks in ascending Z, calculating active extents and subvolume coverage.
  1246  	// Keep track of current layer = batchsize of blocks in Z.
  1247  	var subvolumes subvolumesT
  1248  	subvolumes.Subvolumes = []subvolumeT{}
  1249  	subvolumes.ROI.MinChunk[2] = d.MinZ
  1250  	subvolumes.ROI.MaxChunk[2] = d.MaxZ
  1251  
  1252  	layer := d.newLayer(layerBegZ, layerEndZ)
  1253  
  1254  	db, err := datastore.GetOrderedKeyValueDB(d)
  1255  	if err != nil {
  1256  		return nil, err
  1257  	}
  1258  	var f storage.ChunkFunc = func(chunk *storage.Chunk) error {
  1259  		ibytes, err := chunk.K.ClassBytes(keyROI)
  1260  		if err != nil {
  1261  			return err
  1262  		}
  1263  		index := new(indexRLE)
  1264  		if err = index.IndexFromBytes(ibytes); err != nil {
  1265  			return fmt.Errorf("Unable to get indexRLE out of []byte encoding: %v\n", err)
  1266  		}
  1267  
  1268  		// If we are in new layer, process last one.
  1269  		z := index.start.Value(2)
  1270  		if z > layerEndZ {
  1271  			// Process last layer
  1272  			dvid.Debugf("Computing subvolumes in layer with Z %d -> %d (dz %d)\n", layer.minZ, layer.maxZ, layer.maxZ-layer.minZ+1)
  1273  			d.addSubvolumesGrid(layer, &subvolumes, batchsize)
  1274  
  1275  			// Init variables for next layer
  1276  			layerBegZ = layerEndZ + 1
  1277  			layerEndZ += batchsize
  1278  			layer = d.newLayer(layerBegZ, layerEndZ)
  1279  		}
  1280  
  1281  		// Extend current layer by the current block
  1282  		layer.extend(index)
  1283  		return nil
  1284  	}
  1285  	mintk := storage.MinTKey(keyROI)
  1286  	maxtk := storage.MaxTKey(keyROI)
  1287  	err = db.ProcessRange(ctx, mintk, maxtk, &storage.ChunkOp{}, f)
  1288  	if err != nil {
  1289  		return nil, err
  1290  	}
  1291  
  1292  	// Process last incomplete layer if there is one.
  1293  	if len(layer.activeBlocks) > 0 {
  1294  		dvid.Debugf("Computing subvolumes for final layer Z %d -> %d (dz %d)\n",
  1295  			layer.minZ, layer.maxZ, layer.maxZ-layer.minZ+1)
  1296  		d.addSubvolumesGrid(layer, &subvolumes, batchsize)
  1297  	}
  1298  	subvolumes.NumSubvolumes = int32(len(subvolumes.Subvolumes))
  1299  
  1300  	// Encode as JSON
  1301  	jsonBytes, err := json.MarshalIndent(subvolumes, "", "    ")
  1302  	if err != nil {
  1303  		return nil, err
  1304  	}
  1305  	return jsonBytes, err
  1306  }
  1307  
  1308  // --- DataService interface ---
  1309  
  1310  func (d *Data) Help() string {
  1311  	return fmt.Sprintf(HelpMessage, DefaultBlockSize)
  1312  }
  1313  
  1314  // DoRPC acts as a switchboard for RPC commands.
  1315  func (d *Data) DoRPC(request datastore.Request, reply *datastore.Response) error {
  1316  	return fmt.Errorf("Unknown command.  Data '%s' [%s] does not support '%s' command.",
  1317  		d.DataName(), d.TypeName(), request.TypeCommand())
  1318  }
  1319  
  1320  // ServeHTTP handles all incoming HTTP requests for this data.
  1321  func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) (activity map[string]interface{}) {
  1322  	timedLog := dvid.NewTimeLog()
  1323  
  1324  	// Break URL request into arguments
  1325  	url := r.URL.Path[len(server.WebAPIPath):]
  1326  	parts := strings.Split(url, "/")
  1327  	if len(parts[len(parts)-1]) == 0 {
  1328  		parts = parts[:len(parts)-1]
  1329  	}
  1330  
  1331  	if len(parts) < 4 {
  1332  		server.BadAPIRequest(w, r, d)
  1333  		return
  1334  	}
  1335  
  1336  	// Process help and info.
  1337  	switch parts[3] {
  1338  	case "help":
  1339  		w.Header().Set("Content-Type", "text/plain")
  1340  		fmt.Fprintln(w, d.Help())
  1341  		return
  1342  	case "info":
  1343  		jsonBytes, err := d.MarshalJSON()
  1344  		if err != nil {
  1345  			server.BadRequest(w, r, err)
  1346  			return
  1347  		}
  1348  		w.Header().Set("Content-Type", "application/json")
  1349  		fmt.Fprintf(w, string(jsonBytes))
  1350  		return
  1351  	default:
  1352  	}
  1353  
  1354  	// Get the key and process request
  1355  	var comment string
  1356  	command := parts[3]
  1357  	method := strings.ToLower(r.Method)
  1358  	switch command {
  1359  	case "roi":
  1360  		switch method {
  1361  		case "get":
  1362  			d.RLock()
  1363  			jsonBytes, err := Get(ctx)
  1364  			d.RUnlock()
  1365  			if err != nil {
  1366  				server.BadRequest(w, r, err)
  1367  				return
  1368  			}
  1369  			w.Header().Set("Content-Type", "application/json")
  1370  			fmt.Fprintf(w, string(jsonBytes))
  1371  			comment = fmt.Sprintf("HTTP GET ROI %q: %d bytes", d.DataName(), len(jsonBytes))
  1372  		case "post":
  1373  			data, err := ioutil.ReadAll(r.Body)
  1374  			if err != nil {
  1375  				server.BadRequest(w, r, err)
  1376  				return
  1377  			}
  1378  			err = d.PutJSON(ctx.VersionID(), data)
  1379  			if err != nil {
  1380  				server.BadRequest(w, r, err)
  1381  				return
  1382  			}
  1383  			comment = fmt.Sprintf("HTTP POST ROI %q: %d bytes", d.DataName(), len(data))
  1384  		case "delete":
  1385  			if err := d.Delete(ctx); err != nil {
  1386  				server.BadRequest(w, r, err)
  1387  				return
  1388  			}
  1389  			comment = fmt.Sprintf("HTTP DELETE ROI %q", d.DataName())
  1390  		}
  1391  	case "mask":
  1392  		if method != "get" {
  1393  			server.BadRequest(w, r, "ROI mask only supports GET")
  1394  			return
  1395  		}
  1396  		if len(parts) < 7 {
  1397  			server.BadRequest(w, r, "%q must be followed by shape/size/offset", command)
  1398  			return
  1399  		}
  1400  		shapeStr, sizeStr, offsetStr := parts[4], parts[5], parts[6]
  1401  		planeStr := dvid.DataShapeString(shapeStr)
  1402  		plane, err := planeStr.DataShape()
  1403  		if err != nil {
  1404  			server.BadRequest(w, r, err)
  1405  			return
  1406  		}
  1407  		switch plane.ShapeDimensions() {
  1408  		case 3:
  1409  			subvol, err := dvid.NewSubvolumeFromStrings(offsetStr, sizeStr, "_")
  1410  			if err != nil {
  1411  				server.BadRequest(w, r, err)
  1412  				return
  1413  			}
  1414  			data, err := d.GetMask(ctx, subvol)
  1415  			if err != nil {
  1416  				server.BadRequest(w, r, err)
  1417  				return
  1418  			}
  1419  			w.Header().Set("Content-type", "application/octet-stream")
  1420  			_, err = w.Write(data)
  1421  			if err != nil {
  1422  				server.BadRequest(w, r, err)
  1423  				return
  1424  			}
  1425  		default:
  1426  			server.BadRequest(w, r, "Currently only 3d masks ('0_1_2' shape) is supported")
  1427  			return
  1428  		}
  1429  	case "ptquery":
  1430  		switch method {
  1431  		case "get":
  1432  			server.BadRequest(w, r, "ptquery requires POST with list of points")
  1433  			return
  1434  		case "post":
  1435  			data, err := ioutil.ReadAll(r.Body)
  1436  			if err != nil {
  1437  				server.BadRequest(w, r, err)
  1438  				return
  1439  			}
  1440  			jsonBytes, err := d.PointQuery(ctx, data)
  1441  			if err != nil {
  1442  				server.BadRequest(w, r, err)
  1443  				return
  1444  			}
  1445  			w.Header().Set("Content-Type", "application/json")
  1446  			fmt.Fprintf(w, string(jsonBytes))
  1447  			comment = fmt.Sprintf("HTTP POST ptquery '%s'", d.DataName())
  1448  		}
  1449  	case "partition":
  1450  		if method != "get" {
  1451  			server.BadRequest(w, r, "partition only supports GET request")
  1452  			return
  1453  		}
  1454  		queryStrings := r.URL.Query()
  1455  		batchsizeStr := queryStrings.Get("batchsize")
  1456  		batchsize, err := strconv.Atoi(batchsizeStr)
  1457  		if err != nil {
  1458  			server.BadRequest(w, r, fmt.Sprintf("Error reading batchsize query string: %v", err))
  1459  			return
  1460  		}
  1461  
  1462  		var jsonBytes []byte
  1463  		optimizedStr := queryStrings.Get("optimized")
  1464  		dvid.Infof("queryvalues = %v\n", queryStrings)
  1465  		if optimizedStr == "true" || optimizedStr == "on" {
  1466  			dvid.Infof("Perform optimized partitioning into subvolumes using batchsize %d\n", batchsize)
  1467  			jsonBytes, err = d.Partition(ctx, int32(batchsize))
  1468  		} else {
  1469  			dvid.Infof("Performing simple partitioning into subvolumes using batchsize %d\n", batchsize)
  1470  			jsonBytes, err = d.SimplePartition(ctx, int32(batchsize))
  1471  		}
  1472  		if err != nil {
  1473  			server.BadRequest(w, r, err)
  1474  			return
  1475  		}
  1476  		w.Header().Set("Content-Type", "application/json")
  1477  		fmt.Fprintf(w, string(jsonBytes))
  1478  		comment = fmt.Sprintf("HTTP partition '%s' with batch size %d", d.DataName(), batchsize)
  1479  	default:
  1480  		server.BadAPIRequest(w, r, d)
  1481  		return
  1482  	}
  1483  
  1484  	timedLog.Infof(comment)
  1485  	return
  1486  }