Dela via


Skapa en datakälla i iOS SDK (förhandsversion)

Kommentar

Azure Kartor iOS SDK-tillbakadragning

Azure Kartor Native SDK för iOS är nu inaktuell och kommer att dras tillbaka den 3/31/25. För att undvika tjänststörningar migrerar du till Azure Kartor Web SDK senast 3/31/25. Mer information finns i migreringsguiden för Azure Kartor iOS SDK.

Azure Kartor iOS SDK lagrar data i datakällor. Med hjälp av datakällor optimeras dataåtgärderna för frågor och återgivning. För närvarande finns det två typer av datakällor:

  • GeoJSON-källa: Hanterar rådata i GeoJSON-format lokalt. Bra för små till medelstora datamängder (uppemot hundratusentals former).
  • Källa för vektorpanel: Läser in data som är formaterade som vektorpaneler för den aktuella kartvyn, baserat på kartpanelsystemet. Perfekt för stora till stora datamängder (miljoner eller miljarder former).

GeoJSON-datakälla

Azure Kartor använder GeoJSON som en av sina primära datamodeller. GeoJSON är ett öppet geospatialt standardsätt för att representera geospatiala data i JSON-format. GeoJSON-klasser som är tillgängliga i Azure Kartor iOS SDK för att enkelt skapa och serialisera GeoJSON-data. Läs in och lagra GeoJSON-data i DataSource klassen och återge dem med hjälp av lager. Följande kod visar hur GeoJSON-objekt kan skapas i Azure Kartor.

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

Alternativt kan egenskaperna läsas in i en ordlista (JSON) först och sedan skickas till funktionen när du skapar den, som följande kod visar:

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

När du har skapat en GeoJSON-funktion kan en datakälla läggas till på kartan via sources kartans egenskap. Följande kod visar hur du skapar en DataSource, lägger till den på kartan och lägger till en funktion i datakällan.

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

Följande kod visar flera sätt att skapa geoJSON Feature- och FeatureCollectiongeometrier.

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

Serialisera och deserialisera GeoJSON

Funktionssamlingen, funktionsklasserna och geometriklasserna har fromJson(_:) alla och toJson() statiska metoder som hjälper till med serialisering. Den formaterade giltiga JSON-strängen fromJson() som skickas genom metoden skapar geometriobjektet. Den här fromJson() metoden innebär också att du kan använda JSONSerialization eller andra serialiserings-/deserialiseringsstrategier. Följande kod visar hur du tar en stränganpassad GeoJSON-funktion och deserialiserar den Feature i klassen och sedan serialiserar den tillbaka till en GeoJSON-sträng.

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

Importera GeoJSON-data från webb- eller resursmappen

De flesta GeoJSON-filer innehåller en FeatureCollection. Läs GeoJSON-filer som strängar och använd FeatureCollection.fromJson(_:) metoden för att deserialisera dem.

Klassen DataSource har en inbyggd metod med namnet importData(fromURL:) som kan läsas in i GeoJSON-filer med hjälp av en URL till en fil på webben eller enheten.

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

Metoden importData(fromURL:) ger ett sätt att läsa in en GeoJSON-feed till en datakälla, men ger begränsad kontroll över hur data läses in och vad som händer när de har lästs in. Följande kod är en återanvändbar klass för att importera data från webb- eller resursmappen och returnera dem till användargränssnittstråden via en återanropsfunktion. I återanropet kan du sedan lägga till mer logik efter inläsning för att bearbeta data, lägga till dem på kartan, beräkna dess avgränsningsruta och uppdatera kartkameran.

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

Följande kod visar hur du använder det här verktyget för att importera GeoJSON-data som en sträng och returnera dem till huvudtråden via ett återanrop. I motringningen kan strängdata serialiseras till en GeoJSON-funktionssamling och läggas till i datakällan. Du kan också uppdatera kartkameran så att den fokuserar på data.

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

Uppdatera en funktion

Klassen DataSource gör det enkelt att lägga till och ta bort funktioner. Om du uppdaterar geometrin eller egenskaperna för en funktion måste du ersätta funktionen i datakällan. Det finns två metoder som kan användas för att uppdatera en eller flera funktioner:

  1. Skapa de nya funktionerna med önskade uppdateringar och ersätt alla funktioner i datakällan med hjälp av set metoden . Den här metoden fungerar bra när du vill uppdatera alla funktioner i en datakälla.
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. Håll reda på funktionsinstansen i en variabel och skicka den till datakällans remove metod för att ta bort den. Skapa de nya funktionerna med önskade uppdateringar, uppdatera variabelreferensen och lägg till den i datakällan med hjälp av add metoden.
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)
}

Dricks

Om du har vissa data som kommer att uppdateras regelbundet och andra data som sällan ändras är det bäst att dela upp dessa i separata datakällsinstanser. När en uppdatering sker i en datakälla tvingar den kartan att måla om alla funktioner i datakällan. Genom att dela upp dessa data skulle endast de funktioner som uppdateras regelbundet ommålas när en uppdatering sker i den datakällan, medan funktionerna i den andra datakällan inte behöver målas om. Detta hjälper till med prestanda.

Vektorpanelkälla

En vektorpanelkälla beskriver hur du kommer åt ett vektorpanelskikt. VectorTileSource Använd klassen för att instansiera en vektorpanelkälla. Vektorpanelsskikt liknar panelskikt, men de är inte desamma. Ett panellager är en rasterbild. Vektorpanelslager är en komprimerad fil i PBF-format . Den här komprimerade filen innehåller mappningsdata för vektorer och ett eller flera lager. Filen kan renderas och formateras på klienten baserat på formatet för varje lager. Data i en vektorpanel innehåller geografiska funktioner i form av punkter, linjer och polygoner. Det finns flera fördelar med att använda vektorpanelslager i stället för rasterpanellager:

  • En filstorlek på en vektorpanel är vanligtvis mycket mindre än en motsvarande rasterpanel. Därför används mindre bandbredd. Det innebär kortare svarstid, en snabbare karta och en bättre användarupplevelse.
  • Eftersom vektorpaneler renderas på klienten anpassas de till upplösningen för den enhet som de visas på. Därför visas de renderade kartorna mer väldefinierade med kristallklara etiketter.
  • För att ändra dataformatet i vektorkartor krävs inte att data laddas ned igen, eftersom det nya formatet kan tillämpas på klienten. Att ändra formatet för ett rasterpanellager kräver däremot vanligtvis att paneler läses in från servern och att det nya formatet tillämpas.
  • Eftersom data levereras i vektorform krävs mindre bearbetning på serversidan för att förbereda data. Därför kan nyare data göras tillgängliga snabbare.

Azure Kartor följer Mapbox Vector Tile Specification, en öppen standard. Azure Kartor tillhandahåller följande tjänster för vektorpaneler som en del av plattformen:

Dricks

När du använder vektor- eller rastrbildpaneler från Azure Kartor-renderningstjänsten med iOS SDK kan du ersätta atlas.microsoft.com med AzureMapegenskapen "s"domainPlaceholder. Platshållaren ersätts med samma domän som används av kartan och lägger automatiskt till samma autentiseringsinformation. Detta förenklar autentiseringen avsevärt med renderingstjänsten när du använder Microsoft Entra-autentisering.

Om du vill visa data från en vektorpanelskälla på kartan ansluter du källan till ett av datarenderingsskikten. Alla lager som använder en vektorkälla måste ange ett sourceLayer värde i alternativen. Följande kod läser in Azure Kartor trafikflödesvektorpaneltjänsten som en vektorpanelkälla och visar den sedan på en karta med hjälp av ett linjeskikt. Den här vektorpanelens källa har en enda uppsättning data i källskiktet som kallas "Trafikflöde". Raddata i den här datauppsättningen har en egenskap som heter traffic_level som används i den här koden för att välja färg och skala storleken på linjer.

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

Skärmbild av en karta med färgkodade väglinjer som visar trafikflödesnivåer.

Anslut en datakälla till ett lager

Data återges på kartan med hjälp av återgivningslager. Ett eller flera återgivningslager kan referera till en enda datakälla. Följande återgivningslager kräver en datakälla:

  • Bubbelskikt – återger punktdata som skalade cirklar på kartan.
  • Symbolskikt – återger punktdata som ikoner eller text.
  • Termisk kartskikt – återger punktdata som en densitetsvärmekarta.
  • Linjeskikt – återge en linje och eller återge konturen av polygoner.
  • Polygonskikt – fyller området i en polygon med en solid färg eller ett bildmönster.

Följande kod visar hur du skapar en datakälla, lägger till den på kartan, importerar GeoJSON-punktdata från en fjärrplats till datakällan och ansluter den sedan till ett bubbellager.

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

Det finns andra återgivningslager som inte ansluter till dessa datakällor, men de läser in data direkt för återgivning.

  • Panelskikt – lägger över ett rasterpanellager ovanpå kartan.

En datakälla med flera lager

Flera lager kan anslutas till en enda datakälla. Det finns många olika scenarier där det här alternativet är användbart. Tänk till exempel på scenariot där en användare ritar en polygon. Vi bör rendera och fylla i polygonområdet när användaren lägger till punkter på kartan. Genom att lägga till en linje med stil för att beskriva polygonen blir det enklare att se polygonens kanter när användaren ritar. För att enkelt redigera en enskild position i polygonen kan vi lägga till ett handtag, till exempel en stift eller en markör, ovanför varje position.

Skärmbild av en karta som visar flera lager som återger data från en enda datakälla.

I de flesta mappningsplattformar behöver du ett polygonobjekt, ett linjeobjekt och en pin-kod för varje position i polygonen. När polygonen ändras måste du uppdatera linjen och stiften manuellt, vilket snabbt kan bli komplext.

Med Azure Kartor behöver du bara en enda polygon i en datakälla enligt följande kod.

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

Dricks

Du kan också använda map.layers.insertLayer(_:below:) metoden där ID:t eller instansen av ett befintligt lager kan skickas in som en andra parameter. Detta skulle tala om för kartan att infoga det nya lagret som läggs till under det befintliga lagret. Förutom att skicka in ett lager-ID stöder den här metoden även följande värden.

  • "labels" – Infogar det nya lagret under kartetikettlagren.
  • "transit" – Infogar det nya lagret under kartvägs- och transitskikten.

Ytterligare information

I följande artiklar finns fler kodexempel att lägga till i dina kartor: