def update(self, transect): """ Updates the container data to a profile that intersect the transect line. Returns nothing. Sets attributes as a side effect. Args: transect (LineString): A transect line. """ Notice.info("Updating " + self.__class__.__name__) for name, payload in self.data.items(): print name payload['coords'] = self.linspace payload['data'] = np.zeros_like(self.linspace) if payload['colour_is_file']: payload['colour'] = np.zeros_like(self.linspace) else: payload['colour'] = payload['all_colour'] for i in self.linspace: i = int(i) x, y = transect.interpolate(i).xy xi = np.abs(payload['all_coords'][0][0, :] - x).argmin() yi = np.abs(payload['all_coords'][1][:, 0] - y).argmin() payload['data'][i] = payload['all_data'][yi, xi] if payload['colour_is_file']: try: payload['colour'][i] = payload['all_colour'][yi, xi] except IndexError: payload['colour'][i] = 0 self.data[name] = payload
def update(self, transect): """ Gather the data near the transect. Depth convert if necessary. Returns nothing. Side effect: sets attributes. Args: transect (LineString): A shapely LineString object. """ Notice.info("Updating " + self.__class__.__name__) b = self.settings.get('fine_buffer') or self.settings['buffer'] prepared = prep(transect.buffer(b)) for horizon, points in self.lookup.items(): l = len(points) points = filter(prepared.contains, points) print horizon, len(points), "of", l, "points" data, coords = [], [] for p in points: coords.append(transect.project(p)) if self.domain.lower() in ['depth', 'd', 'z']: "Depth converting horizon" zpt = self.velocity.time2depthpt(p.z/1000, coords[-1]) data.append(zpt) else: data.append(p.z) self.data[horizon] = np.array(data) self.coords[horizon] = np.array(coords)
def insert(root=None): logging.debug("called : %s", __name__) logging.debug("argument root : %s", root) if root is None: logging.debug("empty root is received") root = os.path.abspath(os.path.dirname(__file__)) + "/gen/json" if not os.path.isdir(root): logging.debug("making directory : %s", root) os.makedirs(root) notices = extract.get_notice_list(False) if notices is None: logging.error("error getting notice list") return None count = 0 for notice in notices: timestamp = str(notice['timestamp']) path = root + '/' + timestamp + '.json' if os.path.isfile(path): continue else: count += 1 print "Saved notice dated '{}' titled '{}'.".format( notice['time'], notice['title']) logging.info("Saved notice dated %s titled %s", notice['time'], notice['title']) n = Notice(timestamp) n.save_json(notice) logging.info("%d notices inserted", count) return count
def insert(root = None): logging.debug("called : %s", __name__) logging.debug("argument root : %s", root) if root is None: logging.debug("empty root is received") root = os.path.abspath(os.path.dirname(__file__)) + "/gen/json" if not os.path.isdir(root): logging.debug("making directory : %s", root) os.makedirs(root) notices = extract.get_notice_list(False) if notices is None: logging.error("error getting notice list") return None count = 0 for notice in notices: timestamp = str(notice['timestamp']) path = root + '/' + timestamp + '.json' if os.path.isfile(path): continue; else: count += 1 print "Saved notice dated '{}' titled '{}'.".format(notice['time'], notice['title']) logging.info("Saved notice dated %s titled %s", notice['time'], notice['title']) n = Notice(timestamp) n.save_json(notice) logging.info("%d notices inserted", count) return count
def update(self, transect): """ Gather the data near the transect. Depth convert if necessary. Returns nothing. Side effect: sets attributes. Args: transect (LineString): A shapely LineString object. """ Notice.info("Updating " + self.__class__.__name__) b = self.settings.get('fine_buffer') or self.settings['buffer'] prepared = prep(transect.buffer(b)) for horizon, points in self.lookup.items(): l = len(points) points = filter(prepared.contains, points) print horizon, len(points), "of", l, "points" data, coords = [], [] for p in points: coords.append(transect.project(p)) if self.domain.lower() in ['depth', 'd', 'z']: "Depth converting horizon" zpt = self.velocity.time2depthpt(p.z / 1000, coords[-1]) data.append(zpt) else: data.append(p.z) self.data[horizon] = np.array(data) self.coords[horizon] = np.array(coords)
def update(self, transect): """ Updates the container data to a profile that intersect the transect line. Returns nothing. Sets attributes as a side effect. Args: transect (LineString): A transect line. """ Notice.info("Updating " + self.__class__.__name__) pad = self.settings['map_padding'] aspect = 12/3. # I was expecting it to be 8:3. # Calculate the map bounds and centre. bounds = transect.bounds llx, lly, urx, ury = bounds w, h = urx - llx, ury - lly x_adj, y_adj = 0, 0 if h > (w/aspect): x_adj = ((aspect*h) - w) / 2. # Aspect is hard-coded in uberplot else: y_adj = ((w/aspect) - h) / 2. utm_nad83 = pp.Proj("+init=EPSG:26920") ll_nad83 = pp.Proj("+proj=longlat +ellps=GRS80 +datum=NAD83 +no_defs") utm2lola = partial(pp.transform, utm_nad83, ll_nad83) ll = transform(utm2lola, Point(llx-pad-x_adj, lly-pad-y_adj)) ur = transform(utm2lola, Point(urx+pad+x_adj, ury+pad+y_adj)) self.ll, self.ur = ll, ur self.mid = Point(ll.x + 0.5*(ur.x-ll.x), ll.y + 0.5*(ur.y - ll.y)) # Go over the layers and collect data. for layer, details in self.layers.items(): path = details['file'] print layer, path # Set up convenient params dictionary for plotting function. params = {k: v for k, v in details.items() if k != 'file'} self.layers[layer]['params'] = params # Get a list of shapes from the file. shapes = [] fname, ext = os.path.splitext(os.path.basename(path)) if ext.strip('.').lower() in self.settings['raster_extensions']: # TODO: Deal with rasters. pass elif ext.strip('.').lower() == 'shp': with fiona.open(path) as c: for s in c: shapes.append(shape(s['geometry'])) # name = s.get('name') or s.get('id') or None # data = {name: shape(s['geometry'])} # setattr(self, 'data', data) setattr(self, layer, shapes) else: pass
def send_name(filename): '''Send the notification for the notice of given json filename''' logging.debug("called : %s", __name__) logging.debug("argument filename : %s", filename) if filename is None: logging.error("empty filename received") return n = Notice(filename) notice = n.get_json() # If the notice is not updated, update it. logging.debug("checking if notice is updated") if not notice['updated']: update.update_json(filename) notice = n.get_json() # If the notice is not sent, send it. logging.debug("checking if notice is sent") if not notice['sent']: if send_json(notice): # If notice is sent, save it locally. So that it is not # sent again. logging.debug("notice is sent - saving it locally") notice['sent'] = True n.save_json(notice) return True else: logging.error("failed sending notice") return False else: logging.debug("notice is already sent") return False
def update(self, notice: Notice): assert notice.is_empty() == False # 存在则更新,不存在则新建 self.collection.update_one({'nid': notice.nid}, {'$set': notice.to_dict()}, upsert=True)
def update(self, transect): """ Updates the container data to a profile that intersect the transect line. Returns nothing. Sets attributes as a side effect. Args: transect (LineString): A transect line. """ Notice.info("Updating " + self.__class__.__name__) self.reset_data() points = [Point(xy) for xy in transect.coords] for polygon, properties in self.lookup.items(): for p in filter(polygon.contains, points): self.data.append(properties) self.coords.append(transect.project(p)) # Sort the data to be in order idx = sorted(range(len(self.coords)), key=lambda k: self.coords[k]) self.data = [self.data[i] for i in idx] self.coords = np.array([self.coords[i] for i in idx])
def initialize_notice(self): log_file = self.conf.get("log", None) if log_file is not None: log_notice = OutputToFile(log_file) self.notice = Notice(log_notice, log_notice) else: self.notice = Notice()
def load_work_state(self, event=None): message = 'Any unsaved work that you have open will be lost. Continue?' load_work = tkMessageBox.askyesno('Load work state?', message) if load_work: work_state_file = tkFileDialog.askopenfilename() if work_state_file: self.work_state_filename = work_state_file work_state = cPickle.load(open(self.work_state_filename, 'r')) self.root_notice_file = work_state['root_notice'] self.notices_files = work_state['open_notices'] self.terms = work_state['definitions'] self.always_fix = work_state['always_fix'] self.never_fix = work_state['never_fix'] self.root_notice = Notice(self.root_notice_file) self.current_notice = self.root_notice.document_number self.add_element_to_tree(self.root_notice.tree, None) self.trees[self.root_notice.document_number] = self.root_notice self.populate_definitions() self.notices.append(self.root_notice.document_number) notices = [ Notice(notice_file) for notice_file in self.notices_files ] notices.sort(key=lambda n: n.effective_date) for notice in notices: self.notices.append(notice.document_number) self.trees[notice.document_number] = notice self.update_notices_list() self.populate_definitions()
def get_text_path(path): """ Given path to json file, return a formatted body string with all the details """ logging.debug("called : %s", __name__) logging.debug("argument path : ", path) n = Notice(path) notice = n.get_json() return get_text_dict(notice)
def get_notice(self, content, notice_type): class_name = notice_type.capitalize() notices = content.find(id=notice_type) if notices and len(notices): notices = notices.find(class_="list-block").children for notice in notices: if notice != '\n': description, link, date = self.parse_content(notice) new_notice = Notice(notice_type, description, link, date) new_notice.save_notice()
def open_root_notice(self): load_root = False if self.root_notice is not None: message = "You already have a root notice loaded. If you load " + \ "another root notice, your current set of notices will be replaced. " + \ "Are you sure you want to do this?" result = tkMessageBox.askokcancel('Replace root?', message) if result: load_root = True else: load_root = True if load_root: self.notices = [] self.root_notice = None self.current_notice = None self.trees = {} for child in self.element_tree.get_children(): self.element_tree.delete(child) notice_file = tkFileDialog.askopenfilename() if notice_file: notice = Notice(notice_file) self.current_notice = notice.document_number self.notices.append(notice.document_number) self.root_notice = notice self.root_notice_file = notice_file self.add_element_to_tree(notice.tree, None) self.update_notices_list() self.trees[notice.document_number] = self.root_notice self.populate_definitions() cfr_section = notice.tree.find('{eregs}preamble').find( './/{eregs}section').text all_notice_files = find_all(cfr_section, is_notice=True) message = "There are {} additional notices associated with this root version. ".format(len(all_notice_files)) + \ "Would you like to load all of them? This can take some time if the notices " + \ "are very large." result = tkMessageBox.askokcancel('Load all notices?', message) if result: self.notices_files = all_notice_files all_notices = [ Notice(notice_file) for notice_file in all_notice_files ] all_notices.sort(key=lambda n: n.effective_date) for notice in all_notices: self.notices.append(notice.document_number) self.trees[notice.document_number] = notice self.update_notices_list() self.populate_definitions()
def get(self, uid=None): """ 获取通知,传入 uid: str 来指定获取用于指定用户的通知,缺省 uid=None 获取所有通知 """ notices = [] for r in self.collection.find() or []: n = Notice().from_dict(r) if n.is_expired(): self.drop_expired(n) if uid == None or n.is_for_user(uid): notices.append(n.dumps()) return notices
def update(self, transect): """ Updates the container data to a profile that intersect the transect line. Returns nothing. Sets attributes as a side effect. Args: transect (LineString): A transect line. """ Notice.info("Updating " + self.__class__.__name__) # Preprocess prepared = prep(transect.buffer(self.settings['buffer'])) # Get the intersecting points points = filter(prepared.contains, self.lookup.keys()) self.reset_data() self.names = [] for point in points: name = self.lookup[point] self.names.append(name) print name, pattern = "^" + name + "_out.las" for fname in utils.walk(self.well_dir, pattern): # This is a loop but there should only be one matching file. well = Well(fname, null_subs=np.nan) print well.curves.names self.data.append(well) self.log_lookup[name] = self.data[-1] if not self.log_lookup.get(name): print self.data.append(None) sl_name = getattr(self, 'striplog', None) sl = None if sl_name and (name == self.feature_well): lexicon = Lexicon.default() pattern = "^" + name + ".*striplog.las" for fname in utils.walk(self.well_dir, pattern): # Load the striplog. sl = Well(fname, lexicon=lexicon, null_subs=np.nan) # Add it to the well self.log_lookup[name].add_striplog(sl.striplog[sl_name], sl_name) self.coords.append(transect.project(point))
def sent_false(): logging.debug('called : sent_false') root = os.path.abspath(os.path.dirname(__file__)) path = root + '/gen/json/' listfile = os.listdir(path) if 'old' in listfile: listfile.remove('old') for fl in listfile: n = Notice(fl) notice = n.get_json() if not notice['sent']: notice['sent'] = True n.save_json(notice)
def drop_expired(self, notice=None): """ 调用 `nu.drop_expired()` 将清除数据库中所有过期的通知 调用 `nu.drop_expired(notice)` 将删除数据库中 nid = notice.nid 的一条通知,(这是用来支持 `nu.drop_expired()` 的工作的,也可以用来删除通知,但不推荐使用) """ if isinstance(notice, Notice): self.collection.delete_one({"nid": notice.nid}) else: for r in self.collection.find() or []: n = Notice().from_dict(r) if n.is_expired(): self.drop_expired(n)
def __init__(self, vel_dir, params): # Initialize the base class super(SimpleVelocityContainer, self).__init__(params) self.vel_dir = vel_dir # used by __str__ self.profiles = {} count = 0 for profile in fnmatch.filter(os.listdir(vel_dir), '*.txt'): count += 1 with open(os.path.join(vel_dir, profile), 'r') as f: # Read the location out of the file header = False loc = None while not header: l = f.readline().lstrip() if l.startswith('#'): header = True elif l.startswith('coordinates'): x, y = l.split(',')[1:] x = float(x) y = float(y) loc = Point((x, y)) header = True if not loc: # There were no coordinates. loc = Point((0, 0)) # Build up the time depth profile self.profiles[loc] = [] for line in f.readlines(): # Ignore comments if line.strip().startswith('#'): continue if len(line.strip().split(',')) == 2: time, depth = line.strip().split(',') elif len(line.strip().split()) == 2: time, depth = line.strip().split() else: Notice.warning("Could not read velocity file.") continue self.profiles[loc].append([float(time), float(depth)]) self.count = count
def update_json(name): logging.debug("called : %s", __name__) logging.debug("argument name : %s", name) path = jsondir + name if not os.path.isfile(path): logging.error("json file '%s' is not present.", path) return n = Notice(name) notice = n.get_json() # If the json is erroneous, i.e, has empty fields like topic etc, start # fresh by removing the json file. This way the json will be reloaded at # next cron update. if erroneous_json(notice): logging.error("Encountered errnoneous json %s. Deleting.", str(path)) os.remove(path) return False # If notice is updated, do not update it once more. if notice['updated']: logging.info("notice %s is already updated", name) return False details = get_details_url(notice['url'], notice['num_attachments'] == 1) logging.debug( "Notice has been updated with information from the notice page") notice['updated'] = True notice['text'] = details['text'] if notice['num_attachments'] == 1: notice['attachments'] = details['attachments'] notice['num_attachments'] = len(details['attachments']) logging.debug('Saving the updated notice') n.save_json(notice) return True
def update_json(name): logging.debug("called : %s", __name__) logging.debug("argument name : %s", name) path = jsondir + name if not os.path.isfile(path): logging.error("json file '%s' is not present.", path) return n = Notice(name) notice = n.get_json() # If the json is erroneous, i.e, has empty fields like topic etc, start # fresh by removing the json file. This way the json will be reloaded at # next cron update. if erroneous_json(notice): logging.error("Encountered errnoneous json %s. Deleting.", str(path)) os.remove(path) return False # If notice is updated, do not update it once more. if notice["updated"]: logging.info("notice %s is already updated", name) return False details = get_details_url(notice["url"], notice["num_attachments"] == 1) logging.debug("Notice has been updated with information from the notice page") notice["updated"] = True notice["text"] = details["text"] if notice["num_attachments"] == 1: notice["attachments"] = details["attachments"] notice["num_attachments"] = len(details["attachments"]) logging.debug("Saving the updated notice") n.save_json(notice) return True
def update(self, transect): """ Updates the container data to a profile that intersect the transect line. Returns nothing. Sets attributes as a side effect. Args: transect (LineString): A transect line. """ Notice.info("Updating " + self.__class__.__name__) # space = np.linspace(0, transect.length, self.nsamples) for i, n in enumerate(self.linspace): # interpolate along the transect x, y = transect.interpolate(n).xy # Get the closest elevation points xi = np.abs(self.all_coords[0][0, :] - x).argmin() yi = np.abs(self.all_coords[1][:, 0] - y).argmin() self.data[i] = self.all_data[yi, xi]
def plot(self): """ Generates plot for the transect. """ # Set the extent to the length? Or keep them all the same? self.extents[0] = 0 self.extents[1] = self.data.length Notice.hr_header("Updating") # update the containers self.velocity.update(self.data) self.log.update(self.data) self.seismic.update(self.data) self.horizons.update(self.data) self.elevation.update(self.data) self.bedrock.update(self.data) self.potfield.update(self.data) self.locmap.update(self.data) Notice.hr_header("Plotting") plot(self) plt.show()
def open_additional_notice(self): notice_file = tkFileDialog.askopenfilename() if notice_file: replace_notice = True notice = Notice(notice_file) if notice.document_number in self.notices: message = 'You already have this notice loaded. If you reload it, ' + \ 'your unsaved work will be replaced. Are you sure you want to do this?' replace_notice = tkMessageBox.askokcancel( 'Replace notice?', message) if replace_notice: self.notices.append(notice.document_number) self.notices_files.append(notice_file) self.trees[notice.document_number] = notice self.update_notices_list() self.populate_definitions()
def __init__(self, **kwargs): Notice.title() Notice.hr_header("Initializing") params = kwargs.get('params') layers = kwargs.get('layers') potfields = kwargs.get('potfields') data = kwargs.get('data') velocity = kwargs.get('velocity') super(TransectContainer, self).__init__(params) print "Starting {}, id {}, file {}".format(self.title, self.id, self.config_file) # Set up 'data' — the transect line — from shapefile. self.data = None with fiona.open(data['transect_file']) as c: for line in c: if line['properties']['id'] == self.id: self.data = shape(line['geometry']) if not self.data: Notice.fail("No transect with ID "+self.id) # self.data.length holds the length of the transect in metres # But often we want ints, and sometimes the number of samples. # This will give us a nice linspace. Put them in params. params['length'] = self.length = int(np.floor(self.data.length)) params['nsamples'] = self.nsamples = self.length + 1 params['linspace'] = self.linspace = np.linspace(0, self.length, self.nsamples) self.time = time.strftime("%Y/%m/%d %H:%M", time.localtime()) self.tops_file = data['tops_file'] self.log = LogContainer(data['well_dir'], params) self.velocity = self.__velocity_factory(velocity, params) self.seismic = SeismicContainer(data['seismic_dir'], self.velocity, params) self.horizons = HorizonContainer(data['horizon_dir'], self.velocity, params) self.elevation = ElevationContainer(data['elevation_file'], params) self.bedrock = BedrockContainer(data['bedrock_dir'], params) self.potfield = PotfieldContainer(potfields, params) self.locmap = LocmapContainer(layers, params)
def __init__(self, **kwargs): Notice.title() Notice.hr_header("Initializing") params = kwargs.get('params') layers = kwargs.get('layers') potfields = kwargs.get('potfields') data = kwargs.get('data') velocity = kwargs.get('velocity') super(TransectContainer, self).__init__(params) print "Starting {}, id {}, file {}".format(self.title, self.id, self.config_file) # Set up 'data' — the transect line — from shapefile. self.data = None with fiona.open(data['transect_file']) as c: for line in c: if line['properties']['id'] == self.id: self.data = shape(line['geometry']) if not self.data: Notice.fail("No transect with ID " + self.id) # self.data.length holds the length of the transect in metres # But often we want ints, and sometimes the number of samples. # This will give us a nice linspace. Put them in params. params['length'] = self.length = int(np.floor(self.data.length)) params['nsamples'] = self.nsamples = self.length + 1 params['linspace'] = self.linspace = np.linspace( 0, self.length, self.nsamples) self.time = time.strftime("%Y/%m/%d %H:%M", time.localtime()) self.tops_file = data['tops_file'] self.log = LogContainer(data['well_dir'], params) self.velocity = self.__velocity_factory(velocity, params) self.seismic = SeismicContainer(data['seismic_dir'], self.velocity, params) self.horizons = HorizonContainer(data['horizon_dir'], self.velocity, params) self.elevation = ElevationContainer(data['elevation_file'], params) self.bedrock = BedrockContainer(data['bedrock_dir'], params) self.potfield = PotfieldContainer(potfields, params) self.locmap = LocmapContainer(layers, params)
def notice_dal(self): return Notice() @property
def update(self, transect): """ Updates the container data to a profile that intersect the transect line. Returns nothing. Sets attributes as a side effect. Args: transect (LineString): A transect line. """ Notice.info("Updating " + self.__class__.__name__) pad = self.settings['map_padding'] aspect = 12 / 3. # I was expecting it to be 8:3. # Calculate the map bounds and centre. bounds = transect.bounds llx, lly, urx, ury = bounds w, h = urx - llx, ury - lly x_adj, y_adj = 0, 0 if h > (w / aspect): x_adj = ((aspect * h) - w) / 2. # Aspect is hard-coded in uberplot else: y_adj = ((w / aspect) - h) / 2. utm_nad83 = pp.Proj("+init=EPSG:26920") ll_nad83 = pp.Proj("+proj=longlat +ellps=GRS80 +datum=NAD83 +no_defs") utm2lola = partial(pp.transform, utm_nad83, ll_nad83) ll = transform(utm2lola, Point(llx - pad - x_adj, lly - pad - y_adj)) ur = transform(utm2lola, Point(urx + pad + x_adj, ury + pad + y_adj)) self.ll, self.ur = ll, ur self.mid = Point(ll.x + 0.5 * (ur.x - ll.x), ll.y + 0.5 * (ur.y - ll.y)) # Go over the layers and collect data. for layer, details in self.layers.items(): path = details['file'] print layer, path # Set up convenient params dictionary for plotting function. params = {k: v for k, v in details.items() if k != 'file'} self.layers[layer]['params'] = params # Get a list of shapes from the file. shapes = [] fname, ext = os.path.splitext(os.path.basename(path)) if ext.strip('.').lower() in self.settings['raster_extensions']: # TODO: Deal with rasters. pass elif ext.strip('.').lower() == 'shp': with fiona.open(path) as c: for s in c: shapes.append(shape(s['geometry'])) # name = s.get('name') or s.get('id') or None # data = {name: shape(s['geometry'])} # setattr(self, 'data', data) setattr(self, layer, shapes) else: pass
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 if __name__ == "__main__": Notice.title() parser = argparse.ArgumentParser(description='Plot a SEGY file.') parser.add_argument("-c", "--config", metavar="config file", type=argparse.FileType('r'), default="config.yaml", nargs="?", help="The name of a YAML config file.") parser.add_argument('filename', metavar='SEGY file', type=str, nargs='?', help='The path to a SEGY file.') parser.add_argument('-o', '--out', metavar='Output file', type=str,
nargs='?', default=0, help= 'The number of dimensions of the input seismic, usually 2 or 3. Overrides config file.' ) parser.add_argument('-d', '--demo', action='store_true', help='Run with the demo file, data/31_81_PR.png.') parser.add_argument('-v', '--version', action='store_true', help='Get the version number.') args = parser.parse_args() if args.version: Notice.info(__version__) sys.exit() Notice.title() target = args.filename with args.config as f: cfg = yaml.safe_load(f) Notice.hr_header("Initializing") Notice.info("config {}".format(args.config.name)) # Fill in 'missing' fields in cfg. cfg = {k: cfg.get(k, v) for k, v in utils.DEFAULTS.items()} cfg['outfile'] = args.out cfg['ndim'] = args.ndim or cfg['ndim'] if args.demo: target = './data/31_81_PR.sgy'
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 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()
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 plot_feature_well(tc, gs): """ Plotting function for the feature well. Args: tc (TransectContainer): The container for the main plot. log (axis): A matplotlib axis. gs (GridSpec): A matplotlib gridspec. """ fname = tc.settings['curve_display'] logs = tc.log.get(tc.feature_well) if not logs: # There was no data for this well, so there won't be a feature plot. Notice.fail("There's no well data for feature well " + tc.feature_well) return gs Z = logs.data['DEPT'] curves = [ 'GR', 'DT', 'DPHI_SAN', 'NPHI_SAN', 'DTS', 'RT_HRLT', 'RHOB', 'DRHO' ] window = tc.settings.get('curve_smooth_window') or 51 ntracks = 5 lw = 1.0 smooth = True naxes = 0 ncurv_per_track = np.zeros(ntracks) if getattr(tc.log, 'striplog', None): ncurv_per_track[0] = 1 for curve in curves: naxes += 1 params = get_curve_params(curve, fname) ncurv_per_track[params['track']] += 1 axss = plt.subplot(gs[2:, -5]) axs0 = [axss, axss.twiny()] axs1 = [plt.subplot(gs[2:, -4])] axs2 = [plt.subplot(gs[2:, -3])] axs3 = [plt.subplot(gs[2:, -2])] axs4 = [plt.subplot(gs[2:, -1])] axs = [axs0, axs1, axs2, axs3, axs4] if getattr(tc.log, 'striplog', None): legend = Legend.default() try: logs.striplog[tc.log.striplog].plot_axis(axs0[0], legend=legend) except KeyError: # In fact, this striplog doesn't exist. Notice.fail("There is no such striplog" + tc.log.striplog) # And move on... axs0[0].set_ylim([Z[-1], 0]) label_shift = np.zeros(len(axs)) for curve in curves: try: values = logs.data[curve] except ValueError: Notice.warning("Curve not present: " + curve) values = np.empty_like(Z) values[:] = np.nan params = get_curve_params(curve, fname) i = params['track'] j = 0 label_shift[i] += 1 linOrlog = params['logarithmic'] sxticks = np.array(params['xticks']) xticks = np.array(sxticks, dtype=float) whichticks = 'major' if linOrlog == 'log': midline = np.log(np.mean(xticks)) xpos = midline whichticks = 'minor' else: midline = np.mean(xticks) xpos = midline if smooth: values = utils.rolling_median(values, window) if curve == 'GR': j = 1 # second axis in first track label_shift[i] = 1 if params['fill_left_cond']: # do the fill for the lithology track axs[i][j].fill_betweenx(Z, params['xleft'], values, facecolor=params['fill_left'], alpha=1.0, zorder=11) if (curve == 'DPHI_SAN') and params['fill_left_cond']: # do the fill for the neutron porosity track try: nphi = utils.rolling_median(logs.data['NPHI_SAN'], window) except ValueError: Notice.warning("No NPHI in this well") nphi = np.empty_like(Z) nphi[:] = np.nan axs[i][j].fill_betweenx(Z, nphi, values, where=nphi >= values, facecolor=params['fill_left'], alpha=1.0, zorder=11) axs[i][j].fill_betweenx(Z, nphi, values, where=nphi <= values, facecolor='#8C1717', alpha=0.5, zorder=12) if curve == 'DRHO': blk_drho = 3.2 values += blk_drho # this is a hack to get DRHO on RHOB scale axs[i][j].fill_betweenx(Z, blk_drho, values, where=nphi <= values, facecolor='#CCCCCC', alpha=0.5, zorder=12) # fill right if params['fill_right_cond']: axs[i][j].fill_betweenx(Z, values, params['xright'], facecolor=params['fill_right'], alpha=1.0, zorder=12) # plot curve axs[i][j].plot(values, Z, color=params['hexcolor'], lw=lw, zorder=13) # set scale of curve axs[i][j].set_xlim([params['xleft'], params['xright']]) # ------------------------------------------------- # # curve labels # ------------------------------------------------- # trans = transforms.blended_transform_factory(axs[i][j].transData, axs[i][j].transData) magic = -Z[-1] / 12. axs[i][j].text(xpos, magic - (magic / 4) * (label_shift[i] - 1), curve, horizontalalignment='center', verticalalignment='bottom', fontsize=12, color=params['hexcolor'], transform=trans) # curve units units = '${}$'.format(params['units']) if label_shift[i] <= 1: axs[i][j].text(xpos, magic * 0.5, units, horizontalalignment='center', verticalalignment='top', fontsize=12, color='k', transform=trans) # ------------------------------------------------- # # scales and tickmarks # ------------------------------------------------- # axs[i][j].set_xscale(linOrlog) axs[i][j].set_ylim([Z[-1], 0]) axs[i][j].axes.xaxis.set_ticks(xticks) axs[i][j].axes.xaxis.set_ticklabels(sxticks, fontsize=8) for label in axs[i][j].axes.xaxis.get_ticklabels(): label.set_rotation(90) axs[i][j].tick_params(axis='x', direction='out') axs[i][j].xaxis.tick_top() axs[i][j].xaxis.set_label_position('top') axs[i][j].xaxis.grid(True, which=whichticks, linewidth=0.25, linestyle='-', color='0.75', zorder=100) axs[i][j].yaxis.grid(True, which=whichticks, linewidth=0.25, linestyle='-', color='0.75', zorder=100) axs[i][j].yaxis.set_ticks(np.arange(0, max(Z), 100)) if i != 0: axs[i][j].set_yticklabels("") # ------------------------------------------------- # # End of curve loop # ------------------------------------------------- # # Add Depth label axs[0][0].text(0, 1.05, 'MD\n$m$', fontsize='10', horizontalalignment='center', verticalalignment='center', transform=axs[0][0].transAxes) axs[0][0].axes.yaxis.get_ticklabels() axs[0][0].axes.xaxis.set_ticklabels('') for label in axs[0][0].axes.yaxis.get_ticklabels(): label.set_rotation(90) label.set_fontsize(10) for label in axs[1][0].axes.xaxis.get_ticklabels(): label.set_rotation(90) label.set_fontsize(10) # Add Tops try: if os.path.exists(tc.tops_file): tops = utils.get_tops(tc.tops_file) topx = get_curve_params('DT', fname) topmidpt = np.amax((topx)['xright']) # plot tops for i in range(ntracks): for mkr, depth in tops.iteritems(): # draw horizontal bars at the top position axs[i][-1].axhline(y=depth, xmin=0.01, xmax=.99, color='b', lw=2, alpha=0.5, zorder=100) # draw text box at the right edge of the last track axs[-1][-1].text(x=topmidpt, y=depth, s=mkr, alpha=0.5, color='k', fontsize='8', horizontalalignment='center', verticalalignment='center', zorder=10000, bbox=dict(facecolor='white', edgecolor='k', alpha=0.25, lw=0.25), weight='light') except AttributeError: Notice.warning("No tops for this well") except TypeError: # We didn't get a tops file so move along. print "No tops for this well" return gs
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 update(self, transect, flat=False): """ Updates the container data to a profile that intersect the transect line. Returns nothing. Sets attributes as a side effect. Args: transect (LineString): A transect line. flat (Bool): Reads data into a flat list instead of sorting by files. """ Notice.info("Updating " + self.__class__.__name__) self.reset_data() # Preprocessing prepared = prep(transect.buffer(self.settings['buffer'])) # Get intersecting points points = filter(prepared.contains, self.lookup.keys()) # Lookup for grouping traces into segy files count = 0 file_lookup = {} for point in points: meta = self.lookup[point] count += 1 f = meta["segyfile"] if f in file_lookup: proj_d = transect.project(point) if proj_d: file_lookup[f]["pos"].append(proj_d) file_lookup[f]["trace"].append(meta["trace"]) file_lookup[f]["point"].append(point) else: file_lookup[f] = {} file_lookup[f]["trace"] = [meta["trace"]] file_lookup[f]["pos"] = [transect.project(point)] file_lookup[f]["point"] = [point] # Read in the chunks from the segy file self.files = [] for segyfile in file_lookup.keys(): self.files.append(os.path.basename(segyfile)) segy = readSEGY(segyfile, unpack_trace_headers=True) traces = file_lookup[segyfile]["trace"] coords = file_lookup[segyfile]["pos"] points = file_lookup[segyfile]["point"] # Get the sort order. idx = sorted(range(len(traces)), key=lambda k: traces[k]) idx = filter(None, idx) coords = [coords[i] for i in idx] data = [segy.traces[traces[i]] for i in idx] if flat: self.data += data self.coords += coords else: self.data.append(data) self.coords.append(coords)
def __init__(self, core=None, notice=None): self.core = core if core is not None else Core() self.info = notice if notice is not None else Notice()
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
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 if __name__ == "__main__": Notice.title() parser = argparse.ArgumentParser(description='Plot a SEGY file.') parser.add_argument("-c", "--config", metavar="config file", type=argparse.FileType('r'), default="config.yaml", nargs="?", help="The name of a YAML config file. Default: config.yaml.") parser.add_argument('filename', metavar='SEGY file', type=str, nargs='?', help='The path to one or more SEGY files. Uses Unix-style pathname expansion.') parser.add_argument('-o', '--out', metavar='output file', type=str,
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
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_feature_well(tc, gs): """ Plotting function for the feature well. Args: tc (TransectContainer): The container for the main plot. log (axis): A matplotlib axis. gs (GridSpec): A matplotlib gridspec. """ fname = tc.settings['curve_display'] logs = tc.log.get(tc.feature_well) if not logs: # There was no data for this well, so there won't be a feature plot. Notice.fail("There's no well data for feature well " + tc.feature_well) return gs Z = logs.data['DEPT'] curves = ['GR', 'DT', 'DPHI_SAN', 'NPHI_SAN', 'DTS', 'RT_HRLT', 'RHOB', 'DRHO'] window = tc.settings.get('curve_smooth_window') or 51 ntracks = 5 lw = 1.0 smooth = True naxes = 0 ncurv_per_track = np.zeros(ntracks) if getattr(tc.log, 'striplog', None): ncurv_per_track[0] = 1 for curve in curves: naxes += 1 params = get_curve_params(curve, fname) ncurv_per_track[params['track']] += 1 axss = plt.subplot(gs[2:, -5]) axs0 = [axss, axss.twiny()] axs1 = [plt.subplot(gs[2:, -4])] axs2 = [plt.subplot(gs[2:, -3])] axs3 = [plt.subplot(gs[2:, -2])] axs4 = [plt.subplot(gs[2:, -1])] axs = [axs0, axs1, axs2, axs3, axs4] if getattr(tc.log, 'striplog', None): legend = Legend.default() try: logs.striplog[tc.log.striplog].plot_axis(axs0[0], legend=legend) except KeyError: # In fact, this striplog doesn't exist. Notice.fail("There is no such striplog" + tc.log.striplog) # And move on... axs0[0].set_ylim([Z[-1], 0]) label_shift = np.zeros(len(axs)) for curve in curves: try: values = logs.data[curve] except ValueError: Notice.warning("Curve not present: "+curve) values = np.empty_like(Z) values[:] = np.nan params = get_curve_params(curve, fname) i = params['track'] j = 0 label_shift[i] += 1 linOrlog = params['logarithmic'] sxticks = np.array(params['xticks']) xticks = np.array(sxticks, dtype=float) whichticks = 'major' if linOrlog == 'log': midline = np.log(np.mean(xticks)) xpos = midline whichticks = 'minor' else: midline = np.mean(xticks) xpos = midline if smooth: values = utils.rolling_median(values, window) if curve == 'GR': j = 1 # second axis in first track label_shift[i] = 1 if params['fill_left_cond']: # do the fill for the lithology track axs[i][j].fill_betweenx(Z, params['xleft'], values, facecolor=params['fill_left'], alpha=1.0, zorder=11) if (curve == 'DPHI_SAN') and params['fill_left_cond']: # do the fill for the neutron porosity track try: nphi = utils.rolling_median(logs.data['NPHI_SAN'], window) except ValueError: Notice.warning("No NPHI in this well") nphi = np.empty_like(Z) nphi[:] = np.nan axs[i][j].fill_betweenx(Z, nphi, values, where=nphi >= values, facecolor=params['fill_left'], alpha=1.0, zorder=11) axs[i][j].fill_betweenx(Z, nphi, values, where=nphi <= values, facecolor='#8C1717', alpha=0.5, zorder=12) if curve == 'DRHO': blk_drho = 3.2 values += blk_drho # this is a hack to get DRHO on RHOB scale axs[i][j].fill_betweenx(Z, blk_drho, values, where=nphi <= values, facecolor='#CCCCCC', alpha=0.5, zorder=12) # fill right if params['fill_right_cond']: axs[i][j].fill_betweenx(Z, values, params['xright'], facecolor=params['fill_right'], alpha=1.0, zorder=12) # plot curve axs[i][j].plot(values, Z, color=params['hexcolor'], lw=lw, zorder=13) # set scale of curve axs[i][j].set_xlim([params['xleft'], params['xright']]) # ------------------------------------------------- # # curve labels # ------------------------------------------------- # trans = transforms.blended_transform_factory(axs[i][j].transData, axs[i][j].transData) magic = -Z[-1] / 12. axs[i][j].text(xpos, magic - (magic/4)*(label_shift[i]-1), curve, horizontalalignment='center', verticalalignment='bottom', fontsize=12, color=params['hexcolor'], transform=trans) # curve units units = '${}$'.format(params['units']) if label_shift[i] <= 1: axs[i][j].text(xpos, magic*0.5, units, horizontalalignment='center', verticalalignment='top', fontsize=12, color='k', transform=trans) # ------------------------------------------------- # # scales and tickmarks # ------------------------------------------------- # axs[i][j].set_xscale(linOrlog) axs[i][j].set_ylim([Z[-1], 0]) axs[i][j].axes.xaxis.set_ticks(xticks) axs[i][j].axes.xaxis.set_ticklabels(sxticks, fontsize=8) for label in axs[i][j].axes.xaxis.get_ticklabels(): label.set_rotation(90) axs[i][j].tick_params(axis='x', direction='out') axs[i][j].xaxis.tick_top() axs[i][j].xaxis.set_label_position('top') axs[i][j].xaxis.grid(True, which=whichticks, linewidth=0.25, linestyle='-', color='0.75', zorder=100) axs[i][j].yaxis.grid(True, which=whichticks, linewidth=0.25, linestyle='-', color='0.75', zorder=100) axs[i][j].yaxis.set_ticks(np.arange(0, max(Z), 100)) if i != 0: axs[i][j].set_yticklabels("") # ------------------------------------------------- # # End of curve loop # ------------------------------------------------- # # Add Depth label axs[0][0].text(0, 1.05, 'MD\n$m$', fontsize='10', horizontalalignment='center', verticalalignment='center', transform=axs[0][0].transAxes) axs[0][0].axes.yaxis.get_ticklabels() axs[0][0].axes.xaxis.set_ticklabels('') for label in axs[0][0].axes.yaxis.get_ticklabels(): label.set_rotation(90) label.set_fontsize(10) for label in axs[1][0].axes.xaxis.get_ticklabels(): label.set_rotation(90) label.set_fontsize(10) # Add Tops try: if os.path.exists(tc.tops_file): tops = utils.get_tops(tc.tops_file) topx = get_curve_params('DT', fname) topmidpt = np.amax((topx)['xright']) # plot tops for i in range(ntracks): for mkr, depth in tops.iteritems(): # draw horizontal bars at the top position axs[i][-1].axhline(y=depth, xmin=0.01, xmax=.99, color='b', lw=2, alpha=0.5, zorder=100) # draw text box at the right edge of the last track axs[-1][-1].text(x=topmidpt, y=depth, s=mkr, alpha=0.5, color='k', fontsize='8', horizontalalignment='center', verticalalignment='center', zorder=10000, bbox=dict(facecolor='white', edgecolor='k', alpha=0.25, lw=0.25), weight='light') except AttributeError: Notice.warning("No tops for this well") except TypeError: # We didn't get a tops file so move along. print "No tops for this well" return gs
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 if __name__ == "__main__": Notice.title() parser = argparse.ArgumentParser(description='Plot a SEGY file.') parser.add_argument("-c", "--config", metavar="config file", type=argparse.FileType('r'), default="config.yaml", nargs="?", help="The name of a YAML config file.") parser.add_argument('filename', metavar='SEGY file', type=str, nargs='?', help='The path to a SEGY file.') parser.add_argument('-o', '--out', metavar='Output file', type=str,