@vcmap/viewshed 3.0.5 → 4.1.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.
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <div>
3
+ <!-- CREATE -->
3
4
  <div v-if="viewshedMode === ViewshedPluginModes.CREATE">
4
5
  <VcsHelp v-if="viewshedType === ViewshedTypes.CONE">
5
6
  {{ $t('viewshed.createDescription') }}
@@ -7,19 +8,46 @@
7
8
  <VcsHelp v-else-if="viewshedType === ViewshedTypes.THREESIXTY">
8
9
  {{ $t('viewshed.createThreeSixtyDescription') }}
9
10
  </VcsHelp>
11
+ <v-row no-gutters class="px-1">
12
+ <v-col>
13
+ <VcsLabel html-for="heightMode">
14
+ {{ $t('viewshed.heightMode') }}
15
+ </VcsLabel>
16
+ </v-col>
17
+ <v-col>
18
+ <VcsSelect
19
+ id="heightMode"
20
+ :items="heightModes"
21
+ :model-value="heightMode"
22
+ @update:model-value="changeHeightMode"
23
+ />
24
+ </v-col>
25
+ </v-row>
26
+ <v-divider />
27
+ <v-row no-gutters class="px-2 pt-2 pb-1 d-flex justify-end">
28
+ <VcsFormButton @click="cancel()">
29
+ {{ $t('viewshed.cancel') }}
30
+ </VcsFormButton>
31
+ </v-row>
10
32
  </div>
11
- <VcsFormSection
12
- v-else-if="
33
+ <!-- EDIT AND MOVE -->
34
+ <VcsWorkspaceWrapper
35
+ v-if="
13
36
  viewshedMode === ViewshedPluginModes.EDIT ||
14
37
  viewshedMode === ViewshedPluginModes.MOVE
15
38
  "
16
- :header-actions="headerActions"
17
- heading="viewshed.viewpoint"
18
- :action-button-list-overflow-count="3"
39
+ :disable-add="currentIsPersisted"
40
+ class="wrapper"
41
+ @add-clicked="addToMyWorkspace"
42
+ @new-clicked="createNewViewshed"
19
43
  >
20
- <v-container class="px-1 py-0">
21
- <v-row no-gutters>
22
- <span class="px-1 py-0">{{ $t('viewshed.position') }}</span>
44
+ <VcsFormSection
45
+ :header-actions="headerActions"
46
+ heading="viewshed.viewpoint"
47
+ :action-button-list-overflow-count="3"
48
+ >
49
+ <v-row no-gutters class="px-1">
50
+ <span class="px-1">{{ $t('viewshed.position') }}</span>
23
51
  </v-row>
24
52
  <v-row
25
53
  v-if="
@@ -27,6 +55,7 @@
27
55
  heightMode === HeightModes.ABSOLUTE
28
56
  "
29
57
  no-gutters
58
+ class="px-1"
30
59
  >
31
60
  <VcsFeatureTransforms
32
61
  :transformation-mode="TransformationMode.TRANSLATE"
@@ -35,7 +64,7 @@
35
64
  class="w-100"
36
65
  />
37
66
  </v-row>
38
- <v-row v-else no-gutters>
67
+ <v-row v-else no-gutters class="px-1">
39
68
  <v-col
40
69
  v-for="(value, key, index) in position"
41
70
  :key="index"
@@ -43,7 +72,6 @@
43
72
  >
44
73
  <VcsTextField
45
74
  :model-value="value"
46
- @update:model-value="(v) => setPosition(v, key, index)"
47
75
  type="number"
48
76
  :prefix="key"
49
77
  :step="key === 'Z' ? 1 : 0.0001"
@@ -54,14 +82,13 @@
54
82
  !(key === 'Z' && heightMode === HeightModes.RELATIVE)
55
83
  "
56
84
  :hide-spin-buttons="true"
85
+ @update:model-value="(v: string) => setPosition(v, key, index)"
57
86
  />
58
87
  </v-col>
59
88
  </v-row>
60
- </v-container>
61
- </VcsFormSection>
62
- <v-divider />
63
- <v-container class="px-1 py-0">
64
- <v-row no-gutters>
89
+ </VcsFormSection>
90
+ <v-divider />
91
+ <v-row no-gutters class="px-1">
65
92
  <v-col>
66
93
  <VcsLabel html-for="heightMode">
67
94
  {{ $t('viewshed.heightMode') }}
@@ -70,94 +97,40 @@
70
97
  <v-col>
71
98
  <VcsSelect
72
99
  id="heightMode"
73
- :items="
74
- Object.values(HeightModes).map((item) => ({
75
- value: item,
76
- title: `viewshed.${item}`,
77
- }))
78
- "
100
+ :items="heightModes"
79
101
  :model-value="heightMode"
80
102
  @update:model-value="changeHeightMode"
81
103
  />
82
104
  </v-col>
83
105
  </v-row>
84
- </v-container>
85
- <v-container
86
- class="px-1 pt-0 pb-2"
87
- v-if="
88
- viewshedMode === ViewshedPluginModes.EDIT ||
89
- viewshedMode === ViewshedPluginModes.MOVE
90
- "
91
- >
92
- <v-row no-gutters v-for="(value, key) in parameters" :key="key">
93
- <v-col
106
+ <div v-for="(value, key) in parameters" :key="key" class="px-1">
107
+ <VcsLabeledSlider
94
108
  v-if="
95
109
  key === 'distance' ||
96
110
  (viewshedType === ViewshedTypes.CONE && key !== 'showPrimitive')
97
111
  "
98
- >
99
- <v-row no-gutters class="px-1">
100
- <v-col>
101
- <VcsLabel :html-for="key" class="py-0">
102
- {{ $t(`viewshed.${key}`) }}
103
- </VcsLabel>
104
- </v-col>
105
- <v-col class="d-flex justify-end align-center">
106
- <span v-if="key === 'distance'">{{
107
- `${Math.round(value)} m`
108
- }}</span>
109
- <span v-else>{{ `${Math.round(value)} °` }}</span>
110
- </v-col>
111
- </v-row>
112
- <v-row no-gutters>
113
- <v-col>
114
- <VcsSlider
115
- :id="key"
116
- class="pa-0"
117
- :model-value="value"
118
- @update:model-value="(v) => setParameter(key, v)"
119
- type="number"
120
- step="1"
121
- :min="parameterRanges[key][0]"
122
- :max="parameterRanges[key][1]"
123
- />
124
- </v-col>
125
- </v-row>
126
- </v-col>
127
- </v-row>
128
- </v-container>
129
- <v-divider />
130
- <div
131
- v-if="
132
- viewshedMode === ViewshedPluginModes.EDIT ||
133
- viewshedMode === ViewshedPluginModes.MOVE
134
- "
135
- class="d-flex w-full justify-space-between px-2 pt-2 pb-1"
136
- >
137
- <VcsFormButton
138
- @click="addToMyWorkspace()"
139
- tooltip="viewshed.addToMyWorkspace"
140
- icon="$vcsComponentsPlus"
141
- :disabled="currentIsPersisted || undefined"
142
- />
143
- <VcsFormButton @click="createNewViewshed()" variant="filled">
144
- {{ $t('viewshed.new') }}
145
- </VcsFormButton>
146
- </div>
147
- <div
148
- v-else-if="viewshedMode === ViewshedPluginModes.CREATE"
149
- class="d-flex w-full justify-end px-2 pt-2 pb-1"
150
- >
151
- <VcsFormButton @click="cancel()" variant="outlined">
152
- {{ $t('viewshed.cancel') }}
153
- </VcsFormButton>
154
- </div>
112
+ class="pa-0"
113
+ :label="`viewshed.${key}`"
114
+ :unit="key === 'distance' ? 'm' : '°'"
115
+ :min="parameterRanges[key][0]"
116
+ :max="parameterRanges[key][1]"
117
+ :step="1"
118
+ :text-input-cols="3"
119
+ allow-text-input
120
+ hide-spin-buttons
121
+ :model-value="Math.round(Number(value))"
122
+ @update:model-value="(v: number) => setParameter(key, v)"
123
+ />
124
+ </div>
125
+ </VcsWorkspaceWrapper>
155
126
  </div>
156
127
  </template>
157
128
 
158
- <script>
129
+ <script lang="ts">
130
+ import type { PropType, Ref } from 'vue';
159
131
  import {
160
132
  computed,
133
+ defineComponent,
161
134
  inject,
162
135
  onMounted,
163
136
  onUnmounted,
@@ -167,18 +140,26 @@
167
140
  watch,
168
141
  watchEffect,
169
142
  } from 'vue';
170
- import { VCol, VContainer, VDivider, VRow } from 'vuetify/components';
143
+ import { VCol, VDivider, VRow } from 'vuetify/components';
144
+ import type { CesiumMap } from '@vcmap/core';
171
145
  import { TransformationMode, Viewpoint } from '@vcmap/core';
146
+ import type {
147
+ VcsUiApp,
148
+ VcsAction,
149
+ CollectionComponentListItem,
150
+ } from '@vcmap/ui';
172
151
  import {
173
152
  VcsFormButton,
153
+ VcsFeatureTransforms,
174
154
  VcsFormSection,
155
+ VcsHelp,
175
156
  VcsLabel,
157
+ VcsLabeledSlider,
176
158
  VcsSelect,
177
- VcsSlider,
178
159
  VcsTextField,
179
- VcsFeatureTransforms,
180
- VcsHelp,
160
+ VcsWorkspaceWrapper,
181
161
  } from '@vcmap/ui';
162
+ import type { ViewshedManager } from './viewshedManager.js';
182
163
  import { ViewshedPluginModes, HeightModes } from './viewshedManager.js';
183
164
  import Viewshed, { ViewshedTypes } from './viewshed.js';
184
165
 
@@ -189,42 +170,42 @@
189
170
  pitch: [-90, 90],
190
171
  };
191
172
 
192
- export default {
173
+ type ViewshedParameters = Pick<
174
+ Viewshed,
175
+ 'heading' | 'showPrimitive' | 'distance' | 'fov' | 'pitch'
176
+ >;
177
+
178
+ export default defineComponent({
179
+ name: 'ViewshedWindow',
193
180
  components: {
194
- VcsTextField,
195
- VContainer,
196
- VRow,
197
181
  VCol,
198
- VcsSlider,
199
- VcsLabel,
200
- VcsFormSection,
201
182
  VDivider,
202
- VcsSelect,
183
+ VRow,
203
184
  VcsFormButton,
204
185
  VcsFeatureTransforms,
186
+ VcsFormSection,
205
187
  VcsHelp,
188
+ VcsLabel,
189
+ VcsLabeledSlider,
190
+ VcsSelect,
191
+ VcsTextField,
192
+ VcsWorkspaceWrapper,
206
193
  },
207
- name: 'ViewshedWindow',
208
194
  props: {
209
195
  getViewshed: {
210
- type: Function,
211
- required: false,
212
- default: undefined,
196
+ type: Function as PropType<() => Viewshed | undefined>,
197
+ default: () => undefined,
213
198
  },
214
199
  selection: {
215
- type: Object,
216
- required: false,
217
- default: undefined,
200
+ type: Object as PropType<
201
+ Ref<CollectionComponentListItem[]> | undefined
202
+ >,
203
+ default: () => undefined,
218
204
  },
219
205
  },
220
206
  setup(props) {
221
- const viewshedManager =
222
- /** @type {import("./viewshedManager.js").ViewshedManager} */ (
223
- inject('manager')
224
- );
225
- const app = /** @type {import("@vcmap/ui").VcsUiApp} */ (
226
- inject('vcsApp')
227
- );
207
+ const viewshedManager = inject<ViewshedManager>('manager')!;
208
+ const app = inject('vcsApp') as VcsUiApp;
228
209
  const {
229
210
  currentViewshed,
230
211
  mode: viewshedMode,
@@ -235,40 +216,34 @@
235
216
  const movementDisabled = ref(
236
217
  app.maps.activeMap?.movementApiCallsDisabled ?? false,
237
218
  );
238
- let movementDisabledListener = () => {};
219
+ let movementDisabledListener: (() => void) | undefined;
239
220
 
240
- const selection = computed(() => props.selection);
241
-
242
- let removePositionChangedListener = () => {};
221
+ let removePositionChangedListener = (): void => {};
243
222
 
244
223
  /**
245
224
  * Position of the viewshed as coordinates array. Z value is either absolute height (heightMode === HeightModes.ABSOLUTE) or relative height (heightMode === HeightModes.RELATIVE);
246
- * @type {{X: number | undefined, Y: number | undefined, Z: number | undefined}}
247
225
  */
248
- const position = reactive({
226
+ const position = reactive<{ X?: number; Y?: number; Z?: number }>({
249
227
  X: undefined,
250
228
  Y: undefined,
251
229
  Z: undefined,
252
230
  });
253
231
  /** Cached view point when jumping to view point of viewshed. This allows to jump back to prev position when clicking 'jump to viewpoint' again, without changing position. */
254
- const cachedViewpoint = shallowRef();
232
+ const cachedViewpoint = shallowRef<Viewpoint | null>();
255
233
  /** If currently in `jumpToViewpoint` mode: sets cached viewpoint on null, deletes camera changed listener and reactivates viewshed. Otherwise empty function. */
256
- let deleteCachedViewpoint = () => {};
234
+ let deleteCachedViewpoint = (): void => {};
257
235
 
258
- /**
259
- * @type {{showPrimitive: boolean, distance: number | undefined, fov: number | undefined, heading: number | undefined, pitch: number | undefined}}
260
- */
261
- const parameters = reactive({
236
+ const parameters = reactive<ViewshedParameters>({
262
237
  showPrimitive: false,
263
- distance: undefined,
264
- fov: undefined,
265
- heading: undefined,
266
- pitch: undefined,
238
+ distance: currentViewshed.value?.distance || 0,
239
+ fov: currentViewshed.value?.fov || 0,
240
+ heading: currentViewshed.value?.heading || 0,
241
+ pitch: currentViewshed.value?.pitch || 0,
267
242
  });
268
243
 
269
- const viewshedType = ref();
244
+ const viewshedType = ref<ViewshedTypes | undefined>();
270
245
 
271
- function updatePosition() {
246
+ function updatePosition(): void {
272
247
  if (currentViewshed.value) {
273
248
  position.X = currentViewshed.value.position[0];
274
249
  position.Y = currentViewshed.value.position[1];
@@ -279,7 +254,7 @@
279
254
  }
280
255
  }
281
256
 
282
- function updateWindow() {
257
+ function updateWindow(): void {
283
258
  updatePosition();
284
259
 
285
260
  if (currentViewshed.value) {
@@ -320,19 +295,17 @@
320
295
  /**
321
296
  * Deactivates viewshed and sets up a camera changes listener, which is destroyed when `deleteCachedViewpoint()` is called.
322
297
  */
323
- async function setUpCameraChangedListener() {
298
+ function setUpCameraChangedListener(): void {
324
299
  currentViewshed.value?.deactivate();
325
- const camera = /** @type {import("@vcmap/core").CesiumMap} */ (
326
- app.maps.activeMap
327
- ).getScene()?.camera;
328
- const listener = camera?.changed.addEventListener(() => {
329
- deleteCachedViewpoint();
330
- });
331
- deleteCachedViewpoint = () => {
300
+ const camera = (app.maps.activeMap as CesiumMap).getScene()?.camera;
301
+ const listener = camera?.changed.addEventListener(
302
+ deleteCachedViewpoint,
303
+ );
304
+ deleteCachedViewpoint = (): void => {
332
305
  listener?.();
333
306
  cachedViewpoint.value = null;
334
- currentViewshed.value?.activate(app.maps.activeMap);
335
- deleteCachedViewpoint = () => {};
307
+ currentViewshed.value?.activate(app.maps.activeMap as CesiumMap);
308
+ deleteCachedViewpoint = (): void => {};
336
309
  };
337
310
  }
338
311
 
@@ -353,7 +326,7 @@
353
326
  onUnmounted(() => {
354
327
  removePositionChangedListener();
355
328
  deleteCachedViewpoint();
356
- movementDisabledListener();
329
+ movementDisabledListener?.();
357
330
 
358
331
  if (!currentIsPersisted.value) {
359
332
  // in case of temp editor
@@ -367,15 +340,18 @@
367
340
  viewshedManager.stop();
368
341
  }
369
342
  }
370
- if (selection.value) {
343
+ if (props.selection?.value) {
371
344
  // in case of collection component editor
372
- if (selection.value.length > 1) {
345
+ if (props.selection.value.length > 1) {
373
346
  viewshedManager.setupMultiSelect();
374
- } else if (selection.value.length === 1 && currentViewshed.value) {
347
+ } else if (
348
+ props.selection.value.length === 1 &&
349
+ currentViewshed.value
350
+ ) {
375
351
  viewshedManager.viewViewshed(currentViewshed.value);
376
352
  } else if (
377
353
  // when a persited viewshed is deselected, stops plugin
378
- !selection.value.length &&
354
+ !props.selection.value.length &&
379
355
  viewshedMode.value === ViewshedPluginModes.EDIT
380
356
  ) {
381
357
  viewshedManager.stop(false);
@@ -385,16 +361,20 @@
385
361
 
386
362
  updateWindow();
387
363
 
388
- function setParameter(key, value) {
364
+ function setParameter<K extends keyof ViewshedParameters>(
365
+ key: K,
366
+ value: ViewshedParameters[K],
367
+ ): void {
389
368
  deleteCachedViewpoint();
390
369
  if (currentViewshed.value) {
391
370
  parameters[key] = value;
371
+ // @ts-expect-error not typed
392
372
  currentViewshed.value[key] = value;
393
373
  }
394
374
  }
395
375
 
396
376
  const headerActions = computed(() => {
397
- const actions = [];
377
+ const actions: VcsAction[] = [];
398
378
  if (viewshedType.value === ViewshedTypes.CONE) {
399
379
  actions.push({
400
380
  name: 'jumpToViewpoint',
@@ -425,7 +405,7 @@
425
405
  );
426
406
  setUpCameraChangedListener();
427
407
  } else if (cachedViewpoint.value) {
428
- app.maps.activeMap?.gotoViewpoint(cachedViewpoint.value);
408
+ await app.maps.activeMap?.gotoViewpoint(cachedViewpoint.value);
429
409
  deleteCachedViewpoint();
430
410
  }
431
411
  },
@@ -460,7 +440,7 @@
460
440
 
461
441
  return {
462
442
  position,
463
- setPosition(value, key, index) {
443
+ setPosition(value: string, key: string, index: number): void {
464
444
  deleteCachedViewpoint();
465
445
  if (!currentViewshed.value) {
466
446
  return;
@@ -476,7 +456,7 @@
476
456
  newPosition[index] = Number(value);
477
457
  currentViewshed.value.position = newPosition;
478
458
  }
479
- position[key] = Number(value);
459
+ position[key as keyof typeof position] = Number(value);
480
460
  },
481
461
  parameters,
482
462
  setParameter,
@@ -488,15 +468,27 @@
488
468
  ViewshedTypes,
489
469
  HeightModes,
490
470
  heightMode: viewshedManager.heightMode,
471
+ heightModes: Object.values(HeightModes).map((item) => ({
472
+ value: item,
473
+ title: `viewshed.${item}`,
474
+ })),
491
475
  TransformationMode,
492
- changeHeightMode: viewshedManager.changeHeightMode,
493
- async createNewViewshed() {
494
- await viewshedManager.createViewshed(viewshedType.value);
476
+ changeHeightMode: (v: HeightModes): void => {
477
+ viewshedManager.changeHeightMode(v);
478
+ },
479
+ async createNewViewshed(): Promise<void> {
480
+ await viewshedManager.createViewshed(viewshedType.value!);
481
+ },
482
+ addToMyWorkspace: (): void => {
483
+ viewshedManager.persistCurrent();
484
+ },
485
+ cancel: (): void => {
486
+ viewshedManager.stop();
495
487
  },
496
- addToMyWorkspace: () => viewshedManager.persistCurrent(),
497
- cancel: () => viewshedManager.stop(),
498
488
  headerActions,
499
489
  };
500
490
  },
501
- };
491
+ });
502
492
  </script>
493
+
494
+ <style scoped lang="scss"></style>
@@ -0,0 +1,57 @@
1
+ import { getLogger } from '@vcsuite/logger';
2
+ import { parseEnumValue } from '@vcsuite/parsers';
3
+ import { VcsCallback } from '@vcmap/ui';
4
+ import type { VcsCallbackOptions, VcsUiApp } from '@vcmap/ui';
5
+ import type { ViewshedPlugin, ViewshedPluginState } from '../index.js';
6
+ import { name as pluginName } from '../../package.json';
7
+ import { ViewshedPluginModes } from '../viewshedManager.js';
8
+ import type { ViewshedOptions } from '../viewshed.js';
9
+
10
+ export type ActivateViewshedCallbackOptions = VcsCallbackOptions &
11
+ ViewshedPluginState;
12
+
13
+ class ActivateViewshedCallback extends VcsCallback {
14
+ static get className(): string {
15
+ return 'ActivateViewshedCallback';
16
+ }
17
+
18
+ private _mode: ViewshedPluginModes | null;
19
+ private _viewshedOptions: ViewshedOptions | null;
20
+ constructor(options: ActivateViewshedCallbackOptions, app: VcsUiApp) {
21
+ super(options, app);
22
+ this._mode = parseEnumValue<ViewshedPluginModes>(
23
+ options.m,
24
+ ViewshedPluginModes,
25
+ ViewshedPluginModes.CREATE,
26
+ );
27
+ this._viewshedOptions = options.cv || null;
28
+ }
29
+
30
+ callback(): void {
31
+ const plugin = (this._app as VcsUiApp).plugins.getByKey(pluginName) as
32
+ | ViewshedPlugin
33
+ | undefined;
34
+ if (!plugin) {
35
+ getLogger('ActivateViewshedCallback').warning(
36
+ `Plugin ${pluginName} not found`,
37
+ );
38
+ return;
39
+ }
40
+ plugin.state.m = this._mode;
41
+ plugin.state.cv = this._viewshedOptions;
42
+ plugin.activate();
43
+ }
44
+
45
+ toJSON(): ActivateViewshedCallbackOptions {
46
+ const config: ActivateViewshedCallbackOptions = super.toJSON();
47
+ if (this._mode) {
48
+ config.m = this._mode;
49
+ }
50
+ if (this._viewshedOptions) {
51
+ config.cv = structuredClone(this._viewshedOptions);
52
+ }
53
+ return config;
54
+ }
55
+ }
56
+
57
+ export default ActivateViewshedCallback;
@@ -0,0 +1,26 @@
1
+ import { VcsCallback } from '@vcmap/ui';
2
+ import type { VcsUiApp } from '@vcmap/ui';
3
+ import { getLogger } from '@vcsuite/logger';
4
+ import { name as pluginName } from '../../package.json';
5
+ import type { ViewshedPlugin } from '../index.js';
6
+
7
+ class DeactivateViewshedCallback extends VcsCallback {
8
+ static get className(): string {
9
+ return 'DeactivateViewshedCallback';
10
+ }
11
+
12
+ callback(): void {
13
+ const plugin = (this._app as VcsUiApp).plugins.getByKey(pluginName) as
14
+ | ViewshedPlugin
15
+ | undefined;
16
+ if (!plugin) {
17
+ getLogger('DeactivateShadowCallback').warning(
18
+ `Plugin ${pluginName} not found`,
19
+ );
20
+ return;
21
+ }
22
+ plugin.deactivate();
23
+ }
24
+ }
25
+
26
+ export default DeactivateViewshedCallback;