问题
So I have a shp-file containing a bunch of polygons. In this case, a polygon is a body of in-land water (like lakes and that kind of stuff).
My system is tracking a moving object, so in order to determine what this object is, I would like to see if this object is in water or on land AND how far it is to NEAREST shore (yes, both if it's in or out of water). I will take a sample point from the object once in a while and test it.
The system is written in Java, and I have imported GeoTools snapshot 17. But if other utils is easier to use, there is no requirements to use this.
To test if the point is IN water (that is, inside a polygon), this methods works:
private void findPolygonsForPoint(Coordinate point) {
Filter filter = null;
SimpleFeatureIterator iterator = null;
try {
filter = CQL.toFilter("CONTAINS(the_geom, POINT(" + point.x + " " + point.y + "))");
SimpleFeatureCollection collection = source.getFeatures(filter);
if(collection.size() < 1) {
System.out.println(coordinate2String(point) + " is NOT in a polygon");
} else {
System.out.println(coordinate2String(point) + " IS in a polygon");
insidePolygon++;
iterator = collection.features();
while(iterator.hasNext()) {
SimpleFeature feature = iterator.next();
//find nearest edge of the polygon
}
}
} catch(CQLException e) {
aLog.error("", e);
} catch(IOException e) {
aLog.error("", e);
} finally {
if(iterator != null) {
iterator.close();
}
}
}
Now the questions:
1) If the point is NOT in a polygon, how do I find the nearest polygon in the source (being a SimpleFeatureSource)?
2) How do I find the distance to the edge of the polygon that is closest?
Any help would be highly appreciated! Especially code examples - I'm kind of rusty on math and geometry.
Thank you.
回答1:
The easiest answer is to use a SpatialIndexFeatureCollection
to do the heavy lifting for you, it will find the nearest polygon, then you can check if you are inside or outside.
So a simple class like:
public class NearestPolygon {
private static FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
private static GeometryFactory gf = new GeometryFactory();
private SpatialIndexFeatureCollection index;
private SimpleFeature lastMatched;
public NearestPolygon(SimpleFeatureCollection features) {
index = new SpatialIndexFeatureCollection(features.getSchema());
index.addAll(features);
}
public Point findNearestPolygon(Point p) {
final double MAX_SEARCH_DISTANCE = index.getBounds().getSpan(0);
Coordinate coordinate = p.getCoordinate();
ReferencedEnvelope search = new ReferencedEnvelope(new Envelope(coordinate),
index.getSchema().getCoordinateReferenceSystem());
search.expandBy(MAX_SEARCH_DISTANCE);
BBOX bbox = ff.bbox(ff.property(index.getSchema().getGeometryDescriptor().getName()), (BoundingBox) search);
SimpleFeatureCollection candidates = index.subCollection(bbox);
double minDist = MAX_SEARCH_DISTANCE + 1.0e-6;
Coordinate minDistPoint = null;
try (SimpleFeatureIterator itr = candidates.features()) {
while (itr.hasNext()) {
SimpleFeature feature = itr.next();
LocationIndexedLine line = new LocationIndexedLine(((MultiPolygon) feature.getDefaultGeometry()).getBoundary());
LinearLocation here = line.project(coordinate);
Coordinate point = line.extractPoint(here);
double dist = point.distance(coordinate);
if (dist < minDist) {
minDist = dist;
minDistPoint = point;
lastMatched = feature;
}
}
}
Point ret = null;
if (minDistPoint == null) {
ret = gf.createPoint((Coordinate) null);
} else {
ret = gf.createPoint(minDistPoint);
}
return ret;
}
public SimpleFeature getLastMatched() {
return lastMatched;
}
}
Can be called using some code like:
public static void main(String[] args) throws IOException {
String lakes = "/data/natural_earth/10m_physical/ne_10m_lakes.shp";
HashMap<String, Object> params = new HashMap<>();
params.put("url", DataUtilities.fileToURL(new File(lakes)));
DataStore ds = DataStoreFinder.getDataStore(params);
String name = ds.getTypeNames()[0];
SimpleFeatureSource source = ds.getFeatureSource(name);
SimpleFeatureCollection features = source.getFeatures();
NearestPolygon polyFinder = new NearestPolygon(features);
for (int i = 0; i < 100; i++) {
Point p = GenerateRandomData.createRandomPoint();
Point pointOnLine = polyFinder.findNearestPolygon(p);
if (!pointOnLine.isEmpty()) {
System.out.println(i+" At " + pointOnLine + " is closest to " + p);
SimpleFeature lastMatched2 = polyFinder.getLastMatched();
String attribute = (String) lastMatched2.getAttribute("name");
if(attribute.isEmpty()) {
attribute = (String) lastMatched2.getAttribute("note");
}
if (((Geometry) (lastMatched2.getDefaultGeometry())).contains(p)) {
System.out.println("is in lake " + attribute);
} else {
System.out.println("nearest lake is " + attribute);
}
}
}
}
来源:https://stackoverflow.com/questions/44045598/java-geotools-how-to-find-distance-from-a-point-to-closest-polygon-in-shape-fil