Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional pre triangulation for polygons when creating MLT #383

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

FF-98
Copy link
Contributor

@FF-98 FF-98 commented Nov 13, 2024

Summary

  • add option to triangulate polygons using earcut when converting from MVT to MLT

Adjustments in:

  • MltConverter
  • GeometryEncoder
  • VectorizedGeometryDecoder

@nyurik nyurik requested review from mactrem, springmeyer and msbarry and removed request for springmeyer November 14, 2024 19:27
@@ -27,11 +27,13 @@ dependencies {
implementation 'org.apache.orc:orc-core:1.8.1'
implementation 'com.github.davidmoten:hilbert-curve:0.2.3'
implementation 'com.carrotsearch:hppc:0.10.0'
implementation "io.github.earcut4j:earcut4j:2.2.2"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already depend on JTS, which has a few built-in polygon triangulation utilities: https://github.com/locationtech/jts/tree/master/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon - and I think PolygonEarClipper is similar to earcut. Do you have any quality/performance comparison of those vs. earcut4j? If earcut4j performs better then adding a new dependency seems fine, but if not I'd default to the dependencies we already have.

@@ -266,7 +272,8 @@ private static byte[] encodePropertyColumns(
FeatureTableOptimizations featureTableOptimizations,
List<Feature> sortedFeatures,
List<Feature> mvtFeatures,
PhysicalLevelTechnique physicalLevelTechnique) {
PhysicalLevelTechnique physicalLevelTechnique,
Boolean triangulatePolygons) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a primitive boolean here

@@ -33,12 +34,15 @@ private GeometryEncoder() {}
public static EncodedGeometryColumn encodeGeometryColumn(
List<Geometry> geometries,
PhysicalLevelTechnique physicalLevelTechnique,
SortSettings sortSettings) {
SortSettings sortSettings,
boolean triangulatePolygons) {
Copy link
Collaborator

@msbarry msbarry Nov 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make it possible this take the triangulated polygons as an input to this function instead of embedding the triangulation inside of it?

On planetiler I'm considering some options for fixing invalid polygons that produce triangulated geometries as an intermediate step and I'd like for it to at least be possible to pass those triangulated polygons into this routine without forcing them to be computed again.

import java.util.ArrayList;
import java.util.List;

public class TriangulatedPolygon {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading build.gradle correctly, it looks like we're compiling against java 17. If that's right could this class just be record TriangulatedPolygon(List<Integer> indexBuffer, int numTrianglesPerPolygon) {} ?

Comment on lines +84 to +89
var geometry = feature.getGeometry().toString();
if (geometry.contains(Geometry.TYPENAME_MULTIPOLYGON.toUpperCase())) {
triangulateMultiPolygon((MultiPolygon) feature.getGeometry());
} else if (geometry.contains(Geometry.TYPENAME_POLYGON.toUpperCase())) {
triangulatePolygon((Polygon) feature.getGeometry());
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change these places that switch based on type of geometry to:

Suggested change
var geometry = feature.getGeometry().toString();
if (geometry.contains(Geometry.TYPENAME_MULTIPOLYGON.toUpperCase())) {
triangulateMultiPolygon((MultiPolygon) feature.getGeometry());
} else if (geometry.contains(Geometry.TYPENAME_POLYGON.toUpperCase())) {
triangulatePolygon((Polygon) feature.getGeometry());
}
if (feature.getGeometry() instanceof MultiPolygon multiPolygon) {
triangulateMultiPolygon(multiPolygon);
} else if (feature.getGeometry() instanceof Polygon polygon) {
triangulateMultiPolygon(polygon);
}

public class TriangulationUtils {
private TriangulationUtils() {}

public static TriangulatedPolygon triangulatePolygon(Polygon polygon) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Polygon can have holes, and a MultiPolygon just contains many Polygons (which may each have holes). Can we change this to handle those cases and use the poly.getExteriorRing() poly.getNumInteriorRing() poly.getInteriorRingN(int n) helpers? Let's add some tests for polygons with holes as well.

// assertion: first polygon defines the outer linear ring and the other polygons define its
// holes!
if (i == 0) {
holeIndex = multiPolygon.getGeometryN(i).getCoordinates().length;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each call to getCoordinates may be transforming a packed double[] CoordinateSequence to a Coordinate[] array. Is it possible to just extract the CoordinateSequence once and use double getX(int idx), double getY(int idx) and double getZ(int idx) (only when coordinateSequence.getDimension() > 2)

Comment on lines +64 to +68
when(innerPolygon.getCoordinates()).thenReturn(innerPolygonCoordinates);

when(multiPolygon.getNumGeometries()).thenReturn(2);
when(multiPolygon.getGeometryN(0)).thenReturn(outerPolygon);
when(multiPolygon.getGeometryN(1)).thenReturn(innerPolygon);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest just creating the actual MultiPolygon/Polygon classes instead of using mocks. One easy way is to use new WKTReader().read("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))") to decode the well-known-text representation of a geometry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants