Ultra-fast geospatial windowing with zero-copy memory mapping
Project description
GeoSlice
Ultra-fast geospatial windowing with zero-copy memory mapping.
Performance
Head-to-Head: GeoSlice vs Rasterio
============================================================
COMPARISON: 100 x 512x512 windows (4096x4096 4-band image)
============================================================
Method Time (s) Ops/s Speedup
------------------------------------------------------------
GeoSlice (mmap) 0.0003 305,737 154.6x
Rasterio 0.0506 1,977 1.0x
============================================================
Detailed Benchmarks
| Test | GeoSlice | Rasterio | Speedup |
|---|---|---|---|
| Single 512x512 window | 1.4us (690k ops/s) | 170us (5.9k ops/s) | 117x |
| 100 sequential windows | 129us (7.7k ops/s) | 16.6ms (60 ops/s) | 128x |
| 100 random windows | 126us (8.0k ops/s) | 30.4ms (33 ops/s) | 241x |
| 50-waypoint flight sim | 24us (41k ops/s) | 1.4ms (707 ops/s) | 59x |
Coordinate Transform Performance
| Operation | Time | Throughput |
|---|---|---|
| latlon->pixel (x1000) | 2.0ms | 494 ops/s |
| FOV->pixels (x1000) | 203us | 4,937 ops/s |
Install
pip install geoslice
For converting GeoTIFFs:
pip install geoslice[convert]
sudo apt install gdal-bin # Linux
Supported Python: 3.8+ Supported OS: Linux, macOS
Quick Start
1. Convert GeoTIFF (one-time)
from geoslice import convert_tif_to_raw
convert_tif_to_raw("input.tif", "output_map")
# Creates: output_map.bin, output_map.json
Or via CLI:
gdal_translate -of ENVI -co INTERLEAVE=BSQ input.tif output_map.bin
2. Access Windows
from geoslice import FastGeoMap
loader = FastGeoMap("output_map")
# Zero-copy window access (~690k ops/s)
window = loader.get_window(x=100, y=100, width=512, height=512)
print(window.shape) # (bands, height, width)
# Bounds-checked — raises ValueError if out of range
if loader.is_valid_window(x, y, w, h):
window = loader.get_window(x, y, w, h)
3. Drone Simulation
from geoslice import FastGeoMap, GeoTransform, FlightPath
loader = FastGeoMap("output_map")
geo = GeoTransform(loader.meta.transform, utm_zone=36)
# Generate spiral flight path
path = FlightPath.spiral(
center_lat=31.45,
center_lon=34.80,
num_waypoints=50,
altitudes=[50, 100, 150, 200],
)
# Extract windows along path (~41k waypoints/sec)
for state in path:
win = FlightPath.state_to_window(state, geo)
if win.is_valid(loader.width, loader.height):
data = loader.get_window(win.x, win.y, win.width, win.height)
# Process frame...
API
FastGeoMap
FastGeoMap(base_name: str, use_cpp: bool = None)
get_window(x, y, width, height)->np.ndarray(view, zero-copy). RaisesValueErrorif out of bounds.get_window_copy(x, y, width, height)->np.ndarray(copy, safe for modification)is_valid_window(x, y, width, height)->bool.width,.height,.bands,.shape,.meta
GeoTransform
GeoTransform(transform: tuple, utm_zone: int = 36)
latlon_to_pixel(lat, lon)->(px, py). Validates lat in [-80, 84] (UTM range).pixel_to_latlon(px, py)->(lat, lon)fov_to_pixels(altitude_m, fov_deg)->(width, height)
FlightPath
FlightPath.spiral(center_lat, center_lon, num_waypoints, altitudes, fov_deg)
FlightPath.linear(start_lat, start_lon, end_lat, end_lon, num_waypoints, altitude_m)
FlightPath.grid(min_lat, min_lon, max_lat, max_lon, rows, cols, altitude_m)
state_to_window(state, geo)->WindowParamscompute_windows(geo)->List[WindowParams]
How It Works
Rasterio (standard approach):
Seek -> Read -> Decompress -> Allocate -> Copy to RAM
GeoSlice (mmap approach):
Pointer arithmetic -> OS pages in 4KB chunks on-demand
The OS kernel handles caching, prefetching, and memory management. Random access is 241x faster because there's no decompression overhead.
Data Format
GeoSlice operates on pre-converted raw binary files (BSQ interleave) with JSON metadata sidecars:
.bin— Raw raster data in Band Sequential format (no compression).json— Metadata: dtype, dimensions, affine transform, CRS
The conversion step (convert_tif_to_raw or gdal_translate) is a one-time cost that enables all subsequent reads to be zero-copy via mmap.
Input Validation
The library validates inputs at system boundaries:
- JSON metadata is checked for required fields and valid values
- Binary file size is verified against metadata dimensions
- Window coordinates are bounds-checked before access
- UTM latitude range is validated in coordinate transforms
- Cache keys use collision-resistant hashing with coordinate verification
C++ Usage
#include <geoslice/geoslice.hpp>
geoslice::MMapReader reader("processed_map");
auto view = reader.get_window(100, 100, 512, 512);
// Zero-copy access
uint8_t pixel = view.at<uint8_t>(0, 0, 0); // band, y, x
WindowCache
Thread-safe LRU cache with shared_ptr semantics — cached data stays valid even after eviction as long as you hold a reference:
geoslice::WindowCache cache(64 * 1024 * 1024); // 64MB
cache.put(x, y, w, h, data_ptr, size);
auto entry = cache.get(x, y, w, h); // shared_ptr<const CachedWindow>
if (entry) {
// Safe to use entry->data even if cache evicts this slot later
process(entry->data.data(), entry->data.size());
}
Development
Setup
git clone https://github.com/PavelGuzenfeld/geoslice
cd geoslice
# Create venv
python3 -m venv .venv
source .venv/bin/activate
# Install with dev dependencies
pip install -e ".[dev]"
Run Tests
# All tests
pytest -v
# Benchmarks with comparison table
pytest tests/test_benchmark.py -v -s
# Just the comparison report
pytest tests/test_benchmark.py::TestDirectComparison -v -s
# Detailed benchmark stats
pytest tests/test_benchmark.py --benchmark-only --benchmark-columns=min,max,mean,ops
Build C++ (optional)
cmake -B build -DBUILD_PYTHON=OFF
cmake --build build
ctest --test-dir build --output-on-failure
Release
Releases are automated via GitHub Actions on version tags:
# Update version in pyproject.toml, setup.py, geoslice.hpp, __init__.py
git add -A
git commit -m "Release v0.1.0"
git tag v0.1.0
git push && git push --tags
License
MIT License. See LICENSE file for details.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file geoslice-0.1.0.tar.gz.
File metadata
- Download URL: geoslice-0.1.0.tar.gz
- Upload date:
- Size: 28.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6184a9244b2e249d16208e9e04e8a21ff8cbf83120e1ce1a2084b02c715fc9d4
|
|
| MD5 |
91657d25188356d3f0fa8a1daad4efd3
|
|
| BLAKE2b-256 |
e8b783d79932cab79fae7775498f4989ca67717b31a8cb9b90cddce2ee39722f
|
File details
Details for the file geoslice-0.1.0-py3-none-any.whl.
File metadata
- Download URL: geoslice-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e9843d9ecef7426f67e7573be52c9f8f491a7461a2c327fce9411792a95f998
|
|
| MD5 |
456e8bc23c903df6de7bf47ba5b3f133
|
|
| BLAKE2b-256 |
f65140872e75c4af4a0a96752dd01ab969e41f47d8253be575ab497996eb52f5
|