Можно ли привязать полилинию к аннотации?IOS

Программируем под IOS
Ответить
Anonymous
 Можно ли привязать полилинию к аннотации?

Сообщение Anonymous »

Я создаю картографическое приложение для автономного робота. У меня возникают трудности с совмещением аннотаций и полилиний при перетаскивании аннотации. В настоящее время аннотация будет перемещаться, а затем, после небольшой паузы, полилиния переместится и догонит ее.
Я хочу, чтобы оба объекта перемещались в унисон с поведением «резиновой ленты». Мне не удалось найти руководство или пример перетаскивания аннотации, к которой при перетаскивании были присоединены полилинии. Благодарим за любую помощь/руководство.
// LAMErtkAnnotationManager.swift
// LAME-RTK
// Created: 11/19/2024

import Foundation
import MapboxMaps
import CoreData
import Turf
import UIKit

// MARK: - Notification Extension

extension Notification.Name {
/// Notification posted when an annotation's coordinate is updated.
static let didUpdateAnnotation = Notification.Name("didUpdateAnnotation")
}

// MARK: - LAMErtkAnnotationManager

/// Manages annotations (drag handles) and polylines on the Mapbox map.
/// Ensures that dragging an annotation updates the corresponding polyline in real-time.
class LAMErtkAnnotationManager: NSObject, AnnotationInteractionDelegate {

// MARK: - Properties

/// The MapView instance where annotations and polylines are managed.
var mapView: MapView

/// The Core Data context for fetching and saving GPS data points.
var viewContext: NSManagedObjectContext

/// Manager for point annotations (drag handles).
var pointAnnotationManager: PointAnnotationManager

/// Manager for polyline annotations.
var polylineAnnotationManager: PolylineAnnotationManager

/// Dictionary mapping `zoneID` to its corresponding polyline annotation.
private var polylinesByZone: [String: PolylineAnnotation] = [:]

/// Dictionary to keep track of the last known coordinates of annotations.
/// Used to detect changes during dragging.
private var annotationCoordinates: [String: CLLocationCoordinate2D] = [:]

/// Timer to periodically check for annotation coordinate changes.
/// Since MapboxMaps SDK lacks direct drag event delegates, this timer facilitates synchronization.
private var annotationCheckTimer: Timer?

// MARK: - Initialization

/// Initializes the `LAMErtkAnnotationManager` with the provided `MapView` and `NSManagedObjectContext`.
///
/// - Parameters:
/// - mapView: The `MapView` instance where annotations and polylines are managed.
/// - viewContext: The Core Data context for fetching and saving GPS data points.
init(mapView: MapView, viewContext: NSManagedObjectContext) {
self.mapView = mapView
self.viewContext = viewContext
self.pointAnnotationManager = mapView.annotations.makePointAnnotationManager()
self.polylineAnnotationManager = mapView.annotations.makePolylineAnnotationManager()
super.init()

// Assign the delegate for interaction events.
self.pointAnnotationManager.delegate = self
self.polylineAnnotationManager.delegate = self

print("***** LAMErtkAnnotationManager initialized.")

// Initialize stored annotation coordinates.
initializeAnnotationCoordinates()

// Start the timer to check for annotation updates every second.
annotationCheckTimer = Timer.scheduledTimer(timeInterval: 1.0,
target: self,
selector: #selector(checkForAnnotationUpdates),
userInfo: nil,
repeats: true)

// Observe annotation update notifications.
NotificationCenter.default.addObserver(self,
selector: #selector(handleAnnotationUpdate(notification:)),
name: .didUpdateAnnotation,
object: nil)
}

deinit {
// Invalidate the timer and remove observers to prevent memory leaks.
annotationCheckTimer?.invalidate()
NotificationCenter.default.removeObserver(self)
}

// MARK: - Annotation Management

/// Clears all point annotations and polylines from the map.
func clearAllAnnotations() {
pointAnnotationManager.annotations.removeAll()
polylineAnnotationManager.annotations.removeAll()
polylinesByZone.removeAll()
annotationCoordinates.removeAll()
print("***** All annotations cleared.")
}

/// Loads all point annotations and corresponding polylines from Core Data.
func loadAllAnnotations() {
clearAllAnnotations()
loadPointAnnotations()
loadPolylines()
print("***** All annotations loaded.")
}

/// Loads point annotations (drag handles) from Core Data.
private func loadPointAnnotations() {
let fetchRequest: NSFetchRequest = GPSDataPoint.fetchRequest()
fetchRequest.sortDescriptors = [
NSSortDescriptor(key: "zoneID", ascending: true),
NSSortDescriptor(key: "order", ascending: true)
]

do {
let gpsDataPoints = try viewContext.fetch(fetchRequest)
pointAnnotationManager.annotations = gpsDataPoints.compactMap { point -> PointAnnotation? in
guard let zoneID = point.zoneID else { return nil }
let coordinate = CLLocationCoordinate2D(latitude: point.latitude, longitude: point.longitude)
var annotation = PointAnnotation(coordinate: coordinate)
annotation.isDraggable = true
annotation.customData = ["zoneID": .string(zoneID)]
annotation.iconImage = "diamondFill" // Set the icon image to "diamondFill"

// Store the initial coordinates.
annotationCoordinates[zoneID] = coordinate

return annotation
}
print("***** Loaded \(pointAnnotationManager.annotations.count) point annotations.")
} catch {
print("***** Error fetching point annotations: \(error.localizedDescription)")
}
}

/// Loads polylines from Core Data, grouping them by `zoneID`.
private func loadPolylines() {
let fetchRequest: NSFetchRequest = GPSDataPoint.fetchRequest()
fetchRequest.sortDescriptors = [
NSSortDescriptor(key: "zoneID", ascending: true),
NSSortDescriptor(key: "order", ascending: true)
]

do {
let gpsDataPoints = try viewContext.fetch(fetchRequest)
let groupedPoints = Dictionary(grouping: gpsDataPoints, by: { $0.zoneID ?? "default" })

for (zoneID, points) in groupedPoints {
let coordinates = points.map { CLLocationCoordinate2D(latitude: $0.latitude, longitude: $0.longitude) }
if let lineString = try? LineString(coordinates) {
var polyline = PolylineAnnotation(lineString: lineString)
polyline.lineColor = StyleColor(.systemBlue)
polyline.lineWidth = 3.0
polyline.customData = ["zoneID": .string(zoneID)]
polylineAnnotationManager.annotations.append(polyline)
polylinesByZone[zoneID] = polyline
print("***** Added polyline for zoneID: \(zoneID) with \(coordinates.count) points.")
}
}
} catch {
print("***** Error fetching polylines: \(error.localizedDescription)")
}
}

/// Initializes the stored coordinates from current annotations.
private func initializeAnnotationCoordinates() {
for annotation in pointAnnotationManager.annotations {
if let zoneIDValue = annotation.customData["zoneID"],
case let .string(zoneID) = zoneIDValue {
annotationCoordinates[zoneID] = annotation.coordinate
}
}
}

// MARK: - Dynamic Polyline Updates with Turf

/// Updates the polyline associated with a given `zoneID` using Turf.
///
/// - Parameter zoneID: The `zoneID` whose polyline should be updated.
func updatePolyline(for zoneID: String) {
guard var polyline = polylinesByZone[zoneID] else { return }
let updatedCoordinates = pointAnnotationManager.annotations.compactMap { annotation -> CLLocationCoordinate2D? in
guard let annotationZoneID = annotation.customData["zoneID"],
case let .string(annotationZone) = annotationZoneID,
annotationZone == zoneID else { return nil }
return annotation.coordinate
}
guard !updatedCoordinates.isEmpty else { return }

// Use Turf to validate and create a new LineString.
if let updatedLineString = try? LineString(updatedCoordinates) {
polyline.lineString = updatedLineString
polylineAnnotationManager.annotations = polylineAnnotationManager.annotations.map { $0.id == polyline.id ? polyline : $0 }
print("***** Updated polyline for zoneID: \(zoneID) with \(updatedCoordinates.count) points.")
} else {
print("***** Error creating LineString for zoneID: \(zoneID).")
}
}

// MARK: - AnnotationInteractionDelegate

/// Handles tap interactions on annotations.
///
/// - Parameters:
/// - manager: The `AnnotationManager` instance.
/// - annotations: The list of annotations that were tapped.
func annotationManager(_ manager: AnnotationManager, didDetectTappedAnnotations annotations: [Annotation]) {
for annotation in annotations {
if let point = annotation as? PointAnnotation,
let zoneIDValue = point.customData["zoneID"],
case let .string(zoneID) = zoneIDValue {
updatePolyline(for: zoneID)
}
}
}

// MARK: - Handling Annotation Drags

/// Handles updates to annotations when they are dragged.
/// Since MapboxMaps SDK doesn't provide direct drag event delegates, this method is triggered via notifications.
///
/// - Parameter notification: The notification containing the updated annotation.
@objc private func handleAnnotationUpdate(notification: Notification) {
guard let userInfo = notification.userInfo,
let updatedAnnotation = userInfo["annotation"] as? PointAnnotation,
let zoneIDValue = updatedAnnotation.customData["zoneID"] else { return }

// Use pattern matching to extract the string from JSONValue
guard case let .string(zoneID) = zoneIDValue else { return }

let newCoordinate = updatedAnnotation.coordinate
print("***** Annotation for zoneID: \(zoneID) moved to new coordinate: \(newCoordinate)")

// Update the corresponding GPSDataPoint in Core Data.
let fetchRequest: NSFetchRequest = GPSDataPoint.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "zoneID == %@", zoneID)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "order", ascending: true)]
fetchRequest.fetchLimit = 1

do {
if let gpsDataPoint = try viewContext.fetch(fetchRequest).first {
gpsDataPoint.latitude = newCoordinate.latitude
gpsDataPoint.longitude = newCoordinate.longitude
try viewContext.save()
print("***** Updated GPSDataPoint for zoneID: \(zoneID) with new coordinates.")
}
} catch {
print("***** Error updating GPSDataPoint for zoneID: \(zoneID): \(error.localizedDescription)")
}

// Update the corresponding polyline.
updatePolyline(for: zoneID)
}

// MARK: - Timer for Checking Annotation Updates

/// Periodically checks for changes in annotation coordinates to detect drags.
@objc private func checkForAnnotationUpdates() {
for annotation in pointAnnotationManager.annotations {
if let zoneIDValue = annotation.customData["zoneID"],
case let .string(zoneID) = zoneIDValue,
let oldCoordinate = annotationCoordinates[zoneID],
oldCoordinate.latitude != annotation.coordinate.latitude ||
oldCoordinate.longitude != annotation.coordinate.longitude {

// Update the stored coordinate.
annotationCoordinates[zoneID] = annotation.coordinate

// Post a notification with the updated annotation.
NotificationCenter.default.post(name: .didUpdateAnnotation,
object: nil,
userInfo: ["annotation": annotation])
}
}
}
}


Подробнее здесь: https://stackoverflow.com/questions/793 ... annotation
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»