/// <summary> /// Notifies this layer that the current mapview has changed. /// </summary> /// <param name="map"></param> /// <param name="zoomFactor"></param> /// <param name="center"></param> /// <param name="view"></param> /// <param name="extraView"></param> protected internal override void ViewChanged(Map map, float zoomFactor, GeoCoordinate center, View2D view, View2D extraView) { // calculate the current zoom level. var zoomLevel = (int)System.Math.Round(map.Projection.ToZoomLevel(zoomFactor), 0); if (zoomLevel >= _minZoomLevel && zoomLevel <= _maxZoomLevel) { // build the bounding box. var viewBox = view.OuterBox; // build the tile range. var range = TileRange.CreateAroundBoundingBox(new GeoCoordinateBox(map.Projection.ToGeoCoordinates(viewBox.Min[0], viewBox.Min[1]), map.Projection.ToGeoCoordinates(viewBox.Max[0], viewBox.Max[1])), zoomLevel); OsmSharp.Logging.Log.TraceEvent("LayerMBTile", OsmSharp.Logging.TraceEventType.Verbose, string.Format("Requesting {0} tiles for view.", range.Count)); // request all missing tiles. 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.EnumerateInCenterFirst()) { Image2D value; if (_cache.TryGet(tile, out value)) { // Tile is already in cache. We used TryGet, and not TryPeek, to inform the cache // that we intend to use the datum in question. continue; } Tile invertTile = tile.InvertY(); var tiles = _connection.Query <tiles>("SELECT * FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?", invertTile.Zoom, invertTile.X, invertTile.Y); foreach (var mbTile in tiles) { var box = tile.ToBox(_projection); var nativeImage = _nativeImageCache.Obtain(mbTile.tile_data); var image2D = new Image2D(box.Min[0], box.Min[1], box.Max[1], box.Max[0], nativeImage); image2D.Layer = (uint)(_maxZoomLevel - tile.Zoom); lock (_cache) { // add the result to the cache. _cache.Add(tile, image2D); } } } } OsmSharp.Logging.Log.TraceEvent("LayerMBTile", Logging.TraceEventType.Information, "Cached tiles: {0}", _cache.Count); } }
/// <summary> /// Response the specified myResp and tile. /// </summary> /// <param name="myResp">My resp.</param> /// <param name="tile">Tile.</param> private void Response(WebResponse myResp, Tile tile) { if (_cache == null) { return; } try { Stream stream = myResp.GetResponseStream(); byte[] image = null; if (stream != null) { using (stream) { // there is data: read it. var memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); image = memoryStream.ToArray(); memoryStream.Dispose(); var box = tile.ToBox(_projection); var nativeImage = _nativeImageCache.Obtain(image); var image2D = new Image2D(box.Min[0], box.Min[1], box.Max[1], box.Max[0], nativeImage); image2D.Layer = (uint)(_maxZoomLevel - tile.Zoom); lock (_cache) { // add the result to the cache. _cache.Add(tile, image2D); } } if (!_suspended) { // only raise the event when not suspended but do no throw away a tile, that would be a waste. this.RaiseLayerChanged(); } } else { OsmSharp.Logging.Log.TraceEvent("LayerTile", Logging.TraceEventType.Error, "No response stream!"); } } catch (Exception ex) { // don't worry about exceptions here. OsmSharp.Logging.Log.TraceEvent("LayerTile", Logging.TraceEventType.Error, ex.Message); } }