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

     1  /*
     2  Package labelsz supports ranking labels by # annotations of each type.
     3  */
     4  package labelsz
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"encoding/gob"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"math"
    15  	"net/http"
    16  	"reflect"
    17  	"strconv"
    18  	"strings"
    19  	"sync"
    20  
    21  	"github.com/janelia-flyem/dvid/datastore"
    22  	"github.com/janelia-flyem/dvid/datatype/annotation"
    23  	"github.com/janelia-flyem/dvid/datatype/roi"
    24  	"github.com/janelia-flyem/dvid/dvid"
    25  	"github.com/janelia-flyem/dvid/server"
    26  	"github.com/janelia-flyem/dvid/storage"
    27  )
    28  
    29  const (
    30  	Version  = "0.1"
    31  	RepoURL  = "github.com/janelia-flyem/dvid/datatype/labelsz"
    32  	TypeName = "labelsz"
    33  )
    34  
    35  const helpMessage = `
    36  API for labelsz data type (github.com/janelia-flyem/dvid/datatype/labelsz)
    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 labelsz <data name> <settings...>
    52  
    53  	Adds newly named data of the 'type name' to repo with specified UUID.
    54  
    55  	Example:
    56  
    57  	$ dvid repo 3f8c new labelsz labelrankings
    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., "labelrankings"
    63      settings       Configuration settings in "key=value" format separated by spaces.
    64  
    65      Configuration Settings (case-insensitive keys)
    66  
    67      ROI            Value must be in "<roiname>,<uuid>" format where <roiname> is the name of the
    68  				   static ROI that defines the extent of tracking and <uuid> is the immutable
    69  				   version used for this labelsz.
    70  	
    71      ------------------
    72  
    73  HTTP API (Level 2 REST):
    74  
    75  GET  <api URL>/node/<UUID>/<data name>/help
    76  
    77  	Returns data-specific help message.
    78  
    79  
    80  GET  <api URL>/node/<UUID>/<data name>/info
    81  POST <api URL>/node/<UUID>/<data name>/info
    82  
    83      Retrieves or puts DVID-specific data properties for this labelsz data instance.
    84  
    85      Example: 
    86  
    87      GET <api URL>/node/3f8c/labelrankings/info
    88  
    89      Returns JSON with configuration settings.
    90  
    91      Arguments:
    92  
    93      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
    94      data name     Name of labelsz data.
    95  
    96  
    97   POST /api/repo/{uuid}/instance
    98  
    99  	Creates a new instance of the given data type.  Expects configuration data in JSON
   100  	as the body of the POST.  Configuration data is a JSON object with each property
   101  	corresponding to a configuration keyword for the particular data type.  
   102  
   103  	JSON name/value pairs:
   104  
   105  	REQUIRED "typename"   Should equal "labelsz"
   106  	REQUIRED "dataname"   Name of the new instance
   107  	OPTIONAL "versioned"  If "false" or "0", the data is unversioned and acts as if 
   108  	                      all UUIDs within a repo become the root repo UUID.  (True by default.)
   109  	
   110      OPTIONAL "ROI"        Value must be in "<roiname>,<uuid>" format where <roiname> is the name of the
   111  				   		  static ROI that defines the extent of tracking and <uuid> is the immutable
   112  				   		  version used for this labelsz.
   113  							 
   114  POST <api URL>/node/<UUID>/<data name>/sync?<options>
   115  
   116      Establishes data instances for which the label sizes are computed.  Expects JSON to be POSTed
   117      with the following format:
   118  
   119      { "sync": "synapses" }
   120  
   121  	To delete syncs, pass an empty string of names with query string "replace=true":
   122  
   123  	{ "sync": "" }
   124  
   125      The "sync" property should be followed by a comma-delimited list of data instances that MUST
   126      already exist.  After this sync request, the labelsz data are computed for the first time
   127  	and then kept in sync thereafter.  It is not allowed to change syncs.  You can, however,
   128  	create a new labelsz data instance and sync it as required.
   129  
   130      The labelsz data type only accepts syncs to annotation data instances.
   131  
   132      GET Query-string Options:
   133  
   134      replace    Set to "true" if you want passed syncs to replace and not be appended to current syncs.
   135  			   Default operation is false.
   136  
   137  
   138  GET <api URL>/node/<UUID>/<data name>/count/<label>/<index type>
   139  
   140  	Returns the count of the given annotation element type for the given label.
   141  	The index type may be any annotation element type ("PostSyn", "PreSyn", "Gap", "Note"),
   142  	the catch-all for synapses "AllSyn", or the number of voxels "Voxels".
   143  
   144  	For synapse indexing, the labelsz data instance must be synced with an annotations instance.
   145  	(future) For # voxel indexing, the labelsz data instance must be synced with a labelvol instance.
   146  
   147  	Example:
   148  
   149  	GET <api URL>/node/3f8c/labelrankings/size/21847/PreSyn 
   150  
   151  	Returns:
   152  
   153  	{ "Label": 21847,  "PreSyn": 81 }
   154  
   155  
   156  Note: For the following URL endpoints that return and accept POSTed JSON values, see the JSON format
   157  at end of this documentation.
   158  
   159  GET <api URL>/node/<UUID>/<data name>/counts/<index type>
   160  
   161  	Returns the count of the given annotation element type for the POSTed labels.
   162  	Note "counts" is plural. 
   163  	<index type> is the same as individual GET call (eg, PostSyn, AllSyn, etc).
   164  	
   165  	The body of the request will contain a json list of labels. 
   166  	Return value should be like the /top endpoint: 
   167  	
   168  	[ 
   169  		{ "Label": 188, "PreSyn": 81 }, 
   170  		{ "Label": 23, "PreSyn": 65 }, 
   171  		{ "Label": 8137, "PreSyn": 58 } 
   172  	]
   173  
   174  GET <api URL>/node/<UUID>/<data name>/top/<N>/<index type>
   175  
   176  	Returns a list of the top N labels with respect to number of the specified index type.
   177  	The index type may be any annotation element type ("PostSyn", "PreSyn", "Gap", "Note"),
   178  	the catch-all for synapses "AllSyn", or the number of voxels "Voxels".
   179  
   180  	For synapse indexing, the labelsz data instance must be synced with an annotations instance.
   181  	(future) For # voxel indexing, the labelsz data instance must be synced with a labelvol instance.
   182  
   183  	Example:
   184  
   185  	GET <api URL>/node/3f8c/labelrankings/top/3/PreSyn 
   186  
   187  	Returns:
   188  
   189  	[ { "Label": 188,  "PreSyn": 81 }, { "Label": 23, "PreSyn": 65 }, { "Label": 8137, "PreSyn": 58 } ]
   190  
   191  GET <api URL>/node/<UUID>/<data name>/threshold/<T>/<index type>[?<options>]
   192  
   193  	Returns a list of up to 10,000 labels per request that have # given element types >= T.
   194  	The "page" size is 10,000 labels so a call without any query string will return the 
   195  	largest labels with # given element types >= T.  If there are more than 10,000 labels,
   196  	you can access the next 10,000 by including "?offset=10001".
   197  
   198  	The index type may be any annotation element type ("PostSyn", "PreSyn", "Gap", "Note"),
   199  	the catch-all for synapses "AllSyn", or the number of voxels "Voxels".
   200  
   201  	For synapse indexing, the labelsz data instance must be synced with an annotations instance.
   202  	(future) For # voxel indexing, the labelsz data instance must be synced with a labelvol instance.
   203  
   204      GET Query-string Options:
   205  
   206      offset  The starting rank in the sorted list (in descending order) of labels with # given element types >= T.
   207      n       Number of labels to return.
   208  
   209  	Example:
   210  
   211  	GET <api URL>/node/3f8c/labelrankings/threshold/10/PreSyn?offset=10001&n=3
   212  
   213  	Returns:
   214  
   215  	[ { "Label": 188,  "PreSyn": 38 }, { "Label": 23, "PreSyn": 38 }, { "Label": 8137, "PreSyn": 37 } ]
   216  
   217  	In the above example, the query returns the labels ranked #10,001 to #10,003 in the sorted list, in
   218  	descending order of # PreSyn >= 10.
   219  
   220  POST <api URL>/node/<UUID>/<data name>/reload
   221  
   222  	Forces asynchornous denormalization from its synced annotations instance.  Can be 
   223  	used to initialize a newly added instance.  Note that the labelsz will be locked until
   224  	the denormalization is finished with a log message.
   225  `
   226  
   227  var (
   228  	dtype *Type
   229  )
   230  
   231  const (
   232  	MaxLabelsReturned = 10000 // Maximum number of labels returned in JSON
   233  )
   234  
   235  func init() {
   236  	dtype = new(Type)
   237  	dtype.Type = datastore.Type{
   238  		Name:    TypeName,
   239  		URL:     RepoURL,
   240  		Version: Version,
   241  		Requirements: &storage.Requirements{
   242  			Batcher: true,
   243  		},
   244  	}
   245  
   246  	// See doc for package on why channels are segregated instead of interleaved.
   247  	// Data types must be registered with the datastore to be used.
   248  	datastore.Register(dtype)
   249  
   250  	// Need to register types that will be used to fulfill interfaces.
   251  	gob.Register(&Type{})
   252  	gob.Register(&Data{})
   253  }
   254  
   255  // LabelSize is the count for a given label for some metric like # of PreSyn annotations.
   256  type LabelSize struct {
   257  	Label uint64
   258  	Size  uint32
   259  }
   260  
   261  // LabelSizes is a sortable slice of LabelSize
   262  type LabelSizes []LabelSize
   263  
   264  // --- Sort interface
   265  
   266  func (s LabelSizes) Len() int {
   267  	return len(s)
   268  }
   269  
   270  func (s LabelSizes) Less(i, j int) bool {
   271  	return s[i].Size < s[j].Size
   272  }
   273  
   274  func (s LabelSizes) Swap(i, j int) {
   275  	s[i], s[j] = s[j], s[i]
   276  }
   277  
   278  // NewData returns a pointer to labelsz data.
   279  func NewData(uuid dvid.UUID, id dvid.InstanceID, name dvid.InstanceName, c dvid.Config) (*Data, error) {
   280  	// See if we have a valid DataService ROI
   281  	var roistr string
   282  	roistr, found, err := c.GetString("ROI")
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  	if found {
   287  		parts := strings.Split(roistr, ",")
   288  		if len(parts) != 2 {
   289  			return nil, fmt.Errorf("bad ROI value (%q) expected %q", roistr, "<roiname>,<uuid>")
   290  		}
   291  	}
   292  
   293  	// Initialize the Data for this data type
   294  	basedata, err := datastore.NewDataService(dtype, uuid, id, name, c)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  	data := &Data{
   299  		Data: basedata,
   300  		Properties: Properties{
   301  			StaticROI: roistr,
   302  		},
   303  	}
   304  	return data, nil
   305  }
   306  
   307  // --- Labelsz Datatype -----
   308  
   309  type Type struct {
   310  	datastore.Type
   311  }
   312  
   313  // --- TypeService interface ---
   314  
   315  func (dtype *Type) NewDataService(uuid dvid.UUID, id dvid.InstanceID, name dvid.InstanceName, c dvid.Config) (datastore.DataService, error) {
   316  	return NewData(uuid, id, name, c)
   317  }
   318  
   319  func (dtype *Type) Help() string {
   320  	return helpMessage
   321  }
   322  
   323  // Properties are additional properties for data beyond those in standard datastore.Data.
   324  type Properties struct {
   325  	// StaticROI is an optional static ROI specification of the form "<roiname>,<uuid>"
   326  	// Note that it *cannot* mutate after the labelsz instance is created.
   327  	StaticROI string
   328  }
   329  
   330  // Data instance of labelvol, label sparse volumes.
   331  type Data struct {
   332  	*datastore.Data
   333  	Properties
   334  
   335  	// Keep track of sync operations that could be updating the data.
   336  	datastore.Updater
   337  
   338  	// cache of immutable ROI on which this labelsz is filtered if any.
   339  	iMutex     sync.Mutex
   340  	iROI       *roi.Immutable
   341  	roiChecked bool
   342  
   343  	// channels for processing messages from synced data like annotations
   344  	syncCh   chan datastore.SyncMessage
   345  	syncDone chan *sync.WaitGroup
   346  
   347  	// if true, should just return error if trying to access the stats
   348  	uninitialized bool
   349  }
   350  
   351  func (d *Data) Equals(d2 *Data) bool {
   352  	if !d.Data.Equals(d2.Data) {
   353  		return false
   354  	}
   355  	return reflect.DeepEqual(d.Properties, d2.Properties)
   356  }
   357  
   358  func (d *Data) GetSyncedAnnotation() *annotation.Data {
   359  	for dataUUID := range d.SyncedData() {
   360  		source, err := annotation.GetByDataUUID(dataUUID)
   361  		if err == nil {
   362  			return source
   363  		}
   364  		dvid.Errorf("Got error accessing synced annotation %s: %v\n", dataUUID, err)
   365  	}
   366  	return nil
   367  }
   368  
   369  func (d *Data) inROI(pos dvid.Point3d) bool {
   370  	if d.StaticROI == "" {
   371  		return true // no ROI so ROI == everything
   372  	}
   373  
   374  	// Make sure we have immutable ROI if specified.
   375  	d.iMutex.Lock()
   376  	if !d.roiChecked {
   377  		d.roiChecked = true
   378  		iROI, err := roi.ImmutableBySpec(d.StaticROI)
   379  		if err != nil {
   380  			dvid.Errorf("could not load immutable ROI by spec %q: %v\n", d.StaticROI, err)
   381  			d.iMutex.Unlock()
   382  			return false
   383  		}
   384  		d.iROI = iROI
   385  	}
   386  	d.iMutex.Unlock()
   387  
   388  	if d.iROI == nil {
   389  		return false // ROI cannot be retrieved so use nothing; makes obvious failure since no ranks.
   390  	}
   391  	return d.iROI.VoxelWithin(pos)
   392  }
   393  
   394  // GetCountElementType returns a count of the given ElementType for a given label.
   395  func (d *Data) GetCountElementType(ctx *datastore.VersionedCtx, label uint64, i IndexType) (uint32, error) {
   396  	if d.uninitialized {
   397  		return 0, fmt.Errorf("stats not available for labelsz %q at this time", d.DataName())
   398  	}
   399  
   400  	store, err := datastore.GetOrderedKeyValueDB(d)
   401  	if err != nil {
   402  		return 0, err
   403  	}
   404  
   405  	val, err := store.Get(ctx, NewTypeLabelTKey(i, label))
   406  	if err != nil {
   407  		return 0, err
   408  	}
   409  	if val == nil {
   410  		return 0, nil
   411  	}
   412  	if len(val) != 4 {
   413  		return 0, fmt.Errorf("bad size in value for index type %s, label %d: value has length %d", i, label, len(val))
   414  	}
   415  	count := binary.LittleEndian.Uint32(val)
   416  	return count, nil
   417  }
   418  
   419  // SendCountsByElementType writes the counts for given index type for a list of labels
   420  func (d *Data) SendCountsByElementType(w http.ResponseWriter, ctx *datastore.VersionedCtx, labels []uint64, idxType IndexType) error {
   421  	if d.uninitialized {
   422  		return fmt.Errorf("stats not available for labelsz %q at this time", d.DataName())
   423  	}
   424  
   425  	store, err := datastore.GetOrderedKeyValueDB(d)
   426  	if err != nil {
   427  		return err
   428  	}
   429  
   430  	w.Header().Set("Content-type", "application/json")
   431  	if _, err := fmt.Fprintf(w, "["); err != nil {
   432  		return err
   433  	}
   434  	numLabels := len(labels)
   435  	for i, label := range labels {
   436  		val, err := store.Get(ctx, NewTypeLabelTKey(idxType, label))
   437  		if err != nil {
   438  			dvid.Errorf("problem in GET for index type %s, label %d: %v", idxType, label, err)
   439  			continue
   440  		}
   441  		var count uint32
   442  		if val != nil {
   443  			if len(val) != 4 {
   444  				dvid.Errorf("bad value size %d for index type %s, label %d", len(val), idxType, label)
   445  				continue
   446  			}
   447  			count = binary.LittleEndian.Uint32(val)
   448  		}
   449  		if _, err := fmt.Fprintf(w, `{"Label":%d,%q:%d}`, label, idxType, count); err != nil {
   450  			continue
   451  		}
   452  		if i != numLabels-1 {
   453  			fmt.Fprintf(w, ",")
   454  		}
   455  	}
   456  	fmt.Fprintf(w, "]")
   457  	return nil
   458  }
   459  
   460  // GetTopElementType returns a sorted list of the top N labels that have the given ElementType.
   461  func (d *Data) GetTopElementType(ctx *datastore.VersionedCtx, n int, i IndexType) (LabelSizes, error) {
   462  	if d.uninitialized {
   463  		return nil, fmt.Errorf("stats not available for labelsz %q at this time", d.DataName())
   464  	}
   465  
   466  	if n < 0 {
   467  		return nil, fmt.Errorf("bad N (%d) in top request", n)
   468  	}
   469  	if n == 0 {
   470  		return LabelSizes{}, nil
   471  	}
   472  
   473  	store, err := datastore.GetOrderedKeyValueDB(d)
   474  	if err != nil {
   475  		return nil, err
   476  	}
   477  
   478  	// Setup key range for iterating through keys of this ElementType.
   479  	begTKey := NewTypeSizeLabelTKey(i, math.MaxUint32-1, 0)
   480  	endTKey := NewTypeSizeLabelTKey(i, 0, math.MaxUint64)
   481  
   482  	// Iterate through the first N kv then abort.
   483  	shortCircuitErr := fmt.Errorf("Found data, aborting.")
   484  	lsz := make(LabelSizes, n)
   485  	rank := 0
   486  	err = store.ProcessRange(ctx, begTKey, endTKey, nil, func(chunk *storage.Chunk) error {
   487  		idxType, sz, label, err := DecodeTypeSizeLabelTKey(chunk.K)
   488  		if err != nil {
   489  			return err
   490  		}
   491  		if idxType != i {
   492  			return fmt.Errorf("bad iteration of keys: expected index type %s, got %s", i, idxType)
   493  		}
   494  		lsz[rank] = LabelSize{Label: label, Size: sz}
   495  		rank++
   496  		if rank >= n {
   497  			return shortCircuitErr
   498  		}
   499  		return nil
   500  	})
   501  	if err != shortCircuitErr && err != nil {
   502  		return nil, err
   503  	}
   504  	return lsz[:rank], nil
   505  }
   506  
   507  // GetLabelsByThreshold returns a sorted list of labels that meet the given minSize threshold.
   508  // We allow a maximum of MaxLabelsReturned returned labels and start with rank "offset".
   509  func (d *Data) GetLabelsByThreshold(ctx *datastore.VersionedCtx, i IndexType, minSize uint32, offset, num int) (LabelSizes, error) {
   510  	if d.uninitialized {
   511  		return nil, fmt.Errorf("stats not available for labelsz %q at this time", d.DataName())
   512  	}
   513  
   514  	var nReturns int
   515  	if num == 0 {
   516  		nReturns = MaxLabelsReturned
   517  	} else if num < 0 {
   518  		return nil, fmt.Errorf("bad number of requested labels (%d)", num)
   519  	} else {
   520  		nReturns = num
   521  	}
   522  
   523  	store, err := datastore.GetOrderedKeyValueDB(d)
   524  	if err != nil {
   525  		return nil, err
   526  	}
   527  
   528  	// Setup key range for iterating through keys of this ElementType.
   529  	begTKey := NewTypeSizeLabelTKey(i, math.MaxUint32-1, 0)
   530  	endTKey := NewTypeSizeLabelTKey(i, 0, math.MaxUint64)
   531  
   532  	// Iterate through sorted size list until we get what we need.
   533  	shortCircuitErr := fmt.Errorf("Found data, aborting.")
   534  	lsz := make(LabelSizes, nReturns)
   535  	rank := 0
   536  	saved := 0
   537  	err = store.ProcessRange(ctx, begTKey, endTKey, nil, func(chunk *storage.Chunk) error {
   538  		idxType, sz, label, err := DecodeTypeSizeLabelTKey(chunk.K)
   539  		if err != nil {
   540  			return err
   541  		}
   542  		if idxType != i {
   543  			return fmt.Errorf("bad iteration of keys: expected index type %s, got %s", i, idxType)
   544  		}
   545  		if sz < minSize {
   546  			return shortCircuitErr
   547  		}
   548  		if rank >= offset && rank < offset+nReturns {
   549  			lsz[saved] = LabelSize{Label: label, Size: sz}
   550  			saved++
   551  			if saved == nReturns {
   552  				return shortCircuitErr
   553  			}
   554  		}
   555  		rank++
   556  		return nil
   557  	})
   558  	if err != shortCircuitErr && err != nil {
   559  		return nil, err
   560  	}
   561  	return lsz[:saved], nil
   562  }
   563  
   564  // GetByUUIDName returns a pointer to annotation data given a version (UUID) and data name.
   565  func GetByUUIDName(uuid dvid.UUID, name dvid.InstanceName) (*Data, error) {
   566  	source, err := datastore.GetDataByUUIDName(uuid, name)
   567  	if err != nil {
   568  		return nil, err
   569  	}
   570  	data, ok := source.(*Data)
   571  	if !ok {
   572  		return nil, fmt.Errorf("Instance '%s' is not a labelsz datatype!", name)
   573  	}
   574  	return data, nil
   575  }
   576  
   577  // --- datastore.DataService interface ---------
   578  
   579  func (d *Data) Help() string {
   580  	return helpMessage
   581  }
   582  
   583  func (d *Data) MarshalJSON() ([]byte, error) {
   584  	return json.Marshal(struct {
   585  		Base     *datastore.Data
   586  		Extended Properties
   587  	}{
   588  		d.Data,
   589  		d.Properties,
   590  	})
   591  }
   592  
   593  func (d *Data) GobDecode(b []byte) error {
   594  	buf := bytes.NewBuffer(b)
   595  	dec := gob.NewDecoder(buf)
   596  	if err := dec.Decode(&(d.Data)); err != nil {
   597  		return err
   598  	}
   599  	if err := dec.Decode(&(d.Properties)); err != nil {
   600  		return err
   601  	}
   602  	return nil
   603  }
   604  
   605  func (d *Data) GobEncode() ([]byte, error) {
   606  	var buf bytes.Buffer
   607  	enc := gob.NewEncoder(&buf)
   608  	if err := enc.Encode(d.Data); err != nil {
   609  		return nil, err
   610  	}
   611  	if err := enc.Encode(d.Properties); err != nil {
   612  		return nil, err
   613  	}
   614  	return buf.Bytes(), nil
   615  }
   616  
   617  // DoRPC acts as a switchboard for RPC commands.
   618  func (d *Data) DoRPC(request datastore.Request, reply *datastore.Response) error {
   619  	switch request.TypeCommand() {
   620  	default:
   621  		return fmt.Errorf("Unknown command.  Data type '%s' [%s] does not support '%s' command.",
   622  			d.DataName(), d.TypeName(), request.TypeCommand())
   623  	}
   624  }
   625  
   626  // ServeHTTP handles all incoming HTTP requests for this data.
   627  func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) (activity map[string]interface{}) {
   628  	timedLog := dvid.NewTimeLog()
   629  
   630  	// Get the action (GET, POST)
   631  	action := strings.ToLower(r.Method)
   632  
   633  	// Break URL request into arguments
   634  	url := r.URL.Path[len(server.WebAPIPath):]
   635  	parts := strings.Split(url, "/")
   636  	if len(parts[len(parts)-1]) == 0 {
   637  		parts = parts[:len(parts)-1]
   638  	}
   639  
   640  	// Handle POST on data -> setting of configuration
   641  	if len(parts) == 3 && action == "put" {
   642  		config, err := server.DecodeJSON(r)
   643  		if err != nil {
   644  			server.BadRequest(w, r, err)
   645  			return
   646  		}
   647  		if err := d.ModifyConfig(config); err != nil {
   648  			server.BadRequest(w, r, err)
   649  			return
   650  		}
   651  		if err := datastore.SaveDataByUUID(uuid, d); err != nil {
   652  			server.BadRequest(w, r, err)
   653  			return
   654  		}
   655  		fmt.Fprintf(w, "Changed '%s' based on received configuration:\n%s\n", d.DataName(), config)
   656  		return
   657  	}
   658  
   659  	if len(parts) < 4 {
   660  		server.BadRequest(w, r, "Incomplete API request")
   661  		return
   662  	}
   663  
   664  	// Process help and info.
   665  	switch parts[3] {
   666  	case "help":
   667  		w.Header().Set("Content-Type", "text/plain")
   668  		fmt.Fprintln(w, dtype.Help())
   669  
   670  	case "info":
   671  		jsonBytes, err := d.MarshalJSON()
   672  		if err != nil {
   673  			server.BadRequest(w, r, err)
   674  			return
   675  		}
   676  		w.Header().Set("Content-Type", "application/json")
   677  		fmt.Fprintf(w, string(jsonBytes))
   678  
   679  	case "sync":
   680  		if action != "post" {
   681  			server.BadRequest(w, r, "Only POST allowed to sync endpoint")
   682  			return
   683  		}
   684  		replace := r.URL.Query().Get("replace") == "true"
   685  		if err := datastore.SetSyncByJSON(d, uuid, replace, r.Body); err != nil {
   686  			server.BadRequest(w, r, err)
   687  			return
   688  		}
   689  
   690  	case "count":
   691  		if action != "get" {
   692  			server.BadRequest(w, r, "Only GET action is available on 'count' endpoint.")
   693  			return
   694  		}
   695  		if len(parts) < 6 {
   696  			server.BadRequest(w, r, "Must include label and element type after 'count' endpoint.")
   697  			return
   698  		}
   699  		label, err := strconv.ParseUint(parts[4], 10, 64)
   700  		if err != nil {
   701  			server.BadRequest(w, r, err)
   702  			return
   703  		}
   704  		idxType := StringToIndexType(parts[5])
   705  		if idxType == UnknownIndex {
   706  			server.BadRequest(w, r, fmt.Errorf("unknown index type specified (%q)", parts[5]))
   707  			return
   708  		}
   709  		count, err := d.GetCountElementType(ctx, label, idxType)
   710  		if err != nil {
   711  			server.BadRequest(w, r, err)
   712  			return
   713  		}
   714  		w.Header().Set("Content-type", "application/json")
   715  		jsonStr := fmt.Sprintf(`{"Label":%d,%q:%d}`, label, idxType, count)
   716  		if _, err := io.WriteString(w, jsonStr); err != nil {
   717  			server.BadRequest(w, r, err)
   718  			return
   719  		}
   720  		timedLog.Infof("HTTP %s: get count for label %d, index type %s: %s", r.Method, label, idxType, r.URL)
   721  
   722  	case "counts":
   723  		if action != "get" {
   724  			server.BadRequest(w, r, "Only GET action is available on 'counts' endpoint.")
   725  			return
   726  		}
   727  		data, err := ioutil.ReadAll(r.Body)
   728  		if err != nil {
   729  			server.BadRequest(w, r, "Bad GET request body for counts query: %v", err)
   730  			return
   731  		}
   732  		if len(parts) < 5 {
   733  			server.BadRequest(w, r, "Must include element type after 'counts' endpoint.")
   734  			return
   735  		}
   736  		idxType := StringToIndexType(parts[4])
   737  		if idxType == UnknownIndex {
   738  			server.BadRequest(w, r, fmt.Errorf("unknown index type specified (%q)", parts[4]))
   739  			return
   740  		}
   741  		var labels []uint64
   742  		if err := json.Unmarshal(data, &labels); err != nil {
   743  			server.BadRequest(w, r, fmt.Sprintf("Bad JSON label array sent in 'counts' query: %v", err))
   744  			return
   745  		}
   746  		if err := d.SendCountsByElementType(w, ctx, labels, idxType); err != nil {
   747  			server.BadRequest(w, r, err)
   748  			return
   749  		}
   750  		timedLog.Infof("HTTP GET counts query of %d labels (%s)", len(labels), r.URL)
   751  
   752  	case "top":
   753  		if action != "get" {
   754  			server.BadRequest(w, r, "Only GET action is available on 'top' endpoint.")
   755  			return
   756  		}
   757  		if len(parts) < 6 {
   758  			server.BadRequest(w, r, "Must include N and element type after 'top' endpoint.")
   759  			return
   760  		}
   761  		n, err := strconv.ParseUint(parts[4], 10, 32)
   762  		if err != nil {
   763  			server.BadRequest(w, r, err)
   764  			return
   765  		}
   766  		i := StringToIndexType(parts[5])
   767  		if i == UnknownIndex {
   768  			server.BadRequest(w, r, fmt.Errorf("unknown index type specified (%q)", parts[5]))
   769  			return
   770  		}
   771  		labelSizes, err := d.GetTopElementType(ctx, int(n), i)
   772  		if err != nil {
   773  			server.BadRequest(w, r, err)
   774  			return
   775  		}
   776  		w.Header().Set("Content-type", "application/json")
   777  		jsonBytes, err := json.Marshal(labelSizes)
   778  		if err != nil {
   779  			server.BadRequest(w, r, err)
   780  			return
   781  		}
   782  		if _, err := w.Write(jsonBytes); err != nil {
   783  			server.BadRequest(w, r, err)
   784  			return
   785  		}
   786  		timedLog.Infof("HTTP %s: get top %d labels for index type %s: %s", r.Method, n, i, r.URL)
   787  
   788  	case "threshold":
   789  		if action != "get" {
   790  			server.BadRequest(w, r, "Only GET action is available on 'threshold' endpoint.")
   791  			return
   792  		}
   793  		if len(parts) < 6 {
   794  			server.BadRequest(w, r, "Must include threshold # and element type after 'threshold' endpoint.")
   795  			return
   796  		}
   797  		t, err := strconv.ParseUint(parts[4], 10, 32)
   798  		if err != nil {
   799  			server.BadRequest(w, r, err)
   800  			return
   801  		}
   802  		minSize := uint32(t)
   803  		i := StringToIndexType(parts[5])
   804  		if i == UnknownIndex {
   805  			server.BadRequest(w, r, fmt.Errorf("unknown index type specified (%q)", parts[5]))
   806  			return
   807  		}
   808  
   809  		queryStrings := r.URL.Query()
   810  		var num, offset int
   811  		offsetStr := queryStrings.Get("offset")
   812  		if offsetStr != "" {
   813  			offset, err = strconv.Atoi(offsetStr)
   814  			if err != nil {
   815  				server.BadRequest(w, r, fmt.Errorf("bad offset specified in query string (%q)", offsetStr))
   816  				return
   817  			}
   818  		}
   819  		numStr := queryStrings.Get("n")
   820  		if numStr != "" {
   821  			num, err = strconv.Atoi(numStr)
   822  			if err != nil {
   823  				server.BadRequest(w, r, fmt.Errorf("bad num specified in query string (%q)", numStr))
   824  				return
   825  			}
   826  		}
   827  
   828  		labels, err := d.GetLabelsByThreshold(ctx, i, minSize, offset, num)
   829  		if err != nil {
   830  			server.BadRequest(w, r, err)
   831  			return
   832  		}
   833  		w.Header().Set("Content-type", "application/json")
   834  		jsonBytes, err := json.Marshal(labels)
   835  		if err != nil {
   836  			server.BadRequest(w, r, err)
   837  			return
   838  		}
   839  		if _, err := w.Write(jsonBytes); err != nil {
   840  			server.BadRequest(w, r, err)
   841  			return
   842  		}
   843  		timedLog.Infof("HTTP %s: get %d labels for index type %s with threshold %d: %s", r.Method, num, i, t, r.URL)
   844  
   845  	case "reload":
   846  		// POST <api URL>/node/<UUID>/<data name>/reload
   847  		if action != "post" {
   848  			server.BadRequest(w, r, "Only POST action is available on 'reload' endpoint.")
   849  			return
   850  		}
   851  		d.ReloadData(ctx)
   852  
   853  	default:
   854  		server.BadAPIRequest(w, r, d)
   855  	}
   856  	return
   857  }
   858  
   859  func (d *Data) ReloadData(ctx *datastore.VersionedCtx) {
   860  	go d.resync(ctx)
   861  	dvid.Infof("Started recalculation of labelsz %q...\n", d.DataName())
   862  }
   863  
   864  // Get all labeled annotations from synced annotation instance and repopulate the labelsz.
   865  func (d *Data) resync(ctx *datastore.VersionedCtx) {
   866  	timedLog := dvid.NewTimeLog()
   867  
   868  	annot := d.GetSyncedAnnotation()
   869  	if annot == nil {
   870  		dvid.Errorf("Unable to get synced annotation.  Aborting reload of labelsz %q.\n", d.DataName())
   871  		return
   872  	}
   873  
   874  	store, err := datastore.GetOrderedKeyValueDB(d)
   875  	if err != nil {
   876  		dvid.Errorf("Labelsz %q had error initializing store: %v\n", d.DataName(), err)
   877  		return
   878  	}
   879  
   880  	d.StartUpdate()
   881  	defer d.StopUpdate()
   882  
   883  	d.uninitialized = true
   884  	defer func() {
   885  		d.uninitialized = false
   886  	}()
   887  
   888  	minTSLTKey := storage.MinTKey(keyTypeSizeLabel)
   889  	maxTSLTKey := storage.MaxTKey(keyTypeSizeLabel)
   890  	if err := store.DeleteRange(ctx, minTSLTKey, maxTSLTKey); err != nil {
   891  		dvid.Errorf("Unable to delete type-size-label denormalization for labelsz %q: %v\n", d.DataName(), err)
   892  		return
   893  	}
   894  
   895  	minTypeTKey := storage.MinTKey(keyTypeLabel)
   896  	maxTypeTKey := storage.MaxTKey(keyTypeLabel)
   897  	if err := store.DeleteRange(ctx, minTypeTKey, maxTypeTKey); err != nil {
   898  		dvid.Errorf("Unable to delete type-label denormalization for labelsz %q: %v\n", d.DataName(), err)
   899  		return
   900  	}
   901  	timedLog.Infof("Completed deletion of labelsz %q indices. Now regenerating.", d.DataName())
   902  
   903  	// launch goroutines to write label stats
   904  	var writerWG sync.WaitGroup
   905  	writerCh := make(chan *storage.TKeyValue, 1000)
   906  	for i := 0; i < 4; i++ {
   907  		writerWG.Add(1)
   908  		go func() {
   909  			for tkv := range writerCh {
   910  				if tkv == nil {
   911  					return
   912  				}
   913  				if err := store.Put(ctx, tkv.K, tkv.V); err != nil {
   914  					dvid.Errorf("Unable to write label stat for labelsz %q: %v\n", d.DataName(), err)
   915  				}
   916  			}
   917  			writerWG.Done()
   918  		}()
   919  	}
   920  
   921  	// interate through all label annotations and pass to writer
   922  	var totLabels uint64
   923  	err = annot.ProcessLabelAnnotations(ctx.VersionID(), func(label uint64, elems annotation.ElementsNR) {
   924  		var indexMap [AllSyn]uint32
   925  
   926  		for _, elem := range elems {
   927  			if d.inROI(elem.Pos) {
   928  				indexMap[elementToIndexType(elem.Kind)]++
   929  			}
   930  		}
   931  		var allsyn uint32
   932  		for i := IndexType(0); i < AllSyn; i++ {
   933  			if indexMap[i] > 0 {
   934  				buf := make([]byte, 4)
   935  				binary.LittleEndian.PutUint32(buf, indexMap[i])
   936  				writerCh <- &storage.TKeyValue{K: NewTypeLabelTKey(i, label), V: buf}
   937  				writerCh <- &storage.TKeyValue{K: NewTypeSizeLabelTKey(i, indexMap[i], label)}
   938  				allsyn += indexMap[i]
   939  			}
   940  		}
   941  		buf := make([]byte, 4)
   942  		binary.LittleEndian.PutUint32(buf, allsyn)
   943  		writerCh <- &storage.TKeyValue{K: NewTypeLabelTKey(AllSyn, label), V: buf}
   944  		writerCh <- &storage.TKeyValue{K: NewTypeSizeLabelTKey(AllSyn, allsyn, label)}
   945  
   946  		totLabels++
   947  		if totLabels%10000 == 0 {
   948  			timedLog.Infof("Reloading labelsz %q: %d labels processed", d.DataName(), totLabels)
   949  		}
   950  	})
   951  	if err != nil {
   952  		dvid.Errorf("Error in reload of labelsz %q: %v\n", d.DataName(), err)
   953  	}
   954  	close(writerCh)
   955  	writerWG.Wait()
   956  
   957  	timedLog.Infof("Completed labelsz %q reload of %d labels from annotation %q", d.DataName(), totLabels, annot.DataName())
   958  }