From 3bbe3b7a433659bb2f3560cb0f775f52f28c1c29 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 c28363e0a..d9be8db8b 100644 --- a/src/main/java/com/conveyal/analysis/controllers/OpportunityDatasetController.java +++ b/src/main/java/com/conveyal/analysis/controllers/OpportunityDatasetController.java @@ -357,6 +357,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 4c8a8fd8a821b5719f3d48f1cdac9a3ad98e14a6 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();