diff --git a/web/client/components/map/openlayers/Map.jsx b/web/client/components/map/openlayers/Map.jsx
index 8bc0997ebe..2268f7ac62 100644
--- a/web/client/components/map/openlayers/Map.jsx
+++ b/web/client/components/map/openlayers/Map.jsx
@@ -304,8 +304,8 @@ class OpenlayersMap extends React.Component {
}, 0);
}
- if (this.map && ((this.props.projection !== newProps.projection) || this.haveResolutionsChanged(newProps)) || this.props.limits !== newProps.limits) {
- if (this.props.projection !== newProps.projection || this.props.limits !== newProps.limits) {
+ if (this.map && ((this.props.projection !== newProps.projection) || this.haveResolutionsChanged(newProps)) || this.haveRotationChanged(newProps) || this.props.limits !== newProps.limits) {
+ if (this.props.projection !== newProps.projection || this.props.limits !== newProps.limits || this.haveRotationChanged(newProps)) {
let mapProjection = newProps.projection;
const center = reproject([
newProps.center.x,
@@ -502,6 +502,12 @@ class OpenlayersMap extends React.Component {
return !isEqual(resolutions, newResolutions);
};
+ haveRotationChanged = (newProps) => {
+ const rotation = this.props.mapOptions && this.props.mapOptions.view ? this.props.mapOptions.view.rotation : undefined;
+ const newRotation = newProps.mapOptions && newProps.mapOptions.view ? newProps.mapOptions.view.rotation : undefined;
+ return !isEqual(rotation, newRotation);
+ };
+
createView = (center, zoom, projection, options, limits = {}) => {
// limit has a crs defined
const extent = limits.restrictedExtent && limits.crs && reprojectBbox(limits.restrictedExtent, limits.crs, normalizeSRS(projection));
diff --git a/web/client/components/map/openlayers/__tests__/Map-test.jsx b/web/client/components/map/openlayers/__tests__/Map-test.jsx
index 17de72813a..b9900912f7 100644
--- a/web/client/components/map/openlayers/__tests__/Map-test.jsx
+++ b/web/client/components/map/openlayers/__tests__/Map-test.jsx
@@ -712,6 +712,83 @@ describe('OpenlayersMap', () => {
expect( map.haveResolutionsChanged(testProps({mapOptions: {view: {resolutions: [100, 50, 25]}}})) ).toBe(true);
});
+ it('check result of "haveRotationChanged()" when receiving new props', () => {
+ let map = ReactDOM.render(
+
+ , document.getElementById("map"));
+
+ let origProps = assign({}, map.props);
+ function testProps(newProps) {
+ // update original props with newProps
+ return assign({}, origProps, newProps);
+ }
+
+ map = ReactDOM.render(
+
+ , document.getElementById("map"));
+
+ expect(map.haveRotationChanged(testProps({mapOptions: undefined}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {}}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {}}}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: undefined}}}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: 0}}}))).toBe(true);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: 20}}}))).toBe(true);
+
+ map = ReactDOM.render(
+
+ , document.getElementById("map"));
+ expect(map.haveRotationChanged(testProps({mapOptions: undefined}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {}}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {}}}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: undefined}}}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: 0}}}))).toBe(true);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: 20}}}))).toBe(true);
+
+ map = ReactDOM.render(
+
+ , document.getElementById("map"));
+ expect(map.haveRotationChanged(testProps({mapOptions: undefined}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {}}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {}}}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: undefined}}}))).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: 0}}}))).toBe(true);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: 20}}}))).toBe(true);
+ map = ReactDOM.render(
+
+ , document.getElementById("map"));
+ expect(map.haveRotationChanged(testProps({mapOptions: undefined})) ).toBe(true);
+ expect(map.haveRotationChanged(testProps({mapOptions: {}})) ).toBe(true);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {}}})) ).toBe(true);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: undefined}}})) ).toBe(true);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: 1}}})) ).toBe(false);
+ expect(map.haveRotationChanged(testProps({mapOptions: {view: {rotation: 20}}})) ).toBe(true);
+ });
+
it('check if the map has "auto" cursor as default', () => {
const map = ReactDOM.render(
({
onChangeParameter: setPrintParameter
})(ProjectionComp);
+export const Rotation = connect((state) => ({
+ spec: state.print?.spec || {},
+ additionalProperty: false,
+ property: "rotation",
+ path: "",
+ type: "number",
+ label: "print.rotation"
+}), {
+ onChangeParameter: setPrintParameter
+})(TextInput);
+
export const Layout = connect((state) => ({
spec: state.print?.spec || {},
layouts: state?.print?.capabilities?.layouts || []
@@ -189,13 +200,17 @@ export const standardItems = {
"projections": [{"name": "EPSG:3857", "value": "EPSG:3857"}, {"name": "EPSG:4326", "value": "EPSG:4326"}]
},
position: 4
+ }, {
+ id: "rotation",
+ plugin: Rotation,
+ position: 5
}, {
id: "overlayLayers",
plugin: AdditionalLayers,
cfg: {
enabled: false
},
- position: 5
+ position: 6
}],
"left-panel-accordion": [{
id: "layout",
diff --git a/web/client/reducers/print.js b/web/client/reducers/print.js
index e7569e94bb..a8312dfd93 100644
--- a/web/client/reducers/print.js
+++ b/web/client/reducers/print.js
@@ -37,7 +37,8 @@ const initialSpec = {
resolution: 96,
name: '',
description: '',
- outputFormat: "pdf"
+ outputFormat: "pdf",
+ rotation: 0
};
const getSheetName = (name = '') => {
diff --git a/web/client/themes/default/less/print.less b/web/client/themes/default/less/print.less
index 82dd344f14..f347519f48 100644
--- a/web/client/themes/default/less/print.less
+++ b/web/client/themes/default/less/print.less
@@ -34,4 +34,14 @@
border-width: 1px;
border-style: solid;
}
+ #print_preview {
+ .ol-rotate {
+ background-color: transparent;
+ button{
+ &.ol-rotate-reset{
+ display: none; //hide rotate button on map-preview
+ }
+ }
+ }
+ }
}
diff --git a/web/client/translations/data.de-DE.json b/web/client/translations/data.de-DE.json
index ba844d9ab2..6899dd8899 100644
--- a/web/client/translations/data.de-DE.json
+++ b/web/client/translations/data.de-DE.json
@@ -743,6 +743,7 @@
"iconsSize": "Symbolgröße:",
"dpi": "dpi:"
},
+ "rotation": "Rotation",
"layoutWarning": "Layout nicht erlaubt",
"scale": "Maßstab",
"includeScale": "in Druck einschließen",
diff --git a/web/client/translations/data.en-US.json b/web/client/translations/data.en-US.json
index da89c8780c..c8d9d5caaf 100644
--- a/web/client/translations/data.en-US.json
+++ b/web/client/translations/data.en-US.json
@@ -704,6 +704,7 @@
"iconsSize": "Icons size:",
"dpi": "Dpi:"
},
+ "rotation": "Rotation",
"layoutWarning": "Not allowed layout",
"scale": "Scale",
"includeScale": "Include in print",
diff --git a/web/client/translations/data.es-ES.json b/web/client/translations/data.es-ES.json
index eed58d283c..0dc09cca5e 100644
--- a/web/client/translations/data.es-ES.json
+++ b/web/client/translations/data.es-ES.json
@@ -704,6 +704,7 @@
"iconsSize": "Tamaño de los iconos:",
"dpi": "ppp:"
},
+ "rotation": "Rotation",
"layoutWarning": "Lienzo no permitido",
"scale": "escala",
"includeScale": "incluir en la impresión",
diff --git a/web/client/translations/data.fr-FR.json b/web/client/translations/data.fr-FR.json
index 29563dd488..e1f87eacf2 100644
--- a/web/client/translations/data.fr-FR.json
+++ b/web/client/translations/data.fr-FR.json
@@ -704,6 +704,7 @@
"iconsSize": "Taille d'icône :",
"dpi": "Ppp :"
},
+ "rotation": "Rotation",
"layoutWarning": "Mise en page non permise",
"scale": "échelle",
"includeScale": "inclure dans l'impression",
diff --git a/web/client/translations/data.it-IT.json b/web/client/translations/data.it-IT.json
index bed7a10c0c..81be534871 100644
--- a/web/client/translations/data.it-IT.json
+++ b/web/client/translations/data.it-IT.json
@@ -704,6 +704,7 @@
"iconsSize": "Dimensione icone:",
"dpi": "Dpi:"
},
+ "rotation": "Rotation",
"layoutWarning": "Layout non consentito",
"scale": "Scala",
"includeScale": "Includi nella stampa",
diff --git a/web/client/utils/PrintUtils.js b/web/client/utils/PrintUtils.js
index 5d33b61b57..b2498c1785 100644
--- a/web/client/utils/PrintUtils.js
+++ b/web/client/utils/PrintUtils.js
@@ -31,6 +31,7 @@ import { printSpecificationSelector } from "../selectors/print";
import assign from 'object-assign';
import sortBy from "lodash/sortBy";
import head from "lodash/head";
+import isNil from "lodash/isNil";
import { getGridGeoJson } from "./grids/MapGridsUtils";
@@ -261,7 +262,7 @@ export const getMapfishPrintSpecification = (rawSpec, state) => {
projectedCenter.y
],
"scale": reprojectedScale,
- "rotation": 0
+ "rotation": !isNil(spec.rotation) ? -Number(spec.rotation) : 0 // negate the rotation value to match rotation in map preview and printed output
}
],
"legends": PrintUtils.getMapfishLayersSpecification(spec.layers, projectedSpec, state, 'legend'),