UNPKG

qwc2

Version:
245 lines (244 loc) 9.13 kB
/** * 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;