Giter VIP home page Giter VIP logo

Comments (4)

joeltrew avatar joeltrew commented on June 11, 2024

Hi there! Thanks, it's so cool to see people using something I've made! I'm actually doing the exact thing you ask in one of my apps so I can help you on this one. What you want to do is calculate a hue value for each point based on your elevation data, and create a gradient between the previous point's colour and the current point's colour in the drawMapRect function, for each part of the path.

Here's the class I made so you can see how I've done it. I might add it to this repo if it's something lots of people want.

 //
 //  PedalPathRenderer.swift
 //  Pedal
 //
 //  Created by Joel Trew on 24/04/2016.
 //

 import MapKit

 class PedalPathElevationRenderer: MKOverlayPathRenderer {

    var polyline : MKPolyline
    var elevations: [Int]
    var maxElevation: Int?
    var border: Bool = false
    var borderColor: UIColor?



    //MARK: Initializers
    init(polyline: MKPolyline, elevations: [Int]) {
        self.polyline = polyline
        self.elevations = elevations
        super.init(overlay: polyline)
        self.maxElevation = calculateMaxElevation()
    }

    //MARK: Override methods
    override func drawMapRect(mapRect: MKMapRect, zoomScale: MKZoomScale, inContext context: CGContext) {

        var baseWidth: CGFloat
        if zoomScale > 0.1 {
            baseWidth = self.lineWidth * 2
        } else {
            baseWidth = self.lineWidth / zoomScale
        }

        /*
         Set path width relative to map zoom scale
         */

        var firstColor:UIColor = UIColor(hue: self.getHueFor(elevation: elevations[0]), saturation: 1, brightness: 1, alpha: 1)
        var lastColor: UIColor = UIColor(hue: self.getHueFor(elevation: elevations[1]), saturation: 1, brightness: 1, alpha: 1)


        if self.border {
            CGContextSetLineWidth(context, baseWidth * 1.5)
            CGContextSetLineJoin(context, CGLineJoin.Round)
            CGContextSetLineCap(context, CGLineCap.Round)
            CGContextAddPath(context, self.path)
            CGContextSetStrokeColorWithColor(context, self.borderColor?.CGColor ?? UIColor.whiteColor().CGColor)
            CGContextStrokePath(context)
        }


        for i in 0...self.polyline.pointCount-1 {
            let point: CGPoint = pointForMapPoint(self.polyline.points()[i])
            let path: CGMutablePathRef  = CGPathCreateMutable()

            lastColor = UIColor(hue: self.getHueFor(elevation: elevations[i]), saturation: 1, brightness: 1, alpha: 1)
            if i==0 {
                CGPathMoveToPoint(path, nil, point.x, point.y)
            } else {
                let previousPoint = self.pointForMapPoint(polyline.points()[i-1])
                CGPathMoveToPoint(path, nil, previousPoint.x, previousPoint.y)
                CGPathAddLineToPoint(path, nil, point.x, point.y)


                CGContextSaveGState(context);
                let colorspace = CGColorSpaceCreateDeviceRGB()
                let locations: [CGFloat] = [0,1]
                let gradient = CGGradientCreateWithColors(colorspace, [firstColor.CGColor, lastColor.CGColor], locations)

                let pathToFill = CGPathCreateCopyByStrokingPath(path, nil, baseWidth, CGLineCap.Round, CGLineJoin.Round, self.miterLimit)
                CGContextAddPath(context, pathToFill)
                CGContextClip(context);

                let gradientStart = previousPoint;
                let gradientEnd = point;

                CGContextDrawLinearGradient(context, gradient, gradientStart, gradientEnd, CGGradientDrawingOptions.DrawsBeforeStartLocation);
                CGContextRestoreGState(context)
            }
            firstColor = UIColor(CGColor: lastColor.CGColor)
        }


        super.drawMapRect(mapRect, zoomScale: zoomScale, inContext: context)
    }

    /*
     Create path from polyline
     Thanks to Adrian Schoenig
     (http://adrian.schoenig.me/blog/2013/02/21/drawing-multi-coloured-lines-on-an-mkmapview/ )
     */
    override func createPath() {
        let path: CGMutablePathRef  = CGPathCreateMutable()
        var pathIsEmpty: Bool = true

        for i in 0...self.polyline.pointCount-1 {

            let point: CGPoint = pointForMapPoint(self.polyline.points()[i])
            if pathIsEmpty {
                CGPathMoveToPoint(path, nil, point.x, point.y)
                pathIsEmpty = false
            } else {
                CGPathAddLineToPoint(path, nil, point.x, point.y)
            }
        }
        self.path = path
    }


    private func getHueFor(elevation elevation: Int) -> CGFloat {
        let maxElevation = CGFloat(self.maxElevation ?? 50)
        let hue = CGFloat(elevation).map(from: 0...maxElevation+5, to: 0.0...0.4)
        return 0.4-hue

    }

    private func calculateMaxElevation() -> Int {
        return self.elevations.maxElement() ?? 0
    }

 }


 extension CGFloat {
    func map(from from: ClosedInterval<CGFloat>, to: ClosedInterval<CGFloat>) -> CGFloat {
        let result = ((self - from.start) / (from.end - from.start)) * (to.end - to.start) + to.start
        return result
    }
 }

Hopefully this will help you out with your issue. 😄

  • Joel

from gradientpathrenderer.

NasH5169 avatar NasH5169 commented on June 11, 2024

Thanks for your answer. I did a similar coding. It's working but each drawMapRect call will create multiples CGPath, the map reactivity slow down, the memory increase a lot and on iPad 2 (512 MB), some memory warning come fast. I have some GPX traces with a lot of points (~1000), so for now, i'm searching for a lighter solution.

Thanks again and happy coding :)

from gradientpathrenderer.

joeltrew avatar joeltrew commented on June 11, 2024

Ok good luck :) , you're welcome to share your solution here if you find a better way

from gradientpathrenderer.

wangjinyu avatar wangjinyu commented on June 11, 2024

@NasH5169 What's the solution, can you share it to me. Thanks

from gradientpathrenderer.

Related Issues (5)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.