import { get2DView } from '../MultiViewportMode' import { getSdk } from '../SplitScreen' import { flyTo } from '../global' import Tools from '../../Tools' let tools let state = false let scale = '1:100万' function SheetIndexStatusSwitch(sdk, s = false) { if(!sdk) { return } if (!tools) { tools = new Tools() } state = s ? true : false if (state) { changeScale(sdk, scale) } else { close(sdk) } let sdk2D = get2DView() if (sdk2D) { if (state) { changeScale(sdk, scale) } else { close(sdk2D) } } let sdkD = getSdk().sdkD if(sdkD && sdk !== sdkD) { SheetIndexStatusSwitch(sdkD, s) } // return new Promise(async (resolve, reject) => { // setTimeout(() => { // resolve() // }, 1000); // }) } function changeScale(sdk, v) { scale = v if (state) { open(sdk) } let sdk2D = get2DView() if (sdk2D) { if (state) { open(sdk2D) } } return new Promise(async (resolve, reject) => { setTimeout(() => { resolve() }, 1000); }) } function getStatus() { return state } function open(sdk) { close(sdk) let cartographic = sdk.viewer.camera.positionCartographic let options = { position: { lng: Cesium.Math.toDegrees(cartographic.longitude), lat: Cesium.Math.toDegrees(cartographic.latitude), alt: cartographic.height, }, } let viewer = sdk.viewer; switch (scale) { case '1:100万': options.position.alt = 16000000 break case '1:50万': options.position.alt = 5000000 break case '1:25万': options.position.alt = 2300000 break case '1:10万': options.position.alt = 680000 break case '1:5万': options.position.alt = 385000 break case '1:2.5万': options.position.alt = 180000 break case '1:1万': options.position.alt = 90000 break case '1:5000': options.position.alt = 46000 break } let gridPrimitives let labelCollection for (let i = 0; i < viewer.scene.primitives._primitives.length; i++) { if (viewer.scene.primitives._primitives[i].name === 'SheetIndexGridPrimitives') { gridPrimitives = viewer.scene.primitives._primitives[i]; for (let j = 0; j < gridPrimitives._primitives.length; j++) { if (gridPrimitives._primitives[j].name === 'SheetIndexLabelCollection') { labelCollection = gridPrimitives._primitives[j]; break; } } break; } } if (!gridPrimitives) { gridPrimitives = new Cesium.PrimitiveCollection(); gridPrimitives.name = 'SheetIndexGridPrimitives'; viewer.scene.primitives.add(gridPrimitives); } if (!labelCollection) { labelCollection = new Cesium.LabelCollection(); labelCollection.name = 'SheetIndexLabelCollection'; } let stationaryFrames = 0; let maxRectangle = null; gridPrimitives.postRenderEvent = () => { let height = sdk.viewer.camera.positionCartographic.height switch (scale) { case '1:100万': options.position.alt = 16000000 break case '1:50万': options.position.alt = 5000000 break case '1:25万': options.position.alt = 2300000 break case '1:10万': options.position.alt = 680000 break case '1:5万': options.position.alt = 385000 break case '1:2.5万': options.position.alt = 180000 break case '1:1万': options.position.alt = 90000 break case '1:5000': options.position.alt = 46000 break } if (height > options.position.alt * 5) { maxRectangle = null; gridPrimitives.removeAll(); return } let isChanged = false let rectangle = getViewExtend(); let minLng = Cesium.Math.toDegrees(rectangle.west) let minLat = Cesium.Math.toDegrees(rectangle.south) let maxLng = Cesium.Math.toDegrees(rectangle.east) let maxLat = Cesium.Math.toDegrees(rectangle.north) if (minLng > maxLng) { maxLng += 360 } rectangle = { minLng, minLat, maxLng, maxLat } if (maxRectangle) { if ((maxRectangle.minLng > rectangle.minLng || maxRectangle.minLat > rectangle.minLat || maxRectangle.maxLng < rectangle.maxLng || maxRectangle.maxLat < rectangle.maxLat) && Cesium.Math.toDegrees(sdk.viewer.camera.pitch) < 0) { isChanged = true } } else { countMapSheet(scale) } if (isChanged) { stationaryFrames++; // 确认相机已经静止足够多帧 if (stationaryFrames >= 50) { countMapSheet(scale) isChanged = false } } else { stationaryFrames = 0; } } options.complete = () => { viewer.scene.postRender.addEventListener(gridPrimitives.postRenderEvent); } flyTo(sdk, options, 0.5) /** * 根据比例尺创建图幅线 * @param {string} scale - 比例尺(可选值:'1:100万', '1:50万', '1:25万', '1:10万', '1:5万', '1:2.5万', '1:1万', '1:5000') */ function countMapSheet(scale) { labelCollection.removeAll(); gridPrimitives.removeAll(); labelCollection = new Cesium.LabelCollection(); labelCollection.name = 'SheetIndexLabelCollection'; gridPrimitives.add(labelCollection); let rectangle = getViewExtend(); let lngStep // 经度步长 let latStep // 纬度步长 // let limitLng // 显示界限(根据图幅线数量显隐) // let limitLat // Math.abs(maxLng-minLng)/lngStep, Math.abs(maxLat-minLat)/latStep let scaleByDistance switch (scale) { case '1:100万': lngStep = 6; latStep = 4; scaleByDistance = new Cesium.NearFarScalar( 20000000, 1, 80000000, 0 ) break case '1:50万': lngStep = 3; latStep = 2; scaleByDistance = new Cesium.NearFarScalar( 5000000, 1, 30000000, 0 ) break case '1:25万': lngStep = 1.5; latStep = 1; scaleByDistance = new Cesium.NearFarScalar( 2300000, 1, 20000000, 0 ) break case '1:10万': lngStep = 0.5; latStep = 1 / 3; scaleByDistance = new Cesium.NearFarScalar( 680000, 1, 5000000, 0 ) break case '1:5万': lngStep = 0.25; latStep = 1 / 6; scaleByDistance = new Cesium.NearFarScalar( 385000, 1, 2400000, 0 ) break case '1:2.5万': lngStep = 0.125; latStep = 1 / 12; scaleByDistance = new Cesium.NearFarScalar( 180000, 1, 1200000, 0 ) break case '1:1万': lngStep = 0.0625; latStep = 1 / 24; scaleByDistance = new Cesium.NearFarScalar( 90000, 1, 700000, 0 ) break case '1:5000': lngStep = 0.03125; latStep = 1 / 48; scaleByDistance = new Cesium.NearFarScalar( 46000, 1, 300000, 0 ) break // case '1:1000': // lngStep = 0.01041667; // latStep = 0.00694444; // break // case '1:2000': // lngStep = 0.00520833; // latStep = 0.00347222; // break } let minLng = Math.floor((180 + Cesium.Math.toDegrees(rectangle.west)) / lngStep) * lngStep - 180; let minLat = Math.floor((88 + Cesium.Math.toDegrees(rectangle.south)) / latStep) * latStep - 88; let maxLng = Math.ceil((180 + Cesium.Math.toDegrees(rectangle.east)) / lngStep) * lngStep - 180; let maxLat = Math.ceil((88 + Cesium.Math.toDegrees(rectangle.north)) / latStep) * latStep - 88; if (minLng > maxLng) { maxLng += 360 } maxRectangle = { minLng, minLat, maxLng, maxLat } if (minLat < -88) { minLat = -88 } if (maxLat > 88) { maxLat = 88 } if (((maxRectangle.maxLng - maxRectangle.minLng) / lngStep) * ((maxRectangle.maxLat - maxRectangle.minLat) / latStep) > 7000) { maxRectangle = null return } // 绘制经线 for (let lng = minLng; lng <= maxLng; lng += lngStep) { const positions = []; let a = [] for (let lat = minLat; Math.floor(lat * 1000000000) / 1000000000 <= maxLat; lat += (latStep / 2)) { a.push([lng, lat]) positions.push(Cesium.Cartesian3.fromDegrees(lng, lat, 8848)); } if (maxLat != 88 && maxLat + (latStep / 2) >= 88) { positions.push(Cesium.Cartesian3.fromDegrees(lng, 88, 8848)); } const geometryInstances = new Cesium.GeometryInstance({ geometry: new Cesium.PolylineGeometry({ positions: positions, width: 1, vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT, arcType: Cesium.ArcType.RHUMB, }) }); gridPrimitives.add(new Cesium.Primitive({ geometryInstances: geometryInstances, appearance: new Cesium.PolylineMaterialAppearance({ material: Cesium.Material.fromType('Color', { color: Cesium.Color.fromCssColorString('#fcfc00') }) }) })); if (lng < maxLng) { // 计算图幅中心坐标 for (let lat = minLat; lat < maxLat; lat += latStep) { let position = { lng: lng + (lngStep / 2), lat: lat + (latStep / 2) }; if (position.lat > maxLat) { break } let sheetNumber = calculateMapSheetNumber(position.lng, position.lat, scale); labelCollection.add({ position: Cesium.Cartesian3.fromDegrees(position.lng, position.lat, 8848), text: sheetNumber, font: '16px Inter, sans-serif', fillColor: Cesium.Color.fromCssColorString('#fcfc00'), // backgroundColor: Cesium.Color.fromCssColorString('#FFA145'), // backgroundPadding: new Cesium.Cartesian2(8, 4), pixelOffset: new Cesium.Cartesian2(0, 0), showBackground: false, verticalOrigin: Cesium.VerticalOrigin.BOTTOM, horizontalOrigin: Cesium.HorizontalOrigin.CENTER, scale: 1.0, distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 80000000), scaleByDistance: scaleByDistance }) // labelCollection.add({ // position: Cesium.Cartesian3.fromDegrees(position.lng, position.lat, 10000), // text: position.lng + ' , ' + position.lat, // font: '16px Inter, sans-serif', // fillColor: Cesium.Color.WHITE, // backgroundColor: Cesium.Color.fromCssColorString('#165DFF').withAlpha(0.8), // backgroundPadding: new Cesium.Cartesian2(8, 4), // pixelOffset: new Cesium.Cartesian2(0, 30), // showBackground: true, // verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // scale: 1.0, // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 20000000), // scaleByDistance: new Cesium.NearFarScalar( // 5000000, // 1, // 20000000, // 0 // ) // }) } } } // 绘制纬线 for (let lat = minLat; Math.floor(lat * 1000000000) / 1000000000 <= maxLat; lat += latStep) { const positions = []; let a = [] for (let lng = minLng; lng <= maxLng; lng += (lngStep / 2)) { a.push([lng, lat]) positions.push(Cesium.Cartesian3.fromDegrees(lng, lat, 8848)); } const geometryInstances = new Cesium.GeometryInstance({ geometry: new Cesium.PolylineGeometry({ positions: positions, width: 1, vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT, arcType: Cesium.ArcType.RHUMB, }) }); gridPrimitives.add(new Cesium.Primitive({ geometryInstances: geometryInstances, appearance: new Cesium.PolylineMaterialAppearance({ material: Cesium.Material.fromType('Color', { color: Cesium.Color.fromCssColorString('#fcfc00') }) }) })); } } /** * 根据经纬度和比例尺计算地图图幅编号 * @param {number} longitude - 经度(十进制格式) * @param {number} latitude - 纬度(十进制格式) * @param {string} scale - 比例尺(可选值:'1:100万', '1:50万', '1:25万', '1:10万', '1:5万', '1:2.5万', '1:1万', '1:5000') * @returns {string} 对应的图幅编号 */ function calculateMapSheetNumber(lng, lat, scale) { let lngStep // 经度步长 let latStep // 纬度步长 switch (scale) { case '1:100万': lngStep = 6; latStep = 4; break case '1:50万': lngStep = 3; latStep = 2; break case '1:25万': lngStep = 1.5; latStep = 1; break case '1:10万': lngStep = 0.5; latStep = 1 / 3; break case '1:5万': lngStep = 0.25; latStep = 1 / 6; break case '1:2.5万': lngStep = 0.125; latStep = 1 / 12; break case '1:1万': lngStep = 0.0625; latStep = 1 / 24; break case '1:5000': lngStep = 0.03125; latStep = 1 / 48; break // case '1:1000': // lngStep = 0.01041667; // latStep = 0.00694444; // break // case '1:2000': // lngStep = 0.00520833; // latStep = 0.00347222; // break } // 确保纬度在 -88 到 88 度之间(因为 88° 以上采用特殊分幅) lat = Math.max(-88, Math.min(88, lat)); lat = Math.abs(lat); // 取绝对值 let B6 = 'ABCDEFGHIJKLMNOPQRSTUV' let B2 = lng let B3 = lat // 计算 1:100 万地形图的列号 const col100W = Math.floor(B2 / 6 + 31); // 1:100 万地形图的行号对应的字母(A-V) const rowChar = B6.charAt(Math.floor(B3 / 4 + 1) - 1); // 比例尺代码映射 const scaleCodeMap = { '1:100万': '', // 1:100万不需要额外代码 '1:50万': 'B', '1:25万': 'C', '1:10万': 'D', '1:5万': 'E', '1:2.5万': 'F', '1:1万': 'G', '1:5000': 'H' }; // 获取比例尺代码 const scaleCode = scaleCodeMap[scale]; if (!scaleCode && scale !== '1:100万') { throw new Error('不支持的比例尺,请使用: 1:100万, 1:50万, 1:25万, 1:10万, 1:5万, 1:2.5万, 1:1万, 1:5000'); } // 计算在 1:100 万图幅内的行列号(根据不同比例尺) let rowIn100W, colIn100W; rowIn100W = rowChar + col100W; const num1 = Math.floor((Math.ceil(B3 / 4) * 4 - B3) / latStep) + 1; const rowNum = ("000" + num1).slice(-3); const remainder = B2 - Math.floor(B2 / 6) * 6; const num2 = Math.floor(remainder / lngStep) + 1; const colNum = ("000" + num2).slice(-3); switch (scale) { case '1:100万': // 1:100万直接使用行号和列号 return rowIn100W; case '1:50万': break; case '1:25万': break; case '1:10万': break; case '1:5万': break; case '1:2.5万': break; case '1:1万': break; case '1:5000': break; default: throw new Error('不支持的比例尺'); } // 生成最终编号 return rowIn100W + scaleCode + rowNum + colNum; } // 获取当前视角矩形范围(二维模式) function getViewExtend() { let params = {}; let extend = viewer.camera.computeViewRectangle(); if (viewer.scene.mode == 2) { //2D下会可能拾取不到坐标,extend返回undefined,所以做以下转换 let canvas = viewer.scene.canvas; let upperLeft = new Cesium.Cartesian2(0, 0);//canvas左上角坐标转2d坐标 let lowerRight = new Cesium.Cartesian2( canvas.clientWidth, canvas.clientHeight );//canvas右下角坐标转2d坐标 let ellipsoid = viewer.scene.globe.ellipsoid; let upperLeft3 = viewer.camera.pickEllipsoid( upperLeft, ellipsoid );//2D转3D世界坐标 let lowerRight3 = viewer.camera.pickEllipsoid( lowerRight, ellipsoid );//2D转3D世界坐标 if (!upperLeft3) { let cartesian2 = Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, { x: 0, y: 0, z: 6356755 }); upperLeft.y = cartesian2.y + 5 upperLeft3 = viewer.camera.pickEllipsoid( upperLeft, ellipsoid ); } if (!lowerRight3) { let cartesian2 = Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, { x: 0, y: 0, z: -6356755 }); lowerRight.y = cartesian2.y - 5 lowerRight3 = viewer.camera.pickEllipsoid( lowerRight, ellipsoid ); // console.log('lowerRight3', lowerRight, lowerRight3) } let upperLeftCartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic( upperLeft3 );//3D世界坐标转弧度 let lowerRightCartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic( lowerRight3 );//3D世界坐标转弧度 if ((lowerRight.y - upperLeft.y) / (lowerRight.x - upperLeft.x) <= 0.49998752339363695) { extend = new Cesium.Rectangle(Cesium.Math.toRadians(-180), Cesium.Math.toRadians(-90), Cesium.Math.toRadians(180), Cesium.Math.toRadians(90)) } else { extend = new Cesium.Rectangle(upperLeftCartographic.longitude, lowerRightCartographic.latitude, lowerRightCartographic.longitude, upperLeftCartographic.latitude); } // console.log("经度:" + minx + "----" + maxx); // console.log("纬度:" + miny + "----" + maxy); return extend; } else { //3D获取方式 return extend; } } } function close(sdk) { let viewer = sdk.viewer; let gridPrimitives let labelCollection for (let i = 0; i < viewer.scene.primitives._primitives.length; i++) { if (viewer.scene.primitives._primitives[i].name === 'SheetIndexGridPrimitives') { gridPrimitives = viewer.scene.primitives._primitives[i]; for (let j = 0; j < gridPrimitives._primitives.length; j++) { if (gridPrimitives._primitives[j].name === 'SheetIndexLabelCollection') { labelCollection = gridPrimitives._primitives[j]; break; } } break; } } labelCollection && (labelCollection.removeAll()); gridPrimitives && (gridPrimitives.removeAll()); gridPrimitives && (viewer.scene.postRender.removeEventListener(gridPrimitives.postRenderEvent)); } export { SheetIndexStatusSwitch, changeScale, getStatus }