共用方式為


在 iOS SDK (預覽版) 中建立資料來源

注意

Azure 地圖服務 iOS SDK 淘汰

適用於 iOS 的 Azure 地圖服務 原生 SDK 現在已被取代,將於 3/31/25 淘汰。 若要避免服務中斷,請透過 3/31/25 移轉至 Azure 地圖服務 Web SDK。 如需詳細資訊,請參閱 Azure 地圖服務 iOS SDK 移轉指南

Azure 地圖服務 iOS SDK 會將資料儲存在資料來源中。 使用資料來源可將查詢和轉譯的資料作業最佳化。 目前,資料來源有兩種不同的類型:

  • GeoJSON 來源:在本機管理 GeoJSON 格式的原始位置資料。 適用於小型到中型資料集 (數十萬個圖形)。
  • 向量圖格來源:根據地圖底圖系統,為目前的地圖檢視載入格式化為向量圖格的資料。 適合用於大型資料集 (數百萬或數十億個圖形)。

GeoJSON 資料來源

Azure 地圖服務使用 GeoJSON 作為其主要資料模型之一。 GeoJSON 是以 JSON 格式表示地理空間資料的開放式地理空間標準方法。 透過 Azure 地圖服務 iOS SDK 中提供的 GeoJSON 類別,即可輕鬆建立及序列化 GeoJSON 資料。 在 DataSource 類別中載入及儲存 GeoJSON 資料,並使用圖層加以轉譯。 下列程式碼說明如何在 Azure 地圖服務中建立 GeoJSON 物件。

/*
    Raw GeoJSON feature

    {
        type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [-100, 45]
        },
        "properties": {
            "custom-property": "value"
        }
    }

*/

//Create a point feature.
let feature = Feature(Point(CLLocationCoordinate2D(latitude: 45, longitude: -100)))

//Add a property to the feature.
feature.addProperty("custom-property", value: "value")

//Add the feature to the data source.
source.add(feature: feature)

或者,可先將屬性載入字典中 (JSON),然後在建立特徵時將其傳入特徵中,如下列程式碼所示:

//Create a dictionary to store properties for the feature.
var properties: [String: Any] = [:]
properties["custom-property"] = "value"

let feature = Feature(Point(CLLocationCoordinate2D(latitude: 45, longitude: -100)), properties: properties)

建立 GeoJSON 特徵後,即可透過地圖的 sources 屬性將資料來源新增至地圖。 下列程式碼說明如何建立 DataSource、將其新增至地圖,並將特徵新增至資料來源。

//Create a data source and add it to the map.
let source = DataSource()
map.sources.add(source)

//Add GeoJSON feature to the data source.
source.add(feature: feature)

下列程式碼說明用來建立 GeoJSON FeatureFeatureCollection 和幾何的數種方式。

// GeoJSON Point Geometry
let point = Point(location)

// GeoJSON LineString Geometry
let polyline = Polyline(locations)

// GeoJSON Polygon Geometry
let polygon = Polygon(locations)

let polygonWithInteriorPolygons = Polygon(locations, interiorPolygons: polygons)

// GeoJSON MultiPoint Geometry
let pointCollection = PointCollection(locations)

// GeoJSON MultiLineString Geometry
let multiPolyline = MultiPolyline(polylines)

let multiPolylineFromLocations = MultiPolyline(locations: arrayOfLocationArrays) // [[CLLocationCoordinate2D]]

// GeoJSON MultiPolygon Geometry
let multiPolygon = MultiPolygon(polygons)

let multiPolygonFromLocations = MultiPolygon(locations: arrayOfLocationArrays) // [[CLLocationCoordinate2D]]

// GeoJSON GeometryCollection Geometry

let geometryCollection = GeometryCollection(geometries)

// GeoJSON Feature
let pointFeature = Feature(Point(location))

// GeoJSON FeatureCollection
let featureCollection = FeatureCollection(features)

序列化和還原序列化 GeoJSON

特徵集合、特徵和幾何類別全都有 fromJson(_:)toJson() 靜態方法,有助於序列化。 透過 fromJson() 方法傳遞的格式化有效 JSON 字串,會建立幾何物件。 此 fromJson() 方法也意味著您可以使用 JSONSerialization 或其他序列化/還原序列化策略。 下列程式碼說明如何取用字串化的 GeoJSON 功能,並將其還原序列化為 Feature 類別,然後再將其序列化回 GeoJSON 字串。

// Take a stringified GeoJSON object.
let geoJSONString = """
    {
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [-100, 45]
        },
        "properties": {
            "custom-property": "value"
        }
    }
"""

// Deserialize the JSON string into a feature.
guard let feature = Feature.fromJson(geoJSONString) else {
    throw GeoJSONSerializationError.couldNotSerialize
}

// Serialize a feature collection to a string.
let featureString = feature.toJson()

從 Web 或資產資料夾匯入 GeoJSON 資料

大部分的 GeoJSON 檔案都包含 FeatureCollection。 以字串的形式讀取 GeoJSON 檔案,並使用 FeatureCollection.fromJson(_:) 方法將其還原序列化。

DataSource 類別具有名為 importData(fromURL:) 的內建方法,該方法可使用 Web 上或裝置中所含檔案的 URL 載入 GeoJSON 檔案。

// Create a data source.
let source = DataSource()

// Import the geojson data and add it to the data source.
let url = URL(string: "URL_or_FilePath_to_GeoJSON_data")!
source.importData(fromURL: url)

// Examples:
// source.importData(fromURL: URL(string: "asset://sample_file.json")!)
// source.importData(fromURL: URL(string: "https://example.com/sample_file.json")!)

// Add data source to the map.
map.sources.add(source)

importData(fromURL:) 方法可讓您將 GeoJSON 摘要載入資料來源中,但只能有限地控制資料的載入方式,以及資料載入後會發生什麼情況。 下列程式碼是可重複使用的類別,可從 Web 或資產資料夾匯入資料,並透過回呼函式將其傳回至 UI 執行緒。 在回呼中,您可以新增額外的後續載入邏輯來處理資料、將其新增至地圖、計算其週框方塊,以及更新地圖相機。

import Foundation

@objc
public class Utils: NSObject {
    /// Imports data from a web url or local file url and returns it as a string to a callback on the main thread.
    /// - Parameters:
    ///     - url: A web url or local file url that points to data to load.
    ///     - completion: The callback function to return the data to.
    @objc
    public static func importData(fromURL url: URL, completion: @escaping (String?) -> Void) {
        URLSession.shared.dataTask(with: url) { data, _, _ in
            DispatchQueue.main.async {
                if let data = data {
                    completion(String(decoding: data, as: UTF8.self))
                } else {
                    completion(nil)
                }
            }
        }.resume()
    }
}

下列程式碼說明如何使用此公用程式將 GeoJSON 資料匯入為字串,並透過回撥將其傳回至主要執行緒。 在回呼中,字串資料可序列化為 GeoJSON 特徵集合,並新增至資料來源。 您可以選擇性地更新地圖相機,以將焦點放在資料上。

// Create a data source and add it to the map.
let source = DataSource()
map.sources.add(source)

// Create a web url or a local file url
let url = URL(string: "URL_to_GeoJSON_data")!
// Examples:
// let url = Bundle.main.url(forResource: "FeatureCollectionSample", withExtension: "geojson")!
// let url = URL(string: "www.yourdomain.com/path_to_feature_collection_sample")!

// Import the geojson data and add it to the data source.
Utils.importData(fromURL: url) { result in
    guard let result = result else {
        // No data imported.
        return
    }

    // Parse the data as a GeoJSON Feature Collection.
    guard let fc = FeatureCollection.fromJson(result) else {
        // Invalid data for FeatureCollection type.
        return
    }

    // Add the feature collection to the data source.
    source.add(featureCollection: fc)

    // Optionally, update the maps camera to focus in on the data.

    // Calculate the bounding box of all the data in the Feature Collection.
    guard let bbox = BoundingBox.fromData(fc) else {
        // The feature collection is empty.
        return
    }

    // Update the maps camera so it is focused on the data.
    map.setCameraBoundsOptions([
        .bounds(bbox),
        .padding(20)
    ])
}

更新特徵

類別 DataSource 可讓您輕鬆新增及移除特徵。 更新特徵的幾何或屬性時,需要取代資料來源中的特徵。 有兩種方法可用來更新特徵:

  1. 使用所需的更新建立新特徵,並使用 set 方法來取代資料來源中的所有特徵。 如果您想要更新資料來源中的所有特徵,此方法將可發揮效用。
var source: DataSource!

private func onReady(map: AzureMap) {
    // Create a data source and add it to the map.
    source = DataSource()
    map.sources.add(source)

    // Create a feature and add it to the data source.
    let myFeature = Feature(Point(CLLocationCoordinate2D(latitude: 0, longitude: 0)))
    myFeature.addProperty("Name", value: "Original value")
    source.add(feature: myFeature)
}

private func updateFeature() {
    // Create a new replacement feature with an updated geometry and property value.
    let myNewFeature = Feature(Point(CLLocationCoordinate2D(latitude: -10, longitude: 10)))
    myNewFeature.addProperty("Name", value: "New value")

    // Replace all features to the data source with the new one.
    source.set(feature: myNewFeature)
}
  1. 追蹤變數中的特徵執行個體,並將其傳入資料來源 remove 方法中,而加以移除。 使用所需的更新建立新特徵、更新變數參考,並使用 add 方法將其新增至資料來源。
var source: DataSource!
var myFeature: Feature!

private func onReady(map: AzureMap) {
    // Create a data source and add it to the map.
    source = DataSource()
    map.sources.add(source)

    // Create a feature and add it to the data source.
    myFeature = Feature(Point(CLLocationCoordinate2D(latitude: 0, longitude: 0)))
    myFeature.addProperty("Name", value: "Original value")
    source.add(feature: myFeature)
}

private func updateFeature() {
    // Remove the feature instance from the data source.
    source.remove(feature: myFeature)

    // Get properties from original feature.
    var props = myFeature.properties

    // Update a property.
    props["Name"] = "New value"

    // Create a new replacement feature with an updated geometry.
    myFeature = Feature(
        Point(CLLocationCoordinate2D(latitude: -10, longitude: 10)),
        properties: props
    )

    // Re-add the feature to the data source.
    source.add(feature: myFeature)
}

提示

如果您有一些會定期更新的資料,還有一些很少變更的資料,最好將這些資料分割到個別的資料來源執行個體。 資料來源發生更新時,會強制地圖重新繪製資料來源中的所有特徵。 藉由分割此資料,在該資料來源發生更新時,將只會重新繪製要定期更新的特徵,其他資料來源中的特徵將無需重新繪製。 這有助於提升效能。

向量圖格來源

向量圖格來源說明如何存取向量圖格圖層。 使用 VectorTileSource 類別將向量圖格來源具現化。 向量圖格圖層類似於圖格圖層,但兩者並不相同。 圖格圖層是點陣影像。 向量圖格圖層是 PBF 格式的壓縮檔案。 這個壓縮的檔案包含向量地圖資料,以及一或多個圖層。 此檔案可根據每個圖層的樣式,在用戶端上轉譯及設定樣式。 向量圖格中的資料包含點、線條和多邊形形式的地理特徵。 使用向量圖格圖層 (而非點陣圖格圖層) 有數個優點:

  • 向量圖格的檔案大小通常比對等的點陣圖格小很多。 因此,會使用較少頻寬。 這意味著延遲較低、地圖效能更快,使用者體驗更加理想。
  • 向量圖格會在用戶端上轉譯,因此會根據其顯示所在裝置的解析度進行調整。 因此,轉譯的地圖會顯得更加清晰,可清楚看到標籤。
  • 變更向量地圖中的資料樣式時不需要再次下載資料,因為新的樣式可套用在用戶端上。 相對地,變更點陣圖格圖層的樣式時通常需要從伺服器載入圖格,然後再套用新樣式。
  • 由於資料是以向量形式傳遞的,準備資料時所需的伺服器端處理會比較少。 因此,較新的資料可更快成為可用資料。

Azure 地圖服務遵循 Mapbox 向量圖格規格,這是一個開放式標準。 Azure 地圖服務提供下列向量圖格服務作為平台的一部分:

提示

使用 Azure 地圖服務轉譯服務中的向量或點陣圖格搭配 iOS SDK 時,您可以將 atlas.microsoft.com 取代為 AzureMapdomainPlaceholder 屬性。 此預留位置將會取代為地圖所使用的相同網域,並且會自動附加相同的驗證詳細資料。 這可大幅簡化使用 Microsoft Entra 驗證時對轉譯服務進行驗證的程序。

若要在地圖上顯示向量圖格來源的資料,請將來源連線至其中一個資料轉譯圖層。 所有使用向量來源的圖層,都必須在選項中指定 sourceLayer 值。 下列程式碼會將 Azure 地圖服務的交通流量向量圖格服務載入為向量圖格來源,然後使用線條圖層將其顯示在地圖上。 此向量圖格來源在來源圖層中有單一資料集,名為「交通流量」。 此資料集中的線條資料具有名為 traffic_level 的屬性,可在此程式碼中用來選取色彩和調整線條的大小。

// Formatted URL to the traffic flow vector tiles.
let trafficFlowUrl = "\(map.domainPlaceholder)/traffic/flow/tile/pbf?api-version=1.0&style=relative&zoom={z}&x={x}&y={y}"

// Create a vector tile source and add it to the map.
let source = VectorTileSource(options: [
    .tiles([trafficFlowUrl]),
    .maxSourceZoom(22)
])
map.sources.add(source)

// Create a layer for traffic flow lines.
let layer = LineLayer(
    source: source,
    options: [

        // The name of the data layer within the data source to pass into this rendering layer.
        .sourceLayer("Traffic flow"),

        // Color the roads based on the traffic_level property.
        .strokeColor(
            from: NSExpression(
                forAZMInterpolating: NSExpression(forKeyPath: "traffic_level"),
                curveType: .linear,
                parameters: nil,
                stops: NSExpression(forConstantValue: [
                    0: UIColor.red,
                    0.33: UIColor.yellow,
                    0.66: UIColor.green
                ])
            )
        ),

        // Scale the width of roads based on the traffic_level property.
        .strokeWidth(
            from: NSExpression(
                forAZMInterpolating: NSExpression(forKeyPath: "traffic_level"),
                curveType: .linear,
                parameters: nil,
                stops: NSExpression(forConstantValue: [
                    0: 6,
                    1: 1
                ])
            )
        )
    ]
)

// Add the traffic flow layer below the labels to make the map clearer.
map.layers.insertLayer(layer, below: "labels")

地圖的螢幕快照,其中顯示交通流量層級的彩色編碼道路線。

將資料來源連線至圖層

資料會使用轉譯圖層在地圖上轉譯。 一或多個轉譯圖層可參考單一資料來源。 下列轉譯圖層需要資料來源:

下列程式碼顯示如何建立資料來源、將其新增至地圖、將 GeoJSON 學分資料從遠端位置匯入資料來源,然後將其連接到泡泡圖層。

// Create a data source.
let source = DataSource()

// Create a web url or a local file url
let url = URL(string: "URL_or_FilePath_to_GeoJSON_data")!
// Examples:
// let url = Bundle.main.url(forResource: "FeatureCollectionSample", withExtension: "geojson")!
// let url = URL(string: "yourdomain.com/path_to_feature_collection_sample")!

// Import the geojson data and add it to the data source.
source.importData(fromURL: url)

// Add data source to the map.
map.sources.add(source)

// Create a layer that defines how to render points in the data source and add it to the map.
let layer = BubbleLayer(source: source)
map.layers.addLayer(layer)

還有其他不會連線至這些資料來源的轉譯圖層,但這類圖層會直接載入資料以進行轉譯。

一個具有多個圖層的資料來源

多個圖層可連線至單一資料來源。 在許多不同的情況下,此選項都有其效益。 例如,請考量使用者繪製了多邊形的案例。 當使用者將點新增至地圖時,我們應轉譯並填滿多邊形區域。 對多邊形的外框新增樣式線條,使用者在繪製時就更能清楚看到多邊形的邊緣。 為了方便編輯多邊形中的個別位置,我們可以在每個位置上方新增一個控點,例如圖釘或標記。

地圖的螢幕快照,其中顯示從單一數據源轉譯數據的多個圖層。

在大部分的地圖平台中,您都需要多邊形物件、線條物件,以及多邊形中各個位置的圖釘。 當多邊形經過修改時,您必須手動更新線條和圖釘,這很快就會變得複雜起來。

透過 Azure 地圖服務,您只需在資料來源中有一個多邊形即可,如下列程式碼所示。

// Create a data source and add it to the map.
let source = DataSource()
map.sources.add(source)

// Create a polygon and add it to the data source.
source.add(geometry: Polygon([
    CLLocationCoordinate2D(latitude: 33.15, longitude: -104.5),
    CLLocationCoordinate2D(latitude: 38.5, longitude: -113.5),
    CLLocationCoordinate2D(latitude: 43, longitude: -111.5),
    CLLocationCoordinate2D(latitude: 43.5, longitude: -107),
    CLLocationCoordinate2D(latitude: 43.6, longitude: -94)
]))

// Create a polygon layer to render the filled in area of the polygon.
let polygonLayer = PolygonLayer(
    source: source,
    options: [.fillColor(UIColor(red: 1, green: 165/255, blue: 0, alpha: 0.2))]
)

// Create a line layer for greater control of rendering the outline of the polygon.
let lineLayer = LineLayer(source: source, options: [
    .strokeColor(.orange),
    .strokeWidth(2)
])

// Create a bubble layer to render the vertices of the polygon as scaled circles.
let bubbleLayer = BubbleLayer(
    source: source,
    options: [
        .bubbleColor(.orange),
        .bubbleRadius(5),
        .bubbleStrokeColor(.white),
        .bubbleStrokeWidth(2)
    ]
)

// Add all layers to the map.
map.layers.addLayers([polygonLayer, lineLayer, bubbleLayer])

提示

您也可以使用 map.layers.insertLayer(_:below:) 方法,此方法可以將現有圖層的識別碼或執行個體當做第二個參數傳入。 這會指示地圖插入在現有圖層底下新增的新圖層。 除了傳入圖層識別碼以外,此方法也支援下列值。

  • "labels" - 在地圖標籤圖層底下插入新圖層。
  • "transit" - 在地圖道路和交通圖層底下插入新圖層。

其他資訊

請參閱下列文章,以取得更多可新增至地圖的程式碼範例: