7
7
8
8
import com .fasterxml .jackson .databind .JsonNode ;
9
9
import com .fasterxml .jackson .databind .ObjectMapper ;
10
- import com .fasterxml .jackson .databind .node .ArrayNode ;
11
10
import com .onthegomap .planetiler .ForwardingProfile ;
12
11
import com .onthegomap .planetiler .VectorTile ;
13
12
import com .onthegomap .planetiler .geo .*;
14
13
import com .onthegomap .planetiler .render .TiledGeometry ;
15
14
import java .io .IOException ;
15
+ import java .nio .file .Files ;
16
+ import java .nio .file .Paths ;
16
17
import java .util .*;
17
18
18
19
import com .onthegomap .planetiler .stats .Stats ;
20
+ import com .protomaps .basemap .GeoJSON ;
19
21
import org .locationtech .jts .geom .*;
20
22
import org .locationtech .jts .geom .util .AffineTransformation ;
21
23
import org .locationtech .jts .operation .overlayng .OverlayNG ;
22
24
import org .locationtech .jts .operation .overlayng .OverlayNGRobust ;
23
25
24
26
public class Clip implements ForwardingProfile .TilePostProcessor {
25
-
26
- private final Map <Integer , Map <TileCoord , List <List <CoordinateSequence >>>> data ;
27
+ private final Map <Integer , Map <TileCoord , List <List <CoordinateSequence >>>> tiledGeometries ;
27
28
private final Map <Integer , TiledGeometry .CoveredTiles > coverings ;
28
29
private final Stats stats ;
29
30
30
- private static Coordinate [] parseCoordinates (ArrayNode coordinateArray ) {
31
- Coordinate [] coordinates = new Coordinate [coordinateArray .size ()];
32
- for (int i = 0 ; i < coordinateArray .size (); i ++) {
33
- ArrayNode coordinate = (ArrayNode ) coordinateArray .get (i );
34
- double x = coordinate .get (0 ).asDouble ();
35
- double y = coordinate .get (1 ).asDouble ();
36
- coordinates [i ] = new Coordinate (x , y );
37
- }
38
- return coordinates ;
39
- }
40
-
41
- public Clip (Geometry input ) {
42
- stats = Stats .inMemory ();
31
+ public Clip (Stats stats , Geometry input ) {
32
+ this .stats = stats ;
43
33
var clipGeometry = latLonToWorldCoords (input ).buffer (0.00001 );
44
- data = new HashMap <>();
34
+ tiledGeometries = new HashMap <>();
45
35
coverings = new HashMap <>();
46
36
try {
47
37
for (var i = 0 ; i <= 15 ; i ++) {
48
38
var extents = TileExtents .computeFromWorldBounds (i , WORLD_BOUNDS );
49
39
double scale = 1 << i ;
50
40
Geometry scaled = AffineTransformation .scaleInstance (scale , scale ).transform (clipGeometry );
51
41
// var simplified = DouglasPeuckerSimplifier.simplify(scaled, 0.25/256);
52
- this .data .put (i , sliceIntoTiles (scaled , 0 , 0.015625 , i , extents .getForZoom (i )).getTileData ());
42
+ this .tiledGeometries .put (i , sliceIntoTiles (scaled , 0 , 0.015625 , i , extents .getForZoom (i )).getTileData ());
53
43
this .coverings .put (i , getCoveredTiles (scaled , i , extents .getForZoom (i )));
54
44
}
55
45
} catch (GeometryException e ) {
56
46
throw new RuntimeException ("Error clipping" );
57
47
}
58
48
}
59
49
60
- public static Clip fromGeoJSON (byte [] bytes ) {
61
- Geometry clipGeometry ;
50
+ public static Clip fromGeoJSONFile (Stats stats , String filename ) {
51
+ try {
52
+ return fromGeoJSON (stats , Files .readAllBytes (Paths .get (filename )));
53
+ } catch (IOException e ) {
54
+ throw new IllegalArgumentException ("Could not open clip file" );
55
+ }
56
+ }
57
+
58
+ public static Clip fromGeoJSON (Stats stats , byte [] bytes ) {
62
59
try {
63
60
ObjectMapper mapper = new ObjectMapper ();
64
61
JsonNode geoJson = mapper .readTree (bytes );
65
- if (geoJson .get ("type" ).asText ().equals ("Polygon" )) {
66
- var coords = geoJson .get ("coordinates" );
67
- ArrayNode outerRingNode = (ArrayNode ) coords .get (0 );
68
- Coordinate [] outerRingCoordinates = parseCoordinates (outerRingNode );
69
- LinearRing outerRing = GeoUtils .JTS_FACTORY .createLinearRing (outerRingCoordinates );
70
-
71
- LinearRing [] innerRings = new LinearRing [coords .size () - 1 ];
72
- for (int j = 1 ; j < coords .size (); j ++) {
73
- ArrayNode innerRingNode = (ArrayNode ) coords .get (j );
74
- Coordinate [] innerRingCoordinates = parseCoordinates (innerRingNode );
75
- innerRings [j - 1 ] = GeoUtils .JTS_FACTORY .createLinearRing (innerRingCoordinates );
76
- }
77
-
78
- clipGeometry = (GeoUtils .JTS_FACTORY .createPolygon (outerRing , innerRings ));
79
- return new Clip (clipGeometry );
80
- }
62
+ return new Clip (stats , GeoJSON .parseGeometry (geoJson ));
81
63
} catch (IOException e ) {
82
- throw new RuntimeException ( e );
64
+ throw new IllegalArgumentException ( "Clip GeoJSON is invalid" );
83
65
}
84
- throw new RuntimeException ();
85
66
}
86
67
68
+ // Copied from elsewhere in planetiler
87
69
private static Polygon reassemblePolygon (List <CoordinateSequence > group ) throws GeometryException {
88
70
try {
89
71
LinearRing first = GeoUtils .JTS_FACTORY .createLinearRing (group .getFirst ());
@@ -99,6 +81,7 @@ private static Polygon reassemblePolygon(List<CoordinateSequence> group) throws
99
81
}
100
82
}
101
83
84
+ // Copied from elsewhere in Planetiler
102
85
static Geometry reassemblePolygons (List <List <CoordinateSequence >> groups ) throws GeometryException {
103
86
int numGeoms = groups .size ();
104
87
if (numGeoms == 1 ) {
@@ -117,10 +100,10 @@ public Map<String, List<VectorTile.Feature>> postProcessTile(TileCoord tileCoord
117
100
Map <String , List <VectorTile .Feature >> map ) throws GeometryException {
118
101
if (this .coverings .containsKey (tileCoord .z ()) &&
119
102
this .coverings .get (tileCoord .z ()).test (tileCoord .x (), tileCoord .y ())) {
120
- if (this .data .containsKey (tileCoord .z ()) && this .data .get (tileCoord .z ()).containsKey (tileCoord )) {
121
- List <List <CoordinateSequence >> coords = data .get (tileCoord .z ()).get (tileCoord );
103
+ if (this .tiledGeometries .containsKey (tileCoord .z ()) && this .tiledGeometries .get (tileCoord .z ()).containsKey (tileCoord )) {
104
+ List <List <CoordinateSequence >> coords = tiledGeometries .get (tileCoord .z ()).get (tileCoord );
122
105
var clipGeometry = reassemblePolygons (coords );
123
- var clipGeometry2 = GeoUtils .snapAndFixPolygon (clipGeometry , stats , "render" );
106
+ var clipGeometry2 = GeoUtils .fixPolygon (clipGeometry );
124
107
clipGeometry2 .reverse ();
125
108
Map <String , List <VectorTile .Feature >> output = new HashMap <>();
126
109
@@ -131,7 +114,7 @@ public Map<String, List<VectorTile.Feature>> postProcessTile(TileCoord tileCoord
131
114
var newGeom = OverlayNGRobust .overlay (feature .geometry ().decode (), clipGeometry2 , OverlayNG .INTERSECTION );
132
115
if (!newGeom .isEmpty () && newGeom .getNumGeometries () > 0 ) {
133
116
if (newGeom instanceof Polygonal ) {
134
- newGeom = GeoUtils .snapAndFixPolygon (newGeom , stats , "render " );
117
+ newGeom = GeoUtils .snapAndFixPolygon (newGeom , stats , "clip " );
135
118
newGeom = newGeom .reverse ();
136
119
if (!newGeom .isEmpty () && newGeom .getNumGeometries () > 0 ) {
137
120
if (newGeom instanceof GeometryCollection ) {
0 commit comments