Partager via


Créer une source de données dans le Kit de développement logiciel (SDK) iOS (préversion)

Remarque

Mise hors service du kit de développement logiciel (SDK) iOS Azure Maps

Le Kit de développement logiciel (SDK) natif Azure Maps pour iOS est désormais déconseillé et sera mis hors service le 31 mars 2025. Pour éviter toute interruption de service, nous vous recommandons de migrer vers le kit de développement logiciel (SDK) web Azure Maps avant le 31 mars 2025. Pour plus d’informations, consultez le Guide de migration du kit de développement logiciel (SDK) iOS Azure Maps.

Le SDK iOS Azure Maps stocke les données dans des sources de données. L’utilisation de sources de données optimise les opérations sur les données pour l’interrogation et le rendu. Il existe actuellement deux types de sources de données :

  • Source GeoJSON : gère les données d’emplacement brutes au format GeoJSON localement. Convient aux petits et moyens jeux de données (jusqu’à des centaines de milliers de formes).
  • Source de mosaïque vectorielle : charge des données formatées sous forme de mosaïques vectorielles pour la vue actuelle de la carte, en fonction du système de mosaïque des cartes. Convient aux jeux de données volumineux ou énormes (des millions ou milliards de formes).

Source de données GeoJSON

Azure Maps utilise GeoJSON comme l’un de ses principaux modèles de données. GeoJSON est une norme géospatiale ouverte utilisée pour représenter des données géospatiales au format JSON. Classes GeoJSON disponibles dans le SDK iOS Azure Maps pour créer et sérialiser facilement des données GeoJSON. Chargez et stockez les données GeoJSON dans la classe DataSource et affichez-les à l’aide de couches. Le code suivant illustre la façon dont les objets GeoJSON peuvent être créés dans Azure Maps.

/*
    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)

Les propriétés peuvent également être d’abord chargées dans un dictionnaire (JSON), puis transmises à la fonctionnalité lors de sa création, comme indiqué dans le code suivant :

//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)

Une fois que vous avez créé une fonctionnalité GeoJSON, vous pouvez ajouter une source de données à la carte via la propriété sources de la carte. Le code suivant montre comment créer une DataSource, l’ajouter à la carte et y ajouter une fonctionnalité.

//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)

Le code suivant montre plusieurs façons de créer les éléments GeoJSON suivants : Feature, FeatureCollection et géométries.

// 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)

Sérialiser et désérialiser des objets GeoJSON

Les classes de collection de fonctionnalités, de fonctionnalités et de géométries ont toutes les méthodes statiques fromJson(_:) et toJson(), qui aident à la sérialisation. La chaîne JSON valide mise en forme, transmise via la méthode fromJson(), crée l’objet de géométrie. Cette méthode fromJson() signifie également que vous pouvez utiliser JSONSerialization ou d’autres stratégies de sérialisation/désérialisation. Le code suivant montre comment prendre une fonctionnalité GeoJSON convertie et la désérialiser dans la classe Feature, puis la sérialiser de nouveau dans une chaîne 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()

Importer des données GeoJSON à partir d’un dossier web ou de ressources

La plupart des fichiers GeoJSON contiennent un élément FeatureCollection. Lisez les fichiers GeoJSON en tant que chaînes et utilisez la méthode FeatureCollection.fromJson(_:) pour les désérialiser.

La classe DataSource a une méthode intégrée appelée importData(fromURL:) qui peut être chargée dans des fichiers GeoJSON à l’aide d’une URL vers un fichier sur le web ou un appareil.

// 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)

La méthode importData(fromURL:) offre un moyen de charger un flux GeoJSON dans une source de données, mais offre un contrôle limité sur la façon dont les données sont chargées et sur ce qui se produit après leur chargement. Le code suivant est une classe réutilisable pour l’importation des données à partir d’un dossier web ou de ressources et leur renvoi au thread d’interface utilisateur par le biais d’une fonction de rappel. Dans ce rappel, vous pouvez ajouter une logique d’après chargement supplémentaire pour traiter les données, les ajouter à la carte, calculer leur cadre englobant et mettre à jour la caméra de la carte.

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()
    }
}

Le code suivant montre comment utiliser cet utilitaire pour importer des données GeoJSON sous forme de chaîne et les renvoyer au thread principal par le biais d’un rappel. Dans le rappel, les données de chaîne peuvent être sérialisées dans une collection de fonctionnalités GeoJSON et ajoutées à la source de données. Vous pouvez également mettre à jour la caméra de la carte pour vous concentrer sur les données.

// 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)
    ])
}

Mettre à jour une fonctionnalité

La classe DataSource facilite l’ajout et la suppression de fonctionnalités. La mise à jour de la géométrie ou des propriétés d’une fonctionnalité nécessite de remplacer la fonctionnalité dans la source de données. Il existe deux méthodes qui peuvent être utilisées pour mettre à jour une ou plusieurs fonctionnalités :

  1. Créez la ou les nouvelles fonctionnalités avec les mises à jour souhaitées et remplacez toutes les fonctionnalités de la source de données à l’aide de la méthode set. Cette méthode fonctionne bien lorsque vous souhaitez mettre à jour toutes les fonctionnalités d’une source de données.
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. Effectuez le suivi de l’instance de fonctionnalité dans une variable et transmettez-la dans la méthode remove de sources de données pour la supprimer. Créez la ou les nouvelles fonctionnalités avec les mises à jour souhaitées, mettez à jour la référence de variable et ajoutez-la à la source de données à l’aide de la méthode 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)
}

Conseil

Si vous avez des données qui vont être régulièrement mises à jour et d’autres données qui seront rarement modifiées, il est préférable de les fractionner en instances de source de données distinctes. Quand une mise à jour se produit dans une source de données, elle force la carte à repeindre toutes les fonctionnalités dans la source de données. En fractionnant ces données, seules les fonctionnalités mises à jour régulièrement sont repeintes lorsqu’une mise à jour se produit dans cette source de données alors que les fonctionnalités de l’autre source de données n’auraient pas besoin d’être repeintes. Les performances s’en trouvent donc améliorées.

Source de mosaïque vectorielle

Une source de vignettes vectorielles décrit comment accéder à un calque de vignettes vectorielles. Utilisez la classe VectorTileSource pour instancier une source de vignettes vectorielles. Les calques de vignettes vectorielles sont similaires aux calques de vignettes, mais ils ne sont pas identiques. Un calque de vignettes est une image raster. Une couche de vignette vectorielle est un fichier compressé au format PBF. Ce fichier compressé contient les données d’une carte vectorielle et un ou plusieurs calques. Le fichier peut être rendu et stylisé sur le client, en fonction du style de chaque calque. Les données d’une mosaïque vectorielle contiennent des caractéristiques géographiques sous forme de points, de lignes et de polygones. Il y a plusieurs avantages à utiliser les calques de vignettes vectorielles au lieu des calques de vignettes raster :

  • La taille de fichier d’une mosaïque vectorielle est généralement nettement inférieure à celle d’une mosaïque raster équivalente. Ainsi, la bande passante utilisée est inférieure. Cela signifie une latence plus faible, une carte plus rapide et une meilleure expérience utilisateur.
  • Les vignettes vectorielles étant rendues sur le client, elles peuvent s’adapter à la résolution de l’appareil où elles sont affichées. Par conséquent, les cartes rendues apparaissent mieux définies, avec des étiquettes bien nettes.
  • La modification du style des données dans les cartes vectorielles n’implique pas de retélécharger les données, car le nouveau style peut être appliqué au client. En revanche, la modification du style d’un calque de vignettes raster nécessite généralement le chargement de vignettes à partir du serveur, puis l’application du nouveau style.
  • Les données étant fournies sous une forme vectorielle, moins de traitement côté serveur est nécessaire pour préparer les données. Ainsi, les données plus récentes peuvent être rendues disponibles plus rapidement.

Azure Maps est conforme à la spécification Mapbox Vector Tile, qui est un standard ouvert. Azure Maps fournit les services de vignettes vectorielles suivants dans le cadre de la plateforme :

Conseil

Si vous utilisez des vignettes d’images vectorielles ou raster issues du service de rendu Azure Maps avec le kit SDK iOS, vous pouvez remplacer atlas.microsoft.com par la propriété AzureMapdomainPlaceholder. Cet espace réservé sera remplacé par le domaine de la carte et ajoutera automatiquement les mêmes informations d’authentification. Cela simplifie considérablement l’authentification auprès du service Render en cas d’utilisation de l’authentification Microsoft Entra.

Pour afficher les données d’une source de vignette vectorielle sur la carte, connectez la source à l’une des couches de rendu de données. Toutes les couches qui utilisent une source vectorielle doivent spécifier une valeur sourceLayer dans les options. Le code suivant charge le service de mosaïque vectorielle Débit de circulation Azure Maps comme source de mosaïque vectorielle, puis l’affiche sur une carte à l’aide d’une couche de lignes. Cette source de vignette vectorielle comporte un seul jeu de données dans la couche source, appelé « Débit de circulation ». Les données de lignes de ce jeu de données possèdent une propriété nommée traffic_level qui est utilisée dans ce code pour sélectionner la couleur et mettre à l’échelle la taille des lignes.

// 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")

Carte avec des lignes de route avec un codage à l’aide de couleurs présentant différents niveaux de flux de trafic.

Connecter une source de données à un calque

Les données sont affichées sur la carte à l’aide de calques de rendu. Une ou plusieurs couches de rendu peuvent référencer une seule source de données. Les calques de rendu suivants nécessitent une source de données :

Le code suivant montre comment créer une source de données, l’ajouter à la carte, importer des données de point GeoJSON à partir d’un emplacement distant dans la source de données, puis la connecter à un calque de bulles.

// 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)

Il existe d’autres calques de rendu qui ne se connectent pas à ces sources de données, mais qui chargent directement les données pour les rendre.

Une source de données avec plusieurs calques

Plusieurs calques peuvent être connectés à une source de données unique. Cette option est utile dans de nombreux scénarios. Par exemple, considérez le scénario où un utilisateur dessine un polygone. Nous devons afficher et remplir la surface du polygone quand l’utilisateur ajoute des points à la carte. L’ajout d’une ligne stylisée pour le contour du polygone facilite la visualisation des bords du polygone à mesure que l’utilisateur le dessine. Pour modifier facilement une position individuelle dans le polygone, nous pouvons ajouter une poignée, comme une épingle ou un marqueur, au-dessus de chaque position.

Carte montrant plusieurs calques qui affichent des données d’une source de données unique.

Dans la plupart des plateformes de cartographie, vous avez besoin d’un objet polygone, d’un objet ligne et d’une épingle pour chaque position dans le polygone. Quand le polygone est modifié, vous devez alors mettre à jour manuellement la ligne et les épingles, ce qui peut devenir rapidement complexe.

Avec Azure Maps, tout ce dont vous avez besoin est un seul polygone dans une source de données, comme le montre le code suivant.

// 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])

Conseil

Vous pouvez également utiliser la méthode map.layers.insertLayer(_:below:), où l’ID ou l’instance d’une calque existant peut être transmis en tant que second paramètre. Cela permet d’indiquer à la carte d’insérer la nouvelle couche au-dessous de la couche existante. En plus de transmettre un ID de couche, cette méthode prend en charge les valeurs suivantes.

  • "labels" : insère la nouvelle couche sous les couches d’étiquettes de la carte.
  • "transit" : insère la nouvelle couche sous les couches de routes et de transit de la carte.

Informations supplémentaires

Pour obtenir plus d’exemples de code à ajouter à vos cartes, consultez les articles suivants :