首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >VKDefault -Triangulator无法完全三角化面MapKit

VKDefault -Triangulator无法完全三角化面MapKit
EN

Stack Overflow用户
提问于 2021-09-20 02:28:03
回答 1查看 159关注 0票数 4

我有一个问题,当我放大和缩小我的地图上的特定区域时,我得到警告[VKDefault] Triangulator failed to fully triangulate polygon MapKit

我从可以包含MKPolygon或MKMultiPolygon的json数据创建多边形。200多个多边形中只有几个有这个问题。

对我来说,数据看起来很好。我尝试删除坐标数组中的重复项(如果它们包含任何重复项),但没有成功。也许我做得不对,因为MKMultiPolygon是从[Double]创建的

以下是该项目的链接- https://www.dropbox.com/s/zwy08upcne11dyy/VectorMaps%203.zip?dl=0

这只是一个原型,所以请不要在强行展开时敲打我。

这是带有警告的区域的样子。

我添加了一个列表,列出了我在ViewController中遇到的问题。

解决警告VectorMaps[1611:116104] [VKDefault] Style Z is requested for an invisible rect的额外积分

代码语言:javascript
复制
struct SubRegion: Decodable {
    var id: Int
    var name: String
    var geometry: Geometry

    enum CodingKeys: String, CodingKey {
        case id, name, geometry = "geojson_geometry"
    }
}

enum PolygonType: String {
    case multiPolygon = "MultiPolygon"
    case polygon = "Polygon"
}

struct Geometry: Decodable {
    var type: String
    var coordinates: Array<Any>
    var convexHullGeometry: ConvexGeometry?

    enum CodingKeys: String, CodingKey {
        case type, coordinates, convexHullGeometry = "convex_hull_geojson_geometry"
    }

    enum ParseError: Error {
        case notRecognizedType(Any)
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        type = try container.decode(String.self, forKey: .type)

        switch type {
        case "Polygon":
            coordinates = try container.decode([[[Double]]].self, forKey: .coordinates)
        case "MultiPolygon":
            coordinates = try container.decode([[[[Double]]]].self, forKey: .coordinates)
        default:
            throw ParseError.notRecognizedType(type)
        }
    }
}

struct ConvexGeometry: Codable {
    var coordinates: [[[Double]]]
}

class ViewController: UIViewController {
    var activeMultiRenders = [MKMultiPolygonRenderer]()
    var activeRenders = [MKPolygonRenderer]()
    var mapView: MKMapView!

    let pinView = LocationPinView()
    var showMarker = false
    var showAnnotation = false

    var this = MKPointAnnotation()

    typealias Polygon = [[[Double]]]
    typealias MultiPolygon = [[[[Double]]]]

    let staticMarkerButton: UIButton = {
        let button = UIButton()
        button.setTitle("Show static marker", for: .normal)
        button.addTarget(self, action: #selector(addMarker), for: .touchUpInside)
        button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15)
        button.setTitleColor(.blue, for: .normal)
        button.backgroundColor = .white
        button.layer.cornerRadius = 5
        return button
    }()

    let mapButton: UIButton = {
        let button = UIButton()
        button.setTitle("Radius Map", for: .normal)
        button.addTarget(self, action: #selector(visitRadius), for: .touchUpInside)
        button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15)
        button.setTitleColor(.blue, for: .normal)
        button.backgroundColor = .white
        button.layer.cornerRadius = 5
        return button
    }()

    let showLabelButton: UIButton = {
        let button = UIButton()
        button.setTitle("Show labels", for: .normal)
        button.addTarget(self, action: #selector(addAnnotations), for: .touchUpInside)
        button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15)
        button.setTitleColor(.blue, for: .normal)
        button.backgroundColor = .white
        button.layer.cornerRadius = 5
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        mapView = MKMapView(frame: self.view.frame)
        mapView.isRotateEnabled = false
        mapView.mapType = .standard

        addPolygonsToMap()

        mapView.delegate = self
        self.view.addSubview(mapView)
        
        let tap = UITapGestureRecognizer(target: self, action: #selector(mapTapped(_:)))
        self.mapView.addGestureRecognizer(tap)

        self.view.addSubview(pinView)
        pinView.frame = CGRect(x: mapView.center.x, y: mapView.center.y - pinView.frame.height,
                               width: pinView.frame.width, height: pinView.frame.height)
        pinView.isHidden = true

        view.addSubview(staticMarkerButton)
        staticMarkerButton.translatesAutoresizingMaskIntoConstraints = false
        staticMarkerButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
        staticMarkerButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

        view.addSubview(mapButton)
        mapButton.translatesAutoresizingMaskIntoConstraints = false
        mapButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -100).isActive = true
        mapButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

        view.addSubview(showLabelButton)
        showLabelButton.translatesAutoresizingMaskIntoConstraints = false
        showLabelButton.leadingAnchor.constraint(equalTo: mapButton.trailingAnchor, constant: 10).isActive = true
        showLabelButton.topAnchor.constraint(equalTo: mapButton.topAnchor).isActive = true
    }

    func addPolygonsToMap() {
        let fileURL = Bundle.main.url(forResource: "subregion", withExtension: "json")!
        let decoder = JSONDecoder()

        do {
            let data = try Data(contentsOf: fileURL)
            let subregions = try decoder.decode([SubRegion].self, from: data)

            for each in subregions {
                let geometry = each.geometry

//                if !badData.contains(each.name) {
//                    continue
//                }

                // Type is either a Polygon or MultiPolygon
                let geometryType = each.geometry.type

                if geometryType == PolygonType.polygon.rawValue {
                    // Cast the coordinates to a Polygon type-alias.
                    let coords = geometry.coordinates as! Polygon
                    let lCoords = getCoordinatesFromPolygonArray(coords)
                                        
                    // Create a MKPolygon with locationCoordinates
                    let polygon = MKPolygon(coordinates: lCoords, count: lCoords.count)

                    // Add the MKPolygon to the map
                    mapView.addOverlay(polygon)
                } else if geometryType == PolygonType.multiPolygon.rawValue {
                    // Cast the coordinates to a MultiPolygon type-alias.
                    let coords = geometry.coordinates as! MultiPolygon
                    
                    var polygons:[MKPolygon] = []
                    coords.forEach { polygon in

                        let lCoords = getCoordinatesFromPolygonArray(polygon)

                        // Create a MKPolygon with locationCoordinates
                        let polygon = MKPolygon(coordinates: lCoords, count: lCoords.count)

                        polygons.append(polygon)
                    }
                    
                    let multiPolygon = MKMultiPolygon(polygons)
                    mapView.addOverlay(multiPolygon)
                }
            }
        } catch let error {
            print("Error", error)
        }
    }

    let badData = ["Central Coast", "Dapto and Port Kembla", "Wellington", "South West Perth", "East Arnhem", "Charters Towers", "Ormeau", "Murray River", "Wodonga", "Bruny Island", "Katherine", "Far North", "Tweed Valley", "Richmond Valley", "South East Coast", "Gympie", "Meander Valley"]
    
    private func getCoordinatesFromPolygonArray(_ polygon: Polygon) -> [CLLocationCoordinate2D] {
        var locationCoordinates = [CLLocationCoordinate2D]()

        if let coordinates = polygon.first?.map({ CLLocationCoordinate2D(latitude: $0[1] , longitude: $0[0]) }) {
            locationCoordinates.append(contentsOf: coordinates)
        }

        return locationCoordinates
    }

    
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if let polygon = overlay as? MKPolygon {
            let renderer = MKPolygonRenderer(polygon: polygon)
            renderer.strokeColor = UIColor.blue.withAlphaComponent(0.4)
            return renderer
        } else if let multiPolygon = overlay as? MKMultiPolygon {
            let renderer = MKMultiPolygonRenderer(multiPolygon: multiPolygon)
            renderer.strokeColor = UIColor.blue.withAlphaComponent(0.4)
            return renderer
        }
        
        return MKOverlayRenderer(overlay: overlay)
    }

    @objc func mapTapped(_ gesture: UITapGestureRecognizer) {
        let point = gesture.location(in: self.mapView)
        let coordinate = self.mapView.convert(point, toCoordinateFrom: nil)
        let mappoint = MKMapPoint(coordinate)

        for renderer in self.mapView.polygonRenderers {
            let tapPoint = renderer.point(for: mappoint)
            if renderer.path.contains(tapPoint) && activeRendersContains(renderer) {
                renderer.strokeColor = UIColor.blue.withAlphaComponent(0.4)
                renderer.fillColor = UIColor.clear
                removeActive(renderer)

            } else if renderer.path.contains(tapPoint) {
                renderer.fillColor = UIColor.blue.withAlphaComponent(0.4)
                renderer.strokeColor = UIColor.blue
                appendActive(renderer)
            }
        }
    }

    func activeRendersContains(_ renderer: MKOverlayPathRenderer) -> Bool {
        if let multiRenderer = renderer as? MKMultiPolygonRenderer {
            return activeMultiRenders.contains(multiRenderer)
        } else if let polyRenderer = renderer as? MKPolygonRenderer {
            return activeRenders.contains(polyRenderer)
        } else {
            return false
        }
    }

    func appendActive(_ renderer: MKOverlayPathRenderer) {
        if let multiRenderer = renderer as? MKMultiPolygonRenderer {
            activeMultiRenders.append(multiRenderer)
        } else if let polyRenderer = renderer as? MKPolygonRenderer {
            activeRenders.append(polyRenderer)
        }
    }

    func removeActive(_ renderer: MKOverlayPathRenderer) {
        if let multiRenderer = renderer as? MKMultiPolygonRenderer {
            activeMultiRenders.removeAll(where: {$0 == multiRenderer})
        } else if let polyRenderer = renderer as? MKPolygonRenderer {
            activeRenders.removeAll(where: {$0 == polyRenderer})
        }
    }

    @objc func addMarker() {
        showMarker.toggle()

        if showMarker {
            staticMarkerButton.setTitle("Hide marker", for: .normal)
            pinView.isHidden = false
        } else {
            staticMarkerButton.setTitle("Show marker", for: .normal)
            pinView.isHidden = true
        }
    }

    @objc func visitRadius() {
        let radiusVC = RadiusViewController()
        radiusVC.modalPresentationStyle = .fullScreen
        present(radiusVC, animated: false, completion: nil)
    }

    @objc func addAnnotations() {
        showAnnotation.toggle()

        if showAnnotation {
            // Gets the coordinates and titles of each polygon renderer.
            let details = mapView.activePointDetailsFor(activeRenders)

            // Creates and adds annotations to the map with given details.
            mapView.addAnnotationsWith(details)
        } else {
            mapView.removeAllAnnotations()
        }
    }
}

extension ViewController: MKMapViewDelegate {

    func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
        pinView.pinPosition = .up
    }

    func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
        if showMarker {
            pinView.pinPosition = .down
        }
    }

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let annotationView = LabelAnnotationView(annotation: annotation, reuseIdentifier: annotation.title!)
        return annotationView
    }
}

extension MKMapView {
    var polygonRenderers: [MKOverlayPathRenderer] {
        var renders = [MKOverlayPathRenderer]()
        for overlay in self.overlays {
            if let polygon = overlay as? MKPolygon {
                guard let renderer = self.renderer(for: polygon) as? MKPolygonRenderer else { continue }
                renders.append(renderer)
            } else if let multiPolygon = overlay as? MKMultiPolygon {
                guard let renderer = self.renderer(for: multiPolygon) as? MKMultiPolygonRenderer else { continue }
                renders.append(renderer)
            }
        }
        return renders
    }

    typealias PointDetails = (coordinate: CLLocationCoordinate2D ,title: String)

    func activePointDetailsFor(_ renders: [MKPolygonRenderer]) -> [PointDetails] {
        var details = [PointDetails]()

        for each in renders {
            let title: String = each.polygon.title ?? ""
            let coordinate = each.polygon.coordinate

            let detail = (coordinate: coordinate ,title: title)
            details.append(detail)
        }

        return details
    }

    func addAnnotationsWith(_ details: [PointDetails]) {
        for each in details {
            let annotation = MKPointAnnotation()
            annotation.coordinate = each.coordinate
            annotation.title = each.title
            self.addAnnotation(annotation)
        }
    }

    func removeAllAnnotations() {
        let annotations = self.annotations
        self.removeAnnotations(annotations)
    }
}
EN

回答 1

Stack Overflow用户

发布于 2021-09-30 03:00:33

我认为MapKit在有洞的多边形上有问题,我已经在geojson.io中尝试了相同的区域,并且我验证了您的地理数据没有损坏。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69248520

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档