다음을 통해 공유


확대/축소 수준 및 타일 그리드

Azure Maps는 구형 메르카토르 프로젝션 좌표계(EPSG:3857)를 사용합니다. 프로젝션은 구 모양의 지구를 평면 지도로 변환하는 데 사용되는 수학 모델입니다. 구형 메르카토르 투영은 극지방에서 지도를 늘려 정사각형 지도를 만듭니다. 이 프로젝션은 지도의 배율과 영역을 크게 왜곡하지만 이 왜곡보다 중요한 두 가지 속성이 있습니다.

  • 이는 비교적 작은 개체의 모양을 유지한다는 것을 의미하는 규칙적인 프로젝션입니다. 작은 개체의 모양을 유지하는 것은 항공 이미지를 표시할 때 특히 중요합니다. 예를 들어 건물의 모양이 왜곡되는 것을 방지하려고 합니다. 사각형 건물은 사각형이 아니라 정사각형으로 표시되어야 합니다.
  • 원통형 프로젝션입니다. 남북은 항상 위아래로, 서쪽과 동쪽은 항상 왼쪽과 오른쪽입니다.

지도 검색 및 표시의 성능을 최적화하기 위해 지도는 사각형 타일로 나뉩니다. Azure Maps SDK는 도로 맵의 경우 크기가 512 x 512픽셀이고 위성 이미지의 경우 256 x 256픽셀이 작은 타일을 사용합니다. Azure Maps는 0에서 22까지 번호가 매겨진 23개의 확대/축소 수준에 대한 래스터 및 벡터 타일을 제공합니다. 확대/축소 수준 0에서 전 세계는 단일 타일에 맞습니다.

세계 지도 타일

확대/축소 수준 1은 4개의 타일을 사용하여 세계를 렌더링합니다. 2 x 2 정사각형

2x2 지도 타일 레이아웃

각 추가된 확대/축소 레벨은 이전 것을 4분할하여, 2확대/축소 x 2확대/축소의 그리드를 만듭니다. 확대/축소 수준 22는 그리드2 22 x2 22 또는 4,194,304 x 4,194,304 타일(총 17,592,186,044,416 타일)입니다.

웹용 Azure Maps 대화형 맵 컨트롤은 0에서 24까지 번호가 매겨진 25개의 확대/축소 수준을 지원합니다. 도로 데이터는 타일을 사용할 수 있는 경우에만 확대/축소 수준에서 사용할 수 있습니다.

다음 표에서는 타일 크기가 256 픽셀 정사각형인 확대/축소 수준에 대한 값의 전체 목록을 제공합니다.

확대/축소 수준 미터/픽셀 타일 한 변당 미터 수
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 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

픽셀 좌표

각 확대/축소 수준에서 사용할 프로젝션 및 배율을 선택했으므로 지리적 좌표를 픽셀 좌표로 변환할 수 있습니다. 특정 확대/축소 수준에 대한 세계 지도 이미지의 전체 픽셀 너비와 높이는 다음과 같이 계산됩니다.

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

var mapHeight = mapWidth;

지도 너비와 높이가 각 확대/축소 수준에서 다르기 때문에 픽셀 좌표도 마찬가지입니다. 지도의 왼쪽 위 모서리에 있는 픽셀에는 항상 픽셀 좌표(0, 0)가 있습니다. 지도의 오른쪽 아래 모서리에 있는 픽셀에는 픽셀 좌표 (width-1, height-1) 또는 이전 섹션의 수식 참조 (tileSize * 2확대/축소-1, tileSize * 2확대/축소-1)가 있습니다. 예를 들어 수준 2에서 512 정사각형 타일을 사용하는 경우 픽셀 좌표 범위는 다음과 같이 (0, 0)에서 (2047, 2047)까지입니다.

픽셀 차원을 보여 주는 맵

위도 및 경도 및 세부 수준을 지정하면 픽셀 XY 좌표는 다음과 같이 계산됩니다.

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

위도 및 경도 값은 WGS 84 데이텀에 있는 것으로 간주됩니다. Azure Maps는 구형 프로젝션을 사용하지만 모든 지리적 좌표를 공통 데이텀으로 변환하는 것이 중요합니다. WGS 84는 선택한 데이텀입니다. 경도 값은 -180도에서 +180도까지의 범위로 간주되며 위도 값은 -85.05112878에서 85.05112878까지의 범위로 잘려야 합니다. 이러한 값을 준수하면 극에서의 특이성을 방지하고 프로젝션된 맵이 제곱 모양이 되도록 합니다.

타일 좌표

지도 검색 및 표시의 성능을 최적화하기 위해 렌더링된 맵은 타일로 잘려집니다. 픽셀 수와 타일 수는 각 확대/축소 수준에서 다릅니다.

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

var numberOfTilesHigh = numberOfTilesWide;

각 타일에는 왼쪽 상단의 (0, 0)에서 오른쪽 아래 의 (2확대/축소-1, 2확대/축소-1) 에 이르는 XY 좌표가 제공됩니다. 예를 들어 확대/축소 수준 3에서 타일 좌표는 다음과 같이 (0, 0)에서 (7, 7) 범위입니다.

타일 좌표 지도

픽셀 XY 좌표 쌍을 지정하면 해당 픽셀을 포함하는 타일의 타일 XY 좌표를 쉽게 확인할 수 있습니다.

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

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

타일은 확대/축소 수준에서 호출됩니다. x 및 y 좌표는 해당 확대/축소 수준에 대한 표에서 타일의 위치에 해당합니다.

사용할 줌 수준을 결정할 때, 각 위치는 타일 위에 고정되어 있습니다. 따라서 지정된 영역의 범위를 표시하는 데 필요한 타일 수는 세계 지도에서 확대/축소 그리드의 특정 배치에 따라 달라집니다. 예를 들어 900미터 간격이 2포인트인 경우 확대/축소 수준 17에서 둘 사이의 경로를 표시하는 데 3개의 타일만 걸릴 수 있습니다 . 그러나 서쪽 지점이 타일의 오른쪽에 있고 타일 왼쪽의 동쪽 점이 있는 경우 4개의 타일이 필요할 수 있습니다.

확대/축소 데모 크기 조정

확대/축소 수준이 결정되면 x 및 y 값을 계산할 수 있습니다. 각 확대/축소 그리드의 왼쪽 위 타일은 x=0, y=0입니다. 오른쪽 아래 타일은 x=2zoom-1, y=2zoom-1입니다.

확대/축소 수준 1의 그리드는 다음과 같습니다.

수준 1의 줌 그리드

쿼드키 인덱스

일부 매핑 플랫폼은 타일 ZY 좌표를 1차원 문자열로 결합하는 인덱싱 명명 규칙을 사용하여 이를 quadkey 키 또는 줄여서 quadtree 라고 합니다quadkeys. 각각 quadkey 은 특정 수준의 세부 정보에서 단일 타일을 고유하게 식별하며 공통 데이터베이스 B-트리 인덱스에서 키로 사용할 수 있습니다. Azure Maps SDK는 quadkey 명명 규칙을 사용하는 타일 계층의 오버레이를 지원하며, 이는 타일 계층 추가 문서에 설명된 다른 명명 규칙과 함께 제공됩니다.

비고

명명 규칙은 quadkeys 하나 이상의 확대/축소 수준에서만 작동합니다. Azure Maps SDK의 지원 확대/축소 수준 0은 전 세계에 대한 단일 맵 타일입니다.

타일 좌표를 A quadkey로 변환하려면 Y 및 X 좌표의 비트가 인터리브되고 결과는 base-4 숫자(선행 0이 유지됨)로 해석되고 문자열로 변환됩니다. 예를 들어, 수준 3에서 타일 XY 좌표가 (3, 5)로 지정된 경우, quadkey는 다음과 같이 결정됩니다.

tileX = 3 = 011 (base 2)

tileY = 5 = 101 (base 2)

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

Qquadkeys 에는 몇 가지 흥미로운 속성이 있습니다. 첫째, ( quadkey 숫자 수)의 길이는 해당 타일의 확대/축소 수준과 같습니다. 둘째, 모든 타일은 quadkey 이전 수준에 포함된 타일인 부모 타일의 quadkey로 시작합니다. 다음 예제와 같이 타일 2는 타일 20~23의 부모입니다.

쿼드키 타일 피라미드

마지막으로, quadkeys 일반적으로 XY 공간에서 타일의 근접성을 유지하는 1차원 인덱스 키를 제공합니다. 즉, 인접한 XY 좌표가 있는 두 개의 타일은 일반적으로 quadkeys 비교적 가깝게 위치합니다. 인접한 타일은 종종 그룹에서 요청되기 때문에 데이터베이스 성능을 최적화하는 데 중요하며 디스크 읽기 수를 최소화하기 위해 해당 타일을 동일한 디스크 블록에 유지하는 것이 좋습니다.

타일 수학 소스 코드

다음 샘플 코드에서는 이 문서에 설명된 함수를 구현하는 방법을 보여 줍니다. 이러한 함수는 필요에 따라 다른 프로그래밍 언어로 쉽게 번역할 수 있습니다.

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

비고

Azure Maps SDK의 대화형 지도 컨트롤에는 지리 공간적 위치와 뷰포트 픽셀 간에 변환하기 위한 도우미 함수가 있습니다.

다음 단계

Azure Maps REST 서비스에서 맵 타일에 직접 액세스:

지리 공간적 개념에 대해 자세히 알아보세요.