I'm trying to create a path, so I add two type of anchors : poi, line between pois. When the anchors are created, although the coordinate data is consistent and coherent, some anchors are created at the right position, all the others at the top, all in the same point. Can you tell me what I'm doing wrong?
/*
See LICENSE folder for this sample’s licensing information.
Abstract:
Extensions and helpers.
*/
import MapKit
import ARKit
import RealityKit
import UIKit
import SceneKit
import SwiftUI
// A map overlay for geo anchors the user has added.
class AnchorIndicator: MKCircle {
convenience init(center: CLLocationCoordinate2D) {
self.init(center: center, radius: 3.0)
}
}
var previousColors: [UIColor] = []
// Funzione per generare un colore casuale diverso da quelli precedenti
func generateRandomColor(previousColors: [UIColor]) -> UIColor {
var randomColor: UIColor
repeat {
let red = CGFloat.random(in: 0...1)
let green = CGFloat.random(in: 0...1)
let blue = CGFloat.random(in: 0...1)
randomColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
} while previousColors.contains(randomColor)
return randomColor
}
extension simd_float4x4 {
var translation: SIMD3<Float> {
get {
return SIMD3<Float>(columns.3.x, columns.3.y, columns.3.z)
}
set (newValue) {
columns.3.x = newValue.x
columns.3.y = newValue.y
columns.3.z = newValue.z
}
}
}
extension Entity {
static func placemarkCat(for arAnchor: ARAnchor, distanceFromDevice: Double?) -> AnchorEntity? {
let placemarkAnchor = AnchorEntity(anchor: arAnchor)
let catIndicator = generateCatIndicator(model3D: arAnchor.name!)
print ("nome gatto", arAnchor.name ?? "nop")
let distanceFromGround: Float = 3
catIndicator.move(by: [0, distanceFromGround, 0], after: 0.0, duration: 0.0)
placemarkAnchor.addChild(catIndicator)
return placemarkAnchor
}
static func placemarkPin(for arAnchor: ARAnchor, distanceFromDevice: Double?) -> AnchorEntity? {
let placemarkAnchor = AnchorEntity(anchor: arAnchor)
let pinIndicator = generatePinIndicator(name: arAnchor.name!)
print ("pin gatto ", arAnchor.name ?? "nop")
let distanceFromGround: Float = 3
pinIndicator.move(by: [0, distanceFromGround, 0], after: 0.5, duration: 5.0)
placemarkAnchor.addChild(pinIndicator)
return placemarkAnchor
}
static func placemarkLine(for arAnchor: ARAnchor, distanceFromDevice: Double?) -> AnchorEntity? {
let placemarkAnchor = AnchorEntity(anchor: arAnchor)
var distancexyz: CLLocationDistance
var orientation: Double
var color1: UIColor
var color2: UIColor
var pendenza: Double
let modelarray: [String] = ["gatto", "basic", "gatto1","gatto2","gatto3","gatto4","gatto5","gatto6","gatto7","gatto8","gatto9","gatto10"]
// Recuperare dati
if let infoLine = AnchorDataStore.shared.getData(for: arAnchor.identifier) {
print("Distance: \(infoLine.distance), Orientation: \(infoLine.orientation)")
distancexyz = infoLine.distance
orientation = infoLine.orientation
color1 = infoLine.color1
color2 = infoLine.color2
pendenza = infoLine.pendenza
let lineIndicator = generateLineIndicator(distancexyz: distancexyz, orientation: orientation, color1: color1, color2: color2, pendenza: pendenza)
let distanceFromGround: Float = 3
lineIndicator.move(by: [0, distanceFromGround, 0], after: 0.5, duration: 5.0)
print("UTILITIES linea ", lineIndicator.position)
placemarkAnchor.addChild(lineIndicator)
} else {
print("Data non trovati per la chiave specificata.")
}
return placemarkAnchor
}
static func generateCatIndicator(model3D : String) -> Entity {
let anchor = AnchorEntity()
//let modelEntity = try! ModelEntity.load(named: model3D)
let modelEntity = ModelEntity(mesh: MeshResource.generateSphere(radius: 0.1), materials: [UnlitMaterial(color: #colorLiteral(red: 0, green: 0.3, blue: 1.4, alpha: 1))])
// Modifica la posizione, la scala o altre proprietà del modello se necessario
modelEntity.scale = [10.3, 10.3, 10.3] // Scala il modello
anchor.addChild(modelEntity)
return anchor
}
static func generatePinIndicator(name : String) -> Entity {
let anchor = AnchorEntity()
let modelEntity = try! ModelEntity.load(named: "pin")
// Modifica la posizione, la scala o altre proprietà del modello se necessario
modelEntity.scale = [15.3, 15.3, 15.3] // Scala il modello
anchor.addChild(modelEntity)
/*
// Creazione del testo come Entity
let textEntity = Entity()
let textModelComponent = ModelComponent(
mesh: .generateText(name),
materials: [SimpleMaterial(color: .darkGray, isMetallic: false)]
)
//textEntity.scale = [0.3, 0.3, 0.3]
textEntity.components.set(textModelComponent)
// textEntity.position.y = 2 // Regola l'altezza sopra l'entità 3D
anchor.addChild(textEntity)*/
return anchor
}
static func generateLineIndicator(distancexyz : CLLocationDistance, orientation: Double, color1: UIColor, color2: UIColor, pendenza: Double) -> Entity {
let anchor = AnchorEntity()
var modelEntity = ModelEntity()
modelEntity = ModelEntity(mesh: .generateBox(size: 0.6), materials: [SimpleMaterial(color: color2, isMetallic: false)])
// Modifica la posizione, la scala o altre proprietà del modello se necessario
modelEntity.scale = [1, 1, Float(distancexyz)] // Scala il modello *0.7
// Set the pitch angle (pendenza)
let pitchQuaternion = simd_quatf(angle: Float( pendenza ), axis: [1, 0, 0])
modelEntity.transform.rotation = pitchQuaternion * modelEntity.transform.rotation
//orientation
let quaternion = simd_quatf(angle: Float(orientation), axis: [0, 1, 0])// * 1.03
modelEntity.orientation = quaternion
anchor.addChild(modelEntity)
return anchor
}
func move(by translation: SIMD3<Float>, after delay: TimeInterval, duration: TimeInterval) {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
var transform: Transform = .identity
transform.translation = self.transform.translation + translation
//transform.scale = self.transform.scale * scale
self.move(to: transform, relativeTo: self.parent, duration: duration, timingFunction: .easeInOut)
}
}
}
extension UIView {
func fillParentView(withInset inset: CGFloat = 0) {
if let parentView = superview {
leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: inset).isActive = true
trailingAnchor.constraint(equalTo: parentView.trailingAnchor, constant: -inset).isActive = true
topAnchor.constraint(equalTo: parentView.topAnchor).isActive = true
bottomAnchor.constraint(equalTo: parentView.bottomAnchor).isActive = true
}
}
}
extension ARGeoTrackingStatus.StateReason {
var description: String {
switch self {
case .none: return "None"
case .notAvailableAtLocation: return "Geotracking is unavailable here. Please return to your previous location to continue"
case .needLocationPermissions: return "App needs location permissions"
case .worldTrackingUnstable: return "Limited tracking"
case .geoDataNotLoaded: return "Downloading localization imagery. Please wait"
case .devicePointedTooLow: return "Point the camera at a nearby building"
case .visualLocalizationFailed: return "Point the camera at a building unobstructed by trees or other objects"
case .waitingForLocation: return "ARKit is waiting for the system to provide a precise coordinate for the user"
case .waitingForAvailabilityCheck: return "ARKit is checking Location Anchor availability at your locaiton"
@unknown default: return "Unknown reason"
}
}
}
extension ARGeoTrackingStatus.Accuracy {
var description: String {
switch self {
case .undetermined: return "Undetermined"
case .low: return "Low"
case .medium: return "Medium"
case .high: return "High"
@unknown default: return "Unknown"
}
}
}
extension ARCamera.TrackingState.Reason {
var description: String {
switch self {
case .initializing: return "Initializing"
case .excessiveMotion: return "Too much motion"
case .insufficientFeatures: return "Insufficient features"
case .relocalizing: return "Relocalizing"
@unknown default: return "Unknown"
}
}
}
//
// ARSessionDelegateWrapper.swift
// trackCATS
//
// Created by diletta on 18/10/23.
//
import SwiftUI
import RealityKit
import ARKit
import CoreLocation
import MapKit
typealias AlertCallback = (String, String, Bool) -> Void
typealias CoachingFinishCallback = (Bool) -> Void
class ARSessionDelegateWrapper: NSObject, ARSessionDelegate, ObservableObject {
@Published var coordinate: CLLocationCoordinate2D?
@Published var arView: ARView?
@Published var cats: [Cat]? {
didSet {
if let updatedCats = cats {
handleCatUpdate(updatedCats)
}
}
}
// Funzione chiamata quando 'cats' viene aggiornato
// Inside the ARSessionDelegateWrapper class
func handleCatUpdate(_ updatedCats: [Cat]) {
self.arView?.addDynamicAnchors(at: updatedCats)
}
@Published var alertTitle: String
@Published var alertMessage: String
@Published var isAlertPresented: Bool
var alertCallback: AlertCallback?
var isCoachingFinish :CoachingFinishCallback?
// var catAnchors: [String: ARAnchor] = [:]
init(arView: ARView? = nil, coordinate: CLLocationCoordinate2D? = nil, alertTitle: String = "", alertMessage: String = "", isAlertPresented: Bool = false) {
self.coordinate = coordinate
self.arView = arView
self.alertMessage = alertMessage
self.alertTitle = alertTitle
self.isAlertPresented = isAlertPresented
}
func setAlertProperties(title: String, message: String, isPresented: Bool) {
alertTitle = title
alertMessage = message
isAlertPresented = isPresented
}
func distanceFromDevice(_ coordinate: CLLocationCoordinate2D?) -> Double {
if let devicePosition = coordinate {
return MKMapPoint(coordinate!).distance(to: MKMapPoint(devicePosition))
} else {
return 0
}
}
let catModelArray: [String] = ["gatto", "gatto1", "gatto2", "gatto3", "gatto4", "gatto5", "gatto6", "gatto7", "gatto8", "gatto9", "gatto10"]
let lineModelArray: [String] = ["Line"]
// MARK: - ARSessionDelegate
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
for geoAnchor in anchors.compactMap({ $0 as? ARGeoAnchor }) {
print("IN SESSIONE ")
// self.arView!.addStaticLineAnchors(at: self.cats ?? [])
// self.arView!.addStaticCatAnchors(at: self.cats ?? [])
//if self.lineModelArray.contains(geoAnchor.name ?? "") {
DispatchQueue.main.asyncAfter(deadline: .now() + (distanceFromDevice(geoAnchor.coordinate) / 10)) {
print("IN SESSIONE ", geoAnchor.name)
let distance = self.distanceFromDevice(geoAnchor.coordinate)
self.arView!.scene.addAnchor(Entity.placemarkCat(for: geoAnchor, distanceFromDevice: distance)!)
//self.arView!.scene.addAnchor(Entity.placemarkLine(for: geoAnchor, distanceFromDevice: distance)!)
// else if self.catModelArray.contains(geoAnchor.name ?? "") {
/*
self.arView!.scene.addAnchor(Entity.placemarkCat(for: geoAnchor, distanceFromDevice: (self.distanceFromDevice(geoAnchor.coordinate) / 10))!)
// else {
self.arView!.scene.addAnchor(Entity.placemarkPin(for: geoAnchor, distanceFromDevice: (self.distanceFromDevice(geoAnchor.coordinate) / 10))!)
*/
}}
// self.arView!.scene.addAnchor(Entity.placemarkPin(for: geoAnchor, distanceFromDevice: (distanceFromDevice(geoAnchor.coordinate) / 10))!)
/*
DispatchQueue.main.asyncAfter(deadline: .now() + (distanceFromDevice(geoAnchor.coordinate) / 10)) {
let modelName = geoAnchor.name
let distance = self.distanceFromDevice(geoAnchor.coordinate)
if self.lineModelArray.contains(modelName ?? "") {
self.arView!.scene.addAnchor(Entity.placemarkLine(for: geoAnchor, distanceFromDevice: distance)!)
print("IN SESSIONE ", geoAnchor.name)
}
if self.catModelArray.contains(modelName ?? "") {
self.arView!.scene.addAnchor(Entity.placemarkCat(for: geoAnchor, distanceFromDevice: distance)!)
print("IN SESSIONE ", geoAnchor.name)
}
else {
self.arView!.scene.addAnchor(Entity.placemarkPin(for: geoAnchor, distanceFromDevice: distance)!)
print("IN SESSIONE ", geoAnchor.name)
}
}*/
}
func session(_ session: ARSession, didFailWithError error: Error) {
guard error is ARError else { return }
let errorWithInfo = error as NSError
let messages = [
errorWithInfo.localizedDescription,
errorWithInfo.localizedFailureReason,
errorWithInfo.localizedRecoverySuggestion
]
let errorMessage = messages.compactMap({ $0 }).joined(separator: "\n")
setAlertProperties(title: "The AR session failed.", message: errorMessage, isPresented: true)
alertCallback?(alertTitle, alertMessage, isAlertPresented)
}
/// - Tag: GeoTrackingStatus
func session(_ session: ARSession, didChange geoTrackingStatus: ARGeoTrackingStatus) {
if (geoTrackingStatus.state != .localized){
// self.arView!.addStaticLineAnchors(at: self.cats ?? [])
return
}
var text = ""
// In localized state, show geotracking accuracy
if geoTrackingStatus.state == .localized {
text += "Accuracy: \(geoTrackingStatus.accuracy.description)"
isCoachingFinish?(true)
} else {
// Otherwise show details why geotracking couldn't localize (yet)
switch geoTrackingStatus.stateReason {
case .none:
isCoachingFinish?(false)
break
case .worldTrackingUnstable:
isCoachingFinish?(false)
let arTrackingState = session.currentFrame?.camera.trackingState
if case let .limited(arTrackingStateReason) = arTrackingState {
text += "\n\(geoTrackingStatus.stateReason.description): \(arTrackingStateReason.description)."
} else {
fallthrough
}
default: text += "\n\(geoTrackingStatus.stateReason.description)."
}
}
print(text)
}
}
//
// ARLocalUtilities.swift
// trackCATS
//
// Created by diletta on 18/10/23.
//
import RealityKit
import ARKit
import FirebaseFirestore
let currentTimeStamp = Timestamp(date: Date())
// Calcoliamo il timestamp di 24 ore fa
let twentyFourHoursAgo = Calendar.current.date(byAdding: .hour, value: -24, to: currentTimeStamp.dateValue())!
let twentyFourHoursAgoTimeStamp = Timestamp(date: twentyFourHoursAgo)
var addedAnchors: [String: ARGeoAnchor] = [:]
// TODO: create custom ARView subClass and add this extenion for the local feature
extension ARView {
// Dichiarazione del semaforo
/*
func enabletapGesture(){
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapOnARView(_:)))
self.addGestureRecognizer(tapGestureRecognizer)
}
// Responds to a user tap on the AR view.
@objc
func handleTapOnARView(_ sender: UITapGestureRecognizer) {
let tapLocation = sender.location(in: self)
// Perform ARKit raycast on tap location
if let result = self.raycast(from: tapLocation, allowing: .estimatedPlane, alignment: .any).first {
self.addGeoAnchor(at: result.worldTransform.translation)
} else {
print("No raycast result.\nTry pointing at a different area\nor move closer to a surface.")
}
}
func addGeoAnchor(at worldPosition: SIMD3<Float>) {
self.session.getGeoLocation(forPoint: worldPosition) { (location, altitude, error) in
if let error = error {
print("Cannot add geo anchor" + " An error occurred while translating ARKit coordinates to geo coordinates: \(error.localizedDescription)")
return
}
self.addGeoAnchor(at: location, altitude: altitude)
}
}
func addGeoAnchor(at location: CLLocationCoordinate2D, altitude: CLLocationDistance? = nil) {
var geoAnchor: ARGeoAnchor!
if let altitude = altitude {
geoAnchor = ARGeoAnchor(coordinate: location, altitude: altitude)
} else {
geoAnchor = ARGeoAnchor(coordinate: location)
}
addGeoAnchor(geoAnchor)
}*/
/* func addLineAnchor(at coordinate: CLLocationCoordinate2D) -> ARGeoAnchor {
let lineCoordinate = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
let lineAnchor = ARGeoAnchor(name: "Line", coordinate: lineCoordinate )
addGeoAnchor(lineAnchor)
return lineAnchor
}
*/
/*
func addGeoAnchor(at name: String, location: CLLocationCoordinate2D, altitude: CLLocationDistance? = nil) {
var geoAnchor: ARGeoAnchor!
if let altitude = altitude {
geoAnchor = ARGeoAnchor(name: name, coordinate: location, altitude: altitude)
} else {
geoAnchor = ARGeoAnchor(name: name, coordinate: location)
}
addGeoAnchor(geoAnchor)
}*/
func addPinGeoAnchor(at location: CLLocationCoordinate2D, name: String, altitude: CLLocationDistance? = nil, id: String) {
var geoAnchor: ARGeoAnchor!
if let altitude = altitude {
geoAnchor = ARGeoAnchor(name: name, coordinate: location, altitude: altitude + 10)
} else {
geoAnchor = ARGeoAnchor(name: name, coordinate: location)
}
AnchorManager.shared.addAnchor(geoAnchor, id: id)
addGeoAnchor(geoAnchor)
}
func addCatAnchor(at location: CLLocationCoordinate2D, model3D: String, altitude: CLLocationDistance? = nil) {
var geoAnchor: ARGeoAnchor!
if let altitude = altitude {
geoAnchor = ARGeoAnchor(name: model3D, coordinate: location, altitude: altitude)
} else {
geoAnchor = ARGeoAnchor(name: model3D, coordinate: location)
}
addGeoAnchor(geoAnchor)
}
func addLineAnchor(at coordinate: CLLocationCoordinate2D, altitude: CLLocationDistance) -> ARGeoAnchor {
let lineCoordinate = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
let lineAnchor = ARGeoAnchor(name: "Line", coordinate: lineCoordinate, altitude: altitude )
addGeoAnchor(lineAnchor)
return lineAnchor
}
func addGeoAnchor(_ geoAnchor: ARGeoAnchor) {
// Don't add a geo anchor if Core Location isn't sure yet where the user is.
guard isGeoTrackingLocalized else {
print("Cannot add geo anchor" + "Unable to add geo anchor because geotracking has not yet localized.")
return
}
print("Adding geo Anchor: \(geoAnchor.name ?? "Unnamed") at \(geoAnchor.coordinate) with altitude \(geoAnchor.altitude ?? 0.0)")
self.session.add(anchor: geoAnchor)
}
// ... (All'interno della funzione `addStaticAnchors(at cats: [Cat])`)
func addDynamicAnchors(at cats: [Cat]) {
AnchorManager.shared.printAllAnchors()
// Itera attraverso i gatti e crea pin dinamici per le loro posizioni attuali
for cat in cats {
if let geoAnchor = AnchorManager.shared.anchor(forID: cat.id) {
self.session.remove(anchor: geoAnchor)
}
AnchorManager.shared.removeAnchor(withID: cat.id )
guard let lastCoordinate = cat.coordinates.last,
let lastAltitude = cat.altitudes.last else { continue } // Ottieni l'ultima coordinata del gatto
let location = CLLocationCoordinate2D(latitude: lastCoordinate.latitude, longitude: lastCoordinate.longitude)
let altitude = lastAltitude + 5 // Puoi modificare l'altitudine come necessario
addPinGeoAnchor(at: location, name: cat.name, altitude: altitude, id : cat.id
)
}
}
func removeAllAnchors() {
// Rimuovi tutte le ancoraggi dalla sessione AR
for anchor in self.session.currentFrame?.anchors ?? [] {
self.session.remove(anchor: anchor)
} // Rimuovi tutte le ancoraggi dalla scena
self.scene.anchors.removeAll()
// Rimuovi tutte le ancoraggi gestite dal manager
AnchorManager.shared.removeAllAnchors()
}
func addStaticLineAnchors(at cats: [Cat]) {
guard isGeoTrackingLocalized else {
print("Cannot add geo anchor" + "Unable to add geo anchor because geotracking has not yet localized.")
return // Il geotracking non è ancora localizzato, quindi non aggiungere le ancore
}
for cat in cats {
// Filtriamo gli elementi nell'array datetime per ottenere solo quelli dopo 24 ore fa
// let coordinatesLastTwentyFourHours = zip(cat.datetime, cat.coordinates)
// .filter { $0.0.compare(twentyFourHoursAgoTimeStamp) == .orderedDescending }
// .map { $0.1 }
var prevLocation: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
var prevAltitude: CLLocationDistance = 0.0
var prevDate = Timestamp(seconds: 0, nanoseconds: 0) // Valore predefinito
var prevColor: UIColor = UIColor.blue
for index in cat.coordinates.indices {
let geopoint = cat.coordinates[index]
let location = CLLocationCoordinate2D(latitude: geopoint.latitude, longitude: geopoint.longitude)
let altitude = cat.altitudes[index]
let currentDate = cat.datetime[index]
let distance = distanceBetweenCoordinatesXY(prevLocation, location)
if distance > 10 {
if prevLocation.latitude == 0 && prevLocation.longitude == 0 {}
else{
let distancexyz = distanceBetweenCoordinatesXYZ(coord1: prevLocation, coord2: location, alt1: prevAltitude, alt2: altitude)
let distancexy = distanceBetweenCoordinatesXY(prevLocation, location)
let orientation = getOrientationBetweenTwoPoints(point1: prevLocation, point2: location)
let midCoordinate = midpointBetweenCoordinate(prevLocation, andCoordinate: location)
let midAltitude = midAltitudeBetweenTwoPoints(point1: prevAltitude, point2: altitude)
let lineAnchor = addLineAnchor(at: midCoordinate, altitude: midAltitude)
let pendenza = pendenza(alt1: prevAltitude, alt2: altitude, distance: distancexy)
let currentColor = current_Color(currentDate: currentDate, prevDate: prevDate, distancexy: distancexy)
let diffAlt = abs(prevAltitude - altitude)
if diffAlt == 0.0 {
AnchorDataStore.shared.addData(key: lineAnchor.identifier, distance: distancexy, orientation: orientation, color1: prevColor, color2: currentColor, pendenza: pendenza)
} else {
AnchorDataStore.shared.addData(key: lineAnchor.identifier, distance: distancexyz + diffAlt, orientation: orientation, color1: prevColor, color2: currentColor, pendenza: pendenza)
}
prevDate = currentDate
prevColor = currentColor
}
prevLocation = location
prevAltitude = altitude
}
}
}
}
func addStaticCatAnchors(at cats: [Cat]) {
guard isGeoTrackingLocalized else {
print("Cannot add geo anchor" + "Unable to add geo anchor because geotracking has not yet localized.")
return // Il geotracking non è ancora localizzato, quindi non aggiungere le ancore
}
for cat in cats {
var prevLocation: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
var prevAltitude: CLLocationDistance = 0.0
var prevDate = Timestamp(seconds: 0, nanoseconds: 0) // Valore predefinito
var prevColor: UIColor = UIColor.blue
for index in cat.coordinates.indices {
let geopoint = cat.coordinates[index]
let location = CLLocationCoordinate2D(latitude: geopoint.latitude, longitude: geopoint.longitude)
let altitude = cat.altitudes[index]
let currentDate = cat.datetime[index]
let distance = distanceBetweenCoordinatesXY(prevLocation, location)
if distance > 10 {
addCatAnchor(at: location, model3D: cat.model3D, altitude: altitude )
prevLocation = location
prevAltitude = altitude
}
}
}
}
extension Double {
var degreesToRadians: Double { return self * .pi / 180 }
var radiansToDegrees: Double { return self * 180 / .pi }
}
// Create a separate manager to handle added anchors
class AnchorManager {
static let shared = AnchorManager()
private init() {}
private var addedAnchors: [String: ARGeoAnchor] = [:]
func addAnchor(_ anchor: ARGeoAnchor, id: String) {
addedAnchors[id] = anchor
}
func removeAnchor(withID identifier: String) {
if addedAnchors[identifier] != nil {
addedAnchors.removeValue(forKey: identifier)
}
}
func anchor(forID id: String) -> ARGeoAnchor? {
return addedAnchors[id]
}
func removeAllAnchors() {
addedAnchors.removeAll()
}
func printAllAnchors() {
for (identifier, anchor) in addedAnchors {
print("GEOAnchor ID: \(identifier)")
if let name = anchor.name {
print("GEOName: \(name)")
}
// Check if the coordinates are within valid range
if anchor.coordinate.latitude >= -90 && anchor.coordinate.latitude <= 90 &&
anchor.coordinate.longitude >= -180 && anchor.coordinate.longitude <= 180 {
print("GEOCoordinate: \(anchor.coordinate)")
} else {
print("GEOInvalid coordinates")
}
if let altitude = anchor.altitude {
print("GEOAltitude: \(altitude)")
}
// Add more properties if needed
print("-----------")
}
}
}
I checked the consistent of the data and they are right