Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Azure Maps verwendet das sphärische Mercator-Projektionskoordinatensystem (EPSG:3857). Eine Projektion ist das mathematische Modell, das verwendet wird, um den kugelförmigen Globus in eine flache Karte umzuwandeln. Bei der sphärischen Mercator-Projektion wird die Karte an den Polen gestreckt, um eine quadratische Karte zu erstellen. Diese Projektion verzerrt den Maßstab und den Bereich der Karte erheblich, weist jedoch zwei wichtige Eigenschaften auf, die diese Verzerrung überwiegen:
- Es handelt sich um eine konforme Projektion, was bedeutet, dass sie die Form relativ kleiner Objekte beibehält. Das Beibehalten der Form kleiner Objekte ist besonders wichtig bei der Darstellung von Luftbildern. Wir wollen zum Beispiel vermeiden, die Form von Gebäuden zu verzerren. Quadratische Gebäude sollten quadratisch und nicht rechteckig erscheinen.
- Es handelt sich um eine zylindrische Projektion. Norden und Süden sind immer oben und unten, und Westen und Osten sind immer links und rechts.
Um die Performance beim Abrufen und Anzeigen der Karte zu optimieren, wird die Karte in quadratische Kacheln unterteilt. Die Azure Maps SDKs verwenden Kacheln mit einer Größe von 512 x 512 Pixeln für Straßenkarten und kleineren 256 x 256 Pixeln für Satellitenbilder. Azure Maps bietet Raster- und Vektorkacheln für 23 Zoomstufen mit den Nummern 0 bis 22. Bei Zoomstufe 0 passt die gesamte Welt auf eine einzige Kachel:
Bei Zoomstufe 1 werden vier Kacheln verwendet, um die Welt zu rendern: ein 2 x 2 Quadrat
Jede zusätzliche Zoomstufe teilt die Kacheln der vorherigen vierfach, wodurch ein Raster von 2Zoom x 2Zoom entsteht. Die Zoomstufe 22 ist ein Raster von 222 x 222 oder 4.194.304 x 4.194.304 Kacheln (insgesamt 17.592.186.044.416 Kacheln).
Die interaktiven Kartensteuerelemente von Azure Maps für das Web unterstützen 25 Zoomstufen mit den Nummern 0 bis 24. Straßendaten sind jedoch nur in den Zoomstufen verfügbar, wenn die Kacheln verfügbar sind.
Die folgende Tabelle enthält die vollständige Liste der Werte für Zoomstufen, bei denen die Kachelgröße 256 Pixel im Quadrat beträgt:
Zoomstufe | Meter/Pixel | Meter/Fliesenseite |
---|---|---|
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 |
Pixel-Koordinaten
Nachdem wir die Projektion und den Maßstab ausgewählt haben, die bei jeder Zoomstufe verwendet werden sollen, können wir geografische Koordinaten in Pixelkoordinaten umwandeln. Die volle Pixelbreite und -höhe eines Kartenbildes der Welt für eine bestimmte Zoomstufe wird wie folgt berechnet:
var mapWidth = tileSize * Math.pow(2, zoom);
var mapHeight = mapWidth;
Da die Breite und Höhe der Karte bei jeder Zoomstufe unterschiedlich ist, sind es auch die Pixelkoordinaten. Das Pixel in der oberen linken Ecke der Karte hat immer Pixelkoordinaten (0, 0). Das Pixel in der unteren rechten Ecke der Karte hat Pixelkoordinaten (width-1, height-1) oder bezieht sich auf die Gleichungen im vorherigen Abschnitt (tileSize * 2zoom–1, tileSize * 2zoom–1). Wenn Sie z. B. 512 quadratische Kacheln auf Ebene 2 verwenden, liegen die Pixelkoordinaten zwischen (0, 0) und (2047, 2047), wie folgt:
Bei gegebenem Breiten- und Längengrad in Grad und der Detailgenauigkeit werden die XY-Koordinaten des Pixels wie folgt berechnet:
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);
Es wird davon ausgegangen, dass die Werte für den Breiten- und Längengrad auf dem WGS 84-Datum liegen. Auch wenn Azure Maps eine sphärische Projektion verwendet, ist es wichtig, alle geografischen Koordinaten in ein gemeinsames Datum zu konvertieren. WGS 84 ist das ausgewählte Datum. Es wird davon ausgegangen, dass der Längengrad zwischen -180 Grad und +180 Grad liegt, und der Breitengrad muss auf einen Bereich von -85,05112878 bis 85,05112878 zugeschnitten werden. Wenn Sie sich an diese Werte halten, wird eine Singularität an den Polen vermieden, und es wird sichergestellt, dass die projizierte Karte eine quadratische Form ist.
Koordinaten der Kacheln
Um die Performance beim Abrufen und Anzeigen von Karten zu optimieren, wird die gerenderte Karte in Kacheln geschnitten. Die Anzahl der Pixel und die Anzahl der Kacheln unterscheiden sich je nach Zoomstufe:
var numberOfTilesWide = Math.pow(2, zoom);
var numberOfTilesHigh = numberOfTilesWide;
Jede Kachel erhält XY-Koordinaten von (0, 0) oben links bis (2Zoom – 1, 2Zoom – 1) unten rechts. Bei Zoomstufe 3 liegen die Kachelkoordinaten z. B. wie folgt zwischen (0, 0) und (7, 7):
Wenn Sie ein Paar XY-Pixelkoordinaten verwenden, können Sie die XY-Koordinaten der Kachel, die dieses Pixel enthält, leicht bestimmen:
var tileX = Math.floor(pixelX / tileSize);
var tileY = Math.floor(pixelY / tileSize);
Kacheln werden nach Zoomstufe aufgerufen. Die X- und Y-Koordinaten entsprechen der Position der Kachel auf dem Raster für diese Zoomstufe.
Wenn Sie bestimmen, welche Zoomstufe verwendet werden soll, denken Sie daran, dass sich jede Position an einer festen Position auf ihrer Kachel befindet. Daher hängt die Anzahl der Kacheln, die zum Anzeigen eines bestimmten Gebiets benötigt werden, von der spezifischen Platzierung des Zoom-Gitters auf der Weltkarte ab. Wenn z. B. zwei Punkte 900 Meter voneinander entfernt sind, sind bei Zoomstufe 17 möglicherweise nur drei Kacheln erforderlich, um eine Route zwischen ihnen anzuzeigen. Wenn sich jedoch der westliche Punkt rechts von der Kachel und der östliche Punkt links von der Kachel befindet, können vier Kacheln benötigt werden:
Sobald die Zoomstufe bestimmt ist, können die x- und y-Werte berechnet werden. Die Kachel oben links in jedem Zoomraster ist x=0, y=0; Die Kachel unten rechts befindet sich bei X=2Zoom-1, Y=2Zoom-1.
Hier ist das Zoom-Raster für Zoomstufe 1:
Quadkey-Indizes
Einige Mapping-Plattformen verwenden eine quadkey
Indizierungsnamenskonvention, die die Kachel-ZY-Koordinaten zu einer eindimensionalen Zeichenfolge namens quadtree
keys oder quadkeys
kurz "keys" kombiniert. Jede quadkey
Kachel identifiziert eine einzelne Kachel auf einer bestimmten Detailebene eindeutig und kann als Schlüssel in gängigen B-Struktur-Indizes der Datenbank verwendet werden. Die Azure Maps SDKs unterstützen das Überlagern von Kachelebenen, die zusätzlich zu anderen Namenskonventionen die Namenskonvention verwenden quadkey
, wie im Dokument Hinzufügen einer Kachelebene dokumentiert.
Hinweis
Die quadkeys
Namenskonvention funktioniert nur für Zoomstufen von eins oder höher. Die Azure Maps SDKs unterstützen die Zoomstufe 0, d. h. eine einzelne Kartenkachel für die ganze Welt.
Um Kachelkoordinaten in a quadkey
zu konvertieren, werden die Bits der Y- und X-Koordinaten miteinander verschachtelt, und das Ergebnis wird als Zahl zur Basis 4 interpretiert (wobei führende Nullen beibehalten werden) und in eine Zeichenfolge konvertiert. Bei gegebenen XY-Koordinaten der Kachel (3, 5) auf Ebene 3 wird die quadkey
wie folgt bestimmt:
tileX = 3 = 011 (base 2)
tileY = 5 = 101 (base 2)
quadkey = 100111 (base 2) = 213 (base 4) = "213"
Qquadkeys
haben mehrere interessante Eigenschaften. Zunächst entspricht die Länge von a quadkey
(die Anzahl der Ziffern) dem Zoomfaktor der entsprechenden Kachel. Zweitens beginnt die quadkey
Kachel mit der quadkey
Kachel ihrer übergeordneten Kachel (der enthaltenden Kachel auf der vorherigen Ebene). Wie im folgenden Beispiel gezeigt, ist Kachel 2 das übergeordnete Element der Kacheln 20 bis 23:
quadkeys
Geben Sie abschließend einen eindimensionalen Indexschlüssel an, der in der Regel die Nähe von Kacheln im XY-Raum beibehält. Mit anderen Worten, zwei Kacheln, die nahe XY-Koordinaten haben, haben quadkeys
in der Regel relativ nahe beieinander. Dies ist wichtig für die Optimierung der Datenbankleistung, da benachbarte Kacheln häufig in Gruppen angefordert werden und es wünschenswert ist, diese Kacheln in denselben Datenträgerblöcken zu belassen, um die Anzahl der Datenträgerlesevorgänge zu minimieren.
Kachel-Mathematik-Quellcode
Der folgende Beispielcode veranschaulicht, wie die in diesem Dokument beschriebenen Funktionen implementiert werden. Diese Funktionen können bei Bedarf einfach in andere Programmiersprachen übersetzt werden.
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
};
}
}
}
Hinweis
Die interaktiven Kartensteuerelemente im Azure Maps SDK verfügen über Hilfsfunktionen zum Konvertieren zwischen räumlichen Positionen und Ansichtspixeln.
Nächste Schritte
Direkter Zugriff auf Kartenkacheln über die Azure Maps-REST-Dienste:
Erfahren Sie mehr über Geodatenkonzepte: