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(); UpdateTransform();
} }
protected override void OnRender(DrawingContext drawingContext)
{
drawingContext.DrawRectangle(Background, null, new Rect(RenderSize));
}
protected override void OnViewportChanged() protected override void OnViewportChanged()
{ {
base.OnViewportChanged(); base.OnViewportChanged();

View file

@ -36,28 +36,30 @@ namespace MapControl
} }
} }
public ImageSource Image public ImageSource Source
{ {
get { return Brush.ImageSource; } 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; bitmap.DownloadCompleted += BitmapDownloadCompleted;
bitmap.DownloadFailed += BitmapDownloadFailed;
if (bitmap != null && bitmap.IsDownloading) }
{ else
bitmap.DownloadCompleted += BitmapDownloadCompleted; {
bitmap.DownloadFailed += BitmapDownloadFailed; Brush.BeginAnimation(ImageBrush.OpacityProperty, opacityAnimation);
}
else
{
Brush.BeginAnimation(ImageBrush.OpacityProperty, opacityAnimation);
}
} }
Brush.ImageSource = value;
} }
Source = source;
} }
private void BitmapDownloadCompleted(object sender, EventArgs e) private void BitmapDownloadCompleted(object sender, EventArgs e)
@ -71,7 +73,7 @@ namespace MapControl
{ {
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted; ((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed; ((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) public void AddTileLayers(int index, IEnumerable<TileLayer> tileLayers)
{ {
Matrix transform = GetVisualTransform(); Matrix tileLayerTransform = GetTileLayerTransform();
foreach (TileLayer tileLayer in tileLayers) foreach (TileLayer tileLayer in tileLayers)
{ {
@ -43,7 +43,7 @@ namespace MapControl
} }
Children.Insert(index++, tileLayer); Children.Insert(index++, tileLayer);
tileLayer.TransformMatrix = transform; tileLayer.TransformMatrix = tileLayerTransform;
tileLayer.UpdateTiles(tileZoomLevel, tileGrid); tileLayer.UpdateTiles(tileZoomLevel, tileGrid);
} }
} }
@ -73,12 +73,19 @@ namespace MapControl
public double SetViewportTransform(double mapZoomLevel, double mapRotation, Point mapOrigin, Point viewportOrigin, Size viewportSize) 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; rotation = mapRotation;
size = viewportSize; size = viewportSize;
origin = viewportOrigin; origin = viewportOrigin;
double scale = Math.Pow(2d, zoomLevel) * 256d / 360d;
offset.X = origin.X - (180d + mapOrigin.X) * scale; offset.X = origin.X - (180d + mapOrigin.X) * scale;
offset.Y = origin.Y - (180d - mapOrigin.Y) * scale; offset.Y = origin.Y - (180d - mapOrigin.Y) * scale;
@ -88,19 +95,27 @@ namespace MapControl
transform.RotateAt(rotation, origin.X, origin.Y); transform.RotateAt(rotation, origin.X, origin.Y);
ViewportTransform.Matrix = transform; ViewportTransform.Matrix = transform;
transform = GetVisualTransform(); Matrix tileLayerTransform = GetTileLayerTransform();
foreach (TileLayer tileLayer in Children) 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; return scale;
} }
private Matrix GetVisualTransform() private Matrix GetTileLayerTransform()
{ {
// Calculates the transform matrix that enables rendering of 256x256 tile rectangles in // 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. // TileLayer.UpdateTiles with origin at tileGrid.X and tileGrid.Y to minimize rounding errors.
@ -148,11 +163,11 @@ namespace MapControl
{ {
tileZoomLevel = zoom; tileZoomLevel = zoom;
tileGrid = grid; tileGrid = grid;
transform = GetVisualTransform(); Matrix tileLayerTransform = GetTileLayerTransform();
foreach (TileLayer tileLayer in Children) foreach (TileLayer tileLayer in Children)
{ {
tileLayer.TransformMatrix = transform; tileLayer.TransformMatrix = tileLayerTransform;
tileLayer.UpdateTiles(tileZoomLevel, tileGrid); tileLayer.UpdateTiles(tileZoomLevel, tileGrid);
} }
} }

View file

@ -70,9 +70,9 @@ namespace MapControl
this.tileLayer = tileLayer; 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() internal void CancelGetTiles()
@ -89,23 +89,26 @@ namespace MapControl
{ {
ImageTileSource imageTileSource = (ImageTileSource)tileLayer.TileSource; ImageTileSource imageTileSource = (ImageTileSource)tileLayer.TileSource;
newTiles.ForEach(tile => foreach (Tile tile in newTiles)
{ {
tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background, 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 else
{ {
if (Cache == null) if (Cache == null)
{ {
newTiles.ForEach(tile => pendingTiles.Enqueue(tile)); foreach (Tile tile in newTiles)
{
pendingTiles.Enqueue(tile);
}
} }
else else
{ {
List<Tile> outdatedTiles = new List<Tile>(newTiles.Count); List<Tile> outdatedTiles = new List<Tile>(newTiles.Count);
newTiles.ForEach(tile => foreach (Tile tile in newTiles)
{ {
string key = CacheKey(tile); string key = CacheKey(tile);
byte[] buffer = Cache.Get(key) as byte[]; byte[] buffer = Cache.Get(key) as byte[];
@ -125,9 +128,12 @@ namespace MapControl
// update cached image // update cached image
outdatedTiles.Add(tile); 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)) while (downloadThreadCount < Math.Min(pendingTiles.Count, tileLayer.MaxParallelDownloads))
@ -182,7 +188,7 @@ namespace MapControl
return false; return false;
} }
tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => tile.Image = bitmap)); tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => tile.SetSource(bitmap)));
return true; return true;
} }

View file

@ -17,7 +17,7 @@ namespace MapControl
public class TileLayer : DrawingVisual public class TileLayer : DrawingVisual
{ {
private readonly TileImageLoader tileImageLoader; 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 string description = string.Empty;
private Int32Rect grid; private Int32Rect grid;
private int zoomLevel; private int zoomLevel;
@ -25,8 +25,8 @@ namespace MapControl
public TileLayer() public TileLayer()
{ {
tileImageLoader = new TileImageLoader(this); tileImageLoader = new TileImageLoader(this);
VisualEdgeMode = EdgeMode.Aliased;
VisualTransform = new MatrixTransform(); VisualTransform = new MatrixTransform();
VisualEdgeMode = EdgeMode.Aliased;
MinZoomLevel = 1; MinZoomLevel = 1;
MaxZoomLevel = 18; MaxZoomLevel = 18;
MaxParallelDownloads = 8; MaxParallelDownloads = 8;
@ -62,15 +62,15 @@ namespace MapControl
{ {
SelectTiles(); SelectTiles();
RenderTiles(); RenderTiles();
tileImageLoader.BeginGetTiles(tiles); tileImageLoader.BeginGetTiles(tiles);
} }
} }
internal void ClearTiles() internal void ClearTiles()
{ {
tiles.Clear();
tileImageLoader.CancelGetTiles(); tileImageLoader.CancelGetTiles();
tiles.Clear();
RenderTiles();
} }
private void SelectTiles() private void SelectTiles()
@ -84,7 +84,7 @@ namespace MapControl
minZoomLevel = MinZoomLevel; minZoomLevel = MinZoomLevel;
} }
tiles.RemoveAll(t => t.ZoomLevel < minZoomLevel || t.ZoomLevel > maxZoomLevel); List<Tile> newTiles = new List<Tile>();
for (int z = minZoomLevel; z <= maxZoomLevel; z++) for (int z = minZoomLevel; z <= maxZoomLevel; z++)
{ {
@ -98,35 +98,33 @@ namespace MapControl
{ {
for (int x = x1; x <= x2; x++) 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 = 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 equivalent = tiles.Find(t => t.Source != null && t.ZoomLevel == z && t.XIndex == tile.XIndex && t.Y == y);
if (equivalent != null) 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); tiles = newTiles;
//System.Diagnostics.Trace.TraceInformation("{0} Tiles: {1}", tiles.Count, string.Join(", ", tiles.Select(t => t.ZoomLevel.ToString())));
//System.Diagnostics.Trace.TraceInformation("{0} Tiles: {1}", tiles.Count,
// string.Join(", ", System.Linq.Enumerable.Select(tiles, t => t.ZoomLevel.ToString())));
} }
private void RenderTiles() private void RenderTiles()
{ {
using (DrawingContext drawingContext = RenderOpen()) using (DrawingContext drawingContext = RenderOpen())
{ {
tiles.ForEach(tile => foreach (Tile tile in tiles)
{ {
int tileSize = 256 << (zoomLevel - tile.ZoomLevel); int tileSize = 256 << (zoomLevel - tile.ZoomLevel);
Rect tileRect = new Rect(tileSize * tile.X - 256 * grid.X, tileSize * tile.Y - 256 * grid.Y, tileSize, tileSize); 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) //if (tile.ZoomLevel == zoomLevel)
// drawingContext.DrawText(new FormattedText(string.Format("{0}-{1}-{2}", tile.ZoomLevel, tile.X, tile.Y), // 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); // System.Globalization.CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface("Segoe UI"), 14, Brushes.Black), tileRect.TopLeft);
}); }
} }
} }
} }