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[, attribution])

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

aster_datetime_from_name(name)

Return the acquisition datetime encoded in an ASTER L1A name, or None.

describe_pair(pair_number, pair_directory)

Human-readable label for one multi-view pair.

detect_planetary_body(dem_fn)

Detect planetary body from DEM CRS WKT.

detect_satellite_attribution(directory)

Return the imagery rights-holder name for XML files in a directory.

detect_vantor_satellite(directory)

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

find_pair_directories(directory)

Find the per-pair subdirectories of an ASP multi-view stereo run.

get_acquisition_dates(directory[, extra_dirs])

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

get_pair_images(pair_directory)

Recover the left/right image filenames of one multi-view pair.

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, ...])

Find files matching pattern(s) in a directory.

nmad(a[, c])

Normalized Median Absolute Deviation.

resolve_directory_or_prefix(base_directory, value)

Resolve a CLI value that may name a directory or an ASP output prefix.

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, attribution=None)#

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 an attribution (i.e. the source imagery has a recognized rights-holder). 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).

attribution = None#
cb#
clim_perc = (2, 98)#
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 a copyright text overlay to the bottom-right of a matplotlib axes.

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

  • attribution (str, optional) – Rights-holder name to attribute (e.g. "Vantor", "Airbus DS"), default is "Vantor".

asp_plot.utils.aster_datetime_from_name(name)#

Return the acquisition datetime encoded in an ASTER L1A name, or None.

Parameters:

name (str) – A file or directory name that may be (or contain) an AST_L1A_* granule name.

Returns:

The encoded capture time, or None if name carries no granule name or encodes an impossible date.

Return type:

datetime.datetime or None

asp_plot.utils.describe_pair(pair_number, pair_directory)#

Human-readable label for one multi-view pair.

Returns "Pair N: <left> <-> <right>" when the pair’s images can be recovered from its N-stereo.default (see get_pair_images()), else just "Pair N". The reference (left) image is the same for every pair of an ASP multi-view run.

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_satellite_attribution(directory)#

Return the imagery rights-holder name for XML files in a directory.

This is an attribution check, distinct from sensor/reader identity: it decides which copyright overlay applies (see add_copyright_overlay() and Plotter). It is named for the rights-holder and matches any of that owner’s satellites. Sensor identity — which reader parses the XML — is a separate concern handled by asp_plot.sensors; the two names intentionally differ and should not be “reconciled” into one.

Recognized rights-holders:

  • "Vantor" (formerly Maxar/DigitalGlobe): an XML camera file carries a SATID matching one of VANTOR_SATID_PREFIXES.

  • "Airbus DS": an Airbus DIMAP document (Pléiades, Pléiades Neo, SPOT; root tag Dimap_Document) is present.

Parameters:

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

Returns:

The rights-holder name for the copyright overlay, or None when no attributed imagery is detected.

Return type:

str or None

asp_plot.utils.detect_vantor_satellite(directory)#

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

Thin wrapper around detect_satellite_attribution() kept for backward compatibility.

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.find_pair_directories(directory)#

Find the per-pair subdirectories of an ASP multi-view stereo run.

An ASP multi-view run keeps its per-pair intermediate products (aligned images, match files, disparities) in <prefix>-pairN/ subdirectories of the stereo directory, with only the joint products (PC/DEM/IntersectionErr) at the top level. Files inside pair N are prefixed N- (e.g. run-pair1/1-L_sub.tif).

Parameters:

directory (str) – Stereo directory to search in.

Returns:

(pair_number, pair_directory) tuples sorted by pair number; empty for a standard (two-image) stereo run.

Return type:

list of tuple

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), Airbus DIMAP product XMLs (acquisition start time from the refined geometric model), 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_pair_images(pair_directory)#

Recover the left/right image filenames of one multi-view pair.

Parses the N-stereo.default configuration copy that ASP writes into each <prefix>-pairN/ subdirectory: its # > stereo_parse ... line records the sub-run’s full command, whose first two .tif arguments are the pair’s left (reference) and right images. The N-info.txt file is not used because it lists all images of the joint run, not the pair’s.

Parameters:

pair_directory (str) – One <prefix>-pairN/ subdirectory of a multi-view stereo run.

Returns:

(left_image, right_image) basenames, or None if they could not be recovered.

Return type:

tuple of str or None

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, quiet=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.

  • quiet (bool, optional) – If True, skip the no-match warning — for lookups where absence is an expected layout (e.g. top-level products of a multi-view stereo run). 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.resolve_directory_or_prefix(base_directory, value)#

Resolve a CLI value that may name a directory or an ASP output prefix.

ASP tools take output prefixes (--bundle-adjust-prefix ba/run), while asp_plot historically took the containing directory (ba). This accepts both: an existing directory is returned as-is with no stem, while a prefix is split into its directory and run stem so file globs can be narrowed to that run (run-final_residuals_pointmap.csv).

Parameters:
  • base_directory (str) – Directory the value is relative to (the report’s --directory).

  • value (str or None) – Directory or prefix, relative to base_directory. Trailing slashes are stripped.

Returns:

(subdirectory, stem); stem is None when value names a directory. A separator-free value that is not an existing directory is kept as a directory so downstream “not found” errors point at it.

Return type:

tuple of (str or None, str or None)

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.ASTER_L1A_NAME_RE#
asp_plot.utils.VANTOR_SATID_PREFIXES = ('WV', 'GE', 'QB', 'IK')#
asp_plot.utils.logger#