可编码 MKPolygon?

Codable MKPolygon?

好的,请耐心等待。 我有一个坐标数据库,我正在尝试提取该数据,将其解码为也符合 MKPolygon 的可编码 class,然后才能将该多边形添加到地图视图中。

class CustomPolygon : MKPolygon, Codable {

    var perimeter : [Coordinate]!

}
struct Coordinate : Codable {
    let Latitude : CLLocationDegrees
    let Longitude : CLLocationDegrees
    
    func getCoord() -> CLLocationCoordinate2D {
        return CLLocationCoordinate2D(latitude: self.Latitude, longitude: self.Longitude)
    }
}

我从数据库中解码的重要代码是这样的:

let polygon = try! FirestoreDecoder().decode(CustomPolygon.self, from: data)

encoding/decoding 是在 CodableFirebase CocoaPod 的帮助下完成的,它成功地对其进行了编码,但现在我该如何添加实际坐标来创建多边形?

我不建议继承 MKPolygon。坦率地说,我根本不建议创建 CustomPolygonCoordinate。无需引入反映原生 MKPolygonCLLocationCoordinate2D 类型的类型。

与其尝试创建一个可编码的 MKPolygon,我可能会建议创建一个仅表示坐标集合的 Codable 类型,它对纬度和经度数组进行编码。

但首先,让我们考虑一个 JSON 结构,一个坐标数组:

[
    {"longitude":37.785834, "latitude":-122.406417},
    {"longitude":37.246878, "latitude":-122.245676},
    ...
]

因此,我将创建一个 encodes/decodes 类型,如 Encoding and Decoding Custom Types 的“手动编码和解码”部分所述:

struct Coordinates: Codable {
    var coordinates: [CLLocationCoordinate2D] = []

    // MARK: Codable

    enum CodingKeys: String, CodingKey {
        case latitude, longitude
    }

    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()

        while !container.isAtEnd {
            let subcontainer = try container.nestedContainer(keyedBy: CodingKeys.self)
            let latitude = try subcontainer.decode(CLLocationDegrees.self, forKey: .latitude)
            let longitude = try subcontainer.decode(CLLocationDegrees.self, forKey: .longitude)
            coordinates.append(CLLocationCoordinate2D(latitude: latitude, longitude: longitude))
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.unkeyedContainer()

        for value in coordinates {
            var subcontainer = container.nestedContainer(keyedBy: CodingKeys.self)
            try subcontainer.encode(value.latitude, forKey: .latitude)
            try subcontainer.encode(value.longitude, forKey: .longitude)
        }
    }
}

我可能还会创建一些方便的方法来从 Coordinates 轻松创建 MKPolygonMKPolyline,反之亦然:

extension Coordinates {
    init(from polygon: MKPolygon) {
        self.polygon = polygon
    }

    init(from polyline: MKPolyline) {
        self.polyline = polyline
    }

    var polygon: MKPolygon {
        get { MKPolygon(coordinates: coordinates, count: coordinates.count) }
        set { updateCoordinates(from: newValue) }
    }

    var polyline: MKPolyline {
        get { MKPolyline(coordinates: coordinates, count: coordinates.count) }
        set { updateCoordinates(from: newValue) }
    }

    private mutating func updateCoordinates(from shape: MKMultiPoint) {
        let pointCount = shape.pointCount
        coordinates = .init(repeating: kCLLocationCoordinate2DInvalid, count: pointCount)
        shape.getCoordinates(&coordinates, range: NSRange(location: 0, length: pointCount))
    }
}

extension MKPolyline {
    var coordinates: Coordinates { Coordinates(from: self) }
}

extension MKPolygon {
    var coordinates: Coordinates { Coordinates(from: self) }
}

那么,如果要对多边形的坐标进行编码:

let coordinates = polygon.coordinates            // if you need to extract the `Coordinates` collection from the `MKPolygon`
let data = try JSONEncoder().encode(coordinates)

或者,如果您想解码坐标并从中创建 MKPolygon

let coordinates = try JSONDecoder().decode(Coordinates.self, from: data)
let polygon = coordinates.polygon                // if you want to add a `MKPolygon` represented by this collection of `Coordinates`

但想法是您的模型对象将是 Coordinates 类型(即 CLLocationCoordinate2d 的集合)即 Codable。然后您可以使用 polygonpolyline 计算属性来构建适当的 MKOverlay 类型,然后您可以将其添加到地图中。

并且由于 CoordinatesCodable,您现在可以在自己的 Codable 类型中使用此类型,例如:

struct RegionOfInterest: Codable {
    let name: String
    let coordinates: Coordinates
}