Azure Maps는 구형 메르카토르 프로젝션 좌표계(EPSG:3857)를 사용합니다. 프로젝션은 구 모양의 지구를 평면 지도로 변환하는 데 사용되는 수학 모델입니다. 구형 메르카토르 투영은 극지방에서 지도를 늘려 정사각형 지도를 만듭니다. 이 프로젝션은 지도의 배율과 영역을 크게 왜곡하지만 이 왜곡보다 중요한 두 가지 속성이 있습니다.
- 이는 비교적 작은 개체의 모양을 유지한다는 것을 의미하는 규칙적인 프로젝션입니다. 작은 개체의 모양을 유지하는 것은 항공 이미지를 표시할 때 특히 중요합니다. 예를 들어 건물의 모양이 왜곡되는 것을 방지하려고 합니다. 사각형 건물은 사각형이 아니라 정사각형으로 표시되어야 합니다.
- 원통형 프로젝션입니다. 남북은 항상 위아래로, 서쪽과 동쪽은 항상 왼쪽과 오른쪽입니다.
지도 검색 및 표시의 성능을 최적화하기 위해 지도는 사각형 타일로 나뉩니다. Azure Maps SDK는 도로 맵의 경우 크기가 512 x 512픽셀이고 위성 이미지의 경우 256 x 256픽셀이 작은 타일을 사용합니다. Azure Maps는 0에서 22까지 번호가 매겨진 23개의 확대/축소 수준에 대한 래스터 및 벡터 타일을 제공합니다. 확대/축소 수준 0에서 전 세계는 단일 타일에 맞습니다.
확대/축소 수준 1은 4개의 타일을 사용하여 세계를 렌더링합니다. 2 x 2 정사각형
각 추가된 확대/축소 레벨은 이전 것을 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의 그리드는 다음과 같습니다.
쿼드키 인덱스
일부 매핑 플랫폼은 타일 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 REST 서비스에서 맵 타일에 직접 액세스:
지리 공간적 개념에 대해 자세히 알아보세요.