mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Fixed flicker when map center moves across the date line.
This commit is contained in:
parent
02b9cb4354
commit
1d5703d382
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue