From bcc6eb55da3a82717ef4002879ca86986528e603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Gonz=C3=A1lez?= Date: Wed, 25 Jul 2018 09:45:34 +0200 Subject: [PATCH] back-ported feature-id from https://github.com/ElectronicChartCentre/java-vector-tile/pull/25 --- README.md | 8 ++ .../no/ecc/vectortile/VectorTileDecoder.java | 10 ++- .../no/ecc/vectortile/VectorTileEncoder.java | 27 ++++++- .../ecc/vectortile/VectorTileEncoderTest.java | 80 +++++++++++++++++++ 4 files changed, 120 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4cf5963..c46f125 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,14 @@ encoder.addFeature("road", attributes, geometry); byte[] encoded = encoder.encode(); ``` +or, specifying the feature id: + +```java +VectorTileEncoder encoder = new VectorTileEncoder(); +encoder.addFeature("road", attributes, geometry, id); +byte[] encoded = encoder.encode(); +``` + ## Maven ``` diff --git a/src/main/java/no/ecc/vectortile/VectorTileDecoder.java b/src/main/java/no/ecc/vectortile/VectorTileDecoder.java index 00ef1b5..f3f3cf1 100644 --- a/src/main/java/no/ecc/vectortile/VectorTileDecoder.java +++ b/src/main/java/no/ecc/vectortile/VectorTileDecoder.java @@ -331,7 +331,7 @@ private Feature parseFeature(VectorTile.Tile.Feature feature) { geometry = gf.createGeometryCollection(new Geometry[0]); } - return new Feature(layerName, extent, geometry, Collections.unmodifiableMap(attributes)); + return new Feature(layerName, extent, geometry, Collections.unmodifiableMap(attributes), feature.getId()); } public void remove() { @@ -344,20 +344,26 @@ public static final class Feature { private final String layerName; private final int extent; + private final long id; private final Geometry geometry; private final Map attributes; - public Feature(String layerName, int extent, Geometry geometry, Map attributes) { + public Feature(String layerName, int extent, Geometry geometry, Map attributes, long id) { this.layerName = layerName; this.extent = extent; this.geometry = geometry; this.attributes = attributes; + this.id = id; } public String getLayerName() { return layerName; } + public long getId() { + return id; + } + public int getExtent() { return extent; } diff --git a/src/main/java/no/ecc/vectortile/VectorTileEncoder.java b/src/main/java/no/ecc/vectortile/VectorTileEncoder.java index bf29ae4..ae51504 100644 --- a/src/main/java/no/ecc/vectortile/VectorTileEncoder.java +++ b/src/main/java/no/ecc/vectortile/VectorTileEncoder.java @@ -52,6 +52,10 @@ public class VectorTileEncoder { private final boolean autoScale; + private long autoincrement; + + private final boolean autoincrementIds; + /** * Create a {@link VectorTileEncoder} with the default extent of 4096 and * clip buffer of 8. @@ -68,6 +72,10 @@ public VectorTileEncoder(int extent) { this(extent, 8, true); } + public VectorTileEncoder(int extent, int clipBuffer, boolean autoScale) { + this(extent, clipBuffer, autoScale, false); + } + /** * Create a {@link VectorTileEncoder} with the given extent value. *

@@ -87,11 +95,14 @@ public VectorTileEncoder(int extent) { * and will scale them automatically to the 0..extent-1 range * before encoding. when false, the encoder expects coordinates * in the 0..extent-1 range. + * @param autoincrementIds * */ - public VectorTileEncoder(int extent, int clipBuffer, boolean autoScale) { + public VectorTileEncoder(int extent, int clipBuffer, boolean autoScale, boolean autoincrementIds) { this.extent = extent; this.autoScale = autoScale; + this.autoincrementIds = autoincrementIds; + this.autoincrement = 1; final int size = autoScale ? 256 : extent; clipGeometry = createTileEnvelope(clipBuffer, size); @@ -107,6 +118,9 @@ private static Geometry createTileEnvelope(int buffer, int size) { return new GeometryFactory().createPolygon(coords); } + public void addFeature(String layerName, Map attributes, Geometry geometry) { + this.addFeature(layerName, attributes, geometry, this.autoincrementIds ? this.autoincrement++ : -1); + } /** * Add a feature with layer name (typically feature type name), some @@ -119,8 +133,9 @@ private static Geometry createTileEnvelope(int buffer, int size) { * @param layerName * @param attributes * @param geometry + * @param id */ - public void addFeature(String layerName, Map attributes, Geometry geometry) { + public void addFeature(String layerName, Map attributes, Geometry geometry, long id) { // split up MultiPolygon and GeometryCollection (without subclasses) if (geometry instanceof MultiPolygon || geometry.getClass().equals(GeometryCollection.class)) { splitAndAddFeatures(layerName, attributes, (GeometryCollection) geometry); @@ -163,6 +178,8 @@ public void addFeature(String layerName, Map attributes, Geometry geo Feature feature = new Feature(); feature.geometry = geometry; + feature.id = id; + this.autoincrement = Math.max(this.autoincrement, id + 1); for (Map.Entry e : attributes.entrySet()) { // skip attribute without value @@ -274,6 +291,10 @@ public byte[] encode() { VectorTile.Tile.Feature.Builder featureBuilder = VectorTile.Tile.Feature.newBuilder(); featureBuilder.addAllTags(feature.tags); + if (feature.id >= 0) { + featureBuilder.setId(feature.id); + } + featureBuilder.setType(toGeomType(geometry)); featureBuilder.addAllGeometry(commands(geometry)); @@ -489,7 +510,7 @@ public List values() { } private static final class Feature { - + long id; Geometry geometry; final List tags = new ArrayList(); diff --git a/src/test/java/no/ecc/vectortile/VectorTileEncoderTest.java b/src/test/java/no/ecc/vectortile/VectorTileEncoderTest.java index 5299b7e..42e0888 100644 --- a/src/test/java/no/ecc/vectortile/VectorTileEncoderTest.java +++ b/src/test/java/no/ecc/vectortile/VectorTileEncoderTest.java @@ -20,11 +20,13 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import junit.framework.TestCase; +import no.ecc.vectortile.VectorTileDecoder.Feature; import vector_tile.VectorTile; import com.vividsolutions.jts.algorithm.CGAlgorithms; @@ -323,4 +325,82 @@ public void testAttributeTypes() throws IOException { assertEquals("value6", decodedAttributes.get("key6")); } + public void testProvidedIds() throws IOException { + VectorTileEncoder vtm = new VectorTileEncoder(256); + + Geometry geometry = gf.createPoint(new Coordinate(3, 6)); + Map attributes = Collections.singletonMap("key1", "value1"); + vtm.addFeature("DEPCNT", attributes, geometry, 50); + + List features = encodeDecodeFeatures(vtm); + assertEquals(1, features.size()); + assertEquals(50, features.get(0).getId()); + } + + public void testAutoincrementIds() throws IOException { + VectorTileEncoder vtm = new VectorTileEncoder(256, 8, true, true); + + for (int i = 0; i < 10; i++) { + Geometry geometry = gf.createPoint(new Coordinate(3 * i, 6 * i)); + Map attributes = Collections.singletonMap("key" + i, "value" + i); + vtm.addFeature("DEPCNT", attributes, geometry); + } + + List features = encodeDecodeFeatures(vtm); + for (int i = 0; i < features.size(); i++) { + assertEquals(i + 1, features.get(i).getId()); + } + } + + public void testProvidedAndAutoincrementIds() throws IOException { + VectorTileEncoder vtm = new VectorTileEncoder(256, 8, true, true); + + Geometry geometry = gf.createPoint(new Coordinate(3, 6)); + Map attributes = Collections.singletonMap("key1", "value1"); + vtm.addFeature("DEPCNT", attributes, geometry, 50); + + geometry = gf.createPoint(new Coordinate(3, 6)); + attributes = Collections.singletonMap("key1", "value1"); + vtm.addFeature("DEPCNT", attributes, geometry); + + geometry = gf.createPoint(new Coordinate(3, 6)); + attributes = Collections.singletonMap("key1", "value1"); + vtm.addFeature("DEPCNT", attributes, geometry, 27); + + geometry = gf.createPoint(new Coordinate(3, 6)); + attributes = Collections.singletonMap("key1", "value1"); + vtm.addFeature("DEPCNT", attributes, geometry); + + List features = encodeDecodeFeatures(vtm); + assertEquals(4, features.size()); + assertEquals(50, features.get(0).getId()); + assertEquals(51, features.get(1).getId()); + assertEquals(27, features.get(2).getId()); + assertEquals(52, features.get(3).getId()); + } + + public void testNullIds() throws IOException { + VectorTileEncoder vtm = new VectorTileEncoder(256); + + Geometry geometry = gf.createPoint(new Coordinate(3, 6)); + Map attributes = Collections.singletonMap("key1", "value1"); + vtm.addFeature("DEPCNT", attributes, geometry, 50); + + geometry = gf.createPoint(new Coordinate(3, 6)); + attributes = Collections.singletonMap("key1", "value1"); + vtm.addFeature("DEPCNT", attributes, geometry); + + List features = encodeDecodeFeatures(vtm); + assertEquals(2, features.size()); + assertEquals(50, features.get(0).getId()); + assertEquals(0, features.get(1).getId()); + } + + private List encodeDecodeFeatures(VectorTileEncoder vtm) throws IOException { + byte[] encoded = vtm.encode(); + assertNotSame(0, encoded.length); + + VectorTileDecoder decoder = new VectorTileDecoder(); + return decoder.decode(encoded, "DEPCNT").asList(); + } }