Skip to main content

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.10+ 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.10+. 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

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 Distributions

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

pygeospy-0.2.1-cp310-abi3-win_amd64.whl (239.7 kB view details)

Uploaded CPython 3.10+Windows x86-64

pygeospy-0.2.1-cp310-abi3-manylinux_2_34_x86_64.whl (324.6 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.34+ x86-64

pygeospy-0.2.1-cp310-abi3-macosx_11_0_arm64.whl (302.5 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file pygeospy-0.2.1-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: pygeospy-0.2.1-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 239.7 kB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pygeospy-0.2.1-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 adf949e0db53054245a0913c2e2ad2b43fa896571ce4cc3d00edb13d8b13288c
MD5 60aedb4ab77fa75cd0a90828f79d7683
BLAKE2b-256 d9d8378030000ec526eb251642911177d6310c854ede3053612299b51ca7d9ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygeospy-0.2.1-cp310-abi3-win_amd64.whl:

Publisher: release.yml on Londopy/pygeospy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygeospy-0.2.1-cp310-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for pygeospy-0.2.1-cp310-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 f1fccc7830ba3867def1bf1e661bf7bc9caff4ed95c783fa08e3be99f623f39a
MD5 f827152fb50bd2341fafbb731463c02d
BLAKE2b-256 98ff59f3867a05d82088bee8365b9ad5883cc4d773999f0794c4889dd1f085dd

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygeospy-0.2.1-cp310-abi3-manylinux_2_34_x86_64.whl:

Publisher: release.yml on Londopy/pygeospy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygeospy-0.2.1-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pygeospy-0.2.1-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4fd63e66eb3655acf1a471630e13dadd121c9bc32ea2e47e28da299223723bae
MD5 da52fb428fcef22328217b9e95ac0242
BLAKE2b-256 5611bfff97c47005526a8716e4904998208cbaaf9d5787097135637663ec0913

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygeospy-0.2.1-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on Londopy/pygeospy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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