1

我正在将 WMS 与 Telerik 地图集成。在 Telerik 论坛中,我发现一些示例代码可以正常工作,直到缩放效果很好并且执行计算不正确,因为 MaxX 和 MinX 返回相同的值,而 Miny 和 Maxy 返回相同的值。我不太了解 QuadKey、BBox、Tilex、Tiley ... 的功能,以免更正代码。在这里,我将您的论坛 Telerik 中提供的示例代码。看看是否有人看到这个错误在哪里。

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;
        }
    }
}

我在放大倍数高时返回相同值的函数是:ConvertYToLatitude和ConvertXToLongitude。虽然我提供给 X1 和 X2 的值是不同的。并且 Y1 和 Y2 的值也不同。

我不知道十进制和双精度是否有问题。

谢谢和对不起我的英语

4

1 回答 1

2

我找到了解决方案!我使用http://alastaira.wordpress.com/2011/01/07/accessing-a-wms-tile-server-from-bing-maps-v7/中的另一个函数来转换 BBox 中的 QuadKey 最终类将读:

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}&WIDTH={1}&HEIGHT={1}&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();
    }



    public string QuadKeyToBBox(string quadKey)
    {

        int zoom = quadKey.Length;
        int x = 0, y = 0;
        // Work out the x and y position of this tile

        for (int i = zoom; i > 0; i--)
        {
            int mask = 1 << (i - 1);
            switch (quadKey[zoom - i])
            {
                case '0':
                    break;
                case '1':
                    x |= mask;
                    break;
                case '2':
                    y |= mask;
                    break;
                case '3':
                    x |= mask;
                    y |= mask;
                    break;
                default:
                    throw new ArgumentException("Invalid QuadKey digit sequence.");
            }
        }
        // From the grid position and zoom, work out the min and max Latitude / Longitude values of this tile
        double W = (float)(x * TileSize) * 360 / (float)(TileSize * Math.Pow(2, zoom)) - 180;

        double N = (float)Math.Asin((Math.Exp((0.5 - (y * TileSize) / (TileSize) / Math.Pow(2, zoom)) * 4 * Math.PI) - 1) / (Math.Exp((0.5 - (y * TileSize) / 256 / Math.Pow(2, zoom)) * 4 * Math.PI) + 1)) * 180 / (float)Math.PI;

        double E = (float)((x + 1) * TileSize) * 360 / (float)(TileSize * Math.Pow(2, zoom)) - 180;
        double S = (float)Math.Asin((Math.Exp((0.5 - ((y + 1) * TileSize) / (TileSize) / Math.Pow(2, zoom)) * 4 * Math.PI) - 1) / (Math.Exp((0.5 - ((y + 1) * TileSize) / 256 / Math.Pow(2, zoom)) * 4 * Math.PI) + 1)) * 180 / (float)Math.PI;

        double[] bounds = new double[] { W, S, E, N };

        // Return a comma-separated string of the bounding coordinates
        return string.Join(",", Array.ConvertAll(bounds, s => s.ToString().Replace(',','.')));
    }


    /// <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);

        **string bbox = QuadKeyToBBox(quadKey);**

        string url = string.Format(CultureInfo.InvariantCulture, TileUrlFormat, bbox, TileSize);

        return new Uri(url);
    }


}
于 2012-03-20T17:28:25.240 回答