/// <summary> /// Called when the mapview has changed. /// </summary> /// <param name="map"></param> /// <param name="zoomFactor"></param> /// <param name="center"></param> /// <param name="view"></param> public void ViewChanged(Map map, float zoomFactor, GeoCoordinate center, View2D view) { // calculate the current zoom level. var zoomLevel = (int)System.Math.Round(map.Projection.ToZoomLevel(zoomFactor), 0); // build the boundingbox. var box = new GeoCoordinateBox( map.Projection.ToGeoCoordinates(view.Left, view.Top), map.Projection.ToGeoCoordinates(view.Right, view.Bottom)); // build the tile range. TileRange range = TileRange.CreateAroundBoundingBox(box, zoomLevel); DateTime now = DateTime.Now; // build the new scene. Scene2D newScene = new Scene2D(); if (_connection.State == System.Data.ConnectionState.Closed) { _connection.Open(); } lock (_connection) { // make sure the connection is accessed synchronously. // TODO: Investigate the SQLite multithreaded behaviour.. // TODO: this a very naive way of loading these tiles. Find a way to query SQLite more efficiently // TODO: find a way to have some cached tiles. foreach (var tile in range) { Tile invertTile = tile.InvertY(); SQLiteCommand command = new SQLiteCommand("SELECT * FROM tiles WHERE zoom_level = :zoom_level AND tile_column = :tile_column AND tile_row = :tile_row;", _connection); command.Parameters.AddWithValue("zoom_level", invertTile.Zoom); command.Parameters.AddWithValue("tile_column", invertTile.X); command.Parameters.AddWithValue("tile_row", invertTile.Y); using (var tileReader = command.ExecuteReader()) { while (tileReader.Read()) { //Tile readTile = new Tile((int)tileReader["tile_column"], // (int)tileReader["tile_row"], (int)tileReader["zoom_level"]); float minZoom = (float)map.Projection.ToZoomFactor(tile.Zoom - 0.5f); float maxZoom = (float)map.Projection.ToZoomFactor(tile.Zoom + 0.5f); float left = (float)map.Projection.LongitudeToX(tile.TopLeft.Longitude); float right = (float)map.Projection.LongitudeToX(tile.BottomRight.Longitude); float bottom = (float)map.Projection.LatitudeToY(tile.BottomRight.Latitude); float top = (float)map.Projection.LatitudeToY(tile.TopLeft.Latitude); newScene.AddImage(0, minZoom, maxZoom, left, top, right, bottom, (byte[])tileReader["tile_data"]); } } } this.Scene = newScene; } }
/// <summary> /// Render the current complete scene. /// </summary> void Render() { if (_cacheRenderer.IsRunning) { _cacheRenderer.CancelAndWait(); } if (_rect == null) // only render if a proper size is known. { return; } lock (_cacheRenderer) { // make sure only on thread at the same time is using the renderer. double extra = 1.25; // build the layers list. var layers = new List <ILayer> (); for (int layerIdx = 0; layerIdx < this.Map.LayerCount; layerIdx++) { layers.Add(this.Map [layerIdx]); } // add the internal layer. // TODO: create marker layer. // create a new bitmap context. CGColorSpace space = CGColorSpace.CreateDeviceRGB(); int bytesPerPixel = 4; int bytesPerRow = bytesPerPixel * (int)_rect.Width; int bitsPerComponent = 8; if (_bytescache == null) { _bytescache = new byte[bytesPerRow * (int)_rect.Height]; } CGBitmapContext gctx = new CGBitmapContext(null, (int)_rect.Width, (int)_rect.Height, bitsPerComponent, bytesPerRow, space, // kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast CGBitmapFlags.PremultipliedFirst | CGBitmapFlags.ByteOrder32Big); // create the view. View2D view = _cacheRenderer.Create(_rect.Width, _rect.Height, this.Map, (float)this.Map.Projection.ToZoomFactor(this.MapZoomLevel), this.MapCenter, false, true); long before = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Rendering Start"); // notify the map that the view has changed. this.Map.ViewChanged((float)this.Map.Projection.ToZoomFactor(this.MapZoomLevel), this.MapCenter, view); long afterViewChanged = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "View change took: {0}ms @ zoom level {1}", (new TimeSpan(afterViewChanged - before).TotalMilliseconds), this.MapZoomLevel); // // add the current canvas to the scene. // uint canvasId = _scene.AddImage (-1, float.MinValue, float.MaxValue, // view.Left, view.Top, view.Right, view.Bottom, new byte[0], _canvasBitmap); // does the rendering. bool complete = _cacheRenderer.Render(new CGContextWrapper(gctx, new RectangleF(0, 0, _rect.Width, _rect.Height)), layers, view); long afterRendering = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Rendering took: {0}ms @ zoom level {1}", (new TimeSpan(afterRendering - afterViewChanged).TotalMilliseconds), this.MapZoomLevel); if (complete) // there was no cancellation, the rendering completely finished. // add the result to the scene cache. { lock (_cachedScene) { // add the newly rendered image again. //this.Layer.Contents = gctx.ToImage (); BoxF2D rectangle = view.OuterBox; _cachedScene.Clear(); _cachedScene.AddImage(0, float.MinValue, float.MaxValue, rectangle.Min[0], rectangle.Min[1], rectangle.Max[0], rectangle.Max[1], new byte[0], gctx.ToImage()); // _cachedScene.AddImage (0, float.MinValue, float.MaxValue, // view.Left, view.Top, view.Right, view.Bottom, new byte[0], _layer); } this.InvokeOnMainThread(Test); } long after = DateTime.Now.Ticks; if (!complete) { OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Rendering in {0}ms after cancellation!", new TimeSpan(after - before).TotalMilliseconds); } else { OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Rendering in {0}ms", new TimeSpan(after - before).TotalMilliseconds); } } }
/// <summary> /// Renders the current complete scene. /// </summary> void Render() { if (_cacheRenderer.IsRunning) { _cacheRenderer.CancelAndWait(); } // make sure only on thread at the same time is using the renderer. lock (_cacheRenderer) { double extra = 1.25; // build the layers list. var layers = new List <ILayer> (); for (int layerIdx = 0; layerIdx < this.Map.LayerCount; layerIdx++) { // get the layer. layers.Add(this.Map[layerIdx]); } // add the internal layers. layers.Add(_makerLayer); // create a new cache if size has changed. if (_canvasBitmap == null || _canvasBitmap.Width != (int)(this.Width * extra) || _canvasBitmap.Height != (int)(this.Height * extra)) { // create a bitmap and render there. _canvasBitmap = global::Android.Graphics.Bitmap.CreateBitmap((int)(this.Width * extra), (int)(this.Height * extra), global::Android.Graphics.Bitmap.Config.Argb8888); } else { // clear the cache??? } // create and reset the canvas. global::Android.Graphics.Canvas canvas = new global::Android.Graphics.Canvas(_canvasBitmap); canvas.DrawColor(new global::Android.Graphics.Color( SimpleColor.FromKnownColor(KnownColor.Transparent).Value)); // create the view. View2D view = _cacheRenderer.Create(canvas.Width, canvas.Height, this.Map, (float)this.Map.Projection.ToZoomFactor(this.MapZoomLevel), this.MapCenter, _invertX, _invertY); long before = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Rendering Start"); // notify the map that the view has changed. this.Map.ViewChanged((float)this.Map.Projection.ToZoomFactor(this.MapZoomLevel), this.MapCenter, view); long afterViewChanged = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "View change took: {0}ms @ zoom level {1}", (new TimeSpan(afterViewChanged - before).TotalMilliseconds), this.MapZoomLevel); // add the current canvas to the scene. double left = view.LeftTop [0]; double right = view.RightTop [0]; double top = view.LeftTop [1]; double bottom = view.LeftBottom [1]; uint canvasId = _scene.AddImage(-1, float.MinValue, float.MaxValue, left, top, right, bottom, new byte[0], _canvasBitmap); // does the rendering. bool complete = _cacheRenderer.Render(canvas, layers, view); long afterRendering = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Rendering took: {0}ms @ zoom level {1}", (new TimeSpan(afterRendering - afterViewChanged).TotalMilliseconds), this.MapZoomLevel); if (complete) { // there was no cancellation, the rendering completely finished. // add the result to the scene cache. lock (_scene) { // if (_previousCache.HasValue) { // _scene.Remove (_previousCache.Value); // } // _scene.Remove (canvasId); // // add the newly rendered image again. _scene.Clear(); //_previousCache = BoxF2D viewBox = view.OuterBox; _scene.AddImage(0, float.MinValue, float.MaxValue, viewBox.Min[0], viewBox.Min[1], viewBox.Max[0], viewBox.Max[1], new byte[0], _canvasBitmap); // switch cache and canvas to prevent re-allocation of bitmaps. global::Android.Graphics.Bitmap newCanvas = _cache; _cache = _canvasBitmap; _canvasBitmap = newCanvas; } } this.PostInvalidate(); long after = DateTime.Now.Ticks; if (!complete) { OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Rendering in {0}ms after cancellation!", new TimeSpan(after - before).TotalMilliseconds); } else { OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Rendering in {0}ms", new TimeSpan(after - before).TotalMilliseconds); } } }
/// <summary> /// Translates a node. /// </summary> /// <param name="scene">The scene to add primitives to.</param> /// <param name="projection">The projection used to convert the objects.</param> /// <param name="node"></param> private void TranslateNode(Scene2D scene, IProjection projection, CompleteNode node) { // build the rules. IEnumerable <MapCSSRuleProperties> rules = this.BuildRules(new MapCSSObject(node)); // interpret the results. foreach (var rule in rules) { int zIndex; if (!rule.TryGetProperty <int>("zIndex", out zIndex)) { zIndex = 0; } float minZoom = (float)projection.ToZoomFactor(rule.MinZoom); float maxZoom = (float)projection.ToZoomFactor(rule.MaxZoom); uint?pointId = null; int color; if (rule.TryGetProperty <int>("color", out color)) { float width; if (rule.TryGetProperty <float>("width", out width)) { pointId = scene.AddPoint(projection.LongitudeToX(node.Coordinate.Longitude), projection.LatitudeToY(node.Coordinate.Latitude)); scene.AddStylePoint(pointId.Value, this.CalculateSceneLayer(OffsetPoint, zIndex), minZoom, maxZoom, color, width); } else { pointId = scene.AddPoint(projection.LongitudeToX(node.Coordinate.Longitude), projection.LatitudeToY(node.Coordinate.Latitude)); scene.AddStylePoint(pointId.Value, this.CalculateSceneLayer(OffsetPoint, zIndex), minZoom, maxZoom, color, 1); } } byte[] iconImage; if (rule.TryGetProperty("iconImage", out iconImage)) { if (!pointId.HasValue) { pointId = scene.AddPoint(projection.LongitudeToX(node.Coordinate.Longitude), projection.LatitudeToY(node.Coordinate.Latitude)); } // an icon is to be drawn! ushort imageId = scene.AddImage(iconImage); scene.AddIcon(pointId.Value, this.CalculateSceneLayer(OffsetPoint, zIndex), minZoom, maxZoom, imageId); } string text; if (rule.TryGetProperty("text", out text)) { int textColor; if (!rule.TryGetProperty("textColor", out textColor)) { textColor = SimpleColor.FromKnownColor(KnownColor.Black).Value; } int haloColor; int?haloColorNullable = null; if (rule.TryGetProperty("textHaloColor", out haloColor)) { haloColorNullable = haloColor; } int haloRadius; int?haloRadiusNullable = null; if (rule.TryGetProperty("textHaloRadius", out haloRadius)) { haloRadiusNullable = haloRadius; } int fontSize; if (!rule.TryGetProperty("fontSize", out fontSize)) { fontSize = 10; } string fontFamily; if (!rule.TryGetProperty("fontFamily", out fontFamily)) { fontFamily = "Arial"; // just some default font. } // a text is to be drawn. string value; if (node.Tags.TryGetValue(text, out value)) { if (!pointId.HasValue) { pointId = scene.AddPoint(projection.LongitudeToX(node.Coordinate.Longitude), projection.LatitudeToY(node.Coordinate.Latitude)); } scene.AddText(pointId.Value, this.CalculateSceneLayer(OffsetPointText, zIndex), minZoom, maxZoom, fontSize, value, textColor, haloColorNullable, haloRadiusNullable, fontFamily); } } } }
/// <summary> /// Render the current complete scene. /// </summary> void Render() { try { RectangleF rect = _rect; lock (this.Map) { //lock (_cacheRenderer) { // make sure only on thread at the same time is using the renderer. // create the view. View2D view = _cacheRenderer.Create((int)(rect.Width * _extra), (int)(rect.Height * _extra), this.Map, (float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, _invertX, _invertY, this.MapTilt); if (view.Equals(_previousRenderedZoom)) { return; } if (rect.Width == 0) // only render if a proper size is known. { return; } OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Before lock."); long before = DateTime.Now.Ticks; // OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, // "Rendering Start"); // build the layers list. var layers = new List <ILayer> (); for (int layerIdx = 0; layerIdx < this.Map.LayerCount; layerIdx++) { layers.Add(this.Map [layerIdx]); } // add the internal layer. // TODO: create marker layer. int imageWidth = (int)(rect.Width * _extra * _scaleFactor); int imageHeight = (int)(rect.Height * _extra * _scaleFactor); // create a new bitmap context. CGColorSpace space = CGColorSpace.CreateDeviceRGB(); int bytesPerPixel = 4; int bytesPerRow = bytesPerPixel * imageWidth; int bitsPerComponent = 8; if (_bytescache == null) { _bytescache = new byte[bytesPerRow * imageHeight]; } CGBitmapContext gctx = new CGBitmapContext(null, imageWidth, imageHeight, bitsPerComponent, bytesPerRow, space, // kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast CGBitmapFlags.PremultipliedFirst | CGBitmapFlags.ByteOrder32Big); // notify the map that the view has changed. this.Map.ViewChanged((float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, view); long afterViewChanged = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "View change took: {0}ms @ zoom level {1}", (new TimeSpan(afterViewChanged - before).TotalMilliseconds), this.MapZoom); // does the rendering. bool complete = _cacheRenderer.Render(new CGContextWrapper(gctx, new RectangleF(0, 0, (int)(rect.Width * _extra), (int)(rect.Height * _extra))), layers, view); long afterRendering = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Rendering took: {0}ms @ zoom level {1}", (new TimeSpan(afterRendering - afterViewChanged).TotalMilliseconds), this.MapZoom); if (complete) // there was no cancellation, the rendering completely finished. // add the result to the scene cache. { lock (_cachedScene) { // add the newly rendered image again. _cachedScene.Clear(); _cachedScene.AddImage(0, float.MinValue, float.MaxValue, view.Rectangle, new byte[0], gctx.ToImage()); } this.InvokeOnMainThread(InvalidateMap); // store the previous view. _previousRenderedZoom = view; } long after = DateTime.Now.Ticks; // if (!complete) { // OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information,"Rendering CANCELLED!", // new TimeSpan (after - before).TotalMilliseconds); // } else { OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", System.Diagnostics.TraceEventType.Information, "Rendering in {0}ms", new TimeSpan(after - before).TotalMilliseconds); // } //} } } catch (Exception ex) { _cacheRenderer.Reset(); } }