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 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_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)