kopia lustrzana https://github.com/onthegomap/planetiler
implement line merge
rodzic
0dacc1d33b
commit
3ad8bb9f56
|
@ -0,0 +1,136 @@
|
|||
package com.onthegomap.flatmap;
|
||||
|
||||
import com.onthegomap.flatmap.collections.MutableCoordinateSequence;
|
||||
import com.onthegomap.flatmap.geo.GeoUtils;
|
||||
import com.onthegomap.flatmap.geo.GeometryException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import org.locationtech.jts.geom.CoordinateSequence;
|
||||
import org.locationtech.jts.geom.Envelope;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import org.locationtech.jts.geom.LineString;
|
||||
import org.locationtech.jts.operation.linemerge.LineMerger;
|
||||
import org.locationtech.jts.simplify.DouglasPeuckerSimplifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class FeatureMerge {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FeatureMerge.class);
|
||||
|
||||
public static List<VectorTileEncoder.Feature> mergeLineStrings(List<VectorTileEncoder.Feature> items,
|
||||
double minLength, double tolerance, double clip) throws GeometryException {
|
||||
return mergeLineStrings(items, attrs -> minLength, tolerance, clip);
|
||||
}
|
||||
|
||||
public static List<VectorTileEncoder.Feature> mergeLineStrings(List<VectorTileEncoder.Feature> features,
|
||||
Function<Map<String, Object>, Double> lengthLimitCalculator, double tolerance, double clip)
|
||||
throws GeometryException {
|
||||
List<VectorTileEncoder.Feature> result = new ArrayList<>(features.size());
|
||||
LinkedHashMap<Map<String, Object>, List<VectorTileEncoder.Feature>> groupedByAttrs = new LinkedHashMap<>();
|
||||
for (VectorTileEncoder.Feature feature : features) {
|
||||
if (feature.geometry().geomType() != GeometryType.LINE) {
|
||||
// just ignore and pass through non-linestring features
|
||||
result.add(feature);
|
||||
} else {
|
||||
groupedByAttrs
|
||||
.computeIfAbsent(feature.attrs(), k -> new ArrayList<>())
|
||||
.add(feature);
|
||||
}
|
||||
}
|
||||
for (var entry : groupedByAttrs.entrySet()) {
|
||||
List<VectorTileEncoder.Feature> groupedFeatures = entry.getValue();
|
||||
VectorTileEncoder.Feature feature1 = groupedFeatures.get(0);
|
||||
double lengthLimit = lengthLimitCalculator.apply(feature1.attrs());
|
||||
|
||||
// as a shortcut, can skip line merging only if there is:
|
||||
// - only 1 element in the group
|
||||
// - it doesn't need to be clipped
|
||||
// - it can't possibly be filtered out for being too short
|
||||
if (groupedFeatures.size() == 1 && clip == 0d && lengthLimit == 0) {
|
||||
result.add(feature1);
|
||||
} else {
|
||||
LineMerger merger = new LineMerger();
|
||||
for (VectorTileEncoder.Feature feature : groupedFeatures) {
|
||||
merger.add(feature.geometry().decode());
|
||||
}
|
||||
List<LineString> outputSegments = new ArrayList<>();
|
||||
for (Object merged : merger.getMergedLineStrings()) {
|
||||
if (merged instanceof LineString line && line.getLength() >= lengthLimit) {
|
||||
// re-simplify since some endpoints of merged segments may be unnecessary
|
||||
if (line.getNumPoints() > 2) {
|
||||
DouglasPeuckerSimplifier simplifier = new DouglasPeuckerSimplifier(line);
|
||||
simplifier.setDistanceTolerance(tolerance);
|
||||
simplifier.setEnsureValid(false);
|
||||
Geometry simplified = simplifier.getResultGeometry();
|
||||
if (simplified instanceof LineString simpleLineString) {
|
||||
line = simpleLineString;
|
||||
} else {
|
||||
LOGGER.warn("line string merge simplify emitted " + simplified.getGeometryType());
|
||||
}
|
||||
}
|
||||
if (clip > 0) {
|
||||
removeDetailOutsideTile(line, clip, outputSegments);
|
||||
} else {
|
||||
outputSegments.add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (outputSegments.size() == 0) {
|
||||
// no segments to output - skip this feature
|
||||
} else {
|
||||
Geometry newGeometry =
|
||||
outputSegments.size() == 1 ?
|
||||
outputSegments.get(0) :
|
||||
GeoUtils.createMultiLineString(outputSegments);
|
||||
result.add(feature1.copyWithNewGeometry(newGeometry));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void removeDetailOutsideTile(LineString input, double buffer, List<LineString> output) {
|
||||
MutableCoordinateSequence current = new MutableCoordinateSequence();
|
||||
CoordinateSequence seq = input.getCoordinateSequence();
|
||||
boolean wasIn = false;
|
||||
double min = -buffer, max = 256 + buffer;
|
||||
double x = seq.getX(0), y = seq.getY(0);
|
||||
Envelope env = new Envelope();
|
||||
Envelope outer = new Envelope(min, max, min, max);
|
||||
for (int i = 0; i < seq.size() - 1; i++) {
|
||||
double nextX = seq.getX(i + 1), nextY = seq.getY(i + 1);
|
||||
env.init(x, nextX, y, nextY);
|
||||
boolean nowIn = env.intersects(outer);
|
||||
if (nowIn || wasIn) {
|
||||
current.addPoint(x, y);
|
||||
} else { // out
|
||||
// wait to flush until 2 consecutive outs
|
||||
if (!current.isEmpty()) {
|
||||
output.add(GeoUtils.JTS_FACTORY.createLineString(current));
|
||||
current = new MutableCoordinateSequence();
|
||||
}
|
||||
}
|
||||
wasIn = nowIn;
|
||||
x = nextX;
|
||||
y = nextY;
|
||||
}
|
||||
double lastX = seq.getX(seq.size() - 1), lastY = seq.getY(seq.size() - 1);
|
||||
env.init(x, lastX, y, lastY);
|
||||
if (env.intersects(outer) || wasIn) {
|
||||
current.addPoint(lastX, lastY);
|
||||
}
|
||||
|
||||
if (!current.isEmpty()) {
|
||||
output.add(GeoUtils.JTS_FACTORY.createLineString(current));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<VectorTileEncoder.Feature> mergePolygons(List<VectorTileEncoder.Feature> items, double minSize,
|
||||
double minDist, double buffer) {
|
||||
return items;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package com.onthegomap.flatmap;
|
||||
|
||||
import com.graphhopper.reader.ReaderRelation;
|
||||
import com.onthegomap.flatmap.geo.GeometryException;
|
||||
import com.onthegomap.flatmap.read.OpenStreetMapReader;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -13,7 +14,7 @@ public interface Profile {
|
|||
void release();
|
||||
|
||||
List<VectorTileEncoder.Feature> postProcessLayerFeatures(String layer, int zoom,
|
||||
List<VectorTileEncoder.Feature> items);
|
||||
List<VectorTileEncoder.Feature> items) throws GeometryException;
|
||||
|
||||
String name();
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.onthegomap.flatmap.GeometryType;
|
|||
import com.onthegomap.flatmap.LayerStats;
|
||||
import com.onthegomap.flatmap.Profile;
|
||||
import com.onthegomap.flatmap.VectorTileEncoder;
|
||||
import com.onthegomap.flatmap.geo.GeometryException;
|
||||
import com.onthegomap.flatmap.geo.TileCoord;
|
||||
import com.onthegomap.flatmap.monitoring.Stats;
|
||||
import com.onthegomap.flatmap.render.RenderedFeature;
|
||||
|
@ -371,23 +372,26 @@ public final class FeatureGroup implements Consumer<FeatureSort.Entry>, Iterable
|
|||
if (currentLayer == null) {
|
||||
currentLayer = layer;
|
||||
} else if (!currentLayer.equals(layer)) {
|
||||
encoder.addLayerFeatures(
|
||||
currentLayer,
|
||||
profile.postProcessLayerFeatures(currentLayer, tile.z(), items)
|
||||
);
|
||||
emitLayer(encoder, items, currentLayer);
|
||||
currentLayer = layer;
|
||||
items.clear();
|
||||
}
|
||||
|
||||
items.add(feature);
|
||||
}
|
||||
encoder.addLayerFeatures(
|
||||
currentLayer,
|
||||
profile.postProcessLayerFeatures(currentLayer, tile.z(), items)
|
||||
);
|
||||
emitLayer(encoder, items, currentLayer);
|
||||
return encoder;
|
||||
}
|
||||
|
||||
private void emitLayer(VectorTileEncoder encoder, List<VectorTileEncoder.Feature> items, String currentLayer) {
|
||||
try {
|
||||
items = profile.postProcessLayerFeatures(currentLayer, tile.z(), items);
|
||||
} catch (GeometryException e) {
|
||||
LOGGER.warn("error postprocessing features for " + currentLayer + " layer on " + tile + ": " + e.getMessage());
|
||||
}
|
||||
encoder.addLayerFeatures(currentLayer, items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(FeatureSort.Entry entry) {
|
||||
long sortKey = entry.sortKey();
|
||||
|
|
|
@ -77,6 +77,10 @@ public class MutableCoordinateSequence extends PackedCoordinateSequence {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return points.isEmpty();
|
||||
}
|
||||
|
||||
private static class ScalingSequence extends MutableCoordinateSequence {
|
||||
|
||||
private final double scale;
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
package com.onthegomap.flatmap;
|
||||
|
||||
import static com.onthegomap.flatmap.TestUtils.newLineString;
|
||||
import static com.onthegomap.flatmap.TestUtils.newMultiLineString;
|
||||
import static com.onthegomap.flatmap.TestUtils.newPoint;
|
||||
import static com.onthegomap.flatmap.TestUtils.rectangle;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.onthegomap.flatmap.geo.GeometryException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
|
||||
public class FeatureMergeTest {
|
||||
|
||||
private VectorTileEncoder.Feature feature(long id, Geometry geom, Map<String, Object> attrs) {
|
||||
return new VectorTileEncoder.Feature(
|
||||
"layer",
|
||||
id,
|
||||
VectorTileEncoder.encodeGeometry(geom),
|
||||
attrs
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeMergeZeroLineStrings() throws GeometryException {
|
||||
assertEquals(
|
||||
List.of(),
|
||||
FeatureMerge.mergeLineStrings(
|
||||
List.of(),
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeMergeOneLineStrings() throws GeometryException {
|
||||
assertEquals(
|
||||
List.of(
|
||||
feature(1, newLineString(10, 10, 20, 20), Map.of())
|
||||
),
|
||||
FeatureMerge.mergeLineStrings(
|
||||
List.of(
|
||||
feature(1, newLineString(10, 10, 20, 20), Map.of())
|
||||
),
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dontMergeDisconnectedLineStrings() throws GeometryException {
|
||||
assertEquals(
|
||||
List.of(
|
||||
feature(1, newMultiLineString(
|
||||
newLineString(10, 10, 20, 20),
|
||||
newLineString(30, 30, 40, 40)
|
||||
), Map.of())
|
||||
),
|
||||
FeatureMerge.mergeLineStrings(
|
||||
List.of(
|
||||
feature(1, newLineString(10, 10, 20, 20), Map.of()),
|
||||
feature(2, newLineString(30, 30, 40, 40), Map.of())
|
||||
),
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dontMergeConnectedLineStringsDifferentAttr() throws GeometryException {
|
||||
assertEquals(
|
||||
List.of(
|
||||
feature(1, newLineString(10, 10, 20, 20), Map.of("a", 1)),
|
||||
feature(2, newLineString(20, 20, 30, 30), Map.of("b", 2))
|
||||
),
|
||||
FeatureMerge.mergeLineStrings(
|
||||
List.of(
|
||||
feature(1, newLineString(10, 10, 20, 20), Map.of("a", 1)),
|
||||
feature(2, newLineString(20, 20, 30, 30), Map.of("b", 2))
|
||||
),
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeConnectedLineStringsSameAttrs() throws GeometryException {
|
||||
assertEquals(
|
||||
List.of(
|
||||
feature(1, newLineString(10, 10, 30, 30), Map.of("a", 1))
|
||||
),
|
||||
FeatureMerge.mergeLineStrings(
|
||||
List.of(
|
||||
feature(1, newLineString(10, 10, 20, 20), Map.of("a", 1)),
|
||||
feature(2, newLineString(20, 20, 30, 30), Map.of("a", 1))
|
||||
),
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeMultiLineString() throws GeometryException {
|
||||
assertEquals(
|
||||
List.of(
|
||||
feature(1, newLineString(10, 10, 40, 40), Map.of("a", 1))
|
||||
),
|
||||
FeatureMerge.mergeLineStrings(
|
||||
List.of(
|
||||
feature(1, newMultiLineString(
|
||||
newLineString(10, 10, 20, 20),
|
||||
newLineString(30, 30, 40, 40)
|
||||
), Map.of("a", 1)),
|
||||
feature(2, newLineString(20, 20, 30, 30), Map.of("a", 1))
|
||||
),
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeLineStringIgnoreNonLineString() throws GeometryException {
|
||||
assertEquals(
|
||||
List.of(
|
||||
feature(3, newPoint(5, 5), Map.of("a", 1)),
|
||||
feature(4, rectangle(50, 60), Map.of("a", 1)),
|
||||
feature(1, newLineString(10, 10, 30, 30), Map.of("a", 1))
|
||||
),
|
||||
FeatureMerge.mergeLineStrings(
|
||||
List.of(
|
||||
feature(1, newLineString(10, 10, 20, 20), Map.of("a", 1)),
|
||||
feature(2, newLineString(20, 20, 30, 30), Map.of("a", 1)),
|
||||
feature(3, newPoint(5, 5), Map.of("a", 1)),
|
||||
feature(4, rectangle(50, 60), Map.of("a", 1))
|
||||
),
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeLineStringRemoveDetailOutsideTile() throws GeometryException {
|
||||
assertEquals(
|
||||
List.of(
|
||||
feature(1, newMultiLineString(
|
||||
newLineString(
|
||||
10, 10,
|
||||
-10, 20,
|
||||
10, 30,
|
||||
-10, 40,
|
||||
-10, 50,
|
||||
10, 60,
|
||||
-10, 70
|
||||
),
|
||||
newLineString(
|
||||
-10, 100,
|
||||
10, 100
|
||||
)
|
||||
), Map.of("a", 1))
|
||||
),
|
||||
FeatureMerge.mergeLineStrings(
|
||||
List.of(
|
||||
// one point goes out - dont clip
|
||||
feature(1, newLineString(10, 10, -10, 20), Map.of("a", 1)),
|
||||
feature(2, newLineString(-10, 20, 10, 30), Map.of("a", 1)),
|
||||
feature(3, newLineString(10, 30, -10, 40), Map.of("a", 1)),
|
||||
// two points goes out - dont clip
|
||||
feature(4, newLineString(-10, 40, -10, 50), Map.of("a", 1)),
|
||||
feature(5, newLineString(-10, 50, 10, 60), Map.of("a", 1)),
|
||||
feature(5, newLineString(10, 60, -10, 70), Map.of("a", 1)),
|
||||
// three points out - do clip
|
||||
feature(6, newLineString(-10, 70, -10, 80), Map.of("a", 1)),
|
||||
feature(7, newLineString(-10, 80, -11, 90), Map.of("a", 1)),
|
||||
feature(8, newLineString(-10, 90, -10, 100), Map.of("a", 1)),
|
||||
feature(9, newLineString(-10, 100, 10, 100), Map.of("a", 1))
|
||||
),
|
||||
0,
|
||||
0,
|
||||
1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeLineStringMinLength() throws GeometryException {
|
||||
assertEquals(
|
||||
List.of(
|
||||
feature(2, newLineString(20, 20, 20, 25), Map.of("a", 1))
|
||||
),
|
||||
FeatureMerge.mergeLineStrings(
|
||||
List.of(
|
||||
// too short - omit entire feature
|
||||
feature(1, newLineString(10, 10, 10, 14), Map.of("b", 1)),
|
||||
|
||||
// too short - omit from combined group
|
||||
feature(2, newLineString(20, 10, 20, 12), Map.of("a", 1)),
|
||||
feature(3, newLineString(20, 12, 20, 14), Map.of("a", 1)),
|
||||
|
||||
// just long enough
|
||||
feature(4, newLineString(20, 20, 20, 24), Map.of("a", 1)),
|
||||
feature(5, newLineString(20, 24, 20, 25), Map.of("a", 1))
|
||||
),
|
||||
5,
|
||||
0,
|
||||
0
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import com.onthegomap.flatmap.collections.FeatureGroup;
|
|||
import com.onthegomap.flatmap.collections.FeatureSort;
|
||||
import com.onthegomap.flatmap.collections.LongLongMap;
|
||||
import com.onthegomap.flatmap.geo.GeoUtils;
|
||||
import com.onthegomap.flatmap.geo.GeometryException;
|
||||
import com.onthegomap.flatmap.geo.TileCoord;
|
||||
import com.onthegomap.flatmap.monitoring.Stats;
|
||||
import com.onthegomap.flatmap.read.OpenStreetMapReader;
|
||||
|
@ -34,6 +35,7 @@ import java.util.TreeMap;
|
|||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
@ -171,7 +173,7 @@ public class FlatMapTest {
|
|||
return run(
|
||||
args,
|
||||
(featureGroup, profile, config) -> processOsmFeatures(featureGroup, profile, config, features),
|
||||
new TestProfile(profileFunction, preprocessOsmRelation, (a, b, c) -> null)
|
||||
new TestProfile(profileFunction, preprocessOsmRelation, (a, b, c) -> c)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -933,6 +935,104 @@ public class FlatMapTest {
|
|||
), results.tiles);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeLineStrings() throws Exception {
|
||||
double y = 0.5 + Z14_WIDTH / 2;
|
||||
double lat = GeoUtils.getWorldLat(y);
|
||||
|
||||
double x1 = 0.5 + Z14_WIDTH / 4;
|
||||
double lng1 = GeoUtils.getWorldLon(x1);
|
||||
double lng2 = GeoUtils.getWorldLon(x1 + Z14_WIDTH * 10d / 256);
|
||||
double lng3 = GeoUtils.getWorldLon(x1 + Z14_WIDTH * 20d / 256);
|
||||
double lng4 = GeoUtils.getWorldLon(x1 + Z14_WIDTH * 30d / 256);
|
||||
|
||||
var results = runWithReaderFeatures(
|
||||
Map.of("threads", "1"),
|
||||
List.of(
|
||||
// merge at z13 (same "group"):
|
||||
new ReaderFeature(newLineString(
|
||||
lng1, lat,
|
||||
lng2, lat
|
||||
), Map.of("group", "1", "other", "1")),
|
||||
new ReaderFeature(newLineString(
|
||||
lng2, lat,
|
||||
lng3, lat
|
||||
), Map.of("group", "1", "other", "2")),
|
||||
// don't merge at z13:
|
||||
new ReaderFeature(newLineString(
|
||||
lng3, lat,
|
||||
lng4, lat
|
||||
), Map.of("group", "2", "other", "3"))
|
||||
),
|
||||
(in, features) -> {
|
||||
features.line("layer")
|
||||
.setZoomRange(13, 14)
|
||||
.setAttrWithMinzoom("z14attr", in.getTag("other"), 14)
|
||||
.inheritFromSource("group");
|
||||
},
|
||||
(layer, zoom, items) -> FeatureMerge.mergeLineStrings(items, 0, 0, 0)
|
||||
);
|
||||
|
||||
assertSubmap(sortListValues(Map.of(
|
||||
TileCoord.ofXYZ(Z14_TILES / 2, Z14_TILES / 2, 14), List.of(
|
||||
feature(newLineString(64, 128, 74, 128), Map.of("group", "1", "z14attr", "1")),
|
||||
feature(newLineString(74, 128, 84, 128), Map.of("group", "1", "z14attr", "2")),
|
||||
feature(newLineString(84, 128, 94, 128), Map.of("group", "2", "z14attr", "3"))
|
||||
),
|
||||
TileCoord.ofXYZ(Z13_TILES / 2, Z13_TILES / 2, 13), List.of(
|
||||
// merge 32->37 and 37->42 since they have same attrs
|
||||
feature(newLineString(32, 64, 42, 64), Map.of("group", "1")),
|
||||
feature(newLineString(42, 64, 47, 64), Map.of("group", "2"))
|
||||
)
|
||||
)), sortListValues(results.tiles));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testMergePolygons() throws Exception {
|
||||
var results = runWithReaderFeatures(
|
||||
Map.of("threads", "1"),
|
||||
List.of(
|
||||
// merge at z13 (same "group"):
|
||||
new ReaderFeature(newLineString(z14CoordinateList(
|
||||
10, 10,
|
||||
20, 10,
|
||||
20, 20,
|
||||
10, 20,
|
||||
10, 10
|
||||
)), Map.of("group", "1")),
|
||||
new ReaderFeature(newLineString(
|
||||
20.5, 10,
|
||||
30, 10,
|
||||
30, 20,
|
||||
20.5, 20,
|
||||
20.5, 10
|
||||
), Map.of("group", "1")),
|
||||
// don't merge at z13:
|
||||
new ReaderFeature(newLineString(
|
||||
10, 20.5,
|
||||
20, 20.5,
|
||||
20, 30,
|
||||
10, 30,
|
||||
10, 20.5
|
||||
), Map.of("group", "2"))
|
||||
),
|
||||
(in, features) -> {
|
||||
features.line("layer")
|
||||
.setZoomRange(14, 14)
|
||||
.inheritFromSource("group");
|
||||
},
|
||||
(layer, zoom, items) -> FeatureMerge.mergePolygons(items, 0, 1, 1)
|
||||
);
|
||||
|
||||
assertSubmap(sortListValues(Map.of(
|
||||
TileCoord.ofXYZ(Z14_TILES / 2, Z14_TILES / 2, 14), List.of(
|
||||
feature(rectangle(10, 10, 30, 20), Map.of("group", "1")),
|
||||
feature(rectangle(10, 20.5, 20, 30), Map.of("group", "2"))
|
||||
)
|
||||
)), sortListValues(results.tiles));
|
||||
}
|
||||
|
||||
private <K extends Comparable<? super K>, V extends List<?>> Map<K, ?> sortListValues(Map<K, V> input) {
|
||||
Map<K, List<?>> result = new TreeMap<>();
|
||||
for (var entry : input.entrySet()) {
|
||||
|
@ -954,7 +1054,8 @@ public class FlatMapTest {
|
|||
|
||||
private interface LayerPostprocessFunction {
|
||||
|
||||
List<VectorTileEncoder.Feature> process(String layer, int zoom, List<VectorTileEncoder.Feature> items);
|
||||
List<VectorTileEncoder.Feature> process(String layer, int zoom, List<VectorTileEncoder.Feature> items)
|
||||
throws GeometryException;
|
||||
}
|
||||
|
||||
private static record FlatMapResults(
|
||||
|
@ -982,7 +1083,7 @@ public class FlatMapTest {
|
|||
}
|
||||
|
||||
static TestProfile processSourceFeatures(BiConsumer<SourceFeature, FeatureCollector> processFeature) {
|
||||
return new TestProfile(processFeature, (a) -> null, (a, b, c) -> null);
|
||||
return new TestProfile(processFeature, (a) -> null, (a, b, c) -> c);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1002,7 +1103,7 @@ public class FlatMapTest {
|
|||
|
||||
@Override
|
||||
public List<VectorTileEncoder.Feature> postProcessLayerFeatures(String layer, int zoom,
|
||||
List<VectorTileEncoder.Feature> items) {
|
||||
List<VectorTileEncoder.Feature> items) throws GeometryException {
|
||||
return postprocessLayerFeatures.process(layer, zoom, items);
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue