Home

Awesome

<h1 align="center"> <a href="https://github.com/bjlittle/geovista#--------"> <img src="https://raw.githubusercontent.com/bjlittle/geovista-media/2024.07.0/media/branding/logo/primary/geovistalogo.svg" alt="GeoVista" width="200"></a> </h1> <h3 align="center"> Cartographic rendering and mesh analytics powered by <a href="https://docs.pyvista.org/index.html">PyVista</a> </h3>
โš™๏ธ CIci-citation ci-locks ci-manifest ci-tests ci-wheels pre-commit
๐Ÿ’ฌ CommunityContributor Covenant GH Discussions Bluesky YouTube Channel Subscribers All Contributors Mentioned in Awesome Open Geoscience
๐Ÿ“š DocsDocumentation Status
๐Ÿ“ˆ Healthcodecov
โœจ MetaRuff SPEC 0 โ€” Minimum Supported Dependencies license - bsd-3-clause conda platform
๐Ÿ“ฆ PackageDOI conda-forge pypi pypi - python version
๐Ÿงฐ Repocommits-since contributors release
๐Ÿ›ก๏ธ Statusscitools

Rediscover Your Data

WW3 SMC time-series ๐ŸŽฅ

GeoVista is built on the shoulders of giants, namely PyVista and VTK, thus allowing it to easily leverage the power of the GPU.

As a result, it offers a paradigm shift in rendering performance and interactive user experience, as demonstrated by this realtime, time-series animation of WAVEWATCH IIIยฎ third-generation wave model (WAVE-height, WATer depth and Current Hindcasting) data developed at NOAA/NCEP.

The animation shows a time-series of Sea Surface Wave Significant Height data located on the cell faces of a quasi-unstructured Spherical Multi-Cell (SMC) grid.

Bring your data alive with GeoVista! ๐Ÿš€

Tempted? Keen to know more? Well, let's begin ...

Motivation

The goal of GeoVista is simple; to complement PyVista with a 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.

GeoVista is intentionally agnostic to packages such as geopandas, iris, xarray et al, which specialise in preparing your spatial data for visualisation. Rather, we delegate 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 entire Scientific Python community.

Simply put, "GeoVista is to PyVista", as "Cartopy is to Matplotlib". Well, that's the aspiration.

Installation

GeoVista is available on both conda-forge and PyPI.

We recommend using conda to install GeoVista ๐Ÿ‘

Conda

GeoVista is available on conda-forge, and can be easily installed with conda:

conda install -c conda-forge geovista

For more information see our conda-forge feedstock and prefix.dev dashboard.

Pip

GeoVista is also available on PyPI:

pip install geovista

Checkout out our PyPI Download Stats, if you like that kinda thing.

Quick Start

GeoVista comes with various pre-canned resources to help get you started on your visualisation journey.

Resources

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

Want to know more?

geovista download --help

Plotting Examples

Let's explore a sample of various oceanographic and atmospheric model data using GeoVista.

WAVEWATCH III

First, let's render a WAVEWATCH III (WW3) unstructured triangular mesh, with 10m Natural Earth coastlines, a 1:50m Natural Earth Cross-Blended Hypsometric Tints base layer, and the gorgeous perceptually uniform cmocean balance diverging colormap.

<details aria-expanded="false" aria-label="Click for code"> <summary>๐Ÿ—’ Click for code ...</summary>
import geovista as gv
from geovista.pantry.data 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, connectivity=sample.connectivity, data=sample.data
)

# Plot the mesh.
p = gv.GeoPlotter()
sargs = {"title": f"{sample.name} / {sample.units}"}
p.add_mesh(mesh, show_edges=True, scalar_bar_args=sargs)
p.add_base_layer(texture=gv.natural_earth_hypsometric())
p.add_coastlines()
p.add_graticule()
p.view_xy(negative=True)
p.add_axes()
p.show()
</details> <p align="center"> <img src="https://raw.githubusercontent.com/bjlittle/geovista-media/2024.07.0/media/readme/ww3-tri.png" alt="WAVEWATCH III Model, Unstructured Triangular Mesh of Sea Surface Wave Significant Height" style="width: 75%; height: 75%"> </p>

Finite Volume Community Ocean Model

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 using the lush cmocean deep colormap.

<details aria-expanded="false" aria-label="Click for code"> <summary>๐Ÿ—’ Click for code ...</summary>
import geovista as gv
from geovista.pantry.data 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,
    connectivity=sample.connectivity,
    data=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.
p = gv.GeoPlotter()
sargs = {"title": f"{sample.name} / {sample.units}"}
p.add_mesh(mesh, cmap="deep", scalar_bar_args=sargs)
p.add_axes()
p.show()
</details> <p align="center"> <img src="https://raw.githubusercontent.com/bjlittle/geovista-media/2024.07.0/media/readme/tamar.png" alt="Finite Volume Community Ocean Model, Unstructured Triangular Mesh of Sea Floor Below Geoid" style="width: 75%; height: 75%"> </p>

CF UGRID

Local Area Model

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 10m Natural Earth coastlines and a 1:50m Natural Earth Cross-Blended Hypsometric Tints base layer.

<details aria-expanded="false" aria-label="Click for code"> <summary>๐Ÿ—’ Click for code ...</summary>
import geovista as gv
from geovista.pantry.data import lam_pacific
import geovista.theme

# Load the sample data.
sample = lam_pacific()

# Create the mesh from the sample data.
mesh = gv.Transform.from_unstructured(
    sample.lons,
    sample.lats,
    connectivity=sample.connectivity,
    data=sample.data,
)

# Plot the mesh on a mollweide projection using a Proj string.
p = gv.GeoPlotter(crs="+proj=moll")
sargs = {"title": f"{sample.name} / {sample.units}"}
p.add_mesh(mesh, scalar_bar_args=sargs)
p.add_base_layer(texture=gv.natural_earth_hypsometric())
p.add_coastlines()
p.add_graticule()
p.add_axes()
p.view_xy()
p.show()
</details> <p align="center"> <img src="https://raw.githubusercontent.com/bjlittle/geovista-media/2024.07.0/media/readme/lam-moll.png" alt="CF UGRID Local Area Model, Unstructured Quadrilateral Mesh of Air Potential Temperature" style="width: 75%; height: 75%"> </p>

Using the same unstructured LAM data, reproject to Equidistant Cylindrical but this time using a Cartopy Plate Carrรฉe CRS, also with 10m Natural Earth coastlines and a 1:50m Natural Earth Cross-Blended Hypsometric Tints base layer.

<details aria-expanded="false" aria-label="Click for code"> <summary>๐Ÿ—’ Click for code ...</summary>
import cartopy.crs as ccrs

import geovista as gv
from geovista.pantry.data import lam_pacific
import geovista.theme

# Load the sample data.
sample = lam_pacific()

# Create the mesh from the sample data.
mesh = gv.Transform.from_unstructured(
    sample.lons,
    sample.lats,
    connectivity=sample.connectivity,
    data=sample.data,
)

# Plot the mesh on a Plate Carrรฉe projection using a cartopy CRS.
p = gv.GeoPlotter(crs=ccrs.PlateCarree(central_longitude=180))
sargs = {"title": f"{sample.name} / {sample.units}"}
p.add_mesh(mesh, scalar_bar_args=sargs)
p.add_base_layer(texture=gv.natural_earth_hypsometric())
p.add_coastlines()
p.add_graticule()
p.add_axes()
p.view_xy()
p.show()
</details> <p align="center"> <img src="https://raw.githubusercontent.com/bjlittle/geovista-media/2024.07.0/media/readme/lam-eqc.png" alt="CF UGRID Local Area Model, Unstructured Quadrilateral Mesh of Air Potential Temperature in Plate Carrรฉe Projection" style="width: 75%; height: 75%"> </p>

LFRic Cube-Sphere

Now render a Met Office LFRic C48 cube-sphere unstructured mesh of Sea Surface Temperature data on a Robinson Projection using an ESRI SRID, with 10m Natural Earth coastlines and a cmocean thermal colormap.

<details aria-expanded="false" aria-label="Click for code"> <summary>๐Ÿ—’ Click for code ...</summary>
import geovista as gv
from geovista.pantry.data 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,
    connectivity=sample.connectivity,
    data=sample.data,
)

# Plot the mesh on a Robinson projection using an ESRI spatial reference identifier.
p = gv.GeoPlotter(crs="ESRI:54030")
sargs = {"title": f"{sample.name} / {sample.units}"}
p.add_mesh(mesh, cmap="thermal", show_edges=True, scalar_bar_args=sargs)
p.add_coastlines()
p.view_xy()
p.add_axes()
p.show()
</details> <p align="center"> <img src="https://raw.githubusercontent.com/bjlittle/geovista-media/2024.07.0/media/readme/lfric-robin.png" alt="LFRic Model, Unstructured Cube Sphere of Surface Temperature in Robinson Projection" style="width: 75%; height: 75%"> </p>

NEMO ORCA2

So far we've demonstrated GeoVista's ability to cope with unstructured data. Now let's plot a curvilinear mesh using Nucleus for European Modelling of the Ocean (NEMO) ORCA2 Sea Water Potential Temperature data, with 10m Natural Earth coastlines and a 1:50m Natural Earth I base layer.

<details aria-expanded="false" aria-label="Click for code"> <summary>๐Ÿ—’ Click for code ...</summary>
import geovista as gv
from geovista.pantry.data import nemo_orca2
import geovista.theme

# Load sample data.
sample = nemo_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.
p = gv.GeoPlotter()
sargs = {"title": f"{sample.name} / {sample.units}"}
p.add_mesh(mesh, show_edges=True, scalar_bar_args=sargs)
p.add_base_layer(texture=gv.natural_earth_1())
p.add_coastlines()
p.view_xy()
p.add_axes()
p.show()
</details> <p align="center"> <img src="https://raw.githubusercontent.com/bjlittle/geovista-media/2024.07.0/media/readme/nemo-orca.png" alt="NEMO ORCA2 Model, Curvilinear Quadrilateral Mesh of Sea Water Potential Temperature" style="width: 75%; height: 75%"> </p>

OISST AVHRR

Now 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.

<details aria-expanded="false" aria-label="Click for code"> <summary>๐Ÿ—’ Click for code ...</summary>
import geovista as gv
from geovista.pantry.data 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.
p = gv.GeoPlotter()
sargs = {"title": f"{sample.name} / {sample.units}"}
p.add_mesh(mesh, scalar_bar_args=sargs)
p.add_base_layer(texture=gv.blue_marble())
p.add_coastlines()
p.view_xz()
p.add_axes()
p.show()
</details> <p align="center"> <img src="https://raw.githubusercontent.com/bjlittle/geovista-media/2024.07.0/media/readme/oisst-avhrr.png" alt="Optimum Interpolation Sea Surface Temperature Advanced Very High Resolution Radiometer Model, Rectilinear Quadrilateral Mesh" style="width: 75%; height: 75%"> </p>

DYNAMICO

Finally, to demonstrate support for non-traditional cell geometries i.e., not triangles or quadrilaterals, we plot the unstructured mesh from the DYNAMICO project. This model uses hexagonal and pentagonal cells, and is a new dynamical core for LMD-Z, the atmospheric General Circulation Model (GCM) part of the IPSL-CM Earth System Model. The render also contains 10m Natural Earth coastlines.

<details aria-expanded="false" aria-label="Click for code"> <summary>๐Ÿ—’ Click for code ...</summary>
import geovista as gv
from geovista.pantry.data import dynamico
import geovista.theme

# Load sample data.
sample = dynamico()

# Create the mesh from the sample data.
mesh = gv.Transform.from_unstructured(sample.lons, sample.lats, data=sample.data)

# Plot the mesh.
p = gv.GeoPlotter()
sargs = {"title": f"{sample.name} / {sample.units}"}
p.add_mesh(mesh, scalar_bar_args=sargs)
p.add_coastlines()
p.add_axes()
p.show()
</details> <p align="center"> <img src="https://raw.githubusercontent.com/bjlittle/geovista-media/2024.07.0/media/readme/dynamico-icosahedral.png" alt="DYNAMICO Model, Unstructured Hexagonal Mesh of Surface Air Temperature" style="width: 75%; height: 75%"> </p>

Further Examples

<p align="center"> "<em>Please, sir, I want some more</em>", Charles Dickens, Oliver Twist, 1838. </p>

Certainly, our pleasure! From the command line, simply:

geovista examples --run all --verbose

Want to know more?

geovista examples --help

Candy Store ๐Ÿฌ

We've opened the doors to the Candy Store ๐Ÿฌ.

A community space where you can openly share and promote how you're using GeoVista for your work, research or pleasure!

Showcase your most awesome GeoVista eye candy with undiluted pride and tell us more about your work.

We'd ๐Ÿ’š to hear from you!

Documentation

The documentation is built by Sphinx and hosted on Read the Docs.

Ecosystem

Whilst you're here, why not hop on over to the pyvista-xarray project and check it out!

It's aiming to provide xarray DataArray accessors for PyVista to visualize datasets in 3D for the xarray community, and will be building on top of GeoVista ๐ŸŽ‰

Support

Need help? ๐Ÿ˜ข

Why not check out our existing GitHub issues. See something similar? Well, give it a ๐Ÿ‘ to raise its priority and feel free to chip in on the conversation. Otherwise, don't hesitate to create a new GitHub issue instead.

However, if you'd rather have a natter, then head on over to our GitHub Discussions. That's definitely the place to wax lyrical all things GeoVista!

License

GeoVista is distributed under the terms of the BSD-3-Clause license.

Star History

Diggin' GeoVista? Then why not show it some ๐Ÿ’š with a GitHub โญ!

It's a simple click that means the world to us, and helps others discover our community too!

Star History Chart

#ShowYourStripes

<h4 align="center"> <a href="https://showyourstripes.info/s/globe"> <img src="https://raw.githubusercontent.com/ed-hawkins/show-your-stripes/master/2022/GLOBE---1850-2022-MO.png" height="50" width="800" alt="#showyourstripes Global 1850-2022"></a> </h4>

Graphics and Lead Scientist: Ed Hawkins, National Centre for Atmospheric Science, University of Reading.

Data: Berkeley Earth, NOAA, UK Met Office, MeteoSwiss, DWD, SMHI, UoR, Meteo France & ZAMG.

<p> <a href="https://showyourstripes.info/s/globe">#ShowYourStripes</a> is distributed under a <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a> <a href="https://creativecommons.org/licenses/by/4.0/"> <img src="https://i.creativecommons.org/l/by/4.0/80x15.png" alt="creative-commons-by" style="border-width:0"> </a> </p>