Skip to main content

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.

PyPI Python 3.9+ License: MIT Rust


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


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

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

pygeospy-0.2.0-cp39-abi3-win_amd64.whl (180.2 kB view details)

Uploaded CPython 3.9+Windows x86-64

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

Hashes for pygeospy-0.2.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 ebd977c372ab36e97c9ff8f2f8d34a8d463866496c54e1d0972bfdcfb5842369
MD5 b4cdfa87e0c3dae52998023fc5fa0075
BLAKE2b-256 2e5b8f782550e888b51803b7d88a61689b3c7b1f1b8cc56b403f2a61faaf5985

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