Skip to content

Dimensionality Support in neurospatial

Last Updated: 2025-12-05

Summary

neurospatial supports 1D, 2D, and 3D spatial environments. Full 3D support including affine transformations.


Supported Dimensionalities

1D Environments (Linearized Tracks)

Use case: Linear tracks, mazes with defined paths, sequential spatial trajectories

How to create:

import networkx as nx
from neurospatial import Environment

# From a track graph with an explicit linearization order
graph = nx.Graph()
graph.add_node(0, pos=(0.0, 0.0))
graph.add_node(1, pos=(50.0, 0.0))
graph.add_edge(0, 1, edge_id=0, distance=50.0)

env = Environment.from_graph(
    graph=graph,
    edge_order=[(0, 1)],
    edge_spacing=0.0,
    bin_size=2.0,
)

Unique features: - env.to_linear(nd_position) - Convert N-D coordinates to linear position - env.linear_to_nd(linear_position) - Convert linear position to N-D coordinates - env.plot_1d() - 1D visualization - env.is_linearized_track == True

Typical applications: - Linear tracks (e.g., T-maze arms, figure-8 tracks) - Virtual reality corridors - Sequential navigation analysis


2D Environments (Grid-Based Layouts)

Use case: Open field navigation, arenas, complex 2D environments

How to create:

from neurospatial import Environment
import numpy as np

# From 2D position samples
data_2d = np.random.randn(1000, 2) * 50  # shape (n_samples, 2)
env = Environment.from_samples(data_2d, bin_size=2.0)

# From polygon boundary
from shapely.geometry import Polygon
polygon = Polygon([(0, 0), (100, 0), (100, 100), (0, 100)])
env = Environment.from_polygon(polygon, bin_size=2.0)

# From a pixel / image mask (boolean array, not a file path)
import imageio.v3 as iio
image_mask = iio.imread("arena_mask.png").astype(bool)
env = Environment.from_pixel_mask(image_mask, pixel_size=0.1)  # cm/pixel

Layout engines available: - RegularGridLayout - Standard rectangular grids - HexagonalLayout - Hexagonal tessellations - TriangularMeshLayout - Triangular tessellations - MaskedGridLayout - Grids with arbitrary active/inactive regions - ImageMaskLayout - Binary image-based layouts - ShapelyPolygonLayout - Polygon-bounded grids

Full feature support: - ✅ 2D affine transforms (rotation, scaling, translation) - ✅ Polygon regions - ✅ Image mask layouts - ✅ All spatial queries (bin_at, contains, neighbors, path_between) - ✅ Alignment and probability mapping between environments - ✅ Full visualization suite

Typical applications: - Open field experiments - Water maze navigation - Complex 2D arenas with barriers - Multi-room environments


3D Support Status

What Works in 3D

✅ Basic spatial binning:

data_3d = np.random.randn(1000, 3) * 50  # shape (n_samples, 3)
env_3d = Environment.from_samples(data_3d, bin_size=2.0)

# These all work:
bins = env_3d.bin_at(points_3d)
mask = env_3d.contains(points_3d)
neighbors = env_3d.neighbors(bin_idx)
path = env_3d.path_between(source_bin, target_bin)
dist = env_3d.distance_between(points_3d[0], points_3d[1])
bin_dist = float(env_3d.distance_to([target_bin])[source_bin])

✅ Connectivity graphs - Full 3D graph support

✅ Distance calculations - Euclidean distances in 3D

✅ Path finding - Shortest paths through 3D bin connectivity

✅ Composite environments - Merging 3D environments

✅ 3D affine transforms

from neurospatial.ops import from_rotation_matrix, scale_3d, translate_3d
from scipy.spatial.transform import Rotation

# 3D translation
transform = translate_3d(10, 20, 30)

# 3D scaling
S = scale_3d(1.5, 1.5, 1.5)

# 3D rotation using scipy
R = Rotation.from_euler('z', 45, degrees=True).as_matrix()
transform = from_rotation_matrix(R, translation=[10, 20, 30])

# Apply to 3D environment
from neurospatial.ops import apply_transform_to_environment
env_transformed = apply_transform_to_environment(env_3d, transform)

✅ Transform estimation - Estimate rigid, similarity, or affine transforms from point pairs

from neurospatial.ops import estimate_transform

# Works for 2D or 3D point correspondences
transform = estimate_transform(src_points_3d, dst_points_3d, kind="rigid")

What Doesn't Work in 3D

❌ Polygon regions

# Polygon regions are 2D only
polygon_coords = np.array([[0, 0], [10, 0], [10, 10], [0, 10]])
env_3d.regions.add("goal", polygon=polygon_coords)  # Will raise error

bins = env_3d.bins_in_region("goal")  # ValueError: Polygon regions only supported for 2D

❌ Image mask layouts - Binary images are inherently 2D

❌ Hexagonal and triangular layouts - Currently 2D-only tessellations

❌ 3D-specific visualization - No 3D plotting methods yet


Feature Compatibility Matrix

Feature 1D 2D 3D Notes
Core Functionality
Spatial binning ✅ ✅ ✅
Connectivity graphs ✅ ✅ ✅
Distance calculations ✅ ✅ ✅
Path finding ✅ ✅ ✅
Composite environments ✅ ✅ ✅
Regions
Point regions ✅ ✅ ✅
Polygon regions ❌ ✅ ❌ 2D only
Transforms
AffineND ❌ ✅ ✅ N-D transforms
Affine2D ❌ ✅ ❌ 2D only (legacy)
Rotation matrices ❌ ✅ ✅ scipy integration
Scaling ❌ ✅ ✅ scale_3d()
Translation ❌ ✅ ✅ translate_3d()
Transform estimation ❌ ✅ ✅ Auto-detect dims
Layout Engines
RegularGridLayout ❌ ✅ ✅
HexagonalLayout ❌ ✅ ❌ 2D tessellation
TriangularMeshLayout ❌ ✅ ❌ 2D tessellation
GraphLayout ✅ ❌ ❌ 1D linearization
MaskedGridLayout ❌ ✅ ✅
ImageMaskLayout ❌ ✅ ❌ Images are 2D
ShapelyPolygonLayout ❌ ✅ ❌ Polygons are 2D
Visualization
plot() ✅ ✅ ❌
plot_1d() ✅ ❌ ❌ 1D only
Alignment
Probability mapping ✅ ✅ ✅ N-D rotation matrices
Transform estimation ❌ ✅ ✅ Procrustes analysis

Best Practices

Choosing Dimensionality

Use 1D when: - Your spatial data follows a defined path or track - You need linearized position coordinates - Working with sequential navigation (T-maze, linear track)

Use 2D when: - Your spatial data is in an open 2D arena - You need polygon-based regions - Working with standard behavioral experiments (open field, water maze)

Use 3D when: - Your spatial data is truly 3D (flight, swimming with depth, climbing) - You only need basic binning and connectivity - You don't need transforms or polygon regions

Validation

Always verify dimensionality before assuming features work:

env = Environment.from_samples(data, bin_size=2.0)

# Check dimensionality
print(f"Dimensions: {env.n_dims}")  # 1, 2, or 3

# Check if 1D (linearized)
if env.is_linearized_track:
    linear_pos = env.to_linear(nd_position)
else:
    bin_idx = env.bin_at(nd_position)

# Check before using 2D-only features
if env.n_dims == 2:
    # Safe to use polygon regions, 2D transforms, etc.
    env.regions.add("goal", polygon=polygon_coords)
else:
    # Use point regions instead
    env.regions.add("goal", point=[10, 20, 30])

3D Support Status

Implemented: - ✅ 3D affine transformations (AffineND) - ✅ 3D rotation matrices (scipy integration) - ✅ Transform estimation from point pairs (Procrustes analysis) - ✅ Environment transformation with dimension validation - ✅ N-D probability mapping with rotation support

Planned for future releases: - 3D polyhedron regions (using trimesh or similar) - 3D visualization with matplotlib 3D or plotly - 3D-specific layout engines (volumetric tessellations) - Additional 3D examples and tutorials

Track progress: See GitHub Issues for 3D support roadmap


Getting Help

  • For 1D/2D questions: See main documentation and examples
  • For 3D questions: Check this guide for current limitations
  • Report issues: https://github.com/user/neurospatial/issues
  • Discussions: https://github.com/user/neurospatial/discussions