EasyGIS.NET icon indicating copy to clipboard operation
EasyGIS.NET copied to clipboard

Tile XYZ to WMS

Open theo2f opened this issue 2 years ago • 2 comments

Hi! How can I convert the XYZ Tile format to WMS Tile format? I see many maps on web using this format and I would like to use them.

URL sample: https://ies-ows.jrc.ec.europa.eu/gwis?service=WMS&request=GetMap&layers=admin.countries_borders&styles=&format=image%2Fpng&transparent=true&version=1.1.1&singletile=false&width=2048&height=2048&srs=EPSG%3A3857&bbox=0,5009377.085697314,2504688.5428486555,7514065.628545967

I did some things, like:

Tile URL

Since I'm using the TileCollection class from EGIS sample (with some mods) I changed the parameter format to check and replace later:

https://ies-ows.jrc.ec.europa.eu/gwis?service=WMS&request=GetMap&layers=admin.countries_borders&styles=&format=image%2Fpng&transparent=true&version=1.1.1&singletile=false&width=256&height=256&srs=EPSG%3A3857&bbox={bbLeft},{bbBottom},{bbRight},{bbTop}

Extend converter

This class provide the conversion from XYZ to a extended long/lat. I tryed to use the TileUtil.GetTileLatLonBounds method but I could't make it work:

public static class xnGeoUtilsLongLat
{
    public class Extend
    {
        public double Left { get; set; }
        public double Right { get; set; }
        public double Bottom { get; set; }
        public double Top { get; set; }
        public double Res { get; set; }

        public Extend()
        { }
    }
    public class Tile
    {
        public double X { get; set; }
        public double Y { get; set; }
        public int Z { get; set; }
        public double Top { get; set; }
        public double Left { get; set; }

        public Tile()
        { }
    }

    // web mercator projection extent
    static Extend ProjExtent = new Extend
    {
        Left = -20037508.342789244,
        Right = 20037508.342789244,
        Bottom = -20037508.342789244,
        Top = 20037508.342789244
    };
    //tile seize
    static int TileSize = 256;

    // resolutions
    static double[] Resolutions = new double[] {
            156543.03392804097, 78271.51696402048, 39135.75848201024,
            19567.87924100512, 9783.93962050256, 4891.96981025128, 2445.98490512564,
            1222.99245256282, 611.49622628141, 305.748113140705, 152.8740565703525,
            76.43702828517625, 38.21851414258813, 19.109257071294063, 9.554628535647032,
            4.777314267823516, 2.388657133911758, 1.194328566955879, 0.5971642834779395,
            0.29858214173896974, 0.14929107086948487, 0.07464553543474244,
            0.03732276771737122, 0.01866138385868561 };

    static List<Tile> GetTiles(Extend extent, int z)
    {
        //coordinated in pixel
        var lx = Math.Floor((extent.Left - ProjExtent.Left) / Resolutions[z]);
        var rx = Math.Floor((extent.Right - ProjExtent.Left) / Resolutions[z]);
        var by = Math.Floor((ProjExtent.Top - extent.Bottom) / Resolutions[z]);
        var ty = Math.Floor((ProjExtent.Top - extent.Top) / Resolutions[z]);

        // tile numbers
        var lX = Math.Floor(lx / TileSize);
        var rX = Math.Floor(rx / TileSize);
        var bY = Math.Floor(by / TileSize);
        var tY = Math.Floor(ty / TileSize);

        //top left tile position of top-left tile with respect to window/div
        var top = (tY * TileSize) - ty;
        var topStart = top;
        var left = (lX * TileSize) - lx;
        var tiles = new List<Tile>();
        for (var i = lX; i <= rX; i++)
        {
            top = topStart;
            for (var j = tY; j <= bY; j++)
            {
                tiles.Add(new Tile
                {
                    X = i,
                    Y = j,
                    Z = z,
                    Top = top,
                    Left = left
                });
                top += TileSize;
            }
            left += TileSize;
        }
        return tiles;
    }

    public static Extend TileExtent(double x, double y, int z)
    {
        return TileExtent(new xnGeoUtilsLongLat.Tile()
        {
            X = x,
            Y = y,
            Z = z
        });
    }
    public static Extend TileExtent(Tile tile)
    {
        var right = ProjExtent.Left + tile.X * TileSize * Resolutions[tile.Z];
        var left = right - TileSize * Resolutions[tile.Z];
        var bottom = ProjExtent.Top - tile.Y * TileSize * Resolutions[tile.Z];
        var top = bottom + TileSize * Resolutions[tile.Z];

        return new Extend()
        {
            Left = left,
            Right = right,
            Bottom = bottom,
            Top = top,
            Res = Resolutions[tile.Z]
        };
    }
}

CreateBitmap code

Here the CreateBitmap method that was changed to check if imageUrlFormat contains "{bbLeft}". Case yes then use WMS format else keep using XYZ.

private System.Drawing.Bitmap CreateBitmap()
{
    try
    {
        string strUrl = "";
        if (imageUrlFormat.Contains("{bbLeft}"))
        {
            //force dot as decimal separator
            NumberFormatInfo nfi = new NumberFormatInfo();
            nfi.NumberDecimalSeparator = ".";

            var _ext = xnGeoUtilsLongLat.TileExtent(this.x, this.y, this.zoomLevel);

            imageUrlFormat = imageUrlFormat
                .Replace("{bbLeft}", "{0}")
                .Replace("{bbBottom}", "{1}")
                .Replace("{bbRight}", "{2}")
                .Replace("{bbTop}", "{3}");

            strUrl = string.Format(imageUrlFormat, _ext.Left.ToString(nfi), _ext.Bottom.ToString(nfi), _ext.Right.ToString(nfi), _ext.Top.ToString(nfi));
        }
        else
            strUrl = string.Format(imageUrlFormat, this.zoomLevel, this.x, this.y);

        Console.WriteLine(strUrl);

        var bitmapStream = GetFromCache(strUrl);
        if (bitmapStream != null)
        {
            bitmapStream.Seek(0, System.IO.SeekOrigin.Begin);
            using (Image img = Bitmap.FromStream(bitmapStream))
            {
                //copy the returned image to a new bitmap. If we don't do this then the transparency may not
                //display properly
                Bitmap bm = new Bitmap(img.Width, img.Height);
                using (Graphics g = Graphics.FromImage(bm))
                {
                    g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel);
                }
                return bm;
            }
        }

        //request not in local cache - retrieve asyncronously
        //dont await this call!
        GetBitmapAsync(strUrl);

        Bitmap blankBitmap = new Bitmap(256, 256);
        using (Graphics g = Graphics.FromImage(blankBitmap))
        {
            g.Clear(Color.LightGray);
        }
        return blankBitmap;
    }
    catch (Exception ex)
    {
        Bitmap blankBitmap = new Bitmap(256, 256);
        using (Graphics g = Graphics.FromImage(blankBitmap))
        {
            g.Clear(Color.LightGray);

        }
        return blankBitmap;
    }
}

It's working in part. Tiles is loaded, but it's not on the right position.

image

At moment I'm stuck on this and I can't go ahead. Can someone help me with this?

theo2f avatar Oct 11 '22 18:10 theo2f