def plot_gmt_xyz_contour(data, output_file, title=None,
                         np_posn=None, s_posn=None,
                         cb_label=None, cb_steps=None,
                         colourmap=None, annotate=[], linewidth=1.0,
                         show_graph=False, map_extent=None, 
                         annot_lat = '30m', grid_lat = '30m',
                         annot_lon = '30m', grid_lon = '30m'):
    """A function to take XYZ data and plot contours onto a GMT map.
    
    data         an iterable of values in xyz format (lon, lat, val)
    output_file  path to file that should be generated (*.png, *.eps, etc)
    title        string used to title the plot
    np_posn      code string for north pointer placement, one of:
                     'C'   - centre of plot
                     'NE'  - inside plot, northeast corner
                     'CE'  - inside plot, centre of east edge
                     'NNE' - outside plot, north of NE corner
                     'ENE' - outside plot, east of NE corner
                      etc (see the documentation for 'placement')
    s_posn       code string for scale placement
                     see examples for 'np_posn' above
    cb_label     string containing the label text for the colorbar
                 (if not supplied, no colourbar)
    cb_steps     if supplied is a sequence of discrete values at
                 the colour changes
    colourmap    string containing name of required colormap
                 this could be a GMT name or a local name
    annotate     list of user annotations:
                     if None, no user or system annotations
                     if [],   only system annotations
                     else system and user annotations
    linewidth    width of countour lines in pixels
    show_graph   if True try to display final image in system-independant way
    map_extent   set the extent of the displayed map if supplied
                 (get extent from data if not supplied)
    annot_lat    Spacing in minutes for latitude annotations (e.g. 30m = 0.5 degress) 
    grid_lat     Spacing in minutes for latitude grid lines (e.g. 30m = 0.5 degress) 
    annot_lon    Spacing in minutes for longitude annotations (e.g. 30m = 0.5 degress) 
    grid_lon     Spacing in minutes for longitude grid lines (e.g. 30m = 0.5 degress) 
    """

    # create a scratch directory for ephemeral files
    tmp_dir = tempfile.mkdtemp(prefix='plot_gmt_xyz_contour_')

    # set up the GMT default values
    util.set_gmt_defaults(tmp_dir)

    # handle optional parameters
    if title is None:
        title = ''

    # if no colourmap supplied, use default
    c_map = ColourMap
    if colourmap is not None:
        c_map = colourmap
    if not os.path.exists(c_map):
        c_map = util.get_colourmap(c_map)
    

    # get maximum and minimum values
    max_val = util.max_nan(data[:,2])
    min_val = util.min_nan(data[:,2])

    # get extent of data
    if map_extent:
        extent = map_extent
    else:
        extent = ugxe.get_extent(data, margin=0)
    (ll_lat, ll_lon, ur_lat, ur_lon) = extent
    r_opt = '-R%f/%f/%f/%f' % (ll_lon, ur_lon, ll_lat, ur_lat)

    # set the -J option for Mercator projection
    j_opt = '-JM%fc' % cfg.MapWidthCentimetres

    # write a GMT XYZ file (required by GMT)
    my_xyz_file = os.path.join(tmp_dir, 'data.xyz')
    num.savetxt(my_xyz_file, data)

    # generate CPT file
    my_cpt_file = os.path.join(tmp_dir, 'data.cpt')
    if cb_steps is None:
        cb_steps = []
    if len(cb_steps) > 0:
        util.make_discrete_cpt(my_cpt_file, c_map, cb_steps)
    else:
        (start, stop, step) = util.get_scale_min_max_step(max_val, min_val)
        if os.path.exists(c_map):
            cm = c_map
        else:
            cm = util.get_colourmap(c_map)
        util.do_cmd('makecpt -C%s.cpt -T%f/%f/%f > %s'
                    % (cm, start, stop, step, my_cpt_file))

    # think of a postscript filename for plot output
    my_ps_file = os.path.join(tmp_dir, 'data.ps')

    # linewidth checking - if width < 1.0, no lines
    w_opt = '-W%.1f/0' % linewidth
    if linewidth < 1.0:
        w_opt = '-W+1'

    
    #Apply blockmean to avoid aliasing short wavelengths
    my_grd_file = os.path.join(tmp_dir, 'temp.grd')
    my_grd_file2 = os.path.join(tmp_dir, 'temp2.grd')
    my_grd_file3 = os.path.join(tmp_dir, 'temp3.grd')
    my_mask_file = os.path.join(tmp_dir, 'mask.grd')
    my_hzd_file = os.path.join(tmp_dir, 'hazard.grd')
    print my_grd_file    
    print my_grd_file2 
    
    
    util.do_cmd('surface %s -G%s %s -I0.001 -T0.5'
                % (my_xyz_file, my_grd_file,r_opt))
    util.do_cmd('grdimage %s -K -Q -C%s %s %s > %s'
                % (my_grd_file, my_cpt_file, r_opt, j_opt,
                   my_ps_file))
    
    """
    # METHOD 1
    util.do_cmd('surface %s -G%s %s -I0.001 -T0.5'
                % (my_xyz_file, my_grd_file,r_opt))
    
    util.do_cmd('grdmask %s %s -I0.001 -G%s -NNaN/NaN/1'
                % (my_xyz_file,r_opt,my_mask_file))
    
    util.do_cmd('grdmath %s %s MUL = %s'
                % (my_mask_file, my_grd_file,my_hzd_file))
    
    util.do_cmd('grdimage %s -K -Q -C%s %s %s > %s'
                % (my_hzd_file, my_cpt_file, r_opt, j_opt,
                   my_ps_file))
    """
    
    """
    # METHOD 2
    
    util.do_cmd('xyz2grd %s %s -I0.001 -G%s'
                % (my_xyz_file,r_opt,my_grd_file))
    
    util.do_cmd('grdsample %s %s -I0.005 -G%s'
                % (my_grd_file,r_opt,my_grd_file2))
    
    util.do_cmd('grdmask %s %s -I0.005 -G%s -NNaN/NaN/1'
                % (my_xyz_file,r_opt,my_mask_file))
    
    util.do_cmd('grdmath %s %s MUL = %s'
                % (my_mask_file, my_grd_file2,my_hzd_file))
    
    util.do_cmd('grdimage %s -K -Q -C%s %s %s > %s'
                % (my_hzd_file, my_cpt_file, r_opt, j_opt,
                   my_ps_file))
    """
    
    """
    # METHOD 3
    util.do_cmd('xyz2grd %s %s -I0.001 -G%s'
                % (my_xyz_file,r_opt,my_grd_file))
                
    util.do_cmd('grdmask %s %s -I0.001 -G%s -NNaN/NaN/1'
                % (my_xyz_file,r_opt,my_mask_file))
    
    util.do_cmd('grdmath %s %s MUL = %s'
                % (my_mask_file, my_grd_file,my_hzd_file))
    
    util.do_cmd('psxy %s %s %s -W0.5p/100 -G200 -O -K -M >%s'
                % (my_xyz_file, r_opt,j_opt,my_ps_file)) 
    
    util.do_cmd('grdimage %s -K -Q -C%s %s %s > %s'
                % (my_hzd_file, my_cpt_file, r_opt, j_opt,
                   my_ps_file))
    """
    
    """
    # METHOD 4
    util.do_cmd('xyz2grd %s %s -I0.001 -G%s'
                % (my_xyz_file,r_opt,my_grd_file))
                
    util.do_cmd('grdmask %s %s -I0.001 -G%s -NNaN/NaN/1'
                % (my_xyz_file,r_opt,my_mask_file))
    
    util.do_cmd('grdmath %s %s MUL = %s'
                % (my_mask_file, my_grd_file,my_hzd_file))
    
    util.do_cmd('psxy %s %s %s -W0.5p/100 -K >%s'
                % (my_xyz_file, r_opt,j_opt,my_ps_file)) 
    
    util.do_cmd('grdimage %s -K -Q -C%s %s -O %s > %s'
                % (my_hzd_file, my_cpt_file, r_opt, j_opt,
                   my_ps_file))
    util.do_cmd('psxy %s %s %s -W0.5p/100 -M -O -K >%s'
                % (my_xyz_file, r_opt,j_opt,my_ps_file))                          
     """
               
    
   
    # draw the coast
    util.do_cmd('pscoast %s -K -O %s -Df -W -S192/216/255 >> %s'
                % (r_opt, j_opt, my_ps_file))

    # do annotations
    if annotate is not None:
        ok_opt = '-K -O'
        jok_opt = '%s %s' % (ok_opt, j_opt)
        uga.generated_annotation(tmp_dir, my_ps_file, extent,
                                 cfg.MapWidthCentimetres, jok_opt)
        uga.user_annotation(tmp_dir, my_ps_file, extent, j_opt, ok_opt, annotate)

    # draw the colorbar
    if cb_label:
        x_offset = cfg.MapWidthCentimetres + 0.5 
        y_offset = cfg.MapHeightCentimetres/2.0 - 1.75
        util.do_cmd('psscale -K -O -E -C%s -D%.1fc/%.1fc/9.0c/0.8c "-B:%s:" >> %s'
                    % (my_cpt_file, x_offset, y_offset, cb_label, my_ps_file))

    # draw the rest of the map
    t_opt = ''
    if np_posn:
        t_opt = ugp.get_northpointer_placement(np_posn, extent)
    l_opt = ''
    if s_posn:
        l_opt = ugp.get_scale_placement(s_posn, extent)

    util.do_cmd('psbasemap %s -O %s "-Ba%s/a%s:.%s:WSen" -Bg%s/g%s %s %s >> %s'
                % (r_opt, j_opt, annot_lon, annot_lat, title, grid_lon, grid_lat, t_opt, l_opt, my_ps_file))

    # convert PS to required type
    (_, file_extension) = output_file.rsplit('.', 1)
    try:
        t_opt = util.Extension2TOpt[file_extension.lower()]
    except KeyError:
        raise RuntimeError("Can't handle plot outputfile type: %s" %
                           file_extension)

    util.do_cmd('ps2raster %s -A -T%s' % (my_ps_file, t_opt))
    (my_output_file, _) = my_ps_file.rsplit('.', 1)
    my_output_file += '.' + file_extension
    shutil.copyfile(my_output_file, output_file)

    # if it's required to show the graph ...
    # TODO: Experimental - leave?
    if show_graph:
        import sys
        if sys.platform == 'win32':
            os.startfile(my_output_file)
        else:
            import subprocess
            try:
                subprocess.Popen(['xdg-open', my_output_file])
            except OSError:
                print("Sorry, the 'xdg-open' application is required to "
                      "automatically display images.\nYou can see the image "
                      "in file %s." % output_file)

    # remove the temp directory
    shutil.rmtree(tmp_dir)
Example #2
0
def plot_gmt_xyz(data, output_file, bins=100, title=None, np_posn=None,
                 s_posn=None, cb_label=None, colourmap=None, cb_steps=None,
                 annotate=[], show_graph=False, map_extent=None):
    """A function to take gridded XYZ data and plot onto a GMT map.
    Use a discrete colourbar.
    
    data         an iterable of *gridded* values in xyz format (lon, lat, val)
    output_file  path to file that should be generated (*.png, *.eps, etc)
    bins         number of bins in the X direction if a single integer, else
                 expect 2-tuple of integers (bins_x, bins_y)
    title        string used to title the plot
    np_posn      code string for north pointer placement, one of:
                     'C'   - centre of plot
                     'NE'  - inside plot, northeast corner
                     'CE'  - inside plot, centre of east edge
                     'NNE' - outside plot, north of NE corner
                     'ENE' - outside plot, east of NE corner
                      etc (see the documentation for 'placement')
                   OR
                     (lon,lat) tuple
    s_posn       code string for scale placement
                     see examples for 'np_posn' above
    cb_label     string containing the label text for the colorbar
                 (if not supplied, no colourbar)
    colourmap    string containing name of required colormap
                 (a local file 'hazmap.cpt' or GMT name 'cool')
    cb_steps     an iterable of desired discrete steps in the DISCRETE
                 colourmap, or an empty list (code chooses steps), or None
                 which gives the default continuous colourmap
    annotate     list of user annotations:
                     if None, no user or system annotations
                     if [],   only system annotations
                     else     system and user annotations
    show_graph   if True try to display final image in system-independant way
    map_extent   sets the extent of the displayed map if supplied
                 (get extent from data if not supplied)

    """

    # create a scratch directory for ephemeral files
    tmp_dir = tempfile.mkdtemp(prefix='plot_map_')

    # set up the GMT default values
    util.set_gmt_defaults(tmp_dir)

    # handle bins parameter
    try:
        (bins_y, bins_x) = bins
    except TypeError:
        bins_x = bins_y = bins

    # handle optional parameters
    if title is None:
        title = ''

    c_map = DefaultColourMap
    if colourmap:
        c_map = colourmap
    c_map = util.get_colourmap(c_map)

    # get extent of data (user-specified, or we get from data)
    if map_extent:
        extent = map_extent
    else:
        extent = ugxe.get_extent(data, margin=0)
    (ll_lat, ll_lon, ur_lat, ur_lon) = extent
    r_opt = '-R%f/%f/%f/%f' % (ll_lon, ur_lon, ll_lat, ur_lat)

    # get max NON-NAN value from XYZ data
    max_val = util.max_nan(data[:,2])
    min_val = util.min_nan(data[:,2])

    # write a GMT XYZ file
    my_xyz_file = os.path.join(tmp_dir, 'data.xyz')
    scipy.savetxt(my_xyz_file, data)

    # convert XYZ to GRD file
    my_grd_file = os.path.join(tmp_dir, 'data.grd')
    util.do_cmd('xyz2grd %s %s -I%d+/%d+ -G%s'
                % (my_xyz_file, r_opt, bins_x, bins_y, my_grd_file))

    # generate CPT file
    my_cpt_file = os.path.join(tmp_dir, 'data.cpt')
    if cb_steps is None:
        util.do_cmd('grd2cpt %s -C%s -Z > %s'
                    % (my_grd_file, c_map, my_cpt_file))
    elif hasattr(cb_steps, '__iter__'):
        if len(cb_steps) == 0:           # code chooses steps
            (start, stop, step) = util.get_scale_min_max_step(max_val, min_val)
            util.do_cmd('makecpt -C%s.cpt -T%f/%f/%f > %s'
                        % (c_map, start, stop, step, my_cpt_file))
        else:                               # user chooses steps
            util.make_discrete_cpt(my_cpt_file, c_map, cb_steps)
    else:
        msg = "cb_steps param must be None or list: got %s" % type(cb_steps)
        raise RuntimeError(msg)

    # think of a postscript filename
    my_ps_file = os.path.join(tmp_dir, 'data.ps')

    # draw GRD data on map
    util.do_cmd('grdimage %s -K %s -JM%fc -C%s -Q > %s'
                % (my_grd_file, r_opt, cfg.MapWidthCentimetres,
                   my_cpt_file, my_ps_file))

    # draw the coast
    util.do_cmd('pscoast %s -K -O -JM%fc -Df -W -S192/216/255 >> %s'
                % (r_opt, cfg.MapWidthCentimetres, my_ps_file))

    # draw the rest of the map
    t_opt = ''
    if np_posn:
        t_opt = ugp.get_northpointer_placement(np_posn, extent)
    l_opt = ''
    if s_posn:
        l_opt = ugp.get_scale_placement(s_posn, extent)

    util.do_cmd('psbasemap %s -K -O -JM%fc -Ba30m:".%s":WSen -Bg30m %s %s >> %s'
                % (r_opt, cfg.MapWidthCentimetres, title, t_opt, l_opt, my_ps_file))

    # do annotations
    if annotate is not None:
        j_opt = '-JM%fc' % cfg.MapWidthCentimetres
        ok_opt = '-K -O'
        jok_opt = '%s %s' % (j_opt, ok_opt)
        uga.generated_annotation(tmp_dir, my_ps_file, extent,
                                 cfg.MapWidthCentimetres, jok_opt)
        uga.user_annotation(tmp_dir, my_ps_file, extent, j_opt, ok_opt, annotate)

    # figure out scale skip from max data value
    skip = util.get_scale_skip(max_val)

    # draw the colorbar
    if cb_label:
        x_offset = cfg.MapWidthCentimetres + 0.5
        util.do_cmd('psscale -O -K -C%s -D%.1fc/8.0c/9.0c/0.8c -B%f:"%s": >> %s'
                    % (my_cpt_file, x_offset, skip, cb_label, my_ps_file))
    # force close
    util.do_cmd('psxy -O -J -R -T  >> %s'
                    % (my_ps_file))

    # convert PS to required type
    (_, file_extension) = output_file.rsplit('.', 1)
    print output_file
    try:
        t_opt = util.Extension2TOpt[file_extension.lower()]
        print t_opt
    except KeyError:
        raise RuntimeError("Can't handle plot outputfile type: %s" %
                           file_extension)

    print my_ps_file
    util.do_cmd('ps2raster %s -A -T%s' % (my_ps_file, t_opt))
    (my_output_file, _) = my_ps_file.rsplit('.', 1)
    my_output_file += '.' + file_extension
    print my_ps_file
    print my_output_file
    shutil.copyfile(my_output_file, output_file)
    print my_output_file
    

    # if it's required to show the graph ...
    # TODO: Experimental - leave?
    if show_graph:
        import sys
        if sys.platform == 'win32':
            os.startfile(my_output_file)
        else:
            import subprocess
            try:
                subprocess.Popen(['xdg-open', my_output_file])
            except OSError:
                print("Sorry, the 'xdg-open' application is required to "
                      "automatically display images.\nYou can see the image "
                      "in file %s." % output_file)

    # remove the temp directory
    shutil.rmtree(tmp_dir)
Example #3
0
def plot_gmt_map(extent, output_file, title=None, np_posn=None,
                 s_posn=None, annotate=[], show_graph=False):
    """A function to draw a GMT map, no data, just annotations.
    
    extent       the map extent (ll_lat, ll_lon, ur_lat, ur_lon)
    output_file  path to file that should be generated (*.png, *.eps, etc)
    title        string used to title the plot
    np_posn      position tuple or code string for north pointer placement,
                 one of:
                     'C'   - centre of plot
                     'NE'  - inside plot, northeast corner
                     'CE'  - inside plot, centre of east edge
                     'NNE' - outside plot, north of NE corner
                     'ENE' - outside plot, east of NE corner
                      etc (see the documentation for 'placement')
    s_posn       code string for scale placement
                     see examples for 'np_posn' above
    annotate     list of user annotations:
                     if None, no user or system annotations
                     if [],   only system annotations
                     else     system and user annotations
    show_graph   if True try to display final image in system-independant way

    Probably useful only for testing.

    """

    # unpack the extent
    (ll_lat, ll_lon, ur_lat, ur_lon) = extent
    r_opt = '-R%f/%f/%f/%f' % (ll_lon, ur_lon, ll_lat, ur_lat)

    # create a scratch directory for ephemeral files
    tmp_dir = tempfile.mkdtemp(prefix='plot_gmt_map_')

    # set up the GMT default values
    util.set_gmt_defaults(tmp_dir)

    # handle optional parameters
    if title is None:
        title = ''

    # think of a postscript filename
    my_ps_file = os.path.join(tmp_dir, 'data.ps')

    # draw the coast
    util.do_cmd('pscoast %s -K -JM%fc -Df -W -S192/216/255 >> %s'
                % (r_opt, cfg.MapWidthCentimetres, my_ps_file))

    # draw the rest of the map
    t_opt = ''
    if np_posn:
        t_opt = ugp.get_northpointer_placement(np_posn, extent)
    l_opt = ''
    if s_posn:
        l_opt = ugp.get_scale_placement(s_posn, extent)

    # do annotations
    if annotate is not None:
        j_opt = '-JM%fc' % cfg.MapWidthCentimetres
        ok_opt = '-K -O'
        jok_opt = '%s %s' % (j_opt, ok_opt)
        uga.generated_annotation(tmp_dir, my_ps_file, extent,
                                 cfg.MapWidthCentimetres, jok_opt)
        uga.user_annotation(tmp_dir, my_ps_file, extent, j_opt, ok_opt, annotate)

    util.do_cmd('psbasemap %s -K -O -JM%fc -Ba30m:".%s":WSen -Bg30m %s %s >> %s'
                % (r_opt, cfg.MapWidthCentimetres, title, t_opt, l_opt,
                   my_ps_file))

    cmd = ('psimage austgov-stacked.sun -O -W3.0/2.0 -C144.200/-38.800 '
           '-F1.0,255/0/0 >> %s' % my_ps_file)
    util.do_cmd(cmd)

    # convert PS to required type
    (_, file_extension) = output_file.rsplit('.', 1)
    try:
        t_opt = util.Extension2TOpt[file_extension.lower()]
    except KeyError:
        raise RuntimeError("Can't handle plot outputfile type: %s" %
                           file_extension)

    util.do_cmd('ps2raster %s -A -T%s' % (my_ps_file, t_opt))
    (my_output_file, _) = my_ps_file.rsplit('.', 1)
    my_output_file += '.' + file_extension
    shutil.copyfile(my_output_file, output_file)

    # if it's required to show the graph ...
    # TODO: Experimental - leave?
    if show_graph:
        import sys
        if sys.platform == 'win32':
            os.startfile(my_output_file)
        else:
            import subprocess
            try:
                subprocess.Popen(['xdg-open', my_output_file])
            except OSError:
                print("Sorry, the 'xdg-open' application is required to "
                      "automatically display images.\nYou can see the image "
                      "in file %s." % output_file)

    # remove the temp directory
    shutil.rmtree(tmp_dir)
def plot_gmt_xyz_image_contour(data, output_file, title=None, colourmap=None,
                               cb_label=None, cb_steps=None, annotate=[],
                               show_graph=False, bin_sum=False,
                               map_extent=None):
    """Plot XYZ data as a 'heatmap' and contours onto a GMT map.
    
    data         an iterable of values in xyz format (lon, lat, val)
                 (must be 'binned' XYZ data)
    output_file  path to file that should be generated (*.png, *.eps, etc)
    title        string used to title the plot
    colourmap    name of the colourmap to use, may be name of *.cpt file or 
                 internal GMT colourmap name
    cb_label    label on colourmap legend
    cb_steps     sequence of contour values (default is no contours drawn)
    annotate     list of user annotations:
                     if None, no user or system annotations
                     if [],   only system annotations
                     else     system and user annotations
                 (see documentation for form of user annotations)
    show_graph   if True try to display final image in system-independant way
    bin_sum      True if values in grid bin are to be summed (default: mean)
    map_extent   sets the extent of the displayed map if supplied
                 (get extent from data if not supplied)

    """

    # create a scratch directory for ephemeral files
    tmp_dir = tempfile.mkdtemp(prefix='plot_gmt_xyz_image_contour_')

    # use XYZ data bin numbers
    bins = util.get_xyz_bin_inc(data, 1.0e-4)
    if bins is None:
        raise RuntimeError("plot_gmt_xyz_image_contour(): "
                           "XYZ data has no implicit binning")
    (x_bins, y_bins) = bins
    x_inc = '%d+' % x_bins
    y_inc = '%d+' % y_bins

    # set up the default GMT environment
    util.set_gmt_defaults(tmp_dir)

    # handle optional parameters
    if title is None:
        title = ''

    if colourmap is None:
        colourmap = DefaultColourmap

    # get maximum/minimum values
    max_val = num.max(data[:,2])
    min_val = num.min(data[:,2])

    # get extent of data
    if map_extent:
        extent = map_extent
    else:
        extent = ugxe.get_extent(data, margin=0)
    (ll_lat, ll_lon, ur_lat, ur_lon) = extent
    r_opt = '-R%f/%f/%f/%f' % (ll_lon, ur_lon, ll_lat, ur_lat)

    # set the -J option for Mercator projection
    j_opt = '-JM%fc' % cfg.MapWidthCentimetres

    # write a GMT XYZ *file* (required by GMT)
    tmp_xyz = os.path.join(tmp_dir, 'data.xyz')
    num.savetxt(tmp_xyz, data)

    # create GRD file from XYZ data
    tmp_grd = os.path.join(tmp_dir, 'data.grd')
    a_opt = ''
    if bin_sum:
        a_opt = '-A'
    util.do_cmd('xyz2grd %s -G%s -I%s/%s -F %s %s'
                % (tmp_xyz, tmp_grd, x_inc, y_inc, r_opt, a_opt))

    # generate CPT file
    tmp_cpt = os.path.join(tmp_dir, 'data.cpt')
    if cb_steps is None:
        cb_steps = []
    if len(cb_steps) > 0:
        util.make_discrete_cpt_from_seq(tmp_cpt, cb_steps)
    else:
        (start, stop, step) = util.get_scale_min_max_step(max_val, min_val)
        cm = util.get_colourmap(c_map)
        util.do_cmd('makecpt -C%s.cpt -T%f/%f/%f > %s'
                    % (cm, start, stop, step, tmp_cpt))

    # think of a postscript filename for plot output
    tmp_ps = os.path.join(tmp_dir, 'data.ps')

    # draw image of gridded data
    util.do_cmd('grdimage %s -K -C%s %s -Q -Sc -Ei > %s'
                % (tmp_grd, tmp_cpt, j_opt, tmp_ps))

    # draw contours on the image
    if contours:
        # make a contour file
        tmp_cnt = os.path.join(tmp_dir, 'data.cnt')
        fd = open(tmp_cnt, 'w')
        for n in contours:
            fd.write('%f A\n' % n)
        fd.close()

        # draw the contours
        util.do_cmd('grdcontour %s -K -O -C%s %s %s >> %s'
                    % (tmp_grd, tmp_cnt, j_opt, r_opt, tmp_ps))

    # draw the coast
    util.do_cmd('pscoast %s -K -O %s -Df -W -S192/216/255 >> %s'
                % (r_opt, j_opt, tmp_ps))

    # draw the colorbar
    if cb_label:
        x_offset = cfg.MapWidthCentimetres + 0.5 
        util.do_cmd('psscale -K -O -E -C%s -D%.1fc/8.0c/9.0c/0.8c "-B:%s:" >> %s'
                    % (tmp_cpt, x_offset, cb_label, tmp_ps))

    # do annotations
    if annotate is not None:
        ok_opt = '-K -O'
        jok_opt = '%s %s' % (ok_opt, j_opt)
        uga.generated_annotation(tmp_dir, tmp_ps, extent,
                                 cfg.MapWidthCentimetres, jok_opt)
        uga.user_annotation(tmp_dir, tmp_ps, extent, j_opt, ok_opt, annotate)

    # draw the rest of the map
    util.do_cmd('psbasemap %s -O %s "-Ba30m:.%s:WSen" -Bg30m >> %s'
                % (r_opt, j_opt, title, tmp_ps))

    # convert PS to required type
    (_, file_extension) = output_file.rsplit('.', 1)
    try:
        t_opt = util.Extension2TOpt[file_extension.lower()]
    except KeyError:
        raise RuntimeError("Can't handle plot outputfile type: %s" %
                           file_extension)

    util.do_cmd('ps2raster %s -A -T%s' % (tmp_ps, t_opt))
    (tmp_output, _) = tmp_ps.rsplit('.', 1)
    tmp_output += '.' + file_extension
    shutil.copyfile(tmp_output, output_file)

    # if it's required to show the graph ...
    # TODO: Experimental - leave?
    if show_graph:
        import sys
        if sys.platform == 'win32':
            os.startfile(my_output_file)
        else:
            import subprocess
            try:
                subprocess.Popen(['xdg-open', my_output_file])
            except OSError:
                print("Sorry, the 'xdg-open' application is required to "
                      "automatically display images.\nYou can see the image "
                      "in file %s." % output_file)

    # remove the temp directory
    shutil.rmtree(tmp_dir)