Fixed flicker when map center moves across the date line.

This commit is contained in:
ClemensF 2012-11-06 19:49:29 +01:00
parent 02b9cb4354
commit 1d5703d382
5 changed files with 77 additions and 61 deletions

View file

@ -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();

View file

@ -36,14 +36,17 @@ namespace MapControl
}
}
public ImageSource Image
public ImageSource Source
{
get { return Brush.ImageSource; }
set
set { Brush.ImageSource = value; }
}
public void SetSource(ImageSource source)
{
if (Brush.ImageSource == null)
if (Source == null)
{
BitmapImage bitmap = value as BitmapImage;
BitmapImage bitmap = source as BitmapImage;
if (bitmap != null && bitmap.IsDownloading)
{
@ -56,8 +59,7 @@ namespace MapControl
}
}
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;
}
}
}

View file

@ -33,7 +33,7 @@ namespace MapControl
public void AddTileLayers(int index, IEnumerable<TileLayer> 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);
}
}
@ -72,13 +72,20 @@ namespace MapControl
}
public double SetViewportTransform(double mapZoomLevel, double mapRotation, Point mapOrigin, Point viewportOrigin, Size viewportSize)
{
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;
}
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);
}
}

View file

@ -70,9 +70,9 @@ namespace MapControl
this.tileLayer = tileLayer;
}
internal void BeginGetTiles(ICollection<Tile> tiles)
internal void BeginGetTiles(IEnumerable<Tile> tiles)
{
ThreadPool.QueueUserWorkItem(BeginGetTilesAsync, new List<Tile>(tiles.Where(t => t.Image == null && t.Uri == null)));
ThreadPool.QueueUserWorkItem(BeginGetTilesAsync, new List<Tile>(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<Tile> outdatedTiles = new List<Tile>(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;
}

View file

@ -17,7 +17,7 @@ namespace MapControl
public class TileLayer : DrawingVisual
{
private readonly TileImageLoader tileImageLoader;
private readonly List<Tile> tiles = new List<Tile>();
private List<Tile> tiles = new List<Tile>();
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<Tile> newTiles = new List<Tile>();
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);
});
}
}
}
}