siibra 1.0a11__py3-none-any.whl → 1.0a19__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 +12 -2
- siibra/commons.py +4 -3
- siibra/configuration/__init__.py +1 -1
- siibra/configuration/configuration.py +1 -1
- siibra/configuration/factory.py +164 -117
- siibra/core/__init__.py +1 -1
- siibra/core/assignment.py +1 -1
- siibra/core/atlas.py +4 -3
- siibra/core/concept.py +18 -9
- siibra/core/parcellation.py +9 -3
- siibra/core/region.py +35 -65
- siibra/core/space.py +3 -1
- siibra/core/structure.py +1 -2
- siibra/exceptions.py +9 -1
- siibra/explorer/__init__.py +1 -1
- siibra/explorer/url.py +15 -0
- siibra/explorer/util.py +1 -1
- siibra/features/__init__.py +1 -1
- siibra/features/anchor.py +1 -1
- siibra/features/connectivity/__init__.py +1 -1
- siibra/features/connectivity/functional_connectivity.py +1 -1
- siibra/features/connectivity/regional_connectivity.py +5 -3
- siibra/features/connectivity/streamline_counts.py +1 -1
- siibra/features/connectivity/streamline_lengths.py +1 -1
- siibra/features/connectivity/tracing_connectivity.py +1 -1
- siibra/features/dataset/__init__.py +1 -1
- siibra/features/dataset/ebrains.py +1 -1
- siibra/features/feature.py +42 -15
- siibra/features/image/__init__.py +1 -1
- siibra/features/image/image.py +18 -13
- siibra/features/image/sections.py +1 -1
- siibra/features/image/volume_of_interest.py +1 -1
- siibra/features/tabular/__init__.py +1 -1
- siibra/features/tabular/bigbrain_intensity_profile.py +1 -1
- siibra/features/tabular/cell_density_profile.py +5 -3
- siibra/features/tabular/cortical_profile.py +5 -3
- siibra/features/tabular/gene_expression.py +2 -2
- siibra/features/tabular/layerwise_bigbrain_intensities.py +1 -1
- siibra/features/tabular/layerwise_cell_density.py +5 -3
- siibra/features/tabular/receptor_density_fingerprint.py +5 -3
- siibra/features/tabular/receptor_density_profile.py +5 -3
- siibra/features/tabular/regional_timeseries_activity.py +5 -3
- siibra/features/tabular/tabular.py +5 -3
- siibra/livequeries/__init__.py +1 -1
- siibra/livequeries/allen.py +9 -6
- siibra/livequeries/bigbrain.py +1 -1
- siibra/livequeries/ebrains.py +1 -1
- siibra/livequeries/query.py +1 -1
- siibra/locations/__init__.py +1 -1
- siibra/locations/boundingbox.py +51 -17
- siibra/locations/location.py +12 -4
- siibra/locations/point.py +10 -5
- siibra/locations/pointset.py +45 -11
- siibra/retrieval/__init__.py +1 -1
- siibra/retrieval/cache.py +1 -1
- siibra/retrieval/datasets.py +1 -1
- siibra/retrieval/exceptions/__init__.py +1 -1
- siibra/retrieval/repositories.py +1 -1
- siibra/retrieval/requests.py +1 -1
- siibra/vocabularies/__init__.py +1 -1
- siibra/volumes/__init__.py +1 -1
- siibra/volumes/parcellationmap.py +38 -18
- siibra/volumes/providers/__init__.py +1 -1
- siibra/volumes/providers/freesurfer.py +1 -1
- siibra/volumes/providers/gifti.py +1 -1
- siibra/volumes/providers/neuroglancer.py +7 -7
- siibra/volumes/providers/nifti.py +8 -4
- siibra/volumes/providers/provider.py +2 -2
- siibra/volumes/sparsemap.py +7 -4
- siibra/volumes/volume.py +114 -16
- {siibra-1.0a11.dist-info → siibra-1.0a19.dist-info}/METADATA +3 -3
- siibra-1.0a19.dist-info/RECORD +84 -0
- {siibra-1.0a11.dist-info → siibra-1.0a19.dist-info}/WHEEL +1 -1
- siibra-1.0a11.dist-info/RECORD +0 -84
- {siibra-1.0a11.dist-info → siibra-1.0a19.dist-info}/LICENSE +0 -0
- {siibra-1.0a11.dist-info → siibra-1.0a19.dist-info}/top_level.txt +0 -0
siibra/core/__init__.py
CHANGED
siibra/core/assignment.py
CHANGED
siibra/core/atlas.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2024
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -30,14 +30,15 @@ class Atlas(concept.AtlasConcept, configuration_folder="atlases"):
|
|
|
30
30
|
spaces, as well as common functionalities of those.
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
def __init__(self, identifier: str, name: str, species: Species):
|
|
33
|
+
def __init__(self, identifier: str, name: str, species: Species, **kwargs):
|
|
34
34
|
"""Construct an empty atlas object with a name and identifier."""
|
|
35
35
|
|
|
36
36
|
concept.AtlasConcept.__init__(
|
|
37
37
|
self,
|
|
38
38
|
identifier=identifier,
|
|
39
39
|
name=name,
|
|
40
|
-
species=species
|
|
40
|
+
species=species,
|
|
41
|
+
**kwargs
|
|
41
42
|
)
|
|
42
43
|
self._parcellation_ids: List[str] = []
|
|
43
44
|
self._space_ids: List[str] = []
|
siibra/core/concept.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2024
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -67,7 +67,8 @@ class AtlasConcept:
|
|
|
67
67
|
modality: str = "",
|
|
68
68
|
publications: List[TypePublication] = [],
|
|
69
69
|
datasets: List['TypeDataset'] = [],
|
|
70
|
-
spec=None
|
|
70
|
+
spec=None,
|
|
71
|
+
prerelease: bool = False,
|
|
71
72
|
):
|
|
72
73
|
"""
|
|
73
74
|
Construct a new atlas concept base object.
|
|
@@ -94,7 +95,7 @@ class AtlasConcept:
|
|
|
94
95
|
The preconfigured specification.
|
|
95
96
|
"""
|
|
96
97
|
self._id = identifier
|
|
97
|
-
self.name = name
|
|
98
|
+
self.name = name if not prerelease else f"[PRERELEASE] {name}"
|
|
98
99
|
self._species_cached = None if species is None \
|
|
99
100
|
else Species.decode(species) # overwritable property implementation below
|
|
100
101
|
self.shortname = shortname
|
|
@@ -104,6 +105,7 @@ class AtlasConcept:
|
|
|
104
105
|
self.datasets = datasets
|
|
105
106
|
self._spec = spec
|
|
106
107
|
self._CACHED_MATCHES = {} # we cache match() function results
|
|
108
|
+
self._prerelease = prerelease
|
|
107
109
|
|
|
108
110
|
@property
|
|
109
111
|
def description(self):
|
|
@@ -116,12 +118,19 @@ class AtlasConcept:
|
|
|
116
118
|
|
|
117
119
|
@property
|
|
118
120
|
def LICENSE(self) -> str:
|
|
119
|
-
licenses =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
licenses = []
|
|
122
|
+
for ds in self.datasets:
|
|
123
|
+
if ds.LICENSE is None or ds.LICENSE == "No license information is found.":
|
|
124
|
+
continue
|
|
125
|
+
if isinstance(ds.LICENSE, str):
|
|
126
|
+
licenses.append(ds.LICENSE)
|
|
127
|
+
if isinstance(ds.LICENSE, list):
|
|
128
|
+
licenses.extend(ds.LICENSE)
|
|
129
|
+
if len(licenses) == 0:
|
|
130
|
+
logger.warning("No license information is found.")
|
|
131
|
+
return ""
|
|
132
|
+
if len(licenses) > 1:
|
|
133
|
+
logger.info("Found multiple licenses corresponding to datasets.")
|
|
125
134
|
return '\n'.join(licenses)
|
|
126
135
|
|
|
127
136
|
@property
|
siibra/core/parcellation.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2024
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -82,6 +82,7 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
|
|
|
82
82
|
modality: str = None,
|
|
83
83
|
publications: list = [],
|
|
84
84
|
datasets: list = [],
|
|
85
|
+
prerelease: bool = False,
|
|
85
86
|
):
|
|
86
87
|
"""
|
|
87
88
|
Constructs a new parcellation object.
|
|
@@ -118,7 +119,8 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
|
|
|
118
119
|
description=description,
|
|
119
120
|
publications=publications,
|
|
120
121
|
datasets=datasets,
|
|
121
|
-
modality=modality
|
|
122
|
+
modality=modality,
|
|
123
|
+
prerelease=prerelease,
|
|
122
124
|
)
|
|
123
125
|
self._species_cached = Species.decode(species)
|
|
124
126
|
self._id = identifier
|
|
@@ -309,7 +311,11 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
|
|
|
309
311
|
|
|
310
312
|
# if there exist an exact match of region spec to region name, return
|
|
311
313
|
if isinstance(regionspec, str):
|
|
312
|
-
exact_match = [
|
|
314
|
+
exact_match = [
|
|
315
|
+
region
|
|
316
|
+
for region in self
|
|
317
|
+
if region.name == regionspec or region.key == regionspec
|
|
318
|
+
]
|
|
313
319
|
if len(exact_match) == 1:
|
|
314
320
|
return exact_match[0]
|
|
315
321
|
if len(exact_match) > 1:
|
siibra/core/region.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2024
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -18,12 +18,11 @@ from . import concept, structure, space as _space, parcellation as _parcellation
|
|
|
18
18
|
from .assignment import Qualification, AnatomicalAssignment
|
|
19
19
|
|
|
20
20
|
from ..retrieval.cache import cache_user_fn
|
|
21
|
-
from ..locations import location,
|
|
21
|
+
from ..locations import location, pointset, boundingbox as _boundingbox
|
|
22
22
|
from ..volumes import parcellationmap, volume
|
|
23
23
|
from ..commons import (
|
|
24
24
|
logger,
|
|
25
25
|
MapType,
|
|
26
|
-
affine_scaling,
|
|
27
26
|
create_key,
|
|
28
27
|
clear_name,
|
|
29
28
|
InstanceTable,
|
|
@@ -37,7 +36,6 @@ import re
|
|
|
37
36
|
import anytree
|
|
38
37
|
from typing import List, Union, Iterable, Dict, Callable, Tuple
|
|
39
38
|
from difflib import SequenceMatcher
|
|
40
|
-
from dataclasses import dataclass, field
|
|
41
39
|
from ebrains_drive import BucketApiClient
|
|
42
40
|
import json
|
|
43
41
|
from functools import wraps, reduce
|
|
@@ -49,19 +47,6 @@ REGEX_TYPE = type(re.compile("test"))
|
|
|
49
47
|
THRESHOLD_STATISTICAL_MAPS = None
|
|
50
48
|
|
|
51
49
|
|
|
52
|
-
@dataclass
|
|
53
|
-
class SpatialPropCmpt:
|
|
54
|
-
centroid: point.Point
|
|
55
|
-
volume: int
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
@dataclass
|
|
59
|
-
class SpatialProp:
|
|
60
|
-
cog: SpatialPropCmpt = None
|
|
61
|
-
components: List[SpatialPropCmpt] = field(default_factory=list)
|
|
62
|
-
space: _space.Space = None
|
|
63
|
-
|
|
64
|
-
|
|
65
50
|
class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
66
51
|
"""
|
|
67
52
|
Representation of a region with name and more optional attributes
|
|
@@ -85,6 +70,7 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
85
70
|
datasets: list = [],
|
|
86
71
|
rgb: str = None,
|
|
87
72
|
spec=None,
|
|
73
|
+
prerelease: bool = False,
|
|
88
74
|
):
|
|
89
75
|
"""
|
|
90
76
|
Constructs a new Region object.
|
|
@@ -122,7 +108,8 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
122
108
|
modality=modality,
|
|
123
109
|
publications=publications,
|
|
124
110
|
datasets=datasets,
|
|
125
|
-
spec=spec
|
|
111
|
+
spec=spec,
|
|
112
|
+
prerelease=prerelease,
|
|
126
113
|
)
|
|
127
114
|
|
|
128
115
|
# anytree node will take care to use this appropriately
|
|
@@ -646,17 +633,29 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
646
633
|
self._ASSIGNMENT_CACHE[self, other] = regionmap.assign(other)
|
|
647
634
|
return self._ASSIGNMENT_CACHE[self, other]
|
|
648
635
|
|
|
636
|
+
if isinstance(other, _boundingbox.BoundingBox): # volume.intersection(bbox) gets boundingbox anyway
|
|
637
|
+
try:
|
|
638
|
+
regionbbox_otherspace = self.get_boundingbox(other.space, restrict_space=False)
|
|
639
|
+
if regionbbox_otherspace is not None:
|
|
640
|
+
self._ASSIGNMENT_CACHE[self, other] = regionbbox_otherspace.assign(other)
|
|
641
|
+
return self._ASSIGNMENT_CACHE[self, other]
|
|
642
|
+
except Exception as e:
|
|
643
|
+
logger.debug(e)
|
|
644
|
+
|
|
649
645
|
assignment_result = None
|
|
650
|
-
for
|
|
646
|
+
for targetspace in self.supported_spaces:
|
|
651
647
|
try:
|
|
652
|
-
other_warped = other.warp(
|
|
653
|
-
regionmap = self.get_regional_map(
|
|
648
|
+
other_warped = other.warp(targetspace)
|
|
649
|
+
regionmap = self.get_regional_map(targetspace)
|
|
654
650
|
assignment_result = regionmap.assign(other_warped)
|
|
655
651
|
except SpaceWarpingFailedError:
|
|
656
652
|
try:
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
)
|
|
653
|
+
regionbbox_targetspace = self.get_boundingbox(
|
|
654
|
+
targetspace, restrict_space=True
|
|
655
|
+
)
|
|
656
|
+
if regionbbox_targetspace is None:
|
|
657
|
+
continue
|
|
658
|
+
regionbbox_warped = regionbbox_targetspace.warp(other.space)
|
|
660
659
|
except SpaceWarpingFailedError:
|
|
661
660
|
continue
|
|
662
661
|
assignment_result = regionbbox_warped.assign(other)
|
|
@@ -698,7 +697,7 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
698
697
|
space: _space.Space,
|
|
699
698
|
maptype: MapType = MapType.LABELLED,
|
|
700
699
|
threshold_statistical=None,
|
|
701
|
-
restrict_space=
|
|
700
|
+
restrict_space=True,
|
|
702
701
|
**fetch_kwargs
|
|
703
702
|
):
|
|
704
703
|
"""
|
|
@@ -745,7 +744,7 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
745
744
|
bbox_warped = bbox.warp(spaceobj)
|
|
746
745
|
except SpaceWarpingFailedError:
|
|
747
746
|
continue
|
|
748
|
-
logger.
|
|
747
|
+
logger.debug(
|
|
749
748
|
f"No bounding box for {self.name} defined in {spaceobj.name}, "
|
|
750
749
|
f"warped the bounding box from {other_space.name} instead."
|
|
751
750
|
)
|
|
@@ -776,7 +775,7 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
776
775
|
"""
|
|
777
776
|
props = self.spatial_props(space)
|
|
778
777
|
return pointset.PointSet(
|
|
779
|
-
[c.centroid for c in props
|
|
778
|
+
[c.centroid for c in props],
|
|
780
779
|
space=space
|
|
781
780
|
)
|
|
782
781
|
|
|
@@ -785,12 +784,10 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
785
784
|
space: _space.Space,
|
|
786
785
|
maptype: MapType = MapType.LABELLED,
|
|
787
786
|
threshold_statistical=None,
|
|
788
|
-
)
|
|
787
|
+
):
|
|
789
788
|
"""
|
|
790
789
|
Compute spatial properties for connected components of this region in the given space.
|
|
791
790
|
|
|
792
|
-
TODO: this should go to the Volume class and just be called from here.
|
|
793
|
-
|
|
794
791
|
Parameters
|
|
795
792
|
----------
|
|
796
793
|
space: Space
|
|
@@ -808,49 +805,22 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
808
805
|
Dict
|
|
809
806
|
Dictionary of region's spatial properties
|
|
810
807
|
"""
|
|
811
|
-
from skimage import measure
|
|
812
|
-
|
|
813
808
|
if not isinstance(space, _space.Space):
|
|
814
809
|
space = _space.Space.get_instance(space)
|
|
815
810
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
811
|
+
# build binary mask of the image
|
|
812
|
+
try:
|
|
813
|
+
region_vol = self.get_regional_map(
|
|
814
|
+
space, maptype=maptype, threshold=threshold_statistical
|
|
815
|
+
)
|
|
816
|
+
except NoMapAvailableError:
|
|
817
|
+
raise ValueError(
|
|
820
818
|
f"Spatial properties of {self.name} cannot be computed in {space.name}. "
|
|
821
819
|
"This region is only mapped in these spaces: "
|
|
822
820
|
f"{', '.join(s.name for s in self.supported_spaces)}"
|
|
823
821
|
)
|
|
824
|
-
return result
|
|
825
822
|
|
|
826
|
-
|
|
827
|
-
pimg = self.get_regional_map(
|
|
828
|
-
space, maptype=maptype, threshold=threshold_statistical
|
|
829
|
-
).fetch()
|
|
830
|
-
|
|
831
|
-
# determine scaling factor from voxels to cube mm
|
|
832
|
-
scale = affine_scaling(pimg.affine)
|
|
833
|
-
|
|
834
|
-
# compute properties of labelled volume
|
|
835
|
-
A = np.asarray(pimg.get_fdata(), dtype=np.int32).squeeze()
|
|
836
|
-
C = measure.label(A)
|
|
837
|
-
|
|
838
|
-
# compute spatial properties of each connected component
|
|
839
|
-
for label in range(1, C.max() + 1):
|
|
840
|
-
nonzero = np.c_[np.nonzero(C == label)]
|
|
841
|
-
result.components.append(
|
|
842
|
-
SpatialPropCmpt(
|
|
843
|
-
centroid=point.Point(
|
|
844
|
-
np.dot(pimg.affine, np.r_[nonzero.mean(0), 1])[:3], space=space
|
|
845
|
-
),
|
|
846
|
-
volume=nonzero.shape[0] * scale,
|
|
847
|
-
)
|
|
848
|
-
)
|
|
849
|
-
|
|
850
|
-
# sort by volume
|
|
851
|
-
result.components.sort(key=lambda cmp: cmp.volume, reverse=True)
|
|
852
|
-
|
|
853
|
-
return result
|
|
823
|
+
return region_vol.compute_spatial_props()
|
|
854
824
|
|
|
855
825
|
def __iter__(self):
|
|
856
826
|
"""
|
siibra/core/space.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2024
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -38,6 +38,7 @@ class Space(AtlasConcept, configuration_folder="spaces"):
|
|
|
38
38
|
modality: str = "",
|
|
39
39
|
publications: list = [],
|
|
40
40
|
datasets: list = [],
|
|
41
|
+
prerelease: bool = False,
|
|
41
42
|
):
|
|
42
43
|
"""
|
|
43
44
|
Constructs a new parcellation object.
|
|
@@ -75,6 +76,7 @@ class Space(AtlasConcept, configuration_folder="spaces"):
|
|
|
75
76
|
modality=modality,
|
|
76
77
|
publications=publications,
|
|
77
78
|
datasets=datasets,
|
|
79
|
+
prerelease=prerelease,
|
|
78
80
|
)
|
|
79
81
|
self.volumes = volumes
|
|
80
82
|
for v in self.volumes:
|
siibra/core/structure.py
CHANGED
siibra/exceptions.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2024
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -45,3 +45,11 @@ class NoVolumeFound(RuntimeError):
|
|
|
45
45
|
|
|
46
46
|
class WarmupRegException(Exception):
|
|
47
47
|
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ZeroVolumeBoundingBox(Exception):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class NoneCoordinateSuppliedError(ValueError):
|
|
55
|
+
pass
|
siibra/explorer/__init__.py
CHANGED
siibra/explorer/url.py
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# Copyright 2018-2024
|
|
2
|
+
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
|
+
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
1
16
|
from typing import Optional, TYPE_CHECKING
|
|
2
17
|
from urllib.parse import quote_plus
|
|
3
18
|
from numpy import int32
|
siibra/explorer/util.py
CHANGED
siibra/features/__init__.py
CHANGED
siibra/features/anchor.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2024
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -58,7 +58,8 @@ class RegionalConnectivity(Feature, Compoundable):
|
|
|
58
58
|
datasets: list = [],
|
|
59
59
|
subject: str = "average",
|
|
60
60
|
feature: str = None,
|
|
61
|
-
id: str = None
|
|
61
|
+
id: str = None,
|
|
62
|
+
prerelease: bool = False,
|
|
62
63
|
):
|
|
63
64
|
"""
|
|
64
65
|
Construct a parcellation-averaged connectivity matrix.
|
|
@@ -92,7 +93,8 @@ class RegionalConnectivity(Feature, Compoundable):
|
|
|
92
93
|
description=description,
|
|
93
94
|
anchor=anchor,
|
|
94
95
|
datasets=datasets,
|
|
95
|
-
id=id
|
|
96
|
+
id=id,
|
|
97
|
+
prerelease=prerelease
|
|
96
98
|
)
|
|
97
99
|
self.cohort = cohort.upper()
|
|
98
100
|
if isinstance(connector, str) and connector:
|
siibra/features/feature.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2024
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -99,7 +99,8 @@ class Feature:
|
|
|
99
99
|
description: str,
|
|
100
100
|
anchor: _anchor.AnatomicalAnchor,
|
|
101
101
|
datasets: List['TypeDataset'] = [],
|
|
102
|
-
id: str = None
|
|
102
|
+
id: str = None,
|
|
103
|
+
prerelease: bool = False,
|
|
103
104
|
):
|
|
104
105
|
"""
|
|
105
106
|
Parameters
|
|
@@ -117,6 +118,7 @@ class Feature:
|
|
|
117
118
|
self._anchor_cached = anchor
|
|
118
119
|
self.datasets = datasets
|
|
119
120
|
self._id = id
|
|
121
|
+
self._prerelease = prerelease
|
|
120
122
|
|
|
121
123
|
@property
|
|
122
124
|
def modality(self):
|
|
@@ -170,12 +172,19 @@ class Feature:
|
|
|
170
172
|
|
|
171
173
|
@property
|
|
172
174
|
def LICENSE(self) -> str:
|
|
173
|
-
licenses =
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
licenses = []
|
|
176
|
+
for ds in self.datasets:
|
|
177
|
+
if ds.LICENSE is None or ds.LICENSE == "No license information is found.":
|
|
178
|
+
continue
|
|
179
|
+
if isinstance(ds.LICENSE, str):
|
|
180
|
+
licenses.append(ds.LICENSE)
|
|
181
|
+
if isinstance(ds.LICENSE, list):
|
|
182
|
+
licenses.extend(ds.LICENSE)
|
|
183
|
+
if len(licenses) == 0:
|
|
184
|
+
logger.warning("No license information is found.")
|
|
185
|
+
return ""
|
|
186
|
+
if len(licenses) > 1:
|
|
187
|
+
logger.info("Found multiple licenses corresponding to datasets.")
|
|
179
188
|
return '\n'.join(licenses)
|
|
180
189
|
|
|
181
190
|
@property
|
|
@@ -199,7 +208,8 @@ class Feature:
|
|
|
199
208
|
def name(self):
|
|
200
209
|
"""Returns a short human-readable name of this feature."""
|
|
201
210
|
readable_class_name = sub("([a-z])([A-Z])", r"\g<1> \g<2>", self.__class__.__name__)
|
|
202
|
-
|
|
211
|
+
name_ = sub("([b,B]ig [b,B]rain)", "BigBrain", readable_class_name)
|
|
212
|
+
return name_ if not self._prerelease else f"[PRERELEASE] {name_}"
|
|
203
213
|
|
|
204
214
|
@classmethod
|
|
205
215
|
def _get_instances(cls, **kwargs) -> List['Feature']:
|
|
@@ -268,13 +278,18 @@ class Feature:
|
|
|
268
278
|
if self._id:
|
|
269
279
|
return self._id
|
|
270
280
|
|
|
281
|
+
if self._prerelease:
|
|
282
|
+
name_ = self.name.replace("[PRERELEASE] ", "")
|
|
283
|
+
else:
|
|
284
|
+
name_ = self.name
|
|
285
|
+
|
|
271
286
|
prefix = ''
|
|
272
287
|
for ds in self.datasets:
|
|
273
288
|
if hasattr(ds, "id"):
|
|
274
289
|
prefix = ds.id + '--'
|
|
275
290
|
break
|
|
276
291
|
return prefix + md5(
|
|
277
|
-
f"{
|
|
292
|
+
f"{name_} - {self.anchor}".encode("utf-8")
|
|
278
293
|
).hexdigest()
|
|
279
294
|
|
|
280
295
|
def _to_zip(self, fh: ZipFile):
|
|
@@ -393,6 +408,7 @@ class Feature:
|
|
|
393
408
|
|
|
394
409
|
@staticmethod
|
|
395
410
|
def _encode_concept(concept: concept.AtlasConcept):
|
|
411
|
+
from ..locations import Location
|
|
396
412
|
encoded_c = []
|
|
397
413
|
if isinstance(concept, space.Space):
|
|
398
414
|
encoded_c.append(f"s:{concept.id}")
|
|
@@ -403,6 +419,8 @@ class Feature:
|
|
|
403
419
|
encoded_c.append(f"r:{concept.name}")
|
|
404
420
|
elif isinstance(concept, volume.Volume):
|
|
405
421
|
encoded_c.append(f"v:{concept.name}")
|
|
422
|
+
elif isinstance(concept, Location):
|
|
423
|
+
encoded_c.append(f"loc:{Location}")
|
|
406
424
|
|
|
407
425
|
if len(encoded_c) == 0:
|
|
408
426
|
raise EncodeLiveQueryIdException("no concept is encoded")
|
|
@@ -465,7 +483,10 @@ class Feature:
|
|
|
465
483
|
f"objects linked to {str(concept)}{argstr}"
|
|
466
484
|
)
|
|
467
485
|
q = QueryType(**kwargs)
|
|
468
|
-
|
|
486
|
+
try:
|
|
487
|
+
features = q.query(concept)
|
|
488
|
+
except StopIteration:
|
|
489
|
+
continue
|
|
469
490
|
live_instances.extend(
|
|
470
491
|
Feature._wrap_livequery_feature(f, Feature._serialize_query_context(f, concept))
|
|
471
492
|
for f in features
|
|
@@ -718,7 +739,7 @@ class CompoundFeature(Feature):
|
|
|
718
739
|
def __init__(
|
|
719
740
|
self,
|
|
720
741
|
elements: List['Feature'],
|
|
721
|
-
queryconcept: Union[region.Region, parcellation.Parcellation, space.Space]
|
|
742
|
+
queryconcept: Union[region.Region, parcellation.Parcellation, space.Space],
|
|
722
743
|
):
|
|
723
744
|
"""
|
|
724
745
|
A compound of several features of the same type with an anchor created
|
|
@@ -751,7 +772,8 @@ class CompoundFeature(Feature):
|
|
|
751
772
|
modality=modality,
|
|
752
773
|
description="\n".join({f.description for f in elements}),
|
|
753
774
|
anchor=self._feature_type._merge_anchors([f.anchor for f in elements]),
|
|
754
|
-
datasets=list(dict.fromkeys([ds for f in elements for ds in f.datasets]))
|
|
775
|
+
datasets=list(dict.fromkeys([ds for f in elements for ds in f.datasets])),
|
|
776
|
+
prerelease=all(f._prerelease for f in elements),
|
|
755
777
|
)
|
|
756
778
|
self._queryconcept = queryconcept
|
|
757
779
|
self._merged_feature_cached = None
|
|
@@ -827,16 +849,21 @@ class CompoundFeature(Feature):
|
|
|
827
849
|
for k, v in self._compounding_attributes.items()
|
|
828
850
|
if k != 'modality'
|
|
829
851
|
])
|
|
830
|
-
|
|
852
|
+
cf_name = f"{len(self)} {readable_feature_type} features{f' {groupby}' if groupby else ''}"
|
|
853
|
+
return cf_name if not self._prerelease else f"[PRERELEASE] {cf_name}"
|
|
831
854
|
|
|
832
855
|
@property
|
|
833
856
|
def id(self) -> str:
|
|
857
|
+
if self._prerelease:
|
|
858
|
+
name_ = self.name.replace("[PRERELEASE] ", "")
|
|
859
|
+
else:
|
|
860
|
+
name_ = self.name
|
|
834
861
|
return "::".join((
|
|
835
862
|
"cf0",
|
|
836
863
|
f"{self._feature_type.__name__}",
|
|
837
864
|
self._encode_concept(self._queryconcept),
|
|
838
865
|
self.datasets[0].id if self.datasets else "nodsid",
|
|
839
|
-
md5(
|
|
866
|
+
md5(name_.encode("utf-8")).hexdigest()
|
|
840
867
|
))
|
|
841
868
|
|
|
842
869
|
def __iter__(self) -> Iterator['Feature']:
|