openeolib 0.1.3__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 (34) hide show
  1. openeolib-0.1.3/LICENSE +15 -0
  2. openeolib-0.1.3/PKG-INFO +299 -0
  3. openeolib-0.1.3/README.md +263 -0
  4. openeolib-0.1.3/openeolib/__init__.py +250 -0
  5. openeolib-0.1.3/openeolib/_plot_utils.py +268 -0
  6. openeolib-0.1.3/openeolib/analyzer.py +273 -0
  7. openeolib-0.1.3/openeolib/animation.py +512 -0
  8. openeolib-0.1.3/openeolib/engines.py +202 -0
  9. openeolib-0.1.3/openeolib/eo_cache.py +381 -0
  10. openeolib-0.1.3/openeolib/eo_types.py +313 -0
  11. openeolib-0.1.3/openeolib/eo_utils.py +128 -0
  12. openeolib-0.1.3/openeolib/panels.py +940 -0
  13. openeolib-0.1.3/openeolib/providers/__init__.py +1 -0
  14. openeolib-0.1.3/openeolib/providers/_s2_utils.py +280 -0
  15. openeolib-0.1.3/openeolib/providers/base.py +12 -0
  16. openeolib-0.1.3/openeolib/providers/era5.py +2133 -0
  17. openeolib-0.1.3/openeolib/providers/msm.py +836 -0
  18. openeolib-0.1.3/openeolib/providers/openeo_client.py +147 -0
  19. openeolib-0.1.3/openeolib/providers/provider.py +507 -0
  20. openeolib-0.1.3/openeolib/providers/radar_gpv_client.py +412 -0
  21. openeolib-0.1.3/openeolib/providers/rain.py +551 -0
  22. openeolib-0.1.3/openeolib/providers/sentinel2.py +231 -0
  23. openeolib-0.1.3/openeolib/providers/sentinel5p.py +147 -0
  24. openeolib-0.1.3/openeolib/report.py +184 -0
  25. openeolib-0.1.3/openeolib/roc.py +400 -0
  26. openeolib-0.1.3/openeolib/simulator.py +535 -0
  27. openeolib-0.1.3/openeolib/theme.py +23 -0
  28. openeolib-0.1.3/openeolib.egg-info/PKG-INFO +299 -0
  29. openeolib-0.1.3/openeolib.egg-info/SOURCES.txt +32 -0
  30. openeolib-0.1.3/openeolib.egg-info/dependency_links.txt +1 -0
  31. openeolib-0.1.3/openeolib.egg-info/requires.txt +29 -0
  32. openeolib-0.1.3/openeolib.egg-info/top_level.txt +1 -0
  33. openeolib-0.1.3/pyproject.toml +68 -0
  34. openeolib-0.1.3/setup.cfg +4 -0
@@ -0,0 +1,15 @@
1
+ ApacheLicense2.0
2
+
3
+ Copyright (c) 2025-2026 tkxu
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
@@ -0,0 +1,299 @@
1
+ Metadata-Version: 2.4
2
+ Name: openeolib
3
+ Version: 0.1.3
4
+ Summary: Satellite-based methane emission detection and quantification using openEO
5
+ Author: tkxu
6
+ License: Apache-2.0
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: numpy>=1.24
11
+ Requires-Dist: scipy>=1.10
12
+ Requires-Dist: matplotlib>=3.7
13
+ Provides-Extra: provider
14
+ Requires-Dist: openeo>=0.26; extra == "provider"
15
+ Requires-Dist: rasterio>=1.3; extra == "provider"
16
+ Requires-Dist: xarray>=2023.1; extra == "provider"
17
+ Requires-Dist: netCDF4>=1.6; extra == "provider"
18
+ Provides-Extra: era5
19
+ Requires-Dist: cdsapi>=0.6; extra == "era5"
20
+ Requires-Dist: xarray>=2023.1; extra == "era5"
21
+ Requires-Dist: netCDF4>=1.6; extra == "era5"
22
+ Provides-Extra: jma
23
+ Requires-Dist: xarray>=2023.1; extra == "jma"
24
+ Requires-Dist: cfgrib>=0.9.10; extra == "jma"
25
+ Requires-Dist: dask>=2023.1; extra == "jma"
26
+ Requires-Dist: requests>=2.28; extra == "jma"
27
+ Requires-Dist: beautifulsoup4>=4.12; extra == "jma"
28
+ Requires-Dist: pyyaml>=6.0; extra == "jma"
29
+ Requires-Dist: JMA-grib2>=0.0.4a3; extra == "jma"
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.4; extra == "dev"
32
+ Requires-Dist: pytest-cov>=4.1; extra == "dev"
33
+ Requires-Dist: flake8>=6.1; extra == "dev"
34
+ Requires-Dist: mypy>=1.5; extra == "dev"
35
+ Dynamic: license-file
36
+
37
+ # OpenEO-LIB
38
+
39
+ OpenEO-LIB is a Python library for satellite-based Earth observation workflows, including data providers, geospatial analysis, plume simulation, and reusable visualization components.
40
+
41
+ The package is designed so that reusable library functionality is kept separate from application-specific validation and research code.
42
+
43
+ ## Current architecture
44
+
45
+ ```text
46
+ openeolib/
47
+ ├── __init__.py
48
+ ├── analyzer.py Analysis pipeline orchestration
49
+ ├── animation.py Generic scalar-field animation (GridAnimationEngine)
50
+ ├── engines.py Inference-engine interfaces and built-ins
51
+ ├── eo_cache.py Persistent scientific-data cache (ScienceCacheStore)
52
+ ├── eo_types.py Shared data structures and type definitions
53
+ ├── eo_utils.py Geospatial and wind utilities
54
+ ├── panels.py Generic Matplotlib visualization panels
55
+ ├── report.py Generic figure/report composition
56
+ ├── roc.py ROC construction
57
+ ├── simulator.py Synthetic plume generation
58
+ ├── theme.py Theme definition only (Theme, DEFAULT_THEME)
59
+ └── providers/
60
+ ├── base.py BaseProvider ABC (raw Dataset retrieval)
61
+ ├── provider.py Provider / EOProvider (S2+ERA5+S5P integration)
62
+ ├── openeo_client.py openEO connection client
63
+ ├── sentinel2.py Sentinel-2 L2A band retrieval
64
+ ├── sentinel5p.py Sentinel-5P L2 CH4 column retrieval
65
+ ├── era5.py ERA5 wind/pressure/temperature retrieval
66
+ ├── msm.py JMA MSM (mesoscale model) wind retrieval
67
+ ├── radar_gpv_client.py JMA nationwide composite radar GPV download
68
+ ├── rain.py JMA rainfall / XRAIN providers + animate_rain()
69
+ └── _s2_utils.py Sentinel-2 coordinate/time conversion utilities
70
+
71
+
72
+ ```
73
+
74
+ Provider responsibilities are deliberately separated. `BaseProvider` and `Provider`
75
+ are independent interfaces with different responsibilities; `Provider` does not
76
+ extend `BaseProvider`. `BaseProvider` is the minimal file/data-opening interface
77
+ for providers that return an `xarray.Dataset` without application-level analysis.
78
+ `Provider` is the higher-level interface used by `EOAnalyzer` and returns an
79
+ `ObservationBundle` for a site and observation time. `EOProvider` implements
80
+ `Provider` for the openEO/Sentinel-2/Sentinel-5P workflow and contains the
81
+ workflow-specific quality and time-alignment logic.
82
+
83
+ The visualization layer (`panels.py`, `theme.py`, `report.py`, `animation.py`)
84
+ is kept domain-agnostic. Domain providers may know the schema and units of
85
+ their own source data, but they do not depend on application-specific
86
+ research pipelines.
87
+
88
+ Likewise, `theme.py` contains only the `Theme` data structure and `DEFAULT_THEME`.
89
+
90
+ ## Installation
91
+
92
+ ```bash
93
+ pip install openeolib
94
+ ```
95
+
96
+ Development dependencies are optional and are intended for the private test suite:
97
+
98
+ ```bash
99
+ pip install -e ".[dev]"
100
+ ```
101
+
102
+ Optional provider dependencies are grouped by feature:
103
+
104
+ ```bash
105
+ pip install -e ".[provider]"
106
+ pip install -e ".[era5]"
107
+ pip install -e ".[jma]"
108
+ ```
109
+
110
+ The core package supports Python 3.9 or later. Provider-specific optional dependencies may have their own Python-version requirements.
111
+
112
+ The JMA extra (`openeolib[jma]`) is required for `MSMProvider`. The module
113
+ `openeolib.providers.msm` remains importable without the optional dependencies,
114
+ but constructing `MSMProvider` raises a clear `ImportError` listing the missing
115
+ dependencies and the installation command. This keeps optional JMA support from
116
+ breaking the core package import.
117
+
118
+ ## Public visualization API
119
+
120
+ The reusable visualization API consists of five panels:
121
+
122
+ - `VectorFieldPanel` — arbitrary geospatial vector fields with optional scalar contours.
123
+ - `BasemapPanel` — an already prepared RGB image with an optional marker.
124
+ - `RawBandsPanel` — arbitrary grids of 2D images with a shared scale.
125
+ - `ScalarMapPanel` — arbitrary 2D scalar fields.
126
+ - `DetectionMaskPanel` — boolean, probability, or coverage masks.
127
+
128
+ Example:
129
+
130
+ ```python
131
+ import matplotlib.pyplot as plt
132
+ import numpy as np
133
+ from openeolib import ScalarMapPanel
134
+
135
+ field = np.random.default_rng(0).normal(size=(100, 100))
136
+
137
+ fig = plt.figure(figsize=(6, 5))
138
+ gs = fig.add_gridspec(1, 1)[0]
139
+ ScalarMapPanel().draw(
140
+ fig,
141
+ gs,
142
+ field,
143
+ title="Scalar field",
144
+ axis_mode="none",
145
+ )
146
+ fig.savefig("scalar_field.png", dpi=150, bbox_inches="tight")
147
+ ```
148
+
149
+ ### Vector fields
150
+
151
+ `VectorFieldPanel` is intentionally not ERA5-specific. It accepts latitude/longitude grids and arbitrary vector components. A caller may supply any bounding box through `extent=(west, east, south, north)`.
152
+
153
+ ```python
154
+ from openeolib import VectorFieldPanel
155
+
156
+ panel = VectorFieldPanel(quiver_stride=3)
157
+ panel.draw(
158
+ fig,
159
+ gs,
160
+ lats=lats,
161
+ lons=lons,
162
+ u=u,
163
+ v=v,
164
+ extent=(120, 150, 20, 50),
165
+ title="Wind field",
166
+ )
167
+ ```
168
+
169
+ There is no Japan-specific bounding-box constant in the library.
170
+
171
+ ### Theme
172
+
173
+ Use `Theme` when a caller needs to customize the visual appearance:
174
+
175
+ ```python
176
+ from openeolib import Theme
177
+
178
+ light = Theme(
179
+ bg="#ffffff",
180
+ panel_bg="#ffffff",
181
+ grid_color="#cccccc",
182
+ text_primary="#222222",
183
+ )
184
+ ```
185
+
186
+ `Theme` does not contain plotting operations or application-specific labels, flags, or data extraction rules.
187
+
188
+ ## Generic reports
189
+
190
+ `SiteReportBuilder` composes caller-supplied panels into a single-site report Figure. It does not know about methane, ROC curves, quality flags, or a particular inference engine -- it only handles grid layout, the report title, and file saving.
191
+
192
+ Each entry in `panels` is `(label, factory, height_ratio)`. `factory` is called once with the report's `Theme` and must return a `(fig, gs) -> None` draw function; any panel-specific data (the field to plot, its title, ...) is bound into that closure by the caller, not inspected by `SiteReportBuilder` itself.
193
+
194
+ ```python
195
+ import numpy as np
196
+ from openeolib import SiteReportBuilder, ScalarMapPanel
197
+
198
+ field = np.random.default_rng(0).normal(size=(100, 100))
199
+
200
+ report = SiteReportBuilder(
201
+ panels=[
202
+ (
203
+ "Scalar field",
204
+ lambda theme: lambda fig, gs: ScalarMapPanel(theme=theme).draw(
205
+ fig, gs, field, title="Scalar field", axis_mode="none",
206
+ ),
207
+ 1.0,
208
+ ),
209
+ ],
210
+ report_title="Example report",
211
+ )
212
+
213
+ report.build_site({"site": {"id": "SITE-01"}}, save_path="report.png")
214
+ ```
215
+
216
+ Application-specific validation reports can be built with the components under `examples/validation_panels.py` and `examples/validation_report.py` without adding those domain concepts to the reusable package.
217
+
218
+ ## Analysis and simulation
219
+
220
+ The main public analysis components include:
221
+
222
+ ```python
223
+ from openeolib import EOAnalyzer, PlumeSimulator, InferenceEngine
224
+ ```
225
+
226
+ `EOAnalyzer` coordinates provider data and an inference engine. `PlumeSimulator` provides synthetic plume data for testing and demonstrations. `InferenceEngine` defines the interface for custom detection/quantification algorithms.
227
+
228
+ ## Providers
229
+
230
+ The package contains provider implementations for openEO, Sentinel-2, Sentinel-5P, ERA5, MSM, and JMA radar rainfall products. Provider-specific dependencies are optional where practical.
231
+
232
+ `XrainProvider` accepts NetCDF and CSV input. If a CSV has no time column, it
233
+ creates a single `time` coordinate containing `NaT` rather than inventing an
234
+ observation timestamp. Callers that require a real observation time must provide
235
+ a time column or pass `time_name=`. This distinction is intentional and prevents
236
+ silent fabrication of temporal metadata.
237
+
238
+ `RadarGpvClient` is a public retrieval utility, not a `BaseProvider` or `Provider`.
239
+ It resolves JMA nationwide composite radar GPV archive URLs, downloads the archive,
240
+ and extracts the target GRIB2 file. `JmaRainProvider` is the dataset-opening provider
241
+ that parses those extracted files into an `xarray.Dataset`. This separation keeps
242
+ network/archive handling distinct from dataset parsing.
243
+
244
+ `ERA5Provider` supports surface fields and pressure-level wind processing, including height-aware interpolation for wind products. Provider modules return data; visualization remains a separate concern.
245
+
246
+ `MSMProvider` retrieves JMA MSM GPV wind data and requires the `jma` extra. The
247
+ public facade exposes `MSMProvider`, `MSM_AVAILABLE`, and
248
+ `MSM_MISSING_DEPENDENCIES` so applications can detect optional support without
249
+ catching an import failure from the core package.
250
+
251
+ For ERA5, CDS credentials and the current CDS API/client configuration are required for live retrieval. Unit tests mock retrieval where network access is unnecessary.
252
+
253
+ ## Animation
254
+
255
+ `GridAnimationEngine` in `openeolib.animation` is the generic animation component. Domain-specific wrappers, such as `animate_rain()` in `providers.rain`, configure rainfall-specific variables and color scales before delegating rendering to the generic engine.
256
+
257
+ ```python
258
+ from openeolib.animation import GridAnimationEngine
259
+
260
+ engine = GridAnimationEngine()
261
+ engine.animate_scalar_field(
262
+ grids=grids,
263
+ lats=lats,
264
+ lons=lons,
265
+ timestamps=timestamps,
266
+ output_path="animation.gif",
267
+ )
268
+ ```
269
+
270
+ ## Validation examples
271
+
272
+ Validation-only components are kept outside the package:
273
+
274
+ ```python
275
+ from examples.validation_panels import (
276
+ SpectralPanel,
277
+ StatisticalPanel,
278
+ FlagsPanel,
279
+ )
280
+ from examples.validation_report import ValidationReportBuilder
281
+ ```
282
+
283
+ These modules are useful for project-specific evaluation but are not exported from `openeolib` and should not be treated as stable library APIs.
284
+
285
+ ## Testing
286
+
287
+ Run the test suite with:
288
+
289
+ ```bash
290
+ pytest
291
+ ```
292
+
293
+ The suite is designed to avoid network access for ordinary unit tests. Tests that require optional provider dependencies are skipped when those dependencies are unavailable.
294
+
295
+ The current test suite covers analysis, engines, caching, utilities, ERA5 behavior, MSM behavior, radar GPV handling, rainfall providers, ROC construction, simulation, animation, and reusable visualization panels.
296
+
297
+ ## License
298
+
299
+ OpenEO-LIB is distributed under the Apache License 2.0. See `LICENSE` for the full license text.
@@ -0,0 +1,263 @@
1
+ # OpenEO-LIB
2
+
3
+ OpenEO-LIB is a Python library for satellite-based Earth observation workflows, including data providers, geospatial analysis, plume simulation, and reusable visualization components.
4
+
5
+ The package is designed so that reusable library functionality is kept separate from application-specific validation and research code.
6
+
7
+ ## Current architecture
8
+
9
+ ```text
10
+ openeolib/
11
+ ├── __init__.py
12
+ ├── analyzer.py Analysis pipeline orchestration
13
+ ├── animation.py Generic scalar-field animation (GridAnimationEngine)
14
+ ├── engines.py Inference-engine interfaces and built-ins
15
+ ├── eo_cache.py Persistent scientific-data cache (ScienceCacheStore)
16
+ ├── eo_types.py Shared data structures and type definitions
17
+ ├── eo_utils.py Geospatial and wind utilities
18
+ ├── panels.py Generic Matplotlib visualization panels
19
+ ├── report.py Generic figure/report composition
20
+ ├── roc.py ROC construction
21
+ ├── simulator.py Synthetic plume generation
22
+ ├── theme.py Theme definition only (Theme, DEFAULT_THEME)
23
+ └── providers/
24
+ ├── base.py BaseProvider ABC (raw Dataset retrieval)
25
+ ├── provider.py Provider / EOProvider (S2+ERA5+S5P integration)
26
+ ├── openeo_client.py openEO connection client
27
+ ├── sentinel2.py Sentinel-2 L2A band retrieval
28
+ ├── sentinel5p.py Sentinel-5P L2 CH4 column retrieval
29
+ ├── era5.py ERA5 wind/pressure/temperature retrieval
30
+ ├── msm.py JMA MSM (mesoscale model) wind retrieval
31
+ ├── radar_gpv_client.py JMA nationwide composite radar GPV download
32
+ ├── rain.py JMA rainfall / XRAIN providers + animate_rain()
33
+ └── _s2_utils.py Sentinel-2 coordinate/time conversion utilities
34
+
35
+
36
+ ```
37
+
38
+ Provider responsibilities are deliberately separated. `BaseProvider` and `Provider`
39
+ are independent interfaces with different responsibilities; `Provider` does not
40
+ extend `BaseProvider`. `BaseProvider` is the minimal file/data-opening interface
41
+ for providers that return an `xarray.Dataset` without application-level analysis.
42
+ `Provider` is the higher-level interface used by `EOAnalyzer` and returns an
43
+ `ObservationBundle` for a site and observation time. `EOProvider` implements
44
+ `Provider` for the openEO/Sentinel-2/Sentinel-5P workflow and contains the
45
+ workflow-specific quality and time-alignment logic.
46
+
47
+ The visualization layer (`panels.py`, `theme.py`, `report.py`, `animation.py`)
48
+ is kept domain-agnostic. Domain providers may know the schema and units of
49
+ their own source data, but they do not depend on application-specific
50
+ research pipelines.
51
+
52
+ Likewise, `theme.py` contains only the `Theme` data structure and `DEFAULT_THEME`.
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install openeolib
58
+ ```
59
+
60
+ Development dependencies are optional and are intended for the private test suite:
61
+
62
+ ```bash
63
+ pip install -e ".[dev]"
64
+ ```
65
+
66
+ Optional provider dependencies are grouped by feature:
67
+
68
+ ```bash
69
+ pip install -e ".[provider]"
70
+ pip install -e ".[era5]"
71
+ pip install -e ".[jma]"
72
+ ```
73
+
74
+ The core package supports Python 3.9 or later. Provider-specific optional dependencies may have their own Python-version requirements.
75
+
76
+ The JMA extra (`openeolib[jma]`) is required for `MSMProvider`. The module
77
+ `openeolib.providers.msm` remains importable without the optional dependencies,
78
+ but constructing `MSMProvider` raises a clear `ImportError` listing the missing
79
+ dependencies and the installation command. This keeps optional JMA support from
80
+ breaking the core package import.
81
+
82
+ ## Public visualization API
83
+
84
+ The reusable visualization API consists of five panels:
85
+
86
+ - `VectorFieldPanel` — arbitrary geospatial vector fields with optional scalar contours.
87
+ - `BasemapPanel` — an already prepared RGB image with an optional marker.
88
+ - `RawBandsPanel` — arbitrary grids of 2D images with a shared scale.
89
+ - `ScalarMapPanel` — arbitrary 2D scalar fields.
90
+ - `DetectionMaskPanel` — boolean, probability, or coverage masks.
91
+
92
+ Example:
93
+
94
+ ```python
95
+ import matplotlib.pyplot as plt
96
+ import numpy as np
97
+ from openeolib import ScalarMapPanel
98
+
99
+ field = np.random.default_rng(0).normal(size=(100, 100))
100
+
101
+ fig = plt.figure(figsize=(6, 5))
102
+ gs = fig.add_gridspec(1, 1)[0]
103
+ ScalarMapPanel().draw(
104
+ fig,
105
+ gs,
106
+ field,
107
+ title="Scalar field",
108
+ axis_mode="none",
109
+ )
110
+ fig.savefig("scalar_field.png", dpi=150, bbox_inches="tight")
111
+ ```
112
+
113
+ ### Vector fields
114
+
115
+ `VectorFieldPanel` is intentionally not ERA5-specific. It accepts latitude/longitude grids and arbitrary vector components. A caller may supply any bounding box through `extent=(west, east, south, north)`.
116
+
117
+ ```python
118
+ from openeolib import VectorFieldPanel
119
+
120
+ panel = VectorFieldPanel(quiver_stride=3)
121
+ panel.draw(
122
+ fig,
123
+ gs,
124
+ lats=lats,
125
+ lons=lons,
126
+ u=u,
127
+ v=v,
128
+ extent=(120, 150, 20, 50),
129
+ title="Wind field",
130
+ )
131
+ ```
132
+
133
+ There is no Japan-specific bounding-box constant in the library.
134
+
135
+ ### Theme
136
+
137
+ Use `Theme` when a caller needs to customize the visual appearance:
138
+
139
+ ```python
140
+ from openeolib import Theme
141
+
142
+ light = Theme(
143
+ bg="#ffffff",
144
+ panel_bg="#ffffff",
145
+ grid_color="#cccccc",
146
+ text_primary="#222222",
147
+ )
148
+ ```
149
+
150
+ `Theme` does not contain plotting operations or application-specific labels, flags, or data extraction rules.
151
+
152
+ ## Generic reports
153
+
154
+ `SiteReportBuilder` composes caller-supplied panels into a single-site report Figure. It does not know about methane, ROC curves, quality flags, or a particular inference engine -- it only handles grid layout, the report title, and file saving.
155
+
156
+ Each entry in `panels` is `(label, factory, height_ratio)`. `factory` is called once with the report's `Theme` and must return a `(fig, gs) -> None` draw function; any panel-specific data (the field to plot, its title, ...) is bound into that closure by the caller, not inspected by `SiteReportBuilder` itself.
157
+
158
+ ```python
159
+ import numpy as np
160
+ from openeolib import SiteReportBuilder, ScalarMapPanel
161
+
162
+ field = np.random.default_rng(0).normal(size=(100, 100))
163
+
164
+ report = SiteReportBuilder(
165
+ panels=[
166
+ (
167
+ "Scalar field",
168
+ lambda theme: lambda fig, gs: ScalarMapPanel(theme=theme).draw(
169
+ fig, gs, field, title="Scalar field", axis_mode="none",
170
+ ),
171
+ 1.0,
172
+ ),
173
+ ],
174
+ report_title="Example report",
175
+ )
176
+
177
+ report.build_site({"site": {"id": "SITE-01"}}, save_path="report.png")
178
+ ```
179
+
180
+ Application-specific validation reports can be built with the components under `examples/validation_panels.py` and `examples/validation_report.py` without adding those domain concepts to the reusable package.
181
+
182
+ ## Analysis and simulation
183
+
184
+ The main public analysis components include:
185
+
186
+ ```python
187
+ from openeolib import EOAnalyzer, PlumeSimulator, InferenceEngine
188
+ ```
189
+
190
+ `EOAnalyzer` coordinates provider data and an inference engine. `PlumeSimulator` provides synthetic plume data for testing and demonstrations. `InferenceEngine` defines the interface for custom detection/quantification algorithms.
191
+
192
+ ## Providers
193
+
194
+ The package contains provider implementations for openEO, Sentinel-2, Sentinel-5P, ERA5, MSM, and JMA radar rainfall products. Provider-specific dependencies are optional where practical.
195
+
196
+ `XrainProvider` accepts NetCDF and CSV input. If a CSV has no time column, it
197
+ creates a single `time` coordinate containing `NaT` rather than inventing an
198
+ observation timestamp. Callers that require a real observation time must provide
199
+ a time column or pass `time_name=`. This distinction is intentional and prevents
200
+ silent fabrication of temporal metadata.
201
+
202
+ `RadarGpvClient` is a public retrieval utility, not a `BaseProvider` or `Provider`.
203
+ It resolves JMA nationwide composite radar GPV archive URLs, downloads the archive,
204
+ and extracts the target GRIB2 file. `JmaRainProvider` is the dataset-opening provider
205
+ that parses those extracted files into an `xarray.Dataset`. This separation keeps
206
+ network/archive handling distinct from dataset parsing.
207
+
208
+ `ERA5Provider` supports surface fields and pressure-level wind processing, including height-aware interpolation for wind products. Provider modules return data; visualization remains a separate concern.
209
+
210
+ `MSMProvider` retrieves JMA MSM GPV wind data and requires the `jma` extra. The
211
+ public facade exposes `MSMProvider`, `MSM_AVAILABLE`, and
212
+ `MSM_MISSING_DEPENDENCIES` so applications can detect optional support without
213
+ catching an import failure from the core package.
214
+
215
+ For ERA5, CDS credentials and the current CDS API/client configuration are required for live retrieval. Unit tests mock retrieval where network access is unnecessary.
216
+
217
+ ## Animation
218
+
219
+ `GridAnimationEngine` in `openeolib.animation` is the generic animation component. Domain-specific wrappers, such as `animate_rain()` in `providers.rain`, configure rainfall-specific variables and color scales before delegating rendering to the generic engine.
220
+
221
+ ```python
222
+ from openeolib.animation import GridAnimationEngine
223
+
224
+ engine = GridAnimationEngine()
225
+ engine.animate_scalar_field(
226
+ grids=grids,
227
+ lats=lats,
228
+ lons=lons,
229
+ timestamps=timestamps,
230
+ output_path="animation.gif",
231
+ )
232
+ ```
233
+
234
+ ## Validation examples
235
+
236
+ Validation-only components are kept outside the package:
237
+
238
+ ```python
239
+ from examples.validation_panels import (
240
+ SpectralPanel,
241
+ StatisticalPanel,
242
+ FlagsPanel,
243
+ )
244
+ from examples.validation_report import ValidationReportBuilder
245
+ ```
246
+
247
+ These modules are useful for project-specific evaluation but are not exported from `openeolib` and should not be treated as stable library APIs.
248
+
249
+ ## Testing
250
+
251
+ Run the test suite with:
252
+
253
+ ```bash
254
+ pytest
255
+ ```
256
+
257
+ The suite is designed to avoid network access for ordinary unit tests. Tests that require optional provider dependencies are skipped when those dependencies are unavailable.
258
+
259
+ The current test suite covers analysis, engines, caching, utilities, ERA5 behavior, MSM behavior, radar GPV handling, rainfall providers, ROC construction, simulation, animation, and reusable visualization panels.
260
+
261
+ ## License
262
+
263
+ OpenEO-LIB is distributed under the Apache License 2.0. See `LICENSE` for the full license text.