@orca-fe/x-map
Version:
183 lines (175 loc) • 7.38 kB
JavaScript
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);
}
}