// // Models.swift // Runner // // Created by Groot on 2025/5/9. // import Foundation import MapKit extension Decodable { static func fromJson(json: Any) -> Self? { do { let data = try JSONSerialization.data(withJSONObject: json, options: []) return try JSONDecoder().decode(Self.self, from: data) } catch { print(error.localizedDescription) return nil } } } extension Encodable { func toJson() -> [String: Any] { guard let data = try? JSONEncoder().encode(self) else { return [:] } return try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] } } struct ATMapCameraPosition: Codable { var latitude: CGFloat var longitude: CGFloat // 地图缩放比例,对应span var zoom: CGFloat } struct ATMapPadding: Codable { var top: CGFloat var left: CGFloat var bottom: CGFloat var right: CGFloat var padding: UIEdgeInsets { return UIEdgeInsets(top: top, left: left, bottom: bottom, right: right) } } class ATMapPolyline: NSObject, Codable { var id: String = UUID().uuidString var points: [CLLocationCoordinate2D] var mapPadding: ATMapPadding? var polyline: MKPolyline { return MKPolyline(coordinates: points, count: points.count) } enum CodingKeys: String, CodingKey { case points, mapPadding } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) points = try container.decode([CLLocationCoordinate2D].self, forKey: .points) if container.contains(.mapPadding) { mapPadding = try container.decodeIfPresent(ATMapPadding.self, forKey: .mapPadding) } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(points, forKey: .points) try container.encodeIfPresent(mapPadding, forKey: .mapPadding) } } class ATMapMarker: NSObject, Codable { var id: String var markerName: String? var customAvatarUrl : String? var latitude: CGFloat var longitude: CGFloat var isSelected: Bool var markerType: any MapMarkerSupportType init(id: String, markerName: String?, customAvatarUrl: String?,location: CLLocationCoordinate2D, markerType: any MapMarkerSupportType, isSelected: Bool = false) { self.id = id self.markerName = markerName self.customAvatarUrl = customAvatarUrl self.latitude = location.latitude self.longitude = location.longitude self.markerType = markerType self.isSelected = isSelected } func update(coordinate: CLLocationCoordinate2D, name: String? = nil, isSelect: Bool) { self.latitude = coordinate.latitude self.longitude = coordinate.longitude self.markerName = name self.isSelected = isSelect } enum CodingKeys: String, CodingKey { case id, markerName,customAvatarUrl, latitude, longitude, isSelected, markerType } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(String.self, forKey: .id) if id.isEmpty { id = "user_location" } if container.contains(.markerName) { markerName = try container.decodeIfPresent(String.self, forKey: .markerName) } if container.contains(.customAvatarUrl) { customAvatarUrl = try container.decodeIfPresent(String.self, forKey: .customAvatarUrl) } latitude = try container.decode(CGFloat.self, forKey: .latitude) longitude = try container.decode(CGFloat.self, forKey: .longitude) isSelected = try container.decode(Bool.self, forKey: .isSelected) // 使用工厂方法创建正确类型的markerType let rawValue = try container.decode(Int.self, forKey: .markerType) markerType = MarkerTypeFactory.markerType(from: rawValue) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(markerName, forKey: .markerName) try container.encode(customAvatarUrl, forKey: .customAvatarUrl) try container.encode(latitude, forKey: .latitude) try container.encode(longitude, forKey: .longitude) try container.encode(isSelected, forKey: .isSelected) try container.encode(markerType.rawValue, forKey: .markerType) } } extension ATMapMarker: MKAnnotation { var coordinate: CLLocationCoordinate2D { return CLLocationCoordinate2D(latitude: latitude, longitude: longitude) } } extension CLLocationCoordinate2D: Codable { enum CodingKeys: String, CodingKey { case latitude case longitude } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let latitude = try container.decode(Double.self, forKey: .latitude) let longitude = try container.decode(Double.self, forKey: .longitude) self.init(latitude: latitude, longitude: longitude) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(latitude, forKey: .latitude) try container.encode(longitude, forKey: .longitude) } } class ATMapLocation: NSObject, Codable { var latitude: CGFloat? var longitude: CGFloat? var address: String? var errorCode: Int = 0 var time: Int? var bearing: CGFloat? var speed: CGFloat? init(latitude: CGFloat? = nil, longitude: CGFloat? = nil, address: String? = nil, time: Int? = nil, bearing: CGFloat? = nil, speed: CGFloat? = nil) { self.latitude = latitude self.longitude = longitude self.address = address self.time = time self.bearing = bearing self.speed = speed super.init() } static func fromLocation(location: CLLocation) -> ATMapLocation { return ATMapLocation( latitude: CGFloat(location.coordinate.latitude), longitude: CGFloat(location.coordinate.longitude), address: nil, time: Int(location.timestamp.timeIntervalSince1970 * 1000), bearing: CGFloat(location.course), speed: CGFloat(location.speed) ) } }