From 7e06af0b1d08b16f9e3e4ba23d758f5310a1b167 Mon Sep 17 00:00:00 2001 From: Mike Barry Date: Tue, 18 May 2021 07:30:46 -0400 Subject: [PATCH] in progress --- .../onthegomap/flatmap/FeatureRenderer.java | 48 ++++++++++++++++++- ...SlicedGeometry.java => TiledGeometry.java} | 13 ++--- 2 files changed, 54 insertions(+), 7 deletions(-) rename src/main/java/com/onthegomap/flatmap/{SlicedGeometry.java => TiledGeometry.java} (96%) diff --git a/src/main/java/com/onthegomap/flatmap/FeatureRenderer.java b/src/main/java/com/onthegomap/flatmap/FeatureRenderer.java index c0efe0aa..32dde528 100644 --- a/src/main/java/com/onthegomap/flatmap/FeatureRenderer.java +++ b/src/main/java/com/onthegomap/flatmap/FeatureRenderer.java @@ -4,12 +4,14 @@ import com.onthegomap.flatmap.geo.GeoUtils; import com.onthegomap.flatmap.geo.TileCoord; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; 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.GeometryCollection; @@ -19,6 +21,9 @@ 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.util.AffineTransformation; +import org.locationtech.jts.simplify.DouglasPeuckerSimplifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -144,12 +149,53 @@ public class FeatureRenderer { } } - private void addLinearFeature(FeatureCollector.Feature feature, Geometry geom) { + private void addLinearFeature(FeatureCollector.Feature feature, Geometry input) { + // TODO move to feature? double minSizeAtMaxZoom = 1d / 4096; double normalTolerance = 0.1 / 256; double toleranceAtMaxZoom = 1d / 4096; + boolean area = input instanceof Polygonal; + double worldLength = (area || input.getNumGeometries() > 1) ? 0 : input.getLength(); + for (int z = feature.getMaxZoom(); z >= feature.getMinZoom(); z--) { + boolean isMaxZoom = feature.getMaxZoom() == 14; + double scale = 1 << z; + double tolerance = isMaxZoom ? toleranceAtMaxZoom : normalTolerance; + double minSize = isMaxZoom ? minSizeAtMaxZoom : (feature.getMinPixelSize(z) / 256); + if (area) { + minSize *= minSize; + } else if (worldLength > 0 && worldLength * scale < minSize) { + // skip linestring, too short + continue; + } + Geometry geom = AffineTransformation.scaleInstance(scale, scale).transform(input); + DouglasPeuckerSimplifier simplifier = new DouglasPeuckerSimplifier(geom); + simplifier.setEnsureValid(false); + simplifier.setDistanceTolerance(tolerance); + geom = simplifier.getResultGeometry(); + + List> groups = extractGroups(geom); + double buffer = feature.getBufferPixelsAtZoom(z); + TileExtents.ForZoom extents = config.extents().getForZoom(z); + TiledGeometry sliced = TiledGeometry.sliceIntoTiles(groups, buffer, area, z, extents); + writeTileFeatures(feature, sliced); + } } + private void writeTileFeatures(FeatureCollector.Feature feature, TiledGeometry sliced) { + build polygons, enforce correctness + build linestrings + reduce precision + handle errors + fix orientation + write filled tiles + log stats + } + + private List> extractGroups(Geometry geom) { + limit length + limit area + enforce orientation + } } diff --git a/src/main/java/com/onthegomap/flatmap/SlicedGeometry.java b/src/main/java/com/onthegomap/flatmap/TiledGeometry.java similarity index 96% rename from src/main/java/com/onthegomap/flatmap/SlicedGeometry.java rename to src/main/java/com/onthegomap/flatmap/TiledGeometry.java index 2ce0423b..890c6e67 100644 --- a/src/main/java/com/onthegomap/flatmap/SlicedGeometry.java +++ b/src/main/java/com/onthegomap/flatmap/TiledGeometry.java @@ -34,11 +34,11 @@ import org.slf4j.LoggerFactory; /** * This class is adapted from the stripe clipping algorithm in https://github.com/mapbox/geojson-vt/ and modified so - * that it eagerly produces all sliced tiles at every zoom level for each input geometry. + * that it eagerly produces all sliced tiles at a zoom level for each input geometry. */ -public class SlicedGeometry { +public class TiledGeometry { - private static final Logger LOGGER = LoggerFactory.getLogger(SlicedGeometry.class); + private static final Logger LOGGER = LoggerFactory.getLogger(TiledGeometry.class); private final Map>> tileContents = new HashMap<>(); private final Map filledRanges = new HashMap<>(); @@ -48,7 +48,7 @@ public class SlicedGeometry { private final boolean area; private final int max; - private SlicedGeometry(TileExtents.ForZoom extents, double buffer, int z, boolean area) { + private TiledGeometry(TileExtents.ForZoom extents, double buffer, int z, boolean area) { this.extents = extents; this.buffer = buffer; this.z = z; @@ -56,9 +56,10 @@ public class SlicedGeometry { this.max = 1 << z; } - public static SlicedGeometry slice(List> groups, double buffer, boolean area, int z, + public static TiledGeometry sliceIntoTiles(List> groups, double buffer, boolean area, int z, TileExtents.ForZoom extents) { - SlicedGeometry result = new SlicedGeometry(extents, buffer, z, area); + + TiledGeometry result = new TiledGeometry(extents, buffer, z, area); EnumSet wrapResult = result.sliceWorldCopy(groups, 0); if (wrapResult.contains(Direction.RIGHT)) { result.sliceWorldCopy(groups, 1);