I am integrating a WMS with Telerik map. In the Telerik forums, I found some sample code that works correctly until the zoom is great and performing the calculation is incorrect because MaxX and MinX returns the same value and Miny and Maxy returns the same value. I do not quite understand the functioning of QuadKey, BBox, Tilex, Tiley ... and so as not correct the code. Here I put the sample code provided in your forum telerik. See if someone sees where this error.
public class WMSCustomSource : TiledMapSource
{
private const string TileUrlFormat = @"http://www1.sedecatastro.gob.es/Cartografia/WMS/ServidorWMS.aspx?SERVICE=WMS&REQUEST=GetMap&SRS=EPSG:4326&BBOX={0},{1},{2},{3}&WIDTH={4}&HEIGHT={4}&Layers=Catastro&TRANSPARENT=TRUE&STYLES=PositionStyle&FORMAT=image/png";
private const int TileSize = 256;
/// <summary>
/// Earth Circumference.
/// </summary>
private double earthCircumference;
private double halfEarthCircumference;
private double earthRadius;
/// <summary>
/// Initializes a new instance of the OSMCustomSource class.
/// </summary>
public WMSCustomSource(ISpatialReference spatialReference)
: base(1, 20, TileSize, TileSize)
{
this.earthRadius = spatialReference.SpheroidRadius;
this.earthCircumference = this.earthRadius * 2 * Math.PI;
this.halfEarthCircumference = this.earthCircumference / 2d;
}
/// <summary>
/// Initialize provider.
/// </summary>
public override void Initialize()
{
// Raise provider intialized event.
this.RaiseIntializeCompleted();
}
/// <summary>
/// Returns the bounding BBox for a grid square represented by the given quad key
/// </summary>
/// <param name="quadKey"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="zoomLevel"></param>
/// <returns></returns>
private BBOX ConvertQuadKeyToBBox(string quadKey, int x, int y, int zoomLevel)
{
char c = quadKey[0];
int tileSize = 2 << (18 - zoomLevel - 1);
if (c == '0')
{
y = y - tileSize;
}
else if (c == '1')
{
y = y - tileSize;
x = x + tileSize;
}
else if (c == '3')
{
x = x + tileSize;
}
if (quadKey.Length > 1)
{
return ConvertQuadKeyToBBox(quadKey.Substring(1), x, y, zoomLevel + 1);
}
return new BBOX(x, y, tileSize, tileSize);
}
private BBOX ConvertQuadKeyToBBox(string quadKey)
{
const int x = 0;
const int y = 262144;
return ConvertQuadKeyToBBox(quadKey, x, y, 1);
}
/// <summary>
/// Converts radians to degrees
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
private double RadiansToDegrees(double radians)
{
return radians / Math.PI * 180d;
}
/// <summary>
/// Converts a grid row to Latitude
/// </summary>
/// <param name="y"></param>
/// <param name="zoom"></param>
/// <returns></returns>
private double ConvertYToLatitude(int y, int zoom)
{
double arc = this.earthCircumference / ((double)(1 << zoom) * (double)TileSize);
double metersY = this.halfEarthCircumference - ((double)y * (double)TileSize * arc);
double a = Math.Exp(metersY * 2d / this.earthRadius);
double result = RadiansToDegrees(Math.Asin((a - 1d) / (a + 1d)));
return result;
}
/// <summary>
/// Converts a grid column to Longitude
/// </summary>
/// <param name="x"></param>
/// <param name="zoom"></param>
/// <returns></returns>
private double ConvertXToLongitude(int x, int zoom)
{
double arc = this.earthCircumference / ((double)(1 << zoom) * (double)TileSize);
double metersX = ((double)x * (double)TileSize * arc) - this.halfEarthCircumference;
double result = RadiansToDegrees(metersX / this.earthRadius);
return result;
}
private static string GetQuadKey(int tileX, int tileY, int levelOfDetail)
{
var quadKey = new StringBuilder();
for (int i = levelOfDetail; i > 0; i--)
{
char digit = '0';
int mask = 1 << (i - 1);
if ((tileX & mask) != 0)
{
digit++;
}
if ((tileY & mask) != 0)
{
digit++;
digit++;
}
quadKey.Append(digit);
}
return quadKey.ToString();
}
/// <summary>
/// Gets the image URI.
/// </summary>
/// <param name="tileLevel">Tile level.</param>
/// <param name="tilePositionX">Tile X.</param>
/// <param name="tilePositionY">Tile Y.</param>
/// <returns>URI of image.</returns>
protected override Uri GetTile(int tileLevel, int tilePositionX, int tilePositionY)
{
int zoomLevel = ConvertTileToZoomLevel(tileLevel);
string quadKey = GetQuadKey(tilePositionX, tilePositionY, zoomLevel);
BBOX boundingBox = ConvertQuadKeyToBBox(quadKey);
**double longitude = ConvertXToLongitude(boundingBox.x, 18);
double latitude = ConvertYToLatitude(boundingBox.y, 18);
double longitude2 = ConvertXToLongitude(boundingBox.x + boundingBox.width, 18);
double latitude2 = ConvertYToLatitude(boundingBox.y - boundingBox.height, 18);**
string url = string.Format(CultureInfo.InvariantCulture, TileUrlFormat,
longitude, latitude, longitude2, latitude2, TileSize);
return new Uri(url);
}
/// <summary>
/// The box of the bounds of a tile
/// </summary>
private class BBOX
{
public int x;
public int y;
public int width;
public int height;
public BBOX(int x, int y, int width, int height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
}
The functions that I return the same values when the zoom is high are: ConvertYToLatitude and ConvertXToLongitude. Although the value I provide to X1 and X2 are different. And the value of Y1 and Y2 are also different.
I do not know if it can be a problem with decimal and double.
Thanks and sorry for my English