Dela via


Zoomnivåer och kakelrutnät

Azure Maps använder det sfäriska Mercator-projektionskoordinatsystemet (EPSG:3857). En projektion är den matematiska modell som används för att omvandla det sfäriska klotet till en platt karta. Den sfäriska Mercator-projektionen sträcker ut kartan vid polerna för att skapa en kvadratisk karta. Den här projektionen förvränger kartans skala och yta avsevärt, men har två viktiga egenskaper som uppväger den här förvrängningen:

  • Det är en konform projektion, vilket innebär att den bevarar formen på relativt små föremål. Att bevara formen på små föremål är särskilt viktigt när du visar flygbilder. Vi vill till exempel undvika att förvränga byggnadernas form. Fyrkantiga byggnader ska se fyrkantiga ut, inte rektangulära.
  • Det är en cylindrisk projektion. Nord och syd är alltid upp och ner, och väst och öst är alltid vänster och höger.

För att optimera prestanda för hämtning och visning av kartor är kartan uppdelad i fyrkantiga rutor. Azure Maps SDK:er använder paneler som har en storlek på 512 x 512 bildpunkter för vägkartor och mindre 256 x 256 bildpunkter för satellitbilder. Azure Maps tillhandahåller raster- och vektorpaneler för 23 zoomnivåer, numrerade från 0 till 22. På zoomnivå 0 får hela världen plats på en enda ruta:

Ruta för världskarta

Zoomnivå 1 använder fyra rutor för att återge världen: en kvadrat på 2 x 2

2x2 karta rutlayout

Varje ytterligare zoomnivå quad-delar rutorna i den föregående, vilket skapar ett rutnät med 2zoom x 2zoom. Zoomnivå 22 är ett rutnät 222 x 222 eller 4 194 304 x 4 194 304 rutor (totalt 17 592 186 044 416 rutor).

De Azure Maps interaktiva kartkontrollerna för webben har stöd för 25 zoomnivåer, numrerade från 0 till 24. Även om vägdata endast är tillgängliga på zoomnivåerna när rutorna är tillgängliga.

Följande tabell innehåller en fullständig lista över värden för zoomnivåer där rutstorleken är 256 pixlar i kvadrat:

Zoomnivå Meter/pixel Meter/sida sida
0 156543 40075017
1 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
tretton 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

Pixel koordinater

Efter att ha valt projektion och skala som ska användas på varje zoomnivå kan vi konvertera geografiska koordinater till pixelkoordinater. Den fulla pixelbredden och höjden på en kartbild av världen för en viss zoomnivå beräknas som:

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

var mapHeight = mapWidth;

Eftersom kartans bredd och höjd är olika på varje zoomnivå, är pixelkoordinaterna det också. Pixeln i det övre vänstra hörnet av kartan har alltid pixelkoordinater (0, 0). Pixeln i det nedre högra hörnet på kartan har pixelkoordinater (width-1, height-1), eller refererar till ekvationerna i föregående avsnitt, (tileSize * 2zoom–1, tileSize * 2zoom–1). När du till exempel använder 512 kvadratiska rutor på nivå 2 sträcker sig pixelkoordinaterna från (0, 0) till (2047, 2047), så här:

Karta som visar pixeldimensioner

Med tanke på latitud och longitud i grader och detaljnivån beräknas pixelns XY-koordinater enligt följande:

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

Värdena för latitud och longitud antas vara på WGS 84-datumet. Även om Azure Maps använder en sfärisk projektion är det viktigt att konvertera alla geografiska koordinater till ett gemensamt datum. WGS 84 är det valda datumet. Longitudvärdet antas ligga mellan -180 grader och +180 grader, och latitudvärdet måste klippas ut så att det sträcker sig från -85,05112878 till 85,05112878. Genom att hålla fast vid dessa värden undviker man en singularitet vid polerna, och det säkerställer att den projicerade kartan är en kvadratisk form.

Koordinater för paneler

För att optimera prestanda för hämtning och visning av kartor skärs den renderade kartan ut i rutor. Antalet pixlar och antalet rutor skiljer sig åt på varje zoomnivå:

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

var numberOfTilesHigh = numberOfTilesWide;

Varje ruta får XY-koordinater som sträcker sig från (0, 0) längst upp till vänster till (2zoom–1, 2zoom–1) längst ned till höger. På zoomnivå 3 sträcker sig till exempel panelkoordinaterna från (0, 0) till (7, 7) enligt följande:

Karta över panelkoordinater

Med ett par pixel XY-koordinater kan du enkelt bestämma panelens XY-koordinater för rutan som innehåller den pixeln:

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

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

Paneler anropas efter zoomnivå. X- och y-koordinaterna motsvarar rutans position i rutnätet för den zoomnivån.

När du bestämmer vilken zoomnivå som ska användas ska du komma ihåg att varje plats har en fast position på sin ruta. Som ett resultat är antalet rutor som behövs för att visa ett visst område beroende av den specifika placeringen av zoomrutnätet på världskartan. Till exempel, om det finns två punkter 900 meter från varandra, kanske det bara krävs tre rutor för att visa en rutt mellan dem på zoomnivå 17. Men om den västra punkten är till höger om sin ruta och den östra punkten till vänster om sin ruta, kan den ta fyra brickor:

Zooma demoskala

När zoomnivån har bestämts kan x- och y-värdena beräknas. Den övre vänstra rutan i varje zoomrutnät är x=0, y=0; Den nedre högra rutan är vid x=2zoom-1, y=2zoom-1.

Här är zoomrutnätet för zoomnivå 1:

Zoomrutnät för zoomnivå 1

Quadkey index

Vissa mappningsplattformar använder en quadkey namngivningskonvention för indexering som kombinerar panelens ZY-koordinater till en endimensionssträng som kallas quadtree nycklar eller quadkeys förkortat. Var quadkey och en identifierar unikt en enskild panel på en viss detaljnivå, och den kan användas som en nyckel i vanliga B-trädindex för databasen. Azure Maps SDK:er stöder överlägg av panelskikt som använder quadkey namngivningskonvention utöver andra namngivningskonventioner som dokumenteras i dokumentet Lägg till ett panelskikt .

Anmärkning

Namnkonventionen quadkeys fungerar bara för zoomnivåer på en eller högre. Azure Maps SDK har stöd för zoomnivå 0, som är en enda kartpanel för hela världen.

För att konvertera rutkoordinater till a quadkeyinterfolieras bitarna av Y- och X-koordinaterna och resultatet tolkas som ett bas-4-tal (med inledande nollor bevarade) och konverteras till en sträng. Till exempel, givet panelen XY-koordinater för (3, 5) på nivå 3, bestäms de quadkey enligt följande:

tileX = 3 = 011 (base 2)

tileY = 5 = 101 (base 2)

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

Qquadkeys har flera intressanta egenskaper. För det första är längden på a quadkey (antalet siffror) lika med zoomnivån för motsvarande ruta. För det andra börjar en quadkey ruta med den quadkey överordnade rutan (den innehållande rutan på föregående nivå). Som du ser i följande exempel är panel 2 överordnad panelerna 20 till och med 23:

Quadkey kakel pyramid

Slutligen anger du quadkeys en endimensionell indexnyckel som vanligtvis bevarar närheten till brickor i XY-utrymme. Med andra ord har quadkeys två rutor som har närliggande XY-koordinater vanligtvis som är relativt nära varandra. Detta är viktigt för att optimera databasens prestanda, eftersom intilliggande paneler ofta begärs i grupper, och det är önskvärt att behålla dessa paneler på samma diskblock för att minimera antalet diskläsningar.

Källkod för matematisk panel

Följande exempelkod illustrerar hur du implementerar de funktioner som beskrivs i det här dokumentet. Dessa funktioner kan enkelt översättas till andra programmeringsspråk efter behov.

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

            //Bounding 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="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>
        /// <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. Default: 0</param>
        /// <param name="tileSize">The size of the tiles in the tile pyramid. Default: 512</param>
        /// <param name="maxZoom">Optional maximum zoom level to return. Useful when the bounding box represents a very small area. Default: 24</param>
        /// <param name="allowFloatZoom">Specifies if the returned zoom level should be a float or rounded down to an whole integer zoom level. Default: true</param>
        public static void BestMapView(BoundingBox bounds, double mapWidth, double mapHeight, out double centerLat, out double centerLon, out double zoom, int padding = 0, int tileSize = 512, double maxZoom = 24, bool allowFloatZoom = true)
        {
        	centerLat = 0;
        	centerLon = 0;
        	zoom = 0;
        
        	if (bounds != null && mapWidth > 0 && mapHeight > 0)
        	{
        		//Ensure padding is valid.
        		padding = Math.Abs(padding);
        
        		//Ensure max zoom is within valid range.
        		maxZoom = Clip(maxZoom, 0, 24);
        
        		//Do pixel calculations at zoom level 24 as that will provide a high level of visual accuracy.
        		int pixelZoom = 24;
        
        		//Calculate mercator pixel coordinate at zoom level 24.
        		var wnPixel = PositionToGlobalPixel(new double[] { bounds[0], bounds[3] }, pixelZoom, tileSize);
        		var esPixel = PositionToGlobalPixel(new double[] { bounds[2], bounds[1] }, pixelZoom, tileSize);
        
        		//Calculate the pixel distance between pixels for each axis.
        		double dx = esPixel[0] - wnPixel[0];
        		double dy = esPixel[1] - wnPixel[1];
        
        		//Calculate the average pixel positions to get the visual center.
        		double xAvg = (esPixel[0] + wnPixel[0]) / 2;
        		double yAvg = (esPixel[1] + wnPixel[1]) / 2;
        
        		//Determine if the bounding box crosses the antimeridian. (West pixel will be greater than East pixel).
        		if (wnPixel[0] > esPixel[0])
        		{
        			double mapSize = MapSize(24, tileSize);
        
        			//We are interested in the opposite area of the map. Calculate the opposite area and visual center.
        			dx = mapSize - Math.Abs(dx);
        
        			//Offset the visual center by half the global map width at zoom 24 on the x axis.
        			xAvg += mapSize / 2;
        		}
        
        		//Convert visual center pixel from zoom 24 to lngLat.
        		center = GlobalPixelToPosition(new Pixel(xAvg, yAvg), pixelZoom, tileSize);
        
        		//Calculate scale of screen pixels per unit on the Web Mercator plane.
        		double scaleX = (mapWidth - padding * 2) / Math.Abs(dx) * Math.Pow(2, pixelZoom);
        		double scaleY = (mapHeight - padding * 2) / Math.Abs(dy) * Math.Pow(2, pixelZoom);
        
        		//Calculate zoom levels based on the x/y scales. Choose the most zoomed out value.
        		zoom = Math.Max(0, Math.Min(maxZoom, Math.Log2(Math.Abs(Math.Min(scaleX, scaleY)))));
        
        		//Round down zoom level if float values are not desired.
        		if (!allowFloatZoom)
        		{
        			zoom = Math.Floor(zoom);
        		}
        	}
        
        	return new CameraOptions
        	{
        		Center = center,
        		Zoom = zoom
        	};
        }
    }
}

Anmärkning

De interaktiva kartkontrollerna i Azure Maps SDK:er har hjälpfunktioner för att konvertera mellan geospatiala positioner och visningsbildpunkter.

Nästa steg

Få direkt åtkomst till kartpaneler från Azure Maps REST-tjänster:

Läs mer om geospatiala begrepp: