private void CanvasControl_Draw(CanvasControl sender, CanvasDrawEventArgs args) { if (currentTile == null) { args.DrawingSession.Clear(Windows.UI.Colors.CornflowerBlue); } else { if (GeometryDecoder.renderList != null) { System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); //var size = sender.Size; //args.DrawingSession.Transform = System.Numerics.Matrix3x2.CreateRotation(angle, new System.Numerics.Vector2((float)size.Width/2f, (float)size.Height/2f)); args.DrawingSession.Transform = System.Numerics.Matrix3x2.CreateScale(tileScale, canvasScalePoint) * System.Numerics.Matrix3x2.CreateTranslation(canvasOffset); args.DrawingSession.DrawImage(GeometryDecoder.renderList); GeometryDecoder.ApplyTextLayer(args.DrawingSession); args.DrawingSession.DrawImage(GeometryDecoder.renderText); stopwatch.Stop(); System.Diagnostics.Debug.WriteLine($"Render time: {stopwatch.ElapsedMilliseconds} ms"); } } }
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); }
private async void Map_MapTapped(int tileX, int tileY, int zoomLevel) { float canvasScale = 1f; float canvasSize = 768; float lineScale = 1f; //int zoomLevel = Math.Min(14, Math.Max(1, (int)sender.ZoomLevel)); //var tileAddr = MapUtil.WorldToTilePos(args.Location.Position.Longitude, args.Location.Position.Latitude, zoomLevel); System.Diagnostics.Debug.WriteLine($"{tileX} x {tileY}"); System.Diagnostics.Debug.WriteLine($"zoom: {zoomLevel}"); { var reader = await db.GetTile((int)tileX, (int)tileY, zoomLevel); if (!reader.HasRows) { textOutput.Text = DateTime.Now + ": No data"; } var tile = VectorTile.Parse(reader); if (tile.tile_data_raw != null) { PBF pbf = new PBF(tile.tile_data_raw); currentTile = ProtoBuf.Serializer.Deserialize <Tile>(new MemoryStream(tile.tile_data_raw)); if (GeometryDecoder.CanvasTileId != tile.id) { GeometryDecoder.CanvasTileId = tile.id; if (GeometryDecoder.offscreen == null) { CanvasDevice device = CanvasDevice.GetSharedDevice(); GeometryDecoder.offscreen = new CanvasRenderTarget(device, canvasSize, canvasSize, Windows.Graphics.Display.DisplayInformation.GetForCurrentView().LogicalDpi *canvasScale); //GeometryDecoder.offscreenText = new CanvasRenderTarget(device, canvasSize, canvasSize, Windows.Graphics.Display.DisplayInformation.GetForCurrentView().LogicalDpi * canvasScale); } //Performance benchmark Tile.Layer layer_buildings = null; Tile.Layer layer_landcover = null; Tile.Layer layer_transportation = null; Tile.Layer layer_transportation_name = null; Tile.Layer layer_housenumber = null; Tile.Layer layer_poi = null; if (currentTile.Layers.Any(l => l.Name == "building")) { layer_buildings = currentTile.Layers.Where(l => l.Name == "building").ToList()?.First(); } if (currentTile.Layers.Any(l => l.Name == "landcover")) { layer_landcover = currentTile.Layers.Where(l => l.Name == "landcover").ToList()?.First(); } if (currentTile.Layers.Any(l => l.Name == "transportation")) { layer_transportation = currentTile.Layers.Where(l => l.Name == "transportation").ToList()?.First(); } if (currentTile.Layers.Any(l => l.Name == "transportation_name")) { layer_transportation_name = currentTile.Layers.Where(l => l.Name == "transportation_name").ToList()?.First(); } if (currentTile.Layers.Any(l => l.Name == "housenumber")) { layer_housenumber = currentTile.Layers.Where(l => l.Name == "housenumber").ToList()?.First(); } if (currentTile.Layers.Any(l => l.Name == "poi")) { layer_poi = currentTile.Layers.Where(l => l.Name == "poi").ToList()?.First(); } System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); Dictionary <string, Windows.UI.Color> landColors = new Dictionary <string, Windows.UI.Color>(); landColors.Add("grass", Windows.UI.Colors.LawnGreen); landColors.Add("meadow", Windows.UI.Colors.Green); landColors.Add("wood", Windows.UI.Colors.ForestGreen); landColors.Add("forest", Windows.UI.Colors.DarkGreen); landColors.Add("park", Windows.UI.Colors.LightGreen); landColors.Add("village_green", Windows.UI.Colors.GreenYellow); landColors.Add("wetland", Windows.UI.Colors.CornflowerBlue); landColors.Add("recreation_ground", Windows.UI.Colors.LightYellow); landColors.Add("allotments", Windows.UI.Colors.Red); Dictionary <string, Tuple <Color, float, Color, float> > roadProperties = new Dictionary <string, Tuple <Color, float, Color, float> >(); roadProperties.Add("transit", new Tuple <Color, float, Color, float>(Colors.Black, 0.5f, Colors.Black, 0.5f)); roadProperties.Add("primary", new Tuple <Color, float, Color, float>(Colors.LightYellow, 3, Colors.SandyBrown, 4.5f)); roadProperties.Add("secondary", new Tuple <Color, float, Color, float>(Colors.LightYellow, 2, Colors.SandyBrown, 3f)); roadProperties.Add("tertiary", new Tuple <Color, float, Color, float>(Colors.LightYellow, 2, Colors.SandyBrown, 3f)); roadProperties.Add("minor", new Tuple <Color, float, Color, float>(Colors.WhiteSmoke, 1.8f, Colors.Gray, 2.5f)); roadProperties.Add("service", new Tuple <Color, float, Color, float>(Colors.WhiteSmoke, 1.8f, Colors.Gray, 2.5f)); roadProperties.Add("track", new Tuple <Color, float, Color, float>(Colors.LightGray, 1, Colors.Gray, 2)); roadProperties.Add("path", new Tuple <Color, float, Color, float>(Colors.LightGray, 1, Colors.Gray, 2)); roadProperties.Add("rail", new Tuple <Color, float, Color, float>(Colors.Gainsboro, 0.75f, Colors.DimGray, 1.4f)); roadProperties.Add("motorway", new Tuple <Color, float, Color, float>(Colors.Orange, 3, Colors.Red, 4.5f)); roadProperties.Add("trunk", new Tuple <Color, float, Color, float>(Colors.Orange, 3, Colors.Red, 4.5f)); GeometryDecoder.renderList = new CanvasCommandList(GeometryDecoder.offscreen); GeometryDecoder.renderText = new CanvasCommandList(GeometryDecoder.offscreen); //using (CanvasDrawingSession ds = GeometryDecoder.offscreen.CreateDrawingSession()) using (CanvasDrawingSession textDs = GeometryDecoder.renderText.CreateDrawingSession()) using (CanvasDrawingSession ds = GeometryDecoder.renderList.CreateDrawingSession()) using (CanvasActiveLayer activeLayer = ds.CreateLayer(1, new Rect(0, 0, canvasSize, canvasSize))) using (CanvasActiveLayer activeTextLayer = textDs.CreateLayer(1, new Rect(0, 0, canvasSize, canvasSize))) { ds.Antialiasing = CanvasAntialiasing.Antialiased; ds.Clear(Windows.UI.Colors.Snow); for (int it = 0; it < 1; it++) { layer_landcover?.Features.ForEach(f => { var tags = GeometryDecoder.GetTags(f, layer_landcover); var color = Colors.LightGreen; if (tags.ContainsKey("subclass")) { color = landColors.ContainsKey(tags["subclass"].StringValue) ? landColors[tags["subclass"].StringValue] : Windows.UI.Colors.LightGreen; } GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, color, color); }); layer_buildings?.Features.ForEach(f => { var tags = GeometryDecoder.GetTags(f, layer_buildings); GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, Windows.UI.Colors.SandyBrown, Windows.UI.Colors.Brown); }); List <string> tagsNames = new List <string>(); var names = layer_transportation?.Features.ConvertAll <Tuple <string, string> >(f => { var tags = GeometryDecoder.GetTags(f, layer_transportation); foreach (var t in tags) { tagsNames.Add(t.Key); } return(new Tuple <string, string>(tags.ContainsKey("class") ? tags["class"].StringValue : "", tags.ContainsKey("subclass") ? tags["subclass"].StringValue : "")); }).ToList().Distinct().ToList(); var distinctTagsNames = tagsNames.Distinct().ToList(); Tuple <Color, float, Color, float> rc = new Tuple <Color, float, Color, float>(Colors.Pink, 2, Colors.DeepPink, 2); Dictionary <int, List <Tile.Feature> > transportationFeatures = new Dictionary <int, List <Tile.Feature> >(); foreach (var f in layer_transportation.Features) { var tags = GeometryDecoder.GetTags(f, layer_transportation); int layer = tags.ContainsKey("layer") ? (int)tags["layer"].IntValue : 0; if (!transportationFeatures.ContainsKey(layer)) { transportationFeatures.Add(layer, new List <Tile.Feature>()); } transportationFeatures[layer].Add(f); } foreach (int layerNo in transportationFeatures.Keys.OrderBy(k => k)) { //background / stroke pass foreach (var f in transportationFeatures[layerNo]) { var tags = GeometryDecoder.GetTags(f, layer_transportation); if (tags.ContainsKey("class")) { if (roadProperties.ContainsKey(tags["class"].StringValue)) { var p = roadProperties[tags["class"].StringValue]; GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, p.Item1, p.Item3, 0, p.Item4 * lineScale, layerNo > 0 ? GeometryDecoder.openStrokeStyle : GeometryDecoder.normalStrokeStyle); } else { GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, rc.Item1, rc.Item3, 0, rc.Item4 * lineScale, GeometryDecoder.normalStrokeStyle); } } else { GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, rc.Item1, rc.Item3, 0, rc.Item4 * lineScale, GeometryDecoder.normalStrokeStyle); } } //foreground / fill pass foreach (var f in transportationFeatures[layerNo]) { var tags = GeometryDecoder.GetTags(f, layer_transportation); if (tags.ContainsKey("class")) { if (roadProperties.ContainsKey(tags["class"].StringValue)) { var p = roadProperties[tags["class"].StringValue]; if (tags["class"].StringValue == "rail" || tags["class"].StringValue == "transit") { GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, p.Item1, p.Item3, p.Item2 * lineScale, 0, GeometryDecoder.railStrokeStyle); // System.Diagnostics.Debug.WriteLine(t) } else { GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, p.Item1, p.Item3, p.Item2 * lineScale, 0, GeometryDecoder.normalStrokeStyle); } } else { System.Diagnostics.Debug.WriteLine($"Unsupported class: {tags["class"]}"); GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, rc.Item1, rc.Item3, rc.Item2 * lineScale, 0, GeometryDecoder.normalStrokeStyle); } } else { GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, rc.Item1, rc.Item3, rc.Item2 * lineScale, 0, GeometryDecoder.normalStrokeStyle); } } } // layer_transportation?.Features.ForEach(f => // { // var tags = GeometryDecoder.GetTags(f, layer_transportation); // if (tags.ContainsKey("class")) // { // if (roadProperties.ContainsKey(tags["class"].StringValue)) // { // var p = roadProperties[tags["class"].StringValue]; // GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, p.Item1, p.Item3, 0, p.Item4 * lineScale); // } // else // { // GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, rc.Item1, rc.Item3, 0, rc.Item4 * lineScale); // } // } // else // GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, rc.Item1, rc.Item3, 0, rc.Item4 * lineScale); // }); // layer_transportation?.Features.ForEach(f => // { // var tags = GeometryDecoder.GetTags(f, layer_transportation); // if (tags.ContainsKey("class")) // { // if (roadProperties.ContainsKey(tags["class"].StringValue)) // { // var p = roadProperties[tags["class"].StringValue]; // if (tags["class"].StringValue == "rail") // { // GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, p.Item1, p.Item3, p.Item2 * lineScale, 0, GeometryDecoder.railStrokeStyle); //// System.Diagnostics.Debug.WriteLine(t) // } // else // GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, p.Item1, p.Item3, p.Item2 * lineScale, 0); // } // else // { // System.Diagnostics.Debug.WriteLine($"Unsupported class: {tags["class"]}"); // GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, rc.Item1, rc.Item3, rc.Item2 * lineScale, 0); // } // } // else // GeometryDecoder.DrawGeometry(f, canvasSize / 4096f, ds, rc.Item1, rc.Item3, rc.Item2 * lineScale, 0); // }); layer_housenumber?.Features.ForEach(f => { var tags = GeometryDecoder.GetTags(f, layer_housenumber); string text = tags["housenumber"].StringValue; //GeometryDecoder.DrawHousenumber(f, canvasSize / 4096f, textDs, Colors.Black, Colors.White, 1, 1.2f, text); GeometryDecoder.DrawText(f, canvasSize / 4096f, textDs, Colors.Black, Colors.White, 1, 1.2f, text, 2f); }); layer_poi?.Features.ForEach(f => { var tags = GeometryDecoder.GetTags(f, layer_poi); if (tags.ContainsKey("subclass")) { string subclass = tags["subclass"].StringValue; if (icons.ContainsKey(subclass + "_11")) { GeometryDecoder.DrawIcon(f, canvasSize / 4096f, ds, icons[subclass + "_11"], Colors.Black, Colors.DarkGray); } } }); layer_transportation_name?.Features.ForEach(f => { var tags = GeometryDecoder.GetTags(f, layer_transportation_name); if (tags.ContainsKey("name")) { string text = tags["name"].StringValue; GeometryDecoder.DrawText(f, canvasSize / 4096f, textDs, Colors.Black, Colors.White, 1, 1.2f, text, 3f); } }); } } //StorageFolder storageFolder = KnownFolders.PicturesLibrary; //StorageFile file = await storageFolder.CreateFileAsync("map.bmp", CreationCollisionOption.ReplaceExisting); //var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite); //await GeometryDecoder.offscreen.SaveAsync(stream, CanvasBitmapFileFormat.Bmp); //stream.Dispose(); stopwatch.Stop(); System.Diagnostics.Debug.WriteLine($"TIME: {stopwatch.ElapsedMilliseconds} ms"); textOutput.Text = $"TIME: {stopwatch.ElapsedMilliseconds} ms"; } } else { currentTile = null; } } win2dCanvas.Invalidate(); }
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: if (decoder.AdvanceCommand() && decoder.Command == CommandType.MoveTo) { for (int i = 0; i < decoder.Repeat; i++) { decoder.AdvanceCursor(); handler.OnPoint(decoder.CurrentPoint()); } } break; case PbfGeomType.LineString: while (decoder.AdvanceCommand() && (decoder.Command == CommandType.MoveTo && decoder.Repeat == 1)) { decoder.AdvanceCursor(); if (decoder.AdvanceCommand() && (decoder.Command == CommandType.LineTo && decoder.Repeat > 0)) { handler.OnBeginLineString(); handler.OnPoint(decoder.CurrentPoint()); for (int i = 0; i < decoder.Repeat; i++) { decoder.AdvanceCursor(); handler.OnPoint(decoder.CurrentPoint()); } handler.OnEndLineString(); } } break; case PbfGeomType.Polygon: while (decoder.AdvanceCommand() && (decoder.Command == CommandType.MoveTo && decoder.Repeat == 1)) { handler.OnBeginPolygon(); decoder.AdvanceCursor(); if (decoder.AdvanceCommand() && (decoder.Command == CommandType.LineTo && decoder.Repeat > 0)) { handler.OnBeginLinearRing(); var start = decoder.CurrentPoint(); handler.OnPoint(start); for (int i = 0; i < decoder.Repeat; i++) { decoder.AdvanceCursor(); handler.OnPoint(decoder.CurrentPoint()); } handler.OnPoint(start); handler.OnEndLinearRing(); } if (decoder.AdvanceCommand() && decoder.Command == CommandType.ClosePath) { // We should assert that this condition holds. } handler.OnEndPolygon(); } break; case PbfGeomType.Unknown: break; } return(true); }