Хорошо, терпи меня. У меня есть база данных координат, я пытаюсь извлечь эти данные, декодировать их в кодируемый класс, который также соответствует MKPolygon, а затем просто добавить этот многоугольник в представление карты.

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)

Кодирование / декодирование осуществляется с помощью CodableFirebase CocoaPod, и он успешно кодирует его, но как теперь добавить фактические координаты для создания многоугольника?

1
EricE 13 Фев 2021 в 12:08

1 ответ

Лучший ответ

Я бы не советовал создавать подклассы MKPolygon. Честно говоря, я бы вообще не советовал создавать CustomPolygon и Coordinate. Нет необходимости вводить типы, которые отражают собственные типы MKPolygon и CLLocationCoordinate2D.

Вместо того, чтобы пытаться создать кодируемый MKPolygon, я мог бы вместо этого предложить создать тип Codable, который просто представляет набор координат, который кодирует массив широты и долготы.

Но сначала давайте рассмотрим структуру JSON, массив координат:

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

Итак, я бы создал тип, который кодирует / декодирует, как описано в разделе «Кодирование и декодирование вручную» в Пользовательские типы кодирования и декодирования:

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)
        }
    }
}

Я мог бы также создать несколько удобных методов для простого создания MKPolygon и MKPolyline из Coordinates и наоборот:

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. Затем вы можете использовать вычисленные свойства polygon или polyline для создания соответствующего типа MKOverlay, который затем можно будет добавить на свою карту.

А поскольку Coordinates равно Codable, теперь вы можете использовать этот тип в своих собственных типах Codable, например:

struct RegionOfInterest: Codable {
    let name: String
    let coordinates: Coordinates
}
1
Rob 13 Фев 2021 в 18:31