Search code examples
javagisgeotools

read coordinates from shp file and compute distance


I want to compute the nearest distance from a point to a shp file (ports.shp) from natural earth data.

For example, I am loading the features of the file:

...
String filename = "10m_cultural/ne_10m_ports.shp";
...


 public static void Calcs(String filename) 
    throws IOException, NoSuchAuthorityCodeException, FactoryException, TransformException {

    HashMap<String, Object> params = new HashMap<>();
    params.put("url", DataUtilities.fileToURL(new File(filename)));
    DataStore ds = DataStoreFinder.getDataStore(params);

    String name = ds.getTypeNames()[0];
    SimpleFeatureSource source = ds.getFeatureSource(name);
    SimpleFeatureCollection features = source.getFeatures();

}

Now, a point for example from which I want to compute the distance is:

GeometryFactory gf = JTSFactoryFinder.getGeometryFactory();
Point p = gf.createPoint(new Coordinate(43, 18));

I know that to compute the distance I will do:

     CoordinateReferenceSystem crs = CRS.decode("EPSG:4326");           


      Point start = gf.createPoint(new Coordinate(43, 18));
      Point dest = gf.createPoint(new Coordinate(?????));

      GeodeticCalculator gc = new GeodeticCalculator(crs);
      gc.setStartingPosition(JTS.toDirectPosition(start.getCoordinate(), crs));
      gc.setDestinationPosition(JTS.toDirectPosition(dest.getCoordinate(), crs));


      double distance = gc.getOrthodromicDistance();

but I don't know how to find the coordinates of the destination point (the ports.shp file):

Point dest = gf.createPoint(new Coordinate(?????));

I have the features from loading the file but it doesn't have any getCoordinates() method.

Also, as i can see ports.shp consists of many POINT geometry.Do I have to compute somehow every point with the reference point and then select the nearest?


Solution

  • Feature has a getDefaultGeometry method which will give you the point you need. Then you can get the coordinates from the point.

    EDIT

    Your problem was a units mismatch, you were setting MinDist to the width of the bounding box (in degrees, so about 360) but comparing it to distances in metres (so about 7800000) so you never found a point close enough to save.

    I started playing with making the search more efficient by limiting the initial search bounds but it is sufficiently fast even when using the populated places data set that I can't really tell if it helps.

        final double MAX_SEARCH_DISTANCE = Math.max(index.getBounds().getWidth(), index.getBounds().getHeight());
        double searchDist = 0.01;
    
        while (searchDist < MAX_SEARCH_DISTANCE) {
            // start point (user input)
            Coordinate coordinate = p.getCoordinate();
    
            ReferencedEnvelope search = new ReferencedEnvelope(new Envelope(coordinate),
                    index.getSchema().getCoordinateReferenceSystem());
    
            search.expandBy(searchDist);
            BBOX bbox = ff.bbox(ff.property(index.getSchema().getGeometryDescriptor().getName()), (BoundingBox) search);
            SimpleFeatureCollection candidates = index.subCollection(bbox);
    
            double minDist = Double.POSITIVE_INFINITY; // can't use
                                                        // MAX_Search_dist here
                                                        // as it is degrees and
                                                        // dists are meters
            Coordinate minDistPoint = null;
            double dist = 0;
            Point dest = null;
            SimpleFeatureIterator itr = candidates.features();
            CoordinateReferenceSystem crs = DefaultGeographicCRS.WGS84;
            try {
                SimpleFeature feature = null;
                while (itr.hasNext()) {
                    feature = itr.next();
    
                    // destination point
                    dest = (Point) feature.getDefaultGeometry();
                    GeodeticCalculator gc = new GeodeticCalculator(crs);
                    gc.setStartingPosition(JTS.toDirectPosition(p.getCoordinate(), crs));
                    gc.setDestinationPosition(JTS.toDirectPosition(dest.getCoordinate(), crs));
                    // Calculate distance between points
                    dist = gc.getOrthodromicDistance();
                    // System.out.println(feature.getID()+": "+dist);
                    if (dist < minDist) {
                        minDist = dist;
                        minDistPoint = dest.getCoordinate();
                        lastMatched = feature;
                    }
                }
    
            } finally {
                itr.close();
            }
            Point ret = null;
    
            if (minDistPoint == null) {
                searchDist *= 2.0;
                System.out.println("repeat search");
            } else {
                ret = gf.createPoint(minDistPoint);
                return ret;
            }
        }
        return gf.createPoint(new Coordinate());
    }