Python GEOINT/OSINT library with Rust-accelerated core
Project description
pygeospy ๐
Python GEOINT/OSINT library with a Rust-accelerated core. Given any image, coordinates, IP, or set of clues โ produce a location.
Install
pip install pygeospy
For optional heavy dependencies (vision, OCR, audio, etc.):
pip install "pygeospy[all]" # everything
pip install "pygeospy[coords,exif]" # pick modules
What makes pygeospy different?
| Feature | pygeospy | Other OSINT tools |
|---|---|---|
| Rust core | โ 10โ100ร faster batch math | Pure Python only |
| SAR module | โ NASAR grids + ISRID profiles | Not available |
| Full pipeline | โ analyze(anything) โ coordinates |
Module-only APIs |
| Acoustic analysis | โ BirdNET + siren classification | Not available |
| Offline-first | โ LLaVA/Ollama, zero API keys needed | Cloud-dependent |
Quick Start
import pygeospy
# Haversine distance (Rust-accelerated)
dist = pygeospy.coords.haversine(51.5, -0.1, 48.85, 2.35)
print(f"London โ Paris: {dist:.1f} km")
# Shadow โ latitude band
result = pygeospy.solar.latitude_band_from_shadow(
shadow_ratio=2.5, # shadow is 2.5ร taller than object
shadow_azimuth_deg=195, # shadow points south-southwest
)
print(f"Candidate latitude bands: {result.candidate_lat_bands}")
print(f"Season: {result.estimated_season}")
# EXIF extraction
exif = pygeospy.exif.extract("photo.jpg")
if exif.has_gps:
print(f"GPS: {exif.coordinates}")
# Full pipeline analysis
result = pygeospy.pipeline.analyze(
"mystery_photo.jpg",
shadow_ratio=2.5,
shadow_azimuth_deg=195,
vision_backend="llava", # offline, no API key needed
export=True, # saves HTML report, GeoJSON, KML, GPX
)
print(result.summary)
Modules
v0.1 โ Foundation
| Module | Description | Backend |
|---|---|---|
pygeospy.coords |
Haversine, bearing, UTM, MGRS, bounding boxes, elevation API | Rust + Python |
pygeospy.solar |
Shadow โ sun angle โ latitude bands, sunrise/sunset | Rust + Python |
pygeospy.exif |
GPS, camera fingerprinting, forensic scrub detection, batch | Python |
pygeospy.terrain |
Slope, aspect, TRI, viewshed, elevation profile | Rust + Python |
pygeospy.osm |
Overpass queries, building footprints, road density | Python |
pygeospy.geo |
Nominatim geocoding, reverse geo, IP geolocation | Python |
pygeospy.sar |
NASAR grid, corridors, POA zones, urgency scoring | Rust + Python |
pygeospy.export |
Folium maps, HTML reports, GeoJSON/KML/GPX | Python |
v0.2 โ Visual Intelligence
| Module | Description | Backend |
|---|---|---|
pygeospy.visual |
Infrastructure/sign/vegetation/vehicle clues, Claude/GPT-4V/LLaVA | Python |
pygeospy.chronos |
Shadow โ time of day, vegetation โ season, weather archives | Python |
pygeospy.language |
OCR, script detection (18 systems), sign geocoding | Python |
pygeospy.network |
IP/ASN, WiGLE BSSID, MAC OUI, email headers, crt.sh | Python |
pygeospy.satellite |
Sentinel-2 search, NDVI/EVI/MNDWI, change detection | Rust + Python |
pygeospy.acoustic |
BirdNET species โ region, siren tones, Whisper language | Python |
pygeospy.pipeline |
Unified analyze() engine, parallel execution |
Python |
CLI
# Full analysis
pygeospy analyze mystery_photo.jpg --shadow-ratio 2.5 --shadow-azimuth 195 --export
# Solar position
pygeospy solar position 51.5 -0.1 172 14.0
# Shadow โ latitude bands
pygeospy solar from-shadow 2.5 195 --doy 172
# EXIF extraction
pygeospy exif extract photo.jpg
# Coordinate conversion
pygeospy coords convert 48.8566 2.3522 --fmt all
# Haversine
pygeospy coords haversine 51.5 -0.1 48.85 2.35
# SAR grid
pygeospy sar grid --lat 47.6 --lon -122.3 --radius 3.0 --cell 0.5 --out grid.geojson
# SAR urgency
pygeospy sar urgency --age 8 --medical --hours 6 --night
# IP analysis
pygeospy analyze --ip 8.8.8.8
# Cache management
pygeospy cache stats
pygeospy cache clear
Architecture
pygeospy/
โโโ _rustcore/ # Rust crate (PyO3, abi3)
โ โโโ src/
โ โโโ lib.rs # Module entry point
โ โโโ coords.rs # Haversine, bearing, UTM, bbox
โ โโโ solar.rs # Solar elevation/azimuth, shadow geometry
โ โโโ terrain.rs # Slope, aspect, TRI, viewshed
โ โโโ sar.rs # Grid generation, POA rings, urgency
โ โโโ raster.rs # NDVI, EVI, pixel statistics, Otsu
โโโ pygeospy/ # Python package
โ โโโ __init__.py
โ โโโ _types.py # GeoResult, Clue, LatLon, BoundingBox
โ โโโ _utils.py # Shared utilities, rate limiter
โ โโโ _cache.py # Disk cache with TTL
โ โโโ coords.py # Rust wrapper + elevation/timezone APIs
โ โโโ solar.py # Rust wrapper + GeoJSON export
โ โโโ exif.py # EXIF extraction and forensics
โ โโโ terrain.py # Rust wrapper + DEM download
โ โโโ osm.py # Overpass API queries
โ โโโ geo.py # Nominatim + IP lookup
โ โโโ sar.py # Rust wrapper + GPX export
โ โโโ export.py # Folium maps, HTML reports
โ โโโ visual.py # Vision model integration
โ โโโ chronos.py # Temporal analysis
โ โโโ language.py # OCR + linguistic analysis
โ โโโ network.py # IP/network OSINT
โ โโโ satellite.py # Sentinel-2 + spectral indices
โ โโโ acoustic.py # Audio geographic signals
โ โโโ pipeline.py # Unified analysis engine
โ โโโ cli.py # Typer CLI
โโโ tests/
โ โโโ test_coords.py
โ โโโ test_solar.py
โ โโโ test_sar.py
โ โโโ test_terrain.py
โ โโโ test_pipeline.py
โโโ pyproject.toml
โโโ Makefile
โโโ README.md
Example: Brick-Wall-to-Coordinates Pipeline
The classic GEOINT workflow, automated:
import pygeospy
# Step 1: Check EXIF
exif = pygeospy.exif.extract("brick_wall.jpg")
# โ No GPS found, EXIF timestamp: 2024-06-15 14:23:00
# Step 2: Solar analysis from shadow
solar = pygeospy.solar.analyze_shadow(
shadow_ratio=2.1, # measured from image
shadow_azimuth_deg=200, # estimated from image
timestamp_utc="2024-06-15T14:23:00Z",
)
# โ Candidate bands: 35ยฐNโ55ยฐN (northern summer afternoon)
# Step 3: Visual clues (offline with LLaVA)
pygeospy.visual.set_backend("llava")
clues = pygeospy.visual.extract_clues("brick_wall.jpg")
# โ brick bond: English bond โ Northern Europe / UK
# โ mortar: white repointing โ post-1950 UK
# โ stone sill: grey limestone โ Northern England / Scotland
# Step 4: OSM region narrowing
from pygeospy._types import BoundingBox
bb = BoundingBox(50, -5, 58, 2) # England
arch = pygeospy.osm.architectural_tags(53.8, -1.5, radius_m=500)
# Step 5: Full pipeline
result = pygeospy.pipeline.analyze(
"brick_wall.jpg",
shadow_ratio=2.1,
shadow_azimuth_deg=200,
vision_backend="llava",
export=True,
)
print(result.summary)
print(result.candidate_countries[:3])
Building the Rust Core
The Rust core compiles to a single .pyd/.so file that works on Python 3.9+.
If Rust is unavailable, all modules fall back to pure-Python automatically (with a RuntimeWarning at import).
# Prerequisites
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
pip install maturin
# Development build (fast, unoptimised)
maturin develop
# Release build (optimised)
maturin build --release
Vision Model Backends
import pygeospy.visual as v
# Claude (best accuracy, requires API key)
v.set_backend("claude", api_key="sk-ant-...")
# GPT-4V (requires OpenAI API key)
v.set_backend("gpt4v", api_key="sk-...")
# LLaVA via Ollama โ FULLY OFFLINE, no API key needed
# Install: https://ollama.ai then: ollama pull llava:13b
v.set_backend("llava", base_url="http://localhost:11434", model="llava:13b")
# Rule-based only (no model)
v.set_backend("none")
Optional API Keys
| Service | Module | Required? | Notes |
|---|---|---|---|
| Anthropic Claude | visual |
Optional | Best visual analysis |
| OpenAI GPT-4V | visual |
Optional | Alternative |
| ip-api.com | geo, network |
Optional | Free tier: 45 req/min |
| WiGLE | network |
Optional | Wi-Fi BSSID lookup |
| What3Words | geo |
Optional | W3W address conversion |
| Meteostat | chronos |
Optional | Historical weather |
| Open-Topo-Data | coords, terrain |
Free / no key | Elevation data |
All core features work without any API keys.
Testing
pip install pytest
PYTHONPATH=. pytest tests/ -v
71 tests, no network required.
License
MIT ยฉ pygeospy contributors
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
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 pygeospy-0.2.0-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: pygeospy-0.2.0-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 180.2 kB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ebd977c372ab36e97c9ff8f2f8d34a8d463866496c54e1d0972bfdcfb5842369
|
|
| MD5 |
b4cdfa87e0c3dae52998023fc5fa0075
|
|
| BLAKE2b-256 |
2e5b8f782550e888b51803b7d88a61689b3c7b1f1b8cc56b403f2a61faaf5985
|