2012-11-22 21:42:29 +01:00
|
|
|
|
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
2013-05-07 18:12:25 +02:00
|
|
|
|
// Copyright © Clemens Fischer 2012-2013
|
2012-05-04 12:52:20 +02:00
|
|
|
|
// Licensed under the Microsoft Public License (Ms-PL)
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
2012-04-25 22:02:53 +02:00
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.Text;
|
2012-12-09 22:18:31 +01:00
|
|
|
|
#if NETFX_CORE
|
2012-11-22 21:42:29 +01:00
|
|
|
|
using Windows.Foundation;
|
|
|
|
|
|
#else
|
2012-04-25 22:02:53 +02:00
|
|
|
|
using System.Windows;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
#endif
|
2012-04-25 22:02:53 +02:00
|
|
|
|
|
|
|
|
|
|
namespace MapControl
|
|
|
|
|
|
{
|
2012-05-04 12:52:20 +02:00
|
|
|
|
/// <summary>
|
2012-07-20 21:57:29 +02:00
|
|
|
|
/// Provides the URI of a map tile.
|
2012-05-04 12:52:20 +02:00
|
|
|
|
/// </summary>
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public partial class TileSource
|
2012-04-25 22:02:53 +02:00
|
|
|
|
{
|
2013-05-07 18:12:25 +02:00
|
|
|
|
public const int TileSize = 256;
|
|
|
|
|
|
public const double EarthRadius = 6378137d; // WGS 84 semi major axis
|
|
|
|
|
|
|
2012-07-20 21:57:29 +02:00
|
|
|
|
private Func<int, int, int, Uri> getUri;
|
|
|
|
|
|
private string uriFormat = string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
public TileSource()
|
|
|
|
|
|
{
|
2013-07-19 15:11:48 +02:00
|
|
|
|
MetersPerDegree = EarthRadius * Math.PI / 180d;
|
2012-07-20 21:57:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public TileSource(string uriFormat)
|
2013-07-19 15:11:48 +02:00
|
|
|
|
: this()
|
2012-07-20 21:57:29 +02:00
|
|
|
|
{
|
|
|
|
|
|
UriFormat = uriFormat;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-07-19 15:11:48 +02:00
|
|
|
|
public double MetersPerDegree { get; protected set; }
|
2013-05-07 18:12:25 +02:00
|
|
|
|
|
2012-07-20 21:57:29 +02:00
|
|
|
|
public string UriFormat
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return uriFormat; }
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
|
|
|
|
{
|
2012-11-04 10:16:39 +01:00
|
|
|
|
throw new ArgumentException("The value of the UriFormat property must not be null or empty or white-space only.");
|
2012-07-20 21:57:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-01-28 23:49:22 +01:00
|
|
|
|
uriFormat = value;
|
|
|
|
|
|
|
2013-01-29 17:55:53 +01:00
|
|
|
|
if (uriFormat.Contains("{x}") && uriFormat.Contains("{y}") && uriFormat.Contains("{z}"))
|
2012-07-20 21:57:29 +02:00
|
|
|
|
{
|
2013-01-29 17:55:53 +01:00
|
|
|
|
if (uriFormat.Contains("{c}"))
|
|
|
|
|
|
{
|
|
|
|
|
|
getUri = GetOpenStreetMapUri;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (uriFormat.Contains("{i}"))
|
|
|
|
|
|
{
|
|
|
|
|
|
getUri = GetGoogleMapsUri;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (uriFormat.Contains("{n}"))
|
|
|
|
|
|
{
|
|
|
|
|
|
getUri = GetMapQuestUri;
|
|
|
|
|
|
}
|
2013-05-07 18:12:25 +02:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
getUri = GetBasicUri;
|
|
|
|
|
|
}
|
2012-07-20 21:57:29 +02:00
|
|
|
|
}
|
2013-01-29 17:55:53 +01:00
|
|
|
|
else if (uriFormat.Contains("{q}")) // {i} is optional
|
2012-07-20 21:57:29 +02:00
|
|
|
|
{
|
2013-01-29 17:55:53 +01:00
|
|
|
|
getUri = GetQuadKeyUri;
|
2012-07-20 21:57:29 +02:00
|
|
|
|
}
|
2013-05-07 18:12:25 +02:00
|
|
|
|
else if (uriFormat.Contains("{W}") && uriFormat.Contains("{S}") && uriFormat.Contains("{E}") && uriFormat.Contains("{N}"))
|
2012-07-20 21:57:29 +02:00
|
|
|
|
{
|
2013-01-29 17:55:53 +01:00
|
|
|
|
getUri = GetBoundingBoxUri;
|
2012-07-20 21:57:29 +02:00
|
|
|
|
}
|
2013-05-07 18:12:25 +02:00
|
|
|
|
else if (uriFormat.Contains("{w}") && uriFormat.Contains("{s}") && uriFormat.Contains("{e}") && uriFormat.Contains("{n}"))
|
|
|
|
|
|
{
|
|
|
|
|
|
getUri = GetLatLonBoundingBoxUri;
|
|
|
|
|
|
}
|
2013-04-21 23:56:08 +02:00
|
|
|
|
else if (uriFormat.Contains("{x}") && uriFormat.Contains("{v}") && uriFormat.Contains("{z}"))
|
|
|
|
|
|
{
|
|
|
|
|
|
getUri = GetTmsUri;
|
|
|
|
|
|
}
|
2012-07-20 21:57:29 +02:00
|
|
|
|
}
|
2013-01-29 17:55:53 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual Uri GetUri(int x, int y, int zoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
return getUri != null ? getUri(x, y, zoomLevel) : null;
|
2012-07-20 21:57:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-05-07 18:12:25 +02:00
|
|
|
|
private Uri GetBasicUri(int x, int y, int zoomLevel)
|
2012-04-25 22:02:53 +02:00
|
|
|
|
{
|
|
|
|
|
|
return new Uri(UriFormat.
|
|
|
|
|
|
Replace("{x}", x.ToString()).
|
|
|
|
|
|
Replace("{y}", y.ToString()).
|
|
|
|
|
|
Replace("{z}", zoomLevel.ToString()));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-20 21:57:29 +02:00
|
|
|
|
private Uri GetOpenStreetMapUri(int x, int y, int zoomLevel)
|
2012-04-25 22:02:53 +02:00
|
|
|
|
{
|
2013-11-12 21:14:53 +01:00
|
|
|
|
var hostIndex = (x + y) % 3;
|
2012-04-25 22:02:53 +02:00
|
|
|
|
|
2013-01-29 17:55:53 +01:00
|
|
|
|
return new Uri(UriFormat.
|
|
|
|
|
|
Replace("{c}", "abc".Substring(hostIndex, 1)).
|
|
|
|
|
|
Replace("{x}", x.ToString()).
|
|
|
|
|
|
Replace("{y}", y.ToString()).
|
|
|
|
|
|
Replace("{z}", zoomLevel.ToString()));
|
2012-04-25 22:02:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-20 21:57:29 +02:00
|
|
|
|
private Uri GetGoogleMapsUri(int x, int y, int zoomLevel)
|
2012-04-25 22:02:53 +02:00
|
|
|
|
{
|
2013-11-12 21:14:53 +01:00
|
|
|
|
var hostIndex = (x + y) % 4;
|
2012-04-25 22:02:53 +02:00
|
|
|
|
|
2013-01-29 17:55:53 +01:00
|
|
|
|
return new Uri(UriFormat.
|
|
|
|
|
|
Replace("{i}", hostIndex.ToString()).
|
|
|
|
|
|
Replace("{x}", x.ToString()).
|
|
|
|
|
|
Replace("{y}", y.ToString()).
|
|
|
|
|
|
Replace("{z}", zoomLevel.ToString()));
|
2012-04-25 22:02:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-20 21:57:29 +02:00
|
|
|
|
private Uri GetMapQuestUri(int x, int y, int zoomLevel)
|
2012-04-25 22:02:53 +02:00
|
|
|
|
{
|
2013-11-12 21:14:53 +01:00
|
|
|
|
var hostIndex = (x + y) % 4 + 1;
|
2012-04-25 22:02:53 +02:00
|
|
|
|
|
2013-01-29 17:55:53 +01:00
|
|
|
|
return new Uri(UriFormat.
|
|
|
|
|
|
Replace("{n}", hostIndex.ToString()).
|
|
|
|
|
|
Replace("{x}", x.ToString()).
|
|
|
|
|
|
Replace("{y}", y.ToString()).
|
|
|
|
|
|
Replace("{z}", zoomLevel.ToString()));
|
2012-04-25 22:02:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-21 23:56:08 +02:00
|
|
|
|
private Uri GetTmsUri(int x, int y, int zoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
y = (1 << zoomLevel) - 1 - y;
|
|
|
|
|
|
|
|
|
|
|
|
return new Uri(UriFormat.
|
|
|
|
|
|
Replace("{x}", x.ToString()).
|
|
|
|
|
|
Replace("{v}", y.ToString()).
|
|
|
|
|
|
Replace("{z}", zoomLevel.ToString()));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-20 21:57:29 +02:00
|
|
|
|
private Uri GetQuadKeyUri(int x, int y, int zoomLevel)
|
2012-04-25 22:02:53 +02:00
|
|
|
|
{
|
2013-11-17 16:52:03 +01:00
|
|
|
|
if (zoomLevel < 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
var key = new StringBuilder { Length = zoomLevel };
|
2012-04-25 22:02:53 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
for (var z = zoomLevel - 1; z >= 0; z--, x /= 2, y /= 2)
|
2012-04-25 22:02:53 +02:00
|
|
|
|
{
|
|
|
|
|
|
key[z] = (char)('0' + 2 * (y % 2) + (x % 2));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new Uri(UriFormat.
|
|
|
|
|
|
Replace("{i}", key.ToString(key.Length - 1, 1)).
|
|
|
|
|
|
Replace("{q}", key.ToString()));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-20 21:57:29 +02:00
|
|
|
|
private Uri GetBoundingBoxUri(int x, int y, int zoomLevel)
|
2012-04-25 22:02:53 +02:00
|
|
|
|
{
|
2013-06-19 18:57:54 +02:00
|
|
|
|
var m = MetersPerDegree;
|
2013-05-07 18:12:25 +02:00
|
|
|
|
var n = (double)(1 << zoomLevel);
|
2013-06-19 18:57:54 +02:00
|
|
|
|
var x1 = m * ((double)x * 360d / n - 180d);
|
|
|
|
|
|
var x2 = m * ((double)(x + 1) * 360d / n - 180d);
|
|
|
|
|
|
var y1 = m * (180d - (double)(y + 1) * 360d / n);
|
|
|
|
|
|
var y2 = m * (180d - (double)y * 360d / n);
|
2013-05-07 18:12:25 +02:00
|
|
|
|
|
|
|
|
|
|
return new Uri(UriFormat.
|
|
|
|
|
|
Replace("{W}", x1.ToString(CultureInfo.InvariantCulture)).
|
|
|
|
|
|
Replace("{S}", y1.ToString(CultureInfo.InvariantCulture)).
|
|
|
|
|
|
Replace("{E}", x2.ToString(CultureInfo.InvariantCulture)).
|
|
|
|
|
|
Replace("{N}", y2.ToString(CultureInfo.InvariantCulture)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private Uri GetLatLonBoundingBoxUri(int x, int y, int zoomLevel)
|
|
|
|
|
|
{
|
2013-06-19 18:57:54 +02:00
|
|
|
|
var t = new MercatorTransform();
|
2012-11-22 21:42:29 +01:00
|
|
|
|
var n = (double)(1 << zoomLevel);
|
|
|
|
|
|
var x1 = (double)x * 360d / n - 180d;
|
|
|
|
|
|
var x2 = (double)(x + 1) * 360d / n - 180d;
|
|
|
|
|
|
var y1 = 180d - (double)(y + 1) * 360d / n;
|
|
|
|
|
|
var y2 = 180d - (double)y * 360d / n;
|
|
|
|
|
|
var p1 = t.Transform(new Point(x1, y1));
|
|
|
|
|
|
var p2 = t.Transform(new Point(x2, y2));
|
2012-04-25 22:02:53 +02:00
|
|
|
|
|
|
|
|
|
|
return new Uri(UriFormat.
|
2012-05-04 12:52:20 +02:00
|
|
|
|
Replace("{w}", p1.Longitude.ToString(CultureInfo.InvariantCulture)).
|
|
|
|
|
|
Replace("{s}", p1.Latitude.ToString(CultureInfo.InvariantCulture)).
|
|
|
|
|
|
Replace("{e}", p2.Longitude.ToString(CultureInfo.InvariantCulture)).
|
|
|
|
|
|
Replace("{n}", p2.Latitude.ToString(CultureInfo.InvariantCulture)));
|
2012-04-25 22:02:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|