@vcmap/viewshed 2.0.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/src/index.js ADDED
@@ -0,0 +1,211 @@
1
+ import { CesiumMap, moduleIdSymbol } from '@vcmap/core';
2
+ import Viewshed, { ViewshedTypes } from './viewshed.js';
3
+ import createViewshedManager, {
4
+ ViewshedPluginModes,
5
+ } from './viewshedManager.js';
6
+ import { setupViewshedWindow } from './util/windowHelper.js';
7
+ import addViewshedToolButtons from './util/toolboxHelper.js';
8
+ import { name, version, mapVersion } from '../package.json';
9
+ import ViewshedCategory, { createCategory } from './viewshedCategory.js';
10
+ import ViewshedConfigEditor, {
11
+ getDefaultOptions,
12
+ } from './ViewshedConfigEditor.vue';
13
+
14
+ /**
15
+ * @typedef {Object} ViewshedPluginState
16
+ * @property {import("./viewshedManager.js").ViewshedPluginModes | null} mode Which mode the plugin is currently in.
17
+ * @property {import("./viewshed.js").ViewshedOptions | null} currentViewshed The currents viewshed options.
18
+ */
19
+
20
+ /**
21
+ * @typedef {import("./viewshed.js").ColorOptions & {tools?: Array<ViewshedTypes>}} ViewshedPluginOptions
22
+ */
23
+
24
+ /**
25
+ * Implementation of VcsPlugin interface. This function should not throw! Put exceptions in initialize instead.
26
+ * @param {ViewshedPluginOptions} config - the configuration of this plugin instance, passed in from the app.
27
+ * @returns {import("@vcmap/ui/src/vcsUiApp").VcsPlugin<T, ViewshedPluginState>}
28
+ */
29
+ export default function plugin(config) {
30
+ let viewshedManager;
31
+ let destroy = () => {};
32
+
33
+ return {
34
+ get name() {
35
+ return name;
36
+ },
37
+ get version() {
38
+ return version;
39
+ },
40
+ get mapVersion() {
41
+ return mapVersion;
42
+ },
43
+ /**
44
+ * @param {import("@vcmap/ui").VcsUiApp} vcsUiApp
45
+ * @param {ViewshedPluginState=} state
46
+ * @returns {Promise<void>}
47
+ */
48
+ async initialize(vcsUiApp, state) {
49
+ vcsUiApp.categoryClassRegistry.registerClass(
50
+ this[moduleIdSymbol],
51
+ ViewshedCategory.className,
52
+ ViewshedCategory,
53
+ );
54
+ const viewshedCategoryHelper = await createCategory(vcsUiApp);
55
+ viewshedManager = createViewshedManager(
56
+ vcsUiApp,
57
+ config,
58
+ viewshedCategoryHelper,
59
+ );
60
+ const destroyButtons = addViewshedToolButtons(
61
+ vcsUiApp,
62
+ viewshedManager,
63
+ name,
64
+ config.tools || getDefaultOptions().tools,
65
+ );
66
+ const { destroy: destroyViewshedWindow } = setupViewshedWindow(
67
+ viewshedManager,
68
+ vcsUiApp,
69
+ viewshedCategoryHelper.collectionComponent,
70
+ );
71
+ destroy = () => {
72
+ destroyButtons();
73
+ destroyViewshedWindow();
74
+ viewshedCategoryHelper.destroy();
75
+ };
76
+
77
+ const { activeMap } = vcsUiApp.maps;
78
+ if (
79
+ state?.mode &&
80
+ state.currentViewshed &&
81
+ activeMap instanceof CesiumMap
82
+ ) {
83
+ const activeViewshed = new Viewshed(state.currentViewshed, activeMap);
84
+ if (state.mode === ViewshedPluginModes.VIEW) {
85
+ viewshedManager.viewViewshed(activeViewshed);
86
+ } else {
87
+ viewshedManager.editViewshed(activeViewshed);
88
+ }
89
+ }
90
+ },
91
+ /**
92
+ * should return all default values of the configuration
93
+ * @returns {ViewshedPluginOptions}
94
+ */
95
+ getDefaultOptions,
96
+ /**
97
+ * should return the plugin's serialization excluding all default values
98
+ * @returns {ViewshedPluginOptions}
99
+ */
100
+ toJSON() {
101
+ const serial = {};
102
+ if (
103
+ config.tools &&
104
+ getDefaultOptions().tools.length !== config.tools.length
105
+ ) {
106
+ serial.tools = [...config.tools];
107
+ }
108
+ if (
109
+ config.shadowColor &&
110
+ config.shadowColor !== Viewshed.getDefaultOptions().shadowColor
111
+ ) {
112
+ serial.shadowColor = config.shadowColor;
113
+ }
114
+ if (
115
+ config.visibleColor &&
116
+ config.visibleColor !== Viewshed.getDefaultOptions().visibleColor
117
+ ) {
118
+ serial.visibleColor = config.visibleColor;
119
+ }
120
+ return serial;
121
+ },
122
+ /**
123
+ * should return the plugins state
124
+ * @returns {ViewshedPluginState}
125
+ */
126
+ getState() {
127
+ return {
128
+ mode: viewshedManager.mode.value,
129
+ currentViewshed: viewshedManager.currentViewshed.value?.toJSON(),
130
+ };
131
+ },
132
+ /**
133
+ * components for configuring the plugin and/ or custom items defined by the plugin
134
+ * @returns {Array<import("@vcmap/ui").PluginConfigEditor>}
135
+ */
136
+ getConfigEditors() {
137
+ return [{ component: ViewshedConfigEditor }];
138
+ },
139
+ destroy,
140
+ i18n: {
141
+ en: {
142
+ viewshed: {
143
+ distance: 'Distance',
144
+ fov: 'Field of view',
145
+ heading: 'Heading',
146
+ pitch: 'Pitch',
147
+ position: 'Position',
148
+ showPrimitive: 'Show viewpoint',
149
+ viewpoint: 'Viewpoint',
150
+ viewshed: 'Viewshed',
151
+ heightMode: 'Height mode',
152
+ relative: 'Relativ to ground',
153
+ absolute: 'Absolute',
154
+ new: 'New',
155
+ cancel: 'Cancel',
156
+ [ViewshedTypes.CONE]: 'Cone Viewshed',
157
+ [ViewshedTypes.THREESIXTY]: '360° Viewshed',
158
+ create: 'Create',
159
+ addToMyWorkspace: 'Add to My Workspace',
160
+ temporary: 'Temporary',
161
+ jumpToViewpoint: 'Jump to viewpoint',
162
+ returnToViewpoint: 'Return to previous viewpoint',
163
+ move: 'Move viewpoint',
164
+ createDescription:
165
+ 'Click the map twice. First click places the origin, second defines the distance and direction of the viewshed.',
166
+ remove: 'Remove',
167
+ editor: {
168
+ general: 'General',
169
+ visibleColor: 'Visible color',
170
+ shadowColor: 'Shadow color',
171
+ tools: 'Tools',
172
+ },
173
+ },
174
+ },
175
+ de: {
176
+ viewshed: {
177
+ distance: 'Distanz',
178
+ fov: 'Sichtfeld',
179
+ heading: 'Ausrichtung',
180
+ pitch: 'Neigung',
181
+ position: 'Position',
182
+ showPrimitive: 'Viewpoint zeigen',
183
+ viewpoint: 'Viewpoint',
184
+ viewshed: 'Viewshed',
185
+ heightMode: 'Höhenmodus',
186
+ relative: 'Relativ zum Gelände',
187
+ absolute: 'Absolut',
188
+ new: 'Neu',
189
+ cancel: 'Abbrechen',
190
+ [ViewshedTypes.CONE]: 'Kegel Viewshed',
191
+ [ViewshedTypes.THREESIXTY]: '360° Viewshed',
192
+ create: 'Erzeuge',
193
+ addToMyWorkspace: 'Zu Mein Arbeitsbereich hinzufügen',
194
+ temporary: 'Temporärer',
195
+ jumpToViewpoint: 'Zu Viewpoint springen',
196
+ returnToViewpoint: 'Zu vorherigem Viewpoint zurück springen',
197
+ move: 'Viewpoint verschieben',
198
+ createDescription:
199
+ 'Klicke zweimal in die Karte. Der erste Klick platziert den Ursprung, der zweite die Distanz und die Richtung des Viewsheds.',
200
+ remove: 'Entfernen',
201
+ editor: {
202
+ general: 'Generelles',
203
+ visibleColor: 'Sichtbarer Bereich',
204
+ shadowColor: 'Schatten Bereich',
205
+ tools: 'Werkzeuge',
206
+ },
207
+ },
208
+ },
209
+ },
210
+ };
211
+ }
@@ -0,0 +1,133 @@
1
+ import { CesiumMap } from '@vcmap/core';
2
+ import { ToolboxType } from '@vcmap/ui';
3
+ import { check } from '@vcsuite/check';
4
+ import { reactive, watch } from 'vue';
5
+ import { ViewshedTypes } from '../viewshed.js';
6
+ import { ViewshedIcons } from './windowHelper.js';
7
+ import { ViewshedPluginModes } from '../viewshedManager.js';
8
+
9
+ /**
10
+ * @typedef {Object} ViewshedToolbox
11
+ * @property {import("@vcmap/ui/src/manager/toolbox/toolboxManager").SelectToolboxComponentOptions} toolbox
12
+ * @property {function():void} destroy
13
+ */
14
+
15
+ /**
16
+ *
17
+ * @param {import("@vcmap/ui").VcsUiApp} app The VcsUiApp instance
18
+ * @param {import("../viewshedManager.js").ViewshedManager} manager The viewshed manager.
19
+ * @param {string} name Name of toolbox.
20
+ * @param {Array<string>} tools The tools to be available in the toolbox
21
+ * @returns {ViewshedToolbox} A select toolbox with buttons to create 360 and cone viewshed.
22
+ */
23
+ function createViewshedToolbox(app, manager, name, tools) {
24
+ const toolbox = {
25
+ type: ToolboxType.SELECT,
26
+ action: reactive({
27
+ name,
28
+ currentIndex: 0,
29
+ active: false,
30
+ background: false,
31
+ disabled: false,
32
+ callback() {
33
+ if (this.active) {
34
+ if (this.background && manager.currentViewshed.value) {
35
+ manager.editViewshed(manager.currentViewshed.value);
36
+ } else {
37
+ manager.stop();
38
+ }
39
+ } else {
40
+ const toolName = this.tools[this.currentIndex].name;
41
+ manager.createViewshed(toolName);
42
+ }
43
+ },
44
+ selected(newIndex) {
45
+ if (newIndex !== this.currentIndex) {
46
+ this.currentIndex = newIndex;
47
+ const toolName = this.tools[this.currentIndex].name;
48
+ manager.createViewshed(toolName);
49
+ }
50
+ },
51
+ tools: tools.flatMap((tool) => {
52
+ if (tool === ViewshedTypes.CONE) {
53
+ return {
54
+ name: ViewshedTypes.CONE,
55
+ icon: ViewshedIcons[ViewshedTypes.CONE],
56
+ title: `viewshed.${ViewshedTypes.CONE}`,
57
+ };
58
+ } else if (tool === ViewshedTypes.THREESIXTY) {
59
+ return {
60
+ name: ViewshedTypes.THREESIXTY,
61
+ icon: ViewshedIcons[ViewshedTypes.THREESIXTY],
62
+ title: `viewshed.${ViewshedTypes.THREESIXTY}`,
63
+ };
64
+ } else {
65
+ return [];
66
+ }
67
+ }),
68
+ }),
69
+ };
70
+
71
+ const viewshedModeWatcher = watch(manager.mode, (mode) => {
72
+ if (mode === ViewshedPluginModes.VIEW) {
73
+ toolbox.action.background = true;
74
+ toolbox.action.active = true;
75
+ } else if (mode) {
76
+ toolbox.action.background = false;
77
+ toolbox.action.active = true;
78
+ } else {
79
+ toolbox.action.background = false;
80
+ toolbox.action.active = false;
81
+ }
82
+ });
83
+
84
+ const currentViewshedWatcher = watch(
85
+ manager.currentViewshed,
86
+ (currentViewshed) => {
87
+ if (currentViewshed) {
88
+ toolbox.action.currentIndex = toolbox.action.tools.findIndex(
89
+ (tool) => tool.name === currentViewshed.type,
90
+ );
91
+ }
92
+ },
93
+ );
94
+
95
+ const mapChangedListener = app.maps.mapActivated.addEventListener((map) => {
96
+ if (map instanceof CesiumMap) {
97
+ toolbox.action.disabled = false;
98
+ } else {
99
+ toolbox.action.disabled = true;
100
+ manager.stop();
101
+ }
102
+ });
103
+
104
+ return {
105
+ destroy() {
106
+ viewshedModeWatcher();
107
+ currentViewshedWatcher();
108
+ mapChangedListener();
109
+ },
110
+ toolbox,
111
+ };
112
+ }
113
+
114
+ /**
115
+ *
116
+ * @param {import("@vcmap/ui").VcsUiApp} app The VcsUiApp instance
117
+ * @param {import("../viewshedManager.js").ViewshedManager} manager The viewshed manager.
118
+ * @param {string} name Name of toolbox action and owner of toolbox component
119
+ * @param {Array<string>} tools The tools to be available in the toolbox
120
+ * @returns {function():void} Function to remove toolbox component.
121
+ */
122
+ export default function addToolButtons(app, manager, name, tools) {
123
+ check(tools, [Object.values(ViewshedTypes)]);
124
+
125
+ const { toolbox: createToolbox, destroy: destroyCreateToolbox } =
126
+ createViewshedToolbox(app, manager, name, tools);
127
+ const createId = app.toolboxManager.add(createToolbox, name).id;
128
+
129
+ return () => {
130
+ app.toolboxManager.remove(createId);
131
+ destroyCreateToolbox();
132
+ };
133
+ }
@@ -0,0 +1,128 @@
1
+ import { ref, watch } from 'vue';
2
+ import { WindowSlot, makeEditorCollectionComponentClass } from '@vcmap/ui';
3
+ import ViewshedWindow from '../ViewshedWindow.vue';
4
+ import { name } from '../../package.json';
5
+ import { ViewshedTypes } from '../viewshed.js';
6
+ import { ViewshedPluginModes } from '../viewshedManager.js';
7
+
8
+ export const ViewshedIcons = {
9
+ [ViewshedTypes.CONE]: '$vcsViewshed',
10
+ [ViewshedTypes.THREESIXTY]: '$vcsViewshedCone',
11
+ };
12
+
13
+ /**
14
+ * @typedef {Object} ViewshedWindow
15
+ * @property {function():void} destroy Destroys watchers and removes window.
16
+ * @property {function():void} toggleWindow Toggles window. Only opens window when manager has current viewshed.
17
+ */
18
+
19
+ /**
20
+ *
21
+ * @param {import("../viewshedManager.js").ViewshedManager} manager The viewshed manager.
22
+ * @param {import("@vcmap/ui").VcsUiApp} app The VcsUiApp instance
23
+ * @param {import("@vcmap/ui").CollectionComponent} collectionComponent The collection component of the category.
24
+ * @returns {ViewshedWindow} Viewshed window api.
25
+ */
26
+ export function setupViewshedWindow(manager, app, collectionComponent) {
27
+ /**
28
+ * @type {import("vue").Ref<undefined | string | string[]>}
29
+ */
30
+ const headerTitle = ref();
31
+ const headerIcon = ref();
32
+ const windowId = `${collectionComponent.id}-editor`;
33
+
34
+ const editor = {
35
+ component: ViewshedWindow,
36
+ provides: {
37
+ manager,
38
+ },
39
+ state: {
40
+ headerTitle,
41
+ headerIcon,
42
+ styles: { width: '280px', height: 'auto' },
43
+ infoUrlCallback: app.getHelpUrlCallback('tools/viewshedTool.html'),
44
+ },
45
+ };
46
+
47
+ makeEditorCollectionComponentClass(app, collectionComponent, {
48
+ editor: (item) => ({
49
+ ...editor,
50
+ props: {
51
+ selection: collectionComponent.selection,
52
+ getViewshed: () => collectionComponent.collection.getByKey(item.name),
53
+ },
54
+ }),
55
+ });
56
+
57
+ function updateHeader() {
58
+ if (manager.currentViewshed.value) {
59
+ headerIcon.value = ViewshedIcons[manager.currentViewshed.value.type];
60
+
61
+ if (manager.currentIsPersisted.value) {
62
+ headerTitle.value = manager.currentViewshed.value.properties.title;
63
+ } else if (manager.mode.value === ViewshedPluginModes.CREATE) {
64
+ headerTitle.value = [
65
+ 'viewshed.create',
66
+ `viewshed.${manager.currentViewshed.value.type}`,
67
+ ];
68
+ } else {
69
+ headerTitle.value = [
70
+ 'viewshed.temporary',
71
+ `viewshed.${manager.currentViewshed.value.type}`,
72
+ ];
73
+ }
74
+ }
75
+ }
76
+
77
+ function updateWindow() {
78
+ updateHeader();
79
+ if (
80
+ manager.currentViewshed.value &&
81
+ !manager.currentIsPersisted.value &&
82
+ (manager.mode.value === ViewshedPluginModes.EDIT ||
83
+ manager.mode.value === ViewshedPluginModes.MOVE ||
84
+ manager.mode.value === ViewshedPluginModes.CREATE)
85
+ ) {
86
+ if (!app.windowManager.has(windowId)) {
87
+ app.windowManager.add(
88
+ {
89
+ ...editor,
90
+ id: windowId,
91
+ parentId: 'category-manager',
92
+ slot: WindowSlot.DYNAMIC_LEFT,
93
+ },
94
+ name,
95
+ );
96
+ }
97
+ }
98
+ }
99
+
100
+ const viewshedListener = watch(manager.currentViewshed, (curr) => {
101
+ if (curr) {
102
+ updateWindow();
103
+ }
104
+ });
105
+
106
+ const persistedListener = watch(manager.currentIsPersisted, (curr) => {
107
+ if (curr) {
108
+ updateHeader();
109
+ }
110
+ });
111
+
112
+ const viewshedModeListener = watch(manager.mode, () => {
113
+ if (!manager.mode.value) {
114
+ app.windowManager.remove(windowId);
115
+ } else {
116
+ updateWindow();
117
+ }
118
+ });
119
+
120
+ return {
121
+ destroy() {
122
+ app.windowManager.remove(windowId);
123
+ viewshedListener();
124
+ viewshedModeListener();
125
+ persistedListener();
126
+ },
127
+ };
128
+ }