Skip to content

Commit 325529c

Browse files
committed
Use geoviews everywhere + use pn.State for caching
This commit is quite messy, but we are still at an early stage of the development and the API is not set yet. Anyhow, it mostly addresses the remarks made by @jbednar at: #16 (comment) In a nutshell: - We follow the recommendation of the [FAQ](https://holoviews.org/FAQ.html) and we stop mixing holoviews and geoviews objects. - We use `pn.State` in order to cache the `xr.Dataset` objects among different requests. - We fix cartopy to < 0.20 since we are affected by this issue: holoviz/geoviews#529
1 parent 138fac5 commit 325529c

10 files changed

+241
-6400
lines changed

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@ You will need some data to visualize. If you don't have any you can download a s
99
created with [pyposeidon](https://github.com/ec-jrc/pyPoseidon/):
1010

1111
```
12-
mkdir data
13-
wget -O data/dataset.nc https://static.techrad.eu/thalassa/dataset.nc
12+
mkdir ./data
13+
wget -O data/dataset.nc https://static.techrad.eu/thalassa/dataset.nc
1414
```
1515

1616
## Deploying on a server
1717

1818
1. Install the binary dependencies:
1919

2020
- `python 3.9`
21+
- `proj < 8`
2122

2223
2. Install the python dependencies with
2324

Thalassa.ipynb

+11-6,101
Large diffs are not rendered by default.

poetry.lock

+106-203
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ netCDF4 = "^1.5"
4848
datashader = "^0.13"
4949
ruyaml = "^0.20.0"
5050
geoviews = "^1.9.2"
51+
Cartopy = "<0.20" # due to https://github.com/holoviz/geoviews/issues/529
5152

5253
[tool.poetry.group.dev.dependencies]
5354
black = "^21.9b0"

requirements/requirements-dev.txt

+15-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
anyio==3.3.2; python_full_version >= "3.6.2" and python_version >= "3.6"
1+
anyio==3.3.3; python_full_version >= "3.6.2" and python_version >= "3.6"
22
appnope==0.1.2; sys_platform == "darwin" and python_version >= "3.7" or platform_system == "Darwin" and python_version >= "3.7"
33
argon2-cffi==21.1.0; python_version >= "3.6"
44
astroid==2.8.2; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1"
@@ -9,12 +9,13 @@ backports.entry-points-selectable==1.1.0; python_version >= "2.7" and python_ful
99
black==21.9b0; python_full_version >= "3.6.2"
1010
bleach==4.1.0; python_version >= "3.6"
1111
bokeh==2.4.0; python_version >= "3.7"
12-
certifi==2021.5.30; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.7"
12+
cartopy==0.19.0.post1; python_version >= "3.5"
13+
certifi==2021.10.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.7"
1314
cffi==1.14.6; implementation_name == "pypy" and python_version >= "3.6" or python_version >= "3.6"
1415
cfgv==3.3.1; python_full_version >= "3.6.1"
1516
cftime==1.5.1
16-
charset-normalizer==2.0.6; python_full_version >= "3.6.0" and python_version >= "3.6"
17-
click==8.0.1; python_version >= "3.6" and python_full_version >= "3.6.2" or python_version >= "3.7"
17+
charset-normalizer==2.0.7; python_full_version >= "3.6.0" and python_version >= "3.6"
18+
click==8.0.3; python_version >= "3.6" and python_full_version >= "3.6.2" or python_version >= "3.7"
1819
cloudpickle==2.0.0; python_version >= "3.7" or python_version >= "3.7"
1920
colorama==0.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or python_full_version >= "3.5.0" and python_version >= "3.6" and platform_system == "Windows" or python_version >= "3.6" and python_full_version >= "3.6.2" and platform_system == "Windows" or python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1" and sys_platform == "win32"
2021
colorcet==2.0.6; python_version >= "2.7"
@@ -31,20 +32,21 @@ dodgy==0.2.1; python_full_version >= "3.6.1" and python_version < "4.0"
3132
entrypoints==0.3; python_full_version >= "3.6.1" and python_version >= "3.6" or python_version >= "3.6"
3233
filelock==3.3.0; python_version >= "3.6" and python_full_version >= "3.6.1"
3334
flake8-polyfill==1.0.2; python_full_version >= "3.6.1" and python_version < "4.0"
34-
flake8==3.9.2; python_full_version >= "3.6.1" and python_version < "4.0"
35+
flake8==2.3.0; python_full_version >= "3.6.1" and python_version < "4.0"
3536
fsspec==2021.10.0; python_version >= "3.7"
37+
geoviews==1.9.2; python_version >= "3.7"
3638
heapdict==1.0.1; python_version >= "3.7"
3739
holoviews==1.14.6; python_version >= "2.7"
3840
identify==2.3.0; python_full_version >= "3.6.1"
39-
idna==3.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_full_version >= "3.6.2" and python_version >= "3.6"
41+
idna==3.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_full_version >= "3.6.2" and python_version >= "3.6"
4042
ipykernel==6.4.1; python_version >= "3.7"
4143
ipython-genutils==0.2.0; python_version >= "3.6"
4244
ipython==7.28.0; python_version >= "3.7"
4345
isort==5.9.3; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6"
4446
jedi==0.18.0; python_version >= "3.7"
4547
jinja2==3.0.2; python_version >= "3.7" or python_version >= "3.6"
4648
json5==0.9.6; python_version >= "3.6"
47-
jsonschema==4.0.1; python_version >= "3.7"
49+
jsonschema==4.1.0; python_version >= "3.7"
4850
jupyter-client==7.0.6; python_full_version >= "3.6.1" and python_version >= "3.6" or python_full_version >= "3.6.1" and python_version >= "3.7"
4951
jupyter-core==4.8.1; python_full_version >= "3.6.1" and python_version >= "3.6" or python_version >= "3.6"
5052
jupyter-server==1.11.1; python_version >= "3.6"
@@ -82,6 +84,7 @@ parso==0.8.2; python_version >= "3.7"
8284
partd==1.2.0; python_version >= "3.7"
8385
pathspec==0.9.0; python_full_version >= "3.6.2"
8486
pep8-naming==0.10.0; python_full_version >= "3.6.1" and python_version < "4.0"
87+
pep8==1.7.1; python_full_version >= "3.6.1" and python_version < "4.0"
8588
pexpect==4.8.0; sys_platform != "win32" and python_version >= "3.7"
8689
pickleshare==0.7.5; python_version >= "3.7"
8790
pillow==8.3.2; python_version >= "3.6"
@@ -93,7 +96,7 @@ prospector==1.5.1; python_full_version >= "3.6.1" and python_version < "4.0"
9396
psutil==5.8.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.4.0"
9497
ptyprocess==0.7.0; sys_platform != "win32" and python_version >= "3.7" or os_name != "nt" and python_version >= "3.6"
9598
py==1.10.0; python_version >= "3.6" and python_full_version < "3.0.0" and implementation_name == "pypy" or implementation_name == "pypy" and python_version >= "3.6" and python_full_version >= "3.4.0"
96-
pycodestyle==2.7.0; python_full_version >= "3.6.1" and python_version < "4.0"
99+
pycodestyle==2.8.0; python_full_version >= "3.6.1" and python_version < "4.0"
97100
pycparser==2.20; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
98101
pyct==0.4.8; python_version >= "2.7"
99102
pydocstyle==6.1.1; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6"
@@ -107,14 +110,15 @@ pylint==2.11.1; python_version >= "3.6" and python_version < "4.0" and python_fu
107110
pyparsing==2.4.7; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7"
108111
pyproj==3.2.1; python_version >= "3.7"
109112
pyrsistent==0.18.0; python_version >= "3.7"
113+
pyshp==2.1.3; python_version >= "3.7"
110114
python-dateutil==2.8.2; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "2.7" or python_full_version >= "3.7.1" and python_version >= "2.7" or python_full_version >= "3.6.1" and python_version >= "3.6"
111115
pytz==2021.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.7.1" and python_version >= "2.7"
112116
pyviz-comms==2.1.0
113-
pywin32==301; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_version >= "3.6"
117+
pywin32==302; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_version >= "3.6"
114118
pywinpty==1.1.4; os_name == "nt" and python_version >= "3.6"
115119
pyyaml==5.4.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" or python_full_version >= "3.6.1" and python_version < "4.0" or python_full_version >= "3.6.1"
116120
pyzmq==22.3.0; python_full_version >= "3.6.1" and python_version >= "3.6" or python_version >= "3.6"
117-
regex==2021.9.30; python_full_version >= "3.6.2"
121+
regex==2021.10.8; python_full_version >= "3.6.2"
118122
requests-unixsocket==0.2.0; python_version >= "3.6"
119123
requests==2.26.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
120124
requirements-detector==0.7; python_full_version >= "3.6.1" and python_version < "4.0"
@@ -123,6 +127,7 @@ scipy==1.7.1; python_version >= "3.7" and python_version < "3.10"
123127
send2trash==1.8.0; python_version >= "3.6"
124128
setoptconf-tmp==0.3.1; python_full_version >= "3.6.1" and python_version < "4.0"
125129
setuptools==58.2.0; python_version >= "3.6" and python_version < "3.10" or python_version >= "3.6" or python_version >= "3.7" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1"
130+
shapely==1.7.1; python_version >= "3.7"
126131
six==1.16.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_full_version >= "3.7.1" and python_version >= "2.7" or python_full_version >= "3.6.1" or python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "2.7"
127132
sniffio==1.2.0; python_full_version >= "3.6.2" and python_version >= "3.6"
128133
snowballstemmer==2.1.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6"

requirements/requirements.txt

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
bleach==4.1.0; python_version >= "3.6"
22
bokeh==2.4.0; python_version >= "3.7"
3-
certifi==2021.5.30; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.7"
3+
cartopy==0.19.0.post1; python_version >= "3.5"
4+
certifi==2021.10.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.7"
45
cftime==1.5.1
5-
charset-normalizer==2.0.6; python_full_version >= "3.6.0" and python_version >= "3.6"
6-
click==8.0.1; python_version >= "3.7"
6+
charset-normalizer==2.0.7; python_full_version >= "3.6.0" and python_version >= "3.6"
7+
click==8.0.3; python_version >= "3.7"
78
cloudpickle==2.0.0; python_version >= "3.7" or python_version >= "3.7"
89
colorama==0.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or python_full_version >= "3.5.0" and python_version >= "3.6" and platform_system == "Windows" or python_version >= "3.7" and python_full_version < "3.0.0" and platform_system == "Windows" or python_version >= "3.7" and platform_system == "Windows" and python_full_version >= "3.5.0"
910
colorcet==2.0.6; python_version >= "2.7"
@@ -13,9 +14,10 @@ datashape==0.5.2; python_version >= "2.7"
1314
distributed==2021.9.1; python_version >= "3.7"
1415
distro==1.6.0; python_version >= "3.6"
1516
fsspec==2021.10.0; python_version >= "3.7"
17+
geoviews==1.9.2; python_version >= "3.7"
1618
heapdict==1.0.1; python_version >= "3.7"
1719
holoviews==1.14.6; python_version >= "2.7"
18-
idna==3.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
20+
idna==3.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
1921
jinja2==3.0.2; python_version >= "3.7" or python_version >= "3.7"
2022
llvmlite==0.36.0; python_version >= "3.6" and python_version < "3.10"
2123
locket==0.2.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
@@ -36,6 +38,7 @@ psutil==5.8.0; python_version >= "3.7" and python_full_version < "3.0.0" or pyth
3638
pyct==0.4.8; python_version >= "2.7"
3739
pyparsing==2.4.7; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7"
3840
pyproj==3.2.1; python_version >= "3.7"
41+
pyshp==2.1.3; python_version >= "3.7"
3942
python-dateutil==2.8.2; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "2.7" or python_full_version >= "3.7.1" and python_version >= "2.7"
4043
pytz==2021.3; python_full_version >= "3.7.1" and python_version >= "2.7"
4144
pyviz-comms==2.1.0; python_version >= "2.7"
@@ -44,6 +47,7 @@ requests==2.26.0; python_version >= "3.6" and python_full_version < "3.0.0" or p
4447
ruyaml==0.20.0; python_version >= "3.6"
4548
scipy==1.7.1; python_version >= "3.7" and python_version < "3.10"
4649
setuptools==58.2.0; python_version >= "3.6" and python_version < "3.10" or python_version >= "3.6" or python_version >= "3.7"
50+
shapely==1.7.1; python_version >= "3.7"
4751
six==1.16.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_full_version >= "3.7.1" and python_version >= "2.7" or python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "2.7"
4852
sortedcontainers==2.4.0; python_version >= "3.7"
4953
tblib==1.7.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.5.0"

thalassa/__init__.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
from __future__ import annotations
22

3-
from .utils import coords_to_web_mercator
43
from .utils import open_dataset
5-
from .utils import open_loaded_dataset
64
from .utils import reload
5+
from .visuals import get_max_elevation
6+
from .visuals import get_tiles
77
from .visuals import get_trimesh
8-
from .visuals import get_wireframe_and_max_elevation
8+
from .visuals import get_wireframe
99

1010

1111
__all__: list[str] = [
12-
"coords_to_web_mercator",
1312
"open_dataset",
14-
"open_loaded_dataset",
1513
"reload",
1614
"get_trimesh",
17-
"get_wireframe_and_max_elevation",
15+
"get_tiles",
16+
"get_wireframe",
17+
"get_max_elevation",
1818
]

thalassa/utils.py

+4-18
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,12 @@
77
import pathlib
88
import sys
99

10-
import holoviews as hv
1110
import xarray as xr
1211

1312
logger = logging.getLogger(__name__)
1413

1514

16-
def coords_to_web_mercator(ds: xr.Dataset, longitude_var: str, latitude_var: str) -> xr.Dataset:
17-
lons = ds[longitude_var]
18-
lats = ds[latitude_var]
19-
x, y = hv.util.transform.lon_lat_to_easting_northing(lons, lats)
20-
ds = ds.assign({longitude_var: x, latitude_var: y})
21-
return ds
22-
23-
24-
def open_dataset(path: str | pathlib.Path) -> xr.Dataset:
15+
def open_dataset(path: str | pathlib.Path, load: bool = False) -> xr.Dataset:
2516
path = pathlib.Path(path)
2617
if path.suffix == ".nc":
2718
ds = xr.open_dataset(path, mask_and_scale=True)
@@ -30,14 +21,9 @@ def open_dataset(path: str | pathlib.Path) -> xr.Dataset:
3021
# TODO: extend with GeoTiff, Grib etc
3122
else:
3223
raise ValueError(f"Don't know how to handle this: {path}")
33-
return ds
34-
35-
36-
def open_loaded_dataset(path: str, reproject: bool, longitude_var: str, latitude_var: str) -> xr.Dataset:
37-
ds = open_dataset(path=path)
38-
if reproject:
39-
ds = coords_to_web_mercator(ds, longitude_var, latitude_var)
40-
ds.load()
24+
if load:
25+
# load dataset to memory
26+
ds.load()
4127
return ds
4228

4329

thalassa/visuals.py

+25-19
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import holoviews as hv # type: ignore
55
import pandas as pd # type: ignore
66
import xarray as xr
7-
from holoviews import opts as hvopts
87
from holoviews.operation.datashader import dynspread # type: ignore
98
from holoviews.operation.datashader import rasterize # type: ignore
109

@@ -19,34 +18,41 @@ def get_trimesh(
1918
elevation_var: str,
2019
simplices_var: str,
2120
time_var: str,
22-
) -> hv.TriMesh:
23-
# Prepare the data
21+
) -> gv.TriMesh:
22+
# local aliases
2423
lons = ds[longitude_var].values
2524
lats = ds[latitude_var].values
2625
simplices = ds[simplices_var].values
27-
elevation = ds[elevation_var].max(dim=time_var).values
26+
# get max elevation
27+
max_elevation = ds[elevation_var].max(dim=time_var).values
2828
# Create holoviews objects
29-
points_df = pd.DataFrame(dict(longitude=lons, latitude=lats, elevation=elevation))
30-
points_hv = hv.Points(points_df, kdims=["longitude", "latitude"], vdims="elevation")
31-
trimesh = hv.TriMesh((simplices, points_hv))
29+
points_df = pd.DataFrame(dict(lons=lons, lats=lats, max_elevation=max_elevation))
30+
points_gv = gv.Points(points_df, kdims=["lons", "lats"], vdims="max_elevation")
31+
trimesh = gv.TriMesh((simplices, points_gv))
3232
return trimesh
3333

3434

35-
def get_wireframe_and_max_elevation(trimesh: hv.TriMesh) -> hv.Layout:
36-
# pylint: disable=no-member
37-
# tiles = hv.Tiles("http://c.tile.openstreetmap.org/{Z}/{X}/{Y}.png")
35+
def get_tiles() -> gv.Tiles:
3836
tiles = gv.WMTS("http://c.tile.openstreetmap.org/{Z}/{X}/{Y}.png")
39-
# tiles = hv.element.tiles.tile_sources["EsriStreet"]()
40-
wireframe = dynspread(rasterize(trimesh.edgepaths)).opts(title="Wireframe")
41-
elevation = rasterize(trimesh).opts(
37+
return tiles
38+
39+
40+
def get_wireframe(trimesh: gv.TriMesh) -> hv.Layout:
41+
# If we want the wireframe plot to be connected to the main plot
42+
# we need create the overlay with tiles too.
43+
# That being said, we might be able to use a more lightweight tile for the wireframe
44+
tiles = get_tiles()
45+
wireframe = dynspread(rasterize(trimesh.edgepaths, precompute=True))
46+
wireframe = wireframe.opts(title="Wireframe") # pylint: disable=no-member
47+
return tiles * wireframe
48+
49+
50+
def get_max_elevation(trimesh: gv.TriMesh) -> hv.Layout:
51+
tiles = get_tiles()
52+
elevation = rasterize(trimesh, precompute=True).opts( # pylint: disable=no-member
4253
title="Max Elevation",
4354
colorbar=True,
4455
clabel="meters",
4556
show_legend=True,
4657
)
47-
layout = (tiles * elevation + tiles * wireframe).cols(1)
48-
layout = layout.opts(
49-
hvopts.Image(width=800, height=400, show_title=True, tools=["hover"]),
50-
hvopts.Layout(toolbar="right"),
51-
)
52-
return layout
58+
return tiles * elevation

0 commit comments

Comments
 (0)