ملاحظة
يتطلب الوصول إلى هذه الصفحة تخويلاً. يمكنك محاولة تسجيل الدخول أو تغيير الدلائل.
يتطلب الوصول إلى هذه الصفحة تخويلاً. يمكنك محاولة تغيير الدلائل.
تستخدم خرائط Azure نظام إحداثيات إسقاط Mercator الكروي (EPSG:3857). الإسقاط هو النموذج الرياضي المستخدم لتحويل الكرة الأرضية الكروية إلى خريطة مسطحة. يقوم إسقاط Mercator الكروي بتمديد الخريطة على القطبين لإنشاء خريطة مربعة. هذا الإسقاط يشوه بشكل كبير حجم الخريطة ومساحةها ولكن له خاصيتان مهمتان تفوقان هذا التشويه:
- إنه إسقاط توافقي، ما يعني أنه يحافظ على شكل كائنات صغيرة نسبيا. الحفاظ على شكل الكائنات الصغيرة مهم بشكل خاص عند عرض الصور الجوية. على سبيل المثال، نريد تجنب تشويه شكل المباني. يجب أن تظهر المباني المربعة مربعة، وليست مستطيلة.
- إنه إسقاط أسطواني. الشمال والجنوب دائما لأعلى ولأسفل، وغرب وشرق دائما اليسار واليمين.
لتحسين أداء استرداد الخريطة وعرضها، يتم تقسيم الخريطة إلى تجانبات مربعة. تستخدم Azure Maps SDK الإطارات المتجانبة التي يبلغ حجمها 512 × 512 بكسل لخرائط الطرق، و256 × 256 بكسل أصغر لصور الأقمار الصناعية. توفر خرائط Azure تجانبات نقطية ومتجهة ل 23 مستوى تكبير/تصغير، مرقمة من 0 إلى 22. في مستوى التكبير/التصغير 0، يناسب العالم بأكمله لوحة واحدة:
يستخدم المستوى 1 من التكبير/التصغير أربعة تجانبات لعرض العالم: مربع 2 × 2
كل مستوى تكبير/تصغير إضافي يقسم الإطارات المتجانبة للمستوى السابق، مما يؤدي إلى إنشاء شبكة من 2تكبير/تصغير × 2تكبير/تصغير. مستوى التكبير/التصغير 22 هو شبكة2 2 ×2 22، أو 4,194,304 × 4,194,304 إطار متجانب (إجمالي الإطارات المتجانبة 17,592,186,044,416).
عناصر تحكم الخريطة التفاعلية لخرائط Azure لدعم الويب 25 مستوى تكبير/تصغير، مرقمة من 0 إلى 24. على الرغم من أن بيانات الطريق متوفرة فقط على مستويات التكبير/التصغير عند توفر الإطارات المتجانبة.
يوفر الجدول التالي القائمة الكاملة للقيم لمستويات التكبير/التصغير حيث يكون حجم اللوحة 256 بكسل مربع:
مستوى التكبير/التصغير | عدادات/بكسل | جانب العدادات/التجانب |
---|---|---|
1 | 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). يحتوي البكسل الموجود في الزاوية السفلية اليسرى من الخريطة على إحداثيات بكسل (العرض-1، الارتفاع-1) أو الإشارة إلى المعادلات في القسم السابق، (tileSize * 2zoom-1، tileSize * 2zoom-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 تستخدم إسقاطا كرويا، فمن المهم تحويل جميع الإحداثيات الجغرافية إلى مجموعة مشتركة. WGS 84 هو datum المحدد. يفترض أن تتراوح قيمة خط الطول من -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 لقاعدة البيانات الشائعة. تدعم SDKs خرائط Azure تراكب طبقات التجانب التي تستخدم quadkey
اصطلاح التسمية بالإضافة إلى اصطلاحات التسمية الأخرى كما هو موثق في مستند إضافة طبقة تجانب .
إشعار
quadkeys
يعمل اصطلاح التسمية فقط لمستوي التكبير/التصغير لمستوي واحد أو أكبر. يدعم Azure Maps SDK مستوى 0 للتكبير/التصغير وهو تجانب خريطة واحد للعالم بأكمله.
لتحويل إحداثيات التجانب إلى quadkey
، يتم تداخل بت إحداثيات Y وX، ويتم تفسير النتيجة على أنها رقم base-4 (مع الاحتفاظ بالأصفار البادئة) وتحويلها إلى سلسلة. على سبيل المثال، نظرا لإحداثيات 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. بمعنى آخر، عادة ما يكون لدى quadkeys
اثنين من الإطارات المتجانبة التي تحتوي على إحداثيات XY قريبة قريبة من بعضها البعض. هذا مهم لتحسين أداء قاعدة البيانات، لأنه غالبا ما يتم طلب الإطارات المتجانبة المجاورة في مجموعات، ومن المستحسن الاحتفاظ بهذه الإطارات المتجانبة على كتل القرص نفسها، من أجل تقليل عدد قراءات القرص.
التعليمات البرمجية لمصدر رياضيات التجانب
يوضح نموذج التعليمات البرمجية التالي كيفية تنفيذ الوظائف الموضحة في هذا المستند. يمكن ترجمة هذه الوظائف بسهولة إلى لغات برمجة أخرى حسب الحاجة.
using System;
using System.Text;
namespace AzureMaps
{
/// <summary>
/// Tile System math for the Spherical Mercator projection coordinate system (EPSG:3857)
/// </summary>
public static class TileMath
{
//Earth radius in meters.
private const double EarthRadius = 6378137;
private const double MinLatitude = -85.05112878;
private const double MaxLatitude = 85.05112878;
private const double MinLongitude = -180;
private const double MaxLongitude = 180;
/// <summary>
/// Clips a number to the specified minimum and maximum values.
/// </summary>
/// <param name="n">The number to clip.</param>
/// <param name="minValue">Minimum allowable value.</param>
/// <param name="maxValue">Maximum allowable value.</param>
/// <returns>The clipped value.</returns>
private static double Clip(double n, double minValue, double maxValue)
{
return Math.Min(Math.Max(n, minValue), maxValue);
}
/// <summary>
/// Calculates width and height of the map in pixels at a specific zoom level from -180 degrees to 180 degrees.
/// </summary>
/// <param name="zoom">Zoom Level to calculate width at</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>Width and height of the map in pixels</returns>
public static double MapSize(double zoom, int tileSize)
{
return Math.Ceiling(tileSize * Math.Pow(2, zoom));
}
/// <summary>
/// Calculates the Ground resolution at a specific degree of latitude in meters per pixel.
/// </summary>
/// <param name="latitude">Degree of latitude to calculate resolution at</param>
/// <param name="zoom">Zoom level to calculate resolution at</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>Ground resolution in meters per pixels</returns>
public static double GroundResolution(double latitude, double zoom, int tileSize)
{
latitude = Clip(latitude, MinLatitude, MaxLatitude);
return Math.Cos(latitude * Math.PI / 180) * 2 * Math.PI * EarthRadius / MapSize(zoom, tileSize);
}
/// <summary>
/// Determines the map scale at a specified latitude, level of detail, and screen resolution.
/// </summary>
/// <param name="latitude">Latitude (in degrees) at which to measure the map scale.</param>
/// <param name="zoom">Level of detail, from 1 (lowest detail) to 23 (highest detail).</param>
/// <param name="screenDpi">Resolution of the screen, in dots per inch.</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>The map scale, expressed as the denominator N of the ratio 1 : N.</returns>
public static double MapScale(double latitude, double zoom, int screenDpi, int tileSize)
{
return GroundResolution(latitude, zoom, tileSize) * screenDpi / 0.0254;
}
/// <summary>
/// Global Converts a Pixel coordinate into a geospatial coordinate at a specified zoom level.
/// Global Pixel coordinates are relative to the top left corner of the map (90, -180)
/// </summary>
/// <param name="pixel">Pixel coordinates in the format of [x, y].</param>
/// <param name="zoom">Zoom level</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>A position value in the format [longitude, latitude].</returns>
public static double[] GlobalPixelToPosition(double[] pixel, double zoom, int tileSize)
{
var mapSize = MapSize(zoom, tileSize);
var x = (Clip(pixel[0], 0, mapSize - 1) / mapSize) - 0.5;
var y = 0.5 - (Clip(pixel[1], 0, mapSize - 1) / mapSize);
return new double[] {
360 * x, //Longitude
90 - 360 * Math.Atan(Math.Exp(-y * 2 * Math.PI)) / Math.PI //Latitude
};
}
/// <summary>
/// Converts a point from latitude/longitude WGS-84 coordinates (in degrees) into pixel XY coordinates at a specified level of detail.
/// </summary>
/// <param name="position">Position coordinate in the format [longitude, latitude]</param>
/// <param name="zoom">Zoom level.</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>A global pixel coordinate.</returns>
public static double[] PositionToGlobalPixel(double[] position, int zoom, int tileSize)
{
var latitude = Clip(position[1], MinLatitude, MaxLatitude);
var longitude = Clip(position[0], MinLongitude, MaxLongitude);
var x = (longitude + 180) / 360;
var sinLatitude = Math.Sin(latitude * Math.PI / 180);
var y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI);
var mapSize = MapSize(zoom, tileSize);
return new double[] {
Clip(x * mapSize + 0.5, 0, mapSize - 1),
Clip(y * mapSize + 0.5, 0, mapSize - 1)
};
}
/// <summary>
/// Converts pixel XY coordinates into tile XY coordinates of the tile containing the specified pixel.
/// </summary>
/// <param name="pixel">Pixel coordinates in the format of [x, y].</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <param name="tileX">Output parameter receiving the tile X coordinate.</param>
/// <param name="tileY">Output parameter receiving the tile Y coordinate.</param>
public static void GlobalPixelToTileXY(double[] pixel, int tileSize, out int tileX, out int tileY)
{
tileX = (int)(pixel[0] / tileSize);
tileY = (int)(pixel[1] / tileSize);
}
/// <summary>
/// Performs a scale transform on a global pixel value from one zoom level to another.
/// </summary>
/// <param name="pixel">Pixel coordinates in the format of [x, y].</param>
/// <param name="oldZoom">The zoom level in which the input global pixel value is from.</param>
/// <returns>A scale pixel coordinate.</returns>
public static double[] ScaleGlobalPixel(double[] pixel, double oldZoom, double newZoom)
{
var scale = Math.Pow(2, oldZoom - newZoom);
return new double[] { pixel[0] * scale, pixel[1] * scale };
}
/// <summary>
/// Performs a scale transform on a set of global pixel values from one zoom level to another.
/// </summary>
/// <param name="pixels">A set of global pixel value from the old zoom level. Points are in the format [x,y].</param>
/// <param name="oldZoom">The zoom level in which the input global pixel values is from.</param>
/// <param name="newZoom">The new zoom level in which the output global pixel values should be aligned with.</param>
/// <returns>A set of global pixel values that has been scaled for the new zoom level.</returns>
public static double[][] ScaleGlobalPixels(double[][] pixels, double oldZoom, double newZoom)
{
var scale = Math.Pow(2, oldZoom - newZoom);
var output = new System.Collections.Generic.List<double[]>();
foreach (var p in pixels)
{
output.Add(new double[] { p[0] * scale, p[1] * scale });
}
return output.ToArray();
}
/// <summary>
/// Converts tile XY coordinates into a global pixel XY coordinates of the upper-left pixel of the specified tile.
/// </summary>
/// <param name="tileX">Tile X coordinate.</param>
/// <param name="tileY">Tile Y coordinate.</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <param name="pixelX">Output parameter receiving the X coordinate of the point, in pixels.</param>
/// <param name="pixelY">Output parameter receiving the Y coordinate of the point, in pixels.</param>
public static double[] TileXYToGlobalPixel(int tileX, int tileY, int tileSize)
{
return new double[] { tileX * tileSize, tileY * tileSize };
}
/// <summary>
/// Converts tile XY coordinates into a quadkey at a specified level of detail.
/// </summary>
/// <param name="tileX">Tile X coordinate.</param>
/// <param name="tileY">Tile Y coordinate.</param>
/// <param name="zoom">Zoom level</param>
/// <returns>A string containing the quadkey.</returns>
public static string TileXYToQuadKey(int tileX, int tileY, int zoom)
{
var quadKey = new StringBuilder();
for (int i = zoom; i > 0; i--)
{
char digit = '0';
int mask = 1 << (i - 1);
if ((tileX & mask) != 0)
{
digit++;
}
if ((tileY & mask) != 0)
{
digit++;
digit++;
}
quadKey.Append(digit);
}
return quadKey.ToString();
}
/// <summary>
/// Converts a quadkey into tile XY coordinates.
/// </summary>
/// <param name="quadKey">Quadkey of the tile.</param>
/// <param name="tileX">Output parameter receiving the tile X coordinate.</param>
/// <param name="tileY">Output parameter receiving the tile Y coordinate.</param>
/// <param name="zoom">Output parameter receiving the zoom level.</param>
public static void QuadKeyToTileXY(string quadKey, out int tileX, out int tileY, out int zoom)
{
tileX = tileY = 0;
zoom = quadKey.Length;
for (int i = zoom; i > 0; i--)
{
int mask = 1 << (i - 1);
switch (quadKey[zoom - i])
{
case '0':
break;
case '1':
tileX |= mask;
break;
case '2':
tileY |= mask;
break;
case '3':
tileX |= mask;
tileY |= mask;
break;
default:
throw new ArgumentException("Invalid QuadKey digit sequence.");
}
}
}
/// <summary>
/// Calculates the XY tile coordinates that a coordinate falls into for a specific zoom level.
/// </summary>
/// <param name="position">Position coordinate in the format [longitude, latitude]</param>
/// <param name="zoom">Zoom level</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <param name="tileX">Output parameter receiving the tile X position.</param>
/// <param name="tileY">Output parameter receiving the tile Y position.</param>
public static void PositionToTileXY(double[] position, int zoom, int tileSize, out int tileX, out int tileY)
{
var latitude = Clip(position[1], MinLatitude, MaxLatitude);
var longitude = Clip(position[0], MinLongitude, MaxLongitude);
var x = (longitude + 180) / 360;
var sinLatitude = Math.Sin(latitude * Math.PI / 180);
var y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI);
//tileSize needed in calculations as in rare cases the multiplying/rounding/dividing can make the difference of a pixel which can result in a completely different tile.
var mapSize = MapSize(zoom, tileSize);
tileX = (int)Math.Floor(Clip(x * mapSize + 0.5, 0, mapSize - 1) / tileSize);
tileY = (int)Math.Floor(Clip(y * mapSize + 0.5, 0, mapSize - 1) / tileSize);
}
/// <summary>
/// Calculates the tile quadkey strings that are within a specified viewport.
/// </summary>
/// <param name="position">Position coordinate in the format [longitude, latitude]</param>
/// <param name="zoom">Zoom level</param>
/// <param name="width">The width of the map viewport in pixels.</param>
/// <param name="height">The height of the map viewport in pixels.</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>A list of quadkey strings that are within the specified viewport.</returns>
public static string[] GetQuadkeysInView(double[] position, int zoom, int width, int height, int tileSize)
{
var p = PositionToGlobalPixel(position, zoom, tileSize);
var top = p[1] - height * 0.5;
var left = p[0] - width * 0.5;
var bottom = p[1] + height * 0.5;
var right = p[0] + width * 0.5;
var tl = GlobalPixelToPosition(new double[] { left, top }, zoom, tileSize);
var br = GlobalPixelToPosition(new double[] { right, bottom }, zoom, tileSize);
//Bounding box in the format: [west, south, east, north];
var bounds = new double[] { tl[0], br[1], br[0], tl[1] };
return GetQuadkeysInBoundingBox(bounds, zoom, tileSize);
}
/// <summary>
/// Calculates the tile quadkey strings that are within a bounding box at a specific zoom level.
/// </summary>
/// <param name="bounds">A bounding box defined as an array of numbers in the format of [west, south, east, north].</param>
/// <param name="zoom">Zoom level to calculate tiles for.</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>A list of quadkey strings.</returns>
public static string[] GetQuadkeysInBoundingBox(double[] bounds, int zoom, int tileSize)
{
var keys = new System.Collections.Generic.List<string>();
if (bounds != null && bounds.Length >= 4)
{
PositionToTileXY(new double[] { bounds[3], bounds[0] }, zoom, tileSize, out int tlX, out int tlY);
PositionToTileXY(new double[] { bounds[1], bounds[2] }, zoom, tileSize, out int brX, out int brY);
for (int x = tlX; x <= brX; x++)
{
for (int y = tlY; y <= brY; y++)
{
keys.Add(TileXYToQuadKey(x, y, zoom));
}
}
}
return keys.ToArray();
}
/// <summary>
/// Calculates the bounding box of a tile.
/// </summary>
/// <param name="tileX">Tile X coordinate</param>
/// <param name="tileY">Tile Y coordinate</param>
/// <param name="zoom">Zoom level</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid.</param>
/// <returns>A bounding box of the tile defined as an array of numbers in the format of [west, south, east, north].</returns>
public static double[] TileXYToBoundingBox(int tileX, int tileY, double zoom, int tileSize)
{
//Top left corner pixel coordinates
var x1 = (double)(tileX * tileSize);
var y1 = (double)(tileY * tileSize);
//Bottom right corner pixel coordinates
var x2 = (double)(x1 + tileSize);
var y2 = (double)(y1 + tileSize);
var nw = GlobalPixelToPosition(new double[] { x1, y1 }, zoom, tileSize);
var se = GlobalPixelToPosition(new double[] { x2, y2 }, zoom, tileSize);
return new double[] { nw[0], se[1], se[0], nw[1] };
}
/// <summary>
/// Calculates the best map view (center, zoom) for a bounding box on a map.
/// </summary>
/// <param name="bounds">A bounding box defined as an array of numbers in the format of [west, south, east, north].</param>
/// <param name="mapWidth">Map width in pixels.</param>
/// <param name="mapHeight">Map height in pixels.</param>
/// <param name="latitude">Output parameter receiving the center latitude coordinate.</param>
/// <param name="longitude">Output parameter receiving the center longitude coordinate.</param>
/// <param name="zoom">Output parameter receiving the zoom level</param>
/// <param name="padding">Width in pixels to use to create a buffer around the map. This is to keep markers from being cut off on the edge. Default: 0</param>
/// <param name="tileSize">The size of the tiles in the tile pyramid. Default: 512</param>
/// <param name="maxZoom">Optional maximum zoom level to return. Useful when the bounding box represents a very small area. Default: 24</param>
/// <param name="allowFloatZoom">Specifies if the returned zoom level should be a float or rounded down to an whole integer zoom level. Default: true</param>
public static void BestMapView(BoundingBox bounds, double mapWidth, double mapHeight, out double centerLat, out double centerLon, out double zoom, int padding = 0, int tileSize = 512, double maxZoom = 24, bool allowFloatZoom = true)
{
centerLat = 0;
centerLon = 0;
zoom = 0;
if (bounds != null && mapWidth > 0 && mapHeight > 0)
{
//Ensure padding is valid.
padding = Math.Abs(padding);
//Ensure max zoom is within valid range.
maxZoom = Clip(maxZoom, 0, 24);
//Do pixel calculations at zoom level 24 as that will provide a high level of visual accuracy.
int pixelZoom = 24;
//Calculate mercator pixel coordinate at zoom level 24.
var wnPixel = PositionToGlobalPixel(new double[] { bounds[0], bounds[3] }, pixelZoom, tileSize);
var esPixel = PositionToGlobalPixel(new double[] { bounds[2], bounds[1] }, pixelZoom, tileSize);
//Calculate the pixel distance between pixels for each axis.
double dx = esPixel[0] - wnPixel[0];
double dy = esPixel[1] - wnPixel[1];
//Calculate the average pixel positions to get the visual center.
double xAvg = (esPixel[0] + wnPixel[0]) / 2;
double yAvg = (esPixel[1] + wnPixel[1]) / 2;
//Determine if the bounding box crosses the antimeridian. (West pixel will be greater than East pixel).
if (wnPixel[0] > esPixel[0])
{
double mapSize = MapSize(24, tileSize);
//We are interested in the opposite area of the map. Calculate the opposite area and visual center.
dx = mapSize - Math.Abs(dx);
//Offset the visual center by half the global map width at zoom 24 on the x axis.
xAvg += mapSize / 2;
}
//Convert visual center pixel from zoom 24 to lngLat.
center = GlobalPixelToPosition(new Pixel(xAvg, yAvg), pixelZoom, tileSize);
//Calculate scale of screen pixels per unit on the Web Mercator plane.
double scaleX = (mapWidth - padding * 2) / Math.Abs(dx) * Math.Pow(2, pixelZoom);
double scaleY = (mapHeight - padding * 2) / Math.Abs(dy) * Math.Pow(2, pixelZoom);
//Calculate zoom levels based on the x/y scales. Choose the most zoomed out value.
zoom = Math.Max(0, Math.Min(maxZoom, Math.Log2(Math.Abs(Math.Min(scaleX, scaleY)))));
//Round down zoom level if float values are not desired.
if (!allowFloatZoom)
{
zoom = Math.Floor(zoom);
}
}
return new CameraOptions
{
Center = center,
Zoom = zoom
};
}
}
}
إشعار
تحتوي عناصر تحكم الخريطة التفاعلية في Azure Maps SDK على وظائف مساعد للتحويل بين المواضع الجغرافية المكانية ووحدات بكسل منفذ العرض.
الخطوات التالية
الوصول مباشرة إلى تجانبات الخريطة من خدمات AZURE Maps REST:
تعرف على المزيد حول المفاهيم الجيوفضائية: