次の方法で共有


ズームレベルとタイルグリッド

Azure Maps では、球面メルカトル投影座標系 (EPSG:3857) が使用されます。 投影は、球面地球をフラット マップに変換するために使用される数学モデルです。 球面メルカトル図法では、マップが極に引き伸ばされ、正方形のマップが作成されます。 この投影は、マップの縮尺と面積を大幅に歪めますが、この歪みを上回る 2 つの重要なプロパティがあります。

  • これはコンフォーマル 投影であり、比較的小さなオブジェクトの形状を保持することを意味します。 航空写真を表示する場合は、小さなオブジェクトの形状を維持することが特に重要です。 たとえば、建物の形状が歪むのを防ぐ必要があります。 四角い建物は長方形ではなく、正方形に見えるべきです。
  • 円柱状の投影です。 北と南は常に上下で、西と東は常に左と右です。

マップの取得と表示のパフォーマンスを最適化するために、マップは正方形のタイルに分割されます。 Azure Maps SDK の使用タイルのサイズは、道路地図では 512 x 512 ピクセル、衛星画像では 256 x 256 ピクセル未満です。 Azure Maps には、0 から 22 までの 23 のズーム レベルに対応するラスター タイルとベクター タイルが用意されています。 ズーム レベル 0 では、世界全体が 1 つのタイルに収まります。

世界地図タイル

ズーム レベル 1 では、4 つのタイルを使用してワールドをレンダリングします。2 x 2 四角形

2x2 地図タイルレイアウト

ズーム レベルを追加するたびに、前のタイルが 4 分割され、2 ズーム x 2ズーム のグリッドが作成されます。 ズーム レベル 22 は、グリッド 222 x 222、または 4,194,304 x 4,194,304 タイル (合計で 17,592,186,044,416 タイル) です。

Web 用の Azure Maps 対話型マップ コントロールでは、0 から 24 の番号が付いた 25 のズーム レベルがサポートされます。 ただし、道路データをズーム レベルで使用できるのは、タイルが使用可能な場合のみです。

次の表は、タイル サイズが 256 ピクセルの正方形であるズーム レベルの値の完全な一覧を示しています。

ズーム レベル メートル/ピクセル メートル/タイル一辺
0 156543 40075017
1 78271.5 20037508
2 39135.8 10018754
3 19567.88 5009377.1
4 9783.94 2504688.5
5 4891.97 1252344.3
6 2445.98 626172.1
7 1222.99 313086.1
8 611.5 156543
9 305.75 78,271.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
十七 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
二十四 0.00933075 2.3886575

ピクセル座標

各ズーム レベルで使用する投影とスケールを選択したら、地理座標をピクセル座標に変換できます。 特定のズーム レベルに対するワールドのマップ イメージの全ピクセル幅と高さは、次のように計算されます。

var mapWidth = tileSize * Math.pow(2, zoom);

var mapHeight = mapWidth;

マップの幅と高さはズーム レベルごとに異なるため、ピクセル座標も異なります。 マップの左上隅にあるピクセルには、常にピクセル座標 (0,0) があります。 マップの右下隅のピクセルにはピクセル座標 (幅 1、高さ-1) があります。または前のセクション の数式 (tileSize * 2zoom-1、tileSize * 2zoom-1) を参照します。 たとえば、レベル 2 で 512 平方タイルを使用する場合、ピクセル座標の範囲は (0, 0) から (2047, 2047) です。次のようになります。

ピクセル ディメンションを示すマップ

緯度と経度を度単位で指定し、詳細レベルを指定すると、ピクセル XY 座標は次のように計算されます。

var sinLatitude = Math.sin(latitude * Math.PI/180);

var pixelX = ((longitude + 180) / 360) * tileSize * Math.pow(2, zoom);

var pixelY = (0.5 – Math.log((1 + sinLatitude) / (1 – sinLatitude)) / (4 * Math.PI)) * tileSize * Math.pow(2, zoom);

緯度と経度の値は WGS 84 データム上にあると見なされます。 Azure Maps では球面投影を使用しますが、すべての地理座標を共通のデータムに変換することが重要です。 WGS 84 は、選択したデータムです。 経度の値は、-180 度から +180 度の範囲であると見なされ、緯度の値を -85.05112878 から 85.05112878 までの範囲にクリップする必要があります。 これらの値に従うと、極での特異点が回避され、投影されたマップが 2 乗形状であることが保証されます。

タイル座標

マップの取得と表示のパフォーマンスを最適化するために、レンダリングされたマップをタイルに切り取ります。 ピクセルの数とタイルの数は、ズーム レベルごとに異なります。

var numberOfTilesWide = Math.pow(2, zoom);

var numberOfTilesHigh = numberOfTilesWide;

各タイルには、左上の (0, 0) から右下の (2ズーム - 1、2ズーム - 1) までの XY 座標が指定されます。 たとえば、ズーム レベル 3 では、タイル座標の範囲は (0, 0) ~ (7, 7) です。

タイル座標のマップ

ピクセル XY 座標のペアを指定すると、そのピクセルを含むタイルのタイル XY 座標を簡単に決定できます。

var tileX = Math.floor(pixelX / tileSize);

var tileY = Math.floor(pixelY / tileSize);

タイルはズーム レベルによって呼び出されます。 x 座標と y 座標は、そのズーム レベルのグリッド上のタイルの位置に対応します。

使用するズーム レベルを決定するときは、各場所がタイル上の固定位置にある点に注意してください。 その結果、地域の特定の広がりを表示するために必要なタイルの数は、世界地図上のズーム グリッドの特定の配置に依存します。 たとえば、900 メートル離れた 2 つのポイントがある場合、ズーム レベル 17 でそれらの間のルートを表示するのに 3 つのタイルしか必要と されないことがあります 。 ただし、西のポイントがタイルの右側にあり、タイルの左側の東のポイントにある場合は、次の 4 つのタイルが必要になることがあります。

デモ スケールを拡大する

ズーム レベルが決定されると、x と y の値を計算できます。 各ズーム グリッドの左上のタイルは x=0、y=0 です。右下のタイルは x=2zoom-1、y=2zoom-1 です

ズーム レベル 1 のズーム グリッドを次に示します。

ズーム レベル 1 のズーム グリッド

Quadkey インデックス

一部のマッピング プラットフォームでは、タイルの ZY 座標を、quadtree キーまたはquadkeysと呼ばれる 1 次元文字列に結合するquadkeyインデックス付け名前付け規則を使用します。 各 quadkey は、特定の詳細レベルで 1 つのタイルを一意に識別し、一般的なデータベース B ツリー インデックスのキーとして使用できます。 Azure Maps SDK では、「タイル レイヤーの追加」ドキュメントに記載されている他の名前付け規則に加えて、 quadkey 名前付け規則を使用する タイル レイヤー のオーバーレイがサポートされています。

quadkeysの名前付け規則は、1 つ以上のズーム レベルでのみ機能します。 Azure Maps SDK のサポート ズーム レベル 0 は、世界中の 1 つのマップ タイルです。

タイル座標を quadkeyに変換するには、Y 座標と X 座標のビットがインターリーブされ、結果は base-4 数値 (先頭に 0 が保持されます) として解釈され、文字列に変換されます。 たとえば、レベル 3 のタイル XY 座標 (3, 5) を指定すると、 quadkey は次のように決定されます。

tileX = 3 = 011 (base 2)

tileY = 5 = 101 (base 2)

quadkey = 100111 (base 2) = 213 (base 4) = "213"

Qquadkeys には、いくつかの興味深いプロパティがあります。 まず、 quadkey の長さ (桁数) は、対応するタイルのズーム レベルと等しくなります。 次に、タイルの quadkey は、親タイルの quadkey (前のレベルのタイルを含む) で始まります。 次の例に示すように、タイル 2 はタイル 20 から 23 の親です。

クワッドキー タイル ピラミッド

最後に、 quadkeys は、通常、XY 空間内のタイルの近接性を保持する 1 次元インデックス キーを提供します。 つまり、近くの XY 座標を持つ 2 つのタイルには、通常、比較的近い 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);

            //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="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 サービスからマップ タイルに直接アクセスします。

地理空間の概念の詳細については、以下をご覧ください。