Skip to content

Commit 9395ef6

Browse files
knollengewaechsfm3philippotto
authored
Improve selection of fallback segmentation when creating annotations (#8677)
### URL of deployed dev instance (used for testing): - https://___.webknossos.xyz ### Steps to test: - prepare a dataset with more than one segmentation layer - go to dataset view mode, yield all seg layers invisible and click "create annotation" - you should see a modal that lets you select the seg layer on which to base the volume annotation - if there is only one segmentation layer in the DS or there are more but only one is visible, the visible or only segmentation layer should be used (no modal) ### Issues: - fixes #8627 ------ (Please delete unneeded items, merge only when none are left open) - [x] Updated [changelog](../blob/master/CHANGELOG.unreleased.md#unreleased) - [ ] Updated [migration guide](../blob/master/MIGRATIONS.unreleased.md#unreleased) if applicable - [ ] Updated [documentation](../blob/master/docs) if applicable - [ ] Adapted [wk-libs python client](https://github.com/scalableminds/webknossos-libs/tree/master/webknossos/webknossos/client) if relevant API parts change - [ ] Removed dev-only changes like prints and application.conf edits - [x] Considered [common edge cases](../blob/master/.github/common_edge_cases.md) - [ ] Needs datastore update after deployment --------- Co-authored-by: Florian M <fm3@users.noreply.github.com> Co-authored-by: Philipp Otto <philipp.4096@gmail.com>
1 parent 6d9bcfe commit 9395ef6

File tree

2 files changed

+71
-23
lines changed

2 files changed

+71
-23
lines changed

frontend/javascripts/viewer/view/action_bar_view.tsx

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
import { withAuthentication } from "admin/auth/authentication_modal";
22
import { createExplorational } from "admin/rest_api";
3-
import { Alert, Popover, Space } from "antd";
3+
import { Alert, Modal, Popover, Space } from "antd";
44
import { AsyncButton, type AsyncButtonProps } from "components/async_clickables";
5+
import { NewVolumeLayerSelection } from "dashboard/advanced_dataset/create_explorative_modal";
56
import { useWkSelector } from "libs/react_hooks";
67
import { isUserAdminOrTeamManager } from "libs/utils";
78
import { ArbitraryVectorInput } from "libs/vector_input";
89
import * as React from "react";
910
import { connect, useDispatch } from "react-redux";
1011
import { useHistory } from "react-router-dom";
11-
import type { APIDataset, APIUser } from "types/api_types";
12+
import type { APIDataset, APISegmentationLayer, APIUser } from "types/api_types";
1213
import { APIJobType, type AdditionalCoordinate } from "types/api_types";
1314
import { type ControlMode, MappingStatusEnum, type ViewMode } from "viewer/constants";
1415
import constants, { ControlModeEnum } from "viewer/constants";
1516
import {
1617
doesSupportVolumeWithFallback,
1718
getColorLayers,
1819
getMappingInfoForSupportedLayer,
20+
getSegmentationLayers,
1921
getUnifiedAdditionalCoordinates,
20-
getVisibleSegmentationLayer,
22+
getVisibleSegmentationLayers,
2123
is2dDataset,
2224
} from "viewer/model/accessors/dataset_accessor";
2325
import { setAdditionalCoordinatesAction } from "viewer/model/actions/flycam_actions";
@@ -139,18 +141,23 @@ function AdditionalCoordinatesInputView() {
139141
function CreateAnnotationButton() {
140142
const history = useHistory();
141143
const activeUser = useWkSelector((state) => state.activeUser);
144+
const visibleSegmentationLayers = useWkSelector((state) => getVisibleSegmentationLayers(state));
145+
const segmentationLayers = useWkSelector((state) => getSegmentationLayers(state.dataset));
146+
const dataset = useWkSelector((state) => state.dataset);
147+
const [isLayerSelectionModalVisible, setLayerSelectionModalVisible] =
148+
React.useState<boolean>(false);
149+
const [selectedLayerName, setSelectedLayerName] = React.useState<string | undefined>(undefined);
142150

143-
const onClick = async () => {
144-
const state = Store.getState();
145-
const { dataset } = state;
151+
const getUnambiguousSegmentationLayer = () => {
152+
if (visibleSegmentationLayers?.length === 1) return visibleSegmentationLayers[0];
153+
if (segmentationLayers.length === 1) return segmentationLayers[0];
154+
return null;
155+
};
156+
157+
const continueWithLayer = async (layer: APISegmentationLayer | null | undefined) => {
146158
// If the dataset supports creating an annotation with a fallback segmentation,
147159
// use it (as the fallback can always be removed later)
148-
const maybeSegmentationLayer = getVisibleSegmentationLayer(state);
149-
const fallbackLayerName =
150-
maybeSegmentationLayer && doesSupportVolumeWithFallback(dataset, maybeSegmentationLayer)
151-
? maybeSegmentationLayer.name
152-
: null;
153-
160+
const fallbackLayerName = getFallbackLayerName(layer);
154161
const mappingInfo = getMappingInfoForSupportedLayer(Store.getState());
155162
let maybeMappingName = null;
156163
if (
@@ -170,21 +177,60 @@ function CreateAnnotationButton() {
170177
history.push(`/annotations/${annotation.id}${location.hash}`);
171178
};
172179

180+
const getFallbackLayerName = (segmentationLayer: APISegmentationLayer | null | undefined) => {
181+
return segmentationLayer && doesSupportVolumeWithFallback(dataset, segmentationLayer)
182+
? segmentationLayer.name
183+
: null;
184+
};
185+
186+
const onClick = async () => {
187+
// This will be set in cases where it is clear which layer to use.
188+
const unambiguousSegmentationLayer = getUnambiguousSegmentationLayer();
189+
if (unambiguousSegmentationLayer == null && segmentationLayers.length > 1) {
190+
setLayerSelectionModalVisible(true);
191+
return;
192+
}
193+
await continueWithLayer(unambiguousSegmentationLayer);
194+
};
195+
196+
const handleLayerSelected = async () => {
197+
setLayerSelectionModalVisible(false);
198+
const selectedLayer = selectedLayerName
199+
? segmentationLayers.find((layer) => layer.name === selectedLayerName)
200+
: null;
201+
await continueWithLayer(selectedLayer);
202+
};
203+
173204
const ButtonWithAuthentication = withAuthentication<AsyncButtonProps, typeof AsyncButton>(
174205
AsyncButton,
175206
);
176207
return (
177-
<ButtonWithAuthentication
178-
activeUser={activeUser}
179-
authenticationMessage="You have to register or login to create an annotation."
180-
style={{
181-
marginLeft: 12,
182-
}}
183-
type="primary"
184-
onClick={onClick}
185-
>
186-
Create Annotation
187-
</ButtonWithAuthentication>
208+
<>
209+
<ButtonWithAuthentication
210+
activeUser={activeUser}
211+
authenticationMessage="You have to register or login to create an annotation."
212+
style={{
213+
marginLeft: 12,
214+
}}
215+
type="primary"
216+
onClick={onClick}
217+
>
218+
Create Annotation
219+
</ButtonWithAuthentication>
220+
221+
<Modal
222+
open={isLayerSelectionModalVisible}
223+
onCancel={() => setLayerSelectionModalVisible(false)}
224+
onOk={handleLayerSelected}
225+
>
226+
<NewVolumeLayerSelection
227+
segmentationLayers={segmentationLayers}
228+
dataset={dataset}
229+
selectedSegmentationLayerName={selectedLayerName}
230+
setSelectedSegmentationLayerName={setSelectedLayerName}
231+
/>
232+
</Modal>
233+
</>
188234
);
189235
}
190236

unreleased_changes/8677.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
### Changed
2+
- When creating an annotation from the dataset view mode, and more than one segmentation layer is visible or multiple segmentation layers are invisible but available, a selection modal now appears allowing users to choose which segmentation layer to use for the newly created volume annotation. Per default, annotations are still based on the visible segmentation layer.

0 commit comments

Comments
 (0)