Skip to content
This repository was archived by the owner on Sep 26, 2025. It is now read-only.

Commit f68cb52

Browse files
authored
Added initial paths to Analyzing Meshes (#30)
1 parent d4e804e commit f68cb52

2 files changed

Lines changed: 64 additions & 47 deletions

File tree

earthsim/analysis.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ class LineCrossSection(param.Parameterized):
3535
Distance between samples in meters. Used for interpolation
3636
of the cross-section paths.""")
3737

38-
def __init__(self, obj, **params):
38+
def __init__(self, obj, paths=None, **params):
3939
super(LineCrossSection, self).__init__(**params)
4040
self.obj = obj
41-
self.path = Path([])
41+
paths = [] if paths is None else paths
42+
self.path = Path(paths, crs=ccrs.GOOGLE_MERCATOR)
4243
self.path_stream = PolyDraw(source=self.path)
4344
PolyEdit(source=self.path)
4445
self.sections = Dynamic(self.obj, operation=self._sample,
@@ -72,7 +73,10 @@ def _sample(self, obj, data):
7273
a variable number of elements batching must be enabled and
7374
the legend_limit must be set to 0.
7475
"""
75-
path = self.path_stream.element
76+
if self.path_stream.data is None:
77+
path = self.path
78+
else:
79+
path = self.path_stream.element
7680
if isinstance(obj, TriMesh):
7781
vdim = obj.nodes.vdims[0]
7882
else:
@@ -151,7 +155,10 @@ def _sample(self, obj, data):
151155
Rasterizes the supplied object across times returning
152156
an Image of the sampled data across time and distance.
153157
"""
154-
path = self.path_stream.element
158+
if self.path_stream.data is None:
159+
path = self.path
160+
else:
161+
path = self.path_stream.element
155162
if isinstance(obj, TriMesh):
156163
vdim = obj.nodes.vdims[0]
157164
else:

examples/user_guide/Analyzing_Meshes.ipynb

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
{
22
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"Very often there will be specific regions of a set of results that are the most of interest, and in those cases it can be helpful to pull out a particular region's data for visualization in a clearer way. The ``LineCrossSection`` and ``SurfaceCrossSection`` classes allow taking cross-sections of a mesh (or some other rasterizable object) along poly-lines drawn using the ``PolyDraw`` tool, letting you make an *x*/*z* or *x*/*z*/*t* plot from data laid out in *x*/*y*/*z* or *x*/*y*/*z*/*t* (respectively). These classes rasterize the data at a fixed resolution, and then sample the data at that resolution given the underlying polylines. The ``resolution`` may be defined in the units of the underlying coordinate system."
8+
]
9+
},
310
{
411
"cell_type": "code",
512
"execution_count": null,
@@ -28,22 +35,9 @@
2835
"cell_type": "markdown",
2936
"metadata": {},
3037
"source": [
31-
"This notebook provides a demo of ``LineCrossSection`` and ``SurfaceCrossSection`` helpers which allows taking cross-sections of a mesh along linestrings drawn using the ``PolyDraw`` tool. The helper rasterize the data at a fixed resolution and then samples the data at the same resolution given the underlying line strings. The ``resolution`` may be defined in the units of the underlying\n",
32-
"coordinate system."
33-
]
34-
},
35-
{
36-
"cell_type": "markdown",
37-
"metadata": {},
38-
"source": [
39-
"## Example 1: A static TriMesh"
40-
]
41-
},
42-
{
43-
"cell_type": "markdown",
44-
"metadata": {},
45-
"source": [
46-
"As a simple example we will pass the static TriMesh to the ``CrossSector`` and display it. If now draw poly lines on the plot (left) we can see the depth along the path appear on the right."
38+
"## Example 1: A static TriMesh\n",
39+
"\n",
40+
"As a first simple example, we will pass a static TriMesh to the ``LineCrossSection`` and display it. We'll pass a couple of sample polylines to start with, and if a live Python process is available we will be able to see the depth along the paths appear on the right, for those paths and any more that are subsequently drawn by the user."
4741
]
4842
},
4943
{
@@ -52,27 +46,29 @@
5246
"metadata": {},
5347
"outputs": [],
5448
"source": [
55-
"path = '../data/Chesapeake_and_Delaware_Bays.3dm'\n",
56-
"tris, verts = read_3dm_mesh(path)\n",
49+
"filename = '../data/Chesapeake_and_Delaware_Bays.3dm'\n",
50+
"\n",
51+
"cb_paths = [[(-8523594., 4588993.), (-8476533., 4578872.),\n",
52+
" (-8449931., 4562126.), (-8435409., 4539747.)],\n",
53+
" [(-8477265., 4544109.), (-8469725., 4430387.)]]\n",
54+
" \n",
55+
"tris, verts = read_3dm_mesh(filename)\n",
5756
"points = gv.operation.project_points(gv.Points(verts, vdims=['z']))\n",
5857
"trimesh = hv.TriMesh((tris, points))\n",
5958
"\n",
60-
"sector = LineCrossSection(trimesh)\n",
61-
"sector.view()"
59+
"sector1 = LineCrossSection(trimesh, cb_paths)\n",
60+
"sector1.view()"
6261
]
6362
},
6463
{
6564
"cell_type": "markdown",
6665
"metadata": {},
6766
"source": [
68-
"## Example 2: A time-varying mesh"
69-
]
70-
},
71-
{
72-
"cell_type": "markdown",
73-
"metadata": {},
74-
"source": [
75-
"The ``CrossSector`` also allows working with time-varying meshes. Here we will modify the static TriMesh data with a time-varying random offset to demonstrate that the ``CrossSector`` works even for data that is evolving temporally."
67+
"Each individual polyline (thick black connected line segments) on the left should result in one curve on the right, with a colored dot on that path corresponding to the color of the curve. The location of the dot will change as you hover in the plot on the right, allowing you to see which value in the curve corresponds to which location along the polyline.\n",
68+
"\n",
69+
"## Example 2: A time-varying mesh\n",
70+
"\n",
71+
"``LineCrossSection`` also allows working with time-varying meshes. Here we will modify the static TriMesh data with a time-varying random offset to demonstrate that the ``LineCrossSection`` also works for data that is evolving temporally."
7672
]
7773
},
7874
{
@@ -81,21 +77,29 @@
8177
"metadata": {},
8278
"outputs": [],
8379
"source": [
84-
"fpath = '../data/SanDiego_Mesh/SanDiego.3dm'\n",
85-
"tris, verts = read_3dm_mesh(fpath, skiprows=2)\n",
80+
"sd_paths = [\n",
81+
" [(-13037161., 3843454.), (-13041435., 3854275.), (-13045822., 3857996.),\n",
82+
" (-13048664., 3857210.), (-13050429., 3855575.), (-13048385., 3849452.),\n",
83+
" (-13048027., 3847896.)],\n",
84+
" [(-13042975., 3850040.), (-13063919., 3844636.)]\n",
85+
"]\n",
86+
"\n",
87+
"filename = '../data/SanDiego_Mesh/SanDiego.3dm'\n",
88+
"tris, verts = read_3dm_mesh(filename, skiprows=2)\n",
8689
"points = gv.operation.project_points(gv.Points(verts, vdims=['z'], crs=ccrs.UTM(11)))\n",
8790
"trimesh = gv.TriMesh((tris, points))\n",
8891
"\n",
89-
"fpath = '../data/SanDiego_Mesh/SanDiego_ovl.dat'\n",
90-
"dfs = read_mesh2d(fpath)\n",
92+
"filename2 = '../data/SanDiego_Mesh/SanDiego_ovl.dat'\n",
93+
"dfs = read_mesh2d(filename2)\n",
9194
"\n",
9295
"points = gv.operation.project_points(gv.Points((verts.x, verts.y), crs=ccrs.UTM(11)))\n",
9396
"\n",
9497
"def time_mesh(time):\n",
9598
" depth_points = points.add_dimension('Velocity', 0, dfs[time].values[:, 0], vdim=True)\n",
9699
" return gv.TriMesh((tris, depth_points), crs=ccrs.GOOGLE_MERCATOR)\n",
97100
"\n",
98-
"meshes = hv.DynamicMap(time_mesh, kdims='Time').redim.values(Time=sorted(dfs.keys()))"
101+
"time_dim = hv.Dimension('Time', values=sorted(dfs.keys()), default=3600)\n",
102+
"meshes = hv.DynamicMap(time_mesh, kdims=time_dim)"
99103
]
100104
},
101105
{
@@ -104,32 +108,38 @@
104108
"metadata": {},
105109
"outputs": [],
106110
"source": [
107-
"sector = LineCrossSection(meshes, resolution=100)\n",
108-
"sector.view(cmap=cm_n.rainbow, shade=True)"
111+
"sector2 = LineCrossSection(meshes, sd_paths, resolution=100)\n",
112+
"sector2.view(cmap=cm_n.rainbow_r, shade=True)"
109113
]
110114
},
111115
{
112116
"cell_type": "markdown",
113117
"metadata": {},
114118
"source": [
115-
"## Example 3: Sampling a Surface"
119+
"You can use the scrubber controls to animate both plots, showing both the map-based data and the curve cross sections over time.\n",
120+
"\n",
121+
"## Example 3: Sampling a Surface\n",
122+
"\n",
123+
"Instead of sampling individual Curve elements for each time as in example 2, we can take cross-sections across time, returning an Image plotting the sampled value across both distance and time. The ``SurfaceCrossSection`` class is a simple subclass of ``LineCrossSection`` that overrides the ``sample`` method to apply the sampling for all time values and return an ``Image``."
116124
]
117125
},
118126
{
119-
"cell_type": "markdown",
127+
"cell_type": "code",
128+
"execution_count": null,
120129
"metadata": {},
130+
"outputs": [],
121131
"source": [
122-
"Instead of sampling individual Curve elements for each time, we can also take cross-sections across time returning an Image plotting the sampled value across both distance and time. Here we will subclass the ``CrossSector`` and override the ``sample`` method to apply the sampling for all time values and return an ``Image``."
132+
"sector3 = SurfaceCrossSection(meshes, sd_paths[:1], resolution=100)\n",
133+
"sector3.view(cmap=cm_n.rainbow_r, shade=False)"
123134
]
124135
},
125136
{
126-
"cell_type": "code",
127-
"execution_count": null,
137+
"cell_type": "markdown",
128138
"metadata": {},
129-
"outputs": [],
130139
"source": [
131-
"sector = SurfaceCrossSection(meshes, resolution=100)\n",
132-
"sector.view(cmap=cm_n.rainbow, shade=False)"
140+
"The scrubber will now only animate the plot on the left, as time has been laid out vertically on the plot on the right. \n",
141+
"\n",
142+
"If you have a running Python process and want to draw your own line, first delete the existing one, because an overlay of non-transparent images will only show the one on top."
133143
]
134144
}
135145
],

0 commit comments

Comments
 (0)