Cartographic rendering and mesh analytics powered by PyVista
โ๏ธ CI | |
๐ Docs | |
๐ฆ Package | |
๐ Repo | |
๐ Health | |
โจ Meta | |
The goal of GeoVista is simple; to complement PyVista with convenient cartographic capability.
In this regard, from a design perspective we aim to keep GeoVista as pure to PyVista as possible i.e., minimise specialisation as far as practically possible in order to maximise native compatibility within the PyVista and VTK ecosystems.
We intend GeoVista to be a cartographic gateway into the powerful world of PyVista, and all that it offers.
That said, GeoVista is intentionally agnostic to packages such as geopandas, iris, xarray et al, which specialise in preparing your spatial data for visualisation. Rather, we delagate that responsibility and choice of tool to you the user, as we want GeoVista to remain as flexible and open-ended as possible to the whole Scientific Python community.
Simply put, "GeoVista is to PyVista", as "Cartopy is to Matplotlib". That's the aspiration.
Unfortunately we've not yet tagged our first minor release of GeoVista.
However, we have secured the package namespace on both conda-forge and PyPI. Although note that those initial package versions are simple placeholders for now, containing no functionality.
So in the meantime, life is just a smidge more complicated than necessary if you wish to play with GeoVista.
First, clone the GeoVista GitHub repository:
git clone git@github.com:bjlittle/geovista.git
Change to the root directory:
cd geovista
Create the geovista-dev
conda environment for your preferred platform and Python version e.g.,
conda create -n geovista-dev --file requirements/locks/py310-lock-linux-64.txt
Note that, the requirements/locks
directory contains fully resolved conda package environments, which are automatically updated on a weekly basis. Alternatively, simply:
conda env create --file requirements/geovista.yml
Now activate the environment and install the main
development branch of GeoVista:
conda activate geovista-dev
pip install --no-deps --editable .
Finally, we're good to roll ๐
GeoVista is available on conda-forge, and can be easily installed with conda:
conda install -c conda-forge geovista
or alternatively with mamba:
mamba install geovista
For more information see the feedstock.
GeoVista is available on PyPI:
pip install geovista
GeoVista comes with various pre-canned resources to help get you started on your visualisation journey.
GeoVista makes use of various resources, such as rasters, VTK meshes, Natural Earth features, and sample model data.
If you want to download and cache all registered GeoVista resources to make them available offline, simply:
geovista download --all
Alternatively, just leave GeoVista to download resources on-the-fly, as and when she needs them.
To view the list of registered resources, simply:
geovista download --list
Let's explore a sample of various oceanographic and atmospheric model data using GeoVista.
First, let's render a WAVEWATCH III (WW3) unstructured triangular mesh, with 10m Natural Earth coastlines and a 1:50m Natural Earth Cross-Blended Hypsometric Tints base layer.
๐
import geovista as gv
from geovista.pantry import ww3_global_tri
import geovista.theme
# Load the sample data.
sample = ww3_global_tri()
# Create the mesh from the sample data.
mesh = gv.Transform.from_unstructured(
sample.lons, sample.lats, sample.connectivity, data=sample.data
)
# Plot the mesh.
plotter = gv.GeoPlotter()
sargs = dict(title=f"{sample.name} / {sample.units}")
plotter.add_mesh(
mesh, cmap="balance", show_edges=True, edge_color="grey", scalar_bar_args=sargs
)
plotter.add_base_layer(texture=gv.natural_earth_hypsometric())
plotter.add_coastlines(resolution="10m", color="white")
plotter.view_xy(negative=True)
plotter.add_axes()
plotter.show()
Now, let's visualise the bathymetry of the Plymouth Sound and Tamar River from an FVCOM unstructured mesh, as kindly provided by the Plymouth Marine Laboratory.
๐
import geovista as gv
from geovista.pantry import fvcom_tamar
import geovista.theme
# Load the sample data.
sample = fvcom_tamar()
# Create the mesh from the sample data.
mesh = gv.Transform.from_unstructured(
sample.lons, sample.lats, sample.connectivity, sample.face, name="face"
)
# Warp the mesh nodes by the bathymetry.
mesh.point_data["node"] = sample.node
mesh.compute_normals(cell_normals=False, point_normals=True, inplace=True)
mesh.warp_by_scalar(scalars="node", inplace=True, factor=2e-5)
# Plot the mesh.
plotter = gv.GeoPlotter()
sargs = dict(title=f"{sample.name} / {sample.units}")
plotter.add_mesh(mesh, cmap="balance", scalar_bar_args=sargs)
plotter.add_axes()
plotter.show()
Initial projection support is available within GeoVista for Cylindrical and Pseudo-Cylindrical projections. As GeoVista matures and stabilises, we'll aim to complement this capability with other classes of projections, such as Azimuthal and Conic.
In the meantime, let's showcase our basic projection support with some high-resolution unstructured Local Area Model (LAM) data reprojected to Mollweide using a PROJ string, with a 1:50m Natural Earth Cross-Blended Hypsometric Tints base layer.
๐
import geovista as gv
from geovista.pantry import lam
import geovista.theme
# Load the sample data.
sample = lam()
# Create the mesh from the sample data.
mesh = gv.Transform.from_unstructured(
sample.lons,
sample.lats,
sample.connectivity,
data=sample.data,
start_index=sample.start_index,
)
# Plot the mesh on a mollweide projection using a Proj string.
plotter = gv.GeoPlotter(crs="+proj=moll")
sargs = dict(title=f"{sample.name} / {sample.units}")
plotter.add_mesh(mesh, cmap="balance", scalar_bar_args=sargs)
plotter.add_base_layer(texture=gv.natural_earth_hypsometric())
plotter.add_axes()
plotter.view_xy()
plotter.show()
Using the same unstructured LAM data, reproject to Equidistant Cylindrical but this time using a Cartopy Plate Carrรฉe CRS, also with a 1:50m Natural Earth Cross-Blended Hypsometric Tints base layer.
๐
import cartopy.crs as ccrs
import geovista as gv
from geovista.pantry import lam
import geovista.theme
# Load the sample data.
sample = lam()
# Create the mesh from the sample data.
mesh = gv.Transform.from_unstructured(
sample.lons,
sample.lats,
sample.connectivity,
data=sample.data,
start_index=sample.start_index,
)
# Plot the mesh on a Plate Carrรฉe projection using a cartopy CRS.
plotter = gv.GeoPlotter(crs=ccrs.PlateCarree(central_longitude=180))
sargs = dict(title=f"{sample.name} / {sample.units}")
plotter.add_mesh(mesh, cmap="balance", scalar_bar_args=sargs)
plotter.add_base_layer(texture=gv.natural_earth_hypsometric())
plotter.add_axes()
plotter.view_xy()
plotter.show()
Now render a Met Office LFRic C48 cube-sphere unstructured mesh of Sea Surface Temperature data on a Robinson projection using an ESRI SRID.
๐
import geovista as gv
from geovista.pantry import lfric_sst
import geovista.theme
# Load the sample data.
sample = lfric_sst()
# Create the mesh from the sample data.
mesh = gv.Transform.from_unstructured(
sample.lons,
sample.lats,
sample.connectivity,
data=sample.data,
start_index=sample.start_index,
)
# Plot the mesh on a Robinson projection using an ESRI spatial reference identifier.
plotter = gv.GeoPlotter(crs="ESRI:54030")
sargs = dict(title=f"{sample.name} / {sample.units}")
plotter.add_mesh(mesh, cmap="thermal", show_edges=True, edge_color="grey", scalar_bar_args=sargs)
plotter.view_xy()
plotter.add_axes()
plotter.show()
So far we've demonstrated GeoVista's ability to cope with unstructured data. Now let's plot a curvilinear mesh using some Met Office Unified Model (UM) ORCA2 Sea Water Potential Temperature data, with 10m Natural Earth coastlines and a 1:50m Natural Earth I base layer.
๐
import geovista as gv
from geovista.pantry import um_orca2
import geovista.theme
# Load sample data.
sample = um_orca2()
# Create the mesh from the sample data.
mesh = gv.Transform.from_2d(sample.lons, sample.lats, data=sample.data)
# Remove cells from the mesh with NaN values.
mesh = mesh.threshold()
# Plot the mesh.
plotter = gv.GeoPlotter()
sargs = dict(title=f"{sample.name} / {sample.units}")
plotter.add_mesh(
mesh, cmap="balance", show_edges=True, edge_color="grey", scalar_bar_args=sargs
)
plotter.add_base_layer(texture=gv.natural_earth_1())
plotter.add_coastlines(resolution="10m", color="white")
plotter.view_xy()
plotter.add_axes()
plotter.show()
Finally, let's render a NOAA/NCEI Optimum Interpolation SST (OISST) Advanced Very High Resolution Radiometer (AVHRR) rectilinear mesh, with 10m Natural Earth coastlines and a NASA Blue Marble base layer.
๐
import geovista as gv
from geovista.pantry import oisst_avhrr_sst
import geovista.theme
# Load sample data.
sample = oisst_avhrr_sst()
# Create the mesh from the sample data.
mesh = gv.Transform.from_1d(sample.lons, sample.lats, data=sample.data)
# Remove cells from the mesh with NaN values.
mesh = mesh.threshold()
# Plot the mesh.
plotter = gv.GeoPlotter()
sargs = dict(title=f"{sample.name} / {sample.units}")
plotter.add_mesh(mesh, cmap="balance", scalar_bar_args=sargs)
plotter.add_base_layer(texture=gv.blue_marble())
plotter.add_coastlines(resolution="10m", color="white")
plotter.view_xz()
plotter.add_axes()
plotter.show()
The documentation is built by Sphinx and hosted on Read the Docs.
GeoVista is distributed under the terms of the BSD-3-Clause license.