def polygon_annotation(tmpdir, psfile, extent, j_opt, ok_opt, annotate): """Polygon annotation. tmpdir a temporary scratch directory psfile output postscript file extent extent of the map j_opt GMT -J option string (unused!) ok_opt GMT -O and -K option string annotate the annotation data: ('polygon', ((lon,lat),(lon',lat'),...), <kwargs>) By default the polygon will be closed. The optional <kwargs> will be a dictionary of extra parameters, such as line width, fill colour, etc. The dictionary will be optional. """ # set default polygon attributes linewidth = 1.0 linecolour = '0/0/0' fillcolour = None l_opt = '-L' # unpack the annotate command object try: (_, points, kwargs) = annotate except ValueError: (_, points) = annotate kwargs = {} # set polygon attributes from kwargs linewidth = kwargs.get('linewidth', linewidth) linecolour = kwargs.get('linecolour', linecolour) fillcolour = kwargs.get('fillcolour', fillcolour) if kwargs.get('noclose', False): l_opt = '' # unpack extent, get -R option (ll_lat, ll_lon, ur_lat, ur_lon) = extent r_opt = '-R%f/%f/%f/%f' % (ll_lon, ur_lon, ll_lat, ur_lat) # file we fill with annotation text txt_file = os.path.join(tmpdir, 'polygon.txt') # write polygon data to the text file fd = open(txt_file, 'w') for (lon, lat) in points: fd.write('%f %f\n' % (lon, lat)) fd.close() # do GMT command w_opt = '-W%.2f,%s' % (linewidth, linecolour) g_opt = '' if fillcolour: g_opt = '-G%s' % fillcolour cmd = ('psxy %s %s %s %s %s %s %s >> %s' % (txt_file, j_opt, r_opt, ok_opt, l_opt, w_opt, g_opt, psfile)) util.do_cmd(cmd)
def generated_annotation(tmpdir, psfile, extent, mapsize, jok_opt): """ Place 'generated at ...' annotation on a GMT graph. tmpdir is the path to a temporary scratch directory psfile path to the POSTSCRIPT file to write to extent the map extent () mapsize notional mapsize (cm) jok_opt the GMT -J, -O and -K options string """ # file we fill with annotation text txt_file = os.path.join(tmpdir, 'data.txt') # get string we are going to use ann_str = time.strftime('@:4:Generated at %H:%M:%S on %d %b %Y') # figure out where we are going to put the annotation # -1.0c from origin, -1.0c down (ll_lat, ll_lon, ur_lat, ur_lon) = extent vert_deg = (ur_lat - ll_lat) offset_lon = ll_lon - (vert_deg / mapsize) * 2.0 offset_lat = ll_lat - (vert_deg / mapsize) * 2.0 r_opt = '-R%f/%f/%f/%f' % (ll_lon, ur_lon, ll_lat, ur_lat) # create the annotation text file ann_hdr = '%f %f 6 0 0 bl ' % (offset_lon, offset_lat) fd = open(txt_file, 'w') fd.write(ann_hdr) fd.write(ann_str) fd.close() # annotate the plot cmd = 'pstext %s %s %s -N >> %s' % (txt_file, r_opt, jok_opt, psfile) util.do_cmd(cmd)
def generated_annotation(tmpdir, psfile, extent, mapsize, jok_opt): """ Place 'generated at ...' annotation on a GMT graph. tmpdir is the path to a temporary scratch directory psfile path to the POSTSCRIPT file to write to extent the map extent () mapsize notional mapsize (cm) jok_opt the GMT -J, -O and -K options string """ # file we fill with annotation text txt_file = os.path.join(tmpdir, 'data.txt') # get string we are going to use ann_str = time.strftime('@:4:Generated at %H:%M:%S on %d %b %Y') # figure out where we are going to put the annotation # -1.0c from origin, -1.0c down (ll_lat, ll_lon, ur_lat, ur_lon) = extent vert_deg = (ur_lat - ll_lat) offset_lon = ll_lon - (vert_deg/mapsize)*2.0 offset_lat = ll_lat - (vert_deg/mapsize)*2.0 r_opt = '-R%f/%f/%f/%f' % (ll_lon, ur_lon, ll_lat, ur_lat) # create the annotation text file ann_hdr = '%f %f 6 0 0 bl ' % (offset_lon, offset_lat) fd = open(txt_file, 'w') fd.write(ann_hdr) fd.write(ann_str) fd.close() # annotate the plot cmd = 'pstext %s %s %s -N >> %s' % (txt_file, r_opt, jok_opt, psfile) util.do_cmd(cmd)
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)
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 text_annotation(tmpdir, psfile, extent, j_opt, ok_opt, annotate): """ Place user text annotation on a GMT graph. tmpdir is the path to a temporary scratch directory psfile path to the POSTSCRIPT file to write to extent extent of the map j_opt the GMT J option string ok_opt the GMT O and K option string annotate user annotations a list of tuples like (type, (lon, lat), str) or (type, (lon, lat), str, dict) where lon, lat are lon/lat coordinate values str is the text string to write dict is a set of args controlling the text write The 'dict' annotate parameter has the form: {'fontsize': <fontsize float>, 'colour': <colour>, } The user annotation string may contain any of the '@' escape sequences described at: http://gmt.soest.hawaii.edu/gmt/doc/gmt/html/man/pstext.html#DESCRIPTION """ # file we fill with annotation text txt_file = os.path.join(tmpdir, 'data.txt') # unpack the annotation sequence if len(annotate) == 3: (_, point, annstr) = annotate anndict = {} elif len(annotate) == 4: (_, point, annstr, anndict) = annotate else: # handle overflow (_, point, annstr, anndict, _) = annotate (x, y) = point # set defaults, if required fontsize = anndict.get('fontsize', DefaultFontSize) fontno = anndict.get('fontno', DefaultFontNo) colour = anndict.get('colour', DefaultColour) angle = anndict.get('angle', DefaultAngle) justify = anndict.get('justify', DefaultJustify) linespace = anndict.get('linespace', DefaultLinespace) parwidth = anndict.get('parwidth', DefaultParWidth) parjust = anndict.get('parjust', DefaultParJust) # unpack 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 the annotation text file ann_hdr = ('%f %f %s %s %s %s %s' % (x, y, fontsize, angle, fontno, justify, colour)) fd = open(txt_file, 'w') fd.write(ann_hdr) fd.write(annstr) fd.close() # annotate the plot cmd = ('pstext %s %s %s %s -N >> %s' % (txt_file, r_opt, j_opt, ok_opt, psfile)) util.do_cmd(cmd)
def image_annotation(tmpdir, psfile, extent, j_opt, ok_opt, annotate): """Image annotation. tmpdir a temporary scratch directory psfile output postscript file extent extent of the map j_opt GMT -J option string ok_opt GMT -O and -K option string annotate the annotation data: ('image', <posn>, <imagefile>, <kwargs>) where <posn> is a position specifier (string or lon,lat tuple) <imagefile> is the path to the EPS image file <kwargs> is a dictionary of extra args (optional) """ # set default polygon attributes framewidth = 1.0 framecolour = '0/0/0' imagewidth = 4.0 imageheight = 3.0 # unpack the annotate command object try: (_, posn, imagefile, kwargs) = annotate except ValueError: (_, posn, imagefile) = annotate kwargs = {} # if given image height but not width, calculate width assuming # aspect ratio of default image width and height, etc if (kwargs.get('imagewidth', None) is None and kwargs.get('imageheight', None) is not None): kwargs['imagewidth'] = kwargs['imageheight'] * imagewidth / imageheight elif (kwargs.get('imageheight', None) is None and kwargs.get('imagewidth', None) is not None): kwargs['imageheight'] = kwargs['imagewidth'] * imageheight / imagewidth # override default polygon attributes from kwargs framewidth = float(kwargs.get('framewidth', framewidth)) framecolour = kwargs.get('framecolour', framecolour) imagewidth = float(kwargs.get('imagewidth', imagewidth)) imageheight = float(kwargs.get('imageheight', imageheight)) want_frame = not kwargs.get('noframe', False) # set X and Y margins margin_x = 1.0 margin_y = 1.0 # unpack the extent and get map size in cm (ll_lat, ll_lon, ur_lat, ur_lon) = extent (map_width, map_height) = util.get_coords_cm(ur_lon, ur_lat, extent, j_opt) # figure out image placement, convert to cm if isinstance(posn, basestring): lower_posn = posn.lower() try: eval_str = ImagePlacement[lower_posn] except KeyError: raise RuntimeError("Placement string '%s' is not recognised" % posn) else: # we have a (lon, lat) tuple # TODO: implement! pass # execute the posn eval_str - gives us x, y and justify exec(eval_str) # set -W option - final image size w_opt = '-W%.2f/%.2f' % (imagewidth, imageheight) # do -C option as well as the frame, if required c_opt = '-C%.3f/%.3f/%s' % (x, y, justify) if want_frame: f_opt = '-F%.1f,%s' % (framewidth, framecolour) # now put image into PS file cmd = ('psimage %s %s %s %s %s >> %s' % (imagefile, ok_opt, w_opt, c_opt, f_opt, psfile)) util.do_cmd(cmd)
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)
def image_annotation(tmpdir, psfile, extent, j_opt, ok_opt, annotate): """Image annotation. tmpdir a temporary scratch directory psfile output postscript file extent extent of the map j_opt GMT -J option string ok_opt GMT -O and -K option string annotate the annotation data: ('image', <posn>, <imagefile>, <kwargs>) where <posn> is a position specifier (string or lon,lat tuple) <imagefile> is the path to the EPS image file <kwargs> is a dictionary of extra args (optional) """ # set default polygon attributes framewidth = 1.0 framecolour = '0/0/0' imagewidth = 4.0 imageheight = 3.0 # unpack the annotate command object try: (_, posn, imagefile, kwargs) = annotate except ValueError: (_, posn, imagefile) = annotate kwargs = {} # if given image height but not width, calculate width assuming # aspect ratio of default image width and height, etc if (kwargs.get('imagewidth', None) is None and kwargs.get('imageheight', None) is not None): kwargs['imagewidth'] = kwargs['imageheight']*imagewidth/imageheight elif (kwargs.get('imageheight', None) is None and kwargs.get('imagewidth', None) is not None): kwargs['imageheight'] = kwargs['imagewidth']*imageheight/imagewidth # override default polygon attributes from kwargs framewidth = float(kwargs.get('framewidth', framewidth)) framecolour = kwargs.get('framecolour', framecolour) imagewidth = float(kwargs.get('imagewidth', imagewidth)) imageheight = float(kwargs.get('imageheight', imageheight)) want_frame = not kwargs.get('noframe', False) # set X and Y margins margin_x = 1.0 margin_y = 1.0 # unpack the extent and get map size in cm (ll_lat, ll_lon, ur_lat, ur_lon) = extent (map_width, map_height) = util.get_coords_cm(ur_lon, ur_lat, extent, j_opt) # figure out image placement, convert to cm if isinstance(posn, basestring): lower_posn = posn.lower() try: eval_str = ImagePlacement[lower_posn] except KeyError: raise RuntimeError("Placement string '%s' is not recognised" % posn) else: # we have a (lon, lat) tuple # TODO: implement! pass # execute the posn eval_str - gives us x, y and justify exec(eval_str) # set -W option - final image size w_opt = '-W%.2f/%.2f' % (imagewidth, imageheight) # do -C option as well as the frame, if required c_opt = '-C%.3f/%.3f/%s' % (x, y, justify) if want_frame: f_opt = '-F%.1f,%s' % (framewidth, framecolour) # now put image into PS file cmd = ('psimage %s %s %s %s %s >> %s' % (imagefile, ok_opt, w_opt, c_opt, f_opt, psfile)) util.do_cmd(cmd)
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)