From 00f722f9693ca822e0018a44d7324e76036fc7fd Mon Sep 17 00:00:00 2001 From: songyumeng Date: Thu, 3 Jul 2025 14:47:00 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E3=80=90version=E3=80=9112.0.1-dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/js/include-web.js | 2 +- package.json | 2 +- src/classic/package.json | 4 ++-- src/common/package.json | 2 +- src/leaflet/package.json | 4 ++-- src/mapboxgl/package.json | 4 ++-- src/maplibregl/package.json | 4 ++-- src/openlayers/package.json | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/js/include-web.js b/examples/js/include-web.js index 7f87e748f..8a8b8ce42 100644 --- a/examples/js/include-web.js +++ b/examples/js/include-web.js @@ -236,6 +236,6 @@ }); window.isLocal = false; window.server = document.location.toString().match(/file:\/\//) ? "http://localhost:8090" : document.location.protocol + "//" + document.location.host; - window.version = "12.0.0-r"; + window.version = "12.0.1"; window.preRelease = ""; })(); diff --git a/package.json b/package.json index ba2f973c8..16761009a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "SuperMapiClient", "description": "SuperMap iClient JavaScript 是一套由 JavaScript 语言编写的 GIS 客户端应用开发包, 支持多源数据地图,支持多终端,跨浏览器, 通过本产品可快速实现浏览器上美观、流畅的地图呈现。", - "version": "12.0.0-r", + "version": "12.0.1-dev", "directories": { "doc": "doc", "example": "examples", diff --git a/src/classic/package.json b/src/classic/package.json index 73b01b024..c09e11b5a 100644 --- a/src/classic/package.json +++ b/src/classic/package.json @@ -1,7 +1,7 @@ { "name": "@supermapgis/iclient-classic", "description": "", - "version": "12.0.0-r", + "version": "12.0.1-dev", "keywords": [ "SuperMap" ], @@ -15,6 +15,6 @@ "license": "Apache-2.0", "dependencies": { "mapv": "2.0.62", - "@supermapgis/iclient-common": "12.0.0-r" + "@supermapgis/iclient-common": "12.0.1-dev" } } diff --git a/src/common/package.json b/src/common/package.json index 1747d84c4..07298cd4f 100644 --- a/src/common/package.json +++ b/src/common/package.json @@ -1,7 +1,7 @@ { "name": "@supermapgis/iclient-common", "description": "", - "version": "12.0.0-r", + "version": "12.0.1-dev", "keywords": [ "SuperMap" ], diff --git a/src/leaflet/package.json b/src/leaflet/package.json index 4483f95e8..0982f1ed0 100644 --- a/src/leaflet/package.json +++ b/src/leaflet/package.json @@ -1,7 +1,7 @@ { "name": "@supermapgis/iclient-leaflet", "description": "", - "version": "12.0.0-r", + "version": "12.0.1-dev", "keywords": [ "SuperMap", "Leaflet" @@ -19,7 +19,7 @@ "echarts":"5.5.0", "mapv":"2.0.62", "leaflet": "1.9.4", - "@supermapgis/iclient-common": "12.0.0-r", + "@supermapgis/iclient-common": "12.0.1-dev", "@mapbox/vector-tile": "1.3.1", "jsonsql": "0.2.5", "pbf": "3.2.1", diff --git a/src/mapboxgl/package.json b/src/mapboxgl/package.json index 935474379..3f7b904d9 100644 --- a/src/mapboxgl/package.json +++ b/src/mapboxgl/package.json @@ -1,7 +1,7 @@ { "name": "@supermapgis/iclient-mapboxgl", "description": "", - "version": "12.0.0-r", + "version": "12.0.1-dev", "keywords": [ "SuperMap", "MapboxGL v1" @@ -20,7 +20,7 @@ "@turf/meta": "^7.2.0", "mapv": "2.0.62", "mapbox-gl": "1.13.2", - "@supermapgis/iclient-common": "12.0.0-r", + "@supermapgis/iclient-common": "12.0.1-dev", "lodash.clonedeep": "^4.5.0", "proj4": "2.17.0" } diff --git a/src/maplibregl/package.json b/src/maplibregl/package.json index 32e04ea6d..984475fed 100644 --- a/src/maplibregl/package.json +++ b/src/maplibregl/package.json @@ -1,7 +1,7 @@ { "name": "@supermapgis/iclient-maplibregl", "description": "", - "version": "12.0.0-r", + "version": "12.0.1-dev", "keywords": [ "SuperMap", "maplibregl" @@ -17,7 +17,7 @@ "dependencies": { "@maplibre/maplibre-gl-style-spec": "^23.3.0", "maplibre-gl": "5.6.0", - "@supermapgis/iclient-common": "12.0.0-r", + "@supermapgis/iclient-common": "12.0.1-dev", "lodash.clonedeep": "^4.5.0", "proj4": "2.17.0" } diff --git a/src/openlayers/package.json b/src/openlayers/package.json index 2d2bc37ea..d519f7cee 100644 --- a/src/openlayers/package.json +++ b/src/openlayers/package.json @@ -1,7 +1,7 @@ { "name": "@supermapgis/iclient-ol", "description": "", - "version": "12.0.0-r", + "version": "12.0.1-dev", "keywords": [ "SuperMap", "OpenLayers" @@ -15,7 +15,7 @@ "author": "SuperMap", "license": "Apache-2.0", "dependencies": { - "@supermapgis/iclient-common": "12.0.0-r", + "@supermapgis/iclient-common": "12.0.1-dev", "@supermapgis/tile-decryptor": "^1.0.0", "@turf/turf": "7.2.0", "mapv": "2.0.62", From 55eb9791ec8de2dbe2e921ba46ef2fcc2d6adfc7 Mon Sep 17 00:00:00 2001 From: xiongjj Date: Fri, 4 Jul 2025 11:32:04 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E3=80=90feature=E3=80=91=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E6=97=B6=E4=BC=98=E5=8C=96zoomBase=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=E6=96=B9=E6=B3=95;=20mapboxstyle=20=E5=BA=95=E5=9B=BE?= =?UTF-8?q?=E6=98=BE=E9=9A=90=E4=BC=98=E5=8C=96=EF=BC=9Breview=20by=20song?= =?UTF-8?q?ym?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/mapping/WebMapService.js | 4 +- src/common/mapping/WebMapV2.js | 90 +++++++- test/mapboxgl/mapping/WebMapV2Spec.js | 309 ++++++++++++++++++++++++++ test/resources/WebMapV5.js | 66 +++++- 4 files changed, 454 insertions(+), 15 deletions(-) diff --git a/src/common/mapping/WebMapService.js b/src/common/mapping/WebMapService.js index ed10a9524..229ca1f60 100644 --- a/src/common/mapping/WebMapService.js +++ b/src/common/mapping/WebMapService.js @@ -225,6 +225,7 @@ export class WebMapService { let bounds; let restResourceURL = ''; let kvpResourceUrl = ''; + const scales = []; const proxy = this.handleProxy(); let serviceUrl = Util.urlAppend(layerInfo.url, 'REQUEST=GetCapabilities&SERVICE=WMTS&VERSION=1.0.0'); serviceUrl = this.handleParentRes(serviceUrl); @@ -297,6 +298,7 @@ export class WebMapService { const tileMatrix = tileMatrixSet[i].TileMatrix[j]; const identifier = tileMatrix['ows:Identifier']; const topLeftCorner = [...tileMatrix['TopLeftCorner'].split(' ')]; + scales.push(tileMatrix['ScaleDenominator']); if ( (!this.numberEqual(topLeftCorner[0], defaultCRSTopLeftCorner[0]) || !this.numberEqual(topLeftCorner[1], defaultCRSTopLeftCorner[1])) && @@ -365,7 +367,7 @@ export class WebMapService { restResourceURL = resourceUrl['@_template']; } } - resolve({ isMatched, matchMaxZoom, matchMinZoom, style, bounds, restResourceURL, kvpResourceUrl }); + resolve({ isMatched, matchMaxZoom, matchMinZoom, style, bounds, restResourceURL, kvpResourceUrl, scales }); }) .catch(error => { reject(error); diff --git a/src/common/mapping/WebMapV2.js b/src/common/mapping/WebMapV2.js index 00519e4db..28c97aa90 100644 --- a/src/common/mapping/WebMapV2.js +++ b/src/common/mapping/WebMapV2.js @@ -114,9 +114,10 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, crsMa this._handleLayerInfo(mapInfo, _taskID); } else { setTimeout(() => { - this._createMap(mapInfo); - this.map.on('load', () => { - this._handleLayerInfo(mapInfo, _taskID); + this._createMap(mapInfo).then(() => { + this.map.on('load', () => { + this._handleLayerInfo(mapInfo, _taskID); + }); }); }, 0); } @@ -236,7 +237,78 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, crsMa return true; } - _createMap(mapInfo) { + async _getScales(mapInfo) { + const baseLayerInfo = mapInfo.baseLayer; + const scales = []; + let coordUnit = baseLayerInfo.coordUnit || baseLayerInfo.units || mapRepo.CRS.get(this.baseProjection).unit; + if (!coordUnit) { + coordUnit = this.baseProjection == 'EPSG:3857' ? 'm' : 'degree'; + } + let visibleScales = null; + if (baseLayerInfo.layerType === 'TILE') { + try { + const reqOptions = { + withoutFormatSuffix: true, + withCredentials: this.webMapService.handleWithCredentials('', baseLayerInfo.url, false) + }; + const res = await this.getBounds(`${baseLayerInfo.url}.json`, reqOptions) + if (res && res.visibleScales) { + visibleScales = res.visibleScales; + } + } catch (error) { + console.error(error); + } + } + if (visibleScales && visibleScales.length > 0) { + //底部设置过固定比例尺,则使用设置的 + visibleScales.forEach((scale) => { + const value = 1 / scale; + scales.push(`1:${value}`); + }); + } else if (baseLayerInfo.layerType === 'WMTS') { + try { + const result = await this.webMapService.getWmtsInfo(baseLayerInfo, this.baseProjection); + result.scales.forEach((scale) => { + scales.push(`1:${scale}`); + }); + } catch (error) { + console.error(error); + } + } else if (baseLayerInfo.layerType === 'ZXY_TILE') { + const { resolutions: visibleResolution } = baseLayerInfo; + visibleResolution.forEach((result) => { + const currentScale = '1:' + Util.getScaleFromResolution(result, coordUnit); + scales.push(currentScale); + }); + } else { + const extent = baseLayerInfo.layerType === 'MAPBOXSTYLE' ? mapRepo.CRS.get(this.baseProjection).extent : mapInfo.extent; + const resolutions = this._getResolutionsByExtent({ + extent: this._transExtentToBounds(extent), + tileSize: 512 + }); + for (const res of resolutions) { + const scale = '1:' + Util.getScaleFromResolution(res, coordUnit); + if (scales.indexOf(scale) === -1) { + scales.push(scale); + } + } + } + return scales; + } + + _transExtentToBounds(extent) { + if (extent instanceof Array) { + return extent; + } + return [ + extent.leftBottom.x, + extent.leftBottom.y, + extent.rightTop.x, + extent.rightTop.y + ]; + } + + async _createMap(mapInfo) { // 获取字体样式 const fontFamilys = this._getLabelFontFamily(mapInfo); const center = this._getMapCenter(mapInfo); @@ -247,10 +319,6 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, crsMa const interactive = this.mapOptions.interactive; const tileSize = mapInfo.baseLayer.tileSize; - if (mapInfo.baseLayer.layerType === 'ZXY_TILE') { - const {leftBottom, rightTop} = mapInfo.extent; - mapInfo.visibleExtent = [leftBottom.x, leftBottom.y, rightTop.x, rightTop.y]; - } if (isNaN(minZoom)) { minZoom = mapInfo.minScale ? this._transformScaleToZoom(mapInfo.minScale, mapRepo.CRS.get(this.baseProjection), tileSize) @@ -272,8 +340,9 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, crsMa } if (!bounds) { if (mapInfo.minScale && mapInfo.maxScale) { + const scales = await this._getScales(mapInfo); zoomBase = Math.min( - this._transformScaleToZoom(mapInfo.minScale, mapRepo.CRS.get(this.baseProjection), tileSize), + this._transformScaleToZoom(scales[0] || mapInfo.minScale, mapRepo.CRS.get(this.baseProjection), tileSize), this._transformScaleToZoom(mapInfo.maxScale, mapRepo.CRS.get(this.baseProjection), tileSize) ); } else { @@ -294,7 +363,6 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, crsMa maxZoom, bearing: this.bearing || 0, pitch: this.pitch || 0, - bounds, interactive: interactive === void 0 ? true : interactive, style: { version: 8, @@ -3127,7 +3195,7 @@ export function createWebMapV2Extending(SuperClass, { MapManager, mapRepo, crsMa } _getVisibility(visible) { - const visibility = visible === true || visible === 'visible' ? 'visible' : 'none'; + const visibility = visible === false || visible === 'none' ? 'none' : 'visible'; return visibility; } diff --git a/test/mapboxgl/mapping/WebMapV2Spec.js b/test/mapboxgl/mapping/WebMapV2Spec.js index c2ff4d4e3..a6b3a505f 100644 --- a/test/mapboxgl/mapping/WebMapV2Spec.js +++ b/test/mapboxgl/mapping/WebMapV2Spec.js @@ -3791,4 +3791,313 @@ describe('mapboxgl_WebMapV2', () => { done(); }); }); + + it('baselayer is TILE, calc zoomBase with visibleScales', (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('123/map.json') > -1) { + return Promise.resolve(new Response(JSON.stringify({ + ...dynamicProjectionMapInfo, + layers: [] + }))); + } + if (url.indexOf(`China_Dark.json`) > -1) { + return Promise.resolve( + new Response( + JSON.stringify({ + prjCoordSys: { epsgCode: -1 }, + bounds: { + top: 20037508.342789087, + left: -20037508.342789248, + bottom: -25819498.513543323, + leftBottom: { + x: -20037508.342789248, + y: -25819498.513543323 + }, + right: 20037508.342789244, + rightTop: { + x: 20037508.342789244, + y: 20037508.342789087 + } + }, + visibleScales:[ + 1.6901635716001733e-9, + 3.3803271432574796e-9, + 6.760654286286427e-9, + 1.3521308573486984e-8, + 2.7042617146973967e-8, + 5.408523427932187e-8, + 1.0817046855864374e-7, + 2.163409371172875e-7, + 4.32681874234575e-7, + 8.6536374846915e-7, + 0.0000017307274969383, + 0.0000034614549938766, + 0.0000069229099877532 + ] + }) + ) + ); + } + }); + datavizWebmap = new WebMap('123', { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + }); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(map.getZoom()).toBe(dynamicProjectionMapInfo.level - 1); + done(); + }); + }); + + it('baselayer is ZXY_TILE, calc zoomBase with resolutions, minScale 0', (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('123/map.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(baseLayerIsZXY_TILEMapInfo))); + } + }); + datavizWebmap = new WebMap('123', { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + }); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(map.getZoom()).toBe(baseLayerIsZXY_TILEMapInfo.level); + done(); + }); + }); + + it('baselayer is ZXY_TILE, calc zoomBase with resolutions, minScale 10', (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('123/map.json') > -1) { + return Promise.resolve(new Response(JSON.stringify({ + ...baseLayerIsZXY_TILEMapInfo, + minScale: "1:577791.7098724197" + }))); + } + }); + datavizWebmap = new WebMap('123', { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + }); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(map.getZoom()).toBe(baseLayerIsZXY_TILEMapInfo.level); + done(); + }); + }); + + it('baseLayer is WMTS, calc zoomBase with scales', (done) => { + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('map-china400/wmts100') > -1) { + return Promise.resolve(new Response(wmtsCapabilitiesText)); + } + return Promise.resolve(new Response(JSON.stringify({}))); + }); + datavizWebmap = new WebMap(baseLayers['WMTS'], { ...commonOption }); + const callback = function ({ map }) { + expect(map.getZoom()).toBe(baseLayers['WMTS'].level); + done(); + }; + datavizWebmap.on('mapcreatesucceeded', callback); + }); + + it('baselayer is jinjing mvt, calc zoomBase with resolutions', (done) => { + const mapInfo = { + ...webmap_MAPBOXSTYLE_Tile, + layers: [], + maxScale: "1:70.45225847627215", + minScale: "1:1154289.802875243" + }; + 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({ + visibleScales: [ + 1.6901635716026555e-9, + 3.3803271432053056e-9, + 6.760654286410611e-9, + 1.3521308572821242e-8, + 2.7042617145642484e-8, + 5.408523429128511e-8, + 1.0817046858256998e-7, + 2.1634093716513974e-7, + 4.3268187433028044e-7, + 8.653637486605571e-7, + 0.0000017307274973211203, + 0.0000034614549946422405, + 0.0000069229099892844565 + ] + }))); + }); + datavizWebmap = new WebMap('123', { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + }); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(map.getZoom()).toBe(webmap_MAPBOXSTYLE_Tile.level); + const { layers } = map.getStyle(); + expect(layers[0].layout.visibility).toBe('visible'); + done(); + }); + }); + + it('baselayer is jinjing TILE, calc zoomBase with resolutions', (done) => { + const mapInfo = { + ...webmap_MAPBOXSTYLE_Tile, + baseLayer: { + layerType: 'TILE', + name: '京津地区地图', + url: 'http://localhost:8090/iserver/services/map-jingjin/rest/maps/%E4%BA%AC%E6%B4%A5%E5%9C%B0%E5%8C%BA%E5%9C%B0%E5%9B%BE' + }, + layers: [], + level: 4, + maxScale: '1:0.96376561276023', + minScale: '1:4042325.9646626837', + extent: { + leftBottom: { + x: 114.58902605452259, + y: 37.76434929128856 + }, + rightTop: { + x: 119.51371730073062, + y: 42.31307532235788 + } + } + }; + 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({ + visibleScales: [] + }))); + }); + datavizWebmap = new WebMap('123', { + target: 'map', + serverUrl: 'http://fake/fakeiportal', + withCredentials: false + }); + datavizWebmap.on('mapcreatesucceeded', ({ map }) => { + expect(map.getZoom()).toBeCloseTo(10.19); + done(); + }); + }); + + it('baseLayer is others, calc zoomBase with resolutions', (done) => { + const metaInfo = { + resourceSets: [ + { + resources: [ + { + __type: 'ImageryMetadata:http://schemas.microsoft.com/search/local/ws/rest/v1', + imageHeight: 256, + imageUrl: + 'https://{subdomain}.ssl.ak.dynamic.tiles.virtualearth.net/comp/ch/{quadkey}?mkt=zh-CN&it=G,L&shading=hill&og=2505&n=z', + imageUrlSubdomains: ['t0', 't1', 't2', 't3'], + imageWidth: 256 + } + ] + } + ], + statusCode: 200, + statusDescription: 'OK' + }; + spyOn(FetchRequest, 'get').and.callFake((url) => { + if (url.indexOf('Imagery/Metadata/RoadOnDemand') > -1) { + return Promise.resolve(new Response(JSON.stringify(metaInfo))); + } + return Promise.resolve(); + }); + const mapInfo = baseLayers['BING']; + const callback = function ({ map }) { + expect(map.getZoom()).toBe(mapInfo.level); + done(); + }; + datavizWebmap = new WebMap(mapInfo, { + bingMapsKey: 'AhOVlIlR89XkNyDsXBAb7TjabrEokPoqhjk4ncLm9cQkJ5ae_JyhgV1wMcWnVrko' + }); + datavizWebmap.on('mapcreatesucceeded', callback); + }); + + it('test MAPBOXSTYLE layers visibility', (done) => { + const mapInfo = { + ...webmap_MAPBOXSTYLE_Tile, + layers: [{ + layerType: 'MAPBOXSTYLE', + name: 'China', + dataSource: { + type: 'EXTERNAL', + url: 'https://fakeiportal.supermap.io/iserver/services/map-china400/restjsr/v1/vectortile/maps/China' + }, + visible: false + }] + } + const china4326StyleJSON = JSON.parse(styleJson); + const chinaStyleJSON = { + ...china4326StyleJSON, + sources: { + "china_source": china4326StyleJSON.sources["ChinaqxAlberts_4548@fl-new"] + }, + layers: [{ + ...china4326StyleJSON.layers[1], + id: "china_layer", + "source-layer": "china_source_layer", + source: "china_source" + }] + } + spyOn(FetchRequest, 'get').and.callFake((url, params, options) => { + 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(mapInfo))); + } + if (url.indexOf('maps/China_4326/style.json') > -1) { + return Promise.resolve(new Response(styleJson)); + } + if (url.indexOf('maps/China/style.json') > -1) { + return Promise.resolve(new Response(JSON.stringify(chinaStyleJSON))); + } + return Promise.resolve(); + }); + datavizWebmap = new WebMap(id, { + server: server + }); + datavizWebmap.on('mapcreatesucceeded', (data) => { + expect(data.map.addStyle).toHaveBeenCalledTimes(2); + const hideLayer = datavizWebmap.getLayers().find(layer => layer.id === "china_source_layer"); + expect(hideLayer).not.toBeUndefined(); + expect(hideLayer.visible).toBeFalsy(); + const layersOnMap = data.map.getStyle().layers; + expect(layersOnMap.filter(item => item.layout.visibility === "visible").length).toBe(2); + const matchHideLayerOnMap = layersOnMap.find(layer => layer.id === "china_layer"); + expect(matchHideLayerOnMap).not.toBeUndefined(); + expect(matchHideLayerOnMap.layout.visibility).toBe("none"); + done(); + }); + }); }); diff --git a/test/resources/WebMapV5.js b/test/resources/WebMapV5.js index 4ea7f8ff4..27c9ebfe2 100644 --- a/test/resources/WebMapV5.js +++ b/test/resources/WebMapV5.js @@ -4072,8 +4072,8 @@ var dynamicProjectionMapInfo = { y: 20037508.34258019 } }, - maxScale: '1:144447.9275', - level: 12, + maxScale: '1:144447.92746805', + level: 6, center: { x: 12654327.874745157, y: 248497.88596388634 @@ -4099,8 +4099,68 @@ var dynamicProjectionMapInfo = { ], description: '', projection: 'EPSG:3857', - minScale: '1:591658710.91', + minScale: '1:73957338.8636414', title: '动态投影', version: '2.4.3', rootUrl: 'http://localhost:8190/iportal/' }; + +var baseLayerIsZXY_TILEMapInfo = { + extent: { + leftBottom: { + x: 795233.5770899998, + y: 794267.8361200001 + }, + rightTop: { + x: 872991.5360700004, + y: 853188.3580900002 + } + }, + maxScale: '1:564.249716499433', + level: 10, + center: { + x: 834112.5574142822, + y: 823728.096774991 + }, + baseLayer: { + layerType: 'ZXY_TILE', + subdomains: [], + visible: true, + mapBounds: [795233.5770899998, 794267.8361200001, 872991.5360700004, 853188.3580900002], + maxZoom: 24, + origin: [-4786700, 8353100], + tileSize: 256, + name: 'HK', + minZoom: 10, + resolutions: [ + 156543.03392800014, 78271.51696399994, 39135.75848200009, 19567.87924099992, 9783.93962049996, 4891.96981024998, + 2445.98490512499, 1222.992452562495, 611.4962262813797, 305.74811314055756, 152.87405657041106, 76.43702828507324, + 38.21851414253662, 19.10925707126831, 9.554628535634155, 4.77731426794937, 2.388657133974685, 1.1943285668550503, + 0.5971642835598172, 0.29858214164761665, 0.14929107082380833 + ], + url: 'https://mapapi.geodata.gov.hk/gs/api/v1.0.0/xyz/basemap/HK80/{z}/{x}/{y}.png' + }, + layers: [ + { + layerType: 'ZXY_TILE', + subdomains: [], + visible: true, + maxZoom: 24, + origin: [-4786700, 8353100], + tileSize: 256, + name: 'hk-叠加-0-bounds', + minZoom: 0, + resolutions: [ + 156543.03392800014, 78271.51696399994, 0.5971642835598172, 0.29858214164761665, 0.14929107082380833 + ], + url: 'https://mapapi.geodata.gov.hk/gs/api/v1.0.0/xyz/basemap/HK80/{z}/{x}/{y}.png' + } + ], + description: '', + projection: + 'PROJCS["Hong Kong 1980 Grid System", \r\n GEOGCS["Hong Kong 1980", \r\n DATUM["Hong Kong 1980", \r\n SPHEROID["International 1924", 6378388.0, 297.0, AUTHORITY["EPSG","7022"]], \r\n TOWGS84[-162.619, -276.959, -161.764, 0.067753, -2.243649, -1.158827, -1.094246], \r\n AUTHORITY["EPSG","6611"]], \r\n PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], \r\n UNIT["degree", 0.017453292519943295], \r\n AXIS["lat", NORTH], \r\n AXIS["lon", EAST], \r\n AUTHORITY["EPSG","4611"]], \r\n PROJECTION["Transverse_Mercator", AUTHORITY["EPSG","9807"]], \r\n PARAMETER["central_meridian", 114.17855555555556], \r\n PARAMETER["latitude_of_origin", 22.312133333333335], \r\n PARAMETER["scale_factor", 1.0], \r\n PARAMETER["false_easting", 836694.05], \r\n PARAMETER["false_northing", 819069.8], \r\n UNIT["m", 1.0], \r\n AXIS["Northing", NORTH], \r\n AXIS["Easting", EAST], \r\n AUTHORITY["EPSG","2326"]]', + minScale: '1:591658710.9089769', + title: 'hk-叠加-0-无bounds', + version: '2.4.3', + rootUrl: 'http://172.16.14.44:8190/iportal/' +}; \ No newline at end of file From 5dc375a6406e66f9e4010d720541f3ea578ef6db Mon Sep 17 00:00:00 2001 From: xiongjj Date: Thu, 7 Aug 2025 10:07:17 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E3=80=90feature=E3=80=91mapboxgl/maplibreg?= =?UTF-8?q?l=E6=96=B0=E5=A2=9E=E5=88=87=E6=8D=A2=E5=BA=95=E5=9B=BEapi;=20r?= =?UTF-8?q?eview=20by=20chenxh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/mapping/MapBase.js | 4 + src/common/mapping/WebMapBase.js | 16 + .../mapping/utils/AppreciableLayerBase.js | 130 ++++- src/common/mapping/utils/SourceListModelV2.js | 15 + src/common/mapping/utils/SourceListModelV3.js | 128 ++++- .../mapping/utils/SourceListModelV2Spec.js | 474 +++++++++++++++++- .../mapping/utils/SourceListModelV3Spec.js | 378 ++++++++++++-- 7 files changed, 1082 insertions(+), 63 deletions(-) diff --git a/src/common/mapping/MapBase.js b/src/common/mapping/MapBase.js index 22d3512f8..d4a221adc 100644 --- a/src/common/mapping/MapBase.js +++ b/src/common/mapping/MapBase.js @@ -57,6 +57,10 @@ export function createMapClassExtending(SuperClass = class {}) { return exsitLayers; } + changeBaseLayer() { + this._sourceListModel && this._sourceListModel.changeBaseLayer(...arguments); + } + echartsLayerResize() {} updateOverlayLayer() {} diff --git a/src/common/mapping/WebMapBase.js b/src/common/mapping/WebMapBase.js index 335d6b87b..5912e536a 100644 --- a/src/common/mapping/WebMapBase.js +++ b/src/common/mapping/WebMapBase.js @@ -486,6 +486,22 @@ export function createWebMapBaseExtending(SuperClass, { mapRepo }) { this.clean(false); } + /** + * @version 12.0.1 + * @function WebMapBase.prototype.changeBaseLayer + * @description 切换底图 + * @param {BaseLayerConfig} layer - 图层配置对象 + * @typedef {Object} BaseLayerConfig + * @property {string} id - 唯一标识 + * @property {string} title - 显示名称 + * @property {Array} layers - 上图的图层 + * @property {Object} sources - 图层对应的source + * @returns {Object} 当前底图信息 + */ + changeBaseLayer() { + return this._handler && this._handler.changeBaseLayer(...arguments); + } + _readyForInitializingWebMap() { this._initWebMap(!this.map); } diff --git a/src/common/mapping/utils/AppreciableLayerBase.js b/src/common/mapping/utils/AppreciableLayerBase.js index 45bb6aa85..ec088a003 100644 --- a/src/common/mapping/utils/AppreciableLayerBase.js +++ b/src/common/mapping/utils/AppreciableLayerBase.js @@ -1,12 +1,12 @@ import { Events } from '../../commontypes'; import SourceModel from './SourceModel'; import { createAppreciableLayerId, getLayerInfosFromCatalogs } from './util'; +import cloneDeep from 'lodash.clonedeep'; export class AppreciableLayerBase extends Events { constructor(options = {}) { super(); this.map = options.map; - this.layers = options.layers || []; this.appendLayers = options.appendLayers || false; this.unexpectedSourceNames = [ 'tdt-search-', @@ -21,12 +21,23 @@ export class AppreciableLayerBase extends Events { this.layersVisibleMap = new Map(); this.eventTypes = ['layerupdatechanged']; this._styleDataUpdatedHandler = this._styleDataUpdatedHandler.bind(this); + this.baseLayerInfoOnMap = null; + this.setLayers(options.layers); + this._initBaseLayerInfo(options.layers); } - setLayers(layers) { + setLayers(layers = []) { this.layers = layers; } + setDefaultBaseLayerInfo(baseLayerInfo) { + this.baseLayerInfoOnMap = baseLayerInfo; + } + + setBaseLayer() { + throw new Error('setBaseLayer is not implemented'); + } + createLayerCatalogs() { throw new Error('createLayerCatalogs is not implemented'); } @@ -39,6 +50,14 @@ export class AppreciableLayerBase extends Events { throw new Error('initLayers is not implemented'); } + changeBaseLayer(layer) { + if (this.map) { + this._removeBaseLayer(); + this._addBaseLayer(layer); + return cloneDeep(this.baseLayerInfoOnMap); + } + } + createAppreciableLayers() { const detailLayers = this.initLayers(); return this._initAppreciableLayers(detailLayers); @@ -335,4 +354,111 @@ export class AppreciableLayerBase extends Events { } return topLayers.concat(autoLayers, bottomLayers); } + + _addBaseLayer(layerItem) { + const { layers, sources } = layerItem; + const renderSources = {}; + Object.keys(sources).forEach(sourceId => { + let nextSourceId = sourceId; + if (this.map.getSource(sourceId)) { + renderSources[sourceId] = `${sourceId}_${+new Date()}`; + nextSourceId = renderSources[sourceId]; + } + this.map.addSource(nextSourceId, sources[sourceId]); + }); + const layersToAdd = []; + layers.forEach(layer => { + let { beforeId } = layer; + if (!beforeId) { + const styles = this.map.getStyle(); + beforeId = styles.layers[0] && styles.layers[0].id; + } + const layerToAdd = Object.assign({}, layer); + delete layerToAdd.beforeId; + const sourceId = layerToAdd.source; + if (renderSources[sourceId]) { + layerToAdd.source = renderSources[sourceId]; + } + if (this.map.getLayer(layerToAdd.id)) { + const nextLayerId = `${layerToAdd.id}_${layerToAdd.source || +new Date()}`; + layerToAdd.id = nextLayerId; + } + layersToAdd.push({ + layer: layerToAdd, + beforeId + }); + }); + this.baseLayerInfoOnMap = { + ...layerItem, + layers: layersToAdd.map(item => Object.assign({}, item.layer)), + sources: Object.keys(layerItem.sources).reduce((sources, sourceId) => { + let source = sourceId; + if (renderSources[source]) { + source = renderSources[source]; + } + sources[source] = renderSources[sourceId]; + return sources; + }, {}) + }; + this.setBaseLayer({ ...this.baseLayerInfoOnMap }); + layersToAdd.forEach(({ layer, beforeId }) => { + this.map.addLayer(layer, beforeId); + }) + } + + _removeBaseLayer() { + if (this.baseLayerInfoOnMap) { + const { layers, sources } = this.baseLayerInfoOnMap; + const layersIds = layers.map(item => item.id); + const sourceIds = Object.keys(sources); + layersIds.forEach(layerId => { + if (this.map.getLayer(layerId)) { + this.map.removeLayer(layerId); + } + }); + sourceIds.forEach(sourceId => { + if (this.map.getSource(sourceId)) { + this.map.removeSource(sourceId); + } + }); + } + } + + _initBaseLayerInfo(layers) { + if (layers && layers.length && !this.baseLayerInfoOnMap) { + const firstLayer = this.layers[0]; + const baseLayer = firstLayer; + const layerList = this.map.getStyle().layers; + const baseLayersOnMap = baseLayer.renderLayers.map((layerId) => { + const nextLayer = layerList.find(item => item.id === layerId); + if (nextLayer) { + const layerIndex = layerList.findIndex(item => item.id === layerId); + const nextLayerIndex = layerIndex + 1; + if (layerList[nextLayerIndex]) { + nextLayer.beforeId = layerList[nextLayerIndex].id; + } + if (!nextLayer.metadata || !nextLayer.metadata.SM_Layer_Title) { + nextLayer.metadata = { + ...nextLayer.metadata, + SM_Layer_Title: baseLayer.title + }; + } + } + return nextLayer; + }).filter(Boolean); + const sourcesMap = this.map.getStyle().sources; + this.setDefaultBaseLayerInfo({ + id: `__default__${baseLayer.id}`, + title: baseLayer.title, + layers: baseLayersOnMap, + sources: baseLayersOnMap.reduce((sources, layer) => { + const sourceId = layer.source; + if (sourceId && !sources[sourceId]) { + sources[sourceId] = sourcesMap[sourceId]; + } + return sources; + }, {}) + }); + } + } } diff --git a/src/common/mapping/utils/SourceListModelV2.js b/src/common/mapping/utils/SourceListModelV2.js index 2cbcb7196..e10d7ae28 100644 --- a/src/common/mapping/utils/SourceListModelV2.js +++ b/src/common/mapping/utils/SourceListModelV2.js @@ -47,6 +47,21 @@ export class SourceListModelV2 extends AppreciableLayerBase { return this.concatExpectLayers(selfLayers, selfLayerIds, nextLayers); } + setBaseLayer(layerItem) { + const nextLayers = this.layers.slice(1); + const firstLayer = layerItem.layers[0] || {}; + const defaultId = firstLayer.id || ''; + const baseLayer = { + id: layerItem.id || defaultId, + visible: true, + name: layerItem.title || defaultId, + title: layerItem.title || defaultId, + renderLayers: layerItem.layers.map(item => item.id) + } + nextLayers.unshift(baseLayer); + this.setLayers(nextLayers); + } + _isBelongToMapJSON(layerFromMapJSON, layerOnMap) { return ( layerFromMapJSON.renderLayers && layerFromMapJSON.renderLayers.some((subLayerId) => subLayerId === layerOnMap.id) diff --git a/src/common/mapping/utils/SourceListModelV3.js b/src/common/mapping/utils/SourceListModelV3.js index 82b1ca0cd..af7525eab 100644 --- a/src/common/mapping/utils/SourceListModelV3.js +++ b/src/common/mapping/utils/SourceListModelV3.js @@ -1,4 +1,3 @@ - import { AppreciableLayerBase } from './AppreciableLayerBase'; import { getLayerCatalogRenderLayers, getLayerInfosFromCatalogs, getMainLayerFromCatalog } from './util'; @@ -6,17 +5,23 @@ export class SourceListModelV3 extends AppreciableLayerBase { constructor(options = {}) { super(options); this._mapInfo = options.mapInfo; - this._layerCatalog = options.mapInfo.metadata.layerCatalog; this._mapResourceInfo = options.mapResourceInfo; this._l7LayerUtil = options.l7LayerUtil; this._legendList = options.legendList; // chart 统计图表 point-extrusion 柱状图 this._immutableTopOrderLayers = ['chart', 'point-extrusion']; + const layerCatalogs = options.mapInfo.metadata.layerCatalog; + this.setLayerCatalog(layerCatalogs); const layers = this._generateLayers(); this.setLayers(layers); + this.setDefaultBaseLayerInfo(this._generateBaseLayerInfo(layerCatalogs, layers)); this.initDatas(); } + setLayerCatalog(catalogs) { + this._layerCatalog = catalogs; + } + createLayerCatalogs() { const appreciableLayers = this.getLayers(false); const sourceList = this._createSourceCatalogs(this._layerCatalog, appreciableLayers); @@ -47,6 +52,47 @@ export class SourceListModelV3 extends AppreciableLayerBase { return this.concatExpectLayers(selfLayers, selfLayerIds, nextLayers); } + setBaseLayer(layerItem) { + const nextLayers = this.layers.slice(); + const nextLayerCatalog = this._layerCatalog.slice(); + const originBaseLayerCatalog = nextLayerCatalog.pop(); + this._removeBaseLayerRenderLayers(originBaseLayerCatalog, nextLayers); + const baseLayers = layerItem.layers.map((layer) => { + return { + ...layer, + layerInfo: { + id: layer.id, + title: layer.id, + renderLayers: [layer.id] + } + }; + }); + nextLayers.unshift(...baseLayers); + const firstLayer = layerItem.layers[0] || {}; + const defaultId = firstLayer.id || ''; + const baseLayerCatalog = { + id: defaultId, + title: layerItem.title || defaultId, + type: 'basic', + visible: true + }; + if (layerItem.layers.length > 1) { + baseLayerCatalog.id = layerItem.id || defaultId; + baseLayerCatalog.type = 'group'; + baseLayerCatalog.children = layerItem.layers.map((layer) => { + return { + id: layer.id, + title: layer.id, + type: 'basic', + visible: true + }; + }); + } + nextLayerCatalog.push(baseLayerCatalog); + this.setLayerCatalog(nextLayerCatalog); + this.setLayers(nextLayers); + } + _createSourceCatalogs(catalogs, appreciableLayers) { const formatCatalogs = catalogs.map((catalog) => { let formatItem; @@ -60,7 +106,7 @@ export class SourceListModelV3 extends AppreciableLayerBase { visible }; } else { - const renderLayers = getLayerCatalogRenderLayers(parts, id, this._mapInfo.layers); + const renderLayers = getLayerCatalogRenderLayers(parts, id, this.layers); const matchLayer = appreciableLayers.find((layer) => layer.id === renderLayers[0]); this.removeLayerExtralFields([matchLayer]); formatItem = Object.assign({}, matchLayer); @@ -75,10 +121,15 @@ export class SourceListModelV3 extends AppreciableLayerBase { const projectCataglogs = getLayerInfosFromCatalogs(catalogs, 'catalogType'); const metadataCatalogs = getLayerInfosFromCatalogs(this._mapInfo.metadata.layerCatalog); const l7MarkerLayers = this._l7LayerUtil.getL7MarkerLayers(); - const layerDatas = metadataCatalogs.map(layerCatalog => { + const layerDatas = metadataCatalogs.map((layerCatalog) => { const renderLayers = getLayerCatalogRenderLayers(layerCatalog.parts, layerCatalog.id, this._mapInfo.layers); const layer = getMainLayerFromCatalog(layerCatalog.parts, layerCatalog.id, this._mapInfo.layers); - const layerInfo = { id: layer.id, title: layerCatalog.title, renderLayers, reused: layer.metadata && layer.metadata.reused }; + const layerInfo = { + id: layer.id, + title: layerCatalog.title, + renderLayers, + reused: layer.metadata && layer.metadata.reused + }; const matchProjectCatalog = projectCataglogs.find((item) => item.id === layerCatalog.id) || {}; const { msDatasetId } = matchProjectCatalog; let dataSource = {}; @@ -89,7 +140,7 @@ export class SourceListModelV3 extends AppreciableLayerBase { dataSource = { serverId: matchData.datasetId, type: data.sourceType - } + }; if (data.sourceType === 'REST_DATA') { const [serverUrl, datasourceName] = data.url.split('/rest/data/datasources/'); dataSource.url = `${serverUrl}/rest/data`; @@ -127,7 +178,7 @@ export class SourceListModelV3 extends AppreciableLayerBase { if (validThemeFields.length > 0) { layerInfo.themeSetting = { themeField: validThemeFields }; } - if (this._immutableTopOrderLayers.some(type => type === layer.type)) { + if (this._immutableTopOrderLayers.some((type) => type === layer.type)) { layerInfo.metadata = Object.assign({}, layer.metadata, { SM_Layer_Order: 'top' }); } return Object.assign({}, layer, { layerInfo }); @@ -135,4 +186,67 @@ export class SourceListModelV3 extends AppreciableLayerBase { layerDatas.reverse(); return layerDatas; } + + _generateBaseLayerInfo(layerCatalog, layers) { + const nextLayerCatalog = layerCatalog.slice(); + const originBaseLayerCatalog = nextLayerCatalog.pop(); + const renderLayers = this._getBaseLayerRenderLayers(originBaseLayerCatalog, layers); + const baseLayersOnMap = renderLayers.map((layer) => { + const nextLayer = { ...layer }; + delete nextLayer.layerInfo; + const layerIndex = layers.findIndex((item) => item.id === layer.id); + const nextLayerIndex = layerIndex + 1; + if (layers[nextLayerIndex]) { + nextLayer.beforeId = layers[nextLayerIndex].id; + } + return nextLayer; + }); + const sourcesMap = this.map.getStyle().sources; + return { + id: `__default__${originBaseLayerCatalog.id}`, + title: originBaseLayerCatalog.title, + layers: baseLayersOnMap, + sources: baseLayersOnMap.reduce((sources, layer) => { + const sourceId = layer.source; + if (sourceId && !sources[sourceId]) { + sources[sourceId] = sourcesMap[sourceId]; + } + return sources; + }, {}) + }; + } + + _getBaseLayerRenderLayers(layerCatalog, layersOnMap) { + const uniqueSet = new Set(); + const collectIds = (node) => { + if (node.children) { + node.children.forEach((child) => collectIds(child)); + } + const parts = node.parts || [node.id]; + parts.forEach(part => uniqueSet.add(part)); + }; + collectIds(layerCatalog); + + return layersOnMap.filter(layer => uniqueSet.has(layer.id)); + } + + _removeBaseLayerRenderLayers(layerCatalog, layersOnMap) { + const deleteSet = new Set(); + const collectIds = (node) => { + if (node.children) { + node.children.forEach((child) => collectIds(child)); + } + const parts = node.parts || [node.id]; + parts.forEach(part => deleteSet.add(part)); + }; + collectIds(layerCatalog); + + let writeIndex = 0; + for (let i = 0; i < layersOnMap.length; i++) { + if (!deleteSet.has(layersOnMap[i].id)) { + layersOnMap[writeIndex++] = layersOnMap[i]; + } + } + layersOnMap.length = writeIndex; + } } diff --git a/test/common/mapping/utils/SourceListModelV2Spec.js b/test/common/mapping/utils/SourceListModelV2Spec.js index e6927c076..48cf5fa20 100644 --- a/test/common/mapping/utils/SourceListModelV2Spec.js +++ b/test/common/mapping/utils/SourceListModelV2Spec.js @@ -1,7 +1,32 @@ import { SourceListModelV2 } from '../../../../src/common/mapping/utils/SourceListModelV2'; describe('SourceListV2', () => { - let layers, map, mockEvents; + let layers, sources, map, mockEvents, overlayLayersManager; + const baseLayerInfo = { + id: 'wmts100', + title: 'wmts100', + layers: [ + { + id: 'wmts100', + type: 'raster', + source: 'wmts100', + minzoom: 0, + maxzoom: 12 + } + ], + sources: { + wmts100: { + type: 'raster', + tiles: [ + 'http:/localhost:8195/portalproxy/97d2edb85b0cb5d4/iserver/services/map-China100-2/wmts100?service=WMTS&request=GetTile&version=1.0.0&style=default&layer=China&tilematrixSet=Custom_China&format=image%2Fpng&tilematrix={z}&tilerow={y}&tilecol={x}' + ], + maxzoom: 12, + tileSize: 256, + bounds: [-180, -85.05112877980652, 180, 85.05112877980648], + minzoom: 0 + } + } + }; beforeEach(() => { mockEvents = {}; @@ -170,34 +195,51 @@ describe('SourceListV2', () => { } } ]; + sources = { + graticuleLayer_1723443238046_line: { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: [] + } + } + }; + overlayLayersManager = { + graticuleLayer_1723443238046: { + id: 'graticuleLayer_1723443238046', + overlay: true, + renderingMode: '3d', + type: 'custom', + visible: true, + sourceId: 'graticuleLayer_1723443238046_line' + } + }; map = { + addLayer: function (layer) { + layers.push(layer); + }, + addSource(sourceId, source) { + sources[sourceId] = source; + }, getStyle() { return { - layers + layers, + sources }; }, - getSource() { - return { - type: 'geojson', - data: { - type: 'FeatureCollection', - features: [] - } - }; + getSource(id) { + return sources[id]; }, getLayer(id) { return layers.find((layer) => layer.id === id); }, - overlayLayersManager: { - graticuleLayer_1723443238046: { - id: 'graticuleLayer_1723443238046', - overlay: true, - renderingMode: '3d', - type: 'custom', - visible: true, - sourceId: 'graticuleLayer_1723443238046_line' - } + removeLayer(id) { + return layers.splice(layers.findIndex((layer) => layer.id === id), 1); + }, + removeSource(id) { + delete sources[id]; }, + overlayLayersManager, on(type, callback) { mockEvents[type] = callback; }, @@ -561,4 +603,398 @@ describe('SourceListV2', () => { expect(mockEvents.styledata).not.toBeUndefined(); mockEvents.styledata(); }); + + it('changeBaseLayer not complicit', (done) => { + const layersInfo = [ + { + layerType: 'TIANDITU_IMG_3857', + labelLayerVisible: true, + tk: '50599c913367188e6c94e872032f4cf1', + name: '天地图影像', + title: '天地图影像', + renderLayers: ['天地图影像', '天地图影像-tdt-label'], + metadata: { + SM_Layer_Id: '天地图影像' + }, + id: '天地图影像', + visible: true, + reused: false + }, + { + layerType: 'UNIQUE', + visible: true, + themeSetting: { + themeField: 'parent', + customSettings: { + '{"adcode":110000}': { + strokeWidth: 1, + fillColor: '#D53E4F', + fillOpacity: 0.9, + lineDash: 'solid', + strokeColor: '#ffffff', + type: 'POLYGON', + strokeOpacity: 1 + } + }, + colors: ['#D53E4F', '#FC8D59', '#FEE08B', '#FFFFBF', '#E6F598', '#99D594', '#3288BD'] + }, + name: '北京市(3)', + featureType: 'POLYGON', + style: { + strokeWidth: 1, + fillColor: '#D53E4F', + fillOpacity: 0.9, + lineDash: 'solid', + strokeColor: '#ffffff', + type: 'POLYGON', + strokeOpacity: 1 + }, + projection: 'EPSG:4326', + enableFields: [ + 'parent', + 'adcode', + 'level', + 'childrenNum', + 'smpid', + 'centroid', + 'center', + 'subFeatureIndex', + 'name', + 'acroutes' + ], + dataSource: { + accessType: 'DIRECT', + type: 'PORTAL_DATA', + serverId: '1371715657' + }, + layerID: '北京市(3)', + renderLayers: ['北京市(3)', '北京市(3)-strokeLine'], + metadata: { + SM_Layer_Id: '北京市(3)' + }, + id: '北京市(3)', + reused: false + } + ]; + layers = [ + { + id: '天地图影像', + type: 'raster', + source: '天地图影像', + metadata: { + SM_Layer_Id: '天地图影像' + }, + minzoom: 0, + maxzoom: 22, + layout: { + visibility: 'visible' + } + }, + { + id: '天地图影像-tdt-label', + type: 'raster', + source: '天地图影像-tdt-label', + metadata: { + SM_Layer_Id: '天地图影像' + }, + minzoom: 0, + maxzoom: 22, + layout: { + visibility: 'visible' + } + }, + { + id: '北京市(3)', + type: 'fill', + source: '北京市(3)', + metadata: { + SM_Layer_Id: '北京市(3)' + }, + minzoom: 0, + maxzoom: 22, + layout: { + visibility: 'visible' + }, + paint: { + 'fill-color': '#D53E4F', + 'fill-opacity': 0.9 + } + }, + { + id: '北京市(3)-strokeLine', + type: 'line', + source: '北京市(3)', + metadata: { + SM_Layer_Id: '北京市(3)' + }, + minzoom: 0, + maxzoom: 22, + filter: ['all'], + layout: { + visibility: 'visible' + }, + paint: { + 'line-width': 1, + 'line-color': '#ffffff', + 'line-opacity': 1 + } + } + ]; + sources = { + '北京市(3)': { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: [] + } + }, + '天地图影像': { + type: 'raster', + tiles: [ + 'https://t0.tianditu.gov.cn/img_w/wmts?tk=50599c913367188e6c94e872032f4cf1&service=WMTS&request=GetTile&style=default&version=1.0.0&layer=img&tilematrixSet=w&format=tiles&width=256&height=256&tilematrix={z}&tilerow={y}&tilecol={x}' + ], + minzoom: 0, + maxzoom: 22, + tileSize: 256, + rasterSource: '', + prjCoordSys: '', + proxy: null, + bounds: [-180, -90, 180, 90] + }, + '天地图影像-tdt-label': { + type: 'raster', + tiles: [ + 'https://t0.tianditu.gov.cn/cia_w/wmts?tk=50599c913367188e6c94e872032f4cf1&service=WMTS&request=GetTile&style=default&version=1.0.0&layer=cia&tilematrixSet=w&format=tiles&width=256&height=256&tilematrix={z}&tilerow={y}&tilecol={x}' + ], + minzoom: 0, + maxzoom: 22, + tileSize: 256, + rasterSource: '', + prjCoordSys: '', + proxy: null, + bounds: [-180, -90, 180, 90] + } + }; + overlayLayersManager = {}; + + const sourceListModel = new SourceListModelV2({ map, layers: layersInfo }); + expect(map.getStyle().layers.some(item => ['天地图影像', '天地图影像-tdt-label'].includes(item.id))).toBeTruthy(); + const layerList = sourceListModel.getLayerCatalog(); + const appreciableLayers = sourceListModel.getLayers(); + expect(layerList.length).toBe(2); + expect(appreciableLayers.length).toBe(2); + const nextBaseLayerInfo = sourceListModel.changeBaseLayer(baseLayerInfo); + expect(nextBaseLayerInfo).toBeTruthy(); + expect(nextBaseLayerInfo.layers.length).toBe(1); + expect(nextBaseLayerInfo.layers).toEqual(baseLayerInfo.layers); + expect(map.getStyle().layers.some(item => ['天地图影像', '天地图影像-tdt-label'].includes(item.id))).toBeFalsy(); + done(); + }); + + it('changeBaseLayer complicit', (done) => { + const layersInfo = [ + { + layerType: 'raster', + name: 'wmts100', + title: 'wmts100', + renderLayers: ['wmts100'], + metadata: { + SM_Layer_Id: 'wmts100' + }, + id: 'wmts100', + visible: true, + reused: false + }, + { + layerType: 'TIANDITU_IMG_3857', + labelLayerVisible: true, + tk: '50599c913367188e6c94e872032f4cf1', + name: '天地图影像', + title: '天地图影像', + renderLayers: ['天地图影像', '天地图影像-tdt-label'], + metadata: { + SM_Layer_Id: '天地图影像' + }, + id: '天地图影像', + visible: true, + reused: false + }, + { + layerType: 'UNIQUE', + visible: true, + themeSetting: { + themeField: 'parent', + customSettings: { + '{"adcode":110000}': { + strokeWidth: 1, + fillColor: '#D53E4F', + fillOpacity: 0.9, + lineDash: 'solid', + strokeColor: '#ffffff', + type: 'POLYGON', + strokeOpacity: 1 + } + }, + colors: ['#D53E4F', '#FC8D59', '#FEE08B', '#FFFFBF', '#E6F598', '#99D594', '#3288BD'] + }, + name: '北京市(3)', + featureType: 'POLYGON', + style: { + strokeWidth: 1, + fillColor: '#D53E4F', + fillOpacity: 0.9, + lineDash: 'solid', + strokeColor: '#ffffff', + type: 'POLYGON', + strokeOpacity: 1 + }, + projection: 'EPSG:4326', + enableFields: [ + 'parent', + 'adcode', + 'level', + 'childrenNum', + 'smpid', + 'centroid', + 'center', + 'subFeatureIndex', + 'name', + 'acroutes' + ], + dataSource: { + accessType: 'DIRECT', + type: 'PORTAL_DATA', + serverId: '1371715657' + }, + layerID: '北京市(3)', + renderLayers: ['北京市(3)', '北京市(3)-strokeLine'], + metadata: { + SM_Layer_Id: '北京市(3)' + }, + id: '北京市(3)', + reused: false + } + ]; + layers = [ + ...baseLayerInfo.layers, + { + id: '天地图影像', + type: 'raster', + source: '天地图影像', + metadata: { + SM_Layer_Id: '天地图影像' + }, + minzoom: 0, + maxzoom: 22, + layout: { + visibility: 'visible' + } + }, + { + id: '天地图影像-tdt-label', + type: 'raster', + source: '天地图影像-tdt-label', + metadata: { + SM_Layer_Id: '天地图影像' + }, + minzoom: 0, + maxzoom: 22, + layout: { + visibility: 'visible' + } + }, + { + id: '北京市(3)-strokeLine', + type: 'line', + source: '北京市(3)', + metadata: { + SM_Layer_Id: '北京市(3)' + }, + minzoom: 0, + maxzoom: 22, + filter: ['all'], + layout: { + visibility: 'visible' + }, + paint: { + 'line-width': 1, + 'line-color': '#ffffff', + 'line-opacity': 1 + } + } + ]; + sources = { + '北京市(3)': { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: [] + } + }, + '天地图影像': { + type: 'raster', + tiles: [ + 'https://t0.tianditu.gov.cn/img_w/wmts?tk=50599c913367188e6c94e872032f4cf1&service=WMTS&request=GetTile&style=default&version=1.0.0&layer=img&tilematrixSet=w&format=tiles&width=256&height=256&tilematrix={z}&tilerow={y}&tilecol={x}' + ], + minzoom: 0, + maxzoom: 22, + tileSize: 256, + rasterSource: '', + prjCoordSys: '', + proxy: null, + bounds: [-180, -90, 180, 90] + }, + '天地图影像-tdt-label': { + type: 'raster', + tiles: [ + 'https://t0.tianditu.gov.cn/cia_w/wmts?tk=50599c913367188e6c94e872032f4cf1&service=WMTS&request=GetTile&style=default&version=1.0.0&layer=cia&tilematrixSet=w&format=tiles&width=256&height=256&tilematrix={z}&tilerow={y}&tilecol={x}' + ], + minzoom: 0, + maxzoom: 22, + tileSize: 256, + rasterSource: '', + prjCoordSys: '', + proxy: null, + bounds: [-180, -90, 180, 90] + }, + ...baseLayerInfo.sources + }; + overlayLayersManager = {}; + + const sourceListModel = new SourceListModelV2({ map, layers: layersInfo }); + expect(map.getStyle().layers.some(item => ['天地图影像', '天地图影像-tdt-label'].includes(item.id))).toBeTruthy(); + expect(map.getStyle().layers.some(item => ['wmts100'].includes(item.id))).toBeTruthy(); + let layerList = sourceListModel.getLayerCatalog(); + let appreciableLayers = sourceListModel.getLayers(); + expect(layerList.length).toBe(3); + expect(layerList[layerList.length - 1].id).toBe('wmts100'); + expect(appreciableLayers.length).toBe(3); + expect(appreciableLayers[0].id).toBe('wmts100'); + const sameBaseLayer = { + id: 'sameBaseLayer', + layers: layers.slice(1, 3).map(item => { + const nextItem = Object.assign({}, item); + nextItem.metadata = { SM_Layer_Id: 'sameBaseLayer' }; + return nextItem; + }), + sources: { + '天地图影像': sources['天地图影像'], + '天地图影像-tdt-label': sources['天地图影像-tdt-label'] + } + } + const nextBaseLayerInfo = sourceListModel.changeBaseLayer(sameBaseLayer); + expect(nextBaseLayerInfo).toBeTruthy(); + expect(nextBaseLayerInfo.layers.length).toBe(2); + expect(nextBaseLayerInfo.layers).not.toEqual(sameBaseLayer.layers); + expect(map.getStyle().layers.some(item => ['天地图影像', '天地图影像-tdt-label'].includes(item.id))).toBeTruthy(); + expect(map.getStyle().layers.some(item => [/天地图影像_\d+$/, /天地图影像-tdt-label_\d+$/].some(reg => reg.test(item.id)))).toBeTruthy(); + expect(map.getStyle().layers.some(item => ['wmts100'].includes(item.id))).toBeFalsy(); + layerList = sourceListModel.getLayerCatalog(); + appreciableLayers = sourceListModel.getLayers(); + expect(layerList.length).toBe(3); + expect(layerList[layerList.length - 1].id).toBe('sameBaseLayer'); + expect(appreciableLayers.length).toBe(3); + expect(appreciableLayers[0].id).toBe('sameBaseLayer'); + done(); + }); }); diff --git a/test/common/mapping/utils/SourceListModelV3Spec.js b/test/common/mapping/utils/SourceListModelV3Spec.js index c5d801c8b..3a81d0102 100644 --- a/test/common/mapping/utils/SourceListModelV3Spec.js +++ b/test/common/mapping/utils/SourceListModelV3Spec.js @@ -1,9 +1,140 @@ import { SourceListModelV3 } from '../../../../src/common/mapping/utils/SourceListModelV3'; import { isL7Layer } from '../../../../src/common/mapping/utils/L7LayerUtil'; import '../../../resources/WebMapV3.js'; +import cloneDeep from 'lodash.clonedeep'; describe('SourceListV3', () => { - let map, layers, mockEvents; + let map, layers, sources, mockEvents, overlayLayersManager; + const baseLayerInfo = { + id: 'wmts100', + title: 'wmts100', + layers: [ + { + id: 'wmts100', + type: 'raster', + source: 'wmts100', + minzoom: 0, + maxzoom: 12 + } + ], + sources: { + wmts100: { + type: 'raster', + tiles: [ + 'http:/localhost:8195/portalproxy/97d2edb85b0cb5d4/iserver/services/map-China100-2/wmts100?service=WMTS&request=GetTile&version=1.0.0&style=default&layer=China&tilematrixSet=Custom_China&format=image%2Fpng&tilematrix={z}&tilerow={y}&tilecol={x}' + ], + maxzoom: 12, + tileSize: 256, + bounds: [-180, -85.05112877980652, 180, 85.05112877980648], + minzoom: 0 + } + } + }; + const changeBaseLayerMapInfo = { + metadata: { + layerCatalog: [ + { + visible: true, + parts: ['北京市住宅小区'], + id: 'layer_北京市住宅小区_1754359974753_24', + title: '北京市住宅小区', + type: 'composite' + }, + { + visible: true, + children: [ + { + visible: true, + id: 'ms_TIANDITU_IMG_3857_label', + title: 'ms_TIANDITU_IMG_3857_label', + type: 'basic' + }, + { + visible: true, + id: 'TIANDITU_IMG_3857', + title: 'TIANDITU_IMG_3857', + type: 'basic' + } + ], + id: 'group_tianditu_img_3857_1754377584218_382', + title: 'tianditu_img_3857', + type: 'group' + } + ] + }, + sources: { + ms_TIANDITU_IMG_3857_label: { + tiles: [ + 'https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=50599c913367188e6c94e872032f4cf1&host=172.16.14.44:8190' + ], + tileSize: 256, + attribution: '', + bounds: [-180, -90, 180, 90], + type: 'raster' + }, + ms_1750973565_1754359974753_23: { + tiles: [ + 'http://localhost:8190/iportal/web/datas/1750973565/structureddata/tiles/{z}/{x}/{y}.mvt?epsgCode=3857&returnedFieldNames=%5B%22smpid%22%2C%22%E5%B0%8F%E5%8C%BA%E5%90%8D%22%2C%22SmGeometrySize%22%2C%22%E5%B9%B4%E4%BB%A3%22%2C%22%E5%8D%95%E4%BB%B7%22%2C%22%E6%A5%BC%E5%B1%82%22%2C%22SmID%22%2C%22%E6%80%BB%E4%BB%B7%22%2C%22SmUserID%22%2C%22%E6%88%B7%E5%9E%8B%22%2C%22%E6%9C%9D%E5%90%91%22%2C%22%E5%9C%B0%E5%9D%80%22%2C%22SmY%22%2C%22SmX%22%2C%22SmLibTileID%22%2C%22%E9%9D%A2%E7%A7%AF%22%5D&geometryFieldName=geometry' + ], + bounds: [115.89763001613301, 39.40606, 117.48732001635402, 40.6500100064203], + type: 'vector' + }, + TIANDITU_IMG_3857: { + tiles: [ + 'https://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=50599c913367188e6c94e872032f4cf1&host=172.16.14.44:8190' + ], + tileSize: 256, + attribution: '', + bounds: [-180, -90, 180, 90], + type: 'raster' + } + }, + crs: 'EPSG:3857', + center: [116.22715983919534, 39.878220196575874], + zoom: 8.79189646012174, + glyphs: {}, + version: '3.3.2', + maxzoom: 19, + name: '无标题地图-tianditu', + layers: [ + { + maxzoom: 19, + id: 'TIANDITU_IMG_3857', + source: 'TIANDITU_IMG_3857', + type: 'raster', + minzoom: 0 + }, + { + layout: { + visibility: 'visible' + }, + maxzoom: 19, + id: 'ms_TIANDITU_IMG_3857_label', + source: 'ms_TIANDITU_IMG_3857_label', + type: 'raster', + minzoom: 0 + }, + { + metadata: {}, + maxzoom: 24, + paint: { + 'circle-color': '#EE4D5A', + 'circle-opacity': 0.9, + 'circle-translate-anchor': 'map', + 'circle-radius': 4, + 'circle-translate': [0, 0] + }, + id: '北京市住宅小区', + source: 'ms_1750973565_1754359974753_23', + 'source-layer': '1750973565$geometry', + type: 'circle', + minzoom: 0 + } + ], + sprite: 'http://localhost:8190/iportal/web/maps/1874751978/sprites/sprite', + pitch: 0, + minzoom: 0 + }; beforeEach(() => { mockEvents = {}; @@ -111,7 +242,7 @@ describe('SourceListV3', () => { 'line-opacity': 0.8 } }, - + { id: 'mapbox-gl-draw', type: 'line', @@ -172,34 +303,104 @@ describe('SourceListV3', () => { } } ]; + sources = { + CHINA_DARK: { + type: 'raster', + tiles: [] + }, + 'test-source': { + type: 'raster', + tiles: [] + }, + 'tracklayer-1-line': { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: [] + } + }, + graticuleLayer_1_line: { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: [] + } + }, + 'tdt-search-line': { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: [] + } + }, + 'tdt-route-line': { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: [] + } + }, + smmeasure: { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: [] + } + }, + 'mapbox-gl-draw': { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: [] + } + }, + graticuleLayer_1723443238046_line: { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: [] + } + } + }; + overlayLayersManager = { + graticuleLayer_1723443238046: { + id: 'graticuleLayer_1723443238046', + overlay: true, + renderingMode: '3d', + type: 'custom', + visible: true, + sourceId: 'graticuleLayer_1723443238046_line' + } + }; map = { + addLayer: function (layer) { + layers.push(layer); + }, + addSource(sourceId, source) { + sources[sourceId] = source; + }, getStyle() { return { - layers + layers, + sources }; }, - getSource() { - return { - type: 'geojson', - data: { - type: 'FeatureCollection', - features: [] - } - }; + getSource(id) { + return sources[id]; }, getLayer(id) { return layers.find((layer) => layer.id === id); }, - overlayLayersManager: { - graticuleLayer_1723443238046: { - id: 'graticuleLayer_1723443238046', - overlay: true, - renderingMode: '3d', - type: 'custom', - visible: true, - sourceId: 'graticuleLayer_1723443238046_line' - } + removeLayer(id) { + return layers.splice( + layers.findIndex((layer) => layer.id === id), + 1 + ); }, + removeSource(id) { + delete sources[id]; + }, + overlayLayersManager, on(type, callback) { mockEvents[type] = callback; }, @@ -298,9 +499,9 @@ describe('SourceListV3', () => { }); const layerList = sourceListModel.getLayerCatalog(); expect(layerList.length).toBe(mapInfo.metadata.layerCatalog.length + 3); - const selfIds = mapInfo.metadata.layerCatalog.map(item => item.id); - const selfLayerCatalogs = layerList.filter(layer => selfIds.includes(layer.id)); - expect(selfLayerCatalogs.some(layer => !layer.renderLayers.includes(layer.id))).toBe(false); + const selfIds = mapInfo.metadata.layerCatalog.map((item) => item.id); + const selfLayerCatalogs = layerList.filter((layer) => selfIds.includes(layer.id)); + expect(selfLayerCatalogs.some((layer) => !layer.renderLayers.includes(layer.id))).toBe(false); done(); }); @@ -318,9 +519,9 @@ describe('SourceListV3', () => { }); const layerList = sourceListModel.getLayerCatalog(); expect(layerList.length).toBe(mapInfo.metadata.layerCatalog.length + 2); - const selfIds = mapInfo.metadata.layerCatalog.map(item => item.id); - const selfLayerCatalogs = layerList.filter(layer => selfIds.includes(layer.id)); - expect(selfLayerCatalogs.some(layer => !layer.renderLayers.includes(layer.id))).toBe(false); + const selfIds = mapInfo.metadata.layerCatalog.map((item) => item.id); + const selfLayerCatalogs = layerList.filter((layer) => selfIds.includes(layer.id)); + expect(selfLayerCatalogs.some((layer) => !layer.renderLayers.includes(layer.id))).toBe(false); done(); }); @@ -338,9 +539,9 @@ describe('SourceListV3', () => { }); const layerList = sourceListModel.getLayerCatalog(); expect(layerList.length).toBe(mapInfo.metadata.layerCatalog.length + 3); - const selfIds = mapInfo.metadata.layerCatalog.filter(item => item.parts).map(item => item.id); - const selfLayerCatalogs = layerList.filter(layer => selfIds.includes(layer.id)); - expect(selfLayerCatalogs.some(layer => layer.renderLayers.includes(layer.id))).toBe(false); + const selfIds = mapInfo.metadata.layerCatalog.filter((item) => item.parts).map((item) => item.id); + const selfLayerCatalogs = layerList.filter((layer) => selfIds.includes(layer.id)); + expect(selfLayerCatalogs.some((layer) => layer.renderLayers.includes(layer.id))).toBe(false); done(); }); @@ -367,9 +568,9 @@ describe('SourceListV3', () => { const markerList = { ['中国金牌个人获奖者(1)']: { show: jasmine.createSpy('show').and.callFake(() => {}), - hide: jasmine.createSpy('hide').and.callFake(() => {}), + hide: jasmine.createSpy('hide').and.callFake(() => {}) } - } + }; const sourceListModel = new SourceListModelV3({ map, mapInfo, @@ -401,9 +602,9 @@ describe('SourceListV3', () => { const markerList = { ['中国金牌个人获奖者(1)']: { show: jasmine.createSpy('show').and.callFake(() => {}), - hide: jasmine.createSpy('hide').and.callFake(() => {}), + hide: jasmine.createSpy('hide').and.callFake(() => {}) } - } + }; const sourceListModel = new SourceListModelV3({ map, mapInfo, @@ -435,9 +636,9 @@ describe('SourceListV3', () => { const markerList = { ['中国金牌个人获奖者(1)']: { show: jasmine.createSpy('show').and.callFake(() => {}), - hide: jasmine.createSpy('hide').and.callFake(() => {}), + hide: jasmine.createSpy('hide').and.callFake(() => {}) } - } + }; const sourceListModel = new SourceListModelV3({ map, mapInfo, @@ -487,4 +688,111 @@ describe('SourceListV3', () => { expect(mockEvents.styledata).not.toBeUndefined(); mockEvents.styledata(); }); + + it('changeBaseLayer not complicit', (done) => { + const nextBaseLayerMapInfo = cloneDeep(changeBaseLayerMapInfo); + layers = nextBaseLayerMapInfo.layers; + sources = nextBaseLayerMapInfo.sources; + + overlayLayersManager = {}; + + const sourceListModel = new SourceListModelV3({ + map, + mapInfo: cloneDeep(nextBaseLayerMapInfo), + mapResourceInfo: {}, + legendList: [], + l7LayerUtil: { + isL7Layer, + getL7MarkerLayers: () => [] + } + }); + expect(map.getStyle().layers.some((item) => ['TIANDITU_IMG_3857', 'ms_TIANDITU_IMG_3857_label'].includes(item.id))).toBeTruthy(); + const layerList = sourceListModel.getLayerCatalog(); + const appreciableLayers = sourceListModel.getLayers(); + expect(layerList.length).toBe(2); + expect(appreciableLayers.length).toBe(3); + const nextBaseLayerInfo = sourceListModel.changeBaseLayer(baseLayerInfo); + expect(nextBaseLayerInfo).toBeTruthy(); + expect(nextBaseLayerInfo.layers.length).toBe(1); + expect(nextBaseLayerInfo.layers).toEqual(baseLayerInfo.layers); + expect(map.getStyle().layers.some((item) => ['TIANDITU_IMG_3857', 'ms_TIANDITU_IMG_3857_label'].includes(item.id))).toBeFalsy(); + done(); + }); + + it('changeBaseLayer complicit', (done) => { + let nextBaseLayerMapInfo = cloneDeep(changeBaseLayerMapInfo); + layers = [ + ...baseLayerInfo.layers, + ...nextBaseLayerMapInfo.layers + ]; + sources = { + ...nextBaseLayerMapInfo.sources, + ...baseLayerInfo.sources + }; + const copyMapInfo = cloneDeep(changeBaseLayerMapInfo); + nextBaseLayerMapInfo = { + ...copyMapInfo, + layers: cloneDeep(layers), + sources: cloneDeep(sources), + metadata: { + layerCatalog: copyMapInfo.metadata.layerCatalog.concat(baseLayerInfo.layers.map(item => ({ + id: item.id, + title: item.id, + type: 'basic', + visible: true + }))) + } + } + overlayLayersManager = {}; + + const sourceListModel = new SourceListModelV3({ + map, + mapInfo: nextBaseLayerMapInfo, + mapResourceInfo: {}, + legendList: [], + l7LayerUtil: { + isL7Layer, + getL7MarkerLayers: () => [] + } + }); + expect(map.getStyle().layers.some((item) => ['TIANDITU_IMG_3857', 'ms_TIANDITU_IMG_3857_label'].includes(item.id))).toBeTruthy(); + expect(map.getStyle().layers.some((item) => ['wmts100'].includes(item.id))).toBeTruthy(); + let layerList = sourceListModel.getLayerCatalog(); + let appreciableLayers = sourceListModel.getLayers(); + expect(layerList.length).toBe(3); + expect(layerList[layerList.length - 1].id).toBe('wmts100'); + expect(appreciableLayers.length).toBe(4); + expect(appreciableLayers[0].id).toBe('wmts100'); + const sameBaseLayer = { + id: 'sameBaseLayer', + layers: layers.slice(1, 3).map((item) => { + const nextItem = Object.assign({}, item); + nextItem.metadata = { SM_Layer_Id: 'sameBaseLayer' }; + return nextItem; + }), + sources: { + TIANDITU_IMG_3857: sources['TIANDITU_IMG_3857'], + 'ms_TIANDITU_IMG_3857_label': sources['ms_TIANDITU_IMG_3857_label'] + } + }; + const nextBaseLayerInfo = sourceListModel.changeBaseLayer(sameBaseLayer); + expect(nextBaseLayerInfo).toBeTruthy(); + expect(nextBaseLayerInfo.layers.length).toBe(2); + expect(nextBaseLayerInfo.layers).not.toEqual(sameBaseLayer.layers); + expect(map.getStyle().layers.some((item) => ['TIANDITU_IMG_3857', 'ms_TIANDITU_IMG_3857_label'].includes(item.id))).toBeTruthy(); + expect( + map + .getStyle() + .layers.some((item) => [/TIANDITU_IMG_3857_\d+$/, /ms_TIANDITU_IMG_3857_label_\d+$/].some((reg) => reg.test(item.id))) + ).toBeTruthy(); + expect(map.getStyle().layers.some((item) => ['wmts100'].includes(item.id))).toBeFalsy(); + layerList = sourceListModel.getLayerCatalog(); + appreciableLayers = sourceListModel.getLayers(); + expect(layerList.length).toBe(3); + expect(layerList[layerList.length - 1].id).toBe('sameBaseLayer'); + expect(appreciableLayers.length).toBe(5); + expect(appreciableLayers[0].id.match(/TIANDITU_IMG_3857_TIANDITU_IMG_3857_\d+$/)).toBeTruthy(); + expect(appreciableLayers[1].id.match(/ms_TIANDITU_IMG_3857_label_ms_TIANDITU_IMG_3857_label_\d+$/)).toBeTruthy(); + done(); + }); }); From 0bfe21622dd295f6ac417a29ea999edf79a101c6 Mon Sep 17 00:00:00 2001 From: xiongjj Date: Thu, 7 Aug 2025 11:58:13 +0800 Subject: [PATCH 4/4] fix ut --- src/common/mapping/utils/SourceListModelV3.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common/mapping/utils/SourceListModelV3.js b/src/common/mapping/utils/SourceListModelV3.js index af7525eab..b98253194 100644 --- a/src/common/mapping/utils/SourceListModelV3.js +++ b/src/common/mapping/utils/SourceListModelV3.js @@ -190,6 +190,9 @@ export class SourceListModelV3 extends AppreciableLayerBase { _generateBaseLayerInfo(layerCatalog, layers) { const nextLayerCatalog = layerCatalog.slice(); const originBaseLayerCatalog = nextLayerCatalog.pop(); + if (!originBaseLayerCatalog) { + return; + } const renderLayers = this._getBaseLayerRenderLayers(originBaseLayerCatalog, layers); const baseLayersOnMap = renderLayers.map((layer) => { const nextLayer = { ...layer };