worldarray 0.2.0__tar.gz → 0.3.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.
- {worldarray-0.2.0 → worldarray-0.3.0}/CHANGELOG.md +23 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/PKG-INFO +14 -1
- {worldarray-0.2.0 → worldarray-0.3.0}/README.md +13 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/docs/spec.md +32 -17
- {worldarray-0.2.0 → worldarray-0.3.0}/pyproject.toml +1 -1
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/__init__.py +12 -4
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/array.py +46 -4
- worldarray-0.3.0/src/worldarray/grid.py +492 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/store.py +5 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/units.py +21 -0
- worldarray-0.3.0/tests/test_grid.py +385 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/uv.lock +1 -1
- {worldarray-0.2.0 → worldarray-0.3.0}/.github/workflows/ci.yml +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/.github/workflows/docs.yml +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/.github/workflows/publish.yml +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/.gitignore +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/LICENSE +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/docs/changelog.md +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/docs/index.md +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/mkdocs.yml +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/context.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/geometry.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/levels.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/placement.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/resample.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/retry.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/src/worldarray/write.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/tests/conftest.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/tests/fixtures/metadata.json +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/tests/fixtures/write.json +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/tests/test_access.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/tests/test_geometry.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/tests/test_metadata.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/tests/test_read.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/tests/test_resample.py +0 -0
- {worldarray-0.2.0 → worldarray-0.3.0}/tests/test_write.py +0 -0
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 (2026-10-22)
|
|
4
|
+
|
|
5
|
+
Grids and blocks, built to spec draft 3 (Behaviours 17 to 19). Nothing on the read or
|
|
6
|
+
write side changes.
|
|
7
|
+
|
|
8
|
+
- `blocks(shape, block_shape, context=0, box=None, clip=True)`: the array (or a voxel
|
|
9
|
+
box, which may reach past the array) cut into write boxes that tile it with no overlap
|
|
10
|
+
and no gap, each with a read box grown by `context` (one int, one per axis, or
|
|
11
|
+
`(before, after)` pairs) and clipped to the array when `clip`; C order, `len`,
|
|
12
|
+
`blocks[id]` in O(1), `index_of(begin)`; a `Block` carries `index`, `id`, `begin`,
|
|
13
|
+
`end`, `read_begin`, `read_end`, `at_start` and `at_end` per axis and `inner`, the write
|
|
14
|
+
box's slices inside the read box. Plain picklable records; nothing is scheduled.
|
|
15
|
+
`vol.blocks()` defaults the block to `write_shape`, so default blocks are aligned writes.
|
|
16
|
+
`vol.write_chunk(index, data)` writes one whole unit.
|
|
17
|
+
- `Grid(shape, voxel_size, translation, axes, units, chunks, shards)`: a `WorldArray`
|
|
18
|
+
without a store, with `box`, `corner`, `index_box`, `chunks_covering`, `write_shape`,
|
|
19
|
+
`blocks` and `meta(dtype)`; `Grid.from_meta(meta, voxel_size=)` is an existing array's
|
|
20
|
+
grid or the resample target grid over it. `create(path, grid=g, dtype=)` creates from it.
|
|
21
|
+
- `level_factors(voxel_size, ratio=1.5, ...)`: the anisotropy-aware pyramid schedule
|
|
22
|
+
mesh-n-bone and cellmap-analyze share, per level.
|
|
23
|
+
- `units.abbreviation`, and `store.join`, `store.parent`, `store.basename` as public
|
|
24
|
+
URL-aware path helpers.
|
|
25
|
+
|
|
3
26
|
## 0.2.0 (2026-10-22)
|
|
4
27
|
|
|
5
28
|
Metadata and placed creation, built to spec draft 3 (Behaviours 10 to 16). Nothing on the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: worldarray
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Read zarr, N5 and Neuroglancer precomputed volumes in world coordinates through tensorstore: correct voxel placement, pyramid levels, padded region reads, resampling. numpy + tensorstore only.
|
|
5
5
|
Project-URL: Homepage, https://janeliascicomp.github.io/worldarray/
|
|
6
6
|
Project-URL: Documentation, https://janeliascicomp.github.io/worldarray/
|
|
@@ -156,6 +156,11 @@ s1 = wa.open_pyramid("out.zarr", mode="r+").add_level("s1", factors=2) # place
|
|
|
156
156
|
and only two answers to "it exists": raise, or adopt an identical array after checking;
|
|
157
157
|
whole-unit writes from many processes with no coordination; `add_level` as metadata plus
|
|
158
158
|
an empty array; chunk keys for every format. Nothing is ever deleted or overwritten.
|
|
159
|
+
- **Plans outputs and blocks** (0.3): a store-less `Grid` with the same geometry as an
|
|
160
|
+
array, so an output is planned, cut into blocks and only then created; `blocks` tiling an
|
|
161
|
+
array or a box into write boxes with context-grown read boxes, in C order, as plain
|
|
162
|
+
picklable records a cluster job takes by id; the anisotropy-aware pyramid schedule.
|
|
163
|
+
Nothing is scheduled or run.
|
|
159
164
|
|
|
160
165
|
## The placement rule
|
|
161
166
|
|
|
@@ -202,6 +207,14 @@ wa.ome_multiscales(axes, units, levels, version="0.5", type=None) ; wa.level_pla
|
|
|
202
207
|
wa.level_shape(shape, factors, origin=0) ; wa.chunk_key(index, format, ...) ; wa.parse_chunk_key(key, format, ...)
|
|
203
208
|
wa.ensure_group_entry(group_path, name, voxel_size, translation, axes, units="nanometer") # list an array you made yourself
|
|
204
209
|
wa.units.ome_name("nm") -> "nanometer" ; wa.write.codecs_of(path) -> (codecs, shards)
|
|
210
|
+
|
|
211
|
+
# grids and blocks (0.3)
|
|
212
|
+
g = wa.Grid(shape, voxel_size, translation, axes=None, units="nanometer", chunks=None, shards=None) # or Grid.from_meta(meta, voxel_size=)
|
|
213
|
+
wa.create(path, grid=g, dtype="uint64")
|
|
214
|
+
for b in wa.blocks(shape, block_shape, context=0, box=None, clip=True): # also vol.blocks() and g.blocks()
|
|
215
|
+
b.begin, b.end, b.read_begin, b.read_end, b.inner, b.at_end, b.id # plain records; blocks[id] is O(1)
|
|
216
|
+
wa.level_factors(voxel_size, ratio=1.5, max_levels=None, stop_shape=None, shape=None)
|
|
217
|
+
wa.units.abbreviation("nanometer") ; wa.store.join(path, "s0") ; wa.store.parent(path) ; wa.store.basename(path)
|
|
205
218
|
```
|
|
206
219
|
|
|
207
220
|
`WorldArray`: `ts`, `meta`, `grid`, `shape`, `dtype`, `axes`, `voxel_size`, `translation`,
|
|
@@ -97,6 +97,11 @@ s1 = wa.open_pyramid("out.zarr", mode="r+").add_level("s1", factors=2) # place
|
|
|
97
97
|
and only two answers to "it exists": raise, or adopt an identical array after checking;
|
|
98
98
|
whole-unit writes from many processes with no coordination; `add_level` as metadata plus
|
|
99
99
|
an empty array; chunk keys for every format. Nothing is ever deleted or overwritten.
|
|
100
|
+
- **Plans outputs and blocks** (0.3): a store-less `Grid` with the same geometry as an
|
|
101
|
+
array, so an output is planned, cut into blocks and only then created; `blocks` tiling an
|
|
102
|
+
array or a box into write boxes with context-grown read boxes, in C order, as plain
|
|
103
|
+
picklable records a cluster job takes by id; the anisotropy-aware pyramid schedule.
|
|
104
|
+
Nothing is scheduled or run.
|
|
100
105
|
|
|
101
106
|
## The placement rule
|
|
102
107
|
|
|
@@ -143,6 +148,14 @@ wa.ome_multiscales(axes, units, levels, version="0.5", type=None) ; wa.level_pla
|
|
|
143
148
|
wa.level_shape(shape, factors, origin=0) ; wa.chunk_key(index, format, ...) ; wa.parse_chunk_key(key, format, ...)
|
|
144
149
|
wa.ensure_group_entry(group_path, name, voxel_size, translation, axes, units="nanometer") # list an array you made yourself
|
|
145
150
|
wa.units.ome_name("nm") -> "nanometer" ; wa.write.codecs_of(path) -> (codecs, shards)
|
|
151
|
+
|
|
152
|
+
# grids and blocks (0.3)
|
|
153
|
+
g = wa.Grid(shape, voxel_size, translation, axes=None, units="nanometer", chunks=None, shards=None) # or Grid.from_meta(meta, voxel_size=)
|
|
154
|
+
wa.create(path, grid=g, dtype="uint64")
|
|
155
|
+
for b in wa.blocks(shape, block_shape, context=0, box=None, clip=True): # also vol.blocks() and g.blocks()
|
|
156
|
+
b.begin, b.end, b.read_begin, b.read_end, b.inner, b.at_end, b.id # plain records; blocks[id] is O(1)
|
|
157
|
+
wa.level_factors(voxel_size, ratio=1.5, max_levels=None, stop_shape=None, shape=None)
|
|
158
|
+
wa.units.abbreviation("nanometer") ; wa.store.join(path, "s0") ; wa.store.parent(path) ; wa.store.basename(path)
|
|
146
159
|
```
|
|
147
160
|
|
|
148
161
|
`WorldArray`: `ts`, `meta`, `grid`, `shape`, `dtype`, `axes`, `voxel_size`, `translation`,
|
|
@@ -765,24 +765,38 @@ Behaviours 10 to 16 are 0.2; 17 to 19 are 0.3.
|
|
|
765
765
|
17. **Blocks tile, context grows, nothing runs.** `blocks(shape, block_shape, context, box,
|
|
766
766
|
clip)` yields, in C order, blocks whose write boxes `[begin, end)` partition `[0,
|
|
767
767
|
shape)`, or `box` (a voxel box, the grid anchored at its begin) when given, with no
|
|
768
|
-
overlap and no gap, the last block on an axis clipped
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
768
|
+
overlap and no gap, the last block on an axis clipped to the box. `box` may reach past
|
|
769
|
+
the array on any side (a ROI rounded outward to a factor multiple, mesh-n-bone's halo
|
|
770
|
+
mode): the write boxes tile it exactly, outside the array too. A block's read box is
|
|
771
|
+
its write box grown by `context` on each side, where `context` is one int, one per
|
|
772
|
+
axis, or a `(before, after)` pair per axis, clipped to the **array** (never to `box`,
|
|
773
|
+
never a write box) when `clip=True` (the default; cellmap-analyze's `create_block` edge
|
|
774
|
+
rule) and left unclipped when `clip=False` for a padded `read_index` (its
|
|
775
|
+
`neg_with_edge_pos` mode, which reads past the array for fill). A block also carries
|
|
776
|
+
`at_start` and `at_end` per axis, whether it is the first or last block on that axis,
|
|
777
|
+
so a caller adds context at the array's far faces itself, and `inner`, the slices of
|
|
778
|
+
the write box inside the read box, correct also when `clip=False` lets `read_begin`
|
|
779
|
+
go below 0. `len(blocks)`, `blocks[b.id]` (O(1)), `blocks.index_of(b.begin)` and
|
|
773
780
|
`b.index` agree. `vol.blocks()` defaults `block_shape` to `write_shape`, so default
|
|
774
781
|
blocks are aligned writes. Nothing is scheduled: the iterable and its blocks are plain,
|
|
775
|
-
picklable records
|
|
776
|
-
|
|
782
|
+
picklable records, and the iterable pickles without its blocks, so a worker takes an
|
|
783
|
+
id. One grid per call: a consumer that writes several levels from one block derives
|
|
784
|
+
the per-level boxes itself (mesh-n-bone's direct mode, two lines). `write_chunk(index,
|
|
785
|
+
data)` writes the unit at `index` whole; `data.shape` must equal that unit's clipped
|
|
786
|
+
shape.
|
|
777
787
|
|
|
778
788
|
18. **A `Grid` is a `WorldArray` without a store.** `Grid(shape, voxel_size, translation,
|
|
779
789
|
axes, units, chunks, shards)` answers `box`, `corner`, `index_box`, `chunks_covering`,
|
|
780
790
|
`write_shape` and `blocks` exactly as a `WorldArray` of those facts does, and
|
|
781
|
-
`g.meta(dtype)`
|
|
791
|
+
`g.meta(dtype)` agrees with `wa.create(path, grid=g, dtype=dtype).meta` in every
|
|
792
|
+
field a grid has: shape, dtype, chunk and shard shape, axes, units, voxel size and
|
|
793
|
+
translation (a grid has no path, format, documents or fill value), so a pipeline plans
|
|
782
794
|
its output before anything exists. `Grid.from_meta(meta, voxel_size=t)` is Behaviour
|
|
783
795
|
4's target grid over that array: anchored at its corner with `ceil(n * s / t)` voxels,
|
|
784
|
-
so a `read(voxel_size=t)` of the source fills it exactly
|
|
785
|
-
|
|
796
|
+
so a `read(voxel_size=t)` of the source fills it exactly, and its `box` is the target
|
|
797
|
+
grid's extent, which may reach past the source's own `box` by less than one target
|
|
798
|
+
voxel; this is the output grid cellmap-analyze's commands compute today in the integer
|
|
799
|
+
frame.
|
|
786
800
|
|
|
787
801
|
19. **Schedules and helpers.** `level_factors(voxel_size, ratio, max_levels, stop_shape,
|
|
788
802
|
shape)` returns per level the per-axis factors, 2 on every axis whose current voxel size
|
|
@@ -1017,11 +1031,12 @@ over funlib geometry, by this spec's own assignment.
|
|
|
1017
1031
|
attributes? If so, should the package offer `funlib_attrs(voxel_size, translation, axes,
|
|
1018
1032
|
units)`, writing `offset` as the corner, funlib's meaning, for `create(attrs=)`, or do
|
|
1019
1033
|
the repos keep writing their own?
|
|
1020
|
-
6. **The block iterator's shape** (Behaviour 17)
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1034
|
+
6. **The block iterator's shape** (Behaviour 17). Resolved 2026-10-22: both sessions
|
|
1035
|
+
confirmed it. mesh-n-bone asked that `box` may reach past the array and that `clip`
|
|
1036
|
+
clips read boxes only, and derives its per-level boxes from one grid itself;
|
|
1037
|
+
cellmap-analyze asked for `at_start`/`at_end` per axis and `inner`, and confirmed that
|
|
1038
|
+
`(before, after)` pairs cover its context modes and that `clip=False` with a padded
|
|
1039
|
+
`read_index` is its edge-reading mode. All are in Behaviour 17.
|
|
1025
1040
|
7. **Sidecar attributes.** Resolved 2026-10-22: registration-station keeps `fs` and
|
|
1026
1041
|
`fix_origin_um` on the group through `group_attrs` and the part attributes (`y0`, `y1`,
|
|
1027
1042
|
`y0_core`, `y1_core`, `feather_vox`) on the array through `attrs`, all known at creation;
|
|
@@ -1056,8 +1071,8 @@ over funlib geometry, by this spec's own assignment.
|
|
|
1056
1071
|
13. **World boxes on blocks.** cellmap-flow reads its input at one voxel size by a world
|
|
1057
1072
|
box grown by a context in nanometres and writes at another; `blocks` (0.3) gives index
|
|
1058
1073
|
boxes on one grid. `Grid.blocks` could carry world boxes too (`b.box`, `b.read_box` as
|
|
1059
|
-
`Box`, a context in world units). Two lines on cellmap-flow's side today
|
|
1060
|
-
|
|
1074
|
+
`Box`, a context in world units). Two lines on cellmap-flow's side today. Not planned
|
|
1075
|
+
(2026-10-22): daisy does cellmap-flow's tiling and stays; reopen if cellmap-flow drops it.
|
|
1061
1076
|
|
|
1062
1077
|
## 8. Constraints for whoever builds on this
|
|
1063
1078
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "worldarray"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Read zarr, N5 and Neuroglancer precomputed volumes in world coordinates through tensorstore: correct voxel placement, pyramid levels, padded region reads, resampling. numpy + tensorstore only."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { file = "LICENSE" }
|
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Open zarr v2, zarr v3, N5 and Neuroglancer precomputed arrays and pyramids by path, know
|
|
4
4
|
where each sits (voxel size, the centre of voxel 0, units, axes), and read voxel boxes or
|
|
5
|
-
world boxes as numpy arrays, padded where the box leaves the data
|
|
6
|
-
|
|
5
|
+
world boxes as numpy arrays, padded where the box leaves the data; create placed OME-NGFF
|
|
6
|
+
levels and write them from any number of jobs; plan an output grid and cut it into blocks
|
|
7
|
+
before anything exists. ``docs/spec.md`` is the contract; the README is its first five
|
|
8
|
+
sections.
|
|
7
9
|
"""
|
|
8
10
|
|
|
9
11
|
from __future__ import annotations
|
|
10
12
|
|
|
11
|
-
from . import store, units, write
|
|
13
|
+
from . import grid, store, units, write
|
|
12
14
|
from .array import Pyramid, WorldArray, create, metadata, open, open_pyramid, open_tensorstore
|
|
13
15
|
from .context import capped_context, capped_context_spec, shared_context
|
|
14
16
|
from .geometry import Box, index_range
|
|
17
|
+
from .grid import Block, Grid, blocks, level_factors
|
|
15
18
|
from .levels import select_level
|
|
16
19
|
from .placement import ArrayMeta, corner_to_centre, default_axes
|
|
17
20
|
from .retry import Retry
|
|
@@ -25,19 +28,24 @@ from .write import (
|
|
|
25
28
|
parse_chunk_key,
|
|
26
29
|
)
|
|
27
30
|
|
|
28
|
-
__version__ = "0.
|
|
31
|
+
__version__ = "0.3.0"
|
|
29
32
|
|
|
30
33
|
__all__ = [
|
|
31
34
|
"ArrayMeta",
|
|
35
|
+
"Block",
|
|
32
36
|
"Box",
|
|
37
|
+
"Grid",
|
|
33
38
|
"Node",
|
|
39
|
+
"blocks",
|
|
34
40
|
"chunk_key",
|
|
35
41
|
"corner_to_centre",
|
|
36
42
|
"create",
|
|
37
43
|
"default_axes",
|
|
38
44
|
"detect",
|
|
39
45
|
"ensure_group_entry",
|
|
46
|
+
"grid",
|
|
40
47
|
"index_range",
|
|
48
|
+
"level_factors",
|
|
41
49
|
"level_placement",
|
|
42
50
|
"level_shape",
|
|
43
51
|
"list_chunk_keys",
|
|
@@ -551,6 +551,34 @@ class WorldArray:
|
|
|
551
551
|
)
|
|
552
552
|
self.write_index(begin, end, data, partial=partial)
|
|
553
553
|
|
|
554
|
+
def blocks(self, block_shape=None, context=0, box=None, clip: bool = True):
|
|
555
|
+
"""The array cut into blocks (Behaviour 17): write boxes tiling the stored grid (or a
|
|
556
|
+
voxel ``box``) with ``block_shape`` (default ``write_shape``, so the default blocks are
|
|
557
|
+
aligned writes), read boxes grown by ``context`` and clipped to the array when
|
|
558
|
+
``clip``. Plain records, nothing scheduled."""
|
|
559
|
+
from .grid import blocks as _blocks
|
|
560
|
+
|
|
561
|
+
return _blocks(
|
|
562
|
+
self._grid.shape,
|
|
563
|
+
self.write_shape if block_shape is None else block_shape,
|
|
564
|
+
context=context,
|
|
565
|
+
box=box,
|
|
566
|
+
clip=clip,
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
def write_chunk(self, index: Sequence[int], data: np.ndarray) -> None:
|
|
570
|
+
"""Write the whole write unit at ``index`` on the storage grid (the shard of a sharded
|
|
571
|
+
array); ``data`` has that unit's shape, clipped at the array's end."""
|
|
572
|
+
unit, shape = self.write_shape, self._grid.shape
|
|
573
|
+
index = tuple(int(i) for i in index)
|
|
574
|
+
if len(index) != len(shape):
|
|
575
|
+
raise ValueError(f"{self.path}: index needs one entry per axis of {shape}")
|
|
576
|
+
begin = tuple(i * u for i, u in zip(index, unit))
|
|
577
|
+
if any(i < 0 or b >= n for i, b, n in zip(index, begin, shape)):
|
|
578
|
+
raise ValueError(f"{self.path}: no write unit at {index}")
|
|
579
|
+
end = tuple(min(b + u, n) for b, u, n in zip(begin, unit, shape))
|
|
580
|
+
self.write_index(begin, end, data)
|
|
581
|
+
|
|
554
582
|
def read_index(
|
|
555
583
|
self,
|
|
556
584
|
begin: Sequence[int],
|
|
@@ -1051,11 +1079,12 @@ class Pyramid:
|
|
|
1051
1079
|
|
|
1052
1080
|
def create(
|
|
1053
1081
|
path: str,
|
|
1054
|
-
shape: Sequence[int],
|
|
1055
|
-
dtype,
|
|
1056
|
-
voxel_size,
|
|
1057
|
-
translation,
|
|
1082
|
+
shape: Sequence[int] | None = None,
|
|
1083
|
+
dtype=None,
|
|
1084
|
+
voxel_size=None,
|
|
1085
|
+
translation=None,
|
|
1058
1086
|
*,
|
|
1087
|
+
grid=None,
|
|
1059
1088
|
axes: Sequence[str] | None = None,
|
|
1060
1089
|
units: Units = "nanometer",
|
|
1061
1090
|
chunks: Sequence[int] | None = None,
|
|
@@ -1079,6 +1108,19 @@ def create(
|
|
|
1079
1108
|
raises ``FileExistsError``; with ``"adopt"`` an identical array (shape, dtype, chunks,
|
|
1080
1109
|
shards, placement) is opened for writing and a different one raises ``ValueError``
|
|
1081
1110
|
naming every difference. Returns a writable ``WorldArray``."""
|
|
1111
|
+
if grid is not None:
|
|
1112
|
+
# a Grid planned the output (Behaviour 18): its facts are the array's, chunks and
|
|
1113
|
+
# shards overridable; the placement arguments must not also be given
|
|
1114
|
+
if any(v is not None for v in (shape, voxel_size, translation, axes)):
|
|
1115
|
+
raise ValueError("pass grid= or shape, voxel_size, translation and axes, not both")
|
|
1116
|
+
shape, voxel_size, translation = grid.shape, grid.voxel_size, grid.translation
|
|
1117
|
+
axes, units = grid.axes, grid.units
|
|
1118
|
+
chunks = grid.chunks if chunks is None else chunks
|
|
1119
|
+
shards = grid.shards if shards is None else shards
|
|
1120
|
+
if shape is None or dtype is None or voxel_size is None or translation is None:
|
|
1121
|
+
raise ValueError(
|
|
1122
|
+
"create needs shape, dtype, voxel_size and translation, or grid= and dtype"
|
|
1123
|
+
)
|
|
1082
1124
|
if chunks is None:
|
|
1083
1125
|
raise ValueError("chunks is required: the package never picks a chunk shape")
|
|
1084
1126
|
ndim = len(shape)
|