Udostępnij za pośrednictwem


Tworzenie źródła danych w zestawie SDK systemu iOS (wersja zapoznawcza)

Uwaga

Wycofanie zestawu AZURE Mapy iOS SDK

Zestaw Azure Mapy Native SDK dla systemu iOS jest teraz przestarzały i zostanie wycofany w dniu 3/31/25. Aby uniknąć przerw w działaniu usługi, przeprowadź migrację do zestawu Sdk sieci Web usługi Azure Mapy do 31/31/25. Aby uzyskać więcej informacji, zobacz Przewodnik migracji zestawu SDK platformy Azure Mapy iOS.

Zestaw AZURE Mapy iOS SDK przechowuje dane w źródłach danych. Korzystanie ze źródeł danych optymalizuje operacje na danych na potrzeby wykonywania zapytań i renderowania. Obecnie istnieją dwa typy źródeł danych:

  • Źródło GeoJSON: zarządza nieprzetworzonymi danymi lokalizacji w formacie GeoJSON lokalnie. Dobre dla małych i średnich zestawów danych (w górę setki tysięcy kształtów).
  • Źródło kafelka wektorowego: ładuje dane sformatowane jako kafelki wektorowe dla bieżącego widoku mapy na podstawie systemu układania map. Idealne rozwiązanie dla dużych i ogromnych zestawów danych (miliony lub miliardy kształtów).

Źródło danych GeoJSON

Usługa Azure Mapy używa formatu GeoJSON jako jednego z podstawowych modeli danych. GeoJSON to otwarty standardowy sposób geoprzestrzennego reprezentowania danych geoprzestrzennych w formacie JSON. Klasy GeoJSON dostępne w zestawie SDK platformy Azure Mapy iOS w celu łatwego tworzenia i serializowania danych GeoJSON. Załaduj i zapisz dane GeoJSON w klasie i renderuj DataSource je przy użyciu warstw. Poniższy kod pokazuje, jak można tworzyć obiekty GeoJSON w usłudze Azure Mapy.

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

Alternatywnie właściwości można załadować do słownika (JSON), a następnie przekazać je do funkcji podczas jej tworzenia, jak pokazano w poniższym kodzie:

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

Po utworzeniu funkcji GeoJSON można dodać źródło danych do mapy za pomocą sources właściwości mapy. Poniższy kod pokazuje, jak utworzyć obiekt DataSource, dodać go do mapy i dodać funkcję do źródła danych.

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

Poniższy kod przedstawia kilka sposobów tworzenia geoJSON Feature, FeatureCollectioni geometrii.

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

Serializowanie i deserializowanie danych GeoJSON

Wszystkie klasy kolekcji funkcji, funkcji i geometrii mają fromJson(_:) i toJson() statyczne metody, które ułatwiają serializacji. Sformatowany prawidłowy ciąg JSON przekazany przez fromJson() metodę tworzy obiekt geometryczny. Ta fromJson() metoda oznacza również, że można użyć JSONSerialization lub innych strategii serializacji/deserializacji. Poniższy kod pokazuje, jak pobrać funkcję GeoJSON ze skreśloną ciągiem i deserializować ją do Feature klasy, a następnie serializować ją z powrotem do ciągu 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()

Importowanie danych GeoJSON z folderu internetowego lub zawartości

Większość plików GeoJSON zawiera element FeatureCollection. Odczytywanie plików GeoJSON jako ciągów i używanie FeatureCollection.fromJson(_:) metody do deserializacji.

Klasa DataSource ma wbudowaną metodę o nazwie importData(fromURL:) , która może ładować pliki GeoJSON przy użyciu adresu URL do pliku w Internecie lub na urządzeniu.

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

Metoda importData(fromURL:) zapewnia sposób ładowania źródła danych GeoJSON do źródła danych, ale zapewnia ograniczoną kontrolę nad tym, jak dane są ładowane i co się stanie po załadowaniu. Poniższy kod to klasa wielokrotnego użytku do importowania danych z folderu sieci Web lub zasobów i zwracania ich do wątku interfejsu użytkownika za pośrednictwem funkcji wywołania zwrotnego. W wywołaniu zwrotnym możesz dodać więcej logiki ładowania po załadowaniu, aby przetworzyć dane, dodać je do mapy, obliczyć pole ograniczenia i zaktualizować aparat map.

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

Poniższy kod pokazuje, jak za pomocą tego narzędzia zaimportować dane GeoJSON jako ciąg i zwrócić je do głównego wątku za pośrednictwem wywołania zwrotnego. W wywołaniu zwrotnym dane ciągu można serializować w kolekcji funkcji GeoJSON i dodawane do źródła danych. Opcjonalnie zaktualizuj aparat map, aby skoncentrować się na danych.

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

Aktualizowanie funkcji

Klasa DataSource ułatwia dodawanie i usuwanie funkcji. Zaktualizowanie geometrii lub właściwości funkcji wymaga zastąpienia funkcji w źródle danych. Istnieją dwie metody, które mogą służyć do aktualizowania funkcji:

  1. Utwórz nowe funkcje z żądanymi aktualizacjami i zastąp wszystkie funkcje w źródle danych przy użyciu set metody . Ta metoda sprawdza się dobrze, gdy chcesz zaktualizować wszystkie funkcje w źródle danych.
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. Śledź wystąpienie funkcji w zmiennej i przekaż je do metody źródeł remove danych, aby je usunąć. Utwórz nowe funkcje z żądanymi aktualizacjami, zaktualizuj odwołanie do zmiennej i dodaj ją do źródła danych przy użyciu add metody .
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)
}

Napiwek

Jeśli masz pewne dane, które będą regularnie aktualizowane, a inne dane, które będą rzadko zmieniane, najlepiej podzielić je na oddzielne wystąpienia źródła danych. Gdy aktualizacja występuje w źródle danych, wymusza na mapie przemalowania wszystkich funkcji w źródle danych. Dzięki podzieleniu tych danych tylko funkcje, które są regularnie aktualizowane, zostaną przemalowane po wystąpieniu aktualizacji w tym jednym źródle danych, podczas gdy funkcje w drugim źródle danych nie będą musiały zostać przemalowane. Pomaga to w wydajności.

Źródło kafelka wektorowego

Źródło kafelka wektorowego opisuje sposób uzyskiwania dostępu do warstwy kafelka wektorowego. VectorTileSource Użyj klasy , aby utworzyć wystąpienie źródła kafelka wektorowego. Warstwy kafelków wektorowych są podobne do warstw kafelków, ale nie są takie same. Warstwa kafelka to obraz rastrowy. Warstwy kafelków wektorowych są skompresowanym plikiem w formacie PBF . Ten skompresowany plik zawiera dane mapy wektorów i co najmniej jedną warstwę. Plik można renderować i stylizowany na kliencie na podstawie stylu każdej warstwy. Dane na kafelku wektorowym zawierają cechy geograficzne w postaci punktów, linii i wielokątów. Istnieje kilka zalet używania warstw kafelków wektorowych zamiast warstw kafelków rastrowych:

  • Rozmiar pliku kafelka wektorowego jest zwykle znacznie mniejszy niż równoważny kafelek rastrowy. W związku z tym używana jest mniejsza przepustowość. Oznacza to mniejsze opóźnienie, szybszą mapę i lepsze środowisko użytkownika.
  • Ponieważ kafelki wektorowe są renderowane na kliencie, dostosowują się do rozdzielczości wyświetlanego urządzenia. W związku z tym renderowane mapy są bardziej dobrze zdefiniowane z krystalicznie przejrzystymi etykietami.
  • Zmiana stylu danych w mapach wektorowych nie wymaga ponownego pobrania danych, ponieważ nowy styl można zastosować na kliencie. Z kolei zmiana stylu warstwy kafelka rastrowego zwykle wymaga ładowania kafelków z serwera, a następnie zastosowania nowego stylu.
  • Ponieważ dane są dostarczane w postaci wektorowej, przetwarzanie po stronie serwera jest mniej wymagane do przygotowania danych. W związku z tym nowsze dane mogą być udostępniane szybciej.

Usługa Azure Mapy jest zgodna ze specyfikacją kafelka Mapbox Vector— otwartym standardem. Usługa Azure Mapy udostępnia następujące usługi kafelków wektorowych w ramach platformy:

Napiwek

W przypadku korzystania z kafelków obrazów wektorowych lub rasterowych z usługi renderowania usługi Azure Mapy za pomocą zestawu SDK systemu iOS można zastąpić atlas.microsoft.com element właściwością AzureMap. domainPlaceholder Ten symbol zastępczy zostanie zastąpiony tą samą domeną używaną przez mapę i automatycznie dołączy te same szczegóły uwierzytelniania. Znacznie upraszcza to uwierzytelnianie za pomocą usługi renderowania podczas korzystania z uwierzytelniania firmy Microsoft Entra.

Aby wyświetlić dane ze źródła kafelka wektorowego na mapie, połącz źródło z jedną z warstw renderowania danych. Wszystkie warstwy używające źródła wektorów muszą określać sourceLayer wartość w opcjach. Poniższy kod ładuje usługę kafelek wektora przepływu ruchu platformy Azure Mapy jako źródło kafelka wektora, a następnie wyświetla go na mapie przy użyciu warstwy liniowej. To wektorowe źródło kafelków ma jeden zestaw danych w warstwie źródłowej o nazwie "Przepływ ruchu". Dane wiersza w tym zestawie danych mają właściwość o nazwie traffic_level , która jest używana w tym kodzie do wybierania koloru i skalowania rozmiaru linii.

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

Zrzut ekranu przedstawiający mapę z kolorowymi liniami dróg z poziomami przepływu ruchu.

Połączenie źródła danych do warstwy

Dane są renderowane na mapie przy użyciu warstw renderowania. Co najmniej jedna warstwa renderowania może odwoływać się do jednego źródła danych. Następujące warstwy renderowania wymagają źródła danych:

  • Warstwa bąbelkowa — renderuje dane punktów jako skalowane okręgi na mapie.
  • Warstwa symboli — renderuje dane punktów jako ikony lub tekst.
  • Warstwa mapy cieplnej — renderuje dane punktów jako mapę cieplną o gęstości.
  • Warstwa liniowa — renderuje linię i renderuje kontur wielokątów.
  • Warstwa wielokątna — wypełnia obszar wielokąta kolorem stałym lub wzorcem obrazu.

Poniższy kod pokazuje, jak utworzyć źródło danych, dodać je do mapy, zaimportować dane punktu GeoJSON z lokalizacji zdalnej do źródła danych, a następnie połączyć je z warstwą bąbelka.

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

Istnieją inne warstwy renderowania, które nie łączą się z tymi źródłami danych, ale bezpośrednio ładują dane do renderowania.

  • Warstwa kafelka — nakłada warstwę kafelka rastrowego na wierzchu mapy.

Jedno źródło danych z wieloma warstwami

Wiele warstw można połączyć z jednym źródłem danych. Istnieje wiele różnych scenariuszy, w których ta opcja jest przydatna. Rozważmy na przykład scenariusz, w którym użytkownik rysuje wielokąt. Powinniśmy renderować i wypełniać obszar wielokąta, gdy użytkownik dodaje punkty do mapy. Dodanie linii stylowanej w celu nakreślenia wielokąta ułatwia wyświetlanie krawędzi wielokąta, gdy użytkownik rysuje. Aby wygodnie edytować poszczególne pozycje w wielokącie, możemy dodać uchwyt, taki jak pinezka lub znacznik, nad każdą pozycją.

Zrzut ekranu przedstawiający mapę przedstawiającą wiele warstw renderowania danych z jednego źródła danych.

W większości platform mapowania potrzebny jest obiekt wielokątny, obiekt liniowy i pinezka dla każdej pozycji w wielokącie. W miarę modyfikacji wielokąta należy ręcznie zaktualizować wiersz i pinezki, co może szybko stać się złożone.

W przypadku usługi Azure Mapy wystarczy jeden wielokąt w źródle danych, jak pokazano w poniższym kodzie.

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

Napiwek

Można również użyć map.layers.insertLayer(_:below:) metody , w której identyfikator lub wystąpienie istniejącej warstwy można przekazać jako drugi parametr. Spowoduje to, że mapa wstawia nową warstwę dodaną poniżej istniejącej warstwy. Oprócz przekazania identyfikatora warstwy ta metoda obsługuje również następujące wartości.

  • "labels" — Wstawia nową warstwę poniżej warstw etykiet mapy.
  • "transit" - Wstawia nową warstwę poniżej warstwy mapy i tranzytowej.

Dodatkowe informacje

Więcej przykładów kodu do dodania do map można znaleźć w następujących artykułach: