siibra 1.0a19__py3-none-any.whl → 1.0.1a0__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.
Potentially problematic release.
This version of siibra might be problematic. Click here for more details.
- siibra/VERSION +1 -1
- siibra/__init__.py +3 -3
- siibra/commons.py +0 -46
- siibra/configuration/factory.py +10 -20
- siibra/core/atlas.py +20 -14
- siibra/core/parcellation.py +67 -52
- siibra/core/region.py +133 -123
- siibra/exceptions.py +8 -0
- siibra/experimental/contour.py +6 -6
- siibra/experimental/patch.py +2 -2
- siibra/experimental/plane3d.py +8 -8
- siibra/features/anchor.py +12 -13
- siibra/features/connectivity/regional_connectivity.py +2 -2
- siibra/features/feature.py +14 -16
- siibra/features/tabular/bigbrain_intensity_profile.py +1 -1
- siibra/features/tabular/cell_density_profile.py +97 -63
- siibra/features/tabular/layerwise_cell_density.py +3 -22
- siibra/features/tabular/regional_timeseries_activity.py +2 -2
- siibra/livequeries/allen.py +39 -16
- siibra/livequeries/bigbrain.py +8 -8
- siibra/livequeries/query.py +0 -1
- siibra/locations/__init__.py +9 -9
- siibra/locations/boundingbox.py +29 -24
- siibra/locations/point.py +4 -4
- siibra/locations/{pointset.py → pointcloud.py} +30 -22
- siibra/retrieval/repositories.py +9 -26
- siibra/retrieval/requests.py +19 -2
- siibra/volumes/__init__.py +1 -1
- siibra/volumes/parcellationmap.py +88 -81
- siibra/volumes/providers/neuroglancer.py +62 -36
- siibra/volumes/providers/nifti.py +11 -25
- siibra/volumes/sparsemap.py +124 -245
- siibra/volumes/volume.py +141 -52
- {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/METADATA +16 -3
- {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/RECORD +38 -38
- {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/WHEEL +1 -1
- {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/LICENSE +0 -0
- {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/top_level.txt +0 -0
siibra/locations/__init__.py
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
from .location import Location
|
|
18
18
|
from .point import Point
|
|
19
|
-
from .
|
|
19
|
+
from .pointcloud import PointCloud, from_points
|
|
20
20
|
from .boundingbox import BoundingBox
|
|
21
21
|
|
|
22
22
|
|
|
@@ -36,11 +36,11 @@ def reassign_union(loc0: 'Location', loc1: 'Location') -> 'Location':
|
|
|
36
36
|
Returns
|
|
37
37
|
-------
|
|
38
38
|
Location
|
|
39
|
-
- Point U Point =
|
|
40
|
-
- Point U
|
|
41
|
-
-
|
|
39
|
+
- Point U Point = PointCloud
|
|
40
|
+
- Point U PointCloud = PointCloud
|
|
41
|
+
- PointCloud U PointCloud = PointCloud
|
|
42
42
|
- BoundingBox U BoundingBox = BoundingBox
|
|
43
|
-
- BoundingBox U
|
|
43
|
+
- BoundingBox U PointCloud = BoundingBox
|
|
44
44
|
- BoundingBox U Point = BoundingBox
|
|
45
45
|
- WholeBrain U Location = NotImplementedError
|
|
46
46
|
(all operations are commutative)
|
|
@@ -53,14 +53,14 @@ def reassign_union(loc0: 'Location', loc1: 'Location') -> 'Location':
|
|
|
53
53
|
# location that has its own union method since it is not a part of locations
|
|
54
54
|
# module and to avoid importing Volume here.
|
|
55
55
|
if not all(
|
|
56
|
-
isinstance(loc, (Point,
|
|
56
|
+
isinstance(loc, (Point, PointCloud, BoundingBox)) for loc in [loc0, loc1]
|
|
57
57
|
):
|
|
58
58
|
try:
|
|
59
59
|
return loc1.union(loc0)
|
|
60
60
|
except Exception:
|
|
61
61
|
raise NotImplementedError(f"There are no union method for {(loc0.__class__.__name__, loc1.__class__.__name__)}")
|
|
62
62
|
|
|
63
|
-
# convert Points to
|
|
63
|
+
# convert Points to PointClouds
|
|
64
64
|
loc0, loc1 = [
|
|
65
65
|
from_points([loc]) if isinstance(loc, Point) else loc
|
|
66
66
|
for loc in [loc0, loc1]
|
|
@@ -69,8 +69,8 @@ def reassign_union(loc0: 'Location', loc1: 'Location') -> 'Location':
|
|
|
69
69
|
# adopt the space of the first location
|
|
70
70
|
loc1_w = loc1.warp(loc0.space)
|
|
71
71
|
|
|
72
|
-
if isinstance(loc0,
|
|
73
|
-
if isinstance(loc1_w,
|
|
72
|
+
if isinstance(loc0, PointCloud):
|
|
73
|
+
if isinstance(loc1_w, PointCloud):
|
|
74
74
|
points = list(dict.fromkeys([*loc0, *loc1_w]))
|
|
75
75
|
return from_points(points)
|
|
76
76
|
if isinstance(loc1_w, BoundingBox):
|
siibra/locations/boundingbox.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
"""A box defined by two farthest corner coordinates on a specific space."""
|
|
16
16
|
|
|
17
|
-
from . import point,
|
|
17
|
+
from . import point, pointcloud, location
|
|
18
18
|
|
|
19
19
|
from ..commons import logger
|
|
20
20
|
from ..exceptions import SpaceWarpingFailedError
|
|
@@ -85,7 +85,7 @@ class BoundingBox(location.Location):
|
|
|
85
85
|
self.maxpoint[d] = self.minpoint[d] + minsize
|
|
86
86
|
|
|
87
87
|
if self.volume == 0:
|
|
88
|
-
logger.warning("
|
|
88
|
+
logger.warning(f"Zero-volume bounding box from points {point1} and {point2} in {self.space} space.")
|
|
89
89
|
|
|
90
90
|
@property
|
|
91
91
|
def id(self) -> str:
|
|
@@ -144,16 +144,16 @@ class BoundingBox(location.Location):
|
|
|
144
144
|
if isinstance(other, point.Point):
|
|
145
145
|
warped = other.warp(self.space)
|
|
146
146
|
return other if self.minpoint <= warped <= self.maxpoint else None
|
|
147
|
-
if isinstance(other,
|
|
147
|
+
if isinstance(other, pointcloud.PointCloud):
|
|
148
148
|
points_inside = [p for p in other if self.intersects(p)]
|
|
149
|
-
|
|
149
|
+
if len(points_inside) == 0:
|
|
150
|
+
return None
|
|
151
|
+
result = pointcloud.PointCloud(
|
|
150
152
|
points_inside,
|
|
151
153
|
space=other.space,
|
|
152
154
|
sigma_mm=[p.sigma for p in points_inside]
|
|
153
155
|
)
|
|
154
|
-
if len(result) ==
|
|
155
|
-
return None
|
|
156
|
-
return result[0] if len(result) == 1 else result # if PointSet has single point return as a Point
|
|
156
|
+
return result[0] if len(result) == 1 else result # if PointCloud has single point return as a Point
|
|
157
157
|
|
|
158
158
|
return other.intersection(self)
|
|
159
159
|
|
|
@@ -240,8 +240,8 @@ class BoundingBox(location.Location):
|
|
|
240
240
|
)
|
|
241
241
|
else:
|
|
242
242
|
return self._intersect_bbox(
|
|
243
|
-
|
|
244
|
-
.
|
|
243
|
+
pointcloud
|
|
244
|
+
.PointCloud(XYZ, space=self.space, sigma_mm=voxel_size.max())
|
|
245
245
|
.boundingbox
|
|
246
246
|
)
|
|
247
247
|
|
|
@@ -259,7 +259,7 @@ class BoundingBox(location.Location):
|
|
|
259
259
|
@property
|
|
260
260
|
def corners(self):
|
|
261
261
|
"""
|
|
262
|
-
Returns all 8 corners of the box as a
|
|
262
|
+
Returns all 8 corners of the box as a pointcloud.
|
|
263
263
|
|
|
264
264
|
Note
|
|
265
265
|
----
|
|
@@ -279,7 +279,7 @@ class BoundingBox(location.Location):
|
|
|
279
279
|
TODO: deal with sigma. Currently, returns the mean of min and max point.
|
|
280
280
|
"""
|
|
281
281
|
xs, ys, zs = zip(self.minpoint, self.maxpoint)
|
|
282
|
-
return
|
|
282
|
+
return pointcloud.PointCloud(
|
|
283
283
|
coordinates=[[x, y, z] for x, y, z in product(xs, ys, zs)],
|
|
284
284
|
space=self.space,
|
|
285
285
|
sigma_mm=np.mean([self.minpoint.sigma, self.maxpoint.sigma])
|
|
@@ -360,7 +360,7 @@ class BoundingBox(location.Location):
|
|
|
360
360
|
x1, y1, z1 = self.maxpoint
|
|
361
361
|
|
|
362
362
|
# set of 8 corner points in source space
|
|
363
|
-
corners1 =
|
|
363
|
+
corners1 = pointcloud.PointCloud(
|
|
364
364
|
[
|
|
365
365
|
(x0, y0, z0),
|
|
366
366
|
(x0, y0, z1),
|
|
@@ -417,14 +417,15 @@ class BoundingBox(location.Location):
|
|
|
417
417
|
return super().__hash__()
|
|
418
418
|
|
|
419
419
|
|
|
420
|
-
def _determine_bounds(
|
|
420
|
+
def _determine_bounds(masked_array: np.ndarray, background: float = 0.0):
|
|
421
421
|
"""
|
|
422
|
-
Bounding box of nonzero values in a 3D array.
|
|
422
|
+
Bounding box of nonzero (background) values in a 3D array.
|
|
423
|
+
|
|
423
424
|
https://stackoverflow.com/questions/31400769/bounding-box-of-numpy-array
|
|
424
425
|
"""
|
|
425
|
-
x = np.any(
|
|
426
|
-
y = np.any(
|
|
427
|
-
z = np.any(
|
|
426
|
+
x = np.any(masked_array != background, axis=(1, 2))
|
|
427
|
+
y = np.any(masked_array != background, axis=(0, 2))
|
|
428
|
+
z = np.any(masked_array != background, axis=(0, 1))
|
|
428
429
|
nzx, nzy, nzz = [np.where(v) for v in (x, y, z)]
|
|
429
430
|
if any(len(nz[0]) == 0 for nz in [nzx, nzy, nzz]):
|
|
430
431
|
# empty array
|
|
@@ -435,15 +436,19 @@ def _determine_bounds(array: np.ndarray, threshold=0):
|
|
|
435
436
|
return np.array([[xmin, xmax + 1], [ymin, ymax + 1], [zmin, zmax + 1], [1, 1]])
|
|
436
437
|
|
|
437
438
|
|
|
438
|
-
def from_array(
|
|
439
|
+
def from_array(
|
|
440
|
+
array: np.ndarray,
|
|
441
|
+
background: Union[int, float] = 0.0,
|
|
442
|
+
space: "Space" = None
|
|
443
|
+
) -> BoundingBox:
|
|
439
444
|
"""
|
|
440
|
-
Find the bounding box of
|
|
445
|
+
Find the bounding box of non-background values for any 3D array.
|
|
441
446
|
|
|
442
447
|
Parameters
|
|
443
448
|
----------
|
|
444
|
-
array
|
|
445
|
-
|
|
446
|
-
space
|
|
449
|
+
array: np.ndarray
|
|
450
|
+
background: int or float, default: 0.0
|
|
451
|
+
space: Space, default: None
|
|
447
452
|
"""
|
|
448
|
-
bounds = _determine_bounds(array,
|
|
449
|
-
return BoundingBox(bounds[:3, 0], bounds[:3, 1], space=space)
|
|
453
|
+
bounds = _determine_bounds(array, background)
|
|
454
|
+
return BoundingBox(point1=bounds[:3, 0], point2=bounds[:3, 1], space=space)
|
siibra/locations/point.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
"""Singular coordinate defined on a space, possibly with an uncertainty."""
|
|
16
16
|
|
|
17
|
-
from . import location, boundingbox,
|
|
17
|
+
from . import location, boundingbox, pointcloud
|
|
18
18
|
|
|
19
19
|
from ..commons import logger
|
|
20
20
|
from ..retrieval.requests import HttpRequest
|
|
@@ -115,7 +115,7 @@ class Point(location.Location):
|
|
|
115
115
|
def intersection(self, other: location.Location) -> "Point":
|
|
116
116
|
if isinstance(other, Point):
|
|
117
117
|
return self if self == other else None
|
|
118
|
-
elif isinstance(other,
|
|
118
|
+
elif isinstance(other, pointcloud.PointCloud):
|
|
119
119
|
return self if self in other else None
|
|
120
120
|
else:
|
|
121
121
|
return self if other.intersection(self) else None
|
|
@@ -190,8 +190,8 @@ class Point(location.Location):
|
|
|
190
190
|
return super().__hash__()
|
|
191
191
|
|
|
192
192
|
def __eq__(self, other: 'Point'):
|
|
193
|
-
if isinstance(other,
|
|
194
|
-
return other == self # implemented at
|
|
193
|
+
if isinstance(other, pointcloud.PointCloud):
|
|
194
|
+
return other == self # implemented at pointcloud
|
|
195
195
|
if not isinstance(other, Point):
|
|
196
196
|
return False
|
|
197
197
|
o = other if self.space is None else other.warp(self.space)
|
|
@@ -18,7 +18,7 @@ from . import location, point, boundingbox as _boundingbox
|
|
|
18
18
|
|
|
19
19
|
from ..retrieval.requests import HttpRequest
|
|
20
20
|
from ..commons import logger
|
|
21
|
-
from ..exceptions import SpaceWarpingFailedError
|
|
21
|
+
from ..exceptions import SpaceWarpingFailedError, EmptyPointCloudError
|
|
22
22
|
|
|
23
23
|
from typing import List, Union, Tuple
|
|
24
24
|
import numbers
|
|
@@ -32,13 +32,13 @@ except ImportError:
|
|
|
32
32
|
_HAS_HDBSCAN = False
|
|
33
33
|
logger.warning(
|
|
34
34
|
f"HDBSCAN is not available with your version {sklearn.__version__} of sckit-learn."
|
|
35
|
-
"`
|
|
35
|
+
"`PointCloud.find_clusters()` will not be avaiable."
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
def from_points(points: List["point.Point"], newlabels: List[Union[int, float, tuple]] = None) -> "
|
|
39
|
+
def from_points(points: List["point.Point"], newlabels: List[Union[int, float, tuple]] = None) -> "PointCloud":
|
|
40
40
|
"""
|
|
41
|
-
Create a
|
|
41
|
+
Create a PointCloud from an iterable of Points.
|
|
42
42
|
|
|
43
43
|
Parameters
|
|
44
44
|
----------
|
|
@@ -48,16 +48,17 @@ def from_points(points: List["point.Point"], newlabels: List[Union[int, float, t
|
|
|
48
48
|
|
|
49
49
|
Returns
|
|
50
50
|
-------
|
|
51
|
-
|
|
51
|
+
PointCloud
|
|
52
52
|
"""
|
|
53
53
|
if len(points) == 0:
|
|
54
|
-
|
|
54
|
+
raise EmptyPointCloudError("Cannot create a PointCloud without any points.")
|
|
55
|
+
|
|
55
56
|
spaces = {p.space for p in points}
|
|
56
|
-
assert len(spaces) == 1, f"
|
|
57
|
+
assert len(spaces) == 1, f"PointCloud can only be constructed with points from the same space.\n{spaces}"
|
|
57
58
|
coords, sigmas, labels = zip(*((p.coordinate, p.sigma, p.label) for p in points))
|
|
58
59
|
if all(lb is None for lb in set(labels)):
|
|
59
60
|
labels = None
|
|
60
|
-
return
|
|
61
|
+
return PointCloud(
|
|
61
62
|
coordinates=coords,
|
|
62
63
|
space=next(iter(spaces)),
|
|
63
64
|
sigma_mm=sigmas,
|
|
@@ -65,7 +66,7 @@ def from_points(points: List["point.Point"], newlabels: List[Union[int, float, t
|
|
|
65
66
|
)
|
|
66
67
|
|
|
67
68
|
|
|
68
|
-
class
|
|
69
|
+
class PointCloud(location.Location):
|
|
69
70
|
"""A set of 3D points in the same reference space,
|
|
70
71
|
defined by a list of coordinates."""
|
|
71
72
|
|
|
@@ -91,6 +92,9 @@ class PointSet(location.Location):
|
|
|
91
92
|
"""
|
|
92
93
|
location.Location.__init__(self, space)
|
|
93
94
|
|
|
95
|
+
if len(coordinates) == 0:
|
|
96
|
+
raise EmptyPointCloudError(f"Cannot create a {self.__class__.__name__} without any coordinates.")
|
|
97
|
+
|
|
94
98
|
self._coordinates = coordinates
|
|
95
99
|
if not isinstance(coordinates, np.ndarray):
|
|
96
100
|
self._coordinates = np.array(self._coordinates).reshape((-1, 3))
|
|
@@ -113,7 +117,7 @@ class PointSet(location.Location):
|
|
|
113
117
|
NOTE: The affine matrix of the image must be set to warp voxels
|
|
114
118
|
coordinates into the reference space of this Bounding Box.
|
|
115
119
|
"""
|
|
116
|
-
if not isinstance(other, (point.Point,
|
|
120
|
+
if not isinstance(other, (point.Point, PointCloud, _boundingbox.BoundingBox)):
|
|
117
121
|
return other.intersection(self)
|
|
118
122
|
|
|
119
123
|
intersections = [(i, p) for i, p in enumerate(self) if p.intersects(other)]
|
|
@@ -122,7 +126,7 @@ class PointSet(location.Location):
|
|
|
122
126
|
ids, points = zip(*intersections)
|
|
123
127
|
labels = None if self.labels is None else [self.labels[i] for i in ids]
|
|
124
128
|
sigma = [p.sigma for p in points]
|
|
125
|
-
intersection =
|
|
129
|
+
intersection = PointCloud(
|
|
126
130
|
points,
|
|
127
131
|
space=self.space,
|
|
128
132
|
sigma_mm=sigma,
|
|
@@ -186,7 +190,7 @@ class PointSet(location.Location):
|
|
|
186
190
|
return self.__class__(coordinates=tuple(tgt_points), space=spaceobj, labels=self.labels)
|
|
187
191
|
|
|
188
192
|
def transform(self, affine: np.ndarray, space=None):
|
|
189
|
-
"""Returns a new
|
|
193
|
+
"""Returns a new PointCloud obtained by transforming the
|
|
190
194
|
coordinates of this one with the given affine matrix.
|
|
191
195
|
|
|
192
196
|
Parameters
|
|
@@ -207,7 +211,7 @@ class PointSet(location.Location):
|
|
|
207
211
|
def __getitem__(self, index: int):
|
|
208
212
|
if (abs(index) >= self.__len__()):
|
|
209
213
|
raise IndexError(
|
|
210
|
-
f"
|
|
214
|
+
f"pointcloud with {self.__len__()} points "
|
|
211
215
|
f"cannot be accessed with index {index}."
|
|
212
216
|
)
|
|
213
217
|
return point.Point(
|
|
@@ -229,10 +233,10 @@ class PointSet(location.Location):
|
|
|
229
233
|
for i in range(len(self))
|
|
230
234
|
)
|
|
231
235
|
|
|
232
|
-
def __eq__(self, other: '
|
|
236
|
+
def __eq__(self, other: 'PointCloud'):
|
|
233
237
|
if isinstance(other, point.Point):
|
|
234
238
|
return len(self) == 1 and self[0] == other
|
|
235
|
-
if not isinstance(other,
|
|
239
|
+
if not isinstance(other, PointCloud):
|
|
236
240
|
return False
|
|
237
241
|
return list(self) == list(other)
|
|
238
242
|
|
|
@@ -240,7 +244,7 @@ class PointSet(location.Location):
|
|
|
240
244
|
return super().__hash__()
|
|
241
245
|
|
|
242
246
|
def __len__(self):
|
|
243
|
-
"""The number of points in this
|
|
247
|
+
"""The number of points in this PointCloud."""
|
|
244
248
|
return self.coordinates.shape[0]
|
|
245
249
|
|
|
246
250
|
def __str__(self):
|
|
@@ -249,9 +253,13 @@ class PointSet(location.Location):
|
|
|
249
253
|
@property
|
|
250
254
|
def boundingbox(self):
|
|
251
255
|
"""
|
|
252
|
-
Return the bounding box of these points
|
|
256
|
+
Return the bounding box of these points, or None in the
|
|
257
|
+
special case of an empty PointCloud.
|
|
253
258
|
"""
|
|
259
|
+
if len(self.coordinates) == 0:
|
|
260
|
+
return None
|
|
254
261
|
coords = self.coordinates
|
|
262
|
+
# TODO this needs a more precise treatment of the sigmas
|
|
255
263
|
sigma_min = max(self.sigma[i] for i in coords.argmin(0))
|
|
256
264
|
sigma_max = max(self.sigma[i] for i in coords.argmax(0))
|
|
257
265
|
return _boundingbox.BoundingBox(
|
|
@@ -292,8 +300,8 @@ class PointSet(location.Location):
|
|
|
292
300
|
|
|
293
301
|
Parameters
|
|
294
302
|
----------
|
|
295
|
-
min_fraction: min cluster size as a fraction of total points in the
|
|
296
|
-
max_fraction: max cluster size as a fraction of total points in the
|
|
303
|
+
min_fraction: min cluster size as a fraction of total points in the PointCloud
|
|
304
|
+
max_fraction: max cluster size as a fraction of total points in the PointCloud
|
|
297
305
|
|
|
298
306
|
Returns
|
|
299
307
|
-------
|
|
@@ -302,7 +310,7 @@ class PointSet(location.Location):
|
|
|
302
310
|
|
|
303
311
|
Note
|
|
304
312
|
----
|
|
305
|
-
Replaces the labels of the
|
|
313
|
+
Replaces the labels of the PointCloud instance with these labels.
|
|
306
314
|
|
|
307
315
|
Raises
|
|
308
316
|
------
|
|
@@ -312,7 +320,7 @@ class PointSet(location.Location):
|
|
|
312
320
|
if not _HAS_HDBSCAN:
|
|
313
321
|
raise RuntimeError(
|
|
314
322
|
f"HDBSCAN is not available with your version {sklearn.__version__} "
|
|
315
|
-
"of sckit-learn. `
|
|
323
|
+
"of sckit-learn. `PointCloud.find_clusters()` will not be avaiable."
|
|
316
324
|
)
|
|
317
325
|
points = np.array(self.as_list())
|
|
318
326
|
N = points.shape[0]
|
|
@@ -322,7 +330,7 @@ class PointSet(location.Location):
|
|
|
322
330
|
)
|
|
323
331
|
if self.labels is not None:
|
|
324
332
|
logger.warning(
|
|
325
|
-
"Existing labels of
|
|
333
|
+
"Existing labels of PointCloud will be overwritten with cluster labels."
|
|
326
334
|
)
|
|
327
335
|
self.labels = clustering.fit_predict(points)
|
|
328
336
|
return self.labels
|
siibra/retrieval/repositories.py
CHANGED
|
@@ -19,7 +19,8 @@ from .requests import (
|
|
|
19
19
|
EbrainsRequest,
|
|
20
20
|
SiibraHttpRequestError,
|
|
21
21
|
find_suitiable_decoder,
|
|
22
|
-
DECODERS
|
|
22
|
+
DECODERS,
|
|
23
|
+
FileLoader
|
|
23
24
|
)
|
|
24
25
|
from .cache import CACHE
|
|
25
26
|
|
|
@@ -124,34 +125,16 @@ class LocalFileRepository(RepositoryConnector):
|
|
|
124
125
|
self._folder = pathlib.Path(folder)
|
|
125
126
|
assert pathlib.Path.is_dir(self._folder)
|
|
126
127
|
|
|
127
|
-
def _build_url(self, folder: str, filename: str):
|
|
128
|
-
return pathlib.Path.joinpath(self._folder, folder, filename)
|
|
129
|
-
|
|
130
|
-
class FileLoader:
|
|
131
|
-
"""
|
|
132
|
-
Just a loads a local file, but mimics the behaviour
|
|
133
|
-
of cached http requests used in other connectors.
|
|
134
|
-
"""
|
|
135
|
-
def __init__(self, file_url, decode_func):
|
|
136
|
-
self.url = file_url
|
|
137
|
-
self.func = decode_func
|
|
138
|
-
self.cached = True
|
|
139
|
-
|
|
140
|
-
@property
|
|
141
|
-
def data(self):
|
|
142
|
-
with open(self.url, 'rb') as f:
|
|
143
|
-
return self.func(f.read())
|
|
128
|
+
def _build_url(self, folder: str, filename: str) -> str:
|
|
129
|
+
return pathlib.Path.joinpath(self._folder, folder, filename).as_posix()
|
|
144
130
|
|
|
145
131
|
def get_loader(self, filename, folder="", decode_func=None):
|
|
146
132
|
"""Get a lazy loader for a file, for loading data
|
|
147
133
|
only once loader.data is accessed."""
|
|
148
|
-
|
|
149
|
-
if
|
|
150
|
-
raise RuntimeError(f"
|
|
151
|
-
|
|
152
|
-
return self.FileLoader(url, lambda b: self._decode_response(b, filename))
|
|
153
|
-
else:
|
|
154
|
-
return self.FileLoader(url, decode_func)
|
|
134
|
+
filepath = self._build_url(folder, filename)
|
|
135
|
+
if not pathlib.Path(filepath).is_file():
|
|
136
|
+
raise RuntimeError(f"No file is found in {filepath}")
|
|
137
|
+
return FileLoader(filepath, decode_func)
|
|
155
138
|
|
|
156
139
|
def search_files(self, folder="", suffix=None, recursive=False):
|
|
157
140
|
results = []
|
|
@@ -165,7 +148,7 @@ class LocalFileRepository(RepositoryConnector):
|
|
|
165
148
|
def __str__(self):
|
|
166
149
|
return f"{self.__class__.__name__} at {self._folder}"
|
|
167
150
|
|
|
168
|
-
def __eq__(self, other):
|
|
151
|
+
def __eq__(self, other: "LocalFileRepository"):
|
|
169
152
|
return self._folder == other._folder
|
|
170
153
|
|
|
171
154
|
|
siibra/retrieval/requests.py
CHANGED
|
@@ -117,8 +117,7 @@ def find_suitiable_decoder(url: str) -> Callable:
|
|
|
117
117
|
suitable_decoders = [
|
|
118
118
|
dec for sfx, dec in DECODERS.items() if urlpath.endswith(sfx)
|
|
119
119
|
]
|
|
120
|
-
if len(suitable_decoders)
|
|
121
|
-
assert len(suitable_decoders) == 1
|
|
120
|
+
if len(suitable_decoders) == 1:
|
|
122
121
|
return suitable_decoders[0]
|
|
123
122
|
else:
|
|
124
123
|
return None
|
|
@@ -270,6 +269,24 @@ class HttpRequest:
|
|
|
270
269
|
return self.get()
|
|
271
270
|
|
|
272
271
|
|
|
272
|
+
class FileLoader(HttpRequest):
|
|
273
|
+
"""
|
|
274
|
+
Just a loads a local file, but mimics the behaviour
|
|
275
|
+
of cached http requests used in other connectors.
|
|
276
|
+
"""
|
|
277
|
+
def __init__(self, filepath, func=None):
|
|
278
|
+
HttpRequest.__init__(
|
|
279
|
+
self, filepath, refresh=False,
|
|
280
|
+
func=func or find_suitiable_decoder(filepath)
|
|
281
|
+
)
|
|
282
|
+
self.cachefile = filepath
|
|
283
|
+
|
|
284
|
+
def _retrieve(self, **kwargs):
|
|
285
|
+
if kwargs:
|
|
286
|
+
logger.info(f"Keywords {list(kwargs.keys())} are supplied but won't be used.")
|
|
287
|
+
assert os.path.isfile(self.cachefile)
|
|
288
|
+
|
|
289
|
+
|
|
273
290
|
class ZipfileRequest(HttpRequest):
|
|
274
291
|
def __init__(self, url, filename, func=None, refresh=False):
|
|
275
292
|
HttpRequest.__init__(
|
siibra/volumes/__init__.py
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
from .parcellationmap import Map
|
|
18
18
|
from .providers import provider
|
|
19
|
-
from .volume import from_array, from_file,
|
|
19
|
+
from .volume import from_array, from_file, from_pointcloud, from_nifti, Volume
|
|
20
20
|
|
|
21
21
|
from ..commons import logger
|
|
22
22
|
from typing import List, Union
|