kopia lustrzana https://github.com/onthegomap/planetiler
rearrange
rodzic
72679b1b36
commit
fcaeb88c40
|
@ -1,5 +1,6 @@
|
||||||
package com.onthegomap.flatmap;
|
package com.onthegomap.flatmap;
|
||||||
|
|
||||||
|
import com.onthegomap.flatmap.render.RenderedFeature;
|
||||||
import com.onthegomap.flatmap.write.Mbtiles;
|
import com.onthegomap.flatmap.write.Mbtiles;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
|
@ -100,8 +100,7 @@ public class OpenMapTilesMain {
|
||||||
|
|
||||||
stats.time("sort", featureDb::sort);
|
stats.time("sort", featureDb::sort);
|
||||||
|
|
||||||
stats.time("mbtiles",
|
stats.time("mbtiles", () -> MbtilesWriter.writeOutput(featureMap, output, profile, config, stats));
|
||||||
() -> MbtilesWriter.writeOutput(featureMap, output, profile, config, stats));
|
|
||||||
|
|
||||||
stats.stopTimer("import");
|
stats.stopTimer("import");
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,10 @@ import com.carrotsearch.hppc.LongLongHashMap;
|
||||||
import com.graphhopper.coll.GHLongLongHashMap;
|
import com.graphhopper.coll.GHLongLongHashMap;
|
||||||
import com.onthegomap.flatmap.LayerStats;
|
import com.onthegomap.flatmap.LayerStats;
|
||||||
import com.onthegomap.flatmap.Profile;
|
import com.onthegomap.flatmap.Profile;
|
||||||
import com.onthegomap.flatmap.RenderedFeature;
|
|
||||||
import com.onthegomap.flatmap.VectorTileEncoder;
|
import com.onthegomap.flatmap.VectorTileEncoder;
|
||||||
import com.onthegomap.flatmap.geo.TileCoord;
|
import com.onthegomap.flatmap.geo.TileCoord;
|
||||||
import com.onthegomap.flatmap.monitoring.Stats;
|
import com.onthegomap.flatmap.monitoring.Stats;
|
||||||
|
import com.onthegomap.flatmap.render.RenderedFeature;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.locationtech.jts.geom.GeometryFactory;
|
||||||
import org.locationtech.jts.geom.LineString;
|
import org.locationtech.jts.geom.LineString;
|
||||||
import org.locationtech.jts.geom.MultiPoint;
|
import org.locationtech.jts.geom.MultiPoint;
|
||||||
import org.locationtech.jts.geom.Point;
|
import org.locationtech.jts.geom.Point;
|
||||||
|
import org.locationtech.jts.geom.TopologyException;
|
||||||
import org.locationtech.jts.geom.impl.PackedCoordinateSequence;
|
import org.locationtech.jts.geom.impl.PackedCoordinateSequence;
|
||||||
import org.locationtech.jts.geom.util.GeometryTransformer;
|
import org.locationtech.jts.geom.util.GeometryTransformer;
|
||||||
import org.locationtech.jts.io.WKBReader;
|
import org.locationtech.jts.io.WKBReader;
|
||||||
|
@ -198,4 +199,35 @@ public class GeoUtils {
|
||||||
public static Geometry createMultiLineString(List<LineString> lineStrings) {
|
public static Geometry createMultiLineString(List<LineString> lineStrings) {
|
||||||
return JTS_FACTORY.createMultiLineString(lineStrings.toArray(EMPTY_LINE_STRING_ARRAY));
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ import com.graphhopper.reader.ReaderRelation;
|
||||||
import com.graphhopper.reader.ReaderWay;
|
import com.graphhopper.reader.ReaderWay;
|
||||||
import com.onthegomap.flatmap.CommonParams;
|
import com.onthegomap.flatmap.CommonParams;
|
||||||
import com.onthegomap.flatmap.FeatureCollector;
|
import com.onthegomap.flatmap.FeatureCollector;
|
||||||
import com.onthegomap.flatmap.FeatureRenderer;
|
|
||||||
import com.onthegomap.flatmap.MemoryEstimator;
|
import com.onthegomap.flatmap.MemoryEstimator;
|
||||||
import com.onthegomap.flatmap.Profile;
|
import com.onthegomap.flatmap.Profile;
|
||||||
import com.onthegomap.flatmap.SourceFeature;
|
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.geo.GeoUtils;
|
||||||
import com.onthegomap.flatmap.monitoring.ProgressLoggers;
|
import com.onthegomap.flatmap.monitoring.ProgressLoggers;
|
||||||
import com.onthegomap.flatmap.monitoring.Stats;
|
import com.onthegomap.flatmap.monitoring.Stats;
|
||||||
|
import com.onthegomap.flatmap.render.FeatureRenderer;
|
||||||
import com.onthegomap.flatmap.worker.Topology;
|
import com.onthegomap.flatmap.worker.Topology;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
|
@ -2,13 +2,13 @@ package com.onthegomap.flatmap.read;
|
||||||
|
|
||||||
import com.onthegomap.flatmap.CommonParams;
|
import com.onthegomap.flatmap.CommonParams;
|
||||||
import com.onthegomap.flatmap.FeatureCollector;
|
import com.onthegomap.flatmap.FeatureCollector;
|
||||||
import com.onthegomap.flatmap.FeatureRenderer;
|
|
||||||
import com.onthegomap.flatmap.Profile;
|
import com.onthegomap.flatmap.Profile;
|
||||||
import com.onthegomap.flatmap.SourceFeature;
|
import com.onthegomap.flatmap.SourceFeature;
|
||||||
import com.onthegomap.flatmap.collections.FeatureGroup;
|
import com.onthegomap.flatmap.collections.FeatureGroup;
|
||||||
import com.onthegomap.flatmap.collections.FeatureSort;
|
import com.onthegomap.flatmap.collections.FeatureSort;
|
||||||
import com.onthegomap.flatmap.monitoring.ProgressLoggers;
|
import com.onthegomap.flatmap.monitoring.ProgressLoggers;
|
||||||
import com.onthegomap.flatmap.monitoring.Stats;
|
import com.onthegomap.flatmap.monitoring.Stats;
|
||||||
|
import com.onthegomap.flatmap.render.FeatureRenderer;
|
||||||
import com.onthegomap.flatmap.worker.Topology;
|
import com.onthegomap.flatmap.worker.Topology;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.GeoUtils;
|
||||||
|
import com.onthegomap.flatmap.geo.GeometryException;
|
||||||
import com.onthegomap.flatmap.geo.TileCoord;
|
import com.onthegomap.flatmap.geo.TileCoord;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -11,16 +15,12 @@ import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.function.Consumer;
|
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.Coordinate;
|
||||||
import org.locationtech.jts.geom.CoordinateSequence;
|
import org.locationtech.jts.geom.CoordinateSequence;
|
||||||
import org.locationtech.jts.geom.CoordinateSequences;
|
|
||||||
import org.locationtech.jts.geom.CoordinateXY;
|
import org.locationtech.jts.geom.CoordinateXY;
|
||||||
import org.locationtech.jts.geom.Geometry;
|
import org.locationtech.jts.geom.Geometry;
|
||||||
import org.locationtech.jts.geom.GeometryCollection;
|
import org.locationtech.jts.geom.GeometryCollection;
|
||||||
import org.locationtech.jts.geom.LineString;
|
import org.locationtech.jts.geom.LineString;
|
||||||
import org.locationtech.jts.geom.LinearRing;
|
|
||||||
import org.locationtech.jts.geom.MultiLineString;
|
import org.locationtech.jts.geom.MultiLineString;
|
||||||
import org.locationtech.jts.geom.MultiPoint;
|
import org.locationtech.jts.geom.MultiPoint;
|
||||||
import org.locationtech.jts.geom.MultiPolygon;
|
import org.locationtech.jts.geom.MultiPolygon;
|
||||||
|
@ -65,14 +65,6 @@ public class FeatureRenderer {
|
||||||
return value;
|
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) {
|
public void renderFeature(FeatureCollector.Feature feature) {
|
||||||
renderGeometry(feature.getGeometry(), feature);
|
renderGeometry(feature.getGeometry(), feature);
|
||||||
}
|
}
|
||||||
|
@ -113,6 +105,7 @@ public class FeatureRenderer {
|
||||||
TileCoord tile = TileCoord.ofXYZ(wrapInt(x, tilesAtZoom), y, zoom);
|
TileCoord tile = TileCoord.ofXYZ(wrapInt(x, tilesAtZoom), y, zoom);
|
||||||
double tileY = worldY - y;
|
double tileY = worldY - y;
|
||||||
Coordinate outCoordinate = new CoordinateXY(tileX * 256, tileY * 256);
|
Coordinate outCoordinate = new CoordinateXY(tileX * 256, tileY * 256);
|
||||||
|
tilePrecision.makePrecise(outCoordinate);
|
||||||
output.computeIfAbsent(tile, t -> new HashSet<>()).add(outCoordinate);
|
output.computeIfAbsent(tile, t -> new HashSet<>()).add(outCoordinate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,6 +113,7 @@ public class FeatureRenderer {
|
||||||
|
|
||||||
private void addPointFeature(FeatureCollector.Feature feature, Coordinate... coords) {
|
private void addPointFeature(FeatureCollector.Feature feature, Coordinate... coords) {
|
||||||
long id = idGen.incrementAndGet();
|
long id = idGen.incrementAndGet();
|
||||||
|
boolean hasLabelGrid = feature.hasLabelGrid();
|
||||||
for (int zoom = feature.getMaxZoom(); zoom >= feature.getMinZoom(); zoom--) {
|
for (int zoom = feature.getMaxZoom(); zoom >= feature.getMinZoom(); zoom--) {
|
||||||
Map<TileCoord, Set<Coordinate>> sliced = new HashMap<>();
|
Map<TileCoord, Set<Coordinate>> sliced = new HashMap<>();
|
||||||
Map<String, Object> attrs = feature.getAttrsAtZoom(zoom);
|
Map<String, Object> attrs = feature.getAttrsAtZoom(zoom);
|
||||||
|
@ -131,12 +125,12 @@ public class FeatureRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderedFeature.Group groupInfo = null;
|
RenderedFeature.Group groupInfo = null;
|
||||||
if (feature.hasLabelGrid() && coords.length == 1) {
|
if (hasLabelGrid && coords.length == 1) {
|
||||||
double labelGridTileSize = feature.getLabelGridPixelSizeAtZoom(zoom) / 256d;
|
double labelGridTileSize = feature.getLabelGridPixelSizeAtZoom(zoom) / 256d;
|
||||||
groupInfo = labelGridTileSize >= 1d / 4096d ? new RenderedFeature.Group(GeoUtils.longPair(
|
groupInfo = labelGridTileSize < 1d / 4096d ? null : new RenderedFeature.Group(
|
||||||
(int) Math.floor(wrapDouble(coords[0].getX() * tilesAtZoom, tilesAtZoom) / labelGridTileSize),
|
GeoUtils.labelGridId(tilesAtZoom, labelGridTileSize, coords[0]),
|
||||||
(int) Math.floor((coords[0].getY() * tilesAtZoom) / labelGridTileSize)
|
feature.getLabelGridLimitAtZoom(zoom)
|
||||||
), feature.getLabelGridLimitAtZoom(zoom)) : null;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var entry : sliced.entrySet()) {
|
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);
|
Geometry geom = value.size() == 1 ? GeoUtils.point(value.iterator().next()) : GeoUtils.multiPoint(value);
|
||||||
// TODO stats
|
// TODO stats
|
||||||
// TODO writeTileFeatures
|
// 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) {
|
private void emitFeature(FeatureCollector.Feature feature, long id, Map<String, Object> attrs, TileCoord tile,
|
||||||
emitFeature(feature, id, feature.getAttrsAtZoom(tile.z()), null, tile, geom);
|
Geometry geom, RenderedFeature.Group groupInfo) {
|
||||||
}
|
|
||||||
|
|
||||||
private void emitFeature(FeatureCollector.Feature feature, long id, Map<String, Object> attrs,
|
|
||||||
RenderedFeature.Group groupInfo, TileCoord tile, Geometry geom) {
|
|
||||||
consumer.accept(new RenderedFeature(
|
consumer.accept(new RenderedFeature(
|
||||||
tile,
|
tile,
|
||||||
new VectorTileEncoder.Feature(
|
new VectorTileEncoder.Feature(
|
||||||
|
@ -206,8 +196,7 @@ public class FeatureRenderer {
|
||||||
simplifier.setDistanceTolerance(tolerance);
|
simplifier.setDistanceTolerance(tolerance);
|
||||||
geom = simplifier.getResultGeometry();
|
geom = simplifier.getResultGeometry();
|
||||||
|
|
||||||
List<List<CoordinateSequence>> groups = new ArrayList<>();
|
List<List<CoordinateSequence>> groups = CoordinateSequenceExtractor.extractGroups(geom, minSize);
|
||||||
extractGroups(geom, groups, minSize);
|
|
||||||
double buffer = feature.getBufferPixelsAtZoom(z);
|
double buffer = feature.getBufferPixelsAtZoom(z);
|
||||||
TileExtents.ForZoom extents = config.extents().getForZoom(z);
|
TileExtents.ForZoom extents = config.extents().getForZoom(z);
|
||||||
TiledGeometry sliced = TiledGeometry.sliceIntoTiles(groups, buffer, area, z, extents);
|
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) {
|
private void writeTileFeatures(long id, FeatureCollector.Feature feature, TiledGeometry sliced) {
|
||||||
|
Map<String, Object> attrs = feature.getAttrsAtZoom(sliced.zoomLevel());
|
||||||
for (var entry : sliced.getTileData()) {
|
for (var entry : sliced.getTileData()) {
|
||||||
TileCoord tile = entry.getKey();
|
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 {
|
try {
|
||||||
geom = GeometryPrecisionReducer.reduce(geom, tilePrecision);
|
List<List<CoordinateSequence>> geoms = entry.getValue();
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOGGER.warn("Error reducing precision of " + feature + " on " + tile + ": " + e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!geom.isEmpty()) {
|
Geometry geom;
|
||||||
// JTS utilities "fix" the geometry to be clockwise outer/CCW inner
|
|
||||||
if (feature.area()) {
|
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) {
|
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
|
* 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
|
* re-encoding if groupInfo and vector tile feature are == to previous values.
|
||||||
* attributes at different zoom levels though, so need to cache each vector tile feature instance by zoom level.
|
|
||||||
*/
|
*/
|
||||||
Optional<RenderedFeature.Group> groupInfo = Optional.empty();
|
Optional<RenderedFeature.Group> groupInfo = Optional.empty();
|
||||||
VectorTileEncoder.Feature cachedFeature = null;
|
VectorTileEncoder.Feature vectorTileFeature = new VectorTileEncoder.Feature(
|
||||||
int lastZoom = Integer.MIN_VALUE;
|
feature.getLayer(),
|
||||||
|
id,
|
||||||
|
FILL,
|
||||||
|
feature.getAttrsAtZoom(sliced.zoomLevel())
|
||||||
|
);
|
||||||
|
|
||||||
for (TileCoord tile : sliced.getFilledTilesOrderedByZXY()) {
|
for (TileCoord tile : sliced.getFilledTiles()) {
|
||||||
int zoom = tile.z();
|
|
||||||
if (zoom != lastZoom) {
|
|
||||||
cachedFeature = new VectorTileEncoder.Feature(feature.getLayer(), id, FILL, feature.getAttrsAtZoom(zoom));
|
|
||||||
lastZoom = zoom;
|
|
||||||
}
|
|
||||||
consumer.accept(new RenderedFeature(
|
consumer.accept(new RenderedFeature(
|
||||||
tile,
|
tile,
|
||||||
cachedFeature,
|
vectorTileFeature,
|
||||||
feature.getZorder(),
|
feature.getZorder(),
|
||||||
groupInfo
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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 com.onthegomap.flatmap.geo.TileCoord;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
|
@ -12,12 +12,13 @@
|
||||||
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package com.onthegomap.flatmap;
|
package com.onthegomap.flatmap.render;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.IntObjectMap;
|
import com.carrotsearch.hppc.IntObjectMap;
|
||||||
import com.carrotsearch.hppc.cursors.IntCursor;
|
import com.carrotsearch.hppc.cursors.IntCursor;
|
||||||
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
||||||
import com.graphhopper.coll.GHIntObjectHashMap;
|
import com.graphhopper.coll.GHIntObjectHashMap;
|
||||||
|
import com.onthegomap.flatmap.TileExtents;
|
||||||
import com.onthegomap.flatmap.collections.IntRange;
|
import com.onthegomap.flatmap.collections.IntRange;
|
||||||
import com.onthegomap.flatmap.collections.MutableCoordinateSequence;
|
import com.onthegomap.flatmap.collections.MutableCoordinateSequence;
|
||||||
import com.onthegomap.flatmap.geo.TileCoord;
|
import com.onthegomap.flatmap.geo.TileCoord;
|
||||||
|
@ -26,10 +27,7 @@ import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedMap;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.locationtech.jts.geom.CoordinateSequence;
|
import org.locationtech.jts.geom.CoordinateSequence;
|
||||||
import org.locationtech.jts.geom.impl.PackedCoordinateSequence;
|
import org.locationtech.jts.geom.impl.PackedCoordinateSequence;
|
||||||
import org.slf4j.Logger;
|
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
|
* 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.
|
* 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 static final Logger LOGGER = LoggerFactory.getLogger(TiledGeometry.class);
|
||||||
|
|
||||||
private final Map<TileCoord, List<List<CoordinateSequence>>> tileContents = new HashMap<>();
|
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 TileExtents.ForZoom extents;
|
||||||
private final double buffer;
|
private final double buffer;
|
||||||
private final int z;
|
private final int z;
|
||||||
|
@ -59,6 +57,10 @@ public class TiledGeometry {
|
||||||
this.max = 1 << z;
|
this.max = 1 << z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int zoomLevel() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
public static TiledGeometry sliceIntoTiles(List<List<CoordinateSequence>> groups, double buffer, boolean area, int z,
|
public static TiledGeometry sliceIntoTiles(List<List<CoordinateSequence>> groups, double buffer, boolean area, int z,
|
||||||
TileExtents.ForZoom extents) {
|
TileExtents.ForZoom extents) {
|
||||||
int worldExtent = 1 << z;
|
int worldExtent = 1 << z;
|
||||||
|
@ -73,11 +75,10 @@ public class TiledGeometry {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterable<TileCoord> getFilledTilesOrderedByZXY() {
|
public Iterable<TileCoord> getFilledTiles() {
|
||||||
return () -> filledRanges.entrySet().stream()
|
return () -> filledRanges.entrySet().stream()
|
||||||
.<TileCoord>mapMulti((entry, next) -> {
|
.<TileCoord>mapMulti((entry, next) -> {
|
||||||
Column column = entry.getKey();
|
int x = entry.getKey();
|
||||||
int x = column.x, z = column.z;
|
|
||||||
for (int y : entry.getValue()) {
|
for (int y : entry.getValue()) {
|
||||||
TileCoord coord = TileCoord.ofXYZ(x, y, z);
|
TileCoord coord = TileCoord.ofXYZ(x, y, z);
|
||||||
if (!tileContents.containsKey(coord)) {
|
if (!tileContents.containsKey(coord)) {
|
||||||
|
@ -141,12 +142,12 @@ public class TiledGeometry {
|
||||||
overflow.add(Direction.LEFT);
|
overflow.add(Direction.LEFT);
|
||||||
} else {
|
} else {
|
||||||
for (CoordinateSequence stripeSegment : xCursor.value) {
|
for (CoordinateSequence stripeSegment : xCursor.value) {
|
||||||
IntRange filled = sliceY(stripeSegment, x, outer, inProgressShapes);
|
IntRange filledYRange = sliceY(stripeSegment, x, outer, inProgressShapes);
|
||||||
if (area && filled != null) {
|
if (area && filledYRange != null) {
|
||||||
if (outer) {
|
if (outer) {
|
||||||
addFilledRange(z, x, filled);
|
addFilledRange(x, filledYRange);
|
||||||
} else {
|
} else {
|
||||||
removeFilledRange(z, x, filled);
|
removeFilledRange(x, filledYRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,38 +429,27 @@ public class TiledGeometry {
|
||||||
return rightFilled != null ? rightFilled.intersect(leftFilled) : null;
|
return rightFilled != null ? rightFilled.intersect(leftFilled) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFilledRange(int z, int x, IntRange range) {
|
private void addFilledRange(int x, IntRange yRange) {
|
||||||
if (range == null) {
|
if (yRange == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Column key = new Column(z, x);
|
IntRange existing = filledRanges.get(x);
|
||||||
IntRange existing = filledRanges.get(key);
|
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
filledRanges.put(key, range);
|
filledRanges.put(x, yRange);
|
||||||
} else {
|
} else {
|
||||||
existing.addAll(range);
|
existing.addAll(yRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeFilledRange(int z, int x, IntRange range) {
|
private void removeFilledRange(int x, IntRange yRange) {
|
||||||
if (range == null) {
|
if (yRange == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Column key = new Column(z, x);
|
IntRange existing = filledRanges.get(x);
|
||||||
IntRange existing = filledRanges.get(key);
|
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
existing.removeAll(range);
|
existing.removeAll(yRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum Direction {RIGHT, LEFT}
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
import com.onthegomap.flatmap.geo.GeoUtils;
|
import com.onthegomap.flatmap.geo.GeoUtils;
|
||||||
import com.onthegomap.flatmap.geo.TileCoord;
|
import com.onthegomap.flatmap.geo.TileCoord;
|
||||||
|
import com.onthegomap.flatmap.render.RenderedFeature;
|
||||||
import com.onthegomap.flatmap.write.Mbtiles;
|
import com.onthegomap.flatmap.write.Mbtiles;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
|
@ -29,10 +29,12 @@ import org.locationtech.jts.geom.CoordinateXY;
|
||||||
import org.locationtech.jts.geom.Geometry;
|
import org.locationtech.jts.geom.Geometry;
|
||||||
import org.locationtech.jts.geom.GeometryCollection;
|
import org.locationtech.jts.geom.GeometryCollection;
|
||||||
import org.locationtech.jts.geom.LineString;
|
import org.locationtech.jts.geom.LineString;
|
||||||
|
import org.locationtech.jts.geom.Lineal;
|
||||||
import org.locationtech.jts.geom.MultiPoint;
|
import org.locationtech.jts.geom.MultiPoint;
|
||||||
import org.locationtech.jts.geom.MultiPolygon;
|
import org.locationtech.jts.geom.MultiPolygon;
|
||||||
import org.locationtech.jts.geom.Point;
|
import org.locationtech.jts.geom.Point;
|
||||||
import org.locationtech.jts.geom.Polygon;
|
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.AffineTransformation;
|
||||||
import org.locationtech.jts.geom.util.GeometryTransformer;
|
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) {
|
public static Map<String, Object> toMap(FeatureCollector.Feature feature, int zoom) {
|
||||||
TreeMap<String, Object> result = new TreeMap<>(feature.getAttrsAtZoom(zoom));
|
TreeMap<String, Object> result = new TreeMap<>(feature.getAttrsAtZoom(zoom));
|
||||||
|
Geometry geom = feature.getGeometry();
|
||||||
result.put("_minzoom", feature.getMinZoom());
|
result.put("_minzoom", feature.getMinZoom());
|
||||||
result.put("_maxzoom", feature.getMaxZoom());
|
result.put("_maxzoom", feature.getMaxZoom());
|
||||||
result.put("_buffer", feature.getBufferPixelsAtZoom(zoom));
|
result.put("_buffer", feature.getBufferPixelsAtZoom(zoom));
|
||||||
result.put("_layer", feature.getLayer());
|
result.put("_layer", feature.getLayer());
|
||||||
result.put("_zorder", feature.getZorder());
|
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_limit", feature.getLabelGridLimitAtZoom(zoom));
|
||||||
result.put("_labelgrid_size", feature.getLabelGridPixelSizeAtZoom(zoom));
|
result.put("_labelgrid_size", feature.getLabelGridPixelSizeAtZoom(zoom));
|
||||||
result.put("_minpixelsize", feature.getMinPixelSize(zoom));
|
result.put("_minpixelsize", feature.getMinPixelSize(zoom));
|
||||||
|
result.put("_type", geom instanceof Puntal ? "point" : geom instanceof Lineal ? "line" : "polygon");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||||
|
|
||||||
import com.onthegomap.flatmap.Profile;
|
import com.onthegomap.flatmap.Profile;
|
||||||
import com.onthegomap.flatmap.RenderedFeature;
|
|
||||||
import com.onthegomap.flatmap.VectorTileEncoder;
|
import com.onthegomap.flatmap.VectorTileEncoder;
|
||||||
import com.onthegomap.flatmap.geo.TileCoord;
|
import com.onthegomap.flatmap.geo.TileCoord;
|
||||||
import com.onthegomap.flatmap.monitoring.Stats;
|
import com.onthegomap.flatmap.monitoring.Stats;
|
||||||
|
import com.onthegomap.flatmap.render.RenderedFeature;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -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.assertSameNormalizedFeatures;
|
||||||
import static com.onthegomap.flatmap.TestUtils.emptyGeometry;
|
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.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
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.GeoUtils;
|
||||||
import com.onthegomap.flatmap.geo.TileCoord;
|
import com.onthegomap.flatmap.geo.TileCoord;
|
||||||
import com.onthegomap.flatmap.read.ReaderFeature;
|
import com.onthegomap.flatmap.read.ReaderFeature;
|
Ładowanie…
Reference in New Issue