diff --git a/build/docma-config.json b/build/docma-config.json
index 5f6464212b..8f9bbdc103 100644
--- a/build/docma-config.json
+++ b/build/docma-config.json
@@ -261,6 +261,7 @@
"web/client/plugins/LayerInfo.jsx",
"web/client/plugins/Locate.jsx",
"web/client/plugins/Login.jsx",
+ "web/client/plugins/LongitudinalProfileTool.jsx",
"web/client/plugins/Map.jsx",
"web/client/plugins/MapCatalog.jsx",
"web/client/plugins/MapEditor.jsx",
diff --git a/docs/developer-guide/maps-configuration.md b/docs/developer-guide/maps-configuration.md
index 4cf92dfc45..b0764cb5e2 100644
--- a/docs/developer-guide/maps-configuration.md
+++ b/docs/developer-guide/maps-configuration.md
@@ -1046,6 +1046,7 @@ The `symbolizer` could be of following `kinds`:
| `size` | size of the icon | x | x |
| `opacity` | opacity of the icon | x | x |
| `rotate` | rotation of the icon | x | x |
+ | `anchor` | array of values defined in fractions [0 to 1] | x | x |
| `msBringToFront` | this boolean will allow setting the **disableDepthTestDistance** value for the feature. This would | | x |
| `msHeightReference` | reference to compute the distance of the point geometry, one of **none**, **ground** or **clamp** | | x |
| `msHeight` | height of the point, the original geometry is applied if undefined | | x |
diff --git a/package.json b/package.json
index 3ef95b4243..5963a32288 100644
--- a/package.json
+++ b/package.json
@@ -144,6 +144,7 @@
"@turf/convex": "6.5.0",
"@turf/difference": "6.5.0",
"@turf/great-circle": "5.1.5",
+ "@turf/helpers": "6.5.0",
"@turf/inside": "4.1.0",
"@turf/line-intersect": "4.1.0",
"@turf/point-on-surface": "4.1.0",
@@ -173,6 +174,7 @@
"draft-js-plugins-editor": "2.1.1",
"draft-js-side-toolbar-plugin": "3.0.1",
"draftjs-to-html": "0.8.4",
+ "dxf-parser": "1.1.2",
"element-closest": "2.0.2",
"embed-video": "2.0.4",
"es6-object-assign": "1.1.0",
@@ -184,6 +186,7 @@
"git-revision-webpack-plugin": "5.0.0",
"history": "4.6.1",
"html-to-draftjs": "npm:@geosolutions/html-to-draftjs@1.5.1",
+ "html-to-image": "1.11.11",
"html2canvas": "0.5.0-beta4",
"immutable": "4.0.0-rc.12",
"intersection-observer": "0.7.0",
@@ -217,6 +220,7 @@
"object-fit-images": "3.2.4",
"ogc-schemas": "2.6.1",
"ol": "5.3.0",
+ "pdfmake": "0.2.7",
"pdfviewer": "0.3.2",
"plotly.js-cartesian-dist": "2.5.1",
"prop-types": "15.7.2",
diff --git a/web/client/actions/__tests__/longitudinalPfofile-test.js b/web/client/actions/__tests__/longitudinalPfofile-test.js
new file mode 100644
index 0000000000..86522b2f0b
--- /dev/null
+++ b/web/client/actions/__tests__/longitudinalPfofile-test.js
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2022, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+*/
+import expect from "expect";
+
+import {CONTROL_DOCK_NAME} from '../../plugins/longitudinalProfile/constants';
+import {SET_CONTROL_PROPERTY} from "../controls";
+import {
+ ADD_PROFILE_DATA,
+ addProfileData,
+ CHANGE_DISTANCE,
+ CHANGE_GEOMETRY,
+ CHANGE_REFERENTIAL,
+ changeDistance,
+ changeGeometry,
+ changeReferential,
+ closeDock,
+ initialized,
+ INITIALIZED,
+ loading,
+ LOADING,
+ openDock,
+ setupPlugin,
+ SETUP,
+ TEAR_DOWN,
+ tearDown,
+ TOGGLE_MAXIMIZE,
+ TOGGLE_MODE,
+ toggleMaximize,
+ toggleMode
+} from "../longitudinalProfile";
+
+describe('Test correctness of the actions', () => {
+ it('setup', () => {
+ const config = {configProp1: 'example', configProp2: 'test'};
+ const action = setupPlugin(config);
+ expect(action).toExist();
+ expect(action.type).toBe(SETUP);
+ expect(action.config).toEqual(config);
+ });
+
+ it('initialized', () => {
+ const action = initialized();
+ expect(action).toExist();
+ expect(action.type).toBe(INITIALIZED);
+ });
+
+ it('tearDown', () => {
+ const action = tearDown();
+ expect(action).toExist();
+ expect(action.type).toBe(TEAR_DOWN);
+ });
+
+ it('openDock', () => {
+ const action = openDock();
+ expect(action).toExist();
+ expect(action.type).toBe(SET_CONTROL_PROPERTY);
+ expect(action.control).toBe(CONTROL_DOCK_NAME);
+ expect(action.property).toBe('enabled');
+ expect(action.value).toBe(true);
+ });
+
+ it('closeDock', () => {
+ const action = closeDock();
+ expect(action).toExist();
+ expect(action.type).toBe(SET_CONTROL_PROPERTY);
+ expect(action.control).toBe(CONTROL_DOCK_NAME);
+ expect(action.property).toBe('enabled');
+ expect(action.value).toBe(false);
+ });
+
+ it('toggleMode - no mode passed', () => {
+ const action = toggleMode();
+ expect(action).toExist();
+ expect(action.type).toBe(TOGGLE_MODE);
+ expect(action.mode).toBe(undefined);
+ });
+
+ it('toggleMode - mode passed', () => {
+ const action = toggleMode('draw');
+ expect(action).toExist();
+ expect(action.type).toBe(TOGGLE_MODE);
+ expect(action.mode).toBe('draw');
+ });
+
+ it('addProfileData', () => {
+ const infos = { prop1: true, prop2: 10, prop3: 'test'};
+ const points = [[[1, 2, 5], [2, 3, 5]]];
+ const projection = 'EPSG:3857';
+ const action = addProfileData(infos, points, projection);
+ expect(action).toExist();
+ expect(action.type).toBe(ADD_PROFILE_DATA);
+ expect(action.infos).toEqual(infos);
+ expect(action.points[0]).toEqual(points[0]);
+ expect(action.points[1]).toEqual(points[1]);
+ expect(action.projection).toEqual(projection);
+ });
+
+ it('loading', () => {
+ const action = loading(true);
+ expect(action).toExist();
+ expect(action.type).toBe(LOADING);
+ expect(action.state).toBe(true);
+ });
+
+ it('changeReferential', () => {
+ const action = changeReferential('ref2');
+ expect(action).toExist();
+ expect(action.type).toBe(CHANGE_REFERENTIAL);
+ expect(action.referential).toBe('ref2');
+ });
+
+ it('changeDistance', () => {
+ const action = changeDistance(200);
+ expect(action).toExist();
+ expect(action.type).toBe(CHANGE_DISTANCE);
+ expect(action.distance).toBe(200);
+ });
+
+ it('changeGeometry', () => {
+ const geometry = { point: [2, 5], center: [1, 1]};
+ const action = changeGeometry(geometry);
+ expect(action).toExist();
+ expect(action.type).toBe(CHANGE_GEOMETRY);
+ expect(action.geometry).toExist();
+ expect(action.geometry.point).toEqual([2, 5]);
+ expect(action.geometry.center).toEqual([1, 1]);
+ });
+
+ it('toggleMaximize', () => {
+ const action = toggleMaximize();
+ expect(action).toExist();
+ expect(action.type).toBe(TOGGLE_MAXIMIZE);
+ });
+});
diff --git a/web/client/actions/longitudinalProfile.js b/web/client/actions/longitudinalProfile.js
new file mode 100644
index 0000000000..b275b2f293
--- /dev/null
+++ b/web/client/actions/longitudinalProfile.js
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2023, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+*/
+import {updateAdditionalLayer} from "../actions/additionallayers";
+import {SET_CONTROL_PROPERTY} from "../actions/controls";
+import {
+ updateDockPanelsList
+} from "../actions/maplayout";
+import {error} from "../actions/notifications";
+import {
+ CONTROL_DOCK_NAME,
+ LONGITUDINAL_OWNER,
+ LONGITUDINAL_VECTOR_LAYER_ID,
+ LONGITUDINAL_VECTOR_LAYER_ID_POINT
+} from '../plugins/longitudinalProfile/constants';
+import {configSelector} from "../selectors/longitudinalProfile";
+
+export const ADD_MARKER = "LONGITUDINAL_PROFILE:ADD_MARKER";
+export const ADD_PROFILE_DATA = "LONGITUDINAL_PROFILE:ADD_PROFILE_DATA";
+export const CHANGE_CHART_TITLE = "LONGITUDINAL_PROFILE:CHANGE_CHART_TITLE";
+export const CHANGE_CRS = "LONGITUDINAL_PROFILE:CHANGE_CRS";
+export const CHANGE_DISTANCE = "LONGITUDINAL_PROFILE:CHANGE_DISTANCE";
+export const CHANGE_GEOMETRY = "LONGITUDINAL_PROFILE:CHANGE_GEOMETRY";
+export const CHANGE_REFERENTIAL = "LONGITUDINAL_PROFILE:CHANGE_REFERENTIAL";
+export const HIDE_MARKER = "LONGITUDINAL_PROFILE:HIDE_MARKER";
+export const INITIALIZED = "LONGITUDINAL_PROFILE:INITIALIZED";
+export const LOADING = "LONGITUDINAL_PROFILE:LOADING";
+export const SETUP = "LONGITUDINAL_PROFILE:SETUP";
+export const TEAR_DOWN = "LONGITUDINAL_PROFILE:TEAR_DOWN";
+export const TOGGLE_MAXIMIZE = "LONGITUDINAL_PROFILE:TOGGLE_MAXIMIZE";
+export const TOGGLE_MODE = "LONGITUDINAL_PROFILE:TOGGLE_MODE";
+
+/**
+ * add a vector point layer with a marker style on the map (used to trigger an epic stream)
+ * @prop {Object} point
+ * @prop {Object} point.lat latitude
+ * @prop {Object} point.lng longitude
+ * @prop {Object} point.projection the crs of the point // [ ] not sure this is needed
+ */
+export const addMarker = (point) => ({
+ type: ADD_MARKER,
+ point
+});
+
+/**
+ * add profile data results in the state
+ * @prop {Object} infos
+ * @prop {Object[]} points the list of points used to draw the line chart
+ * @prop {string} projection output projection
+ */
+export const addProfileData = (infos, points, projection) => ({
+ type: ADD_PROFILE_DATA,
+ infos,
+ points,
+ projection
+});
+
+/**
+ * action used to change the chart title
+ * @prop {string} chartTitle the chart title
+ */
+export const changeChartTitle = (chartTitle) => ({
+ type: CHANGE_CHART_TITLE,
+ chartTitle
+});
+
+/**
+ * action used to change the crs to be associated to the DXF
+ * @prop {string} crs the crs to be associated to the DXF
+ */
+export const changeCRS = (crs) => ({
+ type: CHANGE_CRS,
+ crs
+});
+
+/**
+ * action used to change the distance
+ * @prop {number} distance the distance to use to calculate points for the profile
+ */
+export const changeDistance = (distance) => ({
+ type: CHANGE_DISTANCE,
+ distance
+});
+
+/**
+ * action used to change the geometry
+ * @prop {Object} geometry the geometry object of a GeoJSON Feature
+ */
+export const changeGeometry = (geometry) => ({
+ type: CHANGE_GEOMETRY,
+ geometry
+});
+
+/**
+ * action used to change the referential
+ * @prop {string} referential the layer name to be used in the wps process
+ */
+export const changeReferential = (referential) => ({
+ type: CHANGE_REFERENTIAL,
+ referential
+});
+
+/**
+ * action used to close the dock panel
+ */
+export const closeDock = () => ({
+ type: SET_CONTROL_PROPERTY,
+ control: CONTROL_DOCK_NAME,
+ property: 'enabled',
+ value: false
+});
+
+/**
+ * action used to hide the marker when hovering the line chart
+ */
+export const hideMarker = () => ({
+ type: HIDE_MARKER
+});
+
+/**
+ * action used to complete setup phase
+ */
+export const initialized = () => ({
+ type: INITIALIZED
+});
+
+/**
+ * action used to manage loading status
+ * @prop {boolean} state the flag used to manage loading status
+ */
+export const loading = (state) => ({
+ type: LOADING,
+ state
+});
+
+/**
+ * action used to open the dock panel
+ */
+export const openDock = () => ({
+ type: SET_CONTROL_PROPERTY,
+ control: CONTROL_DOCK_NAME,
+ property: 'enabled',
+ value: true
+});
+
+/**
+ * action used to setup the the config of the longitudinal profile plugin
+ * @prop {Object} config the properties of the config (see Plugin documentation)
+ */
+export const setupPlugin = (config) => {
+ return {
+ type: SETUP,
+ config
+ };
+};
+
+/**
+ * action used to show a notification error message
+ * @prop {string} message the message id to use with a localized string
+ */
+export const showError = (message) => error({
+ message: message,
+ title: "errorTitleDefault",
+ position: "tc",
+ autoDismiss: 10
+});
+
+/**
+ * action used to deactivate the plugin
+ */
+export const tearDown = () => ({
+ type: TEAR_DOWN
+});
+
+/**
+ * action used to toggle maximize state of the chart
+ */
+export const toggleMaximize = () => ({
+ type: TOGGLE_MAXIMIZE
+});
+
+/**
+ * action used to change the referential
+ * @prop {string} mode the mode to use for providing the linestring, can be "idle", "draw", "import"
+ */
+export const toggleMode = (mode) => ({
+ type: TOGGLE_MODE,
+ mode
+});
+/**
+ * action used to setup
+ */
+export const setup = (config) => {
+ return (dispatch, getState) => {
+ dispatch(setupPlugin(config));
+ const { referentials, distances, defaultDistance, defaultReferentialName } = config || configSelector(getState());
+ const defaultReferential = referentials.find(el => el.layerName === defaultReferentialName);
+ if (defaultReferentialName && !defaultReferential) {
+ dispatch(error({ title: "Error", message: "longitudinalProfile.errors.defaultReferentialNotFound", autoDismiss: 10 }));
+ }
+
+ dispatch(updateDockPanelsList(CONTROL_DOCK_NAME, "add", "right"));
+ dispatch(changeReferential(defaultReferentialName ?? referentials[0].layerName));
+ dispatch(changeDistance(defaultDistance ?? distances[0]));
+ dispatch(updateAdditionalLayer(
+ LONGITUDINAL_VECTOR_LAYER_ID,
+ LONGITUDINAL_OWNER,
+ 'overlay',
+ {
+ id: LONGITUDINAL_VECTOR_LAYER_ID,
+ features: [],
+ type: "vector",
+ name: "selectedLine",
+ visibility: true
+ }));
+ dispatch(updateAdditionalLayer(
+ LONGITUDINAL_VECTOR_LAYER_ID_POINT,
+ LONGITUDINAL_OWNER,
+ 'overlay',
+ {
+ id: LONGITUDINAL_VECTOR_LAYER_ID_POINT,
+ features: [],
+ type: "vector",
+ name: "point",
+ visibility: true
+ }));
+ dispatch(initialized());
+ };
+};
diff --git a/web/client/components/contextcreator/ConfigurePluginsStep.jsx b/web/client/components/contextcreator/ConfigurePluginsStep.jsx
index adbb4c49d5..b17fb395f6 100644
--- a/web/client/components/contextcreator/ConfigurePluginsStep.jsx
+++ b/web/client/components/contextcreator/ConfigurePluginsStep.jsx
@@ -62,10 +62,10 @@ const getEnabledTools = (plugin, isMandatory, editedPlugin, documentationBaseURL
glyph: 'question-sign',
tooltipId: 'contextCreator.configurePlugins.tooltips.pluginDocumentation',
Element: (props) =>
-
{projection} | +|
{config.referential} | +
Unterstützte Dateitypen: GeoJSON, DXF, Shapefiles
", + "dxfGeometryNotSupported": "Es wird nur LWPOLYLINE unterstützt" + } } } } diff --git a/web/client/translations/data.en-US.json b/web/client/translations/data.en-US.json index b059ef8663..6e89edae27 100644 --- a/web/client/translations/data.en-US.json +++ b/web/client/translations/data.en-US.json @@ -3027,6 +3027,10 @@ "description": "Login tool", "title": "Login" }, + "LongitudinalProfileTool": { + "description": "Allows user to generate a longitudinal profile given a linestring and a layer with elevation attribute. Needs configuration", + "title": "Longitudinal Profile tool" + }, "MapCatalog": { "description": "Allows browsing, editing, deleting and loading of maps that are available on the server", "title": "Map Catalog" @@ -3725,6 +3729,71 @@ }, "sidebarMenu": { "showMoreItems": "Show more items" + }, + "longitudinalProfile": { + "open": "Open Longitudinal Profile", + "close": "Close Longitudinal Profile", + "title": "Longitudinal profile", + "draw": "Draw line", + "import": "Load file", + "select": "Selection to profile", + "parameters": "Parameters", + "elevation": "Elevation (m)", + "crsSelector": "Specify a projection for the DXF file", + "distance": "Distance (m)", + "chart": "Chart", + "infos": "Information", + "preferences": "Preferences", + "CRS": "CRS", + "uom": "Units", + "fileSelected": "File selected: ", + "uomMeters": "meters", + "source": "Source", + "downloadCSV": "CSV", + "downloadPNG": "PNG", + "downloadPDF": "PDF", + "info": { + "points": " points", + "totalPoints": "Number of points processed:", + "layer":"Layer:", + "line":"Distance:", + "up":"Cumulative elevation gain:", + "down":"Cumulative elevation loss:", + "noInfos": "No info available" + }, + "help": { + "draw": "Click on the map to draw the line. Click one more time on end point to finish.", + "select": "Please select layer in TOC and click on the line feature to generate profile.Supported file types: GeoJSON, DXF, Shapefiles
", + "dxfGeometryNotSupported": "Only LWPOLYLINE is supported" + } } } } diff --git a/web/client/translations/data.es-ES.json b/web/client/translations/data.es-ES.json index 46a21e669d..dc99342cf7 100644 --- a/web/client/translations/data.es-ES.json +++ b/web/client/translations/data.es-ES.json @@ -3015,6 +3015,10 @@ "description": "Login Tool", "title": "Login" }, + "LongitudinalProfileTool": { + "description": "Permite al usuario generar un perfil longitudinal dada una línea lineal y una capa con atributo de elevación. Necesita configuración", + "title": "Herramienta Perfil longitudinal" + }, "MapCatalog": { "description": "Permite navegar, editar, borrar y cargar mapas que están disponibles en el servidor", "title": "Catálogo de mapas" @@ -3713,6 +3717,71 @@ }, "sidebarMenu": { "showMoreItems": "Mostrar más elementos" + }, + "longitudinalProfile": { + "open": "Perfil Longitudinal abierto", + "close": "Cerrar Perfil Longitudinal", + "title": "Perfil longitudinal", + "draw": "Dibujar linea", + "import": "Cargar archivo", + "select": "Selección a perfil", + "parameters": "Parámetros", + "elevation": "Elevación (m)", + "crsSelector": "Especifique una proyección para el archivo DXF", + "distance": "Distancia (m)", + "chart": "Cuadro", + "infos": "Información", + "preferences": "Preferencias", + "CRS": "CRS", + "uom": "Unidades de medida", + "fileSelected": "archivo seleccionado: ", + "uomMeters": "metros", + "source": "Fuente", + "downloadCSV": "CSV", + "downloadPNG": "PNG", + "downloadPDF": "PDF", + "info": { + "points": " puntos", + "totalPoints": "Número de puntos procesados:", + "layer":"Capa:", + "line":"Couche:", + "up":"Ganancia de elevación acumulada:", + "down":"Pérdida de elevación acumulada:", + "noInfos": "No hay información disponible" + }, + "help": { + "draw": "Haga clic en el mapa para dibujar la línea. Haga clic una vez más en el punto final para terminar.", + "select": "Seleccione la capa en TOC y haga clic en la entidad de línea para generar el perfil.Tipos de archivos compatibles: GeoJSON, DXF, Shapefiles
", + "dxfGeometryNotSupported": "Solo se admite LWPOLYLINE" + } } } } diff --git a/web/client/translations/data.fr-FR.json b/web/client/translations/data.fr-FR.json index 069f16d956..179de42f0f 100644 --- a/web/client/translations/data.fr-FR.json +++ b/web/client/translations/data.fr-FR.json @@ -3017,6 +3017,10 @@ "description": "Authentification", "title": "Login" }, + "LongitudinalProfileTool": { + "description": "Permet à l'utilisateur de générer un profil longitudinal à partir d'une chaîne de lignes et d'un calque avec attribut d'élévation. Configuration requise", + "title": "Outil Profil longitudinal" + }, "MapCatalog": { "description": "Permet la navigation, l'édition, la suppression et le chargement des cartes disponibles sur le serveur", "title": "Catalogue de cartes" @@ -3714,6 +3718,71 @@ }, "sidebarMenu": { "showMoreItems": "Afficher plus d'éléments" + }, + "longitudinalProfile": { + "open": "Ouvrir le profil en long", + "close": "Fermer le profil en long", + "title": "Profil en long", + "draw": "Dessiner une ligne", + "import": "Fichier de chargement", + "select": "Sélection au profil", + "parameters": "Paramètres", + "elevation": "Élévation (m)", + "crsSelector": "Spécifiez une projection pour le fichier DXF", + "distance": "Distance (m)", + "chart": "Graphique", + "infos": "Information", + "preferences": "Préférences", + "CRS": "CRS", + "uom": "Unités de mesure", + "fileSelected": "Fichier sélectionné: ", + "uomMeters": "mètres", + "source": "Source", + "downloadCSV": "CSV", + "downloadPNG": "PNG", + "downloadPDF": "PDF", + "info": { + "points": " points:", + "totalPoints": "Nombre de points traités:", + "layer":"Layer:", + "line":"Distance:", + "up":"Gain d'altitude cumulé.", + "down":"Perte d'altitude cumulée:", + "noInfos": "Aucune information disponible" + }, + "help": { + "draw": "Cliquez sur la carte pour tracer la ligne. Cliquez une fois de plus sur le point final pour terminer.", + "select": "Veuillez sélectionner la couche dans la table des matières et cliquez sur l'entité linéaire pour générer le profil.Types de fichiers pris en charge: GeoJSON, DXF, Shapefiles
", + "dxfGeometryNotSupported": "Seul LWPOLYLINE est pris en charge" + } } } } diff --git a/web/client/translations/data.it-IT.json b/web/client/translations/data.it-IT.json index e9e39b77c3..13e31c0cdd 100644 --- a/web/client/translations/data.it-IT.json +++ b/web/client/translations/data.it-IT.json @@ -3017,6 +3017,10 @@ "description": "Login Tool", "title": "Login" }, + "LongitudinalProfileTool": { + "description": "Consente all'utente di generare un profilo longitudinale data una geomtria lineare e un layer con attributo di elevazione. Necessita di configurazione", + "title": "Profilo Longitudinale" + }, "MapCatalog": { "description": "Consente la navigazione, la modifica, l'eliminazione e il caricamento di mappe disponibili sul server", "title": "Catalogo mappe" @@ -3714,6 +3718,71 @@ }, "sidebarMenu": { "showMoreItems": "Mostra più elementi" + }, + "longitudinalProfile": { + "open": "Apri profilo longitudinale", + "close": "Chiudi profilo longitudinale", + "title": "Profilo longitudinale", + "draw": "Disegna profilo", + "import": "Carica file", + "select": "Profilo da livello", + "parameters": "Parametri", + "elevation": "Altitudine (m)", + "crsSelector": "Specifica una proiezione per il file DXF", + "distance": "Distanza (m)", + "chart": "Grafico", + "infos": "Informazione", + "preferences": "Preferenze", + "CRS": "CRS", + "uom": "Unità", + "fileSelected": "File selezionato: ", + "uomMeters": "metri", + "source": "Sorgente", + "downloadCSV": "CSV", + "downloadPNG": "PNG", + "downloadPDF": "PDF", + "info": { + "points": " punti", + "totalPoints": "Numero di punti processati:", + "layer":"Livello:", + "line":"Distanza:", + "up":"Dislivello Positivo:", + "down":"Dislivello Negativo:", + "noInfos": "Nessuna informazione disponibile" + }, + "help": { + "draw": "Clicca per disegnare una linea in mappa. Clicca ancora una volta per concludere l'interazione.", + "select": "Per favore seleziona un livello nella TOC e clicca una linea in mappa per generare il profiloFormati supportati: GeoJSON, DXF, Shapefiles
", + "dxfGeometryNotSupported": "Solo LWPOLYLINE è supportata nel DXF" + } } } } diff --git a/web/client/utils/FileUtils.js b/web/client/utils/FileUtils.js index 63ed005733..e58fa75a50 100644 --- a/web/client/utils/FileUtils.js +++ b/web/client/utils/FileUtils.js @@ -8,6 +8,7 @@ import FileSaver from 'file-saver'; +import { DxfParser } from 'dxf-parser'; import toBlob from 'canvas-to-blob'; import shp from 'shpjs'; import tj from '@mapbox/togeojson'; @@ -37,6 +38,7 @@ export const MIME_LOOKUPS = { 'gpx': 'application/gpx+xml', 'kmz': 'application/vnd.google-earth.kmz', 'kml': 'application/vnd.google-earth.kml+xml', + 'dxf': 'image/vnd.dxf', 'zip': 'application/zip', 'json': 'application/json', 'geojson': 'application/json', @@ -135,6 +137,24 @@ export const readGeoJson = function(file, warnings = false) { reader.readAsText(file); }); }; +export const readDxf = function(file) { + return new Promise((resolve, reject) => { + let reader = new FileReader(); + reader.onload = function() { + try { + const parserDXF = new DxfParser(); + const dxf = parserDXF.parseSync(reader.result); + resolve(dxf); + } catch (err) { + reject(err); + } + }; + reader.onerror = function() { + reject(reader.error.name); + }; + reader.readAsText(file); + }); +}; export const readWMC = function(file) { return new Promise((resolve, reject) => { let reader = new FileReader(); diff --git a/web/client/utils/LongitudinalProfileUtils.js b/web/client/utils/LongitudinalProfileUtils.js new file mode 100644 index 0000000000..a4e9da56c3 --- /dev/null +++ b/web/client/utils/LongitudinalProfileUtils.js @@ -0,0 +1,69 @@ +/* + * Copyright 2023, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. +*/ +import {parseURN, reprojectGeoJson} from "../utils/CoordinatesUtils"; + +/** + * Utility function to traverse through json input recursively and build a flat array of features + * @param json + * @param features + * @returns {*[]|*} + */ +export const flattenImportedFeatures = (json, features = undefined) => { + let flatten = []; + if (typeof features !== 'undefined') { + flatten = features; + } + if (json?.layers && Array.isArray(json.layers)) { + return json.layers.forEach(l => flattenImportedFeatures(l, flatten)); + } + if (json?.map && json.map?.layers) { + flattenImportedFeatures(json.map?.layers, flatten); + } + if (Array.isArray(json)) { + json.forEach(el => flattenImportedFeatures(el, flatten)); + } + if (json?.features && Array.isArray(json.features)) { + json.features.forEach(feature => flattenImportedFeatures(feature, flatten)); + } + if (json?.type === 'Feature') { + flatten.push(json); + } + return flatten; +}; + +/** + * Finds first line feature in array of features and reprojects geometry for further use in WPS request + * @param collection + * @param projection + * @returns {{feature: *, coordinates: *, reprojected: (*)}|{feature: undefined, coordinates: undefined, reprojected: undefined}} + */ +export const selectLineFeature = (collection, projection = "EPSG:4326") => { + const parsedProjectionName = parseURN(projection); + const feature = collection.find((f) => ["LineString", "MultiLineString"].includes(f?.geometry?.type)); + if (feature) { + const reprojected = parsedProjectionName !== "EPSG:3857" ? reprojectGeoJson(feature, parsedProjectionName, "EPSG:3857") : feature; + const coordinates = reprojected.geometry.type === "MultiLineString" ? reprojected.geometry.coordinates[0] : reprojected.geometry.coordinates; + return { feature, reprojected, coordinates }; + } + return { feature: undefined, reprojected: undefined, coordinates: undefined }; +}; + +/** + * Applies style to the features list + * @param features + * @param style + * @returns {*} + */ +export const styleFeatures = (features, style) => { + return features.map((feature) => { + return { + ...feature, + style + }; + }); +}; diff --git a/web/client/utils/styleparser/CesiumStyleParser.js b/web/client/utils/styleparser/CesiumStyleParser.js index a64d24fdab..841a5db38e 100644 --- a/web/client/utils/styleparser/CesiumStyleParser.js +++ b/web/client/utils/styleparser/CesiumStyleParser.js @@ -336,6 +336,7 @@ const getGraphics = ({ billboard: new Cesium.BillboardGraphics({ image, scale, + pixelOffset: symbolizer.offset ? new Cesium.Cartesian2(symbolizer.offset[0], symbolizer.offset[1]) : null, rotation: Cesium.Math.toRadians(-1 * symbolizer.rotate || 0), disableDepthTestDistance: symbolizer.msBringToFront ? Number.POSITIVE_INFINITY : 0, heightReference: Cesium.HeightReference[HEIGHT_REFERENCE_CONSTANTS_MAP[symbolizer.msHeightReference] || 'NONE'], diff --git a/web/client/utils/styleparser/OLStyleParser.js b/web/client/utils/styleparser/OLStyleParser.js index 839df670ba..defa7a0efa 100644 --- a/web/client/utils/styleparser/OLStyleParser.js +++ b/web/client/utils/styleparser/OLStyleParser.js @@ -745,7 +745,9 @@ export class OlStyleParser { scale: this._computeIconScaleBasedOnSymbolizer(symbolizer), // Rotation in openlayers is radians while we use degree rotation: (typeof (symbolizer.rotate) === 'number' ? symbolizer.rotate * Math.PI / 180 : undefined), - displacement: symbolizer.offset + displacement: symbolizer.offset, + anchor: symbolizer.anchor + }; // check if IconSymbolizer.image contains a placeholder const prefix = '\\{\\{';