ATTN: C. Kelly, S. Kingry, B. Smith, et al...

My Python code to plot elevation profiles like this: Alls you need for the color mapped profile in comma separated x and z values.
def elevColorMap(fIN, fOUT='elev.png'):
        fIN : str (req'd)
            This is the file name of the data exported from
            Needs to be in a .csv (comma-separated variable) format. Plot
        fOUT : str (opt)
            This is the file image of the image output by this utility.
            Defaults to 'elev.png'. pl.savefig command can infer file
            format from most common image extensions (e.g., .pdf, .gif...)

        >>> os.chdir('users/paul/python/misc')
        >>> from bikes import *
        >>> elevColorMap('users/paul/desktop/elev.csv', 'tues.jpg')
    | Paul M. Hobson             __o    |
    | pmhobson@gmail           _`\<,_   | 
    | 2009/07/30              (_)/ (_)  |
    import pylab as pl
    from numpy import floor, ceil
    x,y,ca,cd = pl.loadtxt(fIN, delimiter=',', skiprows=1, unpack=True)
    Y = y * 10.
    C = int(len(x))
    R = int(ceil(max(Y)))
    g = pl.zeros([C,1])
    for n in range(1,C):
        g[n] = (y[n] - y[n-1])/(x[n]-x[n-1])*100
    g = smoothData(g,15,1)

    yin = pl.arange(R)
    p = pl.zeros([R,C])
    for n in range(C):
        p[0:Y[n],n] = g[n]

    g1 = int(floor(min(g)))
    g2 = int(ceil(max(g)))

    V = range(g1,g2, 1) 
    fig = pl.figure(1, figsize=(6,3.5))
    G = pl.contourf(x/1000, yin/10, p, V, antialiased=True)
    x2 = x.copy()
    x2[-1] += 10
    xu,gu = pl.poly_between(x2,y,max(y))
    pl.fill(xu/1000,gu, facecolor='white', edgecolor='white')

    elv = pl.plot(x/1000, y, 'k-', lw=2.0, label='Elevation')
    cbar = pl.colorbar(G, ticks=range(g1,g2+1))'Gradient (%)', fontsize=10)
    cl = pl.getp(, 'ymajorticklabels')
    pl.setp(cl, fontsize=10)
    pl.xlabel('Distance Ridden (km)')
    pl.ylabel('Elevation (m above MSL)')

    figFormat(1, ff='sans-serif')
    return fig, cbar

def figFormat(fig_num, fs=10, ff='serif', fw='normal'):

        Add functionality to handle colorbars
        fig_num : int (req'd)
            figure handle of figure to modified

        fs : int (optional)
            font size (points) figure text (default = 10)
        ff : str (optional)*
            font family of figure text (default = 'serif')
        fw : str (optional)**
            font weight of figure text (default = 'normal')
        *possible ff (font-families):
            [ 'serif' | 'sans-serif' | 'cursive' | 'fantasy' | 'monospace' ]
            (you should probably stick to serif, sans-serif, or monospace)
        **possible fw (font-weights):
            [ 'light' | 'normal' | 'bold' | 'heavy' ]
            (you should probably stick with normal...i.e., don't specify this)
        >>> from pylab import *
        >>> from MiscUtils import *
        >>> x = arange(16)
        >>> y = x**2 + 5*x - 9
        >>> figure(1)
        >>> h1 = plot(x,y,'ko', label='test1')
        >>> h2 = plot(x,y,'k-', label='test2')
        >>> xlabel('X-data')
        >>> ylabel('Y-data')
        >>> legend()
        >>> figFormat(1, fs=12, ff='sans-serif')
        >>> savefig('test.pdf')
    | Paul M. Hobson             __o    |
    | pmhobson@gmail           _`\<,_   |
    | 2009/05/31              (_)/ (_)  |
    import pylab as pl                      # bring in pylab module
    pl.figure(fig_num)                      # bring spec'd figure to focus
    X = pl.getp(pl.gca(), 'xticklabels')    # get the x-axis labels
    Y = pl.getp(pl.gca(), 'yticklabels')    # get the y-axis labels

    for i in range(len(X)):                 # format x-axis 
        pl.setp(X[i], family=ff, size=fs)
    for i in range(len(Y)):                 # format y-axis
        pl.setp(Y[i], family=ff,size=fs)
    XL = pl.getp(pl.gca(), 'xlabel')        # format x-axis labels   
    pl.xlabel(XL, size=fs, family=ff)
    YL = pl.getp(pl.gca(), 'ylabel')        # format y-axis labels
    pl.ylabel(YL, size=fs, family=ff)
    try:                                    # format the legend if there is one
        L = pl.getp(pl.gca(),'legend')
        LC = pl.getp(L, 'children')
        if len(LC) > 0:
            pl.setp(LC[-1], size=fs, family=ff)
        L = 1
def smoothData(x, d, type):                 
    from numpy import zeros, mean, mod
    X = zeros(len(x))               # initialize output
    if type == 1:                   # for a rolling average sceme
        for n in range(len(x)):            
            if n-d < 0:
                n = range(0, n+d+1)
            elif n+d >= len(x):
                n = range(n-d, len(x))
                n = range(n-d, n+d+1)
            X[n] = mean(x[n])
    elif type == 2:                 # resample at lower frequency
        # NOT FINISHED
        N = len(x) - mod(len(x),d)
        X = zeros(N)
        for n in range(0,N,d):
            m = n + d
            X[n] = mean(x[n:m])
        print 'invalid averaging type'
        X = x
    return X

toekneebullard's picture

I feel that these graphs

I feel that these graphs should always have X and Y on the same scale, that way we can see what the hills actually looked like. Think of it as a new way to draw a landscape.

45 degrees

if you want to better distinguish between the pitch of climbs, 45 degree slopes (on average) would be ideal: (look for the "Aspect ratio" section)


putting rides of about 50 - 70 miles on the same scale as the vertical axis makes it difficult to see even climbs with 4000+ ft of elevation gain.

also, you've got the source code, YOU can make it do want you want it to do

ckdake's picture

FYI, did some stuff you can't

FYI, did some stuff you can't to so code looks better now :)


i had the "code" tags wrapped around the whole thing. why do line breaks, break things?

ckdake's picture

Here are the horribile

Here are the horribile details:

Basically, the default input filter here buts in lil br tags and p tags as it thinks are needed so that people don't have to type html, but it breaks some special use cases and browsers handle funky tag nesting differently, especially with things like pre/code.

theothergraham's picture

thanks, ck

I was having a hard time parsing it before.