From 71b1856a12185255502c39afd961d79faca42284 Mon Sep 17 00:00:00 2001 From: nalo Date: Thu, 24 Feb 2022 16:01:22 +0900 Subject: [PATCH] feat(encoding/mvt): support mvt marshal for GeometryCollection --- encoding/mvt/marshal.go | 55 +++++++++++++++++++--------- encoding/mvt/marshal_test.go | 71 +++++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 18 deletions(-) diff --git a/encoding/mvt/marshal.go b/encoding/mvt/marshal.go index 73ac401..1c89294 100644 --- a/encoding/mvt/marshal.go +++ b/encoding/mvt/marshal.go @@ -4,6 +4,7 @@ import ( "bytes" "compress/gzip" "fmt" + "github.com/paulmach/orb" "strconv" "github.com/paulmach/orb/encoding/mvt/vectortile" @@ -46,6 +47,8 @@ func Marshal(layers Layers) ([]byte, error) { for _, l := range layers { v := l.Version e := l.Extent + + kve := newKeyValueEncoder() layer := &vectortile.Tile_Layer{ Name: &l.Name, Version: &v, @@ -53,24 +56,10 @@ func Marshal(layers Layers) ([]byte, error) { Features: make([]*vectortile.Tile_Feature, 0, len(l.Features)), } - kve := newKeyValueEncoder() - for i, f := range l.Features { - t, g, err := encodeGeometry(f.Geometry) - if err != nil { - return nil, fmt.Errorf("layer %s: feature %d: error encoding geometry: %v", l.Name, i, err) - } - - tags, err := encodeProperties(kve, f.Properties) - if err != nil { - return nil, fmt.Errorf("layer %s: feature %d: error encoding properties: %v", l.Name, i, err) + for _, f := range l.Features { + if err := addFeature(layer, kve, f); err != nil { + return nil, err } - - layer.Features = append(layer.Features, &vectortile.Tile_Feature{ - Id: convertID(f.ID), - Tags: tags, - Type: &t, - Geometry: g, - }) } layer.Keys = kve.Keys @@ -82,6 +71,38 @@ func Marshal(layers Layers) ([]byte, error) { return proto.Marshal(vt) } +func addFeature(layer *vectortile.Tile_Layer, kve *keyValueEncoder, f *geojson.Feature) error { + if f.Geometry.GeoJSONType() == "GeometryCollection" { + for _, g := range f.Geometry.(orb.Collection) { + return addSingleGeometryFeature(layer, kve, g, f.Properties, f.ID) + } + } + return addSingleGeometryFeature(layer, kve, f.Geometry, f.Properties, f.ID) +} + +func addSingleGeometryFeature(layer *vectortile.Tile_Layer, kve *keyValueEncoder, g orb.Geometry, p geojson.Properties, id interface{}) error { + geomType, encodedGeometry, err := encodeGeometry(g) + if err != nil { + return err + } + if err != nil { + return fmt.Errorf("error encoding geometry: %v", g) + } + + tags, err := encodeProperties(kve, p) + if err != nil { + return fmt.Errorf("error encoding geometry: %v", g) + } + + layer.Features = append(layer.Features, &vectortile.Tile_Feature{ + Id: convertID(id), + Tags: tags, + Type: &geomType, + Geometry: encodedGeometry, + }) + return nil +} + func encodeProperties(kve *keyValueEncoder, properties geojson.Properties) ([]uint32, error) { tags := make([]uint32, 0, 2*len(properties)) for k, v := range properties { diff --git a/encoding/mvt/marshal_test.go b/encoding/mvt/marshal_test.go index 2ff804b..bd1d1d7 100644 --- a/encoding/mvt/marshal_test.go +++ b/encoding/mvt/marshal_test.go @@ -13,7 +13,7 @@ import ( "github.com/paulmach/orb/maptile" ) -func TestMarshalMarshalGzipped_Full(t *testing.T) { +func TestMarshalUnmarshalGzipped_Full(t *testing.T) { tile := maptile.New(8956, 12223, 15) ls := orb.LineString{ {-81.60346275, 41.50998572}, @@ -68,6 +68,75 @@ func TestMarshalMarshalGzipped_Full(t *testing.T) { compareOrbGeometry(t, result.Geometry, expected, xe, ye) } +func TestMarshalUnmarshalForGeometryCollection(t *testing.T) { + tile := maptile.New(8956, 12223, 15) + outerRing := orb.Ring{ + {-81.6033000, 41.5099000}, + {-81.6033000, 41.5094000}, + {-81.6039000, 41.5094000}, + {-81.6039000, 41.5099000}, + {-81.6033000, 41.5099000}, + } + hole := orb.Ring{ + {-81.60389989614487, 41.50941085679876}, + {-81.60329908132553, 41.50941085679876}, + {-81.60329908132553, 41.50990496161759}, + {-81.60389989614487, 41.50990496161759}, + {-81.60389989614487, 41.50941085679876}, + } + outerPolygon := orb.Polygon{outerRing, hole} + polygonInHole := orb.Polygon{orb.Ring{ + {-81.60375505685806, 41.5095494475553}, + {-81.6034385561943, 41.5095494475553}, + {-81.6034385561943, 41.50978043149024}, + {-81.60375505685806, 41.50978043149024}, + {-81.60375505685806, 41.5095494475553}, + }} + geometryCollection := orb.Collection{outerPolygon, polygonInHole} + expected := []orb.Geometry{outerPolygon.Clone(), outerPolygon.Clone()} + + f := geojson.NewFeature(geometryCollection) + f.Properties = geojson.Properties{ + "id": float64(246698394), + } + + fc := geojson.NewFeatureCollection() + fc.Append(f) + + layers := Layers{NewLayer("roads", fc)} + + // project to the tile coords + layers.ProjectToTile(tile) + + // marshal + encoded, err := MarshalGzipped(layers) + if err != nil { + t.Fatalf("marshal error: %v", err) + } + + // unmarshal + decoded, err := UnmarshalGzipped(encoded) + if err != nil { + t.Fatalf("unmarshal error: %v", err) + } + + // project back + decoded.ProjectToWGS84(tile) + + // compare the results + results := decoded[0].Features + compareProperties(t, results[0].Properties, f.Properties) + + // compare geometry + xe, ye := tileEpsilon(tile) + if len(results) == len(expected) { + t.Errorf("result geometry count must be splited polygon: %v (but result is %v)", len(results), len(expected)) + } + for i, result := range results { + compareOrbGeometry(t, result.Geometry, expected[i], xe, ye) + } +} + func TestMarshalUnmarshal(t *testing.T) { cases := []struct { name string