11package geoscript.layer.io
22
3- import org.locationtech.jts.algorithm.CGAlgorithms
3+ import no.ecc.vectortile.VectorTileDecoder
4+ import no.ecc.vectortile.VectorTileEncoder
5+ import org.geotools.geometry.jts.JTS
6+ import org.geotools.referencing.operation.transform.ProjectiveTransform
7+ import org.geotools.renderer.lite.RendererUtilities
48import org.locationtech.jts.geom.Coordinate
59import org.locationtech.jts.geom.Envelope
610import org.locationtech.jts.geom.Geometry as JtsGeometry
711import org.locationtech.jts.geom.GeometryFactory
8- import org.locationtech.jts.geom.LinearRing
9- import org.locationtech.jts.geom.Polygon
10- import com.wdtinc.mapbox_vector_tile.VectorTile
11- import com.wdtinc.mapbox_vector_tile.adapt.jts.IGeometryFilter
12- import com.wdtinc.mapbox_vector_tile.adapt.jts.JtsAdapter
13- import com.wdtinc.mapbox_vector_tile.adapt.jts.TagKeyValueMapConverter
14- import com.wdtinc.mapbox_vector_tile.adapt.jts.TileGeomResult
15- import com.wdtinc.mapbox_vector_tile.adapt.jts.UserDataKeyValueMapConverter
16- import com.wdtinc.mapbox_vector_tile.adapt.jts.model.JtsLayer
17- import com.wdtinc.mapbox_vector_tile.adapt.jts.model.JtsMvt
18- import com.wdtinc.mapbox_vector_tile.adapt.jts.MvtReader as JtsMvtReader
19- import com.wdtinc.mapbox_vector_tile.build.MvtLayerBuild
20- import com.wdtinc.mapbox_vector_tile.build.MvtLayerParams
21- import com.wdtinc.mapbox_vector_tile.build.MvtLayerProps
2212import geoscript.feature.Feature
2313import geoscript.feature.Field
2414import geoscript.feature.Schema
@@ -30,6 +20,9 @@ import geoscript.proj.Projection
3020import geoscript.workspace.Memory
3121import org.geotools.renderer.crs.ProjectionHandler
3222import org.geotools.renderer.crs.ProjectionHandlerFinder
23+ import org.opengis.referencing.operation.MathTransform
24+
25+ import java.awt.Rectangle
3326
3427/**
3528 * A MapBox Vector Tile Reader and Writer
@@ -47,42 +40,39 @@ class Pbf {
4740 * @param b The Bounds
4841 */
4942 static List<Layer > read (Map options = [:], byte [] bytes , Bounds b ) {
43+
44+ ProjectionHandler projectionHandler = ProjectionHandlerFinder . getHandler(b. env, b. proj. crs, true )
5045 Projection proj = options. get(" proj" , new Projection (" EPSG:3857" ))
5146 int tileSize = options. get(" tileSize" , 256 )
5247 int extent = options. get(" extent" , 4096 )
53- List<Layer > layers = []
54- GeometryFactory geometryFactory = new GeometryFactory ()
55- ByteArrayInputStream inputStream = new ByteArrayInputStream (bytes)
56- JtsMvt jtsMvt = JtsMvtReader . loadMvt(inputStream, geometryFactory, new TagKeyValueMapConverter (), new NonValidatingRingClassifier ())
5748
58- jtsMvt . layers . each { JtsLayer jtsLayer ->
59- String name = jtsLayer . name
60- Layer layer
61- Schema schema
62- jtsLayer . geometries . eachWithIndex { JtsGeometry jtsGeometry , int i ->
63- Geometry geometry = fromPixel(Geometry . wrap(jtsGeometry ), b, tileSize, extent)
64- ProjectionHandler projectionHandler = ProjectionHandlerFinder . getHandler(b . env, b . proj . crs, true )
49+ Map< String , Layer > layers = [:]
50+ VectorTileDecoder vectorTileDecoder = new VectorTileDecoder ()
51+ VectorTileDecoder.FeatureIterable features = vectorTileDecoder . decode(bytes)
52+ features . iterator() . each { VectorTileDecoder.Feature vectorTileFeature ->
53+ String layerName = vectorTileFeature . layerName
54+ Geometry geometry = fromPixel(Geometry . wrap(vectorTileFeature . geometry ), b, tileSize, extent)
55+ if ( ! geometry . isEmpty()) {
6556 if (projectionHandler) {
6657 JtsGeometry processedGeom = projectionHandler. preProcess(geometry. g)
6758 if (processedGeom) {
6859 geometry = Geometry . wrap(processedGeom)
6960 }
7061 }
71- Map properties = jtsGeometry. userData as Map
72- properties. put(" geometry" , geometry)
73- Feature feature
74- if (! schema) {
75- feature = new Feature (properties, " 1" )
76- schema = new Schema (name, feature. schema. fields). reproject(proj)
77- layer = new Memory (). create(schema)
78- } else {
79- feature = schema. feature(properties)
62+ Map<String , Object > attributes = [geometry : geometry]
63+ attributes. putAll(vectorTileFeature. attributes)
64+ if (! layers. containsKey(layerName)) {
65+ Feature feature = new Feature (attributes, " 1" )
66+ Schema schema = new Schema (layerName, feature. schema. fields). reproject(proj)
67+ layers[layerName] = new Memory (). create(schema)
8068 }
69+ Layer layer = layers[layerName]
70+ Feature feature = layer. schema. feature(attributes)
8171 layer. add(feature)
8272 }
83- layers. add(layer)
8473 }
85- layers
74+
75+ layers. values(). toList()
8676 }
8777
8878 /**
@@ -126,34 +116,27 @@ class Pbf {
126116 final double bufferHeight = b. env. height * 0.1f
127117 clipEnvelope. expandBy(bufferWidth, bufferHeight)
128118
129- VectorTile.Tile.Builder tileBuilder = VectorTile.Tile . newBuilder()
130- MvtLayerParams mvtParams = MvtLayerParams . DEFAULT
131- GeometryFactory geometryFactory = new GeometryFactory ()
132- IGeometryFilter geometryFilter = new IGeometryFilter () {
133- @Override
134- boolean accept (org.locationtech.jts.geom.Geometry geometry ) {
135- true
136- }
137- }
138-
119+ VectorTileEncoder encoder = new VectorTileEncoder ()
139120 layers. each { Layer layer ->
140121 Bounds projectedBounds = b. reproject(layer. proj)
141122 projectedBounds. expandBy(projectedBounds. width * 0.1f )
142123 Geometry boundsGeom = projectedBounds. geometry
143124 boolean isPoint = layer. schema. geom. typ. equalsIgnoreCase(" point" )
144- VectorTile.Tile.Layer.Builder layerBuilder = MvtLayerBuild . newLayerBuilder(layer. name, mvtParams)
145- MvtLayerProps layerProps = new MvtLayerProps ()
125+ // Intersects
146126 List<JtsGeometry > geometries = []
147127 layer. eachFeature(Filter . intersects(layer. schema. geom. name, boundsGeom), { Feature f ->
128+ // Clip
148129 Geometry geom = isPoint ? f. geom : f. geom. intersection(boundsGeom)
149130 if (! geom. empty) {
131+ // Process
150132 ProjectionHandler projectionHandler = ProjectionHandlerFinder . getHandler(b. env, layer. proj. crs, true )
151133 if (projectionHandler) {
152134 JtsGeometry processedGeom = projectionHandler. preProcess(geom. g)
153135 if (processedGeom) {
154136 geom = Geometry . wrap(processedGeom)
155137 }
156138 }
139+ // Reproject
157140 JtsGeometry geometry = Projection . transform(geom, layer. proj, b. proj). g
158141 Map attributes = [:]
159142 layer. schema. fields. each { Field fld ->
@@ -168,72 +151,16 @@ class Pbf {
168151 }
169152 }
170153 geometry. userData = attributes
171-
172- TileGeomResult tileGeom = JtsAdapter . createTileGeom([geometry], b. env, clipEnvelope, geometryFactory, mvtParams, geometryFilter);
173- geometries. addAll(tileGeom. mvtGeoms)
154+ // To Screen
155+ MathTransform worldToScreenTransform = ProjectiveTransform . create(RendererUtilities . worldToScreenTransform(b. env, new Rectangle (0 ,0 , tileSize, tileSize)))
156+ geometry = JTS . transform(geometry, worldToScreenTransform)
157+ // Encode
158+ encoder. addFeature(layer. name, attributes, geometry)
174159 }
175160 })
176-
177- List<VectorTile.Tile.Feature > features = JtsAdapter . toFeatures(geometries, layerProps, new UserDataKeyValueMapConverter ())
178- layerBuilder. addAllFeatures(features);
179- MvtLayerBuild . writeProps(layerBuilder, layerProps);
180-
181- VectorTile.Tile.Layer vtLayer = layerBuilder. build()
182- tileBuilder. addLayers(vtLayer)
183- }
184-
185- VectorTile.Tile tile = tileBuilder. build()
186- byte [] bytes = tile. toByteArray()
187- bytes
188- }
189-
190- private static final class NonValidatingRingClassifier implements com.wdtinc.mapbox_vector_tile.adapt.jts.MvtReader. RingClassifier {
191-
192- @Override
193- public List<Polygon > classifyRings (List<LinearRing > rings , GeometryFactory geomFactory ) {
194-
195- final List<Polygon > polygons = new ArrayList<> ();
196- final List<LinearRing > holes = new ArrayList<> ();
197-
198- double outerArea = 0d ;
199- LinearRing outerPoly = null ;
200-
201- for (LinearRing r : rings) {
202-
203- double area = CGAlgorithms . signedArea(r. getCoordinates());
204-
205- if (area == 0d ) {
206- continue ; // zero-area
207- }
208-
209- if (area > 0d ) {
210- if (outerPoly != null ) {
211- polygons. add(geomFactory. createPolygon(outerPoly, holes. toArray(new LinearRing [holes. size()])));
212- holes. clear();
213- }
214-
215- // Pos --> CCW, Outer
216- outerPoly = r;
217- outerArea = area;
218- } else {
219-
220- if (Math . abs(outerArea) < Math . abs(area)) {
221- continue ; // Holes must have less area, could probably be handled in a isSimple() check
222- }
223-
224- // Neg --> CW, Hole
225- holes. add(r);
226- }
227- }
228-
229- if (outerPoly != null ) {
230- holes. toArray();
231- polygons. add(geomFactory. createPolygon(outerPoly, holes. toArray(new LinearRing [holes. size()])));
232- }
233-
234- return polygons;
235161 }
236162
163+ encoder. encode()
237164 }
238165
239166}
0 commit comments