kopia lustrzana https://github.com/onthegomap/planetiler
more tests
rodzic
9c9bae7d2e
commit
0b00a82ab4
|
@ -3,6 +3,9 @@ package com.onthegomap.flatmap.geo;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.locationtech.jts.algorithm.Area;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.CoordinateSequence;
|
||||
import org.locationtech.jts.geom.CoordinateXY;
|
||||
|
@ -13,6 +16,7 @@ import org.locationtech.jts.geom.GeometryFactory;
|
|||
import org.locationtech.jts.geom.LineString;
|
||||
import org.locationtech.jts.geom.LinearRing;
|
||||
import org.locationtech.jts.geom.MultiPoint;
|
||||
import org.locationtech.jts.geom.MultiPolygon;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.locationtech.jts.geom.Polygon;
|
||||
import org.locationtech.jts.geom.PrecisionModel;
|
||||
|
@ -122,24 +126,6 @@ public class GeoUtils {
|
|||
return 0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
|
||||
}
|
||||
|
||||
public static final GeometryTransformer ProjectWorldCoords = new GeometryTransformer() {
|
||||
@Override
|
||||
protected CoordinateSequence transformCoordinates(CoordinateSequence coords, Geometry parent) {
|
||||
if (coords.getDimension() != 2) {
|
||||
throw new IllegalArgumentException("Dimension must be 2, was: " + coords.getDimension());
|
||||
}
|
||||
if (coords.getMeasures() != 0) {
|
||||
throw new IllegalArgumentException("Measures must be 0, was: " + coords.getMeasures());
|
||||
}
|
||||
CoordinateSequence copy = new PackedCoordinateSequence.Double(coords.size(), 2, 0);
|
||||
for (int i = 0; i < coords.size(); i++) {
|
||||
copy.setOrdinate(i, 0, getWorldX(coords.getX(i)));
|
||||
copy.setOrdinate(i, 1, getWorldY(coords.getY(i)));
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
};
|
||||
|
||||
private static final double QUANTIZED_WORLD_SIZE = Math.pow(2, 31);
|
||||
private static final long LOWER_32_BIT_MASK = (1L << 32) - 1L;
|
||||
|
||||
|
@ -177,14 +163,6 @@ public class GeoUtils {
|
|||
return (((long) a) << 32L) | (((long) b) & LOWER_32_BIT_MASK);
|
||||
}
|
||||
|
||||
public static int first(int pair) {
|
||||
return pair >> 16;
|
||||
}
|
||||
|
||||
public static int second(int pair) {
|
||||
return (pair << 16) >> 16;
|
||||
}
|
||||
|
||||
public static Point point(double x, double y) {
|
||||
return JTS_FACTORY.createPoint(new CoordinateXY(x, y));
|
||||
}
|
||||
|
@ -275,4 +253,30 @@ public class GeoUtils {
|
|||
throw new GeometryException("unrecognized geometry type: " + input.getGeometryType());
|
||||
}
|
||||
}
|
||||
|
||||
private static record PolyAndArea(Polygon poly, double area) implements Comparable<PolyAndArea> {
|
||||
|
||||
PolyAndArea(Polygon poly) {
|
||||
this(poly, Area.ofRing(poly.getExteriorRing().getCoordinateSequence()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull PolyAndArea o) {
|
||||
return -Double.compare(area, o.area);
|
||||
}
|
||||
}
|
||||
|
||||
public static Geometry ensureDescendingPolygonsSizes(Geometry geometry) {
|
||||
if (geometry instanceof MultiPolygon multiPolygon) {
|
||||
PolyAndArea[] areas = new PolyAndArea[multiPolygon.getNumGeometries()];
|
||||
for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
|
||||
areas[i] = new PolyAndArea((Polygon) multiPolygon.getGeometryN(i));
|
||||
}
|
||||
return JTS_FACTORY.createMultiPolygon(
|
||||
Stream.of(areas).sorted().map(d -> d.poly).toArray(Polygon[]::new)
|
||||
);
|
||||
} else {
|
||||
return geometry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,8 @@ public class ReaderFeature extends SourceFeature {
|
|||
|
||||
@Override
|
||||
public Geometry worldGeometry() {
|
||||
return worldGeometry != null ? worldGeometry : (worldGeometry = GeoUtils.latLonToWorldCoords(latLonGeometry));
|
||||
return worldGeometry != null ? worldGeometry
|
||||
: (worldGeometry = GeoUtils.ensureDescendingPolygonsSizes(GeoUtils.latLonToWorldCoords(latLonGeometry)));
|
||||
}
|
||||
|
||||
public Map<String, Object> properties() {
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.onthegomap.flatmap;
|
|||
import static com.onthegomap.flatmap.TestUtils.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.graphhopper.reader.ReaderRelation;
|
||||
import com.onthegomap.flatmap.collections.FeatureGroup;
|
||||
|
@ -30,6 +31,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import org.locationtech.jts.geom.MultiPolygon;
|
||||
import org.locationtech.jts.io.InputStreamInStream;
|
||||
import org.locationtech.jts.io.ParseException;
|
||||
|
@ -356,16 +358,7 @@ public class FlatMapTest {
|
|||
), results.tiles);
|
||||
}
|
||||
|
||||
private List<Coordinate> worldCoordinateList(double... coords) {
|
||||
List<Coordinate> points = newCoordinateList(coords);
|
||||
points.forEach(c -> {
|
||||
c.x = GeoUtils.getWorldLon(c.x);
|
||||
c.y = GeoUtils.getWorldLat(c.y);
|
||||
});
|
||||
return points;
|
||||
}
|
||||
|
||||
private List<Coordinate> z14CoordinateList(double... coords) {
|
||||
public List<Coordinate> z14CoordinateList(double... coords) {
|
||||
List<Coordinate> points = newCoordinateList(coords);
|
||||
points.forEach(c -> {
|
||||
c.x = GeoUtils.getWorldLon(0.5 + c.x * Z14_WIDTH);
|
||||
|
@ -486,7 +479,7 @@ public class FlatMapTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testOceanPolygon() throws IOException, SQLException {
|
||||
public void testFullWorldPolygon() throws IOException, SQLException {
|
||||
List<Coordinate> outerPoints = worldCoordinateList(
|
||||
Z14_WIDTH / 2, Z14_WIDTH / 2,
|
||||
1 - Z14_WIDTH / 2, Z14_WIDTH / 2,
|
||||
|
@ -545,6 +538,44 @@ public class FlatMapTest {
|
|||
assertEquals(expected, results.tiles.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReorderNestedMultipolygons() throws IOException, SQLException {
|
||||
List<Coordinate> outerPoints1 = worldRectangle(10d / 256, 240d / 256);
|
||||
List<Coordinate> innerPoints1 = worldRectangle(20d / 256, 230d / 256);
|
||||
List<Coordinate> outerPoints2 = worldRectangle(30d / 256, 220d / 256);
|
||||
List<Coordinate> innerPoints2 = worldRectangle(40d / 256, 210d / 256);
|
||||
|
||||
var results = runWithReaderFeatures(
|
||||
Map.of("threads", "1"),
|
||||
List.of(
|
||||
new ReaderFeature(newMultiPolygon(
|
||||
newPolygon(outerPoints2, List.of(innerPoints2)),
|
||||
newPolygon(outerPoints1, List.of(innerPoints1))
|
||||
), Map.of())
|
||||
),
|
||||
(in, features) -> {
|
||||
features.polygon("layer")
|
||||
.setZoomRange(0, 0)
|
||||
.setBufferPixels(0);
|
||||
}
|
||||
);
|
||||
|
||||
var tileContents = results.tiles.get(TileCoord.ofXYZ(0, 0, 0));
|
||||
assertEquals(1, tileContents.size());
|
||||
Geometry geom = tileContents.get(0).geometry().geom();
|
||||
assertTrue(geom instanceof MultiPolygon, geom.toString());
|
||||
MultiPolygon multiPolygon = (MultiPolygon) geom;
|
||||
assertSameNormalizedFeature(newPolygon(
|
||||
rectangleCoordList(10, 240),
|
||||
List.of(rectangleCoordList(20, 230))
|
||||
), multiPolygon.getGeometryN(0));
|
||||
assertSameNormalizedFeature(newPolygon(
|
||||
rectangleCoordList(30, 220),
|
||||
List.of(rectangleCoordList(40, 210))
|
||||
), multiPolygon.getGeometryN(1));
|
||||
assertEquals(2, multiPolygon.getNumGeometries());
|
||||
}
|
||||
|
||||
private Map.Entry<TileCoord, List<TestUtils.ComparableFeature>> newTileEntry(int x, int y, int z,
|
||||
List<TestUtils.ComparableFeature> features) {
|
||||
return Map.entry(TileCoord.ofXYZ(x, y, z), features);
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.locationtech.jts.geom.MultiPoint;
|
|||
import org.locationtech.jts.geom.MultiPolygon;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.locationtech.jts.geom.Polygon;
|
||||
import org.locationtech.jts.geom.Polygonal;
|
||||
import org.locationtech.jts.geom.Puntal;
|
||||
import org.locationtech.jts.geom.util.AffineTransformation;
|
||||
import org.locationtech.jts.geom.util.GeometryTransformer;
|
||||
|
@ -168,7 +169,7 @@ public class TestUtils {
|
|||
}
|
||||
return coords;
|
||||
}
|
||||
}.transform(input);
|
||||
}.transform(input.copy());
|
||||
}
|
||||
|
||||
private static byte[] gunzip(byte[] zipped) throws IOException {
|
||||
|
@ -247,8 +248,7 @@ public class TestUtils {
|
|||
return GeoUtils.JTS_FACTORY.createGeometryCollection();
|
||||
}
|
||||
|
||||
public static void validateGeometry(Geometry g) {
|
||||
assertTrue(g.isSimple(), "JTS isValid()");
|
||||
private static void validateGeometryRecursive(Geometry g) {
|
||||
if (g instanceof GeometryCollection gs) {
|
||||
for (int i = 0; i < gs.getNumGeometries(); i++) {
|
||||
validateGeometry(gs.getGeometryN(i));
|
||||
|
@ -275,6 +275,13 @@ public class TestUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void validateGeometry(Geometry g) {
|
||||
if (g instanceof Polygonal) {
|
||||
assertTrue(g.isSimple(), "JTS isValid()");
|
||||
}
|
||||
validateGeometryRecursive(g);
|
||||
}
|
||||
|
||||
public interface GeometryComparision {
|
||||
|
||||
Geometry geom();
|
||||
|
@ -426,4 +433,24 @@ public class TestUtils {
|
|||
mapTileFeatures(actual, NormGeometry::new)
|
||||
);
|
||||
}
|
||||
|
||||
public static List<Coordinate> worldCoordinateList(double... coords) {
|
||||
List<Coordinate> points = newCoordinateList(coords);
|
||||
points.forEach(c -> {
|
||||
c.x = GeoUtils.getWorldLon(c.x);
|
||||
c.y = GeoUtils.getWorldLat(c.y);
|
||||
});
|
||||
return points;
|
||||
}
|
||||
|
||||
public static List<Coordinate> worldRectangle(double min, double max) {
|
||||
return worldCoordinateList(
|
||||
min, min,
|
||||
max, min,
|
||||
max, max,
|
||||
min, max,
|
||||
min, min
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,8 +30,11 @@ public class GeoUtilsTest {
|
|||
|
||||
Point input = newPoint(lon, lat);
|
||||
Point expected = newPoint(worldX, worldY);
|
||||
Geometry actual = ProjectWorldCoords.transform(input);
|
||||
Geometry actual = latLonToWorldCoords(input);
|
||||
assertEquals(round(expected), round(actual));
|
||||
|
||||
Geometry roundTripped = worldToLatLonCoords(actual);
|
||||
assertEquals(round(input), round(roundTripped));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -96,4 +99,14 @@ public class GeoUtilsTest {
|
|||
1, 2
|
||||
)))));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"0, 156543",
|
||||
"8, 611",
|
||||
"14, 9",
|
||||
})
|
||||
public void testMetersPerPixel(int zoom, double meters) {
|
||||
assertEquals(meters, metersPerPixelAtEquator(zoom), 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package com.onthegomap.flatmap.read;
|
||||
|
||||
import static com.onthegomap.flatmap.TestUtils.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.MultiPolygon;
|
||||
|
||||
public class ReaderFeatureTest {
|
||||
|
||||
@Test
|
||||
public void testFixesMultipolygonOrdering() {
|
||||
List<Coordinate> outerPoints1 = worldRectangle(0.1, 0.9);
|
||||
List<Coordinate> innerPoints1 = worldRectangle(0.2, 0.8);
|
||||
List<Coordinate> outerPoints2 = worldRectangle(0.3, 0.7);
|
||||
List<Coordinate> innerPoints2 = worldRectangle(0.4, 0.6);
|
||||
|
||||
MultiPolygon multiPolygon = (MultiPolygon) new ReaderFeature(newMultiPolygon(
|
||||
newPolygon(outerPoints2, List.of(innerPoints2)),
|
||||
newPolygon(outerPoints1, List.of(innerPoints1))
|
||||
), Map.of()).worldGeometry();
|
||||
|
||||
assertEquals(2, multiPolygon.getNumGeometries());
|
||||
assertSameNormalizedFeature(round(newPolygon(
|
||||
rectangleCoordList(0.1, 0.9),
|
||||
List.of(rectangleCoordList(0.2, 0.8))
|
||||
)), round(multiPolygon.getGeometryN(0)));
|
||||
assertSameNormalizedFeature(round(newPolygon(
|
||||
rectangleCoordList(0.3, 0.7),
|
||||
List.of(rectangleCoordList(0.4, 0.6))
|
||||
)), round(multiPolygon.getGeometryN(1)));
|
||||
}
|
||||
}
|
|
@ -448,6 +448,28 @@ public class FeatureRendererTest {
|
|||
assertExactSameFeatures(Map.of(), renderGeometry(feature));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelfIntersectingLineStringOK() {
|
||||
var feature = lineFeature(newLineString(z14WorldCoords(
|
||||
10, 10,
|
||||
20, 20,
|
||||
10, 20,
|
||||
20, 10,
|
||||
10, 10
|
||||
)))
|
||||
.setMinPixelSize(1)
|
||||
.setZoomRange(14, 14);
|
||||
assertExactSameFeatures(Map.of(
|
||||
TileCoord.ofXYZ(Z14_TILES / 2, Z14_TILES / 2, 14), List.of(newLineString(
|
||||
10, 10,
|
||||
20, 20,
|
||||
10, 20,
|
||||
20, 10,
|
||||
10, 10
|
||||
))
|
||||
), renderGeometry(feature));
|
||||
}
|
||||
|
||||
/*
|
||||
* POLYGON TESTS
|
||||
*/
|
||||
|
|
Ładowanie…
Reference in New Issue