Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
msbarry committed Dec 6, 2024
1 parent 1d0bc0c commit d482a59
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.onthegomap.planetiler.collection.Hppc;
import com.onthegomap.planetiler.geo.GeoUtils;
import com.onthegomap.planetiler.geo.GeometryException;
import com.onthegomap.planetiler.geo.GeometryPipeline;
import com.onthegomap.planetiler.geo.GeometryType;
import com.onthegomap.planetiler.geo.MutableCoordinateSequence;
import com.onthegomap.planetiler.stats.DefaultStats;
Expand Down Expand Up @@ -81,7 +82,7 @@ private FeatureMerge() {}
*/
public static List<VectorTile.Feature> mergeLineStrings(List<VectorTile.Feature> features,
double minLength, double tolerance, double buffer, boolean resimplify) {
return mergeLineStrings(features, attrs -> minLength, tolerance, buffer, resimplify);
return mergeLineStrings(features, attrs -> minLength, tolerance, buffer, resimplify, null);
}

/**
Expand Down Expand Up @@ -143,20 +144,22 @@ private static List<VectorTile.Feature> mergeGeometries(
}

/**
* Merges linestrings with the same attributes as {@link #mergeLineStrings(List, Function, double, double, boolean)}
* except sets {@code resimplify=false} by default.
* Merges linestrings with the same attributes as
* {@link #mergeLineStrings(List, Function, double, double, boolean, GeometryPipeline)} except sets
* {@code resimplify=false} by default.
*/
public static List<VectorTile.Feature> mergeLineStrings(List<VectorTile.Feature> features,
Function<Map<String, Object>, Double> lengthLimitCalculator, double tolerance, double buffer) {
return mergeLineStrings(features, lengthLimitCalculator, tolerance, buffer, false);
return mergeLineStrings(features, lengthLimitCalculator, tolerance, buffer, false, null);
}

/**
* Merges linestrings with the same attributes as {@link #mergeLineStrings(List, double, double, double, boolean)}
* except with a dynamic length limit computed by {@code lengthLimitCalculator} for the attributes of each group.
*/
public static List<VectorTile.Feature> mergeLineStrings(List<VectorTile.Feature> features,
Function<Map<String, Object>, Double> lengthLimitCalculator, double tolerance, double buffer, boolean resimplify) {
Function<Map<String, Object>, Double> lengthLimitCalculator, double tolerance, double buffer, boolean resimplify,

Check warning on line 161 in planetiler-core/src/main/java/com/onthegomap/planetiler/FeatureMerge.java

View workflow job for this annotation

GitHub Actions / Analyze with Sonar

MINOR CODE_SMELL

Refactor this code to use the more specialised Functional Interface 'ToDoubleFunction<Map>' rule: java:S4276 (https://sonarcloud.io/organizations/onthegomap/rules?open=java%3AS4276&rule_key=java%3AS4276) issue url: https://sonarcloud.io/project/issues?pullRequest=1118&open=AZOb6fhCvuTsgZ3mwcyi&id=onthegomap_planetiler
GeometryPipeline pipeline) {
List<VectorTile.Feature> result = new ArrayList<>(features.size());
var groupedByAttrs = groupByAttrs(features, result, GeometryType.LINE);
for (List<VectorTile.Feature> groupedFeatures : groupedByAttrs) {
Expand All @@ -176,7 +179,8 @@ public static List<VectorTile.Feature> mergeLineStrings(List<VectorTile.Feature>
.setMergeStrokes(true)
.setMinLength(lengthLimit)
.setLoopMinLength(lengthLimit)
.setStubMinLength(0.5);
.setStubMinLength(0.5)
.setSegmentTransform(pipeline);
for (VectorTile.Feature feature : groupedFeatures) {
try {
merger.add(feature.geometry().decode());
Expand Down Expand Up @@ -287,12 +291,14 @@ public static List<VectorTile.Feature> mergeOverlappingPolygons(List<VectorTile.
* @param buffer the amount (in tile pixels) to expand then contract polygons by in order to combine
* almost-touching polygons
* @param stats for counting data errors
* @param pipeline a transform that should be applied to each merged polygon
* @return a new list containing all unaltered features in their original order, then each of the merged groups
* ordered by the index of the first element in that group from the input list.
* @throws GeometryException if an error occurs encoding the combined geometry
*/
public static List<VectorTile.Feature> mergeNearbyPolygons(List<VectorTile.Feature> features, double minArea,
double minHoleArea, double minDist, double buffer, Stats stats) throws GeometryException {
double minHoleArea, double minDist, double buffer, Stats stats, GeometryPipeline pipeline)
throws GeometryException {
List<VectorTile.Feature> result = new ArrayList<>(features.size());
Collection<List<VectorTile.Feature>> groupedByAttrs = groupByAttrs(features, result, GeometryType.POLYGON);
for (List<VectorTile.Feature> groupedFeatures : groupedByAttrs) {
Expand Down Expand Up @@ -326,12 +332,26 @@ public static List<VectorTile.Feature> mergeNearbyPolygons(List<VectorTile.Featu
if (!(merged instanceof Polygonal) || merged.getEnvelopeInternal().getArea() < minArea) {
continue;
}
if (pipeline != null) {
merged = pipeline.apply(merged);
if (!(merged instanceof Polygonal)) {
continue;
}
}
merged = GeoUtils.snapAndFixPolygon(merged, stats, "merge").reverse();
} else {
merged = polygonGroup.getFirst();
if (!(merged instanceof Polygonal) || merged.getEnvelopeInternal().getArea() < minArea) {
continue;
}
if (pipeline != null) {
Geometry after = pipeline.apply(merged);
if (!(after instanceof Polygonal)) {
continue;
} else if (after != merged) {
merged = GeoUtils.snapAndFixPolygon(after, stats, "merge").reverse();
}
}
}
extractPolygons(merged, outPolygons, minArea, minHoleArea);
}
Expand All @@ -354,7 +374,7 @@ private static <G extends Geometry> List<G> sortByHilbertIndex(List<G> geometrie

public static List<VectorTile.Feature> mergeNearbyPolygons(List<VectorTile.Feature> features, double minArea,
double minHoleArea, double minDist, double buffer) throws GeometryException {
return mergeNearbyPolygons(features, minArea, minHoleArea, minDist, buffer, DefaultStats.get());
return mergeNearbyPolygons(features, minArea, minHoleArea, minDist, buffer, DefaultStats.get(), null);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ protected CoordinateSequence transformCoordinates(CoordinateSequence coords, Geo
return coords.copy();
}
for (int iter = 0; iter < iters; iter++) {
assert iter < iters - 1 || (minSquaredVertexTolerance == 0 && minVertexArea == 0) : "reached max iterations";
MutableCoordinateSequence result = new MutableCoordinateSequence();
int last = coords.size() - 1;
double x1, y1;
Expand Down Expand Up @@ -180,7 +181,9 @@ private void squashVertex(MutableCoordinateSequence result, double x1, double y1
double maxDistSquared = Double.POSITIVE_INFINITY;
if (maxArea > 0) {
double sin = den <= 0 ? 0 : Math.abs(((x1 - x2) * (y3 - y2)) - ((y1 - y2) * (x3 - x2))) / den;
maxDistSquared = 2 * maxArea / sin;
if (sin != 0) {
maxDistSquared = 2 * maxArea / sin;
}
}
if (maxSquaredOffset > 0) {
double cos = den <= 0 ? 0 : Math.clamp(((x1 - x2) * (x3 - x2) + (y1 - y2) * (y3 - y2)) / den, -1, 1);
Expand All @@ -194,7 +197,7 @@ private void squashVertex(MutableCoordinateSequence result, double x1, double y1
if (Double.isNaN(maxDist)) {
maxDist = Math.sqrt(maxDistSquared);
}
nextA = maxDist / magA;
nextA = maxDist / magB;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ public class VWSimplifier extends GeometryTransformer implements GeometryPipelin

private double tolerance;
private double k;
private boolean keepCollapsed = false;

/** Sets the minimum effective triangle area created by 3 consecutive vertices in order to retain that vertex. */
public VWSimplifier setTolerance(double tolerance) {
this.tolerance = tolerance;
return this;
}

/** Set to {@code true} to keep polygons with area smaller than {@code tolerance}. */
public VWSimplifier setKeepCollapsed(boolean keepCollapsed) {
this.keepCollapsed = keepCollapsed;
return this;
}

/**
* Apply a penalty from {@code k=0} to {@code k=1} to drop more sharp corners from the resulting geometry.
* <p>
Expand Down Expand Up @@ -72,7 +79,7 @@ public double updateArea() {
@Override
protected CoordinateSequence transformCoordinates(CoordinateSequence coords, Geometry parent) {
boolean area = parent instanceof LinearRing;
int minPoints = area ? 4 : 2;
int minPoints = keepCollapsed && area ? 4 : 2;
int num = coords.size();
if (num <= minPoints) {
return coords;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.onthegomap.planetiler.geo.DouglasPeuckerSimplifier;
import com.onthegomap.planetiler.geo.GeoUtils;
import com.onthegomap.planetiler.geo.GeometryPipeline;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
Expand Down Expand Up @@ -48,6 +49,7 @@ public class LoopLineMerger {
private double stubMinLength = 0.0;
private double tolerance = -1.0;
private boolean mergeStrokes = false;
private GeometryPipeline pipeline;

/**
* Sets the precision model used to snap points to a grid.
Expand Down Expand Up @@ -105,6 +107,16 @@ public LoopLineMerger setTolerance(double tolerance) {
return this;
}

/**
* Sets a function that should be applied to each segment between intersections instead of simplification.
* <p>
* When this is present, {@code tolerance} is ignored.
*/
public LoopLineMerger setSegmentTransform(GeometryPipeline pipeline) {
this.pipeline = pipeline;
return this;
}

/**
* Enables or disables stroke merging. Stroke merging connects the straightest pairs of linestrings at junctions with
* 3 or more attached linestrings based on the angle between them.
Expand Down Expand Up @@ -388,7 +400,7 @@ public List<LineString> getMergedLineStrings() {
// removeShortStubEdges does degreeTwoMerge internally
}

if (tolerance >= 0.0) {
if (pipeline != null || tolerance >= 0.0) {
simplify();
removeDuplicatedEdges();
degreeTwoMerge();
Expand Down Expand Up @@ -585,7 +597,13 @@ void remove() {
}

void simplify() {
coordinates = DouglasPeuckerSimplifier.simplify(coordinates, tolerance, false);
if (pipeline != null) {
coordinates = List.of(
pipeline.apply(GeoUtils.JTS_FACTORY.createLineString(coordinates.toArray(Coordinate[]::new)))
.getCoordinates());
} else if (tolerance >= 0) {
coordinates = DouglasPeuckerSimplifier.simplify(coordinates, tolerance, false);
}
if (reversed != null) {
reversed.coordinates = coordinates.reversed();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.carrotsearch.hppc.IntObjectMap;
import com.onthegomap.planetiler.collection.Hppc;
import com.onthegomap.planetiler.geo.GeometryException;
import com.onthegomap.planetiler.geo.GeometryPipeline;
import com.onthegomap.planetiler.geo.GeometryType;
import com.onthegomap.planetiler.mbtiles.Mbtiles;
import com.onthegomap.planetiler.stats.Stats;
Expand Down Expand Up @@ -373,6 +374,73 @@ void mergeConnectedPolygonsWithSameAttrs() throws GeometryException {
);
}

@Test
void geometryPipelineWhenMergingOverlappingPolygons() throws GeometryException {
List<VectorTile.Feature> features = List.of(
feature(1, rectangle(10, 10, 20, 19), Map.of("a", 1)),
feature(2, rectangle(11, 10, 20, 20), Map.of("a", 1))
);
assertEquivalentFeatures(
List.of(
feature(1, newPolygon(
10, 10,
20, 10,
20, 20,
11, 20,
// remove this point due to simplification: 11, 19,
10, 19,
10, 10
), Map.of("a", 1))
),
FeatureMerge.mergeNearbyPolygons(
features,
0,
0,
0,
1,
Stats.inMemory(),
GeometryPipeline.simplifyVW(1)
)
);
}

@Test
void geometryPipelineAppliedWhenMergingSinglePolygon() throws GeometryException {
List<VectorTile.Feature> features = List.of(
feature(1, newPolygon(
10, 10,
20, 10,
20, 20,
11, 20,
11, 19,
10, 19,
10, 10), Map.of("a", 1))
);
assertEquivalentFeatures(
List.of(
feature(1, newPolygon(
10, 10,
20, 10,
20, 20,
11, 20,
// remove this point due to simplification: 11, 19,
10, 19,
10, 10
), Map.of("a", 1))
),
FeatureMerge.mergeNearbyPolygons(
features,
0,
0,
0,
1,
Stats.inMemory(),
GeometryPipeline.simplifyVW(1)
)
);
}


@Test
void mergeMultiPolygons() throws GeometryException {
assertEquivalentFeatures(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ void testSmoothWithMaxArea(String inWKT, String outWKT) throws ParseException {
"LINESTRING(0 0, 10 10); LINESTRING(0 0, 10 10)",
"LINESTRING(0 0, 10 0, 10 10); LINESTRING (0 0, 9 0, 10 1, 10 10)",
"LINESTRING(0 0, 10 0, 20 0); LINESTRING (0 0, 7.5 0, 12.5 0, 20 0)",
"LINESTRING(0 0, 10 0, 0 0); LINESTRING (0 0, 9.29289 0, 0 0)",
"LINESTRING(0 0, 10 0, 10 5); LINESTRING (0 0, 9 0, 10 1, 10 5)",
"POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)); POLYGON ((1 0, 9 0, 10 1, 10 9, 9 10, 1 10, 0 9, 0 1, 1 0))",
}, delimiter = ';')
void testSmoothWithMaxOffset(String inWKT, String outWKT) throws ParseException {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.onthegomap.planetiler.geo;

import static com.onthegomap.planetiler.TestUtils.assertSameNormalizedFeature;
import static com.onthegomap.planetiler.TestUtils.newLineString;
import static com.onthegomap.planetiler.TestUtils.newPoint;
import static com.onthegomap.planetiler.TestUtils.newPolygon;
import static com.onthegomap.planetiler.TestUtils.*;

import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.Geometry;
Expand Down Expand Up @@ -69,22 +66,26 @@ void testKeepAPoint() {
}

@Test
void testPolygonLeaveAPoint() {
testSimplify(
newPolygon(
0, 0,
10, 10,
9, 10,
0, 8,
0, 0
),
void testPolygonLeaveAPointWhenAreaLessThenTolerance() {
Geometry in = newPolygon(
0, 0,
10, 10,
9, 10,
0, 8,
0, 0
);
assertSameNormalizedFeature(
emptyGeometry(),
new VWSimplifier().setTolerance(200).setWeight(0).transform(in)
);
assertSameNormalizedFeature(
newPolygon(
0, 0,
0, 8,
10, 10,
0, 0
),
200
new VWSimplifier().setTolerance(200).setKeepCollapsed(true).setWeight(0).transform(in)
);
}

Expand Down
Loading

0 comments on commit d482a59

Please sign in to comment.