diff --git a/build/createExtensionWebpackConfig.js b/build/createExtensionWebpackConfig.js index fbab8b2231..c83af1fb23 100644 --- a/build/createExtensionWebpackConfig.js +++ b/build/createExtensionWebpackConfig.js @@ -35,6 +35,9 @@ module.exports = ({ prod = true, name, exposes, sharedLibrariesEager = true, ali resolve: { fallback: { path: false, + http: false, + https: false, + zlib: false, timers: false, stream: false }, diff --git a/docs/user-guide/catalog.md b/docs/user-guide/catalog.md index 2da7639d9c..ed45266c32 100644 --- a/docs/user-guide/catalog.md +++ b/docs/user-guide/catalog.md @@ -340,3 +340,9 @@ MapStore allows to publish 3D Tiles contents in its 3D mode on top of the [Cesiu In **general settings of** 3D Tiles service, the user can specify the title to assign to this service and the URL of the service. + +!!! warning + MapStore allows you to load also [Google Photorealistic 3D Tiles](https://cloud.google.com/blog/products/maps-platform/create-immersive-3d-map-experiences-photorealistic-3d-tiles) and some constraints need to be respected in this case. + Since the Google Photorealistic 3D Tiles are not ‘survey-grade’ at this time, the use of certain MapStore tools could be considered derivative and, for this reason, prohibited. Please, make sure you have read the [Google conditions of use](https://developers.google.com/maps/documentation/tile/policies) + (some [FAQs](https://cloud.google.com/blog/products/maps-platform/commonly-asked-questions-about-our-recently-launched-photorealistic-3d-tiles) are also available online for this purpose) before providing Google Photorealistic 3D Tile in your MapStore maps in order to enable only allowed tools (e.g. *Measurement* and *Identify* tools should be probably disabled). + For this purpose it is possible to appropriately set the [configuration of MapStore plugins](../../developer-guide/maps-configuration/#map-options) to exclude tools that could conflict with Google policies. Alternatively, it is possible to use a dedicated [application context](application-context.md#configure-plugins) to show Photorealistic 3D Tiles by including only the permitted tools within it. diff --git a/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx b/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx index 80c38f97d3..e51683cf7c 100644 --- a/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx +++ b/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx @@ -95,7 +95,7 @@ function SelectInput({ function VisibilityLimitsForm({ title, layer, - zoom, + zoom: zoomProp, projection, resolutions = getResolutions(), defaultLimitsType, @@ -104,6 +104,8 @@ function VisibilityLimitsForm({ onChange }) { + const zoom = Math.round(zoomProp || 0); + const [limitsType, setLimitsType] = useState(defaultLimitsType || limitsTypesOptions[0].value); const { diff --git a/web/client/components/data/featuregrid/formatters/__tests__/index-test.jsx b/web/client/components/data/featuregrid/formatters/__tests__/index-test.jsx index f65164baf9..5e0a27287e 100644 --- a/web/client/components/data/featuregrid/formatters/__tests__/index-test.jsx +++ b/web/client/components/data/featuregrid/formatters/__tests__/index-test.jsx @@ -5,118 +5,178 @@ * 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 ReactDOM from 'react-dom'; -import expect from 'expect'; +import React from "react"; +import ReactDOM from "react-dom"; +import expect from "expect"; -import NumberFormat from '../../../../I18N/Number'; -import {getFormatter, registerFormatter, unregisterFormatter} from '../index'; +import NumberFormat from "../../../../I18N/Number"; +import { getFormatter, registerFormatter, unregisterFormatter } from "../index"; -describe('Tests for the formatter functions', () => { - it('test getFormatter for booleans', () => { - const formatter = getFormatter({localType: "boolean"}); +describe("Tests for the formatter functions", () => { + it("test getFormatter for booleans", () => { + const formatter = getFormatter({ localType: "boolean" }); expect(typeof formatter).toBe("function"); expect(formatter()).toBe(null); - expect(formatter({value: true}).type).toBe("span"); - expect(formatter({value: true}).props.children).toBe("true"); - expect(formatter({value: false}).props.children).toBe("false"); - expect(formatter({value: null})).toBe(null); - expect(formatter({value: undefined})).toBe(null); + expect(formatter({ value: true }).type).toBe("span"); + expect(formatter({ value: true }).props.children).toBe("true"); + expect(formatter({ value: false }).props.children).toBe("false"); + expect(formatter({ value: null })).toBe(null); + expect(formatter({ value: undefined })).toBe(null); }); - it('test getFormatter for strings', () => { - const value = 'Test https://google.com with google link'; - const formatter = getFormatter({localType: "string"}); + it("test getFormatter for strings", () => { + const value = "Test https://google.com with google link"; + const formatter = getFormatter({ localType: "string" }); expect(typeof formatter).toBe("function"); expect(formatter()).toBe(null); - expect(formatter({value: 'Test no links'})[0]).toBe('Test no links'); - expect(formatter({value})[0]).toBe('Test '); - expect(formatter({value})[1].props.href).toBe('https://google.com'); - expect(formatter({value})[2]).toBe(' with google link'); - expect(formatter({value: null})).toBe(null); - expect(formatter({value: undefined})).toBe(null); + expect(formatter({ value: "Test no links" })[0]).toBe("Test no links"); + expect(formatter({ value })[0]).toBe("Test "); + expect(formatter({ value })[1].props.href).toBe("https://google.com"); + expect(formatter({ value })[2]).toBe(" with google link"); + expect(formatter({ value: null })).toBe(null); + expect(formatter({ value: undefined })).toBe(null); }); - it('test getFormatter for number', () => { - const formatter = getFormatter({localType: "number"}); + it("test getFormatter for number", () => { + const formatter = getFormatter({ localType: "number" }); expect(typeof formatter).toBe("function"); expect(formatter()).toBe(null); - expect(formatter({value: 44.3333434353535}).type).toBe(NumberFormat); - expect(formatter({value: 44.3333434353535}).props.value).toBe(44.3333434353535); - expect(formatter({value: null})).toBe(null); - expect(formatter({value: undefined})).toBe(null); - expect(formatter({value: 0}).props.value).toBe(0); + expect(formatter({ value: 44.3333434353535 }).type).toBe(NumberFormat); + expect(formatter({ value: 44.3333434353535 }).props.value).toBe( + 44.3333434353535 + ); + expect(formatter({ value: null })).toBe(null); + expect(formatter({ value: undefined })).toBe(null); + expect(formatter({ value: 0 }).props.value).toBe(0); }); - it('test getFormatter for int', () => { - const formatter = getFormatter({localType: "int"}); + it("test getFormatter for int", () => { + const formatter = getFormatter({ localType: "int" }); expect(typeof formatter).toBe("function"); expect(formatter()).toBe(null); - expect(formatter({value: 2455567}).type).toBe(NumberFormat); - expect(formatter({value: 2455567}).props.value).toBe(2455567); - expect(formatter({value: null})).toBe(null); - expect(formatter({value: undefined})).toBe(null); - expect(formatter({value: 0}).props.value).toBe(0); + expect(formatter({ value: 2455567 }).type).toBe(NumberFormat); + expect(formatter({ value: 2455567 }).props.value).toBe(2455567); + expect(formatter({ value: null })).toBe(null); + expect(formatter({ value: undefined })).toBe(null); + expect(formatter({ value: 0 }).props.value).toBe(0); }); - it('test getFormatter for geometry', () => { - const formatter = getFormatter({localType: "Geometry"}); + it("test getFormatter for geometry", () => { + const formatter = getFormatter({ localType: "Geometry" }); expect(typeof formatter).toBe("function"); expect(formatter()).toBe(null); - expect(formatter({value: {properties: {}, geometry: {type: "Point", coordinates: [1, 2]}}})).toBe(null); - expect(formatter({value: null})).toBe(null); - expect(formatter({value: undefined})).toBe(null); + expect( + formatter({ + value: { + properties: {}, + geometry: { type: "Point", coordinates: [1, 2] } + } + }) + ).toBe(null); + expect(formatter({ value: null })).toBe(null); + expect(formatter({ value: undefined })).toBe(null); }); - describe('test featureGridFormatter', () => { + describe("test featureGridFormatter", () => { beforeEach((done) => { document.body.innerHTML = '
'; setTimeout(done); }); afterEach((done) => { - ReactDOM.unmountComponentAtNode(document.getElementById("container")); - document.body.innerHTML = ''; + ReactDOM.unmountComponentAtNode( + document.getElementById("container") + ); + document.body.innerHTML = ""; setTimeout(done); }); - it('base', () => { + it("base", () => { try { - registerFormatter("test", ({config, value}) => { + registerFormatter("test", ({ config, value }) => { expect(config).toExist(); expect(value).toBe("test"); return
test
; }); - const Formatter = getFormatter({localType: "test"}, {featureGridFormatter: {name: "test"}}); - ReactDOM.render(, document.getElementById("container")); - expect(document.getElementById("container").innerHTML).toBe('
test
'); + const Formatter = getFormatter( + { localType: "test" }, + { featureGridFormatter: { name: "test" } } + ); + ReactDOM.render( + , + document.getElementById("container") + ); + expect(document.getElementById("container").innerHTML).toBe( + "
test
" + ); } finally { unregisterFormatter("test"); } }); - it('with directRender option', () => { + it("with directRender option", () => { try { const TEST_FUNC = () =>
test
; registerFormatter("test", TEST_FUNC); - const formatter = getFormatter({localType: "test"}, {featureGridFormatter: {name: "test", directRender: true}}); + const formatter = getFormatter( + { localType: "test" }, + { + featureGridFormatter: { + name: "test", + directRender: true + } + } + ); expect(formatter).toBe(TEST_FUNC); } finally { unregisterFormatter("test"); } }); }); - it('test getFormatter for date / date-time / time', () => { + it("test getFormatter for date / date-time / time", () => { const dateFormats = { - date: 'YYYY', - "date-time": 'YYYY DD', - time: 'HH:mm' + date: "YYYY", + "date-time": "YYYY DD", + time: "HH:mm" }; - const dateFormatter = getFormatter({localType: "date"}, undefined, {dateFormats}); - const dateTimeFormatter = getFormatter({localType: "date-time"}, undefined, {dateFormats}); - const timeFormatter = getFormatter({localType: "time"}, undefined, {dateFormats}); + const dateFormatter = getFormatter({ localType: "date" }, undefined, { + dateFormats + }); + const dateTimeFormatter = getFormatter( + { localType: "date-time" }, + undefined, + { dateFormats } + ); + const timeFormatter = getFormatter({ localType: "time" }, undefined, { + dateFormats + }); expect(typeof dateFormatter).toBe("function"); expect(dateFormatter()).toBe(null); - expect(dateFormatter({value: '2015-02-01T12:45:00Z'})).toBe('2015'); + expect(dateFormatter({ value: "2015-02-01T12:45:00Z" })).toBe("2015"); expect(typeof dateTimeFormatter).toBe("function"); expect(dateTimeFormatter()).toBe(null); - expect(dateTimeFormatter({value: '2015-02-01Z'})).toBe('2015 01'); + expect(dateTimeFormatter({ value: "2015-02-01Z" })).toBe("2015 01"); expect(typeof timeFormatter).toBe("function"); expect(timeFormatter()).toBe(null); - expect(timeFormatter({value: '12:45:00Z'})).toBe('12:45'); - expect(timeFormatter({ value: '1970-01-01T02:30:00Z' })).toBe('02:30'); // still able to format time even when found a full date (sometimes GeoServer returns full date instead of time only) + expect(timeFormatter({ value: "12:45:00Z" })).toBe("12:45"); + expect(timeFormatter({ value: "1970-01-01T02:30:00Z" })).toBe("02:30"); // still able to format time even when found a full date (sometimes GeoServer returns full date instead of time only) + }); + + it("test getFormatter for invalid date-time YYYY-MM-DD[Z]", () => { + const dateFormats = { + "date-time": "YYYY-MM-DD[Z]" + }; + const dateTimeWithZFormatter = getFormatter( + { localType: "date-time" }, + undefined, + { dateFormats } + ); + expect(typeof dateTimeWithZFormatter).toBe("function"); + expect(dateTimeWithZFormatter({ value: "2015-02-01Z" })).toBe( + "2015-02-01Z" + ); + expect(dateTimeWithZFormatter({ value: "2015-02-01" })).toBe( + "2015-02-01Z" + ); + expect(dateTimeWithZFormatter({ value: "2015/02/01" })).toBe( + "2015-02-01Z" + ); + expect(dateTimeWithZFormatter({ value: "2015/02/01 03:20:10" })).toBe( + "2015-02-01Z" + ); + expect(dateTimeWithZFormatter({ value: "2015-02-01T12:45:00Z"})).toBe('2015-02-01Z'); }); }); diff --git a/web/client/components/data/featuregrid/formatters/index.js b/web/client/components/data/featuregrid/formatters/index.js index 45979a4ab2..28f2d339a6 100644 --- a/web/client/components/data/featuregrid/formatters/index.js +++ b/web/client/components/data/featuregrid/formatters/index.js @@ -20,13 +20,16 @@ const StringFormatter = ({value} = {}) => !isNil(value) ? reactStringReplace(val )) : null; const NumberFormatter = ({value} = {}) => !isNil(value) ? : null; const DEFAULT_DATE_PART = "1970-01-01"; +const DATE_INPUT_FORAMAT = "YYYY-MM-DD[Z]"; const dateTimeFormatter = ({value, format, type}) => { return !isNil(value) ? moment.utc(value).isValid() // geoserver sometimes returns UTC for time. ? moment.utc(value).format(format) : type === 'time' ? moment(`${DEFAULT_DATE_PART}T${value}`).utc().format(format) // time format append default date part - : moment(value).format(format) // date or date-time formats + : type === "date" && value?.toLowerCase()?.endsWith("z") // in case: date format and value ends with z + ? moment(value, DATE_INPUT_FORAMAT).format(format) + : moment(value).format(format) : null; }; export const register = {}; diff --git a/web/client/components/map/cesium/Map.jsx b/web/client/components/map/cesium/Map.jsx index f5144a1fa5..a0f1f8ffc6 100644 --- a/web/client/components/map/cesium/Map.jsx +++ b/web/client/components/map/cesium/Map.jsx @@ -579,7 +579,7 @@ class CesiumMap extends React.Component { roll: this.map.camera.roll } }, - getResolutions()[zoom] + getResolutions()[Math.round(zoom)] ); }; diff --git a/web/client/components/map/cesium/plugins/ThreeDTilesLayer.js b/web/client/components/map/cesium/plugins/ThreeDTilesLayer.js index 7d72c1decf..8e0d750c09 100644 --- a/web/client/components/map/cesium/plugins/ThreeDTilesLayer.js +++ b/web/client/components/map/cesium/plugins/ThreeDTilesLayer.js @@ -44,9 +44,9 @@ function getStyle({ style }) { function updateModelMatrix(tileSet, { heightOffset }) { if (!isNaN(heightOffset) && isNumber(heightOffset)) { const boundingSphere = tileSet.boundingSphere; - const cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center); - const surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0); - const offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset); + const cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center); // undefined if the cartesian is at the center of the ellipsoid + const surface = Cesium.Cartesian3.fromRadians(cartographic?.longitude || 0, cartographic?.latitude || 0, 0.0); + const offset = Cesium.Cartesian3.fromRadians(cartographic?.longitude || 0, cartographic?.latitude || 0, heightOffset); const translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3()); tileSet.modelMatrix = Cesium.Matrix4.fromTranslation(translation); } diff --git a/web/client/components/map/leaflet/Layer.jsx b/web/client/components/map/leaflet/Layer.jsx index f33499cb8f..c111dfeecf 100644 --- a/web/client/components/map/leaflet/Layer.jsx +++ b/web/client/components/map/leaflet/Layer.jsx @@ -127,8 +127,9 @@ class LeafletLayer extends React.Component { maxResolution = Infinity, disableResolutionLimits } = options || {}; - if (!disableResolutionLimits && !isNil(resolutions[zoom])) { - const resolution = resolutions[zoom]; + const zoomRound = Math.round(zoom); + if (!disableResolutionLimits && !isNil(resolutions[zoomRound])) { + const resolution = resolutions[zoomRound]; // use similar approach of ol // maxResolution is exclusive // minResolution is inclusive diff --git a/web/client/components/map/leaflet/Map.jsx b/web/client/components/map/leaflet/Map.jsx index 53e7b35f1d..e99ea2e8f0 100644 --- a/web/client/components/map/leaflet/Map.jsx +++ b/web/client/components/map/leaflet/Map.jsx @@ -463,7 +463,7 @@ class LeafletMap extends React.Component { this.props.id, this.props.projection, viewerOptions, // viewerOptions - this.getResolutions()[zoom] // resolution + this.getResolutions()[Math.round(zoom)] // resolution ); }; diff --git a/web/client/components/map/openlayers/Map.jsx b/web/client/components/map/openlayers/Map.jsx index a90d3cdbc9..0dd1312a0f 100644 --- a/web/client/components/map/openlayers/Map.jsx +++ b/web/client/components/map/openlayers/Map.jsx @@ -505,7 +505,12 @@ class OpenlayersMap extends React.Component { projection: normalizeSRS(projection), center: [center.x, center.y], zoom: zoom, - minZoom: limits.minZoom + minZoom: limits.minZoom, + // allow to zoom to level 0 and see world wrapping + multiWorld: true, + // does not allow intermediary zoom levels + // we need this at true to set correctly the scale box + constrainResolution: true }, newOptions || {}); return new View(viewOptions); }; diff --git a/web/client/components/map/openlayers/MeasurementSupport.jsx b/web/client/components/map/openlayers/MeasurementSupport.jsx index efa19fc09a..8028a0eed1 100644 --- a/web/client/components/map/openlayers/MeasurementSupport.jsx +++ b/web/client/components/map/openlayers/MeasurementSupport.jsx @@ -26,6 +26,7 @@ import {getMessageById} from '../../../utils/LocaleUtils'; import {createOLGeometry} from '../../../utils/openlayers/DrawUtils'; import {Polygon, LineString} from 'ol/geom'; +import { never } from 'ol/events/condition'; import Overlay from 'ol/Overlay'; import VectorSource from 'ol/source/Vector'; import VectorLayer from 'ol/layer/Vector'; @@ -599,6 +600,7 @@ export default class MeasurementSupport extends React.Component { // create an interaction to draw with draw = new Draw({ source: new VectorSource(), + freehandCondition: never, type: /** @type {ol.geom.GeometryType} */ geometryType, style: new Style({ fill: new Fill({ @@ -621,9 +623,9 @@ export default class MeasurementSupport extends React.Component { }) }); - this.clickListener = this.props.map.on('click', this.updateMeasurementResults.bind(this, this.props)); + this.clickListener = this.props.map.on('click', this.updateMeasurementResults.bind(this)); if (this.props.updateOnMouseMove) { - this.props.map.on('pointermove', this.updateMeasurementResults.bind(this, this.props)); + this.props.map.on('pointermove', this.updateMeasurementResults.bind(this)); } this.props.map.on('pointermove', (evt) => this.pointerMoveHandler(evt)); @@ -896,10 +898,10 @@ export default class MeasurementSupport extends React.Component { this.props.map.removeInteraction(this.drawInteraction); this.drawInteraction = null; this.sketchFeature = null; - this.props.map.un('click', this.updateMeasurementResults.bind(this, this.props), this); + this.props.map.un('click', this.updateMeasurementResults.bind(this), this); unByKey(this.clickListener); if (this.props.updateOnMouseMove) { - this.props.map.un('pointermove', this.updateMeasurementResults.bind(this, this.props), this); + this.props.map.un('pointermove', this.updateMeasurementResults.bind(this), this); } } }; @@ -929,13 +931,13 @@ export default class MeasurementSupport extends React.Component { this.helpTooltipElement.classList.remove('hidden'); }; - updateMeasurementResults = (props) => { + updateMeasurementResults = () => { if (!this.sketchFeature) { return; } let sketchCoords = this.sketchFeature.getGeometry().getCoordinates(); - if (props.measurement.geomType === 'Bearing' && sketchCoords.length > 1) { + if (this.props.measurement.geomType === 'Bearing' && sketchCoords.length > 1) { // calculate the azimuth as base for bearing information if (sketchCoords.length > 2) { this.drawInteraction.sketchCoords_ = [sketchCoords[0], sketchCoords[1], sketchCoords[0]]; diff --git a/web/client/components/mapcontrols/scale/ScaleBox.jsx b/web/client/components/mapcontrols/scale/ScaleBox.jsx index e3c780bda7..39333c2303 100644 --- a/web/client/components/mapcontrols/scale/ScaleBox.jsx +++ b/web/client/components/mapcontrols/scale/ScaleBox.jsx @@ -45,7 +45,7 @@ class ScaleBox extends React.Component { } onComboChange = (event) => { - var selectedZoomLvl = parseInt(event.nativeEvent.target.value, 10); + let selectedZoomLvl = parseInt(event.nativeEvent.target.value, 10); this.props.onChange(selectedZoomLvl, this.props.scales[selectedZoomLvl]); }; @@ -58,14 +58,15 @@ class ScaleBox extends React.Component { }; render() { - var control = null; + let control = null; + const currentZoomLvl = Math.round(this.props.currentZoomLvl); if (this.props.readOnly) { control = - + ; } else if (this.props.useRawInput) { control = - ( {this.getOptions()} ) ; @@ -73,7 +74,7 @@ class ScaleBox extends React.Component { control = (
{this.props.label} - + {this.getOptions()}
) diff --git a/web/client/components/mapcontrols/scale/__tests__/ScaleBox-test.jsx b/web/client/components/mapcontrols/scale/__tests__/ScaleBox-test.jsx index 2ba5a5ef4b..5368dde32e 100644 --- a/web/client/components/mapcontrols/scale/__tests__/ScaleBox-test.jsx +++ b/web/client/components/mapcontrols/scale/__tests__/ScaleBox-test.jsx @@ -96,4 +96,10 @@ describe('ScaleBox', () => { expect(domLabel).toExist(); expect(domLabel.innerHTML).toContain("Scale:"); }); + + it('should support not rounded zoom levels', () => { + TestUtils.act(() => { ReactDOM.render(, document.getElementById('container')); }); + const select = document.querySelector('select'); + expect(select.value).toBe('5'); + }); }); diff --git a/web/client/components/share/SharePanel.jsx b/web/client/components/share/SharePanel.jsx index c47fceb103..962faefefa 100644 --- a/web/client/components/share/SharePanel.jsx +++ b/web/client/components/share/SharePanel.jsx @@ -106,8 +106,8 @@ class SharePanel extends React.Component { modal: false, draggable: true, onClose: () => {}, - shareUrlRegex: "(h[^#]*)#\\/viewer\\/([^\\/]*)\\/([A-Za-z0-9]*)", - shareUrlReplaceString: "$1embedded.html#/$3", + shareUrlRegex: "(h[^#]*)#\\/viewer\\/([A-Za-z0-9]*)", + shareUrlReplaceString: "$1embedded.html#/$2", embedPanel: true, embedOptions: {}, showAPI: true, diff --git a/web/client/components/share/__tests__/SharePanel-test.jsx b/web/client/components/share/__tests__/SharePanel-test.jsx index b417e8f47d..7e6b8768fb 100644 --- a/web/client/components/share/__tests__/SharePanel-test.jsx +++ b/web/client/components/share/__tests__/SharePanel-test.jsx @@ -53,7 +53,9 @@ describe("The SharePanel component", () => { const cmpSharePanel = ReactDOM.render(0} shareUrlRegex=".*" shareUrlReplaceString="ABC" shareUrl="www.geo-solutions.it" isVisible={false} />, document.getElementById("container")); expect(cmpSharePanel).toExist(); const parsed = cmpSharePanel.generateUrl("TEST", "(TE)ST", "$1"); + const embedMap = cmpSharePanel.generateUrl("http://localhost:8081/#/viewer/44asd", "(h[^#]*)#\/viewer\/([^\/]*\/[A-Za-z0-9]*|[A-Za-z0-9]*)", "$2"); expect(parsed).toBe("TE"); + expect(embedMap).toBe("44asd"); }); it('test showAPI flag', () => { let cmpSharePanel = ReactDOM.render(0} shareUrl="www.geo-solutions.it" isVisible />, document.getElementById("container")); diff --git a/web/client/configs/pluginsConfig.json b/web/client/configs/pluginsConfig.json index bd32c9dba0..b6a52b9a8f 100644 --- a/web/client/configs/pluginsConfig.json +++ b/web/client/configs/pluginsConfig.json @@ -177,7 +177,7 @@ "title": "plugins.Annotations.title", "description": "plugins.Annotations.description", "dependencies": [ - "SidebarMenu" + "TOC" ] }, { diff --git a/web/client/plugins/Annotations.jsx b/web/client/plugins/Annotations.jsx index ae74dd2ea2..2425ffc7a1 100644 --- a/web/client/plugins/Annotations.jsx +++ b/web/client/plugins/Annotations.jsx @@ -354,16 +354,6 @@ export default createPlugin('Annotations', { } return false; }) - }, - BurgerMenu: { - name: 'annotations', - position: 40, - text: , - tooltip: "annotations.tooltip", - icon: , - action: conditionalToggle, - priority: 2, - doNotHide: true } }, reducers: { diff --git a/web/client/utils/MapUtils.js b/web/client/utils/MapUtils.js index 68fdc37d5c..380817b304 100644 --- a/web/client/utils/MapUtils.js +++ b/web/client/utils/MapUtils.js @@ -912,7 +912,7 @@ export function calculateExtent(center = {x: 0, y: 0, crs: "EPSG:3857"}, resolut export const reprojectZoom = (zoom, mapProjection, printProjection) => { const multiplier = METERS_PER_UNIT[getUnits(mapProjection)] / METERS_PER_UNIT[getUnits(printProjection)]; - const mapResolution = getResolutions(mapProjection)[zoom] * multiplier; + const mapResolution = getResolutions(mapProjection)[Math.round(zoom)] * multiplier; const printResolutions = getResolutions(printProjection); const printResolution = printResolutions.reduce((nearest, current) => { diff --git a/web/client/utils/__tests__/MapUtils-test.js b/web/client/utils/__tests__/MapUtils-test.js index 1a1cb6719a..0c6619477a 100644 --- a/web/client/utils/__tests__/MapUtils-test.js +++ b/web/client/utils/__tests__/MapUtils-test.js @@ -40,7 +40,8 @@ import { addRootParentGroup, mapUpdated, getZoomFromResolution, - getResolutionObject + getResolutionObject, + reprojectZoom } from '../MapUtils'; import { VisualizationModes } from '../MapTypeUtils'; @@ -2132,6 +2133,10 @@ describe('Test the MapUtils', () => { const resolution = 1000; // ~zoom 7 in Web Mercator expect(getZoomFromResolution(resolution)).toBe(7); }); + it('reprojectZoom', () => { + expect(reprojectZoom(5, 'EPSG:3857', 'EPSG:4326')).toBe(4); + expect(reprojectZoom(5.2, 'EPSG:3857', 'EPSG:4326')).toBe(4); + }); describe("getResolutionObject tests", () => { const resolutions = [156543, 78271, 39135, 19567, 9783, 4891, 2445, 1222]; it('getResolutionObject for visibility limit type scale', ()=> { diff --git a/web/client/utils/grids/MapGridsUtils.js b/web/client/utils/grids/MapGridsUtils.js index 6a4858b627..183e2919cc 100644 --- a/web/client/utils/grids/MapGridsUtils.js +++ b/web/client/utils/grids/MapGridsUtils.js @@ -408,7 +408,7 @@ export function getGridGeoJson({ pixelRatio = devicePixelRatio, frameSize = 0.0 }) { - const resolution = (resolutions ?? getResolutions(mapProjection))[zoom]; + const resolution = (resolutions ?? getResolutions(mapProjection))[Math.round(zoom)]; const mapToGrid = proj4(mapProjection, gridProjection).forward; const gridToMap = proj4(gridProjection, mapProjection).forward; const projectionCenter = mapToGrid(getCenter(getProjection(gridProjection).extent)); diff --git a/web/client/utils/grids/__tests__/MapGridsUtils-test.js b/web/client/utils/grids/__tests__/MapGridsUtils-test.js index 06dce6d8ac..26b4128135 100644 --- a/web/client/utils/grids/__tests__/MapGridsUtils-test.js +++ b/web/client/utils/grids/__tests__/MapGridsUtils-test.js @@ -245,12 +245,12 @@ describe("getGridGeoJson", () => { expect(geoJson.features[5].geometry.coordinates).toEqual([[-180, -90], [180, -90]]); expect(geoJson.features[7].geometry.coordinates).toEqual([[-180, 90], [180, 90]]); }); - it("map and grid in EPSG:4326 and zoom 5, no labels", () => { + it("map and grid in EPSG:4326 and zoom 5.2, no labels", () => { const geoJson = getGridGeoJson({ mapProjection: "EPSG:4326", gridProjection: "EPSG:4326", extent: [5, 40, 10, 44], - zoom: 5 + zoom: 5.2 }); expect(geoJson.type).toBe("FeatureCollection"); expect(geoJson.features.length).toBe(7); diff --git a/web/client/utils/styleparser/CesiumStyleParser.js b/web/client/utils/styleparser/CesiumStyleParser.js index 841a5db38e..f0848cc5f0 100644 --- a/web/client/utils/styleparser/CesiumStyleParser.js +++ b/web/client/utils/styleparser/CesiumStyleParser.js @@ -671,7 +671,10 @@ function getStyleFuncFromRules({ entity._msGlobalOpacity = undefined; return resolve(entity); })) - ); + // map.scene.requestRender(); does not work without a setTimeout + // it seems there is need of a small delay to correctly request the next map rendering + // requestRender is used by layer to update the style + ).then((response) => new Promise((resolve) => setTimeout(() => resolve(response)))); } class CesiumStyleParser {