Freigeben über


Clustering von Punktdaten im iOS SDK (Vorschau)

Hinweis

Einstellung des Azure Maps iOS SDK

Das Azure Maps Native SDK für iOS ist jetzt veraltet und wird am 31.3.25 eingestellt. Um Dienstunterbrechungen zu vermeiden, migrieren Sie bis zum 31.3.25 zum Azure Maps Web SDK. Weitere Informationen finden Sie unter Anleitung zur Migration des Azure Maps iOS SDK.

Wenn auf der Karte viele Datenpunkte angezeigt werden, kann es bei diesen Datenpunkten zu gegenseitigen Überlappungen kommen. Die Überlappungen können dazu führen, dass die Karte unlesbar und schwer zu verwenden ist. Das Clustering von Punktdaten ist der Prozess, bei dem benachbarte Punktdaten kombiniert und auf der Karte als ein einzelner gruppierter Datenpunkt dargestellt werden. Wenn der Benutzer in die Karte zoomt, werden die Cluster in ihre einzelnen Datenpunkte unterteilt. Wenn Sie mit einer großen Anzahl von Datenpunkten arbeiten, verwenden Sie die Clusteringprozesse, um die Erfahrung Ihrer Benutzer zu verbessern.

IoT-Show – Clustering von Punktdaten in Azure Maps

Voraussetzungen

Führen Sie die Schritte im Dokument Erstellen einer iOS-App (Public Preview) aus. Codeblöcke in diesem Artikel können in die viewDidLoad-Funktion von ViewController eingefügt werden.

Aktivieren des Clusterings für eine Datenquelle

Aktivieren des Clusterings in der DataSource-Klasse, indem Sie die Option cluster auf true festlegen. Durch Festlegen von clusterRadius werden nahe gelegene Punkte ausgewählt und zu einem Cluster kombiniert. Die Einheit von clusterRadius ist in Punkten angegeben. Verwenden Sie clusterMaxZoom, um einen Zoomfaktor anzugeben, ab dem die Clusteringlogik deaktiviert werden soll. Hier ist ein Beispiel, wie Sie das Clustering in einer Datenquelle aktivieren können.

// Create a data source and enable clustering.
let source = DataSource(options: [
    //Tell the data source to cluster point data.
    .cluster(true),

    //The radius in points to cluster data points together.
    .clusterRadius(45),

    //The maximum zoom level in which clustering occurs.
    //If you zoom in more than this, all points are rendered as symbols.
    .clusterMaxZoom(15)
])

Achtung

Clustering funktioniert nur mit Point-Features. Wenn Ihre Datenquelle Features anderer Geometrietypen enthält (z. B. Polyline oder Polygon), tritt ein Fehler auf.

Tipp

Wenn zwei Datenpunkte im Gelände dicht beieinander liegen, ist es möglich, dass der Cluster nie aufgelöst wird, unabhängig davon, wie stark der Benutzer vergrößert. Um dies zu umgehen, können Sie die Option clusterMaxZoom so festlegen, dass die Clusteringlogik deaktiviert und somit einfach alles angezeigt wird.

Die Klasse DataSource stellt im Hinblick auf das Clustering auch die folgenden Methoden bereit.

Methode Rückgabetyp BESCHREIBUNG
children(of cluster: Feature) [Feature] Ruft die untergeordneten Elemente des angegebenen Clusters für den nächsten Zoomfaktor ab. Diese untergeordneten Elemente können sowohl Features als auch untergeordnete Cluster sein. Die untergeordneten Cluster werden zu Features mit Eigenschaften, die ClusteredProperties entsprechen.
zoomLevel(forExpanding cluster: Feature) Double Berechnet einen Zoomfaktor, bei dem der Cluster mit der Erweiterung oder Unterteilung beginnt.
leaves(of cluster: Feature, offset: UInt, limit: UInt) [Feature] Ruft alle Punkte in einem Cluster ab. Legen Sie limit fest, um eine Teilmenge der Punkte zurückzugeben, und verwenden Sie offset, um die Punkte zu durchlaufen.

Anzeigen von Clustern mithilfe einer Blasenebene

Eine Blasenebene ist eine hervorragende Möglichkeit, um geclusterte Datenpunkte zu rendern. Verwenden Sie Ausdrücke zum Skalieren des Radius, und ändern Sie die Farbe basierend auf der Anzahl der Punkte im Cluster. Bei der Darstellung von Clustern mit einer Blasenebene sollten Sie eine separate Ebene für das Rendering nicht geclusterter Datenpunkte verwenden.

Um die Größe des Clusters auf der Blase anzuzeigen, verwenden Sie eine Symbolebene mit Text und verwenden kein Symbol.

Der folgende Code zeigt geclusterte Punkte mithilfe einer Blasenebene und die Anzahl von Punkten in jedem Cluster mithilfe einer Symbolebene an. Eine zweite Symbolebene wird verwendet, um einzelne Punkte anzuzeigen, die sich nicht innerhalb eines Clusters befinden.

// Create a data source and enable clustering.
let source = DataSource(options: [
    // Tell the data source to cluster point data.
    .cluster(true),

    // The radius in points to cluster data points together.
    .clusterRadius(45),

    // The maximum zoom level in which clustering occurs.
    // If you zoom in more than this, all points are rendered as symbols.
    .clusterMaxZoom(15)
])

// Import the geojson data and add it to the data source.
let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
source.importData(fromURL: url)

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

// Create a bubble layer for rendering clustered data points.
map.layers.addLayer(
    BubbleLayer(
        source: source,
        options: [
            // Scale the size of the clustered bubble based on the number of points in the cluster.
            .bubbleRadius(
                from: NSExpression(
                    forAZMStepping: NSExpression(forKeyPath: "point_count"),
                    // Default of 20 point radius.
                    from: NSExpression(forConstantValue: 20),
                    stops: NSExpression(forConstantValue: [

                        // If point_count >= 100, radius is 30 points.
                        100: 30,

                        // If point_count >= 750, radius is 40 points.
                        750: 40
                    ])
                )
            ),

            // Change the color of the cluster based on the value on the point_count property of the cluster.
            .bubbleColor(
                from: NSExpression(
                    forAZMStepping: NSExpression(forKeyPath: "point_count"),
                    // Default to green.
                    from: NSExpression(forConstantValue: UIColor.green),
                    stops: NSExpression(forConstantValue: [

                        // If the point_count >= 100, color is yellow.
                        100: UIColor.yellow,

                        // If the point_count >= 100, color is red.
                        750: UIColor.red
                    ])
                )
            ),
            .bubbleStrokeWidth(0),

            // Only rendered data points which have a point_count property, which clusters do.
            .filter(from: NSPredicate(format: "point_count != NIL"))
        ]
    )
)

// Create a symbol layer to render the count of locations in a cluster.
map.layers.addLayer(
    SymbolLayer(
        source: source,
        options: [
            // Hide the icon image.
            .iconImage(nil),

            // Display the point count as text.
            .textField(from: NSExpression(forKeyPath: "point_count")),
            .textOffset(CGVector(dx: 0, dy: 0.4)),
            .textAllowOverlap(true),

            // Allow clustered points in this layer.
            .filter(from: NSPredicate(format: "point_count != NIL"))
        ]
    )
)

// Create a layer to render the individual locations.
map.layers.addLayer(
    SymbolLayer(
        source: source,
        options: [
            // Filter out clustered points from this layer.
            .filter(from: NSPredicate(format: "point_count = NIL"))
        ]
    )
)

Die folgende Abbildung zeigt, wie der obige Code geclusterte Punktfeatures skaliert und basierend auf der Anzahl von Punkten im Cluster farbig dargestellt in einer Blasenebene anzeigt. Nicht geclusterte Punkte werden mithilfe einer Symbolebene gerendert.

Karte geclusterter Standorte, die beim Vergrößern der Karte aufgelöst bzw. unterteilt werden

Anzeigen von Clustern mithilfe einer Symbolebene

Beim Anzeigen von Datenpunkten blendet die Symbolebene automatisch Symbole aus, die einander überlappen, um eine klarere Benutzeroberfläche sicherzustellen. Dieses Standardverhalten ist möglicherweise unerwünscht, wenn Sie die Datenpunktdichte auf der Karte anzeigen möchten. Diese Einstellungen können jedoch geändert werden. Um alle Symbole anzuzeigen, legen Sie die Option iconAllowOverlap der Symbolebenen auf true fest.

Verwenden Sie Clustering, um die Datenpunktdichte anzuzeigen und gleichzeitig eine aufgeräumte Benutzeroberfläche zu erhalten. Das folgende Beispiel zeigt, wie Sie mithilfe der Symbolebene benutzerdefinierte Symbole hinzufügen sowie Cluster und einzelne Datenpunkte darstellen.

// Load all the custom image icons into the map resources.
map.images.add(UIImage(named: "earthquake_icon")!, withID: "earthquake_icon")
map.images.add(UIImage(named: "warning-triangle-icon")!, withID: "warning-triangle-icon")

// Create a data source and add it to the map.
let source = DataSource(options: [
    // Tell the data source to cluster point data.
    .cluster(true)
])

// Import the geojson data and add it to the data source.
let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
source.importData(fromURL: url)

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

// Create a layer to render the individual locations.
map.layers.addLayer(
    SymbolLayer(
        source: source,
        options: [
            .iconImage("earthquake_icon"),

            // Filter out clustered points from this layer.
            .filter(from: NSPredicate(format: "point_count = NIL"))
        ]
    )
)

// Create a symbol layer to render the clusters.
map.layers.addLayer(
    SymbolLayer(
        source: source,
        options: [
            .iconImage("warning-triangle-icon"),
            .textField(from: NSExpression(forKeyPath: "point_count")),
            .textOffset(CGVector(dx: 0, dy: -0.4)),

            // Allow clustered points in this layer.
            .filter(from: NSPredicate(format: "point_count != NIL"))
        ]
    )
)

Für dieses Beispiel wurde das folgende Bild in den Asset-Ordner der App geladen.

Abbildung des Erdbebensymbols Wettersymbolbild für Regenschauer
earthquake-icon.png warning-triangle-icon.png

Die folgende Abbildung zeigt, wie der obige Code geclusterte und nicht geclusterte Punktfeatures mit benutzerdefinierten Symbolen rendert.

Karte geclusterter Punkte – gerendert auf einer Symbolebene

Clustering und Wärmebildebene

Wärmebilder sind eine hervorragende Möglichkeit, die Dichte der Daten auf der Karte anzuzeigen. Diese Visualisierungsmethode kann eigenständig eine große Anzahl von Datenpunkten verarbeiten. Wenn die Datenpunkte geclustert werden und die Clustergröße als Gewichtung des Wärmebilds verwendet wird, kann das Wärmebild sogar noch mehr Daten handhaben. Um diese Option zu realisieren, legen Sie die heatmapWeight-Option der Wärmebildebene auf NSExpression(forKeyPath: "point_count") fest. Bei einem kleinen Clusterradius sieht das Wärmebild fast genauso aus wie das Wärmebild mit den nicht geclusterten Datenpunkte, es ist aber leistungsfähiger. Je kleiner der Clusterradius, desto genauer ist das Wärmebild, jedoch mit geringeren Leistungsvorteilen.

// Create a data source and enable clustering.
let source = DataSource(options: [
    // Tell the data source to cluster point data.
    .cluster(true),

    // The radius in points to cluster points together.
    .clusterRadius(10)
])

// Import the geojson data and add it to the data source.
let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
source.importData(fromURL: url)

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

// Create a heat map and add it to the map.
map.layers.insertLayer(
    HeatMapLayer(
        source: source,
        options: [
            // Set the weight to the point_count property of the data points.
            .heatmapWeight(from: NSExpression(forKeyPath: "point_count")),

            // Optionally adjust the radius of each heat point.
            .heatmapRadius(20)
        ]
    ),
    below: "labels"
)

Die folgende Abbildung zeigt den obigen Code, der ein Wärmebild anzeigt, das mithilfe geclusterter Punktfeatures und der Clusteranzahl als Gewichtung im Wärmebild optimiert wurde.

Karte eines Wärmebilds – optimiert mit geclusterten Punkten als Gewichtung

Klick- bzw. Tippereignisse für geclusterte Datenpunkte

Bei einem Klick- bzw. Tippereignis auf einer Ebene mit geclusterten Datenpunkten wird der geclusterte Datenpunkt dem Ereignis als GeoJSON-Punktfeatureobjekt zurückgegeben. Dieses Punktfeature hat die folgenden Eigenschaften:

Eigenschaftenname Type Beschreibung
cluster boolean Gibt an, ob das Feature einen Cluster darstellt.
point_count number Die Anzahl der Punkte, die der Cluster enthält.
point_count_abbreviated Zeichenfolge Eine Zeichenfolge, die den point_count-Wert abkürzt, falls er zu lang ist. (Beispiel: 4.000 wird zu 4K)

Dieses Beispiel, in dem Clusterpunkte auf einer Blasenebene gerendert werden, fügt ein Tippereignis hinzu. Wenn das Tippereignis ausgelöst wird, berechnet der Code die Karte und zoomt sie auf die nächste Zoomstufe, bei der der Cluster aufgelöst wird. Diese Funktion wird mit der zoomLevel(forExpanding:)-Methode der DataSource-Klasse umgesetzt.

// Create a data source and enable clustering.
let source = DataSource(options: [
    // Tell the data source to cluster point data.
    .cluster(true),

    // The radius in points to cluster data points together.
    .clusterRadius(45),

    // The maximum zoom level in which clustering occurs.
    // If you zoom in more than this, all points are rendered as symbols.
    .clusterMaxZoom(15)
])

// Set data source to the class property to use in events handling later.
self.source = source

// Import the geojson data and add it to the data source.
let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
source.importData(fromURL: url)

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

// Create a bubble layer for rendering clustered data points.
let clusterBubbleLayer = BubbleLayer(
    source: source,
    options: [
        // Scale the size of the clustered bubble based on the number of points in the cluster.
        .bubbleRadius(
            from: NSExpression(
                forAZMStepping: NSExpression(forKeyPath: "point_count"),
                // Default of 20 point radius.
                from: NSExpression(forConstantValue: 20),
                stops: NSExpression(forConstantValue: [
                    // If point_count >= 100, radius is 30 points.
                    100: 30,

                    // If point_count >= 750, radius is 40 points.
                    750: 40
                ])
            )
        ),

        // Change the color of the cluster based on the value on the point_count property of the cluster.
        .bubbleColor(
            from: NSExpression(
                forAZMStepping: NSExpression(forKeyPath: "point_count"),
                // Default to green.
                from: NSExpression(forConstantValue: UIColor.green),
                stops: NSExpression(forConstantValue: [
                    // If the point_count >= 100, color is yellow.
                    100: UIColor.yellow,

                    // If the point_count >= 100, color is red.
                    750: UIColor.red
                ])
            )
        ),
        .bubbleStrokeWidth(0),

        // Only rendered data points which have a point_count property, which clusters do.
        .filter(from: NSPredicate(format: "point_count != NIL"))
    ]
)

// Add the clusterBubbleLayer to the map.
map.layers.addLayer(clusterBubbleLayer)

// Create a symbol layer to render the count of locations in a cluster.
map.layers.addLayer(
    SymbolLayer(
        source: source,
        options: [
            // Hide the icon image.
            .iconImage(nil),

            // Display the point count as text.
            .textField(from: NSExpression(forKeyPath: "point_count_abbreviated")),

            // Offset the text position so that it's centered nicely.
            .textOffset(CGVector(dx: 0, dy: 0.4)),

            // Allow text overlapping so text is visible anyway
            .textAllowOverlap(true),

            // Allow clustered points in this layer.
            .filter(from: NSPredicate(format: "point_count != NIL"))
        ]
    )
)

// Create a layer to render the individual locations.
map.layers.addLayer(
    SymbolLayer(
        source: source,
        options: [
            // Filter out clustered points from this layer.
            .filter(from: NSPredicate(format: "point_count = NIL"))
        ]
    )
)

// Add the delegate to handle taps on the clusterBubbleLayer only.
map.events.addDelegate(self, for: [clusterBubbleLayer.id])
func azureMap(_ map: AzureMap, didTapOn features: [Feature]) {
    guard let source = source, let cluster = features.first else {
        // Data source have been released or no features provided
        return
    }

    // Get the cluster expansion zoom level. This is the zoom level at which the cluster starts to break apart.
    let expansionZoom = source.zoomLevel(forExpanding: cluster)

    // Update the map camera to be centered over the cluster.
    map.setCameraOptions([
        // Center the map over the cluster points location.
        .center(cluster.coordinate),

        // Zoom to the clusters expansion zoom level.
        .zoom(expansionZoom),

        // Animate the movement of the camera to the new position.
        .animationType(.ease),
        .animationDuration(200)
    ])
}

Die folgende Abbildung zeigt den obigen Code, der geclusterte Punkte auf einer Karte anzeigt., die beim antippen auf den nächsten Zoomfaktor vergrößert, ab dem ein Cluster unterteilt und erweitert wird.

Karte geclusterter Features – diese wird bei einem Tippereignis vergrößert und aufgelöst

Anzeigen des Clusterbereichs

Die Punktdaten, die ein Cluster darstellt, sind über einen Bereich verteilt. In diesem Beispiel treten beim Tippen auf einen Cluster zwei primäre Verhaltensweisen auf. Zuerst werden die einzelnen Datenpunkte verwendet, die im Cluster enthalten sind, um eine konvexe Hülle zu berechnen. Anschließend wird die konvexe Hülle auf der Karte angezeigt, um einen Bereich zu zeigen. Eine konvexe Hülle ist ein Polygon, das einen Satz von Punkten wie ein elastisches Band umhüllt und mit der convexHull(from:)-Methode berechnet werden kann. Alle in einem Cluster enthaltenen Punkte können mit der Methode leaves(of:offset:limit:) aus der Datenquelle abgerufen werden.

// Create a data source and enable clustering.
let source = DataSource(options: [
    // Tell the data source to cluster point data.
    .cluster(true)
])

// Set data source to the class property to use in events handling later.
self.source = source

// Import the geojson data and add it to the data source.
let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
source.importData(fromURL: url)

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

// Create a data source for the convex hull polygon.
// Since this will be updated frequently it is more efficient to separate this into its own data source.
let polygonDataSource = DataSource()

// Set polygon data source to the class property to use in events handling later.
self.polygonDataSource = polygonDataSource

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

// Add a polygon layer and a line layer to display the convex hull.
map.layers.addLayer(PolygonLayer(source: polygonDataSource))
map.layers.addLayer(LineLayer(source: polygonDataSource))

// Load an icon into the image sprite of the map.
map.images.add(.azm_markerRed, withID: "marker-red")

// Create a symbol layer to render the clusters.
let clusterLayer = SymbolLayer(
    source: source,
    options: [
        .iconImage("marker-red"),
        .textField(from: NSExpression(forKeyPath: "point_count_abbreviated")),
        .textOffset(CGVector(dx: 0, dy: -1.2)),
        .textColor(.white),
        .textSize(14),

        // Only rendered data points which have a point_count property, which clusters do.
        .filter(from: NSPredicate(format: "point_count != NIL"))
    ]
)

// Add the clusterLayer to the map.
map.layers.addLayer(clusterLayer)

// Create a layer to render the individual locations.
map.layers.addLayer(
    SymbolLayer(
        source: source,
        options: [
            // Filter out clustered points from this layer.
            .filter(from: NSPredicate(format: "point_count = NIL"))
        ]
    )
)

// Add the delegate to handle taps on the clusterLayer only
// and then calculate the convex hull of all the points within a cluster.
map.events.addDelegate(self, for: [clusterLayer.id])
func azureMap(_ map: AzureMap, didTapOn features: [Feature]) {
    guard let source = source, let polygonDataSource = polygonDataSource, let cluster = features.first else {
        // Data source have been released or no features provided
        return
    }

    // Get all points in the cluster. Set the offset to 0 and the max int value to return all points.
    let featureLeaves = source.leaves(of: cluster, offset: 0, limit: .max)

    // When only two points in a cluster. Render a line.
    if featureLeaves.count == 2 {

        // Extract the locations from the feature leaves.
        let locations = featureLeaves.map(\.coordinate)

        // Create a line from the points.
        polygonDataSource.set(geometry: Polyline(locations))

        return
    }

    // When more than two points in a cluster. Render a polygon.
    if let hullPolygon = Math.convexHull(from: featureLeaves) {

        // Overwrite all data in the polygon data source with the newly calculated convex hull polygon.
        polygonDataSource.set(geometry: hullPolygon)
    }
}

Die folgende Abbildung zeigt den obigen Code, der den Bereich aller Punkte innerhalb eines Clusters anzeigt, der angetippt wurde.

Karte, die das Polygon der konvexen Hülle aller Punkte eines Clusters zeigt, auf den getippt wird

Aggregieren von Daten in Clustern

Cluster werden oft mithilfe eines Symbols mit der Anzahl der im Cluster enthaltenen Punkte dargestellt. Manchmal ist es jedoch wünschenswert, den Clusterstil mit zusätzlichen Metriken anzupassen. Mit Clustereigenschaften können benutzerdefinierte Eigenschaften erstellt werden. Sie entsprechen einer Berechnung, die auf den Eigenschaften innerhalb jedes Punkts in einem Cluster basiert. Clustereigenschaften können in der Option clusterProperties der DataSource definiert werden.

Der folgende Code berechnet eine Anzahl basierend auf der Eigenschaft „Entitätstyp“ der einzelnen Datenpunkte in einem Cluster. Wenn ein Benutzer auf einen Cluster tippt, wird ein Popup mit zusätzlichen Informationen über den Cluster angezeigt.

// Create a popup and add it to the map.
let popup = Popup()
map.popups.add(popup)

// Set popup to the class property to use in events handling later.
self.popup = popup

// Close the popup initially.
popup.close()

// Create a data source and enable clustering.
let source = DataSource(options: [
    // Tell the data source to cluster point data.
    .cluster(true),

    // The radius in points to cluster data points together.
    .clusterRadius(50),

    // Calculate counts for each entity type in a cluster as custom aggregate properties.
    .clusterProperties(self.entityTypes.map { entityType in
        ClusterProperty(
            name: entityType,
            operator: NSExpression(
                forFunction: "sum:",
                arguments: [
                    NSExpression.featureAccumulatedAZMVariable,
                    NSExpression(forKeyPath: entityType)
                ]
            ),
            map: NSExpression(
                forConditional: NSPredicate(format: "EntityType = '\(entityType)'"),
                trueExpression: NSExpression(forConstantValue: 1),
                falseExpression: NSExpression(forConstantValue: 0)
            )
        )
    })
])

// Import the geojson data and add it to the data source.
let url = URL(string: "https://samples.azuremaps.com/data/geojson/SamplePoiDataSet.json")!
source.importData(fromURL: url)

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

// Create a bubble layer for rendering clustered data points.
let clusterBubbleLayer = BubbleLayer(
    source: source,
    options: [
        .bubbleRadius(20),
        .bubbleColor(.purple),
        .bubbleStrokeWidth(0),

        // Only rendered data points which have a point_count property, which clusters do.
        .filter(from: NSPredicate(format: "point_count != NIL"))
    ]
)

// Add the clusterBubbleLayer to the map.
map.layers.addLayer(clusterBubbleLayer)

// Create a symbol layer to render the count of locations in a cluster.
map.layers.addLayer(
    SymbolLayer(
        source: source,
        options: [
            // Hide the icon image.
            .iconImage(nil),

            // Display the 'point_count_abbreviated' property value.
            .textField(from: NSExpression(forKeyPath: "point_count_abbreviated")),

            .textColor(.white),
            .textOffset(CGVector(dx: 0, dy: 0.4)),

            // Allow text overlapping so text is visible anyway
            .textAllowOverlap(true),

            // Only rendered data points which have a point_count property, which clusters do.
            .filter(from: NSPredicate(format: "point_count != NIL"))
        ]
    )
)

// Create a layer to render the individual locations.
map.layers.addLayer(
    SymbolLayer(
        source: source,
        options: [
            // Filter out clustered points from this layer.
            SymbolLayerOptions.filter(from: NSPredicate(format: "point_count = NIL"))
        ]
    )
)

// Add the delegate to handle taps on the clusterBubbleLayer only
// and display the aggregate details of the cluster.
map.events.addDelegate(self, for: [clusterBubbleLayer.id])
func azureMap(_ map: AzureMap, didTapOn features: [Feature]) {
    guard let popup = popup, let cluster = features.first else {
        // Popup has been released or no features provided
        return
    }

    // Create a number formatter that removes decimal places.
    let nf = NumberFormatter()
    nf.maximumFractionDigits = 0

    // Create the popup's content.
    var text = ""

    let pointCount = cluster.properties["point_count"] as! Int
    let pointCountString = nf.string(from: pointCount as NSNumber)!

    text.append("Cluster size: \(pointCountString) entities\n")

    entityTypes.forEach { entityType in
        text.append("\n")
        text.append("\(entityType): ")

        // Get the aggregated entity type count from the properties of the cluster by name.
        let aggregatedCount = cluster.properties[entityType] as! Int
        let aggregatedCountString = nf.string(from: aggregatedCount as NSNumber)!

        text.append(aggregatedCountString)
    }

    // Create the custom view for the popup.
    let customView = PopupTextView()

    // Set the text to the custom view.
    customView.setText(text)

    // Get the position of the cluster.
    let position = Math.positions(from: cluster).first!

    // Set the options on the popup.
    popup.setOptions([
        // Set the popups position.
        .position(position),

        // Set the anchor point of the popup content.
        .anchor(.bottom),

        // Set the content of the popup.
        .content(customView)
    ])

    // Open the popup.
    popup.open()
}

Das Popup entspricht den Schritten, die im Dokument Anzeigen eines Popups beschrieben werden.

Die folgende Abbildung zeigt den obigen Code, der ein Popup mit der aggregierten Anzahl der Entitätswerttypen aller Punkte des Clusters anzeigt, der angetippt wurde.

Karte mit einem Popup der aggregierten Anzahl der Entitätswerttypen aller Punkte eines Clusters

Weitere Informationen

So fügen Sie Ihrer Karte weitere Daten hinzu: