From f7a3b62170d00754d1b72ae1011f77cc49f0c9a0 Mon Sep 17 00:00:00 2001 From: Michael Barry Date: Sat, 3 Feb 2024 09:35:20 -0500 Subject: [PATCH] Add `firstCoordinate` vector tile feature helper (#814) --- .../com/onthegomap/planetiler/VectorTile.java | 15 +++++ .../onthegomap/planetiler/VectorTileTest.java | 59 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/planetiler-core/src/main/java/com/onthegomap/planetiler/VectorTile.java b/planetiler-core/src/main/java/com/onthegomap/planetiler/VectorTile.java index e1720143..2d3d3efb 100644 --- a/planetiler-core/src/main/java/com/onthegomap/planetiler/VectorTile.java +++ b/planetiler-core/src/main/java/com/onthegomap/planetiler/VectorTile.java @@ -43,6 +43,7 @@ import javax.annotation.concurrent.NotThreadSafe; import org.locationtech.jts.algorithm.Orientation; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateSequence; +import org.locationtech.jts.geom.CoordinateXY; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineString; @@ -987,6 +988,20 @@ public class VectorTile { return Hilbert.hilbertXYToIndex(15, x >> scale, y >> scale); } + + /** + * Returns the coordinate of the first point in this geometry in tile pixel coordinates from (0,0) at the top left + * to (256,256) at the bottom right. + */ + public CoordinateXY firstCoordinate() { + if (commands.length < 3) { + return null; + } + double factor = 1 << scale; + double x = zigZagDecode(commands[1]) * SIZE / EXTENT / factor; + double y = zigZagDecode(commands[2]) * SIZE / EXTENT / factor; + return new CoordinateXY(x, y); + } } /** diff --git a/planetiler-core/src/test/java/com/onthegomap/planetiler/VectorTileTest.java b/planetiler-core/src/test/java/com/onthegomap/planetiler/VectorTileTest.java index 9f065ae4..ab51a392 100644 --- a/planetiler-core/src/test/java/com/onthegomap/planetiler/VectorTileTest.java +++ b/planetiler-core/src/test/java/com/onthegomap/planetiler/VectorTileTest.java @@ -633,6 +633,65 @@ class VectorTileTest { })); } + @ParameterizedTest + @CsvSource({ + "0,0", + "1,1", + "-10,-1", + "300.25,200.75", + }) + void firstCoordinateOfPoint(double x, double y) { + for (int scale = 0; scale < 10; scale++) { + assertEquals(new CoordinateXY(x, y), VectorTile.encodeGeometry(newPoint(x, y), scale).firstCoordinate(), + "scale=" + scale); + } + } + + @ParameterizedTest + @CsvSource({ + "0,0", + "1,1", + "-10,-1", + "300.25,200.75", + }) + void firstCoordinateOfMultiPoint(double x, double y) { + for (int scale = 0; scale < 10; scale++) { + assertEquals(new CoordinateXY(x, y), + VectorTile.encodeGeometry(newMultiPoint(newPoint(x, y), newPoint(0, 0)), scale).firstCoordinate(), + "scale=" + scale); + } + } + + @ParameterizedTest + @CsvSource({ + "0,0", + "1,1", + "-10,-1", + "300.25,200.75", + }) + void firstCoordinateOfLine(double x, double y) { + for (int scale = 0; scale < 10; scale++) { + assertEquals(new CoordinateXY(x, y), + VectorTile.encodeGeometry(newLineString(x, y, x + 1, y + 1), scale).firstCoordinate(), + "scale=" + scale); + } + } + + @ParameterizedTest + @CsvSource({ + "0,0", + "1,1", + "-10,-1", + "300.25,200.75", + }) + void firstCoordinateOfPolygon(double x, double y) { + for (int scale = 0; scale < 10; scale++) { + assertEquals(new CoordinateXY(x, y), + VectorTile.encodeGeometry(newPolygon(x, y, x + 1, y, x + 1, y + 1, x, y + 1, x, y), scale).firstCoordinate(), + "scale=" + scale); + } + } + private static void assertArrayEquals(int[] a, int[] b) { assertEquals( IntStream.of(a).boxed().toList(),