kopia lustrzana https://github.com/onthegomap/planetiler
add detailed jts debugging info
rodzic
9f960022b8
commit
1b6618cb15
|
|
@ -19,6 +19,7 @@ import java.util.LinkedHashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import org.geotools.geometry.jts.WKTWriter2;
|
||||
import org.locationtech.jts.algorithm.Area;
|
||||
import org.locationtech.jts.geom.CoordinateSequence;
|
||||
import org.locationtech.jts.geom.Envelope;
|
||||
|
|
@ -28,6 +29,7 @@ import org.locationtech.jts.geom.LineString;
|
|||
import org.locationtech.jts.geom.LinearRing;
|
||||
import org.locationtech.jts.geom.Polygon;
|
||||
import org.locationtech.jts.geom.Polygonal;
|
||||
import org.locationtech.jts.geom.TopologyException;
|
||||
import org.locationtech.jts.index.strtree.STRtree;
|
||||
import org.locationtech.jts.operation.buffer.BufferOp;
|
||||
import org.locationtech.jts.operation.buffer.BufferParameters;
|
||||
|
|
@ -410,7 +412,7 @@ public class FeatureMerge {
|
|||
* Merges nearby polygons by expanding each individual polygon by {@code buffer}, unioning them, and contracting the
|
||||
* result.
|
||||
*/
|
||||
private static Geometry bufferUnionUnbuffer(double buffer, List<Geometry> polygonGroup) {
|
||||
private static Geometry bufferUnionUnbuffer(double buffer, List<Geometry> polygonGroup) throws GeometryException {
|
||||
/*
|
||||
* A simpler alternative that might initially appear faster would be:
|
||||
*
|
||||
|
|
@ -424,11 +426,27 @@ public class FeatureMerge {
|
|||
* The following approach is slower most of the time, but faster on average because it does
|
||||
* not choke on dense nearby polygons:
|
||||
*/
|
||||
for (int i = 0; i < polygonGroup.size(); i++) {
|
||||
polygonGroup.set(i, buffer(buffer, polygonGroup.get(i)));
|
||||
List<Geometry> buffered = new ArrayList<>(polygonGroup.size());
|
||||
for (Geometry geometry : polygonGroup) {
|
||||
buffered.add(buffer(buffer, geometry));
|
||||
}
|
||||
Geometry merged = GeoUtils.createGeometryCollection(buffered);
|
||||
try {
|
||||
merged = union(merged);
|
||||
} catch (TopologyException e) {
|
||||
throw new GeometryException("buffer_union_failure", "Error unioning buffered polygons", e).addDetails(() -> {
|
||||
var wktWriter = new WKTWriter2();
|
||||
return """
|
||||
Original polygons: %s
|
||||
Buffer: %f
|
||||
Buffered: %s
|
||||
""".formatted(
|
||||
wktWriter.write(GeoUtils.createGeometryCollection(polygonGroup)),
|
||||
buffer,
|
||||
wktWriter.write(GeoUtils.createGeometryCollection(buffered))
|
||||
);
|
||||
});
|
||||
}
|
||||
Geometry merged = GeoUtils.createGeometryCollection(polygonGroup);
|
||||
merged = union(merged);
|
||||
merged = unbuffer(buffer, merged);
|
||||
return merged;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ public final class FeatureGroup implements Iterable<FeatureGroup.TileFeatures>,
|
|||
// log failures, only throwing when it's a fatal error
|
||||
if (e instanceof GeometryException geoe) {
|
||||
geoe.log(stats, "postprocess_layer",
|
||||
"Caught error postprocessing features for " + layer + " layer on " + tileCoord);
|
||||
"Caught error postprocessing features for " + layer + " layer on " + tileCoord, config.logJtsExceptions());
|
||||
} else if (e instanceof Error err) {
|
||||
LOGGER.error("Caught fatal error postprocessing features {} {}", layer, tileCoord, e);
|
||||
throw err;
|
||||
|
|
|
|||
|
|
@ -58,7 +58,8 @@ public record PlanetilerConfig(
|
|||
String debugUrlPattern,
|
||||
Path tmpDir,
|
||||
Path tileWeights,
|
||||
double maxPointBuffer
|
||||
double maxPointBuffer,
|
||||
boolean logJtsExceptions
|
||||
) {
|
||||
|
||||
public static final int MIN_MINZOOM = 0;
|
||||
|
|
@ -208,7 +209,8 @@ public record PlanetilerConfig(
|
|||
"Max tile pixels to include points outside tile bounds. Set to a lower value to reduce tile size for " +
|
||||
"clients that handle label collisions across tiles (most web and native clients). NOTE: Do not reduce if you need to support " +
|
||||
"raster tile rendering",
|
||||
Double.POSITIVE_INFINITY)
|
||||
Double.POSITIVE_INFINITY),
|
||||
arguments.getBoolean("log_jts_exceptions", "Emit verbose details to debug JTS geometry errors", false)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.onthegomap.planetiler.geo;
|
||||
|
||||
import com.onthegomap.planetiler.stats.Stats;
|
||||
import java.util.function.Supplier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
|
@ -14,6 +15,7 @@ public class GeometryException extends Exception {
|
|||
|
||||
private final String stat;
|
||||
private final boolean nonFatal;
|
||||
private Supplier<String> detailsSupplier;
|
||||
|
||||
/**
|
||||
* Constructs a new exception with a detailed error message caused by {@code cause}.
|
||||
|
|
@ -51,6 +53,11 @@ public class GeometryException extends Exception {
|
|||
this.nonFatal = nonFatal;
|
||||
}
|
||||
|
||||
public GeometryException addDetails(Supplier<String> detailsSupplier) {
|
||||
this.detailsSupplier = detailsSupplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Returns the unique code for this error condition to use for counting the number of occurrences in stats. */
|
||||
public String stat() {
|
||||
return stat;
|
||||
|
|
@ -72,6 +79,17 @@ public class GeometryException extends Exception {
|
|||
assert nonFatal : log; // make unit tests fail if fatal
|
||||
}
|
||||
|
||||
|
||||
/** Logs the error but if {@code logDetails} is true, then also prints detailed debugging info. */
|
||||
public void log(Stats stats, String statPrefix, String logPrefix, boolean logDetails) {
|
||||
if (logDetails && detailsSupplier != null) {
|
||||
stats.dataError(statPrefix + "_" + stat());
|
||||
logMessage(logPrefix + ": " + getMessage() + "\n" + detailsSupplier.get());
|
||||
} else {
|
||||
log(stats, statPrefix, logPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An error that we expect to encounter often so should only be logged at {@code TRACE} level.
|
||||
*/
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue