@geode/opengeodeweb-front 10.32.2 → 10.32.3-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.
@@ -1,4 +1,5 @@
1
1
  <script setup>
2
+ import AttributeRangeSelector from "./AttributeRangeSelector.vue";
2
3
  import ColorMapPicker from "./ColorMapPicker.vue";
3
4
 
4
5
  const emit = defineEmits(["reset"]);
@@ -11,43 +12,12 @@ const colorMap = defineModel("colorMap", { type: String });
11
12
  <template>
12
13
  <div class="attribute-colorbar mt-3">
13
14
  <ColorMapPicker v-model:selected-preset-name="colorMap" :min="minimum" :max="maximum" />
14
- <v-row dense align="center" class="mt-2" no-gutters>
15
- <v-col cols="5" class="pe-1">
16
- <v-text-field
17
- data-testid="attributeMinInput"
18
- :model-value="minimum"
19
- @update:model-value="(value) => (minimum = Number(value))"
20
- label="Min"
21
- type="number"
22
- :max="maximum"
23
- density="compact"
24
- hide-details
25
- variant="outlined"
26
- />
27
- </v-col>
28
- <v-col cols="2" class="d-flex justify-center">
29
- <v-btn
30
- icon="mdi-arrow-left-right"
31
- size="x-small"
32
- variant="text"
33
- @click="emit('reset')"
34
- v-tooltip="'Reset range'"
35
- />
36
- </v-col>
37
- <v-col cols="5" class="ps-1">
38
- <v-text-field
39
- data-testid="attributeMaxInput"
40
- :model-value="maximum"
41
- @update:model-value="(value) => (maximum = Number(value))"
42
- label="Max"
43
- type="number"
44
- :min="minimum"
45
- density="compact"
46
- hide-details
47
- variant="outlined"
48
- />
49
- </v-col>
50
- </v-row>
15
+ <AttributeRangeSelector
16
+ v-model:minimum="minimum"
17
+ v-model:maximum="maximum"
18
+ class="mt-2"
19
+ @reset="emit('reset')"
20
+ />
51
21
  </div>
52
22
  </template>
53
23
 
@@ -0,0 +1,48 @@
1
+ <script setup>
2
+ const emit = defineEmits(["reset"]);
3
+
4
+ const minimum = defineModel("minimum", { type: Number });
5
+ const maximum = defineModel("maximum", { type: Number });
6
+ </script>
7
+
8
+ <template>
9
+ <v-row dense align="center" no-gutters>
10
+ <v-col cols="5" class="pe-1">
11
+ <v-text-field
12
+ data-testid="attributeMinInput"
13
+ :model-value="minimum"
14
+ @update:model-value="(value) => (minimum = Number(value))"
15
+ label="Min"
16
+ type="number"
17
+ :max="maximum"
18
+ density="compact"
19
+ hide-details
20
+ variant="outlined"
21
+ />
22
+ </v-col>
23
+ <v-col cols="2" class="d-flex justify-center">
24
+ <v-btn
25
+ data-testid="resetRangeButton"
26
+ aria-label="Reset range"
27
+ icon="mdi-arrow-left-right"
28
+ size="x-small"
29
+ variant="text"
30
+ @click="emit('reset')"
31
+ v-tooltip="'Reset range'"
32
+ />
33
+ </v-col>
34
+ <v-col cols="5" class="ps-1">
35
+ <v-text-field
36
+ data-testid="attributeMaxInput"
37
+ :model-value="maximum"
38
+ @update:model-value="(value) => (maximum = Number(value))"
39
+ label="Max"
40
+ type="number"
41
+ :min="minimum"
42
+ density="compact"
43
+ hide-details
44
+ variant="outlined"
45
+ />
46
+ </v-col>
47
+ </v-row>
48
+ </template>
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
2
  import ViewerOptionsAttributeColorBar from "@ogw_front/components/Viewer/Options/AttributeColorBar.vue";
3
+ import { getAttributeRange } from "@ogw_front/utils/attributes";
3
4
  import { useBackStore } from "@ogw_front/stores/back";
4
5
 
5
6
  const backStore = useBackStore();
@@ -17,6 +18,9 @@ const { id, componentIds, schema } = defineProps({
17
18
 
18
19
  const attributes = ref([]);
19
20
 
21
+ const currentAttribute = computed(() =>
22
+ attributes.value.find((attr) => attr.attribute_name === attributeName.value),
23
+ );
20
24
  const rangeMin = computed({
21
25
  get: () => (attributeRange.value ? attributeRange.value[0] : undefined),
22
26
  set: (val) => {
@@ -40,10 +44,6 @@ const rangeMax = computed({
40
44
  },
41
45
  });
42
46
 
43
- const currentAttribute = computed(() =>
44
- attributes.value.find((attr) => attr.attribute_name === attributeName.value),
45
- );
46
-
47
47
  const componentItems = computed(() => {
48
48
  if (!currentAttribute.value) {
49
49
  return [];
@@ -57,10 +57,8 @@ const componentItems = computed(() => {
57
57
  function resetRange() {
58
58
  if (currentAttribute.value) {
59
59
  const comp = attributeItem.value ?? 0;
60
- attributeRange.value = [
61
- currentAttribute.value.min_values[comp],
62
- currentAttribute.value.max_values[comp],
63
- ];
60
+ const { min, max } = getAttributeRange(currentAttribute.value, comp);
61
+ attributeRange.value = [min, max];
64
62
  }
65
63
  }
66
64
 
@@ -168,6 +168,7 @@ watch(filteredPresets, drawAllCanvases);
168
168
  </v-list-item>
169
169
  </template>
170
170
  </v-list>
171
+ <slot></slot>
171
172
  </GlassCard>
172
173
  </template>
173
174
 
@@ -1,9 +1,9 @@
1
1
  <script setup>
2
+ import AttributeRangeSelector from "@ogw_front/components/Viewer/Options/AttributeRangeSelector.vue";
2
3
  import ColorMapList from "@ogw_front/components/Viewer/Options/ColorMapList.vue";
3
- import { getPresetsWithCurrentAtTop } from "@ogw_front/utils/colormap";
4
4
 
5
- import { useDataStyleStore } from "@ogw_front/stores/data_style";
6
- import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
5
+ import { getPresetsWithCurrentAtTop } from "@ogw_front/utils/colormap";
6
+ import { useGlobalAttributeStyle } from "@ogw_front/composables/global_attribute_style";
7
7
 
8
8
  const { dataId, x, y } = defineProps({
9
9
  dataId: { required: false, type: String, default: undefined },
@@ -13,94 +13,28 @@ const { dataId, x, y } = defineProps({
13
13
 
14
14
  const show = defineModel("show", { type: Boolean, default: false });
15
15
 
16
- const dataStyleStore = useDataStyleStore();
17
- const hybridViewerStore = useHybridViewerStore();
18
-
19
- const componentNames = [
20
- { getterKey: "meshPoints", setterKey: "MeshPoints", key: "points" },
21
- { getterKey: "meshEdges", setterKey: "MeshEdges", key: "edges" },
22
- { getterKey: "meshPolygons", setterKey: "MeshPolygons", key: "polygons" },
23
- { getterKey: "meshCells", setterKey: "MeshCells", key: "cells" },
24
- { getterKey: "meshPolyhedra", setterKey: "MeshPolyhedra", key: "polyhedra" },
25
- ];
26
-
27
- const current = computed(() => {
28
- const targetId = dataId;
29
- if (!targetId) {
30
- return "batlow";
31
- }
32
-
33
- const style = dataStyleStore.getStyle(targetId);
34
- if (!style) {
35
- return "batlow";
36
- }
37
-
38
- for (const { key, getterKey } of componentNames) {
39
- if (!style[key] || !style[key].coloring) {
40
- continue;
41
- }
42
-
43
- const activeColoring = style[key].coloring.active;
44
- if (!["vertex", "edge", "polygon", "cell", "polyhedron"].includes(activeColoring)) {
45
- continue;
46
- }
47
-
48
- const attributeType = `${activeColoring.charAt(0).toUpperCase()}${activeColoring.slice(1)}Attribute`;
49
- const getterName = `${getterKey}${attributeType}ColorMap`;
50
- const getter = dataStyleStore[getterName];
51
-
52
- if (!getter) {
53
- continue;
54
- }
16
+ const dataIdRef = computed(() => dataId);
17
+ const { currentColormap, currentRange, applyGlobalColormap, resetGlobalRange } =
18
+ useGlobalAttributeStyle(dataIdRef);
55
19
 
56
- const colorMap = getter(targetId);
57
- if (colorMap) {
58
- return colorMap;
59
- }
60
- }
20
+ const minimum = computed({
21
+ get: () => currentRange.value[0],
22
+ set: (val) => {
23
+ currentRange.value = [val, currentRange.value[1]];
24
+ },
25
+ });
61
26
 
62
- return "batlow";
27
+ const maximum = computed({
28
+ get: () => currentRange.value[1],
29
+ set: (val) => {
30
+ currentRange.value = [currentRange.value[0], val];
31
+ },
63
32
  });
64
33
 
65
- const quickColormapPresets = computed(() => getPresetsWithCurrentAtTop(current.value));
34
+ const quickColormapPresets = computed(() => getPresetsWithCurrentAtTop(currentColormap.value));
66
35
 
67
36
  async function onQuickColormapSelect(preset) {
68
- show.value = false;
69
- const newMap = preset.Name;
70
- const targetId = dataId;
71
- if (!targetId) {
72
- return;
73
- }
74
-
75
- const style = dataStyleStore.getStyle(targetId);
76
- if (!style) {
77
- return;
78
- }
79
-
80
- const promises = [];
81
-
82
- for (const { key, setterKey } of componentNames) {
83
- if (!style[key] || !style[key].coloring) {
84
- continue;
85
- }
86
-
87
- const activeColoring = style[key].coloring.active;
88
- if (!["vertex", "edge", "polygon", "cell", "polyhedron"].includes(activeColoring)) {
89
- continue;
90
- }
91
-
92
- const attributeType = `${activeColoring.charAt(0).toUpperCase() + activeColoring.slice(1)}Attribute`;
93
- const setterName = `set${setterKey}${attributeType}ColorMap`;
94
- const setter = dataStyleStore[setterName];
95
-
96
- if (setter) {
97
- promises.push(setter(targetId, newMap));
98
- }
99
- }
100
-
101
- await Promise.all(promises);
102
-
103
- hybridViewerStore.remoteRender();
37
+ await applyGlobalColormap(preset.Name);
104
38
  }
105
39
  </script>
106
40
 
@@ -114,8 +48,15 @@ async function onQuickColormapSelect(preset) {
114
48
  >
115
49
  <ColorMapList
116
50
  :presets="quickColormapPresets"
117
- :selected-preset-name="current"
51
+ :selected-preset-name="currentColormap"
118
52
  @select="onQuickColormapSelect"
119
- />
53
+ >
54
+ <v-divider class="my-2"></v-divider>
55
+ <AttributeRangeSelector
56
+ v-model:minimum="minimum"
57
+ v-model:maximum="maximum"
58
+ @reset="resetGlobalRange"
59
+ />
60
+ </ColorMapList>
120
61
  </v-menu>
121
62
  </template>
@@ -0,0 +1,181 @@
1
+ import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json";
2
+ import { computed } from "vue";
3
+
4
+ import { getAttributeRange } from "@ogw_front/utils/attributes";
5
+ import { useBackStore } from "@ogw_front/stores/back";
6
+ import { useDataStyleStore } from "@ogw_front/stores/data_style";
7
+ import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
8
+
9
+ export function useGlobalAttributeStyle(dataIdRef) {
10
+ const dataStyleStore = useDataStyleStore();
11
+ const hybridViewerStore = useHybridViewerStore();
12
+ const backStore = useBackStore();
13
+
14
+ const componentNames = [
15
+ { getterKey: "meshPoints", setterKey: "MeshPoints", key: "points" },
16
+ { getterKey: "meshEdges", setterKey: "MeshEdges", key: "edges" },
17
+ { getterKey: "meshPolygons", setterKey: "MeshPolygons", key: "polygons" },
18
+ { getterKey: "meshCells", setterKey: "MeshCells", key: "cells" },
19
+ { getterKey: "meshPolyhedra", setterKey: "MeshPolyhedra", key: "polyhedra" },
20
+ ];
21
+
22
+ function getActiveComponents(targetId) {
23
+ const style = dataStyleStore.getStyle(targetId);
24
+ const activeComponents = [];
25
+ if (!style) {
26
+ return activeComponents;
27
+ }
28
+ for (const { key, getterKey, setterKey } of componentNames) {
29
+ if (!style[key] || !style[key].coloring) {
30
+ continue;
31
+ }
32
+
33
+ const activeColoring = style[key].coloring.active;
34
+ if (!["vertex", "edge", "polygon", "cell", "polyhedron"].includes(activeColoring)) {
35
+ continue;
36
+ }
37
+
38
+ const attributeType = `${activeColoring.charAt(0).toUpperCase()}${activeColoring.slice(1)}Attribute`;
39
+ activeComponents.push({ activeColoring, attributeType, getterKey, setterKey });
40
+ }
41
+ return activeComponents;
42
+ }
43
+
44
+ const currentColormap = computed(() => {
45
+ const targetId = dataIdRef.value;
46
+ if (!targetId) {
47
+ return "batlow";
48
+ }
49
+
50
+ for (const comp of getActiveComponents(targetId)) {
51
+ const getterName = `${comp.getterKey}${comp.attributeType}ColorMap`;
52
+ const getter = dataStyleStore[getterName];
53
+ if (getter) {
54
+ const colorMap = getter(targetId);
55
+ if (colorMap) {
56
+ return colorMap;
57
+ }
58
+ }
59
+ }
60
+
61
+ return "batlow";
62
+ });
63
+
64
+ const currentRange = computed({
65
+ get() {
66
+ const targetId = dataIdRef.value;
67
+ if (!targetId) {
68
+ return [0, 1];
69
+ }
70
+
71
+ for (const comp of getActiveComponents(targetId)) {
72
+ const getterName = `${comp.getterKey}${comp.attributeType}Range`;
73
+ const getter = dataStyleStore[getterName];
74
+ if (getter) {
75
+ const range = getter(targetId);
76
+ if (range && range.length === 2) {
77
+ return range;
78
+ }
79
+ }
80
+ }
81
+ return [0, 1];
82
+ },
83
+ set(newValue) {
84
+ const targetId = dataIdRef.value;
85
+ if (!targetId) {
86
+ return;
87
+ }
88
+
89
+ let updated = false;
90
+ for (const comp of getActiveComponents(targetId)) {
91
+ const setterName = `set${comp.setterKey}${comp.attributeType}Range`;
92
+ const setter = dataStyleStore[setterName];
93
+ if (setter) {
94
+ setter(targetId, newValue[0], newValue[1]);
95
+ updated = true;
96
+ }
97
+ }
98
+ if (updated) {
99
+ hybridViewerStore.remoteRender();
100
+ }
101
+ },
102
+ });
103
+
104
+ async function applyGlobalColormap(newMap) {
105
+ const targetId = dataIdRef.value;
106
+ if (!targetId) {
107
+ return;
108
+ }
109
+
110
+ const promises = [];
111
+
112
+ for (const comp of getActiveComponents(targetId)) {
113
+ const setterName = `set${comp.setterKey}${comp.attributeType}ColorMap`;
114
+ const setter = dataStyleStore[setterName];
115
+
116
+ if (setter) {
117
+ promises.push(setter(targetId, newMap));
118
+ }
119
+ }
120
+
121
+ await Promise.all(promises);
122
+ hybridViewerStore.remoteRender();
123
+ }
124
+
125
+ function resetGlobalRange() {
126
+ const targetId = dataIdRef.value;
127
+ if (!targetId) {
128
+ return;
129
+ }
130
+
131
+ for (const comp of getActiveComponents(targetId)) {
132
+ const { activeColoring, attributeType, getterKey, setterKey } = comp;
133
+
134
+ const nameGetter = dataStyleStore[`${getterKey}${attributeType}Name`];
135
+ const itemGetter = dataStyleStore[`${getterKey}${attributeType}Item`];
136
+ if (!nameGetter || !itemGetter) {
137
+ continue;
138
+ }
139
+
140
+ const attrName = nameGetter(targetId);
141
+ const attrItem = itemGetter(targetId) ?? 0;
142
+
143
+ if (!attrName) {
144
+ continue;
145
+ }
146
+
147
+ const schemaName = `${activeColoring}_attribute_names`;
148
+ const schema = back_schemas.opengeodeweb_back[schemaName];
149
+ if (!schema) {
150
+ continue;
151
+ }
152
+
153
+ backStore.request(
154
+ { schema, params: { id: targetId } },
155
+ {
156
+ response_function: (response) => {
157
+ const attributes = response.attributes || [];
158
+ const currentAttribute = attributes.find((attr) => attr.attribute_name === attrName);
159
+ if (currentAttribute) {
160
+ const { min, max } = getAttributeRange(currentAttribute, attrItem);
161
+
162
+ const setterName = `set${setterKey}${attributeType}Range`;
163
+ const setter = dataStyleStore[setterName];
164
+ if (setter) {
165
+ setter(targetId, min, max);
166
+ hybridViewerStore.remoteRender();
167
+ }
168
+ }
169
+ },
170
+ },
171
+ );
172
+ }
173
+ }
174
+
175
+ return {
176
+ currentColormap,
177
+ currentRange,
178
+ applyGlobalColormap,
179
+ resetGlobalRange,
180
+ };
181
+ }
@@ -0,0 +1,23 @@
1
+ export function getAttributeRange(currentAttribute, compIndex = 0) {
2
+ if (!currentAttribute) {
3
+ return { min: 0, max: 1 };
4
+ }
5
+
6
+ const { min_values, max_values, min_value, max_value } = currentAttribute;
7
+ let min = 0;
8
+ let max = 1;
9
+
10
+ if (min_values && min_values[compIndex] !== undefined) {
11
+ min = min_values[compIndex];
12
+ } else if (compIndex === 0 && min_value !== undefined) {
13
+ min = min_value;
14
+ }
15
+
16
+ if (max_values && max_values[compIndex] !== undefined) {
17
+ max = max_values[compIndex];
18
+ } else if (compIndex === 0 && max_value !== undefined) {
19
+ max = max_value;
20
+ }
21
+
22
+ return { min, max };
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geode/opengeodeweb-front",
3
- "version": "10.32.2",
3
+ "version": "10.32.3-rc.1",
4
4
  "description": "OpenSource Vue/Nuxt/Pinia/Vuetify framework for web applications",
5
5
  "homepage": "https://github.com/Geode-solutions/OpenGeodeWeb-Front",
6
6
  "bugs": {
@@ -39,8 +39,8 @@
39
39
  "build": ""
40
40
  },
41
41
  "dependencies": {
42
- "@geode/opengeodeweb-back": "latest",
43
- "@geode/opengeodeweb-viewer": "latest",
42
+ "@geode/opengeodeweb-back": "next",
43
+ "@geode/opengeodeweb-viewer": "next",
44
44
  "@google-cloud/run": "3.2.0",
45
45
  "@kitware/vtk.js": "33.3.0",
46
46
  "@mdi/font": "7.4.47",