Custom MKOverlay & Renderer draw not working

107 Views Asked by At

I'm attempting to create a custom overlay for MKMapView which varies the colour of the polyline according to certain criteria. However, none of my drawing code works, & I can't for the life of me figure out why. I'm using CoreGPX for trackPoints, but I don't think that's relevant to the problem as the overlay's coordinates are set in the normal way.

The subclasses are -

class FlightOverlay: MKPolyline {
    
    enum Route {
        case coordinates([CLLocationCoordinate2D])
        case gpx(GPXRoot)
    }
    
    var trackPoints: [GPXTrackPoint]?
    
    convenience init?(route: Route) {
        
        switch route {
        case .coordinates(let coordinates):
            self.init(coordinates: coordinates, count: coordinates.count)
            
        case .gpx(let gpxRoot):
            
            guard let track = gpxRoot.tracks.first, let segment = track.segments.first else {
                return nil
            }
            
            var trackPoints: [GPXTrackPoint] = []
            
            let coordinates: [CLLocationCoordinate2D] = segment.points.compactMap { (point) -> CLLocationCoordinate2D? in
                guard let latitude = point.latitude, let longitude = point.longitude else {
                    return nil
                }
                trackPoints.append(point) // this ensures that our trackPoints array & the polyline points have the same number of elements
                return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
            }
            
            self.init(coordinates: coordinates, count: coordinates.count)
            
            self.trackPoints = trackPoints
        }
    }
}


class FlightOverlayRenderer: MKPolylineRenderer {
    
    override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
        
        guard let polyline = polyline as? FlightOverlay, let trackPoints = polyline.trackPoints, polyline.pointCount > 1 else {
            super.draw(mapRect, zoomScale: zoomScale, in: context)
            return
        }
        
        context.setLineWidth(lineWidth)
        
        var previousPoint = point(for: polyline.points()[0])
        
        for index in 1..<polyline.pointCount {
                
            let trackPoint = trackPoints[index]
            let acceleration: Double
            if let accelerationValue = trackPoint.extensions?[.acceleration].text, let accel = Double(accelerationValue) {
                acceleration = accel
            } else {
                acceleration = 0
            }
            
            let color = UIColor(red: acceleration * 10, green: 0.5, blue: 0.6, alpha: 1).cgColor
            context.setStrokeColor(color)
            
            context.beginPath()
            
            context.move(to: previousPoint)
            
            let point = point(for: polyline.points()[index])
            context.addLine(to: point)
            
            context.closePath()
            context.drawPath(using: .stroke)
            
            previousPoint = point
        }
    }
}

Even if I comment out all the stuff which attempts to set colours according to a GPXTrackPoint & just draw a start-to-finish line in bright red, nothing appears. When I directly call super, it works fine with the same coordinates.

Any help much appreciated!

1

There are 1 best solutions below

0
SomaMan On

Well, maybe it's a rookie error, but it never occurred to me that I'd need to control the lineWidth as well. Turns out it needs to be adjusted according to the zoomScale - I was drawing the line exactly where it needed to be but it was too thin to be seen. For my case, I do -

let lineWidth = self.lineWidth * 20 / pow(zoomScale, 0.6)
context.setLineWidth(lineWidth)

which gives me a visually pleasing line thickness for all zoom levels.