Image geolocation without EXIF is an inverse problem. A photograph shows effects such as relative position, shape, vegetation, relief, and open water. The location that produced those effects remains hidden. A credible answer needs more than a matching satellite image. It needs an evidence chain that another investigator can inspect, rerun, and challenge.
A public August 2026 case study offers an unusually concrete example. The author converted three island silhouettes into geometric constraints, searched millions of land-polygon triples with a CUDA kernel, applied shape, vegetation, and elevation filters, and then inspected 26 survivors. The reported answer was Oan Resort in Chuuk, Federated States of Micronesia.
The most reusable result is the workflow boundary: computation generates candidates; independent evidence promotes one candidate into a conclusion.
Evidence reviewed: 20 August 2026. I inspected the original challenge, write-up, repository at commit fbca30683357333682c749bc402127a56a2bbc34, public candidate files, official data documentation, and an independent walkthrough. I did not rerun the complete 880 MB-plus geospatial and CUDA pipeline.
Reading time: 10 minutes · About 1,900 words
TL;DR
- Freeze the source image, coordinate conventions, selected landmarks, tolerances, data versions, and code commit before searching.
- Convert visual impressions into explicit predicates. Keep each intermediate candidate set so later filters can be audited.
- The public repository verifies candidate counts of 948, 213, 137, and 26. Several earlier counts and the 204.1 ms GPU timing remain author-reported because the repository does not include equivalent run logs.
- An independent spherical calculation reproduces the final candidate's 324.97° bearing, 31.25° angle, and 0.6903 distance ratio.
- Oan Resort's site and an independent OSINT walkthrough corroborate the identity and location. The independent coordinates are about 85 metres from the repository's candidate point.
- Every major filter is heuristic. CUDA increases search throughput; it does not prove that the visual assumptions or thresholds are correct.
Start with a claim contract
The original OSINT Exercise #004 asks for three outputs: the resort name, island coordinates, and camera direction. That clear target is useful, but a reproducible investigation needs a stricter contract.
Record these items before running a search:
| Artifact | Minimum content |
|---|---|
| Source manifest | Original URL, retrieval time, file hash, dimensions, metadata output |
| Observation record | Which visible objects became P0, P1, and P2; pixel coordinates; uncertainty |
| Search specification | Coordinate order, units, formulas, thresholds, and exclusions |
| Data manifest | Dataset names, versions, licenses, download date, and hashes |
| Execution manifest | Code commit, dependencies, hardware, commands, and random or sampling rules |
| Decision ledger | Candidate counts, rejected candidates, rejection reasons, and final corroboration |
The case study publishes most of the execution layer through its GitHub repository. The repository contains staged Python scripts, a CUDA kernel, four candidate JSON files, and an HTML report. It does not contain the source image fingerprint JSON, the 880 MB land-polygon dataset, the NDVI-stage JSON, or a signed run log. Reproduction is therefore possible in principle, while the checked-in artifacts support only part of the reported execution history.
This distinction matters. Code existence is one evidence level. A recorded run is another. An independently repeated run is stronger again.
Compile the photograph into constraints
The author identified three land masses:
- P0: the small resort islet in the foreground;
- P1: the island to the right;
- P2: the island to the left, with visible relief.
A click tool records the three pixel positions and derives the angle at P0 plus the ratio of the two visible sides. Because the drone altitude, camera pose, lens parameters, and exact island centres are unknown, the author added a ±20% tolerance.
That operation should be understood as a hypothesis compiler. It transforms a qualitative observation into machine-testable fields:
photo observation
→ three labelled points
→ angle window + distance-ratio window
→ candidate predicate
The output is useful because it is inspectable. Its uncertainty remains substantial because image-plane geometry is only an approximation of ground geometry. A stronger implementation would save several independent clicks, report the resulting variance, and run a sensitivity analysis across plausible camera projections.
Reduce the search space before using the GPU
The OpenStreetMap land-polygon service provides worldwide land polygons as WGS84 shapefiles, including a split version designed for easier processing. A split polygon is a processing chunk rather than a guaranteed one-island record. The case study nevertheless treats polygon centroids as land-mass points, an approximation that can create artificial neighbours near coastlines and split boundaries. It then progressively narrows the search:
| Stage | Predicate | Reported or checked output |
|---|---|---|
| Tropical band | Latitude from -30° to +30° | 141,131 polygons, author-reported |
| Local density | No more than 10 neighbours within 5 km | 51,576 points, author-reported |
| Nearby cluster | At least three points within 20 km | 23,500 clusters, author-reported |
| Triple generation | All 3-point combinations after a 60-point cluster cap | 80,690,777 triples, author-reported |
| CUDA geometry mask | Angle, ratio, diameter, separation, and side-length windows | 158,784 raw matches, author-reported |
| Deduplication | One record per P0/P1/P2 identity | 8,915 triples, author-reported |
| Open-water rectangle | No extra land in the visible open-water region | 948 triples, present in repository |
| Coral-cay filter | Compactness and nearby micro-cay conditions | 213, present in repository |
| Oval filter | Aspect and minimum-rectangle fill | 137, present in repository |
| NDVI filter | P0 NDVI at least 0.6 | 66, author-reported; stage file absent |
| Elevation filter | Low P0 and 100–500 m relief in the viewing sector | 26, present in repository |
The ordering is the core engineering decision. Cheap spatial constraints shrink the problem before expensive combinations and remote data access. Network-bound Sentinel-2 sampling appears near the end. The 30 m Copernicus elevation check follows after the vegetation set has already become small.
This is bottleneck-aware search. Faster hardware helps only after the search predicate is small, explicit, and parallelizable.
What CUDA proves, and what it leaves open
The kernel assigns one thread to each candidate triple. It sorts the three points by polygon area, treats the smallest as P0, uses a 2D cross product to order P1 and P2, then evaluates the angle, distance ratio, island diameter, separation, and side-length windows. Passing indices are written through an atomic counter.
The original write-up reports 80.7 million threads, 5,169 MB of VRAM, and a 204.1 ms kernel time on an RTX 3050. Those values are plausible execution observations, but they are not independently reproduced here. The repository contains the code and downstream candidate files rather than a complete benchmark log.
More importantly, the kernel proves a narrow statement:
Given this input triple set and these thresholds, these triples satisfy the implemented predicate.
It does not validate the tropical assumption, the P0 labelling, the ±20% tolerance, the cluster sampling, or the mapping from image geometry to ground geometry. Computation can make a weak predicate run very quickly. The evidence chain must keep predicate validity separate from execution speed.
Recompute the final geometry independently
The eighth row of the final 26-candidate file contains:
P0 = (7.363300, 151.755983)
P1 = (7.386573, 151.739534)
P2 = (7.379860, 151.717975)
Using independent great-circle calculations:
- bearing from P0 to P1: 324.973°;
- bearing from P0 to P2: 293.720°;
- angle between the two bearings: 31.253°;
- P0–P1 distance: 3.160 km;
- P0–P2 distance: 4.578 km;
- distance ratio: 0.69031.
The repository records 31.25° and 0.6903, and the write-up classifies 324.97° as northwest. Those values agree at the published precision.
The current OpenStreetMap coastline object for Oan provides another data-level check. Computing the centroid of its version 11 closed polygon gives 7.3632996°N, 151.7559827°E, effectively the repository's P0. This is an island centroid, not a resort building or camera position.
The identity also has a separate verification path. The Oan Resort site confirms the resort name. An independent 2024 walkthrough used reverse-image search and Google Earth, reported Oan Resort at (7.3626, 151.7563), and concluded that the camera faced northwest. That coordinate is about 85 metres from the repository's P0 point.
The exact heading is less certain than the eight-direction label. The elevation stage uses the short-arc bisector of the P1 and P2 bearings, 309.35°, as the front of the camera. The final answer uses the P0-to-P1 bearing, 324.97°. Both fall within northwest, so the cardinal answer survives; the pipeline does not establish a precise camera heading. The six-decimal coordinates should likewise be read as an island-scale candidate point, not a sub-metre camera location.
The two paths use different mechanisms:
- geometry and global candidate search;
- image identity, map location, and visual orientation.
Their agreement is stronger than either path alone.
Keep a failure ledger for every heuristic
The pipeline succeeds as a case study. Its design also exposes several ways a true location could disappear.
Perspective and point selection
Three image points do not determine ground geometry when camera height, pitch, field of view, lens distortion, and island centres remain uncertain. A tolerance band absorbs some error without measuring it. Repeat the point selection, vary the projection assumptions, and report whether the same candidate survives.
Incomplete cluster sampling
Clusters above 60 points are sampled by polygon area. The resulting triple search is intentionally incomplete. Record the cap, sampling rule, number of discarded points, and whether the final candidate survives alternative samples.
Distance approximation
The clustering code queries a KD-tree directly in longitude and latitude with kilometres / 111. Longitude scale varies with latitude. The later CUDA calculation applies a local cosine correction, but the earlier neighbourhood search does not. At latitude 30°, an east-west radius expressed this way covers about 86.6% of the intended physical distance. Boundary cases can move across a threshold.
Area, perimeter, diameter, compactness, and oval metrics are also estimated directly in EPSG:4326 degrees rather than in a consistent local equal-distance or equal-area projection. Error is relatively small around the final candidate at 7.36°N, but it varies across the full ±30° search band.
Scene selection
The NDVI script queries Element 84's Earth Search STAC API for Sentinel-2 L2A scenes with scene-level cloud cover below 20% and sets limit=1. It does not specify a date range or sortby, nor does it inspect a pixel-level cloud or shadow mask. The code supports “the first returned matching scene,” rather than “the most recent clear target pixel.” Save the item ID, acquisition time, cloud metadata, quality mask, sampled bands, and pixel result.
The NDVI formula itself is standard: (NIR - Red) / (NIR + Red), as documented by the USGS. The threshold of 0.6 remains a case-specific heuristic.
Surface elevation and missing data
The official Copernicus DEM description identifies GLO-30 as a 30 m global digital surface model, which can include vegetation and built surfaces. The script samples P0 plus 77 points across an 11-direction, seven-radius fan. At 20 km, adjacent rays are roughly 3.5 km apart, so narrow relief can be missed. Missing P0 data defaults to zero, which can make the low-island condition easier to pass. A production workflow should carry missing as its own state and reject any candidate whose required measurement is absent.
A reusable evidence protocol for geolocation agents
An OSINT agent can implement this case as seven gates.
1. Preserve
Hash the source file, save metadata output, and work on a copy. Record every transformation. The Berkeley Protocol on Digital Open Source Investigations provides the broader preservation and verification model.
2. Formalize
Convert each observation into a field, unit, tolerance, and reason. Mark whether it is measured, inferred, or assumed.
3. Generate
Use broad, cheap predicates to produce candidates. Save the complete output and the data manifest.
4. Filter
Apply one named predicate per stage. Each row should retain the values that caused it to pass. Each rejected candidate should receive a machine-readable reason.
5. Stress-test
Rerun with plausible alternative clicks, thresholds, samples, dates, and distance calculations. A location that survives only one narrow setting remains fragile.
6. Corroborate
Use a different mechanism such as reverse-image identity, official business information, street or satellite imagery, another dataset, or an independent analyst. Avoid treating two services backed by the same map database as fully independent.
7. Publish by evidence level
| Level | Safe statement |
|---|---|
| Observation | The image contains three apparent land masses with these selected points |
| Candidate | This location satisfies the implemented constraints |
| Surviving candidate | This location also passes the declared shape, vegetation, and relief checks |
| Corroborated location | A separate mechanism supports the same place and orientation |
| Reproduced result | An independent environment reran the complete pipeline from frozen inputs |
This case reaches the corroborated-location level. The checked-in artifacts and independent calculations are strong; a complete independent pipeline rerun remains outstanding.
FAQ
Can you geolocate a photo without EXIF or GPS metadata?
Yes. Visual geometry, object identity, terrain, vegetation, shadows, signs, and map features can generate and test candidates. The output should carry a confidence level and an auditable record of assumptions.
Does a GPU make image geolocation more accurate?
A GPU increases throughput for a parallel predicate. Accuracy depends on the observations, candidate coverage, formulas, thresholds, data quality, and independent checks.
What belongs in an image geolocation evidence chain?
Include the source hash, metadata output, observation coordinates, assumptions, thresholds, dataset versions, code commit, commands, intermediate candidate sets, rejection reasons, independent corroboration, and uncertainty statement.
How should negative evidence be recorded?
Store the candidate, the exact failed predicate, the measured value, the threshold, the data source, and any missing-data state. This prevents a later analyst from reopening the same location without new evidence.
When is an exact coordinate too strong?
Use an area or candidate set when the result changes materially under plausible point selections, projections, thresholds, or datasets. Exact coordinates are justified only when independent evidence resolves the remaining ambiguity.
References
- Yassa9: Geolocating Random Islet Image Using Geometry & CUDA GPU Programming
- Yassa9: geoint/gralhix_004 repository
- Sofia Santos: OSINT Exercise #004
- Mugrat: Sofia's OSINT Exercise 004
- OpenStreetMap: Land Polygons
- OpenStreetMap: Oan islet, way 23706091
- Element 84: Earth Search API
- Copernicus Data Space: Copernicus DEM
- USGS: Normalized Difference Vegetation Index
- Natural Earth: Terms of Use
- Berkeley Protocol on Digital Open Source Investigations
Related: Mathematical AI Agents need a dual-loop verification protocol. The same separation applies here: broad exploration may generate candidates, while only independently checked evidence should enter trusted conclusions.