Aracılığıyla paylaş


Yakınlaştırma seviyeleri ve karo ızgarası

Azure Haritalar, Küresel Mercator projeksiyon koordinat sistemini (EPSG:3857) kullanır. Projeksiyon, küresel küreyi düz bir haritaya dönüştürmek için kullanılan matematiksel modeldir. Küresel Merkatör projeksiyonu, kare bir harita oluşturmak için haritayı kutuplara doğru uzatır. Bu projeksiyon, haritanın ölçeğini ve alanını önemli ölçüde bozar, ancak bu bozulmadan daha ağır basan iki önemli özelliğe sahiptir:

  • Bu uyumlu bir projeksiyondur, bu da nispeten küçük nesnelerin şeklini koruduğu anlamına gelir. Havadan görüntüleri gösterirken küçük nesnelerin şeklini korumak özellikle önemlidir. Örneğin, binaların şeklini bozmaktan kaçınmak istiyoruz. Kare binalar dikdörtgen değil kare şeklinde görünmelidir.
  • Silindirik bir projeksiyon. Kuzey ve güney her zaman yukarı ve aşağıdır ve batı ve doğu her zaman sol ve sağdır.

Harita alma ve görüntüleme performansını optimize etmek için, harita kare döşemelere bölünmüştür. Azure Haritalar SDK'sı, yol haritaları için 512 x 512 piksel boyutunda ve uydu görüntüleri için daha küçük 256 x 256 piksel boyutunda kutucuklar kullanır. Azure Haritalar, 0 ile 22 arasında numaralandırılmış 23 yakınlaştırma düzeyi için raster ve vektör kutucukları sağlar. Yakınlaştırma düzeyi 0'da, tüm dünya tek bir kutucuğa sığar:

Dünya haritası döşemesi

Yakınlaştırma düzeyi 1, dünyayı oluşturmak için dört karo kullanır: 2 x 2 kare

2x2 harita döşeme düzeni

Her ek yakınlaştırma seviyesi, bir öncekinin döşemelerini dörtlü olarak bölerek 2 yakınlaştırma x 2yakınlaştırmadan oluşan bir ızgara oluşturur. Yakınlaştırma düzeyi 22, ızgara 222 x 222 veya 4.194.304 x 4.194.304 karodur (toplamda 17.592.186.044.416 karo).

Web için Azure Haritalar etkileşimli harita denetimleri, 0 ile 24 arasında numaralandırılmış 25 yakınlaştırma düzeyini destekler. Her ne kadar yol verileri yalnızca döşemeler mevcut olduğunda yakınlaştırma seviyelerinde mevcut olsa da.

Aşağıdaki tabloda, döşeme boyutunun 256 piksel kare olduğu yakınlaştırma düzeyleri için değerlerin tam listesi verilmiştir:

Yakınlaştırma düzeyi Metre/piksel Metre/karo tarafı
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
13 (on üç) 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
yirmi dört 0.00933075 2.3886575

Piksel koordinatları

Her yakınlaştırma düzeyinde kullanılacak projeksiyonu ve ölçeği seçtikten sonra, coğrafi koordinatları piksel koordinatlarına dönüştürebiliriz. Belirli bir yakınlaştırma düzeyi için dünyanın bir harita görüntüsünün tam piksel genişliği ve yüksekliği şu şekilde hesaplanır:

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

var mapHeight = mapWidth;

Harita genişliği ve yüksekliği her yakınlaştırma düzeyinde farklı olduğundan, piksel koordinatları da farklıdır. Haritanın sol üst köşesindeki piksel her zaman piksel koordinatlarına (0, 0) sahiptir. Haritanın sağ alt köşesindeki piksel, piksel koordinatlarına (genişlik-1, yükseklik-1) veya önceki bölümdeki denklemlere atıfta bulunur (tileSize * 2zoom–1, tileSize * 2zoom–1). Örneğin, 2. seviyede 512 kare karo kullanıldığında, piksel koordinatları (0, 0) ile (2047, 2047) arasında değişir, örneğin:

Piksel boyutlarını gösteren harita

Derece cinsinden enlem ve boylam ve ayrıntı düzeyi göz önüne alındığında, piksel XY koordinatları aşağıdaki gibi hesaplanır:

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

Enlem ve boylam değerlerinin WGS 84 datumunda olduğu varsayılmaktadır. Azure Haritalar küresel bir projeksiyon kullansa da, tüm coğrafi koordinatları ortak bir veriye dönüştürmek önemlidir. WGS 84 seçilen veridir. Boylam değerinin -180 derece ile +180 derece arasında olduğu varsayılır ve enlem değerinin -85.05112878 ile 85.05112878 arasında olacak şekilde kırpılması gerekir. Bu değerlere bağlı kalmak, kutuplarda bir tekilliği önler ve yansıtılan haritanın kare şeklinde olmasını sağlar.

Döşeme koordinatları

Harita alma ve görüntüleme performansını optimize etmek için, oluşturulan harita döşemeler halinde kesilir. Piksel sayısı ve döşeme sayısı her yakınlaştırma düzeyinde farklılık gösterir:

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

var numberOfTilesHigh = numberOfTilesWide;

Her döşemeye sol üstte (0, 0) ile sağ altta (2yakınlaştırma–1, 2yakınlaştırma–1) arasında değişen XY koordinatları verilir. Örneğin, yakınlaştırma düzeyi 3'te, döşeme koordinatları aşağıdaki gibi (0, 0) ile (7, 7) arasında değişir:

Döşeme koordinatlarının haritası

Bir çift piksel XY koordinatı verildiğinde, bu pikseli içeren döşemenin döşeme XY koordinatlarını kolayca belirleyebilirsiniz:

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

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

Döşemeler yakınlaştırma seviyesine göre çağrılır. x ve y koordinatları, söz konusu yakınlaştırma düzeyi için döşemenin ızgara üzerindeki konumuna karşılık gelir.

Hangi yakınlaştırma düzeyinin kullanılacağını belirlerken, her konumun kutucuğu üzerinde sabit bir konumda olduğunu unutmayın. Sonuç olarak, belirli bir alanı görüntülemek için gereken karo sayısı, yakınlaştırma ızgarasının dünya haritasındaki belirli yerleşimine bağlıdır. Örneğin, birbirinden 900 metre uzakta iki nokta varsa, yakınlaştırma seviyesi 17'de aralarında bir rota göstermek için yalnızca üç karo gerekebilir. Bununla birlikte, batı noktası döşemesinin sağında ve doğu noktası döşemesinin solundaysa, dört döşeme alabilir:

Yakınlaştırma demo ölçeği

Yakınlaştırma seviyesi belirlendikten sonra, x ve y değerleri hesaplanabilir. Her yakınlaştırma ızgarasındaki sol üst döşeme x=0, y=0'dır; Sağ alttaki döşeme X=2Zoom-1, Y=2Zoom-1 konumundadır.

Yakınlaştırma düzeyi 1 için yakınlaştırma kılavuzu şu şekildedir:

Yakınlaştırma düzeyi 1 için yakınlaştırma ızgarası

Dörtlü endeksler

Bazı eşleme platformları, döşeme ZY koordinatlarını anahtarlar veya quadkey kısaca adı verilen quadtree tek boyutlu bir dizede birleştiren bir quadkeys dizin oluşturma adlandırma kuralı kullanır. Her quadkey biri, belirli bir ayrıntı düzeyinde tek bir döşemeyi benzersiz bir şekilde tanımlar ve ortak veritabanı B-ağacı dizinlerinde anahtar olarak kullanılabilir. Azure Haritalar SDK'ları, quadkey belgesinde belgelendiği gibi diğer adlandırma kurallarına ek olarak adlandırma kuralını kullanan döşeme katmanlarının üst üste bindirilmesini destekler.

Uyarı

Adlandırma quadkeys kuralı yalnızca bir veya daha yüksek yakınlaştırma düzeyleri için çalışır. Azure Haritalar SDK'sı, tüm dünya için tek bir harita kutucuğu olan yakınlaştırma düzeyi 0'ı destekler.

Döşeme koordinatlarını a'ya quadkeydönüştürmek için, Y ve X koordinatlarının bitleri serpiştirilir ve sonuç 4 tabanlı bir sayı olarak yorumlanır (baştaki sıfırlar korunur) ve bir dizeye dönüştürülür. Örneğin, 3. seviyede (3, 5) karo XY koordinatları verildiğinde, aşağıdaki quadkey gibi belirlenir:

tileX = 3 = 011 (base 2)

tileY = 5 = 101 (base 2)

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

Qquadkeys birkaç ilginç özelliğe sahip. İlk olarak, a'nın quadkey uzunluğu (basamak sayısı) karşılık gelen döşemenin yakınlaştırma düzeyine eşittir. İkinci olarak, herhangi bir döşemenin quadkey , üst döşemesiyle quadkey (önceki seviyedeki içeren döşeme) başlar. Aşağıdaki örnekte gösterildiği gibi, kutucuk 2, 20 ile 23 arasındaki kutucukların üst öğesidir:

Dörtlü kiremit piramidi

Son olarak, quadkeys genellikle XY alanındaki döşemelerin yakınlığını koruyan tek boyutlu bir dizin anahtarı sağlayın. Başka bir deyişle, yakın XY koordinatlarına sahip iki döşeme genellikle birbirine nispeten yakın olanlara sahiptir quadkeys . Bu, veritabanı performansını en iyi duruma getirmek için önemlidir, çünkü komşu kutucuklar genellikle gruplar halinde istenir ve disk okuma sayısını en aza indirmek için bu kutucukların aynı disk bloklarında tutulması tercih edilir.

Döşeme matematik kaynak kodu

Aşağıdaki örnek kod, bu belgede açıklanan işlevlerin nasıl uygulanacağını gösterir. Bu işlevler, gerektiğinde diğer programlama dillerine kolayca çevrilebilir.

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

Uyarı

Azure Haritalar SDK'larındaki etkileşimli harita denetimleri, jeo-uzamsal konumlar ve görünüm penceresi pikselleri arasında dönüştürme yapmak için yardımcı işlevlere sahiptir.

Sonraki Adımlar

Azure Haritalar REST hizmetlerinden harita kutucuklarına doğrudan erişin:

Jeo-uzamsal kavramlar hakkında daha fazla bilgi edinin: