@geode/opengeodeweb-front 10.40.0 → 10.41.0-rc.1
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.
- package/.build/shared/utils/file.js +1 -1
- package/.build/shared/utils/response_handlers/load.js +19 -1
- package/app/components/MissingFilesSelector.vue +84 -11
- package/app/components/ObjectSelector.vue +147 -13
- package/app/components/ThresholdFilter.vue +25 -17
- package/app/components/Viewer/Generic/Model/BlocksOptions.vue +92 -29
- package/app/components/Viewer/Generic/Model/CornersOptions.vue +12 -4
- package/app/components/Viewer/Generic/Model/LinesOptions.vue +16 -5
- package/app/components/Viewer/Generic/Model/SurfacesOptions.vue +16 -5
- package/app/components/Viewer/Options/AttributeRangeSelector.vue +42 -8
- package/app/components/Viewer/Options/AttributeSelector.vue +101 -47
- package/app/components/Viewer/Options/ColoringTypeSelector.vue +32 -17
- package/app/components/csv-preview/CsvSettings.vue +1 -1
- package/app/composables/global_attribute_style.ts +20 -33
- package/app/utils/attribute_range.ts +56 -0
- package/app/utils/import_workflow.ts +13 -2
- package/internal/stores/data_style/model/blocks/polyhedron.ts +23 -20
- package/internal/utils/upload_file.ts +1 -1
- package/package.json +4 -4
- package/shared/utils/file.ts +1 -1
- package/shared/utils/response_handlers/load.ts +37 -1
|
@@ -4,9 +4,13 @@ import {
|
|
|
4
4
|
type RangesPerData,
|
|
5
5
|
useBatchGroup,
|
|
6
6
|
} from "@ogw_front/composables/batch_style";
|
|
7
|
+
import {
|
|
8
|
+
type AttributeRangeInfo,
|
|
9
|
+
type AttributeSchemas,
|
|
10
|
+
fetchAttributeRange,
|
|
11
|
+
} from "@ogw_front/utils/attribute_range";
|
|
7
12
|
import { getAttributeRange, intersectAttributes } from "@ogw_front/utils/attributes";
|
|
8
13
|
import { DEFAULT_NO_DATA_COLOR } from "@ogw_front/utils/default_styles/constants";
|
|
9
|
-
import type { JsonRpcSchema } from "@ogw_shared/utils/types.js";
|
|
10
14
|
import ViewerOptionsAttributeColorBar from "@ogw_front/components/Viewer/Options/AttributeColorBar.vue";
|
|
11
15
|
import ViewerOptionsColorPicker from "@ogw_front/components/Viewer/Options/ColorPicker.vue";
|
|
12
16
|
import ViewerOptionsTimeStepSlider from "@ogw_front/components/Viewer/Options/TimeStepSlider.vue";
|
|
@@ -27,10 +31,10 @@ const attributeTimeStep = defineModel<number>("attributeTimeStep");
|
|
|
27
31
|
interface Props {
|
|
28
32
|
id: string;
|
|
29
33
|
componentIds?: string[];
|
|
30
|
-
|
|
34
|
+
schemas: AttributeSchemas;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
const { id, componentIds = undefined,
|
|
37
|
+
const { id, componentIds = undefined, schemas } = defineProps<Props>();
|
|
34
38
|
|
|
35
39
|
interface Emits {
|
|
36
40
|
"update:attributeColorMap": [colorMap: string];
|
|
@@ -43,13 +47,15 @@ const emit = defineEmits<Emits>();
|
|
|
43
47
|
interface AttributeInfo {
|
|
44
48
|
attribute_name: string;
|
|
45
49
|
nb_items: number;
|
|
46
|
-
no_data?: boolean;
|
|
47
50
|
time_steps?: number[];
|
|
48
51
|
[key: string]: unknown;
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
const attributes = ref<AttributeInfo[]>([]);
|
|
52
55
|
let attributesPerTarget = new Map<string, AttributeInfo[]>();
|
|
56
|
+
const rangesPerTarget = ref(new Map<string, AttributeRangeInfo>());
|
|
57
|
+
let rangesName: string | undefined = undefined;
|
|
58
|
+
let rangesLoading: Promise<void> = Promise.resolve();
|
|
53
59
|
|
|
54
60
|
const groupTargetIds = useBatchGroup(() => id);
|
|
55
61
|
|
|
@@ -57,6 +63,9 @@ const currentAttribute = computed<AttributeInfo | undefined>(() =>
|
|
|
57
63
|
attributes.value.find((attr) => attr.attribute_name === attributeName.value),
|
|
58
64
|
);
|
|
59
65
|
const timeSteps = computed<number[]>(() => currentAttribute.value?.time_steps ?? []);
|
|
66
|
+
const noData = computed<boolean>(() =>
|
|
67
|
+
[...rangesPerTarget.value.values()].some((range) => range.no_data),
|
|
68
|
+
);
|
|
60
69
|
|
|
61
70
|
let committedSeries: string | undefined = undefined;
|
|
62
71
|
|
|
@@ -72,7 +81,10 @@ function commitSeriesTimeStep(): void {
|
|
|
72
81
|
committedSeries = name;
|
|
73
82
|
const current = attributeTimeStep.value;
|
|
74
83
|
const valid = current !== undefined && current >= 0 && current < timeSteps.value.length;
|
|
75
|
-
|
|
84
|
+
// A valid step is already stored and applied with the series: emitting it again re-applies the attribute
|
|
85
|
+
if (!valid) {
|
|
86
|
+
emit("update:attributeTimeStep", 0);
|
|
87
|
+
}
|
|
76
88
|
}
|
|
77
89
|
|
|
78
90
|
const cssNoDataColor = computed<string>(() => {
|
|
@@ -118,68 +130,105 @@ const componentItems = computed<{ title: string; value: number }[]>(() => {
|
|
|
118
130
|
}));
|
|
119
131
|
});
|
|
120
132
|
|
|
121
|
-
function
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
comp,
|
|
130
|
-
);
|
|
131
|
-
attributeRange.value = [min, max];
|
|
133
|
+
function hasSelectedComponent(components: unknown): boolean {
|
|
134
|
+
return Array.isArray(components) && components.length > 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function requestParams(): { id: string; component_ids?: string[] } | undefined {
|
|
138
|
+
const schemaProperties = schemas.names.properties as Record<string, unknown> | undefined;
|
|
139
|
+
if (schemaProperties?.component_ids === undefined) {
|
|
140
|
+
return { id };
|
|
132
141
|
}
|
|
142
|
+
return hasSelectedComponent(componentIds) ? { id, component_ids: componentIds } : undefined;
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
|
|
136
|
-
|
|
145
|
+
// Ranges are computed by the back for the selected attribute only, as scanning every step of every series is slow
|
|
146
|
+
async function requestRanges(name: string): Promise<void> {
|
|
147
|
+
const targets: [string, { id: string; component_ids?: string[] } | undefined][] =
|
|
148
|
+
groupTargetIds.value
|
|
149
|
+
? [...attributesPerTarget]
|
|
150
|
+
.filter(([, targetAttributes]) =>
|
|
151
|
+
targetAttributes.some((attribute) => attribute.attribute_name === name),
|
|
152
|
+
)
|
|
153
|
+
.map(([targetId]) => [targetId, { id: targetId }])
|
|
154
|
+
: [[id, requestParams()]];
|
|
155
|
+
const entries = await Promise.all(
|
|
156
|
+
targets.map(async ([targetId, params]): Promise<[string, AttributeRangeInfo | undefined]> => [
|
|
157
|
+
targetId,
|
|
158
|
+
params ? await fetchAttributeRange(schemas.range, params, name) : undefined,
|
|
159
|
+
]),
|
|
160
|
+
);
|
|
161
|
+
if (rangesName !== name) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
rangesPerTarget.value = new Map(
|
|
165
|
+
entries.filter((entry): entry is [string, AttributeRangeInfo] => entry[1] !== undefined),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function loadRanges(name: string): Promise<void> {
|
|
170
|
+
if (name !== rangesName) {
|
|
171
|
+
rangesName = name;
|
|
172
|
+
rangesPerTarget.value = new Map();
|
|
173
|
+
rangesLoading = requestRanges(name);
|
|
174
|
+
}
|
|
175
|
+
await rangesLoading;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function applyRange(): void {
|
|
179
|
+
const { min, max } = getAttributeRange(rangesPerTarget.value.get(id), attributeItem.value ?? 0);
|
|
180
|
+
attributeRange.value = [min, max];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function resetRange(): Promise<void> {
|
|
184
|
+
if (currentAttribute.value) {
|
|
185
|
+
await loadRanges(currentAttribute.value.attribute_name);
|
|
186
|
+
applyRange();
|
|
187
|
+
}
|
|
137
188
|
}
|
|
138
189
|
|
|
139
190
|
async function getGroupAttributes(targetIds: string[]): Promise<void> {
|
|
140
|
-
const responses = await requestForTargets<{ attributes: AttributeInfo[] }>(
|
|
141
|
-
|
|
142
|
-
|
|
191
|
+
const responses = await requestForTargets<{ attributes: AttributeInfo[] }>(
|
|
192
|
+
schemas.names,
|
|
193
|
+
targetIds,
|
|
194
|
+
{
|
|
195
|
+
attributes: [],
|
|
196
|
+
},
|
|
197
|
+
);
|
|
143
198
|
attributesPerTarget = new Map(
|
|
144
199
|
responses.map(([targetId, response]) => [targetId, response.attributes]),
|
|
145
200
|
);
|
|
146
201
|
attributes.value = intersectAttributes([...attributesPerTarget.values()]);
|
|
147
202
|
}
|
|
148
203
|
|
|
149
|
-
function initGroupAttribute(name: string, item: number): void {
|
|
204
|
+
async function initGroupAttribute(name: string, item: number): Promise<void> {
|
|
205
|
+
await loadRanges(name);
|
|
206
|
+
if (attributeName.value !== name) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
150
209
|
const ranges = new Map<string, AttributeRange>();
|
|
151
|
-
for (const [targetId,
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
const { min, max } = getAttributeRange(
|
|
155
|
-
attribute as unknown as Parameters<typeof getAttributeRange>[0],
|
|
156
|
-
item,
|
|
157
|
-
);
|
|
158
|
-
ranges.set(targetId, [min, max]);
|
|
159
|
-
}
|
|
210
|
+
for (const [targetId, range] of rangesPerTarget.value) {
|
|
211
|
+
const { min, max } = getAttributeRange(range, item);
|
|
212
|
+
ranges.set(targetId, [min, max]);
|
|
160
213
|
}
|
|
161
214
|
emit("update:attributeColorMap", attributeColorMap.value ?? "batlow");
|
|
162
215
|
emit("ranges_per_data", ranges);
|
|
163
216
|
}
|
|
164
217
|
|
|
165
218
|
async function getAttributes(): Promise<void> {
|
|
219
|
+
rangesName = undefined;
|
|
220
|
+
rangesPerTarget.value = new Map();
|
|
166
221
|
if (groupTargetIds.value) {
|
|
167
222
|
await getGroupAttributes(groupTargetIds.value);
|
|
168
223
|
return;
|
|
169
224
|
}
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
if (requiresComponent && !hasSelectedComponent(componentIds)) {
|
|
225
|
+
const params = requestParams();
|
|
226
|
+
if (!params) {
|
|
173
227
|
return;
|
|
174
228
|
}
|
|
175
229
|
|
|
176
|
-
const params: { id: string; component_ids?: unknown } = { id };
|
|
177
|
-
if (requiresComponent) {
|
|
178
|
-
params.component_ids = componentIds;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
230
|
await backStore.request(
|
|
182
|
-
{ schema, params },
|
|
231
|
+
{ schema: schemas.names, params },
|
|
183
232
|
{
|
|
184
233
|
response_function: (response: unknown) => {
|
|
185
234
|
attributes.value = (response as { attributes: AttributeInfo[] }).attributes;
|
|
@@ -193,19 +242,19 @@ onMounted(async () => {
|
|
|
193
242
|
});
|
|
194
243
|
|
|
195
244
|
watch(
|
|
196
|
-
() =>
|
|
245
|
+
[(): string => id, (): string[] | undefined => componentIds, (): AttributeSchemas => schemas],
|
|
197
246
|
async () => {
|
|
198
247
|
await getAttributes();
|
|
199
248
|
},
|
|
200
249
|
);
|
|
201
250
|
|
|
202
|
-
watch([attributeName, attributeItem], ([name, item]) => {
|
|
251
|
+
watch([attributeName, attributeItem], async ([name, item]) => {
|
|
203
252
|
if (groupTargetIds.value && name !== undefined) {
|
|
204
|
-
initGroupAttribute(name, item ?? 0);
|
|
253
|
+
await initGroupAttribute(name, item ?? 0);
|
|
205
254
|
}
|
|
206
255
|
});
|
|
207
256
|
|
|
208
|
-
watch([attributeName, attributeItem, currentAttribute], () => {
|
|
257
|
+
watch([attributeName, attributeItem, currentAttribute], async () => {
|
|
209
258
|
commitSeriesTimeStep();
|
|
210
259
|
if (groupTargetIds.value) {
|
|
211
260
|
return;
|
|
@@ -216,8 +265,13 @@ watch([attributeName, attributeItem, currentAttribute], () => {
|
|
|
216
265
|
if (attributeNoDataColor.value === undefined) {
|
|
217
266
|
attributeNoDataColor.value = DEFAULT_NO_DATA_COLOR;
|
|
218
267
|
}
|
|
219
|
-
|
|
220
|
-
|
|
268
|
+
const name = currentAttribute.value?.attribute_name;
|
|
269
|
+
if (name === undefined) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
await loadRanges(name);
|
|
273
|
+
if (name === currentAttribute.value?.attribute_name && attributeRange.value[0] === undefined) {
|
|
274
|
+
applyRange();
|
|
221
275
|
}
|
|
222
276
|
});
|
|
223
277
|
</script>
|
|
@@ -247,7 +301,7 @@ watch([attributeName, attributeItem, currentAttribute], () => {
|
|
|
247
301
|
/>
|
|
248
302
|
<ViewerOptionsTimeStepSlider v-model="attributeTimeStep" :time-steps="timeSteps" />
|
|
249
303
|
<div
|
|
250
|
-
v-if="currentAttribute &&
|
|
304
|
+
v-if="currentAttribute && noData"
|
|
251
305
|
class="text-caption text-high-emphasis mt-1 d-flex align-center ga-1"
|
|
252
306
|
data-testid="noDataInfo"
|
|
253
307
|
>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type { AttributeSchemas } from "@ogw_front/utils/attribute_range";
|
|
3
3
|
import type { RGBAColor } from "@ogw_front/utils/default_styles/constants";
|
|
4
4
|
import type { RangesPerData } from "@ogw_front/composables/batch_style";
|
|
5
5
|
import ViewerOptionsAttributeSelector from "@ogw_front/components/Viewer/Options/AttributeSelector.vue";
|
|
@@ -18,11 +18,11 @@ interface CapabilityConfig {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
interface Schemas {
|
|
21
|
-
vertex?:
|
|
22
|
-
edge?:
|
|
23
|
-
cell?:
|
|
24
|
-
polygon?:
|
|
25
|
-
polyhedron?:
|
|
21
|
+
vertex?: AttributeSchemas;
|
|
22
|
+
edge?: AttributeSchemas;
|
|
23
|
+
cell?: AttributeSchemas;
|
|
24
|
+
polygon?: AttributeSchemas;
|
|
25
|
+
polyhedron?: AttributeSchemas;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const coloring_style_key = defineModel<string>("coloring_style_key");
|
|
@@ -95,12 +95,27 @@ const {
|
|
|
95
95
|
allowRandom = false,
|
|
96
96
|
} = defineProps<Props>();
|
|
97
97
|
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
const backSchemas = back_schemas.opengeodeweb_back;
|
|
99
|
+
const vertexSchemas = schemas.vertex ?? {
|
|
100
|
+
names: backSchemas.vertex_attribute_names,
|
|
101
|
+
range: backSchemas.vertex_attribute_range,
|
|
102
|
+
};
|
|
103
|
+
const edgeSchemas = schemas.edge ?? {
|
|
104
|
+
names: backSchemas.edge_attribute_names,
|
|
105
|
+
range: backSchemas.edge_attribute_range,
|
|
106
|
+
};
|
|
107
|
+
const cellSchemas = schemas.cell ?? {
|
|
108
|
+
names: backSchemas.cell_attribute_names,
|
|
109
|
+
range: backSchemas.cell_attribute_range,
|
|
110
|
+
};
|
|
111
|
+
const polygonSchemas = schemas.polygon ?? {
|
|
112
|
+
names: backSchemas.polygon_attribute_names,
|
|
113
|
+
range: backSchemas.polygon_attribute_range,
|
|
114
|
+
};
|
|
115
|
+
const polyhedronSchemas = schemas.polyhedron ?? {
|
|
116
|
+
names: backSchemas.polyhedron_attribute_names,
|
|
117
|
+
range: backSchemas.polyhedron_attribute_range,
|
|
118
|
+
};
|
|
104
119
|
|
|
105
120
|
function isAvailable(key: string): boolean {
|
|
106
121
|
if (capabilities[key] && capabilities[key].available === false) {
|
|
@@ -266,7 +281,7 @@ watch(
|
|
|
266
281
|
@ranges_per_data="emit('vertex_attribute_ranges_per_data', $event)"
|
|
267
282
|
:id="id"
|
|
268
283
|
:componentIds="componentIds"
|
|
269
|
-
:
|
|
284
|
+
:schemas="vertexSchemas"
|
|
270
285
|
/>
|
|
271
286
|
</template>
|
|
272
287
|
<template v-if="active_key === edge_dict['value'] && hasColorMap('edge')">
|
|
@@ -280,7 +295,7 @@ watch(
|
|
|
280
295
|
@ranges_per_data="emit('edge_attribute_ranges_per_data', $event)"
|
|
281
296
|
:id="id"
|
|
282
297
|
:componentIds="componentIds"
|
|
283
|
-
:
|
|
298
|
+
:schemas="edgeSchemas"
|
|
284
299
|
/>
|
|
285
300
|
</template>
|
|
286
301
|
<template v-if="active_key === cell_dict['value'] && hasColorMap('cell')">
|
|
@@ -294,7 +309,7 @@ watch(
|
|
|
294
309
|
@ranges_per_data="emit('cell_attribute_ranges_per_data', $event)"
|
|
295
310
|
:id="id"
|
|
296
311
|
:componentIds="componentIds"
|
|
297
|
-
:
|
|
312
|
+
:schemas="cellSchemas"
|
|
298
313
|
/>
|
|
299
314
|
</template>
|
|
300
315
|
<template v-if="active_key === polygon_dict['value'] && hasColorMap('polygon')">
|
|
@@ -308,7 +323,7 @@ watch(
|
|
|
308
323
|
@ranges_per_data="emit('polygon_attribute_ranges_per_data', $event)"
|
|
309
324
|
:id="id"
|
|
310
325
|
:componentIds="componentIds"
|
|
311
|
-
:
|
|
326
|
+
:schemas="polygonSchemas"
|
|
312
327
|
/>
|
|
313
328
|
</template>
|
|
314
329
|
<template v-if="active_key === polyhedron_dict['value'] && hasColorMap('polyhedron')">
|
|
@@ -322,7 +337,7 @@ watch(
|
|
|
322
337
|
@ranges_per_data="emit('polyhedron_attribute_ranges_per_data', $event)"
|
|
323
338
|
:id="id"
|
|
324
339
|
:componentIds="componentIds"
|
|
325
|
-
:
|
|
340
|
+
:schemas="polyhedronSchemas"
|
|
326
341
|
/>
|
|
327
342
|
</template>
|
|
328
343
|
</v-col>
|
|
@@ -26,7 +26,7 @@ const emit = defineEmits<Emits>();
|
|
|
26
26
|
const separators = [
|
|
27
27
|
{ title: "Comma (,)", value: "," },
|
|
28
28
|
{ title: "Semicolon (;)", value: ";" },
|
|
29
|
-
{ title:
|
|
29
|
+
{ title: String.raw`Tab (\t)`, value: "\t" },
|
|
30
30
|
{ title: "Space ( )", value: " " },
|
|
31
31
|
{ title: "Pipe (|)", value: "|" },
|
|
32
32
|
{ title: "Custom", value: "custom" },
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { JsonRpcSchema } from "@ogw_shared/utils/types";
|
|
2
2
|
import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_typed_schemas.js";
|
|
3
|
+
import { fetchAttributeRange } from "@ogw_front/utils/attribute_range";
|
|
3
4
|
import { getAttributeRange } from "@ogw_front/utils/attributes";
|
|
4
5
|
import { useBackStore } from "@ogw_front/stores/back";
|
|
5
6
|
import { useDataStyleStore } from "@ogw_front/stores/data_style";
|
|
@@ -35,10 +36,6 @@ interface AttributeStyleComponent {
|
|
|
35
36
|
interface AttributeResponse {
|
|
36
37
|
readonly attributes?: readonly {
|
|
37
38
|
readonly attribute_name?: string;
|
|
38
|
-
readonly min_values?: readonly number[];
|
|
39
|
-
readonly max_values?: readonly number[];
|
|
40
|
-
readonly min_value?: number;
|
|
41
|
-
readonly max_value?: number;
|
|
42
39
|
readonly time_steps?: readonly number[];
|
|
43
40
|
}[];
|
|
44
41
|
}
|
|
@@ -259,39 +256,29 @@ export function useGlobalAttributeStyle(dataIdRef: Ref<string | undefined>): {
|
|
|
259
256
|
continue;
|
|
260
257
|
}
|
|
261
258
|
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (
|
|
259
|
+
const rangeSchema = (back_schemas.opengeodeweb_back as Record<string, unknown>)[
|
|
260
|
+
`${activeColoring}_attribute_range`
|
|
261
|
+
];
|
|
262
|
+
if (!isJsonRpcSchema(rangeSchema)) {
|
|
266
263
|
continue;
|
|
267
264
|
}
|
|
268
265
|
|
|
269
266
|
requestPromises.push(
|
|
270
|
-
|
|
271
|
-
{
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
{
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const setterName = `set${setterKey}${attributeType}Range`;
|
|
286
|
-
const setter = getDynamicStoreMethod(dataStyleStore, setterName);
|
|
287
|
-
if (setter) {
|
|
288
|
-
setter(targetId, min, max);
|
|
289
|
-
await hybridViewerStore.remoteRender();
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
},
|
|
294
|
-
),
|
|
267
|
+
(async (): Promise<void> => {
|
|
268
|
+
const range = await fetchAttributeRange(rangeSchema, { id: targetId }, attrName);
|
|
269
|
+
if (!range) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const { min, max } = getAttributeRange(range, attrItem);
|
|
273
|
+
const setter = getDynamicStoreMethod(
|
|
274
|
+
dataStyleStore,
|
|
275
|
+
`set${setterKey}${attributeType}Range`,
|
|
276
|
+
);
|
|
277
|
+
if (setter) {
|
|
278
|
+
setter(targetId, min, max);
|
|
279
|
+
await hybridViewerStore.remoteRender();
|
|
280
|
+
}
|
|
281
|
+
})(),
|
|
295
282
|
);
|
|
296
283
|
}
|
|
297
284
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { JsonRpcSchema } from "@ogw_shared/utils/types.js";
|
|
2
|
+
import { useBackStore } from "@ogw_front/stores/back";
|
|
3
|
+
|
|
4
|
+
interface AttributeRangeInfo {
|
|
5
|
+
min_values: number[];
|
|
6
|
+
max_values: number[];
|
|
7
|
+
no_data: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// The *_attribute_names route listing the attributes, and its *_attribute_range route
|
|
11
|
+
interface AttributeSchemas {
|
|
12
|
+
names: JsonRpcSchema;
|
|
13
|
+
range: JsonRpcSchema;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface AttributeRangeTarget {
|
|
17
|
+
id: string;
|
|
18
|
+
component_ids?: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// A data never changes once imported: selectors showing the same attribute share one request
|
|
22
|
+
const rangeRequests = new Map<string, Promise<AttributeRangeInfo | undefined>>();
|
|
23
|
+
|
|
24
|
+
async function requestAttributeRange(
|
|
25
|
+
rangeSchema: JsonRpcSchema,
|
|
26
|
+
params: Record<string, unknown>,
|
|
27
|
+
key: string,
|
|
28
|
+
): Promise<AttributeRangeInfo | undefined> {
|
|
29
|
+
const backStore = useBackStore();
|
|
30
|
+
try {
|
|
31
|
+
const range = await backStore.request({ schema: rangeSchema, params });
|
|
32
|
+
// oxlint-disable-next-line no-unsafe-type-assertion -- every *_attribute_range schema shares this response.
|
|
33
|
+
return range as AttributeRangeInfo;
|
|
34
|
+
} catch {
|
|
35
|
+
rangeRequests.delete(key);
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function fetchAttributeRange(
|
|
41
|
+
rangeSchema: JsonRpcSchema,
|
|
42
|
+
target: AttributeRangeTarget,
|
|
43
|
+
attributeName: string,
|
|
44
|
+
): Promise<AttributeRangeInfo | undefined> {
|
|
45
|
+
const params = { ...target, attribute_name: attributeName };
|
|
46
|
+
const key = JSON.stringify([rangeSchema.$id, params]);
|
|
47
|
+
let request = rangeRequests.get(key);
|
|
48
|
+
if (request === undefined) {
|
|
49
|
+
request = requestAttributeRange(rangeSchema, params, key);
|
|
50
|
+
rangeRequests.set(key, request);
|
|
51
|
+
}
|
|
52
|
+
const range = await request;
|
|
53
|
+
return range;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { type AttributeRangeInfo, type AttributeSchemas, fetchAttributeRange };
|
|
@@ -96,18 +96,26 @@ async function importWorkflow(files: readonly FileToImport[]): Promise<string[]>
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
const results: string[] = [];
|
|
99
|
+
const errors: unknown[] = [];
|
|
99
100
|
async function processChunk(chunkIndex: number): Promise<void> {
|
|
100
101
|
if (chunkIndex >= chunks.length) {
|
|
101
102
|
return;
|
|
102
103
|
}
|
|
103
104
|
const chunk = chunks[chunkIndex] ?? [];
|
|
104
|
-
|
|
105
|
+
// A failing file does not prevent the other files from being imported
|
|
106
|
+
const chunk_results = await Promise.allSettled(
|
|
105
107
|
chunk.map(async ({ filename, geode_object_type }) => {
|
|
106
108
|
const id = await importFile(filename, geode_object_type);
|
|
107
109
|
return id;
|
|
108
110
|
}),
|
|
109
111
|
);
|
|
110
|
-
|
|
112
|
+
for (const result of chunk_results) {
|
|
113
|
+
if (result.status === "fulfilled") {
|
|
114
|
+
results.push(result.value);
|
|
115
|
+
} else {
|
|
116
|
+
errors.push(result.reason);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
111
119
|
await processChunk(chunkIndex + 1);
|
|
112
120
|
}
|
|
113
121
|
await processChunk(0);
|
|
@@ -116,6 +124,9 @@ async function importWorkflow(files: readonly FileToImport[]): Promise<string[]>
|
|
|
116
124
|
hybridViewerStore.resetCamera();
|
|
117
125
|
}
|
|
118
126
|
await hybridViewerStore.remoteRender();
|
|
127
|
+
if (errors.length > 0) {
|
|
128
|
+
throw new AggregateError(errors, `${errors.length} file(s) could not be imported`);
|
|
129
|
+
}
|
|
119
130
|
return results;
|
|
120
131
|
}
|
|
121
132
|
|