package com.graphhopper.spatialrules; import com.graphhopper.json.geo.JsonFeature; import com.graphhopper.json.geo.JsonFeatureCollection; import com.graphhopper.routing.util.spatialrules.Polygon; import com.graphhopper.routing.util.spatialrules.SpatialRule; import com.graphhopper.routing.util.spatialrules.SpatialRuleLookup; import com.graphhopper.routing.util.spatialrules.SpatialRuleLookupArray; import com.graphhopper.util.shapes.BBox; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; public class SpatialRuleLookupBuilder { public interface SpatialRuleFactory { SpatialRule createSpatialRule(String id, final List<Polygon> polygons); } private static final Logger logger = LoggerFactory.getLogger(SpatialRuleLookupBuilder.class); /** * Builds a SpatialRuleLookup by passing the provided JSON features into the provided * SpatialRuleFactory and collecting all the SpatialRule instances that it returns, * ignoring when it returns SpatialRule.EMPTY. * * See {@link SpatialRuleLookup} and {@link SpatialRule}. * * @param jsonFeatureCollection a feature collection * @param jsonIdField the name of a property in that feature collection which serves as an id * @param spatialRuleFactory a factory which is called with all the (id, geometry) pairs. * It should provide a SpatialRule for each id it knows about, * and SpatialRule.EMPTY otherwise. * @param maxBBox limit the maximum BBox of the SpatialRuleLookup to the given BBox * @return the fully constructed SpatialRuleLookup. */ public static SpatialRuleLookup buildIndex(JsonFeatureCollection jsonFeatureCollection, String jsonIdField, SpatialRuleFactory spatialRuleFactory, BBox maxBBox) { BBox polygonBounds = BBox.createInverse(false); List<SpatialRule> spatialRules = new ArrayList<>(); for (JsonFeature jsonFeature : jsonFeatureCollection.getFeatures()) { String id = (String) jsonFeature.getProperty(jsonIdField); List<Polygon> borders = new ArrayList<>(); for (int i=0; i<jsonFeature.getGeometry().getNumGeometries(); i++) { borders.add(ghPolygonFromJTS((com.vividsolutions.jts.geom.Polygon) jsonFeature.getGeometry().getGeometryN(i))); } SpatialRule spatialRule = spatialRuleFactory.createSpatialRule(id, borders); if (spatialRule != SpatialRule.EMPTY) { spatialRules.add(spatialRule); for (Polygon polygon : spatialRule.getBorders()) { polygonBounds.update(polygon.getMinLat(), polygon.getMinLon()); polygonBounds.update(polygon.getMaxLat(), polygon.getMaxLon()); } } } if (!polygonBounds.isValid()) { return SpatialRuleLookup.EMPTY; } BBox calculatedBounds = polygonBounds.calculateIntersection(maxBBox); if (calculatedBounds == null) return SpatialRuleLookup.EMPTY; SpatialRuleLookup spatialRuleLookup = new SpatialRuleLookupArray(spatialRules, 0.1, true, calculatedBounds); logger.info("Created the SpatialRuleLookup with the following rules: " + Arrays.toString(spatialRules.toArray())); return spatialRuleLookup; } /** * Wrapper Method for {@link SpatialRuleLookupBuilder#buildIndex(JsonFeatureCollection, String, SpatialRuleFactory, BBox)}. * This method simply passes a world-wide BBox, this won't limit the SpatialRuleLookup. */ public static SpatialRuleLookup buildIndex(JsonFeatureCollection jsonFeatureCollection, String jsonIdField, SpatialRuleFactory spatialRuleFactory) { return buildIndex(jsonFeatureCollection, jsonIdField, spatialRuleFactory, new BBox(-180, 180, -90, 90)); } private static Polygon ghPolygonFromJTS(com.vividsolutions.jts.geom.Polygon polygon) { double[] lats = new double[polygon.getNumPoints()]; double[] lons = new double[polygon.getNumPoints()]; for (int i=0; i<polygon.getNumPoints(); i++) { lats[i] = polygon.getCoordinates()[i].y; lons[i] = polygon.getCoordinates()[i].x; } return new Polygon(lats, lons); } }