Example #1
0
    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")
Example #2
0
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
Example #3
0
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
Example #4
0
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()
Example #5
0
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
Example #6
0
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()
Example #7
0
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