0

We're trying to use polygons taken from OpenStreetMap and determine user locations. We're using geojson to parse the GeoJSONs we generate from OSM, but the deserialized polygons aren't being populated correctly - no holes are detected, instead we get a set of polygons.

Examining the the exported GeoJSON from OSM does seem to represent a set of multi-polygons and not a polygon with holes. ie, Rome (OSM), Vatican isn't being populated as a hole, instead there are two polygons - the Rome polygon(containing the Vatican), and the Vatican polygon.

Checking some tools such as Mapbox or JOSM do parse it correct and display the holes.

Is there a java lib that parses polygons with holes even on such cases?

Rome's GeoJSON (OSM)

Snippet (Kotlin):

val romeGeom = GeometryJSON().readGeometryCollection(getFileContent("rome.json")).getGeometryN(0)
    with (org.locationtech.jts.geom.GeometryFactory()) {
        val map = mapOf(
            "rome" to createPoint(org.locationtech.jts.geom.Coordinate(12.4458, 41.9040)),
            "vatican" to createPoint(org.locationtech.jts.geom.Coordinate(12.44597, 41.90197)), // Expected to be false
            "rome2" to createPoint(org.locationtech.jts.geom.Coordinate(12.3049, 42.1121)))

        map.forEach { (key, value) ->
            println("$key: ${value.within(romeGeom)}")
        }
    }

Using gt-geojsondatastore v26.3 yields StackOverflowError (tried couple of geo-jsons):

val store = GeoJSONDataStore(getFileUrl("rome.json"))
val featureSource = store.getFeatureSource(store.typeNames[0])
val feat = featureSource.features //StackOverflowError

Error:

Exception in thread "main" java.lang.StackOverflowError
at java.io.UnixFileSystem.getBooleanAttributes0(Native Method)
at java.io.UnixFileSystem.getBooleanAttributes(UnixFileSystem.java:242)
at java.io.File.isDirectory(File.java:860)
at sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:82)
at sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:188)
at java.net.URL.openStream(URL.java:1068)
at org.geotools.data.geojson.GeoJSONReader.isConnected(GeoJSONReader.java:124)
at org.geotools.data.geojson.GeoJSONReader.getIterator(GeoJSONReader.java:406)
at org.geotools.data.geojson.GeoJSONFeatureSource.buildFeatureType(GeoJSONFeatureSource.java:107)
at org.geotools.data.geojson.GeoJSONFeatureStore.buildFeatureType(GeoJSONFeatureStore.java:78)
at org.geotools.data.store.ContentFeatureSource.getAbsoluteSchema(ContentFeatureSource.java:338)
at org.geotools.data.store.ContentFeatureSource.getSchema(ContentFeatureSource.java:307)
at org.geotools.data.store.ContentDataStore.getSchema(ContentDataStore.java:295)
at org.geotools.data.store.ContentDataStore.getSchema(ContentDataStore.java:633)
at org.geotools.data.geojson.GeoJSONDataStore.getSchema(GeoJSONDataStore.java:111)
at org.geotools.data.geojson.GeoJSONFeatureSource.buildFeatureType(GeoJSONFeatureSource.java:121)
at org.geotools.data.geojson.GeoJSONFeatureStore.buildFeatureType(GeoJSONFeatureStore.java:78)
at org.geotools.data.store.ContentFeatureSource.getAbsoluteSchema(ContentFeatureSource.java:338)
at org.geotools.data.store.ContentFeatureSource.getSchema(ContentFeatureSource.java:307)
at org.geotools.data.store.ContentDataStore.getSchema(ContentDataStore.java:295)
at org.geotools.data.store.ContentDataStore.getSchema(ContentDataStore.java:633)
at org.geotools.data.geojson.GeoJSONDataStore.getSchema(GeoJSONDataStore.java:111)
at org.geotools.data.geojson.GeoJSONFeatureSource.buildFeatureType(GeoJSONFeatureSource.java:121)
at org.geotools.data.geojson.GeoJSONFeatureStore.buildFeatureType(GeoJSONFeatureStore.java:78)
at org.geotools.data.store.ContentFeatureSource.getAbsoluteSchema(ContentFeatureSource.java:338)
at org.geotools.data.store.ContentFeatureSource.getSchema(ContentFeatureSource.java:307)
9
  • Software Recommendations is the SE place for software recommendations.
    – Vince
    Mar 17 at 11:45
  • have you tried GeoTools (geotools.org)
    – Ian Turton
    Mar 17 at 11:56
  • @IanTurton Yes I have. Actually the geojson is by geotools
    – OBY Trance
    Mar 17 at 12:15
  • Can you add the code you are running? I'm not clear on what exactly the issue is
    – Ian Turton
    Mar 17 at 12:36
  • @IanTurton Snippet added.
    – OBY Trance
    Mar 19 at 11:22

2 Answers 2

1

I looked at this a few weeks ago on a separate platform:

Here is what I said:


Well, it looks like the multipolygons are incorrectly generated. From the spec,

A linear ring MUST follow the right-hand rule with respect to the area it bounds, i.e., exterior rings are counterclockwise, and holes are clockwise

Check the exterior ring for Rome. Technically speaking, it is the “inner” ring with another “inner” ring from the Vatican.


For a compliant geojson where software doesn't have to guess, I would use Nominatim. An earlier version of the specification did not specify that winding rule, but software may not want to "guess" what the appropriate behavior is.

Example: https://nominatim.openstreetmap.org/search/Rome?format=geojson&polygon_geojson=1

See https://nominatim.org/release-docs/develop/api/Overview/ for the full API. See https://operations.osmfoundation.org/policies/nominatim/ for the usage policy

0

It seems that you are trying to parse a GeometryCollection not a FeatureCollection. What you have is not GeoJSON but just JSON. There is a class com.bedatadriven.jackson.datatype.jts.parsers.GeometryParser which is packaged in with GeoTools that should handle this sort of file:

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;

import com.bedatadriven.jackson.datatype.jts.JtsModule;
import com.bedatadriven.jackson.datatype.jts.parsers.GenericGeometryParser;
import com.bedatadriven.jackson.datatype.jts.parsers.GeometryParser;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class ReadJSONGeometryCollection {
  private static JsonFactory factory = new JsonFactory();
  public static void main(String[] args) throws IOException {
    GeometryFactory gf = new GeometryFactory();
    File f = new File("/home/ian/Downloads/rome.geojson");
    GenericGeometryParser parser = new GenericGeometryParser(gf);
    try (JsonParser lParser = factory.createParser(new FileInputStream(f))) {
      ObjectMapper mapper = new ObjectMapper();
      mapper.registerModule(new JtsModule());
      ObjectNode node = mapper.readTree(lParser);
      GeometryParser<Geometry> gParser = new GenericGeometryParser(gf);
      Geometry g = gParser.geometryFromJson(node);
      System.out.println(g);
    } catch (IOException e) {
      throw new RuntimeException("problem parsing Geometry", e);
    }
  }
}

Which gives me the following output from your file:

 GEOMETRYCOLLECTION (MULTIPOLYGON (((12.2429442 42.0172481, 12.242935 42.0171496, 12.2429089 ... 12.4457442 41.9019597))))

Which explains why you aren't seeing any holes, there aren't any - it is one big MultiPolygon (inside a GeometryCollection). There should be no problem parsing a geometry with holes in it, the code is all there.

2
  • You say that the (geo)json doesn't contain holes, but it clearly does at OSM. Is there a way to calc those holes? I tried with within, covers etc but none gave me the expected results - f(a)=true, f(b)=false.
    – OBY Trance
    Mar 22 at 10:55
  • Somehow when exporting it you have collapsed it into a single multipolygon with no holes - no idea how to fix it
    – Ian Turton
    Mar 22 at 11:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.