diff --git a/docs/developer-guide/local-config.md b/docs/developer-guide/local-config.md index 1b0e74ad9d..3035f7c15d 100644 --- a/docs/developer-guide/local-config.md +++ b/docs/developer-guide/local-config.md @@ -99,6 +99,20 @@ This is the main structure: "leaflet": { ... }, "cesium": { ... } }, + // allow to define which 2D or 3D map library should be used based on the device + // the configuration below is the default one + // structure -> { mapType: { [visualizationMode]: { [deviceType]: mapLibrary } } } + // note: this configuration does not support expressions + "mapType": { + "2D": { + "desktop": "openlayers", + "mobile": "leaflet" + }, + "3D": { + "desktop": "cesium", + "mobile": "cesium" + } + }, "plugins": { // plugins to load for the mobile mode "mobile": [...] diff --git a/docs/developer-guide/maps-configuration.md b/docs/developer-guide/maps-configuration.md index 8b7211cdef..2ba9ce812b 100644 --- a/docs/developer-guide/maps-configuration.md +++ b/docs/developer-guide/maps-configuration.md @@ -3,12 +3,11 @@ By default MapStore is able to open maps with this path in the URL: ```http -http://localhost:8081/#viewer// +http://localhost:8081/#viewer/ ``` Where: -- `maptype` can be `leaflet` `openlayers` or `cesium`. - `mapId` can be a number or a string. - A **number** represents standard maps, stored on the database. - A **string** instead represents a static json file in the root of the application. @@ -67,6 +66,7 @@ The following options define the map options (projection, position, layers): - `maxExtent: {number[]}` max bbox of the map expressed [minx, miny, maxx, maxy] - `layers: {object[]}` list of layers to be loaded on the map - `groups {object[]}`: contains information about the layer groups +- `visualizationMode: {string}` defines if the map should be visualized in "2D" or "3D" i.e. @@ -77,6 +77,7 @@ i.e. "units": "m", "center": {"x": 1000000.000000, "y": 5528000.000000, "crs": "EPSG:900913"}, "zoom": 15, + "visualizationMode": "2D", "mapOptions": { "view": { "scales": [175000, 125000, 100000, 75000, 50000, 25000, 10000, 5000, 2500], diff --git a/docs/developer-guide/mapstore-migration-guide.md b/docs/developer-guide/mapstore-migration-guide.md index 5349af251c..ec97a818ea 100644 --- a/docs/developer-guide/mapstore-migration-guide.md +++ b/docs/developer-guide/mapstore-migration-guide.md @@ -20,6 +20,73 @@ This is a list of things to check if you want to update from a previous version - Optionally check also accessory files like `.eslinrc`, if you want to keep aligned with lint standards. - Follow the instructions below, in order, from your version to the one you want to update to. +## Migration from 2023.01.xx to 2023.02.00 + +### Visualization mode in map configuration + +The map configuration stores the information related to the visualization mode 2D or 3D after saving a map. +This update include also following changes: + +- `maptype` configuration inside the initialState of localConfig needs to be removed in favor of the global mapType configuration + +```diff +{ + // ... + "initialState": { + "defaultState": { + // ... +- "maptype": { +- "mapType": "{context.mode === 'desktop' ? 'openlayers' : 'leaflet'}" +- }, + // ... + } + } + // ... +} +``` + +- the `changeMapType` action has been deprecated in favor of the `changeVisualizationMode` action + +- the application does not expose the pathname of the viewer with `mapType` anymore. Example: the old path `/viewer/openlayers/1` becomes `/viewer/1` + +- it is possible to change the map library based on the device using the new `mapType` configuration in localConfig.json. This configuration is only needed for project with custom map library settings. The downstream projects based on the MapStore product don't need this update + +```diff +{ + // ... ++ "mapType": { ++ "2D": { ++ "desktop": "openlayers", ++ "mobile": "leaflet" ++ }, ++ "3D": { ++ "desktop": "cesium", ++ "mobile": "cesium" ++ } ++ }, + // ... +} +``` + +- the app pages inside a MapStore project must be updated with a new entry, only for projects with custom pages and that are using context applications, here an example: + +```js +import MapViewer from '@mapstore/product/pages/MapViewer'; +import productAppConfig from "@mapstore/product/appConfig"; + +const appConfig = { + ...productAppConfig, + pages: [ + // my custom pages ..., + { + name: "mapviewer", + path: "/viewer/:mapId/context/:contextId", + component: MapViewer + } + ] +}; +``` + ## Migration from 2022.02.02 to 2023.01.00 ### Log4j update to Log4j2 diff --git a/web/client/actions/__tests__/globeswitcher-test.js b/web/client/actions/__tests__/globeswitcher-test.js deleted file mode 100644 index 0277f2d98e..0000000000 --- a/web/client/actions/__tests__/globeswitcher-test.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017, 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 { toggle3d, TOGGLE_3D } from '../globeswitcher'; - -describe('Test correctness of the maptype actions', () => { - - it('toggle3d', () => { - const retVal = toggle3d(true); - expect(retVal).toExist(); - expect(retVal.type).toBe(TOGGLE_3D); - expect(retVal.enable).toBe(true); - }); -}); diff --git a/web/client/actions/__tests__/maptype-test.js b/web/client/actions/__tests__/maptype-test.js index 4171fafea5..9d341fca76 100644 --- a/web/client/actions/__tests__/maptype-test.js +++ b/web/client/actions/__tests__/maptype-test.js @@ -8,20 +8,24 @@ import expect from 'expect'; -import { MAP_TYPE_CHANGED, changeMapType, updateLast2dMapType, UPDATE_LAST_2D_MAPTYPE } from '../maptype'; +import { + MAP_TYPE_CHANGED, + changeMapType, + VISUALIZATION_MODE_CHANGED, + changeVisualizationMode +} from '../maptype'; describe('Test correctness of the maptype actions', () => { - it('changeMapType', () => { const retVal = changeMapType('maptype'); - expect(retVal).toExist(); + expect(retVal).toBeTruthy(); expect(retVal.type).toBe(MAP_TYPE_CHANGED); expect(retVal.mapType).toBe('maptype'); }); - it('updateLast2dMapType', () => { - const retVal = updateLast2dMapType("leaflet"); - expect(retVal).toExist(); - expect(retVal.type).toBe(UPDATE_LAST_2D_MAPTYPE); - expect(retVal.mapType).toBe('leaflet'); + it('changeVisualizationMode', () => { + const retVal = changeVisualizationMode('3D'); + expect(retVal).toBeTruthy(); + expect(retVal.type).toBe(VISUALIZATION_MODE_CHANGED); + expect(retVal.visualizationMode).toBe('3D'); }); }); diff --git a/web/client/actions/globeswitcher.js b/web/client/actions/globeswitcher.js deleted file mode 100644 index 0f601df1c1..0000000000 --- a/web/client/actions/globeswitcher.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017, 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. - */ - - -export const TOGGLE_3D = "TOGGLE_3D"; -export const UPDATE_LAST_2D_MAPTYPE = "UPDATE_LAST_2D_MAPTYPE"; -/** - * Emitted when 3d map have to be toggled - * @memberof actions.globeswitcher - * @param {boolean} enable true for enable, false for disable - * @return {action} the action of type `TOGGLE_FULLSCREEN` with enable flag and element selector. - * ``` - * { - * type: TOGGLE_3D, - * enable - * } - * ``` - */ -export function toggle3d(enable, originalMapType) { - return { - type: TOGGLE_3D, - enable, - originalMapType - }; -} - -/** - * Actions for Globe Switcher Plugin. - * @name actions.globeswitcher - */ diff --git a/web/client/actions/maptype.js b/web/client/actions/maptype.js index a1f58a1092..8007c6afbb 100644 --- a/web/client/actions/maptype.js +++ b/web/client/actions/maptype.js @@ -7,13 +7,14 @@ */ export const MAP_TYPE_CHANGED = 'MAP_TYPE_CHANGED'; -export const UPDATE_LAST_2D_MAPTYPE = "MAP_TYPE:UPDATE_LAST_2D_MAPTYPE"; +export const VISUALIZATION_MODE_CHANGED = 'MAP_TYPE:VISUALIZATION_MODE_CHANGED'; /** * changes the map type + * @deprecated * @memberof actions.maptype * @param {string} mapType the mapType. - * @return {action} the action of type `MAP_TYPE_CHANGED` with mapType + * @return {action} the action of type `MAP_TYPE_CHANGED` with mapType */ export function changeMapType(mapType) { return { @@ -22,24 +23,17 @@ export function changeMapType(mapType) { }; } /** - * Saves the last 2d map - * @memberof actions.globeswitcher - * @param {string} mapType last maptype - * @return {object} action - * ``` - * { - * type: MAPTYPE_2D_SELECTED, - * mapType - * } - * ``` + * changes the visualization mode + * @memberof actions.maptype + * @param {string} visualizationMode eg: 2D or 3D. + * @return {action} the action of type `VISUALIZATION_MODE_CHANGED` with visualizationMode */ -export function updateLast2dMapType(mapType) { +export function changeVisualizationMode(visualizationMode) { return { - type: UPDATE_LAST_2D_MAPTYPE, - mapType + type: VISUALIZATION_MODE_CHANGED, + visualizationMode }; } - /** * Actions for map type management.Allow to manage the default map type. * @name actions.maptype diff --git a/web/client/api.html b/web/client/api.html index d4f560113b..1d77ffb558 100644 --- a/web/client/api.html +++ b/web/client/api.html @@ -30,7 +30,7 @@ diff --git a/web/client/components/buttons/GoFullButton.jsx b/web/client/components/buttons/GoFullButton.jsx index 159d85225e..4698c61061 100644 --- a/web/client/components/buttons/GoFullButton.jsx +++ b/web/client/components/buttons/GoFullButton.jsx @@ -38,7 +38,7 @@ class GoFullButton extends React.Component { glyph: "share", tooltip: "fullscreen.viewLargerMap", urlRegex: "^(.*?)embedded.html.*?#\\/(\\d?)", - urlReplaceString: "$1#/viewer/leaflet/$2" + urlReplaceString: "$1#/viewer/$2" }; render() { diff --git a/web/client/components/contextcreator/ConfigureMapStep.jsx b/web/client/components/contextcreator/ConfigureMapStep.jsx index 0f600d3432..cf217d0ca8 100644 --- a/web/client/components/contextcreator/ConfigureMapStep.jsx +++ b/web/client/components/contextcreator/ConfigureMapStep.jsx @@ -11,11 +11,12 @@ import React from 'react'; import Message from '../I18N/Message'; import ConfirmDialog from '../misc/ConfirmDialog'; import MapViewer from '../../containers/MapViewer'; +import { MapLibraries } from '../../utils/MapTypeUtils'; export default ({ pluginsConfig = {}, plugins = {}, - mapType = 'openlayers', + mapType = MapLibraries.OPENLAYERS, className = 'viewer context-creator-viewer', showConfirm = false, confirmMessage = 'contextCreator.configureMap.confirm', diff --git a/web/client/components/contextcreator/ContextCreator.jsx b/web/client/components/contextcreator/ContextCreator.jsx index 9d7e170806..776b93a08f 100644 --- a/web/client/components/contextcreator/ContextCreator.jsx +++ b/web/client/components/contextcreator/ContextCreator.jsx @@ -233,7 +233,8 @@ export default class ContextCreator extends React.Component { "Undo", "Redo", "Expander", - "FilterLayer" + "FilterLayer", + "GlobeViewSwitcher" ], ignoreViewerPlugins: false, allAvailablePlugins: [], diff --git a/web/client/components/data/featuregrid/toolbars/Toolbar.jsx b/web/client/components/data/featuregrid/toolbars/Toolbar.jsx index 509ff07105..115872e99c 100644 --- a/web/client/components/data/featuregrid/toolbars/Toolbar.jsx +++ b/web/client/components/data/featuregrid/toolbars/Toolbar.jsx @@ -10,6 +10,7 @@ import { getApi } from '../../../../api/userPersistedStorage'; import TSplitButtonComp from "./TSplitButton"; import Spinner from "react-spinkit"; import Select from "react-select"; +import { MapLibraries } from '../../../../utils/MapTypeUtils'; const TButton = withHint(TButtonComp); const TSplitButton = withHint(TSplitButtonComp); @@ -172,7 +173,7 @@ const standardButtons = { id="snap-button" keyProp="snap-button" tooltipId={snapping ? "featuregrid.toolbar.disableSnapping" : "featuregrid.toolbar.enableSnapping"} - visible={mode === "EDIT" && (pluginCfg?.snapTool ?? true) && mapType === 'openlayers'} + visible={mode === "EDIT" && (pluginCfg?.snapTool ?? true) && mapType === MapLibraries.OPENLAYERS} onClick={() => { events.toggleSnapping && events.toggleSnapping(!snapping); }} diff --git a/web/client/components/geostory/common/map/Controls.jsx b/web/client/components/geostory/common/map/Controls.jsx index 68e3bc3742..c85e5c9e44 100644 --- a/web/client/components/geostory/common/map/Controls.jsx +++ b/web/client/components/geostory/common/map/Controls.jsx @@ -12,6 +12,7 @@ import Message from '../../../I18N/Message'; import Select from "react-select"; import {isNil} from "lodash"; import { applyDefaults } from '../../../../utils/GeoStoryUtils'; +import { is3DVisualizationMode } from '../../../../utils/MapTypeUtils'; import SwitchButton from '../../../misc/switch/SwitchButton'; import localizedProps from '../../../misc/enhancers/localizedProps'; @@ -30,8 +31,9 @@ export const Controls = ({ zoomControl: !isNil(map.zoomControl) ? map.zoomControl : true, mapInfoControl: !isNil(map.mapInfoControl) ? map.mapInfoControl : false }); + const is3D = is3DVisualizationMode(map); return (
- + {!is3D && { @@ -60,7 +62,7 @@ export const Controls = ({ onChange={(val) => onChangeMap("mapOptions.zoomPosition", val && val.value ? val.value : "topLeft")} placeholder="geostory.builder.settings.titlePlaceholder" /> - + } - + {!is3D && { @@ -93,7 +95,7 @@ export const Controls = ({ selectProps={{ wrapperStyle: { marginTop: 10 } }}/>} - + }
); }; diff --git a/web/client/components/geostory/common/map/FitBounds.jsx b/web/client/components/geostory/common/map/FitBounds.jsx index 88ba363d12..087495a366 100644 --- a/web/client/components/geostory/common/map/FitBounds.jsx +++ b/web/client/components/geostory/common/map/FitBounds.jsx @@ -11,9 +11,10 @@ import PropTypes from 'prop-types'; import { reprojectBbox, reproject } from '../../../../utils/CoordinatesUtils'; import Point from 'ol/geom/Point'; +import { MapLibraries } from '../../../../utils/MapTypeUtils'; const zoomTo = { - openlayers: { + [MapLibraries.OPENLAYERS]: { fit: ({ map, geometry, padding, geometryProjection, fixedZoom, maxZoom, duration }) => { const view = map.getView(); const mapProjection = view.getProjection().getCode(); @@ -32,7 +33,7 @@ const zoomTo = { }); } }, - leaflet: { + [MapLibraries.LEAFLET]: { fit: ({ map, geometry, padding, geometryProjection, fixedZoom, maxZoom, duration }) => { const zoom = fixedZoom ? map.getZoom() : maxZoom; const { top = 0, right = 0, bottom = 0, left = 0 } = padding; diff --git a/web/client/components/geostory/common/map/LocalDrawSupport.jsx b/web/client/components/geostory/common/map/LocalDrawSupport.jsx index f4289bbd54..e96d2a1989 100644 --- a/web/client/components/geostory/common/map/LocalDrawSupport.jsx +++ b/web/client/components/geostory/common/map/LocalDrawSupport.jsx @@ -9,7 +9,7 @@ import React, { useState, useEffect } from "react"; import PropTypes from 'prop-types'; import DrawSupport from '../../../map/openlayers/DrawSupport'; - +import { MapLibraries } from '../../../../utils/MapTypeUtils'; /** * A draw support interface to use as component instead of plugin * @prop {object} map map library instance object @@ -37,7 +37,7 @@ function LocalDrawSupport({ setStatus(active ? 'drawOrEdit' : 'clean'); }, [active]); - if (mapType !== 'openlayers') { + if (mapType !== MapLibraries.OPENLAYERS) { return null; } diff --git a/web/client/components/geostory/media/Map.jsx b/web/client/components/geostory/media/Map.jsx index 34569c637a..091a0b0c7d 100644 --- a/web/client/components/geostory/media/Map.jsx +++ b/web/client/components/geostory/media/Map.jsx @@ -16,6 +16,8 @@ import Portal from '../../../components/misc/Portal'; import tooltip from '../../../components/misc/enhancers/tooltip'; import { withResizeDetector } from 'react-resize-detector'; +import { MapLibraries } from '../../../utils/MapTypeUtils'; + import ButtonRB from '../../misc/Button'; const Button = tooltip(ButtonRB); @@ -37,7 +39,7 @@ export default compose( size, showCaption, caption: contentCaption, - mapType = "leaflet", // default for when map MediaViewer is not connected to redux + mapType = MapLibraries.OPENLAYERS, // default for when map MediaViewer is not connected to redux onMapTypeLoaded, layers: geoStoryLayers, children, diff --git a/web/client/components/import/ShapefileUploadAndStyle.jsx b/web/client/components/import/ShapefileUploadAndStyle.jsx index db97d50306..abdd6d5e45 100644 --- a/web/client/components/import/ShapefileUploadAndStyle.jsx +++ b/web/client/components/import/ShapefileUploadAndStyle.jsx @@ -51,7 +51,6 @@ class ShapeFileUploadAndStyle extends React.Component { updateShapeBBox: PropTypes.func, error: PropTypes.string, success: PropTypes.string, - mapType: PropTypes.string, buttonSize: PropTypes.string, uploadMessage: PropTypes.object, cancelMessage: PropTypes.object, @@ -98,7 +97,6 @@ class ShapeFileUploadAndStyle extends React.Component { } return null; }), - mapType: "leaflet", stylers: {}, buttonSize: "small", uploadOptions: {}, diff --git a/web/client/components/import/style/StylePanel.jsx b/web/client/components/import/style/StylePanel.jsx index 1ea7def66d..4720c6fea9 100644 --- a/web/client/components/import/style/StylePanel.jsx +++ b/web/client/components/import/style/StylePanel.jsx @@ -38,7 +38,6 @@ class StylePanel extends React.Component { updateBBox: PropTypes.func, errors: PropTypes.array, success: PropTypes.string, - mapType: PropTypes.string, buttonSize: PropTypes.string, cancelMessage: PropTypes.object, addMessage: PropTypes.object, @@ -53,7 +52,6 @@ class StylePanel extends React.Component { }; static defaultProps = { - mapType: "leaflet", buttonSize: "small", setLayers: () => {}, addLayer: () => {}, diff --git a/web/client/components/map/BaseMap.jsx b/web/client/components/map/BaseMap.jsx index bd88b88511..6da09af94b 100644 --- a/web/client/components/map/BaseMap.jsx +++ b/web/client/components/map/BaseMap.jsx @@ -123,10 +123,14 @@ class BaseMap extends React.Component { }; renderTools = () => { - return this.props.tools.map((tool) => { - const {impl: Tool, name, ...options} = this.getTool(tool); - return ; - }); + return this.props.tools + .filter((tool) => { + return this.props?.plugins?.tools?.[isString(tool) ? tool : tool?.name]; + }) + .map((tool) => { + const {impl: Tool, name, ...options} = this.getTool(tool); + return ; + }); }; render() { diff --git a/web/client/components/map/cesium/Map.jsx b/web/client/components/map/cesium/Map.jsx index 89979f10fa..f42c366b67 100644 --- a/web/client/components/map/cesium/Map.jsx +++ b/web/client/components/map/cesium/Map.jsx @@ -53,7 +53,9 @@ class CesiumMap extends React.Component { orientate: PropTypes.object, zoomControl: PropTypes.bool, errorPanel: PropTypes.func, - onReload: PropTypes.func + onReload: PropTypes.func, + style: PropTypes.object, + interactive: PropTypes.bool }; static defaultProps = { @@ -78,7 +80,8 @@ class CesiumMap extends React.Component { roll: 0 } }, - onReload: () => {} + onReload: () => {}, + interactive: true }; state = { @@ -134,10 +137,12 @@ class CesiumMap extends React.Component { if (this.props.registerHooks) { this.registerHooks(); } - if (this.props.mapOptions?.navigationTools || this.props.zoomControl) { + if (this.props.mapOptions?.navigationTools !== false) { map.extend(viewerCesiumNavigationMixin, { enableCompass: this.props.mapOptions?.navigationTools, - enableZoomControls: this.props.zoomControl, + // the default zoom controls inside CesiumNavigation are not working + // when the enableZoom is false + enableZoomControls: false, enableDistanceLegend: false }); } @@ -160,6 +165,8 @@ class CesiumMap extends React.Component { this.map = map; const scene = this.map.scene; + // update interactions after this.map is defined + this.updateInteractions(this.props); // configure the sky environment scene.skyAtmosphere.show = this.props.mapOptions?.showSkyAtmosphere ?? true; @@ -212,6 +219,11 @@ class CesiumMap extends React.Component { if (prevProps && (this.props.mapOptions.depthTestAgainstTerrain !== prevProps?.mapOptions?.depthTestAgainstTerrain)) { this.map.scene.globe.depthTestAgainstTerrain = this.props.mapOptions.depthTestAgainstTerrain; } + + if (prevProps?.interactive !== this.props.interactive + || !isEqual(prevProps?.mapOptions?.interactions, this.props?.mapOptions?.interactions)) { + this.updateInteractions(this.props); + } } componentWillUnmount() { @@ -364,7 +376,7 @@ class CesiumMap extends React.Component { }) : null; const ErrorPanel = this.props.errorPanel; return ( -
+
{children} {ErrorPanel ? { + const interactionsOptions = { + ...props.mapOptions?.interactions, + ...(!props.interactive && { + dragPan: false, + mouseWheelZoom: false + }) + }; + this.map.scene.screenSpaceCameraController.enableZoom = !(interactionsOptions.mouseWheelZoom === false); + this.map.scene.screenSpaceCameraController.enableRotate = !(interactionsOptions.dragPan === false); + this.map.scene.screenSpaceCameraController.enableTranslate = !(interactionsOptions.dragPan === false); + this.map.scene.screenSpaceCameraController.enableTilt = !(interactionsOptions.dragPan === false); + } } const ReloadCesiumMap = forwardRef((props, ref) => { diff --git a/web/client/components/map/enhancers/autoMapType.js b/web/client/components/map/enhancers/autoMapType.js index 6aca6e503c..4d69c06dd5 100644 --- a/web/client/components/map/enhancers/autoMapType.js +++ b/web/client/components/map/enhancers/autoMapType.js @@ -1,4 +1,18 @@ -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { mapTypeSelector } from '../../../selectors/maptype'; -export default connect(createSelector(mapTypeSelector, mapType => ({mapType}))); +/* + * 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 React from 'react'; +import { getMapLibraryFromVisualizationMode, VisualizationModes } from '../../../utils/MapTypeUtils'; + +const autoMapType = (Component) => props => ( + +); +export default autoMapType; diff --git a/web/client/components/mapcatalog/MapCatalogPanel.jsx b/web/client/components/mapcatalog/MapCatalogPanel.jsx index a68bf6420a..806cd7559d 100644 --- a/web/client/components/mapcatalog/MapCatalogPanel.jsx +++ b/web/client/components/mapcatalog/MapCatalogPanel.jsx @@ -80,7 +80,7 @@ const onClickHandler = (map, router, mapType, toggleCatalog, reloadFunction) => } else { router.history.push(map.contextName ? "/context/" + map.contextName + "/" + map.id : - "/viewer/" + mapType + "/" + map.id + "/viewer/" + map.id ); } }; diff --git a/web/client/components/mapcontrols/Snapshot/SnapshotPanel.jsx b/web/client/components/mapcontrols/Snapshot/SnapshotPanel.jsx index 246b5a82f2..b6c55770ee 100644 --- a/web/client/components/mapcontrols/Snapshot/SnapshotPanel.jsx +++ b/web/client/components/mapcontrols/Snapshot/SnapshotPanel.jsx @@ -25,6 +25,7 @@ import notAvailable from './not-available.png'; import shotingImg from './shoting.gif'; import snapshotSupportComp from './SnapshotSupport'; import PanelHeader from "../../misc/panels/PanelHeader"; +import { MapLibraries } from '../../../utils/MapTypeUtils'; let SnapshotSupport; /** @@ -79,7 +80,7 @@ class SnapshotPanel extends React.Component { googleBingErrorMsg: "snapshot.googleBingError", downloadingMsg: "snapshot.downloadingSnapshots", timeout: 1000, - mapType: 'leaflet', + mapType: MapLibraries.OPENLAYERS, floatingPanel: true, panelStyle: { minWidth: "600px", diff --git a/web/client/components/mapcontrols/Snapshot/SnapshotQueue.jsx b/web/client/components/mapcontrols/Snapshot/SnapshotQueue.jsx index 89bf21a200..7b2e53788c 100644 --- a/web/client/components/mapcontrols/Snapshot/SnapshotQueue.jsx +++ b/web/client/components/mapcontrols/Snapshot/SnapshotQueue.jsx @@ -12,6 +12,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import snapshotSupportComp from './SnapshotSupport'; +import { MapLibraries } from '../../../utils/MapTypeUtils'; let SnapshotSupport; @@ -37,7 +38,7 @@ class SnapshotQueue extends React.Component { static defaultProps = { onRemoveSnapshot: () => {}, onSnapshotError: () => {}, - mapType: 'leaflet' + mapType: MapLibraries.OPENLAYERS }; UNSAFE_componentWillMount() { diff --git a/web/client/components/maps/MapList.jsx b/web/client/components/maps/MapList.jsx index 61cd41a66b..6f11c7c98f 100644 --- a/web/client/components/maps/MapList.jsx +++ b/web/client/components/maps/MapList.jsx @@ -12,7 +12,7 @@ import React from 'react'; import { ListGroup, Panel } from 'react-bootstrap'; import MapItem from './MapItem'; - +import { MapLibraries } from '../../utils/MapTypeUtils'; class MapList extends React.Component { static propTypes = { panelProps: PropTypes.object, @@ -23,7 +23,7 @@ class MapList extends React.Component { static defaultProps = { onChangeMapType: function() {}, - mapType: 'leaflet', + mapType: MapLibraries.OPENLAYERS, maps: [] }; diff --git a/web/client/components/mapviews/MapViewsSupport.jsx b/web/client/components/mapviews/MapViewsSupport.jsx index 9e7e4f2818..f0f733d1f8 100644 --- a/web/client/components/mapviews/MapViewsSupport.jsx +++ b/web/client/components/mapviews/MapViewsSupport.jsx @@ -26,12 +26,13 @@ import Message from '../I18N/Message'; import Loader from '../misc/Loader'; import ButtonMS from '../misc/Button'; import tooltip from '../misc/enhancers/tooltip'; +import { MapLibraries } from '../../utils/MapTypeUtils'; const Button = tooltip(ButtonMS); const mapViewSupports = { - leaflet: lazy(() => import(/* webpackChunkName: 'supports/leafletMapViews' */ '../map/leaflet/MapViewsSupport')), - openlayers: lazy(() => import(/* webpackChunkName: 'supports/olMapViews' */ '../map/openlayers/MapViewsSupport')), - cesium: lazy(() => import(/* webpackChunkName: 'supports/cesiumMapViews' */ '../map/cesium/MapViewsSupport')) + [MapLibraries.LEAFLET]: lazy(() => import(/* webpackChunkName: 'supports/leafletMapViews' */ '../map/leaflet/MapViewsSupport')), + [MapLibraries.OPENLAYERS]: lazy(() => import(/* webpackChunkName: 'supports/olMapViews' */ '../map/openlayers/MapViewsSupport')), + [MapLibraries.CESIUM]: lazy(() => import(/* webpackChunkName: 'supports/cesiumMapViews' */ '../map/cesium/MapViewsSupport')) }; const MapViewSettings = lazy(() => import('./MapViewSettings')); diff --git a/web/client/components/print/MapPreview.jsx b/web/client/components/print/MapPreview.jsx index dfe5628331..2ab74483b1 100644 --- a/web/client/components/print/MapPreview.jsx +++ b/web/client/components/print/MapPreview.jsx @@ -15,6 +15,7 @@ import { getMapZoom, getResolutionMultiplier } from '../../utils/PrintUtils'; import ScaleBox from '../mapcontrols/scale/ScaleBox'; import Button from '../misc/Button'; import isNil from 'lodash/isNil'; +import { MapLibraries } from '../../utils/MapTypeUtils'; let PMap; let Layer; @@ -46,7 +47,7 @@ class MapPreview extends React.Component { static defaultProps = { map: null, layers: [], - mapType: "leaflet", + mapType: MapLibraries.OPENLAYERS, style: {display: "block", border: "1px solid black", position: "relative"}, onChangeZoomLevel: () => {}, onMapViewChanges: () => {}, diff --git a/web/client/components/print/__tests__/MapPreview-test.jsx b/web/client/components/print/__tests__/MapPreview-test.jsx index f6e5241f3f..cccc99c462 100644 --- a/web/client/components/print/__tests__/MapPreview-test.jsx +++ b/web/client/components/print/__tests__/MapPreview-test.jsx @@ -35,6 +35,7 @@ describe("Test the MapPreview component", () => { it('creates a leaflet map', (done) => { const cmp = ReactDOM.render( { if (!loading) { expect(cmp).toExist(); @@ -98,6 +99,7 @@ describe("Test the MapPreview component", () => { }]; const cmp = ReactDOM.render( { if (!loading) { diff --git a/web/client/components/share/SharePanel.jsx b/web/client/components/share/SharePanel.jsx index 430ddbca02..1334d36f32 100644 --- a/web/client/components/share/SharePanel.jsx +++ b/web/client/components/share/SharePanel.jsx @@ -34,6 +34,7 @@ import Editor from '../data/identify/coordinates/Editor'; import {set} from '../../utils/ImmutableUtils'; import OverlayTrigger from '../misc/OverlayTrigger'; import ResizableModal from '../misc/ResizableModal'; +import { MapLibraries } from '../../utils/MapTypeUtils'; /** * SharePanel allow to share the current map in some different ways. @@ -173,7 +174,7 @@ class SharePanel extends React.Component { let newPoint = set('latlng.lng', lng, set('latlng.lat', lat, this.props.point)); settings.markerEnabled ? addMarker(newPoint) : hideMarker(); } - if (this.props.mapType === 'cesium' && this.props.settings.markerEnabled) { + if (this.props.mapType === MapLibraries.CESIUM && this.props.settings.markerEnabled) { this.props.onUpdateSettings({ ...this.props.settings, markerEnabled: !this.props.settings.markerEnabled @@ -215,7 +216,7 @@ class SharePanel extends React.Component { if (settings.bboxEnabled && advancedSettings && advancedSettings.bbox && this.state.bbox) shareUrl = `${shareUrl}?bbox=${this.state.bbox}`; if (settings.showHome && advancedSettings && advancedSettings.homeButton) shareUrl = `${shareUrl}?showHome=true`; if (settings.centerAndZoomEnabled && advancedSettings && advancedSettings.centerAndZoom) { - if (mapType === 'cesium' && viewerOptions && viewerOptions.orientation) { + if (mapType === MapLibraries.CESIUM && viewerOptions && viewerOptions.orientation) { return `${shareUrl}?center=${this.state.coordinate}&zoom=${this.state.zoom}&heading=${convertDegreesToRadian(this.state.heading)}&pitch=${convertDegreesToRadian(this.state.pitch)}&roll=${convertDegreesToRadian(this.state.roll)}`; } shareUrl = `${shareUrl}${settings.markerEnabled ? "?marker=" : "?center="}${this.state.coordinate}&zoom=${this.state.zoom}`; @@ -317,7 +318,7 @@ class SharePanel extends React.Component { title={} expanded={this.state.showAdvanced} onSwitch={() => this.setState({ showAdvanced: !this.state.showAdvanced })}> - {this.props.advancedSettings.bbox && this.props.mapType !== 'cesium' && this.props.onUpdateSettings({ @@ -401,7 +402,7 @@ class SharePanel extends React.Component { }}/> { - this.props.mapType && this.props.mapType === 'cesium' && ( + this.props.mapType && this.props.mapType === MapLibraries.CESIUM && ( @@ -477,7 +478,7 @@ class SharePanel extends React.Component { ) } { - this.props.mapType !== 'cesium' && ( { this.props.onUpdateSettings({ diff --git a/web/client/components/widgets/builder/wizard/map/Toolbar.jsx b/web/client/components/widgets/builder/wizard/map/Toolbar.jsx index f874bcb317..af634d6fb8 100644 --- a/web/client/components/widgets/builder/wizard/map/Toolbar.jsx +++ b/web/client/components/widgets/builder/wizard/map/Toolbar.jsx @@ -10,6 +10,7 @@ import React from 'react'; import isEmpty from 'lodash/isEmpty'; import Toolbar from '../../../../misc/toolbar/Toolbar'; +import { is3DVisualizationMode } from '../../../../../utils/MapTypeUtils'; const getSaveTooltipId = (step, { id } = {}) => { if (id) { @@ -20,6 +21,7 @@ const getSaveTooltipId = (step, { id } = {}) => { export default ({ step = 0, buttons, tocButtons = [], stepButtons = [], dashBoardEditing = false, editorData = {}, setPage = () => { }, onFinish = () => { }, toggleLayerSelector = () => { }, onChange = () => {} } = {}) => { const map = (editorData?.maps || []).find(m => m.mapId === editorData?.selectedMapId) || {}; + const is3D = is3DVisualizationMode(map); const isEmptyMap = editorData?.widgetType === "map" && isEmpty(map); return ( onChange(`maps[${editorData?.selectedMapId}].mapInfoControl`, !map?.mapInfoControl), - visible: !isEmptyMap && dashBoardEditing && editorData?.widgetType === "map", + visible: !is3D && !isEmptyMap && dashBoardEditing && editorData?.widgetType === "map", glyph: "info-sign", bsStyle: map?.mapInfoControl ? "success" : "primary", tooltipId: map?.mapInfoControl ? "widgets.builder.wizard.disableIdentifyTool" : "widgets.builder.wizard.enableIdentifyTool" diff --git a/web/client/components/widgets/builder/wizard/map/enhancers/handleMapSelect.js b/web/client/components/widgets/builder/wizard/map/enhancers/handleMapSelect.js index e279916e66..e6149f8ec1 100644 --- a/web/client/components/widgets/builder/wizard/map/enhancers/handleMapSelect.js +++ b/web/client/components/widgets/builder/wizard/map/enhancers/handleMapSelect.js @@ -12,6 +12,7 @@ import axios from '../../../../../../libs/ajax'; import ConfigUtils from '../../../../../../utils/ConfigUtils'; import { excludeGoogleBackground, extractTileMatrixFromSources } from '../../../../../../utils/LayersUtils'; import { EMPTY_MAP } from "../../../../../../utils/MapUtils"; +import { is3DVisualizationMode } from "../../../../../../utils/MapTypeUtils"; import { getResource } from '../../../../../../api/persistence'; import '../../../../../../libs/bindings/rxjsRecompose'; import uuidv1 from 'uuid/v1'; @@ -50,7 +51,8 @@ const handleMapSelect = compose( const tileMatrix = extractTileMatrixFromSources(res.sources, l); return {...l, ...tileMatrix}; }) : res.layers; - return {...res, mapInfoControl: true}; // enable identify tool on map widgets + // enable identify tool on map widgets only for 2D maps + return {...res, mapInfoControl: !is3DVisualizationMode(res) }; })) ).then((results)=> onMapSelected({ maps: results })); } diff --git a/web/client/configs/localConfig.json b/web/client/configs/localConfig.json index 815e87f4d4..6c82ac1037 100644 --- a/web/client/configs/localConfig.json +++ b/web/client/configs/localConfig.json @@ -72,9 +72,6 @@ }, "defaultTextAnnotation": "New" }, - "maptype": { - "mapType": "{context.mode === 'desktop' ? 'openlayers' : 'leaflet'}" - }, "catalog": { "default": { "newService": { diff --git a/web/client/epics/__tests__/globeswitcher-test.js b/web/client/epics/__tests__/globeswitcher-test.js deleted file mode 100644 index 78ed545f0d..0000000000 --- a/web/client/epics/__tests__/globeswitcher-test.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017, 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 { toggle3d } from '../../actions/globeswitcher'; -import assign from 'object-assign'; -import { updateRouteOn3dSwitch } from '../globeswitcher'; -import { testEpic } from './epicTestUtils'; -import { MAP_TYPE_CHANGED } from './../../actions/maptype'; - -describe('globeswitcher Epics', () => { - - it('toggle to 3d for context maps', (done) => { - const NUM_ACTIONS = 1; - testEpic(updateRouteOn3dSwitch, NUM_ACTIONS, assign({}, toggle3d(true, "openlayers"), { hash: "/context/3dmap/123" }), actions => { - expect(actions.length).toBe(NUM_ACTIONS); - actions.map((action) => { - switch (action.type) { - case MAP_TYPE_CHANGED: - expect(action.mapType).toBe("cesium"); - break; - default: - expect(true).toBe(false); - - } - }); - done(); - }, { - globeswitcher: { last2dMapType: "openlayers" } - }); - }); - it('toggle from 3d for context maps', (done) => { - const NUM_ACTIONS = 1; - testEpic(updateRouteOn3dSwitch, NUM_ACTIONS, assign({ hash: "/context/3dmap/123" }, toggle3d(false, "cesium")), actions => { - expect(actions.length).toBe(NUM_ACTIONS); - actions.map((action) => { - switch (action.type) { - case MAP_TYPE_CHANGED: - expect(action.mapType).toBe("openlayers"); - break; - default: - expect(true).toBe(false); - - } - }); - done(); - }, { - mapType: { last2dMapType: null } - }); - }); -}); diff --git a/web/client/epics/__tests__/identify-test.js b/web/client/epics/__tests__/identify-test.js index f2c7d0b95c..722d454d1d 100644 --- a/web/client/epics/__tests__/identify-test.js +++ b/web/client/epics/__tests__/identify-test.js @@ -71,8 +71,9 @@ import { import { setControlProperties } from '../../actions/controls'; import { BROWSE_DATA } from '../../actions/layers'; import { configureMap } from '../../actions/config'; -import { changeMapType } from './../../actions/maptype'; +import { changeVisualizationMode } from './../../actions/maptype'; import { FORCE_UPDATE_MAP_LAYOUT } from '../../actions/maplayout'; +import { VisualizationModes } from '../../utils/MapTypeUtils'; const TEST_MAP_STATE = { present: { @@ -1207,12 +1208,12 @@ describe('identify Epics', () => { }; testEpic(setMapTriggerEpic, 1, configureMap(), epicResponse, {}); }); - it('should unregister identifyFloatingTool event if mapType is changed to cesium', (done) => { + it('should unregister identifyFloatingTool event if visualization mode is changed to 3D', (done) => { const epicResponse = actions => { expect(actions[0].type).toBe(UNREGISTER_EVENT_LISTENER); done(); }; - testEpic(setMapTriggerEpic, 1, changeMapType("cesium"), epicResponse, { + testEpic(setMapTriggerEpic, 1, changeVisualizationMode(VisualizationModes._3D), epicResponse, { mapInfo: { configuration: { trigger: "click" diff --git a/web/client/epics/__tests__/maptype-test.js b/web/client/epics/__tests__/maptype-test.js index a2d7adc044..5919a85ea1 100644 --- a/web/client/epics/__tests__/maptype-test.js +++ b/web/client/epics/__tests__/maptype-test.js @@ -7,13 +7,13 @@ */ import expect from 'expect'; -import { localConfigLoaded } from '../../actions/localConfig'; import { onLocationChanged } from 'connected-react-router'; -import { syncMapType, updateLast2dMapTypeOnChangeEvents, restore2DMapTypeOnLocationChange } from '../maptype'; -import { changeMapType, MAP_TYPE_CHANGED, UPDATE_LAST_2D_MAPTYPE } from '../../actions/maptype'; +import { syncMapType, restore2DMapTypeOnLocationChange } from '../maptype'; +import { changeVisualizationMode, VISUALIZATION_MODE_CHANGED } from '../../actions/maptype'; import { testEpic, addTimeoutEpic, TEST_TIMEOUT } from './epicTestUtils'; +import { VisualizationModes } from '../../utils/MapTypeUtils'; const NUM_ACTIONS = 1; @@ -23,22 +23,20 @@ describe('maptype epics', () => { it('test to switch to cesium when passing to 3d mode', (done) => { const STATE = { maptype: { - mapType: "openlayers", - last2dMapType: "openlayers" + mapType: "openlayers" } }; testEpic(syncMapType, NUM_ACTIONS, onLocationChanged({ pathname: "/viewer/cesium/10358" }, "PUSH"), (actions) => { expect(actions.length).toEqual(NUM_ACTIONS); - expect(actions[0].type).toEqual(MAP_TYPE_CHANGED); - expect(actions[0].mapType).toEqual("cesium"); + expect(actions[0].type).toEqual(VISUALIZATION_MODE_CHANGED); + expect(actions[0].visualizationMode).toEqual(VisualizationModes._3D); done(); }, STATE); }); - it('no-op when the URL do not contain maptype', (done) => { // restore of last2d type delegated to another epic + it('no-op when the URL do not contain maptype', (done) => { const STATE = { maptype: { - mapType: "cesium", - last2dMapType: "openlayers" + mapType: "cesium" } }; testEpic(addTimeoutEpic(syncMapType, 20), NUM_ACTIONS, onLocationChanged({ pathname: "/viewer/10358" }, "PUSH"), (actions) => { @@ -47,46 +45,34 @@ describe('maptype epics', () => { }, STATE); }); - it('restore last2d cesium when changing location from a 3d mode', (done) => { + it('restore 2D map when changing location from a 3d mode', (done) => { const STATE_3D = { maptype: { - mapType: "cesium", - last2dMapType: "openlayers" + mapType: "cesium" } }; testEpic(restore2DMapTypeOnLocationChange, NUM_ACTIONS, onLocationChanged({pathname: "/"}, "PUSH" ), (actions) => { expect(actions.length).toEqual(NUM_ACTIONS); - expect(actions[0].type).toEqual(MAP_TYPE_CHANGED); - expect(actions[0].mapType).toEqual("openlayers"); + expect(actions[0].type).toEqual(VISUALIZATION_MODE_CHANGED); + expect(actions[0].visualizationMode).toEqual(VisualizationModes._2D); done(); }, STATE_3D); }); - it('restore default last2d cesium when changing location from a 3d mode', (done) => { - const STATE_3D = { - maptype: { - mapType: "cesium", - last2dMapType: null - } - }; - testEpic(restore2DMapTypeOnLocationChange, NUM_ACTIONS, onLocationChanged({pathname: "/"}, "PUSH" ), (actions) => { - expect(actions.length).toEqual(NUM_ACTIONS); - expect(actions[0].type).toEqual(MAP_TYPE_CHANGED); - expect(actions[0].mapType).toEqual("openlayers"); - done(); - }, STATE_3D); - }); - it('update location when map type changes', (done) => { + it('update location when visualization mode changes', (done) => { const STATE = { maptype: { - mapType: "cesium", - last2dMapType: "openlayers" + mapType: "cesium" + }, + router: { + location: { + pathname: '/viewer/cesium/123' + } } }; - // fake location.hash with hash in action - testEpic(syncMapType, NUM_ACTIONS, {... changeMapType('cesium'), hash: '/viewer/openlayers/123'}, ([a1]) => { + testEpic(syncMapType, NUM_ACTIONS, changeVisualizationMode(VisualizationModes._3D), ([a1]) => { expect(a1.type).toBe("@@router/CALL_HISTORY_METHOD"); - expect(a1.payload.args[0]).toEqual("/viewer/cesium/123"); - expect(a1.payload.method).toEqual("push"); + expect(a1.payload.args[0]).toEqual("/viewer/123"); + expect(a1.payload.method).toEqual("replace"); done(); }, STATE); }); @@ -94,32 +80,17 @@ describe('maptype epics', () => { it('no-op when the URL is one with mapType', (done) => { const STATE = { maptype: { - mapType: "cesium", - last2dMapType: "openlayers" + mapType: "cesium" + }, + router: { + location: { + pathname: '/viewer/123' + } } }; - // fake location.hash with hash in action - testEpic(addTimeoutEpic(syncMapType, 20), NUM_ACTIONS, { ...changeMapType('cesium'), hash: '/viewer/123' }, ([a1]) => { + testEpic(addTimeoutEpic(syncMapType, 20), NUM_ACTIONS, changeVisualizationMode(VisualizationModes._3D), ([a1]) => { expect(a1.type).toBe(TEST_TIMEOUT); done(); }, STATE); }); - it('updateLast2dMapTypeOnChangeEvents', (done) => { - const STATE = { - maptype: { - mapType: "openlayers", - last2dMapType: "openlayers" - } - }; - testEpic(updateLast2dMapTypeOnChangeEvents, 3, [localConfigLoaded(), changeMapType("cesium"), changeMapType("leaflet"), changeMapType("cesium"), changeMapType("openlayers")], - ([a1, a2, a3]) => { - [a1, a2, a3].map((action) => expect(action.type).toEqual(UPDATE_LAST_2D_MAPTYPE)); - expect(a1.mapType).toEqual('openlayers'); - expect(a2.mapType).toEqual('leaflet'); - expect(a3.mapType).toEqual('openlayers'); - done(); - }, STATE); - - }); - }); diff --git a/web/client/epics/__tests__/queryparam-test.js b/web/client/epics/__tests__/queryparam-test.js index 2bd4f35065..929d29dc07 100644 --- a/web/client/epics/__tests__/queryparam-test.js +++ b/web/client/epics/__tests__/queryparam-test.js @@ -13,8 +13,9 @@ import { onMapClickForShareEpic, readQueryParamsOnMapEpic } from '../queryparams'; -import { changeMapView, ZOOM_TO_EXTENT, CHANGE_MAP_VIEW, clickOnMap, initMap } from '../../actions/map'; -import { MAP_TYPE_CHANGED } from '../../actions/maptype'; +import { changeMapView, ZOOM_TO_EXTENT, CHANGE_MAP_VIEW, clickOnMap } from '../../actions/map'; +import { configureMap } from '../../actions/config'; +import { VISUALIZATION_MODE_CHANGED } from '../../actions/maptype'; import { SHOW_NOTIFICATION } from '../../actions/notifications'; import { onLocationChanged } from 'connected-react-router'; import {toggleControl} from "../../actions/controls"; @@ -26,6 +27,7 @@ import { } from "../../actions/search"; import {ADD_LAYERS_FROM_CATALOGS} from "../../actions/catalog"; import {SYNC_CURRENT_BACKGROUND_LAYER} from "../../actions/backgroundselector"; +import { VisualizationModes } from '../../utils/MapTypeUtils'; const center = { x: -74.2, @@ -608,12 +610,12 @@ describe('queryparam epics', () => { const NUMBER_OF_ACTIONS = 2; testEpic(addTimeoutEpic(readQueryParamsOnMapEpic, 10), NUMBER_OF_ACTIONS, [ onLocationChanged({}), - initMap(true) + configureMap() ], (actions) => { expect(actions.length).toBe(NUMBER_OF_ACTIONS); try { - expect(actions[0].type).toBe(MAP_TYPE_CHANGED); - expect(actions[0].mapType).toBe('cesium'); + expect(actions[0].type).toBe(VISUALIZATION_MODE_CHANGED); + expect(actions[0].visualizationMode).toBe(VisualizationModes._3D); done(); } catch (e) { done(e); @@ -641,12 +643,12 @@ describe('queryparam epics', () => { const NUMBER_OF_ACTIONS = 2; testEpic(addTimeoutEpic(readQueryParamsOnMapEpic, 10), NUMBER_OF_ACTIONS, [ onLocationChanged({}), - initMap(true) + configureMap() ], (actions) => { expect(actions.length).toBe(NUMBER_OF_ACTIONS); try { - expect(actions[0].type).toBe(MAP_TYPE_CHANGED); - expect(actions[0].mapType).toBe('cesium'); + expect(actions[0].type).toBe(VISUALIZATION_MODE_CHANGED); + expect(actions[0].visualizationMode).toBe(VisualizationModes._3D); done(); } catch (e) { done(e); @@ -676,7 +678,7 @@ describe('queryparam epics', () => { const NUMBER_OF_ACTIONS = 1; testEpic(addTimeoutEpic(readQueryParamsOnMapEpic, 10), NUMBER_OF_ACTIONS, [ onLocationChanged({}), - initMap(true) + configureMap() ], (actions) => { expect(actions.length).toBe(NUMBER_OF_ACTIONS); try { @@ -701,7 +703,7 @@ describe('queryparam epics', () => { const NUMBER_OF_ACTIONS = 1; testEpic(addTimeoutEpic(readQueryParamsOnMapEpic, 10), NUMBER_OF_ACTIONS, [ onLocationChanged({}), - initMap(true) + configureMap() ], (actions) => { expect(actions.length).toBe(NUMBER_OF_ACTIONS); try { @@ -726,7 +728,7 @@ describe('queryparam epics', () => { const NUMBER_OF_ACTIONS = 1; testEpic(addTimeoutEpic(readQueryParamsOnMapEpic, 10), NUMBER_OF_ACTIONS, [ onLocationChanged({}), - initMap(true) + configureMap() ], (actions) => { expect(actions.length).toBe(NUMBER_OF_ACTIONS); try { diff --git a/web/client/epics/context.js b/web/client/epics/context.js index a659f9ce10..34105594c3 100644 --- a/web/client/epics/context.js +++ b/web/client/epics/context.js @@ -13,11 +13,9 @@ import { getResource, getResourceIdByName, getResourceDataByName } from '../api/ import { pluginsSelectorCreator } from '../selectors/localConfig'; import { isLoggedIn, userSelector } from '../selectors/security'; -import { LOAD_CONTEXT, LOAD_FINISHED, loadContext, loading, setContext, setResource, contextLoadError, loadFinished, - SET_CURRENT_CONTEXT, CONTEXT_LOAD_ERROR } from '../actions/context'; +import { LOAD_CONTEXT, LOAD_FINISHED, loadContext, loading, setContext, setResource, contextLoadError, loadFinished, CONTEXT_LOAD_ERROR } from '../actions/context'; import { clearMapTemplates } from '../actions/maptemplates'; import { loadMapConfig, MAP_CONFIG_LOADED, MAP_CONFIG_LOAD_ERROR } from '../actions/config'; -import { changeMapType } from '../actions/maptype'; import { LOGIN_SUCCESS, LOGOUT } from '../actions/security'; import { loadUserSession, USER_SESSION_LOADED, userSessionStartSaving, setUserSession, saveMapConfig } from '../actions/usersession'; @@ -170,9 +168,6 @@ export const loadContextAndMap = (action$, { getState = () => { } } = {}) => * Handles map type change when context changes * @param {observable} action$ stream of actions */ -export const setMapTypeOnContextChange = action$ => action$ - .ofType(SET_CURRENT_CONTEXT) - .switchMap(({context}) => Observable.of(changeMapType(context && context.mapType || 'openlayers'))); /** * Handles the reload of the context and map. This have to be triggered diff --git a/web/client/epics/createnewmap.js b/web/client/epics/createnewmap.js index 1121bd9eac..aea2edb6a8 100644 --- a/web/client/epics/createnewmap.js +++ b/web/client/epics/createnewmap.js @@ -17,8 +17,6 @@ import { } from '../actions/createnewmap'; import { MAPS_LOAD_MAP } from '../actions/maps'; -import { mapTypeSelector } from '../selectors/maptype'; - import { getResources } from '../api/persistence'; import { wrapStartStop } from '../observables/epics'; @@ -41,14 +39,11 @@ export const checkContextsOnMapLoad = (action$) => action$ )) ); -export const createNewMapEpic = (action$, store) => action$ +export const createNewMapEpic = (action$) => action$ .ofType(CREATE_NEW_MAP) .switchMap(({context}) => { - const state = store.getState(); - const mapType = mapTypeSelector(state); - return Rx.Observable.of( showNewMapDialog(false), - push("/viewer/" + mapType + "/new" + (context ? `/context/${context.id}` : '')) + push("/viewer/new" + (context ? `/context/${context.id}` : '')) ); }); diff --git a/web/client/epics/details.js b/web/client/epics/details.js index 1e09c6d191..26b4322883 100644 --- a/web/client/epics/details.js +++ b/web/client/epics/details.js @@ -15,7 +15,8 @@ import { NO_DETAILS_AVAILABLE, updateDetails, detailsLoaded, - openDetailsPanel + openDetailsPanel, + closeDetailsPanel } from '../actions/details'; import { MAP_INFO_LOADED } from '../actions/config'; import { toggleControl, setControlProperty } from '../actions/controls'; @@ -29,6 +30,7 @@ import GeoStoreApi from '../api/GeoStoreDAO'; import { EMPTY_RESOURCE_VALUE } from '../utils/MapInfoUtils'; import { getIdFromUri } from '../utils/MapUtils'; import { basicError } from '../utils/NotificationUtils'; +import { VISUALIZATION_MODE_CHANGED } from '../actions/maptype'; export const fetchDataForDetailsPanel = (action$, store) => action$.ofType(OPEN_DETAILS_PANEL) @@ -89,3 +91,9 @@ export const storeDetailsInfoEpic = (action$, store) => ); }); }); + +export const closeDetailsPanelOn3DToggle = (action$) => + action$.ofType(VISUALIZATION_MODE_CHANGED) + .switchMap(() => { + return Rx.Observable.of(closeDetailsPanel()); + }); diff --git a/web/client/epics/featuregrid.js b/web/client/epics/featuregrid.js index afa4d1d48a..76ab888cbb 100644 --- a/web/client/epics/featuregrid.js +++ b/web/client/epics/featuregrid.js @@ -168,6 +168,7 @@ import MapUtils from '../utils/MapUtils'; import {dockPanelsSelector} from "../selectors/maplayout"; import {shutdownToolOnAnotherToolDrawing} from "../utils/ControlUtils"; import {mapTypeSelector} from "../selectors/maptype"; +import { MapLibraries } from '../utils/MapTypeUtils'; const setupDrawSupport = (state, original) => { const defaultFeatureProj = getDefaultFeatureProjection(); @@ -1249,7 +1250,7 @@ export const toggleSnappingOffOnFeatureGridViewMode = (action$, { getState } = { export const setViewportFilterEpic = (action$, { getState } = {}) => action$ .ofType(OPEN_FEATURE_GRID, SET_VIEWPORT_FILTER, CHANGE_MAP_VIEW) - .filter(() => isFeatureGridOpen(getState()) && isViewportFilterActive(getState()) && mapTypeSelector(getState()) !== 'cesium') + .filter(() => isFeatureGridOpen(getState()) && isViewportFilterActive(getState()) && mapTypeSelector(getState()) !== MapLibraries.CESIUM) .switchMap(() => { return Rx.Observable.of( updateFilter()); diff --git a/web/client/epics/globeswitcher.js b/web/client/epics/globeswitcher.js deleted file mode 100644 index e9ef4e5cfb..0000000000 --- a/web/client/epics/globeswitcher.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017, 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 Rx from 'rxjs'; -import { TOGGLE_3D } from '../actions/globeswitcher'; -import { changeMapType } from '../actions/maptype'; -import { last2dMapTypeSelector } from './../selectors/maptype'; -import { closeDetailsPanel } from '../actions/details'; -/** - * Gets every `TOGGLE_3D` event. - * @memberof epics.globeswitcher - * @param {external:Observable} action$ manages `TOGGLE_3D`. - * @return {external:Observable} emitting connected-react-router push action and {@link #actions.globeswitcher.updateLast2dMapType} actions - */ -export const updateRouteOn3dSwitch = (action$, store) => - - action$.ofType(TOGGLE_3D) - .switchMap((action) => { - const last2dMapType = last2dMapTypeSelector(store.getState()); - - return Rx.Observable.from([ - changeMapType(action.originalMapType !== "cesium" ? "cesium" : last2dMapType), - closeDetailsPanel() - ]); - }); -/** - * Epics for 3d switcher functionality - * @name epics.globeswitcher - * @type {Object} - */ -export default { - updateRouteOn3dSwitch -}; diff --git a/web/client/epics/identify.js b/web/client/epics/identify.js index 7bf0791eb8..24912b22f8 100644 --- a/web/client/epics/identify.js +++ b/web/client/epics/identify.js @@ -61,7 +61,7 @@ const gridGeometryQuickFilter = state => get(find(getAttributeFilters(state), f const stopFeatureInfo = state => stopGetFeatureInfoSelector(state) || isFeatureGridOpen(state) && (gridEditingSelector(state) || gridGeometryQuickFilter(state)); import {getFeatureInfo} from '../api/identify'; -import { MAP_TYPE_CHANGED } from '../actions/maptype'; +import { VISUALIZATION_MODE_CHANGED } from '../actions/maptype'; import {updatePointWithGeometricFilter} from "../utils/IdentifyUtils"; /** @@ -405,7 +405,7 @@ export const removeMapInfoMarkerOnRemoveMapPopupEpic = (action$, {getState}) => * Sets which trigger to use on the map */ export const setMapTriggerEpic = (action$, store) => - action$.ofType(SET_MAP_TRIGGER, MAP_CONFIG_LOADED, MAP_TYPE_CHANGED) + action$.ofType(SET_MAP_TRIGGER, MAP_CONFIG_LOADED, VISUALIZATION_MODE_CHANGED) .switchMap(() => { return Rx.Observable.of( mapTriggerSelector(store.getState()) === 'hover' ? registerEventListener('mousemove', 'identifyFloatingTool') : unRegisterEventListener('mousemove', 'identifyFloatingTool') diff --git a/web/client/epics/maps.js b/web/client/epics/maps.js index fbb64ad18e..dcd6c975c5 100644 --- a/web/client/epics/maps.js +++ b/web/client/epics/maps.js @@ -36,7 +36,6 @@ import { contextsSelector, searchFilterSelector } from '../selectors/maps'; -import { mapTypeSelector } from '../selectors/maptype'; import { userRoleSelector } from '../selectors/security'; import { LOGIN_SUCCESS, @@ -301,7 +300,7 @@ export const deleteMapAndAssociatedResourcesEpic = (action$, store) => /** * Create or update map resource with persistence api */ -export const mapSaveMapResourceEpic = (action$, store) => +export const mapSaveMapResourceEpic = (action$) => action$.ofType(SAVE_MAP_RESOURCE) .exhaustMap(({resource}) => { // convert to json if attribute is an object @@ -345,7 +344,7 @@ export const mapSaveMapResourceEpic = (action$, store) => // if we got a valid context information redirect to a context, instead of the default viewer push(contextResource ? `/context/${contextResource.name}/${rid}` : - `/viewer/${mapTypeSelector(store.getState())}/${rid}`)] + `/viewer/${rid}`)] : []) ]) .merge( diff --git a/web/client/epics/maptype.js b/web/client/epics/maptype.js index ea368cd73e..ae068908ed 100644 --- a/web/client/epics/maptype.js +++ b/web/client/epics/maptype.js @@ -6,62 +6,70 @@ * LICENSE file in the root directory of this source tree. */ import Rx from 'rxjs'; -import { push, LOCATION_CHANGE } from 'connected-react-router'; - -import { changeMapType, updateLast2dMapType, MAP_TYPE_CHANGED } from '../actions/maptype'; -import { mapTypeSelector, isCesium, last2dMapTypeSelector } from '../selectors/maptype'; -import { findMapType, replaceMapType } from '../utils/MapTypeUtils'; - -import { LOCAL_CONFIG_LOADED } from '../actions/localConfig'; +import { replace, LOCATION_CHANGE } from 'connected-react-router'; +import { MAP_CONFIG_LOADED } from '../actions/config'; +import { changeVisualizationMode, VISUALIZATION_MODE_CHANGED } from '../actions/maptype'; +import { is3DMode } from '../selectors/maptype'; +import { + findMapType, + removeMapType, + VisualizationModes, + getVisualizationModeFromMapLibrary +} from '../utils/MapTypeUtils'; /** * Keep in sync mapType in state with mapType in URL. * @memberof epics.maptype * @param {external:Observable} action$ the stream of actions, acts on `LOCATION_CHANGE` * @param {object} store the store middleware API from redux `createMiddleware` - * @return {external:Observable} the stream of the actions to emit. (`changeMapType`) + * @return {external:Observable} the stream of the actions to emit. (`changeVisualizationMode`) */ export const syncMapType = (action$, store) => Rx.Observable.merge( // when location change and the mapType intercepted in the hash is different, update the map type. // It means that the URL has been used from external link - action$.ofType(LOCATION_CHANGE) - .filter(action => { - const hashMapType = findMapType(action?.payload?.location?.pathname); - return hashMapType && hashMapType !== mapTypeSelector(store.getState()); + action$.ofType(LOCATION_CHANGE, MAP_CONFIG_LOADED) + .filter((action) => { + const isFirstRendering = action?.payload?.isFirstRendering === true; + if (isFirstRendering) { + return false; + } + const state = store.getState(); + const pathname = action?.payload?.location?.pathname || state?.router?.location?.pathname; + const hashMapType = findMapType(pathname); + return !!hashMapType; }) - .switchMap((action) => - Rx.Observable.of(changeMapType(findMapType(action.payload.location.pathname))) - ), + .switchMap((action) => { + const state = store.getState(); + const pathname = action?.payload?.location?.pathname || state?.router?.location?.pathname; + return Rx.Observable.of( + changeVisualizationMode( + getVisualizationModeFromMapLibrary( + findMapType(pathname) + ) + ) + ); + }), // when map type change, if the URL hash matches with one of the URLs that includes the maptype, update it // this when map type is changed using the action or not the URL // NOTE: the action can be used anywhere, even if the URL do not include the mapType. - action$.ofType(MAP_TYPE_CHANGED) - .switchMap((action) => { - const hash = action.hash || location.hash; - const hashMapType = findMapType(hash); - const currentMapType = mapTypeSelector(store.getState()); + action$.ofType(VISUALIZATION_MODE_CHANGED) + .switchMap(() => { + const state = store.getState(); + const pathname = state?.router?.location?.pathname; + const hashMapType = findMapType(pathname); // if the URL hash contains the mapType and it is not in sync with the new path, syncronize - if (hashMapType && hashMapType !== currentMapType) { - const newPath = replaceMapType(hash, currentMapType); + if (hashMapType) { + const newPath = removeMapType(pathname); // in this case the URL change - if (newPath !== hash) { - return Rx.Observable.from([push(newPath)]); + if (newPath !== pathname) { + return Rx.Observable.of(replace(newPath)); } } return Rx.Observable.empty(); }) ); -/** - * Keep track of mapType changes and stores the last mapType used, in order to - */ -export const updateLast2dMapTypeOnChangeEvents = (action$, store) => action$ - .ofType(LOCAL_CONFIG_LOADED) - .map(() => mapTypeSelector(store.getState())) - .merge(action$.ofType(MAP_TYPE_CHANGED).pluck('mapType')) - .filter((mapType) => mapType && mapType !== "cesium") - .switchMap(type => Rx.Observable.of(updateLast2dMapType(type))); /** * Restores last 2D map type when switch to a context where maptype is not @@ -71,9 +79,13 @@ export const restore2DMapTypeOnLocationChange = (action$, store) => { return action$.ofType(LOCATION_CHANGE) // NOTE: this do not conflict with syncMapType LOCATION_CHANGE intercept, they are mutually esclusive // because of the `findMapType` check - .filter(action =>!findMapType(action?.payload?.location?.pathname) && isCesium(store.getState())) + .filter(action => + action?.payload?.action !== 'REPLACE' + && !findMapType(action?.payload?.location?.pathname) + && is3DMode(store.getState()) + ) .switchMap(() => { - return Rx.Observable.of(changeMapType(last2dMapTypeSelector(store.getState()))); + return Rx.Observable.of(changeVisualizationMode(VisualizationModes._2D)); }); }; /** @@ -83,6 +95,5 @@ export const restore2DMapTypeOnLocationChange = (action$, store) => { */ export default { syncMapType, - updateLast2dMapTypeOnChangeEvents, restore2DMapTypeOnLocationChange }; diff --git a/web/client/epics/mapviews.js b/web/client/epics/mapviews.js index bb46441821..6070aabce5 100644 --- a/web/client/epics/mapviews.js +++ b/web/client/epics/mapviews.js @@ -29,7 +29,7 @@ import { } from '../actions/additionallayers'; import { CATALOG_CLOSE } from '../actions/catalog'; import { MAP_CONFIG_LOADED } from '../actions/config'; -import { MAP_TYPE_CHANGED } from '../actions/maptype'; +import { VISUALIZATION_MODE_CHANGED } from '../actions/maptype'; import { getSelectedMapView, getResourceById, @@ -107,7 +107,7 @@ export const updateMapViewsLayers = (action$, store) => HIDE_VIEWS, SETUP_VIEWS, MAP_CONFIG_LOADED, - MAP_TYPE_CHANGED + VISUALIZATION_MODE_CHANGED ) .filter(() => { const state = store.getState(); @@ -119,7 +119,7 @@ export const updateMapViewsLayers = (action$, store) => const currentView = getSelectedMapView(state); const { layers = [], mask = {}, id: viewId } = currentView || {}; const shouldUpdate = !!( - action.type === MAP_TYPE_CHANGED + action.type === VISUALIZATION_MODE_CHANGED || !deepCompare(previousView?.layers || [], layers) || !deepCompare(previousView?.mask || {}, mask) ); diff --git a/web/client/epics/queryparams.js b/web/client/epics/queryparams.js index e16f95c808..178537f936 100644 --- a/web/client/epics/queryparams.js +++ b/web/client/epics/queryparams.js @@ -10,7 +10,8 @@ import * as Rx from 'rxjs'; import {LOCATION_CHANGE} from 'connected-react-router'; import {get, head, isUndefined} from 'lodash'; -import {CHANGE_MAP_VIEW, CLICK_ON_MAP, INIT_MAP} from '../actions/map'; +import { CHANGE_MAP_VIEW, CLICK_ON_MAP } from '../actions/map'; +import { MAP_CONFIG_LOADED } from '../actions/config'; import {addMarker, hideMarker, resetSearch} from '../actions/search'; import {setControlProperty, TOGGLE_CONTROL} from '../actions/controls'; @@ -19,9 +20,10 @@ import {hideMapinfoMarker, purgeMapInfoResults, toggleMapInfoState} from "../act import {clickPointSelector, isMapInfoOpen, mapInfoEnabledSelector} from '../selectors/mapInfo'; import {shareSelector} from "../selectors/controls"; import {LAYER_LOAD} from "../actions/layers"; -import {changeMapType} from '../actions/maptype'; +import { changeVisualizationMode } from '../actions/maptype'; import {getCesiumViewerOptions, getParametersValues, getQueryActions, paramActions} from "../utils/QueryParamsUtils"; import {semaphore} from "../utils/EpicsUtils"; +import { VisualizationModes, MapLibraries } from '../utils/MapTypeUtils'; /** * Intercept on `LOCATION_CHANGE` to get query params from router.location.search string. @@ -45,7 +47,7 @@ export const readQueryParamsOnMapEpic = (action$, store) => { .switchMap(() => { const parameters = getParametersValues(paramActions, store.getState()); return Rx.Observable.merge( - action$.ofType(INIT_MAP) + action$.ofType(MAP_CONFIG_LOADED) .take(1) .switchMap(() => { // On map initialization, query params containing cesium viewer options @@ -53,7 +55,7 @@ export const readQueryParamsOnMapEpic = (action$, store) => { const cesiumViewerOptions = getCesiumViewerOptions(parameters); if (cesiumViewerOptions) { skipProcessing = true; - return Rx.Observable.of(changeMapType('cesium')); + return Rx.Observable.of(changeVisualizationMode(VisualizationModes._3D)); } return Rx.Observable.empty(); }), @@ -69,7 +71,7 @@ export const readQueryParamsOnMapEpic = (action$, store) => { .take(1) .switchMap(() => { const mapType = get(store.getState(), 'maptype.mapType') || ''; - if (mapType === 'cesium') { + if (mapType === MapLibraries.CESIUM) { const queryActions = getQueryActions(parameters, paramActions, store.getState()); return head(queryActions) ? Rx.Observable.of(...queryActions) diff --git a/web/client/epics/tutorial.js b/web/client/epics/tutorial.js index cbb77d7876..7563764dc6 100644 --- a/web/client/epics/tutorial.js +++ b/web/client/epics/tutorial.js @@ -20,7 +20,6 @@ import { import { openDetailsPanel } from '../actions/details'; import { CHANGE_MAP_VIEW } from '../actions/map'; import { MAPS_LIST_LOADED } from '../actions/maps'; -import { TOGGLE_3D } from '../actions/globeswitcher'; import { modeSelector } from '../selectors/geostory'; import { CHANGE_MODE } from '../actions/geostory'; import { creationStepSelector } from '../selectors/contextcreator'; @@ -30,6 +29,7 @@ import { isEmpty, isArray, isObject } from 'lodash'; import { getApi } from '../api/userPersistedStorage'; import { mapSelector } from '../selectors/map'; import {REDUCERS_LOADED} from "../actions/storemanager"; +import { VISUALIZATION_MODE_CHANGED } from '../actions/maptype'; const findTutorialId = path => path.match(/\/(viewer)\/(\w+)\/(\d+)/) && path.replace(/\/(viewer)\/(\w+)\/(\d+)/, "$2") || path.match(/\/(\w+)\/(\d+)/) && path.replace(/\/(\w+)\/(\d+)/, "$1") @@ -44,7 +44,7 @@ const findTutorialId = path => path.match(/\/(viewer)\/(\w+)\/(\d+)/) && path.re export const closeTutorialEpic = (action$) => action$.ofType(START_TUTORIAL) - .audit(() => action$.ofType(TOGGLE_3D)) + .audit(() => action$.ofType(VISUALIZATION_MODE_CHANGED)) .switchMap( () => Rx.Observable.of(closeTutorial())); /** diff --git a/web/client/plugins/BurgerMenu.jsx b/web/client/plugins/BurgerMenu.jsx index 6c923795e3..2be17c5e4a 100644 --- a/web/client/plugins/BurgerMenu.jsx +++ b/web/client/plugins/BurgerMenu.jsx @@ -55,7 +55,6 @@ class BurgerMenu extends React.Component { onInit: PropTypes.func, onDetach: PropTypes.func, controls: PropTypes.object, - mapType: PropTypes.string, panelStyle: PropTypes.object, panelClassName: PropTypes.string }; @@ -71,7 +70,6 @@ class BurgerMenu extends React.Component { onItemClick: () => {}, title: , controls: [], - mapType: "leaflet", panelStyle: { minWidth: "300px", right: "52px", @@ -161,7 +159,6 @@ class BurgerMenu extends React.Component { return ( { - this.context.router.history.push("/viewer/" + this.props.mapType + "/new"); + this.context.router.history.push("/viewer/new"); }; isAllowed = () => this.props.isLoggedIn && this.props.allowedRoles.indexOf(this.props.user && this.props.user.role) >= 0; @@ -149,7 +146,6 @@ export default { CreateNewMapPlugin: connect((state) => ({ loading: loadingSelector(state), loadFlags: loadFlagsSelector(state), - mapType: mapTypeSelector(state), isLoggedIn: state && state.security && state.security.user && state.security.user.enabled && !(state.browser && state.browser.mobile) && true || false, user: state && state.security && state.security.user, hasContexts: hasContextsSelector(state), diff --git a/web/client/plugins/Dashboards.jsx b/web/client/plugins/Dashboards.jsx index ddf918461e..7923dcf16c 100644 --- a/web/client/plugins/Dashboards.jsx +++ b/web/client/plugins/Dashboards.jsx @@ -20,7 +20,6 @@ import dashboards from '../epics/dashboards'; import dashboardsReducers from '../reducers/dashboards'; import { totalCountSelector } from '../selectors/dashboards'; import { isFeaturedMapsEnabled } from '../selectors/featuredmaps'; -import { mapTypeSelector } from '../selectors/maptype'; import { userRoleSelector } from '../selectors/security'; import DashboardGrid from './dashboard/DashboardsGrid'; import EmptyDashboardsView from './dashboard/EmptyDashboardsView'; @@ -46,7 +45,6 @@ const dashboardsCountSelector = createSelector( */ class Dashboards extends React.Component { static propTypes = { - mapType: PropTypes.string, title: PropTypes.any, onMount: PropTypes.func, loadDashboards: PropTypes.func, @@ -65,7 +63,6 @@ class Dashboards extends React.Component { }; static defaultProps = { - mapType: "leaflet", onMount: () => {}, loadDashboards: () => {}, fluid: false, @@ -104,13 +101,11 @@ class Dashboards extends React.Component { } const dashboardsPluginSelector = createSelector([ - mapTypeSelector, state => state.dashboards && state.dashboards.searchText, state => state.dashboards && state.dashboards.results ? state.dashboards.results : [], isFeaturedMapsEnabled, userRoleSelector -], (mapType, searchText, resources, featuredEnabled, role) => ({ - mapType, +], (searchText, resources, featuredEnabled, role) => ({ searchText, resources: resources.map(res => ({...res, featuredEnabled: featuredEnabled && role === 'ADMIN'})) // TODO: remove false to enable featuredEnabled })); diff --git a/web/client/plugins/FeaturedMaps.jsx b/web/client/plugins/FeaturedMaps.jsx index 6c3ff0425f..a4ef9e630d 100644 --- a/web/client/plugins/FeaturedMaps.jsx +++ b/web/client/plugins/FeaturedMaps.jsx @@ -19,7 +19,6 @@ import Message from "../components/I18N/Message"; import mapsEpics from '../epics/maps'; import {userRoleSelector} from '../selectors/security'; import {versionSelector} from '../selectors/version'; -import {mapTypeSelector} from '../selectors/maptype'; import {invalidationSelector, searchTextSelector, isFeaturedMapsEnabled} from '../selectors/featuredmaps'; import {loadPage, updateItemsLifecycle} from '../components/maps/enhancers/featuredMaps'; import gridPagination from '../components/misc/enhancers/gridPagination'; @@ -29,7 +28,6 @@ import MapsGrid from './maps/MapsGrid'; import {scrollIntoViewId} from '../utils/DOMUtil'; import featuredmaps from '../reducers/featuredmaps'; -import maptype from '../reducers/maptype'; import { CONTEXT_DEFAULT_SHARE_OPTIONS, DASHBOARD_DEFAULT_SHARE_OPTIONS, @@ -44,7 +42,6 @@ const PAGE_SIZE = 4; class FeaturedMaps extends React.Component { static propTypes = { - mapType: PropTypes.string, items: PropTypes.array, colProps: PropTypes.object, fluid: PropTypes.bool, @@ -144,7 +141,7 @@ class FeaturedMaps extends React.Component { return { url: res.contextName ? "context/" + res.contextName + "/" + res.id : - "viewer/" + this.props.mapType + "/" + res.id, + "viewer/" + res.id, shareApi: this.props.showAPIShare }; @@ -152,15 +149,13 @@ class FeaturedMaps extends React.Component { } const featuredMapsPluginSelector = createSelector([ - mapTypeSelector, userRoleSelector, state => state.browser && state.browser.mobile, searchTextSelector, invalidationSelector, isFeaturedMapsEnabled, versionSelector -], (mapType, role, isMobile, searchText, invalidate, isFeaturedEnabled, version) => ({ - mapType, +], (role, isMobile, searchText, invalidate, isFeaturedEnabled, version) => ({ role, permission: role === 'ADMIN', pagination: isMobile ? 'virtual-scroll-horizontal' : 'show-more', @@ -222,7 +217,6 @@ const FeaturedMapsPlugin = compose( } }), defaultProps({ - mapType: 'leaflet', onGoToMap: () => {}, fluid: false, mapsOptions: {start: 0, limit: 12}, @@ -281,7 +275,6 @@ export default { ...mapsEpics }, reducers: { - featuredmaps, - maptype + featuredmaps } }; diff --git a/web/client/plugins/GeoStories.jsx b/web/client/plugins/GeoStories.jsx index ed633414ad..d2feba800d 100644 --- a/web/client/plugins/GeoStories.jsx +++ b/web/client/plugins/GeoStories.jsx @@ -20,7 +20,6 @@ import epics from '../epics/geostories'; import geostories from '../reducers/geostories'; import { isFeaturedMapsEnabled } from '../selectors/featuredmaps'; import { totalCountSelector } from '../selectors/geostories'; -import { mapTypeSelector } from '../selectors/maptype'; import { userRoleSelector } from '../selectors/security'; import EmptyGeostoriesView from './geostories/EmptyGeostoriesView'; import GeostoryGrid from './geostories/GeostoriesGrid'; @@ -45,7 +44,6 @@ const geostoriesCountSelector = createSelector( */ class Geostories extends React.Component { static propTypes = { - mapType: PropTypes.string, title: PropTypes.any, onMount: PropTypes.func, loadGeostories: PropTypes.func, @@ -64,7 +62,6 @@ class Geostories extends React.Component { }; static defaultProps = { - mapType: "leaflet", onMount: () => {}, loadGeostories: () => {}, fluid: false, @@ -103,13 +100,11 @@ class Geostories extends React.Component { } const geostoriesPluginSelector = createSelector([ - mapTypeSelector, state => state.geostories && state.geostories.searchText, state => state.geostories && state.geostories.results ? state.geostories.results : [], isFeaturedMapsEnabled, userRoleSelector -], (mapType, searchText, resources, featuredEnabled, role) => ({ - mapType, +], (searchText, resources, featuredEnabled, role) => ({ searchText, resources: resources.map(res => ({...res, featuredEnabled: featuredEnabled && role === 'ADMIN'})) // TODO: remove false to enable featuredEnabled })); diff --git a/web/client/plugins/GlobeViewSwitcher.jsx b/web/client/plugins/GlobeViewSwitcher.jsx index 5d7e527ca6..9a8dee9fea 100644 --- a/web/client/plugins/GlobeViewSwitcher.jsx +++ b/web/client/plugins/GlobeViewSwitcher.jsx @@ -7,12 +7,12 @@ */ import { connect } from 'react-redux'; -import assign from 'object-assign'; -import epics from '../epics/globeswitcher'; -import { toggle3d } from '../actions/globeswitcher'; -import { mapTypeSelector, isCesium } from '../selectors/maptype'; +import { changeVisualizationMode } from '../actions/maptype'; +import { mapTypeSelector } from '../selectors/maptype'; import { createSelector } from 'reselect'; import GlobeViewSwitcherButton from '../components/buttons/GlobeViewSwitcherButton'; +import { VisualizationModes, getVisualizationModeFromMapLibrary } from '../utils/MapTypeUtils'; +import { createPlugin } from '../utils/PluginsUtils'; /** * GlobeViewSwitcher Plugin. A button that toggles to 3d mode @@ -24,19 +24,22 @@ import GlobeViewSwitcherButton from '../components/buttons/GlobeViewSwitcherButt * */ -let globeSelector = createSelector([mapTypeSelector, isCesium], (mapType = "leaflet", cesium) => ({ - active: cesium, - options: { - originalMapType: mapType - } -})); +const globeSelector = createSelector( + [mapTypeSelector], + (mapType) => ({ + active: getVisualizationModeFromMapLibrary(mapType) === VisualizationModes._3D + }) +); const GlobeViewSwitcher = connect(globeSelector, { - onClick: (pressed, options) => toggle3d(pressed, options.originalMapType) + onClick: (pressed) => changeVisualizationMode(pressed ? VisualizationModes._3D : VisualizationModes._2D) })(GlobeViewSwitcherButton); -export default { - GlobeViewSwitcherPlugin: assign(GlobeViewSwitcher, { - disablePluginIf: "{state('featuregridmode') === 'EDIT'}", +export default createPlugin('GlobeViewSwitcher', { + component: GlobeViewSwitcher, + options: { + disablePluginIf: "{state('featuregridmode') === 'EDIT'}" + }, + containers: { Toolbar: { name: '3d', position: 10, @@ -44,6 +47,5 @@ export default { tool: true, priority: 1 } - }), - epics -}; + } +}); diff --git a/web/client/plugins/GridContainer.jsx b/web/client/plugins/GridContainer.jsx index 65f4d85441..3f5457a0c2 100644 --- a/web/client/plugins/GridContainer.jsx +++ b/web/client/plugins/GridContainer.jsx @@ -21,16 +21,14 @@ class GridContainer extends React.Component { className: PropTypes.string, style: PropTypes.object, items: PropTypes.array, - id: PropTypes.string, - mapType: PropTypes.string + id: PropTypes.string }; static defaultProps = { items: [], className: "grid-home-container", style: {}, - id: "mapstore-grid-home", - mapType: "leaflet" + id: "mapstore-grid-home" }; getPanels = () => { @@ -54,7 +52,6 @@ class GridContainer extends React.Component { id={this.props.id} style={this.props.style} className={this.props.className} - mapType={this.props.mapType} container={(props) => ( {props.children.map( (item, idx) => {item})} )} diff --git a/web/client/plugins/Map.jsx b/web/client/plugins/Map.jsx index 5d94b29492..c18cacb3b7 100644 --- a/web/client/plugins/Map.jsx +++ b/web/client/plugins/Map.jsx @@ -33,6 +33,7 @@ import ErrorPanel from '../components/map/ErrorPanel'; import catalog from "../epics/catalog"; import backgroundSelector from "../epics/backgroundselector"; import API from '../api/catalog'; +import { MapLibraries } from '../utils/MapTypeUtils'; /** * The Map plugin allows adding mapping library dependent functionality using support tools. @@ -228,7 +229,7 @@ class MapPlugin extends React.Component { }; static defaultProps = { - mapType: 'leaflet', + mapType: MapLibraries.OPENLAYERS, actions: {}, zoomControl: false, mapLoadingMessage: "map.loading", @@ -241,7 +242,7 @@ class MapPlugin extends React.Component { measurement: {}, locate: {}, scalebar: { - leaflet: { + [MapLibraries.LEAFLET]: { position: "bottomright" } }, diff --git a/web/client/plugins/MapCatalog.jsx b/web/client/plugins/MapCatalog.jsx index 7fc5d1dd5c..dd84e20040 100644 --- a/web/client/plugins/MapCatalog.jsx +++ b/web/client/plugins/MapCatalog.jsx @@ -107,7 +107,7 @@ class MapCatalogComponent extends React.Component { onSave={onSave} getShareUrl={(map) => map.contextName ? `context/${map.contextName}/${map.id}` : - `viewer/${mapType}/${map.id}` + `viewer/${map.id}` } toggleCatalog={() => onToggleControl()} shareApi/> diff --git a/web/client/plugins/MapFooter.jsx b/web/client/plugins/MapFooter.jsx index 62e42e1901..f3ed265cce 100644 --- a/web/client/plugins/MapFooter.jsx +++ b/web/client/plugins/MapFooter.jsx @@ -55,8 +55,7 @@ class MapFooter extends React.Component { items: [], className: "mapstore-map-footer", style: {}, - id: "mapstore-map-footer", - mapType: "leaflet" + id: "mapstore-map-footer" }; getPanels = () => { @@ -81,7 +80,6 @@ class MapFooter extends React.Component {
{props.children}
} toolStyle="primary" activeStyle="default" diff --git a/web/client/plugins/Maps.jsx b/web/client/plugins/Maps.jsx index fd05d47dcb..3d99982645 100644 --- a/web/client/plugins/Maps.jsx +++ b/web/client/plugins/Maps.jsx @@ -18,7 +18,6 @@ import mapsEpics from '../epics/maps'; import {userRoleSelector} from '../selectors/security'; import {versionSelector} from '../selectors/version'; import { totalCountSelector } from '../selectors/maps'; -import { mapTypeSelector } from '../selectors/maptype'; import { isFeaturedMapsEnabled } from '../selectors/featuredmaps'; import emptyState from '../components/misc/enhancers/emptyState'; import {createSelector} from 'reselect'; @@ -31,7 +30,6 @@ import EmptyMaps from './maps/EmptyMaps'; import {loadMaps} from '../actions/maps'; import mapsReducer from '../reducers/maps'; -import maptypeReducer from '../reducers/maptype'; const mapsCountSelector = createSelector( totalCountSelector, @@ -68,7 +66,6 @@ const PaginationToolbar = connect((state) => { class Maps extends React.Component { static propTypes = { - mapType: PropTypes.string, title: PropTypes.any, onGoToMap: PropTypes.func, loadMaps: PropTypes.func, @@ -89,7 +86,6 @@ class Maps extends React.Component { }; static defaultProps = { - mapType: "leaflet", onGoToMap: () => {}, loadMaps: () => {}, fluid: false, @@ -118,10 +114,10 @@ class Maps extends React.Component { if (map.contextName) { this.context.router.history.push("/context/" + map.contextName + "/" + map.id); } else { - this.context.router.history.push("/viewer/" + this.props.mapType + "/" + map.id); + this.context.router.history.push("/viewer/" + map.id); } }} - getShareUrl={(map) => map.contextName ? `context/${map.contextName}/${map.id}` : `viewer/${this.props.mapType}/${map.id}`} + getShareUrl={(map) => map.contextName ? `context/${map.contextName}/${map.id}` : `viewer/${map.id}`} shareApi={this.props.showAPIShare} version={this.props.version} shareToolEnabled={this.props.shareToolEnabled} @@ -131,15 +127,13 @@ class Maps extends React.Component { } const mapsPluginSelector = createSelector([ - mapTypeSelector, state => state.maps && state.maps.searchText, state => state.maps && state.maps.results ? state.maps.results : [], state => state.maps && state.maps.loading, isFeaturedMapsEnabled, userRoleSelector, versionSelector -], (mapType, searchText, maps, loading, featuredEnabled, role, version) => ({ - mapType, +], (searchText, maps, loading, featuredEnabled, role, version) => ({ searchText, version, maps: maps.map(map => ({...map, featuredEnabled: featuredEnabled && role === 'ADMIN'})), @@ -197,7 +191,6 @@ export default { ...mapsEpics }, reducers: { - maps: mapsReducer, - maptype: maptypeReducer + maps: mapsReducer } }; diff --git a/web/client/plugins/Measure.jsx b/web/client/plugins/Measure.jsx index 5312e21ae7..e27988a41a 100644 --- a/web/client/plugins/Measure.jsx +++ b/web/client/plugins/Measure.jsx @@ -39,6 +39,7 @@ import {mapLayoutValuesSelector} from "../selectors/maplayout"; import measurement from '../reducers/measurement'; import measurementEpics from '../epics/measurement'; import { defaultUnitOfMeasure } from '../utils/MeasureUtils'; +import { MapLibraries } from '../utils/MapTypeUtils'; const selector = (state) => { return { @@ -168,7 +169,7 @@ const MeasurePlugin = connect( mapType })) )((props) => { - return props.mapType === 'cesium' + return props.mapType === MapLibraries.CESIUM ? null : ; }); diff --git a/web/client/plugins/OmniBar.jsx b/web/client/plugins/OmniBar.jsx index 55ab4b63bf..03e745cf04 100644 --- a/web/client/plugins/OmniBar.jsx +++ b/web/client/plugins/OmniBar.jsx @@ -11,15 +11,13 @@ import PropTypes from 'prop-types'; import './omnibar/omnibar.css'; import ToolsContainer from './containers/ToolsContainer'; import {createPlugin} from "../utils/PluginsUtils"; - class OmniBar extends React.Component { static propTypes = { className: PropTypes.string, style: PropTypes.object, containerWrapperStyle: PropTypes.object, items: PropTypes.array, - id: PropTypes.string, - mapType: PropTypes.string + id: PropTypes.string }; static defaultProps = { @@ -27,8 +25,7 @@ class OmniBar extends React.Component { className: "navbar-dx shadow", style: {}, containerWrapperStyle: {}, - id: "mapstore-navbar", - mapType: "leaflet" + id: "mapstore-navbar" }; getPanels = () => { @@ -53,7 +50,6 @@ class OmniBar extends React.Component { style={this.props.style} containerWrapperStyle={this.props.containerWrapperStyle} className={this.props.className} - mapType={this.props.mapType} container={(props) =>
{props.children}
} toolStyle="primary" activeStyle="default" diff --git a/web/client/plugins/Print.jsx b/web/client/plugins/Print.jsx index 264dd19242..38a39972f3 100644 --- a/web/client/plugins/Print.jsx +++ b/web/client/plugins/Print.jsx @@ -33,6 +33,7 @@ import { defaultGetZoomForExtent, getResolutions, mapUpdated, dpi2dpu, DEFAULT_S import { isInsideResolutionsLimits } from '../utils/LayersUtils'; import { has, includes } from 'lodash'; import {additionalLayersSelector} from "../selectors/additionallayers"; +import { MapLibraries } from '../utils/MapTypeUtils'; /** * Print plugin. This plugin allows to print current map view. **note**: this plugin requires the **printing module** to work. @@ -304,7 +305,7 @@ export default { getZoomForExtent: defaultGetZoomForExtent, pdfUrl: null, mapWidth: 370, - mapType: "leaflet", + mapType: MapLibraries.OPENLAYERS, minZoom: 1, maxZoom: 23, usePreview: true, diff --git a/web/client/plugins/SidebarMenu.jsx b/web/client/plugins/SidebarMenu.jsx index f35d36f360..d9f8ccee36 100644 --- a/web/client/plugins/SidebarMenu.jsx +++ b/web/client/plugins/SidebarMenu.jsx @@ -35,7 +35,6 @@ class SidebarMenu extends React.Component { style: PropTypes.object, items: PropTypes.array, id: PropTypes.string, - mapType: PropTypes.string, onInit: PropTypes.func, onDetach: PropTypes.func, sidebarWidth: PropTypes.number, @@ -53,7 +52,6 @@ class SidebarMenu extends React.Component { items: [], style: {}, id: "mapstore-sidebar-menu", - mapType: "openlayers", onInit: () => {}, onDetach: () => {}, eventSelector: "onClick", @@ -236,7 +234,6 @@ class SidebarMenu extends React.Component { { ({ height }) => <>{props.children}} toolStyle="tray" activeStyle="primary" diff --git a/web/client/plugins/Toolbar.jsx b/web/client/plugins/Toolbar.jsx index 35cae3d9e4..734eb5438f 100644 --- a/web/client/plugins/Toolbar.jsx +++ b/web/client/plugins/Toolbar.jsx @@ -39,7 +39,6 @@ class Toolbar extends React.Component { static propTypes = { id: PropTypes.string, tools: PropTypes.array, - mapType: PropTypes.string, style: PropTypes.object, panelStyle: PropTypes.object, panelClassName: PropTypes.string, @@ -115,7 +114,6 @@ class Toolbar extends React.Component { return ( { MapUtils.clearHooks(); }); - it('creates a Map plugin with default configuration (leaflet)', (done) => { + it('creates a Map plugin with default configuration', (done) => { const { Plugin } = getPluginForTest(MapPlugin, {map}); ReactDOM.render(, document.getElementById("container")); - setTimeout(() => { - expect(document.getElementById('map')).toExist(); - expect(document.getElementsByClassName('leaflet-container').length).toBe(1); - done(); - }, 200); + waitFor(() => expect(document.querySelector('.mapLoadingMessage')).toBeTruthy()) + .then(() => { + done(); + }) + .catch(done); }); it('creates a Map plugin with specified mapType configuration (openlayers)', (done) => { diff --git a/web/client/plugins/containers/ToolsContainer.jsx b/web/client/plugins/containers/ToolsContainer.jsx index 8b1f0cf5dd..c46a515582 100644 --- a/web/client/plugins/containers/ToolsContainer.jsx +++ b/web/client/plugins/containers/ToolsContainer.jsx @@ -20,6 +20,8 @@ import HelpBadgeComp from '../../components/help/HelpBadge'; import Message from '../../components/I18N/Message'; import OverlayTrigger from '../../components/misc/OverlayTrigger'; import Button from '../../components/misc/Button'; +import { MapLibraries } from '../../utils/MapTypeUtils'; +import { mapTypeSelector } from "../../selectors/maptype"; const HelpBadge = connect((state) => ({ isVisible: state.controls && state.controls.help && state.controls.help.enabled @@ -81,7 +83,7 @@ class ToolsContainer extends React.Component { tools: [], panels: [], tool: Button, - mapType: "leaflet", + mapType: MapLibraries.OPENLAYERS, eventSelector: "onClick", panelStyle: {}, panelClassName: "tools-container-panel", @@ -209,4 +211,6 @@ class ToolsContainer extends React.Component { }; } -export default ToolsContainer; +export default connect(state => ({ + mapType: mapTypeSelector(state) +}))(ToolsContainer); diff --git a/web/client/plugins/manager/Manager.jsx b/web/client/plugins/manager/Manager.jsx index a85f258526..8d98921237 100644 --- a/web/client/plugins/manager/Manager.jsx +++ b/web/client/plugins/manager/Manager.jsx @@ -28,7 +28,6 @@ class Manager extends React.Component { static defaultProps = { items: [], - mapType: "openlayers", selectedTool: "importer", itemSelected: () => {}, navStyle: { diff --git a/web/client/plugins/manager/ManagerMenu.jsx b/web/client/plugins/manager/ManagerMenu.jsx index b4ca186ae6..b57e3d9244 100644 --- a/web/client/plugins/manager/ManagerMenu.jsx +++ b/web/client/plugins/manager/ManagerMenu.jsx @@ -40,7 +40,6 @@ class ManagerMenu extends React.Component { onItemClick: PropTypes.func, itemSelected: PropTypes.func, controls: PropTypes.object, - mapType: PropTypes.string, panelStyle: PropTypes.object, panelClassName: PropTypes.string, enableRulesManager: PropTypes.bool, @@ -80,7 +79,6 @@ class ManagerMenu extends React.Component { itemSelected: () => {}, title: Manager, controls: [], - mapType: "leaflet", panelStyle: { minWidth: "300px", right: "52px", @@ -125,7 +123,6 @@ class ManagerMenu extends React.Component { return ( {this.props.loggedIn && this.props.showCreateButton - ? () : null}
); diff --git a/web/client/plugins/measure/index.js b/web/client/plugins/measure/index.js index 8234bf53a4..8b9ce764fe 100644 --- a/web/client/plugins/measure/index.js +++ b/web/client/plugins/measure/index.js @@ -34,6 +34,7 @@ import { import addI18NProps from '../../components/I18N/enhancers/addI18NProps'; import Loader from '../../components/misc/Loader'; import MeasureToolbar from '../../components/mapcontrols/measure/MeasureToolbar'; +import { MapLibraries } from '../../utils/MapTypeUtils'; // number format localization for measurements const addFormatNumber = addI18NProps(['formatNumber']); @@ -42,9 +43,9 @@ export const MeasureComponent = MeasureComponentComp; export const MeasureDialog = MeasureDialogComp; const measureSupports = { - leaflet: lazy(() => import(/* webpackChunkName: 'supports/leafletMeasure' */ '../../components/map/leaflet/MeasurementSupport')), - openlayers: lazy(() => import(/* webpackChunkName: 'supports/olMeasure' */ '../../components/map/openlayers/MeasurementSupport')), - cesium: lazy(() => import(/* webpackChunkName: 'supports/cesiumMeasure' */ '../../components/map/cesium/MeasurementSupport')) + [MapLibraries.LEAFLET]: lazy(() => import(/* webpackChunkName: 'supports/leafletMeasure' */ '../../components/map/leaflet/MeasurementSupport')), + [MapLibraries.OPENLAYERS]: lazy(() => import(/* webpackChunkName: 'supports/olMeasure' */ '../../components/map/openlayers/MeasurementSupport')), + [MapLibraries.CESIUM]: lazy(() => import(/* webpackChunkName: 'supports/cesiumMeasure' */ '../../components/map/cesium/MeasurementSupport')) }; const MeasureSupportWithFormatNumber = addFormatNumber(({ diff --git a/web/client/product/appConfig.js b/web/client/product/appConfig.js index 86a6e734c7..28f09c16b6 100644 --- a/web/client/product/appConfig.js +++ b/web/client/product/appConfig.js @@ -23,6 +23,10 @@ export default { name: "mapviewer", path: "/viewer/:mapType/:mapId/context/:contextId", component: require('./pages/MapViewer').default + }, { + name: "mapviewer", + path: "/viewer/:mapId/context/:contextId", + component: require('./pages/MapViewer').default }, { name: "mapviewer", path: "/viewer/:mapId", diff --git a/web/client/product/plugins/MapType.jsx b/web/client/product/plugins/MapType.jsx index 282b0ad0a6..3062a8beb6 100644 --- a/web/client/product/plugins/MapType.jsx +++ b/web/client/product/plugins/MapType.jsx @@ -15,6 +15,7 @@ import { changeMapType } from '../../actions/maptype'; import { mapTypeSelector } from '../../selectors/maptype'; import { connect } from 'react-redux'; import assign from 'object-assign'; +import { MapLibraries } from '../../utils/MapTypeUtils'; class MapType extends React.Component { static propTypes = { @@ -26,12 +27,12 @@ class MapType extends React.Component { }; static defaultProps = { - mapType: 'leaflet', + mapType: MapLibraries.LEAFLET, onChangeMapType: () => {}, mapTypes: [ - { key: "leaflet", label: "Leaflet"}, - { key: "openlayers", label: "OpenLayers"}, - { key: "cesium", label: "Cesium"} + { key: MapLibraries.LEAFLET, label: "Leaflet"}, + { key: MapLibraries.OPENLAYERS, label: "OpenLayers"}, + { key: MapLibraries.CESIUM, label: "Cesium"} ] }; diff --git a/web/client/reducers/__tests__/mapInfo-test.js b/web/client/reducers/__tests__/mapInfo-test.js index 13b93da3ef..519820522a 100644 --- a/web/client/reducers/__tests__/mapInfo-test.js +++ b/web/client/reducers/__tests__/mapInfo-test.js @@ -21,9 +21,10 @@ import { setShowInMapPopup, onInitPlugin } from '../../actions/mapInfo'; -import {changeMapType} from '../../actions/maptype'; +import {changeVisualizationMode} from '../../actions/maptype'; import { MAP_CONFIG_LOADED } from '../../actions/config'; +import { VisualizationModes } from '../../utils/MapTypeUtils'; import assign from 'object-assign'; import 'babel-polyfill'; @@ -898,14 +899,14 @@ describe('Test the mapInfo reducer', () => { const state = mapInfo(undefined, action); expect(state.configuration.trigger).toBe('hover'); }); - it('test the result of changeMapType action - MAP_TYPE_CHANGED when passing to cesium', () => { - const action = changeMapType('cesium'); + it('test the result of changeVisualizationMode action - VISUALIZATION_MODE_CHANGED when passing to 3D', () => { + const action = changeVisualizationMode(VisualizationModes._3D); const initialState = {configuration: {}}; const state = mapInfo(initialState, action); expect(state.configuration.trigger).toBe("click"); }); - it('test the result of changeMapType action - MAP_TYPE_CHANGED when passing to 2d maptype', () => { - const action = changeMapType('openlayers'); + it('test the result of changeVisualizationMode action - VISUALIZATION_MODE_CHANGED when passing to 2D', () => { + const action = changeVisualizationMode(VisualizationModes._2D); const initialState = {configuration: {trigger: "click"}}; const state = mapInfo(initialState, action); expect(state.configuration.trigger).toBe("click"); diff --git a/web/client/reducers/__tests__/maptype-test.js b/web/client/reducers/__tests__/maptype-test.js index 96e49dc87f..439d030458 100644 --- a/web/client/reducers/__tests__/maptype-test.js +++ b/web/client/reducers/__tests__/maptype-test.js @@ -8,7 +8,7 @@ import expect from 'expect'; import maptype from '../maptype'; -import { changeMapType, updateLast2dMapType } from '../../actions/maptype'; +import { changeMapType } from '../../actions/maptype'; import { mapPluginLoad } from '../../actions/map'; describe('Test the maptype reducer', () => { @@ -16,19 +16,7 @@ describe('Test the maptype reducer', () => { const state = maptype(undefined, changeMapType("leaflet")); expect(state.mapType).toBe('leaflet'); }); - it('check to store last 2d map type', () => { - const state = maptype(undefined, changeMapType("leaflet")); - expect(state.last2dMapType).toBe('leaflet'); - const state2 = maptype(state, changeMapType("cesium")); - expect(state2.last2dMapType).toBe('leaflet'); - - const state3 = maptype(state2, updateLast2dMapType("openlayers")); - expect(state3.last2dMapType).toBe('openlayers'); - const state4 = maptype(state3, { type: "UNKNOWN" }); - expect(state4.last2dMapType).toBe('openlayers'); - - }); - it('check to store last 2d map type', () => { + it('check mapPluginLoad', () => { let state = maptype(undefined, mapPluginLoad(false, "leaflet", true, "errorMap")); expect(state.loaded).toEqual({"leaflet": true}); state = maptype({loaded: {"leaflet": false}}, mapPluginLoad(false, "openlayers", true, "errorMap")); diff --git a/web/client/reducers/config.js b/web/client/reducers/config.js index 6f7b9399c1..8119591743 100644 --- a/web/client/reducers/config.js +++ b/web/client/reducers/config.js @@ -19,11 +19,16 @@ import { import { MAP_CREATED } from '../actions/maps'; import { DETAILS_LOADED } from '../actions/details'; +import { MAP_TYPE_CHANGED, VISUALIZATION_MODE_CHANGED } from '../actions/maptype'; import assign from 'object-assign'; import ConfigUtils from '../utils/ConfigUtils'; import { set, unset } from '../utils/ImmutableUtils'; import { transformLineToArcs } from '../utils/CoordinatesUtils'; import { findIndex, castArray } from 'lodash'; +import { + getVisualizationModeFromMapLibrary, + VisualizationModes +} from '../utils/MapTypeUtils'; function mapConfig(state = null, action) { let map; @@ -139,6 +144,14 @@ function mapConfig(state = null, action) { map = state?.map?.present || state?.map; map = unset('mapSaveErrors', map); return {...state, map}; + case VISUALIZATION_MODE_CHANGED: + case MAP_TYPE_CHANGED: + map = state && state.map && state.map.present ? state.map.present : state && state.map; + const visualizationMode = action.mapType !== undefined + ? getVisualizationModeFromMapLibrary(action.mapType) + : action.visualizationMode || VisualizationModes._2D; + map = set('visualizationMode', visualizationMode, map); + return { ...state, map }; default: return state; } diff --git a/web/client/reducers/mapInfo.js b/web/client/reducers/mapInfo.js index c631b01d49..dfa78f570e 100644 --- a/web/client/reducers/mapInfo.js +++ b/web/client/reducers/mapInfo.js @@ -42,9 +42,10 @@ import { import { MAP_CONFIG_LOADED } from '../actions/config'; import { RESET_CONTROLS } from '../actions/controls'; -import { MAP_TYPE_CHANGED } from '../actions/maptype'; +import { VISUALIZATION_MODE_CHANGED } from '../actions/maptype'; import { getValidator } from '../utils/MapInfoUtils'; +import { VisualizationModes } from '../utils/MapTypeUtils'; /** * Identifies when to update a index when the display information trigger is click (GFI panel) @@ -470,8 +471,8 @@ function mapInfo(state = initState, action) { showInMapPopup: action.value // this is global, actually not saved in map configuration (configuration part) }; } - case MAP_TYPE_CHANGED: { - if (action.mapType === "cesium") { + case VISUALIZATION_MODE_CHANGED: { + if (action.visualizationMode === VisualizationModes._3D) { return { ...state, configuration: { diff --git a/web/client/reducers/maptype.js b/web/client/reducers/maptype.js index b5601a8c33..91490449a0 100644 --- a/web/client/reducers/maptype.js +++ b/web/client/reducers/maptype.js @@ -6,9 +6,14 @@ * LICENSE file in the root directory of this source tree. */ -import { MAP_TYPE_CHANGED, UPDATE_LAST_2D_MAPTYPE } from '../actions/maptype'; +import { MAP_TYPE_CHANGED, VISUALIZATION_MODE_CHANGED } from '../actions/maptype'; import { MAP_PLUGIN_LOAD } from '../actions/map'; +import { MAP_CONFIG_LOADED } from '../actions/config'; +import { + getMapLibraryFromVisualizationMode, + VisualizationModes +} from '../utils/MapTypeUtils'; /** * stores state for the mapType to use (typically one of leaflet, openlayers, cesium... ) @@ -21,26 +26,26 @@ import { MAP_PLUGIN_LOAD } from '../actions/map'; * mapType: "leaflet" * } */ -function maptype(state = { mapType: "leaflet" }, action) { +function maptype(state = { + mapType: getMapLibraryFromVisualizationMode(VisualizationModes._2D) +}, action) { switch (action.type) { + case MAP_CONFIG_LOADED: + const visualizationMode = action?.config?.map?.visualizationMode || VisualizationModes._2D; + return { + ...state, + mapType: getMapLibraryFromVisualizationMode(visualizationMode) + }; case MAP_TYPE_CHANGED: return { ...state, - mapType: action.mapType, - last2dMapType: action.mapType && action.mapType !== "cesium" - ? action.mapType - : state.mapType !== 'cesium' - ? state.mapType - : state.last2dMapType + mapType: action.mapType + }; + case VISUALIZATION_MODE_CHANGED: + return { + ...state, + mapType: getMapLibraryFromVisualizationMode(action.visualizationMode) }; - case UPDATE_LAST_2D_MAPTYPE: - if (action.mapType && action.mapType !== "cesium" && action.mapType !== state.last2dMapType) { - return { - ...state, - last2dMapType: action.mapType - }; - } - return state; case MAP_PLUGIN_LOAD: return { ...state, diff --git a/web/client/selectors/__tests__/maptype-test.js b/web/client/selectors/__tests__/maptype-test.js index f3ca64a7cc..8e0c6dfc29 100644 --- a/web/client/selectors/__tests__/maptype-test.js +++ b/web/client/selectors/__tests__/maptype-test.js @@ -15,72 +15,109 @@ import { isOpenlayers, isLeaflet, mapTypeLoadedSelector, - last2dMapTypeSelector + visualizationModeSelector, + is3DMode, + is2DMode } from '../maptype'; +import { MapLibraries } from '../../utils/MapTypeUtils'; + describe('Test maptype', () => { it('test mapTypeSelector default', () => { const mapType = mapTypeSelector({}); expect(mapType).toExist(); - expect(mapType).toBe("leaflet"); + expect(mapType).toBe(MapLibraries.OPENLAYERS); }); it('test mapTypeSelector', () => { - const mapType = mapTypeSelector({maptype: {mapType: "cesium"}}); + const mapType = mapTypeSelector({maptype: {mapType: MapLibraries.CESIUM}}); expect(mapType).toExist(); - expect(mapType).toBe("cesium"); + expect(mapType).toBe(MapLibraries.CESIUM); }); it('test isCesium', () => { - const bool = isCesium({maptype: {mapType: "cesium"}}); + const bool = isCesium({maptype: {mapType: MapLibraries.CESIUM}}); expect(bool).toExist(); expect(bool).toBe(true); - expect(isCesium({maptype: {mapType: "leaflet"}})).toBe(false); + expect(isCesium({maptype: {mapType: MapLibraries.LEAFLET}})).toBe(false); }); it('test isLeaflet', () => { - const bool = isLeaflet({maptype: {mapType: "leaflet"}}); + const bool = isLeaflet({maptype: {mapType: MapLibraries.LEAFLET}}); expect(bool).toExist(); expect(bool).toBe(true); - expect(isLeaflet({maptype: {mapType: "cesium"}})).toBe(false); + expect(isLeaflet({maptype: {mapType: MapLibraries.CESIUM}})).toBe(false); }); it('test isOpenlayers', () => { - const bool = isOpenlayers({maptype: {mapType: "openlayers"}}); + const bool = isOpenlayers({maptype: {mapType: MapLibraries.OPENLAYERS}}); expect(bool).toExist(); expect(bool).toBe(true); - expect(isOpenlayers({maptype: {mapType: "cesium"}})).toBe(false); + expect(isOpenlayers({maptype: {mapType: MapLibraries.CESIUM}})).toBe(false); }); it('test mapTypeLoadedSelector', () => { const state = mapTypeLoadedSelector({ maptype: { - mapType: "openlayers", + mapType: MapLibraries.OPENLAYERS, loaded: { - "openlayers": true + [MapLibraries.OPENLAYERS]: true } } }); expect(state).toExist(); - expect(state).toEqual({"openlayers": true}); + expect(state).toEqual({[MapLibraries.OPENLAYERS]: true}); }); - it('test default last2dMapTypeSelector', () => { - const state = last2dMapTypeSelector({ + it('test visualizationModeSelector', () => { + expect(visualizationModeSelector({ maptype: { - last2dMapType: null + mapType: MapLibraries.OPENLAYERS } - }); - expect(state).toExist(); - expect(state).toEqual("openlayers"); + })).toBe("2D"); + expect(visualizationModeSelector({ + maptype: { + mapType: MapLibraries.LEAFLET + } + })).toBe("2D"); + expect(visualizationModeSelector({ + maptype: { + mapType: MapLibraries.CESIUM + } + })).toBe("3D"); }); - it('test last2dMapTypeSelector', () => { - const state = last2dMapTypeSelector({ + it('test is2DMode', () => { + expect(is2DMode({ maptype: { - last2dMapType: "leaflet" + mapType: MapLibraries.OPENLAYERS } - }); - expect(state).toExist(); - expect(state).toEqual("leaflet"); + })).toBe(true); + expect(is2DMode({ + maptype: { + mapType: MapLibraries.LEAFLET + } + })).toBe(true); + expect(is2DMode({ + maptype: { + mapType: MapLibraries.CESIUM + } + })).toBe(false); + }); + it('test is3DMode', () => { + expect(is3DMode({ + maptype: { + mapType: MapLibraries.OPENLAYERS + } + })).toBe(false); + expect(is3DMode({ + maptype: { + mapType: MapLibraries.LEAFLET + } + })).toBe(false); + expect(is3DMode({ + maptype: { + mapType: MapLibraries.CESIUM + } + })).toBe(true); }); }); diff --git a/web/client/selectors/featuregrid.js b/web/client/selectors/featuregrid.js index 896ae230c6..f3055453be 100644 --- a/web/client/selectors/featuregrid.js +++ b/web/client/selectors/featuregrid.js @@ -21,7 +21,7 @@ import {createShallowSelectorCreator} from "../utils/ReselectUtils"; import isEqual from "lodash/isEqual"; import {mapBboxSelector, projectionSelector} from "./map"; import {bboxToFeatureGeometry} from "../utils/CoordinatesUtils"; - +import { MapLibraries } from '../utils/MapTypeUtils'; export const getLayerById = getLayerFromId; export const getTitle = (layer = {}) => layer.title || layer.name; @@ -193,7 +193,7 @@ export const useLayerFilterSelector = state => get(state, "featuregrid.useLayerF export const isViewportFilterActive = state => get(state, 'featuregrid.viewportFilter', null); -export const isFilterByViewportSupported = state => mapTypeSelector(state) !== 'cesium'; +export const isFilterByViewportSupported = state => mapTypeSelector(state) !== MapLibraries.CESIUM; export const viewportFilter = createShallowSelectorCreator(isEqual)( isViewportFilterActive, diff --git a/web/client/selectors/globeswitcher.js b/web/client/selectors/globeswitcher.js deleted file mode 100644 index 19df518711..0000000000 --- a/web/client/selectors/globeswitcher.js +++ /dev/null @@ -1,3 +0,0 @@ -import {get} from 'lodash'; - -export const last2dMapTypeSelector = state => get(state, "globeswitcher.last2dMapType") || 'leaflet'; diff --git a/web/client/selectors/mapcatalog.js b/web/client/selectors/mapcatalog.js index 2f0e50d96d..7a6a985ad5 100644 --- a/web/client/selectors/mapcatalog.js +++ b/web/client/selectors/mapcatalog.js @@ -5,16 +5,16 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ -import { mapTypeSelector as mtSelector, isCesium, last2dMapTypeSelector } from '../selectors/maptype'; -import {get} from "lodash"; +import { mapTypeSelector as mtSelector, is3DMode } from '../selectors/maptype'; +import { get } from "lodash"; +import { getMapLibraryFromVisualizationMode, VisualizationModes } from '../utils/MapTypeUtils'; export const isActiveSelector = (state) => get(state, "controls.mapCatalog.enabled"); export const triggerReloadValueSelector = state => state.mapcatalog?.triggerReloadValue; export const filterReloadDelaySelector = state => state.mapcatalog?.filterReloadDelay; export const mapTypeSelector = state => { - if (isCesium(state)) { - return last2dMapTypeSelector(state); + if (is3DMode(state)) { + return getMapLibraryFromVisualizationMode(VisualizationModes._2D); } return mtSelector(state); }; - diff --git a/web/client/selectors/maptype.js b/web/client/selectors/maptype.js index 5306471fb7..96c841ae92 100644 --- a/web/client/selectors/maptype.js +++ b/web/client/selectors/maptype.js @@ -5,7 +5,12 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ -import { get } from 'lodash'; + +import { + MapLibraries, + getVisualizationModeFromMapLibrary, + VisualizationModes +} from '../utils/MapTypeUtils'; /** * selects maptype state @@ -20,8 +25,10 @@ import { get } from 'lodash'; * @param {object} state the state * @return {string} the maptype in the state */ -export const mapTypeSelector = (state) => state && state.maptype && state.maptype.mapType || 'leaflet'; -export const mapTypeLoadedSelector = (state) => state && state.maptype && state.maptype.loaded; +export const mapTypeSelector = (state) => state?.maptype?.mapType || MapLibraries.OPENLAYERS; +export const mapTypeLoadedSelector = (state) => state?.maptype?.loaded; + +export const visualizationModeSelector = (state) => getVisualizationModeFromMapLibrary(mapTypeSelector(state)); /** * Check if the mapType is cesium @@ -30,8 +37,10 @@ export const mapTypeLoadedSelector = (state) => state && state.maptype && state. * @param {object} state the state * @return {boolean} */ -export const isCesium = state => mapTypeSelector(state) === "cesium"; -export const isLeaflet = state => mapTypeSelector(state) === "leaflet"; -export const isOpenlayers = state => mapTypeSelector(state) === "openlayers"; +export const isCesium = state => mapTypeSelector(state) === MapLibraries.CESIUM; +export const isLeaflet = state => mapTypeSelector(state) === MapLibraries.LEAFLET; +export const isOpenlayers = state => mapTypeSelector(state) === MapLibraries.OPENLAYERS; + -export const last2dMapTypeSelector = state => get(state, "maptype.last2dMapType") || 'openlayers'; +export const is3DMode = state => visualizationModeSelector(state) === VisualizationModes._3D; +export const is2DMode = state => visualizationModeSelector(state) === VisualizationModes._2D; diff --git a/web/client/stores/StandardStore.js b/web/client/stores/StandardStore.js index f21c9f4708..6eba832266 100644 --- a/web/client/stores/StandardStore.js +++ b/web/client/stores/StandardStore.js @@ -16,9 +16,6 @@ import localConfig from '../reducers/localConfig'; import locale from '../reducers/locale'; import browser from '../reducers/browser'; import { getApi } from '../api/userPersistedStorage'; -import url from "url"; -import { findMapType } from '../utils/MapTypeUtils'; -import { set } from '../utils/ImmutableUtils'; import {getPlugins} from "../utils/ModulePluginsUtils"; const standardEpics = {}; @@ -55,12 +52,6 @@ const appStore = ( const allReducers = storeManager.reduce; const optsState = storeOpts.initialState || { defaultState: {}, mobile: {} }; let defaultState = { ...initialState.defaultState, ...optsState.defaultState }; - const urlData = url.parse(window.location.href, true); - const mapType = findMapType(urlData.href); - if (mapType) { - defaultState = set("maptype.mapType", mapType, defaultState); - } - const mobileOverride = { ...initialState.mobile, ...optsState.mobile }; const rootReducer = (state, action) => { return rootReducerFunc({ diff --git a/web/client/stores/__tests__/StandardStore-test.js b/web/client/stores/__tests__/StandardStore-test.js index c96c942c35..7c5f277c18 100644 --- a/web/client/stores/__tests__/StandardStore-test.js +++ b/web/client/stores/__tests__/StandardStore-test.js @@ -15,8 +15,6 @@ import {LOAD_MAP_CONFIG} from "../../actions/config"; import mapInfoReducers from "../../reducers/mapInfo"; import browserReducers from "../../reducers/browser"; import { CHANGE_BROWSER_PROPERTIES } from '../../actions/browser'; -import MapType from '../../product/plugins/MapType'; - describe('Test StandardStore', () => { it('storeOpts notify is true by default', () => { @@ -47,52 +45,6 @@ describe('Test StandardStore', () => { type: LOAD_MAP_CONFIG }); }); - it("tests applying the maptype reducer and an override from config", () => { - // this tests is valid also for when in the url there is not maptype - const store = createStore({ - initialState: { - defaultState: {}, - mobile: {} - } - }, { - MapTypePlugin: MapType - }, { - initialState: { - defaultState: { - maptype: { - mapType: "openlayers" - } - }, - mobile: {} - } - }); - const maptype = store.getState().maptype.mapType; - expect(maptype).toBe("openlayers"); - }); - it("tests applying the maptype reducer and an override from config", () => { - const oldHash = window.location.hash; - window.location.hash = "#/viewer/leaflet/1"; - const store = createStore({ - initialState: { - defaultState: {}, - mobile: {} - } - }, { - MapTypePlugin: MapType - }, { - initialState: { - defaultState: { - maptype: { - mapType: "openlayers" - } - }, - mobile: {} - } - }); - const maptype = store.getState().maptype.mapType; - expect(maptype).toBe("leaflet"); - window.location.hash = oldHash; - }); it("tests that mobile overrides don't merge on desktop", () => { const store = createStore({ initialState: { diff --git a/web/client/themes/default/less/widget.less b/web/client/themes/default/less/widget.less index 69066d3eda..57740b31cc 100644 --- a/web/client/themes/default/less/widget.less +++ b/web/client/themes/default/less/widget.less @@ -348,6 +348,7 @@ } } } + } .mapstore-widget-card.single-widget { @@ -360,6 +361,15 @@ } } +.ms-wizard.map-options, +.map-widget-view { + .cesium-widget-cesiumNavigationContainer { + position: absolute; + right: 0; + top: -90px; + } +} + .legend-widget { .setFontSize(@font-size-small); font-size: @fSize; diff --git a/web/client/utils/MapTypeUtils.js b/web/client/utils/MapTypeUtils.js index 9df4d5d891..c1e2456a05 100644 --- a/web/client/utils/MapTypeUtils.js +++ b/web/client/utils/MapTypeUtils.js @@ -1,4 +1,74 @@ -export const viewerMapRegex = /(\/viewer\/)(\w+)(\/\w+)/; +import trimEnd from 'lodash/trimEnd'; +import { getBrowserProperties, getConfigProp } from './ConfigUtils'; + +export const MapLibraries = { + LEAFLET: 'leaflet', + OPENLAYERS: 'openlayers', + CESIUM: 'cesium' +}; + +export const VisualizationModes = { + _2D: '2D', + _3D: '3D' +}; + +const mapLibrariesConfiguration = { + [MapLibraries.LEAFLET]: { + visualizationMode: VisualizationModes._2D + }, + [MapLibraries.OPENLAYERS]: { + visualizationMode: VisualizationModes._2D + }, + [MapLibraries.CESIUM]: { + visualizationMode: VisualizationModes._3D + } +}; + +const DEFAULT_MAP_TYPE_CONFIG = { + [VisualizationModes._2D]: { + mobile: MapLibraries.LEAFLET, + desktop: MapLibraries.OPENLAYERS + }, + [VisualizationModes._3D]: { + mobile: MapLibraries.CESIUM, + desktop: MapLibraries.CESIUM + } +}; + +/** + * Check if a map configuration is in 3D mode + * @param {object} map map configuration + * @returns {boolean} + */ +export const is3DVisualizationMode = (map) => map?.visualizationMode === VisualizationModes._3D; + +/** + * Return the visualization mode given a map library + * @param {string} mapLibrary the name of the map library, one of "leaflet", "openlayers" or "cesium" + * @returns {string} "2D" or "3D" value + */ +export const getVisualizationModeFromMapLibrary = (mapLibrary) => { + const { visualizationMode = VisualizationModes._2D } = mapLibrariesConfiguration[mapLibrary] || {}; + return visualizationMode; +}; + +/** + * Return the map library given a visualization mode + * @param {string} visualizationMode the name of the visualization mode, one of "2D" or "3D" + * @returns {string} leaflet", "openlayers" or "cesium" value + */ +export const getMapLibraryFromVisualizationMode = (visualizationMode = VisualizationModes._2D) => { + const { mobile } = getBrowserProperties(); + const customMapTypeConfig = getConfigProp('mapType') || {}; + const config = { + ...DEFAULT_MAP_TYPE_CONFIG[visualizationMode], + ...customMapTypeConfig[visualizationMode] + }; + const device = mobile ? 'mobile' : 'desktop'; + return config[device]; +}; + +export const viewerMapRegex = /(\/viewer\/)(\w+)(\/\w+\b(? { diff --git a/web/client/utils/VendorParamsUtils.js b/web/client/utils/VendorParamsUtils.js index 80822e68ae..a0597ed3d4 100644 --- a/web/client/utils/VendorParamsUtils.js +++ b/web/client/utils/VendorParamsUtils.js @@ -31,8 +31,15 @@ export const optionsToVendorParams = (options = {}, extraCQLFilter = null) => { } else { CQL_FILTER = cqlFilters.pop(); } + // remove params with undefined value + const params = options?.params && Object.keys(options.params) + .reduce((acc, key) => + options.params[key] === undefined + ? acc + : { ...acc, [key]: options.params[key] }, + {}); return CQL_FILTER ? { - ...options.params, + ...params, CQL_FILTER - } : options.params; + } : params; }; diff --git a/web/client/utils/__tests__/MapTypeUtils-test.js b/web/client/utils/__tests__/MapTypeUtils-test.js index 8cceeac4f0..46812dccf0 100644 --- a/web/client/utils/__tests__/MapTypeUtils-test.js +++ b/web/client/utils/__tests__/MapTypeUtils-test.js @@ -7,7 +7,13 @@ */ import expect from 'expect'; -import { findMapType, replaceMapType } from '../MapTypeUtils'; +import { + findMapType, + replaceMapType, + removeMapType, + getVisualizationModeFromMapLibrary, + getMapLibraryFromVisualizationMode +} from '../MapTypeUtils'; const testHashes = [ ["/viewer/openlayers/1234", "openlayers"], @@ -85,5 +91,44 @@ describe('MapTypeUtils', () => { expect(replaceMapType(url, newMapType)).toBe(expected); }); }); - + it('testing removeMapType with viewer regex', () => { + const urls = [ + { + url: "/viewer/openlayers/123", + expected: "/viewer/123" + }, + { + url: "/viewer/openlayers/new/context/123", + expected: "/viewer/new/context/123" + }, + { + url: "/viewer/cesium/123", + expected: "/viewer/123" + }, + { + url: "/viewer/10/context/123", + expected: "/viewer/10/context/123" + }, + { + url: "/viewer/123", + expected: "/viewer/123" + }, + { + url: "/context/ctxName/123", + expected: "/context/ctxName/123" + } + ]; + urls.forEach(({ url, expected }) => { + expect(removeMapType(url)).toBe(expected); + }); + }); + it('testing getVisualizationModeFromMapLibrary', () => { + expect(getVisualizationModeFromMapLibrary('leaflet')).toBe('2D'); + expect(getVisualizationModeFromMapLibrary('openlayers')).toBe('2D'); + expect(getVisualizationModeFromMapLibrary('cesium')).toBe('3D'); + }); + it('testing getMapLibraryFromVisualizationMode (default)', () => { + expect(getMapLibraryFromVisualizationMode('2D')).toBe('openlayers'); + expect(getMapLibraryFromVisualizationMode('3D')).toBe('cesium'); + }); });