MKPolyline loses border when zooming

I added multiple MKPolyline to a MKMapView. It looks fine on the beginning, but when I start zooming, the border of the MKPolyline gets lost on some poly lines.

zoomed out:

zoomed in:

here is the code I used:

import MapKit
import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var mapView: MKMapView!

    override func viewDidLoad() {
        super.viewDidLoad()

        mapView.delegate = self
        mapView.mapType = .mutedStandard
        mapView.pointOfInterestFilter = .excludingAll

        let tiles = [
            MapTile(x: 8586, y: 5514),
            MapTile(x: 8587, y: 5514),
            MapTile(x: 8588, y: 5514),
            MapTile(x: 8587, y: 5515),
        ]

        let polygons = tiles.map { tile in
            MKPolygon(coordinates: tile.locations, count: tile.locations.count)
        }

        mapView.addOverlays(polygons, level: .aboveLabels)
    }
}


extension ViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if let mapTile = overlay as? MKPolygon {
            let renderer = MKPolygonRenderer(polygon: mapTile)
            renderer.fillColor = .systemBlue.withAlphaComponent(0.15)
            renderer.strokeColor = .systemBlue
            renderer.lineWidth = 0.7

            return renderer
        }

        return MKOverlayRenderer(overlay: overlay)
    }
}

struct MapTile: Identifiable, Hashable {
    let x: Int
    let y: Int

    var id: String { "\(x)-\(y)" }

    static let zoomFactor = Double(1 << 14)

    static func locationOrigin(tileX: Int, tileY: Int) -> CLLocationCoordinate2D {
        let lon = (Double(tileX) / MapTile.zoomFactor) * 360.0 - 180.0
        let lat = atan( sinh (.pi - (Double(tileY) / MapTile.zoomFactor) * 2 * Double.pi)) * (180.0 / .pi)

        return CLLocationCoordinate2D(latitude: lat, longitude: lon)
    }

    var locationOrigin: CLLocationCoordinate2D {
        MapTile.locationOrigin(tileX: x, tileY: y)
    }

    var locations: [CLLocationCoordinate2D] {
        let topLeft = locationOrigin
        let topRight = MapTile.locationOrigin(tileX: x + 1, tileY: y)
        let bottomRight = MapTile.locationOrigin(tileX: x + 1, tileY: y + 1)
        let bottomLeft = MapTile.locationOrigin(tileX: x, tileY: y + 1)

        return [
            topLeft,
            topRight,
            bottomRight,
            bottomLeft
        ]
    }
}

any ideas why this happens?

Accepted Reply

Ah, this is interesting...
If I replace the coordinates with:

CLLocationCoordinate2D(latitude: 50.597186, longitude: 8.657226),
CLLocationCoordinate2D(latitude: 50.597186, longitude: 8.679199),
CLLocationCoordinate2D(latitude: 50.583236, longitude: 8.679199),
CLLocationCoordinate2D(latitude: 50.583236, longitude: 8.657226)

...it works!

Replies

It is most likely caused by:

renderer.lineWidth = 0.7

Try...

renderer.lineWidth = 1

...and see if that fixes it.

  • I tried renderer.lineWidth = 1 and also renderer.lineWidth = 2. Same wrong behaviour.

Add a Comment

Yes, that's a doozy.
I am seeing the problem in the Simulator, and on a real device.

I have simplified your code (using only the first polygon), in case anyone else wants to try it:

import MapKit
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var mapView: MKMapView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        mapView.delegate = self
        
        let coordinates: [CLLocationCoordinate2D] = [
            CLLocationCoordinate2D(latitude: 50.59718623058702, longitude: 8.6572265625),
            CLLocationCoordinate2D(latitude: 50.59718623058702, longitude: 8.67919921875),
            CLLocationCoordinate2D(latitude: 50.58323661480589, longitude: 8.67919921875),
            CLLocationCoordinate2D(latitude: 50.58323661480589, longitude: 8.6572265625)
        ]
        
        let polygon = MKPolygon(coordinates: coordinates, count: coordinates.count)
        mapView.addOverlay(polygon)
        
        mapView.centerCoordinate = polygon.coordinate
        mapView.region.span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
    }
}

extension ViewController: MKMapViewDelegate {
    
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        
        if let polygon = overlay as? MKPolygon {
            let renderer = MKPolygonRenderer(polygon: polygon)
            renderer.strokeColor = .black
            renderer.lineWidth = 5
            return renderer
        }
        
        return MKOverlayRenderer(overlay: overlay)
    }
}

On zooming in, the polygon loses one of it's sides, then another one.

  • ...so it's nothing to do with MapTile.

Add a Comment

Ah, this is interesting...
If I replace the coordinates with:

CLLocationCoordinate2D(latitude: 50.597186, longitude: 8.657226),
CLLocationCoordinate2D(latitude: 50.597186, longitude: 8.679199),
CLLocationCoordinate2D(latitude: 50.583236, longitude: 8.679199),
CLLocationCoordinate2D(latitude: 50.583236, longitude: 8.657226)

...it works!

Oh wow! That's indeed interesting.

I just added some rounding when creating the CLLocationCoordinate2Dobject and it now works fine.

let _lat = (lat * 1_000_000).rounded() / 1_000_000
let _lon = (lon * 1_000_000).rounded() / 1_000_000
CLLocationCoordinate2D(latitude: _lat, longitude: _lon)

Thank you for your help. I will mark your answer as the accepted one, because it works as a workaround.

I also created a feedback: FB10812168 and mentioned this thread in it

Well, that was an interesting one!
It's hard to understand why the original code is failing, but at least you have a workaround.
Thanks for accepting my answer.