pull/1/head
Mike Barry 2021-06-02 07:54:21 -04:00
rodzic 3ad8bb9f56
commit 103b5c43cf
8 zmienionych plików z 558 dodań i 44 usunięć

Wyświetl plik

@ -1,17 +1,27 @@
package com.onthegomap.flatmap;
import com.carrotsearch.hppc.IntArrayList;
import com.carrotsearch.hppc.IntObjectMap;
import com.graphhopper.coll.GHIntObjectHashMap;
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.BitSet;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.locationtech.jts.algorithm.Area;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.operation.buffer.BufferOp;
import org.locationtech.jts.operation.buffer.BufferParameters;
import org.locationtech.jts.operation.linemerge.LineMerger;
import org.locationtech.jts.simplify.DouglasPeuckerSimplifier;
import org.slf4j.Logger;
@ -30,19 +40,8 @@ public class FeatureMerge {
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();
var groupedByAttrs = groupByAttrs(features, result, GeometryType.LINE);
for (List<VectorTileEncoder.Feature> groupedFeatures : groupedByAttrs) {
VectorTileEncoder.Feature feature1 = groupedFeatures.get(0);
double lengthLimit = lengthLimitCalculator.apply(feature1.attrs());
@ -82,10 +81,7 @@ public class FeatureMerge {
if (outputSegments.size() == 0) {
// no segments to output - skip this feature
} else {
Geometry newGeometry =
outputSegments.size() == 1 ?
outputSegments.get(0) :
GeoUtils.createMultiLineString(outputSegments);
Geometry newGeometry = GeoUtils.combineLineStrings(outputSegments);
result.add(feature1.copyWithNewGeometry(newGeometry));
}
}
@ -129,8 +125,195 @@ public class FeatureMerge {
}
}
public static List<VectorTileEncoder.Feature> mergePolygons(List<VectorTileEncoder.Feature> items, double minSize,
double minDist, double buffer) {
return items;
private static final BufferParameters bufferOps = new BufferParameters();
static {
bufferOps.setJoinStyle(BufferParameters.JOIN_MITRE);
}
public static List<VectorTileEncoder.Feature> mergePolygons(List<VectorTileEncoder.Feature> features, double minArea,
double minDist, double buffer) throws GeometryException {
List<VectorTileEncoder.Feature> result = new ArrayList<>(features.size());
Collection<List<VectorTileEncoder.Feature>> groupedByAttrs = groupByAttrs(features, result, GeometryType.POLYGON);
for (List<VectorTileEncoder.Feature> groupedFeatures : groupedByAttrs) {
List<Polygon> outPolygons = new ArrayList<>();
VectorTileEncoder.Feature feature1 = groupedFeatures.get(0);
List<Geometry> geometries = new ArrayList<>(groupedFeatures.size());
for (var feature : groupedFeatures) {
geometries.add(feature.geometry().decode());
}
Collection<List<Geometry>> groupedByProximity = groupPolygonsByProximity(geometries, minDist);
for (List<Geometry> polygonGroup : groupedByProximity) {
Geometry merged;
if (polygonGroup.size() > 1) {
merged = GeoUtils.createGeometryCollection(polygonGroup);
merged = new BufferOp(merged, bufferOps).getResultGeometry(buffer);
if (buffer > 0) {
merged = new BufferOp(merged, bufferOps).getResultGeometry(-buffer);
}
if (!(merged instanceof Polygon poly)
|| Area.ofRing(poly.getExteriorRing().getCoordinateSequence()) < minArea) {
continue;
}
merged = GeoUtils.snapAndFixPolygon(merged).reverse();
} else {
merged = polygonGroup.get(0);
if (!(merged instanceof Polygon poly)
|| Area.ofRing(poly.getExteriorRing().getCoordinateSequence()) < minArea) {
continue;
}
}
// TODO VW simplify?
// TODO limit inner ring area
extractPolygons(merged, outPolygons);
}
if (!outPolygons.isEmpty()) {
Geometry combined = GeoUtils.combinePolygons(outPolygons);
result.add(feature1.copyWithNewGeometry(combined));
}
// TODO:
// - for each geom, find all other geoms within minDist distance
// - grow the groups
// - for each group, buffer/unbuffer
// STRtree
// for (int i = 0; i < groupedFeatures.size(); i++) {
// Geometry poly = allGeoms[i] = groupedFeatures.get(i).geometry().decode();
// Envelope env = poly.getEnvelopeInternal();
// env.expandBy(buffer);
// index.add(envs[i] = env);
// }
// index.finish();
// IntSet visited = new IntHashSet();
// IntArrayDeque queue = new IntArrayDeque();
// for (int i = 0; i < groupedFeatures.size(); i++) {
// if (!visited.contains(i)) {
// List<Geometry> thisGroup = new ArrayList<>();
// queue.addFirst(i);
// visited.add(i);
// while (!queue.isEmpty()) {
// int toVisit = queue.removeLast();
// thisGroup.add(allGeoms[toVisit]);
// IntArrayList matches = index.search(envs[toVisit]);
// for (int k = 0; k < matches.size(); k++) {
// int match = matches.get(k);
// // TODO test if distance < epsilon
// if (!visited.contains(match)) {
// visited.add(match);
// queue.addFirst(match);
// }
// }
// }
//
// Geometry geom;
// if (thisGroup.size() > 1) {
// geom = OSMToVectorTiles.gf.createGeometryCollection(GeometryFactory.toGeometryArray(thisGroup));
// geom = new BufferOp(geom, bufferOps).getResultGeometry(buffer).union();
// if (buffer > 0) {
// geom = new BufferOp(geom, bufferOps).getResultGeometry(-buffer);
// }
// } else {
// geom = thisGroup.get(0);
// }
// // TODO VW simplify
// // TODO limit inner ring area
// extractPolygons(feature1, result, geom, minSize);
// }
// }
}
return result;
}
private static void extractPolygons(Geometry geom, List<Polygon> result) {
if (geom instanceof Polygon poly) {
result.add(poly);
} else if (geom instanceof GeometryCollection) {
for (int i = 0; i < geom.getNumGeometries(); i++) {
extractPolygons(geom.getGeometryN(i), result);
}
}
}
private static IntObjectMap<IntArrayList> extractAdjacencyList(List<Geometry> geometries, double minDist) {
// TODO use spatial index
IntObjectMap<IntArrayList> result = new GHIntObjectHashMap<>();
for (int i = 0; i < geometries.size(); i++) {
Geometry a = geometries.get(i);
for (int j = 0; j < i; j++) {
Geometry b = geometries.get(j);
if (a.isWithinDistance(b, minDist)) {
put(result, i, j);
put(result, j, i);
}
}
}
return result;
}
private static void put(IntObjectMap<IntArrayList> result, int from, int to) {
IntArrayList ilist = result.get(from);
if (ilist == null) {
result.put(from, ilist = new IntArrayList());
}
ilist.add(to);
}
static List<IntArrayList> extractConnectedComponents(IntObjectMap<IntArrayList> adjacencyList, int numItems) {
List<IntArrayList> result = new ArrayList<>();
BitSet visited = new BitSet(numItems);
for (int i = 0; i < numItems; i++) {
if (!visited.get(i)) {
visited.set(i, true);
IntArrayList group = new IntArrayList();
group.add(i);
result.add(group);
dfs(i, group, adjacencyList, visited);
}
}
return result;
}
private static void dfs(int start, IntArrayList group, IntObjectMap<IntArrayList> adjacencyList, BitSet visited) {
IntArrayList adjacent = adjacencyList.get(start);
if (adjacent != null) {
for (var cursor : adjacent) {
int index = cursor.value;
if (!visited.get(index)) {
visited.set(index, true);
group.add(index);
dfs(index, group, adjacencyList, visited);
}
}
}
}
private static Collection<List<Geometry>> groupPolygonsByProximity(List<Geometry> geometries, double minDist) {
IntObjectMap<IntArrayList> adjacencyList = extractAdjacencyList(geometries, minDist);
List<IntArrayList> groups = extractConnectedComponents(adjacencyList, geometries.size());
return groups.stream().map(ids -> {
List<Geometry> geomsInGroup = new ArrayList<>(ids.size());
for (var cursor : ids) {
geomsInGroup.add(geometries.get(cursor.value));
}
return geomsInGroup;
}).toList();
}
private static Collection<List<VectorTileEncoder.Feature>> groupByAttrs(
List<VectorTileEncoder.Feature> features, List<VectorTileEncoder.Feature> result, GeometryType geometryType) {
LinkedHashMap<Map<String, Object>, List<VectorTileEncoder.Feature>> groupedByAttrs = new LinkedHashMap<>();
for (VectorTileEncoder.Feature feature : features) {
if (feature.geometry().geomType() != geometryType) {
// just ignore and pass through non-polygon features
result.add(feature);
} else {
groupedByAttrs
.computeIfAbsent(feature.attrs(), k -> new ArrayList<>())
.add(feature);
}
}
return groupedByAttrs.values();
}
}

Wyświetl plik

@ -188,7 +188,7 @@ public class VectorTileEncoder {
if (first) {
first = false;
outerCCW = ccw;
assert outerCCW;
assert outerCCW : "outer ring is not counter-clockwise";
}
if (ccw == outerCCW) {
ringsForCurrentPolygon = new ArrayList<>();

Wyświetl plik

@ -32,6 +32,7 @@ public class GeoUtils {
public static final WKBReader wkbReader = new WKBReader(JTS_FACTORY);
private static final LineString[] EMPTY_LINE_STRING_ARRAY = new LineString[0];
private static final Polygon[] EMPTY_POLYGON_ARRAY = new Polygon[0];
private static final Coordinate[] EMPTY_COORD_ARRAY = new Coordinate[0];
private static final Point[] EMPTY_POINT_ARRAY = new Point[0];
@ -168,6 +169,10 @@ public class GeoUtils {
return JTS_FACTORY.createMultiLineString(lineStrings.toArray(EMPTY_LINE_STRING_ARRAY));
}
public static Geometry createMultiPolygon(List<Polygon> polygon) {
return JTS_FACTORY.createMultiPolygon(polygon.toArray(EMPTY_POLYGON_ARRAY));
}
public static Geometry fixPolygon(Geometry geom) throws GeometryException {
try {
return geom.buffer(0);
@ -176,6 +181,25 @@ public class GeoUtils {
}
}
public static Geometry combineLineStrings(List<LineString> lineStrings) {
return lineStrings.size() == 1 ? lineStrings.get(0) : createMultiLineString(lineStrings);
}
public static Geometry combinePolygons(List<Polygon> polys) {
return polys.size() == 1 ? polys.get(0) : createMultiPolygon(polys);
}
public static Geometry combinePoints(List<Point> points) {
return points.size() == 1 ? points.get(0) : createMultiPoint(points);
}
public static final PrecisionModel TILE_PRECISON = new PrecisionModel(4096d / 256d);
public static Geometry snapAndFixPolygon(Geometry geom) throws GeometryException {
return snapAndFixPolygon(geom, TILE_PRECISON);
}
public static Geometry snapAndFixPolygon(Geometry geom, PrecisionModel tilePrecision) throws GeometryException {
try {
return GeometryPrecisionReducer.reduce(geom, tilePrecision);
@ -251,6 +275,10 @@ public class GeoUtils {
}
}
public static Geometry createGeometryCollection(List<Geometry> polygonGroup) {
return JTS_FACTORY.createGeometryCollection(polygonGroup.toArray(Geometry[]::new));
}
private static record PolyAndArea(Polygon poly, double area) implements Comparable<PolyAndArea> {
PolyAndArea(Polygon poly) {

Wyświetl plik

@ -79,7 +79,7 @@ class CoordinateSequenceExtractor {
}
}
}
return lineStrings.size() == 1 ? lineStrings.get(0) : GeoUtils.createMultiLineString(lineStrings);
return GeoUtils.combineLineStrings(lineStrings);
}
@NotNull
@ -120,6 +120,6 @@ class CoordinateSequenceExtractor {
}
}
}
return points.size() == 1 ? points.get(0) : GeoUtils.createMultiPoint(points);
return GeoUtils.combinePoints(points);
}
}

Wyświetl plik

@ -1,5 +1,7 @@
package com.onthegomap.flatmap.render;
import static com.onthegomap.flatmap.geo.GeoUtils.TILE_PRECISON;
import com.onthegomap.flatmap.CommonParams;
import com.onthegomap.flatmap.FeatureCollector;
import com.onthegomap.flatmap.TileExtents;
@ -23,7 +25,6 @@ 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.PrecisionModel;
import org.locationtech.jts.geom.impl.PackedCoordinateSequence;
import org.locationtech.jts.geom.util.AffineTransformation;
import org.locationtech.jts.simplify.DouglasPeuckerSimplifier;
@ -35,7 +36,6 @@ public class FeatureRenderer implements Consumer<FeatureCollector.Feature> {
private static final AtomicLong idGen = new AtomicLong(0);
private static final Logger LOGGER = LoggerFactory.getLogger(FeatureRenderer.class);
private static final PrecisionModel tilePrecision = new PrecisionModel(4096d / 256d);
private static final VectorTileEncoder.VectorGeometry FILL = VectorTileEncoder.encodeGeometry(GeoUtils.JTS_FACTORY
.createPolygon(GeoUtils.JTS_FACTORY.createLinearRing(new PackedCoordinateSequence.Double(new double[]{
-5, -5,
@ -172,7 +172,7 @@ public class FeatureRenderer implements Consumer<FeatureCollector.Feature> {
Geometry geom;
if (feature.area()) {
geom = CoordinateSequenceExtractor.reassemblePolygons(geoms);
geom = GeoUtils.snapAndFixPolygon(geom, tilePrecision);
geom = GeoUtils.snapAndFixPolygon(geom, TILE_PRECISON);
// JTS utilities "fix" the geometry to be clockwise outer/CCW inner
geom = geom.reverse();
} else {

Wyświetl plik

@ -1,11 +1,11 @@
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 com.onthegomap.flatmap.TestUtils.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.carrotsearch.hppc.IntArrayList;
import com.carrotsearch.hppc.IntObjectMap;
import com.graphhopper.coll.GHIntObjectHashMap;
import com.onthegomap.flatmap.geo.GeometryException;
import java.util.List;
import java.util.Map;
@ -222,4 +222,301 @@ public class FeatureMergeTest {
)
);
}
/*
* POLYGON MERGE TESTS
*/
@Test
public void mergePolygonEmptyList() throws GeometryException {
assertEquivalentFeatures(
List.of(),
FeatureMerge.mergePolygons(
List.of(),
0,
0,
0
)
);
}
@Test
public void dontMergeDisconnectedPolygons() throws GeometryException {
assertEquivalentFeatures(
List.of(
feature(1, newMultiPolygon(
rectangle(10, 20),
rectangle(22, 10, 30, 20)
), Map.of("a", 1))
),
FeatureMerge.mergePolygons(
List.of(
feature(1, rectangle(10, 20), Map.of("a", 1)),
feature(2, rectangle(22, 10, 30, 20), Map.of("a", 1))
),
0,
0,
0
)
);
}
@Test
public void dontMergeConnectedPolygonsWithDifferentAttrs() throws GeometryException {
assertEquivalentFeatures(
List.of(
feature(1, rectangle(10, 20), Map.of("a", 1)),
feature(2, rectangle(20, 10, 30, 20), Map.of("b", 1))
),
FeatureMerge.mergePolygons(
List.of(
feature(1, rectangle(10, 20), Map.of("a", 1)),
feature(2, rectangle(20, 10, 30, 20), Map.of("b", 1))
),
0,
0,
0
)
);
}
@Test
public void mergeConnectedPolygonsWithSameAttrs() throws GeometryException {
assertEquivalentFeatures(
List.of(
feature(1, rectangle(10, 10, 30, 20), Map.of("a", 1))
),
FeatureMerge.mergePolygons(
List.of(
feature(1, rectangle(10, 20), Map.of("a", 1)),
feature(2, rectangle(20, 10, 30, 20), Map.of("a", 1))
),
0,
0,
1
)
);
}
@Test
public void mergeMultiPolygons() throws GeometryException {
assertEquivalentFeatures(
List.of(
feature(1, rectangle(10, 10, 40, 20), Map.of("a", 1))
),
FeatureMerge.mergePolygons(
List.of(
feature(1, newMultiPolygon(
rectangle(10, 20),
rectangle(30, 10, 40, 20)
), Map.of("a", 1)),
feature(2, rectangle(15, 10, 35, 20), Map.of("a", 1))
),
0,
0,
1
)
);
}
@Test
public void mergePolygonsIgnoreNonPolygons() throws GeometryException {
assertEquivalentFeatures(
List.of(
feature(2, newLineString(20, 10, 30, 20), Map.of("a", 1)),
feature(1, rectangle(10, 20), Map.of("a", 1))
),
FeatureMerge.mergePolygons(
List.of(
feature(1, rectangle(10, 20), Map.of("a", 1)),
feature(2, newLineString(20, 10, 30, 20), Map.of("a", 1))
),
0,
0,
0
)
);
}
@Test
public void mergePolygonsWithinMinDist() throws GeometryException {
assertEquivalentFeatures(
List.of(
feature(1, rectangle(10, 10, 30, 20), Map.of("a", 1))
),
FeatureMerge.mergePolygons(
List.of(
feature(1, rectangle(10, 20), Map.of("a", 1)),
feature(2, rectangle(20.9, 10, 30, 20), Map.of("a", 1))
),
0,
1,
1
)
);
}
@Test
public void dontMergePolygonsAboveMinDist() throws GeometryException {
assertEquivalentFeatures(
List.of(
feature(1, newMultiPolygon(
rectangle(10, 20),
rectangle(21.1, 10, 30, 20)
), Map.of("a", 1))
),
FeatureMerge.mergePolygons(
List.of(
feature(1, rectangle(10, 20), Map.of("a", 1)),
feature(2, rectangle(21.1, 10, 30, 20), Map.of("a", 1))
),
0,
1,
1
)
);
}
@Test
public void removePolygonsBelowMinSize() throws GeometryException {
assertEquivalentFeatures(
List.of(),
FeatureMerge.mergePolygons(
List.of(
feature(1, rectangle(10, 20), Map.of("a", 1)),
feature(2, rectangle(30, 10, 36, 20), Map.of("a", 1)),
feature(3, rectangle(35, 10, 40, 20), Map.of("a", 1))
),
101,
0,
0
)
);
}
@Test
public void allowPolygonsAboveMinSize() throws GeometryException {
assertEquivalentFeatures(
List.of(
feature(1, newMultiPolygon(
rectangle(10, 20),
rectangle(30, 10, 40, 20)
), Map.of("a", 1))
),
FeatureMerge.mergePolygons(
List.of(
feature(1, rectangle(10, 20), Map.of("a", 1)),
feature(2, rectangle(30, 10, 36, 20), Map.of("a", 1)),
feature(3, rectangle(35, 10, 40, 20), Map.of("a", 1))
),
99,
0,
1
)
);
}
private static void assertEquivalentFeatures(List<VectorTileEncoder.Feature> expected,
List<VectorTileEncoder.Feature> actual) throws GeometryException {
for (var feature : actual) {
Geometry geom = feature.geometry().decode();
TestUtils.validateGeometry(geom);
}
assertEquals(
expected.stream().map(f -> f.copyWithNewGeometry(newPoint(0, 0))).toList(),
actual.stream().map(f -> f.copyWithNewGeometry(newPoint(0, 0))).toList(),
"comparison without geometries"
);
assertEquals(
expected.stream().map(f -> new NormGeometry(silence(() -> f.geometry().decode()))).toList(),
actual.stream().map(f -> new NormGeometry(silence(() -> f.geometry().decode()))).toList(),
"geometry comparison"
);
}
private interface SupplierThatThrows<T> {
T get() throws Exception;
}
private static <T> T silence(SupplierThatThrows<T> fn) {
try {
return fn.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SafeVarargs
private static IntObjectMap<IntArrayList> adjacencyListFromGroups(List<Integer>... groups) {
IntObjectMap<IntArrayList> result = new GHIntObjectHashMap<>();
for (List<Integer> group : groups) {
for (int i = 0; i < group.size(); i++) {
Integer a = group.get(i);
for (int j = 0; j < i; j++) {
Integer b = group.get(j);
var aval = result.getOrDefault(a, new IntArrayList());
aval.add(b);
result.put(a, aval);
var bval = result.getOrDefault(b, new IntArrayList());
bval.add(a);
result.put(b, bval);
}
}
}
return result;
}
@Test
public void testExtractConnectedComponentsEmpty() {
assertEquals(
List.of(), FeatureMerge.extractConnectedComponents(new GHIntObjectHashMap<>(), 0)
);
}
@Test
public void testExtractConnectedComponentsOne() {
assertEquals(
List.of(
IntArrayList.from(0)
), FeatureMerge.extractConnectedComponents(new GHIntObjectHashMap<>(), 1)
);
}
@Test
public void testExtractConnectedComponentsTwoDisconnected() {
assertEquals(
List.of(
IntArrayList.from(0),
IntArrayList.from(1)
), FeatureMerge.extractConnectedComponents(new GHIntObjectHashMap<>(), 2)
);
}
@Test
public void testExtractConnectedComponentsTwoConnected() {
assertEquals(
List.of(
IntArrayList.from(0, 1)
), FeatureMerge.extractConnectedComponents(adjacencyListFromGroups(
List.of(0, 1)
), 2)
);
}
@Test
public void testExtractConnectedComponents() {
assertEquals(
List.of(
IntArrayList.from(0, 1, 2, 3),
IntArrayList.from(4),
IntArrayList.from(5, 6)
), FeatureMerge.extractConnectedComponents(adjacencyListFromGroups(
List.of(0, 1, 2, 3),
List.of(4),
List.of(5, 6)
), 7)
);
}
}

Wyświetl plik

@ -35,7 +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 java.util.stream.DoubleStream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
@ -43,7 +43,6 @@ import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.io.InputStreamInStream;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKBReader;
/**
@ -445,6 +444,10 @@ public class FlatMapTest {
return points;
}
public List<Coordinate> z14CoordinatePixelList(double... coords) {
return z14CoordinateList(DoubleStream.of(coords).map(c -> c / 256d).toArray());
}
@Test
public void testPolygonWithHoleSpanningMultipleTiles() throws Exception {
List<Coordinate> outerPoints = z14CoordinateList(
@ -595,7 +598,7 @@ public class FlatMapTest {
"njshore.wkb, 10571"
})
public void testComplexShorelinePolygons__TAKES_A_MINUTE_OR_TWO(String fileName, int expected)
throws Exception, ParseException {
throws Exception {
MultiPolygon geometry = (MultiPolygon) new WKBReader()
.read(new InputStreamInStream(Files.newInputStream(Path.of("src", "test", "resources", fileName))));
assertNotNull(geometry);
@ -988,37 +991,36 @@ public class FlatMapTest {
}
@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(
// merge same group:
new ReaderFeature(newPolygon(z14CoordinatePixelList(
10, 10,
20, 10,
20, 20,
10, 20,
10, 10
)), Map.of("group", "1")),
new ReaderFeature(newLineString(
new ReaderFeature(newPolygon(z14CoordinatePixelList(
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(
)), Map.of("group", "1")),
// don't merge - different group:
new ReaderFeature(newPolygon(z14CoordinatePixelList(
10, 20.5,
20, 20.5,
20, 30,
10, 30,
10, 20.5
), Map.of("group", "2"))
)), Map.of("group", "2"))
),
(in, features) -> {
features.line("layer")
features.polygon("layer")
.setZoomRange(14, 14)
.inheritFromSource("group");
},

Wyświetl plik

@ -121,6 +121,10 @@ public class TestUtils {
return rectangle(min, min, max, max);
}
public static Polygon newPolygon(List<Coordinate> outer) {
return newPolygon(outer, List.of());
}
public static Polygon newPolygon(List<Coordinate> outer, List<List<Coordinate>> inner) {
return GeoUtils.JTS_FACTORY.createPolygon(
GeoUtils.JTS_FACTORY.createLinearRing(outer.toArray(new Coordinate[0])),
@ -281,7 +285,7 @@ public class TestUtils {
public static void validateGeometry(Geometry g) {
if (g instanceof Polygonal) {
assertTrue(g.isSimple(), "JTS isValid()");
assertTrue(g.isSimple(), "JTS isSimple()");
}
validateGeometryRecursive(g);
}
@ -331,7 +335,7 @@ public class TestUtils {
}
}
private static record TopoGeometry(Geometry geom) implements GeometryComparision {
public static record TopoGeometry(Geometry geom) implements GeometryComparision {
@Override
public boolean equals(Object o) {