canvodpy 0.2.0__py3-none-any.whl

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.
canvodpy/.DS_Store ADDED
Binary file
canvodpy/__init__.py ADDED
@@ -0,0 +1,276 @@
1
+ """canvodpy: GNSS Vegetation Optical Depth Analysis.
2
+
3
+ A modern Python package for processing GNSS data and calculating
4
+ vegetation optical depth (VOD) using the tau-omega model.
5
+
6
+ Quick Start
7
+ -----------
8
+ Three levels of API to match your needs:
9
+
10
+ **Level 1: Simple (one-liners)**
11
+ >>> from canvodpy import process_date, calculate_vod
12
+ >>> data = process_date("Rosalia", "2025001")
13
+ >>> vod = calculate_vod("Rosalia", "canopy_01", "reference_01", "2025001")
14
+
15
+ **Level 2: Object-oriented (more control)**
16
+ >>> from canvodpy import Site, Pipeline
17
+ >>> site = Site("Rosalia")
18
+ >>> pipeline = site.pipeline()
19
+ >>> data = pipeline.process_date("2025001")
20
+ >>> vod = pipeline.calculate_vod("canopy_01", "reference_01", "2025001")
21
+
22
+ **Level 3: Low-level (full control)**
23
+ >>> from canvod.store import GnssResearchSite
24
+ >>> from canvod.vod import VODCalculator
25
+ >>> # Direct access to all internals
26
+
27
+ Community Extensions
28
+ --------------------
29
+ Extend canvodpy with custom components using factories:
30
+
31
+ >>> from canvodpy import VODFactory
32
+ >>> from my_package import CustomCalculator
33
+ >>> VODFactory.register("custom", CustomCalculator)
34
+ >>> calc = VODFactory.create("custom", **params)
35
+
36
+ Package Structure
37
+ -----------------
38
+ - canvod.readers - RINEX file parsing
39
+ - canvod.auxiliary - Auxiliary data (ephemeris, clocks)
40
+ - canvod.grids - Hemisphere grid structures
41
+ - canvod.vod - VOD calculation algorithms
42
+ - canvod.viz - 2D/3D visualization
43
+ - canvod.store - Icechunk data storage
44
+
45
+ Configuration
46
+ -------------
47
+ Site configurations are stored in ``config/sites.yaml``.
48
+ Default variables and settings are in `globals.py`.
49
+
50
+ Examples
51
+ --------
52
+ Process one day of data:
53
+ >>> from canvodpy import process_date
54
+ >>> data = process_date("Rosalia", "2025001")
55
+
56
+ Process a week:
57
+ >>> from canvodpy import Pipeline
58
+ >>> pipeline = Pipeline("Rosalia")
59
+ >>> for date, datasets in pipeline.process_range("2025001", "2025007"):
60
+ ... print(f"Processed {date}")
61
+
62
+ Calculate and visualize VOD:
63
+ >>> from canvodpy import calculate_vod
64
+ >>> from canvod.viz import HemisphereVisualizer
65
+ >>> vod = calculate_vod("Rosalia", "canopy_01", "reference_01", "2025001")
66
+ >>> viz = HemisphereVisualizer()
67
+ >>> fig = viz.plot_2d(vod)
68
+
69
+ """
70
+
71
+ from canvodpy.api import (
72
+ Pipeline,
73
+ Site,
74
+ calculate_vod,
75
+ preview_processing,
76
+ process_date,
77
+ )
78
+
79
+ # Factories (for community extensions)
80
+ from canvodpy.factories import (
81
+ AugmentationFactory,
82
+ GridFactory,
83
+ ReaderFactory,
84
+ VODFactory,
85
+ )
86
+
87
+ # Fluent workflow API (deferred execution)
88
+ from canvodpy.fluent import FluentWorkflow
89
+
90
+ # Functional API (Airflow-compatible)
91
+ from canvodpy.functional import (
92
+ assign_grid_cells,
93
+ assign_grid_cells_to_file,
94
+ augment_with_ephemeris,
95
+ calculate_vod_to_file,
96
+ create_grid,
97
+ create_grid_to_file,
98
+ read_rinex,
99
+ read_rinex_to_file,
100
+ )
101
+
102
+ # Logging (for all users)
103
+ from canvodpy.logging import get_logger, setup_logging
104
+
105
+ # VOD computation helper
106
+ from canvodpy.vod_computer import VodComputer
107
+
108
+ # New workflow API
109
+ from canvodpy.workflow import VODWorkflow
110
+
111
+
112
+ def workflow(site: str, **kwargs) -> FluentWorkflow:
113
+ """Create a fluent workflow with deferred execution.
114
+
115
+ Parameters
116
+ ----------
117
+ site : str
118
+ Site name (e.g. ``"Rosalia"``).
119
+ **kwargs
120
+ Forwarded to :class:`FluentWorkflow`.
121
+
122
+ Returns
123
+ -------
124
+ FluentWorkflow
125
+
126
+ Examples
127
+ --------
128
+ >>> import canvodpy
129
+ >>> result = (canvodpy.workflow("Rosalia")
130
+ ... .read("2025001")
131
+ ... .preprocess()
132
+ ... .grid()
133
+ ... .vod("canopy_01", "reference_01")
134
+ ... .result())
135
+ """
136
+ return FluentWorkflow(site, **kwargs)
137
+
138
+
139
+ # ============================================================================
140
+ # Level 3 API: Re-export subpackages for advanced users
141
+ # ============================================================================
142
+
143
+
144
+ # Lazy import subpackages on access to avoid circular dependencies
145
+ def __getattr__(name: str):
146
+ """Lazy import subpackages when accessed."""
147
+ _subpackages = {
148
+ "auxiliary": "canvod.auxiliary",
149
+ "grids": "canvod.grids",
150
+ "readers": "canvod.readers",
151
+ "store": "canvod.store",
152
+ "viz": "canvod.viz",
153
+ "vod": "canvod.vod",
154
+ }
155
+
156
+ if name in _subpackages:
157
+ import importlib
158
+ import sys
159
+
160
+ module = importlib.import_module(_subpackages[name])
161
+ # Cache the imported module (can't use globals() as it's shadowed by canvodpy.globals)
162
+ setattr(sys.modules[__name__], name, module)
163
+ return module
164
+
165
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
166
+
167
+
168
+ # ============================================================================
169
+ # Version
170
+ # ============================================================================
171
+
172
+ __version__ = "0.1.0"
173
+
174
+ # ============================================================================
175
+ # Public API
176
+ # ============================================================================
177
+
178
+ __all__ = [ # noqa: RUF022
179
+ # Version
180
+ "__version__",
181
+ # High-level API (most users)
182
+ "Site",
183
+ "Pipeline",
184
+ "process_date",
185
+ "calculate_vod",
186
+ "preview_processing",
187
+ # New workflow API
188
+ "VODWorkflow",
189
+ # Fluent workflow API
190
+ "FluentWorkflow",
191
+ "workflow",
192
+ # Functional API (notebooks & Airflow)
193
+ "read_rinex",
194
+ "read_rinex_to_file",
195
+ "create_grid",
196
+ "create_grid_to_file",
197
+ "assign_grid_cells",
198
+ "assign_grid_cells_to_file",
199
+ "calculate_vod_to_file",
200
+ # Logging
201
+ "setup_logging",
202
+ "get_logger",
203
+ # Factories (community extensions)
204
+ "ReaderFactory",
205
+ "GridFactory",
206
+ "VODFactory",
207
+ "AugmentationFactory",
208
+ # Subpackages (advanced users)
209
+ "readers",
210
+ "aux",
211
+ "grids",
212
+ "vod",
213
+ "viz",
214
+ "store",
215
+ ]
216
+
217
+
218
+ # ============================================================================
219
+ # Auto-register built-in components
220
+ # ============================================================================
221
+
222
+
223
+ def _register_builtin_components() -> None:
224
+ """
225
+ Register built-in component implementations.
226
+
227
+ Called automatically on package import. Registers:
228
+ - Rnxv3Obs reader (rinex3)
229
+ - EqualAreaGridBuilder (equal_area)
230
+ - TauOmegaZerothOrder calculator (tau_omega)
231
+
232
+ Notes
233
+ -----
234
+ Uses lazy imports to avoid loading heavy dependencies unless needed.
235
+ """
236
+ log = get_logger(__name__)
237
+
238
+ # Set ABC classes for validation
239
+ ReaderFactory._set_abc_class()
240
+ GridFactory._set_abc_class()
241
+ VODFactory._set_abc_class()
242
+ AugmentationFactory._set_abc_class()
243
+
244
+ try:
245
+ from canvod.readers import Rnxv3Obs
246
+
247
+ ReaderFactory.register("rinex3", Rnxv3Obs)
248
+ except ImportError:
249
+ log.debug("canvod-readers not available, skipping reader registration")
250
+
251
+ try:
252
+ from canvod.readers.sbf import SbfReader
253
+
254
+ ReaderFactory.register("sbf", SbfReader)
255
+ except ImportError:
256
+ log.debug("SbfReader not available, skipping sbf reader registration")
257
+
258
+ try:
259
+ from canvod.grids import EqualAreaBuilder
260
+
261
+ GridFactory.register("equal_area", EqualAreaBuilder)
262
+ except ImportError:
263
+ log.debug("canvod-grids not available, skipping grid registration")
264
+
265
+ try:
266
+ from canvod.vod.calculator import TauOmegaZerothOrder
267
+
268
+ VODFactory.register("tau_omega", TauOmegaZerothOrder)
269
+ except ImportError:
270
+ log.debug("canvod-vod not available, skipping VOD registration")
271
+
272
+ log.info("builtin_components_registered")
273
+
274
+
275
+ # Auto-register on import
276
+ _register_builtin_components()