* fix: 修复 GeometryLayer 失效

* feat: 线图层支持箭头样式

* style: lint style

* feat: 增加注释

* feat: 变量名替换
This commit is contained in:
YiQianYao 2022-04-13 11:43:51 +08:00 committed by GitHub
parent 791b99035b
commit d4d2068150
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 272 additions and 17 deletions

View File

@ -111,6 +111,7 @@ export interface IVertexAttributeDescriptor
vertex: number[],
attributeIdx: number,
normal: number[],
vertexIndex?: number,
) => number[];
}
@ -168,6 +169,7 @@ export type Triangulation = (
indices: number[];
size: number;
normals?: number[];
indexes?: number[];
};
export interface IStyleAttributeUpdateOptions {

View File

@ -53,6 +53,7 @@ export default class StyleAttributeService implements IStyleAttributeService {
vertices: number[];
normals: number[];
offset: number;
indexes?: number[];
}>;
} = {
sizePerElement: 0,
@ -218,6 +219,7 @@ export default class StyleAttributeService implements IStyleAttributeService {
vertices: verticesForCurrentFeature,
normals: normalsForCurrentFeature,
size: vertexSize,
indexes,
} = this.triangulation(feature, segmentNumber);
indicesForCurrentFeature.forEach((i) => {
indices.push(i + verticesNum);
@ -249,6 +251,12 @@ export default class StyleAttributeService implements IStyleAttributeService {
vertexIdx * vertexSize,
vertexIdx * vertexSize + vertexSize,
);
let vertexIndex = 0;
if (indexes && indexes[vertexIdx] !== undefined) {
vertexIndex = indexes[vertexIdx];
}
descriptors.forEach((descriptor, attributeIdx) => {
if (descriptor && descriptor.update) {
(descriptor.buffer.data as number[]).push(
@ -258,6 +266,7 @@ export default class StyleAttributeService implements IStyleAttributeService {
vertice,
vertexIdx, // 当前顶点所在feature索引
normal,
vertexIndex,
// TODO: 传入顶点索引 vertexIdx
),
);

View File

@ -7,6 +7,12 @@ export enum lineStyleType {
'dash' = 1.0,
}
interface ILineArrow {
enable: boolean;
arrowWidth: number;
arrowHeight: number;
}
export interface ILineLayerStyleOptions {
opacity: styleSingle;
lineType?: keyof typeof lineStyleType; // 可选参数、线类型(all - dash/solid)
@ -34,6 +40,8 @@ export interface ILineLayerStyleOptions {
mask?: boolean; // 可选参数 时候允许蒙层
maskInside?: boolean; // 可选参数 控制图层是否显示在蒙层的内部
arrow?: ILineArrow;
}
export interface IPointLayerStyleOptions {

View File

@ -139,6 +139,7 @@ export function LineTriangulation(feature: IEncodeFeature) {
vertices: linebuffer.positions, // [ x,y,z, distance, miter,total ]
indices: linebuffer.indices,
normals: linebuffer.normals,
indexes: linebuffer.indexes,
size: 6,
};
}

View File

@ -45,6 +45,11 @@ export default class LineModel extends BaseModel {
borderColor = '#ccc',
raisingHeight = 0,
heightfixed = false,
arrow = {
enable: false,
arrowWidth: 2,
arrowHeight: 3,
},
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
if (dashArray.length === 2) {
dashArray.push(0, 0);
@ -124,6 +129,11 @@ export default class LineModel extends BaseModel {
// 顶点高度 scale
u_vertexScale: vertexHeightScale,
u_raisingHeight: Number(raisingHeight),
// arrow
u_arrow: Number(arrow.enable),
u_arrowHeight: arrow.arrowHeight || 3,
u_arrowWidth: arrow.arrowWidth || 2,
};
}
public getAnimateUniforms(): IModelUniform {
@ -216,14 +226,18 @@ export default class LineModel extends BaseModel {
data: [],
type: gl.FLOAT,
},
size: 1,
size: 2,
update: (
feature: IEncodeFeature,
featureIdx: number,
vertex: number[],
attributeIdx: number,
normal: number[],
vertexIndex?: number,
) => {
return [vertex[3]];
return vertexIndex === undefined
? [vertex[3], 10]
: [vertex[3], vertexIndex];
},
},
});

View File

@ -11,7 +11,7 @@ attribute vec2 a_iconMapUV;
// dash line
attribute float a_Total_Distance;
attribute float a_Distance;
attribute vec2 a_Distance;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
@ -32,6 +32,9 @@ varying vec2 v_iconMapUV;
uniform float u_linearColor: 0;
uniform float u_arrow: 0.0;
uniform float u_arrowHeight: 3.0;
uniform float u_arrowWidth: 2.0;
uniform float u_opacity: 1.0;
varying mat4 styleMappingMat; // 用于将在顶点着色器中计算好的样式值传递给片元
@ -39,6 +42,48 @@ varying mat4 styleMappingMat; // 用于将在顶点着色器中计算好的样
#pragma include "styleMapping"
#pragma include "styleMappingCalOpacity"
vec2 calculateArrow(vec2 offset) {
/*
* 在支持箭头的时候,第二、第三组顶点是额外插入用于构建顶点的
*/
float arrowFlag = -1.0;
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) {
// 高德 2.0 的旋转角度不同
arrowFlag = 1.0;
}
float pi = arrowFlag * 3.1415926/2.;
if(a_Miter < 0.) {
// 根据线的两侧偏移不同、旋转的方向相反
pi = -pi;
}
highp float angle_sin = sin(pi);
highp float angle_cos = cos(pi);
// 计算垂直与线方向的旋转矩阵
mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);
float arrowWidth = u_arrowWidth;
float arrowHeight = u_arrowHeight;
vec2 arrowOffset = vec2(0.0);
/*
* a_Distance.y 用于标记当前顶点属于哪一组(两个顶点一组,构成线的其实是矩形,最简需要四个顶点、两组顶点构成)
*/
if(a_Distance.y == 0.0) {
// 箭头尖部
offset = vec2(0.0);
} else if(a_Distance.y == 1.0) {
// 箭头两侧
arrowOffset = rotation_matrix*(offset * arrowHeight);
offset += arrowOffset; // 沿线偏移
offset = offset * arrowWidth; // 垂直线向外偏移(是构建箭头两侧的顶点)
} else if(a_Distance.y == 2.0 || a_Distance.y == 3.0 || a_Distance.y == 4.0) {
// 偏移其余的点位(将长度让位给箭头)
arrowOffset = rotation_matrix*(offset * arrowHeight) * arrowWidth;
offset += arrowOffset;// 沿线偏移
}
return offset;
}
void main() {
// cal style mapping - 数据纹理映射部分的计算
styleMappingMat = mat4(
@ -79,14 +124,19 @@ void main() {
vec3 size = a_Miter * setPickingSize(a_Size.x) * reverse_offset_normal(a_Normal);
vec2 offset = project_pixel(size.xy);
if(u_arrow > 0.0) {
// 计算箭头
offset = calculateArrow(offset);
}
float lineOffsetWidth = length(offset + offset * sign(a_Miter)); // 线横向偏移的距离(向两侧偏移的和)
float linePixelSize = project_pixel(a_Size.x) * 2.0; // 定点位置偏移,按地图等级缩放后的距离 单侧 * 2
float texV = lineOffsetWidth/linePixelSize; // 线图层贴图部分的 v 坐标值
// 设置数据集的参数
styleMappingMat[3][0] = a_Distance / a_Total_Distance;; // 当前点位距离占线总长的比例
styleMappingMat[3][1] = a_Distance; // 当前顶点的距离
styleMappingMat[3][0] = a_Distance.x / a_Total_Distance;; // 当前点位距离占线总长的比例
styleMappingMat[3][1] = a_Distance.x; // 当前顶点的距离
styleMappingMat[3][2] = d_texPixelLen; // 贴图的像素长度,根据地图层级缩放
styleMappingMat[3][3] = texV; // 线图层贴图部分的 v 坐标值

View File

@ -9,13 +9,15 @@ import {
IParseDataItem,
IStyleAttribute,
IStyleAttributeService,
Position,
TYPES,
} from '@antv/l7-core';
import { Version } from '@antv/l7-maps';
import { isColor, rgb2arr, unProjectFlat } from '@antv/l7-utils';
import { isColor, normalize, rgb2arr, unProjectFlat } from '@antv/l7-utils';
import { inject, injectable } from 'inversify';
import { cloneDeep } from 'lodash';
import 'reflect-metadata';
import { ILineLayerStyleOptions } from '../core/interface';
@injectable()
export default class DataMappingPlugin implements ILayerPlugin {
@ -54,21 +56,24 @@ export default class DataMappingPlugin implements ILayerPlugin {
const attributes = styleAttributeService.getLayerStyleAttributes() || [];
const filter = styleAttributeService.getLayerStyleAttribute('filter');
const { dataArray } = layer.getSource().data;
const attributesToRemapping = attributes.filter(
(attribute) => attribute.needRemapping, // 如果filter变化
);
let filterData = dataArray;
// 数据过滤完 再执行数据映射
if (filter?.needRemapping && filter?.scale) {
filterData = dataArray.filter((record: IParseDataItem) => {
return this.applyAttributeMapping(filter, record, bottomColor)[0];
});
}
if (attributesToRemapping.length) {
// 过滤数据
if (filter?.needRemapping) {
layer.setEncodedData(
this.mapping(attributes, filterData, undefined, bottomColor),
this.mapping(attributes, filterData, undefined, bottomColor, layer),
);
filter.needRemapping = false;
} else {
@ -78,6 +83,7 @@ export default class DataMappingPlugin implements ILayerPlugin {
filterData,
layer.getEncodedData(),
bottomColor,
layer,
),
);
}
@ -104,19 +110,34 @@ export default class DataMappingPlugin implements ILayerPlugin {
});
}
layer.setEncodedData(
this.mapping(attributes, filterData, undefined, bottomColor),
this.mapping(attributes, filterData, undefined, bottomColor, layer),
);
// 对外暴露事件
layer.emit('dataUpdate', null);
}
private getArrowPoints(p1: Position, p2: Position) {
const dir = [p2[0] - p1[0], p2[1] - p1[1]];
const normalizeDir = normalize(dir);
const arrowPoint = [
p1[0] + normalizeDir[0] * 0.0001,
p1[1] + normalizeDir[1] * 0.0001,
];
return arrowPoint;
}
private mapping(
attributes: IStyleAttribute[],
data: IParseDataItem[],
predata?: IEncodeFeature[],
minimumColor?: string,
layer?: ILayer,
): IEncodeFeature[] {
// console.log('data', data)
const {
arrow = {
enable: false,
},
} = layer?.getLayerConfig() as ILineLayerStyleOptions;
const mappedData = data.map((record: IParseDataItem, i) => {
const preRecord = predata ? predata[i] : {};
const encodeRecord: IEncodeFeature = {
@ -124,11 +145,9 @@ export default class DataMappingPlugin implements ILayerPlugin {
coordinates: record.coordinates,
...preRecord,
};
// console.log('attributes', attributes)
attributes
.filter((attribute) => attribute.scale !== undefined)
.forEach((attribute: IStyleAttribute) => {
// console.log('attribute', attribute)
// console.log('record', record)
let values = this.applyAttributeMapping(
attribute,
@ -156,6 +175,13 @@ export default class DataMappingPlugin implements ILayerPlugin {
);
}
});
if (encodeRecord.shape === 'line' && arrow.enable) {
// 只有在线图层且支持配置箭头的时候进行插入顶点的处理
const coords = encodeRecord.coordinates as Position[];
const arrowPoint = this.getArrowPoints(coords[0], coords[1]);
encodeRecord.coordinates.splice(1, 0, arrowPoint, arrowPoint);
}
return encodeRecord;
}) as IEncodeFeature[];
// console.log('mappedData', mappedData)
@ -165,7 +191,7 @@ export default class DataMappingPlugin implements ILayerPlugin {
// 调整数据兼容 SimpleCoordinates
this.adjustData2SimpleCoordinates(mappedData);
// console.log('mappedData', mappedData)
return mappedData;
}

View File

@ -62,6 +62,7 @@ export default class ExtrudePolyline {
indices: number[];
normals: number[];
startIndex: number;
indexes: number[];
};
private join: string;
private cap: string;
@ -73,6 +74,7 @@ export default class ExtrudePolyline {
private started: boolean = false;
private dash: boolean = false;
private totalDistance: number = 0;
private currentIndex: number = 0;
constructor(opts: Partial<IExtrudeLineOption> = {}) {
this.join = opts.join || 'miter';
@ -85,6 +87,7 @@ export default class ExtrudePolyline {
indices: [],
normals: [],
startIndex: 0,
indexes: [],
};
}
@ -313,6 +316,7 @@ export default class ExtrudePolyline {
-this.thickness,
last[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
positions.push(
last[0],
last[1],
@ -321,6 +325,8 @@ export default class ExtrudePolyline {
this.thickness,
last[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
this.currentIndex++;
} else {
this.extrusions(
positions,
@ -354,6 +360,7 @@ export default class ExtrudePolyline {
this.thickness,
cur[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
positions.push(
cur[0],
cur[1],
@ -362,6 +369,8 @@ export default class ExtrudePolyline {
this.thickness,
cur[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
this.currentIndex++;
} else {
this.extrusions(
positions,
@ -429,6 +438,7 @@ export default class ExtrudePolyline {
-this.thickness * flip,
cur[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
positions.push(
cur[0],
cur[1],
@ -437,6 +447,8 @@ export default class ExtrudePolyline {
this.thickness * flip,
cur[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
this.currentIndex++;
indices.push(
...(this.lastFlip !== -flip
? [index, index + 2, index + 3]
@ -457,6 +469,8 @@ export default class ExtrudePolyline {
-this.thickness * flip,
cur[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
this.currentIndex++;
count += 3;
} else {
this.extrusions(
@ -636,6 +650,7 @@ export default class ExtrudePolyline {
-this.thickness,
last[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
positions.push(
last[0],
last[1],
@ -644,7 +659,8 @@ export default class ExtrudePolyline {
this.thickness,
last[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
this.currentIndex++;
// this.extrusions(positions, normals, last, out, this.thickness);
// last = capEnd;
} else {
@ -681,6 +697,7 @@ export default class ExtrudePolyline {
this.thickness,
cur[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
positions.push(
cur[0],
cur[1],
@ -689,6 +706,8 @@ export default class ExtrudePolyline {
this.thickness,
cur[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
this.currentIndex++;
} else {
this.extrusions(
positions,
@ -750,6 +769,7 @@ export default class ExtrudePolyline {
-this.thickness * flip,
cur[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
positions.push(
cur[0],
cur[1],
@ -758,6 +778,8 @@ export default class ExtrudePolyline {
this.thickness * flip,
cur[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
this.currentIndex++;
indices.push(
...(this.lastFlip !== -flip
? [index, index + 2, index + 3]
@ -778,6 +800,8 @@ export default class ExtrudePolyline {
-this.thickness * flip,
cur[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
this.currentIndex++;
count += 3;
} else {
this.extrusions(
@ -822,6 +846,7 @@ export default class ExtrudePolyline {
-thickness,
point[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
positions.push(
point[0],
point[1],
@ -830,6 +855,8 @@ export default class ExtrudePolyline {
thickness,
point[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
this.currentIndex++;
}
private lineSegmentDistance(b1: vec3, a1: vec3) {
const dx = a1[0] - b1[0];

View File

@ -54,12 +54,21 @@ export default function json(data: IJsonData, cfg: IParserCfg): IParserData {
}
// TODO: 提供默认数据和解析器
export const defaultData = [];
export const defaultData = [
{
lng1: 100,
lat1: 30.0,
lng2: 130,
lat2: 30,
},
];
export const defaultParser = {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
};

View File

@ -251,6 +251,11 @@ export function bBoxToBounds(b1: BBox): IBounds {
];
}
export function normalize(v: Point) {
const len = calDistance(v, [0, 0]);
return [v[0] / len, v[1] / len];
}
export function calDistance(p1: Point, p2: Point) {
return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2));
}

View File

@ -0,0 +1,102 @@
import { GeometryLayer, Scene, LineLayer } from '@antv/l7';
import { GaodeMap, GaodeMapV2, Mapbox } from '@antv/l7-maps';
import * as React from 'react';
export default class Demo extends React.Component {
// @ts-ignore
private scene: Scene;
public componentWillUnmount() {
this.scene.destroy();
}
public async componentDidMount() {
const scene = new Scene({
id: 'map',
map: new GaodeMap({
// map: new GaodeMapV2({
// map: new Mapbox({
pitch: 0,
// style: 'dark',
center: [120, 30],
zoom: 3,
}),
});
this.scene = scene;
const layer = new LineLayer()
// .source([
// {
// lng1: 100,
// lat1: 30.0,
// lng2: 105,
// lat2: 30,
// },
// {
// lng1: 105,
// lat1: 30.0,
// lng2: 130,
// lat2: 30,
// },
// ], {
// parser: {
// type: 'json',
// x: 'lng1',
// y: 'lat1',
// x1: 'lng2',
// y1: 'lat2',
// }
// })
.source({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [
[100, 30],
[120, 30],
[120, 25],
[125, 25],
],
},
},
],
})
.shape('line')
.size(10)
// .color('lng1', ['#f00', '#ff0'])
.color('#f00')
.style({
opacity: 0.3,
arrow: {
enable: true,
arrowWidth: 2,
arrowHeight: 3,
},
});
scene.on('loaded', () => {
scene.addLayer(layer);
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -8,6 +8,7 @@ import CanvasDemo from './components/canvas';
import Plane from './components/plane';
import PlaneTerrain from './components/planeTerrain';
import Cursor from './components/cursor';
import Arrow from './components/arrow';
storiesOf('Object', module)
.add('water', () => <Water />)
@ -17,4 +18,5 @@ storiesOf('Object', module)
.add('CanvasDemo', () => <CanvasDemo/>)
.add('Plane', () => <Plane/>)
.add('PlaneTerrain', () => <PlaneTerrain/>)
.add('Cursor', () => <Cursor/>)
.add('Cursor', () => <Cursor/>)
.add('Arrow', () => <Arrow/>)