|
|
@@ -0,0 +1,722 @@
|
|
|
+/**
|
|
|
+ * 矢量数据加载器
|
|
|
+ * 负责从超图iServer REST数据服务获取矢量数据并转换为标准GeoJSON格式
|
|
|
+ * 优化版:支持分页加载,避免一次性请求全量数据,无截断、无超时丢失
|
|
|
+ */
|
|
|
+
|
|
|
+import { actions } from '../store/store.js';
|
|
|
+
|
|
|
+// 图层映射,用于后续管理
|
|
|
+const layerMap = new Map();
|
|
|
+
|
|
|
+/**
|
|
|
+ * 检测是否为投影坐标(大数值坐标)
|
|
|
+ * @param {number} x - 横坐标
|
|
|
+ * @param {number} y - 纵坐标
|
|
|
+ * @returns {boolean}
|
|
|
+ */
|
|
|
+function isProjectedCoordinate(x, y) {
|
|
|
+ return Math.abs(x) > 200 || Math.abs(y) > 100;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 简单的高斯-克吕格投影转经纬度(简化版)
|
|
|
+ * 适用于中国常用的CGCS2000或WGS84高斯-克吕格投影
|
|
|
+ * @param {number} x - 横坐标
|
|
|
+ * @param {number} y - 纵坐标
|
|
|
+ * @returns {number[]} - [经度, 纬度]
|
|
|
+ */
|
|
|
+function gaussKrugerToWGS84(x, y) {
|
|
|
+ let L0 = 0;
|
|
|
+ const zoneNumber = Math.floor(x / 1000000);
|
|
|
+
|
|
|
+ if (zoneNumber >= 1 && zoneNumber <= 60) {
|
|
|
+ L0 = (zoneNumber * 6 - 3) * Math.PI / 180;
|
|
|
+ } else if (x > 0) {
|
|
|
+ L0 = 105 * Math.PI / 180;
|
|
|
+ }
|
|
|
+
|
|
|
+ const a = 6378137;
|
|
|
+ const f = 1 / 298.257223563;
|
|
|
+ const e2 = 2 * f - f * f;
|
|
|
+
|
|
|
+ const x0 = x - zoneNumber * 1000000;
|
|
|
+ const N0 = 0;
|
|
|
+ const E0 = 500000;
|
|
|
+
|
|
|
+ const m = (x0 - E0) / a;
|
|
|
+ const n = (y - N0) / a;
|
|
|
+
|
|
|
+ const B = n + (1 + e2 * Math.cos(N0) * Math.cos(N0)) * Math.sin(N0) * Math.cos(N0) * m * m / 2;
|
|
|
+ const L = L0 + Math.tan(N0) * m / Math.cos(N0);
|
|
|
+
|
|
|
+ const lat = B * 180 / Math.PI;
|
|
|
+ const lon = L * 180 / Math.PI;
|
|
|
+
|
|
|
+ return [lon, lat];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 转换超图几何格式为标准GeoJSON格式
|
|
|
+ * @param {Object} superMapGeom - 超图几何对象
|
|
|
+ * @returns {Object} - GeoJSON几何对象
|
|
|
+ */
|
|
|
+function convertSuperMapGeometry(superMapGeom) {
|
|
|
+ const geoJsonGeom = {
|
|
|
+ type: 'Polygon',
|
|
|
+ coordinates: []
|
|
|
+ };
|
|
|
+
|
|
|
+ const points = superMapGeom.points || [];
|
|
|
+ const parts = superMapGeom.parts || [];
|
|
|
+ const type = superMapGeom.type || 'REGION';
|
|
|
+
|
|
|
+ if (points.length === 0) {
|
|
|
+ console.warn('几何对象没有点数据:', type);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ let needTransform = false;
|
|
|
+ if (points.length > 0) {
|
|
|
+ needTransform = isProjectedCoordinate(points[0].x, points[0].y);
|
|
|
+ }
|
|
|
+
|
|
|
+ const transformPoint = (p) => {
|
|
|
+ const [x, y] = needTransform ? gaussKrugerToWGS84(p.x, p.y) : [p.x, p.y];
|
|
|
+ return [x, y];
|
|
|
+ };
|
|
|
+
|
|
|
+ switch (type.toUpperCase()) {
|
|
|
+ case 'POINT':
|
|
|
+ geoJsonGeom.type = 'Point';
|
|
|
+ geoJsonGeom.coordinates = transformPoint(points[0]);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'MULTIPOINT':
|
|
|
+ geoJsonGeom.type = 'MultiPoint';
|
|
|
+ geoJsonGeom.coordinates = points.map(transformPoint);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'LINE':
|
|
|
+ case 'CURVE':
|
|
|
+ geoJsonGeom.type = 'LineString';
|
|
|
+ geoJsonGeom.coordinates = points.map(transformPoint);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'REGION':
|
|
|
+ geoJsonGeom.type = 'Polygon';
|
|
|
+ const rings = [];
|
|
|
+ let startIndex = 0;
|
|
|
+
|
|
|
+ if (parts.length > 0) {
|
|
|
+ for (let i = 0; i < parts.length; i++) {
|
|
|
+ const endIndex = i < parts.length - 1 ? parts[i + 1] : points.length;
|
|
|
+ const ring = points.slice(startIndex, endIndex).map(transformPoint);
|
|
|
+ if (ring.length >= 3) {
|
|
|
+ rings.push(ring);
|
|
|
+ } else if (ring.length > 0) {
|
|
|
+ console.warn('多边形环点数量不足:', ring.length);
|
|
|
+ }
|
|
|
+ startIndex = endIndex;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (points.length >= 3) {
|
|
|
+ rings.push(points.map(transformPoint));
|
|
|
+ } else {
|
|
|
+ console.warn('多边形点数量不足:', points.length);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ geoJsonGeom.coordinates = rings;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'MULTIPOLYGON':
|
|
|
+ geoJsonGeom.type = 'MultiPolygon';
|
|
|
+ const multiRings = [];
|
|
|
+ startIndex = 0;
|
|
|
+
|
|
|
+ if (parts.length > 0) {
|
|
|
+ for (let i = 0; i < parts.length; i++) {
|
|
|
+ const endIndex = i < parts.length - 1 ? parts[i + 1] : points.length;
|
|
|
+ const ring = points.slice(startIndex, endIndex).map(transformPoint);
|
|
|
+ if (ring.length >= 3) {
|
|
|
+ multiRings.push([ring]);
|
|
|
+ }
|
|
|
+ startIndex = endIndex;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (points.length >= 3) {
|
|
|
+ multiRings.push([points.map(transformPoint)]);
|
|
|
+ } else {
|
|
|
+ console.warn('多多边形点数量不足:', points.length);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ geoJsonGeom.coordinates = multiRings;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'LINESTRING':
|
|
|
+ geoJsonGeom.type = 'LineString';
|
|
|
+ geoJsonGeom.coordinates = points.map(transformPoint);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'MULTILINESTRING':
|
|
|
+ geoJsonGeom.type = 'MultiLineString';
|
|
|
+ const lines = [];
|
|
|
+ startIndex = 0;
|
|
|
+
|
|
|
+ if (parts.length > 0) {
|
|
|
+ for (let i = 0; i < parts.length; i++) {
|
|
|
+ const endIndex = i < parts.length - 1 ? parts[i + 1] : points.length;
|
|
|
+ const line = points.slice(startIndex, endIndex).map(transformPoint);
|
|
|
+ if (line.length >= 2) {
|
|
|
+ lines.push(line);
|
|
|
+ }
|
|
|
+ startIndex = endIndex;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (points.length >= 2) {
|
|
|
+ lines.push(points.map(transformPoint));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ geoJsonGeom.coordinates = lines;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ console.warn('未知几何类型:', type, '尝试作为Polygon处理');
|
|
|
+ geoJsonGeom.type = 'Polygon';
|
|
|
+ if (points.length >= 3) {
|
|
|
+ geoJsonGeom.coordinates = [points.map(transformPoint)];
|
|
|
+ } else {
|
|
|
+ console.warn('未知类型点数量不足,无法转换');
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return geoJsonGeom;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 转换坐标数组
|
|
|
+ * @param {number[]|number[][]} coordinates - 坐标数组
|
|
|
+ * @returns {number[]|number[][]} - 转换后的坐标数组
|
|
|
+ */
|
|
|
+function convertCoordinates(coordinates) {
|
|
|
+ if (!Array.isArray(coordinates)) {
|
|
|
+ return coordinates;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (typeof coordinates[0] === 'number' && typeof coordinates[1] === 'number') {
|
|
|
+ if (isProjectedCoordinate(coordinates[0], coordinates[1])) {
|
|
|
+ return gaussKrugerToWGS84(coordinates[0], coordinates[1]);
|
|
|
+ }
|
|
|
+ return coordinates;
|
|
|
+ }
|
|
|
+
|
|
|
+ return coordinates.map(coord => convertCoordinates(coord));
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 根据URI列表批量获取要素详情(优化版:无超时限制,自动重试)
|
|
|
+ * @param {string[]} uriList - 要素URI列表
|
|
|
+ * @returns {Promise<Object>} - GeoJSON FeatureCollection
|
|
|
+ */
|
|
|
+async function fetchFeaturesByUriList(uriList) {
|
|
|
+ console.log('开始根据URI列表获取要素详情,总数:', uriList.length);
|
|
|
+
|
|
|
+ const maxConcurrent = 10;
|
|
|
+ const allFeatures = [];
|
|
|
+
|
|
|
+ for (let i = 0; i < uriList.length; i += maxConcurrent) {
|
|
|
+ const batch = uriList.slice(i, i + maxConcurrent);
|
|
|
+ const batchPromises = batch.map(async (uri) => {
|
|
|
+ const featureUrl = uri.endsWith('.json') ? uri : `${uri}.json`;
|
|
|
+ console.log('获取要素:', featureUrl);
|
|
|
+
|
|
|
+ let retries = 3;
|
|
|
+ let lastError = null;
|
|
|
+
|
|
|
+ while (retries > 0) {
|
|
|
+ try {
|
|
|
+ const response = await fetch(featureUrl);
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(`HTTP错误: ${response.status}`);
|
|
|
+ }
|
|
|
+ const text = await response.text();
|
|
|
+ let featureData;
|
|
|
+ try {
|
|
|
+ featureData = JSON.parse(text);
|
|
|
+ } catch (e) {
|
|
|
+ console.error('要素JSON解析失败:', text.substring(0, 200));
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+
|
|
|
+ let feature = featureData;
|
|
|
+
|
|
|
+ if (featureData.feature) {
|
|
|
+ feature = featureData.feature;
|
|
|
+ } else if (featureData.result && featureData.result.feature) {
|
|
|
+ feature = featureData.result.feature;
|
|
|
+ }
|
|
|
+
|
|
|
+ let geometry = null;
|
|
|
+ let properties = {};
|
|
|
+
|
|
|
+ if (feature.geometry) {
|
|
|
+ geometry = feature.geometry;
|
|
|
+ } else if (feature.Geometry) {
|
|
|
+ geometry = feature.Geometry;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (feature.fieldNames && feature.fieldValues) {
|
|
|
+ feature.fieldNames.forEach((name, index) => {
|
|
|
+ properties[name] = feature.fieldValues[index];
|
|
|
+ });
|
|
|
+ } else if (feature.attributes) {
|
|
|
+ properties = feature.attributes;
|
|
|
+ } else if (feature.Attributes) {
|
|
|
+ properties = feature.Attributes;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!geometry) {
|
|
|
+ console.error('要素没有有效几何:', featureUrl);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const geoJsonGeom = convertSuperMapGeometry(geometry);
|
|
|
+
|
|
|
+ return {
|
|
|
+ type: 'Feature',
|
|
|
+ geometry: geoJsonGeom,
|
|
|
+ properties: properties
|
|
|
+ };
|
|
|
+ } catch (error) {
|
|
|
+ lastError = error;
|
|
|
+ retries--;
|
|
|
+ console.warn(`获取要素失败,剩余重试次数: ${retries}`, error.message);
|
|
|
+ if (retries > 0) {
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 1000 * (4 - retries)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ console.error('获取要素最终失败:', featureUrl, lastError);
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+
|
|
|
+ const results = await Promise.all(batchPromises);
|
|
|
+ const validFeatures = results.filter(Boolean);
|
|
|
+ allFeatures.push(...validFeatures);
|
|
|
+ console.log(`已加载 ${allFeatures.length} / ${uriList.length} 个要素`);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`URI列表加载完成,成功: ${allFeatures.length} / ${uriList.length}`);
|
|
|
+
|
|
|
+ return {
|
|
|
+ type: 'FeatureCollection',
|
|
|
+ features: allFeatures
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 从超图服务获取要素数据并转换为标准GeoJSON(优化版)
|
|
|
+ * @param {string} url - 超图服务URL
|
|
|
+ * @returns {Promise<Object>} - GeoJSON FeatureCollection
|
|
|
+ */
|
|
|
+async function fetchFeaturesFromSuperMap(url) {
|
|
|
+ const response = await fetch(url);
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(`HTTP错误: ${response.status}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ const text = await response.text();
|
|
|
+ try {
|
|
|
+ return JSON.parse(text);
|
|
|
+ } catch (e) {
|
|
|
+ console.error('JSON解析失败:', text.substring(0, 200));
|
|
|
+ throw new Error('服务返回的不是有效的JSON数据');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 加载单个分页的数据(处理childUriList情况)
|
|
|
+ * @param {string} url - 请求URL
|
|
|
+ * @returns {Promise<Array>} - 要素数组
|
|
|
+ */
|
|
|
+async function loadPageFeatures(url) {
|
|
|
+ const result = await fetchFeaturesFromSuperMap(url);
|
|
|
+ if (result && result.features) {
|
|
|
+ return result.features;
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 递归获取所有子URI的要素数据(优化版:无超时限制)
|
|
|
+ * @param {string} baseUrl - 数据集基础URL
|
|
|
+ * @param {number} startIndex - 起始索引
|
|
|
+ * @param {number} pageSize - 每页大小
|
|
|
+ * @param {number} totalCount - 总要素数
|
|
|
+ * @param {Array} accumulatedFeatures - 已累积的要素
|
|
|
+ * @returns {Promise<Array>} - 所有要素数组
|
|
|
+ */
|
|
|
+async function fetchWithRetry(url, maxRetries = 3, delayMs = 1000) {
|
|
|
+ let retries = 0;
|
|
|
+ while (retries < maxRetries) {
|
|
|
+ try {
|
|
|
+ const response = await fetch(url);
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(`HTTP错误: ${response.status}`);
|
|
|
+ }
|
|
|
+ return response.json();
|
|
|
+ } catch (error) {
|
|
|
+ retries++;
|
|
|
+ console.warn(`请求失败,第 ${retries}/${maxRetries} 次尝试`, error.message);
|
|
|
+ if (retries < maxRetries) {
|
|
|
+ await new Promise(resolve => setTimeout(resolve, delayMs * retries));
|
|
|
+ } else {
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function recursiveLoadFeatures(baseUrl, startIndex, pageSize, totalCount, accumulatedFeatures) {
|
|
|
+ const featuresUrl = `${baseUrl}/features.json?returnContent=true&startIndex=${startIndex}&maxFeatures=${pageSize}`;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const superMapData = await fetchWithRetry(featuresUrl);
|
|
|
+
|
|
|
+ const actualCount = superMapData.features?.length || superMapData.recordset?.length || superMapData.childUriList?.length || 0;
|
|
|
+
|
|
|
+ if (actualCount === 0 && startIndex === 0) {
|
|
|
+ console.warn('第一页就没有数据,可能是服务配置问题');
|
|
|
+ return accumulatedFeatures;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (superMapData.childUriList && Array.isArray(superMapData.childUriList)) {
|
|
|
+ console.log(`页面 ${startIndex}-${startIndex + pageSize} 返回了 ${superMapData.childUriList.length} 个URI`);
|
|
|
+ const result = await fetchFeaturesByUriList(superMapData.childUriList);
|
|
|
+ const uriFeatures = result.features || [];
|
|
|
+ console.log(`从URI列表获取了 ${uriFeatures.length} 个要素`);
|
|
|
+ accumulatedFeatures.push(...uriFeatures);
|
|
|
+ } else {
|
|
|
+ let currentPageFeatures = [];
|
|
|
+
|
|
|
+ if (superMapData.features && Array.isArray(superMapData.features)) {
|
|
|
+ currentPageFeatures = superMapData.features;
|
|
|
+ console.log(`页面 ${startIndex}-${startIndex + pageSize} 直接返回了 ${currentPageFeatures.length} 个要素`);
|
|
|
+ } else if (superMapData.recordset && Array.isArray(superMapData.recordset)) {
|
|
|
+ currentPageFeatures = superMapData.recordset;
|
|
|
+ console.log(`页面 ${startIndex}-${startIndex + pageSize} 从recordset返回了 ${currentPageFeatures.length} 个要素`);
|
|
|
+ }
|
|
|
+
|
|
|
+ currentPageFeatures = currentPageFeatures.map(feature => {
|
|
|
+ if (!feature) {
|
|
|
+ console.warn('发现空要素');
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ let geometry = feature.geometry || feature.Geometry;
|
|
|
+ if (!geometry) {
|
|
|
+ console.warn('要素没有几何信息:', JSON.stringify(feature).substring(0, 200));
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const geoJsonGeom = convertSuperMapGeometry(geometry);
|
|
|
+ if (!geoJsonGeom) {
|
|
|
+ console.warn('几何转换失败:', geometry.type);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ let properties = {};
|
|
|
+ if (feature.fieldNames && feature.fieldValues) {
|
|
|
+ feature.fieldNames.forEach((name, index) => {
|
|
|
+ properties[name] = feature.fieldValues[index];
|
|
|
+ });
|
|
|
+ } else if (feature.attributes) {
|
|
|
+ properties = feature.attributes;
|
|
|
+ } else if (feature.Attributes) {
|
|
|
+ properties = feature.Attributes;
|
|
|
+ } else if (feature.properties) {
|
|
|
+ properties = feature.properties;
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ type: 'Feature',
|
|
|
+ geometry: geoJsonGeom,
|
|
|
+ properties: properties
|
|
|
+ };
|
|
|
+ }).filter(Boolean);
|
|
|
+
|
|
|
+ accumulatedFeatures.push(...currentPageFeatures);
|
|
|
+ console.log(`本页成功转换 ${currentPageFeatures.length} 个要素`);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (actualCount < pageSize) {
|
|
|
+ console.log(`最后一页,实际获取 ${actualCount} 个要素,总共获取 ${accumulatedFeatures.length} 个要素`);
|
|
|
+ return accumulatedFeatures;
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`加载页面 ${startIndex}-${startIndex + pageSize} 失败:`, error);
|
|
|
+ // 如果是最后一次重试失败,记录但继续尝试下一页
|
|
|
+ }
|
|
|
+
|
|
|
+ return recursiveLoadFeatures(baseUrl, startIndex + pageSize, pageSize, totalCount, accumulatedFeatures);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 直接加载数据集的要素(优化版:支持分页加载,无超时限制,自动重试)
|
|
|
+ * @param {string} baseUrl - 数据集URL
|
|
|
+ * @param {string} name - 图层名称
|
|
|
+ * @param {Function} callback - 回调函数
|
|
|
+ */
|
|
|
+async function loadDatasetFeatures(baseUrl, name, callback) {
|
|
|
+ console.log('直接加载数据集要素:', baseUrl);
|
|
|
+
|
|
|
+ try {
|
|
|
+ const countUrl = `${baseUrl}/features.json?returnContent=false&maxFeatures=1`;
|
|
|
+ console.log('获取要素总数:', countUrl);
|
|
|
+
|
|
|
+ const countData = await fetch(countUrl).then(response => response.json());
|
|
|
+ const totalCount = countData.featureCount || countData.totalCount || 10000;
|
|
|
+ console.log(`要素总数: ${totalCount}`);
|
|
|
+
|
|
|
+ // 使用递归方式加载所有要素,确保正确处理childUriList情况
|
|
|
+ const pageSize = 50;
|
|
|
+ const allFeatures = await recursiveLoadFeatures(baseUrl, 0, pageSize, totalCount, []);
|
|
|
+
|
|
|
+ console.log(`最终获取到 ${allFeatures.length} 个要素`);
|
|
|
+
|
|
|
+ const geoJsonData = {
|
|
|
+ type: 'FeatureCollection',
|
|
|
+ features: allFeatures
|
|
|
+ };
|
|
|
+
|
|
|
+ const featureCount = geoJsonData.features ? geoJsonData.features.length : 0;
|
|
|
+ console.log(`最终加载了 ${featureCount} 个要素`);
|
|
|
+
|
|
|
+ const geoJsonDataSource = await Cesium.GeoJsonDataSource.load(geoJsonData, {
|
|
|
+ stroke: Cesium.Color.fromCssColorString('#0055FF').withAlpha(1),
|
|
|
+ fill: Cesium.Color.fromCssColorString('#00FF00').withAlpha(0.4),
|
|
|
+ strokeWidth: 3,
|
|
|
+ clampToGround: true
|
|
|
+ });
|
|
|
+
|
|
|
+ geoJsonDataSource.name = name;
|
|
|
+ viewer.dataSources.add(geoJsonDataSource);
|
|
|
+ processGeoJsonEntities(geoJsonDataSource, name);
|
|
|
+ viewer.flyTo(geoJsonDataSource, { duration: 2 });
|
|
|
+ actions.setChangeLayers();
|
|
|
+
|
|
|
+ if (callback) callback(geoJsonDataSource);
|
|
|
+
|
|
|
+ return geoJsonDataSource;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载数据集要素失败:', error);
|
|
|
+ if (callback) callback(null);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 加载超图REST数据服务(优化版:无超时丢失)
|
|
|
+ * @param {string} baseUrl - 服务基础URL
|
|
|
+ * @param {string} name - 图层名称
|
|
|
+ * @param {Function} callback - 回调函数
|
|
|
+ */
|
|
|
+async function loadSuperMapRestDataService(baseUrl, name, callback) {
|
|
|
+ console.log('开始加载超图REST数据服务:', baseUrl);
|
|
|
+
|
|
|
+ try {
|
|
|
+ let datasourceUrl = baseUrl;
|
|
|
+ if (!datasourceUrl.endsWith('.json')) {
|
|
|
+ datasourceUrl = `${datasourceUrl}.json`;
|
|
|
+ }
|
|
|
+ console.log('数据源URL:', datasourceUrl);
|
|
|
+
|
|
|
+ const response = await fetch(datasourceUrl);
|
|
|
+ const data = await response.json();
|
|
|
+ console.log('获取到数据源信息:', data);
|
|
|
+
|
|
|
+ let datasetUrl = baseUrl;
|
|
|
+
|
|
|
+ if (data.childUriList && Array.isArray(data.childUriList)) {
|
|
|
+ if (data.datasetInfo) {
|
|
|
+ console.log('URL直接指向数据集');
|
|
|
+ datasetUrl = baseUrl;
|
|
|
+ } else {
|
|
|
+ const datasetNames = data.childUriList.map(uri => {
|
|
|
+ const parts = uri.split('/');
|
|
|
+ return parts[parts.length - 1].replace('.json', '');
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log('数据集列表:', datasetNames);
|
|
|
+
|
|
|
+ if (datasetNames.length === 0) {
|
|
|
+ console.error('未找到任何数据集');
|
|
|
+ if (callback) callback(null);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const firstDatasetName = datasetNames[0];
|
|
|
+ datasetUrl = `${baseUrl}/${firstDatasetName}`;
|
|
|
+ console.log('加载第一个数据集:', datasetUrl);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const geoJsonDataSource = await loadDatasetFeatures(datasetUrl, name, callback);
|
|
|
+
|
|
|
+ return geoJsonDataSource;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载超图REST数据服务失败:', error);
|
|
|
+ if (callback) callback(null);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 处理GeoJSON实体(异步方式,避免阻塞主线程)
|
|
|
+ * @param {Object} dataSource - Cesium GeoJsonDataSource对象
|
|
|
+ * @param {string} name - 图层名称
|
|
|
+ * @param {Object} customStyle - 自定义样式配置
|
|
|
+ */
|
|
|
+function processGeoJsonEntities(dataSource, name, customStyle) {
|
|
|
+ try {
|
|
|
+ let entities = dataSource.entities.values;
|
|
|
+ let entitiesLength = entities.length;
|
|
|
+
|
|
|
+ console.log(`开始处理 ${entitiesLength} 个实体`);
|
|
|
+
|
|
|
+ // 获取样式配置
|
|
|
+ const style = customStyle || window.currentGeoJsonStyle || {
|
|
|
+ fillColor: '#00FF00',
|
|
|
+ fillOpacity: 0.4,
|
|
|
+ strokeColor: '#0055FF',
|
|
|
+ strokeWidth: 2
|
|
|
+ };
|
|
|
+
|
|
|
+ const fillColor = Cesium.Color.fromCssColorString(style.fillColor).withAlpha(style.fillOpacity);
|
|
|
+ const strokeColor = Cesium.Color.fromCssColorString(style.strokeColor);
|
|
|
+ const strokeWidth = style.strokeWidth || 2;
|
|
|
+
|
|
|
+ // 使用异步批量处理
|
|
|
+ const batchSize = 100;
|
|
|
+ let currentIndex = 0;
|
|
|
+
|
|
|
+ const processBatch = () => {
|
|
|
+ if (currentIndex >= entitiesLength) {
|
|
|
+ console.log(`处理完成,共处理 ${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 = fillColor;
|
|
|
+ entity.polygon.outlineColor = strokeColor;
|
|
|
+ entity.polygon.outlineWidth = strokeWidth;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (entity.polyline) {
|
|
|
+ entity.polyline.clampToGround = true;
|
|
|
+ entity.polyline.classificationType = Cesium.ClassificationType.TERRAIN;
|
|
|
+ entity.polyline.material = strokeColor;
|
|
|
+ entity.polyline.width = strokeWidth;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (entity.point) {
|
|
|
+ entity.point.clampToGround = true;
|
|
|
+ entity.point.color = strokeColor;
|
|
|
+ entity.point.pixelSize = style.pointSize || 10;
|
|
|
+ entity.point.outlineColor = Cesium.Color.WHITE;
|
|
|
+ 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(processBatch);
|
|
|
+ };
|
|
|
+
|
|
|
+ processBatch();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('处理GeoJSON实体失败:', error);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取图层映射
|
|
|
+ * @returns {Map} - 图层映射
|
|
|
+ */
|
|
|
+function getLayerMap() {
|
|
|
+ return layerMap;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 移除指定图层
|
|
|
+ * @param {string} layerName - 图层名称
|
|
|
+ */
|
|
|
+function removeLayer(layerName) {
|
|
|
+ const layer = layerMap.get(layerName);
|
|
|
+ if (layer) {
|
|
|
+ viewer.scene.primitives.remove(layer);
|
|
|
+ layerMap.delete(layerName);
|
|
|
+ console.log(`图层 ${layerName} 已移除`);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 导出函数
|
|
|
+export default {
|
|
|
+ fetchFeaturesFromSuperMap,
|
|
|
+ loadDatasetFeatures,
|
|
|
+ loadSuperMapRestDataService,
|
|
|
+ convertSuperMapGeometry,
|
|
|
+ convertCoordinates,
|
|
|
+ gaussKrugerToWGS84,
|
|
|
+ isProjectedCoordinate,
|
|
|
+ getLayerMap,
|
|
|
+ removeLayer
|
|
|
+};
|
|
|
+
|
|
|
+export {
|
|
|
+ fetchFeaturesFromSuperMap,
|
|
|
+ loadDatasetFeatures,
|
|
|
+ loadSuperMapRestDataService,
|
|
|
+ convertSuperMapGeometry,
|
|
|
+ convertCoordinates,
|
|
|
+ gaussKrugerToWGS84,
|
|
|
+ isProjectedCoordinate,
|
|
|
+ processGeoJsonEntities,
|
|
|
+ getLayerMap,
|
|
|
+ removeLayer
|
|
|
+};
|