chronozarr 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.
- chronozarr-0.2.0/.gitignore +49 -0
- chronozarr-0.2.0/LICENSE +202 -0
- chronozarr-0.2.0/PKG-INFO +255 -0
- chronozarr-0.2.0/README.md +207 -0
- chronozarr-0.2.0/deploy/README.md +43 -0
- chronozarr-0.2.0/examples/sentinel2_pc/README.md +81 -0
- chronozarr-0.2.0/js/LICENSE +202 -0
- chronozarr-0.2.0/js/README.md +105 -0
- chronozarr-0.2.0/js/maplibre/README.md +112 -0
- chronozarr-0.2.0/js/vendor/numcodecs/LICENSE +21 -0
- chronozarr-0.2.0/js/vendor/zarrita/LICENSE +21 -0
- chronozarr-0.2.0/js/vendor/zarrita-storage/LICENSE +21 -0
- chronozarr-0.2.0/pyproject.toml +99 -0
- chronozarr-0.2.0/spec/CHANGES-0.2.md +1 -0
- chronozarr-0.2.0/spec/CHRONOZARR.md +602 -0
- chronozarr-0.2.0/src/chronozarr/__init__.py +21 -0
- chronozarr-0.2.0/src/chronozarr/append.py +642 -0
- chronozarr-0.2.0/src/chronozarr/backend.py +191 -0
- chronozarr-0.2.0/src/chronozarr/cli.py +663 -0
- chronozarr-0.2.0/src/chronozarr/convert.py +1716 -0
- chronozarr-0.2.0/src/chronozarr/decode.py +403 -0
- chronozarr-0.2.0/src/chronozarr/doctor.py +573 -0
- chronozarr-0.2.0/src/chronozarr/encode.py +1371 -0
- chronozarr-0.2.0/src/chronozarr/export.py +221 -0
- chronozarr-0.2.0/src/chronozarr/schema.py +1158 -0
- chronozarr-0.2.0/src/chronozarr/stac.py +309 -0
- chronozarr-0.2.0/src/chronozarr/view.py +215 -0
- chronozarr-0.2.0/tests/__init__.py +0 -0
- chronozarr-0.2.0/tests/fixtures/__init__.py +0 -0
- chronozarr-0.2.0/tests/fixtures/cog_sources.py +393 -0
- chronozarr-0.2.0/tests/fixtures/png_frames.py +152 -0
- chronozarr-0.2.0/tests/synthetic.py +170 -0
- chronozarr-0.2.0/tests/test_append.py +671 -0
- chronozarr-0.2.0/tests/test_cli.py +371 -0
- chronozarr-0.2.0/tests/test_clipping.py +82 -0
- chronozarr-0.2.0/tests/test_convert.py +1281 -0
- chronozarr-0.2.0/tests/test_doctor.py +490 -0
- chronozarr-0.2.0/tests/test_export.py +373 -0
- chronozarr-0.2.0/tests/test_format_backend.py +364 -0
- chronozarr-0.2.0/tests/test_format_encoding.py +198 -0
- chronozarr-0.2.0/tests/test_format_layout.py +542 -0
- chronozarr-0.2.0/tests/test_format_profiles.py +415 -0
- chronozarr-0.2.0/tests/test_format_stream.py +249 -0
- chronozarr-0.2.0/tests/test_reads.py +186 -0
- chronozarr-0.2.0/tests/test_roundtrip.py +173 -0
- chronozarr-0.2.0/tests/test_schema.py +375 -0
- chronozarr-0.2.0/tests/test_stac.py +171 -0
- chronozarr-0.2.0/tests/test_xarray_compat.py +82 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Data artifacts (large, regenerable)
|
|
2
|
+
data/
|
|
3
|
+
*.tif
|
|
4
|
+
*.tiff
|
|
5
|
+
*.zarr/
|
|
6
|
+
|
|
7
|
+
# Python
|
|
8
|
+
__pycache__/
|
|
9
|
+
*.pyc
|
|
10
|
+
*.pyo
|
|
11
|
+
*.egg-info/
|
|
12
|
+
dist/
|
|
13
|
+
build/
|
|
14
|
+
.venv
|
|
15
|
+
|
|
16
|
+
# Environment
|
|
17
|
+
.env
|
|
18
|
+
.env.*
|
|
19
|
+
|
|
20
|
+
# IDE
|
|
21
|
+
.vscode/
|
|
22
|
+
.idea/
|
|
23
|
+
|
|
24
|
+
# OS
|
|
25
|
+
.DS_Store
|
|
26
|
+
Thumbs.db
|
|
27
|
+
|
|
28
|
+
# Reports (regenerable)
|
|
29
|
+
reports/
|
|
30
|
+
|
|
31
|
+
# Handover
|
|
32
|
+
HANDOVER.md
|
|
33
|
+
|
|
34
|
+
# Napkin
|
|
35
|
+
.napkin.md
|
|
36
|
+
|
|
37
|
+
# dgov / sentrux
|
|
38
|
+
.dgov/
|
|
39
|
+
.sentrux/
|
|
40
|
+
|
|
41
|
+
# Test/bench artifacts
|
|
42
|
+
.playwright-mcp/
|
|
43
|
+
|
|
44
|
+
# Screenshots (development artifacts)
|
|
45
|
+
*.png
|
|
46
|
+
HANDOVER.md
|
|
47
|
+
|
|
48
|
+
# root tooling (wrangler pinned in package.json)
|
|
49
|
+
/node_modules/
|
chronozarr-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: chronozarr
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Zarr v3 convention and reader for raster time series with optional star-delta temporal encoding
|
|
5
|
+
Project-URL: Homepage, https://tileripper.com
|
|
6
|
+
Project-URL: Documentation, https://github.com/chronozarr/chronozarr/blob/main/spec/CHRONOZARR.md
|
|
7
|
+
Project-URL: Repository, https://github.com/chronozarr/chronozarr
|
|
8
|
+
Project-URL: Issues, https://github.com/chronozarr/chronozarr/issues
|
|
9
|
+
Author: James H. Gearon
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: cloud-native,earth observation,raster,remote sensing,sentinel-2,time series,xarray,zarr
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: click>=8.1
|
|
25
|
+
Requires-Dist: numpy>=1.26
|
|
26
|
+
Requires-Dist: xarray>=2024.1
|
|
27
|
+
Requires-Dist: zarr>=3.1
|
|
28
|
+
Provides-Extra: dask
|
|
29
|
+
Requires-Dist: dask>=2024.1; extra == 'dask'
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
33
|
+
Requires-Dist: ty>=0.0.23; extra == 'dev'
|
|
34
|
+
Provides-Extra: geo
|
|
35
|
+
Requires-Dist: rasterio>=1.3; extra == 'geo'
|
|
36
|
+
Provides-Extra: ingest
|
|
37
|
+
Requires-Dist: planetary-computer>=1.0; extra == 'ingest'
|
|
38
|
+
Requires-Dist: pystac-client>=0.8; extra == 'ingest'
|
|
39
|
+
Requires-Dist: pystac>=1.10; extra == 'ingest'
|
|
40
|
+
Requires-Dist: pyyaml>=6.0; extra == 'ingest'
|
|
41
|
+
Requires-Dist: rasterio>=1.3; extra == 'ingest'
|
|
42
|
+
Provides-Extra: netcdf
|
|
43
|
+
Requires-Dist: h5netcdf>=1.3; extra == 'netcdf'
|
|
44
|
+
Requires-Dist: h5py>=3.10; extra == 'netcdf'
|
|
45
|
+
Provides-Extra: notebook
|
|
46
|
+
Requires-Dist: ipython>=8; extra == 'notebook'
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
# TileRipper
|
|
50
|
+
|
|
51
|
+
Open a decade of analysis-ready satellite time series in a browser tab from a static bucket. Scrub it like video. Click for real numbers.
|
|
52
|
+
|
|
53
|
+
TileRipper is the viewer. **chronozarr** is the format underneath it: plain Zarr v3 with one group per pyramid level and one object per chunk, so a timestep of a spatial cell is one plain `GET`. Sharding, one file per cell for the time axis, is an option (`--shard`). The layout follows ndpyramid's `multiscales` attribute and the zarr `proj` and `spatial` conventions. An optional temporal profile, star-delta, stores most timesteps as residuals against a nearby anchor; the writer measures a sample of cells and enables it only when it shrinks the compressed bytes to 0.85 of the plain size or better. A store without it needs no chronozarr-aware reader.
|
|
54
|
+
|
|
55
|
+
Reading a store without chronozarr:
|
|
56
|
+
|
|
57
|
+
- xarray and other zarr-python 3 or zarrita clients open both layouts, unsharded (the default) and sharded (`tests/test_xarray_compat.py`). With chronozarr installed, `xr.open_dataset(path_or_url, engine="chronozarr")` also reconstructs star-delta timesteps lazily and returns float32 physical values (`stored * scale + offset`, NaN where invalid); `physical=False` returns the stored values.
|
|
58
|
+
- GDAL reads the layout through its Zarr driver. Stores without sharding (the default) open in GDAL 3.12 as a raster of time-major bands; sharded stores need GDAL 3.13 or newer, where the driver documents sharding support ([GDAL Zarr driver](https://gdal.org/en/stable/drivers/raster/zarr.html)). The writer emits the `_CRS` array attribute that GDAL reads, so GDAL assigns the CRS of an EPSG store. In a star-delta store GDAL sees residuals for non-anchor timesteps. `chronozarr export-cog STORE OUT_DIR` writes true-value Cloud Optimized GeoTIFFs for GDAL and QGIS from any store, including for GDAL versions that cannot read the store directly (extra `geo`).
|
|
59
|
+
- CarbonPlan zarr-layer reads the ndpyramid `multiscales` layout. A store without the temporal profile that was written after `pixels_per_tile` was dropped from `multiscales` (spec section 13) opens in zarr-layer unmodified (verified on the Ucayali store at level 1, point values equal to a direct Zarr read); a store written earlier opens with zarr-layer's `crs` and `bounds` constructor options; a star-delta store needs an adapter that reconstructs the residuals.
|
|
60
|
+
|
|
61
|
+
Spec: [spec/CHRONOZARR.md](https://github.com/chronozarr/chronozarr/blob/main/spec/CHRONOZARR.md) (v0.2, draft). Hosting: [docs/hosting.md](https://github.com/chronozarr/chronozarr/blob/main/docs/hosting.md). Growing a store: [docs/append.md](https://github.com/chronozarr/chronozarr/blob/main/docs/append.md). Embedding the viewer: [docs/embedding.md](https://github.com/chronozarr/chronozarr/blob/main/docs/embedding.md). Comparison with other formats: [docs/format-comparison.md](https://github.com/chronozarr/chronozarr/blob/main/docs/format-comparison.md).
|
|
62
|
+
|
|
63
|
+
## Who this is for
|
|
64
|
+
|
|
65
|
+
- **Researchers with stacks.** You have a Sentinel-2, Landsat or model time series in xarray, NetCDF or GeoTIFFs. `chronozarr encode` writes a store from an array in memory, `chronozarr convert` streams a COG manifest, a Zarr variable or a NetCDF file into one a timestep at a time, `xarray.open_zarr` or the `chronozarr` engine reads it back, and the viewer scrubs it.
|
|
66
|
+
- **Data publishers.** A store is one immutable prefix in a bucket with byte ranges and CORS, and nothing to run. `chronozarr doctor <url>` checks CORS, ranges, caching and decoding against the live URL. Recipes for S3 with CloudFront, R2, GCS and Source Cooperative are in [docs/hosting.md](https://github.com/chronozarr/chronozarr/blob/main/docs/hosting.md).
|
|
67
|
+
- **Map libraries integrating the decoder.** `js/chronozarr/` is a DOM-free reader on zarrita: `(lod, row, col, t)` to a typed-array cell, shard index cache, worker-pool decode and prefetch. A MapLibre custom layer built on it is in `js/maplibre/`.
|
|
68
|
+
- **Notebooks.** `chronozarr.open_store(path_or_url).to_xarray()` for arrays, and `chronozarr.view(store)` to look at a local store in the viewer from Jupyter (extra `notebook`).
|
|
69
|
+
|
|
70
|
+
## What is in this repo
|
|
71
|
+
|
|
72
|
+
| Part | Path | What it does |
|
|
73
|
+
|------|------|--------------|
|
|
74
|
+
| Format spec | `spec/CHRONOZARR.md` | Normative layout (one object per chunk by default, sharding optional), attributes, temporal encoding, pyramid, hosting rules |
|
|
75
|
+
| Python package `chronozarr` | `src/chronozarr/` | `encode()`, `open_store()`, `validate()`, `view()`; CLI `chronozarr encode / convert / append / validate / info / doctor / export-cog / stac`; xarray engine `chronozarr` |
|
|
76
|
+
| JS reader | `js/chronozarr/` | DOM-free reader on top of zarrita: cells by (lod, row, col, t), cache, prefetch |
|
|
77
|
+
| MapLibre layer | `js/maplibre/` | Custom layer that renders a store through the JS reader |
|
|
78
|
+
| TileRipper viewer | `js/tileripper/` | WebGL2 viewer: time scrub, looping playback up to 60 steps per second, click for values and a time-series chart, permalinks, WebM and GIF export; `?embed=1` compact mode with a postMessage API for host pages |
|
|
79
|
+
| Ingest example | `examples/sentinel2_pc/` | Monthly Sentinel-2 median composites from Planetary Computer |
|
|
80
|
+
| Water-mask example | `examples/water_masks/` | Derived NDWI and water-fraction stores with validity masks from the monthly mosaics ([docs/user-zero.md](https://github.com/chronozarr/chronozarr/blob/main/docs/user-zero.md)) |
|
|
81
|
+
| PNG frames example | `examples/png_frames/` | Georeferenced PNG frames converted into a store with no GeoTIFF step ([docs/png-frames.md](https://github.com/chronozarr/chronozarr/blob/main/docs/png-frames.md)) |
|
|
82
|
+
| Docs | `docs/` | Host recipes, appending, embedding, the format comparison, the PNG rules and the user-zero write-up |
|
|
83
|
+
|
|
84
|
+
## How it compares
|
|
85
|
+
|
|
86
|
+
- **PMTiles** packs tiles, images or vectors, for one moment. Its tile scheme is Web Mercator in practice and it has no native time axis; per-tile values are whatever the image encoding carries.
|
|
87
|
+
- **Mapbox raster-array** (MRT) is multi-band numeric tiles with a time series. Its decoder code is published in mapbox-gl-js (`src/data/mrt`), but the format is tied to Mapbox's tiling service and renderer.
|
|
88
|
+
- **CarbonPlan ndpyramid + zarr-layer** put Zarr pyramids in MapLibre with a time selector. zarr-layer supports arbitrary CRS through proj4 reprojection. Each timestep is its own chunk fetch, and stock zarr-layer needs an adapter to reconstruct star-delta residuals.
|
|
89
|
+
|
|
90
|
+
chronozarr keeps native projection and lossless values, serves a timestep as one plain `GET` of one chunk, and pre-stages a window of the time axis in the client so a timestep switch costs zero bytes on the wire once cached. The temporal encoding is the optional part: it was about 25% smaller on arid scenes, about 6% on vegetated ones, and 2.6% on the whole Ucayali demo store, so the writer decides per store. The layout is a trade the writer makes too: unsharded (the default) is one object per cell, level and timestep (about 5,900 for the 117-month imagery store), with no index reads, cheap CDN misses and appends that write only new objects; sharded (`--shard`) is 93 objects and one range read per timestep once the shard index is cached, with a CDN miss that costs time proportional to the shard size. [docs/format-comparison.md](https://github.com/chronozarr/chronozarr/blob/main/docs/format-comparison.md) has the row-by-row table, including when to choose each of the other tools.
|
|
91
|
+
|
|
92
|
+
## Install
|
|
93
|
+
|
|
94
|
+
The first release (0.2.0 of both packages) is pending: `chronozarr` is not on PyPI and `chronozarr` is not on npm yet, so the install commands below fail until the `v0.2.0` tag is published. Both packages release from one `v*` tag, so their versions move together.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install chronozarr # Python package and CLI; extras: geo (GeoTIFF input), notebook (view()), netcdf, dask
|
|
98
|
+
npm install chronozarr # JavaScript reader and MapLibre layer
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Until then, work from a checkout of this repository:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
uv sync # Python package and CLI; add --extra geo for GeoTIFF input, --extra notebook for view()
|
|
105
|
+
uv run chronozarr --help
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`chronozarr` is the ES modules under `js/chronozarr/` and `js/maplibre/`, published as they are (no build step, no runtime dependency): `import { openStore } from 'chronozarr'` and `import { ChronozarrLayer } from 'chronozarr/maplibre'`. From a checkout, `cd js && npm install` installs only the test tooling. zarrita and its codecs are vendored under `js/vendor` (zarrita 0.7.5, @zarrita/storage 0.2.0, numcodecs 0.3.2, all MIT), so the viewer has no runtime third-party host; each vendored file header records its version, license and the SHA-256 of the published file, and the page loads no web font. The MapLibre demo page (`js/maplibre/index.html`) is the exception by design: it loads maplibre-gl from a pinned CDN version. Usage examples are in [js/README.md](https://github.com/chronozarr/chronozarr/blob/main/js/README.md).
|
|
109
|
+
|
|
110
|
+
## Quickstart
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import chronozarr
|
|
114
|
+
|
|
115
|
+
chronozarr.encode(da, "my_store", crs="EPSG:32631") # da: (time, band, y, x) with x/y coordinates; the writer picks the temporal encoding
|
|
116
|
+
store = chronozarr.open_store("my_store")
|
|
117
|
+
store.read(t=42) # (band, y, x), exact stored values
|
|
118
|
+
store.to_xarray(lod=0)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
uv run chronozarr encode scenes.nc my_store # INPUT: Zarr, NetCDF, or a quoted GeoTIFF glob
|
|
123
|
+
uv run chronozarr validate my_store
|
|
124
|
+
uv run chronozarr doctor https://your-host/my_store
|
|
125
|
+
uv run chronozarr export-cog my_store cogs/ # true-value COGs for GDAL and QGIS (extra geo)
|
|
126
|
+
uv run chronozarr stac my_store --out catalog/ # static STAC Collection and Item (extra geo)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
A plain reader needs no chronozarr at all (anchor timesteps are exact; in a star-delta store the other timesteps are residuals):
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
import xarray as xr
|
|
133
|
+
|
|
134
|
+
ds = xr.open_zarr("my_store", group="0", zarr_format=3, chunks=None)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Serve the store from any static host that supports GET, byte ranges and CORS (S3, R2, GCS, Source Cooperative, a local range-capable server), then open the viewer:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
js/tileripper/index.html?store=https://your-bucket/my_store
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Command line
|
|
144
|
+
|
|
145
|
+
`uv run chronozarr <command> --help` lists every option.
|
|
146
|
+
|
|
147
|
+
| Command | What it does |
|
|
148
|
+
|---------|--------------|
|
|
149
|
+
| `encode INPUT OUT` | Encode a Zarr store or NetCDF file with dims `(time, band, y, x)`, or a quoted glob of GeoTIFFs with the date in the file name, into a store. Options include `--encoding auto\|none\|star-delta`, `--codec`, `--level`, `--chunk-size`, `--shard/--no-shard` (default off), `--shard-time` (needs `--shard`), `--lods`. |
|
|
150
|
+
| `convert SOURCE OUT` | Convert a manifest (`.csv` with `uri,datetime[,bands]`, or `.json`) of COGs or georeferenced PNG frames (world file plus `--crs`, `.aux.xml`, or `--bounds` with `--crs`; RGBA alpha becomes the mask), a Zarr store or a NetCDF file into a store one timestep at a time, without loading the whole stack. Warps COGs that are off the target grid (`--crs`, `--transform`, `--shape`, `--resampling`), stages timesteps so `--resume` can continue an interrupted run, and `--dry-run` prints the size and time estimate only. Takes the encode options too (`--encoding`, `--codec`, `--chunk-size`, `--shard-time`, `--read-ahead`). |
|
|
151
|
+
| `append STORE INPUT` | Add timesteps at the end of an existing store: another store (for example one month written by `convert`), a Zarr store, a NetCDF file or a GeoTIFF glob. Writes only the objects that gain data and leaves every existing chunk byte-identical. The default unsharded layout appends by writing only new objects, where a sharded store rewrites its trailing shard; see [docs/append.md](https://github.com/chronozarr/chronozarr/blob/main/docs/append.md). |
|
|
152
|
+
| `validate STORE` | Check a store against the spec. Exit status 1 if it does not conform. |
|
|
153
|
+
| `info STORE` | Summarise a store: times, bands, temporal encoding and pyramid levels. |
|
|
154
|
+
| `doctor TARGET` | Diagnose an https URL or a local store path. A URL is probed as a browser would: root `zarr.json`, byte ranges, CORS, `HEAD` and caching headers. Both kinds then get the layout validated and one cell per level decoded and compared with a plain Zarr read. Exit status 1 only if a check fails; warnings and info lines are advice. `--origin` sets the `Origin` header. |
|
|
155
|
+
| `export-cog STORE OUT_DIR` | Write timesteps as true-value Cloud Optimized GeoTIFFs readable by GDAL and QGIS, one file per timestep. `--level` picks the pyramid level, `--times` picks timesteps (`all`, indices, slices, dates, date ranges). Needs extra `geo`. |
|
|
156
|
+
| `stac STORE --out DIR` | Write a static STAC Collection and Item for a store: the Zarr asset, extent, band metadata, the datacube extension and the recorded provenance. `--href` sets the public store location. Needs extra `geo`. |
|
|
157
|
+
|
|
158
|
+
`examples/sentinel2_pc/ingest.py` builds a Sentinel-2 store from Planetary Computer with band metadata, a 0/1 coverage plane and provenance recorded in it; `--stac` also writes a static STAC Collection and Item.
|
|
159
|
+
|
|
160
|
+
## Measured
|
|
161
|
+
|
|
162
|
+
Browser decode of one real Sentinel-2 chunk (4 x 512 x 512 uint16, 2 MB raw), median of 15, 2026-09-29:
|
|
163
|
+
|
|
164
|
+
| Codec | Bytes | Decode |
|
|
165
|
+
|-------|------:|-------:|
|
|
166
|
+
| zstd via zarrita (WASM) | 1,284,781 | 4.5 ms |
|
|
167
|
+
| gzip via native DecompressionStream | 1,387,409 | 6.7 ms |
|
|
168
|
+
| zstd via fzstd (pure JS) | 1,284,781 | 14.1 ms |
|
|
169
|
+
|
|
170
|
+
Four real Ucayali LOD 0 chunks (2,097,152 bytes each), zarrita 0.7.5 with vendored codec modules served locally, headless Chrome, median of 20, 2026-09-30, bit-exact in every case:
|
|
171
|
+
|
|
172
|
+
| Codec | Bytes per chunk | Decode, steady state | Decode inside a worker |
|
|
173
|
+
|-------|------:|-------:|-------:|
|
|
174
|
+
| zstd level 5 (writer default) | 1,376,469 | 4.4 ms | 4.4 ms |
|
|
175
|
+
| blosc, zstd level 1, byte shuffle | 1,520,154 (+10.4%) | 3.5 ms | 3.3 ms |
|
|
176
|
+
|
|
177
|
+
Byte shuffle did not shrink the zstd stream on this data. The 10.4% size penalty alone keeps zstd level 5 as the default; blosc is permitted.
|
|
178
|
+
|
|
179
|
+
Temporal encoding, compressed bytes of star-delta against plain storage with the same codec: about 25% smaller on arid scenes, about 6% smaller on vegetated ones, and 2.6% smaller (6,285.2 MB against 6,451.9 MB) on the full Ucayali demo store. The writer samples level 0 cells and keeps star-delta only at 0.85 of the plain size or better.
|
|
180
|
+
|
|
181
|
+
Viewer on the v0.1 Sahara store (128 months, 4 bands, 6 x 6 cells at LOD 0, 3.9 GB in 93 files), localhost, HTTP cache bypassed, 2026-09-29:
|
|
182
|
+
|
|
183
|
+
| Measurement | Real-GPU Chromium, 36-cell 12-month stress store | Built-in browser pane, full 128-month Sahara store |
|
|
184
|
+
|-------------|------:|------:|
|
|
185
|
+
| Cold open to first complete frame | 257 ms, 109 requests, 43.8 MB | 475 ms median, 109 requests, 35.2 MB |
|
|
186
|
+
| Cold open without consolidated metadata | 256 ms | 556 to 1478 ms |
|
|
187
|
+
| Warm timestep switch, stepping (median / p95) | 3.5 / 15 ms | 6.3 / 19 ms |
|
|
188
|
+
| Warm switch wire bytes and requests | 0 / 0 | 0 / 0 |
|
|
189
|
+
| Product switch | about 1 ms | under 1 ms |
|
|
190
|
+
| Decode per 2 MB chunk | 4.6 ms | 10 ms under load (4.5 ms idle) |
|
|
191
|
+
|
|
192
|
+
Perceived scrub latency, input event to the frame showing the new timestep, 20 steps, same 36-cell 48-month store and browser before and after the decoder rewrite (worker-pool decode, time-window prefetch, no debounce, per-cell paint), 2026-09-30:
|
|
193
|
+
|
|
194
|
+
| Scenario | Steps shown, before | Steps shown, after | Lag median / p95, before | Lag median / p95, after | Frames over 33 ms, before / after |
|
|
195
|
+
|---|---:|---:|---:|---:|---:|
|
|
196
|
+
| Overview, cold, arrow keys | 4 of 20 | 20 of 20 | 263 / 527 ms | 3.2 / 15.8 ms | 16 / 0 |
|
|
197
|
+
| Overview, cold, slider drag | 2 of 20 | 20 of 20 | 334 / 693 ms | 5.3 / 16.6 ms | 0 / 0 |
|
|
198
|
+
| Overview, after 5 s idle, keys | 6 of 20 | 20 of 20 | 243 / 543 ms | 4.6 / 5.2 ms | 15 / 0 |
|
|
199
|
+
| 9 cells at LOD 0, cold, keys | 10 of 20 | 20 of 20 | 90 / 521 ms | 5.6 / 27.8 ms | 4 / 0 |
|
|
200
|
+
| 9 cells at LOD 0, after idle, keys | 20 of 20 | 20 of 20 | 8.3 / 8.8 ms | 2.7 / 3.7 ms | 0 / 0 |
|
|
201
|
+
|
|
202
|
+
Movie playback, two loops, holds counted separately at the wrap, 2026-09-30:
|
|
203
|
+
|
|
204
|
+
| Store, view, state | Requested | Achieved | Holds at wrap / elsewhere |
|
|
205
|
+
|---|---:|---:|---:|
|
|
206
|
+
| 36-cell local store, 9 cells, warm | 60 /s | 59.95 /s | 0 / 0 |
|
|
207
|
+
| 36-cell local store, 9 cells, cold | 60 /s | 56.4 /s | 0 / 9 |
|
|
208
|
+
| Ucayali over the internet, 4 cells, warm | 60 /s | 60.03 /s | 0 / 0 |
|
|
209
|
+
| Ucayali over the internet, 9 cells, cold | 60 /s | 6.0 /s | 0 / 155 |
|
|
210
|
+
|
|
211
|
+
A 9-cell overview of the 117-month store is about 11.7 MB per timestep, so a cold loop is bound by the link (about 55 MB/s here), and the cache, 1.5 GiB shared by the decoded and compressed tiers on an 8 GB machine, holds roughly half of the 117 timesteps at that size; idle prefetch stops after 64 MiB and expands only during playback. A 4-cell view fits entirely and plays at the display rate.
|
|
212
|
+
|
|
213
|
+
Coarse-first loading and the bandwidth-aware movie level, live Ucayali store with the link throttled to 50 Mbit/s and 40 ms, 2026-09-30, medians:
|
|
214
|
+
|
|
215
|
+
| Interaction | Before | After, first usable frame | After, full resolution |
|
|
216
|
+
|---|---:|---:|---:|
|
|
217
|
+
| Open | 2.03 s | 421 ms | 1.97 s |
|
|
218
|
+
| Big time jump | 6.00 s | 927 ms | 3.97 s |
|
|
219
|
+
| Zoom in | 4.86 s | under 2 ms (cached coarser cells) | 2.10 s |
|
|
220
|
+
| Playback at 10 steps/s requested | 3.4 /s | | 5.3 /s, level dropped by the link rule |
|
|
221
|
+
|
|
222
|
+
On an unthrottled link a frame expected within a second loads directly, so fast connections pay nothing for the staging. The 150 ms coarse-frame target holds on fast links (74 ms after metadata) and not at 50 Mbit/s, where three sequential requests per stage set a floor near 230 ms; shard_bytes hints remove one of them.
|
|
223
|
+
|
|
224
|
+
Whole frames and buffered playback, live Ucayali store, cold, device pixel ratio 2, 2026-10-01. A frame is one level and one timestep for every visible cell, shown complete or not at all: the target level when it is in memory, else the finest coarser level that is, else the frame already on screen. The whole loop is kept in memory at the coarsest useful level (level 3 here, about 80 MB) so a complete frame exists for every timestep. Playback buffers 2 s of frames before it starts and pauses on an indicator when it runs dry instead of holding frame by frame. "Partial" counts frames with some cells at another level or timestep; "whole" is the first frame covering the view at any level.
|
|
225
|
+
|
|
226
|
+
| Link | Tree | Open: whole / full | Scrub 20 steps: partial frames | Play 10 /s: achieved | Initial buffer | Buffering pauses | Holds |
|
|
227
|
+
|---|---|---:|---:|---:|---:|---:|---:|
|
|
228
|
+
| Natural | Before | 505 ms / 1.41 s | 8 of 9 | 5.7 /s | | | 98 (19.0 s) |
|
|
229
|
+
| Natural | After | 605 ms / 2.06 s | 0 of 37 | 10.0 /s | 1.9 s | 0 | 0 |
|
|
230
|
+
| 50 Mbit/s, 40 ms | Before | 545 ms / 2.25 s | 8 of 9 | 4.1 /s | | | 110 (35.3 s) |
|
|
231
|
+
| 50 Mbit/s, 40 ms | After | 499 ms / 2.58 s | 0 of 36 | 7.8 /s | 7.3 s | 2 (6.4 s) | 0 |
|
|
232
|
+
|
|
233
|
+
The cost is the full-resolution frame of a scrub step on a slow link, which lands later (median 3.7 to 4.5 s against 2.9 to 3.2 s at 50 Mbit/s) because a whole coarse frame of the right timestep is fetched first.
|
|
234
|
+
|
|
235
|
+
Appending one month to a 12-month Ucayali store (4 bands, 36 cells at level 0), against a re-encode of 32 s and 6.3 GB written, 2026-10-01 (full tables in [docs/append.md](https://github.com/chronozarr/chronozarr/blob/main/docs/append.md)):
|
|
236
|
+
|
|
237
|
+
| Layout | Append wall time | Bytes written per month | Rewritten |
|
|
238
|
+
|---|---:|---:|---|
|
|
239
|
+
| Unsharded (default) | 0.6 s | 55 MB | metadata only |
|
|
240
|
+
| Yearly time shards | 0.6 s | 55 to 660 MB, 4.4 GB over a 12-month cycle | the trailing shard, whole |
|
|
241
|
+
| Whole-axis shard (`--shard`) | 0.6 s | 55 MB, then 111 MB and growing | a second shard that grows every month |
|
|
242
|
+
|
|
243
|
+
The encoder default is now unsharded: a cold open of the published sharded store spent 6.5 of 6.8 s on the nine shard-index reads, because a CDN miss on a 2 KB range at the end of an 83 to 174 MB shard pulls the whole object, and an append to a sharded store rewrites the trailing shard. The cold-open and append numbers for the unsharded layout will be measured on the live store after the re-upload.
|
|
244
|
+
|
|
245
|
+
Cold open of the live unsharded store `chronozarr-4` from the deployed viewer with a cold edge cache, 2026-10-01: first whole frame 390 ms, complete frame at the target level 622 ms, against 6.8 s on the sharded store the same morning, where 6.5 s were the nine shard-index reads missing the edge cache. Publishing it took 274 s for 5,893 objects through the R2 S3 API (`scripts/r2_sync.py`), 150 s for the 17,088 objects of the water store. An append to a live store has not been exercised yet.
|
|
246
|
+
|
|
247
|
+
Star-delta reconstruction runs in the fragment shader; the CPU loop it replaces cost 54 ms per 36-cell frame. Known limits: the coarse loop pulls the whole time axis at its level after the first frame (tens of MB for a store a few cells wide); cold open of very small stores costs 30 to 40 ms for worker startup.
|
|
248
|
+
|
|
249
|
+
## Status
|
|
250
|
+
|
|
251
|
+
v0.2 draft (spec version `0.2.0`; every v0.1 store is a valid v0.2 store and readers accept both). The layout is Zarr v3 groups per level. The writer default is unsharded: one object per chunk, that is per cell, level and timestep (about 5,900 objects for the 117-month imagery store), so there is no shard index to read, a CDN miss costs one chunk and an append writes only new objects. Sharding stays available (`--shard`, `shard_time`) and every store written sharded stays valid: 93 objects and one range read per timestep once the shard index is cached, with a miss that costs time proportional to the shard size.
|
|
252
|
+
|
|
253
|
+
The public demo store is `ucayali_santa_maria/chronozarr-3`: the Ucayali River near Santa María, Peru, 117 monthly Sentinel-2 composites from 2015 to 2026 over one of the fastest-migrating meandering reaches on Earth, served from an R2 bucket at data.tileripper.com. It is a v0.2 store. The writer's auto rule chose temporal encoding `none`: star-delta compressed to 0.988 of the plain size on the sampled cells, far short of the 0.85 needed to keep it. `chronozarr-3` is sharded: 6,451.9 MB in 93 files. `chronozarr-4` is the same data written with the unsharded default (6,451.8 MB in 5,893 files, values bit-identical) and replaces it once uploaded. The same data with star-delta forced is 6,285.2 MB, so the plain store is 166.7 MB (about 2.7%) larger. That is the price of a store any Zarr v3 reader decodes without an adapter. The Measured tables above were taken on earlier stores of this reach and of the Sahara. Sahara and Iowa remain the benchmark pair and can be re-encoded from the ingest example.
|
|
254
|
+
|
|
255
|
+
A second public store, `ucayali_santa_maria/water-1`, is derived from the same mosaics: NDWI as int16 with a scale of 1e-4, water as a scaled fraction, a validity mask where no scene was observed, 1.73 GB in 201 files, built by `examples/water_masks/`. `water-2` is the same store written unsharded (1.73 GB in 17,088 files, values bit-identical) and replaces it once uploaded. It is the first store with a dtype other than uint16, explicit masks and physical units through the whole path, and the viewer lists both stores. Stores can now grow with `chronozarr append`, the viewer embeds in other pages with `?embed=1`, and georeferenced PNG frames convert directly.
|