【问题标题】:Java GeoTools: how to find distance from a point to closest polygon in shape fileJava GeoTools:如何在形状文件中查找点到最近多边形的距离
【发布时间】:2017-10-18 03:22:38
【问题描述】:

所以我有一个包含一堆多边形的 shp 文件。在这种情况下,多边形是一片内陆水域(如湖泊之类的东西)。

我的系统正在跟踪一个移动的物体,所以为了确定这个物体是什么,我想看看这个物体​​是在水中还是在陆地上,以及它离最近的海岸有多远(是的,如果它在或出水)。我会不时从对象中抽取一个样本点并对其进行测试。

系统是Java编写的,我已经导入了GeoTools snapshot 17。但是如果其他utils更容易使用,没有要求使用这个。

要测试该点是否在水中(即在多边形内),此方法有效:

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

现在问题:

1) 如果点不在多边形中,我如何在源中找到最近的多边形(作为 SimpleFeatureSource)?

2) 如何找到距离最近的多边形边缘的距离?

任何帮助将不胜感激!尤其是代码示例——我对数学和几何有点生疏了。

谢谢。

【问题讨论】:

    标签: java polygon distance geotools


    【解决方案1】:

    最简单的答案是使用SpatialIndexFeatureCollection 为您完成繁重的工作,它会找到最近的多边形,然后您可以检查您是在里面还是在外面。

    这样一个简单的类:

    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;
      }
    }
    

    可以使用如下代码调用:

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

    【讨论】:

    • 成功了 - 非常感谢!一个额外的问题......假设我有多个 shp 文件(它们可能会用数据相互补充,或者属于不同的领域),最好的方法可能是什么? - 在一个来源中收集所有功能(随着功能数量的增加,它似乎变得很慢)。 - 或者只是循环遍历每个源,然后比较每个源的结果(跳过 index.getBounds() 不包含点的那些)?关于什么可能更快的任何想法?再次,非常感谢:-)
    • 如果您可以通过检查其边界来排除整个文件,那么这将是最快的。随着文件大小的增加,它不应该变得更慢。如果您有大量数据,那么使用 postgis 之类的数据库将是最快的
    • 另一个相关的问题:我正在阅读 3 个 shapefile(目前)来检查我的观点。其中两个工作得很好,但最后一个(包含欧洲的海岸线)在一些不同的坐标系中。因此,当我调用 source.getFeatures().getBounds() 来获取我想知道我的点是否在其中的边界框时,我会得到类似 ReferencedEnvelope[943609.7539000002 : 7601958.499999998, -375446.03119999985 : 6825119.3209000025] 的东西,而不是坐标期待。如何将其转换为我可以使用的东西?
    • 您应该提出新问题,而不是在现有问题上使用 cmets - docs.geotools.org/latest/tutorials/geometry/geometrycrs.html
    • 标签点是在地图上放置标签的位置,您只想着陆。
    猜你喜欢
    • 2021-07-20
    • 2021-12-06
    • 2020-05-10
    • 2016-01-23
    • 2013-05-03
    • 2021-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多