UNPKG

@orca-fe/x-map

Version:
183 lines (175 loc) 7.38 kB
import { Color, DoubleSide, Mesh, RawShaderMaterial, Shape, ShapeGeometry } from 'three'; import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'; import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry'; import { Line2 } from 'three/examples/jsm/lines/Line2'; import { difference, polygon, union } from '@turf/turf'; import { lonLat2Mercator } from '../../utils/coord'; import ThreeObject from './ThreeObject'; // Polygon | MultiPolygon -> MultiPolygon const geoJson2multiPolygon = (geoJson) => { const multiPolygon = { type: 'MultiPolygon', coordinates: [], }; if (geoJson.type === 'Polygon') { multiPolygon.coordinates = [geoJson.coordinates]; } else { multiPolygon.coordinates = geoJson.coordinates; } return multiPolygon; }; // 合成多个holes,并返回在范围内部分 const formatPolygonJson = (polygonJson) => { var _a; const { coordinates } = polygonJson; const shapePoint = [...coordinates]; const holes = shapePoint.splice(1).map(i => polygon([i])); if (holes.length === 0) { return polygonJson; } let [unionHoles] = holes; if (holes.length > 1) { unionHoles = holes.reduce((prev, next) => { const res = union(prev, next); if (res) return res; return prev; }); } return (_a = difference(polygon(shapePoint), unionHoles)) === null || _a === void 0 ? void 0 : _a.geometry; }; export default class PolygonObject extends ThreeObject { constructor(options = {}) { var _a; super(options); this.object3D = new Mesh(); const { polygonGeoJson, fill = { color: 0x33ccff, opacity: 0.1 }, border = false, polygonMaterialParameters, } = options; this.polygonGeoJson = polygonGeoJson; const color = new Color(fill.color); this.polygonMaterial = new RawShaderMaterial(Object.assign({ uniforms: { baseColor: { value: [color.r, color.g, color.b, (_a = fill.opacity) !== null && _a !== void 0 ? _a : 1] }, }, vertexShader: ` precision mediump float; precision mediump int; uniform mat4 modelViewMatrix; // optional uniform mat4 projectionMatrix; // optional attribute vec3 position; attribute vec4 color; varying vec3 vPosition; void main() { vPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); } `, fragmentShader: ` precision mediump float; precision mediump int; uniform vec4 baseColor; varying vec3 vPosition; void main() { gl_FragColor = baseColor; } `, side: DoubleSide, depthWrite: false, depthTest: false, transparent: true }, polygonMaterialParameters)); if (border) { this.lineMaterial = new LineMaterial(Object.assign({ transparent: true }, border)); } this.object3D.position.set(0, 0, this.z); } // 创建一个常规(不带孔)的shape及line createNormalPolygonShape(polygonJson) { var _a; const shape = new Shape(); const positions = []; if (!((_a = this.layer) === null || _a === void 0 ? void 0 : _a.map)) return { shape }; const { threeCenter } = this.layer.map; const { coordinates } = polygonJson; const [shapePoint = []] = coordinates; for (let i = 0; i < shapePoint.length; i++) { const [x, y] = lonLat2Mercator(shapePoint[i]).map((value, index) => value - threeCenter[index]); if (i === 0) { shape.moveTo(x, y); } shape.lineTo(x, y); positions.push(x, y, 0); } let line; if (this.lineMaterial && positions.length > 0) { const linGeometry = new LineGeometry(); linGeometry.setPositions(positions); line = new Line2(linGeometry, this.lineMaterial); line.computeLineDistances(); line.scale.set(1, 1, 1); } return { shape, line }; } createObject() { var _a; if (!((_a = this.layer) === null || _a === void 0 ? void 0 : _a.map)) return; if (!this.polygonGeoJson) return; // 统一格式化为MultiPolygon const multiPolygon = geoJson2multiPolygon(this.polygonGeoJson); const shapeList = []; const lineList = []; multiPolygon.coordinates.forEach((polygonCoordinates) => { // 合成holes const polygonJson = formatPolygonJson({ type: 'Polygon', coordinates: polygonCoordinates }); if (polygonJson) { // 再次格式化为MultiPolygon const normalizeMultiPolygon = geoJson2multiPolygon(polygonJson); normalizeMultiPolygon.coordinates.forEach((normalizeMultiPolygonCoordinates) => { const mainShapePoint = [...normalizeMultiPolygonCoordinates]; const holes = mainShapePoint.splice(1); // 主体 const { shape, line } = this.createNormalPolygonShape({ type: 'Polygon', coordinates: mainShapePoint }); if (line) { lineList.push(line); } // 孔 holes.forEach((hole) => { const { shape: holeShape, line: holeLine } = this.createNormalPolygonShape({ type: 'Polygon', coordinates: [hole], }); shape.holes.push(holeShape); if (holeLine) { lineList.push(holeLine); } }); shapeList.push(shape); }); } }); const polygonGeometry = new ShapeGeometry(shapeList); this.object3D.geometry = polygonGeometry; this.object3D.material = this.polygonMaterial; this.object3D.clear(); this.object3D.renderOrder = Math.round(this.z); lineList.forEach((line) => { this.object3D.add(line); }); } setPolygon(polygon) { this.polygonGeoJson = polygon; this.createObject(); } updatePosition() { if (this.layer && this.lineMaterial) { const [width, height] = this.layer.getSize(); this.lineMaterial.resolution.set(width, height); } } updateBorder(border) { var _a, _b; (_a = this.lineMaterial) === null || _a === void 0 ? void 0 : _a.setValues(border); (_b = this.layer) === null || _b === void 0 ? void 0 : _b.updatePosition(); } updateFill(fill) { var _a, _b; const color = new Color(fill.color); this.polygonMaterial.uniforms.baseColor.value = [color.r, color.g, color.b, fill.opacity]; (_b = (_a = this.layer) === null || _a === void 0 ? void 0 : _a.updatePositionDebounce) === null || _b === void 0 ? void 0 : _b.call(_a); } }