diff --git a/RELEASE.md b/RELEASE.md index 21716f2..c103ad5 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,4 +1,4 @@ -✅ Tested for map.apps 4.18.3 / Linie 4 +✅ Tested for map.apps 4.19.0 / Linie 4 #### Release Notes - autogenerated SNAPSHOT-Release diff --git a/package.json b/package.json index c92d05f..5576ac4 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "mapapps-what3words", + "name": "mapapps-4-developers", "description": "test build", "version": "0.0.1", "license": "CC0-1.0", @@ -9,28 +9,30 @@ "watch-types": "tsc -w --noEmit" }, "devDependencies": { - "@conterra/ct-mapapps-typings": "~4.18.3", + "@conterra/ct-mapapps-typings": "~4.19.0", "@conterra/mapapps-mocha-runner": "1.1.1", - "@conterra/reactivity-core": "^0.4.0", + "@conterra/reactivity-core": "^0.4.4", "@types/chai": "4.3.10", "@types/license-checker": "^25.0.6", - "@types/mocha": "10.0.4", - "arcgis-js-api": "4.29.10", - "chai": "4.3.10", - "ct-mapapps-browser-sync": "0.0.41", - "ct-mapapps-gulp-js": "0.10.3", + "@types/mocha": "10.0.10", + "@types/sinon": "^17.0.3", + "arcgis-js-api": "4.31.6", + "chai": "4.5.0", + "ct-mapapps-browser-sync": "0.0.42", + "ct-mapapps-gulp-js": "0.10.8", "eslint-config-ct-prodeng": "1.4.0", "license-checker": "25.0.1", - "mocha": "10.2.0", - "puppeteer": "21.5.2", + "mocha": "11.0.1", + "puppeteer": "23.11.1", + "sinon": "19.0.2", "stylelint": "15.11.0", "stylelint-config-ct-prodeng": "2.0.0", "stylelint-config-recommended": "13.0.0", "stylelint-config-recommended-less": "2.0.0", "ts-node": "^10.9.1", - "tsx": "^4.6.0", + "tsx": "^4.19.2", "typescript": "5.4.5", - "vue": "2.7.15", - "vue-template-compiler": "2.7.15" + "vue": "2.7.16", + "vue-template-compiler": "2.7.16" } } diff --git a/pom.xml b/pom.xml index cf1a00c..4ac257a 100644 --- a/pom.xml +++ b/pom.xml @@ -150,7 +150,7 @@ de.conterra.maven setproperties-maven-plugin - 1.0.2 + 1.0.5 @@ -476,8 +476,7 @@ scm:git:https://github.com/conterra/mapapps-mapapps-what3words.git - scm:git:https://github.com/conterra/mapapps-what3words.git - + scm:git:https://github.com/conterra/mapapps-what3words.git https://github.com/conterra/mapapps-what3words HEAD @@ -496,12 +495,12 @@ ${project.build.directory}/webapp ${root.build.outputPath}/js - 4.18.3 + 4.19.0 ${mapapps.version} - 2.1.3 + 2.1.4 sample diff --git a/src/main/js/apps/sample/app.json b/src/main/js/apps/sample/app.json index bb031cd..b590ff1 100644 --- a/src/main/js/apps/sample/app.json +++ b/src/main/js/apps/sample/app.json @@ -5,15 +5,15 @@ "bundles" ], "allowedBundles": [ - "system@^4.6.2", - "splashscreen@^4.6.2", - "templatelayout@^4.6.2", - "template-seasons@^4.6.2", - "theme-autumn@^4.6.2", - "toolset@^4.6.2", - "map-init@^4.6.2", - "omnisearch@^4.6.2", - "banner@^4.6.2", + "system", + "splashscreen", + "templatelayout", + "template-seasons", + "theme-autumn", + "toolset", + "map-init", + "search-ui", + "banner", "basemapswitcher", "locator-store", "dn_welcome", @@ -21,6 +21,16 @@ ] }, "bundles": { + "search-ui": { + "Config": { + "actions": [ + "what3words-open-popup", + "zoomto", + "openpopup" + ], + "zoomto-point-scale": 1000 + } + }, "banner": { "BannerWidget": { "label": "Developer Network", @@ -110,9 +120,9 @@ "ToolsetManager": { "toolsets": [ { - "id": "popup", + "id": "what3wordstoolset", "tools": [ - "popupToggleTool" + "what3wordstool" ], "container": "map", "windowType": "fixed", diff --git a/src/main/js/bundles/dn_what3words/MapClickPopupHandler.js b/src/main/js/bundles/dn_what3words/MapClickPopupHandler.js deleted file mode 100644 index c799245..0000000 --- a/src/main/js/bundles/dn_what3words/MapClickPopupHandler.js +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2023 con terra GmbH (info@conterra.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import apprt_request from "apprt-request"; -import Locale from "apprt-core/Locale"; - -const coordsUrl = "https://api.what3words.com/v3/convert-to-3wa"; - -function MapClickPopupHandler(i18n) { - - let _clickHandle; - let mapWidgetModel; - let key; - i18n = i18n._i18n.get(); - - function cleanup() { - _clear(); - _clickHandle && _clickHandle.remove(); - } - - function _clear() { - const view = mapWidgetModel && mapWidgetModel.get("view"); - if (view) { - view.popup.content = ""; - view.popup.close(); - } - } - - return { - activateTool: function () { - mapWidgetModel = this.mapWidgetModel; - key = this.what3wordsModel.get("apiKey"); - const view = mapWidgetModel.get("view"); - if (!view) { - return; - } - // Defines an action to zoom out from the selected feature - let copyAction = { - // This text is displayed as a tooltip - title: i18n.popup.button, - // The ID by which to reference the action in the event handler - id: "copy-result", - // Sets the icon font used to style the action button - image: "js/bundles/dn_what3words/images/copy.svg" - }; - // Adds the custom action to the popup. - view.popup.actions.push(copyAction); - - view.popup.on("trigger-action", function (event) { - // If the zoom-out action is clicked, fire the zoomOut() function - if (event.action.id === "copy-result") { - copyText(); - } - }); - - function copyText() { - const copyText = document.getElementsByClassName("popupTitle")[0]; - navigator.clipboard.writeText(copyText.textContent); - const tooltip = document.getElementsByClassName("tooltiptext")[0]; - tooltip.style.visibility = "visible"; - tooltip.style.opacity = "1"; - setTimeout(() => { - tooltip.style.visibility = "hidden"; - tooltip.style.opacity = "0"; - }, 5000); - - } - - view.popup.autoOpenEnabled = false; - _clickHandle = view.on("click", (event) => { - _clear(); - - if (key === "") { - console.warn("API key for what3words is empty!"); - return; - } - - const latitude = event.mapPoint.latitude; - const lat = Math.round(latitude * 1000) / 1000; - const longitude = event.mapPoint.longitude; - const lon = Math.round(longitude * 1000) / 1000; - - view.popup.open({ - // Set the location of the popup to the clicked location - location: event.mapPoint, - // Set the popup's content to the coordinates of the location - content: "what3words für: [" + lon + ", " + lat + "]" - }); - - const currentLang = Locale.getCurrent().getLocaleString(); - const queryParams = { key, coordinates: `${latitude},${longitude}`, language: currentLang }; - - - - apprt_request(coordsUrl, { query: queryParams }).then( - (response) => { - view.popup.title = "
" - + ""+ i18n.popup.tooltip +"" - + "///" + response.words + '
'; - } - ).catch((e) => { - console.warn("Geocoding failed: " + e.response.data.error.message); - }); - - }); - }, - deactivateTool: cleanup, - deactivate: cleanup - }; - -} - -export default MapClickPopupHandler; diff --git a/src/main/js/bundles/dn_what3words/MapClickPopupHandler.ts b/src/main/js/bundles/dn_what3words/MapClickPopupHandler.ts new file mode 100644 index 0000000..83f5b64 --- /dev/null +++ b/src/main/js/bundles/dn_what3words/MapClickPopupHandler.ts @@ -0,0 +1,104 @@ +/// +/// Copyright (C) 2023 con terra GmbH (info@conterra.de) +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import apprt_request from "apprt-request"; +import Locale from "apprt-core/Locale"; +import Graphic from "esri/Graphic"; + +import type { InjectedReference } from "apprt-core/InjectedReference"; +import type { Messages } from "./nls/bundle"; +import type { I18N } from "apprt/api"; + +export class MapClickPopupHandler { + private _i18n: InjectedReference>; + private _clickHandle?: IHandle; + private _mapWidgetModel: InjectedReference; + private _what3WordsModel: InjectedReference; + + public activateTool(): void { + const i18n = this._i18n!.get().ui; + this.getView().then((view) => { + this.addClickHandler(view, i18n); + }); + } + + public deactivateTool(): void { + if (this._clickHandle) { + this._clickHandle.remove(); + this._clickHandle = undefined; + } + + this.getView().then((view) => { + view.popup.close(); + }); + } + + private addClickHandler(view: __esri.View, i18n: Messages["ui"]) { + const w3wModel = this._what3WordsModel; + const key = w3wModel.apiKey; + const coordsUrl = w3wModel.what3wordsUrl; + + this._clickHandle = view.on("click", (event: { mapPoint: __esri.Point }) => { + if (key === "") { + console.warn(i18n.missingApiKeyWarning); + return; + } + + const currentLang = Locale.getCurrent().getLocaleString(); + const latitude = event.mapPoint.latitude; + const longitude = event.mapPoint.longitude; + const queryParams = { key, coordinates: `${latitude},${longitude}`, language: currentLang }; + + apprt_request(coordsUrl, { query: queryParams }).then( + (response) => { + view.popup.open({ + features: [ + new Graphic({ + geometry: event.mapPoint, + attributes: { + words: response.words, + roundedLatitude: Math.round(latitude * 1000) / 1000, + roundedLongitude: Math.round(longitude * 1000) / 1000 + }, + popupTemplate: { + title: "///{words}", + customActions: ["popup-action-copy-what3words"], + content: `${i18n.popup.coordinatePrefix}: [{roundedLatitude}, {roundedLongitude}]` + } as __esri.PopupTemplate & { customActions: string[] } + }) + ] + }); + } + ).catch((e) => { + console.warn(`${i18n.popup.geocodingErrorPrefix}: ${e?.response?.data?.error?.message}`); + }); + }); + } + + private getView(): Promise<__esri.MapView | __esri.SceneView> { + const mapWidgetModel = this._mapWidgetModel; + return new Promise((resolve) => { + if (mapWidgetModel.view) { + resolve(mapWidgetModel.view); + } else { + const watcher = mapWidgetModel.watch("view", ({ value: view }: { value: __esri.MapView | __esri.SceneView }) => { + watcher.remove(); + resolve(view); + }); + } + }); + } +} diff --git a/src/main/js/bundles/dn_what3words/PopupActionCopyWhat3WordsFactory.ts b/src/main/js/bundles/dn_what3words/PopupActionCopyWhat3WordsFactory.ts new file mode 100644 index 0000000..f854f91 --- /dev/null +++ b/src/main/js/bundles/dn_what3words/PopupActionCopyWhat3WordsFactory.ts @@ -0,0 +1,47 @@ +/// +/// Copyright (C) 2023 con terra GmbH (info@conterra.de) +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +export class PopupActionCopyWhat3WordsFactory { + + public createAction(type: string): any { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const that = this; + const i18n = that._i18n.get().ui; + + return { + id: type, + type: "button", + title: i18n.popup.button, + className: "icon-editor-copy", + + trigger(context: any): void { + that.copyText(context.features[0]); + }, + + isVisibleForFeature(feature: __esri.Feature): boolean { + return (feature?.attributes?.words) ? true : false; + } + }; + } + + public getTypes(): Array { + return ["popup-action-copy-what3words"]; + } + + public copyText(feature: __esri.Feature): void { + navigator.clipboard.writeText(`///${feature?.attributes?.words}`); + } +} diff --git a/src/main/js/bundles/dn_what3words/PopupOmnisearchHandler.js b/src/main/js/bundles/dn_what3words/PopupOmnisearchHandler.js deleted file mode 100644 index 8c5ac02..0000000 --- a/src/main/js/bundles/dn_what3words/PopupOmnisearchHandler.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2023 con terra GmbH (info@conterra.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -function PopupOmnisearchHandler(){ - - let id = "PopupHandler"; - let type = [ - "select", - "clear" - ]; - - return { - activate: function () { - const props = this._properties || {}; - this.id = props.id || this.id; - this.type = props.type || this.type; - }, - - deactivate: function () { - this._clear(); - }, - - handle: function (item, opts) { - // Check if mode is clear - if (opts.clear) { - this._clear(); - return; - } - opts = opts || {}; - const store = opts.store; - const metadata = store.getMetadata(); - if (!metadata || (metadata && metadata.supportsGeometry !== undefined && metadata.supportsGeometry === false)) { - console.warn("PopupOmnisearchHandler.handle: Unable to render popup item, store does not support geometry"); - return; - } - const geometry = item.geometry; - if (!geometry) { - console.warn("PopupOmnisearchHandler.handle: Unable to render popup item since no geometry is found."); - return; - } - const mapWidgetModel = this.model; - - this._clear(); - - const view = mapWidgetModel.get("view"); - - - const lat = Math.round(item.geometry.latitude * 1000) / 1000; - const lon = Math.round(item.geometry.longitude * 1000) / 1000; - - view.popup.open({ - // Set the popup's title to the coordinates of the location - title: "what3words", - location: item.geometry // Set the location of the popup to the clicked location - }); - view.popup.content = `///${item.title} <--> [${lon}, ${lat}]`; - - }, - - _clear: function () { - const view = this.model.get("view"); - view && view.popup.close(); - } - }; -} - -export default PopupOmnisearchHandler; - diff --git a/src/main/js/bundles/dn_what3words/What3WordsModel.ts b/src/main/js/bundles/dn_what3words/What3WordsModel.ts new file mode 100644 index 0000000..98efbc0 --- /dev/null +++ b/src/main/js/bundles/dn_what3words/What3WordsModel.ts @@ -0,0 +1,45 @@ +/// +/// Copyright (C) 2023 con terra GmbH (info@conterra.de) +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Mutable, properties, type Mutable as MutableType } from "apprt-core/Mutable"; + +function defineProperties(mutableDefinition: any, mutableProperties: { + apiKey: "", + what3wordsUrl: "", + suggestUrl: "", + coordsUrl: "" +}): Impl & MutableType

{ + properties(mutableDefinition, mutableProperties); + return mutableDefinition; +} + +class What3WordsModel extends Mutable { +} + +interface What3WordsModelProps { + apiKey: string, + what3wordsUrl: string, + suggestUrl: string, + coordsUrl: string +} + +export default defineProperties(What3WordsModel, + { + apiKey: "", + what3wordsUrl: "", + suggestUrl: "", + coordsUrl: "" + }); diff --git a/src/main/js/bundles/dn_what3words/What3WordsOpenPopupMapAction.ts b/src/main/js/bundles/dn_what3words/What3WordsOpenPopupMapAction.ts new file mode 100644 index 0000000..77d3612 --- /dev/null +++ b/src/main/js/bundles/dn_what3words/What3WordsOpenPopupMapAction.ts @@ -0,0 +1,43 @@ +/// +/// Copyright (C) 2023 con terra GmbH (info@conterra.de) +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import type { InjectedReference } from "apprt-core/InjectedReference"; +import type { ActionService } from "map-actions/api"; + +export class What3WordsOpenPopupMapAction { + private id: string; + private immediate: boolean; + private _actionService: InjectedReference; + + constructor() { + this.id = "what3words-open-popup"; + this.immediate = false; + } + + async trigger(event: any): Promise { + const actionService = this._actionService; + + if (!event.source) { return; } + if (event.source.id !== "what3wordsstore" || !event.items || event.items.length === 0) { return; } + + const item = event.items[0]; + actionService!.trigger(["zoomto", "openpopup"], { + "items": [item], + "zoomto-point-scale": 1000, + "source": event.source + }); + } +} diff --git a/src/main/js/bundles/dn_what3words/What3WordsStore.ts b/src/main/js/bundles/dn_what3words/What3WordsStore.ts new file mode 100644 index 0000000..05bbbd9 --- /dev/null +++ b/src/main/js/bundles/dn_what3words/What3WordsStore.ts @@ -0,0 +1,140 @@ +/// +/// Copyright (C) 2023 con terra GmbH (info@conterra.de) +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import apprt_request from "apprt-request"; +import when from "apprt-core/when"; +import Point from "esri/geometry/Point"; +import ComplexQuery from "store-api/ComplexQuery"; +import QueryResults from "store-api/QueryResults"; +import { QueryResult, type QueryOptions } from "store-api/api"; +import { SyncInMemoryStore, type ConstructorOptions } from "store-api/InMemoryStore"; + +import type { InjectedReference } from "apprt-core/InjectedReference"; +import type What3WordsModel from "./What3WordsModel"; +import type { Messages } from "./nls/bundle"; +import type { I18N } from "apprt/api"; +import { What3WordsQueryResult, What3WordsQueryReturnObject, What3WordsResults, What3WordsSuggestionItems } from "./api"; + + +export class What3WordsStore extends SyncInMemoryStore, string> { + private _i18n: InjectedReference>; + private _model: InjectedReference; + + public activate(): void { + this.initComponent(); + } + + public initComponent(): void { + const i18n = this._i18n!.get().ui; + + this.popupTemplate = { + title: "///{words}", + customActions: ["popup-action-copy-what3words"], + content: `${i18n.popup.coordinatePrefix}: [{roundedLatitude}, {roundedLongitude}]` + }; + } + + public get(id: string): Promise { + return when(this.query({ $eq: id }, { isGet: true }), features => features[0]); + } + + public query(query: any, queryopts: QueryOptions): QueryResult { + const model = this._model!; + const key = model.apiKey; + + if (key === "") { + console.warn("API key for what3words is empty"); + return []; + } + + const inputWords = this.handleInputs(query, queryopts); + if (inputWords?.length === 0) { + return []; + } + + let promise; + if (queryopts.isGet === true) { + promise = when( + apprt_request(model.coordsUrl, { query: { key, words: inputWords } }).then( + (response) => this.getCallback(response) + ).catch((e) => { + console.warn(`"Geocoding failed: ${e.response.data.error.message}`); + return []; + }) + ); + } else { + promise = when( + apprt_request(model.suggestUrl, { query: { key, input: inputWords } }).then( + (response) => this.suggestCallback(response) + ).catch((e) => { + console.warn(`"Geocoding failed: ${e.response.data.error.message}`); + return []; + }) + ); + } + + return QueryResults(promise); + } + + private handleInputs(query: any, queryopts: QueryOptions): string | [] { + // eslint-disable-next-line max-len + const regex = /^\/{0,}[^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/";:£§º©®\s]{1,}[・.。][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/";:£§º©®\s]{1,}[・.。][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/";:£§º©®\s]{1,}$/i; + + const ast = ComplexQuery.parse(query, queryopts).ast; + let value = ast.root().v; + + if (value.indexOf("///") === 0) { + value = value.slice(3); + } + + if (!regex.test(value)) { + return []; + } + + return value; + } + private suggestCallback(response: What3WordsSuggestionItems): { words: string }[] & { total: number } { + const results: { words: string }[] & { total: number } = []; + response.suggestions.forEach((suggest) => { + results.push({ + words: `///${suggest.words}` + }); + }); + + results.total = results.length; + return results; + } + + private getCallback(response: What3WordsQueryResult): What3WordsResults { + const results = []; + + results.push({ + words: response.words, + roundedLatitude: Math.round(response.coordinates.lng * 1000) / 1000, + roundedLongitude: Math.round(response.coordinates.lat * 1000) / 1000, + geometry: new Point({ + longitude: response.coordinates.lng, + latitude: response.coordinates.lat, + spatialReference: { + wkid: 4326 + } + }) + }); + + results.total = results.length; + return results; + } +} diff --git a/src/main/js/bundles/dn_what3words/What3wordsModel.js b/src/main/js/bundles/dn_what3words/What3wordsModel.js deleted file mode 100644 index fcc08c8..0000000 --- a/src/main/js/bundles/dn_what3words/What3wordsModel.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2023 con terra GmbH (info@conterra.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {declare} from "apprt-core/Mutable"; - -const What3wordsModel = declare({ - apiKey: "" -}); - -export default What3wordsModel; diff --git a/src/main/js/bundles/dn_what3words/What3wordsStore.js b/src/main/js/bundles/dn_what3words/What3wordsStore.js deleted file mode 100644 index b4e8f54..0000000 --- a/src/main/js/bundles/dn_what3words/What3wordsStore.js +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2023 con terra GmbH (info@conterra.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import QueryResults from "store-api/QueryResults"; -import apprt_request from "apprt-request"; -import when from "apprt-core/when"; -import ComplexQuery from "store-api/ComplexQuery"; -import Point from "esri/geometry/Point"; - -const coordsUrl = "https://api.what3words.com/v3/convert-to-coordinates"; -const suggestUrl = "https://api.what3words.com/v3/autosuggest"; -// eslint-disable-next-line max-len -const regex = /^\/{0,}[^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/";:£§º©®\s]{1,}[・.。][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/";:£§º©®\s]{1,}[・.。][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/";:£§º©®\s]{1,}$/i; - -const suggestCallback = (response) => { - const results = []; - response.suggestions.forEach( (suggest) => { - results.push({ - id: suggest.words, - title: "///" + suggest.words + " (" + suggest.nearestPlace + ")" - }); - }); - - results.total = results.length; - return results; -}; - -const getCallback = (response) => { - const results = []; - - const coordinate = new Point({ - longitude: response.coordinates.lng, - latitude: response.coordinates.lat, - wkid: 4326 - }); - - results.push({ - id: response.words, - title: response.words, - geometry: coordinate - }); - - results.total = results.length; - return results; -}; - -const emptyResult = function () { - const results = []; - results.total = results.length; - return QueryResults(results); -}; - -class What3wordsStore { - - constructor(properties) { - this.key = properties.apiKey; - } - - get(id, options) { - const query = {}; - query["id"] = {$eq: id}; - options.isGet = true; - return when(this.query(query, options), function (features) { - return features[0]; - }); - } - - getIdentity(item) { - return item["id"]; - } - - getMetadata() { - return { - supportsGeometry: true - }; - } - - query(query, queryopts) { - - const ast = ComplexQuery.parse(query, queryopts).ast; - let value = ast.root().v; - - if(value.indexOf("///") === 0){ - value = value.slice(3); - } - - //no 3 word address - if(!regex.test(value)){ - return emptyResult(); - } - - const key = this.key; - - if (key === ""){ - console.warn("API key for what3words is empty"); - return emptyResult(); - } - - const queryParams = { key }; - let targetUrl = suggestUrl; - let callback = suggestCallback; - - //if this.query() was called from this.get(), otherwise it is a suggest query - if(queryopts.isGet === true){ - queryParams.words = value; - targetUrl = coordsUrl; - callback =getCallback; - }else{ - queryParams.input=value; - } - - const promise = when( - apprt_request(targetUrl, {query: queryParams}).then(callback).catch((e) => { - console.warn("Geocoding failed: " + e.response.data.error.message); - return emptyResult(); - }) - ); - return QueryResults(promise); - } -} - -export default What3wordsStore; diff --git a/src/main/js/bundles/dn_what3words/What3wordsStoreFactory.js b/src/main/js/bundles/dn_what3words/What3wordsStoreFactory.js deleted file mode 100644 index 3a3173d..0000000 --- a/src/main/js/bundles/dn_what3words/What3wordsStoreFactory.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 con terra GmbH (info@conterra.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import What3wordsStore from "./What3wordsStore"; - -function What3wordsStoreFactory() { - let _registration; - - function _unregisterStore() { - const reg = _registration; - _registration = undefined; - if (reg) { - reg.unregister(); - } - } - - return { - activate() { - this._initStore(); - }, - _initStore() { - const props = this._properties; - props.apiKey = this.model.get("apiKey"); - const store = new What3wordsStore(props); - _registration = this._componentContext.getBundleContext().registerService(["ct.api.Store"], store, props); - }, - deactivate() { - _unregisterStore(); - } - }; -} - -export default What3wordsStoreFactory; diff --git a/src/main/js/bundles/dn_what3words/api.ts b/src/main/js/bundles/dn_what3words/api.ts new file mode 100644 index 0000000..401d6c4 --- /dev/null +++ b/src/main/js/bundles/dn_what3words/api.ts @@ -0,0 +1,67 @@ +/// +/// Copyright (C) 2023 con terra GmbH (info@conterra.de) +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +export interface What3WordsQueryReturnObject { + geometry: __esri.Geometry; + id: string; + title: string; +} + +export interface What3WordsQueryResult { + coordinates: { + lat: number; + lng: number; + } + country: string; + language: string; + map: string; + nearestPlace: string; + square: { + southwest: { + lat: number; + lng: number; + }, + northeast: { + lat: number; + lng: number; + } + } + words: string; +} + +export interface What3WordsResult { + geometry: __esri.Point; + roundedLatitude: number; + roundedLongitude: number; + words: string; +} + +export interface What3WordsResults { + results: What3WordsResult[]; + total: number; +} + +export interface What3WordsSuggestionItem { + country: string; + language: string; + nearestPlace: string; + rank: number; + words: string; +} + +export interface What3WordsSuggestionItems { + suggestions: What3WordsSuggestionItem[]; +} diff --git a/src/main/js/bundles/dn_what3words/nls/de/bundle.js b/src/main/js/bundles/dn_what3words/build.config.js similarity index 60% rename from src/main/js/bundles/dn_what3words/nls/de/bundle.js rename to src/main/js/bundles/dn_what3words/build.config.js index 9dcf204..473529f 100644 --- a/src/main/js/bundles/dn_what3words/nls/de/bundle.js +++ b/src/main/js/bundles/dn_what3words/build.config.js @@ -13,15 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* + This build file, configures information for the rollup-build task: + See: https://www.npmjs.com/package/ct-mapapps-gulp-js#user-content-rollup-build +*/ module.exports = { - root: ({ - tool: { - tooltip: "w3w Adresse in der Karte identifizieren", - title: "w3w Adresse in der Karte identifizieren" - }, - popup: { - button: "Kopiere Adresse", - tooltip: "Kopiert" - } - }) + // normally the type should be "bundle" + type: "bundle", + // list all files, which should stay after the build + // In this case only the "module.js" is the remaining artifact, all other files will be integrated into this file. + entryPoints: ["./module"] }; diff --git a/src/main/js/bundles/dn_what3words/manifest.json b/src/main/js/bundles/dn_what3words/manifest.json index d0bf7f3..405068a 100644 --- a/src/main/js/bundles/dn_what3words/manifest.json +++ b/src/main/js/bundles/dn_what3words/manifest.json @@ -12,69 +12,113 @@ "esri": "^4.10.1", "apprt-core": "^4.6.2", "apprt-binding": "^4.6.2", + "apprt-request": "^4.6.2", "ct": "^4.6.2", - "omnisearch": "^4.6.2" + "store-api": "^4.6.2" }, - "CSS-Themes-Extension": [{ - "name": "*", - "files": ["styles/styles.css"] - }], + "cssThemesExtension": [ + { + "name": "*", + "files": [ + "styles/styles.css" + ] + } + ], "components": [ { "name": "Config", - "impl": "./What3wordsModel", - "provides": "what3words.What3wordsModel", + "provides": "dn_what3words.What3WordsModel", "propertiesConstructor": true, "properties": { - "apiKey": "" + "apiKey": "", + "what3wordsUrl": "https://api.what3words.com/v3/convert-to-3wa", + "suggestUrl": "https://api.what3words.com/v3/autosuggest", + "coordsUrl": "https://api.what3words.com/v3/convert-to-coordinates" } }, { - "name": "What3wordsStoreFactory", + "name": "What3WordsStore", + "provides": [ + "ct.api.Store" + ], + "propertiesConstructor": true, "properties": { - "id": "What3wordsStore", + "id": "what3wordsstore", + "title": "${ui.store.title}", + "description": "${ui.store.description}", "useIn": [ - "omnisearch" + "search" ], - "omniSearchTypingDelay": 200, - "omniSearchPageSize": 20, - "omniSearchSearchAttr": "title", - "omniSearchDefaultLabel": "///", - "omniSearchPriority": 9999, - "omniSearchAutoActivate": true, - "-filterQuery": {}, - "-filterOptions": { - "suggestContains": true + "searchAttribute": "words", + "searchLabel": "words", + "idProperty": "words", + "metadata": { + "supportsGeometry": true, + "fields": [ + { + "title": "Words", + "name": "words", + "type": "string" + }, + { + "title": "Latitude", + "name": "roundedLatitude", + "type": "number" + }, + { + "title": "Longitude", + "name": "roundedLongitude", + "type": "number" + } + ] } }, - "references": [{ - "name": "model", - "providing": "what3words.What3wordsModel" - }] + "references": [ + { + "name": "_model", + "providing": "dn_what3words.What3WordsModel" + }, + { + "name": "_popupActionFactory", + "providing": "dn_what3words.PopupActionCopyWhat3WordsFactory" + } + ] }, { - "name": "PopupOmnisearchHandler", - "provides": ["ct.api.omnisearch.ResultHandler"], + "name": "What3WordsOpenPopupMapAction", + "provides": "map-actions.Action", + "immediate": true, "properties": { - "id": "PopupHandler", - "type": ["select", "clear"] + "popupActionTitle": "temp", + "iconClass": "icon-what3words" }, - "references": [{ - "name": "model", - "providing": "map-widget.MapWidgetModel" - }] + "propertiesConstructor": true, + "references": [ + { + "name": "_mapWidgetModel", + "providing": "map-widget.MapWidgetModel" + }, + { + "name": "_actionService", + "providing": "map-actions.ActionService" + }, + { + "name": "_what3WordsModel", + "providing": "dn_what3words.What3WordsModel" + } + ] }, { - "name": "PopupToggleTool", + "name": "WhatThreeWordsToggleTool", "impl": "ct/tools/Tool", "provides": [ "ct.tools.Tool" ], "propertiesConstructor": true, "properties": { - "id": "popupToggleTool", - "title": "${tool.title}", - "tooltip": "${tool.tooltip}", + "id": "what3wordstool", + "title": "${ui.tool.title}", + "tooltip": "${ui.tool.tooltip}", "toolRole": "toolset", "iconClass": "", "togglable": true, @@ -84,26 +128,33 @@ "references": [ { "name": "handlerScope", - "providing": "editing.MapClickPopupHandler" + "providing": "dn_what3words.MapClickPopupHandler" } ] }, { "name": "MapClickPopupHandler", "provides": [ - "editing.MapClickPopupHandler" + "dn_what3words.MapClickPopupHandler" ], "propertiesConstructor": true, "references": [ { - "name": "mapWidgetModel", + "name": "_mapWidgetModel", "providing": "map-widget.MapWidgetModel" }, { - "name": "what3wordsModel", - "providing": "what3words.What3wordsModel" + "name": "_what3WordsModel", + "providing": "dn_what3words.What3WordsModel" } ] + }, + { + "name": "PopupActionCopyWhat3WordsFactory", + "provides": [ + "popups.ActionFactory", + "dn_what3words.PopupActionCopyWhat3WordsFactory" + ] } ] } diff --git a/src/main/js/bundles/dn_what3words/module.js b/src/main/js/bundles/dn_what3words/module.js deleted file mode 100644 index 73be004..0000000 --- a/src/main/js/bundles/dn_what3words/module.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2023 con terra GmbH (info@conterra.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import "."; -import "./What3wordsStoreFactory"; -import "./PopupOmnisearchHandler"; -import "./MapClickPopupHandler"; -import "./What3wordsModel"; diff --git a/src/main/js/bundles/dn_what3words/module.ts b/src/main/js/bundles/dn_what3words/module.ts new file mode 100644 index 0000000..99336b1 --- /dev/null +++ b/src/main/js/bundles/dn_what3words/module.ts @@ -0,0 +1,21 @@ +/// +/// Copyright (C) 2023 con terra GmbH (info@conterra.de) +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +export { default as Config } from "./What3WordsModel"; +export { What3WordsStore } from "./What3WordsStore"; +export { MapClickPopupHandler } from "./MapClickPopupHandler"; +export { What3WordsOpenPopupMapAction } from "./What3WordsOpenPopupMapAction"; +export { PopupActionCopyWhat3WordsFactory } from "./PopupActionCopyWhat3WordsFactory"; diff --git a/src/main/js/bundles/dn_what3words/nls/bundle.js b/src/main/js/bundles/dn_what3words/nls/bundle.js deleted file mode 100644 index 3b8e641..0000000 --- a/src/main/js/bundles/dn_what3words/nls/bundle.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2023 con terra GmbH (info@conterra.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -module.exports = { - root: ({ - tool: { - tooltip: "Identify w3w address in the map", - title: "Identify w3w address in the map" - }, - popup: { - button: "Copy Adress", - tooltip: "Copied" - } - }), - de: true -}; diff --git a/src/main/js/bundles/dn_what3words/nls/bundle.ts b/src/main/js/bundles/dn_what3words/nls/bundle.ts new file mode 100644 index 0000000..ccf0ceb --- /dev/null +++ b/src/main/js/bundles/dn_what3words/nls/bundle.ts @@ -0,0 +1,47 @@ +/// +/// Copyright (C) 2023 con terra GmbH (info@conterra.de) +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +const i18n = { + root: { + bundleName: "What3Word", + bundleDescription: "tbd", + ui: { + missingApiKeyWarning: "API key for what3words is empty", + noPlace: "No Place", + store: { + title: "What3Words Store", + description: "Store for What3Words addresses" + }, + tool: { + tooltip: "Identify w3w address in the map", + title: "Identify w3w address in the map" + }, + popup: { + button: "Copy Adress", + tooltip: "Copied", + coordinatePrefix: "what3words for", + geocodingErrorPrefix: "Geocoding failed" + } + } + }, + de: true +}; + +export type Messages = (typeof i18n)["root"]; +export interface MessagesReference { + get: () => Messages +} +export default i18n; diff --git a/src/main/js/bundles/dn_what3words/nls/de/bundle.ts b/src/main/js/bundles/dn_what3words/nls/de/bundle.ts new file mode 100644 index 0000000..eb43c94 --- /dev/null +++ b/src/main/js/bundles/dn_what3words/nls/de/bundle.ts @@ -0,0 +1,36 @@ +/// +/// Copyright (C) 2023 con terra GmbH (info@conterra.de) +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Messages } from "../bundle"; + +export default { + bundleName: "What3Words", + bundleDescription: "tbd", + ui: { + missingApiKeyWarning: "API Schlüssel für what3words fehlt", + noPlace: "Kein Ort", + tool: { + tooltip: "w3w Adresse in der Karte identifizieren", + title: "w3w Adresse in der Karte identifizieren" + }, + popup: { + button: "Kopiere Adresse", + tooltip: "Kopiert", + coordinatePrefix: "what3words für", + geocodingErrorPrefix: "Geokodierung fehlgeschlagen" + } + } +} satisfies Messages; diff --git a/src/main/js/bundles/dn_what3words/styles/styles.css b/src/main/js/bundles/dn_what3words/styles/styles.css index 24f0223..d27fc89 100644 --- a/src/main/js/bundles/dn_what3words/styles/styles.css +++ b/src/main/js/bundles/dn_what3words/styles/styles.css @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -.ctAppRoot.ctActiveTool_popupToggleTool .esri-view-root { +.ctAppRoot.ctActiveTool_what3wordstool .esri-view-root { cursor: crosshair; } @@ -26,69 +26,33 @@ background-color: transparent !important; } -.ctAppRoot .ctTool_popupToggleTool { +.ctAppRoot .dijitHover .ctTool_what3wordstool, +.ctAppRoot .ctActiveTool .ctTool_what3wordstool { margin: 0; + border: 2px #fff solid; + border-radius: 5px; } -/*.ctAppRoot .ctTool_popupToggleTool .dijitButtonNode {*/ -/* background-color: #fff !important;*/ -/*}*/ - -.ctAppRoot .ctTool_popupToggleTool .dijitButtonNode .ctToolIcon_popupToggleTool { +.ctAppRoot .ctTool_what3wordstool .dijitButtonNode .ctToolIcon_what3wordstool { height: 47px; width: 47px; background: url(./../images/w3w_logo.svg) no-repeat center; background-size: contain; } -.ctAppRoot .ctTool_popupToggleTool .dijitButtonNode .dijitButtonText { +.ctAppRoot .ctTool_what3wordstool .dijitButtonNode .dijitButtonText { display: block; font-size: 15px; color: #45474d; } -.ctAppRoot .dijitHover .ctTool_popupToggleTool .dijitButtonNode .dijitButtonText { +.ctAppRoot .dijitHover .ctTool_what3wordstool .dijitButtonNode .dijitButtonText, +.ctAppRoot .ctActiveTool .ctTool_what3wordstool .dijitButtonNode .dijitButtonText { display: block; font-size: 15px; color: #fff; } -.tooltip { - position: relative; - display: inline-block; - } - - .tooltip .tooltiptext { - visibility: hidden; - width: 80px; - background-color: #555; - color: #fff; - text-align: center; - border-radius: 6px; - padding: 5px 0; - - /* Position the tooltip */ - position: absolute; - z-index: 1; - top: -5px; - left: 105%; - opacity: 0; - transition: opacity 0.3s; - - } - - .tooltip .tooltiptext::after { - content: " "; - position: absolute; - top: 50%; - right: 100%; /* To the left of the tooltip */ - margin-top: -5px; - border-width: 5px; - border-style: solid; - border-color: transparent #555 transparent transparent; - height: 10px; - } - .ctAppRoot .esri-popup__main-container { cursor: default; } diff --git a/src/main/js/bundles/dn_what3words/tests/all.js b/src/main/js/bundles/dn_what3words/tests/all.js deleted file mode 100644 index 7ecb546..0000000 --- a/src/main/js/bundles/dn_what3words/tests/all.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (C) 2023 con terra GmbH (info@conterra.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ diff --git a/src/main/js/bundles/dn_what3words/tests/all.ts b/src/main/js/bundles/dn_what3words/tests/all.ts new file mode 100644 index 0000000..f16542b --- /dev/null +++ b/src/main/js/bundles/dn_what3words/tests/all.ts @@ -0,0 +1,17 @@ +/// +/// Copyright (C) 2023 con terra GmbH (info@conterra.de) +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +///