geo-sleuth photo geolocation workflow

Document type
Field manual
Updated
Reading time
11 min

Install geo-sleuth, process photos, compare location candidates and terrain, and assess uploads, precision, evidence rules and published case results.

Source
Document details
oldcircle/geo-sleuth
License
MIT

Compare candidate locations using traceable clues and computations, then report the supported scope, confidence levels, and evidence.

Workflow layers
3Decision, perception, and judgment
Python requirement
3.10+Run individual scripts with uv
Code license
MITCheck separate data and imagery terms

01Inputs, outputs, and scope

System purpose

geo-sleuth provides Python scripts for an agent to investigate photo locations. The skill organizes candidate locations and evidence records for photos you took or have permission to analyze.

Input or outputContents
InputOriginal photo and known context; hints still require verification
Main conclusionSupported level of the camera location and its uncertainty range
GeometryWGS84 coordinates, error radius, and camera heading when supported by evidence
Supporting materialEvidence image, actual commands, and output files
Unresolved questionsAlternative candidates, blocked routes, and missing evidence
Three layers of geolocation work
  1. Decision

    • board.py
    • Candidates, evidence, and ranking
  2. Perception

    • Preprocessing and lookups
    • Map and terrain calculations
    • Image similarity ranking
  3. Judgment

    • Agent inspects leading candidates
    • Checks evidence
    • Explains uncertainty

Scripts manage candidates and calculations; the agent judges actual outputs. A ranking does not confirm a location.

This is a skill folder for an agent that can read SKILL.md and execute shell commands. It does not provide a standalone photo geolocation website.

02Skill location and runtime

Installation and diagnostics

  • Not completed:

    Python 3.10 or later.

  • Not completed:

    Installed uv and curl.

  • Not completed:

    An agent that can execute shell commands and view images.

  • Not completed:

    The complete skill folder, including helper modules.

  • Not completed:

    Chrome or Playwright Chromium when reverse-image search is needed.

Select your agent in the skills CLI's interactive installer:

bash
npx skills add Oldcircle/geo-sleuth

This installation route requires Node.js/npm; the Python scripts use uv to manage dependencies. To inspect and run the source, use these paths:

bash
git clone https://github.com/Oldcircle/geo-sleuth
cd geo-sleuth
uv run skills/geo-sleuth/scripts/doctor.py --json

To check service connectivity, run:

bash
uv run skills/geo-sleuth/scripts/doctor.py --network --json

doctor.py --network does not upload photos or load OCR or ML models. Dependency installation may still access the network on the first uv run.

If the reverse-search browser is missing, install Chromium with the documented command:

bash
uvx playwright install chromium

03Computation paths selected by evidence

Core tools

The cards below name scripts or subcommands. When running from the clone root, prefix each name with uv run skills/geo-sleuth/scripts/; for an installed skill, use its actual path.

Photo preprocessing

intake.py

Prepare initial material

Collect metadata, crops, OCR, and reverse-search results; --no-rev skips the reverse-search branch that uploads photos.

Candidate management

board.py

Record and compare evidence

Manage candidates, clue status, and ranking; read check output instead of relying on its exit code.

Map geometry

osm.py

Build geographic candidates

Query OSM features and linear infrastructure; sparse data does not establish that a real feature is absent.

Ridgeline comparison

terrain.py

Scan terrain and constrain sight lines

Calculate horizons from elevation and compare ridges; a single matching sight line cannot confirm the camera position.

Satellite image ranking

sat_scan.py

Narrow the image inspection scope

Rank grid cells or candidate points with CLIP; the agent still needs to inspect leading thumbnails.

Ground image ranking

match.py

Compare Street View candidates

Use DINOv2 or CLIP, with optional SIFT reranking; a similarity score does not prove a location.

Evidence delivery

evidence.py

Combine maps and comparison images

Draw evidence from existing images and a JSON spec; this does not perform the preceding geolocation calculations.

Photo clueAvailable toolsVerification limit
Readable text or signsocr.py, clues.py, poi.pyCheck text found only after enlargement against the original
Shadows or sunsun.pyVerify assumptions about time, mirroring, and camera direction
Ridgelines and linear infrastructureterrain.py, osm.py, geo.pyAnother independent geometric constraint is needed to confirm the position
Image control points with known locationspose.pyFeature identification and coordinate quality affect the solution
Regional or country cluesregions.py, gazetteer.pyDo not assume a well-known place is the only candidate

04Hypotheses, downweighting, and exclusion

Candidate board and evidence rules

Record clues by status to distinguish hypotheses from measurements:

StatusMeaningUse
observedAn observation in the imageRecord visible features and keep the crop or original
readText or data that was readPoint to a source file that can be checked
inferredA hypothesis drawn from cluesReinspect the image when hints conflict with metadata
computedA script's calculation resultPoint to the command actually run and its output

observed and inferred can change candidate weights but cannot directly justify exclusion. Exclusion must meet the evidence conditions; the existence of a file does not establish that its contents were interpreted correctly.

OperationRule
Add candidatesList the full reasonable candidate set; do not select an answer by population or fame
Record counterevidenceDownweight first; an unexamined candidate cannot count as excluded
Exclude an area or roadThe evidence coverage must support the exclusion scope; record covered locations with --covers
Refine a locationPreserve candidate containment relationships with --parent
Resolve a tieUse next to choose a shared discriminating test before scheduling a scan
Handle discrete candidatesSelect the best-supported main candidate and list alternatives; do not average their coordinates

The board's share is a relative score among the recorded candidates, not a calibrated photo geolocation success rate. Treat final confidence levels separately from candidate ranking.

05Coverage, error, and cost

Operating principles

Check data coverage first

If OSM or Street View has no data, use another justified source. A failed lookup does not prove that a place is absent.

Keep the original resolution

README display images are not the original case inputs. Keep the original and inspect crops; do not use thumbnails to reproduce the author's reported error.

Separate independent evidence

Repeated photos of the same sign do not provide multiple independent supports. Compare candidates using stable terrain or public infrastructure.

Set an investigation budget

Agree on the required level and stopping conditions first. When a service fails, choose a viable route instead of repeatedly sending the same failed request.

On first use, match.py and sat_scan.py install ML dependencies and download weights. The scripts try devices in this order; use GEO_DEVICE to select a supported device:

  • CUDA.

  • MPS.

  • CPU.

Allow time and disk space for model downloads and image processing. Agent costs depend on the host tool and model plan; the MIT code license does not guarantee a cost-free investigation.

06Preprocessing, investigation, and delivery

Public landscape example

This example uses public-landscape.jpg, a photo you took on a public trail and may lawfully analyze. Set the initial target to area level, and do not locate people who have not consented.

These commands define a workflow example. This report did not analyze a photo. Create a new case from the clone root:

bash
uv run skills/geo-sleuth/scripts/intake.py public-landscape.jpg --out-dir case-landscape/intake --no-rev
uv run skills/geo-sleuth/scripts/board.py --board case-landscape/board.json init --photo public-landscape.jpg --case public-landscape

Inspect these outputs first; the initial board contains no geolocation evidence:

FileCheck
case-landscape/intake/intake.mdSuccessful, failed, and skipped steps
case-landscape/intake/intake.jsonresult and each step's status
case-landscape/intake/edges/Whether crops support the initial observations
case-landscape/board.jsonEmpty candidate records initialized for the photo

Give the following task specification to an agent with the skill loaded:

text
Analyze public-landscape.jpg, which I took on a public trail. Aim for area-level identification first. Begin with case-landscape/intake and case-landscape/board.json. Ask before uploading the photo to reverse-image search. Record every executed command and its output file. Compare all justified candidates. Do not invent coordinates or precision. Report alternatives, failed routes, and missing evidence if the location remains unresolved.

The agent should choose a path from the actual clues. Create case-landscape/spec.json only after confirming the map and geometry:

Field or dependencyCondition
mapPath to a tiles.py fetch image; a same-stem JSON georeference file must sit beside it
cameraEvidence-supported WGS84 camera position
heading, hfovDetermined by actual measurements or justified camera geometry
panelsGround comparison images that can be checked; paths are relative to the spec

Inspect the map sidecar's failed_tiles and the actual image before composing the evidence image:

bash
uv run skills/geo-sleuth/scripts/evidence.py case-landscape/spec.json --out case-landscape/evidence.jpg

Then check the candidate board:

bash
uv run skills/geo-sleuth/scripts/board.py --board case-landscape/board.json check --evidence case-landscape/evidence.jpg

If the command prints FAIL, stop and fix the problem. After the content checks pass, merge the candidate records into the result file:

bash
uv run skills/geo-sleuth/scripts/board.py --board case-landscape/board.json report --merge case-landscape/result.json

report --merge organizes candidate records; it does not calculate true coordinates from the photo. If evidence is insufficient, report the supported level and missing information instead of generating a location to fill the fields.

07Scope supported by the report

Precision and confidence levels

For an error radius no greater than 100 m, the skill requires the intersection of two independent constraints or ground image agreement on at least three invariant features. This evidence threshold does not replace checks of source quality and camera geometry.

Report levelHigh-confidence conditionsMedium-confidence conditions or limits
CityEvidence such as readable text or a confirmed landmarkMultiple independent weak clues agree
AreaAt least three overhead features match uniquely, the candidate area has been examined, and no second match remainsThe closest match still has unexamined alternatives
RoadAt least two distinctive ground features matchLayout and one feature agree; without ground imagery, high area confidence and a heading self-check are required
BuildingTwo independent camera position constraints plus at least three matching ground featuresOnly one of those evidence groups is available
FloorTwo types of reference agreeA single reference; omit the floor when confidence is low

Rate each level separately and report the finest level that reaches at least medium confidence. A single matching ridgeline only constrains a sight line; it cannot establish a camera position with meter-level precision.

The board's --level values differ from the report confidence table. Use levels supported by the CLI when creating candidates; do not pass report labels directly as arguments:

Supported candidate board levels
country, admin1, admin2, city, district, area, road, point

08Uploads, data completeness, and licensing

Limits and precautions

ConditionAction
doctor returns 0Read WARN entries and suggested fixes
intake returns 0It may be partial; inspect the report and per-step status
Reverse search returns no resultsCheck for search failure, CAPTCHA, or service unavailability
Black blocks appear on the maptiles.py fetch may still exit normally; check failed_tiles and do not treat missing imagery as counterevidence
Terrain tiles are missingThe default is failure; gaps after --allow-missing cannot count as valid terrain
Street View comparison scores are lowCheck the viewing angle and imagery date; unrefined does not mean zero matches
A large OSM query times outNarrow the area or change paths; do not repeat the same request
Rerunning board initHandle the existing file separately; do not erase investigation records you need to keep with --force

Evidence images use a WGS84-aligned basemap. Check the datum before using coordinates read from another map:

NameCommon use
WGS84GPS, OSM, and elevation data
GCJ-02Some Chinese map services
BD-09Baidu latitude and longitude
Baidu MercatorProjected coordinates for Baidu maps and panorama APIs

Check code and data licensing separately:

ItemTerms or action
Skill codeMIT
Wikipedia-derived lookup dataCC BY-SA 4.0; retain original sources and required attribution
Publishing OSM query resultsCredit OpenStreetMap contributors as required by ODbL and the source
Satellite and Street View imageryFollow each service's terms; this project's MIT license does not automatically grant unrestricted redistribution rights

Case directories and .geo-cache/ may retain source information. Inspect file contents before sharing; do not publish unauthorized photos or private locations.

09Interpreting the author's records

Official cases and evaluation limits

The README lists these reruns by fresh agents. The figures are author-reported and were not reproduced for this report:

CaseHintAuthor-reported distanceTime
Desert ridgelineIn China87 m from the confirmed camera position32 min
Vancouver cherry blossom streetNone3 m from the confirmed camera position53 min

Both photos had previously been solved, and the resulting lessons were incorporated into the skill. The README therefore treats them as regression checks. They do not establish overall accuracy on unseen photos; a public blind-test set of unseen photos remains on the roadmap.

An individual operator's retrieval ranking does not establish end-to-end geolocation success. When evaluating your own use case, retain unresolved cases and actual investigation costs.

10Environment changes and source revision

Next actions

Next taskAction
Service failureRerun the relevant doctor diagnostics and record failed routes
Change model deviceCheck GEO_DEVICE and the current torch build's support
New country dataReview region packs and lookup sources before deciding the supported scope
Report an errorProvide the first step that diverged and the minimum checkable data; do not post private homes or personally identifiable material

Current tool messages and documentation are primarily in English. OCR and source text retain their original language; the agent explains them in the reader's language. The frozen zh-final tag does not replace the current mainline documentation.

This manual reviews revision 1a9ecee153b02e2db2e62352002fa5b17bbd819d; the skill document is labeled v2. This report did not perform:

  • Photo geolocation or case reruns.

  • Photo uploads or map service queries.

  • Skill installation or model downloads.

References