github.com/janelia-flyem/dvid@v1.0.0/datatype/roi/iterator.go (about) 1 package roi 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/janelia-flyem/dvid/datastore" 8 "github.com/janelia-flyem/dvid/dvid" 9 "github.com/janelia-flyem/dvid/storage" 10 ) 11 12 // Iterator is optimized for detecting whether given keys are within an ROI. 13 // It exploits the key, and in particular IndexZYX, ordering so that checks 14 // across a volume can be done quickly. 15 type Iterator struct { 16 spans []dvid.Span 17 curSpan int32 18 } 19 20 func NewIterator(roiName dvid.InstanceName, versionID dvid.VersionID, b dvid.Bounder) (*Iterator, error) { 21 dataservice, err := datastore.GetDataByVersionName(versionID, roiName) 22 if err != nil { 23 return nil, fmt.Errorf("Can't get ROI with name %q: %v", roiName, err) 24 } 25 data, ok := dataservice.(*Data) 26 if !ok { 27 return nil, fmt.Errorf("Data name %q was not of roi data type\n", roiName) 28 } 29 30 // Convert voxel extents to block Z extents 31 minPt := b.StartPoint().(dvid.Chunkable) 32 maxPt := b.EndPoint().(dvid.Chunkable) 33 34 minBlockCoord := minPt.Chunk(data.BlockSize) 35 maxBlockCoord := maxPt.Chunk(data.BlockSize) 36 37 minIndex := minIndexByBlockZ(minBlockCoord.Value(2)) 38 maxIndex := maxIndexByBlockZ(maxBlockCoord.Value(2)) 39 40 ctx := datastore.NewVersionedCtx(data, versionID) 41 it := new(Iterator) 42 it.spans, err = getSpans(ctx, minIndex, maxIndex) 43 return it, err 44 } 45 46 // ParseFilterSpec returns the specified ROI instance name and version within a FilterSpec. 47 // Currently, only one ROI can be specified in a FilterSpec. Multiple ROIs should use a 48 // different FilterSpec like "intersect" instead of "roi". 49 func ParseFilterSpec(spec storage.FilterSpec) (name dvid.InstanceName, v dvid.VersionID, found bool, err error) { 50 var filterval string 51 filterval, found = spec.GetFilterSpec("roi") 52 if !found { 53 return 54 } 55 roispec := strings.Split(filterval, ",") 56 if len(roispec) != 2 { 57 err = fmt.Errorf("bad ROI spec: %s", filterval) 58 return 59 } 60 name = dvid.InstanceName(roispec[0]) 61 _, v, err = datastore.MatchingUUID(roispec[1]) 62 return 63 } 64 65 // NewIteratorBySpec returns a ROI iterator based on a string specification of the form 66 // "roi:<roiname>,<uuid>" where the ROI instance name and uniquely identifying string form 67 // of uuid are given. If the given string is not parsable, the "found" return value is false. 68 func NewIteratorBySpec(spec storage.FilterSpec, b dvid.Bounder) (it *Iterator, v dvid.VersionID, found bool, err error) { 69 var name dvid.InstanceName 70 name, v, found, err = ParseFilterSpec(spec) 71 if err != nil || !found { 72 return 73 } 74 75 // Create new iterator based on spec. 76 it, err = NewIterator(name, v, b) 77 return 78 } 79 80 func (it *Iterator) Reset() { 81 it.curSpan = 0 82 } 83 84 // Returns true if the index is inside the ROI volume. Note that this optimized 85 // function maintains state and is not concurrency safe; it assumes sequential 86 // calls where the considered indexZYX is increasing in Z, Y, and X after either 87 // NewIterator() or Reset(). 88 func (it *Iterator) InsideFast(indexZYX dvid.IndexZYX) bool { 89 // Fast forward through spans to make sure we are either in span or past all 90 // smaller spans. 91 numSpans := int32(len(it.spans)) 92 for { 93 if it.curSpan >= numSpans { 94 return false 95 } 96 span := it.spans[it.curSpan] 97 if span[0] > indexZYX[2] { // check z 98 return false 99 } 100 if span[0] < indexZYX[2] { 101 it.curSpan++ 102 continue 103 } 104 if span[1] > indexZYX[1] { // check y 105 return false 106 } 107 if span[1] < indexZYX[1] { 108 it.curSpan++ 109 continue 110 } 111 if span[2] > indexZYX[0] { // check x0 112 return false 113 } 114 if span[3] >= indexZYX[0] { // check x1 115 return true 116 } 117 // We are in correct z,y but current span is before key's coordinate, so iterate. 118 it.curSpan++ 119 } 120 }