Creare un'origine dati in iOS SDK (anteprima)
Nota
ritiro di iOS SDK Mappe di Azure
Il Mappe di Azure Native SDK per iOS è ora deprecato e verrà ritirato il 3/31/25. Per evitare interruzioni del servizio, eseguire la migrazione all'SDK Web di Mappe di Azure entro il 3/31/25. Per altre informazioni, vedere La guida alla migrazione Mappe di Azure di iOS SDK per iOS.
L'SDK iOS Mappe di Azure archivia i dati nelle origini dati. L'uso delle origini dati ottimizza le operazioni sui dati per l'esecuzione di query e il rendering. Attualmente esistono due tipi di origini dati:
- Origine GeoJSON: gestisce i dati di posizione non elaborati in formato GeoJSON in locale. Buona per set di dati di piccole e medie dimensioni (verso l'alto di centinaia di migliaia di forme).
- Origine riquadro vettoriale: carica i dati formattati come riquadri vettoriali per la visualizzazione mappa corrente, in base al sistema di affiancamento delle mappe. Ideale per set di dati di grandi dimensioni (milioni o miliardi di forme).
Origine dati GeoJSON
Mappe di Azure usa GeoJSON come uno dei modelli di dati principali. GeoJSON è un modo standard geospaziale aperto per rappresentare i dati geospaziali in formato JSON. Classi GeoJSON disponibili nell'SDK iOS Mappe di Azure per semplificare la creazione e la serializzazione dei dati GeoJSON. Caricare e archiviare i dati GeoJSON nella classe ed eseguirne il DataSource
rendering usando i livelli. Nel codice seguente viene illustrato come creare oggetti GeoJSON in Mappe di Azure.
/*
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)
In alternativa, le proprietà possono essere caricate prima in un dizionario (JSON) e quindi passate alla funzionalità durante la creazione, come illustrato nel codice seguente:
//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)
Dopo aver creato una funzionalità GeoJSON, è possibile aggiungere un'origine dati alla mappa tramite la sources
proprietà della mappa. Il codice seguente illustra come creare un DataSource
oggetto , aggiungerlo alla mappa e aggiungere una funzionalità all'origine dati.
//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)
Il codice seguente illustra diversi modi per creare geometrie , FeatureCollection
e GeoJSONFeature
.
// 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)
Serializzare e deserializzare GeoJSON
Le classi feature collection, feature e geometry dispongono fromJson(_:)
di metodi statici e toJson()
che consentono la serializzazione. La stringa JSON valida formattata passata tramite il fromJson()
metodo crea l'oggetto geometry. Questo fromJson()
metodo significa anche che è possibile usare JSONSerialization
o altre strategie di serializzazione/deserializzazione. Il codice seguente illustra come acquisire una funzionalità GeoJSON stringata e deserializzarla nella Feature
classe , quindi serializzarla nuovamente in una stringa 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()
Importare i dati GeoJSON dal Web o dalla cartella assets
La maggior parte dei file GeoJSON contiene un oggetto FeatureCollection
. Leggere i file GeoJSON come stringhe e usare il FeatureCollection.fromJson(_:)
metodo per deserializzarlo.
La DataSource
classe ha un metodo predefinito denominato importData(fromURL:)
che può essere caricato nei file GeoJSON usando un URL di un file sul Web o sul dispositivo.
// 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)
Il importData(fromURL:)
metodo consente di caricare un feed GeoJSON in un'origine dati, ma fornisce un controllo limitato sulla modalità di caricamento dei dati e su cosa accade dopo il caricamento. Il codice seguente è una classe riutilizzabile per l'importazione di dati dalla cartella Web o assets e la restituzione al thread dell'interfaccia utente tramite una funzione di callback. Nel callback è quindi possibile aggiungere più logica di post-caricamento per elaborare i dati, aggiungerli alla mappa, calcolarne il rettangolo di selezione e aggiornare la fotocamera delle mappe.
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()
}
}
Il codice seguente illustra come usare questa utilità per importare dati GeoJSON come stringa e restituirli al thread principale tramite un callback. Nel callback i dati stringa possono essere serializzati in una raccolta di funzionalità GeoJSON e aggiunti all'origine dati. Facoltativamente, aggiornare la fotocamera delle mappe per concentrarsi sui dati.
// 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)
])
}
Aggiornare una funzionalità
La DataSource
classe semplifica l'aggiunta e la rimozione di funzionalità. Per aggiornare la geometria o le proprietà di una funzionalità è necessario sostituire la funzionalità nell'origine dati. Esistono due metodi che possono essere usati per aggiornare una o più funzionalità:
- Creare le nuove funzionalità con gli aggiornamenti desiderati e sostituire tutte le funzionalità nell'origine dati usando il
set
metodo . Questo metodo funziona correttamente quando si desidera aggiornare tutte le funzionalità in un'origine dati.
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)
}
- Tenere traccia dell'istanza della funzionalità in una variabile e passarla al metodo delle origini
remove
dati per rimuoverla. Creare le nuove funzionalità con gli aggiornamenti desiderati, aggiornare il riferimento alla variabile e aggiungerla all'origine dati usando iladd
metodo .
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)
}
Suggerimento
Se si dispone di alcuni dati che verranno aggiornati regolarmente e altri dati che verranno raramente modificati, è consigliabile suddividerli in istanze separate dell'origine dati. Quando si verifica un aggiornamento in un'origine dati, forza la mappa a ridipingere tutte le funzionalità nell'origine dati. Suddividendo questi dati, solo le funzionalità che vengono aggiornate regolarmente vengono aggiornate quando si verifica un aggiornamento in tale origine dati, mentre le funzionalità nell'altra origine dati non devono essere ridisegnate. Ciò consente di migliorare le prestazioni.
Origine riquadro vettoriale
Un'origine di riquadri vettoriali descrive come accedere a un livello di riquadro vettoriale. Usare la VectorTileSource
classe per creare un'istanza di un'origine riquadro vettoriale. I livelli dei riquadri vettoriali sono simili ai livelli affiancati, ma non sono uguali. Un livello riquadro è un'immagine raster. I livelli dei riquadri vettoriali sono un file compresso, in formato PBF . Questo file compresso contiene i dati della mappa vettoriale e uno o più livelli. È possibile eseguire il rendering e lo stile del file nel client, in base allo stile di ogni livello. I dati in un riquadro vettoriale contengono caratteristiche geografiche sotto forma di punti, linee e poligoni. L'uso dei livelli dei riquadri vettoriali offre diversi vantaggi invece dei livelli di riquadri raster:
- Le dimensioni di un file di un riquadro vettoriale sono in genere molto più piccole rispetto a un riquadro raster equivalente. Di conseguenza, viene usata una minore larghezza di banda. Significa una latenza inferiore, una mappa più veloce e un'esperienza utente migliore.
- Poiché il rendering dei riquadri vettoriali viene eseguito sul client, si adattano alla risoluzione del dispositivo in cui vengono visualizzati. Di conseguenza, le mappe sottoposte a rendering appaiono più definite, con etichette cristalline.
- La modifica dello stile dei dati nelle mappe vettoriali non richiede di nuovo il download dei dati, perché il nuovo stile può essere applicato al client. Al contrario, la modifica dello stile di un livello riquadro raster richiede in genere il caricamento di riquadri dal server e quindi l'applicazione del nuovo stile.
- Poiché i dati vengono recapitati in formato vettoriale, per preparare i dati è necessaria meno elaborazione lato server. Di conseguenza, i dati più recenti possono essere resi disponibili più velocemente.
Mappe di Azure è conforme al Mapbox Vector Tile Specification, uno standard aperto. Mappe di Azure fornisce i seguenti servizi di riquadri vettoriali come parte della piattaforma:
- Tessere stradali
- Eventi imprevisti del traffico
- Flusso del traffico
- Mappe di Azure Creator consente anche la creazione e l'accesso ai riquadri vettoriali personalizzati tramite Rendering - OTTENERE l'API riquadro mappa
Suggerimento
Quando si usano riquadri di immagini vettoriali o raster dal servizio di rendering Mappe di Azure con iOS SDK, è possibile sostituire atlas.microsoft.com
con la AzureMap
"proprietàdomainPlaceholder
" . Questo segnaposto verrà sostituito con lo stesso dominio usato dalla mappa e aggiungerà automaticamente gli stessi dettagli di autenticazione. Ciò semplifica notevolmente l'autenticazione con il servizio di rendering quando si usa l'autenticazione Di Microsoft Entra.
Per visualizzare i dati da un'origine riquadro vettoriale sulla mappa, connettere l'origine a uno dei livelli di rendering dei dati. Tutti i livelli che usano un'origine vettoriale devono specificare un sourceLayer
valore nelle opzioni. Il codice seguente carica il servizio riquadro vettoriale del flusso di traffico Mappe di Azure come origine di riquadri vettoriali, quindi lo visualizza su una mappa usando un livello linea. Questa origine di riquadri vettoriali ha un singolo set di dati nel livello di origine denominato "Flusso di traffico". I dati di riga in questo set di dati hanno una proprietà denominata utilizzata traffic_level
in questo codice per selezionare il colore e ridimensionare le dimensioni delle righe.
// 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")
Connessione un'origine dati a un livello
Il rendering dei dati viene eseguito sulla mappa usando i livelli di rendering. Uno o più livelli di rendering possono fare riferimento a una singola origine dati. I livelli di rendering seguenti richiedono un'origine dati:
- Livello bolla: esegue il rendering dei dati dei punti come cerchi ridimensionati sulla mappa.
- Livello simbolo: esegue il rendering dei dati dei punti come icone o testo.
- Livello mappa termica: esegue il rendering dei dati dei punti come mappa termica della densità.
- Livello linea: eseguire il rendering di una linea ed eseguire il rendering del contorno dei poligoni.
- Livello poligono: riempie l'area di un poligono con un colore a tinta unita o un motivo di immagine.
Il codice seguente illustra come creare un'origine dati, aggiungerla alla mappa, importare i dati dei punti GeoJSON da una posizione remota nell'origine dati e quindi connetterla a un livello a bolle.
// 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)
Esistono altri livelli di rendering che non si connettono a queste origini dati, ma caricano direttamente i dati per il rendering.
Un'origine dati con più livelli
È possibile connettere più livelli a una singola origine dati. Esistono molti scenari diversi in cui questa opzione è utile. Si consideri, ad esempio, lo scenario in cui un utente disegna un poligono. È necessario eseguire il rendering e riempire l'area poligono man mano che l'utente aggiunge punti alla mappa. L'aggiunta di una linea in stile per delineare il poligono rende più semplice visualizzare i bordi del poligono, man mano che l'utente disegna. Per modificare facilmente una singola posizione nel poligono, è possibile aggiungere un punto di manipolazione, ad esempio un segnaposto o un marcatore, sopra ogni posizione.
Nella maggior parte delle piattaforme di mapping è necessario un oggetto poligono, un oggetto linea e un segnaposto per ogni posizione nel poligono. Quando il poligono viene modificato, è necessario aggiornare manualmente la riga e i pin, che possono diventare rapidamente complessi.
Con Mappe di Azure, è sufficiente un singolo poligono in un'origine dati, come illustrato nel codice seguente.
// 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])
Suggerimento
È anche possibile usare il map.layers.insertLayer(_:below:)
metodo , in cui l'ID o l'istanza di un livello esistente può essere passato come secondo parametro. In questo modo si indica che mappa per inserire il nuovo livello aggiunto sotto il livello esistente. Oltre a passare un ID livello, questo metodo supporta anche i valori seguenti.
"labels"
- Inserisce il nuovo livello sotto i livelli dell'etichetta della mappa."transit"
- Inserisce il nuovo livello sotto i livelli di percorso e transito della mappa.
Informazioni aggiuntive
Per altri esempi di codice da aggiungere alle mappe, vedere gli articoli seguenti: