func (p *Properties) setByConfig(config dvid.Config) error { s, found, err := config.GetString("BlockSize") if err != nil { return err } if found { p.BlockSize, err = dvid.StringToPoint3d(s, ",") if err != nil { return err } } s, found, err = config.GetString("VoxelSize") if err != nil { return err } if found { dvid.Infof("Changing resolution of voxels to %s\n", s) p.Resolution.VoxelSize, err = dvid.StringToNdFloat32(s, ",") if err != nil { return err } } s, found, err = config.GetString("VoxelUnits") if err != nil { return err } if found { p.Resolution.VoxelUnits, err = dvid.StringToNdString(s, ",") if err != nil { return err } } return nil }
// ServeImage returns an image with appropriate Content-Type set. This function differs // from ServeTile in the way parameters are passed to it. ServeTile accepts a tile coordinate. // This function allows arbitrary offset and size, unconstrained by tile sizes. func (d *Data) ServeImage(w http.ResponseWriter, r *http.Request, parts []string) error { if len(parts) < 7 { return fmt.Errorf("%q must be followed by shape/size/offset", parts[3]) } shapeStr, sizeStr, offsetStr := parts[4], parts[5], parts[6] planeStr := dvid.DataShapeString(shapeStr) plane, err := planeStr.DataShape() if err != nil { return err } if plane.ShapeDimensions() != 2 { return fmt.Errorf("Quadtrees can only return 2d images not %s", plane) } size, err := dvid.StringToPoint2d(sizeStr, "_") if err != nil { return err } offset, err := dvid.StringToPoint3d(offsetStr, "_") if err != nil { return err } var formatStr string if len(parts) >= 8 { formatStr = parts[7] } if formatStr == "" { formatStr = DefaultTileFormat } // See if scaling was specified in query string, otherwise use high-res (scale 0) var scale Scaling queryValues := r.URL.Query() scalingStr := queryValues.Get("scale") if scalingStr != "" { scale64, err := strconv.ParseUint(scalingStr, 10, 8) if err != nil { return fmt.Errorf("Illegal tile scale: %s (%v)", scalingStr, err) } scale = Scaling(scale64) } // Determine how this request sits in the available scaled volumes. googleTile, err := d.GetGoogleSpec(scale, plane, offset, size) if err != nil { return err } // Send the tile. return d.serveTile(w, r, googleTile, formatStr, true) }
// handleImageReq returns an image with appropriate Content-Type set. This function differs // from handleTileReq in the way parameters are passed to it. handleTileReq accepts a tile coordinate. // This function allows arbitrary offset and size, unconstrained by tile sizes. func (d *Data) handleImageReq(w http.ResponseWriter, r *http.Request, parts []string) error { if len(parts) < 7 { return fmt.Errorf("%q must be followed by shape/size/offset", parts[3]) } shapeStr, sizeStr, offsetStr := parts[4], parts[5], parts[6] planeStr := dvid.DataShapeString(shapeStr) plane, err := planeStr.DataShape() if err != nil { return err } var size dvid.Point if size, err = dvid.StringToPoint(sizeStr, "_"); err != nil { return err } offset, err := dvid.StringToPoint3d(offsetStr, "_") if err != nil { return err } // Determine how this request sits in the available scaled volumes. scale, err := getScale(r) if err != nil { return err } geom, err := d.GetGoogleSubvolGeom(scale, plane, offset, size) if err != nil { return err } switch plane.ShapeDimensions() { case 2: var formatStr string if len(parts) >= 8 { formatStr = parts[7] } if formatStr == "" { formatStr = DefaultTileFormat } return d.serveTile(w, r, geom, formatStr, false) case 3: return d.serveVolume(w, r, geom, false) } return nil }
// ServeHTTP handles all incoming HTTP requests for this data. func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) { timedLog := dvid.NewTimeLog() // versionID := ctx.VersionID() // Get the action (GET, POST) action := strings.ToLower(r.Method) // Break URL request into arguments url := r.URL.Path[len(server.WebAPIPath):] parts := strings.Split(url, "/") if len(parts[len(parts)-1]) == 0 { parts = parts[:len(parts)-1] } // Handle POST on data -> setting of configuration if len(parts) == 3 && action == "put" { config, err := server.DecodeJSON(r) if err != nil { server.BadRequest(w, r, err) return } if err := d.ModifyConfig(config); err != nil { server.BadRequest(w, r, err) return } if err := datastore.SaveDataByUUID(uuid, d); err != nil { server.BadRequest(w, r, err) return } fmt.Fprintf(w, "Changed '%s' based on received configuration:\n%s\n", d.DataName(), config) return } if len(parts) < 4 { server.BadRequest(w, r, "Incomplete API request") return } // Process help and info. switch parts[3] { case "help": w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, dtype.Help()) case "info": jsonBytes, err := d.MarshalJSON() if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) case "sync": if action != "post" { server.BadRequest(w, r, "Only POST allowed to sync endpoint") return } replace := r.URL.Query().Get("replace") == "true" if err := datastore.SetSyncByJSON(d, uuid, replace, r.Body); err != nil { server.BadRequest(w, r, err) return } case "label": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'label' endpoint.") return } if len(parts) < 5 { server.BadRequest(w, r, "Must include label after 'label' endpoint.") return } label, err := strconv.ParseUint(parts[4], 10, 64) if err != nil { server.BadRequest(w, r, err) return } if label == 0 { server.BadRequest(w, r, "Label 0 is protected background value and cannot be used for query.") return } queryStrings := r.URL.Query() jsonBytes, err := d.GetLabelJSON(ctx, label, queryStrings.Get("relationships") == "true") if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get synaptic elements for label %d (%s)", r.Method, label, r.URL) case "tag": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'tag' endpoint.") return } if len(parts) < 5 { server.BadRequest(w, r, "Must include tag string after 'tag' endpoint.") return } tag := Tag(parts[4]) queryStrings := r.URL.Query() jsonBytes, err := d.GetTagJSON(ctx, tag, queryStrings.Get("relationships") == "true") if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get synaptic elements for tag %s (%s)", r.Method, tag, r.URL) case "elements": switch action { case "get": // GET <api URL>/node/<UUID>/<data name>/elements/<size>/<offset> if len(parts) < 6 { server.BadRequest(w, r, "Expect size and offset to follow 'elements' in GET request") return } sizeStr, offsetStr := parts[4], parts[5] ext3d, err := dvid.NewExtents3dFromStrings(offsetStr, sizeStr, "_") if err != nil { server.BadRequest(w, r, err) return } elements, err := d.GetRegionSynapses(ctx, ext3d) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") jsonBytes, err := json.Marshal(elements) if err != nil { server.BadRequest(w, r, err) return } if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: synapse elements in subvolume (size %s, offset %s) (%s)", r.Method, sizeStr, offsetStr, r.URL) case "post": if err := d.StoreSynapses(ctx, r.Body); err != nil { server.BadRequest(w, r, err) return } default: server.BadRequest(w, r, "Only GET or POST action is available on 'elements' endpoint.") return } case "element": // DELETE <api URL>/node/<UUID>/<data name>/element/<coord> if action != "delete" { server.BadRequest(w, r, "Only DELETE action is available on 'element' endpoint.") return } if len(parts) < 5 { server.BadRequest(w, r, "Must include coordinate after DELETE on 'element' endpoint.") return } pt, err := dvid.StringToPoint3d(parts[4], "_") if err != nil { server.BadRequest(w, r, err) return } if err := d.DeleteElement(ctx, pt); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: delete synaptic element at %s (%s)", r.Method, pt, r.URL) case "move": // POST <api URL>/node/<UUID>/<data name>/move/<from_coord>/<to_coord> if action != "post" { server.BadRequest(w, r, "Only POST action is available on 'move' endpoint.") return } if len(parts) < 6 { server.BadRequest(w, r, "Must include 'from' and 'to' coordinate after 'move' endpoint.") return } fromPt, err := dvid.StringToPoint3d(parts[4], "_") if err != nil { server.BadRequest(w, r, err) return } toPt, err := dvid.StringToPoint3d(parts[5], "_") if err != nil { server.BadRequest(w, r, err) return } if err := d.MoveElement(ctx, fromPt, toPt); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: move synaptic element from %s to %s (%s)", r.Method, fromPt, toPt, r.URL) default: server.BadAPIRequest(w, r, d) } }