Source code for xtgeo.plot.xtmap

"""Module for map plots of surfaces, using matplotlib."""

import numpy as np
import numpy.ma as ma

from xtgeo.common.log import null_logger

from .baseplot import BasePlot

logger = null_logger(__name__)


[docs] class Map(BasePlot): """Class for plotting a map, using matplotlib."""
[docs] def __init__(self): """The constructor method for a Map object.""" super().__init__() clsname = f"{type(self).__module__}.{type(self).__name__}" logger.info(clsname) self._wells = None self._surface = None self._tight = False self._wfence = None self._showok = True # to indicate if plot is OK to show self._legendtitle = "Map"
# ========================================================================= # Properties # ========================================================================= @property def pagesize(self): """Returns page size.""" return self._pagesize # ========================================================================= # Functions methods (public) # =========================================================================
[docs] def plot_surface( self, surf, minvalue=None, maxvalue=None, contourlevels=None, xlabelrotation=None, colormap=None, logarithmic=False, ): """Input a surface and plot it.""" # need a deep copy to avoid changes in the original surf logger.info("The key contourlevels %s is not in use", contourlevels) usesurf = surf.copy() if usesurf.yflip < 0: usesurf.swapaxes() if abs(surf.rotation) > 0.001: usesurf.unrotate() xi, yi, zi = usesurf.get_xyz_values() zimask = ma.getmaskarray(zi).copy() # yes need a copy! legendticks = None if minvalue is not None and maxvalue is not None: minv = float(minvalue) maxv = float(maxvalue) step = (maxv - minv) / 10.0 legendticks = [] for i in range(10 + 1): llabel = float(f"{minv + step * i:9.4f}") legendticks.append(llabel) zi.unshare_mask() zi[zi < minv] = minv zi[zi > maxv] = maxv # need to restore the mask: zi.mask = zimask # note use surf.min, not usesurf.min here ... notetxt = ( "Note: map values are truncated from [" + str(surf.values.min()) + ", " + str(surf.values.max()) + "] " + "to interval [" + str(minvalue) + ", " + str(maxvalue) + "]" ) self._fig.text(0.99, 0.02, notetxt, ha="right", va="center", fontsize=8) logger.info("Legendticks: %s", legendticks) if minvalue is None: minvalue = usesurf.values.min() if maxvalue is None: maxvalue = usesurf.values.max() # this will override current instance colormap locally, and is # therefore reset afterwards keepcolor = self.colormap if colormap is not None: self.colormap = colormap levels = np.linspace(minvalue, maxvalue, self.contourlevels) logger.debug("Number of contour levels: %s", levels) import matplotlib.pyplot as plt plt.setp(self._ax.xaxis.get_majorticklabels(), rotation=xlabelrotation) # zi = ma.masked_where(zimask, zi) # zi = ma.masked_greater(zi, xtgeo.UNDEF_LIMIT) logger.info("Current colormap is %s, requested is %s", self.colormap, colormap) logger.info("Current colormap name is %s", self.colormap.name) uselevels = levels if ma.std(zi) > 1e-07 else 1 try: if logarithmic is False: locator = None ticks = legendticks im = self._ax.contourf( xi, yi, zi, uselevels, locator=locator, cmap=self.colormap ) else: logger.info("use LogLocator") import matplotlib as mpl locator = mpl.ticker.LogLocator() ticks = None uselevels = None im = self._ax.contourf(xi, yi, zi, locator=locator, cmap=self.colormap) self._fig.colorbar(im, ticks=ticks) except ValueError as err: logger.warning("Could not make plot: %s", err) plt.gca().set_aspect("equal", adjustable="box") self.colormap = keepcolor
[docs] def plot_faults( self, fpoly, idname="POLY_ID", color="k", edgecolor="k", alpha=0.7, linewidth=0.8, ): """Plot the faults. Args: fpoly (object): A XTGeo Polygons object idname (str): Name of column which has the faults ID color (c): Fill color model c according to Matplotlib_ edgecolor (c): Edge color according to Matplotlib_ alpha (float): Degree of opacity linewidth (float): Line width .. _Matplotlib: http://matplotlib.org/api/colors_api.html """ import matplotlib as mpl aff = fpoly.get_dataframe(copy=False).groupby(idname) for name, _group in aff: # make a dataframe sorted on faults (groupname) myfault = aff.get_group(name) # make a list [(X,Y) ...]; af = list(zip(myfault["X_UTME"].values, myfault["Y_UTMN"].values)) px = mpl.patches.Polygon( af, alpha=alpha, color=color, ec=edgecolor, lw=linewidth, ) if px.get_closed(): self._ax.add_artist(px) else: IOError(f"A polygon is not closed: {px}")
[docs] def plot_polygons(self, fpoly, idname="POLY_ID", color="k", linewidth=0.8): """Plot a polygons instance. Args: fpoly (object): A XTGeo Polygons object idname (str): Name of column which has the faults ID color (c): Line color model c according to Matplotlib_ linewidth (float): Line width .. _Matplotlib: http://matplotlib.org/api/colors_api.html """ aff = fpoly.get_dataframe(copy=False).groupby(idname) for _name, group in aff: # make a dataframe sorted on groupname pname = fpoly.name xarr = group[fpoly.xname].values yarr = group[fpoly.yname].values self._ax.plot(xarr, yarr, label=pname, lw=linewidth, color=color) self._ax.legend()
[docs] def plot_points(self, points): """Plot a points set on the map. This can be be useful e.g. for plotting the underlying point set that makes a gridded map. Args: points (Points): A XTGeo Points object X Y VALUE """ # This function is "in prep" dataframe = points.get_dataframe(copy=False) self._ax.scatter( dataframe["X_UTME"].values, dataframe["Y_UTMN"].values, marker="x" )
[docs] def plot_wells(self, wells): """Plot wells on the map. Args: wells (Wells): A XTGeo Wells object (contains a number of Well instances). """ for well in wells.wells: dataframe = well.get_dataframe(copy=False) xval = dataframe["X_UTME"].values yval = dataframe["Y_UTMN"].values self._ax.plot(xval, yval) self._ax.annotate(well.name, xy=(xval[-1], yval[-1]))