Úrovně přiblížení a mřížka dlaždic

Azure Mapy používat souřadnicový systém souferického Mercatoru (EPSG:3857). Projekce je matematický model, který slouží k transformaci kulového glóbusu na plochou mapu. Projekce kulového Mercatoru roztáhne mapu na pólech a vytvoří čtvercovou mapu. Tato projekce výrazně zkresluje měřítko a oblast mapy, ale má dvě důležité vlastnosti, které převáží nad tímto zkreslením:

  • Jedná se o konformní projekci, což znamená, že zachovává tvar relativně malých objektů. Zachování tvaru malých objektů je zvláště důležité při zobrazení leteckých snímků. Chceme například zabránit zkreslení tvaru budov. Čtvercové budovy by se měly objevit čtverce, ne obdélníkové.
  • Je to válcová projekce. Sever a jih jsou vždy nahoru a dolů a západ a východ jsou vždy vlevo a vpravo.

Pro optimalizaci výkonu načítání a zobrazení mapy je mapa rozdělena na čtvercové dlaždice. Sada Azure Mapy SDK používá dlaždice, které mají velikost 512 × 512 pixelů pro silniční mapy a menší 256 × 256 pixelů pro satelitní snímky. Azure Mapy poskytuje rastrové a vektorové dlaždice pro 23 úrovní přiblížení, číslovaný 0 až 22. Na úrovni přiblížení 0 se celý svět vejde na jednu dlaždici:

Dlaždice Mapa světa

Úroveň přiblížení 1 používá k vykreslení světa čtyři dlaždice: 2 x 2 čtverce

Rozložení dlaždice mapy 2x2

Každý další čtyřúhelník úrovně přiblížení rozdělí dlaždice předchozího a vytvoří mřížku 2lupy x 2lupy. Úroveň přiblížení 22 je mřížka 2 22 x 222 nebo 4 194 304 x 4 194 304 dlaždic (celkem 17 592 186 044 416 dlaždic).

Interaktivní ovládací prvky mapy Azure Mapy pro web a Android podporují 25 úrovní přiblížení, očíslované 0 až 24. I když jsou silniční data k dispozici pouze na úrovních přiblížení, když jsou dlaždice k dispozici.

Následující tabulka obsahuje úplný seznam hodnot pro úrovně přiblížení, ve kterých je velikost dlaždice 256 pixelů:

Úroveň přiblížení Měřiče/pixel Metry nebo dlaždice na straně
0 156543 40075017
0 78271.5 20037508
2 39135.8 10018754
3 19567.88 5009377.1
4 9783.94 2504688.5
5 4891.97 1252344.3
6 2445.98 626172.1
7 1222.99 313086.1
8 611.5 156543
9 305.75 78271.5
10 152.87 39135.8
11 76.44 19567.9
12 38.219 9783.94
13 19.109 4891.97
14 9.555 2445.98
15 4.777 1222.99
16 2.3887 611.496
17 1.1943 305.748
18 0.5972 152.874
19 0.2986 76.437
20 0.14929 38.2185
21 0.074646 19.10926
22 0.037323 9.55463
23 0.0186615 4.777315
24 0.00933075 2.3886575

Souřadnice pixelů

Při výběru projekce a měřítka pro použití na jednotlivých úrovních přiblížení můžeme převést geografické souřadnice na souřadnice pixelů. Šířka a výška celého pixelu obrázku mapy pro určitou úroveň přiblížení se vypočítá takto:

var mapWidth = tileSize * Math.pow(2, zoom);

var mapHeight = mapWidth;

Vzhledem k tomu, že šířka a výška mapy se na každé úrovni přiblížení liší, jsou tedy souřadnice pixelů. Pixel v levém horním rohu mapy má vždy souřadnice pixelů (0, 0). Pixel v pravém dolním rohu mapy má souřadnice pixelů (šířka-1, výška-1) nebo odkazování na rovnice v předchozí části (tileSize * 2zoom–1, tileSize * 2zoom–1). Například při použití čtvercových dlaždic 512 na úrovni 2 jsou souřadnice pixelů v rozsahu od (0, 0) do (2047, 2047), například takto:

Mapa zobrazující rozměry pixelů

Vzhledem k zeměpisné šířce a délce ve stupních a úrovni podrobností se souřadnice XY v pixelech vypočítají takto:

var sinLatitude = Math.sin(latitude * Math.PI/180);

var pixelX = ((longitude + 180) / 360) * tileSize * Math.pow(2, zoom);

var pixelY = (0.5 – Math.log((1 + sinLatitude) / (1 – sinLatitude)) / (4 * Math.PI)) * tileSize * Math.pow(2, zoom);

Hodnoty zeměpisné šířky a délky se předpokládají v datech WGS 84. I když Azure Mapy používá kulovou projekci, je důležité převést všechny geografické souřadnice na společný datum. WGS 84 je vybraný datum. Hodnota zeměpisné délky se předpokládá v rozsahu od -180 stupňů do +180 stupňů a hodnota zeměpisné šířky musí být oříznuta tak, aby byla v rozsahu od -85,05112878 do 85,05112878. Dodržování těchto hodnot zabraňuje jednotnému čísle na pólech a zajišťuje, že projektovaná mapa je čtvercový tvar.

Souřadnice dlaždic

Pro optimalizaci výkonu načítání a zobrazení mapy se vykreslená mapa vyřezá do dlaždic. Počet pixelů a počet dlaždic se liší na jednotlivých úrovních přiblížení:

var numberOfTilesWide = Math.pow(2, zoom);

var numberOfTilesHigh = numberOfTilesWide;

Každá dlaždice má souřadnice XY v rozsahu od (0, 0) v levém horním rohu do (2přiblížení–1, 2přiblížení–1) v pravém dolním rohu. Například na úrovni přiblížení 3 jsou souřadnice dlaždic v rozsahu od (0, 0) do (7, 7) následujícím způsobem:

Mapa souřadnic dlaždic

Vzhledem k páru souřadnic XY pixelů můžete snadno určit souřadnice dlaždice XY dlaždice obsahující daný pixel:

var tileX = Math.floor(pixelX / tileSize);

var tileY = Math.floor(pixelY / tileSize);

Dlaždice se volají podle úrovně přiblížení. Souřadnice x a y odpovídají pozici dlaždice v mřížce pro danou úroveň přiblížení.

Při určování úrovně přiblížení, kterou chcete použít, nezapomeňte, že každé umístění je v pevné poloze na dlaždici. V důsledku toho je počet dlaždic potřebných k zobrazení dané velikosti území závislý na konkrétním umístění mřížky lupy na mapě světa. Pokud jsou například dva body 900 metrů od sebe, může trvat pouze tři dlaždice, aby se mezi nimi zobrazila trasa na úrovni přiblížení 17. Pokud je však západní bod na pravé straně dlaždice a východní bod na levé straně dlaždice, může mít čtyři dlaždice:

Měřítko ukázky lupy

Po určení úrovně přiblížení je možné vypočítat hodnoty x a y. Levá horní dlaždice v každé mřížce lupy je x=0, y=0; Dlaždice vpravo dole je na x=2zoom-1, y=2zoom-1.

Tady je mřížka lupy pro úroveň přiblížení 1:

Zoom grid for zoom level 1

Indexy quadkey

Některé mapovací platformy používají quadkey konvenci vytváření názvů indexování, která kombinuje souřadnice ZY dlaždic do řetězce jedné dimenze označovaného jako quadtree klíče nebo quadkeys zkráceně. Každá quadkey jedinečně identifikuje jednu dlaždici na konkrétní úrovni podrobností a dá se použít jako klíč v běžných indexech databáze B-tree. Sady AZURE Mapy SDK podporují překryvné vrstvy dlaždic, které kromě dalších zásad vytváření názvů používají quadkey i další zásady vytváření názvů, jak je uvedeno v dokumentu Přidat vrstvu dlaždic.

Poznámka:

Zásady quadkeys vytváření názvů fungují jenom pro úrovně přiblížení jedné nebo větší. Sada Azure Mapy SDK podporuje úroveň přiblížení 0, což je jedna dlaždice mapy pro celý svět.

Chcete-li převést souřadnice dlaždic na , quadkeyjsou bity souřadnic Y a X prokládání a výsledek je interpretován jako číslo base-4 (se zachováním počátečních nul) a převedeno na řetězec. Například vzhledem k souřadnicům XY dlaždice (3, 5) na úrovni 3 quadkey se určí takto:

tileX = 3 = 011 (base 2)

tileY = 5 = 101 (base 2)

quadkey = 100111 (base 2) = 213 (base 4) = "213"

Qquadkeys mají několik zajímavých vlastností. Za prvé, délka quadkey (počet číslic) se rovná úrovni přiblížení odpovídající dlaždice. Za druhé, quadkey každá dlaždice začíná quadkey nadřazenou dlaždicí (obsahující dlaždici na předchozí úrovni). Jak je znázorněno v následujícím příkladu, dlaždice 2 je nadřazená dlaždice 20 až 23:

Čtyřúhelník dlaždice jehlan

Nakonec zadejte jednorozměrný indexový klíč, quadkeys který obvykle zachovává blízkost dlaždic v prostoru XY. Jinými slovy, dvě dlaždice, které mají blízké souřadnice XY, obvykle mají quadkeys relativně blízko sebe. To je důležité pro optimalizaci výkonu databáze, protože sousední dlaždice jsou často požadovány ve skupinách a je žádoucí zachovat tyto dlaždice na stejných discích, aby se minimalizoval počet čtení disku.

Dlaždice matematického zdrojového kódu

Následující ukázkový kód ukazuje, jak implementovat funkce popsané v tomto dokumentu. Tyto funkce lze podle potřeby snadno přeložit do jiných programovacích jazyků.

using System;
using System.Text;

namespace AzureMaps
{
    /// <summary>
    /// Tile System math for the Spherical Mercator projection coordinate system (EPSG:3857)
    /// </summary>
    public static class TileMath
    {
        //Earth radius in meters.
        private const double EarthRadius = 6378137;

        private const double MinLatitude = -85.05112878;
        private const double MaxLatitude = 85.05112878;
        private const double MinLongitude = -180;
        private const double MaxLongitude = 180;

        /// <summary>
        /// Clips a number to the specified minimum and maximum values.
        /// </summary>
        /// <param name="n">The number to clip.</param>
        /// <param name="minValue">Minimum allowable value.</param>
        /// <param name="maxValue">Maximum allowable value.</param>
        /// <returns>The clipped value.</returns>
        private static double Clip(double n, double minValue, double maxValue)
        {
            return Math.Min(Math.Max(n, minValue), maxValue);
        }

        /// <summary>
        /// Calculates width and height of the map in pixels at a specific zoom level from -180 degrees to 180 degrees.
        /// </summary>
        /// <param name="zoom">Zoom Level to calculate width at</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <returns>Width and height of the map in pixels</returns>
        public static double MapSize(double zoom, int tileSize)
        {
            return Math.Ceiling(tileSize * Math.Pow(2, zoom));
        }

        /// <summary>
        /// Calculates the Ground resolution at a specific degree of latitude in meters per pixel.
        /// </summary>
        /// <param name="latitude">Degree of latitude to calculate resolution at</param>
        /// <param name="zoom">Zoom level to calculate resolution at</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <returns>Ground resolution in meters per pixels</returns>
        public static double GroundResolution(double latitude, double zoom, int tileSize)
        {
            latitude = Clip(latitude, MinLatitude, MaxLatitude);
            return Math.Cos(latitude * Math.PI / 180) * 2 * Math.PI * EarthRadius / MapSize(zoom, tileSize);
        }

        /// <summary>
        /// Determines the map scale at a specified latitude, level of detail, and screen resolution.
        /// </summary>
        /// <param name="latitude">Latitude (in degrees) at which to measure the map scale.</param>
        /// <param name="zoom">Level of detail, from 1 (lowest detail) to 23 (highest detail).</param>
        /// <param name="screenDpi">Resolution of the screen, in dots per inch.</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <returns>The map scale, expressed as the denominator N of the ratio 1 : N.</returns>
        public static double MapScale(double latitude, double zoom, int screenDpi, int tileSize)
        {
            return GroundResolution(latitude, zoom, tileSize) * screenDpi / 0.0254;
        }

        /// <summary>
        /// Global Converts a Pixel coordinate into a geospatial coordinate at a specified zoom level. 
        /// Global Pixel coordinates are relative to the top left corner of the map (90, -180)
        /// </summary>
        /// <param name="pixel">Pixel coordinates in the format of [x, y].</param>  
        /// <param name="zoom">Zoom level</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <returns>A position value in the format [longitude, latitude].</returns>
        public static double[] GlobalPixelToPosition(double[] pixel, double zoom, int tileSize)
        {
            var mapSize = MapSize(zoom, tileSize);

            var x = (Clip(pixel[0], 0, mapSize - 1) / mapSize) - 0.5;
            var y = 0.5 - (Clip(pixel[1], 0, mapSize - 1) / mapSize);

            return new double[] {
                360 * x,    //Longitude
                90 - 360 * Math.Atan(Math.Exp(-y * 2 * Math.PI)) / Math.PI  //Latitude
            };
        }

        /// <summary>
        /// Converts a point from latitude/longitude WGS-84 coordinates (in degrees) into pixel XY coordinates at a specified level of detail.
        /// </summary>
        /// <param name="position">Position coordinate in the format [longitude, latitude]</param>
        /// <param name="zoom">Zoom level.</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param> 
        /// <returns>A global pixel coordinate.</returns>
        public static double[] PositionToGlobalPixel(double[] position, int zoom, int tileSize)
        {
            var latitude = Clip(position[1], MinLatitude, MaxLatitude);
            var longitude = Clip(position[0], MinLongitude, MaxLongitude);

            var x = (longitude + 180) / 360;
            var sinLatitude = Math.Sin(latitude * Math.PI / 180);
            var y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI);

            var mapSize = MapSize(zoom, tileSize);

            return new double[] {
                 Clip(x * mapSize + 0.5, 0, mapSize - 1),
                 Clip(y * mapSize + 0.5, 0, mapSize - 1)
            };
        }

        /// <summary>
        /// Converts pixel XY coordinates into tile XY coordinates of the tile containing the specified pixel.
        /// </summary>
        /// <param name="pixel">Pixel coordinates in the format of [x, y].</param>  
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <param name="tileX">Output parameter receiving the tile X coordinate.</param>
        /// <param name="tileY">Output parameter receiving the tile Y coordinate.</param>
        public static void GlobalPixelToTileXY(double[] pixel, int tileSize, out int tileX, out int tileY)
        {
            tileX = (int)(pixel[0] / tileSize);
            tileY = (int)(pixel[1] / tileSize);
        }

        /// <summary>
        /// Performs a scale transform on a global pixel value from one zoom level to another.
        /// </summary>
        /// <param name="pixel">Pixel coordinates in the format of [x, y].</param>  
        /// <param name="oldZoom">The zoom level in which the input global pixel value is from.</param>  
        /// <returns>A scale pixel coordinate.</returns>
        public static double[] ScaleGlobalPixel(double[] pixel, double oldZoom, double newZoom)
        {
            var scale = Math.Pow(2, oldZoom - newZoom);

            return new double[] { pixel[0] * scale, pixel[1] * scale };
        }

        /// <summary>
        /// Performs a scale transform on a set of global pixel values from one zoom level to another.
        /// </summary>
        /// <param name="pixels">A set of global pixel value from the old zoom level. Points are in the format [x,y].</param>
        /// <param name="oldZoom">The zoom level in which the input global pixel values is from.</param>
        /// <param name="newZoom">The new zoom level in which the output global pixel values should be aligned with.</param>
        /// <returns>A set of global pixel values that has been scaled for the new zoom level.</returns>
        public static double[][] ScaleGlobalPixels(double[][] pixels, double oldZoom, double newZoom)
        {
            var scale = Math.Pow(2, oldZoom - newZoom);

            var output = new System.Collections.Generic.List<double[]>();
            foreach (var p in pixels)
            {
                output.Add(new double[] { p[0] * scale, p[1] * scale });
            }

            return output.ToArray();
        }

        /// <summary>
        /// Converts tile XY coordinates into a global pixel XY coordinates of the upper-left pixel of the specified tile.
        /// </summary>
        /// <param name="tileX">Tile X coordinate.</param>
        /// <param name="tileY">Tile Y coordinate.</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <param name="pixelX">Output parameter receiving the X coordinate of the point, in pixels.</param>  
        /// <param name="pixelY">Output parameter receiving the Y coordinate of the point, in pixels.</param>  
        public static double[] TileXYToGlobalPixel(int tileX, int tileY, int tileSize)
        {
            return new double[] { tileX * tileSize, tileY * tileSize };
        }

        /// <summary>
        /// Converts tile XY coordinates into a quadkey at a specified level of detail.
        /// </summary>
        /// <param name="tileX">Tile X coordinate.</param>
        /// <param name="tileY">Tile Y coordinate.</param>
        /// <param name="zoom">Zoom level</param>
        /// <returns>A string containing the quadkey.</returns>
        public static string TileXYToQuadKey(int tileX, int tileY, int zoom)
        {
            var quadKey = new StringBuilder();
            for (int i = zoom; 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>
        /// Converts a quadkey into tile XY coordinates.
        /// </summary>
        /// <param name="quadKey">Quadkey of the tile.</param>
        /// <param name="tileX">Output parameter receiving the tile X coordinate.</param>
        /// <param name="tileY">Output parameter receiving the tile Y coordinate.</param>
        /// <param name="zoom">Output parameter receiving the zoom level.</param>
        public static void QuadKeyToTileXY(string quadKey, out int tileX, out int tileY, out int zoom)
        {
            tileX = tileY = 0;
            zoom = quadKey.Length;
            for (int i = zoom; i > 0; i--)
            {
                int mask = 1 << (i - 1);
                switch (quadKey[zoom - i])
                {
                    case '0':
                        break;

                    case '1':
                        tileX |= mask;
                        break;

                    case '2':
                        tileY |= mask;
                        break;

                    case '3':
                        tileX |= mask;
                        tileY |= mask;
                        break;

                    default:
                        throw new ArgumentException("Invalid QuadKey digit sequence.");
                }
            }
        }

        /// <summary>
        /// Calculates the XY tile coordinates that a coordinate falls into for a specific zoom level.
        /// </summary>
        /// <param name="position">Position coordinate in the format [longitude, latitude]</param>
        /// <param name="zoom">Zoom level</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <param name="tileX">Output parameter receiving the tile X position.</param>
        /// <param name="tileY">Output parameter receiving the tile Y position.</param>
        public static void PositionToTileXY(double[] position, int zoom, int tileSize, out int tileX, out int tileY)
        {
            var latitude = Clip(position[1], MinLatitude, MaxLatitude);
            var longitude = Clip(position[0], MinLongitude, MaxLongitude);

            var x = (longitude + 180) / 360;
            var sinLatitude = Math.Sin(latitude * Math.PI / 180);
            var y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI);

            //tileSize needed in calculations as in rare cases the multiplying/rounding/dividing can make the difference of a pixel which can result in a completely different tile. 
            var mapSize = MapSize(zoom, tileSize);
            tileX = (int)Math.Floor(Clip(x * mapSize + 0.5, 0, mapSize - 1) / tileSize);
            tileY = (int)Math.Floor(Clip(y * mapSize + 0.5, 0, mapSize - 1) / tileSize);
        }

        /// <summary>
        /// Calculates the tile quadkey strings that are within a specified viewport.
        /// </summary>
        /// <param name="position">Position coordinate in the format [longitude, latitude]</param>
        /// <param name="zoom">Zoom level</param>
        /// <param name="width">The width of the map viewport in pixels.</param>
        /// <param name="height">The height of the map viewport in pixels.</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <returns>A list of quadkey strings that are within the specified viewport.</returns>
        public static string[] GetQuadkeysInView(double[] position, int zoom, int width, int height, int tileSize)
        {
            var p = PositionToGlobalPixel(position, zoom, tileSize);

            var top = p[1] - height * 0.5;
            var left = p[0] - width * 0.5;

            var bottom = p[1] + height * 0.5;
            var right = p[0] + width * 0.5;

            var tl = GlobalPixelToPosition(new double[] { left, top }, zoom, tileSize);
            var br = GlobalPixelToPosition(new double[] { right, bottom }, zoom, tileSize);

            //Boudning box in the format: [west, south, east, north];
            var bounds = new double[] { tl[0], br[1], br[0], tl[1] };

            return GetQuadkeysInBoundingBox(bounds, zoom, tileSize);
        }

        /// <summary>
        /// Calculates the tile quadkey strings that are within a bounding box at a specific zoom level.
        /// </summary>
        /// <param name="bounds">A bounding box defined as an array of numbers in the format of [west, south, east, north].</param>
        /// <param name="zoom">Zoom level to calculate tiles for.</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <returns>A list of quadkey strings.</returns>
        public static string[] GetQuadkeysInBoundingBox(double[] bounds, int zoom, int tileSize)
        {
            var keys = new System.Collections.Generic.List<string>();

            if (bounds != null && bounds.Length >= 4)
            {
                PositionToTileXY(new double[] { bounds[3], bounds[0] }, zoom, tileSize, out int tlX, out int tlY);
                PositionToTileXY(new double[] { bounds[1], bounds[2] }, zoom, tileSize, out int brX, out int brY);

                for (int x = tlX; x <= brX; x++)
                {
                    for (int y = tlY; y <= brY; y++)
                    {
                        keys.Add(TileXYToQuadKey(x, y, zoom));
                    }
                }
            }

            return keys.ToArray();
        }

        /// <summary>
        /// Calculates the bounding box of a tile.
        /// </summary>
        /// <param name="tileX">Tile X coordinate</param>
        /// <param name="tileY">Tile Y coordinate</param>
        /// <param name="zoom">Zoom level</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <returns>A bounding box of the tile defined as an array of numbers in the format of [west, south, east, north].</returns>
        public static double[] TileXYToBoundingBox(int tileX, int tileY, double zoom, int tileSize)
        {
            //Top left corner pixel coordinates
            var x1 = (double)(tileX * tileSize);
            var y1 = (double)(tileY * tileSize);

            //Bottom right corner pixel coordinates
            var x2 = (double)(x1 + tileSize);
            var y2 = (double)(y1 + tileSize);

            var nw = GlobalPixelToPosition(new double[] { x1, y1 }, zoom, tileSize);
            var se = GlobalPixelToPosition(new double[] { x2, y2 }, zoom, tileSize);

            return new double[] { nw[0], se[1], se[0], nw[1] };
        }

        /// <summary>
        /// Calculates the best map view (center, zoom) for a bounding box on a map.
        /// </summary>
        /// <param name="bounds">A bounding box defined as an array of numbers in the format of [west, south, east, north].</param>
        /// <param name="mapWidth">Map width in pixels.</param>
        /// <param name="mapHeight">Map height in pixels.</param>
        /// <param name="padding">Width in pixels to use to create a buffer around the map. This is to keep markers from being cut off on the edge</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
        /// <param name="latitude">Output parameter receiving the center latitude coordinate.</param>
        /// <param name="longitude">Output parameter receiving the center longitude coordinate.</param>
        /// <param name="zoom">Output parameter receiving the zoom level</param>
        public static void BestMapView(double[] bounds, double mapWidth, double mapHeight, int padding, int tileSize, out double centerLat, out double centerLon, out double zoom)
        {
            if (bounds == null || bounds.Length < 4)
            {
                centerLat = 0;
                centerLon = 0;
                zoom = 1;
                return;
            }

            double boundsDeltaX;

            //Check if east value is greater than west value which would indicate that bounding box crosses the antimeridian.
            if (bounds[2] > bounds[0])
            {
                boundsDeltaX = bounds[2] - bounds[0];
                centerLon = (bounds[2] + bounds[0]) / 2;
            }
            else
            {
                boundsDeltaX = 360 - (bounds[0] - bounds[2]);
                centerLon = ((bounds[2] + bounds[0]) / 2 + 360) % 360 - 180;
            }

            var ry1 = Math.Log((Math.Sin(bounds[1] * Math.PI / 180) + 1) / Math.Cos(bounds[1] * Math.PI / 180));
            var ry2 = Math.Log((Math.Sin(bounds[3] * Math.PI / 180) + 1) / Math.Cos(bounds[3] * Math.PI / 180));
            var ryc = (ry1 + ry2) / 2;

            centerLat = Math.Atan(Math.Sinh(ryc)) * 180 / Math.PI;

            var resolutionHorizontal = boundsDeltaX / (mapWidth - padding * 2);

            var vy0 = Math.Log(Math.Tan(Math.PI * (0.25 + centerLat / 360)));
            var vy1 = Math.Log(Math.Tan(Math.PI * (0.25 + bounds[3] / 360)));
            var zoomFactorPowered = (mapHeight * 0.5 - padding) / (40.7436654315252 * (vy1 - vy0));
            var resolutionVertical = 360.0 / (zoomFactorPowered * tileSize);

            var resolution = Math.Max(resolutionHorizontal, resolutionVertical);

            zoom = Math.Log(360 / (resolution * tileSize), 2);
        }
    }
}

Poznámka:

Interaktivní ovládací prvky mapy v sadě Azure Mapy SDK mají pomocné funkce pro převod mezi geoprostorovými pozicemi a pixely zobrazení.

Další kroky

Přímo přistupovat k mapovým dlaždicím ze služeb Azure Mapy REST:

Další informace o geoprostorových konceptech: