Skip to main content

geoh5vista: a Geoh5 <> PyVista (VTK) interface

A PyVista (and VTK) interface for the Geoh5 file format providing Python 3D visualization and useable data structures for processing datasets in the geoh5 specification. This library allows geologists to access the powerful tools available in the PyVista ecosystem for their data stored in the Geoh5 format.

The structure and interfaces of this project are heavily inspired by the 'omfvista' package, which provides a similar interface for the 'omf' format.

omfvista package: https://github.com/OpenGeoVis/omfvista

Geoh5 Python interface package: https://mirageoscience-geoh5py.readthedocs-hosted.com/en/stable/index.html

Documentation is hosted at https://github.com/derek-kinakin/geoh5vista

Installation

From PyPI

pip install geoh5vista

From Github

pip install git+https://github.com/derek-kinakin/geoh5vista.git

Current Status of Supported Geoh5 Entities

This table provides the list of supported entities. Read from and write to Geoh5 support is the goal for each entity.

Geoh5 Entity PyVista Object Read from Geoh5 Write to Geoh5 Notes
Workspace MultiBlock Yes Yes A multiblock containing several object can be written to Geoh5
Points PointSet Yes Yes
Curve PolyData Yes Yes
Surface PolyData Yes Yes
2D Grid ImageData Yes Yes 2D grid with dimensions nU x nV x 1
Block model ImageData / StructuredGrid Yes Yes ImageData for uniform spacing within each axis; StructuredGrid for variable spacing
Drillholes Nested MultiBlock Yes No Independent collars, traces, and sampling tables; optional merged PolyData view
Slicer PolyData Yes No Geometry available as object metadata

Block models with uniform spacing within each axis are read as ImageData; U, V, and Z spacing may differ from each other. Models with variable spacing within any axis are read as StructuredGrid with explicit, rotated point coordinates. Use blockmodel_to_vtk to select the representation automatically, or blockmodel_grid_geom_to_image_vtk / blockmodel_grid_geom_to_structured_vtk for geometry only.

StructuredGrid block models support common dataset attributes and filters such as cell_data, user_dict, bounds, slicing, clipping, thresholding, contouring, cell centres, and structured subsets. ImageData-only attributes and filters, such as spacing, origin, direction_matrix, image smoothing, FFT, and morphology, are not available. For positive cell volumes, descending delimiter axes are reversed in StructuredGrid index order; cell data are reordered to remain spatially aligned.

Block-model imports preserve rotation, nonzero delimiter offsets, and descending-axis cell-data alignment. ImageData uses the transformed first delimiter corner as its origin, positive spacing, and signed axis directions. The geometry helper applies no rotation when rotation_matrix=None; blockmodel_to_vtk applies the model's rotation automatically.

Block-model exports preserve horizontal rotation, signed axis directions, and cell-data alignment. Tilted, sheared, or scaled direction matrices are rejected. Export normalizes delimiters to start at zero and moves their offset into the model origin. A reversed U axis is represented by an equivalent rotation and signed V spacing, so geometry and data are preserved, but the original origin, delimiter offsets, and rotation representation may differ after a round trip.

StructuredGrid exports infer rotation and delimiters from the current point coordinates. Points must form an orthogonal rectilinear lattice with horizontal U/V axes and a vertical Z axis; tilted, sheared, warped, or degenerate grids are rejected. The first grid node becomes the model origin.

Multi-component arrays

geoh5 data stores one value per vertex or cell, so multi-component VTK point and cell arrays are split on export. Three-component arrays such as Normals become <name>_x, <name>_y and <name>_z; other widths use <name>_0, <name>_1, and so on. Unsigned 8-bit RGB/RGBA arrays are written as native geoh5 colour data on entities that support it (Points, Curve, Grid2D, BlockModel); on Surfaces they are split into components like other arrays. Split components are not recombined on import.

Geoh5 Entity Metadata Support

add_entity_metadata stores metadata in ob.user_dict["geoh5"] using schema version 1. The source dictionary contains the original UID, entity class (entity_type), and parent UID. The display dictionary contains name, colour (RGB integers), and visible (a boolean). Integer/NumPy visibility values from geoh5py are normalized (0 = hidden); unrecognized values are treated as visible with a warning.

geoh5vista.data.restore_entity_metadata(entity, mesh, name=None) restores the display fields onto an already-created geoh5 entity. An explicit name overrides the stored name. Missing metadata or display fields leave the entity unchanged for those fields; invalid recognized values or unsupported schema versions raise ValueError before display changes are applied. Colour restoration creates Visual Parameters if needed and otherwise updates the existing child's colour without replacing its other settings. Source identity, parent relationships, and entity class are not restored. This helper is not yet called automatically by the exporters.

Drillhole Representation

Drillhole imports now return nested MultiBlock objects instead of a single merged PolyData. geoh5wrap(hole) or geoh5vista.drillholes.drillhole_to_vtk(hole) converts one hole; drillholes_to_vtk(group) converts a group. Modern concatenated drillholes and legacy drillholes are supported. Workspace loading includes each hole once, inside its drillhole group, and applies load_only_visible to both groups and individual holes.

Each hole has four named components:

Component Representation Arrays
collar One-vertex PolyData Scalar object-associated observations as point arrays; longer object arrays in field data
trace Connected two-point line cells Measured depth on points; segment from and to on cells
intervals MultiBlock of sampling tables One polyline cell per source interval, with observations, from, to, and source_row on cells
points MultiBlock of sampling tables Vertex cells at observation depths, with observations, depth, and source_row on points

Arrays sharing a modern property group share a dataset; different sampling tables remain independent. Legacy arrays retain their shared cell/depth alignment. Gaps are not bridged in interval tables, overlapping intervals remain separate cells, and repeated point depths remain separate observations. Point observations are never interpolated onto the trace automatically.

Geometry is generated from collar and surveys through geoh5py desurveying; a stored trace is not required and elevation differences are not used as measured depth. Curved interval cells include intermediate vertices. The default curve_tolerance=0.01 bounds circular-arc chord error in workspace distance units, with depth steps also limited to 5 units. This is an approximation tolerance relative to geoh5py's desurveyed path, not a measure of survey accuracy. Survey/desurvey boundaries are always included.

A missing or zero end-of-hole depth is treated as unspecified (geoh5py may normalize missing depths to zero); the extent is then inferred from sampling and survey depths. Without a positive extent the result is collar-only, with an empty trace. Missing surveys use geoh5py's default vertical direction with a warning when a path is generated. Negative/non-finite depths and sampling beyond an explicit positive end of hole are rejected. Reversed and zero-length intervals (from >= to) are omitted with a warning identifying the hole, table, and zero-based source rows. Remaining arrays keep their original source_row IDs; omitted row IDs and bounds are retained in the table's user_dict["geoh5"]["drillhole"]["omitted_intervals"]. No bounds are guessed or repaired, and the source workspace is unchanged.

The existing user_dict["geoh5"] source/display metadata is preserved per hole and per leaf dataset. Additional drillhole metadata records component roles, sampling-table identity, original surveys, survey/extent provenance, hole attributes, workspace distance units, and the geometry tolerance. The arrays dictionary records source array UIDs, names, types, associations, and referenced-value maps. Numeric, Boolean, referenced, and text arrays are preserved, with companion validity arrays named <array>__valid and referenced labels named <array>_names. Boolean False is valid; native numeric/text no-data values are distinguished using validity arrays. Duplicate names and collisions with geometry-array names are disambiguated; metadata retains the original names and UIDs. Modern data without a resolvable sampling table are rejected rather than silently omitted. Cached legacy arrays that are shorter than their extended shared geometry are padded using geoh5py's native no-data conventions. PyVista/VTK rejects non-ASCII string arrays; non-ASCII text values and referenced labels therefore raise an explicit error rather than being transliterated or discarded. JSON source/display metadata can retain Unicode.

import pyvista as pv
import geoh5vista
from geoh5vista.drillholes import save_drillholes, read_drillholes

project = geoh5vista.read_geoh5("project.geoh5")
hole = project["Drillholes"]["DH-001"]
assays = hole["intervals"][0]

plotter = pv.Plotter()
plotter.add_mesh(hole["trace"], color="black")
plotter.add_mesh(assays, scalars="Au", preference="cell", line_width=5)
plotter.add_mesh(hole["collar"], color="red", point_size=10)
plotter.show()

save_drillholes(project["Drillholes"], "drillholes.vtm")
restored = read_drillholes("drillholes.vtm")

Use save_drillholes / read_drillholes for VTM persistence: ordinary MultiBlock.save loses nested container metadata. These helpers pack metadata into a root registry without modifying the input and restore it on loading. They also support a containing workspace MultiBlock. Extra nested container field arrays are rejected rather than silently discarded; leaf field arrays are preserved, including Boolean dtypes that VTK otherwise stores as integers.

For a common-line visualization, use drillhole_to_vtk(hole, representation="merged"), drillholes_to_vtk(group, representation="merged"), or read_geoh5(path, drillhole_representation="merged"). This view splits at all sampling boundaries, maps interval values to line cells, and places point values only at exact observation depths. Validity arrays distinguish gaps from valid zero/False values, and point/cell dh_uid arrays retain hole identity, even for coincident holes. Overlapping valid observations in one array or incompatible array definitions across holes raise an error: use the composite representation instead. Fields sharing a sampling table reuse the same row mapping. Missing observations do not overwrite valid overlapping observations; referenced labels follow the same validity mask as their codes and remain empty where no valid value is mapped.

Composite export to geoh5 drillholes is not implemented and is explicitly rejected. An explicit merged view may be exported as ordinary curves, not as a faithful drillhole round trip.

Example Use

import pyvista as pv
import geoh5vista

project = geoh5vista.read_geoh5('test_file.geoh5')
project

Once the data is loaded as a pyvista.MultiBlock dataset from geoh5vista, that object can be directly used for interactive 3D visualization from PyVista:

An interactive scene can be created and manipulated to create a figure. First, grab the elements from the project:

# Grab a few elements of interest and plot em up!
vol = project["Block Model"]
topo = project["Topography"]
dacite = project["Dacite"]

Then create a 3D scene with these spatial data and apply a filtering tool from PyVista to the volumetric data:

# Create a plotting window
p = pv.Plotter(notebook=False)
# Add our datasets
p.add_mesh(topo, cmap="gist_earth", opacity=0.5)
p.add_mesh(dacite, color=dacite.user_dict["gh5_colour"], opacity=0.6)
# Add the volumetric dataset with a thresholding tool
p.add_mesh_threshold(vol)
# Add the bounds axis
p.show_bounds()
# Render the scene in a pop out window
p.show()

Writing PyVista objects to a Geoh5 file can be as simple as:

# Write a single object to geoh5
geoh5vista.write_geoh5(topo, "new_gh5_topo_file.geoh5")

# Write a multiblock to geoh5
new_project = pv.MultiBlock()
new_project["Topo"] = topo
new_project["Dacite"] = dacite

geoh5vista.write_geoh5(new_project, "new_project.geoh5")

Metadata

Release files for geoh5vista 0.6.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for geoh5vista 0.6.1
File Size Uploaded
geoh5vista-0.6.1.tar.gz 49.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for geoh5vista 0.6.1
File Interpreter ABI Platform
geoh5vista-0.6.1-py3-none-any.whl Python 3 none any Details

Total release size: 87.8 kB

Release files / geoh5vista-0.6.1.tar.gz

Download URL geoh5vista-0.6.1.tar.gz
Size 49.8 kB
Tags Source
SHA-256 checksum
How to use checksums
ad635d33ce9f28e3850bf07d6e551605848e10f354ec790d51df2ad5e0a70b31
BLAKE2b-256 checksum
How to use checksums
b68c221ba5be6437ceabf7edcd8275dd9c231404e832af518746c1e10cb390d0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / geoh5vista-0.6.1-py3-none-any.whl

Download URL geoh5vista-0.6.1-py3-none-any.whl
Size 38.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7c912e49a861099fbfa86a3df8bee2211c49c9864c015c6ad5194d4f98069d17
BLAKE2b-256 checksum
How to use checksums
d356fe876e30aed8a673c9343abe87e930604ddfcd014e69e933fed877051321
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.6.1 This release

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.5

2 release files

0.3.1

2 release files

0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page