Search in sources :

Example 1 with SpatialIndex

use of org.locationtech.jts.index.SpatialIndex in project qupath by qupath.

the class PathObjectTileCache method addToCache.

/**
 * Add a PathObject to the cache, optionally including children.
 *
 * @param pathObject
 * @param includeChildren
 */
private void addToCache(PathObject pathObject, boolean includeChildren, Class<? extends PathObject> limitToClass) {
    // If the cache isn't active, we can ignore this... it will be constructed when it is needed
    if (!isActive())
        return;
    if (pathObject.hasROI()) {
        Class<? extends PathObject> cls = pathObject.getClass();
        if (limitToClass == null || cls == limitToClass) {
            SpatialIndex mapObjects = map.get(cls);
            if (mapObjects == null) {
                mapObjects = createSpatialIndex();
                map.put(cls, mapObjects);
            }
            Envelope envelope = getEnvelope(pathObject);
            mapObjects.insert(envelope, pathObject);
        }
    }
    // Add the children
    if (includeChildren && !(pathObject instanceof TemporaryObject) && pathObject.hasChildren()) {
        for (PathObject child : pathObject.getChildObjectsAsArray()) addToCache(child, includeChildren, limitToClass);
    }
}
Also used : PathObject(qupath.lib.objects.PathObject) SpatialIndex(org.locationtech.jts.index.SpatialIndex) TemporaryObject(qupath.lib.objects.TemporaryObject) Envelope(org.locationtech.jts.geom.Envelope)

Example 2 with SpatialIndex

use of org.locationtech.jts.index.SpatialIndex in project qupath by qupath.

the class PathObjectTileCache method getObjectsForRegion.

// /**
// * Add a PathObject to the cache.  Child objects are not added.
// * @param pathObject
// */
// private void addToCache(PathObject pathObject) {
// addToCache(pathObject, false);
// }
/**
 * Get all the PathObjects stored in this cache of a specified type and having ROIs with bounds overlapping a specified region.
 * This does not guarantee that the ROI (which may not be rectangular) overlaps the region...
 * but a quick test is preferred over a more expensive one.
 * <p>
 * Note that pathObjects will be added to the collection provided, if there is one.
 * The same object will be added to this collection multiple times if it overlaps different tiles -
 * again in the interests of speed, no check is made.
 * However this can be addressed by using a Set as the collection.
 * <p>
 * If a collection is not provided, another Collection is created & used instead.
 *
 * @param cls a PathObject class, or null if all object types should be returned
 * @param region an image region, or null if all objects with ROIs should be return
 * @param pathObjects an (optional) existing collection to which PathObjects should be added
 * @param includeSubclasses true if subclasses of the specified class should be included
 * @return
 */
public Collection<PathObject> getObjectsForRegion(Class<? extends PathObject> cls, ImageRegion region, Collection<PathObject> pathObjects, boolean includeSubclasses) {
    ensureCacheConstructed();
    var envelope = region == null ? MAX_ENVELOPE : getEnvelope(region);
    int z = region == null ? -1 : region.getZ();
    int t = region == null ? -1 : region.getT();
    r.lock();
    try {
        // Iterate through all the classes, getting objects of the specified class or subclasses thereof
        for (Entry<Class<? extends PathObject>, SpatialIndex> entry : map.entrySet()) {
            if (cls == null || (includeSubclasses && cls.isAssignableFrom(entry.getKey())) || cls.isInstance(entry.getKey())) {
                if (entry.getValue() != null) {
                    var list = entry.getValue().query(envelope);
                    if (list.isEmpty())
                        continue;
                    if (pathObjects == null)
                        pathObjects = new HashSet<PathObject>();
                    // Add all objects that have a parent, i.e. might be in the hierarchy
                    for (PathObject pathObject : (List<PathObject>) list) {
                        var roi = pathObject.getROI();
                        if (roi == null || region == null || (roi.getZ() == z && roi.getT() == t)) {
                            if (pathObject.getParent() != null || pathObject.isRootObject()) {
                                if (envelope.intersects(getEnvelope(pathObject)))
                                    pathObjects.add(pathObject);
                            }
                        }
                    }
                }
            // pathObjects = entry.getValue().getObjectsForRegion(region, pathObjects);
            }
        }
        // logger.info("Objects for " + region + ": " + (pathObjects == null ? 0 : pathObjects.size()));
        if (pathObjects == null)
            return Collections.emptySet();
        return pathObjects;
    } finally {
        r.unlock();
    }
}
Also used : PathObject(qupath.lib.objects.PathObject) SpatialIndex(org.locationtech.jts.index.SpatialIndex) List(java.util.List) HashSet(java.util.HashSet)

Example 3 with SpatialIndex

use of org.locationtech.jts.index.SpatialIndex in project qupath by qupath.

the class PathObjectTileCache method removeFromCache.

/**
 * This doesn't acquire the lock! The locking is done first.
 *
 * @param pathObject
 * @param removeChildren
 */
private void removeFromCache(PathObject pathObject, boolean removeChildren) {
    // If the cache isn't active, then nothing to remove
    if (!isActive())
        return;
    SpatialIndex mapObjects = map.get(pathObject.getClass());
    // We can remove objects from a Quadtree
    if (mapObjects instanceof Quadtree) {
        Envelope envelope = lastEnvelopeMap.get(pathObject);
        envelope = MAX_ENVELOPE;
        if (envelope != null) {
            if (mapObjects.remove(envelope, pathObject)) {
                logger.debug("Removed {} from cache", pathObject);
            } else
                logger.debug("Unable to remove {} from cache", pathObject);
        } else {
            logger.debug("No envelope found for {}", pathObject);
        }
        // Remove the children
        if (removeChildren) {
            for (PathObject child : pathObject.getChildObjectsAsArray()) removeFromCache(child, removeChildren);
        }
    } else if (mapObjects instanceof SpatialIndex && !removeChildren) {
        // We can't remove objects from a STRtree, but since we're just removing one object we can rebuild only the cache for this class
        constructCache(pathObject.getClass());
    } else {
        // If we need to remove multiple objects, better to just rebuild the entire cache
        constructCache(null);
    }
}
Also used : Quadtree(org.locationtech.jts.index.quadtree.Quadtree) PathObject(qupath.lib.objects.PathObject) SpatialIndex(org.locationtech.jts.index.SpatialIndex) Envelope(org.locationtech.jts.geom.Envelope)

Example 4 with SpatialIndex

use of org.locationtech.jts.index.SpatialIndex in project OpenTripPlanner by opentripplanner.

the class HashGridTest method testHashGridRandom.

/**
 * We perform a non-regression random test. We insert many random-envelop objects
 * into both a hash grid (OTP) and STRtree (JTS) spatial indexes. We check with
 * many random query that the set of returned objects is the same (after pruning
 * because both could return false positives).
 */
@SuppressWarnings("unchecked")
public void testHashGridRandom() {
    final double X0 = -0.05;
    final double Y0 = 44.0;
    final double DX = 0.1;
    final double DY = 0.1;
    final int N_OBJS = 1000;
    final int N_QUERIES = 1000;
    Random rand = new Random(42);
    SpatialIndex hashGrid = new HashGridSpatialIndex<>();
    SpatialIndex strTree = new STRtree();
    for (int i = 0; i < N_OBJS; i++) {
        Coordinate a = new Coordinate(rand.nextDouble() * DX + X0, rand.nextDouble() * DY + Y0);
        Coordinate b = new Coordinate(rand.nextDouble() * DX + X0, rand.nextDouble() * DY + Y0);
        DummyObject obj = new DummyObject();
        obj.envelope = new Envelope(a, b);
        hashGrid.insert(obj.envelope, obj);
        strTree.insert(obj.envelope, obj);
    }
    for (int i = 0; i < N_QUERIES; i++) {
        Coordinate a = new Coordinate(rand.nextDouble() * DX + X0, rand.nextDouble() * DY + Y0);
        Coordinate b = new Coordinate(rand.nextDouble() * DX + X0, rand.nextDouble() * DY + Y0);
        Envelope searchEnv = new Envelope(a, b);
        List<DummyObject> hashGridObjs = hashGrid.query(searchEnv);
        // Need to remove non intersecting
        Set<DummyObject> hashGridObjs2 = new HashSet<>();
        for (DummyObject obj : hashGridObjs) {
            if (obj.envelope.intersects(searchEnv))
                hashGridObjs2.add(obj);
        }
        List<DummyObject> strtreeObjs = hashGrid.query(searchEnv);
        // Need to remove non intersecting
        Set<DummyObject> strtreeObjs2 = new HashSet<>();
        for (DummyObject obj : strtreeObjs) {
            if (obj.envelope.intersects(searchEnv))
                strtreeObjs2.add(obj);
        }
        boolean equals = hashGridObjs2.equals(strtreeObjs2);
        assertTrue(equals);
    }
}
Also used : Envelope(org.locationtech.jts.geom.Envelope) Random(java.util.Random) Coordinate(org.locationtech.jts.geom.Coordinate) SpatialIndex(org.locationtech.jts.index.SpatialIndex) STRtree(org.locationtech.jts.index.strtree.STRtree) HashSet(java.util.HashSet)

Aggregations

SpatialIndex (org.locationtech.jts.index.SpatialIndex)4 Envelope (org.locationtech.jts.geom.Envelope)3 PathObject (qupath.lib.objects.PathObject)3 HashSet (java.util.HashSet)2 List (java.util.List)1 Random (java.util.Random)1 Coordinate (org.locationtech.jts.geom.Coordinate)1 Quadtree (org.locationtech.jts.index.quadtree.Quadtree)1 STRtree (org.locationtech.jts.index.strtree.STRtree)1 TemporaryObject (qupath.lib.objects.TemporaryObject)1