@geode/opengeodeweb-front 10.31.1-rc.1 → 10.32.0

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.
@@ -55,6 +55,14 @@ function debounce(func, wait) {
55
55
 
56
56
  async function handleClick(event) {
57
57
  const { offsetX, offsetY, clientX, clientY } = event;
58
+ if (hybridViewerStore.is_ruler_active) {
59
+ const rect = container.value.$el.getBoundingClientRect();
60
+ const x = event.clientX - rect.left;
61
+ const y = elementHeight.value - (event.clientY - rect.top);
62
+ await hybridViewerStore.handleRulerClick(x, y);
63
+ return;
64
+ }
65
+
58
66
  if (viewerStore.picking_mode) {
59
67
  const rect = container.value.$el.getBoundingClientRect();
60
68
  const x = event.clientX - rect.left;
@@ -0,0 +1,144 @@
1
+ <script setup>
2
+ import ToolPanel from "@ogw_front/components/ToolPanel";
3
+ import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
4
+
5
+ const show = defineModel("show", { type: Boolean, default: false });
6
+ const hybridViewerStore = useHybridViewerStore();
7
+
8
+ const localPoint1 = ref([0, 0, 0]);
9
+ const localPoint2 = ref([0, 0, 0]);
10
+
11
+ watch(
12
+ () => hybridViewerStore.ruler_point1,
13
+ (point) => {
14
+ if (point) {
15
+ localPoint1.value = [...point];
16
+ }
17
+ },
18
+ );
19
+
20
+ watch(
21
+ () => hybridViewerStore.ruler_point2,
22
+ (point) => {
23
+ if (point) {
24
+ localPoint2.value = [...point];
25
+ }
26
+ },
27
+ );
28
+
29
+ watch(show, (visible) => {
30
+ if (visible) {
31
+ hybridViewerStore.is_ruler_active = true;
32
+ } else if (hybridViewerStore.is_ruler_active) {
33
+ hybridViewerStore.deactivateRuler();
34
+ }
35
+ });
36
+
37
+ watch(
38
+ () => hybridViewerStore.is_ruler_active,
39
+ (active) => {
40
+ if (!active) {
41
+ show.value = false;
42
+ }
43
+ },
44
+ );
45
+
46
+ async function applyManualCoords() {
47
+ hybridViewerStore.ruler_point1 = [...localPoint1.value];
48
+ hybridViewerStore.ruler_point2 = [...localPoint2.value];
49
+ await hybridViewerStore.applyRuler();
50
+ }
51
+ </script>
52
+
53
+ <template>
54
+ <ToolPanel
55
+ v-model="show"
56
+ title="Ruler"
57
+ :width="300"
58
+ close-label="Stop ruler"
59
+ :click-outside="false"
60
+ >
61
+ <v-card-text class="pa-3">
62
+ <v-switch
63
+ v-model="hybridViewerStore.ruler_snap"
64
+ data-testid="rulerSnapToggle"
65
+ label="Snap to vertex"
66
+ color="primary"
67
+ density="compact"
68
+ hide-details
69
+ class="mb-4"
70
+ />
71
+
72
+ <v-divider class="mb-3" />
73
+
74
+ <div class="text-caption font-weight-bold mb-2 text-medium-emphasis">Point 1</div>
75
+ <v-row dense class="mb-1" data-testid="rulerPointCard">
76
+ <v-col v-for="(axis, index) in ['X', 'Y', 'Z']" :key="axis">
77
+ <v-text-field
78
+ v-model.number="localPoint1[index]"
79
+ data-testid="rulerPointCoordInput"
80
+ :label="axis"
81
+ type="number"
82
+ density="compact"
83
+ variant="outlined"
84
+ hide-details
85
+ />
86
+ </v-col>
87
+ </v-row>
88
+
89
+ <div class="text-caption font-weight-bold mb-2 mt-3 text-medium-emphasis">Point 2</div>
90
+ <v-row dense class="mb-1" data-testid="rulerPointCard">
91
+ <v-col v-for="(axis, index) in ['X', 'Y', 'Z']" :key="axis">
92
+ <v-text-field
93
+ v-model.number="localPoint2[index]"
94
+ data-testid="rulerPointCoordInput"
95
+ :label="axis"
96
+ type="number"
97
+ density="compact"
98
+ variant="outlined"
99
+ hide-details
100
+ />
101
+ </v-col>
102
+ </v-row>
103
+
104
+ <v-btn
105
+ data-testid="rulerApplyButton"
106
+ variant="tonal"
107
+ size="small"
108
+ color="primary"
109
+ block
110
+ class="mt-3 text-caption text-none"
111
+ @click="applyManualCoords"
112
+ >
113
+ Apply
114
+ </v-btn>
115
+
116
+ <v-divider class="my-3" />
117
+
118
+ <div v-if="hybridViewerStore.ruler_distance !== undefined" class="text-center">
119
+ <div class="text-caption text-medium-emphasis mb-1">Distance</div>
120
+ <div data-testid="rulerDistance" class="text-h6 font-weight-bold">
121
+ {{ hybridViewerStore.ruler_distance.toFixed(4) }}
122
+ </div>
123
+ </div>
124
+ <div v-else class="text-caption text-medium-emphasis text-center">
125
+ Click in the scene to set both points
126
+ </div>
127
+ </v-card-text>
128
+
129
+ <template #actions>
130
+ <v-card-actions class="justify-center pb-3 pt-0">
131
+ <v-btn
132
+ data-testid="rulerClearButton"
133
+ variant="text"
134
+ size="small"
135
+ color="error"
136
+ class="text-caption text-none"
137
+ @click="hybridViewerStore.clearRuler()"
138
+ >
139
+ Clear
140
+ </v-btn>
141
+ </v-card-actions>
142
+ </template>
143
+ </ToolPanel>
144
+ </template>
@@ -4,6 +4,7 @@ import CameraBookmarkIcon from "@ogw_front/assets/viewer_svgs/camera-bookmark.sv
4
4
  import CameraManager from "@ogw_front/components/CameraManager";
5
5
  import CameraOrientation from "@ogw_front/components/CameraOrientation";
6
6
  import ClippingPlanes from "@ogw_front/components/ClippingPlanes";
7
+ import Ruler from "@ogw_front/components/Ruler";
7
8
  import Screenshot from "@ogw_front/components/Screenshot";
8
9
  import ShrinkFilter from "@ogw_front/components/ShrinkFilter";
9
10
  import ZScaling from "@ogw_front/components/ZScaling";
@@ -19,6 +20,7 @@ const showCameraOrientation = ref(false);
19
20
  const showZScaling = ref(false);
20
21
  const showClippingPlanes = ref(false);
21
22
  const showShrinkFilter = ref(false);
23
+ const showRuler = ref(false);
22
24
  const grid_scale = ref(false);
23
25
  const zScale = ref(hybridViewerStore.zScale);
24
26
 
@@ -173,6 +175,15 @@ const camera_options = computed(() => [
173
175
  showShrinkFilter.value = !showShrinkFilter.value;
174
176
  },
175
177
  },
178
+ {
179
+ testId: "rulerButton",
180
+ tooltip: "Ruler",
181
+ icon: "mdi-ruler",
182
+ color: showRuler.value ? "primary" : undefined,
183
+ action: () => {
184
+ showRuler.value = !showRuler.value;
185
+ },
186
+ },
176
187
  ]);
177
188
  </script>
178
189
 
@@ -247,6 +258,7 @@ const camera_options = computed(() => [
247
258
  />
248
259
  <ClippingPlanes v-model:show="showClippingPlanes" />
249
260
  <ShrinkFilter v-model:show="showShrinkFilter" />
261
+ <Ruler v-model:show="showRuler" />
250
262
  </template>
251
263
 
252
264
  <style module>
@@ -25,7 +25,9 @@ function stopHoverHighlight() {
25
25
  }
26
26
 
27
27
  onKeyStroke("Escape", () => {
28
- if (hybridViewerStore.is_hover_highlight) {
28
+ if (hybridViewerStore.is_ruler_active) {
29
+ hybridViewerStore.deactivateRuler();
30
+ } else if (hybridViewerStore.is_hover_highlight) {
29
31
  stopHoverHighlight();
30
32
  }
31
33
  });
@@ -129,6 +131,30 @@ defineExpose({ get_viewer_id });
129
131
  </v-chip>
130
132
  </div>
131
133
  </v-fade-transition>
134
+
135
+ <v-fade-transition>
136
+ <div
137
+ v-if="hybridViewerStore.is_ruler_active"
138
+ class="picking-message-container d-flex justify-center w-100 pa-4"
139
+ >
140
+ <v-chip
141
+ data-testid="rulerActiveChip"
142
+ color="secondary"
143
+ elevation="8"
144
+ size="large"
145
+ variant="flat"
146
+ class="pick-pulse"
147
+ style="pointer-events: auto"
148
+ prepend-icon="mdi-ruler"
149
+ @click="hybridViewerStore.clearRuler()"
150
+ >
151
+ Ruler &mdash; click to set point {{ hybridViewerStore.ruler_awaiting_point }}
152
+ &middot; Esc to stop
153
+ <v-divider vertical class="mx-2 my-1" opacity="0.3" />
154
+ <v-icon icon="mdi-close" size="small" />
155
+ </v-chip>
156
+ </div>
157
+ </v-fade-transition>
132
158
  </template>
133
159
 
134
160
  <style scoped>
@@ -7,6 +7,7 @@ import { BACKGROUND_COLOR } from "@ogw_internal/stores/hybrid_viewer/constants";
7
7
  import { useHybridViewerBrightness } from "@ogw_internal/stores/hybrid_viewer/brightness";
8
8
  import { useHybridViewerFilters } from "@ogw_internal/stores/hybrid_viewer/filters";
9
9
  import { useHybridViewerHighlight } from "@ogw_internal/stores/hybrid_viewer/highlight";
10
+ import { useHybridViewerRuler } from "@ogw_internal/stores/hybrid_viewer/ruler";
10
11
  import { useHybridViewerScene } from "@ogw_internal/stores/hybrid_viewer/scene";
11
12
  import { useHybridViewerViewport } from "@ogw_internal/stores/hybrid_viewer/viewport";
12
13
  import { newInstance as vtkGenericRenderWindow } from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow";
@@ -25,7 +26,17 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
25
26
  const is_picking = ref(false);
26
27
  let imageStyle = undefined;
27
28
 
28
- watch(is_picking, (value) => {
29
+ const brightnessStore = useHybridViewerBrightness();
30
+ const sceneStore = useHybridViewerScene();
31
+ const filtersStore = useHybridViewerFilters();
32
+ const highlightStore = useHybridViewerHighlight();
33
+ const rulerStore = useHybridViewerRuler();
34
+ const cameraStore = useHybridViewerCamera();
35
+ const viewportStore = useHybridViewerViewport();
36
+
37
+ const is_cursor_crosshair = computed(() => is_picking.value || rulerStore.is_ruler_active.value);
38
+
39
+ watch(is_cursor_crosshair, (value) => {
29
40
  if (!genericRenderWindow.value) {
30
41
  return;
31
42
  }
@@ -36,8 +47,6 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
36
47
  }
37
48
  });
38
49
 
39
- const brightnessStore = useHybridViewerBrightness();
40
-
41
50
  async function initHybridViewer() {
42
51
  if (status.value !== Status.NOT_CREATED) {
43
52
  return;
@@ -94,12 +103,6 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
94
103
  return renderPromise;
95
104
  }
96
105
 
97
- const sceneStore = useHybridViewerScene();
98
- const filtersStore = useHybridViewerFilters();
99
- const highlightStore = useHybridViewerHighlight();
100
- const cameraStore = useHybridViewerCamera();
101
- const viewportStore = useHybridViewerViewport();
102
-
103
106
  function exportStores() {
104
107
  const renderer = genericRenderWindow.value.getRenderer();
105
108
  const camera = renderer.getActiveCamera();
@@ -123,6 +126,7 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
123
126
  ...viewportStore,
124
127
  ...filtersStore,
125
128
  ...highlightStore,
129
+ ...rulerStore,
126
130
  ...cameraStore,
127
131
  };
128
132
  });
@@ -0,0 +1,121 @@
1
+ import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
2
+ import { useViewerStore } from "@ogw_front/stores/viewer";
3
+ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
4
+
5
+ function useHybridViewerRuler() {
6
+ const is_ruler_active = ref(false);
7
+ const ruler_snap = ref(false);
8
+ const ruler_point1 = ref(undefined);
9
+ const ruler_point2 = ref(undefined);
10
+ const ruler_distance = ref(undefined);
11
+ const ruler_awaiting_point = ref(1);
12
+ const ruler_previous_hover_state = ref({ active: false, fieldType: "CELL" });
13
+
14
+ function updateRulerSnapHighlight() {
15
+ const hybridViewerStore = useHybridViewerStore();
16
+ const { is_hover_highlight, hover_highlight_field_type } = storeToRefs(hybridViewerStore);
17
+ const { clearHoverHighlight } = hybridViewerStore;
18
+
19
+ if (is_ruler_active.value && ruler_snap.value) {
20
+ ruler_previous_hover_state.value = {
21
+ active: is_hover_highlight.value,
22
+ fieldType: hover_highlight_field_type.value,
23
+ };
24
+ is_hover_highlight.value = true;
25
+ hover_highlight_field_type.value = "POINT";
26
+ } else {
27
+ is_hover_highlight.value = ruler_previous_hover_state.value.active;
28
+ hover_highlight_field_type.value = ruler_previous_hover_state.value.fieldType;
29
+ if (!is_hover_highlight.value) {
30
+ clearHoverHighlight();
31
+ }
32
+ }
33
+ }
34
+
35
+ watch([is_ruler_active, ruler_snap], updateRulerSnapHighlight);
36
+
37
+ async function applyRuler() {
38
+ if (!ruler_point1.value) {
39
+ return;
40
+ }
41
+ const points = ruler_point2.value
42
+ ? [ruler_point1.value, ruler_point2.value]
43
+ : [ruler_point1.value];
44
+ const { remoteRender } = useHybridViewerStore();
45
+ const viewerStore = useViewerStore();
46
+ const schema = viewer_schemas.opengeodeweb_viewer.viewer.ruler;
47
+ const response = await viewerStore.request({ schema, params: { points } });
48
+ ruler_distance.value = response.distance;
49
+ await remoteRender();
50
+ }
51
+
52
+ async function handleRulerClick(x, y) {
53
+ const { hybridDb } = useHybridViewerStore();
54
+ const viewerStore = useViewerStore();
55
+ let coords = undefined;
56
+ if (ruler_snap.value) {
57
+ const schema = viewer_schemas.opengeodeweb_viewer.viewer.highlight;
58
+ const params = {
59
+ x: Math.round(x),
60
+ y: Math.round(y),
61
+ field_type: "POINT",
62
+ ids: Object.keys(hybridDb),
63
+ };
64
+ const response = await viewerStore.request({ schema, params });
65
+ coords = response.attributes?.coordinates;
66
+ } else {
67
+ const schema = viewer_schemas.opengeodeweb_viewer.viewer.get_point_position;
68
+ const params = { x: Math.round(x), y: Math.round(y) };
69
+ const response = await viewerStore.request({ schema, params });
70
+ coords = [response.x, response.y, response.z];
71
+ }
72
+ if (!coords) {
73
+ return;
74
+ }
75
+
76
+ if (ruler_awaiting_point.value === 1) {
77
+ ruler_point1.value = coords;
78
+ ruler_point2.value = undefined;
79
+ ruler_distance.value = undefined;
80
+ ruler_awaiting_point.value = 2;
81
+ } else {
82
+ ruler_point2.value = coords;
83
+ ruler_awaiting_point.value = 1;
84
+ }
85
+ await applyRuler();
86
+ }
87
+
88
+ function deactivateRuler() {
89
+ const { clearHoverHighlight } = useHybridViewerStore();
90
+ is_ruler_active.value = false;
91
+ clearHoverHighlight();
92
+ }
93
+
94
+ async function clearRuler() {
95
+ ruler_point1.value = undefined;
96
+ ruler_point2.value = undefined;
97
+ ruler_distance.value = undefined;
98
+ ruler_awaiting_point.value = 1;
99
+ deactivateRuler();
100
+ const { remoteRender } = useHybridViewerStore();
101
+ const viewerStore = useViewerStore();
102
+ const schema = viewer_schemas.opengeodeweb_viewer.viewer.reset_ruler;
103
+ await viewerStore.request({ schema });
104
+ await remoteRender();
105
+ }
106
+
107
+ return {
108
+ is_ruler_active,
109
+ ruler_snap,
110
+ ruler_point1,
111
+ ruler_point2,
112
+ ruler_distance,
113
+ ruler_awaiting_point,
114
+ handleRulerClick,
115
+ applyRuler,
116
+ clearRuler,
117
+ deactivateRuler,
118
+ };
119
+ }
120
+
121
+ export { useHybridViewerRuler };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geode/opengeodeweb-front",
3
- "version": "10.31.1-rc.1",
3
+ "version": "10.32.0",
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": "next",
43
- "@geode/opengeodeweb-viewer": "next",
42
+ "@geode/opengeodeweb-back": "latest",
43
+ "@geode/opengeodeweb-viewer": "latest",
44
44
  "@google-cloud/run": "3.2.0",
45
45
  "@kitware/vtk.js": "33.3.0",
46
46
  "@mdi/font": "7.4.47",