3

My application is backed by PostGIS. There is a particular set of tables which have one of their columns being a geographic polygon. They are relatively small in size (few thousand rows) and can easily fit in memory. They do not change that frequently so I am thinking of caching them in memory and refreshing them every few hours in case there is a change.

The queries I will be getting is to find the polygon that contains a specific point. I might also need to get the closest polygon to the specific point (typically if it does not fall within any of the polygons in that specific table).

Is there a Java API that supports this functionality in memory? It will need to support the conventional geo indexes for fast lookup of these two queries I need to use. A lot of the Java GIS APIs I've seen seem to be backed by either Shape files or a database, so not really what I need, since I can already do that.

I was looking at this R-tree implementation: https://github.com/davidmoten/rtree But for some reason it feels too low level.

On a separate question, I read this API is also possible to use, but it uses a quad-tree implementation: https://github.com/Esri/geometry-api-java

Apparently a quad-tree is more complicated (requires tile sizing configuration and similar fine-tuning parameters) and generally performs worse than an R-Tree. http://www.dpi.inpe.br/livros/bdados/artigos/oracle_r_tree.pdf

I was wondering if there is something which operates at a higher level, which supports the queries to get polygons that contain a point, and get closest polygon to a point, that is efficient.

UPDATE

Further search after a hint from one of the answers led me to GeoTools, which from the documentation seems quite comprehensive, which will probably be the first one to try out.

3

TL;DR; Don't bother, PostGIS is your friend and will be way faster than storing in memory and searching.

I did some tests with random bounding boxes on a relatively sparse data set (Electricity Transmission Lines in Great Britain) and got the following headline figures:

Memory Time: 121.0 time, 0.0121 average for 10000 queries
Indexed Time: 108.0 time, 0.0108 average for 10000 queries
PostGIS Time: 29.0 time, 0.0029 average for 10000 queries

As you can see simply sending the queries to the database is nearly an order of magnitude faster than storing in memory with or without a specific spatial index.

Longer Answer

For the truly interested here is a longer answer.

I set up three different feature sources and then made 10,000 random 10km bounding box queries against the dataset. The data is the OS Open Stack electricity transmission lines which are 3523 simple linestrings and are stored in a local PostGIS table with a spatial index.

DataStore postgisDS = DataStoreFinder.getDataStore(params);

SimpleFeatureSource featureSource = postgisDS.getFeatureSource("etl");
MemoryDataStore mDS = new MemoryDataStore(featureSource.getFeatures());

SpatialIndexFeatureCollection ifc = new SpatialIndexFeatureCollection(featureSource.getFeatures());

SimpleFeatureSource indexDS = DataUtilities.source(ifc);

So a straight to PostGIS store, a MemoryDataStore which reads all the data into memory and a source created on top of a SpatialIndexFeatureCollection which creates a QuadTree index on the features to speed things up.

I then called a timer function with the source.

  public double time(SimpleFeatureSource fs, int count) throws IOException {
    double sum = 0.0;
    PropertyName property = ff.property(fs.getSchema().getGeometryDescriptor().getLocalName());
    for (int i = 0; i < count; i++) {
      long start = new Date().getTime();
      ReferencedEnvelope bbox = getBBOX(10000);
      Filter filter = ff.bbox(property, bbox);
      Query q = new Query(typeName, filter);
      SimpleFeatureCollection features = fs.getFeatures(q);
      long end = new Date().getTime();
      try (SimpleFeatureIterator itr = features.features()) {
        int c = 0;
        Envelope b = new ReferencedEnvelope(crs);
        while (itr.hasNext()) {
          SimpleFeature f = itr.next();
          b.expandToInclude(((Geometry) f.getDefaultGeometry()).getCentroid().getCoordinate());
          c++;
        }
        // System.out.println("got " + c + " features");
      }
      sum += (end - start);

    }
    return sum;
  }  

I threw some make work into the loop to make sure the compiler doesn't optimise the actual fetch away (I never trust the compiler). The full code is here.

Finally, just for fun I tried the same for the roads_national layer which has 123k rows, oddly the index slows things down but I suspect it was a memory contention issue or the GC running from the last run.

Memory Time: 142.0 time, 0.0142 average for 10000 queries
Indexed Time: 506.0 time, 0.0506 average for 10000 queries
PostGIS Time: 27.0 time, 0.0027 average for 10000 queries
2
  • Thanks a lot for going through the hassle to benchmark this! Very interesting results. Was the database server on the same localhost or was it on a separate server? How come querying PostGIS is faster than querying an in-memory indexed data structure (is PostGIS more optimised than the Java index implementations)? Do you think an RTree instead of a QuadTree would have made it slightly faster? Regarding GC interference, maybe a test with the new JDK11 Noop GC would clear any suspicions. – jbx Oct 14 '18 at 23:16
  • Selecting this as the answer because with this solution I can actually switch easily between in-memory and postgis. – jbx Oct 18 '18 at 17:26
1

Not used it myself (not used Java for ages) but Java Topology Suite should be worth looking into. I'm not sure what you'd use for the database connectivity, but this library should cover the spatial indexing and operations side of things.

The C++ port of JTS is GEOS (which is what QGIS uses for a lot of its geometry work), but some other Java based GIS tools (e.g. gvSIG, OpenJUMP) use JTS instead.

2
  • Database connectivity is not an issue. I already take care of that to extract the polygons I need in memory from my PostGIS tables. – jbx Oct 11 '18 at 7:20
  • 1
    It seems that GeoTools also uses JTS. – jbx Oct 11 '18 at 7:27

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.