Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В Azure Maps используется система координат проекции Spherical Mercator (EPSG:3857). Проекция — это математическая модель, используемая для преобразования сферического глобуса в плоскую карту. Сферическая проекция Меркатор растягивает карту на полюсах, чтобы создать квадратную карту. Эта проекция значительно искажает масштаб и область карты, но имеет два важных свойства, которые перевешивают это искажение:
- Это конформальная проекция, которая означает, что она сохраняет форму относительно небольших объектов. Сохранение формы небольших объектов особенно важно при отображении воздушных изображений. Например, мы хотим избежать искажения формы зданий. Квадратные здания должны быть квадратными, а не прямоугольными.
- Это цилиндрическая проекция. Север и юг всегда вверх и вниз, а запад и восток всегда слева и справа.
Чтобы оптимизировать производительность извлечения карты и отображения, карта делится на квадратные плитки. Пакет SDK для Azure Maps использует плитки размером 512 x 512 пикселей для дорожных карт и меньше 256 x 256 пикселей для спутниковых изображений. Azure Maps предоставляет растровые и векторные плитки для 23 уровней масштабирования, нумеруемые от 0 до 22. На уровне 0 масштаба весь мир помещается на одну плитку:
Уровень масштабирования 1 использует четыре плитки для отрисовки мира: 2 x 2 квадрата
Каждый дополнительный уровень масштабирования делит плитки предыдущего, создавая сетку размером 2уровень увеличения x 2уровень увеличения. Уровень масштабирования 22 — это сетка22 x22 или 4 194 304 x 4 194 304 плитки (17 592 186 044 416 плиток).
Элементы управления интерактивной картой Azure Maps для веб-платформ поддерживают 25 уровней масштабирования, которые нумеруются от 0 до 24. Дорожные данные доступны только на уровнях масштабирования, где доступны плитки.
В следующей таблице представлен полный список значений для уровней масштабирования, где размер плитки составляет 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 | 19 567,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 |
| двадцать один | 0.074646 | 19.10926 |
| двадцать два | 0.037323 | 9.55463 |
| двадцать три | 0.0186615 | 4.777315 |
| двадцать четыре | 0.00933075 | 2.3886575 |
Координаты пикселей
Выбрав проекцию и масштаб для использования на каждом уровне масштабирования, можно преобразовать географические координаты в координаты пикселей. Полная ширина пикселя и высота изображения карты мира для определенного уровня масштабирования вычисляется следующим образом:
var mapWidth = tileSize * Math.pow(2, zoom);
var mapHeight = mapWidth;
Так как ширина и высота карты отличаются на каждом уровне масштабирования, поэтому координаты пикселей отличаются. Пиксель в левом верхнем углу карты всегда имеет координаты пикселей (0, 0). Пиксель в правом нижнем углу карты имеет координаты пикселей (ширина-1, высота-1), или, ссылаясь на уравнения в предыдущем разделе, (размер плитки * 2масштаб–1, размер плитки * 2масштаб–1). Например, при использовании 512 квадратных плиток на уровне 2 координаты пикселей варьируются от (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;
Каждая плитка получает координаты XY в диапазоне от (0, 0) в левом верхнем углу до (2масштабирования– 1, 2масштабирования–1) в правом нижнем углу. Например, на уровне приближения 3 координаты плитки варьируются от (0, 0) до (7, 7).
Учитывая пару координат XY пикселей, можно легко определить координаты плитки XY плитки, содержащей этот пиксель:
var tileX = Math.floor(pixelX / tileSize);
var tileY = Math.floor(pixelY / tileSize);
Плитки определяются по уровню масштабирования. Координаты x и y соответствуют положению плитки на сетке для этого уровня масштабирования.
При определении используемого уровня масштабирования следует помнить, что каждое расположение находится в фиксированном положении на плитке. В результате количество плиток, необходимых для отображения заданного пространства территории, зависит от конкретного размещения сетки масштабирования на карте мира. Например, если между ними есть две точки 900 метров, может потребоваться только три плитки для отображения маршрута между ними на уровне масштабирования 17. Однако если западная точка находится справа от своей плитки, а восточная точка слева от своей плитки, она может занять четыре плитки.
После определения уровня масштабирования можно вычислить значения x и y. В каждой сетке масштабирования верхняя левая ячейка — x=0, y=0; ячейка в правом нижнем углу находится на x=2zoom-1, y=2zoom-1.
Ниже приведена сетка масштабирования для уровня 1.
Quadkey индексы
Некоторые платформы картографии используют quadkey соглашение об именовании индексирования, которое объединяет координаты плитки ZY в одномерную строку, называемую quadtree ключами или quadkeys сокращённо. Каждый quadkey однозначно идентифицирует одну плитку на определенном уровне детализации и может использоваться в качестве ключа в индексах B-дерева базы данных. Пакеты SDK Для Azure Maps поддерживают наложение слоев плиток, использующих quadkey соглашение об именовании в дополнение к другим соглашениям об именовании, как описано в документе "Добавление слоя плиток ".
Замечание
Соглашение quadkeys об именовании работает только для уровней увеличения единицы или выше. Пакет SDK для Azure Maps поддерживает уровень масштабирования 0, который является одной плиткой карты для всего мира.
Чтобы преобразовать координаты плитки в quadkey, биты координат Y и X перемежаются, и результат интерпретируется как число в четверичной системе (с сохранением ведущих нулей) и преобразуется в строку. Например, учитывая координаты XY плитки (3, 5) на уровне 3, 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. Другими словами, две плитки, имеющие близлежащие координаты 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
};
}
}
}
Замечание
Элементы управления интерактивной карты в пакете SDK Azure Maps включают функции, облегчающие преобразование геопространственных позиций в пиксели на экране и наоборот.
Дальнейшие шаги
Прямой доступ к плиткам карты из служб REST Azure Maps:
Дополнительные сведения о геопространственных концепциях: