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);
}
}
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();
}
}
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);
}
}
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);
}
}
Aggregations