daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

maps-and-satellite-imagery.md (70775B)


      1 ---
      2 title: "Maps, Satellite & Street-Level Imagery"
      3 description: "Choose the right imagery source for the question, find historical coverage, and work with the data in QGIS."
      4 category: osint
      5 subcategory: "Geospatial"
      6 tags: [osint, satellite, maps, gis, street-view]
      7 tools: [qgis, gdal, ogr2ogr, overpass, copernicus-data-space, google-earth-pro, google-earth-engine, mapillary, earthexplorer, landsatlook, stac-client, nasa-firms, nasa-gibs]
      8 difficulty: intermediate
      9 updated: 2026-10-04
     10 references:
     11   - name: "Bellingcat's Online Investigation Toolkit"
     12     url: "https://bellingcat.gitbook.io/toolkit"
     13     author: "Bellingcat"
     14     license: none
     15     relation: derived
     16     note: "Tool catalogue: names, descriptions, cost flags and links for this area."
     17   - name: "OSINT Newsletter Tools Library"
     18     url: "https://tools.osintnewsletter.com"
     19     author: "The OSINT Newsletter"
     20     license: none
     21     relation: derived
     22     note: "Second tool catalogue, cross-checked against the above."
     23   - name: "GDAL programs documentation"
     24     url: "https://gdal.org/en/stable/programs/"
     25     author: "OSGeo"
     26     relation: link-only
     27     note: "Upstream reference for every gdal* and ogr* flag quoted on this page."
     28   - name: "Overpass QL reference"
     29     url: "https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL"
     30     author: "OpenStreetMap contributors"
     31     relation: link-only
     32     note: "Upstream reference for the Overpass settings, filters and out modes used here."
     33   - name: "Copernicus Data Space OData API"
     34     url: "https://documentation.dataspace.copernicus.eu/APIs/OData.html"
     35     author: "Copernicus Data Space Ecosystem"
     36     relation: link-only
     37     note: "Upstream reference for the $filter, $orderby and attribute query syntax."
     38   - name: "Mapillary API documentation"
     39     url: "https://www.mapillary.com/developer/api-documentation"
     40     author: "Mapillary"
     41     relation: link-only
     42     note: "Upstream reference for the Graph API endpoints, parameters and image fields."
     43   - name: "NASA GIBS API documentation"
     44     url: "https://nasa-gibs.github.io/gibs-api-docs/"
     45     author: "NASA EOSDIS"
     46     relation: link-only
     47     note: "Upstream reference for the WMTS and WMS endpoint templates."
     48   - name: "NASA FIRMS Area API"
     49     url: "https://firms.modaps.eosdis.nasa.gov/api/area/"
     50     author: "NASA FIRMS"
     51     relation: link-only
     52     note: "Upstream reference for the fire-detection API path parameters."
     53 ---
     54 
     55 ## What this covers
     56 
     57 Which imagery to use, how to get at older coverage, and the street-level sources beyond Google.
     58 The main skill is matching the source to the question — resolution, revisit frequency and archive
     59 depth trade off against each other and no single provider wins on all three.
     60 
     61 ## Method
     62 
     63 1. **Fix the question before the source.** "Has this compound grown since 2019" and "what is the
     64    writing on that sign" are answered by different satellites, and no source answers both.
     65 2. **Bound the area and the dates.** A coordinate with no radius and a year with no window will
     66    produce thousands of scenes and no answer. Write both down first.
     67 3. **Start free and coarse.** Sentinel-2 at 10m settles most change-detection questions. Spend
     68    high-resolution effort only on confirming the specific thing it points at.
     69 4. **Record the capture date of every frame you use**, from the source's own metadata rather than
     70    the page you found it on. An undated image supports no time-sensitive claim.
     71 5. **Cross-check the base layer.** Two providers disagree on building footprints, place names and
     72    borders. Say which one you used, and look at a second before concluding a feature is new.
     73 6. **Measure in a metric CRS.** Distances taken in degrees are wrong by a factor that varies with
     74    latitude, and the tool will not warn you.
     75 7. **Pivot to street level last.** Once satellite imagery has given you candidates, Mapillary or
     76    Street View either confirms the ground detail or kills the candidate in seconds.
     77 
     78 ## Choosing a source
     79 
     80 | Question | Source |
     81 | --- | --- |
     82 | What does this place look like in detail? | Google Earth Pro, Esri World Imagery — sub-metre, but infrequent |
     83 | What changed between two dates? | Sentinel-2 (5-day revisit, 10m), Landsat (16-day, 30m, back to 1972) |
     84 | What did it look like years ago? | Google Earth Pro's historical slider, Landsat archive |
     85 | Was there a fire / flood / new construction? | Sentinel-2 false-colour composites, NASA FIRMS |
     86 | What is at street level? | Google Street View, Mapillary, KartaView, Yandex Panoramas |
     87 | What features exist here, as data? | OpenStreetMap via Overpass |
     88 
     89 Free high-cadence optical imagery bottoms out around 10m per pixel. Anything finer is commercial
     90 and usually costs real money, so plan around Sentinel for change detection and reserve high-res for
     91 confirming a specific thing.
     92 
     93 ## Imagery sources at a glance
     94 
     95 Resolution is the number people quote and the least useful of the four. Revisit and archive depth
     96 decide whether a question is answerable at all, and scriptability decides whether you can answer it
     97 for fifty coordinates instead of one.
     98 
     99 | Source | Resolution | Revisit | Archive back to | Scriptable access |
    100 | --- | --- | --- | --- | --- |
    101 | Sentinel-2 (Copernicus) | 10m visible | ~5 days | 2015 | OData and STAC, free, token for download |
    102 | Sentinel-1 (radar) | 5x20m | ~6 days | 2014 | same OData catalogue |
    103 | Landsat 8/9 | 30m, 15m pan | 16 days each | 1972 across the series | LandsatLook STAC, no key for search |
    104 | MODIS / VIIRS | 250m–1km | sub-daily | 2000 / 2012 | GIBS WMTS and WMS, no key |
    105 | Google Earth Pro | sub-metre | irregular, years apart | varies by place, often 1985 | none; desktop export only |
    106 | Esri World Imagery | sub-metre | irregular | current only, no slider | XYZ tile URL |
    107 | Mapillary | street level | contributor-driven | 2014 | Graph API, free token |
    108 | OpenStreetMap | vector, not imagery | continuous | full edit history | Overpass API, no key |
    109 
    110 Two consequences worth internalising. Nothing free gives you both sub-metre resolution and a dated
    111 archive, which is why high-resolution work means Google Earth Pro screenshots with the status-bar
    112 date, and change detection means Sentinel. And the published revisit figures are orbital, not
    113 usable: see the cloud arithmetic in the worked example below, where a nominal five-day revisit
    114 yielded eighteen scenes in a month and two worth opening.
    115 
    116 ## Historical imagery
    117 
    118 **Google Earth Pro** is free desktop software and its historical imagery slider is the most
    119 accessible archive of high-resolution coverage. Note the imagery date shown at the bottom — it is
    120 the single most important piece of context and the most commonly ignored.
    121 
    122 **Landsat** goes back to 1972 and is the only free option for multi-decade change. Browse it via
    123 [EarthExplorer](https://earthexplorer.usgs.gov/), covered below, or query the same archive through
    124 the LandsatLook STAC API, which needs no account.
    125 
    126 ## Street-level beyond Google
    127 
    128 Google's coverage is deep but not universal, and its capture dates are sometimes years old:
    129 
    130 - **Mapillary** — crowd-sourced, often covers roads Google skipped, frequently more recent.
    131 - **KartaView** — similar model, strong in parts of Europe.
    132 - **Yandex Panoramas** — the best coverage across Russia and Central Asia by a wide margin.
    133 - **Baidu Total View** — mainland China.
    134 
    135 Always check several; a street with no Google coverage often has Mapillary imagery.
    136 
    137 ## Key tools
    138 
    139 ### Overpass API
    140 
    141 OpenStreetMap's query interface, and the reason OSM is a database rather than a picture. Give it a
    142 tag combination and a bounding box and it returns the features — which is how you turn "a church
    143 with a red roof next to a roundabout, somewhere in this province" into a list of candidates.
    144 [Overpass Turbo](https://overpass-turbo.eu/) is the browser front end; the API behind it takes
    145 `curl`, and automating it is what makes a large search tractable.
    146 
    147 ```bash
    148 # the public endpoint rejects requests without a User-Agent — this is the usual first failure
    149 UA='osint-research/1.0'
    150 OVERPASS='https://overpass-api.de/api/interpreter'
    151 
    152 # every pharmacy in a bounding box (south,west,north,east)
    153 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    154   'data=[out:json][timeout:25];node["amenity"="pharmacy"](52.37,4.88,52.38,4.90);out body;'
    155 
    156 # bridges over waterways — matching a described scene to candidate locations
    157 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    158   'data=[out:json][timeout:60];way["bridge"="yes"](52.3,4.8,52.4,4.95);out geom;' \
    159   | jq '.elements | length'
    160 
    161 # two features near each other: a mosque within 200m of a school
    162 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    163   'data=[out:json][timeout:90];
    164    way["amenity"="place_of_worship"]["religion"="muslim"](35.6,51.3,35.8,51.5)->.w;
    165    node(around.w:200)["amenity"="school"];out center;'
    166 
    167 # cell masts, which are mapped far more completely than people expect
    168 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    169   'data=[out:json];node["man_made"="mast"]["tower:type"="communication"](48.1,16.3,48.3,16.5);out center;'
    170 
    171 # count first, then fetch: a national-scale query that returns nothing costs you nothing
    172 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    173   'data=[out:csv(::count)][timeout:120];area["ISO3166-1"="NL"]->.a;node["amenity"="fuel"](area.a);out count;'
    174 
    175 # straight to a file your GIS can open
    176 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    177   'data=[out:json][timeout:60];node["aeroway"="aerodrome"](34.0,43.0,35.0,44.5);out center;' \
    178   -o aerodromes.json
    179 ```
    180 
    181 Four forms of the query that earn their keep once the search is more than one box. `nwr` matches
    182 nodes, ways and relations in one pass, which matters because a feature mapped as a node in one
    183 country is a way in the next; a `node[...]` query silently misses half of them. The `[bbox:...]`
    184 setting applies to every statement that carries no explicit box, so an exploratory query stops
    185 being a wall of repeated coordinates. `out:csv` with named fields lands in `awk` or a spreadsheet
    186 without a `jq` filter in between. And `(newer:...)` asks OSM's own edit history what changed,
    187 which is a change-detection signal that costs nothing and arrives before any satellite pass.
    188 
    189 ```bash
    190 # CSV with chosen fields: header line on, comma separator. Note the renaming --
    191 # ::id comes back as the column @id, which is what you grep for afterwards
    192 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    193   'data=[out:csv(::id,::type,::lat,::lon,"name";true;",")][timeout:30];
    194    node["amenity"="pharmacy"](52.37,4.88,52.38,4.90);out center;'
    195 # @id,@type,@lat,@lon,name
    196 # 1819064252,node,52.3723707,4.8940844,Dam Apotheek
    197 # 2720875314,node,52.3783767,4.8823525,Medicijnman Apotheek Jordaan
    198 
    199 # nwr, so a feature mapped as a way somewhere is not missed. Counting first:
    200 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    201   'data=[out:csv(::count)][timeout:60];area["ISO3166-1"="NL"]->.a;
    202    nwr["amenity"="fuel"](area.a);out count;'
    203 # @count
    204 # 4117
    205 
    206 # a global bbox in the settings, so each statement inherits it
    207 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    208   'data=[bbox:52.3730,4.8900,52.3740,4.8915][out:json][timeout:25];nwr["historic"];out center;'
    209 
    210 # what OSM itself says changed: buildings touched since a date, with edit metadata
    211 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    212   'data=[out:json][timeout:60];
    213    way["building"](52.3730,4.8900,52.3740,4.8915)(newer:"2020-01-01T00:00:00Z");out meta center;' \
    214   | jq -r '.elements[] | "\(.timestamp)  v\(.version)  \(.id)"'
    215 ```
    216 
    217 Getting the result into a GIS is the step people do by hand and should not. Overpass will emit OSM
    218 XML, which GDAL's OSM driver reads directly, so one `curl` and one `ogr2ogr` produce a GeoPackage.
    219 The recurse-down operator `>` is what makes the XML usable: without `(._;>;)` you get ways with no
    220 node coordinates and every geometry comes out empty.
    221 
    222 ```bash
    223 # ways plus the nodes that define them, as OSM XML
    224 curl -s -A "$UA" "$OVERPASS" --data-urlencode \
    225   'data=[out:xml][timeout:60];way["building"](52.3730,4.8900,52.3740,4.8915);(._;>;);out meta;' \
    226   -o area.osm
    227 
    228 # the driver exposes five fixed layers: points, lines, multilinestrings,
    229 # multipolygons, other_relations. Closed building ways land in multipolygons.
    230 ogr2ogr -f GPKG area.gpkg area.osm multipolygons -nln buildings
    231 
    232 # tag filtering needs the SQLite dialect, because tags arrive as one other_tags blob
    233 ogr2ogr -f GeoJSON masts.geojson area.osm -dialect sqlite \
    234   -sql "SELECT * FROM points WHERE other_tags LIKE '%\"man_made\"=>\"mast\"%'"
    235 
    236 # reproject on the way out, so the areas and distances you measure are in metres
    237 ogr2ogr -f GPKG -t_srs EPSG:32631 buildings_utm.gpkg area.gpkg buildings
    238 ```
    239 
    240 Queries are metered by server CPU time, not request count: a careless `[timeout:900]` over a whole
    241 country will get you a 429 and then a temporary ban from the main instance. Run the count form
    242 first. Because OSM is crowd-sourced, a feature's absence means nobody mapped it — never that it
    243 is not there — and tag completeness varies by an order of magnitude between countries. For
    244 proximity searches without writing Overpass QL by hand, Bellingcat's
    245 [OpenStreetMap Search](https://osm-search.bellingcat.com/) wraps the same data.
    246 
    247 ### GDAL
    248 
    249 The library every GIS tool is built on, and faster than any of them from a shell. Install
    250 `gdal` with your package manager (`brew install gdal`, `apt install gdal-bin`). In OSINT work it
    251 does four things: tell you what a file actually contains, crop and reproject it, georeference a
    252 photograph or scanned map, and build band maths like NDVI without opening a GUI.
    253 
    254 ```bash
    255 # what is in this file: CRS, extent, pixel size, bands, and the capture metadata
    256 gdalinfo -stats scene.tif | head -40
    257 
    258 # the same, machine-readable, when you are checking fifty files rather than reading one
    259 gdalinfo -json scene.tif | jq '{crs: .coordinateSystem.wkt[0:60], size, bands: [.bands[].band]}'
    260 
    261 # crop to a bounding box in the file's own CRS, so you stop moving 2GB around
    262 gdal_translate -projwin 4.88 52.38 4.90 52.37 -projwin_srs EPSG:4326 scene.tif crop.tif
    263 
    264 # the same crop by pixel window, when you are working off a screenshot's coordinates
    265 gdal_translate -srcwin 2400 1850 512 512 scene.tif tile.tif
    266 
    267 # reproject to Web Mercator for overlay on a slippy basemap
    268 gdalwarp -t_srs EPSG:3857 -r cubic crop.tif crop_3857.tif
    269 
    270 # clip to an area of interest polygon rather than a rectangle, and trim the canvas to it
    271 gdalwarp -cutline aoi.gpkg -crop_to_cutline -dstalpha -overwrite scene.tif aoi_only.tif
    272 
    273 # georeference a scanned map: four ground control points (pixel x, pixel y, lon, lat), then warp
    274 gdal_translate -of GTiff -a_srs EPSG:4326 \
    275   -gcp 120 95 4.8855 52.3805 -gcp 1890 110 4.9015 52.3799 \
    276   -gcp 1875 1410 4.9010 52.3702 -gcp 135 1395 4.8860 52.3708 \
    277   scan.png scan_gcp.tif
    278 gdalwarp -r cubic -t_srs EPSG:4326 -overwrite scan_gcp.tif scan_geo.tif
    279 
    280 # mosaic a directory of tiles into one virtual raster — no copying, instant
    281 gdalbuildvrt mosaic.vrt tiles/*.tif
    282 
    283 # NDVI from Sentinel-2 bands: vegetation loss, burn scars, new earthworks
    284 gdal_calc.py -A B08.jp2 -B B04.jp2 --outfile=ndvi.tif \
    285   --calc="(A.astype(float)-B)/(A.astype(float)+B+0.0001)"
    286 
    287 # differencing two dates: --extent=intersect is what stops a silent misalignment
    288 gdal_calc.py -A ndvi_may.tif -B ndvi_sep.tif --outfile=ndvi_delta.tif \
    289   --calc="B-A" --type=Float32 --extent=intersect --projectionCheck --overwrite
    290 
    291 # hillshade from a DEM, for reading terrain in a photograph's background
    292 gdaldem hillshade -z 2 dem.tif hillshade.tif
    293 
    294 # multidirectional hillshade keeps slopes facing away from the light readable
    295 gdaldem hillshade -multidirectional -compute_edges dem.tif hillshade_multi.tif
    296 
    297 # slope in degrees, for arguing about whether a vehicle track is plausible
    298 gdaldem slope -compute_edges dem.tif slope.tif
    299 
    300 # a shareable PNG at a sane size, with the world file so it stays georeferenced
    301 gdal_translate -of PNG -outsize 25% 25% -co WORLDFILE=YES crop.tif preview.png
    302 ```
    303 
    304 The one command that settles arguments is `gdallocationinfo`: it reads the pixel value at a
    305 coordinate, which turns "that looks darker" into a number you can put in a report.
    306 
    307 ```bash
    308 # the band values under one WGS84 coordinate, values only, coordinate echoed back
    309 echo "4.8909 52.3738" | gdallocationinfo -wgs84 -valonly -E -field_sep , scene.tif
    310 
    311 # a whole candidate list in one pass: stdin is read line by line
    312 gdallocationinfo -wgs84 -valonly -E -field_sep , ndvi_delta.tif < candidates.txt
    313 
    314 # the elevation under a coordinate, which is how you check a claimed camera height
    315 echo "4.8909 52.3738" | gdallocationinfo -wgs84 -valonly dem.tif
    316 ```
    317 
    318 `gdalinfo` is the honesty check: if it reports no CRS, the file is a picture and any measurement
    319 you take off it is invented. Georeferencing error concentrates away from your control points, so
    320 put them at the corners of the area you care about and expect metres of error, not centimetres.
    321 Resampling with `-r cubic` makes imagery look better and makes pixel-level forensics worse — use
    322 `-r near` when the pixels themselves are the evidence. And `gdal_calc.py` will happily difference
    323 two rasters of different extents unless you say `--extent=intersect`, producing a delta image whose
    324 bright edges are registration error rather than change.
    325 
    326 ### QGIS
    327 
    328 The desktop GIS, and where a question stops being "look at this" and becomes "measure this,
    329 against these layers". Free from [qgis.org](https://www.qgis.org). Add satellite imagery as a
    330 basemap with **Layer → Add Layer → Add XYZ Layer** and one of these URLs:
    331 
    332 ```text
    333 https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}
    334 https://tile.openstreetmap.org/{z}/{x}/{y}.png
    335 https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}
    336 ```
    337 
    338 Note the `{z}/{y}/{x}` order on the Esri service and `{z}/{x}/{y}` on the others — swapped axes
    339 are the reason a basemap loads as noise. The Georeferencer (**Layer → Georeferencer**) does the
    340 same job as the `gdal_translate -gcp` run above with a point-and-click interface and a visible
    341 residual error per point, which is worth the GUI on its own.
    342 
    343 Everything in Processing also runs headless, which is how a one-off analysis becomes repeatable:
    344 
    345 ```bash
    346 # every algorithm available, including the ones your installed plugins add
    347 qgis_process list
    348 
    349 # the parameters for one algorithm, before you guess at them
    350 qgis_process help native:buffer
    351 
    352 # machine-readable, for building a pipeline against the real parameter names
    353 qgis_process --json help native:extractbylocation | jq '.parameters | keys'
    354 
    355 # a 200m buffer around candidate points
    356 qgis_process run native:buffer -- INPUT=candidates.gpkg DISTANCE=200 OUTPUT=buffered.gpkg
    357 
    358 # reproject a layer to a metric CRS so distances mean something
    359 qgis_process run native:reprojectlayer -- \
    360   INPUT=candidates.geojson TARGET_CRS='EPSG:3857' OUTPUT=candidates_3857.gpkg
    361 
    362 # centroids of building polygons, for matching against a geotagged photo set
    363 qgis_process run native:centroids -- INPUT=buildings.gpkg OUTPUT=centroids.gpkg
    364 
    365 # keep only the features inside an area of interest. PREDICATE is an enum, not a word:
    366 # 0 intersect, 1 contain, 2 disjoint, 3 equal, 4 touch, 5 overlap, 6 are within, 7 cross
    367 qgis_process run native:extractbylocation -- \
    368   INPUT=centroids.gpkg PREDICATE=0 INTERSECT=aoi.gpkg OUTPUT=inside.gpkg
    369 
    370 # units are a run-time choice, so a buffer in metres has to say so
    371 qgis_process run native:buffer --distance_units=meters --area_units=m2 \
    372   --ellipsoid=EPSG:7030 -- INPUT=candidates.gpkg DISTANCE=200 OUTPUT=buffered.gpkg
    373 
    374 # against a project, so layer references and saved styles resolve
    375 qgis_process run native:centroids --project_path=case.qgz -- \
    376   INPUT=buildings.gpkg OUTPUT=centroids.gpkg
    377 
    378 # parameters as JSON on stdin, which is how this goes into a script without quoting pain
    379 echo '{"inputs": {"INPUT": "candidates.gpkg", "DISTANCE": 200, "OUTPUT": "buffered.gpkg"}}' \
    380   | qgis_process run native:buffer -
    381 
    382 # startup is dominated by plugin loading; skip it for a batch of a hundred runs
    383 qgis_process --no-python --skip-loading-plugins run native:centroids -- \
    384   INPUT=buildings.gpkg OUTPUT=centroids.gpkg
    385 ```
    386 
    387 Measurements in a geographic CRS (`EPSG:4326`) are in degrees, not metres, and QGIS will happily
    388 give you a meaningless number. Reproject to a local metric CRS or a UTM zone before you measure
    389 anything you intend to publish, and say which CRS you used. `--skip-loading-plugins` is not free of
    390 consequence either: an algorithm provided by a plugin disappears from `list` when you pass it, and
    391 the failure reads as a missing algorithm rather than a missing plugin.
    392 
    393 ### Copernicus Data Space
    394 
    395 The free Sentinel archive, and the only no-cost source with a revisit frequency short enough for
    396 change detection. Sentinel-2 gives 10m optical every five days; Sentinel-1 is radar and sees
    397 through cloud. The catalogue is searchable without an account; downloading needs a free
    398 registration. Note that Sentinel Hub Playground and the old EO Browser are retired — the browser
    399 is now [Copernicus Browser](https://browser.dataspace.copernicus.eu/).
    400 
    401 ```bash
    402 CAT='https://catalogue.dataspace.copernicus.eu/odata/v1/Products'
    403 
    404 # what Sentinel-2 exists for a date window — no token needed for search
    405 curl -s -G "$CAT" \
    406   --data-urlencode "\$filter=Collection/Name eq 'SENTINEL-2' and ContentDate/Start gt 2026-09-01T00:00:00.000Z and ContentDate/Start lt 2026-09-05T00:00:00.000Z" \
    407   --data-urlencode '$top=5' | jq -r '.value[] | "\(.ContentDate.Start)  \(.Name)"'
    408 
    409 # the same, bounded to an area of interest
    410 curl -s -G "$CAT" \
    411   --data-urlencode "\$filter=Collection/Name eq 'SENTINEL-2' and OData.CSC.Intersects(area=geography'SRID=4326;POLYGON((4.85 52.36,4.95 52.36,4.95 52.40,4.85 52.40,4.85 52.36))') and ContentDate/Start gt 2026-08-01T00:00:00.000Z" \
    412   --data-urlencode '$top=10' | jq -r '.value[].Name'
    413 
    414 # radar instead, for a cloudy week
    415 curl -s -G "$CAT" \
    416   --data-urlencode "\$filter=Collection/Name eq 'SENTINEL-1' and ContentDate/Start gt 2026-09-01T00:00:00.000Z" \
    417   --data-urlencode '$top=5' | jq -r '.value[].Name'
    418 
    419 # a download needs a token from the Keycloak identity service
    420 export ACCESS_TOKEN=$(curl -s -d 'client_id=cdse-public' -d "username=$CDSE_USER" \
    421   -d "password=$CDSE_PASS" -d 'grant_type=password' \
    422   'https://identity.dataspace.copernicus.eu/auth/realms/CDSE/protocol/openid-connect/token' \
    423   | jq -r .access_token)
    424 
    425 # then fetch the product by its Id
    426 curl -s -L -H "Authorization: Bearer $ACCESS_TOKEN" \
    427   "$CAT(08f7cbba-56c6-4730-b2d1-63ee8a5b9536)/\$value" -o product.zip
    428 ```
    429 
    430 Three refinements turn that from a listing into a search. `contains(Name,'MSIL2A')` restricts to
    431 the atmospherically corrected processing level, which is the only level you may compare between
    432 dates. The attribute form filters on cloud cover, which is the single number that decides whether a
    433 scene is worth downloading. And `$count=True` reports the total so you learn how many scenes exist
    434 before you page through them.
    435 
    436 ```bash
    437 POLY="POLYGON((4.885 52.370,4.897 52.370,4.897 52.378,4.885 52.378,4.885 52.370))"
    438 
    439 # L2A only, over the polygon, under 10 per cent cloud, oldest first, with a total count.
    440 # The attribute syntax is verbose and exact: the value type appears twice.
    441 curl -s -G "$CAT" \
    442   --data-urlencode "\$filter=Collection/Name eq 'SENTINEL-2' and contains(Name,'MSIL2A') and OData.CSC.Intersects(area=geography'SRID=4326;$POLY') and ContentDate/Start gt 2026-05-01T00:00:00.000Z and ContentDate/Start lt 2026-06-01T00:00:00.000Z and Attributes/OData.CSC.DoubleAttribute/any(att:att/Name eq 'cloudCover' and att/OData.CSC.DoubleAttribute/Value lt 10.00)" \
    443   --data-urlencode '$orderby=ContentDate/Start asc' \
    444   --data-urlencode '$count=True' --data-urlencode '$top=3' \
    445   | jq -r '"count: \(.["@odata.count"])", (.value[] | "\(.ContentDate.Start[0:19])  \(.Name)")'
    446 
    447 # paging: the response carries @odata.nextLink, already signed with every parameter.
    448 # Follow it rather than incrementing $skip by hand.
    449 curl -s -G "$CAT" \
    450   --data-urlencode "\$filter=Collection/Name eq 'SENTINEL-1' and ContentDate/Start gt 2026-09-01T00:00:00.000Z" \
    451   --data-urlencode '$count=True' --data-urlencode '$top=20' \
    452   | jq -r '.["@odata.nextLink"]'
    453 
    454 # the Id, which is the only thing the download endpoint accepts
    455 curl -s -G "$CAT" \
    456   --data-urlencode "\$filter=contains(Name,'S2A_MSIL2A_20260501T104651')" \
    457   | jq -r '.value[] | "\(.Id)  \(.ContentLength)  \(.Online)"'
    458 ```
    459 
    460 Filter on `Collection/Name` or the query will be slow enough to time out. A Sentinel-2 product
    461 name encodes the tile and the processing level — `L2A` is atmospherically corrected and what you
    462 want for comparing two dates; `L1C` is not. The cloud percentage in the metadata is for the whole
    463 100km tile, so a "12% cloud" scene can still be solid cloud over your target, and the inverse also
    464 bites: a 60 per cent scene can be perfectly clear over your 500m box. Filter on it to rank, never
    465 to exclude outright. `Online: false` means the product has been moved to cold storage and the
    466 download will stall rather than fail. Tokens expire in minutes; re-request rather than caching them.
    467 
    468 ### Google Earth Pro
    469 
    470 Web and desktop, free, and still the most accessible archive of sub-metre historical imagery. The
    471 desktop build is the one worth having, because the browser version hides the two features that
    472 matter.
    473 
    474 The historical slider (**View → Historical Imagery**, or the clock icon) steps through every
    475 coverage date for the view. Work it deliberately: note the date stamp in the status bar for every
    476 frame you use, because that stamp is the whole evidentiary value of the screenshot. The elevation
    477 profile (draw a path, then **Edit → Show Elevation Profile**) answers "could you see X from Y",
    478 which is a common verification question that imagery alone cannot settle.
    479 
    480 ```text
    481 Ruler tool            line and path lengths, plus a bearing in degrees
    482 Polygon + area        rooftop and compound areas, for matching a described structure
    483 Show Elevation Profile line-of-sight and terrain between two points
    484 Sun / lighting slider  shadow direction at a chosen date and time
    485 Import KML/GPX         drop in Overpass output or a track for overlay
    486 Status-bar date stamp  the capture date of the imagery currently drawn
    487 ```
    488 
    489 There is no command line, so the scriptable surface is KML, which Earth Pro reads and writes. Two
    490 documents are worth keeping as templates. A `Placemark` with a `LookAt` reproduces an exact view —
    491 position, bearing and camera distance — so a colleague opens the same frame rather than the same
    492 coordinate. A `GroundOverlay` drapes a georeferenced scan or a cropped Sentinel tile over the
    493 imagery at a stated extent, which is how you compare your own raster against Earth Pro's archive
    494 without leaving Earth Pro.
    495 
    496 ```xml
    497 <?xml version="1.0" encoding="UTF-8"?>
    498 <kml xmlns="http://www.opengis.net/kml/2.2">
    499   <Document>
    500     <!-- reproduces a view, not just a point: heading is the bearing the camera
    501          faces, tilt 0 is straight down, range is metres from the target -->
    502     <Placemark>
    503       <name>Warehouse, south elevation</name>
    504       <LookAt>
    505         <longitude>4.8909</longitude>
    506         <latitude>52.3738</latitude>
    507         <altitude>0</altitude>
    508         <heading>312</heading>
    509         <tilt>65</tilt>
    510         <range>400</range>
    511       </LookAt>
    512       <TimeSpan>
    513         <begin>2026-05-01</begin>
    514         <end>2026-09-30</end>
    515       </TimeSpan>
    516       <Point><coordinates>4.8909,52.3738,0</coordinates></Point>
    517     </Placemark>
    518 
    519     <!-- drape your own raster over Earth Pro's imagery. The LatLonBox must match
    520          the file's real extent: gdalinfo prints it, and guessing it shifts
    521          everything you then "measure" off the overlay -->
    522     <GroundOverlay>
    523       <name>Sentinel-2 L2A crop, 2026-05-01</name>
    524       <color>b4ffffff</color>
    525       <drawOrder>1</drawOrder>
    526       <Icon><href>crop_wgs84.png</href></Icon>
    527       <altitudeMode>clampToGround</altitudeMode>
    528       <LatLonBox>
    529         <north>52.378</north>
    530         <south>52.370</south>
    531         <east>4.897</east>
    532         <west>4.885</west>
    533         <rotation>0</rotation>
    534       </LatLonBox>
    535     </GroundOverlay>
    536   </Document>
    537 </kml>
    538 ```
    539 
    540 ```bash
    541 # produce the overlay PNG the KML above expects: WGS84, so the LatLonBox is honest
    542 gdalwarp -t_srs EPSG:4326 -r near crop.tif crop_wgs84.tif
    543 gdal_translate -of PNG crop_wgs84.tif crop_wgs84.png
    544 
    545 # read the extent back out, in the order the LatLonBox wants it
    546 gdalinfo -json crop_wgs84.tif \
    547   | jq -r '.wgs84Extent.coordinates[0] | "west \(.[0][0])  south \(.[0][1])  east \(.[2][0])  north \(.[2][1])"'
    548 ```
    549 
    550 Sharing a view outside Earth Pro has a documented URL form as well, which is the fastest way to
    551 hand someone a Street View frame at a specific bearing rather than a coordinate they then have to
    552 orient themselves in.
    553 
    554 ```text
    555 https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=52.3738,4.8909&heading=312&pitch=0&fov=80
    556 https://www.google.com/maps/@?api=1&map_action=map&center=52.3738,4.8909&zoom=18&basemap=satellite
    557 
    558   api=1         required, and the URL silently misbehaves without it
    559   viewpoint     lat,lon -- Google snaps to the nearest panorama, which may be the wrong street
    560   pano          a specific panorama id, when you need that exact capture and not the nearest
    561   heading       -180 to 360, compass bearing the camera faces
    562   pitch         -90 to 90, 0 is horizontal
    563   fov           10 to 100, default 90 -- narrowing it is the closest thing to a zoom
    564   zoom          0 to 21 on map_action=map
    565   basemap       roadmap, satellite or terrain
    566 ```
    567 
    568 The displayed date is the date of the *dominant* image in the view; a mosaic can blend captures
    569 months apart, with a visible seam. Zooming changes which image is drawn, so the date can change
    570 under you without the view appearing to move. 3D buildings are models, not imagery, and are not
    571 evidence of anything. A `viewpoint` link resolves to whatever panorama is nearest at the time
    572 someone opens it, so for anything you intend to cite, capture the `pano` id instead — the nearest
    573 panorama changes when Google drives the street again.
    574 
    575 ### Google Earth Engine
    576 
    577 The free analysis platform for the same archives, and the right tool when the question spans more
    578 scenes than you want to download. The [code editor](https://code.earthengine.google.com/) runs
    579 JavaScript server-side against the full Sentinel and Landsat catalogues; a free account is needed
    580 and non-commercial use is free. The pattern is always the same: a geometry, a collection, filters,
    581 then a composite or a difference.
    582 
    583 ```javascript
    584 // an area of interest, not a point: filterBounds takes any geometry
    585 var aoi = ee.Geometry.Rectangle([4.885, 52.370, 4.897, 52.378]);
    586 
    587 // the harmonised collection, because the pre-2022 and post-2022 scenes are
    588 // otherwise offset by a processing baseline change and every difference is wrong
    589 var s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
    590   .filterBounds(aoi)
    591   .filterDate('2026-05-01', '2026-06-01')
    592   .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20));
    593 
    594 print('scenes matched', s2.size());
    595 
    596 // the least cloudy scene in the window, rather than the first one returned
    597 var best = ee.Image(s2.sort('CLOUDY_PIXEL_PERCENTAGE').first());
    598 print('chosen', best.get('system:index'), best.get('CLOUDY_PIXEL_PERCENTAGE'));
    599 
    600 Map.setCenter(4.891, 52.374, 16);
    601 Map.addLayer(best, {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000}, 'true colour');
    602 
    603 // false colour: vegetation bright red, bare ground and new works pale.
    604 // This is the composite that makes a demolition or an earthwork obvious at 10m.
    605 Map.addLayer(best, {bands: ['B8', 'B4', 'B3'], min: 0, max: 4000}, 'false colour');
    606 
    607 // a two-date NDVI difference, which is change detection without downloading anything
    608 var ndvi = function (img) {
    609   return img.normalizedDifference(['B8', 'B4']).rename('ndvi');
    610 };
    611 var may = ndvi(ee.Image(s2.sort('CLOUDY_PIXEL_PERCENTAGE').first()));
    612 var sep = ndvi(ee.Image(
    613   ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
    614     .filterBounds(aoi)
    615     .filterDate('2026-09-01', '2026-10-01')
    616     .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 40))
    617     .sort('CLOUDY_PIXEL_PERCENTAGE')
    618     .first()));
    619 
    620 Map.addLayer(sep.subtract(may).clip(aoi),
    621   {min: -0.6, max: 0.6, palette: ['red', 'white', 'green']}, 'NDVI delta');
    622 ```
    623 
    624 The thing to distrust here is the compositing. A median over a date range looks clean and is not an
    625 image of any moment — it is a statistic, and it cannot carry a capture date, so it supports no
    626 time-sensitive claim. Pick a single scene and name it when the claim is about a date.
    627 `CLOUDY_PIXEL_PERCENTAGE` is the whole-tile figure again, so a 15 per cent scene can still be
    628 clouded over your box; look at the picture before you trust the number. And `Map.addLayer` stretches
    629 reflectance to a `min`/`max` you chose, so two dates displayed with different stretches will look
    630 different whether or not anything changed.
    631 
    632 ### Mapillary
    633 
    634 Crowd-sourced street-level imagery, frequently covering roads Google never drove and often more
    635 recent. Free API, and unlike Street View it is queryable by bounding box and capture date, which
    636 makes "what did this junction look like in March" a scriptable question.
    637 
    638 ```bash
    639 # token from mapillary.com/dashboard/developers — the prefix is literally "MLY|"
    640 TOKEN='MLY|xxxx|xxxx'
    641 API='https://graph.mapillary.com'
    642 
    643 # images in a bounding box (left,bottom,right,top) — must be under 0.01 degrees square
    644 curl -s -H "Authorization: OAuth $TOKEN" \
    645   "$API/images?bbox=4.890,52.372,4.895,52.375&fields=id,captured_at,compass_angle,geometry&limit=50" \
    646   | jq -r '.data[] | "\(.captured_at)  \(.id)"'
    647 
    648 # only images from a date window, which is the point of using this over Street View
    649 curl -s -H "Authorization: OAuth $TOKEN" \
    650   "$API/images?bbox=4.890,52.372,4.895,52.375&start_captured_at=2026-03-01T00:00:00Z&end_captured_at=2026-04-01T00:00:00Z&fields=id,captured_at"
    651 
    652 # one image in full, including the direction the camera faced and the original file
    653 curl -s -H "Authorization: OAuth $TOKEN" \
    654   "$API/IMAGE_ID?fields=id,captured_at,compass_angle,camera_type,thumb_2048_url,geometry"
    655 
    656 # the detected objects in a frame — traffic signs are the usable ones for geolocation
    657 curl -s -H "Authorization: OAuth $TOKEN" \
    658   "$API/images?bbox=4.890,52.372,4.895,52.375&fields=id,detections.value"
    659 
    660 # download the frame you are going to cite
    661 curl -s -H "Authorization: OAuth $TOKEN" "$API/IMAGE_ID?fields=thumb_2048_url" \
    662   | jq -r .thumb_2048_url | xargs curl -s -o frame.jpg
    663 ```
    664 
    665 Four more parameters are worth knowing. `is_pano` separates 360-degree captures, which are the
    666 ones where you can look behind the camera; `creator_username` and `organization_id` let you decide
    667 how much to trust a sequence by who uploaded it; and `sequence_ids` plus the `image_ids` endpoint
    668 walks a single drive in capture order, which is what turns a set of frames into a direction of
    669 travel.
    670 
    671 ```bash
    672 # 360 captures only, with the computed fields alongside the raw ones
    673 curl -s -H "Authorization: OAuth $TOKEN" \
    674   "$API/images?bbox=4.890,52.372,4.895,52.375&is_pano=true&fields=id,captured_at,is_pano,compass_angle,computed_compass_angle,computed_geometry&limit=100" \
    675   | jq -r '.data[] | "\(.captured_at)  raw \(.compass_angle)  computed \(.computed_compass_angle)"'
    676 
    677 # who uploaded what here: trust a municipal fleet differently from one contributor
    678 curl -s -H "Authorization: OAuth $TOKEN" \
    679   "$API/images?bbox=4.890,52.372,4.895,52.375&fields=id,captured_at,creator,organization" \
    680   | jq -r '.data[] | "\(.captured_at)  \(.creator.username // "-")  \(.organization // "-")"'
    681 
    682 # every image id in one sequence, in capture order. This endpoint ignores `fields`.
    683 curl -s -H "Authorization: OAuth $TOKEN" "$API/image_ids?sequence_id=SEQUENCE_ID" \
    684   | jq -r '.data[].id' | head -20
    685 
    686 # the sequence a frame belongs to, then the frames either side of it
    687 curl -s -H "Authorization: OAuth $TOKEN" "$API/IMAGE_ID?fields=sequence,captured_at"
    688 
    689 # detections on one frame as a separate edge, with geometry per detection
    690 curl -s -H "Authorization: OAuth $TOKEN" \
    691   "$API/IMAGE_ID/detections?fields=value,geometry,created_at" \
    692   | jq -r '.data[] | .value' | sort | uniq -c | sort -rn
    693 ```
    694 
    695 The bounding box limit is real: anything larger than 0.01 degrees square is rejected rather than
    696 truncated, so tile your area. `limit` defaults to 2000 and tops out there, so a dense city tile can
    697 be truncated without an error — compare the count you get against the count you expected rather
    698 than assuming you have everything. `compass_angle` is the camera bearing and is what lets you say
    699 which side of the street a feature is on; `computed_compass_angle` and `computed_geometry` are the
    700 structure-from-motion refinements and are usually the better of the two, but they exist only for
    701 frames that were successfully reconstructed, so a `null` there is a frame whose position is raw GPS.
    702 Coverage is contributor-driven, so a road can have 2019 and 2026 imagery and nothing between, and
    703 sequence positions are GPS traces — metres of error in cities, more in canyons.
    704 
    705 ### EarthExplorer and the LandsatLook STAC API
    706 
    707 Web only, free with registration, and the only practical route to two archives nothing else
    708 carries: Landsat back to 1972, and declassified US reconnaissance imagery from the 1960s to the
    709 1980s at resolutions that still surprise people.
    710 
    711 Search at [earthexplorer.usgs.gov](https://earthexplorer.usgs.gov/). The interface is four tabs
    712 and the order matters:
    713 
    714 ```text
    715 1 Search Criteria   draw a polygon or enter coordinates, then set the date range
    716 2 Data Sets         Landsat → Landsat Collection 2 Level-2 for analysis-ready scenes
    717                     Declassified Data → Declass 1/2/3 for CORONA, ARGON, KH-7 and KH-9
    718 3 Additional Criteria  cloud cover ceiling, and the sensor for declassified missions
    719 4 Results           the footprint icon shows coverage; the browse icon previews it
    720 ```
    721 
    722 For the Landsat half of that, there is a scriptable route that needs no account at all. USGS runs a
    723 STAC API at `landsatlook.usgs.gov/stac-server`, and search is open. This is the fastest way to find
    724 out whether a usable Landsat scene exists over a point in a window, and the response carries the
    725 sun azimuth and elevation per scene, which is the shadow geometry you need for
    726 [chronolocation](/sheets/osint/geolocation) without computing anything.
    727 
    728 ```bash
    729 STAC='https://landsatlook.usgs.gov/stac-server'
    730 
    731 # what collections exist, and their ids. landsat-c2l2-sr is Level-2 surface reflectance.
    732 curl -s "$STAC/collections" | jq -r '.collections[] | "\(.id)  \(.title)"'
    733 
    734 # a search: bbox, date window, cloud ceiling, oldest first
    735 curl -s -X POST "$STAC/search" -H 'Content-Type: application/json' -d '{
    736   "collections": ["landsat-c2l2-sr"],
    737   "bbox": [4.885, 52.370, 4.897, 52.378],
    738   "datetime": "2026-05-01T00:00:00Z/2026-09-30T23:59:59Z",
    739   "query": {"eo:cloud_cover": {"lt": 20}},
    740   "sortby": [{"field": "properties.datetime", "direction": "asc"}],
    741   "limit": 5
    742 }' | jq -r '"matched: \(.numberMatched)",
    743   (.features[] | "\(.properties.datetime[0:19])  \(.id)  cloud \(.properties["eo:cloud_cover"])  sun_az \(.properties["view:sun_azimuth"])  sun_el \(.properties["view:sun_elevation"])")'
    744 
    745 # the asset list for one scene: per-band COGs, addressable without downloading the scene
    746 curl -s "$STAC/collections/landsat-c2l2-sr/items/LC09_L2SP_199023_20260528_20260530_02_T1_SR" \
    747   | jq -r '.assets | to_entries[] | "\(.key)  \(.value.href)"' | head -20
    748 ```
    749 
    750 The same API from a shell, via `pystac-client`, which handles paging and saves a searchable item
    751 collection. `--matched` asks only for the count, which is the cheap question to ask first.
    752 
    753 ```bash
    754 pipx install pystac-client    # or: pip install pystac-client
    755 
    756 # how many scenes exist, before fetching any of them
    757 stac-client search "$STAC" -c landsat-c2l2-sr \
    758   --bbox 4.885 52.370 4.897 52.378 --datetime 2026-05-01/2026-09-30 --matched
    759 
    760 # the same search, cloud-filtered, newest first, saved to a file
    761 stac-client search "$STAC" -c landsat-c2l2-sr \
    762   --bbox 4.885 52.370 4.897 52.378 --datetime 2026-05-01/2026-09-30 \
    763   --query "eo:cloud_cover<20" --sortby "-properties.datetime" \
    764   --max-items 20 --save landsat_items.json
    765 
    766 # only the fields you need, piped straight into a table
    767 stac-client search "$STAC" -c landsat-c2l2-sr \
    768   --bbox 4.885 52.370 4.897 52.378 --datetime 2026-07-01/2026-07-31 \
    769   --fields "id,properties.datetime,properties.eo:cloud_cover" \
    770   | jq -r '.features[] | [.id, .properties.datetime, .properties["eo:cloud_cover"]] | @tsv'
    771 
    772 # an arbitrary polygon instead of a box: a GeoJSON file or an inline geometry
    773 stac-client search "$STAC" -c landsat-c2l2-sr --intersects aoi.geojson \
    774   --datetime 2026-01-01/2026-12-31 --matched
    775 ```
    776 
    777 Register before you search EarthExplorer, because the download buttons are hidden until you log in,
    778 and read the scene's entity ID — it encodes the mission and date, and it is what you cite.
    779 Declassified frames arrive as scanned film with no georeferencing at all, which is where the
    780 `gdal_translate -gcp` workflow above earns its keep. Landsat Collection 2 Level-2 is corrected and
    781 comparable between dates; Level-1 is not, so do not difference the two. The STAC route covers only
    782 Landsat: the declassified archive is not in it, so that half of the job stays in the web interface.
    783 
    784 ### NASA FIRMS
    785 
    786 Active-fire detections from MODIS and VIIRS, as a CSV you can query by box and date. This is the
    787 only source on this page that answers "was there a fire or an explosion here, and when" to the
    788 hour, because the thermal sensors pass several times a day where the optical archive manages one
    789 cloud-free frame a week. A free map key from the FIRMS site is the only requirement.
    790 
    791 ```bash
    792 KEY='your_map_key'
    793 FIRMS='https://firms.modaps.eosdis.nasa.gov/api'
    794 
    795 # which dates each product actually covers, before you ask for one that does not exist
    796 curl -s "$FIRMS/data_availability/csv/$KEY/ALL"
    797 
    798 # detections in an area over the last 3 days. AREA is west,south,east,north --
    799 # the opposite order to Overpass, and the usual source of an empty result set
    800 curl -s "$FIRMS/area/csv/$KEY/VIIRS_SNPP_NRT/34.0,31.0,36.5,33.5/3"
    801 
    802 # a historical window: same path plus a start date, YYYY-MM-DD. Day range is 1 to 5,
    803 # so a month is six requests, not one.
    804 curl -s "$FIRMS/area/csv/$KEY/VIIRS_NOAA20_NRT/34.0,31.0,36.5,33.5/5/2026-09-01"
    805 
    806 # the whole globe for a day, when you do not yet know where to look
    807 curl -s "$FIRMS/area/csv/$KEY/MODIS_NRT/world/1"
    808 
    809 # high-confidence night-time detections only, sorted by brightness
    810 curl -s "$FIRMS/area/csv/$KEY/VIIRS_SNPP_NRT/34.0,31.0,36.5,33.5/3" \
    811   | awk -F, 'NR==1 || ($14=="h" && $12=="N")' | sort -t, -k3 -rn | head
    812 ```
    813 
    814 Read the columns before you read the map. `confidence` is `l`/`n`/`h` for VIIRS and a percentage
    815 for MODIS, `daynight` is `D` or `N`, `frp` is fire radiative power in megawatts, and `scan`/`track`
    816 give the pixel footprint — which is 375m for VIIRS and 1km for MODIS, so a detection is an area, not
    817 a point, and plotting it as a dot overstates your precision by hundreds of metres. The `_NRT`
    818 sources are near-real-time and get reprocessed; the `_SP` standard-product versions are the ones to
    819 cite weeks later, and they will not agree exactly. A detection is a thermal anomaly: gas flares,
    820 industrial furnaces and sunglint off metal roofs all produce them, so the question a FIRMS hit
    821 answers is "was something hot here at 22:14 UTC", not "was there an airstrike". Conflict-specific
    822 use of this feed is on [Conflict & Environment](/sheets/osint/conflict-and-environment).
    823 
    824 ### NASA Worldview and GIBS
    825 
    826 Worldview is the browser ([worldview.earthdata.nasa.gov](https://worldview.earthdata.nasa.gov/))
    827 and GIBS is the tile service behind it. The resolution is coarse — 250m at best — and that is not
    828 the point: GIBS serves a dated, global, cloud-free-ish image for *every single day* back years,
    829 with no key and no account, which no other source on this page does. It is how you establish what
    830 the weather was doing on the day your high-resolution scene is missing.
    831 
    832 ```bash
    833 GIBS='https://gibs.earthdata.nasa.gov'
    834 
    835 # one WMTS tile, REST form:
    836 #   /wmts/{projection}/best/{layer}/default/{time}/{tilematrixset}/{z}/{row}/{col}.{ext}
    837 curl -s -o tile.jpg \
    838   "$GIBS/wmts/epsg4326/best/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/2026-09-20/250m/6/13/36.jpg"
    839 
    840 # a bounded image via WMS instead, which is what you want for an area of interest.
    841 # Version 1.3.0 uses CRS and, for EPSG:4326, BBOX in lat,lon order: south,west,north,east
    842 curl -s -o scene.png "$GIBS/wms/epsg4326/best/wms.cgi?\
    843 version=1.3.0&service=WMS&request=GetMap&format=image/png&STYLE=default\
    844 &CRS=EPSG:4326&BBOX=52.3,4.8,52.5,5.0&WIDTH=1200&HEIGHT=1200&TIME=2026-09-20\
    845 &LAYERS=VIIRS_SNPP_CorrectedReflectance_TrueColor"
    846 
    847 # the layer catalogue, including each layer's available date range
    848 curl -s "$GIBS/wmts/epsg4326/best/1.0.0/WMTSCapabilities.xml" \
    849   | grep -oE '<ows:Identifier>[^<]+' | sed 's/.*>//' | head -40
    850 ```
    851 
    852 The WMS endpoint is a GDAL data source, so the day-by-day archive can be pulled straight into the
    853 same pipeline as everything else, which beats screenshotting the browser:
    854 
    855 ```bash
    856 # GDAL reads the WMS URL directly, so crop and reproject in one step
    857 gdal_translate -of GTiff -projwin 4.8 52.5 5.0 52.3 -projwin_srs EPSG:4326 \
    858   "WMS:$GIBS/wms/epsg4326/best/wms.cgi?LAYERS=VIIRS_SNPP_CorrectedReflectance_TrueColor&TIME=2026-09-20&SRS=EPSG:4326&FORMAT=image/png&VERSION=1.1.1" \
    859   day.tif
    860 
    861 # a week of daily frames, to find the cloud-free day worth paying attention to
    862 for d in 2026-09-{14..20}; do
    863   curl -s -o "gibs_$d.png" "$GIBS/wms/epsg4326/best/wms.cgi?\
    864 version=1.3.0&service=WMS&request=GetMap&format=image/png&STYLE=default\
    865 &CRS=EPSG:4326&BBOX=52.0,4.0,53.0,5.5&WIDTH=800&HEIGHT=800&TIME=$d\
    866 &LAYERS=VIIRS_SNPP_CorrectedReflectance_TrueColor"
    867 done
    868 ```
    869 
    870 Projections are separate endpoints — `epsg4326`, `epsg3857`, `epsg3413` and `epsg3031` — and a
    871 layer present in one is not necessarily present in another, with the polar projections carrying far
    872 fewer. WMS 1.3.0 reverses the `BBOX` axis order for EPSG:4326 relative to 1.1.1, which is the
    873 reason a request returns ocean when you asked for land; if the image looks like the wrong
    874 hemisphere, swap the pairs rather than doubting the coordinates. `TIME` is a date, not a timestamp,
    875 and the frame you get is the composite for that day's overpasses, so a "2026-09-20" image spans
    876 hours. At 250m a building is a fifth of a pixel: use this to date weather and smoke plumes, never
    877 to identify a structure.
    878 
    879 ### SunCalc and ShadeMap
    880 
    881 Web only, free, and the fastest way to put a time on a photograph you have already geolocated. A
    882 shadow's direction and length at a known place is a clock, and these two read it in opposite
    883 directions.
    884 
    885 [SunCalc](https://www.suncalc.org/) takes a coordinate and a date and draws the sun's azimuth and
    886 elevation through the day; you match the shadow bearing in the image to the time that produces it.
    887 [ShadeMap](https://shademap.app) does the inverse, simulating the shadows that buildings and
    888 terrain actually cast at a chosen moment, which is what you need in a city where the shadow comes
    889 off a tower rather than the subject.
    890 
    891 ```text
    892 Input:   coordinate, date, and the shadow bearing measured off the image
    893 Read:    the time or times of day whose azimuth matches that bearing
    894 Check:   shadow LENGTH against solar elevation — bearing alone gives two candidate times
    895 Capture: the coordinate, date, computed azimuth and elevation, and the tool's own URL
    896 Caveat:  a date you have not independently established makes the whole result circular
    897 ```
    898 
    899 SunCalc keeps its entire state in the URL fragment, which is the only part of it worth treating as
    900 a command line. The form is `#/<lat>,<lon>,<zoom>/<YYYY.MM.DD>/<HH:MM>/<object height>`, so a link
    901 reproduces a specific reading rather than just opening the tool:
    902 
    903 ```text
    904 https://www.suncalc.org/#/52.3738,4.8909,17/2026.07.14/17:40/5/2
    905 
    906   52.3738,4.8909  the coordinate
    907   17              map zoom
    908   2026.07.14      date, dot-separated
    909   17:40           local time at that coordinate, in the timezone the page reports
    910   5               object height in metres, which drives the shadow-length readout
    911 ```
    912 
    913 Both assume you have the location right; a 50m error in position barely moves the azimuth, but a
    914 wrong date moves it by degrees per week near the solstices. Shadow work narrows a time, it does
    915 not prove one — pair it with [image and video forensics](/sheets/osint/image-video-forensics) and
    916 with whatever the metadata claims. The full arithmetic, the two-date-window problem and the Python
    917 libraries that sweep a whole year are on
    918 [Geolocation & Chronolocation](/sheets/osint/geolocation).
    919 
    920 ## Tool reference
    921 
    922 | Tool | What it does | Cost |
    923 | --- | --- | --- |
    924 | [Apple Maps](https://maps.apple.com) | Apple Maps is a digital mapping service with detailed maps, satellite imagery, and location-based information. | free |
    925 | [Baidu Maps](http://map.baidu.com/) | Baidu’s mapping service offering satellite imagery, street maps, and streetview (“Panorama” - zh:百度全景). | free |
    926 | [Bellingcat OpenStreetMap Search](https://osm-search.bellingcat.com/) | A user interface to search OpenStreetMap data for features in proximity to each other. | free |
    927 | [Bing Maps](https://www.bing.com/maps/) | Bing Maps is a web mapping service provided by Microsoft that offers detailed geographical information and tools for location search, and satellite… | partly free |
    928 | [Carte.ma](http://carte.ma/) | Mapping/streetview service for Morocco | free |
    929 | [Convert Geographic Units](http://rcn.montana.edu/resources/Converter.aspx) | A tool that converts various geographic coordinates to support diverse mapping and spatial analysis needs. | free |
    930 | [Copernicus Browser (formerly Sentinel Hub Playground, EO Browser)](https://browser.dataspace.copernicus.eu/) | A free web-based platform for viewing, analyzing, and downloading satellite imagery from the European Space Agency's Sentinel missions, with data updated… | free |
    931 | [EarthExplorer](https://earthexplorer.usgs.gov/) | EarthExplorer is an archive portal from the U.S. Geological Survey (USGS) that allows users search a location and time range to discover and access… | partly free |
    932 | [EOS Landviewer](http://eos.com/landviewer) | EOS Landviewer provides free services for up to 10 images. More images and analysis are available to journalists at a discount. Contact: Artem Seredyuk… | paid |
    933 | [F4Map](https://demo.f4map.com) | F4Map is an interactive 3D map visualization tool that provides detailed rendering of urban landscapes and geographical features. | free |
    934 | [Gaode Maps](https://amap.com) | Gaode Maps (also known as AMap) is a mapping application and technology from the Chinese company Alibaba. | free |
    935 | [GeoHints](https://geohints.com/) | GeoHints is a website that provides information about things like traffic lights, utility poles, bollards etc. for different regions of the world to help… | free |
    936 | [Gjirafa](https://gjirafa.biz/) | Mapping service for Albania (specially Kosovo) | free |
    937 | [Global Forest Watch](https://www.globalforestwatch.org/map/) | Explore tree cover loss and gain data, recent deforestation and fire alerts, land use designations, carbon emissions, biodiversity metrics and more. | free |
    938 | [Google Earth Engine](https://code.earthengine.google.com/) | Google Earth Engine is a platform for environmental monitoring, land use change and object/infrastructure detection through satellite imagery and… | free |
    939 | [Google Earth Pro](https://www.google.com/earth/about/versions/) | Google Earth is a geospatial tool that provides detailed, global satellite imagery, maps, 3D terrain models, and the ability to explore geographic data… | partly free |
    940 | [Google Maps](https://www.google.com/maps) | Google Maps provides mapping information, satellite imagery and Google Street View imagery including historical Street View images. | free |
    941 | [GovMap](https://www.govmap.gov.il/) | GovMap provides an interactive map of Israel, offering users a wide range of data including property boundaries, planning information, and infrastructure… | free |
    942 | [HERE WeGo](https://wego.here.com/) | Mapping service similar to Google Maps or Apple Maps. | free |
    943 | [Hitta.se](https://www.hitta.se/) | Mapping service for Sweden | free |
    944 | [Index Database](https://www.indexdatabase.de/) | A database which relates remote sensing indices with satellite imaging sensors | free |
    945 | [Kakao Map](https://map.kakao.com) | A mapping application provided by South Korean technology company Kakao Corp. | free |
    946 | [KartaView](https://kartaview.org/map) | KartaView is a crowdsourced platform for street view imagery. | free |
    947 | [Mapa.sk](http://mapa.sk/) | Mapping service for Slovakia | free |
    948 | [MapChecking](https://www.mapchecking.com/) | This tool helps you estimate and fact-check the maximum number of people standing in a given area. | free |
    949 | [Mapillary](https://www.mapillary.com/) | Mapillary is a crowdsourced street-level imagery platform. | free |
    950 | [Mappy](http://en.mappy.com/) | Mapping service (and streetview in a couple of French cities \[double check this!]) | free |
    951 | [MapSwitcher](https://github.com/david-r-edgar/MapSwitcher) | Chrome extension switches between online map apps, maintaining (as far as possible) the map centre, zoom level, & directions of the source map. | free |
    952 | [mapy.cz](http://mapy.cz) | Mapping service for Czechia | free |
    953 | [Maritime Awareness Project](https://map.nbr.org/interactivemap/) | South China Sea maps with oil and gas fields, fishing areas, air defense zones and administrative, claimed, disputed zones, submarine data cables. | free |
    954 | [NASA FIRMS](https://firms2.modaps.eosdis.nasa.gov/map/) | Displays a world map overlaid with infra-red data from one or more satellites, some, but not all of which may represent heat from fires and explosions. | free |
    955 | [NASA Worldview](https://worldview.earthdata.nasa.gov/) | NASA Worldview is an online tool for visualizing and downloading near real-time satellite imagery and scientific data of Earth's atmosphere, land, and… | free |
    956 | [OpenAerialMap](https://openaerialmap.org/) | Platform for accessing open-licensed satellite and unmanned aerial vehicle (UAV) imagery | free |
    957 | [OpenInfraMap](https://openinframap.org/#2/26/12) | Power lines, telecoms, solar, oil, gas & water infrastructure mapped globally. | free |
    958 | [OpenSeaMap](https://map.openseamap.org/) | Sea map of borders, special zones, shipping lanes, with overlays of MarineTraffic and other sources | free |
    959 | [OpenStreetMap](http://openstreetmap.org/) | OpenStreetMap is a collaborative project to create a free editable map of the world. | free |
    960 | [OrbTrack](https://www.orbtrack.org) | Predicts & describes the position & path of >15,000 satellites in Earth orbit, relative to points on the earth's surface input by the user, for 5 days… | free |
    961 | [Overpass Turbo](https://overpass-turbo.eu/) | Overpass Turbo is a web-based tool for querying and visualizing OpenStreetMap crowd sourced data, aiding in extracting specific information like locations… | free |
    962 | [PeakVisor](https://peakvisor.com/) | Dual window views for any global location: (1) a 2-D map & (2) a 3-D rendered terrain model, with photo fitting, shade/slope mapping, sun trails & weather… | free |
    963 | [Photo-Map.RU](http://photo-map.ru/) | Geotagged VK posts. | free |
    964 | [Planet Labs](https://www.planet.com/) | Planet Labs PBC is an American optical satellite imagery company that sells access to imagery. | partly free |
    965 | [QGIS](https://www.qgis.org) | QGIS is a free Open Source Geographic Information System (GIS). | free |
    966 | [Quick geolocation search](https://cybdetective.com/quickgeolocationsearch.html) | A tool that brings several maps into one place for easy location search. | free |
    967 | [Radar Interference Tracker (RIT)](https://ollielballinger.users.earthengine.app/view/bellingcat-radar-interference-tracker#lon=49.9507;lat=26.6056;zoom=4) | Bellingcat's radar interference tracker can be used to locate and monitor active military radar systems. | free |
    968 | [RAMMB SLIDER](https://rammb-slider.cira.colostate.edu/) | Real-time weather satellites of the entire globe | free |
    969 | [Satellites.pro](https://satellites.pro/) | Satellites.pro allows open source researchers to quickly switch between several free satellite imagery and mapping services. | free |
    970 | [ShadeMap](https://shademap.app) | ShadeMap is a global simulation of mountain, building & tree shadows for a given date & time. Base data is free, but users can buy 30cm accurate data per… | partly free |
    971 | [ShadowMap](https://app.shadowmap.org/) | Global map of 3D buildlings and the shadows they cast at a specific time a day | free |
    972 | [SkyFi](https://skyfi.com/) | SkyFi is used to purchase commercial satellite imagery and task (order the collection of images) satellites without a subscription. | paid |
    973 | [Strava](https://www.strava.com) | A fitness tracking platform where publicly shared GPS activity data can reveal movement patterns, routines, and precise locations of individuals… | partly free |
    974 | [Tencent Maps](http://map.qq.com/) | Tencent Maps (formerly SOSO Maps) is a desktop and web mapping service application and technology provided by Chinese company Tencent, offering satellite… | free |
    975 | [The European Space Agency (ESA) - Earth Online](https://earth.esa.int/eogateway/tools) | The ESA's Earth Online product offers a portal for accessing satellite imagery and environmental data, supporting a range of applications from climate… | free |
    976 | [Topotijdreis.nl](http://topotijdreis.nl) | Over 200 years of maps and topography from the Netherlands. | free |
    977 | [Umbra Space](https://umbra.space/) | Umbra is an American synthetic aperture radar (SAR) satellite imaging company that sells on-demand taskings for satellite imagery. | paid |
    978 | [UTM grid zones](http://dmap.co.uk/utmworld.htm) | An overview of the Universal Transverse Mercator coordinate system. | free |
    979 | [what3words](http://what3words.com/) | A proprietary geocode system which identifies any location on the surface of the earth to a resolution of 3 metres. The identifier is a unique combination… | partly free |
    980 | [Wikimapia](https://wikimapia.org/) | Wikimapia is a long-running collaborative mapping project that remains partially accessible, providing open source researchers with a unique database of… | free |
    981 | [Yandex Maps](https://yandex.com/maps/) | A platform offering detailed maps, satellite imagery, street views (static & sometimes dynamic imagery, including aerial views). Often the best available… | partly free |
    982 | About Maps and Satellites | A guide to using map and satellite tools. | free |
    983 
    984 ## Pitfalls
    985 
    986 - **Undated imagery is useless for a time-sensitive claim.** Record the capture date every time.
    987 - **Cloud cover ruins optical revisit rates.** A 5-day nominal revisit can mean a month of usable
    988   imagery in the wet season. Radar (Sentinel-1) sees through cloud but is much harder to read.
    989 - **A scene's cloud percentage is for the whole tile, not your target.** It is a 100km tile for
    990   Sentinel-2 and a 185km swath for Landsat. Use the number to rank candidates and then look at the
    991   picture; a 60 per cent scene can be clear over your box and a 12 per cent scene can be solid
    992   cloud over it.
    993 - **OSM is crowd-sourced.** Completeness varies enormously by region, and an absent feature may
    994   simply be unmapped.
    995 - **Basemap labels disagree**, particularly on disputed borders and place names. Say which source
    996   you used.
    997 - **Bounding boxes are in four different orders across these tools.** Overpass takes
    998   `south,west,north,east`; Mapillary and STAC take `left,bottom,right,top`; FIRMS takes
    999   `west,south,east,north`; and WMS 1.3.0 flips to lat,lon for EPSG:4326 where 1.1.1 does not. An
   1000   empty result set is this mistake far more often than it is an absence of data.
   1001 - **A median composite has no capture date.** It is a statistic over a window, so it cannot support
   1002   a claim about a day. Pick a single scene and name it.
   1003 - **Processing levels are not comparable.** L2A against L1C, or Landsat Level-2 against Level-1,
   1004   produces a difference image of the atmospheric correction rather than of the ground.
   1005 - **Differencing two rasters of different extents aligns them silently.** `gdal_calc.py` without
   1006   `--extent=intersect` will give you a delta whose brightest features are registration error.
   1007 - **A 10m pixel cannot resolve a 10m object.** Two or three pixels across is the floor for seeing
   1008   that something is there; you need an order of magnitude better to say what it is. A roof six
   1009   pixels wide is enough to see it disappear and not enough to see how.
   1010 - **Street-level coverage is a sample, not a survey.** A junction with 2019 and 2026 frames and
   1011   nothing between does not mean nothing happened in between, and the newest frame is not the frame
   1012   nearest your date.
   1013 - **A thermal detection is an area, not a point.** VIIRS pixels are 375m and MODIS 1km, so plotting
   1014   a FIRMS hit as a dot claims a precision the sensor does not have.
   1015 - **Near-real-time products get reprocessed.** A FIRMS `_NRT` detection and the later `_SP` version
   1016   of the same pass will not agree exactly, so cite the one you can still retrieve.
   1017 - **Google's historical slider date is the dominant image's date.** A mosaic blends captures months
   1018   apart, and changing zoom can change which image is drawn without the view appearing to move.
   1019 
   1020 ## Worked example
   1021 
   1022 One datum: the coordinate **52.3738, 4.8909**, pulled from a photograph's caption, and a claim
   1023 that a warehouse there was demolished in the summer of 2026.
   1024 
   1025 1. **Establish what is mapped.** An Overpass query for `building` ways in a small box around the
   1026    coordinate returns three polygons, one tagged `building=warehouse` with an `addr:street`. That
   1027    gives the feature a name and an address to search on. The `(newer:"2026-06-01T00:00:00Z")`
   1028    form on the same query shows one of the three edited in August 2026, which is a free first
   1029    corroboration of the claim's timing from OSM's own history.
   1030 2. **Find free imagery either side of the claim.** The Copernicus OData catalogue, filtered to
   1031    `SENTINEL-2`, `contains(Name,'MSIL2A')` and intersected with a 1.3km polygon around the point.
   1032    Run once per month with a 10 per cent cloud ceiling, and once without, because the gap between
   1033    those two answers is the real story:
   1034 
   1035 ```text
   1036 === MAY 2026, cloudCover < 10 ===
   1037 count: 3
   1038   2026-05-01T10:36:19  S2B_MSIL2A_20260501T103619_N0512_R008_T31UFU_20260501T143617.SAFE
   1039   2026-05-01T10:46:51  S2A_MSIL2A_20260501T104651_N0512_R051_T31UFU_20260501T173800.SAFE
   1040   2026-05-26T10:36:21  S2C_MSIL2A_20260526T103621_N0512_R008_T31UFU_20260526T140311.SAFE
   1041 
   1042 === SEPTEMBER 2026, cloudCover < 10 ===
   1043 count: 0
   1044 
   1045 === SEPTEMBER 2026, cloudCover < 40 ===
   1046 count: 2
   1047   2026-09-01T10:46:19  S2B_MSIL2A_20260901T104619_N0512_R051_T31UFU_20260901T131914.SAFE
   1048   2026-09-25T10:40:41  S2A_MSIL2A_20260925T104041_N0513_R008_T31UFU_20260925T171206.SAFE
   1049 
   1050 === SEPTEMBER 2026, no cloud filter ===
   1051 count: 18
   1052 ```
   1053 
   1054    Eighteen scenes in the month, two under 40 per cent cloud, none under 10. The nominal five-day
   1055    revisit is an orbital fact; the usable cadence over the Netherlands in September is a fortnight.
   1056    Take the 25 September scene and accept that it needs looking at rather than trusting.
   1057 3. **Crop and compare.** `gdal_translate -projwin` cuts both scenes to the same 500m box,
   1058    `gdalwarp -t_srs EPSG:3857` puts them in the same CRS, and the pair opened in QGIS shows the
   1059    roof present on 1 May and bare ground on 25 September. At 10m the roof is six pixels across —
   1060    enough to see it go, not enough to see how. `gdallocationinfo -wgs84 -valonly` on the NDVI
   1061    difference at the coordinate returns **+0.02**, confirming what the eye says: this is roof
   1062    giving way to bare ground, not vegetation change.
   1063 4. **Bracket it with Landsat, for the sun geometry.** The LandsatLook STAC search over the same box
   1064    needs no account and returns, for May to September with a 20 per cent cloud ceiling:
   1065 
   1066 ```text
   1067 matched: 12
   1068 2026-05-28T10:38:56  LC09_L2SP_199023_20260528_20260530_02_T1_SR  cloud 4.51   sun_az 153.53  sun_el 56.26
   1069 2026-06-22T10:33:16  LC09_L2SP_198024_20260622_20260623_02_T1_SR  cloud 7.52   sun_az 148.59  sun_el 58.83
   1070 2026-06-29T10:39:07  LC09_L2SP_199023_20260629_20260630_02_T1_SR  cloud 6.09   sun_az 150.22  sun_el 57.48
   1071 2026-07-15T10:39:14  LC09_L2SP_199023_20260715_20260717_02_T1_SR  cloud 0.22   sun_az 150.28  sun_el 55.66
   1072 ```
   1073 
   1074    30m is too coarse to see the building, so this is not the change-detection source here. What it
   1075    gives you is `view:sun_elevation` around **55.7 degrees** at 10:39 UTC in mid-July, which is the
   1076    number to check the photograph's shadows against in step 7.
   1077 5. **Confirm at resolution.** Google Earth Pro's historical slider over the same point has a
   1078    **July 2026** frame at sub-metre scale showing partial demolition and plant on site. Record the
   1079    status-bar date, not the date you looked, and capture the view as a `LookAt` placemark so the
   1080    frame is reproducible rather than described.
   1081 6. **Check the ground.** Mapillary `images?bbox=4.8900,52.3730,4.8915,52.3745&start_captured_at=2026-08-01T00:00:00Z`
   1082    returns a contributor sequence from August with `computed_compass_angle` 312 degrees, facing the
   1083    plot: hoarding up, structure gone. Street level dates the end of the work more precisely than any
   1084    satellite pass. The sequence runs north-west along the street, so the frames either side
   1085    establish that the hoarding is on the plot boundary and not on the one next door.
   1086 7. **Measure, then say so.** Reprojected to UTM zone 31N (`EPSG:32631`), the QGIS measure tool puts
   1087    the cleared footprint at **1,840 m²**, against **1,795 m²** for the OSM polygon — a 2.5 per cent
   1088    disagreement, which is within what a 10m pixel edge can produce and is therefore consistent
   1089    rather than confirming. Quote the CRS alongside the number.
   1090 8. **Time the photograph, if it matters.** The caption claims mid-July. SunCalc for the coordinate
   1091    on 14 July, with the lamp standard's 5m height entered, returns an azimuth matching the shadow
   1092    bearing at around **17:40 local** and a solar elevation of about 30 degrees — consistent with
   1093    the Landsat-derived geometry for the same week, and recorded as consistent rather than proven.
   1094 
   1095 What you can assert: a structure present on a named, dated 10m scene on 1 May 2026 and absent from
   1096 a named, dated 10m scene on 25 September 2026; a sub-metre Google Earth Pro frame stamped July 2026
   1097 showing demolition in progress; a Mapillary sequence from August 2026 showing the site hoarded and
   1098 cleared; and a cleared footprint of 1,840 m² in EPSG:32631. The demolition therefore falls between
   1099 1 May and 25 September, and the July frame narrows it to the first half of that window. What you
   1100 cannot assert: who did it, or why — no imagery source on this page carries that.
   1101 
   1102 What would falsify it: a July Google Earth Pro frame that turns out to be a mosaic blending a
   1103 pre-demolition capture from a neighbouring strip, which the visible seam would show and the
   1104 status-bar date would not; a Mapillary sequence whose `computed_geometry` is absent, leaving the
   1105 position as raw GPS and the "facing the plot" claim unsupported; or a warehouse that was rebuilt
   1106 and re-demolished, which two dated frames five months apart cannot distinguish from one event. The
   1107 measurement carrying the most risk is the **1,840 m² footprint**. It is traced off 10m pixels, so
   1108 each edge carries at best half a pixel of uncertainty: on a roughly 43m square that is about ±5m
   1109 per side, or **±8 per cent on the area**. Quote it as 1,840 m² ±150 m² or do not quote a figure at
   1110 all — and note that this tolerance is why the 2.5 per cent agreement with the OSM polygon in step 7
   1111 corroborates nothing. Two numbers that agree inside their error bars are not a cross-check.
   1112 
   1113 ## Broader catalogues
   1114 
   1115 - [Geolocation and Maps OSINT](https://tools.osintnewsletter.com/tool-categories/geolocation-and-maps-osint)
   1116 
   1117 
   1118 ## More tools
   1119 
   1120 Further tools for this area from the OSINT Newsletter Tools Library ([Geolocation and Maps OSINT](https://tools.osintnewsletter.com/tool-categories/geolocation-and-maps-osint)), excluding those already listed above.
   1121 
   1122 | Tool | What it does |
   1123 | --- | --- |
   1124 | [EarthPoint Convert](https://www.earthpoint.us/Convert.aspx) | A web-based conversion tool for transforming geographic coordinate data between formats. |
   1125 | [Geoconfirmed](https://geoconfirmed.org/) | A collaborative OSINT platform to help analysts verify and geolocate images, videos, and events from conflicts worldwide. |
   1126 | [GeoSpy](https://geospy.ai/) | An AI-powered geolocation tool that analyses images to estimate where they were taken by examining visual features like… |
   1127 | [MoonCalc](https://www.mooncalc.org/) | A free web-based lunar positioning tool that visualises the position, phase and illumination of the moon for any location, date… |
   1128 | [MW Geofind](https://mattw.io/youtube-geofind/location) | Finds geotagged YouTube videos on a map. |
   1129 | [N2YO Satellite Tracker](https://www.n2yo.com/) | Real-time satellite tracking platform providing orbital data, pass predictions and positional information for thousands of… |
   1130 | [Open Infrastructure Map](https://openinframap.org/) | An interactive mapping platform that visualises critical infrastructure worldwide using data primarily sourced from OpenStreetMap. |
   1131 | [Picarta](https://picarta.ai/) | AI-powered geolocation tool that finds where a photo was taken using visual analysis. |
   1132 | [SPOT](https://www.findthatspot.io/) | AI-powered geolocation tool to help identify where an image was taken by analysing visual elements including landmarks, terrain… |
   1133 | [SunCalc](https://www.suncalc.org/) | A free web-based geolocation tool that visualises the position of the sun and shadows for any location, date, and time. |
   1134 | [Surveillance under Surveillance](https://sunders.uber.space/) | An interactive map that visualises CCTV and surveillance camera locations worldwide using OpenStreetMap data. |
   1135 
   1136 ## Sources
   1137 
   1138 Both catalogues below are maintained by other people and are considerably larger than
   1139 this page. Use them as the canonical index; this sheet is a working route through them.
   1140 
   1141 - [Bellingcat's Online Investigation Toolkit](https://bellingcat.gitbook.io/toolkit) — ~340 tools, each with its own
   1142   review page covering cost, difficulty, requirements and limitations.
   1143 - [OSINT Newsletter Tools Library](https://tools.osintnewsletter.com) — ~280 tools, organised by investigative goal.
   1144 
   1145 Neither publishes a licence, so nothing here is copied from them: tool names, one-line
   1146 descriptions, cost flags and links are catalogue facts, and the method and commentary are
   1147 this site's own. See [credits](/credits).