import { actions, storeDate } from '../store/store.js' //局部状态管理 // 添加s3m function addS3mLayers(scps, callback) { let promiseArray = []; try { if (scps) { scps.forEach(scp => { promiseArray.push( viewer.scene.addS3MTilesLayerByScp(scp.url, scp.options) ); }); } promiseWhen(promiseArray, callback, 'S3M') } catch (e) { let widget = viewer.cesiumWidget; if (widget._showRenderLoopErrors) { let title = "渲染时发生错误,已停止渲染。"; widget.showErrorPanel(title, undefined, e); } } }; // 添加场景 function addScene(url, options, callback) { if (options && options.SceneToken) { Cesium.Credential.CREDENTIAL = new Cesium.Credential(options.SceneToken); } let flag = true; if (options && options.autoSetView !== undefined) { flag = options.autoSetView } if (checkURL(url)) { try { let s = [viewer.scene.open(url, undefined, { 'autoSetView': flag })]; promiseWhen(s, callback, 'SCENE'); } catch (e) { let widget = viewer.cesiumWidget; if (widget._showRenderLoopErrors) { let title = "渲染时发生错误,已停止渲染。"; widget.showErrorPanel(title, undefined, e); } } } }; // 添加地形 function addTerrainLayer(LayerURL, isSct) { try { const terrainProvider = new Cesium.CesiumTerrainProvider({ url: LayerURL, isSct: isSct, }); viewer.terrainProvider = terrainProvider; return terrainProvider; } catch (e) { let widget = viewer.cesiumWidget; if (widget._showRenderLoopErrors) { let title = "渲染时发生错误,已停止渲染。"; widget.showErrorPanel(title, undefined, e); } return null; } }; // ======================= ✅ 已修复:万能自动坐标系转换 ======================= function addImageLayer(LayerURL, layerName) { try { let url = LayerURL; let mapName = ''; // 清理URL参数,不手动解析投影 if (LayerURL.includes('prjCoordSys')) { try { const urlObj = new URL(LayerURL); url = urlObj.origin + urlObj.pathname; } catch (e) { console.warn('解析URL失败:', e); } } // 提取地图名称 if (url.includes('/maps/')) { const mapsIndex = url.lastIndexOf('/maps/'); mapName = url.substring(mapsIndex + 6); } let options = { url: url, }; if (mapName) { options.name = mapName; } // ✅ 核心:自动将任意坐标系 → 输出 WGS84 options.outputPrjCoordSys = { epsgCode: 4326, name: "WGS 84", type: "EPSG", coordinateSystemType: "GEODETIC" }; // ✅ 不手动指定原投影,不硬编码范围,交给超图自动处理 let layer = viewer.imageryLayers.addImageryProvider( new Cesium.SuperMapImageryProvider(options) ); viewer.imageryLayers.raiseToTop(layer); layer.show = true; layer._loadedUrl = LayerURL; layer._loadedLayerName = layerName || ''; // 自动定位到图层范围 checkLayerExtentAndAdjust(layer); return layer; } catch (e) { console.error('加载影像图层失败:', e); let widget = viewer.cesiumWidget; if (widget._showRenderLoopErrors) { let title = "渲染时发生错误,已停止渲染。"; widget.showErrorPanel(title, undefined, e); } } }; // 检查图层范围并自动定位 function checkLayerExtentAndAdjust(layer) { try { const provider = layer.imageryProvider; Cesium.when(provider.readyPromise, function () { const rectangle = provider.rectangle; if (rectangle) { const west = Cesium.Math.toDegrees(rectangle.west); const south = Cesium.Math.toDegrees(rectangle.south); const east = Cesium.Math.toDegrees(rectangle.east); const north = Cesium.Math.toDegrees(rectangle.north); console.log('图层真实范围:', { west, south, east, north }); // 范围无效时飞到中国区域 const isInvalidExtent = (south < -90 || north > 90) || (south > north) || (west > east); if (isInvalidExtent) { console.warn('范围无效,自动定位到中国区域'); flyToChinaRegion(); } else { // 有效范围 → 自动定位到图层 viewer.flyTo(layer, { duration: 1.5 }); } } }); } catch (e) { console.error('检查图层范围失败:', e); } } // 定位到中国 function flyToChinaRegion() { viewer.scene.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(104.0, 35.0, 4000000), duration: 2, orientation: { heading: 0, pitch: -60, roll: 0 } }); } // 添加mvt function addMvtLayer(LayerURL, name, callback) { try { let mvtMap = viewer.scene.addVectorTilesMap({ url: LayerURL, canvasWidth: 512, name: name || 'mvt', viewer: viewer }); Cesium.when(mvtMap.readyPromise, function (data) { var bounds = mvtMap.rectangle; viewer.scene.camera.flyTo({ destination: new Cesium.Cartesian3.fromRadians( (bounds.east + bounds.west) * 0.5, (bounds.north + bounds.south) * 0.5, 10000 ), duration: 0, orientation: { heading: 0, roll: 0 } }); actions.setChangeLayers(); callback(mvtMap) }); return mvtMap } catch (e) { let widget = viewer.cesiumWidget; if (widget._showRenderLoopErrors) { let title = "渲染时发生错误,已停止渲染。"; widget.showErrorPanel(title, undefined, e); } } }; // 加载GeoJson图层 function addGeoJsonLayer(url, name, callback) { try { // 判断是否为超图REST数据服务 if (url.includes('/iserver/services/') && url.includes('/rest/data/datasources')) { loadSuperMapRestDataService(url, name, callback); return; } const loadPromise = Cesium.GeoJsonDataSource.load(url, { stroke: Cesium.Color.fromCssColorString('#0055FF').withAlpha(1), fill: Cesium.Color.fromCssColorString('#00FF00').withAlpha(0.4), strokeWidth: 3, clampToGround: true }); Cesium.when(loadPromise, function (dataSource) { viewer.dataSources.add(dataSource); processGeoJsonEntities(dataSource, name); viewer.flyTo(dataSource, { duration: 2 }); actions.setChangeLayers(); if (callback) callback(dataSource); }, function (error) { console.error("加载GeoJSON失败:", error); }); } catch (e) { console.error("加载GeoJSON异常:", e); } }; // 从超图REST服务获取要素并转换为标准GeoJSON function fetchFeaturesFromSuperMap(url, timeout) { const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('请求超时')), timeout); }); return Promise.race([ fetch(url, { timeout: timeout }), timeoutPromise ]) .then(response => { if (!response.ok) { throw new Error(`HTTP错误: ${response.status}`); } return response.text(); }) .then(text => { try { return JSON.parse(text); } catch (e) { console.error('JSON解析失败:', text.substring(0, 200)); throw new Error('服务返回的不是有效的JSON数据'); } }) .then(superMapData => { // 超图返回的格式是: { features: [...], totalCount: N, ... } // 需要转换为标准GeoJSON格式: { type: 'FeatureCollection', features: [...] } // 调试:查看完整返回数据结构 console.log('超图服务返回的数据:', JSON.stringify(superMapData, null, 2).substring(0, 2000)); console.log('数据类型:', typeof superMapData); console.log('数据键:', Object.keys(superMapData || {})); let features = []; // 尝试多种可能的数据结构 if (superMapData.features && Array.isArray(superMapData.features)) { features = superMapData.features; console.log('从 features 属性获取要素'); } else if (superMapData.recordset && Array.isArray(superMapData.recordset)) { features = superMapData.recordset; console.log('从 recordset 属性获取要素'); } else if (superMapData.result && superMapData.result.features && Array.isArray(superMapData.result.features)) { features = superMapData.result.features; console.log('从 result.features 属性获取要素'); } else if (Array.isArray(superMapData)) { features = superMapData; console.log('数据本身是数组'); } else { console.warn('未找到要素数据,尝试查找其他属性'); // 遍历所有属性查找数组 for (const key of Object.keys(superMapData || {})) { if (Array.isArray(superMapData[key])) { console.log(`发现数组属性 ${key},长度:`, superMapData[key].length); if (superMapData[key].length > 0 && superMapData[key][0].geometry) { features = superMapData[key]; console.log(`使用 ${key} 作为要素数组`); break; } } } } // 转换每个要素的坐标格式(超图可能返回平面坐标,需要转换为经纬度) console.log('原始要素数量:', features.length); if (features.length > 0) { console.log('第一个要素示例:', JSON.stringify(features[0], null, 2).substring(0, 1000)); } features = features.map((feature, index) => { if (!feature) { console.warn(`要素 ${index} 为空`); return null; } if (!feature.geometry) { console.warn(`要素 ${index} 没有geometry`); return null; } if (!feature.geometry.coordinates) { console.warn(`要素 ${index} 没有coordinates`); return null; } // 尝试转换坐标 try { feature.geometry.coordinates = convertCoordinates(feature.geometry.coordinates); return feature; } catch (e) { console.error(`要素 ${index} 坐标转换失败:`, e); return null; } }).filter(Boolean); console.log('转换后的要素数量:', features.length); // 返回标准GeoJSON FeatureCollection return { type: 'FeatureCollection', features: features }; }); } // 坐标转换:尝试将平面坐标转换为经纬度 function convertCoordinates(coordinates) { if (!Array.isArray(coordinates)) { return coordinates; } // 检查是否是经纬度范围(-180到180,-90到90) const isLatLng = (coord) => { return Math.abs(coord[0]) <= 180 && Math.abs(coord[1]) <= 90; }; // 检查是否是Web Mercator或其他大坐标值 const isLargeCoord = (coord) => { return Math.abs(coord[0]) > 10000 || Math.abs(coord[1]) > 10000; }; // 处理点坐标 if (coordinates.length >= 2 && typeof coordinates[0] === 'number') { if (isLargeCoord(coordinates)) { console.log('检测到大坐标值,尝试Web Mercator转WGS84:', coordinates); try { const cartesian3 = Cesium.Cartesian3.fromArray(coordinates); const cartographic = Cesium.Cartographic.fromCartesian(cartesian3); return [ Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude), cartographic.height || 0 ]; } catch (e) { console.warn('坐标转换失败:', e); return coordinates; } } return coordinates; } // 处理线坐标(二维数组) if (coordinates.length > 0 && Array.isArray(coordinates[0])) { return coordinates.map(coord => convertCoordinates(coord)); } // 处理多边形坐标(三维数组) if (coordinates.length > 0 && Array.isArray(coordinates[0][0])) { return coordinates.map(ring => ring.map(coord => convertCoordinates(coord))); } return coordinates; } // 加载超图REST数据服务 function loadSuperMapRestDataService(baseUrl, name, callback) { console.log('开始加载超图REST数据服务:', baseUrl); // 设置超时时间 const timeout = 30000; // 30秒 // 创建超时Promise const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('请求超时')), timeout); }); // 修正URL:添加.json后缀(如果缺少的话) let datasourcesUrl = baseUrl; if (!datasourcesUrl.endsWith('.json')) { datasourcesUrl = `${baseUrl}.json`; } console.log('修正后的数据源URL:', datasourcesUrl); // 获取数据源列表 Promise.race([ fetch(datasourcesUrl, { timeout: timeout }), timeoutPromise ]) .then(response => { if (!response.ok) { // 如果.json后缀失败,尝试不加后缀 if (datasourcesUrl !== baseUrl) { console.log(`HTTP错误: ${response.status},尝试不加.json后缀`); return fetch(baseUrl, { timeout: timeout }); } throw new Error(`HTTP错误: ${response.status}`); } return response; }) .then(response => { if (!response.ok) { throw new Error(`HTTP错误: ${response.status}`); } return response.text(); }) .then(text => { // 尝试解析JSON try { return JSON.parse(text); } catch (e) { // 如果JSON解析失败,检查是否是HTML错误页面 console.error('JSON解析失败,响应内容:', text.substring(0, 200)); throw new Error('服务返回的不是有效的JSON数据'); } }) .then(dataSources => { console.log('获取到数据源列表:', dataSources); // 处理超图REST数据服务返回的格式 // 超图返回的格式是: { datasourceNames: [...], childUriList: [...], datasourceCount: N } let dataSourceNames = []; if (Array.isArray(dataSources)) { // 如果返回的是数组,直接使用 dataSourceNames = dataSources.map(ds => ds.name || ds); } else if (dataSources && dataSources.datasourceNames && Array.isArray(dataSources.datasourceNames)) { // 超图REST数据服务格式 dataSourceNames = dataSources.datasourceNames; } if (!dataSourceNames || dataSourceNames.length === 0) { console.warn('未找到数据源'); if (callback) callback(null); return; } // 遍历所有数据源 const loadPromises = []; dataSourceNames.forEach(dataSourceName => { // 确保数据源名称是字符串 const name = typeof dataSourceName === 'object' ? (dataSourceName.name || dataSourceName) : dataSourceName; let datasetsUrl = `${baseUrl}/${encodeURIComponent(name)}/datasets`; if (!datasetsUrl.endsWith('.json')) { datasetsUrl += '.json'; } // 获取数据集列表 const datasetPromise = Promise.race([ fetch(datasetsUrl, { timeout: timeout }), timeoutPromise ]) .then(response => { if (!response.ok) { if (datasetsUrl !== `${baseUrl}/${encodeURIComponent(name)}/datasets`) { return fetch(`${baseUrl}/${encodeURIComponent(name)}/datasets`, { timeout: timeout }); } throw new Error(`HTTP错误: ${response.status}`); } return response; }) .then(response => { if (!response.ok) { throw new Error(`HTTP错误: ${response.status}`); } return response.text(); }) .then(text => { try { return JSON.parse(text); } catch (e) { console.error(`数据集列表JSON解析失败 (${name}):`, text.substring(0, 200)); throw new Error('数据集列表不是有效的JSON'); } }) .then(datasets => { console.log(`数据源 ${name} 的数据集列表:`, datasets); // 处理数据集格式 let datasetNames = []; if (Array.isArray(datasets)) { datasetNames = datasets.map(ds => ds.name || ds); } else if (datasets && datasets.datasetNames && Array.isArray(datasets.datasetNames)) { datasetNames = datasets.datasetNames; } if (!datasetNames || datasetNames.length === 0) { return Promise.resolve([]); } // 加载每个数据集的要素(限制同时加载的数量) const featurePromises = []; const maxConcurrent = 3; // 最大并发数 for (let i = 0; i < datasetNames.length; i += maxConcurrent) { const batch = datasetNames.slice(i, i + maxConcurrent); const batchPromises = batch.map(dsName => { // 确保数据集名称是字符串 const datasetName = typeof dsName === 'object' ? (dsName.name || dsName) : dsName; let featuresUrl = `${baseUrl}/${encodeURIComponent(name)}/datasets/${encodeURIComponent(datasetName)}/features.json?returnContent=true`; console.log('加载要素URL:', featuresUrl); // 先获取超图格式的要素数据,转换为标准GeoJSON后再加载 return fetchFeaturesFromSuperMap(featuresUrl, timeout) .then(geoJsonData => { if (!geoJsonData) { throw new Error('未获取到有效要素数据'); } // 使用Cesium加载GeoJSON return Cesium.GeoJsonDataSource.load(geoJsonData, { stroke: Cesium.Color.fromCssColorString('#0055FF').withAlpha(1), fill: Cesium.Color.fromCssColorString('#00FF00').withAlpha(0.4), strokeWidth: 3, clampToGround: true }); }) .then(geoJsonDataSource => { viewer.dataSources.add(geoJsonDataSource); processGeoJsonEntities(geoJsonDataSource, `${name}-${datasetName}`); return geoJsonDataSource; }) .catch(error => { console.error(`加载数据集 ${datasetName} 失败:`, error); return null; }); }); featurePromises.push(Promise.all(batchPromises)); } return Promise.all(featurePromises).then(results => results.flat()); }) .catch(error => { console.error(`获取数据集列表失败 (${name}):`, error); return []; }); loadPromises.push(datasetPromise); }); Promise.all(loadPromises) .then(results => { const allDataSources = results.flat().filter(Boolean); console.log('所有加载的GeoJSON数据源:', allDataSources); if (allDataSources.length > 0) { viewer.flyTo(allDataSources[0], { duration: 2 }); } actions.setChangeLayers(); if (callback) callback(allDataSources.length > 0 ? allDataSources : null); }) .catch(error => { console.error('加载超图REST数据服务失败:', error); if (callback) callback(null); }); }) .catch(error => { console.error('获取数据源列表失败:', error); if (callback) callback(null); }); } // 处理GeoJSON实体(异步方式,避免阻塞主线程) function processGeoJsonEntities(dataSource, name) { try { let entities = dataSource.entities.values; let entitiesLength = entities.length; // 限制处理的实体数量,防止大量数据导致卡死 const maxEntities = 5000; if (entitiesLength > maxEntities) { console.warn(`实体数量过多(${entitiesLength}),仅处理前${maxEntities}个`); entitiesLength = maxEntities; } // 使用异步批量处理 const batchSize = 50; // 每批处理50个实体 let currentIndex = 0; const processBatch = () => { if (currentIndex >= entitiesLength) { return; // 处理完成 } const endIndex = Math.min(currentIndex + batchSize, entitiesLength); for (let i = currentIndex; i < endIndex; i++) { let entity = entities[i]; if (!entity) continue; entity.name = name || 'GeoJSON'; if (entity.polygon) { entity.polygon.fill = true; entity.polygon.outline = true; entity.polygon.clampToGround = true; entity.polygon.arcType = Cesium.ArcType.GEODESIC; entity.polygon.perPositionHeight = false; entity.polygon.classificationType = Cesium.ClassificationType.TERRAIN; entity.polygon.disableDepthTestDistance = Number.POSITIVE_INFINITY; entity.polygon.material = Cesium.Color.fromCssColorString('#00FF00').withAlpha(0.4); entity.polygon.outlineColor = Cesium.Color.fromCssColorString('#0055FF'); entity.polygon.outlineWidth = 4; } if (entity.polyline) { entity.polyline.clampToGround = true; entity.polyline.classificationType = Cesium.ClassificationType.TERRAIN; entity.polyline.material = Cesium.Color.fromCssColorString('#0055FF'); entity.polyline.width = 4; } if (entity.point) { entity.point.clampToGround = true; entity.point.color = Cesium.Color.fromCssColorString('#FF0000'); entity.point.pixelSize = 10; entity.point.outlineColor = Cesium.Color.fromCssColorString('#FFFFFF'); entity.point.outlineWidth = 2; entity.point.disableDepthTestDistance = Number.POSITIVE_INFINITY; entity.isGeoJsonPoint = true; } if (entity.billboard) { entity.billboard.show = false; entity.point = { color: Cesium.Color.fromCssColorString('#FF0000'), pixelSize: 10, outlineColor: Cesium.Color.fromCssColorString('#FFFFFF'), outlineWidth: 2, clampToGround: true, disableDepthTestDistance: Number.POSITIVE_INFINITY }; entity.isGeoJsonPoint = true; } } currentIndex = endIndex; // 使用 requestAnimationFrame 让出主线程给渲染 requestAnimationFrame(processBatch); }; // 开始处理 processBatch(); } catch (error) { console.error('处理GeoJSON实体失败:', error); } } // 加载s3m和场景 function promiseWhen(promiseArray, callback, type) { Cesium.when.all( promiseArray, function (layers) { storeDate.layers = viewer.scene.layers.layerQueue; actions.setChangeLayers(); callback(layers, type); storeDate.layers.forEach((s3mlayer) => { if (!s3mlayer.visibleDistanceMax || s3mlayer.visibleDistanceMax > 12000) { s3mlayer.visibleDistanceMax = 12000 } }) }, function (e) { let widget = viewer.cesiumWidget; if (widget._showRenderLoopErrors) { let title = '请检查url地址是否正确?'; widget.showErrorPanel(title, undefined, e); } } ); }; // 检验url function checkURL(url) { if (url === null || url === "") { return false; } if (url.charAt(0) == '"' || url.charAt(0) == "'") { let reg = /^['|"](.*)['|"]$/; url = url.replace(reg, "$1"); } return true }; // 删除图层 function layersDelete(type, id_name, callback) { switch (type) { case "SCENE": if (viewer.scene.layers && viewer.scene.layers.layerQueue) { const layers = viewer.scene.layers.layerQueue; for (let i = layers.length - 1; i >= 0; i--) { viewer.scene.layers.remove(layers[i]); } } actions.setChangeLayers(); if (callback) callback(); break; case "S3M": viewer.scene.layers.remove(id_name); actions.setChangeLayers(); if (callback) callback(); break; case "IMG": case "IMAGE": let img_layer; if (typeof (id_name) === 'number') { img_layer = viewer.imageryLayers.get(id_name); } else { let img_layers = viewer.imageryLayers._layers; for (let i = 0; i < img_layers.length; i++) { const layer = img_layers[i]; const provider = layer.imageryProvider; let matched = false; if (provider.tablename && provider.tablename === id_name) matched = true; if (!matched && layer._loadedLayerName && (layer._loadedLayerName === id_name || layer._loadedLayerName.includes(id_name) || id_name.includes(layer._loadedLayerName))) matched = true; if (!matched && layer._loadedUrl && (layer._loadedUrl === id_name || layer._loadedUrl.includes(id_name) || id_name.includes(layer._loadedUrl))) matched = true; if (!matched && provider.url) { const urlStr = typeof provider.url === 'string' ? provider.url : JSON.stringify(provider.url); if (urlStr.includes(id_name) || id_name.includes(urlStr)) matched = true; } if (!matched && provider.name && (provider.name === id_name || provider.name.includes(id_name))) matched = true; if (matched) { img_layer = layer; break; } } } if (img_layer) { viewer.imageryLayers.remove(img_layer); actions.setChangeLayers(); if (callback) callback(); } break; case "TERRAIN": viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider(); actions.setChangeLayers(); if (callback) callback(); break; case "MVT": viewer.scene.removeVectorTilesMap(id_name); actions.setChangeLayers(); if (callback) callback(); break; case "GEOJSON": case "SHP": if (viewer.dataSources) { const dataSources = viewer.dataSources._dataSources; let removed = false; for (let i = dataSources.length - 1; i >= 0; i--) { const ds = dataSources[i]; if (ds.name === id_name || ds._name === id_name || (ds.name && ds.name.includes(id_name)) || (ds._name && ds._name.includes(id_name))) { viewer.dataSources.remove(ds); removed = true; break; } const entities = ds.entities ? ds.entities.values : []; for (let j = 0; j < entities.length; j++) { const entity = entities[j]; if (entity.name === id_name || (entity.name && entity.name.includes(id_name))) { viewer.dataSources.remove(ds); removed = true; break; } } if (removed) break; } } actions.setChangeLayers(); if (callback) callback(); break; default: if (callback) callback(); } } export default { addS3mLayers, addScene, addTerrainLayer, addImageLayer, layersDelete, addMvtLayer, addGeoJsonLayer }; export { addS3mLayers, addScene, addTerrainLayer, addImageLayer, layersDelete, addMvtLayer, addGeoJsonLayer };