qwc2
Version:
QGIS Web Client
245 lines (244 loc) • 9.13 kB
JavaScript
/**
* Copyright 2015-2016 GeoSolutions Sas
* Copyright 2016-2024 Sourcepole AG
* 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 isEmpty from 'lodash.isempty';
import ConfigUtils from './ConfigUtils';
import CoordinatesUtils from './CoordinatesUtils';
import { UrlParams } from './PermaLinkUtils';
var DEFAULT_SCREEN_DPI = 96;
var METERS_PER_UNIT = {
'm': 1,
'degrees': 111194.87428468118,
'ft': 0.3048,
'us-ft': 1200 / 3937
};
var hooks = {};
var MapUtils = {
GET_PIXEL_FROM_COORDINATES_HOOK: 'GET_PIXEL_FROM_COORDINATES_HOOK',
GET_COORDINATES_FROM_PIXEL_HOOK: 'GET_COORDINATES_FROM_PIXEL_HOOK',
GET_SNAPPED_COORDINATES_FROM_PIXEL_HOOK: 'GET_SNAPPED_COORDINATES_FROM_PIXEL_HOOK',
GET_NATIVE_LAYER: 'GET_NATIVE_LAYER',
ADD_POINTER_MOVE_LISTENER: 'ADD_POINTER_MOVE_LISTENER',
REMOVE_POINTER_MOVE_LISTENER: 'REMOVE_POINTER_MOVE_LISTENER',
GET_MAP: 'GET_MAP',
registerHook: function registerHook(name, hook) {
hooks[name] = hook;
},
getHook: function getHook(name) {
return hooks[name];
},
/**
* @param dpi {number} dot per inch resolution
* @return {number} dot per meter resolution
*/
dpi2dpm: function dpi2dpm(dpi) {
return (dpi || DEFAULT_SCREEN_DPI) * (100 / 2.54);
},
/**
* Get a list of scales for each zoom level of the Google Mercator.
* @param minZoom {number} min zoom level.
* @param maxZoom {number} max zoom level.
* @return {array} a list of scale for each zoom level in the given interval.
*/
getGoogleMercatorScales: function getGoogleMercatorScales(minZoom, maxZoom) {
var dpi = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_SCREEN_DPI;
// Google mercator params
var RADIUS = 6378137;
var TILE_WIDTH = 256;
var ZOOM_FACTOR = 2;
var retval = [];
for (var l = minZoom; l <= maxZoom; l++) {
retval.push(2 * Math.PI * RADIUS / (TILE_WIDTH * Math.pow(ZOOM_FACTOR, l) / MapUtils.dpi2dpm(dpi)));
}
return retval;
},
/**
* @param scales {array} list of scales.
* @param projection {string} map projection.
* @param dpi {number} screen resolution in dots per inch.
* @return {array} a list of resolutions corresponding to the given scales, projection and dpi.
*/
getResolutionsForScales: function getResolutionsForScales(scales, projection) {
var dpi = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_SCREEN_DPI;
var units = CoordinatesUtils.getUnits(projection);
var resolutions = scales.map(function (scale) {
return scale * 0.0254 / (dpi * METERS_PER_UNIT[units]);
});
return resolutions;
},
/**
* Calculates the best fitting zoom level for the given extent.
*
* @param extent {Array} [minx, miny, maxx, maxy]
* @param resolutions {Array} The list of available map resolutions
* @param mapSize {Object} current size of the map.
* @param minZoom {number} min zoom level.
* @param maxZoom {number} max zoom level.
* @param dpi {number} screen resolution in dot per inch.
* @return {Number} the zoom level fitting th extent
*/
getZoomForExtent: function getZoomForExtent(extent, resolutions, mapSize, minZoom, maxZoom) {
var wExtent = extent[2] - extent[0];
var hExtent = extent[3] - extent[1];
var xResolution = Math.abs(wExtent / mapSize.width);
var yResolution = Math.abs(hExtent / mapSize.height);
var extentResolution = Math.max(xResolution, yResolution);
var zoom = this.computeZoom(resolutions, extentResolution);
if (ConfigUtils.getConfigProp("allowFractionalZoom") === true) {
return Math.max(minZoom, Math.min(zoom, maxZoom));
} else {
return Math.max(minZoom, Math.min(Math.round(zoom), maxZoom));
}
},
/**
* Calculates the extent for the provided center and zoom level
* @param center {Array} [x, y]
* @param zoom {number} The zoom level
* @param resolutions {Array} The list of map resolutions
* @param mapSize {Object} The current size of the map
*/
getExtentForCenterAndZoom: function getExtentForCenterAndZoom(center, zoom, resolutions, mapSize) {
if (ConfigUtils.getConfigProp("allowFractionalZoom") !== true) {
zoom = Math.round(zoom);
}
var width = this.computeForZoom(resolutions, zoom) * mapSize.width;
var height = this.computeForZoom(resolutions, zoom) * mapSize.height;
return [center[0] - 0.5 * width, center[1] - 0.5 * height, center[0] + 0.5 * width, center[1] + 0.5 * height];
},
/**
* Transform width and height specified in meters to the units of the specified projection
*
* @param projection {string} projection.
* @param center {Array} Center of extent in EPSG:4326 coordinates.
* @param width {number} Width in meters.
* @param height {number} Height in meters.
*/
transformExtent: function transformExtent(projection, center, width, height) {
var units = CoordinatesUtils.getUnits(projection);
if (units === 'ft') {
return {
width: width / METERS_PER_UNIT.ft,
height: height / METERS_PER_UNIT.ft
};
} else if (units === 'us-ft') {
return {
width: width / METERS_PER_UNIT['us-ft'],
height: height / METERS_PER_UNIT['us-ft']
};
} else if (units === 'degrees') {
// https://en.wikipedia.org/wiki/Geographic_coordinate_system#Length_of_a_degree
var phi = center[1] / 180 * Math.PI;
var latPerM = 111132.92 - 559.82 * Math.cos(2 * phi) + 1.175 * Math.cos(4 * phi) - 0.0023 * Math.cos(6 * phi);
var lonPerM = 111412.84 * Math.cos(phi) - 93.5 * Math.cos(3 * phi) + 0.118 * Math.cos(5 * phi);
return {
width: width / lonPerM,
height: height / latPerM
};
}
return {
width: width,
height: height
};
},
/**
* Compute the scale or resolution matching a (possibly fractional) zoom level.
*
* @param list {Array} List of scales or resolutions.
* @param zoomLevel (number) Zoom level (integer or fractional).
* @return Scale of resolution matching zoomLevel
*/
computeForZoom: function computeForZoom(list, zoomLevel) {
if (ConfigUtils.getConfigProp("allowFractionalZoom") !== true) {
return list[Math.min(list.length - 1, Math.round(zoomLevel))];
}
zoomLevel = Math.max(zoomLevel, 0);
var upper = Math.ceil(zoomLevel);
var lower = Math.floor(zoomLevel);
if (upper >= list.length) {
return list[list.length - 1];
}
var frac = zoomLevel - lower;
return list[lower] * (1 - frac) + list[upper] * frac;
},
/**
* Compute the (possibly fractional) zoom level matching the specified scale or resolution.
*
* @param list {Array} List of scales or resolutions.
* @param value (number) Scale or resolution.
* @return Zoom level matching the specified scale or resolution.
*/
computeZoom: function computeZoom(list, value) {
if (ConfigUtils.getConfigProp("allowFractionalZoom") === true) {
var index = 0;
for (var i = 1; i < list.length - 1; ++i) {
if (value <= list[i]) {
index = i;
}
}
return index + (value - list[index]) / (list[index + 1] - list[index]);
} else {
var closestVal = Math.abs(value - list[0]);
var closestIdx = 0;
for (var _i = 1; _i < list.length; ++_i) {
var currVal = Math.abs(value - list[_i]);
if (currVal < closestVal) {
closestVal = currVal;
closestIdx = _i;
}
}
return closestIdx;
}
},
/**
* Convert degrees to radians
* @param degrees {number}
* @return {number} in radians
*/
degreesToRadians: function degreesToRadians(degrees) {
var pi = Math.PI;
return degrees * (pi / 180);
},
updateMapUrlParams: function updateMapUrlParams(state) {
var newParams = {};
var positionFormat = ConfigUtils.getConfigProp("urlPositionFormat");
var positionCrs = ConfigUtils.getConfigProp("urlPositionCrs") || state.projection;
var prec = CoordinatesUtils.getPrecision(positionCrs);
// Don't set params for empty map
if (state.bbox.bounds.every(function (x) {
return x === 0;
})) {
return;
}
if (positionFormat === "centerAndZoom") {
var center = CoordinatesUtils.reproject(state.center, state.projection, positionCrs);
var scale = Math.round(MapUtils.computeForZoom(state.scales, state.zoom));
Object.assign(newParams, {
c: center.map(function (x) {
return x.toFixed(prec);
}).join(","),
s: scale
});
} else {
var bounds = CoordinatesUtils.reprojectBbox(state.bbox.bounds, state.projection, positionCrs);
Object.assign(newParams, {
e: bounds.map(function (x) {
return x.toFixed(prec);
}).join(",")
});
}
if (positionCrs !== state.projection) {
Object.assign(newParams, {
crs: positionCrs
});
}
if (!isEmpty(newParams)) {
UrlParams.updateParams(newParams);
}
}
};
export default MapUtils;