From b00b4fb925e1fd34a6a923acc30ad54399ca4bee Mon Sep 17 00:00:00 2001 From: Gabriel Roldan Date: Sun, 28 Jun 2015 03:18:47 -0300 Subject: [PATCH] Change VectorTileBuilder.addFeature() signature to simplify and clarify implementations. Avoid duplicated code, make it clear what geometry to encode. --- .../wms/geojson/GeoJsonWMSBuilder.java | 62 ++++--------------- .../wms/mapbox/MapBoxTileBuilder.java | 25 +------- .../wms/topojson/TopologyBuilder.java | 53 +++------------- .../wms/vector/VectorTileBuilder.java | 7 ++- .../wms/vector/VectorTileMapOutputFormat.java | 58 +++++++++++++---- 5 files changed, 73 insertions(+), 132 deletions(-) diff --git a/src/community/vectortiles/src/main/java/org/geoserver/wms/geojson/GeoJsonWMSBuilder.java b/src/community/vectortiles/src/main/java/org/geoserver/wms/geojson/GeoJsonWMSBuilder.java index 3f0d7d1b8d4..3b2d462ad37 100644 --- a/src/community/vectortiles/src/main/java/org/geoserver/wms/geojson/GeoJsonWMSBuilder.java +++ b/src/community/vectortiles/src/main/java/org/geoserver/wms/geojson/GeoJsonWMSBuilder.java @@ -7,7 +7,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; -import java.util.List; +import java.util.Map; import javax.measure.unit.SI; import javax.measure.unit.Unit; @@ -20,16 +20,10 @@ import org.geoserver.wms.vector.VectorTileBuilder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; -import org.opengis.feature.simple.SimpleFeature; -import org.opengis.feature.simple.SimpleFeatureType; -import org.opengis.feature.type.AttributeDescriptor; -import org.opengis.feature.type.GeometryDescriptor; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.google.common.base.Charsets; -import com.google.common.base.Preconditions; import com.vividsolutions.jts.geom.Geometry; -import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.PrecisionModel; import com.vividsolutions.jts.precision.CoordinatePrecisionReducerFilter; @@ -73,19 +67,9 @@ public GeoJsonWMSBuilder(Rectangle mapSize, ReferencedEnvelope mapArea) { } @Override - public void addFeature(SimpleFeature feature) { + public void addFeature(String layerName, String featureId, String geometryName, Geometry aGeom, + Map properties) { - final SimpleFeatureType fType = feature.getFeatureType(); - final GeometryDescriptor defaultGeomType = fType.getGeometryDescriptor(); - Preconditions.checkNotNull(defaultGeomType); - - Geometry aGeom = (Geometry) feature.getDefaultGeometry(); - if (aGeom == null) { - return; - } - if (aGeom instanceof GeometryCollection && aGeom.getNumGeometries() == 1) { - aGeom = aGeom.getGeometryN(0); - } if (precisionReducerFilter != null) { aGeom.apply(precisionReducerFilter); } @@ -93,47 +77,26 @@ public void addFeature(SimpleFeature feature) { jsonWriter.object(); jsonWriter.key("type").value("Feature"); - List types = fType.getAttributeDescriptors(); - - jsonWriter.key("id").value(feature.getID()); + jsonWriter.key("id").value(featureId); jsonWriter.key("geometry"); // Write the geometry, whether it is a null or not jsonWriter.writeGeom(aGeom); - jsonWriter.key("geometry_name").value(defaultGeomType.getLocalName()); + jsonWriter.key("geometry_name").value(geometryName); jsonWriter.key("properties"); jsonWriter.object(); - for (int j = 0; j < types.size(); j++) { - Object value = feature.getAttribute(j); - AttributeDescriptor attributeDescriptor = types.get(j); - - if (value != null) { - if (value instanceof Geometry) { - // This is an area of the spec where they - // decided to 'let convention evolve', - // that is how to handle multiple - // geometries. My take is to print the - // geometry here if it's not the default. - // If it's the default that you already - // printed above, so you don't need it here. - if (attributeDescriptor.equals(defaultGeomType)) { - // Do nothing, we wrote it above - // jsonWriter.value("geometry_name"); - } else { - jsonWriter.key(attributeDescriptor.getLocalName()); - jsonWriter.writeGeom((Geometry) value); - } - } else { - jsonWriter.key(attributeDescriptor.getLocalName()); - jsonWriter.value(value); - } + for (Map.Entry e : properties.entrySet()) { + String attributeName = e.getKey(); + Object value = e.getValue(); - } else { - jsonWriter.key(attributeDescriptor.getLocalName()); + jsonWriter.key(attributeName); + if (value == null) { jsonWriter.value(null); + } else { + jsonWriter.value(value); } } @@ -165,5 +128,4 @@ public WebMap build(WMSMapContent mapContent) throws IOException { return map; } - } diff --git a/src/community/vectortiles/src/main/java/org/geoserver/wms/mapbox/MapBoxTileBuilder.java b/src/community/vectortiles/src/main/java/org/geoserver/wms/mapbox/MapBoxTileBuilder.java index 193f9581d0f..b643a0ec745 100644 --- a/src/community/vectortiles/src/main/java/org/geoserver/wms/mapbox/MapBoxTileBuilder.java +++ b/src/community/vectortiles/src/main/java/org/geoserver/wms/mapbox/MapBoxTileBuilder.java @@ -4,9 +4,6 @@ import java.awt.Rectangle; import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import no.ecc.vectortile.VectorTileEncoder; @@ -16,8 +13,6 @@ import org.geoserver.wms.map.RawMap; import org.geoserver.wms.vector.VectorTileBuilder; import org.geotools.geometry.jts.ReferencedEnvelope; -import org.opengis.feature.Property; -import org.opengis.feature.simple.SimpleFeature; import com.vividsolutions.jts.geom.Geometry; @@ -37,28 +32,14 @@ public MapBoxTileBuilder(Rectangle mapSize, ReferencedEnvelope mapArea) { } @Override - public void addFeature(SimpleFeature feature) { - String layerName = feature.getFeatureType().getTypeName(); - Map attributes = propertiesToAttributes(feature.getProperties()); - Geometry geometry = (Geometry) feature.getDefaultGeometry(); - encoder.addFeature(layerName, attributes, geometry); - } + public void addFeature(String layerName, String featureId, String geometryName, + Geometry geometry, Map properties) { - protected Map propertiesToAttributes(Collection properties) { - Map attributes = new HashMap(); - Iterator it = properties.iterator(); - while (it.hasNext()) { - Property property = it.next(); - if (!(property.getValue() instanceof Geometry)) { - attributes.put(property.getName().getLocalPart(), property.getValue()); - } - } - return attributes; + encoder.addFeature(layerName, properties, geometry); } @Override public WebMap build(WMSMapContent mapContent) throws IOException { - byte[] contents = encoder.encode(); return new RawMap(mapContent, contents, MIME_TYPE); } diff --git a/src/community/vectortiles/src/main/java/org/geoserver/wms/topojson/TopologyBuilder.java b/src/community/vectortiles/src/main/java/org/geoserver/wms/topojson/TopologyBuilder.java index 697507ef40e..6f5f50bc1ad 100644 --- a/src/community/vectortiles/src/main/java/org/geoserver/wms/topojson/TopologyBuilder.java +++ b/src/community/vectortiles/src/main/java/org/geoserver/wms/topojson/TopologyBuilder.java @@ -25,12 +25,6 @@ import org.geoserver.wms.vector.VectorTileBuilder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.renderer.lite.RendererUtilities; -import org.opengis.feature.Attribute; -import org.opengis.feature.ComplexAttribute; -import org.opengis.feature.Feature; -import org.opengis.feature.GeometryAttribute; -import org.opengis.feature.Property; -import org.opengis.feature.simple.SimpleFeature; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.referencing.operation.TransformException; @@ -77,18 +71,18 @@ public TopologyBuilder(Rectangle mapSize, ReferencedEnvelope mapArea) { } @Override - public void addFeature(SimpleFeature feature) { - String layer = feature.getName().getLocalPart(); + public void addFeature(String layerName, String featureId, String geometryName, + Geometry geometry, Map properties) { TopoGeom topoObj; try { - topoObj = createObject(feature); + topoObj = createObject(featureId, geometry, properties); } catch (MismatchedDimensionException | TransformException e) { e.printStackTrace(); throw Throwables.propagate(e); } if (topoObj != null) { - layers.put(layer, topoObj); + layers.put(layerName, topoObj); } } @@ -135,19 +129,8 @@ public RawMap build(WMSMapContent mapContent) throws IOException { } @Nullable - private TopoGeom createObject(Feature feature) throws MismatchedDimensionException, - TransformException { - Geometry geom; - {// ignore geometry-less features - GeometryAttribute defaultGeometryProperty = feature.getDefaultGeometryProperty(); - if (null == defaultGeometryProperty) { - return null; - } - geom = (Geometry) defaultGeometryProperty.getValue(); - if (geom == null) { - return null; - } - } + private TopoGeom createObject(String featureId, Geometry geom, Map properties) + throws MismatchedDimensionException, TransformException { // // snap to pixel geom = fixedGeometryFactory.createGeometry(geom); @@ -161,33 +144,13 @@ private TopoGeom createObject(Feature feature) throws MismatchedDimensionExcepti } TopoGeom geometry = createGeometry(geom); - Map properties = getProperties(feature); + geometry.setProperties(properties); - geometry.setId(feature.getIdentifier().getID()); + geometry.setId(featureId); return geometry; } - private Map getProperties(ComplexAttribute feature) { - Map props = new HashMap<>(); - for (Property p : feature.getProperties()) { - if (!(p instanceof Attribute) || (p instanceof GeometryAttribute)) { - continue; - } - String name = p.getName().getLocalPart(); - Object value; - if (p instanceof ComplexAttribute) { - value = getProperties((ComplexAttribute) p); - } else { - value = p.getValue(); - } - if (value != null) { - props.put(name, value); - } - } - return props; - } - private TopoGeom createGeometry(Geometry geom) { Preconditions.checkNotNull(geom); diff --git a/src/community/vectortiles/src/main/java/org/geoserver/wms/vector/VectorTileBuilder.java b/src/community/vectortiles/src/main/java/org/geoserver/wms/vector/VectorTileBuilder.java index c8fb9b2115c..ecba7eb9b6d 100644 --- a/src/community/vectortiles/src/main/java/org/geoserver/wms/vector/VectorTileBuilder.java +++ b/src/community/vectortiles/src/main/java/org/geoserver/wms/vector/VectorTileBuilder.java @@ -1,14 +1,17 @@ package org.geoserver.wms.vector; import java.io.IOException; +import java.util.Map; import org.geoserver.wms.WMSMapContent; import org.geoserver.wms.WebMap; -import org.opengis.feature.simple.SimpleFeature; + +import com.vividsolutions.jts.geom.Geometry; public interface VectorTileBuilder { - void addFeature(SimpleFeature feature); + void addFeature(String layerName, String featureId, String geometryName, Geometry geometry, + Map properties); WebMap build(WMSMapContent mapContent) throws IOException; diff --git a/src/community/vectortiles/src/main/java/org/geoserver/wms/vector/VectorTileMapOutputFormat.java b/src/community/vectortiles/src/main/java/org/geoserver/wms/vector/VectorTileMapOutputFormat.java index 30886aec3f0..f64c7d6c511 100644 --- a/src/community/vectortiles/src/main/java/org/geoserver/wms/vector/VectorTileMapOutputFormat.java +++ b/src/community/vectortiles/src/main/java/org/geoserver/wms/vector/VectorTileMapOutputFormat.java @@ -12,6 +12,8 @@ import java.awt.geom.AffineTransform; import java.io.IOException; import java.util.List; +import java.util.Map; +import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,8 +44,11 @@ import org.geotools.styling.FeatureTypeStyle; import org.geotools.styling.Style; import org.geotools.util.logging.Logging; +import org.opengis.feature.Attribute; +import org.opengis.feature.ComplexAttribute; import org.opengis.feature.Feature; -import org.opengis.feature.simple.SimpleFeature; +import org.opengis.feature.GeometryAttribute; +import org.opengis.feature.Property; import org.opengis.feature.type.FeatureType; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.geometry.MismatchedDimensionException; @@ -91,9 +96,9 @@ public void setTransformToScreenCoordinates(boolean useScreenCoords) { @Override public WebMap produceMap(final WMSMapContent mapContent) throws ServiceException, IOException { - mapContent.setMapWidth(5 * mapContent.getMapWidth()); - mapContent.setMapHeight(5 * mapContent.getMapHeight()); - + // mapContent.setMapWidth(5 * mapContent.getMapWidth()); + // mapContent.setMapHeight(5 * mapContent.getMapHeight()); + final ReferencedEnvelope renderingArea = mapContent.getRenderingArea(); final CoordinateReferenceSystem mapCrs = renderingArea.getCoordinateReferenceSystem(); final AffineTransform worldToScreen = mapContent.getRenderingTransform(); @@ -139,7 +144,7 @@ public WebMap produceMap(final WMSMapContent mapContent) throws ServiceException query.getHints().remove(Hints.SCREENMAP); FeatureCollection features = featureSource.getFeatures(query); - Feature next; + Feature feature; Stopwatch sw = Stopwatch.createStarted(); int count = 0; int total = 0; @@ -149,7 +154,7 @@ public WebMap produceMap(final WMSMapContent mapContent) throws ServiceException final MathTransform tx = transformToScreenCoordinates ? sourceToScreen : sourceToTargetCrs; - final double pixelDistance = 1; + final double pixelDistance = 0.25; final double simplificationDistance = getSimplificationDistance(sourceToScreen, paintArea, pixelDistance); final double distanceTolerance = transformToScreenCoordinates ? pixelDistance @@ -158,13 +163,13 @@ public WebMap produceMap(final WMSMapContent mapContent) throws ServiceException try (FeatureIterator it = features.features()) { while (it.hasNext()) { - next = it.next(); + feature = it.next(); total++; Geometry originalGeom; Geometry preProcessed; Geometry finalGeom; - originalGeom = (Geometry) next.getDefaultGeometryProperty().getValue(); + originalGeom = (Geometry) feature.getDefaultGeometryProperty().getValue(); try { preProcessed = preprocess(originalGeom, projectionHandler, screenMap); } catch (TransformException | FactoryException e) { @@ -182,6 +187,9 @@ public WebMap produceMap(final WMSMapContent mapContent) throws ServiceException } if (finalGeom.getDimension() > 0) { finalGeom = DouglasPeuckerSimplifier.simplify(finalGeom, distanceTolerance); + if (finalGeom.isEmpty()) { + continue; + } } Geometry clipped = finalGeom; @@ -202,11 +210,15 @@ public WebMap produceMap(final WMSMapContent mapContent) throws ServiceException clipped = clipped.getGeometryN(0); } - // Can't do this, SimpleFeatureImpl is broken - // next.getDefaultGeometryProperty().setValue(screenGeom); - ((SimpleFeature) next).setDefaultGeometry(clipped); + final String layerName = feature.getName().getLocalPart(); + final String featureId = feature.getIdentifier().toString(); + final String geometryName = geometryDescriptor.getName().getLocalPart(); - vectorTileBuilder.addFeature((SimpleFeature) next); + final Map properties = getProperties(feature); + final Geometry geometry = clipped; + + vectorTileBuilder.addFeature(layerName, featureId, geometryName, geometry, + properties); count++; } } @@ -214,7 +226,7 @@ public WebMap produceMap(final WMSMapContent mapContent) throws ServiceException if (LOGGER.isLoggable(Level.FINE)) { String msg = String.format("Added %,d out of %,d features of '%s' in %s", count, total, layer.getTitle(), sw); - System.err.println(msg); + // System.err.println(msg); LOGGER.fine(msg); } } @@ -223,6 +235,26 @@ public WebMap produceMap(final WMSMapContent mapContent) throws ServiceException return map; } + private Map getProperties(ComplexAttribute feature) { + Map props = new TreeMap<>(); + for (Property p : feature.getProperties()) { + if (!(p instanceof Attribute) || (p instanceof GeometryAttribute)) { + continue; + } + String name = p.getName().getLocalPart(); + Object value; + if (p instanceof ComplexAttribute) { + value = getProperties((ComplexAttribute) p); + } else { + value = p.getValue(); + } + if (value != null) { + props.put(name, value); + } + } + return props; + } + private double getSimplificationDistance(MathTransform worldToScreen, Rectangle paintArea, double pixelDistance) {