From 1d5703d3822fb9fa61163ec2d4f05e9ad1154cb1 Mon Sep 17 00:00:00 2001 From: ClemensF Date: Tue, 6 Nov 2012 19:49:29 +0100 Subject: [PATCH] Fixed flicker when map center moves across the date line. --- MapControl/MapBase.cs | 5 ----- MapControl/Tile.cs | 36 ++++++++++++++++++----------------- MapControl/TileContainer.cs | 35 ++++++++++++++++++++++++---------- MapControl/TileImageLoader.cs | 26 +++++++++++++++---------- MapControl/TileLayer.cs | 36 +++++++++++++++++------------------ 5 files changed, 77 insertions(+), 61 deletions(-) diff --git a/MapControl/MapBase.cs b/MapControl/MapBase.cs index f5cfb70a..ca3d702a 100644 --- a/MapControl/MapBase.cs +++ b/MapControl/MapBase.cs @@ -458,11 +458,6 @@ namespace MapControl UpdateTransform(); } - protected override void OnRender(DrawingContext drawingContext) - { - drawingContext.DrawRectangle(Background, null, new Rect(RenderSize)); - } - protected override void OnViewportChanged() { base.OnViewportChanged(); diff --git a/MapControl/Tile.cs b/MapControl/Tile.cs index 2f5ef2e0..647ff658 100644 --- a/MapControl/Tile.cs +++ b/MapControl/Tile.cs @@ -36,28 +36,30 @@ namespace MapControl } } - public ImageSource Image + public ImageSource Source { get { return Brush.ImageSource; } - set + set { Brush.ImageSource = value; } + } + + public void SetSource(ImageSource source) + { + if (Source == null) { - if (Brush.ImageSource == null) + BitmapImage bitmap = source as BitmapImage; + + if (bitmap != null && bitmap.IsDownloading) { - BitmapImage bitmap = value as BitmapImage; - - if (bitmap != null && bitmap.IsDownloading) - { - bitmap.DownloadCompleted += BitmapDownloadCompleted; - bitmap.DownloadFailed += BitmapDownloadFailed; - } - else - { - Brush.BeginAnimation(ImageBrush.OpacityProperty, opacityAnimation); - } + bitmap.DownloadCompleted += BitmapDownloadCompleted; + bitmap.DownloadFailed += BitmapDownloadFailed; + } + else + { + Brush.BeginAnimation(ImageBrush.OpacityProperty, opacityAnimation); } - - Brush.ImageSource = value; } + + Source = source; } private void BitmapDownloadCompleted(object sender, EventArgs e) @@ -71,7 +73,7 @@ namespace MapControl { ((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted; ((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed; - Brush.ImageSource = null; + Source = null; } } } diff --git a/MapControl/TileContainer.cs b/MapControl/TileContainer.cs index 8423dcc7..69513901 100644 --- a/MapControl/TileContainer.cs +++ b/MapControl/TileContainer.cs @@ -33,7 +33,7 @@ namespace MapControl public void AddTileLayers(int index, IEnumerable tileLayers) { - Matrix transform = GetVisualTransform(); + Matrix tileLayerTransform = GetTileLayerTransform(); foreach (TileLayer tileLayer in tileLayers) { @@ -43,7 +43,7 @@ namespace MapControl } Children.Insert(index++, tileLayer); - tileLayer.TransformMatrix = transform; + tileLayer.TransformMatrix = tileLayerTransform; tileLayer.UpdateTiles(tileZoomLevel, tileGrid); } } @@ -73,12 +73,19 @@ namespace MapControl public double SetViewportTransform(double mapZoomLevel, double mapRotation, Point mapOrigin, Point viewportOrigin, Size viewportSize) { - zoomLevel = mapZoomLevel; + double scale = Math.Pow(2d, zoomLevel) * 256d / 360d; + double oldMapOriginX = (origin.X - offset.X) / scale - 180d; + + if (zoomLevel != mapZoomLevel) + { + zoomLevel = mapZoomLevel; + scale = Math.Pow(2d, zoomLevel) * 256d / 360d; + } + rotation = mapRotation; size = viewportSize; origin = viewportOrigin; - double scale = Math.Pow(2d, zoomLevel) * 256d / 360d; offset.X = origin.X - (180d + mapOrigin.X) * scale; offset.Y = origin.Y - (180d - mapOrigin.Y) * scale; @@ -88,19 +95,27 @@ namespace MapControl transform.RotateAt(rotation, origin.X, origin.Y); ViewportTransform.Matrix = transform; - transform = GetVisualTransform(); + Matrix tileLayerTransform = GetTileLayerTransform(); foreach (TileLayer tileLayer in Children) { - tileLayer.TransformMatrix = transform; + tileLayer.TransformMatrix = tileLayerTransform; } - updateTimer.IsEnabled = true; + if (Math.Sign(mapOrigin.X) == Math.Sign(oldMapOriginX)) + { + updateTimer.IsEnabled = true; + } + else + { + // immediately handle map origin leap when map center moves across the date line + UpdateTiles(this, EventArgs.Empty); + } return scale; } - private Matrix GetVisualTransform() + private Matrix GetTileLayerTransform() { // Calculates the transform matrix that enables rendering of 256x256 tile rectangles in // TileLayer.UpdateTiles with origin at tileGrid.X and tileGrid.Y to minimize rounding errors. @@ -148,11 +163,11 @@ namespace MapControl { tileZoomLevel = zoom; tileGrid = grid; - transform = GetVisualTransform(); + Matrix tileLayerTransform = GetTileLayerTransform(); foreach (TileLayer tileLayer in Children) { - tileLayer.TransformMatrix = transform; + tileLayer.TransformMatrix = tileLayerTransform; tileLayer.UpdateTiles(tileZoomLevel, tileGrid); } } diff --git a/MapControl/TileImageLoader.cs b/MapControl/TileImageLoader.cs index f0759e6d..41c45973 100644 --- a/MapControl/TileImageLoader.cs +++ b/MapControl/TileImageLoader.cs @@ -70,9 +70,9 @@ namespace MapControl this.tileLayer = tileLayer; } - internal void BeginGetTiles(ICollection tiles) + internal void BeginGetTiles(IEnumerable tiles) { - ThreadPool.QueueUserWorkItem(BeginGetTilesAsync, new List(tiles.Where(t => t.Image == null && t.Uri == null))); + ThreadPool.QueueUserWorkItem(BeginGetTilesAsync, new List(tiles.Where(t => t.Source == null && t.Uri == null))); } internal void CancelGetTiles() @@ -89,23 +89,26 @@ namespace MapControl { ImageTileSource imageTileSource = (ImageTileSource)tileLayer.TileSource; - newTiles.ForEach(tile => + foreach (Tile tile in newTiles) { tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background, - (Action)(() => tile.Image = imageTileSource.GetImage(tile.XIndex, tile.Y, tile.ZoomLevel))); - }); + (Action)(() => tile.SetSource(imageTileSource.GetImage(tile.XIndex, tile.Y, tile.ZoomLevel)))); + } } else { if (Cache == null) { - newTiles.ForEach(tile => pendingTiles.Enqueue(tile)); + foreach (Tile tile in newTiles) + { + pendingTiles.Enqueue(tile); + } } else { List outdatedTiles = new List(newTiles.Count); - newTiles.ForEach(tile => + foreach (Tile tile in newTiles) { string key = CacheKey(tile); byte[] buffer = Cache.Get(key) as byte[]; @@ -125,9 +128,12 @@ namespace MapControl // update cached image outdatedTiles.Add(tile); } - }); + } - outdatedTiles.ForEach(tile => pendingTiles.Enqueue(tile)); + foreach (Tile tile in outdatedTiles) + { + pendingTiles.Enqueue(tile); + } } while (downloadThreadCount < Math.Min(pendingTiles.Count, tileLayer.MaxParallelDownloads)) @@ -182,7 +188,7 @@ namespace MapControl return false; } - tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => tile.Image = bitmap)); + tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => tile.SetSource(bitmap))); return true; } diff --git a/MapControl/TileLayer.cs b/MapControl/TileLayer.cs index 33a437e9..e5ad527b 100644 --- a/MapControl/TileLayer.cs +++ b/MapControl/TileLayer.cs @@ -17,7 +17,7 @@ namespace MapControl public class TileLayer : DrawingVisual { private readonly TileImageLoader tileImageLoader; - private readonly List tiles = new List(); + private List tiles = new List(); private string description = string.Empty; private Int32Rect grid; private int zoomLevel; @@ -25,8 +25,8 @@ namespace MapControl public TileLayer() { tileImageLoader = new TileImageLoader(this); - VisualEdgeMode = EdgeMode.Aliased; VisualTransform = new MatrixTransform(); + VisualEdgeMode = EdgeMode.Aliased; MinZoomLevel = 1; MaxZoomLevel = 18; MaxParallelDownloads = 8; @@ -62,15 +62,15 @@ namespace MapControl { SelectTiles(); RenderTiles(); - tileImageLoader.BeginGetTiles(tiles); } } internal void ClearTiles() { - tiles.Clear(); tileImageLoader.CancelGetTiles(); + tiles.Clear(); + RenderTiles(); } private void SelectTiles() @@ -84,7 +84,7 @@ namespace MapControl minZoomLevel = MinZoomLevel; } - tiles.RemoveAll(t => t.ZoomLevel < minZoomLevel || t.ZoomLevel > maxZoomLevel); + List newTiles = new List(); for (int z = minZoomLevel; z <= maxZoomLevel; z++) { @@ -98,35 +98,33 @@ namespace MapControl { for (int x = x1; x <= x2; x++) { - if (tiles.Find(t => t.ZoomLevel == z && t.X == x && t.Y == y) == null) + Tile tile = tiles.Find(t => t.ZoomLevel == z && t.X == x && t.Y == y); + + if (tile == null) { - Tile tile = new Tile(z, x, y); - Tile equivalent = tiles.Find(t => t.Image != null && t.ZoomLevel == tile.ZoomLevel && t.XIndex == tile.XIndex && t.Y == tile.Y); + tile = new Tile(z, x, y); + Tile equivalent = tiles.Find(t => t.Source != null && t.ZoomLevel == z && t.XIndex == tile.XIndex && t.Y == y); if (equivalent != null) { - tile.Image = equivalent.Image; + tile.Source = equivalent.Source; } - - tiles.Add(tile); } + + newTiles.Add(tile); } } - - tiles.RemoveAll(t => t.ZoomLevel == z && (t.X < x1 || t.X > x2 || t.Y < y1 || t.Y > y2)); } - tiles.Sort((t1, t2) => t1.ZoomLevel - t2.ZoomLevel); - - //System.Diagnostics.Trace.TraceInformation("{0} Tiles: {1}", tiles.Count, - // string.Join(", ", System.Linq.Enumerable.Select(tiles, t => t.ZoomLevel.ToString()))); + tiles = newTiles; + //System.Diagnostics.Trace.TraceInformation("{0} Tiles: {1}", tiles.Count, string.Join(", ", tiles.Select(t => t.ZoomLevel.ToString()))); } private void RenderTiles() { using (DrawingContext drawingContext = RenderOpen()) { - tiles.ForEach(tile => + foreach (Tile tile in tiles) { int tileSize = 256 << (zoomLevel - tile.ZoomLevel); Rect tileRect = new Rect(tileSize * tile.X - 256 * grid.X, tileSize * tile.Y - 256 * grid.Y, tileSize, tileSize); @@ -136,7 +134,7 @@ namespace MapControl //if (tile.ZoomLevel == zoomLevel) // drawingContext.DrawText(new FormattedText(string.Format("{0}-{1}-{2}", tile.ZoomLevel, tile.X, tile.Y), // System.Globalization.CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface("Segoe UI"), 14, Brushes.Black), tileRect.TopLeft); - }); + } } } }