/// <summary> /// Adds the image. /// </summary> /// <returns>The image.</returns> /// <param name="layer">Layer.</param> /// <param name="minZoom">Minimum zoom.</param> /// <param name="maxZoom">Max zoom.</param> /// <param name="rectangle">Rectangle.</param> /// <param name="imageData">Image data.</param> /// <param name="tag">Tag.</param> public override uint AddImage(int layer, float minZoom, float maxZoom, RectangleF2D rectangle, byte[] imageData, object tag) { if (imageData == null) { throw new ArgumentNullException("imageData"); } lock (_primitives) { uint id = _nextId; _nextId++; var imageTilted2D = new ImageTilted2D(rectangle, imageData, minZoom, maxZoom); imageTilted2D.Tag = tag; this.AddPrimitive(layer, id, imageTilted2D); return(id); } }
/// <summary> /// Diposes of all resources associated with this object. /// </summary> /// <param name="disposing"></param> protected override void Dispose(bool disposing) { if (disposing == true) { //someone wants the deterministic release of all resources //Let us release all the managed resources } else { // Do nothing, no one asked a dispose, the object went out of // scope and finalized is called so lets next round of GC // release these resources } // Release the unmanaged resource in any case as they will not be // released by GC this._cacheRenderer = null; if (this._offScreenBuffer != null) { // dispose of the map view surface. this._offScreenBuffer.Dispose(); this._offScreenBuffer = null; } if (this._onScreenBuffer != null) { // dispose of the map view surface. this._onScreenBuffer.Dispose(); this._onScreenBuffer = null; } if (this._mapViewAnimator != null) { _mapViewAnimator.Stop(); _mapViewAnimator = null; } if (this._map != null) { this._map = null; } this._scaleGestureDetector = null; this._tagGestureDetector = null; this._moveGestureDetector = null; this._rotateGestureDetector = null; }
/// <summary> /// Renders the primities for the given scene. /// </summary> /// <param name="target"></param> /// <param name="view"></param> /// <param name="zoomFactor"></param> /// <param name="primitives"></param> private bool RenderPrimitives(Target2DWrapper <TTarget> target, View2D view, float zoomFactor, IEnumerable <Primitive2D> primitives) { try { // calculate current simplification epsilon. double epsilon = Scene2D.CalculateSimplificationEpsilon(new WebMercator(), zoomFactor); // loop over all primitives in the scene. int simplifiedLines = 0; int droppedLines = 0; foreach (Primitive2D primitive in primitives) { // the primitive is visible. if (_cancelFlag) { return(false); // stop rendering on cancel and return false for an incomplete rendering. } if (primitive == null) { continue; } double[] x, y; switch (primitive.Primitive2DType) { case Primitive2DType.Line2D: Line2D line = (Line2D)primitive; x = line.X; y = line.Y; if (x.Length > 4 && line.MaxZoom > zoomFactor * 2 && line.MaxZoom < 512) { // try and simplify. double[][] simplified = OsmSharp.Math.Algorithms.SimplifyCurve.Simplify(new double[][] { x, y }, epsilon); if (simplified[0].Length < line.X.Length) { simplifiedLines++; x = simplified[0]; y = simplified[1]; } double distance = epsilon * 2; if (simplified[0].Length == 2) { // check if the simplified version is smaller than epsilon. distance = System.Math.Sqrt( System.Math.Pow((simplified[0][0] - simplified[0][1]), 2) + System.Math.Pow((simplified[1][0] - simplified[1][1]), 2)); } if (distance < epsilon) { droppedLines++; continue; } } this.DrawLine(target, x, y, line.Color, this.FromPixels(target, view, line.Width), line.LineJoin, line.Dashes); break; case Primitive2DType.Polygon2D: Polygon2D polygon = (Polygon2D)primitive; x = polygon.X; y = polygon.Y; //if (x.Length > 4 && polygon.MaxZoom > zoomFactor * 2 && polygon.MaxZoom < 512) //{ // try and simplify. // double[][] simplified = OsmSharp.Math.Algorithms.SimplifyCurve.Simplify(new double[][] { x, y }, // epsilon); // if (simplified[0].Length < polygon.X.Length) // { // simplifiedLines++; // x = simplified[0]; // y = simplified[1]; // } // double distance = epsilon * 2; // if (simplified[0].Length == 2) // { // check if the simplified version is smaller than epsilon. // distance = System.Math.Sqrt( // System.Math.Pow((simplified[0][0] - simplified[0][1]), 2) + // System.Math.Pow((simplified[1][0] - simplified[1][1]), 2)); // } // //if (distance < epsilon) // //{ // // droppedLines++; // // continue; // //} //} this.DrawPolygon(target, x, y, polygon.Color, this.FromPixels(target, view, polygon.Width), polygon.Fill); break; case Primitive2DType.LineText2D: LineText2D lineText = (LineText2D)primitive; this.DrawLineText(target, lineText.X, lineText.Y, lineText.Text, lineText.Color, this.FromPixels(target, view, lineText.Size), lineText.HaloColor, lineText.HaloRadius, lineText.Font); break; case Primitive2DType.Point2D: Point2D point = (Point2D)primitive; this.DrawPoint(target, point.X, point.Y, point.Color, this.FromPixels(target, view, point.Size)); break; case Primitive2DType.Icon2D: Icon2D icon = (Icon2D)primitive; this.DrawIcon(target, icon.X, icon.Y, icon.Image); break; case Primitive2DType.ImageTilted2D: ImageTilted2D imageTilted = (ImageTilted2D)primitive; this.DrawImage(target, imageTilted.Bounds, imageTilted.NativeImage); break; case Primitive2DType.Image2D: Image2D image = (Image2D)primitive; this.DrawImage(target, image.Left, image.Top, image.Right, image.Bottom, image.NativeImage); break; case Primitive2DType.Text2D: Text2D text = (Text2D)primitive; this.DrawText(target, text.X, text.Y, text.Text, text.Color, this.FromPixels(target, view, text.Size), text.HaloColor, text.HaloRadius, text.Font); break; } } return(true); } catch (Exception ex) { OsmSharp.Logging.Log.TraceEvent("Renderer2D", OsmSharp.Logging.TraceEventType.Error, ex.Message); throw ex; } }
/// <summary> /// Renders the current complete scene. /// </summary> private void Render() { try { if (_renderingSuspended) { // no rendering when rendering is suspended. return; } if (_cacheRenderer.IsRunning) { // cancel previous render. _cacheRenderer.CancelAndWait(); } // make sure only on thread at the same time is using the renderer. lock (_cacheRenderer) { this.Map.ViewChangedCancel(); // build the layers list. var layers = new List <Layer>(); for (int layerIdx = 0; layerIdx < this.Map.LayerCount; layerIdx++) { // get the layer. if (this.Map[layerIdx].IsVisible) { layers.Add(this.Map[layerIdx]); } } // add the internal layers. layers.Add(_makerLayer); if (this.SurfaceHeight == 0) { // the surface has no height yet. Impossible to render like this. return; } // get old image if available. NativeImage image = null; if (_offScreenBuffer != null) { // get the native image from the off-screen buffer. image = _offScreenBuffer.NativeImage as NativeImage; } // resize image if needed. float sizeX = this.SurfaceWidth; float sizeY = this.SurfaceHeight; //if(this.MapAllowTilt) //{ // when rotation is allowed make sure a square is rendered. // sizeX = System.Math.Max(this.SurfaceWidth, this.SurfaceHeight); // sizeY = System.Math.Max(this.SurfaceWidth, this.SurfaceHeight); //} // float size = System.Math.Max(this.SurfaceHeight, this.SurfaceWidth); if (image == null || image.Image.Width != (int)(sizeX * _extra) || image.Image.Height != (int)(sizeY * _extra)) { // create a bitmap and render there. if (image != null) { // make sure to dispose the old image. image.Dispose(); } image = new NativeImage(global::Android.Graphics.Bitmap.CreateBitmap((int)(sizeX * _extra), (int)(sizeY * _extra), global::Android.Graphics.Bitmap.Config.Argb8888)); } // create and reset the canvas. using (var canvas = new global::Android.Graphics.Canvas(image.Image)) { canvas.DrawColor(new global::Android.Graphics.Color( SimpleColor.FromKnownColor(KnownColor.White).Value)); // create the view. double[] sceneCenter = this.Map.Projection.ToPixel(this.MapCenter.Latitude, this.MapCenter.Longitude); float mapZoom = this.MapZoom; float sceneZoomFactor = (float)this.Map.Projection.ToZoomFactor(this.MapZoom); // create the view for this control. float scaledNormalWidth = image.Image.Width / _bufferFactor; float scaledNormalHeight = image.Image.Height / _bufferFactor; var view = View2D.CreateFrom((float)sceneCenter[0], (float)sceneCenter[1], scaledNormalWidth * _extra, scaledNormalHeight * _extra, sceneZoomFactor, _invertX, _invertY, this.MapTilt); long before = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "Rendering Start"); // notify the map that the view has changed. if (_previouslyChangedView == null || !_previouslyChangedView.Equals(view)) { // report change once! var normalView = View2D.CreateFrom((float)sceneCenter[0], (float)sceneCenter[1], scaledNormalWidth, scaledNormalHeight, sceneZoomFactor, _invertX, _invertY, this.MapTilt); this.Map.ViewChanged((float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, normalView, view); _previouslyChangedView = view; long afterViewChanged = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "View change took: {0}ms @ zoom level {1}", (new TimeSpan(afterViewChanged - before).TotalMilliseconds), this.MapZoom); } // does the rendering. bool complete = _cacheRenderer.Render(canvas, layers, view, (float)this.Map.Projection.ToZoomFactor(this.MapZoom)); long afterRendering = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "Rendering took: {0}ms @ zoom level {1} and {2}", (new TimeSpan(afterRendering - before).TotalMilliseconds), this.MapZoom, this.MapCenter); if (complete) { // there was no cancellation, the rendering completely finished. // add the result to the scene cache. // add the newly rendered image again. if (_offScreenBuffer == null) { // create the offscreen buffer first. _offScreenBuffer = new ImageTilted2D(view.Rectangle, image, float.MinValue, float.MaxValue); } else { // augment the previous buffer. _offScreenBuffer.Bounds = view.Rectangle; _offScreenBuffer.NativeImage = image; } var temp = _onScreenBuffer; _onScreenBuffer = _offScreenBuffer; _offScreenBuffer = temp; } long after = DateTime.Now.Ticks; if (complete) { // report a successful render to listener. _listener.NotifyRenderSuccess(view, mapZoom, (int)new TimeSpan(after - before).TotalMilliseconds); } } } // notify the the current surface of the new rendering. this.PostInvalidate(); } catch (Exception ex) { // exceptions can be thrown when the mapview is disposed while rendering. // don't worry too much about these, the mapview is garbage anyway. OsmSharp.Logging.Log.TraceEvent("MapViewSurface", TraceEventType.Critical, string.Format("An unhandled exception occured:{0}", ex.ToString())); } }
/// <summary> /// Renders the current complete scene. /// </summary> private void Render() { if (_cacheRenderer.IsRunning) { _cacheRenderer.CancelAndWait(); } // make sure only on thread at the same time is using the renderer. lock (_cacheRenderer) { this.Map.ViewChangedCancel(); // build the layers list. var layers = new List <Layer> (); for (int layerIdx = 0; layerIdx < this.Map.LayerCount; layerIdx++) { // get the layer. layers.Add(this.Map[layerIdx]); } // add the internal layers. layers.Add(_makerLayer); // get old image if available. global::Android.Graphics.Bitmap image = null; if (_offScreenBuffer != null) { image = _offScreenBuffer.Tag as global::Android.Graphics.Bitmap; } if (this.SurfaceHeight == 0) { return; } // resize image if needed. float size = System.Math.Max(this.SurfaceHeight, this.SurfaceWidth); if (image == null || image.Width != (int)(size * _extra) || image.Height != (int)(size * _extra)) { // create a bitmap and render there. image = global::Android.Graphics.Bitmap.CreateBitmap((int)(size * _extra), (int)(size * _extra), global::Android.Graphics.Bitmap.Config.Argb8888); } // create and reset the canvas. global::Android.Graphics.Canvas canvas = new global::Android.Graphics.Canvas(image); canvas.DrawColor(new global::Android.Graphics.Color( SimpleColor.FromKnownColor(KnownColor.White).Value)); // create the view. double[] sceneCenter = this.Map.Projection.ToPixel(this.MapCenter.Latitude, this.MapCenter.Longitude); float mapZoom = this.MapZoom; float sceneZoomFactor = (float)this.Map.Projection.ToZoomFactor(this.MapZoom); // create the view for this control. View2D view = View2D.CreateFrom((float)sceneCenter[0], (float)sceneCenter[1], size * _extra, size * _extra, sceneZoomFactor, _invertX, _invertY, this.MapTilt); long before = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "Rendering Start"); // 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", TraceEventType.Information, "View change took: {0}ms @ zoom level {1}", (new TimeSpan(afterViewChanged - before).TotalMilliseconds), this.MapZoom); // does the rendering. bool complete = _cacheRenderer.Render(canvas, layers, view, (float)this.Map.Projection.ToZoomFactor(this.MapZoom)); long afterRendering = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", 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. // add the newly rendered image again. _offScreenBuffer = new ImageTilted2D(view.Rectangle, new byte[0], float.MinValue, float.MaxValue); _offScreenBuffer.Tag = image; var temp = _onScreenBuffer; _onScreenBuffer = _offScreenBuffer; _offScreenBuffer = temp; } // notify the the current surface of the new rendering. this.PostInvalidate(); long after = DateTime.Now.Ticks; if (complete) { // report a successfull render to listener. _listener.NotifyRenderSuccess(view, mapZoom, (int)new TimeSpan(after - before).TotalMilliseconds); } } }
/// <summary> /// Render the current complete scene. /// </summary> void Render() { try { if (Monitor.TryEnter(_cacheRenderer, 1000)) { try { // use object RectangleF rect = _rect; // create the view. float size = System.Math.Max(_rect.Width, _rect.Height); View2D view = _cacheRenderer.Create((int)(size * _extra), (int)(size * _extra), this.Map, (float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, _invertX, _invertY, this.MapTilt); if (rect.Width == 0) { // only render if a proper size is known. return; } // calculate width/height. int imageWidth = (int)(size * _extra * _scaleFactor); int imageHeight = (int)(size * _extra * _scaleFactor); // create a new bitmap context. CGColorSpace space = CGColorSpace.CreateDeviceRGB(); int bytesPerPixel = 4; int bytesPerRow = bytesPerPixel * imageWidth; int bitsPerComponent = 8; // get old image if available. CGBitmapContext image = new CGBitmapContext(null, imageWidth, imageHeight, bitsPerComponent, bytesPerRow, space, // kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast CGBitmapFlags.PremultipliedFirst | CGBitmapFlags.ByteOrder32Big); long before = DateTime.Now.Ticks; // build the layers list. var layers = new List <Layer>(); for (int layerIdx = 0; layerIdx < this.Map.LayerCount; layerIdx++) { layers.Add(this.Map[layerIdx]); } // add the internal layer. try { image.SetRGBFillColor(1, 1, 1, 1); image.FillRect(new RectangleF( 0, 0, imageWidth, imageHeight)); // 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", TraceEventType.Information, "View change took: {0}ms @ zoom level {1}", (new TimeSpan(afterViewChanged - before).TotalMilliseconds), this.MapZoom); float zoomFactor = this.MapZoom; float sceneZoomFactor = (float)this.Map.Projection.ToZoomFactor(this.MapZoom); // does the rendering. bool complete = _cacheRenderer.Render(new CGContextWrapper(image, new RectangleF(0, 0, (int)(size * _extra), (int)(size * _extra))), layers, view, sceneZoomFactor); long afterRendering = DateTime.Now.Ticks; if (complete) { // there was no cancellation, the rendering completely finished. lock (_bufferSynchronisation) { if (_onScreenBuffer != null && _onScreenBuffer.Tag != null) { // on screen buffer. (_onScreenBuffer.Tag as CGImage).Dispose(); } // add the newly rendered image again. _onScreenBuffer = new ImageTilted2D(view.Rectangle, new byte[0], float.MinValue, float.MaxValue); _onScreenBuffer.Tag = image.ToImage(); // store the previous view. _previouslyRenderedView = view; } // make sure this view knows that there is a new rendering. this.InvokeOnMainThread(SetNeedsDisplay); } long after = DateTime.Now.Ticks; if (complete) { // notify invalidation listener about a succesfull rendering. OsmSharp.Logging.Log.TraceEvent("OsmSharp.iOS.UI.MapView", TraceEventType.Information, "Rendering succesfull after {0}ms.", new TimeSpan(after - before).TotalMilliseconds); _listener.NotifyRenderSuccess(view, zoomFactor, (int)new TimeSpan(after - before).TotalMilliseconds); } else { // rendering incomplete. OsmSharp.Logging.Log.TraceEvent("OsmSharp.iOS.UI.MapView", TraceEventType.Information, "Rendering cancelled.", new TimeSpan(after - before).TotalMilliseconds); } } finally { } } finally { // make sure the object lock is release. Monitor.Exit(_cacheRenderer); } } } catch (Exception) { _cacheRenderer.Reset(); } }