asp_plot.utils#

Attributes#

Classes#

ColorBar

Utility class for managing colorbar limits and normalization.

Plotter

Base class for plotting array and vector data.

Raster

Utility class for raster data handling and processing.

Functions#

add_copyright_overlay(ax)

Add Vantor copyright text overlay to the bottom-right of a matplotlib axes.

detect_planetary_body(dem_fn)

Detect planetary body from DEM CRS WKT.

detect_vantor_satellite(directory)

Check if XML files in directory indicate a Vantor-owned satellite.

get_acquisition_dates(directory[, extra_dirs])

Extract scene acquisition date(s) from metadata in a processing directory.

get_planetary_bounds(dem_fn[, body])

Get DEM bounds in planetocentric lon/lat with 0-360 east-positive longitude.

get_utm_epsg(lon, lat)

Get the UTM EPSG code for a given longitude and latitude.

get_xml_tag(xml, tag[, all])

Extract value(s) from XML tag(s).

glob_file(directory, *patterns[, all_files, recursive])

Find files matching pattern(s) in a directory.

nmad(a[, c])

Normalized Median Absolute Deviation.

run_subprocess_command(command)

Run a subprocess command and stream output.

save_figure(fig[, save_dir, fig_fn, dpi])

Save a matplotlib figure to a file.

show_existing_figure(filename)

Display an existing figure from a file.

Module Contents#

class asp_plot.utils.ColorBar(perc_range=(2, 98), symm=False)#

Utility class for managing colorbar limits and normalization.

This class handles color scaling for data visualization, including percentile-based limits, symmetric color mapping, and logarithmic normalization.

perc_range#

Percentile range (min, max) for color limits, default is (2, 98)

Type:

tuple

symm#

Whether to use symmetric color limits, default is False

Type:

bool

clim#

Current color limits (min, max), None until calculated

Type:

tuple or None

Examples

>>> cb = ColorBar(perc_range=(5, 95), symm=True)
>>> clim = cb.get_clim(data)
>>> norm = cb.get_norm(lognorm=False)
>>> im = ax.imshow(data, norm=norm)
>>> plt.colorbar(im, extend=cb.get_cbar_extend(data))
find_common_clim(inputs)#

Find common color limits across multiple inputs.

Parameters:

inputs (list) – List of input data arrays

Returns:

Common color limits (min, max)

Return type:

tuple

get_cbar_extend(input, clim=None)#

Determine colorbar extension mode based on data and limits.

Parameters:
  • input (array_like) – Input data array

  • clim (tuple, optional) – Color limits (min, max), if None uses current or calculated limits

Returns:

Colorbar extension mode: ‘neither’, ‘min’, ‘max’, or ‘both’

Return type:

str

get_clim(input)#

Calculate color limits based on data percentiles.

Parameters:

input (array_like) – Input data array (can be masked)

Returns:

Color limits (min, max)

Return type:

tuple

get_norm(lognorm=False, clim=None)#

Get normalization for colormap.

Parameters:
  • lognorm (bool, optional) – Whether to use logarithmic normalization, default is False

  • clim (tuple, optional) – Explicit color limits (min, max) to normalize against. If None (default), falls back to self.clim (set by a prior get_clim or find_common_clim call). Passing clim explicitly avoids relying on the mutable self.clim state.

Returns:

Normalization object for matplotlib colormaps

Return type:

matplotlib.colors.Normalize

symm_clim()#

Make color limits symmetric around zero.

Returns:

Symmetric color limits (-max, max)

Return type:

tuple

clim = None#
perc_range = (2, 98)#
symm = False#
class asp_plot.utils.Plotter(clim_perc=(2, 98), lognorm=False, title=None, is_vantor=False)#

Base class for plotting array and vector data.

This class provides common plotting functionality, including color management, colorbar customization, and basemap addition.

clim_perc#

Percentile range for color limits, default is (2, 98)

Type:

tuple

lognorm#

Whether to use logarithmic color normalization, default is False

Type:

bool

title#

Plot title, default is None

Type:

str or None

cb#

ColorBar instance for managing color scaling

Type:

ColorBar

Examples

>>> plotter = Plotter(clim_perc=(5, 95), title="My Plot")
>>> fig, ax = plt.subplots()
>>> plotter.plot_array(ax, data, cmap="viridis", cbar_label="Elevation (m)")
plot_array(ax, array, clim=None, cmap='inferno', add_cbar=True, cbar_label=None, alpha=1, copyright=False)#

Plot a 2D array on the given axes.

Parameters:
  • ax (matplotlib.axes.Axes) – Axes to plot on

  • array (array_like) – 2D array to plot

  • clim (tuple, optional) – Color limits (min, max), default is None (auto-calculated)

  • cmap (str or matplotlib.colors.Colormap, optional) – Colormap to use, default is “inferno”

  • add_cbar (bool, optional) – Whether to add a colorbar, default is True

  • cbar_label (str, optional) – Label for the colorbar, default is None

  • alpha (float, optional) – Transparency (0-1), default is 1

  • copyright (bool, optional) – Whether to add a satellite-imagery copyright overlay, default is False. The overlay is only drawn when this is True and the Plotter was created with is_vantor=True (i.e. the source imagery is from a Vantor-owned satellite). Set on the panels that display the optical scenes.

Returns:

The plotted image

Return type:

matplotlib.image.AxesImage

Notes

If clim is None, color limits are calculated using the ColorBar instance.

plot_geodataframe(ax, gdf, column_name, clim=None, cmap='inferno', cbar_label=None, **ctx_kwargs)#

Plot a GeoDataFrame with color mapping and optional basemap.

Parameters:
  • ax (matplotlib.axes.Axes) – Axes to plot on

  • gdf (geopandas.GeoDataFrame) – GeoDataFrame to plot

  • column_name (str) – Column name to use for color mapping

  • clim (tuple, optional) – Color limits (min, max), default is None (auto-calculated)

  • cmap (str or matplotlib.colors.Colormap, optional) – Colormap to use, default is “inferno”

  • cbar_label (str, optional) – Label for the colorbar, default is None

  • **ctx_kwargs – Additional keyword arguments for contextily.add_basemap

Returns:

The axes with the plot

Return type:

matplotlib.axes.Axes

Notes

If ctx_kwargs are provided, adds a basemap using contextily.

static plot_missing(ax, message='One or more required\nfiles are missing')#

Draw a centered “missing files” placeholder on an empty axes.

Replaces the placeholder text block duplicated across plotting methods.

Parameters:
  • ax (matplotlib.axes.Axes) – Axes to annotate.

  • message (str, optional) – Placeholder text, default “One or more requirednfiles are missing”.

save(fig, save_dir=None, fig_fn=None, tight_layout=True, **kwargs)#

Finalize and optionally save a figure.

Centralizes the save tail duplicated across plotting methods: an optional fig.tight_layout() followed by a write to disk only when both save_dir and fig_fn are given.

Parameters:
  • fig (matplotlib.figure.Figure) – Figure to finalize.

  • save_dir (str or None, optional) – Directory to save the figure, default is None (don’t save).

  • fig_fn (str or None, optional) – Filename for the saved figure, default is None (don’t save).

  • tight_layout (bool, optional) – Whether to call fig.tight_layout() first, default is True. Pass False for figures using absolute (add_axes) layouts.

  • **kwargs – Additional keyword arguments forwarded to save_figure (e.g. dpi).

cb#
clim_perc = (2, 98)#
is_vantor = False#
lognorm = False#
title = None#
class asp_plot.utils.Raster(fn, downsample=1)#

Utility class for raster data handling and processing.

This class provides convenient methods for reading, processing, and analyzing raster data using rasterio and GDAL.

fn#

Path to the raster file

Type:

str

ds#

Open rasterio dataset

Type:

rasterio.DatasetReader

downsample#

Downsampling factor for reading data

Type:

int or float

data#

Cached raster data (loaded on demand)

Type:

numpy.ma.MaskedArray or None

transform#

Affine transform for the raster (adjusted if downsampled)

Type:

affine.Affine

Examples

>>> raster = Raster("path/to/dem.tif")
>>> data = raster.read_array()
>>> hillshade = raster.hillshade()
>>> epsg = raster.get_epsg_code()
>>> gsd = raster.get_gsd()
>>> # Downsample for faster plotting
>>> raster_ds = Raster("path/to/dem.tif", downsample=10)
>>> raster_ds.plot(ax=ax, cmap="viridis")
compute_difference(second_fn, save=False)#

Compute the difference between this raster and another.

Parameters:
  • second_fn (str) – Path to the second raster file

  • save (bool, optional) – Whether to save the difference raster to disk, default is False

Returns:

Difference array (second_raster - this_raster)

Return type:

numpy.ndarray

Notes

Aligns rasters to the grid of the second raster before differencing. If save=True, saves the difference raster with “_diff.tif” suffix.

get_bounds(latlon=True, json_format=True)#

Get the geographic bounds of the raster.

Parameters:
  • latlon (bool, optional) – Whether to return bounds in latitude/longitude, default is True

  • json_format (bool, optional) – Whether to return bounds in GeoJSON-like format, default is True

Returns:

If json_format=True: list of corner coordinates as dictionaries If json_format=False: tuple of (min_x, min_y, max_x, max_y)

Return type:

list or tuple

get_epsg_code()#

Get the EPSG code for the raster’s coordinate reference system.

Returns:

EPSG code

Return type:

int

Notes

If the CRS has no exact EPSG match (e.g. a compound or 3D-promoted CRS such as “EPSG:32610+EPSG:4979”), falls back to the EPSG code of the horizontal (2D) component.

get_gsd()#

Get the ground sample distance (resolution) of the raster.

Returns:

Ground sample distance (pixel size) in raster units

Return type:

float

get_ndv()#

Get the no-data value for the raster.

Returns:

No-data value

Return type:

float or int

Notes

If no-data value is not defined in the raster metadata, tries to infer it from the first pixel value.

get_utm_epsg_code()#

Estimate the appropriate UTM EPSG code for this raster’s location.

Uses the raster’s center coordinates to determine the correct UTM zone via the PROJ database. Works regardless of the raster’s current CRS (geographic or projected).

Returns:

UTM EPSG code (e.g., 32616 for UTM Zone 16N)

Return type:

int

hillshade(shape=None)#

Generate a hillshade from the raster.

Parameters:

shape (tuple of int or None, optional) – Target (height, width) for the hillshade. When given, the source is resampled to this shape (in memory) before computing the hillshade, so the result matches a downsampled DEM array. When None (default), the hillshade is computed at full resolution and a cached *_hs.tif file is reused if present.

Returns:

Hillshade array (nodata masked at 0).

Return type:

numpy.ma.MaskedArray

Notes

At full resolution this first checks for a sibling *_hs.tif file and reuses it; otherwise it generates the hillshade using GDAL (computeEdges). The cache is skipped when shape is given, since the cached file is full-resolution.

static load_and_diff_rasters(first_fn, second_fn)#

Load two rasters, align them, and compute their difference.

Parameters:
  • first_fn (str) – Path to the first raster file

  • second_fn (str) – Path to the second raster file (used as reference grid)

Returns:

(difference array (second_raster - first_raster), transform, CRS, nodata)

Return type:

tuple

Notes

The first raster is reprojected and resampled to match the second raster’s grid before differencing. Both rasters are cropped to their intersection first (matching geoutils behavior). Uses rioxarray for efficient reprojection.

read_array(b=1, extent=False)#

Read raster data as a numpy masked array.

Parameters:
  • b (int, optional) – Band number to read, default is 1

  • extent (bool, optional) – Whether to return extent information for plotting, default is False

Returns:

If extent=False: masked array of raster data If extent=True: tuple of (masked array, extent)

Return type:

numpy.ma.MaskedArray or tuple

Notes

No-data values are properly masked, and invalid values are fixed. If downsample > 1, reads a downsampled version of the raster.

read_raster_subset(bbox, b=1)#

Read a subset of raster data defined by a bounding box.

Parameters:
  • bbox (tuple) – Bounding box in the format (ul_x, lr_y, lr_x, ul_y) (upper-left x, lower-right y, lower-right x, upper-left y)

  • b (int, optional) – Band number to read, default is 1

Returns:

Masked array of subset raster data with nodata values masked

Return type:

numpy.ma.MaskedArray

Notes

No-data values are properly masked, similar to read_array()

static save_raster(data, output_fn, reference_fn, dtype=None, nodata=None)#

Save a numpy array as a GeoTIFF using a reference raster’s profile.

Parameters:
  • data (numpy.ndarray) – Data array to save

  • output_fn (str) – Output file path

  • reference_fn (str) – Reference raster file to copy metadata from

  • dtype (numpy.dtype or str, optional) – Data type for output raster, default is None (uses float32)

  • nodata (int or float, optional) – No-data value for output raster, default is None (uses reference nodata)

Return type:

None

Notes

Copies CRS, transform, and other metadata from the reference raster. Useful for saving processed data that should align with an existing raster.

Examples

>>> diff = raster1.data - raster2.data
>>> Raster.save_raster(diff, "difference.tif", reference_fn="raster2.tif")
property data#

Lazy-loaded raster data.

Returns:

Raster data (loaded on first access)

Return type:

numpy.ma.MaskedArray

downsample = 1#
fn#
property transform#

Get the affine transform for the raster.

Returns:

Affine transform (adjusted for downsampling if applicable) or None if the transform is identity (not georeferenced)

Return type:

affine.Affine

Add Vantor copyright text overlay to the bottom-right of a matplotlib axes.

Parameters:

ax (matplotlib.axes.Axes) – The axes to add the copyright overlay to.

asp_plot.utils.detect_planetary_body(dem_fn)#

Detect planetary body from DEM CRS WKT.

Inspects the DATUM/ELLIPSOID fields of the DEM’s CRS WKT string to determine the planetary body. ASP’s point2dem consistently encodes the body name in these fields regardless of projection type.

Parameters:

dem_fn (str) – Path to the DEM file.

Returns:

One of "earth", "moon", or "mars".

Return type:

str

asp_plot.utils.detect_vantor_satellite(directory)#

Check if XML files in directory indicate a Vantor-owned satellite.

This is an attribution check, distinct from sensor/reader identity: it decides whether the “© Vantor” copyright overlay applies (see add_copyright_overlay() and Plotter). It is named for the rights-holder (Vantor, formerly Maxar/DigitalGlobe) and matches any Vantor satellite, not just WorldView. Sensor identity — which reader parses the XML — is a separate concern handled by the WorldView-named abstraction in asp_plot.sensors; the two names intentionally differ and should not be “reconciled” into one.

A scene is considered Vantor-owned when an XML camera file carries a SATID matching one of VANTOR_SATID_PREFIXES.

Parameters:

directory (str) – Path to directory containing XML camera model files.

Returns:

True if any XML file contains a Vantor-heritage SATID (e.g. WV01-WV04, WVLG, GE01, QB02).

Return type:

bool

asp_plot.utils.get_acquisition_dates(directory, extra_dirs=None)#

Extract scene acquisition date(s) from metadata in a processing directory.

Looks for WorldView/Maxar-style XML camera files (using the FIRSTLINETIME tag) and ASTER L1A file or directory names (which encode the capture date in the filename: AST_L1A_<prodcode><MMDDYYYY><HHMMSS>_...). Returns a sorted, deduplicated list of date strings. An empty list is returned if nothing is found.

Parameters:
  • directory (str) – Top-level ASP processing directory to search (non-recursive for XMLs, recursive for ASTER L1A filenames).

  • extra_dirs (list of str, optional) – Additional directories to search non-recursively for XML files (e.g. a stereo or bundle-adjust subdirectory).

Returns:

Acquisition datetime strings formatted as YYYY-MM-DD HH:MM:SS UTC.

Return type:

list of str

asp_plot.utils.get_planetary_bounds(dem_fn, body=None)#

Get DEM bounds in planetocentric lon/lat with 0-360 east-positive longitude.

Reprojects the DEM’s native-CRS bounding box into the body’s geographic coordinate system, which is the format required by the ODE GDS REST API.

Parameters:
  • dem_fn (str) – Path to the DEM file.

  • body (str or None, optional) – Planetary body ("earth", "moon", "mars"). If None, auto-detected from the DEM CRS via detect_planetary_body().

Returns:

Dictionary with keys westernlon, easternlon, minlat, maxlat in planetocentric coordinates with east-positive 0-360 longitude.

Return type:

dict

asp_plot.utils.get_utm_epsg(lon, lat)#

Get the UTM EPSG code for a given longitude and latitude.

Uses the PROJ database to determine the correct UTM zone.

Parameters:
  • lon (float) – Longitude in degrees

  • lat (float) – Latitude in degrees

Returns:

UTM EPSG code (e.g., 32616 for UTM Zone 16N)

Return type:

int

asp_plot.utils.get_xml_tag(xml, tag, all=False)#

Extract value(s) from XML tag(s).

Parses an XML file and extracts the content of specified tag(s).

Parameters:
  • xml (str) – Path to XML file

  • tag (str) – XML tag to extract

  • all (bool, optional) – If True, find all occurrences of the tag; if False, find first occurrence Default is False

Returns:

If all=False: string content of the first matching tag If all=True: list of string contents for all matching tags

Return type:

str or list

Raises:

ValueError – If the tag is not found in the XML file

Examples

>>> satid = get_xml_tag("path/to/file.xml", "SATID")
>>> ephemeris = get_xml_tag("path/to/file.xml", "EPHEMLIST", all=True)
asp_plot.utils.glob_file(directory, *patterns, all_files=False, recursive=False)#

Find files matching pattern(s) in a directory.

Searches a directory for files matching one or more glob patterns. By default, returns the first matching file found. If all_files is True, returns all matching files.

Parameters:
  • directory (str) – Directory to search in

  • *patterns (str) – One or more glob patterns (e.g., “*.tif”, “*DEM.tif”)

  • all_files (bool, optional) – If True, return all matching files; if False, return only the first match. Default is False.

  • recursive (bool, optional) – If True, enable recursive globbing so a ** in a pattern matches this directory and all subdirectories. Default is False.

Returns:

If all_files is False, returns the first matching file path or None if no matches If all_files is True, returns a list of matching file paths or None if no matches

Return type:

str or list or None

Examples

>>> first_tif = glob_file("/path/to/dir", "*.tif")
>>> all_tifs = glob_file("/path/to/dir", "*.tif", all_files=True)
>>> dem_file = glob_file("/path/to/dir", "*-DEM.tif", "*_dem.tif")
>>> nested = glob_file("/path/to/dir", "**/*.xml", all_files=True, recursive=True)
asp_plot.utils.nmad(a, c=1.4826)#

Normalized Median Absolute Deviation.

A robust measure of dispersion that is less sensitive to outliers than the standard deviation: c * median(|a - median(a)|).

asp_plot.utils.run_subprocess_command(command)#

Run a subprocess command and stream output.

Executes a shell command using subprocess, streaming its output in real time to the console.

Parameters:

command (list or str) – Command to execute as a list of arguments or a single string

Returns:

Return code from the command (0 for success)

Return type:

int

Notes

This function prints command output in real time and indicates whether command execution was successful.

Examples

>>> run_subprocess_command(["ls", "-la"])
>>> run_subprocess_command("dg_mosaic --skip-tif-gen --output-prefix output_file input_files")
asp_plot.utils.save_figure(fig, save_dir=None, fig_fn=None, dpi=None)#

Save a matplotlib figure to a file.

Saves a figure to the specified directory with the given filename. Creates the directory if it doesn’t exist.

Parameters:
  • fig (matplotlib.figure.Figure) – Figure object to save

  • save_dir (str, optional) – Directory to save the figure in

  • fig_fn (str, optional) – Filename for the saved figure

  • dpi (int or None, optional) – Resolution in dots per inch. Default is None, which uses the figure’s own DPI setting.

Return type:

None

Raises:

ValueError – If save_dir or fig_fn is not provided

Examples

>>> fig, ax = plt.subplots()
>>> ax.plot([1, 2, 3], [1, 2, 3])
>>> save_figure(fig, save_dir='plots', fig_fn='line_plot.png')
asp_plot.utils.show_existing_figure(filename)#

Display an existing figure from a file.

Loads and displays an image file using matplotlib.

Parameters:

filename (str) – Path to the image file

Returns:

Displays the figure in the current matplotlib figure

Return type:

None

Notes

If the file does not exist, a message is printed and no figure is displayed.

asp_plot.utils.VANTOR_SATID_PREFIXES = ('WV', 'GE', 'QB', 'IK')#
asp_plot.utils.logger#