class SpatialRuleLookupBuilder {

    public static SpatialRuleLookup buildIndex(List<JsonFeatureCollection> jsonFeatureCollections, String jsonIdField,
                                               SpatialRuleFactory spatialRuleFactory, Envelope maxBBox) {
        Envelope envelope = new Envelope();
        List<SpatialRule> spatialRules = new ArrayList<>();

        for (JsonFeatureCollection featureCollection : jsonFeatureCollections) {
            for (JsonFeature jsonFeature : featureCollection.getFeatures()) {
                String id = jsonIdField.isEmpty() || toLowerCase(jsonIdField).equals("id") ? jsonFeature.getId() : (String) jsonFeature.getProperty(jsonIdField);
                if (id == null || id.isEmpty())
                    throw new IllegalArgumentException("ID cannot be empty but was for JsonFeature " + featureCollection.getFeatures().indexOf(jsonFeature));

                List<Polygon> borders = new ArrayList<>();
                for (int i = 0; i < jsonFeature.getGeometry().getNumGeometries(); i++) {
                    Geometry poly = jsonFeature.getGeometry().getGeometryN(i);
                    if (poly instanceof Polygon)
                        borders.add((Polygon) poly);
                    else
                        throw new IllegalArgumentException("Geometry for " + id + " (" + i + ") not supported " + poly.getClass().getSimpleName());
                }
                
                SpatialRule spatialRule = spatialRuleFactory.createSpatialRule(id, borders);
                if (spatialRule != SpatialRule.EMPTY) {
                    spatialRules.add(spatialRule);
                    for (Polygon polygon : spatialRule.getBorders()) {
                        envelope.expandToInclude(polygon.getEnvelopeInternal());
                    }
                }
            }
        }

        Envelope calculatedBounds = envelope.intersection(maxBBox);
        if (calculatedBounds.isNull())
            return SpatialRuleLookup.EMPTY;


        // ensure that the spatial ID is stable
        Collections.sort(spatialRules, SPATIAL_ID_COMP);
        SpatialRuleLookup spatialRuleLookup = new SpatialRuleLookupJTS(spatialRules, calculatedBounds);

        logger.info("Created the SpatialRuleLookup with the following rules: {}", Arrays.toString(spatialRules.toArray()));

        return spatialRuleLookup;
    }

}