public override bool HandleGeometry(IGeometryHandler handler) { var decoder = new GeometryDecoder(feature, layer); // From https://github.com/mapbox/vector-tile-spec/tree/master/2.1 // // The POINT geometry type encodes a point or multipoint geometry. The geometry command sequence for a point // geometry MUST consist of a single MoveTo command with a command count greater than 0. // // The LINESTRING geometry type encodes a linestring or multilinestring geometry. The geometry command sequence // for a linestring geometry MUST consist of one or more repetitions of the following sequence: // 1. A MoveTo command with a command count of 1 // 2. A LineTo command with a command count greater than 0 // // The POLYGON geometry type encodes a polygon or multipolygon geometry, each polygon consisting of exactly one // exterior ring that contains zero or more interior rings. The geometry command sequence for a polygon consists // of one or more repetitions of the following sequence: // 1. An ExteriorRing // 2. Zero or more InteriorRings // Each ExteriorRing and InteriorRing MUST consist of the following sequence: // 1. A MoveTo command with a command count of 1 // 2. A LineTo command with a command count greater than 1 // 3. A ClosePath command switch (feature.Type) { case PbfGeomType.Point: while (decoder.HasData()) { decoder.AdvanceCommand(); Debug.Assert(decoder.Command == CommandType.MoveTo && decoder.Repeat > 0); for (int i = decoder.Repeat; i > 0; i--) { handler.OnPoint(decoder.AdvanceCursor()); } } break; case PbfGeomType.LineString: while (decoder.HasData()) { handler.OnBeginLineString(); decoder.AdvanceCommand(); Debug.Assert(decoder.Command == CommandType.MoveTo && decoder.Repeat == 1); handler.OnPoint(decoder.AdvanceCursor()); decoder.AdvanceCommand(); Debug.Assert(decoder.Command == CommandType.LineTo && decoder.Repeat > 0); for (int i = decoder.Repeat; i > 0; i--) { handler.OnPoint(decoder.AdvanceCursor()); } handler.OnEndLineString(); } break; case PbfGeomType.Polygon: // Create temporary storage to hold rings until we determine whether they are interior or exterior. var ring = new List <Point>(); var isPolygonStarted = false; while (decoder.HasData()) { // Read out the coordinates of the next ring. decoder.AdvanceCommand(); Debug.Assert(decoder.Command == CommandType.MoveTo && decoder.Repeat == 1); ring.Add(decoder.AdvanceCursor()); decoder.AdvanceCommand(); for (int i = decoder.Repeat; i > 0; i--) { ring.Add(decoder.AdvanceCursor()); } ring.Add(ring.First()); decoder.AdvanceCommand(); Debug.Assert(decoder.Command == CommandType.ClosePath && decoder.Repeat == 1); // If ring is exterior, end the current polygon, start a new one, add ring to new polygon. // If ring is interior, add the ring to current polygon. var area = SignedArea(ring); if (area > 0) { if (isPolygonStarted) { handler.OnEndPolygon(); } handler.OnBeginPolygon(); isPolygonStarted = true; } handler.OnBeginLinearRing(); foreach (var point in ring) { handler.OnPoint(point); } handler.OnEndLinearRing(); ring.Clear(); } handler.OnEndPolygon(); break; case PbfGeomType.Unknown: return(false); } return(true); }