diff --git a/index.html b/index.html
index 0403a1d04..75005964b 100644
--- a/index.html
+++ b/index.html
@@ -4,8 +4,10 @@
-
-
+
+
Origo exempel
diff --git a/package-lock.json b/package-lock.json
index c8e97eadf..d670ad46b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3459,9 +3459,9 @@
}
},
"dom-to-image-more": {
- "version": "2.9.1",
- "resolved": "https://registry.npmjs.org/dom-to-image-more/-/dom-to-image-more-2.9.1.tgz",
- "integrity": "sha512-yKNHXkJZJhwdJ+D112qi9rbA8jw7ENAe1zxdwE0pTCF039yWBT8gZ229cC2ic/zsPBp3ODvBc7ft4vDfX5tEkw=="
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/dom-to-image-more/-/dom-to-image-more-2.8.0.tgz",
+ "integrity": "sha512-YqlHI1i+TMuaKwkFRO5oDPjC3eWf+6Hln9rHZcnFYvmoXwCrGZmZ7BYXBJOjw5utYg2Lp+QF9YO96F7CsDC4eQ=="
},
"domain-browser": {
"version": "1.2.0",
diff --git a/package.json b/package.json
index f29c004c9..d41435fb1 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
"copyfiles": "^2.4.1",
"core-js": "^3.8.2",
"cuid": "^2.1.4",
- "dom-to-image-more": "^2.8.0",
+ "dom-to-image-more": "2.8.0",
"downloadjs": "^1.4.7",
"elm-pep": "^1.0.6",
"html2canvas": "^1.0.0-rc.7",
diff --git a/src/controls/print/index.js b/src/controls/print/index.js
index e5c72b95b..ca1854ad4 100644
--- a/src/controls/print/index.js
+++ b/src/controls/print/index.js
@@ -47,7 +47,8 @@ const Print = function Print(options = {}) {
rotationStep = 1,
leftFooterText = '',
filename,
- mapInteractionsActive = false
+ mapInteractionsActive = false,
+ supressResolutionsRecalculation = false
} = options;
let {
showNorthArrow = true
@@ -107,7 +108,8 @@ const Print = function Print(options = {}) {
rotation,
rotationStep,
leftFooterText,
- mapInteractionsActive
+ mapInteractionsActive,
+ supressResolutionsRecalculation
});
if (placement.indexOf('screen') > -1) {
mapTools = `${viewer.getMain().getMapTools().getId()}`;
diff --git a/src/controls/print/print-component.js b/src/controls/print/print-component.js
index 97ef0196d..144c4fb1a 100644
--- a/src/controls/print/print-component.js
+++ b/src/controls/print/print-component.js
@@ -1,6 +1,10 @@
import olAttribution from 'ol/control/Attribution';
import olScaleLine from 'ol/control/ScaleLine';
import { getPointResolution } from 'ol/proj';
+import TileImage from 'ol/source/TileImage';
+import TileWMSSource from 'ol/source/TileWMS';
+import TileGrid from 'ol/tilegrid/TileGrid';
+import { Group } from 'ol/layer';
import {
Button, Component, cuid, dom
} from '../../ui';
@@ -11,6 +15,7 @@ import PrintInteractionToggle from './print-interaction-toggle';
import PrintToolbar from './print-toolbar';
import { downloadPNG, downloadPDF, printToScalePDF } from '../../utils/download';
import { afterRender, beforeRender } from './download-callback';
+import maputils from '../../maputils';
const PrintComponent = function PrintComponent(options = {}) {
const {
@@ -34,13 +39,13 @@ const PrintComponent = function PrintComponent(options = {}) {
sizeCustomMinWidth,
sizeCustomMaxWidth,
resolutions,
- scales,
scaleInitial,
createdPrefix,
rotation,
rotationStep,
leftFooterText,
- mapInteractionsActive
+ mapInteractionsActive,
+ supressResolutionsRecalculation
} = options;
let {
@@ -51,6 +56,7 @@ const PrintComponent = function PrintComponent(options = {}) {
size,
orientation,
resolution,
+ scales,
showMargins,
showCreated,
showScale,
@@ -70,6 +76,111 @@ const PrintComponent = function PrintComponent(options = {}) {
let printScale = 0;
let widthImage = 0;
let heightImage = 0;
+ const originalResolutions = viewer.getResolutions().map(item => item);
+ const originalGrids = new Map();
+
+ if (!Array.isArray(scales) || scales.length === 0) {
+ scales = originalResolutions.map(currRes => maputils.resolutionToFormattedScale(currRes, viewer.getProjection()));
+ }
+
+ /**
+ * Recalulates a resoultions array to reflect dpi changes
+ * @param {number []} src the array of resolutions
+ * @returns {number []} A new array with recalculated values
+ */
+ const recalculateResolutionsArray = function recalculateResolutionsArray(src) {
+ const retval = [];
+ for (let ix = 0; ix < src.length; ix += 1) {
+ // Do the calculation the same way as when setting the scale. Otherwise there will be rounding errors and the scale bar will have another scale than selected
+ // (and probably not an even nice looking number)
+ const scale = maputils.resolutionToScale(src[ix], viewer.getProjection()) / 1000;
+ const scaleResolution = scale / getPointResolution(viewer.getProjection(), resolution / 25.4, map.getView().getCenter());
+ retval.push(scaleResolution);
+ }
+ return retval;
+ };
+
+ /**
+ * Recursively flattens group layers in an array of layers
+ * @param {any []} layers Array of layers
+ * @returns {any []} Array of layers with group layers flattened
+ */
+ const flattenLayers = function flattenLayers(layers) {
+ return layers.reduce((acc, currLayer) => {
+ if (currLayer instanceof Group) {
+ // eslint-disable-next-line no-param-reassign
+ acc = acc.concat(flattenLayers(currLayer.getLayers().getArray()));
+ } else {
+ acc.push(currLayer);
+ }
+ return acc;
+ }, []);
+ };
+
+ /** Recalculate the grid of tiled layers when resolution has changed as more tiles may be needed to cover the extent */
+ const updateTileGrids = function updateTileGrids() {
+ const layers = flattenLayers(viewer.getLayers());
+ for (let i = 0; i < layers.length; i += 1) {
+ const currLayer = layers[i];
+ if (currLayer.getSource() instanceof TileImage) {
+ const grid = currLayer.getSource().getTileGrid();
+ let needNewGrid = false;
+ // Store original grid if we don't have it already
+ // Don't always copy as it is only the first time it is original grid
+ if (!originalGrids.has(currLayer)) {
+ originalGrids.set(currLayer, grid);
+ }
+
+ // Deep copy grid so we can exchange it without messing with anything else
+ const newgridOptions = {};
+ // If layer uses the grid from the viewer, it is a shared instance and has been changed when resolutions were recalculated
+ // as the grid uses a pointer to resolutions
+ if (grid === viewer.getTileGrid()) {
+ // No need for deep copy yet
+ newgridOptions.resolutions = originalResolutions;
+ needNewGrid = true;
+ } else {
+ // No need for deep copy yet
+ newgridOptions.resolutions = originalGrids.get(currLayer).getResolutions();
+ }
+
+ // TileWms is actually not a tile service. So we can create a new dynamic grid to create crisp tiles matching the new
+ // resolutions, it will just cost a few cache misses on the server. All other tile sources are tiled, so we can't assume that there exist tiles for a "random" resolution
+ // so the new grid is actually the same as before, but it may have been reverted to the resolutions it had before map resolutions was recalculated
+ // An alternative (better) solution would have been to deep copy resolutions when making the grid in viewer, but that might upset someone else.
+ // In theory we could have added a flag to which layers should be recalculated. Someone might have multiple grids to support different resolutions.
+ if (currLayer.getSource() instanceof TileWMSSource) {
+ // This is actually a deep copy
+ newgridOptions.resolutions = recalculateResolutionsArray(newgridOptions.resolutions);
+ needNewGrid = true;
+ }
+ // Would be silly to create a deep clone if it is exactly the same.
+ if (needNewGrid) {
+ newgridOptions.extent = grid.getExtent();
+ newgridOptions.minZoom = grid.getMinZoom();
+ newgridOptions.origin = grid.getOrigin();
+ newgridOptions.tileSize = grid.getTileSize();
+ const newGrid = new TileGrid(newgridOptions);
+ // Set our brand new grid on current layer
+ currLayer.getSource().tileGrid = newGrid;
+ }
+ }
+ }
+ };
+
+ /** Recalculate the array of allowed zoomlevels to reflect changes in DPI and updates the view */
+ const updateResolutions = function updateResolutions() {
+ const viewerResolutions = viewer.getResolutions();
+ const newResolutions = recalculateResolutionsArray(originalResolutions);
+ for (let ix = 0; ix < viewerResolutions.length; ix += 1) {
+ viewerResolutions[ix] = newResolutions[ix];
+ }
+ // As we do a "dirty" update of resolutions we have to trigger a re-read of the limits, otherwise the outer limits still apply.
+ map.getView().setMinZoom(0);
+ map.getView().setMaxZoom(viewerResolutions.length - 1);
+ // Have to recalculate tiles extents as well.
+ updateTileGrids();
+ };
const setCustomSize = function setCustomSize(sizeObj) {
if ('width' in sizeObj) {
@@ -157,7 +268,8 @@ const PrintComponent = function PrintComponent(options = {}) {
showScale,
showNorthArrow,
rotation,
- rotationStep
+ rotationStep,
+ viewerResolutions: originalResolutions
});
const printInteractionToggle = PrintInteractionToggle({ map, target, mapInteractionsActive, pageSettings: viewer.getViewerOptions().pageSettings });
const printToolbar = PrintToolbar();
@@ -170,6 +282,7 @@ const PrintComponent = function PrintComponent(options = {}) {
name: 'printComponent',
onInit() {
this.on('render', this.onRender);
+
this.addComponent(printSettings);
this.addComponent(printInteractionToggle);
this.addComponent(printToolbar);
@@ -246,6 +359,10 @@ const PrintComponent = function PrintComponent(options = {}) {
},
changeResolution(evt) {
resolution = evt.resolution;
+ if (!supressResolutionsRecalculation) {
+ updateResolutions();
+ }
+
this.updatePageSize();
if (printScale > 0) {
this.changeScale({ scale: printScale });
@@ -276,6 +393,22 @@ const PrintComponent = function PrintComponent(options = {}) {
printMapComponent.dispatch('change:toggleNorthArrow', { showNorthArrow });
},
close() {
+ // Restore scales
+ if (!supressResolutionsRecalculation) {
+ const viewerResolutions = viewer.getResolutions();
+ for (let ix = 0; ix < viewerResolutions.length; ix += 1) {
+ viewerResolutions[ix] = originalResolutions[ix];
+ }
+ originalGrids.forEach((value, key) => {
+ // Sorry, but there is no setter and a map does not allow indexing.
+ // eslint-disable-next-line no-param-reassign
+ key.getSource().tileGrid = value;
+ });
+ // As we do a "dirty" update of resolutions we have to trigger a re-read of the limits, otherwise the outer limits still apply.
+ map.getView().setMinZoom(0);
+ map.getView().setMaxZoom(viewerResolutions.length - 1);
+ originalGrids.clear();
+ }
printMapComponent.removePrintControls();
if (map.getView().getRotation() !== 0) {
map.getView().setRotation(0);
@@ -354,6 +487,9 @@ const PrintComponent = function PrintComponent(options = {}) {
map.setTarget(printMapComponent.getId());
this.removeViewerControls();
printMapComponent.addPrintControls();
+ if (!supressResolutionsRecalculation) {
+ updateResolutions();
+ }
printMapComponent.dispatch('change:toggleScale', { showScale });
this.updatePageSize();
},
diff --git a/src/controls/print/set-scale-control.js b/src/controls/print/set-scale-control.js
index 5368f63c8..7bc0d7df8 100644
--- a/src/controls/print/set-scale-control.js
+++ b/src/controls/print/set-scale-control.js
@@ -7,12 +7,14 @@ export default function SetScaleControl(options = {}, map) {
initialScale
} = options;
- let projection;
- let resolutions;
let selectScale;
- function getScales() {
- return resolutions.map(resolution => mapUtils.resolutionToFormattedScale(resolution, projection));
+ /**
+ * Parses a formatted scale string and returns the denominator as a number
+ * @param {any} scale
+ */
+ function parseScale(scale) {
+ return parseInt(scale.replace(/\s+/g, '').split(':').pop(), 10);
}
return Component({
@@ -26,21 +28,15 @@ export default function SetScaleControl(options = {}, map) {
text: 'Välj skala'
});
this.addComponents([selectScale]);
- projection = map.getView().getProjection();
- resolutions = map.getView().getResolutions();
},
onChangeScale(evt) {
- const scaleDenominator = parseInt(evt.replace(/\s+/g, '').split(':').pop(), 10);
+ const scaleDenominator = parseScale(evt);
this.dispatch('change:scale', { scale: scaleDenominator / 1000 });
selectScale.setButtonText(evt);
},
onRender() {
this.dispatch('render');
- if (Array.isArray(scales) && scales.length) {
- selectScale.setItems(scales);
- } else {
- selectScale.setItems(getScales());
- }
+ selectScale.setItems(scales);
document.getElementById(selectScale.getId()).addEventListener('dropdown:select', (evt) => {
this.onChangeScale(evt.target.textContent);
});
@@ -48,8 +44,10 @@ export default function SetScaleControl(options = {}, map) {
this.onChangeScale(initialScale);
} else {
const viewResolution = map.getView().getResolution();
- const closest = resolutions.reduce((prev, curr) => (Math.abs(curr - viewResolution) < Math.abs(prev - viewResolution) ? curr : prev));
- this.onChangeScale(mapUtils.resolutionToFormattedScale(closest, projection));
+ const projection = map.getView().getProjection();
+ const mapScale = mapUtils.resolutionToScale(viewResolution, projection);
+ const closest = scales.reduce((prev, curr) => (Math.abs(parseScale(curr) - mapScale) < Math.abs(parseScale(prev) - mapScale) ? curr : prev));
+ this.onChangeScale(closest);
}
},
render() {
diff --git a/src/layer/wms.js b/src/layer/wms.js
index fe61a4c09..976542348 100644
--- a/src/layer/wms.js
+++ b/src/layer/wms.js
@@ -66,6 +66,7 @@ const wms = function wms(layerOptions, viewer) {
sourceOptions.tileGrid = viewer.getTileGrid();
if (wmsOptions.extent) {
+ // FIXME: there is no "extent" property to set. Code has no effect. Probably must create a new grid from viewer.getTileGridSettings .
sourceOptions.tileGrid.extent = wmsOptions.extent;
}
}
diff --git a/src/maputils.js b/src/maputils.js
index da9ba84ac..699e73c58 100644
--- a/src/maputils.js
+++ b/src/maputils.js
@@ -3,10 +3,14 @@ import TileGrid from 'ol/tilegrid/TileGrid';
import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
import Vector from 'ol/source/Vector';
+import VectorLayer from 'ol/layer/Vector';
+import Overlay from 'ol/Overlay';
import GeoJSON from 'ol/format/GeoJSON';
import { getTopLeft, getBottomLeft } from 'ol/extent';
import WKT from 'ol/format/WKT';
import numberFormatter from './utils/numberformatter';
+import Style from './style';
+import Popup from './popup';
const maputils = {
isWithinVisibleScales: function isWithinVisibleScales(scale, maxScale, minScale) {
@@ -130,6 +134,62 @@ const maputils = {
scaleValue += (10 - differens);
}
return scaleValue;
+ },
+ createMarker: function createMarker(coordinates, title, content, viewer) {
+ const featureStyles = {
+ stroke: {
+ color: [100, 149, 237, 1],
+ width: 4,
+ lineDash: null
+ },
+ fill: {
+ color: [100, 149, 237, 0.2]
+ },
+ circle: {
+ radius: 7,
+ stroke: {
+ color: [100, 149, 237, 1],
+ width: 2
+ },
+ fill: {
+ color: [255, 255, 255, 1]
+ }
+ }
+ };
+ const vectorStyles = Style.createStyleRule(featureStyles);
+ const layer = new VectorLayer({
+ source: new Vector({
+ features: [
+ new Feature({
+ geometry: new Point(coordinates)
+ })
+ ]
+ }),
+ style: vectorStyles
+ });
+ const popup = Popup(`#${viewer.getId()}`);
+ popup.setContent({
+ content,
+ title
+ });
+ popup.setTitle(title);
+ popup.setVisibility(true);
+ const popupEl = popup.getEl();
+ const popupHeight = document.querySelector('.o-popup').offsetHeight + 10;
+ popupEl.style.height = `${popupHeight}px`;
+ const overlay = new Overlay({
+ element: popupEl,
+ autoPan: {
+ margin: 55,
+ animation: {
+ duration: 500
+ }
+ },
+ positioning: 'bottom-center'
+ });
+ viewer.getMap().addOverlay(overlay);
+ overlay.setPosition(coordinates);
+ return layer;
}
};
diff --git a/src/viewer.js b/src/viewer.js
index 1deb96b15..dc6bf040e 100644
--- a/src/viewer.js
+++ b/src/viewer.js
@@ -288,12 +288,12 @@ const Viewer = function Viewer(targetOption, options = {}) {
if (capabilitiesLayers && Object.keys(capabilitiesLayers).length > 0) {
return layerlist.map(layer => {
let secure;
+ let layername = layer.name;
+ // remove workspace if syntax is workspace:layername
+ layername = layername.split(':').pop();
// remove double underscore plus a suffix from layer name
- let layername = '';
- if (layer.name.includes('__')) {
- layername = layer.name.substring(0, layer.name.lastIndexOf('__'));
- } else {
- layername = layer.name;
+ if (layername.includes('__')) {
+ layername = layername.substring(0, layername.lastIndexOf('__'));
}
const layerSourceOptions = layer.source ? getSource2(layer.source) : undefined;
if (layerSourceOptions && layerSourceOptions.capabilitiesURL) {
@@ -440,6 +440,11 @@ const Viewer = function Viewer(targetOption, options = {}) {
}
};
+ const addMarker = function addMarker(coordinates, title, content) {
+ const layer = maputils.createMarker(coordinates, title, content, this);
+ map.addLayer(layer);
+ };
+
const getUrlParams = function getUrlParams() {
return urlParams;
};
@@ -556,6 +561,7 @@ const Viewer = function Viewer(targetOption, options = {}) {
addLayers,
addSource,
addStyle,
+ addMarker,
getBreakPoints,
getCenter,
getClusterOptions,