[GIS] Reverse Geocode latitude longitude with Geotools using Natural Earth Shapefiles in Java

geotoolsjavanatural-earthreverse-geocoding

I am trying to perform reverse geocoding in Java using GeoTools and Natural Earth Shapefiles. I just want to pass latitude longitude and get the corresponding country name + state name. I have searched entire internet and all the posts on this forum but couldn't find an answer for my specific use case. I am not sure how to pass the latitude longitude value and use Natural Earth shape files to get the country name and state name. I don't want to display anything on the map. Just the country and state name would be enough.

Best Answer

I'm not entirely clear on which part of your question you can't find an answer as all of these steps are pretty well documented on this site (mostly by me :-)) as well as in the tutorials and documentation. But here you go, first open the shapefiles and store the features in a memory store:

  SpatialIndexFeatureCollection countries;
  final static FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
  final static public String countryFile = "/data/natural_earth/10m_cultural/10m_admin_0_countries.shp";

  public SimpleGeoCoder() throws IOException {
    // load the country shapefile
    URL countryURL = DataUtilities.fileToURL(new File(countryFile));
    HashMap<String, Object> params = new HashMap<>();
    params.put("url", countryURL);
    DataStore ds = DataStoreFinder.getDataStore(params);
    if (ds == null) {
      throw new IOException("couldn't open " + params.get("url"));
    }
    Name name = ds.getNames().get(0);
    countries = new SpatialIndexFeatureCollection(ds.getFeatureSource(name).getFeatures());
  }

Then create a lon/lat point (as Natural Earth stores its coordinates in that order):

GeometryFactory gf = new GeometryFactory();
Point london = gf.createPoint(new Coordinate(0.0, 51.0));

Finally look up the the point in the stored features:

 public SimpleFeatureCollection lookup(Point p) {
    Filter f = ff.contains(ff.property("the_geom"), ff.literal(p));
    return countries.subCollection(f);

  }

Then print out the name (or other attributes):

SimpleFeatureIterator itr = features.features();
try {
  while (itr.hasNext()) {
    SimpleFeature f = itr.next();
    System.out.println(f.getAttribute("NAME"));
  }
} finally {
  itr.close();
}

I've put all the code into a gist for you to download. Adding administrative sub units is left as an exercise for the reader.