pull/1/head
Mike Barry 2021-05-20 05:59:18 -04:00
rodzic 72679b1b36
commit fcaeb88c40
15 zmienionych plików z 243 dodań i 183 usunięć

Wyświetl plik

@ -1,5 +1,6 @@
package com.onthegomap.flatmap;
import com.onthegomap.flatmap.render.RenderedFeature;
import com.onthegomap.flatmap.write.Mbtiles;
import java.util.ArrayList;
import java.util.Collections;

Wyświetl plik

@ -100,8 +100,7 @@ public class OpenMapTilesMain {
stats.time("sort", featureDb::sort);
stats.time("mbtiles",
() -> MbtilesWriter.writeOutput(featureMap, output, profile, config, stats));
stats.time("mbtiles", () -> MbtilesWriter.writeOutput(featureMap, output, profile, config, stats));
stats.stopTimer("import");

Wyświetl plik

@ -4,10 +4,10 @@ import com.carrotsearch.hppc.LongLongHashMap;
import com.graphhopper.coll.GHLongLongHashMap;
import com.onthegomap.flatmap.LayerStats;
import com.onthegomap.flatmap.Profile;
import com.onthegomap.flatmap.RenderedFeature;
import com.onthegomap.flatmap.VectorTileEncoder;
import com.onthegomap.flatmap.geo.TileCoord;
import com.onthegomap.flatmap.monitoring.Stats;
import com.onthegomap.flatmap.render.RenderedFeature;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

Wyświetl plik

@ -11,6 +11,7 @@ import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.MultiPoint;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.TopologyException;
import org.locationtech.jts.geom.impl.PackedCoordinateSequence;
import org.locationtech.jts.geom.util.GeometryTransformer;
import org.locationtech.jts.io.WKBReader;
@ -198,4 +199,35 @@ public class GeoUtils {
public static Geometry createMultiLineString(List<LineString> lineStrings) {
return JTS_FACTORY.createMultiLineString(lineStrings.toArray(EMPTY_LINE_STRING_ARRAY));
}
public static Geometry fixPolygon(Geometry geom, int maxAttempts) throws GeometryException {
try {
int attempts;
for (attempts = 0; attempts < maxAttempts && !geom.isValid(); attempts++) {
geom = geom.buffer(0);
}
if (attempts == maxAttempts) {
throw new GeometryException("Geometry still invalid after 2 buffers");
}
return geom;
} catch (TopologyException e) {
throw new GeometryException("Unable to fix polygon: " + e);
}
}
private static double wrapDouble(double value, double max) {
value %= max;
if (value < 0) {
value += max;
}
return value;
}
public static long labelGridId(int tilesAtZoom, double labelGridTileSize, Coordinate coord) {
return GeoUtils.longPair(
(int) Math.floor(wrapDouble(coord.getX() * tilesAtZoom, tilesAtZoom) / labelGridTileSize),
(int) Math.floor((coord.getY() * tilesAtZoom) / labelGridTileSize)
);
}
}

Wyświetl plik

@ -0,0 +1,17 @@
package com.onthegomap.flatmap.geo;
public class GeometryException extends Exception {
public GeometryException(Throwable cause) {
super(cause);
}
public GeometryException(String message, Throwable cause) {
super(message, cause);
}
public GeometryException(String message) {
super(message);
}
}

Wyświetl plik

@ -12,7 +12,6 @@ import com.graphhopper.reader.ReaderRelation;
import com.graphhopper.reader.ReaderWay;
import com.onthegomap.flatmap.CommonParams;
import com.onthegomap.flatmap.FeatureCollector;
import com.onthegomap.flatmap.FeatureRenderer;
import com.onthegomap.flatmap.MemoryEstimator;
import com.onthegomap.flatmap.Profile;
import com.onthegomap.flatmap.SourceFeature;
@ -23,6 +22,7 @@ import com.onthegomap.flatmap.collections.LongLongMultimap;
import com.onthegomap.flatmap.geo.GeoUtils;
import com.onthegomap.flatmap.monitoring.ProgressLoggers;
import com.onthegomap.flatmap.monitoring.Stats;
import com.onthegomap.flatmap.render.FeatureRenderer;
import com.onthegomap.flatmap.worker.Topology;
import java.io.Closeable;
import java.io.IOException;

Wyświetl plik

@ -2,13 +2,13 @@ package com.onthegomap.flatmap.read;
import com.onthegomap.flatmap.CommonParams;
import com.onthegomap.flatmap.FeatureCollector;
import com.onthegomap.flatmap.FeatureRenderer;
import com.onthegomap.flatmap.Profile;
import com.onthegomap.flatmap.SourceFeature;
import com.onthegomap.flatmap.collections.FeatureGroup;
import com.onthegomap.flatmap.collections.FeatureSort;
import com.onthegomap.flatmap.monitoring.ProgressLoggers;
import com.onthegomap.flatmap.monitoring.Stats;
import com.onthegomap.flatmap.render.FeatureRenderer;
import com.onthegomap.flatmap.worker.Topology;
import java.io.Closeable;
import java.util.concurrent.atomic.AtomicLong;

Wyświetl plik

@ -0,0 +1,105 @@
package com.onthegomap.flatmap.render;
import com.onthegomap.flatmap.FeatureCollector;
import com.onthegomap.flatmap.geo.GeoUtils;
import com.onthegomap.flatmap.geo.TileCoord;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.locationtech.jts.algorithm.Area;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.CoordinateSequences;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.Polygon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class CoordinateSequenceExtractor {
private static final Logger LOGGER = LoggerFactory.getLogger(CoordinateSequenceExtractor.class);
static Geometry reassembleLineString(List<List<CoordinateSequence>> geoms) {
Geometry geom;
List<LineString> lineStrings = new ArrayList<>();
for (List<CoordinateSequence> inner : geoms) {
for (CoordinateSequence coordinateSequence : inner) {
lineStrings.add(GeoUtils.JTS_FACTORY.createLineString(coordinateSequence));
}
}
geom = GeoUtils.createMultiLineString(lineStrings);
return geom;
}
@NotNull
static Geometry reassemblePolygon(FeatureCollector.Feature feature, TileCoord tile,
List<List<CoordinateSequence>> geoms) {
Geometry geom;
int numGeoms = geoms.size();
Polygon[] polygons = new Polygon[numGeoms];
for (int i = 0; i < numGeoms; i++) {
List<CoordinateSequence> group = geoms.get(i);
LinearRing first = GeoUtils.JTS_FACTORY.createLinearRing(group.get(0));
LinearRing[] rest = new LinearRing[group.size() - 1];
for (int j = 1; j < group.size(); j++) {
CoordinateSequence seq = group.get(j);
CoordinateSequences.reverse(seq);
rest[j - 1] = GeoUtils.JTS_FACTORY.createLinearRing(seq);
}
polygons[i] = GeoUtils.JTS_FACTORY.createPolygon(first, rest);
}
geom = GeoUtils.JTS_FACTORY.createMultiPolygon(polygons);
return geom;
}
static List<List<CoordinateSequence>> extractGroups(Geometry geom, double minSize) {
List<List<CoordinateSequence>> result = new ArrayList<>();
extractGroups(geom, result, minSize);
return result;
}
private static void extractGroups(Geometry geom, List<List<CoordinateSequence>> groups, double minSize) {
if (geom.isEmpty()) {
// ignore
} else if (geom instanceof GeometryCollection) {
for (int i = 0; i < geom.getNumGeometries(); i++) {
extractGroups(geom.getGeometryN(i), groups, minSize);
}
} else if (geom instanceof Polygon polygon) {
extractGroupsFromPolygon(groups, minSize, polygon);
} else if (geom instanceof LinearRing linearRing) {
extractGroups(GeoUtils.JTS_FACTORY.createPolygon(linearRing), groups, minSize);
} else if (geom instanceof LineString lineString) {
if (lineString.getLength() >= minSize) {
groups.add(List.of(lineString.getCoordinateSequence()));
}
} else {
throw new RuntimeException("unrecognized geometry type: " + geom.getGeometryType());
}
}
private static void extractGroupsFromPolygon(List<List<CoordinateSequence>> groups, double minSize, Polygon polygon) {
CoordinateSequence outer = polygon.getExteriorRing().getCoordinateSequence();
double outerArea = Area.ofRingSigned(outer);
if (outerArea > 0) {
CoordinateSequences.reverse(outer);
}
if (Math.abs(outerArea) >= minSize) {
List<CoordinateSequence> group = new ArrayList<>(1 + polygon.getNumInteriorRing());
groups.add(group);
group.add(outer);
for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
CoordinateSequence inner = polygon.getInteriorRingN(i).getCoordinateSequence();
double innerArea = Area.ofRingSigned(inner);
if (innerArea > 0) {
CoordinateSequences.reverse(inner);
}
if (Math.abs(innerArea) >= minSize) {
group.add(inner);
}
}
}
}
}

Wyświetl plik

@ -1,8 +1,12 @@
package com.onthegomap.flatmap;
package com.onthegomap.flatmap.render;
import com.onthegomap.flatmap.CommonParams;
import com.onthegomap.flatmap.FeatureCollector;
import com.onthegomap.flatmap.TileExtents;
import com.onthegomap.flatmap.VectorTileEncoder;
import com.onthegomap.flatmap.geo.GeoUtils;
import com.onthegomap.flatmap.geo.GeometryException;
import com.onthegomap.flatmap.geo.TileCoord;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -11,16 +15,12 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
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.CoordinateSequences;
import org.locationtech.jts.geom.CoordinateXY;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.MultiLineString;
import org.locationtech.jts.geom.MultiPoint;
import org.locationtech.jts.geom.MultiPolygon;
@ -65,14 +65,6 @@ public class FeatureRenderer {
return value;
}
private static double wrapDouble(double value, double max) {
value %= max;
if (value < 0) {
value += max;
}
return value;
}
public void renderFeature(FeatureCollector.Feature feature) {
renderGeometry(feature.getGeometry(), feature);
}
@ -113,6 +105,7 @@ public class FeatureRenderer {
TileCoord tile = TileCoord.ofXYZ(wrapInt(x, tilesAtZoom), y, zoom);
double tileY = worldY - y;
Coordinate outCoordinate = new CoordinateXY(tileX * 256, tileY * 256);
tilePrecision.makePrecise(outCoordinate);
output.computeIfAbsent(tile, t -> new HashSet<>()).add(outCoordinate);
}
}
@ -120,6 +113,7 @@ public class FeatureRenderer {
private void addPointFeature(FeatureCollector.Feature feature, Coordinate... coords) {
long id = idGen.incrementAndGet();
boolean hasLabelGrid = feature.hasLabelGrid();
for (int zoom = feature.getMaxZoom(); zoom >= feature.getMinZoom(); zoom--) {
Map<TileCoord, Set<Coordinate>> sliced = new HashMap<>();
Map<String, Object> attrs = feature.getAttrsAtZoom(zoom);
@ -131,12 +125,12 @@ public class FeatureRenderer {
}
RenderedFeature.Group groupInfo = null;
if (feature.hasLabelGrid() && coords.length == 1) {
if (hasLabelGrid && coords.length == 1) {
double labelGridTileSize = feature.getLabelGridPixelSizeAtZoom(zoom) / 256d;
groupInfo = labelGridTileSize >= 1d / 4096d ? new RenderedFeature.Group(GeoUtils.longPair(
(int) Math.floor(wrapDouble(coords[0].getX() * tilesAtZoom, tilesAtZoom) / labelGridTileSize),
(int) Math.floor((coords[0].getY() * tilesAtZoom) / labelGridTileSize)
), feature.getLabelGridLimitAtZoom(zoom)) : null;
groupInfo = labelGridTileSize < 1d / 4096d ? null : new RenderedFeature.Group(
GeoUtils.labelGridId(tilesAtZoom, labelGridTileSize, coords[0]),
feature.getLabelGridLimitAtZoom(zoom)
);
}
for (var entry : sliced.entrySet()) {
@ -145,17 +139,13 @@ public class FeatureRenderer {
Geometry geom = value.size() == 1 ? GeoUtils.point(value.iterator().next()) : GeoUtils.multiPoint(value);
// TODO stats
// TODO writeTileFeatures
emitFeature(feature, id, attrs, groupInfo, tile, geom);
emitFeature(feature, id, attrs, tile, geom, groupInfo);
}
}
}
private void emitFeature(FeatureCollector.Feature feature, long id, TileCoord tile, Geometry geom) {
emitFeature(feature, id, feature.getAttrsAtZoom(tile.z()), null, tile, geom);
}
private void emitFeature(FeatureCollector.Feature feature, long id, Map<String, Object> attrs,
RenderedFeature.Group groupInfo, TileCoord tile, Geometry geom) {
private void emitFeature(FeatureCollector.Feature feature, long id, Map<String, Object> attrs, TileCoord tile,
Geometry geom, RenderedFeature.Group groupInfo) {
consumer.accept(new RenderedFeature(
tile,
new VectorTileEncoder.Feature(
@ -206,8 +196,7 @@ public class FeatureRenderer {
simplifier.setDistanceTolerance(tolerance);
geom = simplifier.getResultGeometry();
List<List<CoordinateSequence>> groups = new ArrayList<>();
extractGroups(geom, groups, minSize);
List<List<CoordinateSequence>> groups = CoordinateSequenceExtractor.extractGroups(geom, minSize);
double buffer = feature.getBufferPixelsAtZoom(z);
TileExtents.ForZoom extents = config.extents().getForZoom(z);
TiledGeometry sliced = TiledGeometry.sliceIntoTiles(groups, buffer, area, z, extents);
@ -216,29 +205,35 @@ public class FeatureRenderer {
}
private void writeTileFeatures(long id, FeatureCollector.Feature feature, TiledGeometry sliced) {
Map<String, Object> attrs = feature.getAttrsAtZoom(sliced.zoomLevel());
for (var entry : sliced.getTileData()) {
TileCoord tile = entry.getKey();
List<List<CoordinateSequence>> geoms = entry.getValue();
Geometry geom;
if (feature.area()) {
geom = reassemblePolygon(feature, tile, geoms);
} else {
geom = reassembleLineString(geoms);
}
try {
geom = GeometryPrecisionReducer.reduce(geom, tilePrecision);
} catch (IllegalArgumentException e) {
LOGGER.warn("Error reducing precision of " + feature + " on " + tile + ": " + e);
}
List<List<CoordinateSequence>> geoms = entry.getValue();
if (!geom.isEmpty()) {
// JTS utilities "fix" the geometry to be clockwise outer/CCW inner
Geometry geom;
if (feature.area()) {
geom = geom.reverse();
geom = CoordinateSequenceExtractor.reassemblePolygon(feature, tile, geoms);
geom = GeoUtils.fixPolygon(geom, 2);
} else {
geom = CoordinateSequenceExtractor.reassembleLineString(geoms);
}
emitFeature(feature, id, tile, geom);
try {
geom = GeometryPrecisionReducer.reduce(geom, tilePrecision);
} catch (IllegalArgumentException e) {
throw new GeometryException("Error reducing precision");
}
if (!geom.isEmpty()) {
// JTS utilities "fix" the geometry to be clockwise outer/CCW inner
if (feature.area()) {
geom = geom.reverse();
}
emitFeature(feature, id, attrs, tile, geom, null);
}
} catch (GeometryException e) {
LOGGER.warn(e.getMessage() + ": " + tile + " " + feature);
}
}
@ -251,110 +246,23 @@ public class FeatureRenderer {
private void emitFilledTiles(long id, FeatureCollector.Feature feature, TiledGeometry sliced) {
/*
* Optimization: large input polygons that generate many filled interior tiles (ie. the ocean), the encoder avoids
* re-encoding if groupInfo and vector tile feature are == to previous values. The feature can have different
* attributes at different zoom levels though, so need to cache each vector tile feature instance by zoom level.
* re-encoding if groupInfo and vector tile feature are == to previous values.
*/
Optional<RenderedFeature.Group> groupInfo = Optional.empty();
VectorTileEncoder.Feature cachedFeature = null;
int lastZoom = Integer.MIN_VALUE;
VectorTileEncoder.Feature vectorTileFeature = new VectorTileEncoder.Feature(
feature.getLayer(),
id,
FILL,
feature.getAttrsAtZoom(sliced.zoomLevel())
);
for (TileCoord tile : sliced.getFilledTilesOrderedByZXY()) {
int zoom = tile.z();
if (zoom != lastZoom) {
cachedFeature = new VectorTileEncoder.Feature(feature.getLayer(), id, FILL, feature.getAttrsAtZoom(zoom));
lastZoom = zoom;
}
for (TileCoord tile : sliced.getFilledTiles()) {
consumer.accept(new RenderedFeature(
tile,
cachedFeature,
vectorTileFeature,
feature.getZorder(),
groupInfo
));
}
}
private Geometry reassembleLineString(List<List<CoordinateSequence>> geoms) {
Geometry geom;
List<LineString> lineStrings = new ArrayList<>();
for (List<CoordinateSequence> inner : geoms) {
for (CoordinateSequence coordinateSequence : inner) {
lineStrings.add(GeoUtils.JTS_FACTORY.createLineString(coordinateSequence));
}
}
geom = GeoUtils.createMultiLineString(lineStrings);
return geom;
}
@NotNull
private Geometry reassemblePolygon(FeatureCollector.Feature feature, TileCoord tile,
List<List<CoordinateSequence>> geoms) {
Geometry geom;
int numGeoms = geoms.size();
Polygon[] polygons = new Polygon[numGeoms];
for (int i = 0; i < numGeoms; i++) {
List<CoordinateSequence> group = geoms.get(i);
LinearRing first = GeoUtils.JTS_FACTORY.createLinearRing(group.get(0));
LinearRing[] rest = new LinearRing[group.size() - 1];
for (int j = 1; j < group.size(); j++) {
CoordinateSequence seq = group.get(j);
CoordinateSequences.reverse(seq);
rest[j - 1] = GeoUtils.JTS_FACTORY.createLinearRing(seq);
}
polygons[i] = GeoUtils.JTS_FACTORY.createPolygon(first, rest);
}
geom = GeoUtils.JTS_FACTORY.createMultiPolygon(polygons);
if (!geom.isValid()) {
geom = geom.buffer(0);
if (!geom.isValid()) {
geom = geom.buffer(0);
if (!geom.isValid()) {
LOGGER.warn("Geometry still invalid after 2 buffers " + feature + " on " + tile);
}
}
}
return geom;
}
private void extractGroups(Geometry geom, List<List<CoordinateSequence>> groups, double minSize) {
if (geom.isEmpty()) {
// ignore
} else if (geom instanceof GeometryCollection) {
for (int i = 0; i < geom.getNumGeometries(); i++) {
extractGroups(geom.getGeometryN(i), groups, minSize);
}
} else if (geom instanceof Polygon polygon) {
extractGroupsFromPolygon(groups, minSize, polygon);
} else if (geom instanceof LinearRing linearRing) {
extractGroups(GeoUtils.JTS_FACTORY.createPolygon(linearRing), groups, minSize);
} else if (geom instanceof LineString lineString) {
if (lineString.getLength() >= minSize) {
groups.add(List.of(lineString.getCoordinateSequence()));
}
} else {
throw new RuntimeException("unrecognized geometry type: " + geom.getGeometryType());
}
}
private void extractGroupsFromPolygon(List<List<CoordinateSequence>> groups, double minSize, Polygon polygon) {
CoordinateSequence outer = polygon.getExteriorRing().getCoordinateSequence();
double outerArea = Area.ofRingSigned(outer);
if (outerArea > 0) {
CoordinateSequences.reverse(outer);
}
if (Math.abs(outerArea) >= minSize) {
List<CoordinateSequence> group = new ArrayList<>(1 + polygon.getNumInteriorRing());
groups.add(group);
group.add(outer);
for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
CoordinateSequence inner = polygon.getInteriorRingN(i).getCoordinateSequence();
double innerArea = Area.ofRingSigned(inner);
if (innerArea > 0) {
CoordinateSequences.reverse(inner);
}
if (Math.abs(innerArea) >= minSize) {
group.add(inner);
}
}
}
}
}

Wyświetl plik

@ -1,5 +1,6 @@
package com.onthegomap.flatmap;
package com.onthegomap.flatmap.render;
import com.onthegomap.flatmap.VectorTileEncoder;
import com.onthegomap.flatmap.geo.TileCoord;
import java.util.Optional;

Wyświetl plik

@ -12,12 +12,13 @@
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
package com.onthegomap.flatmap;
package com.onthegomap.flatmap.render;
import com.carrotsearch.hppc.IntObjectMap;
import com.carrotsearch.hppc.cursors.IntCursor;
import com.carrotsearch.hppc.cursors.IntObjectCursor;
import com.graphhopper.coll.GHIntObjectHashMap;
import com.onthegomap.flatmap.TileExtents;
import com.onthegomap.flatmap.collections.IntRange;
import com.onthegomap.flatmap.collections.MutableCoordinateSequence;
import com.onthegomap.flatmap.geo.TileCoord;
@ -26,10 +27,7 @@ import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import org.jetbrains.annotations.NotNull;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.impl.PackedCoordinateSequence;
import org.slf4j.Logger;
@ -39,12 +37,12 @@ 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 a zoom level for each input geometry.
*/
public class TiledGeometry {
class TiledGeometry {
private static final Logger LOGGER = LoggerFactory.getLogger(TiledGeometry.class);
private final Map<TileCoord, List<List<CoordinateSequence>>> tileContents = new HashMap<>();
private final SortedMap<Column, IntRange> filledRanges = new TreeMap<>();
private final Map<Integer, IntRange> filledRanges = new HashMap<>();
private final TileExtents.ForZoom extents;
private final double buffer;
private final int z;
@ -59,6 +57,10 @@ public class TiledGeometry {
this.max = 1 << z;
}
public int zoomLevel() {
return z;
}
public static TiledGeometry sliceIntoTiles(List<List<CoordinateSequence>> groups, double buffer, boolean area, int z,
TileExtents.ForZoom extents) {
int worldExtent = 1 << z;
@ -73,11 +75,10 @@ public class TiledGeometry {
return result;
}
public Iterable<TileCoord> getFilledTilesOrderedByZXY() {
public Iterable<TileCoord> getFilledTiles() {
return () -> filledRanges.entrySet().stream()
.<TileCoord>mapMulti((entry, next) -> {
Column column = entry.getKey();
int x = column.x, z = column.z;
int x = entry.getKey();
for (int y : entry.getValue()) {
TileCoord coord = TileCoord.ofXYZ(x, y, z);
if (!tileContents.containsKey(coord)) {
@ -141,12 +142,12 @@ public class TiledGeometry {
overflow.add(Direction.LEFT);
} else {
for (CoordinateSequence stripeSegment : xCursor.value) {
IntRange filled = sliceY(stripeSegment, x, outer, inProgressShapes);
if (area && filled != null) {
IntRange filledYRange = sliceY(stripeSegment, x, outer, inProgressShapes);
if (area && filledYRange != null) {
if (outer) {
addFilledRange(z, x, filled);
addFilledRange(x, filledYRange);
} else {
removeFilledRange(z, x, filled);
removeFilledRange(x, filledYRange);
}
}
}
@ -428,38 +429,27 @@ public class TiledGeometry {
return rightFilled != null ? rightFilled.intersect(leftFilled) : null;
}
private void addFilledRange(int z, int x, IntRange range) {
if (range == null) {
private void addFilledRange(int x, IntRange yRange) {
if (yRange == null) {
return;
}
Column key = new Column(z, x);
IntRange existing = filledRanges.get(key);
IntRange existing = filledRanges.get(x);
if (existing == null) {
filledRanges.put(key, range);
filledRanges.put(x, yRange);
} else {
existing.addAll(range);
existing.addAll(yRange);
}
}
private void removeFilledRange(int z, int x, IntRange range) {
if (range == null) {
private void removeFilledRange(int x, IntRange yRange) {
if (yRange == null) {
return;
}
Column key = new Column(z, x);
IntRange existing = filledRanges.get(key);
IntRange existing = filledRanges.get(x);
if (existing != null) {
existing.removeAll(range);
existing.removeAll(yRange);
}
}
private enum Direction {RIGHT, LEFT}
private static record Column(int z, int x) implements Comparable<Column> {
@Override
public int compareTo(@NotNull Column o) {
int result = Integer.compare(z, o.z);
return result == 0 ? Integer.compare(x, o.x) : result;
}
}
}

Wyświetl plik

@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import com.onthegomap.flatmap.geo.GeoUtils;
import com.onthegomap.flatmap.geo.TileCoord;
import com.onthegomap.flatmap.render.RenderedFeature;
import com.onthegomap.flatmap.write.Mbtiles;
import java.util.Map;
import java.util.Optional;

Wyświetl plik

@ -29,10 +29,12 @@ import org.locationtech.jts.geom.CoordinateXY;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Lineal;
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.Puntal;
import org.locationtech.jts.geom.util.AffineTransformation;
import org.locationtech.jts.geom.util.GeometryTransformer;
@ -228,15 +230,17 @@ public class TestUtils {
public static Map<String, Object> toMap(FeatureCollector.Feature feature, int zoom) {
TreeMap<String, Object> result = new TreeMap<>(feature.getAttrsAtZoom(zoom));
Geometry geom = feature.getGeometry();
result.put("_minzoom", feature.getMinZoom());
result.put("_maxzoom", feature.getMaxZoom());
result.put("_buffer", feature.getBufferPixelsAtZoom(zoom));
result.put("_layer", feature.getLayer());
result.put("_zorder", feature.getZorder());
result.put("_geom", new NormGeometry(feature.getGeometry()));
result.put("_geom", new NormGeometry(geom));
result.put("_labelgrid_limit", feature.getLabelGridLimitAtZoom(zoom));
result.put("_labelgrid_size", feature.getLabelGridPixelSizeAtZoom(zoom));
result.put("_minpixelsize", feature.getMinPixelSize(zoom));
result.put("_type", geom instanceof Puntal ? "point" : geom instanceof Lineal ? "line" : "polygon");
return result;
}

Wyświetl plik

@ -7,10 +7,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
import com.onthegomap.flatmap.Profile;
import com.onthegomap.flatmap.RenderedFeature;
import com.onthegomap.flatmap.VectorTileEncoder;
import com.onthegomap.flatmap.geo.TileCoord;
import com.onthegomap.flatmap.monitoring.Stats;
import com.onthegomap.flatmap.render.RenderedFeature;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

Wyświetl plik

@ -1,4 +1,4 @@
package com.onthegomap.flatmap;
package com.onthegomap.flatmap.render;
import static com.onthegomap.flatmap.TestUtils.assertSameNormalizedFeatures;
import static com.onthegomap.flatmap.TestUtils.emptyGeometry;
@ -8,6 +8,8 @@ import static com.onthegomap.flatmap.TestUtils.newPoint;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
import com.onthegomap.flatmap.CommonParams;
import com.onthegomap.flatmap.FeatureCollector;
import com.onthegomap.flatmap.geo.GeoUtils;
import com.onthegomap.flatmap.geo.TileCoord;
import com.onthegomap.flatmap.read.ReaderFeature;