diff --git a/src/common/mapping/MapStyle.js b/src/common/mapping/MapStyle.js index 6446f273e..9b9bceb83 100644 --- a/src/common/mapping/MapStyle.js +++ b/src/common/mapping/MapStyle.js @@ -2,7 +2,7 @@ import { WebMapService } from './WebMapService'; import { SourceListModelV2 } from './utils/SourceListModelV2'; import { createAppreciableLayerId, isSameRasterLayer } from './utils/util'; -export function createMapStyleExtending(SuperClass, { MapManager, mapRepo }) { +export function createMapStyleExtending(SuperClass, { MapManager, crsManager }) { return class MapStyle extends SuperClass { constructor(id, options = {}, mapOptions = {}) { super(); @@ -11,24 +11,22 @@ export function createMapStyleExtending(SuperClass, { MapManager, mapRepo }) { this.webMapService = new WebMapService(id, options); this._layerIdRenameMapList = []; this._appendLayers = false; + this._baseProjection = ''; } initializeMap(_, map) { + this._baseProjection = this._registerMapCRS(this.mapOptions); if (map) { + if (!crsManager.isSameProjection(map, this._baseProjection)) { + this.fire('projectionnotmatch'); + return; + } this._appendLayers = true; this.map = map; this._addLayersToMap(); return; } this.mapOptions.container = this.options.target; - if (typeof this.mapOptions.crs === 'object' && this.mapOptions.crs.epsgCode) { - this.mapOptions.crs = new mapRepo.CRS( - this.mapOptions.crs.epsgCode, - this.mapOptions.crs.WKT, - this.mapOptions.crs.extent, - this.mapOptions.crs.unit - ); - } if (!this.mapOptions.transformRequest) { this.mapOptions.transformRequest = (url, resourceType) => { let proxy = ''; @@ -53,7 +51,7 @@ export function createMapStyleExtending(SuperClass, { MapManager, mapRepo }) { if (Object.prototype.hasOwnProperty.call(this.mapOptions, 'fadeDuration')) { fadeDuration = this.mapOptions.fadeDuration; } - this.map = new MapManager({ ...this.mapOptions, fadeDuration }); + this.map = new MapManager({ ...this.mapOptions, fadeDuration, crs: this._baseProjection }); this.fire('mapinitialized', { map: this.map }); this.map.on('load', () => { this._sendMapToUser(); @@ -71,6 +69,17 @@ export function createMapStyleExtending(SuperClass, { MapManager, mapRepo }) { } } + _registerMapCRS(mapOptions) { + const { crs } = mapOptions; + let epsgCode = crs; + if (typeof crs === 'object' && crs.epsgCode) { + const { epsgCode: name, WKT: wkt, extent, unit } = crs; + crsManager.registerCRS({ name, wkt, extent, unit }); + epsgCode = name; + } + return epsgCode; + } + _addLayersToMap() { const { sources, layers, layerIdMapList } = this._setUniqueId(this.mapOptions.style); layers.forEach((layer) => { diff --git a/src/common/mapping/WebMapBase.js b/src/common/mapping/WebMapBase.js index 2a202c90e..7268c2682 100644 --- a/src/common/mapping/WebMapBase.js +++ b/src/common/mapping/WebMapBase.js @@ -172,7 +172,6 @@ export function createWebMapBaseExtending(SuperClass, { mapRepo }) { this._mapInitializedHandler = this._mapInitializedHandler.bind(this); this._mapCreateSucceededHandler = this._mapCreateSucceededHandler.bind(this); this._addLayerChangedHandler = this._addLayerChangedHandler.bind(this); - this._initWebMap(!this.map); } /** @@ -193,33 +192,6 @@ export function createWebMapBaseExtending(SuperClass, { mapRepo }) { } } - /** - * @function WebMapBase.prototype.setCRS - * @description 更新地图投影。 - * @param {string|Object} crs - 地图 crs。 - */ - setCRS(crs) { - if (this.map) { - this.mapOptions.crs = crs; - if (this.mapOptions.crs) { - if (this.map.getCRS(typeof crs === 'string' ? crs : crs.epsgCode)) { - return; - } - if (crs.epsgCode) { - this.mapOptions.crs = new mapRepo.CRS( - this.mapOptions.crs.epsgCode, - this.mapOptions.crs.WKT, - this.mapOptions.crs.extent, - this.mapOptions.crs.unit - ); - this.map.setCRS(this.mapOptions.crs); - } else { - this.map.setCRS(mapRepo.CRS.get(crs)); - } - } - } - } - /** * @function WebMapBase.prototype.setCenter * @description 更新地图中心点。 @@ -500,6 +472,10 @@ export function createWebMapBaseExtending(SuperClass, { mapRepo }) { this.clean(false); } + _readyForInitializingWebMap() { + this._initWebMap(!this.map); + } + _initWebMap(clean = true) { clean && this.clean(); if (this.webMapInfo) { @@ -660,6 +636,7 @@ export function createWebMapBaseExtending(SuperClass, { mapRepo }) { }; } this.type = type; + // initializeMap 完成3个步骤:1. 注册投影 2. 判断投影与存在的map是否一致 3. 创建地图 this._handler.initializeMap(_mapInfo, this.map); } diff --git a/src/common/mapping/WebMapV2.js b/src/common/mapping/WebMapV2.js index 247fa58c6..6711f3587 100644 --- a/src/common/mapping/WebMapV2.js +++ b/src/common/mapping/WebMapV2.js @@ -2,7 +2,7 @@ * This program are made available under the terms of the Apache License, Version 2.0 * which accompanies this distribution and is available at http://www.apache.org/licenses/LICENSE-2.0.html. */ import cloneDeep from 'lodash.clonedeep'; -import { getProjection, registerProjection, toEpsgCode, transformCoodinates } from './utils/epsg-define'; +import { getProjection, toEpsgCode, transformCoodinates } from './utils/epsg-define'; import { ColorsPickerUtil } from '../util/ColorsPickerUtil'; import { Util } from '../commontypes/Util'; import { ArrayStatistic } from '../util/ArrayStatistic'; @@ -17,7 +17,7 @@ const INTERNET_MAP_BOUNDS = { BING: [-180, -90, 180, 90] } -export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, DataFlowService, GraticuleLayer }) { +export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, crsManager, DataFlowService, GraticuleLayer }) { return class WebMapV2 extends SuperClass { constructor( id, @@ -49,12 +49,23 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, DataF this._appendLayers = false; } - initializeMap(mapInfo, map) { - if (map) { - this._appendLayers = true; - this.map = map; + async initializeMap(mapInfo, map) { + try { + this.baseProjection = await this._registerMapCRS(mapInfo); + if (map) { + if (!crsManager.isSameProjection(map, this.baseProjection) && !this.ignoreBaseProjection) { + this.fire('projectionnotmatch'); + return; + } + this._appendLayers = true; + this.map = map; + } + this._mapInfo = mapInfo; + this._loadLayers(mapInfo, this._taskID); + } catch (error) { + console.error(error); + this.fire('mapcreatefailed', { error }); } - this._getMapInfo(mapInfo, this._taskID); } cleanLayers(layers) { @@ -95,12 +106,10 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, DataF _initWebMap() {} + _getMapInfo() {} + _loadLayers(mapInfo, _taskID) { if (this.map) { - if (this.map.getCRS().epsgCode !== this.baseProjection && !this.ignoreBaseProjection) { - this.fire('projectionnotmatch', {}); - return; - } this._handleLayerInfo(mapInfo, _taskID); } else { setTimeout(() => { @@ -112,95 +121,59 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, DataF } } - _setCRS(baseProjection, wkt, bounds) { - if (mapRepo.CRS.get(baseProjection)) { - return; - } - const crs = new mapRepo.CRS(baseProjection, wkt, bounds, bounds[2] > 180 ? 'meter' : 'degree'); - mapRepo.CRS.set(crs); - } - - _getMapInfo(mapInfo, _taskID) { - this._mapInfo = mapInfo; - const { projection } = mapInfo; - let bounds, wkt; - this.baseProjection = toEpsgCode(projection); - let defaultWktValue = getProjection(this.baseProjection, this.specifiedProj4); - - if (defaultWktValue) { - wkt = defaultWktValue; - } - if (!mapRepo.CRS.get(this.baseProjection)) { - if (mapInfo.baseLayer && mapInfo.baseLayer.layerType === 'MAPBOXSTYLE') { - let url = mapInfo.baseLayer.dataSource.url; - if (url.indexOf('/restjsr/') > -1 && !/\/style\.json$/.test(url)) { - url += '/style.json'; - } - this.webMapService.getMapBoxStyle(url).then((res) => { + async _registerMapCRS(mapInfo) { + const { projection, extent, baseLayer = {} } = mapInfo; + const epsgCode = toEpsgCode(projection); + let crs = { + name: epsgCode, + extent: [extent.leftBottom.x, extent.leftBottom.y, extent.rightTop.x, extent.rightTop.y], + wkt: this._getProjectionWKT(projection) + }; + if (!crsManager.getCRS(epsgCode)) { + switch (baseLayer.layerType) { + case 'MAPBOXSTYLE': { + let url = baseLayer.dataSource.url; + if (url.indexOf('/restjsr/') > -1 && !/\/style\.json$/.test(url)) { + url += '/style.json'; + } + const res = await this.webMapService.getMapBoxStyle(url); if (res && res.metadata && res.metadata.indexbounds) { - bounds = res.metadata.indexbounds; - } else { - bounds = [ - mapInfo.extent.leftBottom.x, - mapInfo.extent.leftBottom.y, - mapInfo.extent.rightTop.x, - mapInfo.extent.rightTop.y - ]; + crs.extent = res.metadata.indexbounds; } - this._defineProj4(projection); - this._setCRS(this.baseProjection, wkt, bounds); - this._loadLayers(mapInfo, _taskID); - }); - } else if (mapInfo.baseLayer && mapInfo.baseLayer.layerType === 'TILE') { - // 获取地图的wkt - this.getEpsgCodeWKT(`${mapInfo.baseLayer.url}/prjCoordSys.wkt`, { - withoutFormatSuffix: true, - withCredentials: this.webMapService.handleWithCredentials('', mapInfo.baseLayer.url, false) - }).then((res) => { - if (!wkt) { - wkt = res; + break; + } + case 'TILE': { + // 获取地图的wkt + if (!crs.wkt) { + crs.wkt = await this.getEpsgCodeWKT(`${baseLayer.url}/prjCoordSys.wkt`, { + withoutFormatSuffix: true, + withCredentials: this.webMapService.handleWithCredentials('', baseLayer.url, false) + }); } - this.getBounds(`${mapInfo.baseLayer.url}.json`, { + const boundsRes = await this.getBounds(`${baseLayer.url}.json`, { withoutFormatSuffix: true, - withCredentials: this.webMapService.handleWithCredentials('', mapInfo.baseLayer.url, false) - }).then((res) => { - if (res && res.bounds) { - bounds = [ - res.bounds.leftBottom.x, - res.bounds.leftBottom.y, - res.bounds.rightTop.x, - res.bounds.rightTop.y - ]; - } else { - bounds = [ - mapInfo.extent.leftBottom.x, - mapInfo.extent.leftBottom.y, - mapInfo.extent.rightTop.x, - mapInfo.extent.rightTop.y - ]; - } - this._defineProj4(wkt, projection); - this._setCRS(this.baseProjection, wkt, bounds); - this._loadLayers(mapInfo, _taskID); + withCredentials: this.webMapService.handleWithCredentials('', baseLayer.url, false) }); - }); - } else { - const error = new Error('Unsupported coordinate system!'); - console.log(error); - this.fire('mapcreatefailed', { error }); + if (boundsRes && boundsRes.bounds) { + crs.extent = [ + boundsRes.bounds.leftBottom.x, + boundsRes.bounds.leftBottom.y, + boundsRes.bounds.rightTop.x, + boundsRes.bounds.rightTop.y + ]; + } + break; + } + default: + crs = null; + break; } - } else { - wkt = mapRepo.CRS.get(this.baseProjection).WKT - this._defineProj4(wkt || projection); - bounds = [ - mapInfo.extent.leftBottom.x, - mapInfo.extent.leftBottom.y, - mapInfo.extent.rightTop.x, - mapInfo.extent.rightTop.y - ]; - this._setCRS(this.baseProjection, wkt, bounds); - this._loadLayers(mapInfo, _taskID); } + if (!crs) { + throw new Error('Unsupported coordinate system!') + } + crsManager.registerCRS(crs); + return epsgCode; } _handleLayerInfo(mapInfo, _taskID) { @@ -499,7 +472,7 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, DataF layerInfo.dataSource && layerInfo.dataSource.type !== 'REST_DATA' ) { - this._unprojectProjection = this._defineProj4(projection); + this._unprojectProjection = toEpsgCode(projection); features = this.transformFeatures(features); } @@ -2772,15 +2745,10 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, DataF return tiandituUrls; } - _defineProj4(projection, defaultEpsgCode) { + _getProjectionWKT(projection) { let epsgCode = toEpsgCode(projection); const reg = /^EPSG:/; - const defValue = epsgCode && projection.match(reg) ? getProjection(epsgCode, this.specifiedProj4) : projection; - if (!epsgCode && defaultEpsgCode && defaultEpsgCode.match(reg)) { - epsgCode = defaultEpsgCode; - } - registerProjection(epsgCode, defValue, this.specifiedProj4); - return epsgCode; + return epsgCode && projection.match(reg) ? getProjection(epsgCode, this.specifiedProj4) : projection; } _fetchRequest(url, type, options) { @@ -2805,7 +2773,7 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, DataF getBounds(baseUrl, options) { if (!baseUrl) { - return; + return Promise.resolve(null); } return this._fetchRequest(baseUrl, 'json', options); } diff --git a/src/common/mapping/WebMapV3.js b/src/common/mapping/WebMapV3.js index 951231c87..308f6070d 100644 --- a/src/common/mapping/WebMapV3.js +++ b/src/common/mapping/WebMapV3.js @@ -158,7 +158,7 @@ export const LEGEND_STYLE_TYPES = { IMAGE: 'image', STYLE: 'style' }; -export function createWebMapV3Extending(SuperClass, { MapManager, mapRepo, mapRepoName, l7LayerUtil }) { +export function createWebMapV3Extending(SuperClass, { MapManager, mapRepo, crsManager, l7LayerUtil }) { return class WebMapV3 extends SuperClass { constructor(mapId, options, mapOptions = {}) { super(); @@ -175,13 +175,14 @@ export function createWebMapV3Extending(SuperClass, { MapManager, mapRepo, mapRe initializeMap(mapInfo, map) { this._mapInfo = mapInfo; - const proj = this._setBaseProjection(); - if (!proj) { - return; - } + this._baseProjection = this._registerMapCRS(mapInfo); if (map) { - this._appendLayers = true; this.map = map; + if (!crsManager.isSameProjection(this.map, this._baseProjection)) { + this.fire('projectionnotmatch'); + return; + } + this._appendLayers = true; // 处理图层管理添加 sprite const sprite = this._mapInfo.sprite; if (sprite) { @@ -323,29 +324,14 @@ export function createWebMapV3Extending(SuperClass, { MapManager, mapRepo, mapRe }); } - _setBaseProjection() { - let crs = this._mapInfo.crs; - let baseProjection = crs; + _registerMapCRS(mapInfo) { + const { crs } = mapInfo; + let epsgCode = crs; if (typeof crs === 'object') { - baseProjection = crs.name; - if (!mapRepo.CRS) { - const error = `The EPSG code ${baseProjection} needs to include ${mapRepoName}-enhance.js. Refer to the example: https://iclient.supermap.io/examples/${mapRepoName.replace('-', '')}/editor.html#mvtVectorTile_2362`; - this.fire('mapcreatefailed', { error: error }); - console.error(error); - return; - } - this._setCRS(crs); - } - this._baseProjection = baseProjection; - return this._baseProjection; - } - - _setCRS({ name, wkt, extent }) { - if (mapRepo.CRS.get(name)) { - return; + crsManager.registerCRS(crs); + epsgCode = crs.name; } - const crs = new mapRepo.CRS(name, wkt, extent, extent[2] > 180 ? 'meter' : 'degree'); - mapRepo.CRS.set(crs); + return epsgCode; } /** @@ -354,10 +340,6 @@ export function createWebMapV3Extending(SuperClass, { MapManager, mapRepo, mapRe * @description emit 图层加载成功事件。 */ async _initLayers() { - if (this.map.getCRS && this.map.getCRS().epsgCode !== this._baseProjection) { - this.fire('projectionnotmatch'); - return; - } await this._getSpriteDatas(); if (Object.prototype.toString.call(this.mapId) === '[object Object]') { this.mapParams = { diff --git a/src/common/mapping/utils/epsg-define.js b/src/common/mapping/utils/epsg-define.js index 83f6339a9..ede2fa5e2 100644 --- a/src/common/mapping/utils/epsg-define.js +++ b/src/common/mapping/utils/epsg-define.js @@ -35,7 +35,7 @@ export function getProjection(epsgKey, proj4 = proj4FromNpm) { 'GEOGCS["China Geodetic Coordinate System 2000", DATUM["China 2000", SPHEROID["CGCS2000", 6378137.0, 298.257222101, AUTHORITY["EPSG","1024"]], AUTHORITY["EPSG","1043"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["degree", 0.017453292519943295], AXIS["Geodetic latitude", NORTH], AXIS["Geodetic longitude", EAST], AUTHORITY["EPSG","4490"]]' }; if (epsgCodes[epsgKey]) { - registerProjection(epsgKey, epsgCodes[epsgKey]); + registerProjection(epsgKey, epsgCodes[epsgKey], proj4); } } return proj4.defs(epsgKey); diff --git a/src/mapboxgl/mapping/WebMap.js b/src/mapboxgl/mapping/WebMap.js index 03cb934eb..6fbbda329 100644 --- a/src/mapboxgl/mapping/WebMap.js +++ b/src/mapboxgl/mapping/WebMap.js @@ -10,71 +10,118 @@ import { featureFilter, expression } from '@mapbox/mapbox-gl-style-spec'; import spec from '@mapbox/mapbox-gl-style-spec/reference/v8'; import { L7Layer, L7 } from '../overlay/L7Layer'; import MapManager from './webmap/MapManager'; +import { CRSManager } from './webmap/CRSManager'; import { DataFlowService } from '../services/DataFlowService'; import { GraticuleLayer } from '../overlay/GraticuleLayer'; /** - * @class WebMap - * @version 9.1.2 - * @extends {WebMapBase} - * @category iPortal/Online Resources Map - * @classdesc 对接 iPortal/Online 地图类。 - *
- *

Notice

- *

该功能可能依赖以下插件,请确认引入该插件。

- *

geostats: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/geostats/geostats.js"></script>

- *

jsonsql: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/jsonsql/jsonsql.js"></script>

- *

EchartLayer: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/echartsLayer/EchartsLayer.min.js"></script>

- *

echarts: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/echarts/5.5.0/echarts.min.js"></script>

- *

L7: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/mapboxgl-l7-render/0.0.1/mapboxgl-l7-render.js"></script>

- *

G2: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/antv/g2/4.2.8/g2.min.js"></script>

- *

G6: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/antv/g6/4.3.2/g6.min.js"></script>

- *

fast-xml-parser: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/fast-xml-parser/4.2.7/fxparser.min.js"></script>

- *

mapbox-gl-js-enhance: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/mapbox-gl-js-enhance/1.12.1-7/mapbox-gl-enhance.js"></script>

- *
- * @modulecategory Mapping - * @param {number} id - iPortal|Online 地图 ID。 - * @param {Object} options - 基础参数。 - * @param {string} [options.target='map'] - 地图容器 ID。 - * @param {string} [options.server="https://www.supermapol.com"] - 地图的地址。当设置 `id` 时有效。 - * @param {string} [options.credentialKey] - 凭证密钥。当设置 `id` 时有效。 - * @param {string} [options.credentialValue] - 凭证值。当设置 `id` 时有效。 - * @param {string} [options.tiandituKey] - 用于访问天地图的服务。当设置 `id` 时有效。 - * @param {string} [options.googleMapsAPIKey] - 用于访问谷歌地图。当设置 `id` 时有效。 - * @param {string} [options.googleMapsLanguage] - 用于定义在谷歌地图图块上显示标签的语言。当设置 `id` 且底图为谷歌地图时有效。 - * @param {boolean} [options.withCredentials=false] - 请求是否携带 cookie。当设置 `id` 时有效。 - * @param {boolean} [options.excludePortalProxyUrl] - server 传递过来的 URL 是否带有代理。当设置 `id` 时有效。 - * @param {boolean} [options.ignoreBaseProjection = false] - 是否忽略底图坐标系和叠加图层坐标系不一致。 - * @param {boolean} [options.isSuperMapOnline] - 是否是 SuperMap Online 地图。 - * @param {string} [options.iportalServiceProxyUrlPrefix] - iportal的代理服务地址前缀。 - * @param {string|boolean} [options.proxy] - HTTP 请求代理地址 。布尔值表示使用 iPortal 默认代理地址。 - * @param {Object} mapOptions - 地图参数。 - * @param {Array} [mapOptions.center] - 中心点。 - * @param {number} [mapOptions.zoom] - 缩放级别。 - * @param {number} [mapOptions.bearing] - 旋转角度。 - * @param {number} [mapOptions.pitch] - 倾角。 - * @param {string|Object} [mapOptions.crs] - 投影。 - * @param {boolean} [mapOptions.renderWorldCopies] - 连续渲染。 - * @param {number} [mapOptions.rasterTileSize] - 栅格瓦片大小。 - * @param {Object} [mapOptions.style] - style 样式。 - * @fires WebMap#mapinitialized - * @fires WebMap#mapcreatesucceeded - * @fires WebMap#mapcreatefailed - * @fires WebMap#addlayerssucceeded - * @fires WebMap#layercreatefailed - * @fires WebMap#baidumapnotsupport - * @fires WebMap#layerorsourcenameduplicated - * @fires WebMap#dataflowfeatureupdated - * @fires WebMap#projectionnotmatch - * @fires WebMap#mapbeforeremove - * @fires WebMap#getmapfailed - * @fires WebMap#getlayersfailed - * @usage + * @class WebMap + * @version 9.1.2 + * @extends {WebMapBase} + * @category iPortal/Online Resources Map + * @classdesc 对接 iPortal/Online 地图类。 + *
+ *

Notice

+ *

该功能可能依赖以下插件,请确认引入该插件。

+ *

geostats: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/geostats/geostats.js"></script>

+ *

jsonsql: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/jsonsql/jsonsql.js"></script>

+ *

EchartLayer: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/echartsLayer/EchartsLayer.min.js"></script>

+ *

echarts: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/echarts/5.5.0/echarts.min.js"></script>

+ *

L7: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/mapboxgl-l7-render/0.0.1/mapboxgl-l7-render.js"></script>

+ *

G2: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/antv/g2/4.2.8/g2.min.js"></script>

+ *

G6: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/antv/g6/4.3.2/g6.min.js"></script>

+ *

fast-xml-parser: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/fast-xml-parser/4.2.7/fxparser.min.js"></script>

+ *

mapbox-gl-js-enhance: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/mapbox-gl-js-enhance/1.12.1-7/mapbox-gl-enhance.js"></script>

+ *
+ * @modulecategory Mapping + * @param {number} id - iPortal|Online 地图 ID。 + * @param {Object} options - 基础参数。 + * @param {string} [options.target='map'] - 地图容器 ID。 + * @param {string} [options.server="https://www.supermapol.com"] - 地图的地址。当设置 `id` 时有效。 + * @param {string} [options.credentialKey] - 凭证密钥。当设置 `id` 时有效。 + * @param {string} [options.credentialValue] - 凭证值。当设置 `id` 时有效。 + * @param {string} [options.tiandituKey] - 用于访问天地图的服务。当设置 `id` 时有效。 + * @param {string} [options.googleMapsAPIKey] - 用于访问谷歌地图。当设置 `id` 时有效。 + * @param {string} [options.googleMapsLanguage] - 用于定义在谷歌地图图块上显示标签的语言。当设置 `id` 且底图为谷歌地图时有效。 + * @param {boolean} [options.withCredentials=false] - 请求是否携带 cookie。当设置 `id` 时有效。 + * @param {boolean} [options.excludePortalProxyUrl] - server 传递过来的 URL 是否带有代理。当设置 `id` 时有效。 + * @param {boolean} [options.ignoreBaseProjection = false] - 是否忽略底图坐标系和叠加图层坐标系不一致。 + * @param {boolean} [options.isSuperMapOnline] - 是否是 SuperMap Online 地图。 + * @param {string} [options.iportalServiceProxyUrlPrefix] - iportal的代理服务地址前缀。 + * @param {string|boolean} [options.proxy] - HTTP 请求代理地址 。布尔值表示使用 iPortal 默认代理地址。 + * @param {Object} mapOptions - 地图参数。 + * @param {Array} [mapOptions.center] - 中心点。 + * @param {number} [mapOptions.zoom] - 缩放级别。 + * @param {number} [mapOptions.bearing] - 旋转角度。 + * @param {number} [mapOptions.pitch] - 倾角。 + * @param {string|Object} [mapOptions.crs] - 投影。 + * @param {boolean} [mapOptions.renderWorldCopies] - 连续渲染。 + * @param {number} [mapOptions.rasterTileSize] - 栅格瓦片大小。 + * @param {Object} [mapOptions.style] - style 样式。 + * @fires WebMap#mapinitialized + * @fires WebMap#mapcreatesucceeded + * @fires WebMap#mapcreatefailed + * @fires WebMap#addlayerssucceeded + * @fires WebMap#layercreatefailed + * @fires WebMap#baidumapnotsupport + * @fires WebMap#layerorsourcenameduplicated + * @fires WebMap#dataflowfeatureupdated + * @fires WebMap#projectionnotmatch + * @fires WebMap#mapbeforeremove + * @fires WebMap#getmapfailed + * @fires WebMap#getlayersfailed + * @usage */ export class WebMap extends createWebMapBaseExtending(mapboxgl.Evented, { mapRepo: mapboxgl }) { + constructor(id, options, mapOptions) { + const crsManager = new CRSManager(options && options.proj4); + const proj4 = crsManager.getProj4(); + super(id, { ...options, proj4 }, mapOptions); + this._crsManager = crsManager; + if (!mapboxgl.CRS) { + const error = + 'WebMap needs to include mapbox-gl-enhance.js. Refer to the example: https://iclient.supermap.io/examples/mapboxgl/editor.html#mvtVectorTile_2362'; + Promise.resolve().then(() => { + this._fire('mapcreatefailed', { error: error }); + }); + } else { + this._readyForInitializingWebMap(); + } + } + + /** + * @function WebMap.prototype.setCRS + * @description 更新地图投影。 + * @param {string|Object} crs - 地图 crs。 + */ + setCRS(crs) { + if (this.map) { + this.mapOptions.crs = crs; + if (this.mapOptions.crs) { + const crsVal = crs.epsgCode + ? this._crsManager.registerCRS({ name: crs.epsgCode, wkt: crs.WKT, ...this.mapOptions.crs }, false) + : this._crsManager.getCRS(crs); + this.map.setCRS(crsVal); + } + } + } + _createWebMapFactory(type) { - const commonFactoryOptions = { MapManager, mapRepo: mapboxgl, mapRepoName: 'mapbox-gl', DataFlowService, GraticuleLayer }; - const l7LayerUtil = L7LayerUtil({ featureFilter, expression, spec, L7Layer, L7, proj4: this.options.proj4 }); + const commonFactoryOptions = { + MapManager, + mapRepo: mapboxgl, + crsManager: this._crsManager, + DataFlowService, + GraticuleLayer + }; + const l7LayerUtil = L7LayerUtil({ + featureFilter, + expression, + spec, + L7Layer, + L7, + proj4: this._crsManager.getProj4() + }); switch (type) { case 'MapStyle': return createMapStyleExtending(createMapClassExtending(mapboxgl.Evented), commonFactoryOptions); diff --git a/src/mapboxgl/mapping/webmap/CRSManager.js b/src/mapboxgl/mapping/webmap/CRSManager.js new file mode 100644 index 000000000..f5e4bde86 --- /dev/null +++ b/src/mapboxgl/mapping/webmap/CRSManager.js @@ -0,0 +1,32 @@ +import mapboxgl from 'mapbox-gl'; + +export class CRSManager { + constructor(proj4) { + this.proj4 = proj4; + this.builtInEPSG = ['EPSG:3857', 'EPSG:4326']; + } + + isSameProjection(map, epsgCode) { + return map.getCRS().epsgCode === epsgCode; + } + + getProj4() { + return this.proj4 || mapboxgl.proj4; + } + + getCRS(epsgCode) { + return mapboxgl.CRS.get(epsgCode); + } + + registerCRS({ name, wkt, extent, unit }, autoRegister = true) { + let crs = mapboxgl.CRS.get(name); + if (!crs) { + crs = new mapboxgl.CRS(name, wkt, extent, unit || extent[2] > 180 ? 'meter' : 'degree'); + autoRegister && mapboxgl.CRS.set(crs); + } + if (this.proj4 && !this.builtInEPSG.includes(name)) { + this.proj4.defs(name, crs.getWKT()); + } + return crs; + } +} diff --git a/src/maplibregl/mapping/WebMap.js b/src/maplibregl/mapping/WebMap.js index 92339ba2e..8524db29d 100644 --- a/src/maplibregl/mapping/WebMap.js +++ b/src/maplibregl/mapping/WebMap.js @@ -10,71 +10,118 @@ import { featureFilter, expression } from '@maplibre/maplibre-gl-style-spec'; import spec from '@maplibre/maplibre-gl-style-spec/src/reference/v8'; import { L7Layer, L7 } from '../overlay/L7Layer'; import MapManager from './webmap/MapManager'; +import { CRSManager } from './webmap/CRSManager'; import { DataFlowService } from '../services/DataFlowService'; import { GraticuleLayer } from '../overlay/GraticuleLayer'; /** - * @class WebMap - * @version 11.3.0 - * @extends {WebMapBase} - * @category iPortal/Online Resources Map - * @classdesc 对接 iPortal/Online 地图类。 - *
- *

Notice

- *

该功能可能依赖以下插件,请确认引入该插件。

- *

geostats: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/geostats/geostats.js"></script>

- *

jsonsql: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/jsonsql/jsonsql.js"></script>

- *

EchartLayer: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/echartsLayer/EchartsLayer.min.js"></script>

- *

echarts: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/echarts/5.5.0/echarts.min.js"></script>

- *

- *

G2: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/antv/g2/4.2.8/g2.min.js"></script>

- *

G6: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/antv/g6/4.3.2/g6.min.js"></script>

- *

fast-xml-parser: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/fast-xml-parser/4.2.7/fxparser.min.js"></script>

- *

maplibre-gl-js-enhance: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/maplibre-gl-js-enhance/4.3.0-2/maplibre-gl-enhance.js"></script>

- *
- * @modulecategory Mapping - * @param {number} id - iPortal|Online 地图 ID。 - * @param {Object} options - 基础参数。 - * @param {string} [options.target='map'] - 地图容器 ID。 - * @param {string} [options.server="https://www.supermapol.com"] - 地图的地址。当设置 `id` 时有效。 - * @param {string} [options.credentialKey] - 凭证密钥。当设置 `id` 时有效。 - * @param {string} [options.credentialValue] - 凭证值。当设置 `id` 时有效。 - * @param {string} [options.tiandituKey] - 用于访问天地图的服务。当设置 `id` 时有效。 - * @param {string} [options.googleMapsAPIKey] - 用于访问谷歌地图。当设置 `id` 时有效。 - * @param {string} [options.googleMapsLanguage] - 用于定义在谷歌地图图块上显示标签的语言。当设置 `id` 且底图为谷歌地图时有效。 - * @param {boolean} [options.withCredentials=false] - 请求是否携带 cookie。当设置 `id` 时有效。 - * @param {boolean} [options.excludePortalProxyUrl] - server 传递过来的 URL 是否带有代理。当设置 `id` 时有效。 - * @param {boolean} [options.ignoreBaseProjection = false] - 是否忽略底图坐标系和叠加图层坐标系不一致。 - * @param {boolean} [options.isSuperMapOnline] - 是否是 SuperMap Online 地图。 - * @param {string} [options.iportalServiceProxyUrlPrefix] - iportal的代理服务地址前缀。 - * @param {string|boolean} [options.proxy] - HTTP 请求代理地址 。布尔值表示使用 iPortal 默认代理地址。 - * @param {Object} mapOptions - 地图参数。 - * @param {Array} [mapOptions.center] - 中心点。 - * @param {number} [mapOptions.zoom] - 缩放级别。 - * @param {number} [mapOptions.bearing] - 旋转角度。 - * @param {number} [mapOptions.pitch] - 倾角。 - * @param {string|Object} [mapOptions.crs] - 投影。 - * @param {boolean} [mapOptions.renderWorldCopies] - 连续渲染。 - * @param {number} [mapOptions.rasterTileSize] - 栅格瓦片大小。 - * @param {Object} [mapOptions.style] - style 样式。 - * @fires WebMap#mapinitialized - * @fires WebMap#mapcreatesucceeded - * @fires WebMap#mapcreatefailed - * @fires WebMap#addlayerssucceeded - * @fires WebMap#layercreatefailed - * @fires WebMap#baidumapnotsupport - * @fires WebMap#layerorsourcenameduplicated - * @fires WebMap#dataflowfeatureupdated - * @fires WebMap#projectionnotmatch - * @fires WebMap#mapbeforeremove - * @fires WebMap#getmapfailed - * @fires WebMap#getlayersfailed - * @usage + * @class WebMap + * @version 11.3.0 + * @extends {WebMapBase} + * @category iPortal/Online Resources Map + * @classdesc 对接 iPortal/Online 地图类。 + *
+ *

Notice

+ *

该功能可能依赖以下插件,请确认引入该插件。

+ *

geostats: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/geostats/geostats.js"></script>

+ *

jsonsql: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/jsonsql/jsonsql.js"></script>

+ *

EchartLayer: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/echartsLayer/EchartsLayer.min.js"></script>

+ *

echarts: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/echarts/5.5.0/echarts.min.js"></script>

+ *

+ *

G2: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/antv/g2/4.2.8/g2.min.js"></script>

+ *

G6: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/antv/g6/4.3.2/g6.min.js"></script>

+ *

fast-xml-parser: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/fast-xml-parser/4.2.7/fxparser.min.js"></script>

+ *

maplibre-gl-js-enhance: <script type="text/javascript" src="https://iclient.supermap.io/web/libs/maplibre-gl-js-enhance/4.3.0-2/maplibre-gl-enhance.js"></script>

+ *
+ * @modulecategory Mapping + * @param {number} id - iPortal|Online 地图 ID。 + * @param {Object} options - 基础参数。 + * @param {string} [options.target='map'] - 地图容器 ID。 + * @param {string} [options.server="https://www.supermapol.com"] - 地图的地址。当设置 `id` 时有效。 + * @param {string} [options.credentialKey] - 凭证密钥。当设置 `id` 时有效。 + * @param {string} [options.credentialValue] - 凭证值。当设置 `id` 时有效。 + * @param {string} [options.tiandituKey] - 用于访问天地图的服务。当设置 `id` 时有效。 + * @param {string} [options.googleMapsAPIKey] - 用于访问谷歌地图。当设置 `id` 时有效。 + * @param {string} [options.googleMapsLanguage] - 用于定义在谷歌地图图块上显示标签的语言。当设置 `id` 且底图为谷歌地图时有效。 + * @param {boolean} [options.withCredentials=false] - 请求是否携带 cookie。当设置 `id` 时有效。 + * @param {boolean} [options.excludePortalProxyUrl] - server 传递过来的 URL 是否带有代理。当设置 `id` 时有效。 + * @param {boolean} [options.ignoreBaseProjection = false] - 是否忽略底图坐标系和叠加图层坐标系不一致。 + * @param {boolean} [options.isSuperMapOnline] - 是否是 SuperMap Online 地图。 + * @param {string} [options.iportalServiceProxyUrlPrefix] - iportal的代理服务地址前缀。 + * @param {string|boolean} [options.proxy] - HTTP 请求代理地址 。布尔值表示使用 iPortal 默认代理地址。 + * @param {Object} mapOptions - 地图参数。 + * @param {Array} [mapOptions.center] - 中心点。 + * @param {number} [mapOptions.zoom] - 缩放级别。 + * @param {number} [mapOptions.bearing] - 旋转角度。 + * @param {number} [mapOptions.pitch] - 倾角。 + * @param {string|Object} [mapOptions.crs] - 投影。 + * @param {boolean} [mapOptions.renderWorldCopies] - 连续渲染。 + * @param {number} [mapOptions.rasterTileSize] - 栅格瓦片大小。 + * @param {Object} [mapOptions.style] - style 样式。 + * @fires WebMap#mapinitialized + * @fires WebMap#mapcreatesucceeded + * @fires WebMap#mapcreatefailed + * @fires WebMap#addlayerssucceeded + * @fires WebMap#layercreatefailed + * @fires WebMap#baidumapnotsupport + * @fires WebMap#layerorsourcenameduplicated + * @fires WebMap#dataflowfeatureupdated + * @fires WebMap#projectionnotmatch + * @fires WebMap#mapbeforeremove + * @fires WebMap#getmapfailed + * @fires WebMap#getlayersfailed + * @usage */ export class WebMap extends createWebMapBaseExtending(maplibregl.Evented, { mapRepo: maplibregl }) { + constructor(id, options, mapOptions) { + const crsManager = new CRSManager(options && options.proj4); + const proj4 = crsManager.getProj4(); + super(id, { ...options, proj4 }, mapOptions); + this._crsManager = crsManager; + if (!maplibregl.CRS) { + const error = + 'WebMap needs to include maplibre-gl-enhance.js. Refer to the example: https://iclient.supermap.io/examples/maplibregl/editor.html#mvtVectorTile_2362'; + Promise.resolve().then(() => { + this._fire('mapcreatefailed', { error }); + }); + } else { + this._readyForInitializingWebMap(); + } + } + + /** + * @function WebMap.prototype.setCRS + * @description 更新地图投影。 + * @param {string|Object} crs - 地图 crs。 + */ + setCRS(crs) { + if (this.map) { + this.mapOptions.crs = crs; + if (this.mapOptions.crs) { + const crsVal = crs.epsgCode + ? this._crsManager.registerCRS({ name: crs.epsgCode, wkt: crs.WKT, ...this.mapOptions.crs }, false) + : this._crsManager.getCRS(crs); + this.map.setCRS(crsVal); + } + } + } + _createWebMapFactory(type) { - const commonFactoryOptions = { MapManager, mapRepo: maplibregl, mapRepoName: 'maplibre-gl', DataFlowService, GraticuleLayer }; - const l7LayerUtil = L7LayerUtil({ featureFilter, expression, spec, L7Layer, L7, proj4: this.options.proj4 }); + const commonFactoryOptions = { + MapManager, + mapRepo: maplibregl, + crsManager: this._crsManager, + DataFlowService, + GraticuleLayer + }; + const l7LayerUtil = L7LayerUtil({ + featureFilter, + expression, + spec, + L7Layer, + L7, + proj4: this._crsManager.getProj4() + }); switch (type) { case 'MapStyle': return createMapStyleExtending(createMapClassExtending(maplibregl.Evented), commonFactoryOptions); diff --git a/src/maplibregl/mapping/webmap/CRSManager.js b/src/maplibregl/mapping/webmap/CRSManager.js new file mode 100644 index 000000000..327608fea --- /dev/null +++ b/src/maplibregl/mapping/webmap/CRSManager.js @@ -0,0 +1,32 @@ +import maplibregl from 'maplibre-gl'; + +export class CRSManager { + constructor(proj4) { + this.proj4 = proj4; + this.builtInEPSG = ['EPSG:3857', 'EPSG:4326']; + } + + isSameProjection(map, epsgCode) { + return map.getCRS().epsgCode === epsgCode; + } + + getProj4() { + return this.proj4 || maplibregl.proj4; + } + + getCRS(epsgCode) { + return maplibregl.CRS.get(epsgCode); + } + + registerCRS({ name, wkt, extent, unit }, autoRegister = true) { + let crs = maplibregl.CRS.get(name); + if (!crs) { + crs = new maplibregl.CRS(name, wkt, extent, unit || extent[2] > 180 ? 'meter' : 'degree'); + autoRegister && maplibregl.CRS.set(crs); + } + if (this.proj4 && !this.builtInEPSG.includes(name)) { + this.proj4.defs(name, crs.getWKT()); + } + return crs; + } +} diff --git a/test/mapboxgl/mapping/WebMapSpec.js b/test/mapboxgl/mapping/WebMapSpec.js index 0a75398b7..a46a7a403 100644 --- a/test/mapboxgl/mapping/WebMapSpec.js +++ b/test/mapboxgl/mapping/WebMapSpec.js @@ -1,5 +1,5 @@ import mapboxgl from 'mapbox-gl'; -import mbglmap, { CRS } from '../../tool/mock_mapboxgl_map'; +import mbglmap, { CRS, proj4 } from '../../tool/mock_mapboxgl_map'; import { WebMap } from '../../../src/mapboxgl/mapping/WebMap'; import * as MapManagerUtil from '../../../src/mapboxgl/mapping/webmap/MapManager'; import { FetchRequest } from '@supermapgis/iclient-common/util/FetchRequest'; @@ -14,6 +14,7 @@ describe('mapboxgl_WebMap', () => { beforeEach(() => { spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); mapboxgl.CRS = CRS; + mapboxgl.proj4 = proj4; testDiv = window.document.createElement('div'); testDiv.setAttribute('id', 'map'); @@ -36,6 +37,7 @@ describe('mapboxgl_WebMap', () => { window.document.body.removeChild(testDiv); jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; mapboxgl.CRS = undefined; + mapboxgl.proj4 = undefined; window.jsonsql = undefined; }); it('initialize_TIANDITU_VEC', (done) => { @@ -199,6 +201,7 @@ describe('mapboxgl_WebMap', () => { done(); }); }); + it('initialize_TIANDITU_IMAGE', (done) => { spyOn(FetchRequest, 'get').and.callFake((url) => { if (url.indexOf('web/config/portal.json') > -1) { @@ -1135,8 +1138,6 @@ describe('mapboxgl_WebMap', () => { } return null; }); - datavizWebmap.setCRS('EPSG:4326'); - expect(map.setCRS).not.toHaveBeenCalled(); datavizWebmap.setCRS('EPSG:3857'); expect(map.setCRS).toHaveBeenCalled(); datavizWebmap.map = null; @@ -1334,4 +1335,68 @@ describe('mapboxgl_WebMap', () => { }; datavizWebmap.once('mapcreatesucceeded', callback); }); + + it('when builtIn crs was defined, dont set repeat', (done) => { + const originCrs = mapboxgl.CRS.get('EPSG:3857'); + const crsSetSpy = spyOn(mapboxgl.CRS, 'set').and.callThrough(); + const commonOption = { + server: 'http://fack:8190/iportal/', + target: 'map', + withCredentials: false + }; + datavizWebmap = new WebMap('', { ...commonOption }, mapOptionsList[0]); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(crsSetSpy).not.toHaveBeenCalled(); + expect(mapboxgl.CRS.get('EPSG:3857')).toEqual(originCrs); + expect(mapboxgl.CRS.get('EPSG:3857')).toEqual(map.getCRS()); + done(); + }); + }); + + it('when uncommon crs was defined, dont set repeat', (done) => { + const wkt_4222 = 'GEOGCS["Beijing 1954",DATUM["Beijing_1954",SPHEROID["Krassowsky 1940",6378245,298.3],TOWGS84[15.8,-154.4,-82.3,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4222"]]'; + const epsgCode = 'EPSG:4222'; + const originCrs = mapboxgl.CRS.get(epsgCode); + const crsSetSpy = spyOn(mapboxgl.CRS, 'set').and.callThrough(); + const commonOption = { + server: 'http://fack:8190/iportal/', + target: 'map', + withCredentials: false + }; + datavizWebmap = new WebMap('', { ...commonOption }, { ...mapOptionsList[0], crs: { + epsgCode: epsgCode, + extent: [-180, -85, 180, 85], + WKT: wkt_4222 + }}); + datavizWebmap.once('mapcreatesucceeded', ({ map: map1 }) => { + expect(originCrs).toBeFalsy(); + expect(mapboxgl.CRS.get(epsgCode)).toBeTruthy(); + expect(mapboxgl.CRS.get(epsgCode)).toEqual(map1.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map1.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map1.getCRS().getWKT()).toBe(wkt_4222); + const originRange = [-180, 85]; + expect(map1.getCRS().getOrigin()).toEqual(originRange); + datavizWebmap.setMapOptions({ + ...mapOptionsList[1], + crs: { + name: epsgCode, + extent: [-120, -65, 120, 65], + wkt: wkt_4222 + } + }); + datavizWebmap.setStyle(mapOptionsList[1].style); + datavizWebmap.once('mapcreatesucceeded', ({ map: map2 }) => { + expect(mapboxgl.CRS.get(epsgCode)).toBeTruthy(); + expect(mapboxgl.CRS.get(epsgCode)).toEqual(map2.getCRS()); + expect(map1.getCRS()).toEqual(map2.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map2.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map2.getCRS().getWKT()).toBe(wkt_4222); + expect(map2.getCRS().getOrigin()).toEqual(originRange); + delete mapboxgl.CRS[epsgCode.replace(':', '')]; + done(); + }); + }); + }); }); diff --git a/test/mapboxgl/mapping/WebMapV2Spec.js b/test/mapboxgl/mapping/WebMapV2Spec.js index 51d13e565..c8d55d45d 100644 --- a/test/mapboxgl/mapping/WebMapV2Spec.js +++ b/test/mapboxgl/mapping/WebMapV2Spec.js @@ -1,5 +1,5 @@ import mapboxgl from 'mapbox-gl'; -import mbglmap, { CRS } from '../../tool/mock_mapboxgl_map'; +import mbglmap, { CRS, proj4 } from '../../tool/mock_mapboxgl_map'; import { WebMap } from '../../../src/mapboxgl/mapping/WebMap'; import * as MapManagerUtil from '../../../src/mapboxgl/mapping/webmap/MapManager'; import { ArrayStatistic } from '@supermapgis/iclient-common/util/ArrayStatistic'; @@ -195,6 +195,7 @@ describe('mapboxgl_WebMapV2', () => { spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); dataFlowServiceSpyTest = spyOn(DataFlowServiceUtil, 'DataFlowService').and.callFake(DataFlowService); mapboxgl.CRS = CRS; + mapboxgl.proj4 = proj4; commonMap = { style: {}, resize: jasmine.createSpy('resize').and.callFake(() => {}), @@ -343,6 +344,7 @@ describe('mapboxgl_WebMapV2', () => { window.document.body.removeChild(testDiv); jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; mapboxgl.CRS = undefined; + mapboxgl.proj4 = undefined; window.jsonsql = undefined; window.canvg = undefined; window.geostats = undefined; @@ -350,38 +352,6 @@ describe('mapboxgl_WebMapV2', () => { dataFlowServiceSpyTest = null; }); - xit('_setCRS', (done) => { - spyOn(FetchRequest, 'get').and.callFake((url) => { - if (url.indexOf('portal.json') > -1) { - return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); - } - if (url.indexOf('map.json') > -1) { - return Promise.resolve(new Response(JSON.stringify(webmap_MAPBOXSTYLE_Tile))); - } - if (url.indexOf('maps/China_4326/style.json') > -1) { - return Promise.resolve(new Response(styleJson)); - } - return Promise.resolve(); - }); - datavizWebmap = new WebMap(id, { - server: server - }); - - spyOn(mapboxgl.CRS.prototype, 'get').and.callFake((crs) => { - if (crs === 'EPSG:4326') { - return crs; - } - return null; - }); - datavizWebmap.on('mapcreatesucceeded', () => { - spyOn(mapboxgl.CRS.prototype, 'set'); - datavizWebmap._handler._setCRS('EPSG:4326', 'test', { left: -180, right: 180 }); - expect(mapboxgl.CRS.prototype.set).not.toHaveBeenCalled(); - datavizWebmap._handler._setCRS('EPSG:2362', 'test', { left: -180, right: 180 }); - expect(mapboxgl.CRS.prototype.set).toHaveBeenCalled(); - done(); - }); - }); it('test baseLayer layers count maploaded', (done) => { spyOn(FetchRequest, 'get').and.callFake((url) => { if (url.indexOf('portal.json') > -1) { @@ -674,7 +644,7 @@ describe('mapboxgl_WebMapV2', () => { it('request wkt info and visibleExtend without EPSFG Prefix ', (done) => { const epsgeCode = - 'PROJCS["Google Maps Global Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_2SP"],PARAMETER["standard_parallel_1",0],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],AXIS["Northing", "NORTH"],AXIS["Easting", "EAST"],UNIT["Meter",1],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","900913"]]'; + 'PROJCS["unnamed",GEOGCS["GRS 1980(IUGG, 1980)",DATUM["unknown",SPHEROID["GRS80",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic_1SP"],PARAMETER["latitude_of_origin",43.0695160375],PARAMETER["central_meridian",-89.42222222222223],PARAMETER["scale_factor",1.0000384786],PARAMETER["false_easting",811000],PARAMETER["false_northing",480943.886],AXIS["Northing", "NORTH"],AXIS["Easting", "EAST"],UNIT["Foot_US",0.3048006096012192],AUTHORITY["epsg","7599"]]'; spyOn(FetchRequest, 'get').and.callFake((url) => { if (url.indexOf('web/datas/676516522/content.json') > -1) { return Promise.resolve(new Response(layerData_CSV)); @@ -3232,4 +3202,262 @@ describe('mapboxgl_WebMapV2', () => { done(); }); }); + + it('when builtIn crs was defined, dont set repeat', (done) => { + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('web/config/portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('map.json') > -1) { + var mapJson = datavizWebMap_RestMap; + return Promise.resolve(new Response(mapJson)); + } + return Promise.resolve(new Response(JSON.stringify({}))); + }); + const originCrs = mapboxgl.CRS.get('EPSG:3857'); + const crsSetSpy = spyOn(mapboxgl.CRS, 'set').and.callThrough(); + + datavizWebmap = new WebMap(id, { + server: server + }); + datavizWebmap.on('mapcreatesucceeded', function ({ map }) { + expect(crsSetSpy).not.toHaveBeenCalled(); + expect(mapboxgl.CRS.get('EPSG:3857')).toEqual(originCrs); + expect(mapboxgl.CRS.get('EPSG:3857')).toEqual(map.getCRS()); + expect(map.getStyle().layers.length).toBe(2); + done(); + }); + }); + + it('when uncommon crs was defined, dont set repeat', (done) => { + const mapInfo = JSON.parse(raster4490); + const epsgCode = 'EPSG:4214'; + const wkt_4214 = 'GEOGCS["Beijing 1954",DATUM["Beijing_1954",SPHEROID["Krassowsky 1940",6378245,298.3],TOWGS84[15.8,-154.4,-82.3,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4214"]]'; + mapInfo.projection = wkt_4214; + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('123/map.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + ...mapInfo, + baseLayer: { + ...mapInfo.baseLayer, + url: 'http://fake:8090/iserver/services/map-test4241/rest/maps/4214_123' + } + }) + ) + ); + } + if (url.indexOf('456/map.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + ...mapInfo, + baseLayer: { + ...mapInfo.baseLayer, + url: 'http://fake:8090/iserver/services/map-test4241/rest/maps/4214_456' + } + }) + ) + ); + } + if (url.indexOf('4214_123.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + prjCoordSys: { epsgCode: 4214 }, + bounds: { + top: 85, + left: -180, + bottom: -85, + leftBottom: { + x: -180, + y: -85 + }, + right: 180, + rightTop: { + x: 180, + y: 85 + } + } + }) + ) + ); + } + if (url.indexOf('4214_456.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + prjCoordSys: { epsgCode: 4214 }, + bounds: { + top: 65, + left: -120, + bottom: -65, + leftBottom: { + x: -120, + y: -65 + }, + right: 120, + rightTop: { + x: 120, + y: 6 + } + } + }) + ) + ); + } + }); + const originCrs = mapboxgl.CRS.get(epsgCode); + const crsSetSpy = spyOn(mapboxgl.CRS, 'set').and.callThrough(); + datavizWebmap = new WebMap( + '123', + { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + } + ); + datavizWebmap.once('mapcreatesucceeded', ({ map: map1 }) => { + expect(originCrs).toBeFalsy(); + expect(mapboxgl.CRS.get(epsgCode)).toBeTruthy(); + expect(mapboxgl.CRS.get(epsgCode)).toEqual(map1.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map1.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map1.getCRS().getWKT()).toBe(wkt_4214); + const originRange = [-180, 85]; + expect(map1.getCRS().getOrigin()).toEqual(originRange); + expect(map1.getStyle().layers.length).toBe(1); + datavizWebmap.setMapId('456'); + datavizWebmap.on('mapcreatesucceeded', ({ map: map2 }) => { + expect(mapboxgl.CRS.get(epsgCode)).toBeTruthy(); + expect(mapboxgl.CRS.get(epsgCode)).toEqual(map2.getCRS()); + expect(map1.getCRS()).toEqual(map2.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map2.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map2.getCRS().getWKT()).toBe(wkt_4214); + expect(map2.getCRS().getOrigin()).toEqual(originRange); + expect(map2.getStyle().layers.length).toBe(1); + done(); + }); + }); + }); + + it('when uncommon crs was defined, baselayer is TILE', (done) => { + const mapInfo = JSON.parse(raster4490); + const epsgCode = 'EPSG:4215'; + mapInfo.projection = epsgCode; + const wkt_4215 = 'GEOGCS["Beijing 1954",DATUM["Beijing_1954",SPHEROID["Krassowsky 1940",6378245,298.3],TOWGS84[15.8,-154.4,-82.3,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4215"]]'; + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('123/map.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + ...mapInfo, + baseLayer: { + ...mapInfo.baseLayer, + url: 'http://fake:8090/iserver/services/map-test4241/rest/maps/4215_123' + } + }) + ) + ); + } + if (url.indexOf('4215_123.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + prjCoordSys: { epsgCode: 4215 }, + bounds: { + top: 85, + left: -180, + bottom: -85, + leftBottom: { + x: -180, + y: -85 + }, + right: 180, + rightTop: { + x: 180, + y: 85 + } + } + }) + ) + ); + } + if (url.indexOf('prjCoordSys.wkt')) { + return Promise.resolve(new Response(wkt_4215)); + } + }); + const originCrs = mapboxgl.CRS.get(epsgCode); + const crsSetSpy = spyOn(mapboxgl.CRS, 'set').and.callThrough(); + datavizWebmap = new WebMap( + '123', + { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + } + ); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(originCrs).toBeFalsy(); + expect(mapboxgl.CRS.get(epsgCode)).toBeTruthy(); + expect(mapboxgl.CRS.get(epsgCode)).toEqual(map.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map.getCRS().getWKT()).toBe(wkt_4215); + expect(map.getCRS().getOrigin()).toEqual([-180, 85]); + expect(map.getStyle().layers.length).toBe(1); + done(); + }); + }); + + it('when uncommon crs was defined, baselayer is MAPBOXSTYLE', (done) => { + const epsgCode = 'EPSG:4216'; + const wkt_4216 = 'GEOGCS["Beijing 1954",DATUM["Beijing_1954",SPHEROID["Krassowsky 1940",6378245,298.3],TOWGS84[15.8,-154.4,-82.3,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4216"]]'; + const mapInfo = { + ...webmap_MAPBOXSTYLE_Tile, + layers: [], + projection: wkt_4216 + }; + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('123/map.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(mapInfo))); + } + if (url.indexOf('/style.json')) { + return Promise.resolve(new Response(JSON.stringify(vectorTile_style))); + } + return Promise.resolve(new Response(JSON.stringify({}))); + }); + const originCrs = mapboxgl.CRS.get(epsgCode); + const crsSetSpy = spyOn(mapboxgl.CRS, 'set').and.callThrough(); + datavizWebmap = new WebMap( + '123', + { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + } + ); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(originCrs).toBeFalsy(); + expect(mapboxgl.CRS.get(epsgCode)).toBeTruthy(); + expect(mapboxgl.CRS.get(epsgCode)).toEqual(map.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map.getCRS().getWKT()).toBe(wkt_4216); + expect(map.getCRS().getOrigin()).toEqual([vectorTile_style.metadata.indexbounds[0], vectorTile_style.metadata.indexbounds[3]]); + expect(map.getStyle().layers.length).toBe(vectorTile_style.layers.length - 1); + done(); + }); + }); }); diff --git a/test/mapboxgl/mapping/WebMapV3Spec.js b/test/mapboxgl/mapping/WebMapV3Spec.js index 445e732b1..c405dae49 100644 --- a/test/mapboxgl/mapping/WebMapV3Spec.js +++ b/test/mapboxgl/mapping/WebMapV3Spec.js @@ -5,11 +5,12 @@ import { createMapClassExtending } from '@supermapgis/iclient-common/mapping/Map import { L7LayerUtil } from '@supermapgis/iclient-common/mapping/utils/L7LayerUtil'; import { WebMap } from '../../../src/mapboxgl/mapping/WebMap'; import * as MapManagerUtil from '../../../src/mapboxgl/mapping/webmap/MapManager'; +import { CRSManager } from '../../../src/mapboxgl/mapping/webmap/CRSManager'; import { featureFilter, expression } from '@mapbox/mapbox-gl-style-spec'; import spec from '@mapbox/mapbox-gl-style-spec/reference/v8'; import { L7, L7Layer } from '../../../src/mapboxgl/overlay/L7Layer'; import * as mockL7 from '../../tool/mock_l7'; -import mbglmap, { CRS } from '../../tool/mock_mapboxgl_map'; +import mbglmap, { CRS, proj4 } from '../../tool/mock_mapboxgl_map'; import '../../resources/WebMapV3.js'; import '../../resources/WebMapV5.js'; @@ -22,7 +23,7 @@ describe('mapboxgl-webmap3.0', () => { const extendOptions = { MapManager: MapManagerUtil.default, mapRepo: mapboxgl, - mapRepoName: 'mapbox-gl', + crsManager: new CRSManager(), l7LayerUtil }; const WebMapV3 = createWebMapV3Extending(createMapClassExtending(mapboxgl.Evented), extendOptions); @@ -39,6 +40,8 @@ describe('mapboxgl-webmap3.0', () => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; mapboxgl.Map.prototype.overlayLayersManager = {}; mbglmap.prototype.getL7Scene = mapboxgl.Map.prototype.getL7Scene; + mapboxgl.CRS = CRS; + mapboxgl.proj4 = proj4; }); afterEach(() => { if (mapstudioWebmap && mapstudioWebmap.map) { @@ -49,6 +52,8 @@ describe('mapboxgl-webmap3.0', () => { window.document.body.removeChild(testDiv); jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; mbglmap.prototype.getL7Scene = undefined; + mapboxgl.CRS = undefined; + mapboxgl.proj4 = undefined; }); it('initialize_background', (done) => { @@ -122,41 +127,6 @@ describe('mapboxgl-webmap3.0', () => { }); }); - xit('_setCRS', (done) => { - spyOn(FetchRequest, 'get').and.callFake((url) => { - if (url.indexOf('web/config/portal.json') > -1) { - return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); - } - if (url.indexOf('map.json') > -1) { - var mapJson = mapstudioWebMap_raster; - return Promise.resolve(new Response(mapJson)); - } - if (url.indexOf('617580084.json') > -1) { - var appInfo = mapstudioAppInfo; - return Promise.resolve(new Response(appInfo)); - } - return Promise.resolve(); - }); - mapstudioWebmap = new WebMap(id, { - server: server, - iportalServiceProxyUrl: 'initialize_raster' - }); - spyOn(mapboxgl.CRS, 'get').and.callFake((crs) => { - if (crs === 'EPSG:4326' || crs === 'EPSG:3957') { - return crs; - } - return null; - }); - spyOn(mapboxgl.CRS, 'set'); - datavizWebmap.on('mapcreatesucceeded', () => { - datavizWebmap._handler._setCRS('EPSG:4326', 'test', { left: -180, right: 180 }); - expect(mapboxgl.CRS.set).not.toHaveBeenCalled(); - datavizWebmap._handler._setCRS('EPSG:2362', 'test', { left: -180, right: 180 }); - expect(mapboxgl.CRS.set).toHaveBeenCalled(); - done(); - }); - }); - it('vector_symbol', (done) => { spyOn(FetchRequest, 'get').and.callFake((url) => { if (url.indexOf('web/config/portal.json') > -1) { @@ -240,26 +210,30 @@ describe('mapboxgl-webmap3.0', () => { wkt: 'GEOGCS["China Geodetic Coordinate System 2000", DATUM["China 2000", SPHEROID["CGCS2000", 6378137.0, 298.257222101, AUTHORITY["EPSG","1024"]], AUTHORITY["EPSG","1043"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["degree", 0.017453292519943295], AXIS["Geodetic latitude", NORTH], AXIS["Geodetic longitude", EAST], AUTHORITY["EPSG","4490"]]' } }; - mapstudioWebmap = new WebMapV3(nextMapInfo, { + spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('web/config/portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('/sprite') > -1) { + return Promise.resolve(new Response(msSpriteInfo)); + } + return Promise.resolve(); + }); + mapboxgl.CRS = undefined; + mapstudioWebmap = new WebMap(nextMapInfo, { server: server, target: 'map' }); mapstudioWebmap.on('mapcreatefailed', ({ error }) => { - const throwError = `The EPSG code ${nextMapInfo.crs.name} needs to include mapbox-gl-enhance.js. Refer to the example: https://iclient.supermap.io/examples/mapboxgl/editor.html#mvtVectorTile_2362`; + const throwError = 'WebMap needs to include mapbox-gl-enhance.js. Refer to the example: https://iclient.supermap.io/examples/mapboxgl/editor.html#mvtVectorTile_2362'; expect(mapstudioWebmap.map).toBeUndefined(); expect(error).toBe(throwError); done(); }); - mapstudioWebmap.initializeMap(nextMapInfo); }); it('projection is 4490 and include mapbox-gl-enhance', (done) => { - spyOn(FetchRequest, 'get').and.callFake((url) => { - if (url.indexOf('/sprite') > -1) { - return Promise.resolve(new Response(msSpriteInfo)); - } - return Promise.resolve(); - }); const mapInfo = JSON.parse(mapstudioWebMap_symbol); const nextMapInfo = { ...mapInfo, @@ -269,13 +243,21 @@ describe('mapboxgl-webmap3.0', () => { wkt: 'GEOGCS["China Geodetic Coordinate System 2000", DATUM["China 2000", SPHEROID["CGCS2000", 6378137.0, 298.257222101, AUTHORITY["EPSG","1024"]], AUTHORITY["EPSG","1043"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["degree", 0.017453292519943295], AXIS["Geodetic latitude", NORTH], AXIS["Geodetic longitude", EAST], AUTHORITY["EPSG","4490"]]' } }; - mapboxgl.CRS = CRS; - mapstudioWebmap = new WebMapV3(nextMapInfo, { + spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('web/config/portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('/sprite') > -1) { + return Promise.resolve(new Response(msSpriteInfo)); + } + return Promise.resolve(); + }); + mapstudioWebmap = new WebMap(nextMapInfo, { server: server, target: 'map', iportalServiceProxyUrl: 'projection is 4490 and include mapbox-gl-enhance' }); - mapstudioWebmap.initializeMap(nextMapInfo); mapstudioWebmap.on('mapcreatesucceeded', ({ map }) => { expect(map).not.toBeUndefined(); @@ -286,7 +268,6 @@ describe('mapboxgl-webmap3.0', () => { const layerCatalogs = mapstudioWebmap.getLayerCatalog(); expect(layerCatalogs.length).toBeLessThanOrEqual(appreciableLayers.length); expect(mapstudioWebmap.getLegends().length).toBe(0); - delete mapboxgl.CRS; done(); }); }); @@ -308,7 +289,6 @@ describe('mapboxgl-webmap3.0', () => { wkt: 'GEOGCS["China Geodetic Coordinate System 2000", DATUM["China 2000", SPHEROID["CGCS2000", 6378137.0, 298.257222101, AUTHORITY["EPSG","1024"]], AUTHORITY["EPSG","1043"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["degree", 0.017453292519943295], AXIS["Geodetic latitude", NORTH], AXIS["Geodetic longitude", EAST], AUTHORITY["EPSG","4490"]]' } }; - mapboxgl.CRS = CRS; mapstudioWebmap = new WebMapV3(nextMapInfo, { server: server, target: 'map' @@ -347,7 +327,6 @@ describe('mapboxgl-webmap3.0', () => { const appreciableLayers = mapstudioWebmap.getLayers(); const layerCatalogs = mapstudioWebmap.getLayerCatalog(); expect(layerCatalogs.length).toBeLessThanOrEqual(appreciableLayers.length); - delete mapboxgl.CRS; done(); }); }); @@ -601,7 +580,6 @@ describe('mapboxgl-webmap3.0', () => { } return Promise.resolve(); }); - mapboxgl.CRS = CRS; const mapOptions = { transformRequest: function (url) { return { url }; @@ -626,7 +604,6 @@ describe('mapboxgl-webmap3.0', () => { expect(mapstudioWebmap.getLegends().length).toBe(8); expect(mapOptions.transformRequest.calls.count()).toBeGreaterThan(0); delete mapboxgl.Map.prototype.getCRS; - delete mapboxgl.CRS; done(); }); }); @@ -639,22 +616,43 @@ describe('mapboxgl-webmap3.0', () => { return Promise.resolve(); }); const mapInfo = JSON.parse(mapstudioWebMap_symbol); - mapboxgl.Map.prototype.getCRS = function () { + + const existedMap = new mapboxgl.Map({ + container: testDiv, + style: { + version: 8, + sources: {}, + layers: [ + { + paint: { + 'background-color': '#242424' + }, + id: 'background1', + type: 'background' + } + ] + }, + center: [116.640545, 40.531714], + zoom: 7 + }); + existedMap.getCRS = function () { return { epsgCode: '' }; }; mapstudioWebmap = new WebMapV3(mapInfo, { server: server, target: 'map' }); + existedMap.on('load', function () { + mapstudioWebmap.initializeMap(mapInfo, existedMap); + }); mapstudioWebmap.on('projectionnotmatch', () => { expect(mapstudioWebmap.map).not.toBeUndefined(); + expect(mapstudioWebmap.map).toEqual(existedMap); const style = mapstudioWebmap.map.getStyle(); - expect(style.layers.length).toBe(0); - delete mapboxgl.Map.prototype.getCRS; + expect(style.layers.length).toBe(1); done(); }); - mapstudioWebmap.initializeMap(mapInfo); }); it('layerdatas', (done) => { @@ -764,9 +762,6 @@ describe('mapboxgl-webmap3.0', () => { spyOn(L7, 'HeatmapLayer').and.callFake(mockL7.PointLayer); spyOn(L7, 'Scene').and.callFake(mockL7.Scene); spyOn(L7, 'Mapbox').and.callFake(mockL7.Mapbox); - mapboxgl.Map.prototype.getCRS = function () { - return { epsgCode: mapInfo.crs.name, getExtent: () => {} }; - }; spyOn(FetchRequest, 'get').and.callFake((url) => { if (url.indexOf('web/config/portal.json') > -1) { return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); @@ -794,15 +789,12 @@ describe('mapboxgl-webmap3.0', () => { } return Promise.resolve(); }); - mapboxgl.CRS = CRS; mapstudioWebmap = new WebMap(id, { server: server }); mapstudioWebmap.on('mapcreatesucceeded', ({ map }) => { const webmapInstance = mapstudioWebmap._getWebMapInstance(); expect(webmapInstance.getLegends().length).toBe(4); - delete mapboxgl.Map.prototype.getCRS; - delete mapboxgl.CRS; spyTest.calls.reset(); done(); }); @@ -846,7 +838,6 @@ describe('mapboxgl-webmap3.0', () => { } return Promise.resolve(); }); - mapboxgl.CRS = CRS; mapstudioWebmap = new WebMap(id, { server: server }); @@ -870,7 +861,6 @@ describe('mapboxgl-webmap3.0', () => { expect(currentOverlayLayers.length).toBe(overlayLayers.length + 1); expect(currentAppreciableLayers.length).toBe(appreciableLayers.length + 1); delete mapboxgl.Map.prototype.getCRS; - delete mapboxgl.CRS; done(); }); }); @@ -965,7 +955,6 @@ describe('mapboxgl-webmap3.0', () => { } return Promise.resolve(); }); - mapboxgl.CRS = CRS; const mapOptions = { transformRequest: function (url) { return { url }; @@ -1008,7 +997,6 @@ describe('mapboxgl-webmap3.0', () => { const newFeatures = map.getSource('ms_1052943054_1715672103742_8').getData().features; expect(newFeatures.length).toBe(1); delete mapboxgl.Map.prototype.getCRS; - delete mapboxgl.CRS; done(); }); }); @@ -1053,7 +1041,6 @@ describe('mapboxgl-webmap3.0', () => { ...mapInfo, crs: crsInfo }; - mapboxgl.CRS = CRS; mapstudioWebmap = new WebMapV3(nextMapInfo, { server: server, target: 'map' @@ -1085,7 +1072,6 @@ describe('mapboxgl-webmap3.0', () => { expect(map).toEqual(existedMap); expect(mapstudioWebmap.map).toEqual(map); expect(map.addStyle).toHaveBeenCalled(); - delete mapboxgl.CRS; done(); }); }); @@ -1114,9 +1100,6 @@ describe('mapboxgl-webmap3.0', () => { } return Promise.resolve(); }); - mapboxgl.Map.prototype.getCRS = function () { - return { epsgCode: 'EPSG:3857', getExtent: () => {} }; - }; mapstudioWebmap = new WebMap(id, { server: server }); @@ -1125,7 +1108,6 @@ describe('mapboxgl-webmap3.0', () => { const webMapV3 = mapstudioWebmap._getWebMapInstance(); expect(map).not.toBeUndefined(); expect(webMapV3.getLegends().length).toBe(9); - delete mapboxgl.Map.prototype.getCRS; mbglmap.prototype.getL7Scene = undefined; spyTest.calls.reset(); done(); @@ -1465,4 +1447,93 @@ describe('mapboxgl-webmap3.0', () => { done(); }); }); + + it('when builtIn crs was defined, dont set repeat', (done) => { + spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('web/config/portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('map.json') > -1) { + var mapJson = mapstudioWebMap_raster; + return Promise.resolve(new Response(mapJson)); + } + if (url.indexOf('617580084.json') > -1) { + var appInfo = mapstudioAppInfo; + return Promise.resolve(new Response(appInfo)); + } + return Promise.resolve(); + }); + const originCrs = mapboxgl.CRS.get('EPSG:3857'); + const crsSetSpy = spyOn(mapboxgl.CRS, 'set').and.callThrough(); + mapstudioWebmap = new WebMap(id, { + server: server + }); + mapstudioWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(crsSetSpy).not.toHaveBeenCalled(); + expect(mapboxgl.CRS.get('EPSG:3857')).toEqual(originCrs); + expect(mapboxgl.CRS.get('EPSG:3857')).toEqual(map.getCRS()); + done(); + }); + }); + + it('when uncommon crs was defined, dont set repeat', (done) => { + const mapInfo = JSON.parse(mapstudioWebMap_symbol); + const wkt_4221 = 'GEOGCS["Beijing 1954",DATUM["Beijing_1954",SPHEROID["Krassowsky 1940",6378245,298.3],TOWGS84[15.8,-154.4,-82.3,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4221"]]'; + const epsgCode = 'EPSG:4221'; + const nextMapInfo = { + ...mapInfo, + crs: { + name: epsgCode, + extent: [-180, -85, 180, 85], + wkt: wkt_4221 + } + }; + spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('web/config/portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('/sprite') > -1) { + return Promise.resolve(new Response(msSpriteInfo)); + } + return Promise.resolve(); + }); + const originCrs = mapboxgl.CRS.get(epsgCode); + const crsSetSpy = spyOn(mapboxgl.CRS, 'set').and.callThrough(); + mapstudioWebmap = new WebMap(nextMapInfo, { + server: server, + target: 'map' + }); + + mapstudioWebmap.once('mapcreatesucceeded', ({ map: map1 }) => { + expect(originCrs).toBeFalsy(); + expect(mapboxgl.CRS.get(epsgCode)).toBeTruthy(); + expect(mapboxgl.CRS.get(epsgCode)).toEqual(map1.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map1.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map1.getCRS().getWKT()).toBe(wkt_4221); + const originRange = [-180, 85]; + expect(map1.getCRS().getOrigin()).toEqual(originRange); + mapstudioWebmap.setMapId({ + ...mapInfo, + crs: { + name: epsgCode, + extent: [-120, -65, 120, 65], + wkt: wkt_4221 + } + }); + mapstudioWebmap.once('mapcreatesucceeded', ({ map: map2 }) => { + expect(mapboxgl.CRS.get(epsgCode)).toBeTruthy(); + expect(mapboxgl.CRS.get(epsgCode)).toEqual(map2.getCRS()); + expect(map1.getCRS()).toEqual(map2.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map2.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map2.getCRS().getWKT()).toBe(wkt_4221); + expect(map2.getCRS().getOrigin()).toEqual(originRange); + delete mapboxgl.CRS[epsgCode.replace(':', '')]; + done(); + }); + }); + }); }); diff --git a/test/maplibregl/mapping/WebMapSpec.js b/test/maplibregl/mapping/WebMapSpec.js index b2616ab26..2ad367e3d 100644 --- a/test/maplibregl/mapping/WebMapSpec.js +++ b/test/maplibregl/mapping/WebMapSpec.js @@ -1,5 +1,5 @@ import maplibregl from 'maplibre-gl'; -import mbglmap, { CRS } from '../../tool/mock_maplibregl_map'; +import mbglmap, { CRS, proj4 } from '../../tool/mock_maplibregl_map'; import { WebMap } from '../../../src/maplibregl/mapping/WebMap'; import * as MapManagerUtil from '../../../src/maplibregl/mapping/webmap/MapManager'; import { FetchRequest } from '@supermapgis/iclient-common/util/FetchRequest'; @@ -14,6 +14,7 @@ describe('maplibregl_WebMap', () => { beforeEach(() => { spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); maplibregl.CRS = CRS; + maplibregl.proj4 = proj4; testDiv = window.document.createElement('div'); testDiv.setAttribute('id', 'map'); @@ -36,6 +37,7 @@ describe('maplibregl_WebMap', () => { window.document.body.removeChild(testDiv); jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; maplibregl.CRS = undefined; + maplibregl.proj4 = undefined; window.jsonsql = undefined; }); it('initialize_TIANDITU_VEC', (done) => { @@ -1314,4 +1316,68 @@ describe('maplibregl_WebMap', () => { }; datavizWebmap.once('mapcreatesucceeded', callback); }); + + it('when builtIn crs was defined, dont set repeat', (done) => { + const originCrs = maplibregl.CRS.get('EPSG:3857'); + const crsSetSpy = spyOn(maplibregl.CRS, 'set').and.callThrough(); + const commonOption = { + server: 'http://fack:8190/iportal/', + target: 'map', + withCredentials: false + }; + datavizWebmap = new WebMap('', { ...commonOption }, mapOptionsList[0]); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(crsSetSpy).not.toHaveBeenCalled(); + expect(maplibregl.CRS.get('EPSG:3857')).toEqual(originCrs); + expect(maplibregl.CRS.get('EPSG:3857')).toEqual(map.getCRS()); + done(); + }); + }); + + it('when uncommon crs was defined, dont set repeat', (done) => { + const wkt_4223 = 'GEOGCS["Beijing 1954",DATUM["Beijing_1954",SPHEROID["Krassowsky 1940",6378245,298.3],TOWGS84[15.8,-154.4,-82.3,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4223"]]'; + const epsgCode = 'EPSG:4223'; + const originCrs = maplibregl.CRS.get(epsgCode); + const crsSetSpy = spyOn(maplibregl.CRS, 'set').and.callThrough(); + const commonOption = { + server: 'http://fack:8190/iportal/', + target: 'map', + withCredentials: false + }; + datavizWebmap = new WebMap('', { ...commonOption }, { ...mapOptionsList[0], crs: { + epsgCode: epsgCode, + extent: [-180, -85, 180, 85], + WKT: wkt_4223 + }}); + datavizWebmap.once('mapcreatesucceeded', ({ map: map1 }) => { + expect(originCrs).toBeFalsy(); + expect(maplibregl.CRS.get(epsgCode)).toBeTruthy(); + expect(maplibregl.CRS.get(epsgCode)).toEqual(map1.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map1.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map1.getCRS().getWKT()).toBe(wkt_4223); + const originRange = [-180, 85]; + expect(map1.getCRS().getOrigin()).toEqual(originRange); + datavizWebmap.setMapOptions({ + ...mapOptionsList[1], + crs: { + name: epsgCode, + extent: [-120, -65, 120, 65], + wkt: wkt_4223 + } + }); + datavizWebmap.setStyle(mapOptionsList[1].style); + datavizWebmap.once('mapcreatesucceeded', ({ map: map2 }) => { + expect(maplibregl.CRS.get(epsgCode)).toBeTruthy(); + expect(maplibregl.CRS.get(epsgCode)).toEqual(map2.getCRS()); + expect(map1.getCRS()).toEqual(map2.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map2.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map2.getCRS().getWKT()).toBe(wkt_4223); + expect(map2.getCRS().getOrigin()).toEqual(originRange); + delete maplibregl.CRS[epsgCode.replace(':', '')]; + done(); + }); + }); + }); }); diff --git a/test/maplibregl/mapping/WebMapV2Spec.js b/test/maplibregl/mapping/WebMapV2Spec.js index f641ba630..9a9105cde 100644 --- a/test/maplibregl/mapping/WebMapV2Spec.js +++ b/test/maplibregl/mapping/WebMapV2Spec.js @@ -1,5 +1,5 @@ import maplibregl from 'maplibre-gl'; -import mbglmap, { CRS } from '../../tool/mock_maplibregl_map'; +import mbglmap, { CRS, proj4 } from '../../tool/mock_maplibregl_map'; import { WebMap } from '../../../src/maplibregl/mapping/WebMap'; import * as MapManagerUtil from '../../../src/maplibregl/mapping/webmap/MapManager'; import { ArrayStatistic } from '@supermapgis/iclient-common/util/ArrayStatistic'; @@ -195,6 +195,7 @@ describe('maplibregl_WebMapV2', () => { spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); dataFlowServiceSpyTest = spyOn(DataFlowServiceUtil, 'DataFlowService').and.callFake(DataFlowService); maplibregl.CRS = CRS; + maplibregl.proj4 = proj4; commonMap = { style: {}, resize: jasmine.createSpy('resize').and.callFake(() => {}), @@ -344,6 +345,7 @@ describe('maplibregl_WebMapV2', () => { window.document.body.removeChild(testDiv); jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; maplibregl.CRS = undefined; + maplibregl.proj4 = undefined; window.jsonsql = undefined; window.canvg = undefined; window.geostats = undefined; @@ -361,6 +363,9 @@ describe('maplibregl_WebMapV2', () => { if (url.indexOf('maps/China_4326/style.json') > -1) { return Promise.resolve(new Response(styleJson)); } + if (url.indexOf('%E4%BA%AC%E6%B4%A5%E5%9C%B0%E5%8C%BA%E5%9C%B0%E5%9B%BE.json') > -1) { + return Promise.reject(new Error('TIle service error')); + } return Promise.resolve(); }); datavizWebmap = new WebMap(id, { @@ -628,7 +633,7 @@ describe('maplibregl_WebMapV2', () => { it('request wkt info and visibleExtend without EPSFG Prefix ', (done) => { const epsgeCode = - 'PROJCS["Google Maps Global Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_2SP"],PARAMETER["standard_parallel_1",0],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],AXIS["Northing", "NORTH"],AXIS["Easting", "EAST"],UNIT["Meter",1],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","900913"]]'; + 'PROJCS["unnamed",GEOGCS["GRS 1980(IUGG, 1980)",DATUM["unknown",SPHEROID["GRS80",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic_1SP"],PARAMETER["latitude_of_origin",43.0695160375],PARAMETER["central_meridian",-89.42222222222223],PARAMETER["scale_factor",1.0000384786],PARAMETER["false_easting",811000],PARAMETER["false_northing",480943.886],AXIS["Northing", "NORTH"],AXIS["Easting", "EAST"],UNIT["Foot_US",0.3048006096012192],AUTHORITY["epsg","7599"]]'; spyOn(FetchRequest, 'get').and.callFake((url) => { if (url.indexOf('web/datas/676516522/content.json') > -1) { return Promise.resolve(new Response(layerData_CSV)); @@ -2452,7 +2457,6 @@ describe('maplibregl_WebMapV2', () => { const vectorLayerLine = layers[1]; expect(vectorLayerLine.type).toBe('line'); expect(vectorLayerLine.paint['line-width']).toBe(7); - done(); }); }); @@ -2624,6 +2628,7 @@ describe('maplibregl_WebMapV2', () => { server: server }); datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(map.getStyle().layers.length).toBe(2); done(); }); }); @@ -3105,4 +3110,261 @@ describe('maplibregl_WebMapV2', () => { }); }); }); + + it('when builtIn crs was defined, dont set repeat', (done) => { + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('web/config/portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('map.json') > -1) { + var mapJson = datavizWebMap_RestMap; + return Promise.resolve(new Response(mapJson)); + } + return Promise.resolve(new Response(JSON.stringify({}))); + }); + const originCrs = maplibregl.CRS.get('EPSG:3857'); + const crsSetSpy = spyOn(maplibregl.CRS, 'set').and.callThrough(); + + datavizWebmap = new WebMap(id, { + server: server + }); + datavizWebmap.on('mapcreatesucceeded', function ({ map }) { + expect(crsSetSpy).not.toHaveBeenCalled(); + expect(maplibregl.CRS.get('EPSG:3857')).toEqual(originCrs); + expect(maplibregl.CRS.get('EPSG:3857')).toEqual(map.getCRS()); + expect(map.getStyle().layers.length).toBe(2); + done(); + }); + }); + + it('when uncommon crs was defined, dont set repeat', (done) => { + const mapInfo = JSON.parse(raster4490); + const epsgCode = 'EPSG:4217'; + const wkt_4217 = 'GEOGCS["Beijing 1954",DATUM["Beijing_1954",SPHEROID["Krassowsky 1940",6378245,298.3],TOWGS84[15.8,-154.4,-82.3,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4217"]]'; + mapInfo.projection = wkt_4217; + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('123/map.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + ...mapInfo, + baseLayer: { + ...mapInfo.baseLayer, + url: 'http://fake:8090/iserver/services/map-test4241/rest/maps/4217_123' + } + }) + ) + ); + } + if (url.indexOf('456/map.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + ...mapInfo, + baseLayer: { + ...mapInfo.baseLayer, + url: 'http://fake:8090/iserver/services/map-test4241/rest/maps/4217_456' + } + }) + ) + ); + } + if (url.indexOf('4217_123.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + prjCoordSys: { epsgCode: 4217 }, + bounds: { + top: 85, + left: -180, + bottom: -85, + leftBottom: { + x: -180, + y: -85 + }, + right: 180, + rightTop: { + x: 180, + y: 85 + } + } + }) + ) + ); + } + if (url.indexOf('4217_456.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + prjCoordSys: { epsgCode: 4217 }, + bounds: { + top: 65, + left: -120, + bottom: -65, + leftBottom: { + x: -120, + y: -65 + }, + right: 120, + rightTop: { + x: 120, + y: 6 + } + } + }) + ) + ); + } + }); + const originCrs = maplibregl.CRS.get(epsgCode); + const crsSetSpy = spyOn(maplibregl.CRS, 'set').and.callThrough(); + datavizWebmap = new WebMap( + '123', + { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + } + ); + datavizWebmap.once('mapcreatesucceeded', ({ map: map1 }) => { + expect(originCrs).toBeFalsy(); + expect(maplibregl.CRS.get(epsgCode)).toBeTruthy(); + expect(maplibregl.CRS.get(epsgCode)).toEqual(map1.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map1.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map1.getCRS().getWKT()).toBe(wkt_4217); + const originRange = [-180, 85]; + expect(map1.getCRS().getOrigin()).toEqual(originRange); + expect(map1.getStyle().layers.length).toBe(1); + datavizWebmap.setMapId('456'); + datavizWebmap.on('mapcreatesucceeded', ({ map: map2 }) => { + expect(maplibregl.CRS.get(epsgCode)).toBeTruthy(); + expect(maplibregl.CRS.get(epsgCode)).toEqual(map2.getCRS()); + expect(map1.getCRS()).toEqual(map2.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map2.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map2.getCRS().getWKT()).toBe(wkt_4217); + expect(map2.getCRS().getOrigin()).toEqual(originRange); + expect(map2.getStyle().layers.length).toBe(1); + done(); + }); + }); + }); + + it('when uncommon crs was defined, baselayer is TILE', (done) => { + const mapInfo = JSON.parse(raster4490); + const epsgCode = 'EPSG:4218'; + mapInfo.projection = epsgCode; + const wkt_4218 = 'GEOGCS["Beijing 1954",DATUM["Beijing_1954",SPHEROID["Krassowsky 1940",6378245,298.3],TOWGS84[15.8,-154.4,-82.3,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4218"]]'; + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('123/map.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + ...mapInfo, + baseLayer: { + ...mapInfo.baseLayer, + url: 'http://fake:8090/iserver/services/map-test4241/rest/maps/4218_123' + } + }) + ) + ); + } + if (url.indexOf('4218_123.json') > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + prjCoordSys: { epsgCode: 4218 }, + bounds: { + top: 85, + left: -180, + bottom: -85, + leftBottom: { + x: -180, + y: -85 + }, + right: 180, + rightTop: { + x: 180, + y: 85 + } + } + }) + ) + ); + } + if (url.indexOf('prjCoordSys.wkt')) { + return Promise.resolve(new Response(wkt_4218)); + } + }); + const originCrs = maplibregl.CRS.get(epsgCode); + const crsSetSpy = spyOn(maplibregl.CRS, 'set').and.callThrough(); + datavizWebmap = new WebMap( + '123', + { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + } + ); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(originCrs).toBeFalsy(); + expect(maplibregl.CRS.get(epsgCode)).toBeTruthy(); + expect(maplibregl.CRS.get(epsgCode)).toEqual(map.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map.getCRS().getWKT()).toBe(wkt_4218); + expect(map.getCRS().getOrigin()).toEqual([-180, 85]); + expect(map.getStyle().layers.length).toBe(1); + done(); + }); + }); + + it('when uncommon crs was defined, baselayer is MAPBOXSTYLE', (done) => { + const epsgCode = 'EPSG:4219'; + const wkt_4219 = 'GEOGCS["Beijing 1954",DATUM["Beijing_1954",SPHEROID["Krassowsky 1940",6378245,298.3],TOWGS84[15.8,-154.4,-82.3,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4219"]]'; + const mapInfo = { + ...webmap_MAPBOXSTYLE_Tile, + layers: [], + projection: wkt_4219 + }; + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('123/map.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(mapInfo))); + } + if (url.indexOf('/style.json')) { + return Promise.resolve(new Response(JSON.stringify(vectorTile_style))); + } + }); + const originCrs = maplibregl.CRS.get(epsgCode); + const crsSetSpy = spyOn(maplibregl.CRS, 'set').and.callThrough(); + datavizWebmap = new WebMap( + '123', + { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + } + ); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(originCrs).toBeFalsy(); + expect(maplibregl.CRS.get(epsgCode)).toBeTruthy(); + expect(maplibregl.CRS.get(epsgCode)).toEqual(map.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map.getCRS().getWKT()).toBe(wkt_4219); + expect(map.getCRS().getOrigin()).toEqual([vectorTile_style.metadata.indexbounds[0], vectorTile_style.metadata.indexbounds[3]]); + expect(map.getStyle().layers.length).toBe(vectorTile_style.layers.length - 1); + done(); + }); + }); }); diff --git a/test/maplibregl/mapping/WebMapV3Spec.js b/test/maplibregl/mapping/WebMapV3Spec.js index 296d13917..d091c9d46 100644 --- a/test/maplibregl/mapping/WebMapV3Spec.js +++ b/test/maplibregl/mapping/WebMapV3Spec.js @@ -5,11 +5,12 @@ import { createMapClassExtending } from '@supermapgis/iclient-common/mapping/Map import { L7LayerUtil } from '@supermapgis/iclient-common/mapping/utils/L7LayerUtil'; import { WebMap } from '../../../src/maplibregl/mapping/WebMap'; import * as MapManagerUtil from '../../../src/maplibregl/mapping/webmap/MapManager'; +import { CRSManager } from '../../../src/maplibregl/mapping/webmap/CRSManager'; import { featureFilter, expression } from '@maplibre/maplibre-gl-style-spec'; import spec from '@maplibre/maplibre-gl-style-spec/src/reference/v8'; import { L7, L7Layer } from '../../../src/maplibregl/overlay/L7Layer'; import * as mockL7 from '../../tool/mock_l7'; -import mbglmap, { CRS } from '../../tool/mock_maplibregl_map'; +import mbglmap, { CRS, proj4 } from '../../tool/mock_maplibregl_map'; import '../../resources/WebMapV3.js'; import '../../resources/WebMapV5.js'; @@ -22,7 +23,7 @@ describe('maplibregl-webmap3.0', () => { const extendOptions = { MapManager: MapManagerUtil.default, mapRepo: maplibregl, - mapRepoName: 'maplibre-gl', + crsManager: new CRSManager(), l7LayerUtil }; const WebMapV3 = createWebMapV3Extending(createMapClassExtending(maplibregl.Evented), extendOptions); @@ -39,6 +40,8 @@ describe('maplibregl-webmap3.0', () => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; maplibregl.Map.prototype.overlayLayersManager = {}; mbglmap.prototype.getL7Scene = maplibregl.Map.prototype.getL7Scene; + maplibregl.CRS = CRS; + maplibregl.proj4 = proj4; }); afterEach(() => { if (mapstudioWebmap && mapstudioWebmap.map) { @@ -49,6 +52,8 @@ describe('maplibregl-webmap3.0', () => { window.document.body.removeChild(testDiv); jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; mbglmap.prototype.getL7Scene = undefined; + maplibregl.CRS = undefined; + maplibregl.proj4 = undefined; }); it('initialize_background', (done) => { @@ -205,26 +210,30 @@ describe('maplibregl-webmap3.0', () => { wkt: 'GEOGCS["China Geodetic Coordinate System 2000", DATUM["China 2000", SPHEROID["CGCS2000", 6378137.0, 298.257222101, AUTHORITY["EPSG","1024"]], AUTHORITY["EPSG","1043"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["degree", 0.017453292519943295], AXIS["Geodetic latitude", NORTH], AXIS["Geodetic longitude", EAST], AUTHORITY["EPSG","4490"]]' } }; - mapstudioWebmap = new WebMapV3(nextMapInfo, { + spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('web/config/portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('/sprite') > -1) { + return Promise.resolve(new Response(msSpriteInfo)); + } + return Promise.resolve(); + }); + maplibregl.CRS = undefined; + mapstudioWebmap = new WebMap(nextMapInfo, { server: server, target: 'map' }); mapstudioWebmap.on('mapcreatefailed', ({ error }) => { - const throwError = `The EPSG code ${nextMapInfo.crs.name} needs to include maplibre-gl-enhance.js. Refer to the example: https://iclient.supermap.io/examples/maplibregl/editor.html#mvtVectorTile_2362`; + const throwError = 'WebMap needs to include maplibre-gl-enhance.js. Refer to the example: https://iclient.supermap.io/examples/maplibregl/editor.html#mvtVectorTile_2362'; expect(mapstudioWebmap.map).toBeUndefined(); expect(error).toBe(throwError); done(); }); - mapstudioWebmap.initializeMap(nextMapInfo); }); it('projection is 4490 and include maplibre-gl-enhance', (done) => { - spyOn(FetchRequest, 'get').and.callFake((url) => { - if (url.indexOf('/sprite') > -1) { - return Promise.resolve(new Response(msSpriteInfo)); - } - return Promise.resolve(); - }); const mapInfo = JSON.parse(mapstudioWebMap_symbol); const nextMapInfo = { ...mapInfo, @@ -234,13 +243,21 @@ describe('maplibregl-webmap3.0', () => { wkt: 'GEOGCS["China Geodetic Coordinate System 2000", DATUM["China 2000", SPHEROID["CGCS2000", 6378137.0, 298.257222101, AUTHORITY["EPSG","1024"]], AUTHORITY["EPSG","1043"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["degree", 0.017453292519943295], AXIS["Geodetic latitude", NORTH], AXIS["Geodetic longitude", EAST], AUTHORITY["EPSG","4490"]]' } }; - maplibregl.CRS = CRS; - mapstudioWebmap = new WebMapV3(nextMapInfo, { + spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('web/config/portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('/sprite') > -1) { + return Promise.resolve(new Response(msSpriteInfo)); + } + return Promise.resolve(); + }); + mapstudioWebmap = new WebMap(nextMapInfo, { server: server, target: 'map', iportalServiceProxyUrl: 'projection is 4490 and include maplibre-gl-enhance' }); - mapstudioWebmap.initializeMap(nextMapInfo); mapstudioWebmap.on('mapcreatesucceeded', ({ map }) => { expect(map).not.toBeUndefined(); @@ -251,7 +268,6 @@ describe('maplibregl-webmap3.0', () => { const layerCatalogs = mapstudioWebmap.getLayerCatalog(); expect(layerCatalogs.length).toBeLessThanOrEqual(appreciableLayers.length); expect(mapstudioWebmap.getLegends().length).toBe(0); - delete maplibregl.CRS; done(); }); }); @@ -273,7 +289,6 @@ describe('maplibregl-webmap3.0', () => { wkt: 'GEOGCS["China Geodetic Coordinate System 2000", DATUM["China 2000", SPHEROID["CGCS2000", 6378137.0, 298.257222101, AUTHORITY["EPSG","1024"]], AUTHORITY["EPSG","1043"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["degree", 0.017453292519943295], AXIS["Geodetic latitude", NORTH], AXIS["Geodetic longitude", EAST], AUTHORITY["EPSG","4490"]]' } }; - maplibregl.CRS = CRS; mapstudioWebmap = new WebMapV3(nextMapInfo, { server: server, target: 'map' @@ -312,7 +327,6 @@ describe('maplibregl-webmap3.0', () => { const appreciableLayers = mapstudioWebmap.getLayers(); const layerCatalogs = mapstudioWebmap.getLayerCatalog(); expect(layerCatalogs.length).toBeLessThanOrEqual(appreciableLayers.length); - delete maplibregl.CRS; done(); }); }); @@ -566,7 +580,6 @@ describe('maplibregl-webmap3.0', () => { } return Promise.resolve(); }); - maplibregl.CRS = CRS; const mapOptions = { transformRequest: function (url) { return { url }; @@ -591,7 +604,6 @@ describe('maplibregl-webmap3.0', () => { expect(mapstudioWebmap.getLegends().length).toBe(8); expect(mapOptions.transformRequest.calls.count()).toBeGreaterThan(0); delete maplibregl.Map.prototype.getCRS; - delete maplibregl.CRS; done(); }); }); @@ -604,22 +616,42 @@ describe('maplibregl-webmap3.0', () => { return Promise.resolve(); }); const mapInfo = JSON.parse(mapstudioWebMap_symbol); - maplibregl.Map.prototype.getCRS = function () { + const existedMap = new maplibregl.Map({ + container: testDiv, + style: { + version: 8, + sources: {}, + layers: [ + { + paint: { + 'background-color': '#242424' + }, + id: 'background1', + type: 'background' + } + ] + }, + center: [116.640545, 40.531714], + zoom: 7 + }); + existedMap.getCRS = function () { return { epsgCode: '' }; }; mapstudioWebmap = new WebMapV3(mapInfo, { server: server, target: 'map' }); + existedMap.on('load', function () { + mapstudioWebmap.initializeMap(mapInfo, existedMap); + }); mapstudioWebmap.on('projectionnotmatch', () => { expect(mapstudioWebmap.map).not.toBeUndefined(); + expect(mapstudioWebmap.map).toEqual(existedMap); const style = mapstudioWebmap.map.getStyle(); - expect(style.layers.length).toBe(0); - delete maplibregl.Map.prototype.getCRS; + expect(style.layers.length).toBe(1); done(); }); - mapstudioWebmap.initializeMap(mapInfo); }); it('layerdatas', (done) => { @@ -729,9 +761,6 @@ describe('maplibregl-webmap3.0', () => { spyOn(L7, 'HeatmapLayer').and.callFake(mockL7.PointLayer); spyOn(L7, 'Scene').and.callFake(mockL7.Scene); spyOn(L7, 'Maplibre').and.callFake(mockL7.Maplibre); - maplibregl.Map.prototype.getCRS = function () { - return { epsgCode: mapInfo.crs.name, getExtent: () => {} }; - }; spyOn(FetchRequest, 'get').and.callFake((url) => { if (url.indexOf('web/config/portal.json') > -1) { return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); @@ -759,15 +788,12 @@ describe('maplibregl-webmap3.0', () => { } return Promise.resolve(); }); - maplibregl.CRS = CRS; mapstudioWebmap = new WebMap(id, { server: server }); mapstudioWebmap.on('mapcreatesucceeded', ({ map }) => { const webmapInstance = mapstudioWebmap._getWebMapInstance(); expect(webmapInstance.getLegends().length).toBe(4); - delete maplibregl.Map.prototype.getCRS; - delete maplibregl.CRS; spyTest.calls.reset(); done(); }); @@ -811,7 +837,6 @@ describe('maplibregl-webmap3.0', () => { } return Promise.resolve(); }); - maplibregl.CRS = CRS; mapstudioWebmap = new WebMap(id, { server: server }); @@ -835,7 +860,6 @@ describe('maplibregl-webmap3.0', () => { expect(currentOverlayLayers.length).toBe(overlayLayers.length + 1); expect(currentAppreciableLayers.length).toBe(appreciableLayers.length + 1); delete maplibregl.Map.prototype.getCRS; - delete maplibregl.CRS; done(); }); }); @@ -930,7 +954,6 @@ describe('maplibregl-webmap3.0', () => { } return Promise.resolve(); }); - maplibregl.CRS = CRS; const mapOptions = { transformRequest: function (url) { return { url }; @@ -973,7 +996,6 @@ describe('maplibregl-webmap3.0', () => { const newFeatures = map.getSource('ms_1052943054_1715672103742_8').getData().features; expect(newFeatures.length).toBe(1); delete maplibregl.Map.prototype.getCRS; - delete maplibregl.CRS; done(); }); }); @@ -1018,7 +1040,6 @@ describe('maplibregl-webmap3.0', () => { ...mapInfo, crs: crsInfo }; - maplibregl.CRS = CRS; mapstudioWebmap = new WebMapV3(nextMapInfo, { server: server, target: 'map' @@ -1050,7 +1071,6 @@ describe('maplibregl-webmap3.0', () => { expect(map).toEqual(existedMap); expect(mapstudioWebmap.map).toEqual(map); expect(map.addStyle).toHaveBeenCalled(); - delete maplibregl.CRS; done(); }); }); @@ -1079,9 +1099,6 @@ describe('maplibregl-webmap3.0', () => { } return Promise.resolve(); }); - maplibregl.Map.prototype.getCRS = function () { - return { epsgCode: 'EPSG:3857', getExtent: () => {} }; - }; mapstudioWebmap = new WebMap(id, { server: server }); @@ -1090,7 +1107,6 @@ describe('maplibregl-webmap3.0', () => { const webMapV3 = mapstudioWebmap._getWebMapInstance(); expect(map).not.toBeUndefined(); expect(webMapV3.getLegends().length).toBe(9); - delete maplibregl.Map.prototype.getCRS; mbglmap.prototype.getL7Scene = undefined; spyTest.calls.reset(); done(); @@ -1418,4 +1434,93 @@ describe('maplibregl-webmap3.0', () => { done(); }); }); + + it('when builtIn crs was defined, dont set repeat', (done) => { + spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('web/config/portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('map.json') > -1) { + var mapJson = mapstudioWebMap_raster; + return Promise.resolve(new Response(mapJson)); + } + if (url.indexOf('617580084.json') > -1) { + var appInfo = mapstudioAppInfo; + return Promise.resolve(new Response(appInfo)); + } + return Promise.resolve(); + }); + const originCrs = maplibregl.CRS.get('EPSG:3857'); + const crsSetSpy = spyOn(maplibregl.CRS, 'set').and.callThrough(); + mapstudioWebmap = new WebMap(id, { + server: server + }); + mapstudioWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(crsSetSpy).not.toHaveBeenCalled(); + expect(maplibregl.CRS.get('EPSG:3857')).toEqual(originCrs); + expect(maplibregl.CRS.get('EPSG:3857')).toEqual(map.getCRS()); + done(); + }); + }); + + it('when uncommon crs was defined, dont set repeat', (done) => { + const mapInfo = JSON.parse(mapstudioWebMap_symbol); + const wkt_4220 = 'GEOGCS["Beijing 1954",DATUM["Beijing_1954",SPHEROID["Krassowsky 1940",6378245,298.3],TOWGS84[15.8,-154.4,-82.3,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4220"]]'; + const epsgCode = 'EPSG:4220'; + const nextMapInfo = { + ...mapInfo, + crs: { + name: epsgCode, + extent: [-180, -85, 180, 85], + wkt: wkt_4220 + } + }; + spyOn(MapManagerUtil, 'default').and.callFake(mbglmap); + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('web/config/portal.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(iportal_serviceProxy))); + } + if (url.indexOf('/sprite') > -1) { + return Promise.resolve(new Response(msSpriteInfo)); + } + return Promise.resolve(); + }); + const originCrs = maplibregl.CRS.get(epsgCode); + const crsSetSpy = spyOn(maplibregl.CRS, 'set').and.callThrough(); + mapstudioWebmap = new WebMap(nextMapInfo, { + server: server, + target: 'map' + }); + + mapstudioWebmap.once('mapcreatesucceeded', ({ map: map1 }) => { + expect(originCrs).toBeFalsy(); + expect(maplibregl.CRS.get(epsgCode)).toBeTruthy(); + expect(maplibregl.CRS.get(epsgCode)).toEqual(map1.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map1.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map1.getCRS().getWKT()).toBe(wkt_4220); + const originRange = [-180, 85]; + expect(map1.getCRS().getOrigin()).toEqual(originRange); + mapstudioWebmap.setMapId({ + ...mapInfo, + crs: { + name: epsgCode, + extent: [-120, -65, 120, 65], + wkt: wkt_4220 + } + }); + mapstudioWebmap.once('mapcreatesucceeded', ({ map: map2 }) => { + expect(maplibregl.CRS.get(epsgCode)).toBeTruthy(); + expect(maplibregl.CRS.get(epsgCode)).toEqual(map2.getCRS()); + expect(map1.getCRS()).toEqual(map2.getCRS()); + expect(crsSetSpy).toHaveBeenCalledTimes(2); + expect(map2.getCRS().getEpsgCode()).toBe(epsgCode); + expect(map2.getCRS().getWKT()).toBe(wkt_4220); + expect(map2.getCRS().getOrigin()).toEqual(originRange); + delete maplibregl.CRS[epsgCode.replace(':', '')]; + done(); + }); + }); + }); }); diff --git a/test/tool/mock_mapboxgl_map.js b/test/tool/mock_mapboxgl_map.js index ac3786d17..b6b027ac2 100644 --- a/test/tool/mock_mapboxgl_map.js +++ b/test/tool/mock_mapboxgl_map.js @@ -1,4 +1,6 @@ import mapboxgl from 'mapbox-gl'; +import proj4 from 'proj4'; + class VectorTileSource { constructor(id, options) { this.id = id; @@ -408,10 +410,9 @@ const Map = function (options) { }; class CRS { - constructor(epsgCode, WKT, extent, unit) { + constructor(epsgCode, WKT, extent) { this.epsgCode = epsgCode; this.extent = extent; - this.unit = unit; if (Array.isArray(WKT)) { this.extent = WKT; WKT = null; @@ -420,6 +421,14 @@ class CRS { this.extent[1] = Math.max(this.extent[1], -90); } this.WKT = WKT || CRS.defaultWKTs[epsgCode]; + if (this.WKT) { + proj4.defs(epsgCode, this.WKT); + } + const defs = proj4.defs(epsgCode); + if (!defs) { + throw new Error(`${epsgCode} was not defined,make sure the WKT param was not null`); + } + this.unit = defs.units || 'degree'; CRS.set(this); } @@ -441,6 +450,14 @@ class CRS { return this.epsgCode; } + getUnit() { + return this.unit; + } + + getWKT() { + return this.WKT; + } + getOrigin() { return [this.extent[0], this.extent[3]]; } @@ -473,4 +490,4 @@ CRS.EPSG4326 = new CRS('EPSG:4326', [-180, -90, 180, 90]); export default Map; var mapboxglMock = { Map }; -export { mapboxglMock, CRS }; +export { mapboxglMock, CRS, proj4 }; diff --git a/test/tool/mock_maplibregl_map.js b/test/tool/mock_maplibregl_map.js index 2072d89e9..e8ff7dc04 100644 --- a/test/tool/mock_maplibregl_map.js +++ b/test/tool/mock_maplibregl_map.js @@ -1,4 +1,6 @@ import maplibregl from 'maplibre-gl'; +import proj4 from 'proj4'; + class VectorTileSource { constructor(id, options) { this.id = id; @@ -408,10 +410,9 @@ const Map = function (options) { }; class CRS { - constructor(epsgCode, WKT, extent, unit) { + constructor(epsgCode, WKT, extent) { this.epsgCode = epsgCode; this.extent = extent; - this.unit = unit; if (Array.isArray(WKT)) { this.extent = WKT; WKT = null; @@ -420,6 +421,14 @@ class CRS { this.extent[1] = Math.max(this.extent[1], -90); } this.WKT = WKT || CRS.defaultWKTs[epsgCode]; + if (this.WKT) { + proj4.defs(epsgCode, this.WKT); + } + const defs = proj4.defs(epsgCode); + if (!defs) { + throw new Error(`${epsgCode} was not defined,make sure the WKT param was not null`); + } + this.unit = defs.units || 'degree'; CRS.set(this); } @@ -441,6 +450,14 @@ class CRS { return this.epsgCode; } + getUnit() { + return this.unit; + } + + getWKT() { + return this.WKT; + } + getOrigin() { return [this.extent[0], this.extent[3]]; } @@ -473,4 +490,4 @@ CRS.EPSG4326 = new CRS('EPSG:4326', [-180, -90, 180, 90]); export default Map; var maplibreglMock = { Map }; -export { maplibreglMock, CRS }; +export { maplibreglMock, CRS, proj4 };