From 2a627de0dd7243a3be50bc32a041d5dfed6836e8 Mon Sep 17 00:00:00 2001 From: Daniel Kastl Date: Tue, 4 Jun 2024 17:35:20 +0900 Subject: [PATCH 1/6] Adds shortcut information in notification Signed-off-by: Daniel Kastl --- config/locales/de.yml | 1 + config/locales/en.yml | 1 + config/locales/ja.yml | 1 + src/components/gtt-client/openlayers/index.ts | 1 + 4 files changed, 4 insertions(+) diff --git a/config/locales/de.yml b/config/locales/de.yml index 9795c66..770c693 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -81,6 +81,7 @@ de: messages: baselayer_missing: "Es ist kein Baselayer verfügbar!" zoom_in_more: "Zoomen Sie hinein, um die Objekte zu sehen." + modify_start: "Hold down ALT and click on a vertex to delete it." gtt_map_rotate_label: Kartenrotation gtt_map_rotate_info_html: Halten Sie Shift+Alt gedrückt und ziehen Sie die Karte, um sie zu drehen. diff --git a/config/locales/en.yml b/config/locales/en.yml index ae97bef..264d1e9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -130,3 +130,4 @@ en: messages: baselayer_missing: "There is no baselayer available!" zoom_in_more: "Zoom in to view objects." + modify_start: "Hold down ALT and click on a vertex to delete it." diff --git a/config/locales/ja.yml b/config/locales/ja.yml index c1687aa..d30135e 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -125,3 +125,4 @@ ja: messages: baselayer_missing: "背景レイヤーが存在しません!" zoom_in_more: "地物表示のためズームします。" + modify_start: "Hold down ALT and click on a vertex to delete it." diff --git a/src/components/gtt-client/openlayers/index.ts b/src/components/gtt-client/openlayers/index.ts index e57f04a..b7ee522 100644 --- a/src/components/gtt-client/openlayers/index.ts +++ b/src/components/gtt-client/openlayers/index.ts @@ -165,6 +165,7 @@ export function setControls(types: Array) { }) this.map.addInteraction(modify) + this.map.notification.show(this.i18n.messages.modify_start,7000); const mainbar = new Bar() mainbar.setPosition("top-left" as position) From 7c55b945e7c9166cebd2606ffbbccee40f1483d3 Mon Sep 17 00:00:00 2001 From: Daniel Kastl Date: Wed, 5 Jun 2024 01:36:02 +0900 Subject: [PATCH 2/6] Adds edit control and logic Signed-off-by: Daniel Kastl --- config/locales/de.yml | 1 + config/locales/en.yml | 1 + config/locales/ja.yml | 1 + src/components/gtt-client/openlayers/index.ts | 66 +++++++++++++++++-- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/config/locales/de.yml b/config/locales/de.yml index 770c693..0651952 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -72,6 +72,7 @@ de: linestring: Linieneditor polygon: Flächeneditor clear_map: "Clear map" + remove_point: "Remove point" geolocation_activated: Geolokalisierung aktiviert geolocation_deactivated: Geolokalisierung deaktiviert copied_location_to_clipboard: Standort in die Zwischenablage kopiert diff --git a/config/locales/en.yml b/config/locales/en.yml index 264d1e9..1108368 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -123,6 +123,7 @@ en: linestring: "Line editor" polygon: "Area editor" clear_map: "Clear map" + remove_point: "Remove point" copied_location_to_clipboard: "Copied location to clipboard" modal: load: "Load" diff --git a/config/locales/ja.yml b/config/locales/ja.yml index d30135e..18240c2 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -118,6 +118,7 @@ ja: linestring: ライン編集 polygon: エリア編集 clear_map: "地図をクリア" + remove_point: "Remove point" copied_location_to_clipboard: 位置情報をクリップボードにコピーしました modal: load: 読み込み diff --git a/src/components/gtt-client/openlayers/index.ts b/src/components/gtt-client/openlayers/index.ts index b7ee522..0ca2a29 100644 --- a/src/components/gtt-client/openlayers/index.ts +++ b/src/components/gtt-client/openlayers/index.ts @@ -6,7 +6,8 @@ import { Style, Fill, Stroke, Circle } from 'ol/style'; import { createEmpty, extend, containsCoordinate } from 'ol/extent'; import { transform, fromLonLat } from 'ol/proj'; -import { Modify, Draw, Select } from 'ol/interaction' +import { Draw, Select, Snap } from 'ol/interaction' +import ModifyTouch from 'ol-ext/interaction/ModifyTouch'; import Bar from 'ol-ext/control/Bar'; import Button from 'ol-ext/control/Button'; import Toggle from 'ol-ext/control/Toggle'; @@ -156,16 +157,23 @@ function createTooltip(): ExtendedTooltip { */ export function setControls(types: Array) { // Make vector features editable - const modify = new Modify({ + const modify = new ModifyTouch({ + title: this.i18n.control.remove_point, features: this.vector.getSource().getFeaturesCollection() - }) + } as any) + + modify.on('showpopup', evt => { + const geometryType = evt.feature.getGeometry().getType(); + if (geometryType === 'Point') { + modify.removePoint(); // don't show the popup + } + }); modify.on('modifyend', evt => { updateForm(this, evt.features.getArray(), true) }) this.map.addInteraction(modify) - this.map.notification.show(this.i18n.messages.modify_start,7000); const mainbar = new Bar() mainbar.setPosition("top-left" as position) @@ -198,6 +206,20 @@ export function setControls(types: Array) { }) draw.on('drawstart', evt => { + // Change the style of existing features to light gray and transparent and dashed line + this.vector.getSource().getFeatures().forEach((feature: any) => { + feature.setStyle(new Style({ + fill: new Fill({ + color: 'rgba(0, 0, 0, 0.1)' + }), + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.5)', + width: 2, + lineDash: [5, 5] + }) + })); + }); + if (this.contents.measure) { tooltip.setFeature(evt.feature) } @@ -231,11 +253,39 @@ export function setControls(types: Array) { html: ``, title: this.i18n.control[type.toLowerCase()], interaction: draw, - active: (type === geometryType) + active: (type === geometryType), + onToggle: (active: boolean) => { + modify.setActive(false); + if (active) { + draw.setActive(true); + } else { + draw.setActive(false); + } + } }) editbar.addControl(control) }) + const editModeControl = new Toggle({ + html: '', + title: this.i18n.control.edit_mode, + active: false, + onToggle: (active: boolean) => { + if (active) { + modify.setActive(true); + this.map.getInteractions().forEach((interaction: any) => { + if (interaction instanceof Draw) { + interaction.setActive(false); + } + }); + this.map.notification.show(this.i18n.messages.modify_start); + } else { + modify.setActive(false); + } + } + }); + editbar.addControl(editModeControl); + // Add the clear map control const clearMapCtrl = new Button({ html: '', @@ -247,6 +297,12 @@ export function setControls(types: Array) { }); editbar.addControl(clearMapCtrl); + // Add the snap interaction + const snap = new Snap({ + source: this.vector.getSource(), + }); + this.map.addInteraction(snap); + // Uses jQuery UI for GeoJSON Upload modal window const mapObj = this const dialog = $("#dialog-geojson-upload").dialog({ From be2a79d048d646ea19a1654154a18a115bcbce88 Mon Sep 17 00:00:00 2001 From: Daniel Kastl Date: Wed, 5 Jun 2024 01:52:16 +0900 Subject: [PATCH 3/6] Handle active tab logic Signed-off-by: Daniel Kastl --- src/components/gtt-client/openlayers/index.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/gtt-client/openlayers/index.ts b/src/components/gtt-client/openlayers/index.ts index 0ca2a29..7504295 100644 --- a/src/components/gtt-client/openlayers/index.ts +++ b/src/components/gtt-client/openlayers/index.ts @@ -253,7 +253,7 @@ export function setControls(types: Array) { html: ``, title: this.i18n.control[type.toLowerCase()], interaction: draw, - active: (type === geometryType), + active: false, onToggle: (active: boolean) => { modify.setActive(false); if (active) { @@ -266,6 +266,7 @@ export function setControls(types: Array) { editbar.addControl(control) }) + // Add the edit control const editModeControl = new Toggle({ html: '', title: this.i18n.control.edit_mode, @@ -286,6 +287,16 @@ export function setControls(types: Array) { }); editbar.addControl(editModeControl); + // if the vector layer is not empty, set the editModeControl to active + if (this.vector.getSource().getFeatures().length > 0) { + editModeControl.setActive(true); + } + // otherwise set the first draw control to active + else { + const firstControl = editbar.getControls()[0] as Toggle; + firstControl.setActive(true); + } + // Add the clear map control const clearMapCtrl = new Button({ html: '', From 0a6ae793f7f388505627a058347a7ae1faae5a39 Mon Sep 17 00:00:00 2001 From: Daniel Kastl Date: Wed, 5 Jun 2024 01:59:39 +0900 Subject: [PATCH 4/6] Handle case when a feature is not completed Signed-off-by: Daniel Kastl --- src/components/gtt-client/openlayers/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/gtt-client/openlayers/index.ts b/src/components/gtt-client/openlayers/index.ts index 7504295..51f0c58 100644 --- a/src/components/gtt-client/openlayers/index.ts +++ b/src/components/gtt-client/openlayers/index.ts @@ -226,8 +226,16 @@ export function setControls(types: Array) { }) draw.on('change:active', evt => { - tooltip.removeFeature() - }) + // If the Draw interaction is deactivated + if (!evt.target.getActive()) { + // Reset the style of existing features + this.vector.getSource().getFeatures().forEach((feature: any) => { + feature.setStyle(null); // Reset the style to the default style + }); + } + + tooltip.removeFeature(); + }); draw.on('drawend', evt => { tooltip.removeFeature() From de01a9da51b992ef2434e49bdb15aa3a1edc7db3 Mon Sep 17 00:00:00 2001 From: Daniel Kastl Date: Wed, 5 Jun 2024 13:01:05 +0900 Subject: [PATCH 5/6] Implements ControlManager Signed-off-by: Daniel Kastl --- src/components/gtt-client/openlayers/index.ts | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/components/gtt-client/openlayers/index.ts b/src/components/gtt-client/openlayers/index.ts index 51f0c58..bbb53c6 100644 --- a/src/components/gtt-client/openlayers/index.ts +++ b/src/components/gtt-client/openlayers/index.ts @@ -23,6 +23,23 @@ interface ExtendedTooltip extends Tooltip { prevHTML?: string; } +/** + * Helper class to manage the visibility of controls. + */ +class ControlManager { + static hide(controls: any[]) { + controls.forEach(control => { + control.element.style.display = 'none'; + }); + } + + static show(controls: any[]) { + controls.forEach(control => { + control.element.style.display = ''; + }); + } +} + /** * Get the z-value for a given geometry. * If the geometry is a Point, return the z-coordinate of the Point. @@ -242,6 +259,7 @@ export function setControls(types: Array) { this.vector.getSource().clear() const feature = setZValueForGeometry(evt.feature, zValue); updateForm(this, [feature], true) + ControlManager.show([editModeControl, clearMapCtrl]); }) // Material design icon @@ -295,16 +313,6 @@ export function setControls(types: Array) { }); editbar.addControl(editModeControl); - // if the vector layer is not empty, set the editModeControl to active - if (this.vector.getSource().getFeatures().length > 0) { - editModeControl.setActive(true); - } - // otherwise set the first draw control to active - else { - const firstControl = editbar.getControls()[0] as Toggle; - firstControl.setActive(true); - } - // Add the clear map control const clearMapCtrl = new Button({ html: '', @@ -312,10 +320,23 @@ export function setControls(types: Array) { handleClick: () => { this.vector.getSource().clear(); updateForm(this, null); + (editbar.getControls()[0] as Toggle).setActive(true); + ControlManager.hide([editModeControl, clearMapCtrl]); } }); editbar.addControl(clearMapCtrl); + // if the vector layer is not empty, set the editModeControl to active + if (this.vector.getSource().getFeatures().length > 0) { + editModeControl.setActive(true); + ControlManager.show([editModeControl, clearMapCtrl]); + } + // otherwise set the first draw control to active + else { + (editbar.getControls()[0] as Toggle).setActive(true); + ControlManager.hide([editModeControl, clearMapCtrl]); + } + // Add the snap interaction const snap = new Snap({ source: this.vector.getSource(), From 66b8d0934bc8a71ac42686ec2979e8fd26554151 Mon Sep 17 00:00:00 2001 From: Daniel Kastl Date: Wed, 5 Jun 2024 13:18:57 +0900 Subject: [PATCH 6/6] Handle message for platforms and geometry Signed-off-by: Daniel Kastl --- config/locales/de.yml | 2 ++ config/locales/en.yml | 2 ++ config/locales/ja.yml | 2 ++ src/components/gtt-client/helpers/platforms.ts | 17 +++++++++++++++++ src/components/gtt-client/openlayers/index.ts | 16 +++++++++++++++- 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/components/gtt-client/helpers/platforms.ts diff --git a/config/locales/de.yml b/config/locales/de.yml index 0651952..92ffb4c 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -83,6 +83,8 @@ de: baselayer_missing: "Es ist kein Baselayer verfügbar!" zoom_in_more: "Zoomen Sie hinein, um die Objekte zu sehen." modify_start: "Hold down ALT and click on a vertex to delete it." + modify_start_mac: "Hold down Option and click on a vertex to delete it." + modify_start_touch: "Tap on a vertex to delete it." gtt_map_rotate_label: Kartenrotation gtt_map_rotate_info_html: Halten Sie Shift+Alt gedrückt und ziehen Sie die Karte, um sie zu drehen. diff --git a/config/locales/en.yml b/config/locales/en.yml index 1108368..fe22c99 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -132,3 +132,5 @@ en: baselayer_missing: "There is no baselayer available!" zoom_in_more: "Zoom in to view objects." modify_start: "Hold down ALT and click on a vertex to delete it." + modify_start_mac: "Hold down Option and click on a vertex to delete it." + modify_start_touch: "Tap on a vertex to delete it." diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 18240c2..b6789da 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -127,3 +127,5 @@ ja: baselayer_missing: "背景レイヤーが存在しません!" zoom_in_more: "地物表示のためズームします。" modify_start: "Hold down ALT and click on a vertex to delete it." + modify_start_mac: "Hold down Option and click on a vertex to delete it." + modify_start_touch: "Tap on a vertex to delete it." diff --git a/src/components/gtt-client/helpers/platforms.ts b/src/components/gtt-client/helpers/platforms.ts new file mode 100644 index 0000000..f0cdd12 --- /dev/null +++ b/src/components/gtt-client/helpers/platforms.ts @@ -0,0 +1,17 @@ +/** + * Utility function to detect touch devices + * @param isTouchDevice - boolean + * @returns boolean + */ +export const isTouchDevice = (): boolean => { + return ('ontouchstart' in window) || (navigator.maxTouchPoints > 0); +} + +/** + * Utility function to detect macOS + * @param isMacOS - boolean + * @returns boolean + */ +export const isMacOS = (): boolean => { + return /Macintosh|MacIntel|MacPPC|Mac68K/.test(navigator.userAgent); +} diff --git a/src/components/gtt-client/openlayers/index.ts b/src/components/gtt-client/openlayers/index.ts index bbb53c6..c6fe69f 100644 --- a/src/components/gtt-client/openlayers/index.ts +++ b/src/components/gtt-client/openlayers/index.ts @@ -17,6 +17,7 @@ import { position } from 'ol-ext/control/control'; import { GeoJSON } from 'ol/format'; import { getCookie, getMapSize, degreesToRadians, updateForm, formatLength, formatArea } from "../helpers"; +import { isTouchDevice, isMacOS } from "../helpers/platforms"; // Define the types for the Tooltip and the custom methods you added interface ExtendedTooltip extends Tooltip { @@ -305,7 +306,20 @@ export function setControls(types: Array) { interaction.setActive(false); } }); - this.map.notification.show(this.i18n.messages.modify_start); + + if (this.vector.getSource().getFeatures().length > 0) { + const firstFeature = this.vector.getSource().getFeatures()[0]; + if (firstFeature && firstFeature.getGeometry().getType() !== 'Point') { + // Code to execute if the first feature is not a Point + let message = this.i18n.messages.modify_start; + if (isTouchDevice()) { + message = this.i18n.messages.modify_start_touch; + } else if (isMacOS()) { + message = this.i18n.messages.modify_start_mac; + } + this.map.notification.show(message); + } + } } else { modify.setActive(false); }