Skip to main content

Multi Spatial Subdivision System - A unified Python package for working with spatial grid systems

Project description

M3S - Multi Spatial Subdivision System

A unified Python package for working with hierarchical spatial grid systems. M3S (Multi Spatial Subdivision System) provides a consistent interface for working with different spatial indexing systems including Geohash, MGRS, H3, Quadkey, S2, Slippy Map tiles, A5, C-squares, GARS, Maidenhead, Plus Codes, and What3Words.

Features

✨ New Simplified API (v0.5.1+)

  • 🎯 Direct Grid Access: No instantiation needed—just m3s.H3, m3s.Geohash, etc.
  • 🌐 Universal Geometry: Single from_geometry() handles points, polygons, bboxes, GeoDataFrames
  • 🔍 Smart Precision: Auto-select optimal precision or choose by use case, area, or cell count
  • 🔄 Easy Conversion: Convert between grids with .to_h3(), .to_geohash(), etc.
  • 📦 Powerful Collections: Filter, map, export with GridCellCollection
  • 🔙 Fully Compatible: Existing code works unchanged

Core Capabilities

  • 12 Grid Systems: Geohash, MGRS, H3, Quadkey, S2, Slippy, A5, C-squares, GARS, Maidenhead, Plus Codes, What3Words
  • Area Calculations: All grids support area_km2 property
  • GeoPandas Integration: Native GeoDataFrame support with automatic CRS transformation
  • UTM Zone Integration: Automatic UTM zone detection and inclusion
  • Polygon Intersection: Find grid cells intersecting any Shapely polygon or GeoDataFrame
  • Hierarchical Operations: Parent/child relationships and multi-resolution analysis
  • Neighbor Finding: Get neighboring cells across all grid systems
  • Parallel Processing: Threaded parallelism and streaming for large datasets
  • Modern Python: Type hints, comprehensive documentation, full test coverage

Installation

uv pip install m3s

For development:

git clone https://github.com/yourusername/m3s.git
cd m3s
uv pip install -e ".[dev]"

Quick Start

✨ New Simplified API (v0.5.1+)

The new simplified API makes M3S easier to use with direct access to grid systems, auto-precision selection, and universal geometry handling:

import m3s
from shapely.geometry import Polygon

# Direct access to grid systems (no instantiation needed!)
cell = m3s.Geohash.from_geometry((40.7128, -74.0060))  # Point tuple (lat, lon)
print(f"Cell: {cell.id}, Area: {cell.area_km2:.2f} km²")
# Output: Cell: dr5rs, Area: 18.11 km²

# Works with any geometry type: points, polygons, GeoDataFrames, bbox tuples
polygon = Polygon([(-74.1, 40.7), (-73.9, 40.7), (-73.9, 40.8), (-74.1, 40.8)])
cells = m3s.H3.from_geometry(polygon)  # Uses default precision (7)
print(f"Found {len(cells)} H3 cells with total area {cells.total_area_km2:.2f} km²")
# Output: Found 3 H3 cells with total area 763.44 km²

# For optimal precision with large areas, find it explicitly first:
precision = m3s.H3.find_precision(polygon, method='auto')  # Minimizes coverage variance
cells = m3s.H3.from_geometry(polygon, precision=precision)

# Get neighbors
neighbors = m3s.Geohash.neighbors(cell, depth=1)
print(f"Cell has {len(neighbors)} neighbors (including itself)")
# Output: Cell has 9 neighbors (including itself)

# Specific precision when needed
cells = m3s.A5.with_precision(8).from_geometry(polygon)

# Or find optimal precision
precision = m3s.H3.find_precision(polygon, method='auto')  # Minimizes coverage variance
cells = m3s.H3.from_geometry(polygon, precision=precision)

# Easy conversions between grid systems
h3_cells = cells.to_geohash()  # Convert H3 → Geohash
gdf = h3_cells.to_gdf()        # Convert to GeoDataFrame

# Collection operations
large_cells = cells.filter(lambda c: c.area_km2 > 10.0)
ids = cells.to_ids()
polygons = cells.to_polygons()

# Find precision by use case
precision = m3s.Geohash.find_precision_for_use_case('neighborhood')  # ~1-10 km²
# Other use cases: 'building', 'block', 'city', 'region', 'country'

Available grid systems:

  • m3s.A5 - Pentagonal DGGS (dodecahedral)
  • m3s.Geohash - Base32-encoded spatial index
  • m3s.MGRS - Military Grid Reference System
  • m3s.H3 - Uber's hexagonal grid
  • m3s.S2 - Google's spherical geometry
  • m3s.Quadkey - Microsoft Bing Maps tiles
  • m3s.Slippy - OpenStreetMap tiles
  • m3s.CSquares - Marine data indexing
  • m3s.GARS - Global Area Reference System
  • m3s.Maidenhead - Amateur radio locator
  • m3s.PlusCode - Open Location Codes
  • m3s.What3Words - 3-meter precision squares

🆚 API Comparison

Task New Simplified API Classic API
Get cell at point m3s.Geohash.from_geometry((40.7, -74.0)) GeohashGrid(precision=5).get_cell_from_point(40.7, -74.0)
Get cells in area m3s.H3.from_geometry(polygon) H3Grid(resolution=7).intersects(gdf)
Get neighbors m3s.Geohash.neighbors(cell) grid.get_neighbors(cell)
Find precision m3s.H3.find_precision_for_use_case('city') Manual selection
Convert grids cells.to_h3() Use conversion utilities
Export cells.to_gdf() Multiple steps

When to use each:

  • Simplified API: Quick start, exploratory analysis, standard workflows
  • Classic API: Fine-grained control, advanced customization, existing codebases

Both APIs work together seamlessly—choose what fits your workflow!

All Grid Systems (Classic API)

from m3s import (
    GeohashGrid, MGRSGrid, H3Grid, QuadkeyGrid, S2Grid, SlippyGrid,
    A5Grid, CSquaresGrid, GARSGrid, MaidenheadGrid, PlusCodeGrid, What3WordsGrid
)
from shapely.geometry import Point, box
import geopandas as gpd

# Create grids with different systems
grids = {
    'Geohash': GeohashGrid(precision=5),        # ~4,892 km² cells
    'MGRS': MGRSGrid(precision=1),              # 100 km² cells
    'H3': H3Grid(resolution=7),                 # ~5.16 km² cells
    'Quadkey': QuadkeyGrid(level=12),           # ~95.73 km² cells
    'S2': S2Grid(level=10),                     # ~81.07 km² cells
    'Slippy': SlippyGrid(zoom=12),              # ~95.73 km² cells
    'A5': A5Grid(resolution=5),                 # Pentagonal DGGS
    'C-squares': CSquaresGrid(precision=3),     # Marine data indexing
    'GARS': GARSGrid(precision=2),              # Global Area Reference System
    'Maidenhead': MaidenheadGrid(precision=3),  # Amateur radio locator
    'Plus Codes': PlusCodeGrid(precision=10),   # Open Location Codes
    'What3Words': What3WordsGrid(precision=3)   # 3-meter precision squares
}

# Get cell areas
for name, grid in grids.items():
    print(f"{name}: {grid.area_km2:.2f} km² per cell")

# Get cells for NYC coordinates
lat, lon = 40.7128, -74.0060
for name, grid in grids.items():
    cell = grid.get_cell_from_point(lat, lon)
    print(f"{name}: {cell.identifier}")

# Example output:
# Geohash: 5,892.00 km² per cell
# MGRS: 100.00 km² per cell  
# H3: 5.16 km² per cell
# Quadkey: 95.73 km² per cell
# S2: 81.07 km² per cell
# Slippy: 95.73 km² per cell
#
# Geohash: dr5ru
# MGRS: 18TWL8451
# H3: 871fb4662ffffff
# Quadkey: 120220012313
# S2: 89c2594
# Slippy: 12/1207/1539

GeoDataFrame Integration with UTM Zones

import geopandas as gpd
from m3s import H3Grid, QuadkeyGrid, SlippyGrid
from shapely.geometry import Point, box

# Create a GeoDataFrame
gdf = gpd.GeoDataFrame({
    'city': ['NYC', 'LA', 'Chicago'],
    'population': [8_336_817, 3_979_576, 2_693_976]
}, geometry=[
    Point(-74.0060, 40.7128),  # NYC
    Point(-118.2437, 34.0522), # LA  
    Point(-87.6298, 41.8781)   # Chicago
], crs="EPSG:4326")

# Intersect with any grid system - includes UTM zone information for applicable grids
grid = H3Grid(resolution=7)
result = grid.intersects(gdf)
print(f"Grid cells: {len(result)}")
print(result[['cell_id', 'utm', 'city', 'population']].head())

# Example output:
#            cell_id    utm       city  population
# 0  8828308281fffff  32618        NYC    8336817
# 1  88283096773ffff  32611         LA    3979576
# 2  8828872c0ffffff  32616    Chicago    2693976

# Web mapping grids (Quadkey, Slippy) don't include UTM zones
web_grid = SlippyGrid(zoom=12)
web_result = web_grid.intersects(gdf)
print(web_result[['cell_id', 'city']].head())
# Output:
#        cell_id       city
# 0  12/1207/1539        NYC
# 1  12/696/1582          LA
# 2  12/1030/1493    Chicago

MGRS Grids with UTM Integration

from m3s import MGRSGrid

# Create an MGRS grid with 1km precision
grid = MGRSGrid(precision=2)

# Get a grid cell from coordinates
cell = grid.get_cell_from_point(40.7128, -74.0060)
print(f"MGRS: {cell.identifier}")

# Intersect with GeoDataFrame - automatically includes UTM zone
result = grid.intersects(gdf)
print(result[['cell_id', 'utm']].head())
# Output shows MGRS cells with their corresponding UTM zones:
#   cell_id    utm
# 0  18TWL23  32618  # UTM Zone 18N for NYC area

H3 Grids

from m3s import H3Grid

# Create an H3 grid with resolution 7 (~4.5km edge length)
grid = H3Grid(resolution=7)

# Get a hexagonal cell from coordinates
cell = grid.get_cell_from_point(40.7128, -74.0060)
print(f"H3: {cell.identifier}")

# Get neighboring hexagons (6 neighbors)
neighbors = grid.get_neighbors(cell)
print(f"Neighbors: {len(neighbors)}")

# Get children at higher resolution
children = grid.get_children(cell)
print(f"Children: {len(children)}")  # Always 7 for H3

# Find intersecting cells with UTM zone information
result = grid.intersects(gdf)
print(result[['cell_id', 'utm', 'city']].head())

Grid Systems

Geohash

Hierarchical spatial data structure using Base32 encoding. Each character represents 5 bits of spatial precision.

  • Precision Levels: 1-12
  • Cell Shape: Rectangular
  • Use Cases: Databases, simple spatial indexing

MGRS (Military Grid Reference System)

Coordinate system based on UTM with standardized square cells.

  • Precision Levels: 0-5 (100km to 1m)
  • Cell Shape: Square
  • Use Cases: Military, surveying, precise location reference

H3 (Uber's Hexagonal Hierarchical Spatial Index)

Hexagonal grid system with uniform neighbor relationships and excellent area representation.

  • Resolution Levels: 0-15
  • Cell Shape: Hexagonal
  • Use Cases: Spatial analysis, ride-sharing, logistics

Quadkey (Microsoft Bing Maps)

Quadtree-based square tiles used by Microsoft Bing Maps.

  • Levels: 1-23
  • Cell Shape: Square
  • Use Cases: Web mapping, tile-based applications

S2 (Google's Spherical Geometry)

Spherical geometry cells using Hilbert curve for optimal spatial locality.

  • Levels: 0-30
  • Cell Shape: Curved (spherical quadrilaterals)
  • Use Cases: Large-scale applications, global spatial indexing

Slippy Map Tiles

Standard web map tiles used by OpenStreetMap and most web mapping services.

  • Zoom Levels: 0-22
  • Cell Shape: Square (in Web Mercator projection)
  • Use Cases: Web mapping, tile servers, caching

API Reference

BaseGrid

All grid classes inherit from BaseGrid:

class BaseGrid:
    @property
    def area_km2(self) -> float
        """Theoretical area in km² for cells at this precision/resolution/level"""
    
    def get_cell_from_point(self, lat: float, lon: float) -> GridCell
    def get_cell_from_identifier(self, identifier: str) -> GridCell
    def get_neighbors(self, cell: GridCell) -> List[GridCell]
    def get_children(self, cell: GridCell) -> List[GridCell]
    def get_parent(self, cell: GridCell) -> Optional[GridCell]
    def get_cells_in_bbox(self, min_lat: float, min_lon: float, 
                         max_lat: float, max_lon: float) -> List[GridCell]
    def get_covering_cells(self, polygon: Polygon, max_cells: int = 100) -> List[GridCell]
    
    # GeoDataFrame integration methods with UTM zone support
    def intersects(self, gdf: gpd.GeoDataFrame, 
                  target_crs: str = "EPSG:4326") -> gpd.GeoDataFrame

Parallel Processing

from m3s.parallel import ParallelGridEngine, ParallelConfig

# Configure parallel processing
config = ParallelConfig(
    n_workers=4,
    chunk_size=10000
)

# Process large datasets in parallel
engine = ParallelGridEngine(config)
result = engine.intersect_parallel(grid, large_gdf)

UTM Zone Integration

All grid systems now automatically include a utm column in their intersects() results:

  • MGRS: UTM zone extracted directly from MGRS identifier
  • Geohash: UTM zone calculated from cell centroid coordinates
  • H3: UTM zone calculated from hexagon centroid coordinates

The UTM column contains EPSG codes (e.g., 32614 for UTM Zone 14N, 32723 for UTM Zone 23S).

Development

Setup

git clone https://github.com/yourusername/m3s.git
cd m3s
uv pip install -e ".[dev]"

Running Tests

pytest

Code Formatting

black m3s tests examples

Type Checking

mypy m3s

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Dependencies

Required

  • Shapely - Geometric operations
  • PyProj - Coordinate transformations
  • GeoPandas - Geospatial data manipulation
  • mgrs - MGRS coordinate conversions
  • h3 - H3 hexagonal grid operations
  • s2sphere - S2 spherical geometry operations

Notes:

  • Geohash, Quadkey, and Slippy Map Tiles are implemented using pure Python (no external dependencies)
  • S2 functionality requires the s2sphere library for proper spherical geometry calculations

Acknowledgments

  • Built for geospatial analysis and location intelligence applications
  • Thanks to the maintainers of the underlying spatial libraries

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

m3s-0.5.2.tar.gz (181.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

m3s-0.5.2-py3-none-any.whl (163.4 kB view details)

Uploaded Python 3

File details

Details for the file m3s-0.5.2.tar.gz.

File metadata

  • Download URL: m3s-0.5.2.tar.gz
  • Upload date:
  • Size: 181.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for m3s-0.5.2.tar.gz
Algorithm Hash digest
SHA256 a3e814911c9bb59790a2e40777d49e623f2d10c98998420a23310a0aeb7f5a0e
MD5 21e21711ad2241796af8ea5443e38841
BLAKE2b-256 68b819d3dc19f2d4caccf3a8b164a97cf80a5ca55d3dd74b366abbf464739110

See more details on using hashes here.

File details

Details for the file m3s-0.5.2-py3-none-any.whl.

File metadata

  • Download URL: m3s-0.5.2-py3-none-any.whl
  • Upload date:
  • Size: 163.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for m3s-0.5.2-py3-none-any.whl
Algorithm Hash digest
SHA256 69502429e6ce80027ebe0f97ca2a4091b468a5ad168746b34d26eb991a1343f3
MD5 39a05ae7aff4879839c10df26bed526f
BLAKE2b-256 fd8cf033e46fe15b8fb226152d32ddf4a0b7cc35cc918a9af01bfbaedafe7fab

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page