@geode/opengeodeweb-front 10.37.0 → 10.38.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.
Files changed (29) hide show
  1. package/README.md +0 -1
  2. package/app/components/Viewer/ContextMenu/ContextMenu.vue +2 -0
  3. package/app/components/Viewer/EdgedCurve/SpecificEdgesOptions.vue +21 -1
  4. package/app/components/Viewer/Generic/Mesh/CellsOptions.vue +21 -1
  5. package/app/components/Viewer/Generic/Mesh/EdgesOptions.vue +21 -1
  6. package/app/components/Viewer/Generic/Mesh/PointsOptions.vue +13 -1
  7. package/app/components/Viewer/Generic/Mesh/PolygonsOptions.vue +21 -1
  8. package/app/components/Viewer/Generic/Mesh/PolyhedraOptions.vue +21 -1
  9. package/app/components/Viewer/Generic/Model/ModelStyleCard.vue +29 -31
  10. package/app/components/Viewer/ObjectTree/Layout.vue +2 -1
  11. package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +24 -10
  12. package/app/components/Viewer/ObjectTree/Views/ModelTree.vue +5 -15
  13. package/app/components/Viewer/Options/AttributeColorBar.vue +7 -0
  14. package/app/components/Viewer/Options/AttributeSelector.vue +71 -21
  15. package/app/components/Viewer/Options/ColoringTypeSelector.vue +16 -0
  16. package/app/components/Viewer/Options/TextureItem.vue +15 -11
  17. package/app/components/Viewer/PointSet/SpecificPointsOptions.vue +13 -1
  18. package/app/components/Viewer/Solid/SpecificPolyhedraOptions.vue +21 -1
  19. package/app/components/Viewer/Surface/PolygonsOptions.vue +21 -1
  20. package/app/components/Viewer/Ui.vue +7 -3
  21. package/app/composables/batch_style.ts +64 -1
  22. package/app/composables/tree_filter.ts +8 -8
  23. package/app/composables/use_overlapping_picker.ts +2 -0
  24. package/app/composables/viewer_context_menu.ts +123 -0
  25. package/app/stores/menu.ts +3 -0
  26. package/app/utils/attributes.ts +38 -1
  27. package/app/utils/request_for_targets.ts +24 -0
  28. package/app/utils/treeview.ts +30 -0
  29. package/package.json +7 -9
@@ -1,16 +1,24 @@
1
1
  <script setup lang="ts">
2
+ import {
3
+ type AttributeRange,
4
+ type RangesPerData,
5
+ useBatchGroup,
6
+ } from "@ogw_front/composables/batch_style";
7
+ import { getAttributeRange, intersectAttributes } from "@ogw_front/utils/attributes";
2
8
  import { DEFAULT_NO_DATA_COLOR } from "@ogw_front/utils/default_styles/constants";
3
9
  import type { JsonRpcSchema } from "@ogw_shared/utils/types.js";
4
10
  import ViewerOptionsAttributeColorBar from "@ogw_front/components/Viewer/Options/AttributeColorBar.vue";
5
11
  import ViewerOptionsColorPicker from "@ogw_front/components/Viewer/Options/ColorPicker.vue";
6
- import { getAttributeRange } from "@ogw_front/utils/attributes";
12
+ import { requestForTargets } from "@ogw_front/utils/request_for_targets";
7
13
  import { useBackStore } from "@ogw_front/stores/back";
8
14
 
9
15
  const backStore = useBackStore();
10
16
 
11
17
  const attributeName = defineModel<string>("attributeName");
12
18
  const attributeItem = defineModel<number>("attributeItem");
13
- const attributeRange = defineModel<(number | undefined)[]>("attributeRange");
19
+ const attributeRange = defineModel<(number | undefined)[]>("attributeRange", {
20
+ default: () => [],
21
+ });
14
22
  const attributeColorMap = defineModel<string>("attributeColorMap");
15
23
  const attributeNoDataColor = defineModel<typeof DEFAULT_NO_DATA_COLOR>("attributeNoDataColor");
16
24
 
@@ -22,6 +30,13 @@ interface Props {
22
30
 
23
31
  const { id, componentIds = undefined, schema } = defineProps<Props>();
24
32
 
33
+ interface Emits {
34
+ "update:attributeColorMap": [colorMap: string];
35
+ ranges_per_data: [ranges: RangesPerData];
36
+ }
37
+
38
+ const emit = defineEmits<Emits>();
39
+
25
40
  interface AttributeInfo {
26
41
  attribute_name: string;
27
42
  nb_items: number;
@@ -30,6 +45,9 @@ interface AttributeInfo {
30
45
  }
31
46
 
32
47
  const attributes = ref<AttributeInfo[]>([]);
48
+ let attributesPerTarget = new Map<string, AttributeInfo[]>();
49
+
50
+ const groupTargetIds = useBatchGroup(() => id);
33
51
 
34
52
  const currentAttribute = computed<AttributeInfo | undefined>(() =>
35
53
  attributes.value.find((attr) => attr.attribute_name === attributeName.value),
@@ -39,16 +57,12 @@ const cssNoDataColor = computed<string>(() => {
39
57
  return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
40
58
  });
41
59
  const rangeMin = computed<number | undefined>({
42
- get: () => {
43
- const range = attributeRange.value as number[] | undefined;
44
- return range ? range[0] : undefined;
45
- },
60
+ get: () => attributeRange.value[0],
46
61
  set: (val: number | undefined) => {
47
62
  if (val === undefined) {
48
63
  return;
49
64
  }
50
- const range = attributeRange.value as number[] | undefined;
51
- const currentMax = range ? range[1] : undefined;
65
+ const [, currentMax] = attributeRange.value;
52
66
  let newMin = val;
53
67
  if (typeof currentMax === "number" && val > currentMax) {
54
68
  newMin = currentMax;
@@ -57,16 +71,12 @@ const rangeMin = computed<number | undefined>({
57
71
  },
58
72
  });
59
73
  const rangeMax = computed<number | undefined>({
60
- get: () => {
61
- const range = attributeRange.value as number[] | undefined;
62
- return range ? range[1] : undefined;
63
- },
74
+ get: () => attributeRange.value[1],
64
75
  set: (val: number | undefined) => {
65
76
  if (val === undefined) {
66
77
  return;
67
78
  }
68
- const range = attributeRange.value as number[] | undefined;
69
- const currentMin = range ? range[0] : undefined;
79
+ const [currentMin] = attributeRange.value;
70
80
  let newMax = val;
71
81
  if (typeof currentMin === "number" && val < currentMin) {
72
82
  newMax = currentMin;
@@ -103,7 +113,37 @@ function hasSelectedComponent(components: unknown): boolean {
103
113
  return Array.isArray(components) && components.length > 0;
104
114
  }
105
115
 
106
- function getAttributes(): void {
116
+ async function getGroupAttributes(targetIds: string[]): Promise<void> {
117
+ const responses = await requestForTargets<{ attributes: AttributeInfo[] }>(schema, targetIds, {
118
+ attributes: [],
119
+ });
120
+ attributesPerTarget = new Map(
121
+ responses.map(([targetId, response]) => [targetId, response.attributes]),
122
+ );
123
+ attributes.value = intersectAttributes([...attributesPerTarget.values()]);
124
+ }
125
+
126
+ function initGroupAttribute(name: string, item: number): void {
127
+ const ranges = new Map<string, AttributeRange>();
128
+ for (const [targetId, targetAttributes] of attributesPerTarget) {
129
+ const attribute = targetAttributes.find((candidate) => candidate.attribute_name === name);
130
+ if (attribute) {
131
+ const { min, max } = getAttributeRange(
132
+ attribute as unknown as Parameters<typeof getAttributeRange>[0],
133
+ item,
134
+ );
135
+ ranges.set(targetId, [min, max]);
136
+ }
137
+ }
138
+ emit("update:attributeColorMap", attributeColorMap.value ?? "batlow");
139
+ emit("ranges_per_data", ranges);
140
+ }
141
+
142
+ async function getAttributes(): Promise<void> {
143
+ if (groupTargetIds.value) {
144
+ await getGroupAttributes(groupTargetIds.value);
145
+ return;
146
+ }
107
147
  const schemaProperties = schema.properties as Record<string, unknown> | undefined;
108
148
  const requiresComponent = schemaProperties?.component_ids !== undefined;
109
149
  if (requiresComponent && !hasSelectedComponent(componentIds)) {
@@ -115,7 +155,7 @@ function getAttributes(): void {
115
155
  params.component_ids = componentIds;
116
156
  }
117
157
 
118
- backStore.request(
158
+ await backStore.request(
119
159
  { schema, params },
120
160
  {
121
161
  response_function: (response: unknown) => {
@@ -125,25 +165,34 @@ function getAttributes(): void {
125
165
  );
126
166
  }
127
167
 
128
- onMounted(() => {
129
- getAttributes();
168
+ onMounted(async () => {
169
+ await getAttributes();
130
170
  });
131
171
 
132
172
  watch(
133
173
  () => [id, componentIds, schema],
134
- () => {
135
- getAttributes();
174
+ async () => {
175
+ await getAttributes();
136
176
  },
137
177
  );
138
178
 
179
+ watch([attributeName, attributeItem], ([name, item]) => {
180
+ if (groupTargetIds.value && name !== undefined) {
181
+ initGroupAttribute(name, item ?? 0);
182
+ }
183
+ });
184
+
139
185
  watch([attributeName, attributeItem, currentAttribute], () => {
186
+ if (groupTargetIds.value) {
187
+ return;
188
+ }
140
189
  if (attributeColorMap.value === undefined) {
141
190
  attributeColorMap.value = "batlow";
142
191
  }
143
192
  if (attributeNoDataColor.value === undefined) {
144
193
  attributeNoDataColor.value = DEFAULT_NO_DATA_COLOR;
145
194
  }
146
- if (!attributeRange.value || attributeRange.value[0] === undefined) {
195
+ if (attributeRange.value[0] === undefined) {
147
196
  resetRange();
148
197
  }
149
198
  });
@@ -200,6 +249,7 @@ watch([attributeName, attributeItem, currentAttribute], () => {
200
249
  v-model:minimum="rangeMin"
201
250
  v-model:maximum="rangeMax"
202
251
  v-model:colorMap="attributeColorMap"
252
+ :hide-range="groupTargetIds !== undefined"
203
253
  @reset="resetRange"
204
254
  />
205
255
  </template>
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { JsonRpcSchema } from "@ogw_shared/utils/types.js";
3
3
  import type { RGBAColor } from "@ogw_front/utils/default_styles/constants";
4
+ import type { RangesPerData } from "@ogw_front/composables/batch_style";
4
5
  import ViewerOptionsAttributeSelector from "@ogw_front/components/Viewer/Options/AttributeSelector.vue";
5
6
  import ViewerOptionsColorPicker from "@ogw_front/components/Viewer/Options/ColorPicker.vue";
6
7
  import ViewerOptionsTexturesSelector from "@ogw_front/components/Viewer/Options/TexturesSelector.vue";
@@ -26,6 +27,16 @@ interface Schemas {
26
27
 
27
28
  const coloring_style_key = defineModel<string>("coloring_style_key");
28
29
 
30
+ interface Emits {
31
+ vertex_attribute_ranges_per_data: [ranges: RangesPerData];
32
+ edge_attribute_ranges_per_data: [ranges: RangesPerData];
33
+ cell_attribute_ranges_per_data: [ranges: RangesPerData];
34
+ polygon_attribute_ranges_per_data: [ranges: RangesPerData];
35
+ polyhedron_attribute_ranges_per_data: [ranges: RangesPerData];
36
+ }
37
+
38
+ const emit = defineEmits<Emits>();
39
+
29
40
  const color = defineModel<RGBAColor>("color");
30
41
  const textures = defineModel<Texture[]>("textures");
31
42
 
@@ -246,6 +257,7 @@ watch(
246
257
  v-model:attributeRange="vertex_attribute_range"
247
258
  v-model:attributeColorMap="vertex_attribute_color_map"
248
259
  v-model:attributeNoDataColor="vertex_attribute_no_data_color"
260
+ @ranges_per_data="emit('vertex_attribute_ranges_per_data', $event)"
249
261
  :id="id"
250
262
  :componentIds="componentIds"
251
263
  :schema="vertexSchema"
@@ -258,6 +270,7 @@ watch(
258
270
  v-model:attributeRange="edge_attribute_range"
259
271
  v-model:attributeColorMap="edge_attribute_color_map"
260
272
  v-model:attributeNoDataColor="edge_attribute_no_data_color"
273
+ @ranges_per_data="emit('edge_attribute_ranges_per_data', $event)"
261
274
  :id="id"
262
275
  :componentIds="componentIds"
263
276
  :schema="edgeSchema"
@@ -270,6 +283,7 @@ watch(
270
283
  v-model:attributeRange="cell_attribute_range"
271
284
  v-model:attributeColorMap="cell_attribute_color_map"
272
285
  v-model:attributeNoDataColor="cell_attribute_no_data_color"
286
+ @ranges_per_data="emit('cell_attribute_ranges_per_data', $event)"
273
287
  :id="id"
274
288
  :componentIds="componentIds"
275
289
  :schema="cellSchema"
@@ -282,6 +296,7 @@ watch(
282
296
  v-model:attributeRange="polygon_attribute_range"
283
297
  v-model:attributeColorMap="polygon_attribute_color_map"
284
298
  v-model:attributeNoDataColor="polygon_attribute_no_data_color"
299
+ @ranges_per_data="emit('polygon_attribute_ranges_per_data', $event)"
285
300
  :id="id"
286
301
  :componentIds="componentIds"
287
302
  :schema="polygonSchema"
@@ -294,6 +309,7 @@ watch(
294
309
  v-model:attributeRange="polyhedron_attribute_range"
295
310
  v-model:attributeColorMap="polyhedron_attribute_color_map"
296
311
  v-model:attributeNoDataColor="polyhedron_attribute_no_data_color"
312
+ @ranges_per_data="emit('polyhedron_attribute_ranges_per_data', $event)"
297
313
  :id="id"
298
314
  :componentIds="componentIds"
299
315
  :schema="polyhedronSchema"
@@ -1,7 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import FileUploader from "@ogw_front/components/FileUploader.vue";
3
3
  import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_typed_schemas.js";
4
+ import { intersectBy } from "@ogw_front/utils/attributes";
5
+ import { requestForTargets } from "@ogw_front/utils/request_for_targets";
4
6
  import { useBackStore } from "@ogw_front/stores/back";
7
+ import { useBatchGroup } from "@ogw_front/composables/batch_style";
5
8
 
6
9
  // Mirrors FileUploader's own (unexported) UploadFile type.
7
10
  type UploadFile = File & { isConfigured?: boolean; displayName?: string };
@@ -40,21 +43,22 @@ watch(
40
43
  const textureCoordinates = ref<string[]>([]);
41
44
  const backStore = useBackStore();
42
45
 
43
- function getTextureCoordinates(): void {
46
+ const groupTargetIds = useBatchGroup(() => id);
47
+
48
+ async function getTextureCoordinates(): Promise<void> {
44
49
  const schema = back_schemas.opengeodeweb_back.texture_coordinates;
45
- const params = { id };
46
- backStore.request(
47
- { schema, params },
48
- {
49
- response_function: (response) => {
50
- textureCoordinates.value = response.texture_coordinates;
51
- },
52
- },
50
+ const targetIds = groupTargetIds.value ?? [id];
51
+ const responses = await requestForTargets<{ texture_coordinates: string[] }>(schema, targetIds, {
52
+ texture_coordinates: [],
53
+ });
54
+ textureCoordinates.value = intersectBy(
55
+ responses.map(([, response]) => response.texture_coordinates),
56
+ (coordinate) => coordinate,
53
57
  );
54
58
  }
55
59
 
56
- onMounted(() => {
57
- getTextureCoordinates();
60
+ onMounted(async () => {
61
+ await getTextureCoordinates();
58
62
  });
59
63
 
60
64
  async function files_uploaded_event(value: UploadFile[]): Promise<void> {
@@ -1,4 +1,9 @@
1
1
  <script setup lang="ts">
2
+ import {
3
+ type RangesPerData,
4
+ applyRangesPerData,
5
+ useBatchStyle,
6
+ } from "@ogw_front/composables/batch_style";
2
7
  import ViewerContextMenuItem, {
3
8
  type ItemProps,
4
9
  } from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem.vue";
@@ -7,7 +12,6 @@ import type { RGBAColor } from "@ogw_front/utils/default_styles/constants";
7
12
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector.vue";
8
13
  import ViewerOptionsSizeSlider from "@ogw_front/components/Viewer/Options/Sliders/Size.vue";
9
14
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch.vue";
10
- import { useBatchStyle } from "@ogw_front/composables/batch_style";
11
15
  import { useDataStyleStore } from "@ogw_front/stores/data_style";
12
16
  import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
13
17
 
@@ -120,6 +124,13 @@ const vertex_attribute_no_data_color = computed<RGBAColor>({
120
124
  hybridViewerStore.remoteRender();
121
125
  },
122
126
  });
127
+
128
+ async function setVertexAttributeRangesPerData(ranges: RangesPerData): Promise<void> {
129
+ await applyRangesPerData(ranges, (targetId, minimum, maximum) =>
130
+ dataStyleStore.setMeshPointsVertexAttributeRange(targetId, minimum, maximum),
131
+ );
132
+ hybridViewerStore.remoteRender();
133
+ }
123
134
  </script>
124
135
  <template>
125
136
  <ViewerContextMenuItem
@@ -144,6 +155,7 @@ const vertex_attribute_no_data_color = computed<RGBAColor>({
144
155
  v-model:vertex_attribute_name="vertex_attribute_name"
145
156
  v-model:vertex_attribute_item="vertex_attribute_item"
146
157
  v-model:vertex_attribute_range="vertex_attribute_range"
158
+ @vertex_attribute_ranges_per_data="setVertexAttributeRangesPerData"
147
159
  v-model:vertex_attribute_color_map="vertex_attribute_color_map"
148
160
  v-model:vertex_attribute_no_data_color="vertex_attribute_no_data_color"
149
161
  :vertex_has_colormap="true"
@@ -1,4 +1,9 @@
1
1
  <script setup lang="ts">
2
+ import {
3
+ type RangesPerData,
4
+ applyRangesPerData,
5
+ useBatchStyle,
6
+ } from "@ogw_front/composables/batch_style";
2
7
  import ViewerContextMenuItem, {
3
8
  type ItemProps,
4
9
  } from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem.vue";
@@ -6,7 +11,6 @@ import type { RGBAColor } from "@ogw_front/utils/default_styles/constants";
6
11
  import SolidPolyhedra from "@ogw_front/assets/viewer_svgs/solid_polyhedra.svg";
7
12
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector.vue";
8
13
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch.vue";
9
- import { useBatchStyle } from "@ogw_front/composables/batch_style";
10
14
  import { useDataStyleStore } from "@ogw_front/stores/data_style";
11
15
  import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
12
16
 
@@ -163,6 +167,20 @@ const polyhedron_attribute_no_data_color = computed<RGBAColor>({
163
167
  hybridViewerStore.remoteRender();
164
168
  },
165
169
  });
170
+
171
+ async function setVertexAttributeRangesPerData(ranges: RangesPerData): Promise<void> {
172
+ await applyRangesPerData(ranges, (targetId, minimum, maximum) =>
173
+ dataStyleStore.setMeshPolyhedraVertexAttributeRange(targetId, minimum, maximum),
174
+ );
175
+ hybridViewerStore.remoteRender();
176
+ }
177
+
178
+ async function setPolyhedronAttributeRangesPerData(ranges: RangesPerData): Promise<void> {
179
+ await applyRangesPerData(ranges, (targetId, minimum, maximum) =>
180
+ dataStyleStore.setMeshPolyhedraPolyhedronAttributeRange(targetId, minimum, maximum),
181
+ );
182
+ hybridViewerStore.remoteRender();
183
+ }
166
184
  </script>
167
185
  <template>
168
186
  <ViewerContextMenuItem
@@ -185,11 +203,13 @@ const polyhedron_attribute_no_data_color = computed<RGBAColor>({
185
203
  v-model:vertex_attribute_name="vertex_attribute_name"
186
204
  v-model:vertex_attribute_item="vertex_attribute_item"
187
205
  v-model:vertex_attribute_range="vertex_attribute_range"
206
+ @vertex_attribute_ranges_per_data="setVertexAttributeRangesPerData"
188
207
  v-model:vertex_attribute_color_map="vertex_attribute_color_map"
189
208
  v-model:vertex_attribute_no_data_color="vertex_attribute_no_data_color"
190
209
  v-model:polyhedron_attribute_name="polyhedron_attribute_name"
191
210
  v-model:polyhedron_attribute_item="polyhedron_attribute_item"
192
211
  v-model:polyhedron_attribute_range="polyhedron_attribute_range"
212
+ @polyhedron_attribute_ranges_per_data="setPolyhedronAttributeRangesPerData"
193
213
  v-model:polyhedron_attribute_color_map="polyhedron_attribute_color_map"
194
214
  v-model:polyhedron_attribute_no_data_color="polyhedron_attribute_no_data_color"
195
215
  />
@@ -1,4 +1,9 @@
1
1
  <script setup lang="ts">
2
+ import {
3
+ type RangesPerData,
4
+ applyRangesPerData,
5
+ useBatchStyle,
6
+ } from "@ogw_front/composables/batch_style";
2
7
  import ViewerContextMenuItem, {
3
8
  type ItemProps,
4
9
  } from "@ogw_front/components/Viewer/ContextMenu/ContextMenuItem.vue";
@@ -6,7 +11,6 @@ import PolygonalSurfacePolygons from "@ogw_front/assets/viewer_svgs/surface_tria
6
11
  import type { RGBAColor } from "@ogw_front/utils/default_styles/constants";
7
12
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector.vue";
8
13
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch.vue";
9
- import { useBatchStyle } from "@ogw_front/composables/batch_style";
10
14
  import { useDataStyleStore } from "@ogw_front/stores/data_style";
11
15
  import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
12
16
 
@@ -178,6 +182,20 @@ const polygon_attribute_no_data_color = computed<RGBAColor>({
178
182
  hybridViewerStore.remoteRender();
179
183
  },
180
184
  });
185
+
186
+ async function setVertexAttributeRangesPerData(ranges: RangesPerData): Promise<void> {
187
+ await applyRangesPerData(ranges, (targetId, minimum, maximum) =>
188
+ dataStyleStore.setMeshPolygonsVertexAttributeRange(targetId, minimum, maximum),
189
+ );
190
+ hybridViewerStore.remoteRender();
191
+ }
192
+
193
+ async function setPolygonAttributeRangesPerData(ranges: RangesPerData): Promise<void> {
194
+ await applyRangesPerData(ranges, (targetId, minimum, maximum) =>
195
+ dataStyleStore.setMeshPolygonsPolygonAttributeRange(targetId, minimum, maximum),
196
+ );
197
+ hybridViewerStore.remoteRender();
198
+ }
181
199
  </script>
182
200
 
183
201
  <template>
@@ -202,11 +220,13 @@ const polygon_attribute_no_data_color = computed<RGBAColor>({
202
220
  v-model:vertex_attribute_name="vertex_attribute_name"
203
221
  v-model:vertex_attribute_item="vertex_attribute_item"
204
222
  v-model:vertex_attribute_range="vertex_attribute_range"
223
+ @vertex_attribute_ranges_per_data="setVertexAttributeRangesPerData"
205
224
  v-model:vertex_attribute_color_map="vertex_attribute_color_map"
206
225
  v-model:vertex_attribute_no_data_color="vertex_attribute_no_data_color"
207
226
  v-model:polygon_attribute_name="polygon_attribute_name"
208
227
  v-model:polygon_attribute_item="polygon_attribute_item"
209
228
  v-model:polygon_attribute_range="polygon_attribute_range"
229
+ @polygon_attribute_ranges_per_data="setPolygonAttributeRangesPerData"
210
230
  v-model:polygon_attribute_color_map="polygon_attribute_color_map"
211
231
  v-model:polygon_attribute_no_data_color="polygon_attribute_no_data_color"
212
232
  />
@@ -1,12 +1,16 @@
1
1
  <script setup lang="ts">
2
+ import {
3
+ type ViewerIdResult,
4
+ useOverlappingPicker,
5
+ } from "@ogw_front/composables/use_overlapping_picker";
2
6
  import OverlappingObjectsPicker from "@ogw_front/components/Viewer/OverlappingObjectsPicker.vue";
3
7
  import ToolActiveChip from "@ogw_front/components/Viewer/ToolActiveChip.vue";
8
+ import type { TreeMenuPayload } from "@ogw_front/utils/treeview";
4
9
  import ViewerContextMenu from "@ogw_front/components/Viewer/ContextMenu/ContextMenu.vue";
5
10
  import ViewerObjectTreeLayout from "@ogw_front/components/Viewer/ObjectTree/Layout.vue";
6
11
  import { getCurrentInstance } from "vue";
7
12
  import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
8
13
  import { useMenuStore } from "@ogw_front/stores/menu";
9
- import { useOverlappingPicker } from "@ogw_front/composables/use_overlapping_picker";
10
14
  import { useViewerStore } from "@ogw_front/stores/viewer";
11
15
 
12
16
  interface Props {
@@ -18,7 +22,7 @@ interface Props {
18
22
  const { displayMenu, containerWidth, containerHeight } = defineProps<Props>();
19
23
 
20
24
  const emit = defineEmits<{
21
- "show-menu": [args: unknown];
25
+ "show-menu": [payload: TreeMenuPayload];
22
26
  }>();
23
27
  const menuStore = useMenuStore();
24
28
  const viewerStore = useViewerStore();
@@ -66,7 +70,7 @@ const {
66
70
  get_viewer_id: trigger_picker,
67
71
  } = useOverlappingPicker();
68
72
 
69
- function get_viewer_id(x: number, y: number): string {
73
+ function get_viewer_id(x: number, y: number): Promise<ViewerIdResult> {
70
74
  const instance = getCurrentInstance();
71
75
  const containerRect = instance?.proxy?.$el
72
76
  ?.closest?.('[data-testid="hybridViewer"]')
@@ -1,14 +1,74 @@
1
+ import {
2
+ type ComputedRef,
3
+ type InjectionKey,
4
+ type MaybeRefOrGetter,
5
+ computed,
6
+ inject,
7
+ provide,
8
+ toValue,
9
+ } from "vue";
1
10
  import { useDataStore } from "@ogw_front/stores/data";
2
11
  import { useTreeviewStore } from "@ogw_front/stores/treeview";
3
12
 
4
- export function useBatchStyle(): { applyBatchStyle: typeof applyBatchStyle } {
13
+ type AttributeRange = [number, number];
14
+ type RangesPerData = ReadonlyMap<string, AttributeRange>;
15
+
16
+ const BATCH_GROUP_KEY: InjectionKey<ComputedRef<string[]>> = Symbol("batch_group");
17
+
18
+ function provideBatchGroup(targetIds: () => string[]): void {
19
+ provide(BATCH_GROUP_KEY, computed(targetIds));
20
+ }
21
+
22
+ function injectBatchGroup(): ComputedRef<string[]> {
23
+ return inject(BATCH_GROUP_KEY, () => computed(() => []), true);
24
+ }
25
+
26
+ function groupTargetsOf(targetIds: string[], id: string): string[] | undefined {
27
+ return targetIds.includes(id) ? targetIds : undefined;
28
+ }
29
+
30
+ function useBatchGroup(id: MaybeRefOrGetter<string>): ComputedRef<string[] | undefined> {
31
+ const group = injectBatchGroup();
32
+ return computed(() => groupTargetsOf(group.value, toValue(id)));
33
+ }
34
+
35
+ async function applyRangesPerData(
36
+ ranges: RangesPerData,
37
+ setRange: (id: string, minimum: number, maximum: number) => unknown,
38
+ ): Promise<void> {
39
+ await Promise.all(
40
+ [...ranges].map(async ([targetId, [minimum, maximum]]) => {
41
+ await setRange(targetId, minimum, maximum);
42
+ }),
43
+ );
44
+ }
45
+
46
+ async function applyActionOn(
47
+ targets: readonly string[],
48
+ action: (id: string) => Promise<unknown>,
49
+ ): Promise<void> {
50
+ await Promise.all(
51
+ targets.map(async (targetId) => {
52
+ await action(targetId);
53
+ }),
54
+ );
55
+ }
56
+
57
+ function useBatchStyle(): { applyBatchStyle: typeof applyBatchStyle } {
5
58
  const treeviewStore = useTreeviewStore();
6
59
  const dataStore = useDataStore();
60
+ const group = injectBatchGroup();
7
61
 
8
62
  async function applyBatchStyle(
9
63
  id: string,
10
64
  action: (id: string) => Promise<unknown>,
11
65
  ): Promise<void> {
66
+ const groupTargets = groupTargetsOf(group.value, id);
67
+ if (groupTargets) {
68
+ await applyActionOn(groupTargets, action);
69
+ return;
70
+ }
71
+
12
72
  const isActive = treeviewStore.activeItems.includes(id);
13
73
  if (!isActive || treeviewStore.activeItems.length <= 1) {
14
74
  await action(id);
@@ -38,3 +98,6 @@ export function useBatchStyle(): { applyBatchStyle: typeof applyBatchStyle } {
38
98
 
39
99
  return { applyBatchStyle };
40
100
  }
101
+
102
+ export { BATCH_GROUP_KEY, applyRangesPerData, provideBatchGroup, useBatchGroup, useBatchStyle };
103
+ export type { AttributeRange, RangesPerData };
@@ -30,10 +30,10 @@ interface UseTreeFilterReturn {
30
30
  availableFilterOptions: ComputedRef<string[]>;
31
31
  toggleSort: () => void;
32
32
  customFilter: typeof customFilter;
33
- applySearchFilter: (
34
- newSelection: readonly unknown[],
35
- previousSelection?: readonly unknown[],
36
- ) => unknown[];
33
+ applySearchFilter: <TId>(
34
+ newSelection: readonly TId[],
35
+ previousSelection?: readonly TId[],
36
+ ) => TId[];
37
37
  }
38
38
 
39
39
  function customFilter(
@@ -163,10 +163,10 @@ function useTreeFilter(
163
163
  return map;
164
164
  });
165
165
 
166
- function applySearchFilter(
167
- newSelection: readonly unknown[],
168
- previousSelection: readonly unknown[] = [],
169
- ): unknown[] {
166
+ function applySearchFilter<TId>(
167
+ newSelection: readonly TId[],
168
+ previousSelection: readonly TId[] = [],
169
+ ): TId[] {
170
170
  if (!search.value) {
171
171
  return [...newSelection];
172
172
  }
@@ -192,3 +192,5 @@ export function useOverlappingPicker(): UseOverlappingPickerReturn {
192
192
  get_viewer_id,
193
193
  };
194
194
  }
195
+
196
+ export type { ViewerIdResult };