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, Affine3D) - ✅ 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