parser = argparse.ArgumentParser( description='Convert a RAD file to SEG-Y.') parser.add_argument( 'filename', metavar='RAD file', type=str, nargs='*', default='./*.RA[D,1,2]', help= 'The path to one or more RAD files. Uses Unix-style pathname expansion. Omit to find all RAD files in current directory.' ) parser.add_argument( '-o', '--out', metavar='output file', type=str, nargs='?', default='', help= 'The path to an output file. Default: same as input file, but with the nominal frequency and sgy file extension.' ) args = parser.parse_args() Notice.header("This is rad2segy. Stand back.") for f in args.filename: Notice.info("{}".format(f), hold=True) Notice.ok(" >>> ", hold=True) outfile = rad2segy(f, args.out) Notice.info(outfile) Notice.header("Done")
def main(target, cfg): """ Puts everything together. """ t0 = time.time() ##################################################################### # # READ SEGY # ##################################################################### if cfg['segy_library'].lower() == 'obspy': s = Seismic.from_segy_with_obspy(target, params={'ndim': cfg['ndim']}) else: s = Seismic.from_segy(target, params={'ndim': cfg['ndim']}) # Set the line and/or xline number. try: n, xl = cfg['number'] except: n, xl = cfg['number'], 0.5 # Set the direction. if (s.ndim) == 2: direction = ['inline'] elif cfg['direction'].lower()[0] == 'i': direction = ['inline'] elif cfg['direction'].lower()[0] in ['x', 'c']: # Allow 'crossline' too. direction = ['xline'] elif cfg['direction'].lower()[0] == 't': direction = ['tslice'] else: direction = ['xline', 'inline'] # Get the data. try: ss = [ Seismic.from_seismic(s, n=n, direction=d) for n, d in zip((n, xl), direction) ] except IndexError: # Perhaps misinterpreted 2D as 3D s = Seismic.from_segy(target, params={'ndim': 2}) direction = ['inline'] ss = [ Seismic.from_seismic(s, n=n, direction=d) for n, d in zip((n, xl), direction) ] clip_val = np.percentile(s.data, cfg['percentile']) if clip_val < 10: fstr = '{:.3f}' elif clip_val < 100: fstr = '{:.2f}' elif clip_val < 1000: fstr = '{:.1f}' else: fstr = '{:.0f}' # Notify user of parameters. Notice.info("n_traces {}".format(s.ntraces)) Notice.info("n_samples {}".format(s.nsamples)) Notice.info("dt {}".format(s.dt)) Notice.info("t_start {}".format(s.tstart)) Notice.info("t_end {}".format(s.tend)) Notice.info("max_val " + fstr.format(np.amax(s.data))) Notice.info("min_val " + fstr.format(np.amin(s.data))) Notice.info("clip_val " + fstr.format(clip_val)) t1 = time.time() Notice.ok("Read data in {:.1f} s".format(t1 - t0)) ##################################################################### # # MAKE PLOT # ##################################################################### Notice.hr_header("Plotting") # Plot size parameters. wsl = 6 # Width of sidelabel, inches mih = 11 # Minimum plot height, inches fhh = 5 # File header box height, inches m = 0.75 # basic unit of margins, inches # Margins, CSS like: top, right, bottom, left. mt, mr, mb, ml = m, 1.5 * m, m, 1.5 * m mm = 2 * m # padded margin between seismic and label # Determine plot dimensions. Kind of laborious and repetitive (see below). if cfg['plot_width']: seismic_width = cfg['plot_width'] - wsl - mm - ml - mr tpi = max([s.ntraces for s in ss]) / seismic_width else: tpi = cfg['tpi'] if cfg['plot_height']: seismic_height = max( mih, cfg['plot_height']) - mb - 0.75 * (len(ss) - 1) - mt seismic_height_raw = seismic_height / len(ss) ips = seismic_height_raw / (s.tbasis[-1] - s.tbasis[0]) else: ips = cfg['ips'] # Width is determined by seismic width, plus sidelabel, plus margins. # Height is given by ips, but with a minimum of mih inches. if 'tslice' in direction: seismic_width = [s.ntraces / tpi for s in ss] seismic_height_raw = max([s.nxlines for s in ss]) / tpi else: seismic_width = [s.ntraces / tpi for s in ss] seismic_height_raw = ips * (s.tbasis[-1] - s.tbasis[0]) w = ml + max(seismic_width) + mm + wsl + mr # inches seismic_height = len(ss) * seismic_height_raw h_reqd = mb + seismic_height + 0.75 * (len(ss) - 1) + mt # inches h = max(mih, h_reqd) # Calculate where to start sidelabel and seismic data. # Depends on whether sidelabel is on the left or right. if cfg['sidelabel'] == 'right': ssl = (ml + max(seismic_width) + mm) / w # Start of side label (ratio) seismic_left = ml / w else: ssl = ml / w seismic_left = (ml + wsl + mm) / w adj = max(0, h - h_reqd) / 2 seismic_bottom = (mb / h) + adj / h seismic_width_fraction = [sw / w for sw in seismic_width] seismic_height_fraction = seismic_height_raw / h # Publish some notices so user knows plot size. Notice.info("plot width {:.2f} in".format(w)) Notice.info("plot height {:.2f} in".format(h)) # Make the figure. fig = plt.figure(figsize=(w, h), facecolor='w') # Set the tickformat. tickfmt = mtick.FormatStrFormatter('%.0f') # Could definitely do better for default fontsize than 10. # Ideally would be adaptive to plot size. cfg['fontsize'] = cfg['fontsize'] or 10 # Plot title. if cfg['title']: # Deal with Windows paths: \1 gets interpreted as a group by regex. newt = re.sub(r'\\', '@@@@@', target) temp = re.sub(r'_filename', newt, cfg['title']) title = re.sub(r'@@@', r'\\', temp) title_ax = fig.add_axes([ssl, 1 - mt / h, wsl / w, mt / h]) title_ax = plotter.plot_title(title_ax, title, fs=1.4 * cfg['fontsize'], cfg=cfg) # Plot title. if cfg['subtitle']: date = str(datetime.date.today()) subtitle = re.sub(r'_date', date, cfg['subtitle']) subtitle_ax = fig.add_axes([ssl, 1 - mt / h, wsl / w, mt / h], label='subtitle') title_ax = plotter.plot_subtitle(subtitle_ax, subtitle, fs=0.75 * cfg['fontsize'], cfg=cfg) # Plot text header. start = (h - 1.5 * mt - fhh) / h head_ax = fig.add_axes([ssl, start, wsl / w, fhh / h]) head_ax = plotter.plot_header(head_ax, s.header, fs=9, cfg=cfg, version=__version__) # Plot histogram. # Params for histogram plot. pady = 0.75 / h # 0.75 inch padx = 0.75 / w # 0.75 inch cstrip = 0.3 / h # color_strip height = 0.3 in charth = 1.25 / h # height of charts = 1.25 in chartw = wsl / w - mr / w - padx # or ml/w for left-hand sidelabel; same thing chartx = (ssl + padx) histy = 1.5 * mb / h + charth + pady # Plot colourbar under histogram. clrbar_ax = fig.add_axes([chartx, histy - cstrip, chartw, cstrip]) clrbar_ax = plotter.plot_colourbar(clrbar_ax, cmap=cfg['cmap']) # Plot histogram itself. hist_ax = fig.add_axes([chartx, histy, chartw, charth]) hist_ax = plotter.plot_histogram(hist_ax, s.data, tickfmt, cfg) # Plot spectrum. specy = 1.5 * mb / h spec_ax = fig.add_axes([chartx, specy, chartw, charth]) try: colour = utils.rgb_to_hex(cfg['highlight_colour']) spec_ax = s.plot_spectrum( ax=spec_ax, tickfmt=tickfmt, ntraces=20, fontsize=cfg['fontsize'], colour=colour, ) except: pass # No spectrum, oh well. for i, line in enumerate(ss): # Add the seismic axis. ax = fig.add_axes([ seismic_left, seismic_bottom + i * seismic_height_fraction + i * pady, seismic_width_fraction[i], seismic_height_fraction ]) # Plot seismic data. if cfg['display'].lower() in ['vd', 'varden', 'variable', 'both']: im = ax.imshow(line.data.T, cmap=cfg['cmap'], clim=[-clip_val, clip_val], extent=[ line.olineidx[0], line.olineidx[-1], 1000 * line.tbasis[-1], line.tbasis[0] ], aspect='auto', interpolation=cfg['interpolation']) if np.argmin(seismic_width) == i: cax = utils.add_subplot_axes(ax, [1.01, 0.02, 0.01, 0.2]) _ = plt.colorbar(im, cax=cax) if cfg['display'].lower() in ['wiggle', 'both']: ax = line.wiggle_plot( cfg['number'], direction, ax=ax, skip=cfg['skip'], gain=cfg['gain'], rgb=cfg['colour'], alpha=cfg['opacity'], lw=cfg['lineweight'], ) valid = ['vd', 'varden', 'variable', 'wiggle', 'both'] if cfg['display'].lower() not in valid: Notice.fail("You must specify the display: wiggle, vd, both.") return # Seismic axis annotations. ax.set_ylabel(utils.LABELS[line.ylabel], fontsize=cfg['fontsize']) ax.set_xlabel(utils.LABELS[line.xlabel], fontsize=cfg['fontsize'], ha='center') ax.tick_params(axis='both', labelsize=cfg['fontsize'] - 2) ax.xaxis.set_major_formatter(tickfmt) ax.yaxis.set_major_formatter(tickfmt) if ('tslice' not in direction): ax.set_ylim(1000 * cfg['trange'][1] or 1000 * line.tbasis[-1], 1000 * cfg['trange'][0]) # Crossing point. Will only work for non-arb lines. try: ax.axvline(ss[i - 1].slineidx[0], c=utils.rgb_to_hex(cfg['highlight_colour']), alpha=0.5) except IndexError: pass # Nevermind. # Grid, optional. if cfg['grid_time'] or cfg['grid_traces']: ax.grid() for l in ax.get_xgridlines(): l.set_color(utils.rgb_to_hex(cfg['grid_colour'])) l.set_linestyle('-') if cfg['grid_traces']: l.set_linewidth(1) else: l.set_linewidth(0) l.set_alpha(min(1, cfg['grid_alpha'])) for l in ax.get_ygridlines(): l.set_color(utils.rgb_to_hex(cfg['grid_colour'])) l.set_linestyle('-') if cfg['grid_time']: if 'tslice' in direction: l.set_linewidth(1) else: l.set_linewidth(1.4) else: l.set_linewidth(0) l.set_alpha(min(1, 2 * cfg['grid_alpha'])) # Watermark. if cfg['watermark_text']: ax = plotter.watermark_seismic(ax, cfg) # Make parasitic (top) axis for labeling CDP number. if (s.data.ndim > 2) and ('tslice' not in direction): ylim = ax.get_ylim() par1 = ax.twiny() par1.spines["top"].set_position(("axes", 1.0)) par1.plot(line.slineidx, np.zeros_like(line.slineidx), alpha=0) par1.set_xlabel(utils.LABELS[line.slabel], fontsize=cfg['fontsize']) par1.set_ylim(ylim) # Adjust ticks tx = par1.get_xticks() newtx = [ line.slineidx[len(line.slineidx) * (i // len(tx))] for i, _ in enumerate(tx) ] par1.set_xticklabels(newtx, fontsize=cfg['fontsize'] - 2) t2 = time.time() Notice.ok("Built plot in {:.1f} s".format(t2 - t1)) ##################################################################### # # SAVE FILE # ##################################################################### Notice.hr_header("Saving") dname, fname, ext = utils.path_bits(target) outfile = cfg['outfile'] or '' if not os.path.splitext(outfile)[1]: outfile = os.path.join(cfg['outfile'] or dname, fname + '.png') fig.savefig(outfile) t3 = time.time() Notice.info("output file {}".format(outfile)) Notice.ok("Saved output in {:.1f} s".format(t3 - t2)) if cfg['stain_paper'] or cfg['coffee_rings'] or cfg['distort'] or cfg[ 'scribble']: fname = os.path.splitext(outfile)[0] + ".stupid.png" fig.savefig(fname) else: return ##################################################################### # # SAVE STUPID FILE # ##################################################################### Notice.hr_header("Applying the stupidity") stupid_image = Image.open(fname) if cfg['stain_paper']: utils.stain_paper(stupid_image) utils.add_rings(stupid_image, cfg['coffee_rings']) if cfg['scribble']: utils.add_scribble(stupid_image) # Trick to remove remaining semi-transparent pixels. result = Image.new("RGB", stupid_image.size, (255, 255, 255)) result.paste(stupid_image) result.save(fname) t4 = time.time() Notice.info("output file {}".format(fname)) Notice.ok("Saved stupidity in {:.1f} s".format(t4 - t3)) return
def main(target, cfg): """ Puts everything together. """ t0 = time.time() # Read the file. section = readSEGY(target, unpack_headers=True) # Calculate some things. # NB Getting nsamples and dt from the first trace assumes that all # traces are the same length, which is not a safe assumption in SEGY v2. ninlines = section.traces[-1].header.trace_sequence_number_within_line last_tr = section.traces[-1].header.trace_sequence_number_within_segy_file nxlines = last_tr / ninlines nsamples = section.traces[0].header.number_of_samples_in_this_trace dt = section.traces[0].header.sample_interval_in_ms_for_this_trace ntraces = len(section.traces) tbase = 0.001 * np.arange(0, nsamples * dt, dt) tstart = 0 tend = np.amax(tbase) # Make the data array. data = np.vstack([t.data for t in section.traces]).T threed = False if nxlines > 1: # Then it's a 3D and `data` is an ensemble. threed = True cube = np.reshape(data.T, (ninlines, nxlines, nsamples)) l = cfg['number'] if cfg['direction'].lower()[0] == 'i': direction = 'inline' ntraces = nxlines l *= ninlines if (l < 1) else 1 data = cube[l, :, :].T else: direction = 'xline' ntraces = ninlines l *= nxlines if (l < 1) else 1 data = cube[:, l, :].T # Collect some other data. Use a for loop because there are several. elev, esp, ens, tsq = [], [], [], [] for i, trace in enumerate(section.traces): elev.append(trace.header.receiver_group_elevation) esp.append(trace.header.energy_source_point_number) tsq.append(trace.header.trace_sequence_number_within_line) if threed: trs = [] if direction == 'inline': cdp_label_text = 'Crossline number' trace_label_text = 'Trace number' ens.append(trace.header.for_3d_poststack_data_this_field_is_for_cross_line_number) trs.append(trace.header.for_3d_poststack_data_this_field_is_for_in_line_number) else: cdp_label_text = 'Inline number' trace_label_text = 'Trace number' ens.append(trace.header.for_3d_poststack_data_this_field_is_for_in_line_number) trs.append(trace.header.for_3d_poststack_data_this_field_is_for_cross_line_number) line_no = min(trs) else: cdp_label_text = 'CDP number' trace_label_text = 'Trace number' ens.append(trace.header.ensemble_number) min_tr, max_tr = 0, ntraces traces = (min_tr, max_tr) clip_val = np.percentile(data, cfg['percentile']) # Notify user of parameters Notice.info("n_traces {}".format(ntraces)) Notice.info("n_samples {}".format(nsamples)) Notice.info("dt {}".format(dt)) Notice.info("t_start {}".format(tstart)) Notice.info("t_end {}".format(tend)) Notice.info("max_val {}".format(np.amax(data))) Notice.info("min_val {}".format(np.amin(data))) Notice.info("clip_val {}".format(clip_val)) t1 = time.time() Notice.ok("Read data in {:.1f} s".format(t1-t0)) ##################################################################### # # MAKE PLOT # ##################################################################### Notice.hr_header("Plotting") ################################## # Plot size parameters # Some constants fs = cfg['fontsize'] wsl = 6 # Width of sidelabel, inches mih = 12 # Minimum plot height, inches fhh = 5 # File header box height, inches m = 0.5 # basic unit of margins, inches # Margins, CSS like: top, right, bottom, left. mt, mr, mb, ml = m, 2 * m, m, 2 * m mm = m # padded margin between seismic and label # Width is determined by seismic width, plus sidelabel, plus margins. seismic_width = ntraces / cfg['tpi'] w = ml + seismic_width + mm + wsl + mr # inches # Height is given by ips, but with a minimum of mih inches seismic_height = cfg['ips'] * (tbase[-1] - tbase[0]) / 1000 h_reqd = mb + seismic_height + mt # inches h = max(mih, h_reqd) # Calculate where to start sidelabel and seismic data. # Depends on whether sidelabel is on the left or right. if cfg['sidelabel'] == 'right': ssl = (ml + seismic_width + mm) / w # Start of side label (ratio) seismic_left = ml / w else: ssl = ml / w seismic_left = (ml + wsl + mm) / w adj = max(0, h - h_reqd) / 2 seismic_bottom = (mb / h) + adj / h seismic_width_fraction = seismic_width / w seismic_height_fraction = seismic_height / h # Publish some notices so user knows plot size. Notice.info("Width of plot {} in".format(w)) Notice.info("Height of plot {} in".format(h)) ################################## # Make the figure. fig = plt.figure(figsize=(w, h), facecolor='w') # Add the main seismic axis. ax = fig.add_axes([seismic_left, seismic_bottom, seismic_width_fraction, seismic_height_fraction ]) # make parasitic axes for labeling CDP number par1 = ax.twiny() par1.spines["top"].set_position(("axes", 1.0)) tickfmt = mtick.FormatStrFormatter('%.0f') par1.plot(ens, np.zeros_like(ens)) par1.set_xlabel(cdp_label_text, fontsize=fs-2) par1.set_xticklabels(par1.get_xticks(), fontsize=fs-2) par1.xaxis.set_major_formatter(tickfmt) # Plot title title_ax = fig.add_axes([ssl, 1-mt/h, wsl/w, mt/(h)]) title_ax = plotter.plot_title(title_ax, target, fs=1.5*fs, cfg=cfg) if threed: title_ax.text(0.0, 0.0, '{} {}'.format(direction.title(), line_no)) # Plot text header. s = section.textual_file_header.decode() start = (h - 1.5*mt - fhh) / h head_ax = fig.add_axes([ssl, start, wsl/w, fhh/h]) head_ax = plotter.plot_header(head_ax, s, fs=fs-1, cfg=cfg) # Plot histogram. # Params for histogram plot pady = 0.75 / h # 0.75 inch padx = 0.75 / w # 0.75 inch cstrip = 0.3/h # color_strip height = 0.3 in charth = 1.5/h # height of charts = 1.5 in chartw = wsl/w - mr/w - padx # or ml/w for left-hand sidelabel; same thing chartx = (ssl + padx) histy = 1.5 * mb/h + charth + pady # Plot colourbar under histogram clrbar_ax = fig.add_axes([chartx, histy - cstrip, chartw, cstrip]) clrbar_ax = plotter.plot_colourbar(clrbar_ax, cmap=cfg['cmap']) # Plot histogram itself hist_ax = fig.add_axes([chartx, histy, chartw, charth]) hist_ax = plotter.plot_histogram(hist_ax, data, tickfmt, percentile=cfg['percentile'], fs=fs) # Plot spectrum. specy = 1.5 * mb/h spec_ax = fig.add_axes([chartx, specy, chartw, charth]) try: spec_ax = plotter.plot_spectrum(spec_ax, data, dt, tickfmt, ntraces=20, fontsize=fs) except: pass # Plot seismic data. if cfg['display'].lower() in ['vd', 'varden', 'variable']: _ = ax.imshow(data, cmap=cfg['cmap'], clim=[-clip_val, clip_val], extent=[0, ntraces, tbase[-1], tbase[0]], aspect='auto' ) elif cfg['display'].lower() == 'wiggle': ax = plotter.wiggle_plot(ax, data, tbase, ntraces, skip=cfg['skip'], gain=cfg['gain'], rgb=cfg['colour'], alpha=cfg['opacity'], lw=cfg['lineweight'] ) ax.set_ylim(ax.get_ylim()[::-1]) elif cfg['display'].lower() == 'both': # variable density goes on first _ = ax.imshow(data, cmap=cfg['cmap'], clim=[-clip_val, clip_val], extent=[0, ntraces, tbase[-1], tbase[0]], aspect='auto' ) # wiggle plots go on top ax = plotter.wiggle_plot(ax, data, tbase, ntraces, skip=cfg['skip'], gain=cfg['gain'], rgb=cfg['colour'], alpha=cfg['opacity'], lw=cfg['lineweight'] ) # ax.set_ylim(ax.get_ylim()[::-1]) else: Notice.fail("You need to specify the type of display: wiggle or vd") # Seismic axis annotations. ax = plotter.decorate_seismic(ax, traces, trace_label_text, tickfmt, cfg) # Watermark. if cfg['watermark_text']: Notice.info("Adding watermark") ax = plotter.watermark_seismic(ax, cfg) t2 = time.time() Notice.ok("Built plot in {:.1f} s".format(t2-t1)) ##################################################################### # # SAVE FILE # ##################################################################### Notice.hr_header("Saving") if cfg['stain_paper'] or cfg['coffee_rings'] or cfg['distort'] or cfg['scribble']: stupid = True else: stupid = False s = "Saved image file {} in {:.1f} s" if cfg['outfile']: if os.path.isfile(cfg['outfile']): outfile = cfg['outfile'] else: # is directory stem, ext = os.path.splitext(os.path.split(target)[1]) outfile = os.path.join(cfg['outfile'], stem + '.png') stem, _ = os.path.splitext(outfile) # Needed for stupidity. fig.savefig(outfile) t3 = time.time() Notice.ok(s.format(outfile, t3-t2)) else: # Do the default: save a PNG in the same dir as the target. stem, _ = os.path.splitext(target) fig.savefig(stem) t3 = time.time() Notice.ok(s.format(stem+'.png', t3-t2)) if stupid: fig.savefig(stem + ".stupid.png") else: return ##################################################################### # # SAVE STUPID FILE # ##################################################################### Notice.hr_header("Applying the stupidity") stupid_image = Image.open(stem + ".stupid.png") if cfg['stain_paper']: utils.stain_paper(stupid_image) utils.add_rings(stupid_image, cfg['coffee_rings']) if cfg['scribble']: utils.add_scribble(stupid_image) stupid_image.save(stem + ".stupid.png") s = "Saved stupid file stupid.png in {:.1f} s" t4 = time.time() Notice.ok(s.format(t4-t3)) return
def plot(tc): """ Constructs a multi-subplot matplotlib figure. Args: transect (TransectContainer): A transect container. """ h = 15 mw = 16 # width of main section (inches) must be div by 4 fw = 5 # width of the feature plot (inches) must be div by 5 n_grids = len(tc.potfield.data) # We will save the same figure we make, to ensure the saved figure # has everything in the right places. For example, see this discussion: # http://stackoverflow.com/questions/7906365/ save_dpi = getattr(tc, 'save_dpi', tc.settings.get('default_dpi')) dpi = save_dpi or 80 fig = plt.figure(figsize=(mw + fw + 1, 15), facecolor='w', edgecolor='k', dpi=dpi, frameon=True) gs = gridspec.GridSpec(h, mw + fw + 1) # Left-hand column. header = fig.add_subplot(gs[0:1, 0:mw / 2]) description = fig.add_subplot(gs[1:3, 0:mw / 2]) locmap = fig.add_subplot(gs[0:3, mw / 2:mw]) # Aspect = 8:3 elevation = fig.add_subplot(gs[3, :mw]) xsection = fig.add_subplot(gs[4:h - n_grids, :mw]) xsec_logs = fig.add_subplot(gs[4:h - n_grids, :mw]) potfield = fig.add_subplot(gs[h - n_grids:, :mw]) # Right-hand column. log_header = fig.add_subplot(gs[0:1, -1 * fw:]) # log_plot is dealt with by passing gs to feature_plot.plot_feature_well() # Adjust white space between subplots # ------------------------------------------------------------ # left = 0.05 # left side of the subplots of the figure right = 0.95 # right side of the subplots of the figure bottom = 0.05 # bottom of the subplots of the figure top = 0.95 # top of the subplots of the figure wspace = 0.05 # blank w space between subplots hspace = 0.1 # blank h space between subplots fig.subplots_adjust(left, bottom, right, top, wspace, hspace) bbox = {'fc': 'w', 'pad': 0, 'ec': 'none', 'alpha': 0.5} props = {'ha': 'left', 'va': 'center', 'bbox': bbox} # Header # ---------------------------------------------------------# print "Header" header.axis("off") dy = 0.2 header.text(0.0, 0.5 + dy, tc.title, props, fontsize=30, horizontalalignment='left', verticalalignment='bottom') # horizontal line header.axhline(y=0.5, xmin=0, xmax=1.25, linewidth=1.5, color='k') # Subtitle header.text(1.0, 0.5 + dy, (tc.subtitle), fontsize=15, horizontalalignment='right', verticalalignment='bottom', weight='bold') descr = tc.description if tc.meta: description.text(0, 1.0, tc.domain.upper(), horizontalalignment='left', verticalalignment='bottom', fontsize=14) description.text(1.0, 1.0, tc.velocity, horizontalalignment='right', verticalalignment='bottom', fontsize=12) descr_pos = 0.8 # Where to position the rest. else: descr_pos = 1.0 # Paragraph description # -----------------------------------------------------# description.text(0, descr_pos, descr, horizontalalignment='left', verticalalignment='top', fontsize=12, family='serif') description.axis('off') # Wrap text fig.canvas.mpl_connect('draw_event', lambda event: on_draw(event, description)) # Map # ---------------------------------------------------------# print "Locmap" tx, ty = tc.data.coords.xy res = 'h' # c, l, i, h, f # Generate Basemap object. m = Basemap(projection='tmerc', lon_0=tc.locmap.mid.x, lat_0=tc.locmap.mid.y, resolution=res, llcrnrlon=tc.locmap.ll.x, llcrnrlat=tc.locmap.ll.y, urcrnrlon=tc.locmap.ur.x, urcrnrlat=tc.locmap.ur.y, ax=locmap) # Finish drawing the basemap. draw_basemap(m, tc) for layer, details in tc.locmap.layers.items(): data = getattr(tc.locmap, layer) for l in data: line_t = utils.utm2lola(l) params = details.get('params', None) if params: plot_line(m, line_t, **params) else: plot_line(m, line_t, colour='k', alpha=0.5) if layer == "wells": for p in data: point_t = utils.utm2lola(p) params = details.get('params', None) if params: plot_point(m, point_t, zorder=100, **params) else: plot_point(m, point_t, zorder=100, colour='k', alpha=0.5) lo, la = point_t.xy x, y = m(lo, la) # Plot the names of wells in the xsection # When we have names we can also plot special symbol # for the feature well. # Plot this transect line line_t = utils.utm2lola(tc.data) plot_line(m, line_t, colour='r', lw=3) # Adjust border thickness [i.set_linewidth(8) for i in locmap.spines.itervalues()] [i.set_color("white") for i in locmap.spines.itervalues()] # Elevation and bedrock plot # -----------------------------------------------------------# print "Elevation" for i, geo in enumerate(tc.bedrock.data[:-1]): lim1 = tc.bedrock.coords[i] lim2 = tc.bedrock.coords[i + 1] idx = np.where( np.logical_and(tc.elevation.coords >= lim1, tc.elevation.coords <= lim2))[0] if len(idx) > 1: if idx[-1] < tc.elevation.coords.size - 1: idx = np.append(idx, (idx[-1] + 1)) hsv = np.array([[geo["AV_HUE"], geo["AV_SAT"], geo["AV_VAL"]]]).reshape(1, 1, 3) color = hsv_to_rgb(hsv / 255.) elevation.bar(tc.elevation.coords[idx], tc.elevation.data[idx], width=1.0, linewidth=0, color=color.flatten()) elevation.plot(tc.elevation.coords[idx], tc.elevation.data[idx], lw=0.5, color='k') max_height = np.amax(tc.elevation.all_data) elevation.set_ylim((0, max_height)) elevation.set_xlim(tc.extents[:2]) elevation.set_yticks([0, int(max_height), int(np.amax(tc.elevation.data))]) elevation.set_xticklabels([]) elevation.tick_params(axis='y', which='major', labelsize=8) elevation.patch.set_alpha(0.1) elevation.set_ylabel("Elevation [m]", fontsize=8) elevation.grid(True) elevation.tick_params(axis='x', which='major', labelsize=0) elevation.xaxis.grid(True, which='major') elevation.text(0.01, 0.8, "Elevation", props, va='bottom', fontsize=11, weight='bold', transform=elevation.transAxes) elevation.text(0.01, 0.75, "Surface geology", props, va='top', fontsize=10, transform=elevation.transAxes) elevation.set_frame_on(False) for tick in elevation.get_xaxis().get_major_ticks(): tick.set_pad(-8.) tick.label1 = tick._get_text1() # Seismic cross section # ------------------------------------------------------------# print "Seismic" for coords, data in zip(tc.seismic.coords, tc.seismic.data): max_z = data["traces"].shape[0] * data["sample_interval"] im = xsection.imshow(data["traces"], extent=[ np.amin(coords) / 1000.0, np.amax(coords) / 1000.0, max_z, 0 ], aspect="auto", cmap=tc.seismic_cmap) # Horizons colours = ['b', 'g', 'orange', 'c', 'magenta', 'pink'] for i, (horizon, data) in enumerate(tc.horizons.data.items()): coords = tc.horizons.coords[horizon] xsection.scatter(coords / 1000, data, color=colours[i], marker='.') # labels xsection.text(0.01, 0.025 * (i + 1), horizon, transform=xsection.transAxes, ha='left', color=colours[i], va='center', fontsize=12) # Axes etc. plot_axis = [ tc.extents[0] / 1000., tc.extents[1] / 1000., tc.extents[2], tc.extents[3] ] xsection.axis(plot_axis) xsection.set_xticklabels([]) if tc.domain.lower() in ['depth', 'd', 'z']: xsection.set_ylabel("Depth [m]", fontsize=8) else: xsection.set_ylabel("TWTT [ms]", fontsize=8) xsection.tick_params(axis='y', which='major', labelsize=8) xsection.tick_params(axis='x', which='major', labelsize=0) xsection.yaxis.grid(True, which='major') xsection.xaxis.grid(True, which='major') xsection.set_frame_on(False) # Seismic colorbar colorbar_ax = add_subplot_axes(xsection, [0.975, 0.025, 0.01, 0.1]) fig.colorbar(im, cax=colorbar_ax) colorbar_ax.text(0.5, 0.9, "+", transform=colorbar_ax.transAxes, ha='center', color='white', va='center', fontsize=12) colorbar_ax.text(0.5, 0.15, "-", transform=colorbar_ax.transAxes, color='k', ha='center', va='center', fontsize=16) colorbar_ax.set_axis_off() # Title xsection.text(0.01, 0.99, "Seismic", color='k', ha='left', va='top', fontsize=12, weight='bold', transform=xsec_logs.transAxes) # Potential field data # -----------------------------------------------------------# print "Potfields" for i, (field, payload) in enumerate(tc.potfield.data.items()): bot = 1 - (i + 1.) / n_grids height = (1. / n_grids) - 0.05 rect = [0, bot, 1, height] this_ax = add_subplot_axes(potfield, rect) sc = this_ax.scatter(payload['coords'], payload['data'], c=payload['colour'], cmap=payload['cmap'], s=1, edgecolor='', vmin=-50, vmax=150) this_ax.set_xlim(tc.extents[:2]) this_ax.set_frame_on(False) this_ax.set_xticks([]) this_ax.tick_params(axis='y', which='major', labelsize=8) this_ax.grid(True) scale = payload['scale'] if scale: this_ax.set_ylim(float(scale[1]), float(scale[0])) if payload['colour_is_file']: tcol = '#555555' else: tcol = payload['colour'] this_ax.text(0.01, 0.01, field, ha='left', va='bottom', fontsize=10, color=tcol, transform=this_ax.transAxes) # potfield colorbar # TODO: This doesn't work. if payload.get('cmap'): # pf_cbar_ax = add_subplot_axes(this_ax, [0.975, 0.1, 0.01, 0.8]) # fig.colorbar(sc, cax=pf_cbar_ax) # pf_cbar_ax.set_axis_off() pass potfield.axis(plot_axis) potfield.set_frame_on(False) potfield.set_yticks([]) potfield.xaxis.grid(True, which='major') potfield.tick_params(axis='x', which='major', labelsize=10) potfield.set_xlabel("Transect range [km]", fontsize=10, ha='center') potfield.text(0.01, 1.0, "Potential fields", ha='left', va='top', fontsize=11, weight='bold', color='k', transform=potfield.transAxes) # Log overlays # --------------------------------------------------------# print "Logs" if tc.locmap.layers.get('wells'): if tc.locmap.layers['wells'].get('colour'): well_colour = tc.locmap.layers['wells']['colour'] else: well_colour = tc.settings.get('default_colour') if not well_colour: well_colour = 'k' c = tc.seismic_log_colour for name, las, pos in zip(tc.log.names, tc.log.data, tc.log.coords): if name == tc.feature_well: alpha, lw = 0.5, 1.5 weight = 'bold' else: alpha, lw = 0.25, 1.0 weight = 'normal' if las: data = np.nan_to_num(las.data[tc.seismic_log]) data /= np.amax(data) z = las.data['DEPT'] if tc.domain.lower() in ['time', 'twt', 'twtt', 't']: dt = 0.001 data = tc.seismic.velocity.depth2time(data, pos, dz=z, dt=dt) start = tc.seismic.velocity.depth2timept(las.start, pos) z = np.arange(0, len(data), 1) + 1000 * start # ms # Some post-processing for display lgsc = 0.015 # hack to compress the log width data *= lgsc * (tc.extents[1] - tc.extents[0]) if data.size > 0: data += pos - 0.5 * np.amax(data) xsec_logs.axvline(x=pos, color=well_colour, alpha=alpha, lw=lw, ymin=0, ymax=z[-1]) xsec_logs.plot(data, z, c, lw=0.5, alpha=0.75) else: # Need to get TD from SHP or well header sheet. z = [tc.extents[2] - 40] xsec_logs.axvline(x=pos, color=well_colour, alpha=0.25) elevation.axvline(x=pos, color=well_colour, alpha=alpha, lw=lw) potfield.axvline(x=pos / 1000, color=well_colour, alpha=alpha, lw=lw) # Well name annotation elevation.text(pos, max_height - 10, name, color=well_colour, va='top', ha='center', fontsize=10, weight=weight) xsec_logs.set_xlim((tc.extents[0], tc.extents[1])) xsec_logs.set_ylim((tc.extents[2], tc.extents[3])) xsec_logs.axis("off") # Log type annotation, top left xsec_logs.text(0.01, 0.965, tc.seismic_log + ' log', color=c, ha='left', va='top', fontsize=12, transform=xsec_logs.transAxes) # Feature plot # -----------------------------------------------------# if tc.feature_well: print "Feature well:", tc.feature_well log_header.text(0.0, 1.0, ("Well " + tc.feature_well), verticalalignment='top', horizontalalignment='left', fontsize=14, fontweight='bold') # horizontal line log_header.axhline(y=0.5, xmin=0, xmax=1.25, linewidth=1, color='k') plot_feature_well(tc, gs) else: Notice.warning("No feature well") log_header.axis("off") # Logo etc. # --------------------------------------------------------------# print "Logo" try: path = os.path.join(tc.data_dir, tc.settings['logo_file']) im = Image.open(path) im.thumbnail((fig.dpi, fig.dpi), Image.ANTIALIAS) except IOError: print "Image is missing", path im = np.zeros((1, 1, 3)) w, h = im.size # We need a float array between 0-1, rather than # a uint8 array between 0-255 im = np.array(im).astype(np.float) / 255 # With newer (1.0) versions of matplotlib, you can # use the "zorder" kwarg to make the image overlay # the plot, rather than hide behind it... (e.g. zorder=10) fig.figimage(im, (0.95 * fig.bbox.xmax) - w, (0.96 * fig.bbox.ymax) - h) # Annotate config file and creation date plt.figtext(0.950, 0.030, tc.time, ha="right", va="bottom", color="gray", size=8) plt.figtext(0.950, 0.04, tc.config_file, ha="right", va="bottom", color="gray", size=8) year = datetime.datetime.now().year text = "$\copyright$ {} Department of Energy".format(year) plt.figtext(0.750, 0.03, text, ha="left", va="bottom", color="gray", size=8) # Finish # --------------------------------------------------------------# save_file = getattr(tc, 'save_file', None) if save_file: if type(save_file) != 'str': # Then it's probably a bool from 'yes' or 'true' in the config save_file = tc.config_file.split('.')[0] + '.png' if save_file == 'config.png': save_file = 'temp.png' Notice.ok("Saving file " + save_file + "...", hold=True) plt.savefig(save_file, dpi=fig.dpi) Notice.ok("Done") Notice.warning("The displayed image is not identical to the saved one") plt.show() else: plt.show()
def main(target, cfg): """ Puts everything together. """ t0 = time.time() # Read the file. section = readSEGY(target, unpack_headers=True) # Calculate some things nsamples = section.traces[0].header.number_of_samples_in_this_trace dt = section.traces[0].header.sample_interval_in_ms_for_this_trace ntraces = len(section.traces) tbase = 0.001 * np.arange(0, nsamples * dt, dt) tstart = 0 tend = np.amax(tbase) wsd = ntraces / cfg['tpi'] # Build the data container elev, esp, ens, tsq = [], [], [], [] # energy source point number data = np.zeros((nsamples, ntraces)) for i, trace in enumerate(section.traces): data[:, i] = trace.data elev.append(trace.header.receiver_group_elevation) esp.append(trace.header.energy_source_point_number) ens.append(trace.header.ensemble_number) tsq.append(trace.header.trace_sequence_number_within_line) clip_val = np.percentile(data, 99.0) # Notify user of parameters Notice.info("n_traces {}".format(ntraces)) Notice.info("n_samples {}".format(nsamples)) Notice.info("dt {}".format(dt)) Notice.info("t_start {}".format(tstart)) Notice.info("t_end {}".format(tend)) Notice.info("max_val {}".format(np.amax(data))) Notice.info("min_val {}".format(np.amin(data))) Notice.info("clip_val {}".format(clip_val)) t1 = time.time() Notice.ok("Read data successfully in {:.1f} s".format(t1-t0)) ##################################################################### # # MAKE PLOT # ##################################################################### Notice.hr_header("Plotting") ################################## # Plot size parameters # Some constants wsl = 6 # Width of sidelabel mih = 10 # Minimum plot height fhh = 5 # File header height m = 0.5 # margin in inches # Margins, CSS like mt, mb, ml, mr = m, m, 2 * m, 2 * m mm = mr / 2 # padded margin between seismic and label # Width is determined by tpi, plus a constant for the sidelabel, plus 1 in w = ml + wsd + wsl + mr + mm # Height is given by ips, but with a minimum of 8 inches, plus 1 in h = max(mih, cfg['ips'] * (np.amax(tbase) - np.amin(tbase)) / 1000 + mt + mb) # More settings ssl = (ml + wsd + mm) / w # Start of side label (ratio) fs = cfg['fontsize'] Notice.info("Width of plot {} in".format(w)) Notice.info("Height of plot {} in".format(h)) ################################## # Make the figure. fig = plt.figure(figsize=(w, h), facecolor='w') ax = fig.add_axes([ml / w, mb / h, wsd / w, (h - mb - mt) / h]) # make parasitic axes for labeling CDP number par1 = ax.twiny() par1.spines["top"].set_position(("axes", 1.0)) tickfmt = mtick.FormatStrFormatter('%.0f') par1.plot(ens, np.zeros_like(ens)) par1.set_xlabel("CDP number", fontsize=fs-2) par1.set_xticklabels(par1.get_xticks(), fontsize=fs-2) par1.xaxis.set_major_formatter(tickfmt) ax = wiggle_plot(ax, data, tbase, ntraces, skip=cfg['skip'], gain=cfg['gain'], rgb=cfg['colour'], alpha=cfg['opacity'], lw=cfg['lineweight'] ) ax = decorate_seismic(ax, ntraces, tickfmt, fs) # Plot title title_ax = fig.add_axes([ssl, 1-mt/h, wsl/w, mt/(2*h)]) title_ax = plot_title(title_ax, target, fs=fs) # Plot text header. s = str(section.textual_file_header)[2:-1] start = (h - mt - fhh) / h head_ax = fig.add_axes([ssl, start, wsl/w, fhh/h]) head_ax = plot_header(head_ax, s, fs) # Plot histogram. pad = 0.05 charty = 0.125 # height of chart xhist = (ssl + pad) whist = (1 - ssl - (ml/w)) - 2 * pad hist_ax = fig.add_axes([xhist, 1.5 * mb/h + charty + pad, whist, charty]) hist_ax = plot_histogram(hist_ax, data, fs) # Plot spectrum. spec_ax = fig.add_axes([xhist, 1.5 * mb/h, whist, charty]) spec_ax = plot_spectrum(spec_ax, data, dt, fs) t2 = time.time() Notice.ok("Built plot in {:.1f} s".format(t2-t1)) ##################################################################### # # SAVE FILE # ##################################################################### Notice.hr_header("Saving") s = "Saved image file {} in {:.1f} s" if cfg['outfile']: fig.savefig(cfg['outfile']) t3 = time.time() Notice.ok(s.format(cfg['outfile'], t3-t2)) else: stem, _ = os.path.splitext(target) fig.savefig(stem) t3 = time.time() Notice.ok(s.format(stem+'.png', t3-t2)) return
def plot(tc): """ Constructs a multi-subplot matplotlib figure. Args: transect (TransectContainer): A transect container. """ h = 15 mw = 16 # width of main section (inches) must be div by 4 fw = 5 # width of the feature plot (inches) must be div by 5 n_grids = len(tc.potfield.data) # We will save the same figure we make, to ensure the saved figure # has everything in the right places. For example, see this discussion: # http://stackoverflow.com/questions/7906365/ save_dpi = getattr(tc, 'save_dpi', tc.settings.get('default_dpi')) dpi = save_dpi or 80 fig = plt.figure(figsize=(mw + fw + 1, 15), facecolor='w', edgecolor='k', dpi=dpi, frameon=True) gs = gridspec.GridSpec(h, mw + fw + 1) # Left-hand column. header = fig.add_subplot(gs[0:1, 0:mw/2]) description = fig.add_subplot(gs[1:3, 0:mw/2]) locmap = fig.add_subplot(gs[0:3, mw/2:mw]) # Aspect = 8:3 elevation = fig.add_subplot(gs[3, :mw]) xsection = fig.add_subplot(gs[4:h-n_grids, :mw]) xsec_logs = fig.add_subplot(gs[4:h-n_grids, :mw]) potfield = fig.add_subplot(gs[h-n_grids:, :mw]) # Right-hand column. log_header = fig.add_subplot(gs[0:1, -1*fw:]) # log_plot is dealt with by passing gs to feature_plot.plot_feature_well() # Adjust white space between subplots # ------------------------------------------------------------ # left = 0.05 # left side of the subplots of the figure right = 0.95 # right side of the subplots of the figure bottom = 0.05 # bottom of the subplots of the figure top = 0.95 # top of the subplots of the figure wspace = 0.05 # blank w space between subplots hspace = 0.1 # blank h space between subplots fig.subplots_adjust(left, bottom, right, top, wspace, hspace) bbox = {'fc': 'w', 'pad': 0, 'ec': 'none', 'alpha': 0.5} props = {'ha': 'left', 'va': 'center', 'bbox': bbox} # Header # ---------------------------------------------------------# print "Header" header.axis("off") dy = 0.2 header.text(0.0, 0.5 + dy, tc.title, props, fontsize=30, horizontalalignment='left', verticalalignment='bottom' ) # horizontal line header.axhline(y=0.5, xmin=0, xmax=1.25, linewidth=1.5, color='k') # Subtitle header.text(1.0, 0.5 + dy, (tc.subtitle), fontsize=15, horizontalalignment='right', verticalalignment='bottom', weight='bold') descr = tc.description if tc.meta: description.text(0, 1.0, tc.domain.upper(), horizontalalignment='left', verticalalignment='bottom', fontsize=14 ) description.text(1.0, 1.0, tc.velocity, horizontalalignment='right', verticalalignment='bottom', fontsize=12 ) descr_pos = 0.8 # Where to position the rest. else: descr_pos = 1.0 # Paragraph description # -----------------------------------------------------# description.text(0, descr_pos, descr, horizontalalignment='left', verticalalignment='top', fontsize=12, family='serif' ) description.axis('off') # Wrap text fig.canvas.mpl_connect('draw_event', lambda event: on_draw(event, description)) # Map # ---------------------------------------------------------# print "Locmap" tx, ty = tc.data.coords.xy res = 'h' # c, l, i, h, f # Generate Basemap object. m = Basemap(projection='tmerc', lon_0=tc.locmap.mid.x, lat_0=tc.locmap.mid.y, resolution=res, llcrnrlon=tc.locmap.ll.x, llcrnrlat=tc.locmap.ll.y, urcrnrlon=tc.locmap.ur.x, urcrnrlat=tc.locmap.ur.y, ax=locmap) # Finish drawing the basemap. draw_basemap(m, tc) for layer, details in tc.locmap.layers.items(): data = getattr(tc.locmap, layer) for l in data: line_t = utils.utm2lola(l) params = details.get('params', None) if params: plot_line(m, line_t, **params) else: plot_line(m, line_t, colour='k', alpha=0.5) if layer == "wells": for p in data: point_t = utils.utm2lola(p) params = details.get('params', None) if params: plot_point(m, point_t, zorder=100, **params) else: plot_point(m, point_t, zorder=100, colour='k', alpha=0.5) lo, la = point_t.xy x, y = m(lo, la) # Plot the names of wells in the xsection # When we have names we can also plot special symbol # for the feature well. # Plot this transect line line_t = utils.utm2lola(tc.data) plot_line(m, line_t, colour='r', lw=3) # Adjust border thickness [i.set_linewidth(8) for i in locmap.spines.itervalues()] [i.set_color("white") for i in locmap.spines.itervalues()] # Elevation and bedrock plot # -----------------------------------------------------------# print "Elevation" for i, geo in enumerate(tc.bedrock.data[:-1]): lim1 = tc.bedrock.coords[i] lim2 = tc.bedrock.coords[i + 1] idx = np.where(np.logical_and(tc.elevation.coords >= lim1, tc.elevation.coords <= lim2))[0] if len(idx) > 1: if idx[-1] < tc.elevation.coords.size - 1: idx = np.append(idx, (idx[-1] + 1)) hsv = np.array([[geo["AV_HUE"], geo["AV_SAT"], geo["AV_VAL"]]]).reshape(1, 1, 3) color = hsv_to_rgb(hsv / 255.) elevation.bar(tc.elevation.coords[idx], tc.elevation.data[idx], width=1.0, linewidth=0, color=color.flatten()) elevation.plot(tc.elevation.coords[idx], tc.elevation.data[idx], lw=0.5, color='k') max_height = np.amax(tc.elevation.all_data) elevation.set_ylim((0, max_height)) elevation.set_xlim(tc.extents[:2]) elevation.set_yticks([0, int(max_height), int(np.amax(tc.elevation.data))]) elevation.set_xticklabels([]) elevation.tick_params(axis='y', which='major', labelsize=8) elevation.patch.set_alpha(0.1) elevation.set_ylabel("Elevation [m]", fontsize=8) elevation.grid(True) elevation.tick_params(axis='x', which='major', labelsize=0) elevation.xaxis.grid(True, which='major') elevation.text(0.01, 0.8, "Elevation", props, va='bottom', fontsize=11, weight='bold', transform=elevation.transAxes) elevation.text(0.01, 0.75, "Surface geology", props, va='top', fontsize=10, transform=elevation.transAxes) elevation.set_frame_on(False) for tick in elevation.get_xaxis().get_major_ticks(): tick.set_pad(-8.) tick.label1 = tick._get_text1() # Seismic cross section # ------------------------------------------------------------# print "Seismic" for coords, data in zip(tc.seismic.coords, tc.seismic.data): max_z = data["traces"].shape[0] * data["sample_interval"] im = xsection.imshow(data["traces"], extent=[np.amin(coords) / 1000.0, np.amax(coords) / 1000.0, max_z, 0], aspect="auto", cmap=tc.seismic_cmap) # Horizons colours = ['b', 'g', 'orange', 'c', 'magenta', 'pink'] for i, (horizon, data) in enumerate(tc.horizons.data.items()): coords = tc.horizons.coords[horizon] xsection.scatter(coords/1000, data, color=colours[i], marker='.') # labels xsection.text(0.01, 0.025*(i+1), horizon, transform=xsection.transAxes, ha='left', color=colours[i], va='center', fontsize=12) # Axes etc. plot_axis = [tc.extents[0] / 1000., tc.extents[1] / 1000., tc.extents[2], tc.extents[3]] xsection.axis(plot_axis) xsection.set_xticklabels([]) if tc.domain.lower() in ['depth', 'd', 'z']: xsection.set_ylabel("Depth [m]", fontsize=8) else: xsection.set_ylabel("TWTT [ms]", fontsize=8) xsection.tick_params(axis='y', which='major', labelsize=8) xsection.tick_params(axis='x', which='major', labelsize=0) xsection.yaxis.grid(True, which='major') xsection.xaxis.grid(True, which='major') xsection.set_frame_on(False) # Seismic colorbar colorbar_ax = add_subplot_axes(xsection, [0.975, 0.025, 0.01, 0.1]) fig.colorbar(im, cax=colorbar_ax) colorbar_ax.text(0.5, 0.9, "+", transform=colorbar_ax.transAxes, ha='center', color='white', va='center', fontsize=12) colorbar_ax.text(0.5, 0.15, "-", transform=colorbar_ax.transAxes, color='k', ha='center', va='center', fontsize=16) colorbar_ax.set_axis_off() # Title xsection.text(0.01, 0.99, "Seismic", color='k', ha='left', va='top', fontsize=12, weight='bold', transform=xsec_logs.transAxes) # Potential field data # -----------------------------------------------------------# print "Potfields" for i, (field, payload) in enumerate(tc.potfield.data.items()): bot = 1 - (i+1.)/n_grids height = (1./n_grids) - 0.05 rect = [0, bot, 1, height] this_ax = add_subplot_axes(potfield, rect) sc = this_ax.scatter(payload['coords'], payload['data'], c=payload['colour'], cmap=payload['cmap'], s=1, edgecolor='', vmin=-50, vmax=150) this_ax.set_xlim(tc.extents[:2]) this_ax.set_frame_on(False) this_ax.set_xticks([]) this_ax.tick_params(axis='y', which='major', labelsize=8) this_ax.grid(True) scale = payload['scale'] if scale: this_ax.set_ylim(float(scale[1]), float(scale[0])) if payload['colour_is_file']: tcol = '#555555' else: tcol = payload['colour'] this_ax.text(0.01, 0.01, field, ha='left', va='bottom', fontsize=10, color=tcol, transform=this_ax.transAxes) # potfield colorbar # TODO: This doesn't work. if payload.get('cmap'): # pf_cbar_ax = add_subplot_axes(this_ax, [0.975, 0.1, 0.01, 0.8]) # fig.colorbar(sc, cax=pf_cbar_ax) # pf_cbar_ax.set_axis_off() pass potfield.axis(plot_axis) potfield.set_frame_on(False) potfield.set_yticks([]) potfield.xaxis.grid(True, which='major') potfield.tick_params(axis='x', which='major', labelsize=10) potfield.set_xlabel("Transect range [km]", fontsize=10, ha='center') potfield.text(0.01, 1.0, "Potential fields", ha='left', va='top', fontsize=11, weight='bold', color='k', transform=potfield.transAxes) # Log overlays # --------------------------------------------------------# print "Logs" if tc.locmap.layers.get('wells'): if tc.locmap.layers['wells'].get('colour'): well_colour = tc.locmap.layers['wells']['colour'] else: well_colour = tc.settings.get('default_colour') if not well_colour: well_colour = 'k' c = tc.seismic_log_colour for name, las, pos in zip(tc.log.names, tc.log.data, tc.log.coords): if name == tc.feature_well: alpha, lw = 0.5, 1.5 weight = 'bold' else: alpha, lw = 0.25, 1.0 weight = 'normal' if las: data = np.nan_to_num(las.data[tc.seismic_log]) data /= np.amax(data) z = las.data['DEPT'] if tc.domain.lower() in ['time', 'twt', 'twtt', 't']: dt = 0.001 data = tc.seismic.velocity.depth2time(data, pos, dz=z, dt=dt) start = tc.seismic.velocity.depth2timept(las.start, pos) z = np.arange(0, len(data), 1) + 1000*start # ms # Some post-processing for display lgsc = 0.015 # hack to compress the log width data *= lgsc * (tc.extents[1] - tc.extents[0]) if data.size > 0: data += pos - 0.5 * np.amax(data) xsec_logs.axvline(x=pos, color=well_colour, alpha=alpha, lw=lw, ymin=0, ymax=z[-1]) xsec_logs.plot(data, z, c, lw=0.5, alpha=0.75) else: # Need to get TD from SHP or well header sheet. z = [tc.extents[2]-40] xsec_logs.axvline(x=pos, color=well_colour, alpha=0.25) elevation.axvline(x=pos, color=well_colour, alpha=alpha, lw=lw) potfield.axvline(x=pos/1000, color=well_colour, alpha=alpha, lw=lw) # Well name annotation elevation.text(pos, max_height-10, name, color=well_colour, va='top', ha='center', fontsize=10, weight=weight) xsec_logs.set_xlim((tc.extents[0], tc.extents[1])) xsec_logs.set_ylim((tc.extents[2], tc.extents[3])) xsec_logs.axis("off") # Log type annotation, top left xsec_logs.text(0.01, 0.965, tc.seismic_log+' log', color=c, ha='left', va='top', fontsize=12, transform=xsec_logs.transAxes) # Feature plot # -----------------------------------------------------# if tc.feature_well: print "Feature well:", tc.feature_well log_header.text(0.0, 1.0, ("Well " + tc.feature_well), verticalalignment='top', horizontalalignment='left', fontsize=14, fontweight='bold' ) # horizontal line log_header.axhline(y=0.5, xmin=0, xmax=1.25, linewidth=1, color='k') plot_feature_well(tc, gs) else: Notice.warning("No feature well") log_header.axis("off") # Logo etc. # --------------------------------------------------------------# print "Logo" try: path = os.path.join(tc.data_dir, tc.settings['logo_file']) im = Image.open(path) im.thumbnail((fig.dpi, fig.dpi), Image.ANTIALIAS) except IOError: print "Image is missing", path im = np.zeros((1, 1, 3)) w, h = im.size # We need a float array between 0-1, rather than # a uint8 array between 0-255 im = np.array(im).astype(np.float) / 255 # With newer (1.0) versions of matplotlib, you can # use the "zorder" kwarg to make the image overlay # the plot, rather than hide behind it... (e.g. zorder=10) fig.figimage(im, (0.95*fig.bbox.xmax) - w, (0.96*fig.bbox.ymax) - h) # Annotate config file and creation date plt.figtext(0.950, 0.030, tc.time, ha="right", va="bottom", color="gray", size=8) plt.figtext(0.950, 0.04, tc.config_file, ha="right", va="bottom", color="gray", size=8) year = datetime.datetime.now().year text = "$\copyright$ {} Department of Energy".format(year) plt.figtext(0.750, 0.03, text, ha="left", va="bottom", color="gray", size=8) # Finish # --------------------------------------------------------------# save_file = getattr(tc, 'save_file', None) if save_file: if type(save_file) != 'str': # Then it's probably a bool from 'yes' or 'true' in the config save_file = tc.config_file.split('.')[0] + '.png' if save_file == 'config.png': save_file = 'temp.png' Notice.ok("Saving file "+save_file+"...", hold=True) plt.savefig(save_file, dpi=fig.dpi) Notice.ok("Done") Notice.warning("The displayed image is not identical to the saved one") plt.show() else: plt.show()
def main(target, cfg): """ Puts everything together. """ t0 = time.time() ##################################################################### # # READ SEGY # ##################################################################### s = Seismic.from_segy(target, params={'ndim': cfg['ndim']}) # Set the line and/or xline number. try: n, xl = cfg['number'] except: n, xl = cfg['number'], 0.5 # Set the direction. if (s.ndim) == 2: direction = ['inline'] elif cfg['direction'].lower()[0] == 'i': direction = ['inline'] elif cfg['direction'].lower()[0] == 'x': direction = ['xline'] elif cfg['direction'].lower()[0] == 't': direction = ['tslice'] else: direction = ['inline', 'xline'] # Get the data. ss = [Seismic.from_seismic(s, n=n, direction=d) for n, d in zip((n, xl), direction)] data = [s.data for s in ss] clip_val = np.percentile(s.data, cfg['percentile']) # Notify user of parameters. Notice.info("n_traces {}".format(s.ntraces)) Notice.info("n_samples {}".format(s.nsamples)) Notice.info("dt {}".format(s.dt)) Notice.info("t_start {}".format(s.tstart)) Notice.info("t_end {}".format(s.tend)) Notice.info("max_val {:.3f}".format(np.amax(s.data))) Notice.info("min_val {:.3f}".format(np.amin(s.data))) Notice.info("clip_val {:.3f}".format(clip_val)) t1 = time.time() Notice.ok("Read data in {:.1f} s".format(t1-t0)) ##################################################################### # # MAKE PLOT # ##################################################################### Notice.hr_header("Plotting") # Plot size parameters. fs = cfg['fontsize'] wsl = 6 # Width of sidelabel, inches mih = 12 # Minimum plot height, inches fhh = 5 # File header box height, inches m = 0.5 # basic unit of margins, inches # Margins, CSS like: top, right, bottom, left. mt, mr, mb, ml = m, 2 * m, m, 2 * m mm = m # padded margin between seismic and label # Width is determined by seismic width, plus sidelabel, plus margins. # Height is given by ips, but with a minimum of mih inches. if 'tslice' in direction: print('doing tslice') seismic_width = max([s.ninlines for s in ss]) / cfg['tpi'] seismic_height_raw = max([s.nxlines for s in ss]) / cfg['tpi'] print(seismic_width, seismic_height_raw) else: seismic_width = max([s.ntraces for s in ss]) / cfg['tpi'] seismic_height_raw = cfg['ips'] * (s.tbasis[-1] - s.tbasis[0]) w = ml + seismic_width + mm + wsl + mr # inches seismic_height = len(ss) * seismic_height_raw h_reqd = mb + seismic_height + 0.75*(len(ss)-1) + mt # inches h = max(mih, h_reqd) # Calculate where to start sidelabel and seismic data. # Depends on whether sidelabel is on the left or right. if cfg['sidelabel'] == 'right': ssl = (ml + seismic_width + mm) / w # Start of side label (ratio) seismic_left = ml / w else: ssl = ml / w seismic_left = (ml + wsl + mm) / w adj = max(0, h - h_reqd) / 2 seismic_bottom = (mb / h) + adj / h seismic_width_fraction = seismic_width / w seismic_height_fraction = seismic_height_raw / h # Publish some notices so user knows plot size. Notice.info("Width of plot {} in".format(w)) Notice.info("Height of plot {} in".format(h)) # Make the figure. fig = plt.figure(figsize=(w, h), facecolor='w') # Set the tickformat. tickfmt = mtick.FormatStrFormatter('%.0f') # Plot title. if cfg['filename']: title_ax = fig.add_axes([ssl, 1-mt/h, wsl/w, mt/h]) title_ax = plotter.plot_title(title_ax, target, fs=1.5*fs, cfg=cfg) # Plot text header. start = (h - 1.5*mt - fhh) / h head_ax = fig.add_axes([ssl, start, wsl/w, fhh/h]) head_ax = plotter.plot_header(head_ax, s.header, fs=fs-1, cfg=cfg) # Plot histogram. # Params for histogram plot. pady = 0.75 / h # 0.75 inch padx = 0.75 / w # 0.75 inch cstrip = 0.3/h # color_strip height = 0.3 in charth = 1.5/h # height of charts = 1.5 in chartw = wsl/w - mr/w - padx # or ml/w for left-hand sidelabel; same thing chartx = (ssl + padx) histy = 1.5 * mb/h + charth + pady # Plot colourbar under histogram. clrbar_ax = fig.add_axes([chartx, histy - cstrip, chartw, cstrip]) clrbar_ax = plotter.plot_colourbar(clrbar_ax, cmap=cfg['cmap']) # Plot histogram itself. hist_ax = fig.add_axes([chartx, histy, chartw, charth]) hist_ax = plotter.plot_histogram(hist_ax, s.data, tickfmt, percentile=cfg['percentile'], fs=fs) # Plot spectrum. specy = 1.5 * mb/h spec_ax = fig.add_axes([chartx, specy, chartw, charth]) try: spec_ax = s.plot_spectrum(ax=spec_ax, tickfmt=tickfmt, ntraces=20, fontsize=fs) except: pass for i, line in enumerate(ss): # Add the seismic axis. ax = fig.add_axes([seismic_left, seismic_bottom + i*seismic_height_fraction + i*pady, seismic_width_fraction, seismic_height_fraction ]) # Plot seismic data. if cfg['display'].lower() in ['vd', 'varden', 'variable', 'both']: _ = ax.imshow(line.data.T, cmap=cfg['cmap'], clim=[-clip_val, clip_val], extent=[0, line.ntraces, 1000*line.tbasis[-1], line.tbasis[0]], aspect='auto' ) # This does not work: should cut off line at cfg['tmax'] # ax.set_ylim(1000*cfg['tmax'] or 1000*line.tbasis[-1], line.tbasis[0]) if cfg['display'].lower() in ['wiggle', 'both']: ax = line.wiggle_plot(cfg['number'], direction, ax=ax, skip=cfg['skip'], gain=cfg['gain'], rgb=cfg['colour'], alpha=cfg['opacity'], lw=cfg['lineweight'], tmax=cfg['tmax'], ) if cfg['display'].lower() not in ['vd', 'varden', 'variable', 'wiggle', 'both']: Notice.fail("You must specify the type of display: wiggle, vd, both.") return # Seismic axis annotations. fs = cfg['fontsize'] - 2 ax.set_xlim([0, line.data.shape[0]]) ax.set_ylabel(line.ylabel, fontsize=fs) ax.set_xlabel(line.xlabel, fontsize=fs, horizontalalignment='center') ax.set_xticklabels(ax.get_xticks(), fontsize=fs) ax.set_yticklabels(ax.get_yticks(), fontsize=fs) ax.xaxis.set_major_formatter(tickfmt) ax.yaxis.set_major_formatter(tickfmt) # Watermark. if cfg['watermark_text']: ax = plotter.watermark_seismic(ax, cfg) # Make parasitic axes for labeling CDP number. par1 = ax.twiny() par1.spines["top"].set_position(("axes", 1.0)) par1.plot(s.xlines, np.zeros_like(s.xlines)) par1.set_xlabel(line.xlabel, fontsize=fs) par1.set_xticklabels(par1.get_xticks(), fontsize=fs) par1.xaxis.set_major_formatter(tickfmt) t2 = time.time() Notice.ok("Built plot in {:.1f} s".format(t2-t1)) ##################################################################### # # SAVE FILE # ##################################################################### Notice.hr_header("Saving") dname, fname, ext = utils.path_bits(target) outfile = cfg['outfile'] or '' if not os.path.splitext(outfile)[1]: outfile = os.path.join(cfg['outfile'] or dname, fname + '.png') fig.savefig(outfile) t3 = time.time() Notice.ok("Saved image file {} in {:.1f} s".format(outfile, t3-t2)) if cfg['stain_paper'] or cfg['coffee_rings'] or cfg['distort'] or cfg['scribble']: fname = os.path.splitext(outfile)[0] + ".stupid.png" fig.savefig(fname) else: return ##################################################################### # # SAVE STUPID FILE # ##################################################################### Notice.hr_header("Applying the stupidity") stupid_image = Image.open(fname) if cfg['stain_paper']: utils.stain_paper(stupid_image) utils.add_rings(stupid_image, cfg['coffee_rings']) if cfg['scribble']: utils.add_scribble(stupid_image) stupid_image.save(fname) t4 = time.time() Notice.ok("Saved stupid file {} in {:.1f} s".format(fname, t4-t3)) return