From 36c1d9f9fb854c3618a626398aecbe699b047a17 Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Thu, 26 Oct 2023 17:44:15 +0800 Subject: [PATCH 1/2] Create opportunity datasets from GeoJSON Closely mirrors the way Shapefiles are converted into grids. With GeoTools it is quite simple. It's possible to abstract common creation code between the two types but may not be necessary unless more types are added. --- .../OpportunityDatasetController.java | 3 + .../java/com/conveyal/r5/analyst/Grid.java | 67 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/src/main/java/com/conveyal/analysis/controllers/OpportunityDatasetController.java b/src/main/java/com/conveyal/analysis/controllers/OpportunityDatasetController.java index b1afcfc05..4700caafa 100644 --- a/src/main/java/com/conveyal/analysis/controllers/OpportunityDatasetController.java +++ b/src/main/java/com/conveyal/analysis/controllers/OpportunityDatasetController.java @@ -358,6 +358,9 @@ private OpportunityDatasetUploadStatus createOpportunityDataset(Request req, Res } else if (uploadFormat == FileStorageFormat.SHP) { LOG.info("Detected opportunity dataset stored as ESRI shapefile."); pointsets.addAll(createGridsFromShapefile(fileItems, zoom, status)); + } else if (uploadFormat == FileStorageFormat.GEOJSON) { + LOG.info("Detected opportunity dataset stored as GeoJSON."); + pointsets.addAll(Grid.fromGeoJson(fileItems.get(0).getInputStream(), zoom, status)); } else if (uploadFormat == FileStorageFormat.CSV) { LOG.info("Detected opportunity dataset stored as CSV"); // Create a grid even when user has requested a freeform pointset so we have something to visualize. diff --git a/src/main/java/com/conveyal/r5/analyst/Grid.java b/src/main/java/com/conveyal/r5/analyst/Grid.java index ad9faf1ed..4469c5a37 100644 --- a/src/main/java/com/conveyal/r5/analyst/Grid.java +++ b/src/main/java/com/conveyal/r5/analyst/Grid.java @@ -16,6 +16,9 @@ import org.geotools.data.FileDataStore; import org.geotools.data.FileDataStoreFinder; import org.geotools.data.Transaction; +import org.geotools.data.geojson.GeoJSONReader; +import org.geotools.data.simple.SimpleFeatureCollection; +import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.gce.geotiff.GeoTiffFormat; import org.geotools.gce.geotiff.GeoTiffWriteParams; @@ -753,6 +756,70 @@ public static List fromShapefile (File shapefile, int zoom, ProgressListen return new ArrayList<>(grids.values()); } + /** + * Take an `InputStream` containing GeoJson Features and turn it into an opportunity grid. + */ + public static List fromGeoJson (InputStream geoJsonInputStream, int zoom, ProgressListener progressListener) + throws IOException { + GeoJSONReader reader = new GeoJSONReader(geoJsonInputStream); + SimpleFeatureCollection features = reader.getFeatures(); + Envelope envelope = features.getBounds(); + + checkWgsEnvelopeSize(envelope, "Shapefile"); + WebMercatorExtents extents = WebMercatorExtents.forWgsEnvelope(envelope, zoom); + + int total = features.size(); + if (progressListener != null) { + progressListener.setTotalItems(total); + } + + AtomicInteger count = new AtomicInteger(0); + HashMap grids = new HashMap<>(); + + SimpleFeatureIterator featureIterator = features.features(); + while (featureIterator.hasNext()) { + SimpleFeature feature = featureIterator.next(); + Geometry geom = (Geometry) feature.getDefaultGeometry(); + + for (var p : feature.getProperties()) { + var val = p.getValue(); + + if (!(val instanceof Number)) continue; + double numericVal = ((Number) val).doubleValue(); + if (numericVal == 0) continue; + + String attributeName = p.getName().getLocalPart(); + + Grid grid = grids.get(attributeName); + if (grid == null) { + grid = new Grid(extents); + grid.name = attributeName; + grids.put(attributeName, grid); + } + + if (geom instanceof Point) { + Point point = (Point) geom; + // already in WGS 84 + grid.incrementPoint(point.getY(), point.getX(), numericVal); + } else if (geom instanceof Polygon || geom instanceof MultiPolygon) { + grid.rasterize(geom, numericVal); + } else { + throw new IllegalArgumentException("Unsupported geometry type: " + geom); + } + } + + int currentCount = count.incrementAndGet(); + if (progressListener != null) { + progressListener.setCompletedItems(currentCount); + } + if (currentCount % 10000 == 0) { + LOG.info("{} / {} features read", human(currentCount), human(total)); + } + } + reader.close(); + return new ArrayList<>(grids.values()); + } + @Override public double sumTotalOpportunities() { double totalOpportunities = 0; From cfe9fd30ef02a19c89718f023328191dfb82fa7d Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Tue, 27 Feb 2024 21:59:00 +0800 Subject: [PATCH 2/2] Update src/main/java/com/conveyal/r5/analyst/Grid.java Co-authored-by: Anson Stewart --- src/main/java/com/conveyal/r5/analyst/Grid.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/r5/analyst/Grid.java b/src/main/java/com/conveyal/r5/analyst/Grid.java index 4469c5a37..546e9490b 100644 --- a/src/main/java/com/conveyal/r5/analyst/Grid.java +++ b/src/main/java/com/conveyal/r5/analyst/Grid.java @@ -765,7 +765,7 @@ public static List fromGeoJson (InputStream geoJsonInputStream, int zoom, SimpleFeatureCollection features = reader.getFeatures(); Envelope envelope = features.getBounds(); - checkWgsEnvelopeSize(envelope, "Shapefile"); + checkWgsEnvelopeSize(envelope, "GeoJSON file"); WebMercatorExtents extents = WebMercatorExtents.forWgsEnvelope(envelope, zoom); int total = features.size();