-
Notifications
You must be signed in to change notification settings - Fork 5
/
TransformTools.cs
106 lines (89 loc) · 3.84 KB
/
TransformTools.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
using System;
using System.Windows;
namespace SpatialTutorial
{
/// <summary>
/// A tools class that does the arithmetics for tiled maps
/// </summary>
public static class TransformTools
{
/// <summary>
/// Convert a WGS84 coordinate (Lon/Lat) to generic spherical mercator.
/// When using tiles with wgs, the actual earth radius doesn't matter, we can just use radius 1.
/// To use this formula with "Google Mercator", you have to multiply the output coordinates by 6378137.
/// For "PTV Mercator" use 6371000
/// </summary>
public static Point WgsToSphereMercator(Point point)
{
double x = point.X * Math.PI / 180.0;
double y = Math.Log(Math.Tan(Math.PI / 4.0 + point.Y * Math.PI / 360.0));
return new Point(x, y);
}
/// <summary>
/// The reverse of the function above
/// To use this formula with "Google Mercator", you have to divide the input coordinates by 6378137.
/// For "PTV Mercator" use 6371000
/// </summary>
public static Point SphereMercatorToWgs(Point point)
{
double x = (180 / Math.PI) * point.X;
double y = (360 / Math.PI) * (Math.Atan(Math.Exp(point.Y)) - (Math.PI / 4));
return new Point(x, y);
}
/// <summary>
/// Calculate the Mercator bounds for a tile key
/// </summary>
public static Rect TileToSphereMercator(uint x, uint y, uint z)
{
// the width of a tile (when the earth has radius 1)
double arc = Math.PI * 2.0 / Math.Pow(2, z);
double x1 = -Math.PI + x * arc;
double x2 = x1 + arc;
double y1 = Math.PI - y * arc;
double y2 = y1 - arc;
return new Rect(new Point(x1, y2), new Point(x2, y1));
}
/// <summary>
/// Calculate WGS (Lon/Lat) bounds for a tile key
/// </summary>
public static Rect TileToWgs(uint x, uint y, uint z, int bleedingPixels = 0)
{
var rect = TileToSphereMercator(x, y, z);
if(bleedingPixels != 0)
{
double bleedingFactor = bleedingPixels / 256.0 * 2;
rect.Inflate(rect.Width * bleedingFactor, rect.Height * bleedingFactor);
}
return new Rect(SphereMercatorToWgs(rect.TopLeft), SphereMercatorToWgs(rect.BottomRight));
}
/// <summary>
/// Convert a point relative to a mercator viewport to a point relative to an image
/// </summary>
public static System.Drawing.Point MercatorToImage(Rect mercatorRect, Size imageSize, Point mercatorPoint)
{
return new System.Drawing.Point(
(int)((mercatorPoint.X - mercatorRect.Left) / (mercatorRect.Right - mercatorRect.Left) * imageSize.Width),
(int)(imageSize.Height - (mercatorPoint.Y - mercatorRect.Top) / (mercatorRect.Bottom - mercatorRect.Top) * imageSize.Height));
}
/// <summary>
/// Convert a WGS (Lon,Lat) coordinate to a point relative to a tile image
/// </summary>
public static System.Drawing.Point WgsToTile(uint x, uint y, uint z, Point wgsPoint, double clipWgsAtDegrees = 85.05)
{
if (clipWgsAtDegrees < 90)
wgsPoint = ClipWgsPoint(wgsPoint, clipWgsAtDegrees);
return MercatorToImage(TileToSphereMercator(x, y, z), new Size(256, 256), WgsToSphereMercator(wgsPoint));
}
/// <summary>
/// Clip the latitude value to avoid overflow at the poles
/// </summary>
public static Point ClipWgsPoint(Point p, double degrees = 85.05)
{
if (p.Y > degrees)
p.Y = degrees;
if (p.Y < -degrees)
p.Y = -degrees;
return p;
}
}
}