3D Spatial Visualization#
ARGscape can visualize tree sequences with spatial location data in 3D, placing samples and ancestors on a geographic plane with time as the vertical axis.
All tree sequences in this tutorial were simulated in SLiM with continuous-space spatial models.
Loading Spatial Data#
For this tutorial, we’ll use tree sequences simulated in SLiM with spatial locations already embedded. Let’s start by loading a population expansion simulation.
import tskit
import argscape
# Load tree sequence with spatial coordinates
ts_spatial = tskit.load("../data/population_expansion_simplified.trees")
print(f"Samples: {ts_spatial.num_samples}")
print(f"Individuals with locations: {ts_spatial.num_individuals}")
# Show sample locations
print(f"\nSample locations (first 5):")
for node_id in ts_spatial.samples()[:5]:
node = ts_spatial.node(node_id)
if node.individual >= 0:
ind = ts_spatial.individual(node.individual)
if len(ind.location) >= 2:
print(f" Sample {node_id}: ({ind.location[0]:.3f}, {ind.location[1]:.3f})")
Samples: 60
Individuals with locations: 81
Sample locations (first 5):
Sample 0: (7.365, 8.225)
Sample 1: (7.365, 8.225)
Sample 2: (9.602, 7.876)
Sample 3: (9.602, 7.876)
Sample 4: (4.593, 2.328)
Basic 3D Visualization#
Use mode="spatial_3d" to create a 3D visualization. The default geographic_base="unit_grid" works with coordinates in the 0-1 range.
viz = argscape.visualize(ts_spatial, mode="spatial_3d")
viz.display()
<argscape.visualize.VizResult at 0x10c9a9be0>
Adjusting Spatial and Temporal Scaling#
The spatial_multiplier and temporal_multiplier parameters control the relative scaling of the X-Y plane and Z (time) axis.
# Emphasize spatial extent
viz = argscape.visualize(
ts_spatial,
mode="spatial_3d",
spatial_multiplier=400, # Expand X-Y plane
temporal_multiplier=6, # Compress time axis
width=1400
)
viz.display()
<argscape.visualize.VizResult at 0x10c992710>
# Emphasize temporal depth
viz = argscape.visualize(
ts_spatial,
mode="spatial_3d",
spatial_multiplier=120, # Normal X-Y
temporal_multiplier=10, # Stretch time axis
)
viz.display()
<argscape.visualize.VizResult at 0x10a0a79d0>
Dark Theme for 3D#
The tskit dark theme often works well for 3D visualizations, providing good contrast.
viz = argscape.visualize(
ts_spatial,
mode="spatial_3d",
theme="tskit",
sample_node_size=10,
edge_opacity=0.5,
height=600
)
viz.display()
<argscape.visualize.VizResult at 0x10ca21a70>
Coloring by Population#
When your tree sequence has population assignments, you can color nodes by their population rather than by node type. This is useful for visualizing population structure and migration patterns.
Let’s load a different SLiM simulation that models a population split scenario.
# Load tree sequence with population structure
ts_pops = tskit.load("../data/population_split_simplified.trees")
print(f"Samples: {ts_pops.num_samples}")
print(f"Populations: {ts_pops.num_populations}")
# Show population assignments for samples
for pop in ts_pops.populations():
samples_in_pop = [n for n in ts_pops.samples() if ts_pops.node(n).population == pop.id]
print(f" Population {pop.id}: {len(samples_in_pop)} samples")
Samples: 60
Populations: 4
Population 0: 0 samples
Population 1: 20 samples
Population 2: 20 samples
Population 3: 20 samples
# Visualize with population coloring
viz = argscape.visualize(
ts_pops,
mode="spatial_3d",
theme="paper",
color_by_population=True, # Color nodes by population
sample_node_size=10,
spatial_multiplier=160,
temporal_multiplier=6
)
viz.display()
<argscape.visualize.VizResult at 0x10ca21350>
Each population gets a distinct color using golden-ratio-based hue distribution for visual separation. You can trace how lineages from different populations merge back in time.
Population coloring also works in 2D force graph mode:
# Population coloring in 2D mode
viz_2d = argscape.visualize(
ts_pops,
color_by_population=True,
sample_node_size=10,
show_sample_ids=True,
height=800
)
viz_2d.display()
<argscape.visualize.VizResult at 0x10ca74b90>
Complex Population Structures#
Let’s also look at a more complex SLiM simulation featuring a population split followed by reintroduction.
# Load tree sequence with complex population structure
ts_remix = tskit.load("../data/population_remix_simplified.trees")
print(f"Samples: {ts_remix.num_samples}")
print(f"Populations: {ts_remix.num_populations}")
print(f"Trees: {ts_remix.num_trees}")
# Show uneven population distribution
for pop in ts_remix.populations():
samples_in_pop = [n for n in ts_remix.samples() if ts_remix.node(n).population == pop.id]
print(f" Population {pop.id}: {len(samples_in_pop)} samples")
Samples: 60
Populations: 4
Trees: 6
Population 0: 0 samples
Population 1: 3 samples
Population 2: 25 samples
Population 3: 32 samples
# Visualize complex population structure
viz = argscape.visualize(
ts_remix,
mode="spatial_3d",
color_by_population=True,
sample_node_size=10,
spatial_multiplier=160,
temporal_multiplier=12
)
viz.display()
<argscape.visualize.VizResult at 0x10cb24270>
Geographic Bases#
ARGscape provides built-in geographic bases:
"unit_grid"- For coordinates in 0-1 range (default)"eastern_hemisphere"- For global coordinates (lon/lat)"world"- Full world map
You can also provide custom shapefiles - see Custom Shapefiles and Coordinate Reference Systems for details.
Filtering with Spatial Data#
Temporal and genomic filters work the same way in 3D mode.
# Filter to recent history
max_time = max(ts_spatial.node(n).time for n in range(ts_spatial.num_nodes))
viz = argscape.visualize(
ts_spatial,
mode="spatial_3d",
temporal_range=(0, max_time * 0.3), # Only recent 30% of history
spatial_multiplier=160,
temporal_multiplier=7,
width=900,
height=700
)
viz.display()
<argscape.visualize.VizResult at 0x10cb24160>
Comparing 2D and 3D Views#
The same tree sequence can be visualized in both modes to highlight different aspects.
# 2D view emphasizes topology
viz_2d = argscape.visualize(
ts_spatial,
mode="force_graph",
height=700
)
viz_2d.display()
<argscape.visualize.VizResult at 0x10ca54f50>
# 3D view shows spatial relationships
viz_3d = argscape.visualize(
ts_spatial,
mode="spatial_3d",
spatial_multiplier=160,
temporal_multiplier=12,
height=700
)
viz_3d.display()
<argscape.visualize.VizResult at 0x10ca55450>
Next Steps#
Custom Shapefiles and Coordinate Reference Systems - Use custom shapefiles and CRS
Filtering Data - Learn about data filtering
Exporting Visualizations - Export 3D visualizations