org.integratedmodelling.engine.geospace.extents.SpatialIndex Maven / Gradle / Ivy
The newest version!
package org.integratedmodelling.engine.geospace.extents;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.integratedmodelling.api.modelling.IScale.Locator;
import org.integratedmodelling.api.space.IShape.Type;
import org.integratedmodelling.api.space.ISpatialExtent;
import org.integratedmodelling.api.space.ISpatialIndex;
import org.integratedmodelling.collections.Pair;
import org.integratedmodelling.common.space.IGeometricShape;
import org.integratedmodelling.common.vocabulary.GeoNS;
import org.integratedmodelling.engine.geospace.Geospace;
import org.integratedmodelling.engine.geospace.literals.ShapeValue;
import com.infomatiq.jsi.Rectangle;
import com.infomatiq.jsi.rtree.RTree;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.operation.distance.DistanceOp;
import gnu.trove.procedure.TIntProcedure;
public class SpatialIndex implements ISpatialIndex {
RTree rtree = new RTree();
Map ids = new HashMap<>();
Map names = new HashMap<>();
Map exts = new HashMap<>();
ISpatialExtent extent;
int nextId = 1;
public SpatialIndex(ISpatialExtent extent) {
this.extent = extent;
this.rtree.init(new Properties());
}
@Override
public void add(ISpatialExtent extent, String name) {
exts.put(name, extent);
rtree.add(getRectangle(extent), getId(name));
}
private int getId(String name) {
Integer ret = ids.get(name);
if (ret == null) {
ret = nextId++;
ids.put(name, ret);
names.put(ret, name);
}
return ret;
}
private Rectangle getRectangle(ISpatialExtent extent) {
Geometry shape = ((IGeometricShape) extent.getShape()).getStandardizedGeometry();
ReferencedEnvelope env = new ShapeValue(shape, Geospace.get().getDefaultCRS()).getEnvelope();
return new Rectangle((float) env.getMinX(), (float) env.getMinY(), (float) env
.getMaxX(), (float) env.getMaxY());
}
@Override
public double distanceToNearestObjectFrom(Locator position) {
return distanceToNearestObjectFrom(extent.locate(position));
}
@Override
public double distanceToNearestObjectFrom(int offset) {
double[] xy = getCoordinates(offset);
int id = new FeatureFinder(xy).find();
return getDistance(xy, exts.get(names.get(id)));
}
private double getDistance(double[] xy, ISpatialExtent extent) {
if (extent == null) {
return Double.NaN;
}
/*
* we're on the feature, distance is 0. Intersector does check for actual point on
* actual feature.
*/
if (!new FeatureIntersector(xy).find().isEmpty()) {
return 0;
}
if (extent.getShape().getGeometryType() == Type.POINT) {
Point point = (Point) ((IGeometricShape) extent.getShape()).getGeometry();
return GeoNS
.getDistance(new Coordinate(xy[0], xy[1]), new Coordinate(point.getX(), point.getY()))
* 1000.0;
}
/*
* complicated intersection finding between extent and extent boundary. Let JTS help.
*/
Coordinate[] coords = DistanceOp.nearestPoints(ShapeValue
.makePoint(xy[0], xy[1]), ((IGeometricShape) extent.getShape()).getGeometry());
if (coords == null || coords.length < 2) {
return Double.NaN;
}
return GeoNS.getDistance(coords[0], coords[1]) * 1000.0;
}
@Override
public Collection> getNearest(Locator position, int maxResults) {
// TODO Auto-generated method stub
return null;
}
double[] getCoordinates(int ofs) {
ISpatialExtent loc = extent.getExtent(ofs);
ShapeValue sh = new ShapeValue(((IGeometricShape) loc.getShape()).getStandardizedGeometry(), Geospace
.get().getDefaultCRS());
Point point = ((Point) sh.getCentroid().getGeometry());
return new double[] { point.getX(), point.getY() };
}
@Override
public ISpatialExtent getExtent() {
return extent;
}
@Override
public double distanceBetween(int offset, String objectId) {
// TODO Auto-generated method stub
return 0;
}
@Override
public boolean contains(String objectId) {
// TODO Auto-generated method stub
return false;
}
@Override
public double distanceBetween(Locator position, String objectId) {
// TODO Auto-generated method stub
return 0;
}
class FeatureFinder {
int idx;
private com.infomatiq.jsi.Point point;
FeatureFinder(double[] xy) {
this.point = new com.infomatiq.jsi.Point((float) xy[0], (float) xy[1]);
}
int find() {
rtree.nearest(point, new TIntProcedure() {
@Override
public boolean execute(int arg0) {
idx = arg0;
return true;
}
}, Float.POSITIVE_INFINITY);
return idx;
}
}
class FeatureIntersector {
Set idx = new HashSet<>();
private com.infomatiq.jsi.Point point;
FeatureIntersector(double[] xy) {
this.point = new com.infomatiq.jsi.Point((float) xy[0], (float) xy[1]);
}
Set find() {
rtree.intersects(new Rectangle(point.x, point.y, point.x, point.y), new TIntProcedure() {
@Override
public boolean execute(int arg0) {
if (ShapeValue.makePoint(point.x, point.y)
.overlaps(((IGeometricShape) exts.get(names.get(arg0)).getShape())
.getGeometry())) {
idx.add(arg0);
}
return true;
}
});
return idx;
}
}
@Override
public int size() {
return ids.size();
}
}