diff --git a/tileverse-vectortiles/src/main/java/io/tileverse/vectortile/mvt/GeometryDecoder.java b/tileverse-vectortiles/src/main/java/io/tileverse/vectortile/mvt/GeometryDecoder.java index 6a5a203..19bd048 100644 --- a/tileverse-vectortiles/src/main/java/io/tileverse/vectortile/mvt/GeometryDecoder.java +++ b/tileverse-vectortiles/src/main/java/io/tileverse/vectortile/mvt/GeometryDecoder.java @@ -27,6 +27,7 @@ import org.locationtech.jts.algorithm.Area; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateSequence; +import org.locationtech.jts.geom.CoordinateSequenceFactory; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineString; @@ -151,8 +152,8 @@ private CoordinateSequence extractAllCoordinates( final GeomType geomType = feature.getType(); final IntList commands = (IntList) feature.getGeometryList(); final int cmdCount = commands.size(); - final int maxPossibleSize = calculateCoordinateCount(commands); - CoordinateSequence allCoords = gf.getCoordinateSequenceFactory().create(maxPossibleSize, 2); + final int exactCoordCount = calculateCoordinateCount(commands); + CoordinateSequence allCoords = gf.getCoordinateSequenceFactory().create(exactCoordCount, 2); int coordIndex = 0; // unscaled cursor position to compute each deltified ordinate @@ -246,10 +247,12 @@ private CoordinateSequence extractAllCoordinates( } // Return original sequence if we used exactly the allocated space, otherwise create subsequence - if (coordIndex == maxPossibleSize) { - return allCoords; + if (coordIndex != exactCoordCount) { + throw new IllegalStateException( + "number of coordinates (%d) doesn't match calculated number of coordinates (%d)" + .formatted(coordIndex, exactCoordCount)); } - return new SubCoordinateSequence(allCoords, 0, coordIndex, gf.getCoordinateSequenceFactory()); + return allCoords; } /** @@ -399,8 +402,7 @@ private static int calculateCoordinateCount(IntList commands) { // Accurate size: (total - command_words) / 2 + ClosePath coordinates int ordinateCount = size - commandCount; - int coordinateCount = (ordinateCount / 2) + closePathCount; - return coordinateCount; + return (ordinateCount / 2) + closePathCount; } /** @@ -410,7 +412,17 @@ private static int calculateCoordinateCount(IntList commands) { * @return coordinate subsequence for the specified part */ private CoordinateSequence createSubSequence(Part part, CoordinateSequence allCoords, GeometryFactory gf) { - return new SubCoordinateSequence(allCoords, part.start(), part.length(), gf.getCoordinateSequenceFactory()); + CoordinateSequenceFactory sequenceFactory = gf.getCoordinateSequenceFactory(); + int start = part.start(); + int length = part.length(); + if (start == 0 && length == allCoords.size()) { + return allCoords; + } + CoordinateSequence subsequence = new SubCoordinateSequence(allCoords, start, length, sequenceFactory); + if (gf != DEFAULT_GEOMETRY_FACTORY) { + subsequence = subsequence.copy(); + } + return subsequence; } /** diff --git a/tileverse-vectortiles/src/test/java/io/tileverse/vectortile/mvt/VectorTileCodecTest.java b/tileverse-vectortiles/src/test/java/io/tileverse/vectortile/mvt/VectorTileCodecTest.java index 63dd627..785a776 100644 --- a/tileverse-vectortiles/src/test/java/io/tileverse/vectortile/mvt/VectorTileCodecTest.java +++ b/tileverse-vectortiles/src/test/java/io/tileverse/vectortile/mvt/VectorTileCodecTest.java @@ -167,8 +167,14 @@ void testLineString() throws IOException { @Test void testMultiLineString() throws IOException { - Geometry geometry = geom("MULTILINESTRING((1 2, 5 6, 7 8), (8 10, 11 12))"); - testFeatureRoundTrip(geometry, Map.of()); + Geometry geom = geom("MULTILINESTRING EMPTY"); + testFeatureRoundTrip(geom, null, Map.of()); + + geom = geom("MULTILINESTRING((1 2, 5 6, 7 8))"); + testFeatureRoundTrip(geom, geom("LINESTRING(1 2, 5 6, 7 8)"), Map.of()); + + geom = geom("MULTILINESTRING((1 2, 5 6, 7 8), (8 10, 11 12))"); + testFeatureRoundTrip(geom, Map.of()); } @Test @@ -394,6 +400,10 @@ private Feature testFeatureRoundTrip(Geometry geometry, Geometry expectedGeometr VectorTile decoded = decode(encoded); + if (expectedGeometry == null) { + assertThat(decoded.getLayers().isEmpty()); + return null; + } assertEquals(Set.of(layerName), decoded.getLayerNames()); // Apply coordinate transformation if needed for round-trip testing