Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Slice Explorer): adding inital version of the slice explorer #115

Merged
merged 2 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ jobs:
run: npm run build
working-directory: pan3d-js

# Conditionally install pykdtree from source for MacOS only
- name: Install pykdtree from source on macOS
if: matrix.config.os == 'macos-latest'
run: |
export USE_OMP=0
pip install --no-binary pykdtree pykdtree>=1.3.13

- name: Install and Run Tests
run: |
pip install .
Expand Down
121 changes: 121 additions & 0 deletions examples/jupyter/slice_explorer.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "503a3444-159b-4f8e-a877-dda66d4922b4",
"metadata": {},
"outputs": [],
"source": [
"# Create an instance of a DatasetBuilder to initialize the Slice Explorer with\n",
"\n",
"from pan3d import DatasetBuilder\n",
"builder = DatasetBuilder()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fc5f043e-00c4-4a04-8ea7-096ed987ea33",
"metadata": {},
"outputs": [],
"source": [
"# Initialize the DatasetBuilder using an existing example configuration\n",
"\n",
"config_path = './example_config_xarray.json'\n",
"builder.import_config(config_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1794f187-98bc-4c21-b390-e097bb33d10b",
"metadata": {},
"outputs": [],
"source": [
"# New API to query the spatial extents for the data\n",
"\n",
"builder.extents"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a1881411-d67f-4f05-ac0b-8f8a29e5e2b9",
"metadata": {},
"outputs": [],
"source": [
"# Initialize the SliceExplorer using the configured DatasetBuilder\n",
"\n",
"from pan3d import SliceExplorer\n",
"explorer = SliceExplorer(builder)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "105415d1-46c3-4e32-8f1d-918bc27f07fd",
"metadata": {},
"outputs": [],
"source": [
"# Wait for the UI to render and view the Trame UI \n",
"\n",
"await explorer.ui.ready\n",
"explorer.ui"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5a58d42e-0385-4f0a-a746-912d80cc2b9e",
"metadata": {},
"outputs": [],
"source": [
"# Interact with various properties of the SliceExplorer and watch the above Trame UI respond\n",
"\n",
"explorer.slice_dimension = \"level\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "530ad43e-d484-48f1-8465-9a11302b0ffe",
"metadata": {},
"outputs": [],
"source": [
"explorer.slice_value = 800"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "460818e8-3b93-4258-b303-8abea84d23fe",
"metadata": {},
"outputs": [],
"source": [
"explorer.view_mode = \"2D\""
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
26 changes: 26 additions & 0 deletions examples/slice_explorer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from argparse import ArgumentParser, BooleanOptionalAction

from pan3d import DatasetBuilder
from pan3d import SliceExplorer


def serve():
parser = ArgumentParser(
ayenpure marked this conversation as resolved.
Show resolved Hide resolved
prog="Pan3D",
description="Launch the Pan3D GeoTrame App",
)

parser.add_argument("--config_path")
parser.add_argument("--server", action=BooleanOptionalAction)
parser.add_argument("--debug", action=BooleanOptionalAction)
args = parser.parse_args()

builder = DatasetBuilder()
builder.import_config(args.config_path)

viewer = SliceExplorer(builder)
jourdain marked this conversation as resolved.
Show resolved Hide resolved
viewer.start()


if __name__ == "__main__":
serve()
4 changes: 3 additions & 1 deletion pan3d/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import logging
from .dataset_builder import DatasetBuilder


logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

__version__ = "0.8.9"

try:
from .dataset_viewer import DatasetViewer
from .explorers.slice_explorer import SliceExplorer
ayenpure marked this conversation as resolved.
Show resolved Hide resolved

__all__ = [DatasetBuilder, DatasetViewer]
__all__ = [DatasetBuilder, DatasetViewer, SliceExplorer]
except Exception:
# Trame is not installed, DatasetViewer will not be accessible
__all__ = [DatasetBuilder]
59 changes: 54 additions & 5 deletions pan3d/dataset_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,39 @@ def t_index(self, t_index: int) -> None:
self._viewer._time_index_changed()
self._viewer._mesh_changed()

@property
def t_size(self) -> int:
"""Returns the number of time slices available"""
if not self.t:
raise ValueError("Cannot set time index > 0 without setting t array first.")
t_coords = self.dataset[self.data_array_name].coords[self.t]
return t_coords.size

@property
def t_range(self) -> Tuple[Any]:
"""Returns the min and max values for the temporal coordinate"""
if not self.t:
raise ValueError("Cannot set time index > 0 without setting t array first.")
t_coords = self.dataset[self.data_array_name].coords[self.t].to_numpy().tolist()
return (t_coords[0], t_coords[-1])

@property
def t_values(self) -> List:
"""Returns the values for the temporal dimension"""
if not self.t:
raise ValueError("Cannot set time index > 0 without setting t array first.")
t_coords = self.dataset[self.data_array_name].coords[self.t]
return t_coords.to_numpy().tolist()

@property
def var_ranges(self) -> map:
"""Returns a map with variable names as keys and their ranges as values"""
range_map = {}
for var in self.dataset.data_vars:
arr = self.dataset[var].to_numpy()
range_map[var] = (arr.min(), arr.max())
return range_map

@property
def slicing(self) -> Dict[str, List]:
"""Dictionary mapping of coordinate names to slice arrays.
Expand All @@ -328,6 +361,20 @@ def slicing(self) -> Dict[str, List]:
"""
return self._algorithm.slicing

@property
def extents(self) -> map:
"""
Returns a map with dimension name as keys and their range (extents)
as values
"""
extents = {}
ayenpure marked this conversation as resolved.
Show resolved Hide resolved
dims = [self.x, self.y, self.z]
for i, dim in enumerate(dims):
if dim:
coords = self.dataset.coords[dim].to_numpy()
extents[dim] = (coords.min(), coords.max())
return extents

@slicing.setter
def slicing(self, slicing: Dict[str, List]) -> None:
if slicing is not None:
Expand Down Expand Up @@ -469,11 +516,13 @@ def _auto_select_slicing(
k: [
v[0],
v[1],
math.ceil((v[1] - v[0]) / self._resolution)
if self._resolution > 1 and v[1] - v[0] > 0 and k != self.t
else steps.get(k, 1)
if steps is not None and k != self.t
else 1,
(
math.ceil((v[1] - v[0]) / self._resolution)
if self._resolution > 1 and v[1] - v[0] > 0 and k != self.t
else steps.get(k, 1)
if steps is not None and k != self.t
else 1
),
]
for k, v in bounds.items()
}
Expand Down
Loading