Skip to content

Commit a56a4c4

Browse files
Xingchen Heseisman
andauthored
Add a tutorial for plotting features on a 3-D surface (#4466)
Co-authored-by: Dongdong Tian <seisman.info@gmail.com>
1 parent ca22361 commit a56a4c4

1 file changed

Lines changed: 141 additions & 0 deletions

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
"""
2+
Plotting features on a 3-D surface
3+
==================================
4+
5+
In addition to draping a dataset (grid or image) on top of a topographic surface,
6+
you may want to add additional features like coastlines, symbols, and text
7+
annotations. This tutorial shows how to use :meth:`pygmt.Figure.coast`,
8+
:meth:`pygmt.Figure.plot3d`, and :meth:`pygmt.Figure.text` to add these features
9+
on a 3-D surface created by :meth:`pygmt.Figure.grdview`.
10+
11+
This tutorial builds a 3-D map with additional features in four steps:
12+
13+
1. Creating a 3-D surface
14+
2. Adding coastlines on a 3-D surface
15+
3. Adding symbols on a 3-D surface
16+
4. Adding text annotations on a 3-D surface
17+
"""
18+
19+
# %%
20+
21+
import pandas as pd
22+
import pygmt
23+
from pygmt.params import Axis, Frame
24+
25+
# %%
26+
# 1. Creating a 3-D surface
27+
# -------------------------
28+
#
29+
# In the first step, we create a 3-D topographic map using :meth:`pygmt.Figure.grdview`.
30+
# We use a region around Taiwan to demonstrate adding features on a 3-D surface.
31+
32+
# Define the study area in degrees East or North
33+
region_2d = [119, 123, 21, 26] # [lon_min, lon_max, lat_min, lat_max]
34+
35+
# Download elevation grid for the study region with a resolution of 5 arc-minutes.
36+
grd_relief = pygmt.datasets.load_earth_relief(resolution="05m", region=region_2d)
37+
38+
# Determine the 3-D region from the minimum and maximum values of the relief grid
39+
region_3d = [*region_2d, grd_relief.min().to_numpy(), grd_relief.max().to_numpy()]
40+
41+
fig = pygmt.Figure()
42+
43+
# Set up a colormap for topography and bathymetry that matches the grid range
44+
pygmt.makecpt(
45+
cmap="gmt/etopo1",
46+
series=[grd_relief.min().to_numpy(), grd_relief.max().to_numpy()],
47+
)
48+
49+
# Create a 3-D surface
50+
fig.grdview(
51+
projection="M12c", # Mercator projection with a width of 12 cm
52+
region=region_3d,
53+
grid=grd_relief,
54+
cmap=True,
55+
surftype="surface",
56+
shading="+a0/270+ne0.6",
57+
perspective=[157.5, 30], # Azimuth and elevation for the 3-D plot
58+
zsize="1.5c",
59+
facade_fill="darkgray",
60+
frame=Frame(axes="wSnE", axis=Axis(annot=True, tick=True)),
61+
)
62+
63+
# Add a colorbar
64+
fig.colorbar(perspective=True, annot=1000, tick=500, label="Elevation", unit="m")
65+
66+
fig.show()
67+
68+
# %%
69+
# 2. Adding coastlines on a 3-D surface
70+
# -------------------------------------
71+
#
72+
# Next, we add coastlines using :meth:`pygmt.Figure.coast` with a matching
73+
# ``perspective`` setting. Here we set the z-level to 0 so coastlines are drawn
74+
# at sea level.
75+
76+
# Add coastlines on top of the 3-D surface
77+
# Use an explicit perspective to match grdview (azimuth=157.5, elevation=30)
78+
# and set the z-level to 0 so the coastlines are drawn at sea level.
79+
fig.coast(perspective=[157.5, 30, 0], resolution="high", shorelines="1/1p,black")
80+
81+
fig.show()
82+
83+
# %%
84+
# 3. Adding symbols on a 3-D surface
85+
# ----------------------------------
86+
#
87+
# In the third step, we add star symbols on top of the same 3-D map. To plot
88+
# symbols on a 3-D surface, use :meth:`pygmt.Figure.plot3d`. The z-coordinate should be
89+
# set to a value at or above the maximum elevation to ensure the symbols are visible.
90+
# Note that 3-D rendering in GMT/PyGMT uses a painter's algorithm (depth sorting)
91+
# rather than true 3-D occlusion. From some viewpoints, symbols that should be
92+
# hidden behind terrain may still appear visible.
93+
94+
# Sample point data: five coastal cities around Taiwan
95+
cities = pd.DataFrame(
96+
{
97+
"longitude": [121.74, 121.61, 121.14, 120.30, 120.53],
98+
"latitude": [25.13, 23.99, 22.76, 22.63, 24.27],
99+
"name": ["Keelung", "Hualien", "Taitung", "Kaohsiung", "Taichung Port"],
100+
}
101+
)
102+
103+
# Use one common z-level so all stars are drawn at the same height above the surface.
104+
cities["z"] = grd_relief.max().to_numpy()
105+
106+
# Add five identical star symbols on top of the 3-D surface
107+
fig.plot3d(
108+
x=cities.longitude,
109+
y=cities.latitude,
110+
z=cities.z,
111+
style="a0.65c",
112+
fill="gold",
113+
pen="0.8p,black",
114+
perspective=True,
115+
)
116+
117+
fig.show()
118+
119+
# %%
120+
# 4. Adding text annotations on a 3-D surface
121+
# -------------------------------------------
122+
#
123+
# In the final step, we add text labels to the same 3-D map. To add text
124+
# annotations on a 3-D surface, use :meth:`pygmt.Figure.text` with
125+
# ``perspective=True``. Note that the current implementation of ``text`` does not
126+
# support a ``z`` parameter for controlling the vertical position of text labels.
127+
# The text will be placed at the base of the 3-D plot (z=0).
128+
129+
# Add text labels for cities
130+
# Note: text is placed at z=0 (base level) since z parameter is not yet supported
131+
fig.text(
132+
x=cities.longitude,
133+
y=cities.latitude,
134+
text=cities.name,
135+
perspective=True,
136+
font="11p,Times-Bold,red",
137+
)
138+
139+
fig.show()
140+
141+
# sphinx_gallery_thumbnail_number = 4

0 commit comments

Comments
 (0)