Yakınlaştırma düzeyleri ve kutucuk kılavuzu
Azure Haritalar Spherical Mercator projeksiyon koordinat sistemini (EPSG: 3857) kullanır. Projeksiyon, küresel dünyayı düz bir haritaya dönüştürmek için kullanılan matematiksel modeldir. Spherical Mercator projeksiyonu, bir kare harita oluşturmak için haritayı kutuplara uzatıyor. Bu projeksiyon haritanın ölçeğini ve alanı önemli ölçüde çarpıtmaktadır, ancak bu bozulmaya ağır basan iki önemli özelliği vardır:
- Bu bir uyumluluk projeksiyonu, yani görece küçük nesnelerin şeklini korur. Hava fotoğrafları gösterirken küçük nesnelerin şeklini korumak özellikle önemlidir. Örneğin, binanın şeklini çarpıtmaktan kaçınmak istiyoruz. Kare binalar dikdörtgen değil kare şeklinde görünseler.
- Bu döngüsel bir projeksiyon. Kuzey ve güney her zaman yukarı ve aşağı, batı ve doğu da her zaman sola ve sağa doğru olur.
Harita alma ve görüntüleme performansını iyileştirmek için harita kare kutucuklara bölündü. Azure Haritalar SDK'sı, yol haritaları için 512 x 512 piksel boyuta ve uydu görüntüleri için 256 x 256 pikselden küçük olan kutucuklar kullanır. Azure Haritalar, 0 ile 22 arasında numaralara sahip 23 yakınlaştırma düzeyi için tarama ve vektör kutucukları sağlar. Yakınlaştırma düzeyinde 0, tüm dünya tek bir kutucuğunun üzerine sığar:
Yakınlaştırma düzeyi 1, dünyayı işlemek için dört kutucuk kullanır: 2 x 2 kare
Her ek yakınlaştırma düzeyi, öncekinin kutucuklarını bölerek 2 yakınlaştırma x 2yakınlaştırmalık bir kılavuzoluşturuyor. Yakınlaştırma düzeyi 22, kılavuz 222 x2 22veya 4.194.304 x 4.194.304 kutucuktır (toplamda 17.592.186.044.416 kutucuk).
Azure Haritalar web ve Android için etkileşimli harita denetimleri, 0 ile 24 arasında numaralara sahip 25 yakınlaştırma düzeylerini destekler. Yol verileri yalnızca kutucuklar kullanılabilir olduğunda yakınlaştırma düzeylerinde kullanılabilir olacaktır.
Aşağıdaki tabloda kutucuk boyutunun enlem 0'da 512 piksel kare olduğu yakınlaştırma düzeyleri için değerlerin tam listesi ve bulunmaktadır:
| Yakınlaştırma düzeyi | Metre/piksel | Metre/kutucuk tarafı |
|---|---|---|
| 0 | 156543 | 40075017 |
| 1 | 78271.5 | 20037508 |
| 2 | 39135.8 | 10018754 |
| 3 | 19567.88 | 5009377.1 |
| 4 | 9783.94 | 2504688.5 |
| 5 | 4891.97 | 1252344.3 |
| 6 | 2445.98 | 626172.1 |
| 7 | 1222.99 | 313086.1 |
| 8 | 611.5 | 156543 |
| 9 | 305.75 | 78271.5 |
| 10 | 152.87 | 39135.8 |
| 11 | 76.44 | 19567.9 |
| 12 | 38.219 | 9783.94 |
| 13 | 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.14929 | 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 |
Piksel koordinatları
Her yakınlaştırma düzeyinde kullanmak üzere projeksiyonu ve ölçeği seçtikten sonra coğrafi koordinatları piksel koordinatlarına dönüştüre 3. Belirli bir yakınlaştırma düzeyi için dünyanın harita görüntüsünün tam piksel genişliği ve yüksekliği şu şekilde hesaplanır:
var mapWidth = tileSize * Math.pow(2, zoom);
var mapHeight = mapWidth;
Harita genişliği ve yüksekliği her yakınlaştırma düzeyinde farklı olduğu için piksel koordinatları da farklıdır. Haritanın sol üst köşesindeki piksel her zaman piksel koordinatlarına (0, 0) sahip olur. Haritanın sağ alt köşesindeki pikselin piksel koordinatları (width-1, height-1) veya önceki bölümdeki denklemlere bakarak (tileSize * 2 zoom–1, tileSize * 2 zoom–1) pikseli vardır. Örneğin, düzey 2'de 512 kare kutucuk kullanırken piksel koordinatları şu şekilde (0, 0) ile (2047, 2047) arasında olabilir:
Derecelerde enlem ve boylam ve ayrıntı düzeyine göre piksel XY koordinatları aşağıdaki gibi hesaplanır:
var sinLatitude = Math.sin(latitude * Math.PI/180);
var pixelX = ((longitude + 180) / 360) * tileSize * Math.pow(2, zoom);
var pixelY = (0.5 – Math.log((1 + sinLatitude) / (1 – sinLatitude)) / (4 * Math.PI)) * tileSize * Math.pow(2, zoom);
Enlem ve boylam değerlerinin WGS 84'te olduğu varsayılır. Azure Haritalar küresel bir projeksiyon kullanıyor olsa da, tüm coğrafi koordinatları ortak bir veriye dönüştürmek önemlidir. Seçilen datum WGS 84'tir. Boylam değerinin -180 derece ile +180 derece arasında olduğu varsayılır ve enlem değeri -85,05112878 ile 85,05112878 arasında olacak şekilde kırpılır. Bu değerlere devam etmek, kutuplarda tekilliği önler ve projeli haritanın kareli bir şekile sahip olduğunu garanti eder.
Kutucuk koordinatları
Harita alma ve görüntüleme performansını iyileştirmek için işlenmiş harita kutucuklara kesilir. Piksel sayısı ve kutucuk sayısı her yakınlaştırma düzeyinde farklılık gösterir:
var numberOfTilesWide = Math.pow(2, zoom);
var numberOfTilesHigh = numberOfTilesWide;
Her kutucuya, sağ altta (0, 0) ile (2 yakınlaştırma–1,2 yakınlaştırma –1) arasında değişen XY koordinatları verilir. Örneğin, yakınlaştırma düzeyi 3'te kutucuk koordinatları aşağıdaki gibi (0, 0) ile (7, 7) arasında olabilir:
Bir çift piksel XY koordinatı ver, kutucuğun bu pikseli içeren kutucuğun XY koordinatlarını kolayca tespit edersiniz:
var tileX = Math.floor(pixelX / tileSize);
var tileY = Math.floor(pixelY / tileSize);
Kutucuklar yakınlaştırma düzeyiyle çağrılır. x ve y koordinatları, kutucuğun kılavuzda bu yakınlaştırma düzeyine ilişkin konumunu gösterir.
Hangi yakınlaştırma düzeyinin kullanıcağunu belirlerken, her konumun kutucuğun sabit bir konumunda olduğunu unutmayın. Sonuç olarak, belirli bir bölgeyi görüntülemek için gereken kutucuk sayısı, dünya haritasında yakınlaştırma kılavuzuna özgü yerleşime bağlıdır. Örneğin, aralarında 900 metre uzunluğunda iki nokta varsa, aralarında 17 yakınlaştırma düzeyinde bir yol görüntülemek için yalnızca üç kutucuk olabilir. Ancak, batı noktası kutucuğunun sağ tarafından, doğu noktası ise kutucuğun sol tarafından dört kutucuk olabilir:
Yakınlaştırma düzeyi belirlendiktan sonra x ve y değerleri hesaplanabilir. Her yakınlaştırma kılavuzunda sol üst kutucuk x=0, y=0'dır; sağ alttaki kutucuk x=2yakınlaştırma-1, y=2yakınlaştırma-1 olur.
Yakınlaştırma düzeyi 1 için yakınlaştırma kılavuzu şu şekildedir:
Dört anahtar dizinleri
Bazı eşleme platformları, kutucuk ZY koordinatlarını anahtarlar veya kısaca adlı tek boyut dizesinde birleştiren quadkey bir dizin adlandırma kuralı quadtree quadkeys kullanır. Her biri belirli bir ayrıntı düzeyinde tek bir kutucuğu benzersiz olarak tanımlar ve ortak veritabanı B ağacı dizinleri içinde quadkey anahtar olarak kullanılabilir. Azure Haritalar SDK'leri, Kutucuk katmanı ekleme belgesinde belgelenmiş olan diğer adlandırma kurallarına ek olarak adlandırma kuralını kullanan kutucuk katmanlarının quadkey katmanlamalarını destekler.
Not
Adlandırma quadkeys kuralı yalnızca bir veya daha yüksek yakınlaştırma düzeyleri için çalışır. Azure Haritalar SDK'sı, tüm dünya için tek bir harita kutucuğu olan yakınlaştırma düzeyi 0 desteği sunar.
Kutucuk koordinatlarını bir içine dönüştürmek için Y ve X koordinatlarının bitleri araya dönüştürülür ve sonuç base-4 (baştaki sıfırlar korunur) olarak yorumlanır ve bir dizeye quadkey dönüştürülür. Örneğin, (3, 5) düzeyi 3 olan XY kutucuğuna verilen quadkey koordinatlar aşağıdaki gibi belirlenir:
tileX = 3 = 011 (base 2)
tileY = 5 = 101 (base 2)
quadkey = 100111 (base 2) = 213 (base 4) = "213"
Qquadkeys birkaç ilgi çekici özelliği vardır. İlk olarak, bir quadkey (basamak sayısı) uzunluğu, ilgili kutucuğun yakınlaştırma düzeyine eşittir. İkincisi, quadkey herhangi bir kutucuğun üst quadkey kutucuğu (önceki düzeyde içeren kutucuk) ile başlar. Aşağıdaki örnekte gösterildiği gibi, 2. kutucuk 20 ile 23 arasında kutucukların üst öğesidir:
Son quadkeys olarak, genellikle XY alanı içinde kutucukların yakınlıklarını koruyan tek boyutlu bir dizin anahtarı sağlar. Başka bir deyişle, yakındaki XY koordinatlarına sahip iki kutucuk genellikle quadkeys birbirine göre daha yakın olur. Bu, veritabanı performansını en iyi duruma getirme açısından önemlidir çünkü komşu kutucuklar genellikle gruplarda istenmektedir ve disk okuma sayısını en aza indirmek için bu kutucukları aynı disk bloklarında tutmak tercih edilir.
Kutucuk matematik kaynak kodu
Aşağıdaki örnek kod, bu belgede açıklanan işlevlerin nasıl uygulandığını gösterir. Bu işlevler gerektiğinde diğer programlama dillerine kolayca çevrilebilir.
using System;
using System.Text;
namespace AzureMaps
{
/// <summary>
/// Tile System math for the Spherical Mercator projection coordinate system (EPSG:3857)
/// </summary>
public static class TileMath
{
//Earth radius in meters.
private const double EarthRadius = 6378137;
private const double MinLatitude = -85.05112878;
private const double MaxLatitude = 85.05112878;
private const double MinLongitude = -180;
private const double MaxLongitude = 180;
/// <summary>
/// Clips a number to the specified minimum and maximum values.
/// </summary>
/// <param name="n">The number to clip.</param>
/// <param name="minValue">Minimum allowable value.</param>
/// <param name="maxValue">Maximum allowable value.</param>
/// <returns>The clipped value.</returns>
private static double Clip(double n, double minValue, double maxValue)
{
return Math.Min(Math.Max(n, minValue), maxValue);
}
/// <summary>
/// Calculates width and height of the map in pixels at a specific zoom level from -180 degrees to 180 degrees.
/// </summary>
/// <param name="zoom">Zoom Level to calculate width at</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>Width and height of the map in pixels</returns>
public static double MapSize(double zoom, int tileSize)
{
return Math.Ceiling(tileSize * Math.Pow(2, zoom));
}
/// <summary>
/// Calculates the Ground resolution at a specific degree of latitude in meters per pixel.
/// </summary>
/// <param name="latitude">Degree of latitude to calculate resolution at</param>
/// <param name="zoom">Zoom level to calculate resolution at</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>Ground resolution in meters per pixels</returns>
public static double GroundResolution(double latitude, double zoom, int tileSize)
{
latitude = Clip(latitude, MinLatitude, MaxLatitude);
return Math.Cos(latitude * Math.PI / 180) * 2 * Math.PI * EarthRadius / MapSize(zoom, tileSize);
}
/// <summary>
/// Determines the map scale at a specified latitude, level of detail, and screen resolution.
/// </summary>
/// <param name="latitude">Latitude (in degrees) at which to measure the map scale.</param>
/// <param name="zoom">Level of detail, from 1 (lowest detail) to 23 (highest detail).</param>
/// <param name="screenDpi">Resolution of the screen, in dots per inch.</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>The map scale, expressed as the denominator N of the ratio 1 : N.</returns>
public static double MapScale(double latitude, double zoom, int screenDpi, int tileSize)
{
return GroundResolution(latitude, zoom, tileSize) * screenDpi / 0.0254;
}
/// <summary>
/// Global Converts a Pixel coordinate into a geospatial coordinate at a specified zoom level.
/// Global Pixel coordinates are relative to the top left corner of the map (90, -180)
/// </summary>
/// <param name="pixel">Pixel coordinates in the format of [x, y].</param>
/// <param name="zoom">Zoom level</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>A position value in the format [longitude, latitude].</returns>
public static double[] GlobalPixelToPosition(double[] pixel, double zoom, int tileSize)
{
var mapSize = MapSize(zoom, tileSize);
var x = (Clip(pixel[0], 0, mapSize - 1) / mapSize) - 0.5;
var y = 0.5 - (Clip(pixel[1], 0, mapSize - 1) / mapSize);
return new double[] {
360 * x, //Longitude
90 - 360 * Math.Atan(Math.Exp(-y * 2 * Math.PI)) / Math.PI //Latitude
};
}
/// <summary>
/// Converts a point from latitude/longitude WGS-84 coordinates (in degrees) into pixel XY coordinates at a specified level of detail.
/// </summary>
/// <param name="position">Position coordinate in the format [longitude, latitude]</param>
/// <param name="zoom">Zoom level.</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>A global pixel coordinate.</returns>
public static double[] PositionToGlobalPixel(double[] position, int zoom, int tileSize)
{
var latitude = Clip(position[1], MinLatitude, MaxLatitude);
var longitude = Clip(position[0], MinLongitude, MaxLongitude);
var x = (longitude + 180) / 360;
var sinLatitude = Math.Sin(latitude * Math.PI / 180);
var y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI);
var mapSize = MapSize(zoom, tileSize);
return new double[] {
Clip(x * mapSize + 0.5, 0, mapSize - 1),
Clip(y * mapSize + 0.5, 0, mapSize - 1)
};
}
/// <summary>
/// Converts pixel XY coordinates into tile XY coordinates of the tile containing the specified pixel.
/// </summary>
/// <param name="pixel">Pixel coordinates in the format of [x, y].</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <param name="tileX">Output parameter receiving the tile X coordinate.</param>
/// <param name="tileY">Output parameter receiving the tile Y coordinate.</param>
public static void GlobalPixelToTileXY(double[] pixel, int tileSize, out int tileX, out int tileY)
{
tileX = (int)(pixel[0] / tileSize);
tileY = (int)(pixel[1] / tileSize);
}
/// <summary>
/// Performs a scale transform on a global pixel value from one zoom level to another.
/// </summary>
/// <param name="pixel">Pixel coordinates in the format of [x, y].</param>
/// <param name="oldZoom">The zoom level in which the input global pixel value is from.</param>
/// <returns>A scale pixel coordinate.</returns>
public static double[] ScaleGlobalPixel(double[] pixel, double oldZoom, double newZoom)
{
var scale = Math.Pow(2, oldZoom - newZoom);
return new double[] { pixel[0] * scale, pixel[1] * scale };
}
/// <summary>
/// Performs a scale transform on a set of global pixel values from one zoom level to another.
/// </summary>
/// <param name="pixels">A set of global pixel value from the old zoom level. Points are in the format [x,y].</param>
/// <param name="oldZoom">The zoom level in which the input global pixel values is from.</param>
/// <param name="newZoom">The new zoom level in which the output global pixel values should be aligned with.</param>
/// <returns>A set of global pixel values that has been scaled for the new zoom level.</returns>
public static double[][] ScaleGlobalPixels(double[][] pixels, double oldZoom, double newZoom)
{
var scale = Math.Pow(2, oldZoom - newZoom);
var output = new System.Collections.Generic.List<double[]>();
foreach (var p in pixels)
{
output.Add(new double[] { p[0] * scale, p[1] * scale });
}
return output.ToArray();
}
/// <summary>
/// Converts tile XY coordinates into a global pixel XY coordinates of the upper-left pixel of the specified tile.
/// </summary>
/// <param name="tileX">Tile X coordinate.</param>
/// <param name="tileY">Tile Y coordinate.</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <param name="pixelX">Output parameter receiving the X coordinate of the point, in pixels.</param>
/// <param name="pixelY">Output parameter receiving the Y coordinate of the point, in pixels.</param>
public static double[] TileXYToGlobalPixel(int tileX, int tileY, int tileSize)
{
return new double[] { tileX * tileSize, tileY * tileSize };
}
/// <summary>
/// Converts tile XY coordinates into a quadkey at a specified level of detail.
/// </summary>
/// <param name="tileX">Tile X coordinate.</param>
/// <param name="tileY">Tile Y coordinate.</param>
/// <param name="zoom">Zoom level</param>
/// <returns>A string containing the quadkey.</returns>
public static string TileXYToQuadKey(int tileX, int tileY, int zoom)
{
var quadKey = new StringBuilder();
for (int i = zoom; i > 0; i--)
{
char digit = '0';
int mask = 1 << (i - 1);
if ((tileX & mask) != 0)
{
digit++;
}
if ((tileY & mask) != 0)
{
digit++;
digit++;
}
quadKey.Append(digit);
}
return quadKey.ToString();
}
/// <summary>
/// Converts a quadkey into tile XY coordinates.
/// </summary>
/// <param name="quadKey">Quadkey of the tile.</param>
/// <param name="tileX">Output parameter receiving the tile X coordinate.</param>
/// <param name="tileY">Output parameter receiving the tile Y coordinate.</param>
/// <param name="zoom">Output parameter receiving the zoom level.</param>
public static void QuadKeyToTileXY(string quadKey, out int tileX, out int tileY, out int zoom)
{
tileX = tileY = 0;
zoom = quadKey.Length;
for (int i = zoom; i > 0; i--)
{
int mask = 1 << (i - 1);
switch (quadKey[zoom - i])
{
case '0':
break;
case '1':
tileX |= mask;
break;
case '2':
tileY |= mask;
break;
case '3':
tileX |= mask;
tileY |= mask;
break;
default:
throw new ArgumentException("Invalid QuadKey digit sequence.");
}
}
}
/// <summary>
/// Calculates the XY tile coordinates that a coordinate falls into for a specific zoom level.
/// </summary>
/// <param name="position">Position coordinate in the format [longitude, latitude]</param>
/// <param name="zoom">Zoom level</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <param name="tileX">Output parameter receiving the tile X position.</param>
/// <param name="tileY">Output parameter receiving the tile Y position.</param>
public static void PositionToTileXY(double[] position, int zoom, int tileSize, out int tileX, out int tileY)
{
var latitude = Clip(position[1], MinLatitude, MaxLatitude);
var longitude = Clip(position[0], MinLongitude, MaxLongitude);
var x = (longitude + 180) / 360;
var sinLatitude = Math.Sin(latitude * Math.PI / 180);
var y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI);
//tileSize needed in calculations as in rare cases the multiplying/rounding/dividing can make the difference of a pixel which can result in a completely different tile.
var mapSize = MapSize(zoom, tileSize);
tileX = (int)Math.Floor(Clip(x * mapSize + 0.5, 0, mapSize - 1) / tileSize);
tileY = (int)Math.Floor(Clip(y * mapSize + 0.5, 0, mapSize - 1) / tileSize);
}
/// <summary>
/// Calculates the tile quadkey strings that are within a specified viewport.
/// </summary>
/// <param name="position">Position coordinate in the format [longitude, latitude]</param>
/// <param name="zoom">Zoom level</param>
/// <param name="width">The width of the map viewport in pixels.</param>
/// <param name="height">The height of the map viewport in pixels.</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>A list of quadkey strings that are within the specified viewport.</returns>
public static string[] GetQuadkeysInView(double[] position, int zoom, int width, int height, int tileSize)
{
var p = PositionToGlobalPixel(position, zoom, tileSize);
var top = p[1] - height * 0.5;
var left = p[0] - width * 0.5;
var bottom = p[1] + height * 0.5;
var right = p[0] + width * 0.5;
var tl = GlobalPixelToPosition(new double[] { left, top }, zoom, tileSize);
var br = GlobalPixelToPosition(new double[] { right, bottom }, zoom, tileSize);
//Boudning 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="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</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</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>
public static void BestMapView(double[] bounds, double mapWidth, double mapHeight, int padding, int tileSize, out double centerLat, out double centerLon, out double zoom)
{
if (bounds == null || bounds.Length < 4)
{
centerLat = 0;
centerLon = 0;
zoom = 1;
return;
}
double boundsDeltaX;
//Check if east value is greater than west value which would indicate that bounding box crosses the antimeridian.
if (bounds[2] > bounds[0])
{
boundsDeltaX = bounds[2] - bounds[0];
centerLon = (bounds[2] + bounds[0]) / 2;
}
else
{
boundsDeltaX = 360 - (bounds[0] - bounds[2]);
centerLon = ((bounds[2] + bounds[0]) / 2 + 360) % 360 - 180;
}
var ry1 = Math.Log((Math.Sin(bounds[1] * Math.PI / 180) + 1) / Math.Cos(bounds[1] * Math.PI / 180));
var ry2 = Math.Log((Math.Sin(bounds[3] * Math.PI / 180) + 1) / Math.Cos(bounds[3] * Math.PI / 180));
var ryc = (ry1 + ry2) / 2;
centerLat = Math.Atan(Math.Sinh(ryc)) * 180 / Math.PI;
var resolutionHorizontal = boundsDeltaX / (mapWidth - padding * 2);
var vy0 = Math.Log(Math.Tan(Math.PI * (0.25 + centerLat / 360)));
var vy1 = Math.Log(Math.Tan(Math.PI * (0.25 + bounds[3] / 360)));
var zoomFactorPowered = (mapHeight * 0.5 - padding) / (40.7436654315252 * (vy1 - vy0));
var resolutionVertical = 360.0 / (zoomFactorPowered * tileSize);
var resolution = Math.Max(resolutionHorizontal, resolutionVertical);
zoom = Math.Log(360 / (resolution * tileSize), 2);
}
}
}
Not
Azure Haritalar SDK'larında yer alan etkileşimli harita denetimleri, jeo-uzamsal konumlar ve görünümport pikselleri arasında dönüştürmeye yardımcı işlevlere sahiptir.
Sonraki adımlar
Azure Haritalar REST hizmetlerinden harita kutucuklarına doğrudan erişin:
Jeo-uzamsal kavramlar hakkında daha fazla bilgi edinmek için: