earthdiff 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. earthdiff-0.2.0/.gitignore +15 -0
  2. earthdiff-0.2.0/CHANGELOG.md +59 -0
  3. earthdiff-0.2.0/CONTRIBUTING.md +30 -0
  4. earthdiff-0.2.0/LICENSE +21 -0
  5. earthdiff-0.2.0/PKG-INFO +368 -0
  6. earthdiff-0.2.0/README.md +328 -0
  7. earthdiff-0.2.0/pyproject.toml +58 -0
  8. earthdiff-0.2.0/src/earthdiff/__init__.py +28 -0
  9. earthdiff-0.2.0/src/earthdiff/__main__.py +3 -0
  10. earthdiff-0.2.0/src/earthdiff/aoi.py +161 -0
  11. earthdiff-0.2.0/src/earthdiff/cause.py +144 -0
  12. earthdiff-0.2.0/src/earthdiff/change.py +337 -0
  13. earthdiff-0.2.0/src/earthdiff/cli.py +202 -0
  14. earthdiff-0.2.0/src/earthdiff/composite.py +169 -0
  15. earthdiff-0.2.0/src/earthdiff/geocode.py +186 -0
  16. earthdiff-0.2.0/src/earthdiff/history.py +86 -0
  17. earthdiff-0.2.0/src/earthdiff/i18n.py +201 -0
  18. earthdiff-0.2.0/src/earthdiff/indices.py +52 -0
  19. earthdiff-0.2.0/src/earthdiff/mcp_prompts.py +54 -0
  20. earthdiff-0.2.0/src/earthdiff/mcp_server.py +338 -0
  21. earthdiff-0.2.0/src/earthdiff/mcp_service.py +887 -0
  22. earthdiff-0.2.0/src/earthdiff/periods.py +87 -0
  23. earthdiff-0.2.0/src/earthdiff/pipeline.py +476 -0
  24. earthdiff-0.2.0/src/earthdiff/reader.py +59 -0
  25. earthdiff-0.2.0/src/earthdiff/render.py +146 -0
  26. earthdiff-0.2.0/src/earthdiff/report.py +113 -0
  27. earthdiff-0.2.0/src/earthdiff/safety.py +93 -0
  28. earthdiff-0.2.0/src/earthdiff/seasonal.py +155 -0
  29. earthdiff-0.2.0/src/earthdiff/stac.py +163 -0
  30. earthdiff-0.2.0/src/earthdiff/static/ui.html +239 -0
  31. earthdiff-0.2.0/src/earthdiff/templates/report.html +850 -0
  32. earthdiff-0.2.0/src/earthdiff/terrain.py +120 -0
  33. earthdiff-0.2.0/src/earthdiff/ui.py +227 -0
  34. earthdiff-0.2.0/src/earthdiff/vendor/LEAFLET-LICENSE.txt +26 -0
  35. earthdiff-0.2.0/src/earthdiff/vendor/leaflet.css +661 -0
  36. earthdiff-0.2.0/src/earthdiff/vendor/leaflet.js +6 -0
  37. earthdiff-0.2.0/src/earthdiff/watch.py +138 -0
  38. earthdiff-0.2.0/src/earthdiff/xlsx.py +183 -0
  39. earthdiff-0.2.0/src/earthdiff/yearly.py +109 -0
  40. earthdiff-0.2.0/tests/fakes.py +134 -0
  41. earthdiff-0.2.0/tests/mcp_fake_server.py +55 -0
  42. earthdiff-0.2.0/tests/test_cause_seasonal_bright.py +323 -0
  43. earthdiff-0.2.0/tests/test_core.py +268 -0
  44. earthdiff-0.2.0/tests/test_history.py +44 -0
  45. earthdiff-0.2.0/tests/test_i18n.py +51 -0
  46. earthdiff-0.2.0/tests/test_mcp_stdio.py +286 -0
  47. earthdiff-0.2.0/tests/test_mcp_units.py +538 -0
  48. earthdiff-0.2.0/tests/test_network.py +54 -0
  49. earthdiff-0.2.0/tests/test_pipeline.py +121 -0
  50. earthdiff-0.2.0/tests/test_terrain.py +51 -0
  51. earthdiff-0.2.0/tests/test_ui.py +73 -0
@@ -0,0 +1,15 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ report/
9
+ runs/
10
+ validation/runs/
11
+ .env*
12
+ validation/mcp_runs/
13
+ validation/cause_runs/
14
+ validation/mcp/*/*.tif
15
+ validation/mcp/*/*/*.tif
@@ -0,0 +1,59 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. Format: [Keep a Changelog](https://keepachangelog.com/), versioning: [SemVer](https://semver.org/).
4
+
5
+ ## [0.2.0] - 2026-11-11
6
+
7
+ ### Added (second round)
8
+ - **`likely_cause` for vegetation loss** (`fire-like` / `clearing-like` / `unclear`, a hint with measurements and a stated basis, not a new class): reads Sentinel-2 B12 (optional: an unreadable B12 never blocks a run), NBR / dNBR with the Key & Benson (2006) severity thresholds also used by Copernicus EFFIS, plus post-event brightness and outline shape (these two thresholds are this project's own, set on two public cases). In `changes.geojson`, `summary.json`, `report.html` (card, popup, table column), the CLI summary (`--no-likely-cause` turns it off) and the MCP answer. Checked on Manavgat 2021 (3,075 of 3,119 ha fire-like) and Istanbul Airport 2017 to 2019 (8.6 of 558 ha fire-like, a recorded miss).
9
+ - **Seasonal check** (`--seasonal-check`, MCP `seasonal_check`, off by default): compares vegetation-loss polygons with the same windows of 2 earlier years and flags `likely_seasonal`; polygons are never removed. Measured +46 s on a 39 km2 Konya plain test; `plan_change` reports the extra windows and the cost.
10
+ - **Bright-surface warning**: when at least 20 % of the comparable area is very bright, non-vegetated surface (salt, sand, snow), the answer says change there is not reliable, caps the confidence at low, grades data quality poor and lowers the confidence of polygons on bright pixels. Reports the NDWI water-like share of the bright pixels before and after as an exploratory wet/dry signal.
11
+ - `explain_method` topics `likely_cause`, `seasonal`, `bright_surfaces`; `validation/mcp_questions.md` now covers Manavgat, Lake Mead, Istanbul Airport, Konya plain (with and without the seasonal check) and Lake Tuz (late summer and spring baseline, index experiment `validation/tuz_experiment.py`).
12
+
13
+ ### Added
14
+ - **MCP server** (`earthdiff-mcp`, also `earthdiff mcp`; stdio; install with `pip install "earthdiff[mcp]"` or `uvx --from "earthdiff[mcp]" earthdiff-mcp`) so that AI assistants (Claude Desktop, Claude Code, Cursor, any MCP client) can answer "what changed here?" with free Sentinel-2 imagery, no account or API key.
15
+ - Tools: `find_place` (OSM Nominatim: identifying User-Agent, at most 1 request/s, on-disk cache, attribution; ambiguous names return candidates instead of a guess; oversized boxes come with narrowing options), `plan_change` (scene counts and warnings without downloading), `detect_change` (hectares and polygons per class, comparable share, confidence, data quality, largest areas, all warnings; before/after/change-map images returned as MCP image content; `report.html` and `changes.geojson` written locally; progress notifications and a heartbeat for long runs), `explain_method` (rules, pipeline, confidence, limits, data).
16
+ - Safety: unknown tool parameters are rejected; output confined to `EARTHDIFF_OUTPUT_DIR`; `name` sanitised; configurable area (`EARTHDIFF_MAX_AREA_KM2`, default and maximum 2,000) and run-time (`EARTHDIFF_MAX_RUNTIME_S`) limits; one analysis at a time; error messages are scrubbed of file paths; scene URLs are checked against a Sentinel-2 host allow-list; nothing is written outside the output folder and no existing file is ever deleted (only an empty run folder created by a failed run is removed).
17
+ - `pipeline.plan()` accepts an injectable `search` and reports scene cloud cover; `pipeline.period_warnings()` shared by the CLI and the MCP server.
18
+ - Tests: fake STAC / raster / Nominatim unit tests, a real MCP stdio test (`tools/list`, `tools/call`, images, progress, strict arguments, redaction) and a `network`-marked end-to-end test against the live services.
19
+ - `validation/run_mcp_questions.py` and `validation/mcp_questions.md`: real questions asked through the MCP server (Manavgat wildfire 2021, Lake Mead 2019 vs 2022, Lake Tuz 2019 vs 2021) with sources and honest caveats.
20
+ - `server.json` (MCP Registry), a PyPI trusted-publishing workflow and a CI workflow (Linux 3.11 and 3.13, Windows 3.12; network tests deselected).
21
+ - The MCP server reports its version in `initialize` (mcp 2.x).
22
+
23
+ ### Changed
24
+ - Version 0.2.0; `mcp` extra added; `python_requires` unchanged.
25
+ - A change share above 30 % of the area is no longer treated as a data mismatch when at least 80 % of the changed area is fire-like vegetation loss (dNBR): the warning says the share fits one large burn and the MCP `data_quality` is not lowered for it. Found on a 98 km2 box inside the 2021 Manavgat scar, which was graded poor.
26
+ - One area limit everywhere: 2,000 km2 in the CLI, `watch`, the web UI and the MCP server (`aoi.MAX_AREA_KM2`; was 400). Larger areas are read at 20/30/60 m.
27
+ - Report map: every change is described in plain words (what changed, the likely cause, and whether it is a strong, moderate or weak change) in a hover/tap tooltip, the popup and the table; outlines are drawn by strength (weak ones dashed). Changes too small to see at the current zoom are also marked with a dot (solid = strong, half = moderate, hollow = weak) so none is missed. The legend states the smallest change mapped.
28
+ - Web UI: "Smallest change to show" (0.1 / 0.5 / 2 ha).
29
+ - Report and web UI in **Turkish and English** (TR / EN switch; Turkish browsers start in Turkish; the choice is remembered). Warnings, the method section and the likely-cause hints have Turkish versions (`earthdiff/i18n.py`); a test checks that every pipeline warning is translated.
30
+ - Report **scene by scene** timeline: every satellite pass used (before and after) as its own frame, in date order, with play / pause, a slider and arrow keys; the date, period and clear share of each frame are shown live. The changes stay marked (and pulse) on every frame. Each frame shows only what that pass saw clearly: cloud, cloud shadow, snow and gaps are left transparent (not filled from other dates); passes showing less than 30 % of the area are left out of the timeline.
31
+ - Swipe is a **time cursor**: the whole line can be dragged (not only the knob); it sits between two consecutive satellite passes, the left side shows the earlier pass, the right side the later one, and both dates follow the line live. The changes stay marked on both sides.
32
+ - Choosing a basemap fades the satellite image out (a "Satellite" opacity slider blends them), so the basemap shows inside the analysed area too; basemaps are drawn only inside the drawn area, which stays cut out, in 2D and in 3D (in 3D the OpenStreetMap / OpenTopoMap tiles are draped on the terrain when online; the relief always works offline).
33
+ - 3D markers: rings lie on the terrain around the main changes; when zoomed in, small changes stand as pins on the ground; the selected change blinks.
34
+ - Table and map are linked: click a row to fly to the change (it blinks and its popup opens); click a change on the map (2D polygon or dot, 3D pin) and its row lights up. The table scrolls on its own so the page does not jump.
35
+ - **In-between years**: when the before and after windows are years apart, the timeline gets one small frame per in-between year (same season as the after window, the clearest pass, read from the COG overviews; at most 10; `--no-yearly-frames` skips them). They are for viewing only and do not change the analysis.
36
+ - **Old events are no longer washed out**: when the windows are years apart, each vegetation-loss polygon is followed through the in-between years (NBR, NDVI and brightness of one pass per year, read with the timeline frames). The largest one-year NBR drop is taken as the event; if it is clearly larger than the end-to-end drop (the area has partly recovered, as a burn scar does in a few years), the likely cause is judged on that step and the polygon gets `event_before`, `event_after` and `event_dnbr`. The report, its tooltips and the Excel sheet say when the largest drop happened. Found on Manavgat 2020 vs 2024, where the 2021 burn had regrown enough to look "unclear". Checked on Manavgat 2020 vs 2024 (fire-like 159 -> 4,454 ha; 4,674 ha dated to the 2021 step) and North Evia, Greece 2020 vs 2024 (2,082 ha fire-like, the main loss dated between 27 Jul 2021 and 18 Aug 2022, around the August 2021 fire). A year with no clear pass (cloud, smoke) is retried with its window widened by a month on each side.
37
+ - **Excel export**: `changes.xlsx` in the output folder (sheets: summary with totals and warnings, one row per change with a plain-language description, numbers as numbers and an OpenStreetMap link, scenes), written without extra dependencies; the report's Excel button downloads it in the language shown (Turkish or English), and a Print / PDF button prints the report. The MCP answer lists the workbook.
38
+ - **Different seasons are refused** (CLI, web UI, MCP): when the two windows are more than 60 days apart in the yearly cycle the run stops with an explanation (winter vs autumn made almost everything look changed: snow, low sun, leaves, crops); 45 to 60 days still runs with a warning; the CLI can override with `--any-season`. In the web UI "before" is now a year (same month as "after"), so a different season cannot be picked.
39
+ - Web UI: the basemap is chosen on the drawing page (satellite only, relief, OpenStreetMap, topographic), shown there on the drawing map and used by the report (`report.html#base=topo`). On the drawing map "satellite only" is the cloudless Sentinel-2 mosaic of 2023 (EOX) and "relief" is shaded by the local server from the open AWS Terrain Tiles; the report keeps its own images and its embedded Copernicus DEM relief. The 2D / 3D switch is in the report only.
40
+ - Report: the changes and scenes tables have a search box (number, change, strength, cause, place; scenes by period, id, date) and are paged (10 / 25 / 50 / 100 rows per page) and list every change, not only the largest 300; picking a change on the map turns the table to its page; printing lists every row.
41
+ - 3D: the before/after swipe works (the left of the line shows the earlier pass, the right the later one); no basemap choice in 3D (the satellite image on the embedded relief, as before); the labels name in-between frames.
42
+ - One window-length limit everywhere: each time window may span at most 366 days (was enforced only by the MCP server); the two windows may still be years apart.
43
+ - Animated pulse rings on the largest and strongest changes (toggle) so they are easy to spot.
44
+ - Basemap choice and an **offline 3D view**. During the run a small window of the Copernicus DEM GLO-30 (30 m, public COGs on AWS) around the area is read (about 1 to 2 s; `--no-terrain` skips it) and embedded: a relief basemap (offline) and a WebGL terrain with the satellite image (before, after or the timeline frame) draped on it, a dot on every change and pulse rings on the main ones, click a dot for what changed, drag to rotate, wheel or pinch to zoom, height exaggeration x1 to x5. The report needs no internet for any of this; only the optional OpenStreetMap and OpenTopoMap basemaps load online.
45
+ - Web UI (`earthdiff ui`): areas up to 2,000 km2. An oversized area now shows a red warning, colours the shape red and the Run button says why it is disabled; the server enforces the same limit. While drawing, the size is shown live next to the cursor (red over the limit) with a thicker outline; polygons can be finished with Enter, Backspace removes the last point, Esc cancels.
46
+
47
+ ## [0.1.0] - unreleased
48
+
49
+ ### Added
50
+ - `earthdiff run`: AOI (GeoJSON or bbox) + two time windows -> report folder
51
+ (`report.html`, `changes.geojson`, `before.png`, `after.png`, `change_map.png`, `overview.png`, `change_class.tif`, `summary.json`).
52
+ - Free Sentinel-2 L2A access without account or key: Element84 Earth Search (default) and Microsoft Planetary Computer (optional), windowed COG reads only.
53
+ - SCL cloud / shadow / snow masking, per-period median composites, scene selection by real cloud-free AOI coverage.
54
+ - Explainable rules on NDVI, MNDWI and BSI: vegetation loss / gain, new water / water loss, new bare soil / built-up; MMU, morphological cleanup, polygonization, hectares, heuristic confidence.
55
+ - Guard rails: AOI size limit, minimum cloud-free fraction, seasonal-mismatch / single-scene / implausibly-large-change warnings.
56
+ - Self-contained HTML report: before/after swipe, change map, clickable polygons with before/after crops and index values, sortable table, method and caveats.
57
+ - `earthdiff plan` (dry run), `earthdiff watch` (monthly re-runs from TOML, history CSV; you schedule it), `earthdiff ui` (local drawing UI on 127.0.0.1).
58
+ - pytest suite with synthetic rasters; one `network`-marked STAC smoke test.
59
+ - Validation cases in `validation/` with honest notes.
@@ -0,0 +1,30 @@
1
+ # Contributing to earthdiff
2
+
3
+ Thanks for helping. The project's promise is **explainable, account-free change alerts**, so contributions that keep the method inspectable are the most welcome.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
9
+ pip install -e ".[dev,planetary-computer]"
10
+ pytest # offline tests with synthetic rasters
11
+ pytest -m network # also hits the public STAC API
12
+ ```
13
+
14
+ ## Guidelines
15
+
16
+ - **No black boxes in the core.** New change classes must be a documented rule on named indices with thresholds in `Thresholds` (`src/earthdiff/change.py`), a test in `tests/test_core.py`, and an entry in the README method table. ML models are welcome as *optional* extras, never as a hard dependency.
17
+ - **Open data only.** Sentinel-2 via public STAC. No commercial/closed imagery sources.
18
+ - **No surveillance use cases.** No military targeting, no tracking of people or vehicles. Docs and examples should be about land, water, vegetation and infrastructure at area scale.
19
+ - **Be honest about accuracy.** If you add a validation case, state the source of the reference figure, what the tool got right and what it got wrong.
20
+ - Keep RAM in mind: read windows, free big arrays, no whole-tile reads.
21
+ - Tests: add synthetic-raster tests for any math. Network tests must be marked `@pytest.mark.network`.
22
+ - Style: type hints, small functions, docstrings that state units and edge cases.
23
+
24
+ ## Good first issues
25
+
26
+ Threshold presets per biome, NBR/dNBR burn index (needs B12), Turkish/other-language report strings, a Docker image, more validation cases.
27
+
28
+ ## Reporting bugs
29
+
30
+ Include: command line, `summary.json`, earthdiff version, and the scene ids from the report if the problem looks data-related.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mehmet Veysel Delibaş
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,368 @@
1
+ Metadata-Version: 2.5
2
+ Name: earthdiff
3
+ Version: 0.2.0
4
+ Summary: What changed here? Explainable change reports from free Sentinel-2 imagery, for you and for AI assistants (MCP server included). No account, no API key.
5
+ Project-URL: Homepage, https://github.com/mveyseldelibas/earthdiff
6
+ Project-URL: Issues, https://github.com/mveyseldelibas/earthdiff/issues
7
+ Project-URL: Changelog, https://github.com/mveyseldelibas/earthdiff/blob/main/CHANGELOG.md
8
+ Author: Mehmet Veysel Delibaş
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: change-detection,claude,cursor,deforestation,gis,mcp,model-context-protocol,ndvi,open-data,remote-sensing,sentinel-2,stac,wildfire
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: GIS
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: numpy>=1.26
22
+ Requires-Dist: pillow>=10.0
23
+ Requires-Dist: pyproj>=3.6
24
+ Requires-Dist: pystac-client>=0.8
25
+ Requires-Dist: rasterio>=1.3.9
26
+ Requires-Dist: scipy>=1.11
27
+ Requires-Dist: shapely>=2.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: anyio>=4.0; extra == 'dev'
30
+ Requires-Dist: httpx>=0.27; extra == 'dev'
31
+ Requires-Dist: mcp<3,>=1.12; extra == 'dev'
32
+ Requires-Dist: pytest>=8; extra == 'dev'
33
+ Provides-Extra: mcp
34
+ Requires-Dist: anyio>=4.0; extra == 'mcp'
35
+ Requires-Dist: httpx>=0.27; extra == 'mcp'
36
+ Requires-Dist: mcp<3,>=1.12; extra == 'mcp'
37
+ Provides-Extra: planetary-computer
38
+ Requires-Dist: planetary-computer>=1.0; extra == 'planetary-computer'
39
+ Description-Content-Type: text/markdown
40
+
41
+ # earthdiff
42
+
43
+ <!-- mcp-name: io.github.mveyseldelibas/earthdiff -->
44
+
45
+ **Satellite eyes for your AI assistant: "what changed here?" answered from free Sentinel-2 imagery, with before/after images, a change map, hectares, confidence and limits.**
46
+ No account, no API key, no paid service, no black-box model. Works as an [MCP](https://modelcontextprotocol.io) server (Claude Desktop, Claude Code, Cursor, any MCP client) and as a command line tool.
47
+
48
+ ![earthdiff MCP demo: question, tool calls, before/after/change images](https://raw.githubusercontent.com/mveyseldelibas/earthdiff/main/docs/images/earthdiff-mcp-demo.gif)
49
+
50
+ *The GIF is built by `scripts/make_demo_gif.py` from the literal outputs of the MCP tools (`validation/mcp/`).*
51
+
52
+ **Türkçe:** [MCP kurulumu ve kullanım aşağıda](#türkçe)
53
+
54
+ ## Use it from your AI assistant in 30 seconds
55
+
56
+ Requires [uv](https://docs.astral.sh/uv/) (it installs Python and the packages on first start; the first start downloads about 200 MB of geo libraries).
57
+
58
+ **Claude Code**
59
+
60
+ ```bash
61
+ claude mcp add earthdiff -- uvx --from "earthdiff[mcp]" earthdiff-mcp
62
+ ```
63
+
64
+ **Claude Desktop**: edit `claude_desktop_config.json` (macOS `~/Library/Application Support/Claude/`, Windows `%APPDATA%\Claude\`) and restart.
65
+
66
+ **Cursor**: `~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project). Same block:
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "earthdiff": {
72
+ "command": "uvx",
73
+ "args": ["--from", "earthdiff[mcp]", "earthdiff-mcp"],
74
+ "env": { "EARTHDIFF_OUTPUT_DIR": "C:/Users/you/earthdiff-output" }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ From a clone instead of PyPI: `pip install -e ".[mcp]"` and use `"command": "earthdiff-mcp"` (or `python -m earthdiff.mcp_server`). Check the setup with `earthdiff-mcp --check`.
81
+
82
+ Then ask, for example:
83
+
84
+ > *"What changed around Manavgat after the July 2021 wildfire?"*
85
+ > *"How did Lake Mead change between July 2019 and July 2022?"*
86
+ > *"Compare this April with last April around Lake X."* (leave `before` empty: it defaults to the same period one year earlier)
87
+
88
+ ### What the assistant does and what you get
89
+
90
+ 1. `find_place` turns the name into a bounding box (OpenStreetMap Nominatim). It does **not** guess: Manavgat returned the district (about 5,750 km2, larger than the 2,000 km2 limit) and the river, so the assistant is handed candidates and narrowing options and asks you.
91
+ 2. `plan_change` (a few seconds, nothing downloaded) shows how many scenes exist and warns about season mismatch, clouds, few scenes.
92
+ 3. `detect_change` (about 1 to 4 minutes, with progress notifications) returns the **before, after and change-map images**, hectares and polygon counts per class, the comparable share of the area, confidence, data quality, the largest changed areas and every warning. It writes `report.html` (interactive swipe map) and `changes.geojson` to your output folder and tells you where.
93
+
94
+ ![Manavgat: before, after and detected changes as returned by detect_change](https://raw.githubusercontent.com/mveyseldelibas/earthdiff/main/docs/images/earthdiff-mcp-manavgat.jpg)
95
+
96
+ *The three images are exactly what the client receives (360 km2, 20 m, 2021-07 vs 2021-08/09, 99 s). Result: 3,119 ha vegetation loss in 291 polygons, 100 % comparable, confidence high (0.80), no warnings. Context and caveats in [validation/mcp_questions.md](validation/mcp_questions.md).*
97
+
98
+ An answer an assistant can write from that output (shortened):
99
+
100
+ > Between the July and August/September 2021 windows I found **3,119 ha of vegetation loss** (291 polygons) around the Manavgat reservoir, concentrated south of it; the dark scar is visible in the "after" image. 100 % of the area was cloud-free in both periods (8 scenes each), confidence is high (0.80, a heuristic, not a probability). No warnings. This says vegetation turned much less green, not why; a wildfire is consistent with the timing but this tool cannot prove it. It is a screening result, not an official burned-area figure; the box (360 km2) covers only part of the fire. *Contains modified Copernicus Sentinel data 2021.* Files: `report.html`, `changes.geojson` in your output folder.
101
+
102
+ ### Tools
103
+
104
+ | Tool | What it does |
105
+ |---|---|
106
+ | `find_place` | Place name to bounding box(es). Several different places: returns candidates, never guesses. Box over the area limit: returns a centred box and tiles to choose from. Nominatim policy: User-Agent, at most 1 request/s, cache, attribution. `margin_km` adds the surroundings. |
107
+ | `plan_change` | Validates area and windows, searches scenes, **downloads nothing**. Scene counts and dates, grid size, expected runtime, warnings. |
108
+ | `detect_change` | The analysis (reuses the same pipeline as `earthdiff run`). Images, hectares and polygons per class, `likely_cause` hint for vegetation loss, optional `seasonal_check`, comparable share, confidence, bright-surface check, data quality, largest areas, warnings, file paths. |
109
+ | `explain_method` | The rules with thresholds, pipeline, how confidence is computed, limits, data and attribution. |
110
+
111
+ Parameters are validated and **unknown parameters are rejected** with the list of allowed ones (a model that invents `crs=` gets an error, not a silently different analysis). If `before` is empty it is the `after` window one year earlier. Warnings (season mismatch, cloud, a single scene, an implausibly large share of the area flagged) are always part of the answer and the server instructions tell the assistant to report all of them.
112
+
113
+ ### Fire, clearing, harvest, salt: three extra checks
114
+
115
+ - **`likely_cause` for vegetation loss** (always on; B12 is read as well, about +20 s on a 360 km2 run). Each vegetation-loss polygon gets `fire-like`, `clearing-like` or `unclear` plus the measurements and a plain-language basis (dNBR, post-event brightness, outline shape). It is a **hint, never a verdict**, and not a new class. dNBR uses the burn-severity thresholds of Key & Benson (2006, FIREMON) that Copernicus EFFIS also applies to Sentinel-2 (0.10 / 0.27 / 0.44 / 0.66; [EFFIS](https://forest-fire.emergency.copernicus.eu/about-effis/technical-background/fire-severity), [UN-SPIDER](https://un-spider.org/advisory-support/recommended-practices/recommended-practice-burn-severity/in-detail/normalized-burn-ratio)). Those were derived for Landsat in specific ecosystems, so they are indicative. The brightness (dark = visible reflectance <= 0.09) and outline thresholds are **this project's own** and were set by looking at two public cases only. In the tests: Manavgat 2021 fire, 3,075 of 3,119 ha `fire-like`; Istanbul Airport 2017 to 2019 (construction), 8.6 of 558 ha `fire-like` (1.5 %, a wrong hint), 52 ha `clearing-like`, 497 ha `unclear`. Old, grey burn scars are brighter than fresh char and will often come out `unclear`.
116
+ - **Seasonal check** (`seasonal_check: true`, CLI `--seasonal-check`; **off by default**). Compares the vegetation-loss polygons with the same two windows of the 2 earlier years (red and NIR only, at most 3 scenes per window, only the area around the loss). Polygons whose NDVI fell by a similar amount in earlier years are flagged `likely_seasonal`; they are never removed. It costs extra time: measured +47 s on a 39 km2 test (4 extra composites, +75 % on a 61 s run), and `plan_change` lists the windows it would use. Konya plain (irrigated cropland, May vs July 2023): 181 of 668 ha (27 %) flagged. Limits: crop rotation (a field harvested every second year may be missed), needs scenes from earlier years (none before 2017), a one-off event that repeats is flagged too.
117
+ - **Bright-surface warning.** If at least 20 % of the comparable area is very bright, non-vegetated surface (salt, sand, snow: mean visible reflectance >= 0.25 and NDVI < 0.15), the answer says that change there is **not reliable**, `bright_surface.reliable` is false, the confidence is capped at `low`, `data_quality` is `poor` and polygons on bright pixels lose up to half of their confidence. Lake Tuz now gets this warning (56 % bright) instead of a confident-looking result. It also reports the share of water-like pixels by NDWI on the bright pixels before and after as an exploratory wet/dry signal (see [validation/mcp_questions.md](validation/mcp_questions.md)).
118
+
119
+ ### Safety and limits of the server
120
+
121
+ - **Read-only toward data.** It only reads public STAC and Sentinel-2 COGs and writes files to one output folder (`EARTHDIFF_OUTPUT_DIR`, default `~/earthdiff-mcp-output`). Each run gets its own `<timestamp>_<name>` subfolder; the `name` is reduced to a safe ASCII slug (no `..`, separators or control characters). It never deletes existing files.
122
+ - **Network only to**: the STAC API (Element 84 Earth Search, optionally Microsoft Planetary Computer), the Sentinel-2 COG hosts (scene URLs from a STAC answer are checked against an allow-list and others are dropped), the Copernicus DEM GLO-30 tiles on `copernicus-dem-30m.s3.amazonaws.com` (a small elevation window for the report's offline 3D view; `--no-terrain` / `terrain=False` turns it off) and `nominatim.openstreetmap.org`. This is enforced in the code, it is not an operating-system firewall.
123
+ - **Area and time limits**: 2,000 km2 per analysis, the same limit as the CLI and the web UI (`EARTHDIFF_MAX_AREA_KM2` can lower it; the model cannot raise it), windows of at most 366 days, run-time limit 480 s (`EARTHDIFF_MAX_RUNTIME_S`), one analysis at a time.
124
+ - **Errors never contain file paths** (home folder, output folder or other paths are replaced with `<path>`).
125
+ - Other settings: `EARTHDIFF_CONTACT` (an e-mail or URL appended to the Nominatim User-Agent, recommended), `EARTHDIFF_OPEN_REPORT=1` / `--open-report` (open `report.html` in the browser for clients that do not show images).
126
+ - **Timeouts.** A run takes about 100 s for 360 km2 at 20 m on a home connection. The server sends a progress notification at every step and a heartbeat every 8 s. If your client still gives up early, use `plan_change` first, a smaller area or a coarser `resolution_m`, and raise the client's tool timeout if it has one (Claude Code: `MCP_TOOL_TIMEOUT` in milliseconds).
127
+
128
+ ### Limits: please read
129
+
130
+ **earthdiff is a screening tool. It is not an official damage, burned-area, flood or deforestation assessment.** It tells you *where free 10 to 60 m imagery changed in vegetation, water or soil brightness*, not *why*.
131
+
132
+ - Fire, felling, harvest, drought and construction can look alike. Seasonal change and agriculture create false alarms: compare the same season of different years.
133
+ - It does not see built-up growth on ground that was already bare, features narrower than a few pixels or smaller than the minimum mapping unit (0.5 ha by default).
134
+ - **Bright surfaces are unreliable.** In the Lake Tuz test (salt flat, late summer 2019 vs 2021) both composites show a saturated white salt crust and the rules reported no change on the lake bed, so they cannot show the drying. The server now says so (warning, low confidence, poor data quality), but it does not fix the mapping. A spring baseline did not help the rules either; an exploratory NDWI signal does separate wet and dry salt (it is reported as a warning, not as a mapped class). See [validation/mcp_questions.md](validation/mcp_questions.md).
135
+ - `likely_cause` is a hint and can be wrong (in our Istanbul Airport test 1.5 % of the loss was called fire-like); `likely_seasonal` can miss crop rotations.
136
+ - Confidence is a heuristic score from 0 to 1, not a probability. `data_quality` grades the comparison (cloud-free share, season gap, number of scenes), not the correctness of the polygons.
137
+ - Place search results are only as good as OpenStreetMap; an oversized or ambiguous place is never analysed silently.
138
+ - Not for surveillance, targeting, or tracking people or vehicles.
139
+
140
+ ### Related projects
141
+
142
+ Other free, key-less MCP servers cover parts of this: for example `stac-eo-mcp`, `open-earth-mcp`, `chuk-mcp-stac` and `eo-processor-mcp` search STAC catalogues and compute indices. earthdiff's focus is the end-to-end "what changed?" answer: two time windows compared by readable rules, with a change map, area in hectares, confidence and explicit limits. I looked for an existing server that does this without an account and did not find one; there may be others.
143
+
144
+ ---
145
+
146
+ ## Command line and report
147
+
148
+ **Draw an area. Get an explainable "what changed here?" report from free Sentinel-2 imagery.**
149
+ No account, no API key, no paid service, no black-box model.
150
+
151
+ ![Manavgat wildfire burn scar detected by earthdiff](https://raw.githubusercontent.com/mveyseldelibas/earthdiff/main/docs/img/manavgat_wide_overview.png)
152
+
153
+ *Manavgat (Antalya, Turkey), before vs. after the July 2021 wildfire: earthdiff maps ~31,000 ha of vegetation loss from public Sentinel-2 data in a ~5 minute run on a laptop. Details and honest caveats in [Validation](#validation).*
154
+
155
+ ---
156
+
157
+ ## Why
158
+
159
+ Tools for "has anything changed on this piece of land?" mostly sit behind accounts (Google Earth Engine), paid tiers (Sentinel Hub EO Browser) or answer one question only (Global Forest Watch: forest). Deep-learning GeoAI libraries are powerful but ask you to trust a model.
160
+
161
+ earthdiff takes the opposite road: **a plain spectral-index method you can read on one screen**, applied to the free [Copernicus Sentinel-2](https://sentinels.copernicus.eu/) archive through public STAC APIs. Every alert shows its evidence (before/after crops, NDVI/MNDWI/BSI values, number of valid observations) and every report states its own limitations.
162
+
163
+ For journalists, NGOs, municipalities, farmers, researchers and curious citizens.
164
+
165
+ | You give | You get |
166
+ |---|---|
167
+ | a GeoJSON polygon or `--bbox`, two time windows | `report.html` and `changes.xlsx` (Excel: summary, one row per change, scenes); the report is self-contained, Turkish or English: swipe slider, scene-by-scene timeline with the date of every satellite pass, change map, plain-language description and strength of every change, pulsing change spots, clickable polygons, sortable table, method + caveats; an offline 3D terrain view and relief basemap from the embedded Copernicus DEM; only the optional OpenStreetMap / OpenTopoMap basemaps load online) |
168
+ | (optional) a TOML file for monthly re-runs | `changes.geojson` (polygons with class, hectares, confidence, index values), `before.png`, `after.png`, `change_map.png`, `overview.png`, `change_class.tif`, `summary.json` |
169
+
170
+ Detected classes: **vegetation loss** (deforestation, fire scars, clearing), **vegetation gain**, **new water** (flooding, filling reservoirs), **water loss** (drying reservoirs/lakes), **new bare soil / built-up** (quarries, construction on vegetated or sparse land).
171
+
172
+ ## Quickstart
173
+
174
+ Requires Python 3.11+.
175
+
176
+ ```bash
177
+ git clone <this repo> && cd earthdiff
178
+ python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
179
+ pip install -e . # add [planetary-computer] for the optional backend
180
+
181
+ # 1) dry run: how many scenes exist for this area/time? (downloads nothing)
182
+ earthdiff plan --bbox 31.45,36.90,31.70,37.06 --before 2021-07-01/2021-07-27 --after 2021-08-10/2021-09-10
183
+
184
+ # 2) analyse
185
+ earthdiff run --aoi examples/aoi.geojson \
186
+ --before 2021-07-01/2021-07-27 --after 2021-08-10/2021-09-10 \
187
+ --name "Manavgat" --out report/
188
+ # open report/report.html
189
+ ```
190
+
191
+ A `--bbox` with a negative longitude must be written `--bbox=-114.8,36.05,-114.6,36.2`.
192
+
193
+ Time windows: `2023-06` (whole month), `2023-06-10` (day), `2023-06/2023-07` (month range) or `2023-06-01/2023-07-15`. If you omit `--before`, the baseline is the **same window one year earlier** (same season = fewer false alarms).
194
+
195
+ Other commands:
196
+
197
+ ```bash
198
+ earthdiff ui # local web UI on http://127.0.0.1:8765: draw rectangle/polygon (up to 2,000 km2), pick months, Run
199
+ earthdiff watch --init watch.toml # write an example config
200
+ earthdiff watch watch.toml # re-run all areas for the last full month; appends history.csv
201
+ ```
202
+
203
+ `watch` does **not** install a scheduler. Put it into cron / Windows Task Scheduler / a CI cron yourself, e.g. `0 6 5 * * earthdiff watch /path/watch.toml`.
204
+
205
+ Useful options: `--seasonal-check` (flag recurring harvest-like loss, adds a few small downloads), `--no-likely-cause` (skip B12 and the fire-like / clearing-like hint), `--sensitivity strict|default|sensitive`, `--mmu-ha 0.5`, `--resolution 10|20|30|60`, `--max-scenes 8`, `--max-cloud 80`, `--backend earth-search|planetary-computer`, `--max-area-km2 2000`, `--force`.
206
+
207
+ ## How it works
208
+
209
+ 1. **Data.** Sentinel-2 L2A surface reflectance (B02, B03, B04, B08, B11 and the SCL scene classification) from the public STAC API of [Element 84 Earth Search](https://earth-search.aws.element84.com/v1) (Cloud-Optimized GeoTIFFs on AWS Open Data); optionally [Microsoft Planetary Computer](https://planetarycomputer.microsoft.com/) (`pip install "earthdiff[planetary-computer]"`; signing tokens are anonymous, no account). Only the **window of your AOI** is read through HTTP range requests and warped onto a UTM grid (10/20/30/60 m, chosen so that the window stays under ~4 M pixels).
210
+ 2. **Scene selection and masking.** For each period the least-cloudy candidates (scene-level) are checked with the SCL band **inside your AOI**; the best `--max-scenes` (default 8) by cloud-free AOI coverage are kept. SCL classes 0 (no data), 1 (saturated), 3 (cloud shadow), 8/9 (cloud), 10 (cirrus) and 11 (snow/ice) are masked; cloud/shadow pixels are buffered by 30 m.
211
+ 3. **Composites.** Per period and band, the per-pixel **median** of the valid observations (the +1000 DN radiometric offset of processing baseline >= 04.00 is handled).
212
+ 4. **Indices** (reflectance 0..1):
213
+
214
+ ```
215
+ NDVI = (NIR - Red) / (NIR + Red) B08, B04
216
+ MNDWI = (Green - SWIR1) / (Green + SWIR1) B03, B11
217
+ BSI = ((SWIR1 + Red) - (NIR + Blue)) / ((SWIR1 + Red) + (NIR + Blue))
218
+ ```
219
+ 5. **Rules** (d = after - before; thresholds live in `Thresholds`, presets via `--sensitivity`):
220
+
221
+ | Class | Rule (default thresholds) |
222
+ |---|---|
223
+ | Vegetation loss | NDVI before >= 0.40 and dNDVI <= -0.25 and not water after |
224
+ | Vegetation gain | NDVI after >= 0.40 and dNDVI >= +0.25 and not water before |
225
+ | New water | water after (MNDWI > 0.10 and NDVI < 0.30), MNDWI before < -0.05, dMNDWI >= 0.25 |
226
+ | Water loss | water before, MNDWI after < -0.05, dMNDWI <= -0.25 |
227
+ | New bare / built-up | dBSI >= 0.15 and NDVI after < 0.25 and no water before/after |
228
+
229
+ On top of the classes, vegetation-loss polygons carry `likely_cause` (`fire-like` / `clearing-like` / `unclear`, from dNBR = NBR before - NBR after with NBR = (NIR - SWIR2) / (NIR + SWIR2), post-event brightness and outline; dNBR thresholds from Key & Benson 2006 as used by Copernicus EFFIS, the other two are this project's heuristics), and optionally `likely_seasonal`. See "Fire, clearing, harvest, salt" above.
230
+
231
+ Classes are mutually exclusive (priority: new water > water loss > vegetation loss > vegetation gain > bare). `strict` / `sensitive` use 0.35 / 0.18 instead of 0.25 for the NDVI and MNDWI deltas (and 0.22 / 0.10 for BSI).
232
+ 6. **Cleanup.** Morphological opening (3x3, for pixels <= 20 m) removes narrow noise, connected components (8-neighbour) below the **minimum mapping unit** (0.5 ha) are dropped, the rest are polygonized. Area = pixel count x pixel area on the UTM grid.
233
+ 7. **Confidence** (heuristic 0..1, *not* a probability) = 0.5 x magnitude + 0.3 x valid observations + 0.2 x size, where magnitude = clip((mean delta - threshold) / threshold, 0, 1), observations = clip(min(before, after valid obs) / 3, 0, 1), size = clip(log10(area / MMU), 0, 1). >= 0.70 is "high", >= 0.45 "medium".
234
+ 8. **Guard rails.** AOI limit 2,000 km2, the same in the CLI, the web UI and the MCP server (large areas are read at 20/30/60 m; the CLI can raise it with `--max-area-km2`); refuses when < 20 % of the AOI is cloud-free in both periods (override: `--force`); warns on < 60 % cloud-free, single-scene composites, windows in different seasons (> 45 days apart in the annual cycle), very short windows, and when > 30 % of the AOI is flagged (usually a seasonal/atmospheric mismatch; when at least 80 % of the change is fire-like by dNBR the warning says it fits one large burn instead, and the MCP data-quality grade is not lowered).
235
+
236
+ Everything is plain NumPy / SciPy / rasterio. No deep-learning dependency.
237
+
238
+ ## Validation
239
+
240
+ Six runs, exact commands in [`validation/cases.json`](validation/cases.json) (`python validation/run_all.py`); per-case numbers in [`validation/results/`](validation/results/). These are **qualitative checks and rough area comparisons, not an accuracy assessment**: there are no field-verified reference polygons here, and we did not compare against EFFIS / Copernicus EMS polygons.
241
+
242
+ ### 1. Manavgat wildfire (Antalya, Turkey), 28 July 2021 - works well
243
+
244
+ ![Manavgat wide](https://raw.githubusercontent.com/mveyseldelibas/earthdiff/main/docs/img/manavgat_wide_overview.png)
245
+
246
+ Before 2021-07-01..27, after 2021-08-10..09-10, 2,560 km2 window at 30 m: **31,362 ha vegetation loss** in 1,271 polygons (+1,519 ha "new bare/built", mostly bare patches inside the scar); 100 % comparable. Published figures for the same fire: ~30,600 ha affected by 30 July from Landsat-8/Sentinel-2 mapping by Gebze Technical University ([GTU](https://www.gtu.edu.tr/en/icerik/8/12549/display.aspx)); ~56,700 ha for Manavgat in a later study of that group ([bianet](https://bianet.org/english/environment/248673-study-83-000-hectares-of-forests-burned-in-turkey-s-south-and-southwest-in-two-weeks-of-fires)); ~60,000 ha (forest, shrub, settlements, farmland) in an academic modelling study ([overview](https://en.wikipedia.org/wiki/Wildfires_in_Turkey)). **Reading:** the scar's shape is detected cleanly and the area is the same order of magnitude as the early published figure, but we do not reproduce the larger final numbers: the rule only sees vegetation that was green (NDVI >= 0.4) in July, so burnt sparse shrub and fields are under-counted. Treat the number as "vegetation that clearly lost greenness", not as an official burned area.
247
+
248
+ Detailed 20 m view of a 395 km2 sub-window ([`docs/img/manavgat_overview.png`](docs/img/manavgat_overview.png)): 6,138 ha vegetation loss.
249
+
250
+ ### 2. Lake Mead (USA), July 2019 vs July 2022 - works well
251
+
252
+ ![Lake Mead](https://raw.githubusercontent.com/mveyseldelibas/earthdiff/main/docs/img/lake_mead_overview.png)
253
+
254
+ 300 km2 at 10 m: **421 ha water loss in 84 polygons**, essentially a ring around the retreating shoreline; nothing else flagged. Reference: July 2022 was the lowest level on record at the time, ~1,040 ft ([news3lv](https://news3lv.com/news/local/lake-mead-dips-to-lowest-water-level-on-record)). We did not convert level change into an area from reservoir bathymetry. In the images the south-west corner of the 2022 composite looks hazier than the 2019 one (scene differences survive compositing); no change was flagged there.
255
+
256
+ ### 3. Flood after Storm Daniel (Thessaly, Greece), September 2023 - works, with caveats
257
+
258
+ ![Thessaly flood](https://raw.githubusercontent.com/mveyseldelibas/earthdiff/main/docs/img/thessaly_flood_overview.png)
259
+
260
+ Before 2023-08-15..31, after 2023-09-08..30, 294 km2: **3,861 ha new water** in 41 polygons, the bulk of it west of the Lake Karla reservoir where flood water spread over farmland, plus a patch east of the reservoir dyke that we did not verify. ~823 ha of "vegetation loss" are mostly agricultural fields (harvest vs. flood damage cannot be told apart). Context: Copernicus Rapid Mapping estimated ~73,000 ha flooded across Thessaly by 7 September ([Wikipedia: Storm Daniel](https://en.wikipedia.org/wiki/Storm_Daniel)); our window covers a small part of that, so this is a plausibility check, not a validation of a total. Muddy flood water is darker/browner than clean water, and parts of the flood edge are classified as vegetation loss rather than water.
261
+
262
+ ### 4. Istanbul Airport (Turkey), 2017 vs 2019 - the tool's limit, shown on purpose
263
+
264
+ ![Istanbul Airport](https://raw.githubusercontent.com/mveyseldelibas/earthdiff/main/docs/img/istanbul_airport_overview.png)
265
+
266
+ The airport site is ~7,650 ha, 6,173 ha of it forest according to the environmental-impact report ([Wikipedia](https://en.wikipedia.org/wiki/Istanbul_Airport)). It was already cleared, bare ground in 2017 (no usable Sentinel-2 L2A COG scenes for this area in 2015-2016), so earthdiff flags only **8 ha** of "new bare/built-up" and **does not detect the runways and terminals**: bare soil turning into concrete is not a change that NDVI/MNDWI/BSI rules see. Around the site it reports 656 ha vegetation loss, 265 ha gain and some "water" changes along the Black Sea coast (sediment plumes), which are not land change. **Lesson: v0.1 finds vegetation, water and soil-brightness change; it cannot see built-up growth on already-bare ground.**
267
+
268
+ ### 5. Control: northern Istanbul forest and suburbs, July 2022 vs July 2023
269
+
270
+ ![control](https://raw.githubusercontent.com/mveyseldelibas/earthdiff/main/docs/img/control_belgrad_overview.png)
271
+
272
+ 168 km2, same season in consecutive years: 0.27 % of the window flagged (40 ha vegetation loss in 26 polygons, 3 ha gain, 2 ha water loss), mostly small patches at the urban fringe. A cheap sanity check that the thresholds do not hallucinate change in a stable landscape.
273
+
274
+ ## Limitations (please read)
275
+
276
+ - **Seasonality and agriculture.** Crops, grassland and deciduous forest change with season and management; harvest, ploughing, irrigation, grazing and drought look like vegetation change. Compare the same season in different years.
277
+ - **It does not say why.** "Vegetation loss" can be fire, felling, harvest, drought or construction. "New bare/built-up" can also be dry crops, soil moisture or exposed lake bed.
278
+ - **Water.** Turbid, shallow or algae-covered water, wet soil, cloud shadow and terrain shadow can fall on either side of the MNDWI rule.
279
+ - **Resolution.** 10-60 m pixels (SWIR is natively 20 m). Narrow features (roads, small buildings, streams) are not reliably detected; everything under the MMU is hidden.
280
+ - **Built-up growth on bare ground is not detected** (see Istanbul Airport).
281
+ - **Atmosphere/geometry.** Residual haze, cloud shadow, sun/view-angle differences and ~10 m co-registration create edge false alarms. The SCL mask is imperfect over bright surfaces (snow, salt, sand) and for thin cloud.
282
+ - **Availability** of Sentinel-2 L2A COGs in the free archives varies by region and year; old JPEG2000-only items on requester-pays S3 are skipped.
283
+ - **Bright surfaces.** Salt pans, sand and snow saturate the image and are masked or compared unreliably (see the Lake Tuz case in `validation/mcp_questions.md`).
284
+ - **Screening tool, not ground truth.** Thresholds are conservative defaults, not tuned per biome. Verify anything consequential with higher-resolution data or in the field. Hectares are for flagged pixels, not an official burned / flooded / deforested area.
285
+ - **Intended uses:** land, water, vegetation and infrastructure at area scale. Not for surveillance, targeting, or tracking people or vehicles.
286
+
287
+ ## Data licence and attribution
288
+
289
+ *Contains modified Copernicus Sentinel data [years of the scenes used]* - printed automatically in every report. Sentinel data are free and open under the [Copernicus Sentinel data terms](https://sentinels.copernicus.eu/documents/247904/690755/Sentinel_Data_Legal_Notice). Imagery is served by Element 84 Earth Search (AWS Open Data) or Microsoft Planetary Computer. Place search (`find_place`) uses OpenStreetMap Nominatim: (c) OpenStreetMap contributors, ODbL, following the [Nominatim usage policy](https://operations.osmfoundation.org/policies/nominatim/). Optional basemap tiles are (c) OpenStreetMap contributors. Leaflet 1.9.4 (BSD-2-Clause) is bundled in `src/earthdiff/vendor/`.
290
+
291
+ ## Development
292
+
293
+ ```bash
294
+ pip install -e ".[dev]"
295
+ pytest # offline: synthetic rasters (indices, masking, compositing, classification, MMU, end-to-end, watch) + MCP tools with fake STAC/Nominatim + real MCP stdio tools/list and tools/call
296
+ pytest -m network # + live STAC smoke test and a real end-to-end MCP run (Nominatim, STAC, Sentinel-2; a few minutes)
297
+ python validation/run_all.py [case ...] # CLI validation cases
298
+ python validation/run_mcp_questions.py [case ...] # real questions through the MCP server (validation/mcp_questions.md)
299
+ python scripts/make_demo_gif.py # demo GIF / square image from those outputs
300
+ ```
301
+
302
+ Layout: `src/earthdiff/{aoi,periods,stac,reader,composite,indices,change,render,report,i18n,xlsx,terrain,yearly,pipeline,watch,ui,cli}.py`, MCP server `{mcp_server,mcp_service,mcp_prompts,geocode,safety}.py`, `templates/report.html`, `static/ui.html`.
303
+
304
+ PostGIS/QGIS installs export `PROJ_LIB` / `GDAL_DATA` pointing to an older PROJ database, which breaks rasterio ("EPSG code is unknown"). earthdiff points its own process to the data bundled with rasterio (opt out: `EARTHDIFF_KEEP_PROJ_ENV=1`).
305
+
306
+ ## Roadmap
307
+
308
+ - v0.3: dNBR / burn severity (needs B12), built-up growth via SWIR/NDBI + texture, optional Sentinel-1 radar for floods under cloud, per-biome threshold presets, land-cover mask (ESA WorldCover) to flag cropland and bright surfaces (salt, sand), a data-quality warning for saturated bright surfaces, Docker image, more report languages.
309
+ - v0.4: time-series mode (trend instead of two snapshots), alerts by e-mail/RSS from `watch`, optional ML refinement as an opt-in plugin.
310
+
311
+ ## License
312
+
313
+ MIT (c) 2026 Mehmet Veysel Delibaş. See [LICENSE](LICENSE), [CONTRIBUTING](CONTRIBUTING.md), [CHANGELOG](CHANGELOG.md).
314
+
315
+ ---
316
+
317
+ # Türkçe
318
+
319
+ ## Yapay zekâ asistanınıza "uydu gözü" (MCP)
320
+
321
+ **earthdiff**, Claude Desktop, Claude Code, Cursor gibi MCP destekli asistanlara ücretsiz **Sentinel-2** verisiyle "burada ne değişti?" sorusunu cevaplatır. Hesap ya da API anahtarı gerekmez. Asistan yeri bulur, sahneleri kontrol eder, analizi çalıştırır ve size önce/sonra görselini, değişim haritasını, sınıf başına hektarı, güven düzeyini, uyarıları ve sınırları verir.
322
+
323
+ **Kurulum (30 saniye)** ([uv](https://docs.astral.sh/uv/) gerekir):
324
+
325
+ ```bash
326
+ claude mcp add earthdiff -- uvx --from "earthdiff[mcp]" earthdiff-mcp # Claude Code
327
+ ```
328
+
329
+ Claude Desktop ve Cursor için yukarıdaki JSON bloğunu `claude_desktop_config.json` / `.cursor/mcp.json` dosyasına ekleyin. Çıktı klasörü: `EARTHDIFF_OUTPUT_DIR` (varsayılan `~/earthdiff-mcp-output`).
330
+
331
+ **Örnek sorular:** "Manavgat'ta Temmuz 2021 yangınından sonra ne değişti?", "Lake Mead 2019 ile 2022 arasında nasıl değişti?", "Bu nisanı geçen nisanla karşılaştır." (`before` boşsa geçen yılın aynı dönemi kullanılır).
332
+
333
+ **Araçlar:** `find_place` (yer adından kutu; belirsizse aday listesi döner, tahmin etmez; çok büyükse daraltma seçenekleri sunar; OSM Nominatim kullanım kuralları: User-Agent, en fazla 1 istek/sn, önbellek, atıf), `plan_change` (indirmeden sahne sayısı ve uyarılar), `detect_change` (analiz; önce/sonra/değişim görselleri, hektar, poligon sayısı, karşılaştırılabilir oran, güven, veri kalitesi, uyarılar, en büyük alanlar; `report.html` ve `changes.geojson` yerelde kalır; ilerleme bildirimi gönderir), `explain_method` (kurallar ve sınırlar). Bilinmeyen parametreler reddedilir.
334
+
335
+ **Üç ek kontrol:** (1) Bitki kaybı poligonlarına `likely_cause` ipucu (`fire-like`, `clearing-like`, `unclear`) eklenir; B12 ile dNBR, yanık sonrası parlaklık ve şekil kullanılır. dNBR eşikleri Key & Benson (2006) / Copernicus EFFIS kaynaklıdır (0,10 / 0,27 / 0,44 / 0,66); parlaklık ve şekil eşikleri bu projenin kendi sezgisidir ve yalnız iki örneğe bakılarak belirlendi. **Bu bir ipucudur, hüküm değildir.** Testte Manavgat'ta 3.119 ha'nın 3.075 ha'sı `fire-like`; İstanbul Havalimanı inşaatında 558 ha'nın yalnız 8,6 ha'sı `fire-like` (yanlış ipucu, %1,5), geri kalanı çoğunlukla `unclear`. (2) `seasonal_check` (varsayılan **kapalı**, CLI `--seasonal-check`): önceki 2 yılın aynı pencerelerine bakıp her yıl tekrarlayan kaybı (hasat gibi) `likely_seasonal` diye işaretler, poligonu silmez; ölçülen ek süre 39 km² testte +47 sn. Konya ovası örneğinde 668 ha'nın 181 ha'sı (%27) işaretlendi. (3) Parlak yüzey uyarısı: alanın en az %20'si tuz/kum/kar gibi çok parlak yüzeyse cevap "bu yüzeyde değişim güvenilir değil" der, güven `low`'a çekilir, veri kalitesi `poor` olur. Tuz Gölü artık bu uyarıyı alıyor (%56 parlak); yöntem hâlâ kurumayı haritalayamıyor, bahar tabanı kurallara yardımcı olmadı. NDWI ile yaş/kuru tuz ayrışıyor (%84 ve %1,5) ve bu, haritalanmış sınıf olarak değil, deneysel uyarı olarak verilir.
336
+
337
+ **Güvenlik:** yalnız okuma; dosyalar tek bir çıktı klasörüne yazılır, `name` temizlenir; alan sınırı 2.000 km2 (komut satırı, arayüz ve MCP'de aynı), pencere en çok 366 gün, süre sınırı 480 sn, aynı anda tek analiz; hata mesajlarında dosya yolu yoktur; ağ yalnız STAC, Sentinel-2 COG, Copernicus DEM (raporun internetsiz 3B görünümü için küçük bir yükseklik penceresi) ve Nominatim'e açılır.
338
+
339
+ **Gerçek denemeler** ([validation/mcp_questions.md](validation/mcp_questions.md)): Manavgat 2021 (360 km2 kutuda 3.119 ha bitki kaybı; yayımlanan toplamlar çok daha büyük yangın alanını kapsar, bu bir makullük kontrolüdür, doğrulama değildir), Lake Mead 2019/2022 (616 ha su kaybı, kıyı çizgisi geri çekilmesi), **Tuz Gölü 2019/2021 (yetersiz: iki dönemde de parlak tuz kabuğu görünüyor, göl yatağında değişim bulunmadı, kuruma gösterilemedi)**.
340
+
341
+ **Sınırlar:** earthdiff bir **ön tarama aracıdır, resmi hasar tespiti değildir**. Neyin değiştiğini söyler, nedenini söylemez (yangın, kesim, hasat, kuraklık, inşaat benzer görünebilir). Mevsim ve tarım etkisi, bulut/gölge, parlak yüzeyler (tuz, kum, kar), 10-60 m çözünürlük ve 0,5 ha altı değişiklikler sınırlayıcıdır. Önemli kararlar için yüksek çözünürlüklü veri veya saha doğrulaması gerekir.
342
+
343
+ **Atıf:** *Contains modified Copernicus Sentinel data* (kullanılan yıllar). Yer araması: (c) OpenStreetMap katkıcıları (ODbL).
344
+
345
+ ## Komut satırı
346
+
347
+ **earthdiff**, haritada bir alan çizerek (veya GeoJSON / bbox vererek) o alanda **ne değişti?** sorusuna ücretsiz **Sentinel-2** uydu görüntüleriyle, **açıklanabilir** bir rapor üreten açık kaynaklı bir araçtır. Hesap, API anahtarı veya ücretli servis gerekmez. "Kara kutu" yapay zekâ yoktur: NDVI / MNDWI / BSI endekslerine dayalı, eşikleri yukarıda yazılı basit kurallar kullanılır.
348
+
349
+ **Ne bulur?** Bitki örtüsü kaybı (orman kaybı, yangın izi, açma), bitki kazancı, yeni su (taşkın, dolan baraj), su kaybı (kuruyan baraj/göl), yeni çıplak toprak / yapılaşma. Her değişiklik poligonu için sınıf, hektar, güven puanı, önce/sonra endeks değerleri ve önce/sonra görüntü kesitleri verilir.
350
+
351
+ **Hızlı başlangıç**
352
+
353
+ ```bash
354
+ python -m venv .venv && .venv\Scripts\activate
355
+ pip install -e .
356
+ earthdiff plan --bbox 31.45,36.90,31.70,37.06 --before 2021-07-01/2021-07-27 --after 2021-08-10/2021-09-10
357
+ earthdiff run --bbox 31.45,36.90,31.70,37.06 --before 2021-07-01/2021-07-27 --after 2021-08-10/2021-09-10 --name "Manavgat" --out rapor/
358
+ earthdiff ui # yerel arayüz: http://127.0.0.1:8765 (yalnızca bu bilgisayardan erişilir; alan en çok 2.000 km2)
359
+ earthdiff watch watch.toml # aylık yeniden çalıştırma; zamanlamayı (Görev Zamanlayıcı / cron) siz kurarsınız
360
+ ```
361
+
362
+ `--before` verilmezse `--after` penceresinin **bir yıl öncesi** (aynı mevsim) kullanılır; böylece mevsimsel yanlış alarmlar azalır.
363
+
364
+ **Doğrulama özeti:** 2021 Manavgat yangını için ~31.400 ha bitki kaybı (yayımlanan tahminler: GTÜ ~30.600 ha, 30 Temmuz'a kadar; sonraki çalışmada ~56.700 ha). Yanık izi net çıkıyor, alan büyüklük sırası olarak erken tahminle uyumlu ama nihai büyük sayıları yakalamıyor. Lake Mead 2019→2022'de kıyı çizgisi gerilemesi (421 ha su kaybı). Teselya 2023 Daniel fırtınası taşkını (pencere içinde 3.861 ha yeni su). İstanbul Havalimanı'nda araç **başarısız**: 2017'de zaten çıplak olan arazide pist/terminal yapımını görmüyor (bilerek gösterildi). Kontrol alanında (kuzey İstanbul) pencerenin %0,27'si işaretlendi.
365
+
366
+ **Sınırlar:** tarım/mevsim etkisi, "neden" sorusuna cevap vermez, gölge/bulut/bulanıklık, 10-60 m çözünürlük, MMU (0,5 ha) altındaki değişiklikler görünmez. Bu bir **tarama aracıdır**; önemli kararlar için yüksek çözünürlüklü veri veya saha doğrulaması gerekir. Gözetleme, hedefleme veya kişi/araç takibi için kullanılmaz.
367
+
368
+ **Atıf:** Contains modified Copernicus Sentinel data (kullanılan yıllar) - her raporda otomatik yazılır. Lisans: MIT.