Skip to content

Commit 3bbe3b7

Browse files
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.
1 parent 9ba72c3 commit 3bbe3b7

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

src/main/java/com/conveyal/analysis/controllers/OpportunityDatasetController.java

+3
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,9 @@ private OpportunityDatasetUploadStatus createOpportunityDataset(Request req, Res
357357
} else if (uploadFormat == FileStorageFormat.SHP) {
358358
LOG.info("Detected opportunity dataset stored as ESRI shapefile.");
359359
pointsets.addAll(createGridsFromShapefile(fileItems, zoom, status));
360+
} else if (uploadFormat == FileStorageFormat.GEOJSON) {
361+
LOG.info("Detected opportunity dataset stored as GeoJSON.");
362+
pointsets.addAll(Grid.fromGeoJson(fileItems.get(0).getInputStream(), zoom, status));
360363
} else if (uploadFormat == FileStorageFormat.CSV) {
361364
LOG.info("Detected opportunity dataset stored as CSV");
362365
// Create a grid even when user has requested a freeform pointset so we have something to visualize.

src/main/java/com/conveyal/r5/analyst/Grid.java

+67
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
import org.geotools.data.FileDataStore;
1717
import org.geotools.data.FileDataStoreFinder;
1818
import org.geotools.data.Transaction;
19+
import org.geotools.data.geojson.GeoJSONReader;
20+
import org.geotools.data.simple.SimpleFeatureCollection;
21+
import org.geotools.data.simple.SimpleFeatureIterator;
1922
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
2023
import org.geotools.gce.geotiff.GeoTiffFormat;
2124
import org.geotools.gce.geotiff.GeoTiffWriteParams;
@@ -753,6 +756,70 @@ public static List<Grid> fromShapefile (File shapefile, int zoom, ProgressListen
753756
return new ArrayList<>(grids.values());
754757
}
755758

759+
/**
760+
* Take an `InputStream` containing GeoJson Features and turn it into an opportunity grid.
761+
*/
762+
public static List<Grid> fromGeoJson (InputStream geoJsonInputStream, int zoom, ProgressListener progressListener)
763+
throws IOException {
764+
GeoJSONReader reader = new GeoJSONReader(geoJsonInputStream);
765+
SimpleFeatureCollection features = reader.getFeatures();
766+
Envelope envelope = features.getBounds();
767+
768+
checkWgsEnvelopeSize(envelope, "Shapefile");
769+
WebMercatorExtents extents = WebMercatorExtents.forWgsEnvelope(envelope, zoom);
770+
771+
int total = features.size();
772+
if (progressListener != null) {
773+
progressListener.setTotalItems(total);
774+
}
775+
776+
AtomicInteger count = new AtomicInteger(0);
777+
HashMap<String, Grid> grids = new HashMap<>();
778+
779+
SimpleFeatureIterator featureIterator = features.features();
780+
while (featureIterator.hasNext()) {
781+
SimpleFeature feature = featureIterator.next();
782+
Geometry geom = (Geometry) feature.getDefaultGeometry();
783+
784+
for (var p : feature.getProperties()) {
785+
var val = p.getValue();
786+
787+
if (!(val instanceof Number)) continue;
788+
double numericVal = ((Number) val).doubleValue();
789+
if (numericVal == 0) continue;
790+
791+
String attributeName = p.getName().getLocalPart();
792+
793+
Grid grid = grids.get(attributeName);
794+
if (grid == null) {
795+
grid = new Grid(extents);
796+
grid.name = attributeName;
797+
grids.put(attributeName, grid);
798+
}
799+
800+
if (geom instanceof Point) {
801+
Point point = (Point) geom;
802+
// already in WGS 84
803+
grid.incrementPoint(point.getY(), point.getX(), numericVal);
804+
} else if (geom instanceof Polygon || geom instanceof MultiPolygon) {
805+
grid.rasterize(geom, numericVal);
806+
} else {
807+
throw new IllegalArgumentException("Unsupported geometry type: " + geom);
808+
}
809+
}
810+
811+
int currentCount = count.incrementAndGet();
812+
if (progressListener != null) {
813+
progressListener.setCompletedItems(currentCount);
814+
}
815+
if (currentCount % 10000 == 0) {
816+
LOG.info("{} / {} features read", human(currentCount), human(total));
817+
}
818+
}
819+
reader.close();
820+
return new ArrayList<>(grids.values());
821+
}
822+
756823
@Override
757824
public double sumTotalOpportunities() {
758825
double totalOpportunities = 0;

0 commit comments

Comments
 (0)