kopia lustrzana https://github.com/onthegomap/planetiler
Add `setAttrWithMinSize` to feature API (#725)
rodzic
c22d379734
commit
1df1bf04e4
|
@ -200,6 +200,28 @@ public class FeatureCollector implements Iterable<FeatureCollector.Feature> {
|
|||
return innermostPoint(layer, 0.1);
|
||||
}
|
||||
|
||||
/** Returns the minimum zoom level at which this feature is at least {@code pixelSize} pixels large. */
|
||||
public int getMinZoomForPixelSize(double pixelSize) {
|
||||
try {
|
||||
return GeoUtils.minZoomForPixelSize(source.size(), pixelSize);
|
||||
} catch (GeometryException e) {
|
||||
e.log(stats, "min_zoom_for_size_failure", "Error getting min zoom for size from geometry " + source.id());
|
||||
return config.maxzoom();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Returns the actual pixel size of the source feature at {@code zoom} (length if line, sqrt(area) if polygon). */
|
||||
public double getPixelSizeAtZoom(int zoom) {
|
||||
try {
|
||||
return source.size() * (256 << zoom);
|
||||
} catch (GeometryException e) {
|
||||
e.log(stats, "source_feature_pixel_size_at_zoom_failure",
|
||||
"Error getting source feature pixel size at zoom from geometry " + source.id());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new feature collector instances for each source feature that we encounter.
|
||||
*/
|
||||
|
@ -703,6 +725,29 @@ public class FeatureCollector implements Iterable<FeatureCollector.Feature> {
|
|||
return setAttr(key, ZoomFunction.minZoom(minzoom, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for {@code key} only at zoom levels where the feature is at least {@code minPixelSize} pixels in
|
||||
* size.
|
||||
*/
|
||||
public Feature setAttrWithMinSize(String key, Object value, double minPixelSize) {
|
||||
return setAttrWithMinzoom(key, value, getMinZoomForPixelSize(minPixelSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for {@code key} so that it always shows when {@code zoom_level >= minZoomToShowAlways} but only
|
||||
* shows when {@code minZoomIfBigEnough <= zoom_level < minZoomToShowAlways} when it is at least
|
||||
* {@code minPixelSize} pixels in size.
|
||||
* <p>
|
||||
* If you need more flexibility, use {@link #getMinZoomForPixelSize(double)} directly, or create a
|
||||
* {@link ZoomFunction} that calculates {@link #getPixelSizeAtZoom(int)} and applies a custom threshold based on the
|
||||
* zoom level.
|
||||
*/
|
||||
public Feature setAttrWithMinSize(String key, Object value, double minPixelSize, int minZoomIfBigEnough,
|
||||
int minZoomToShowAlways) {
|
||||
return setAttrWithMinzoom(key, value,
|
||||
Math.clamp(getMinZoomForPixelSize(minPixelSize), minZoomIfBigEnough, minZoomToShowAlways));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts all key/value pairs in {@code attrs} into the set of attribute to emit on the output feature at or above
|
||||
* {@code minzoom}.
|
||||
|
@ -735,6 +780,14 @@ public class FeatureCollector implements Iterable<FeatureCollector.Feature> {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attribute key that the renderer should use to store the number of points in the simplified geometry
|
||||
* before slicing it into tiles.
|
||||
*/
|
||||
public String getNumPointsAttr() {
|
||||
return numPointsAttr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a special attribute key that the renderer will use to store the number of points in the simplified geometry
|
||||
* before slicing it into tiles.
|
||||
|
@ -744,14 +797,6 @@ public class FeatureCollector implements Iterable<FeatureCollector.Feature> {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attribute key that the renderer should use to store the number of points in the simplified geometry
|
||||
* before slicing it into tiles.
|
||||
*/
|
||||
public String getNumPointsAttr() {
|
||||
return numPointsAttr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Feature{" +
|
||||
|
@ -763,12 +808,7 @@ public class FeatureCollector implements Iterable<FeatureCollector.Feature> {
|
|||
|
||||
/** Returns the actual pixel size of the source feature at {@code zoom} (length if line, sqrt(area) if polygon). */
|
||||
public double getSourceFeaturePixelSizeAtZoom(int zoom) {
|
||||
try {
|
||||
return source.size() * (256 << zoom);
|
||||
} catch (GeometryException e) {
|
||||
e.log(stats, "point_get_size_failure", "Error getting min size for point from geometry " + source.id());
|
||||
return 0;
|
||||
}
|
||||
return getPixelSizeAtZoom(zoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.onthegomap.planetiler.geo;
|
||||
|
||||
import com.onthegomap.planetiler.collection.LongLongMap;
|
||||
import com.onthegomap.planetiler.config.PlanetilerConfig;
|
||||
import com.onthegomap.planetiler.stats.Stats;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -51,6 +52,7 @@ public class GeoUtils {
|
|||
public static final double WORLD_CIRCUMFERENCE_METERS = Math.PI * 2 * WORLD_RADIUS_METERS;
|
||||
private static final double RADIANS_PER_DEGREE = Math.PI / 180;
|
||||
private static final double DEGREES_PER_RADIAN = 180 / Math.PI;
|
||||
private static final double LOG2 = Math.log(2);
|
||||
/**
|
||||
* Transform web mercator coordinates where top-left corner of the planet is (0,0) and bottom-right is (1,1) to
|
||||
* latitude/longitude coordinates.
|
||||
|
@ -534,6 +536,18 @@ public class GeoUtils {
|
|||
JTS_FACTORY.createGeometryCollection(innerGeometries.toArray(Geometry[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* For a feature of size {@code worldGeometrySize} (where 1=full planet), determine the minimum zoom level at which
|
||||
* the feature appears at least {@code minPixelSize} pixels large.
|
||||
* <p>
|
||||
* The result will be clamped to the range [0, {@link PlanetilerConfig#MAX_MAXZOOM}].
|
||||
*/
|
||||
public static int minZoomForPixelSize(double worldGeometrySize, double minPixelSize) {
|
||||
double worldPixels = worldGeometrySize * 256;
|
||||
return Math.clamp((int) Math.ceil(Math.log(minPixelSize / worldPixels) / LOG2), 0,
|
||||
PlanetilerConfig.MAX_MAXZOOM);
|
||||
}
|
||||
|
||||
/** Helper class to sort polygons by area of their outer shell. */
|
||||
private record PolyAndArea(Polygon poly, double area) implements Comparable<PolyAndArea> {
|
||||
|
||||
|
|
|
@ -87,6 +87,8 @@ class PlanetilerTests {
|
|||
private static final double Z13_WIDTH = 1d / Z13_TILES;
|
||||
private static final int Z12_TILES = 1 << 12;
|
||||
private static final double Z12_WIDTH = 1d / Z12_TILES;
|
||||
private static final int Z11_TILES = 1 << 11;
|
||||
private static final double Z11_WIDTH = 1d / Z11_TILES;
|
||||
private static final int Z4_TILES = 1 << 4;
|
||||
private static final Polygon WORLD_POLYGON = newPolygon(
|
||||
worldCoordinateList(
|
||||
|
@ -2434,6 +2436,74 @@ class PlanetilerTests {
|
|||
), results.tiles);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAttributeMinSizeLine() throws Exception {
|
||||
List<Coordinate> points = z14CoordinatePixelList(0, 4, 40, 4);
|
||||
|
||||
var results = runWithReaderFeatures(
|
||||
Map.of("threads", "1"),
|
||||
List.of(
|
||||
newReaderFeature(newLineString(points), Map.of())
|
||||
),
|
||||
(in, features) -> features.line("layer")
|
||||
.setZoomRange(11, 14)
|
||||
.setBufferPixels(0)
|
||||
.setAttrWithMinSize("a", "1", 10)
|
||||
.setAttrWithMinSize("b", "2", 20)
|
||||
.setAttrWithMinSize("c", "3", 40)
|
||||
.setAttrWithMinSize("d", "4", 40, 0, 13) // should show up at z13 and above
|
||||
);
|
||||
|
||||
assertEquals(Map.ofEntries(
|
||||
newTileEntry(Z11_TILES / 2, Z11_TILES / 2, 11, List.of(
|
||||
feature(newLineString(0, 0.5, 5, 0.5), Map.of())
|
||||
)),
|
||||
newTileEntry(Z12_TILES / 2, Z12_TILES / 2, 12, List.of(
|
||||
feature(newLineString(0, 1, 10, 1), Map.of("a", "1"))
|
||||
)),
|
||||
newTileEntry(Z13_TILES / 2, Z13_TILES / 2, 13, List.of(
|
||||
feature(newLineString(0, 2, 20, 2), Map.of("a", "1", "b", "2", "d", "4"))
|
||||
)),
|
||||
newTileEntry(Z14_TILES / 2, Z14_TILES / 2, 14, List.of(
|
||||
feature(newLineString(0, 4, 40, 4), Map.of("a", "1", "b", "2", "c", "3", "d", "4"))
|
||||
))
|
||||
), results.tiles);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAttributeMinSizePoint() throws Exception {
|
||||
List<Coordinate> points = z14CoordinatePixelList(0, 4, 40, 4);
|
||||
|
||||
var results = runWithReaderFeatures(
|
||||
Map.of("threads", "1"),
|
||||
List.of(
|
||||
newReaderFeature(newLineString(points), Map.of())
|
||||
),
|
||||
(in, features) -> features.centroid("layer")
|
||||
.setZoomRange(11, 14)
|
||||
.setBufferPixels(0)
|
||||
.setAttrWithMinSize("a", "1", 10)
|
||||
.setAttrWithMinSize("b", "2", 20)
|
||||
.setAttrWithMinSize("c", "3", 40)
|
||||
.setAttrWithMinSize("d", "4", 40, 0, 13) // should show up at z13 and above
|
||||
);
|
||||
|
||||
assertEquals(Map.ofEntries(
|
||||
newTileEntry(Z11_TILES / 2, Z11_TILES / 2, 11, List.of(
|
||||
feature(newPoint(2.5, 0.5), Map.of())
|
||||
)),
|
||||
newTileEntry(Z12_TILES / 2, Z12_TILES / 2, 12, List.of(
|
||||
feature(newPoint(5, 1), Map.of("a", "1"))
|
||||
)),
|
||||
newTileEntry(Z13_TILES / 2, Z13_TILES / 2, 13, List.of(
|
||||
feature(newPoint(10, 2), Map.of("a", "1", "b", "2", "d", "4"))
|
||||
)),
|
||||
newTileEntry(Z14_TILES / 2, Z14_TILES / 2, 14, List.of(
|
||||
feature(newPoint(20, 4), Map.of("a", "1", "b", "2", "c", "3", "d", "4"))
|
||||
))
|
||||
), results.tiles);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBoundFiltersFill() throws Exception {
|
||||
var polyResultz8 = runForBoundsTest(8, 8, "polygon", TestUtils.pathToResource("bottomrightearth.poly").toString());
|
||||
|
|
|
@ -447,4 +447,30 @@ class GeoUtilsTest {
|
|||
assertTrue(result.isValid());
|
||||
assertFalse(result.contains(point));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"1,0,0",
|
||||
"1,10,0",
|
||||
"1,255,0",
|
||||
|
||||
"0.5,0,0",
|
||||
"0.5,128,0",
|
||||
"0.5,129,1",
|
||||
"0.5,256,1",
|
||||
|
||||
"0.25,0,0",
|
||||
"0.25,128,1",
|
||||
"0.25,129,2",
|
||||
"0.25,256,2",
|
||||
})
|
||||
void minZoomForPixelSize(double worldGeometrySize, double minPixelSize, int expectedMinZoom) {
|
||||
assertEquals(expectedMinZoom, GeoUtils.minZoomForPixelSize(worldGeometrySize, minPixelSize));
|
||||
}
|
||||
|
||||
@Test
|
||||
void minZoomForPixelSizesAtZ9_10() {
|
||||
assertEquals(10, GeoUtils.minZoomForPixelSize(3.1 / (256 << 10), 3));
|
||||
assertEquals(9, GeoUtils.minZoomForPixelSize(6.1 / (256 << 10), 3));
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue