swift4mapkitmkoverlay

inverted circle map fill in swift 4


I am trying to draw a circle on map, when user select option 1 the circle is filled with blue color, when user select option 2, the whole map will be filled with blue while the circle area is colorless. how is that possible?

`func addRadiusOverlay(forGeotification geotification: Geotification) {

        mapView?.addOverlay(MKCircle(center: geotification.coordinate, radius: 300))
    }`



`func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if overlay is MKCircle {
            let circleRenderer = MKCircleRenderer(overlay: overlay)
            circleRenderer.lineWidth = 5.0
            circleRenderer.strokeColor = UIColor(red: 0/255, green: 122/255, blue: 255/255, alpha: 1.0)
            circleRenderer.fillColor = circleRenderer.strokeColor!.withAlphaComponent(0.1)


            return circleRenderer
        }
        return MKOverlayRenderer(overlay: overlay)
    }`

Solution

  • In case of Option-2, to draw a circle with filled outside and transparent hole, use MKPolygon.polygonWithPoints:count:interiorPolygons: with interiorPolygons parameter to be the circle MKPolygon, like so:

    MKPolygon(coordinates: WORLD_COORDINATES, count: WORLD_COORDINATES.count, interiorPolygons: circlePolygon)
    

    Use following method to generate polygons

    func setupRadiusOverlay(forGeotification geotification: Geotification) {
        let c = makeCircleCoordinates(geotification.coordinate, radius: RADIUS)
        self.option1polygon = MKPolygon(coordinates: c, count: c.count, interiorPolygons: nil)
        self.option2polygon = MKPolygon(coordinates: WORLD_COORDINATES, count: WORLD_COORDINATES.count, interiorPolygons: option1polygon)
    }
    

    Use following method to add a polygon

    func addRadiusOverlay(isOption2Selected: Bool) {
        guard let mapView = mapView else { return }
    
        let overlay = isOption2Selected ? self.option2polygon : self.option1polygon
        if mapView.overlays.index(where: { $0 === overlay }) == nil {
            mapView.removeOverlays(mapView.overlays.filter{ $0 is MKPolygon })
            mapView.addOverlay(overlay)
        }
    }
    

    Change delegate method mapView(_:rendererFor:)

    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        guard overlay is MKPolygon else {
            return MKOverlayRenderer(overlay: overlay)
        }
    
        let color = UIColor(red: 0/255, green: 122/255, blue: 255/255, alpha: 1.0)
        let renderer = MKPolygonRenderer(overlay: overlay)
        renderer.lineWidth = 5.0
        renderer.strokeColor = color
        renderer.fillColor = color.withAlphaComponent(0.1)
        return renderer
    }
    

    following would be world coordinates

    let WORLD_COORDINATES = [
        CLLocationCoordinate2D(latitude: 90, longitude: 0),
        CLLocationCoordinate2D(latitude: 90, longitude: 180),
        CLLocationCoordinate2D(latitude:-90, longitude: 180),
        CLLocationCoordinate2D(latitude:-90, longitude: 0),
        CLLocationCoordinate2D(latitude:-90, longitude:-180),
        CLLocationCoordinate2D(latitude: 90, longitude:-180)
    ]
    

    And following helper method, courtesy of my old answer

    func makeCircleCoordinates(_ coordinate: CLLocationCoordinate2D, radius: Double, tolerance: Double = 3.0) -> [CLLocationCoordinate2D] {
        let latRadian = coordinate.latitude * .pi / 180
        let lngRadian = coordinate.longitude * .pi / 180
        let distance = (radius / 1000) / 6371 // kms
        return stride(from: 0.0, to: 360.0, by: tolerance).map {
            let bearing = $0 * .pi / 180
    
            let lat2 = asin(sin(latRadian) * cos(distance) + cos(latRadian) * sin(distance) * cos(bearing))
            var lon2 = lngRadian + atan2(sin(bearing) * sin(distance) * cos(latRadian),cos(distance) - sin(latRadian) * sin(lat2))
            lon2 = fmod(lon2 + 3 * .pi, 2 * .pi) - .pi  // normalise to -180..+180º
            return CLLocationCoordinate2D(latitude: lat2 * (180.0 / .pi), longitude: lon2 * (180.0 / .pi))
        }
    }
    

    option-2 selection yields

    enter image description here

    option-1 should do inverse :)