mirror of https://gitee.com/antv-l7/antv-l7
feat: 新增支持多渐变色多线图层 (#1086)
* chore: update version 2.8.30 -> 2.8.31 * feat: 新增多渐变色线图层 * style: lint style
This commit is contained in:
parent
c40518677d
commit
061935a1ba
|
@ -43,6 +43,8 @@ export interface ILineLayerStyleOptions {
|
|||
maskInside?: boolean; // 可选参数 控制图层是否显示在蒙层的内部
|
||||
|
||||
arrow?: ILineArrow;
|
||||
|
||||
rampColors?: IColorRamp;
|
||||
}
|
||||
|
||||
export interface IPointLayerStyleOptions {
|
||||
|
|
|
@ -29,6 +29,7 @@ export default class LineLayer extends BaseLayer<ILineLayerStyleOptions> {
|
|||
const type = this.getModelType();
|
||||
const defaultConfig = {
|
||||
line: {},
|
||||
linearline: {},
|
||||
simple: {},
|
||||
wall: {},
|
||||
arc3d: { blend: 'additive' },
|
||||
|
|
|
@ -3,6 +3,7 @@ import Arc3DModel from './arc_3d';
|
|||
import ArcMiniModel from './arcmini';
|
||||
import GreatCircleModel from './great_circle';
|
||||
import LineModel from './line';
|
||||
import LinearLine from './linearline';
|
||||
import SimpleLineModel from './simpleLine';
|
||||
import LineWallModel from './wall';
|
||||
|
||||
|
@ -13,7 +14,8 @@ export type LineModelType =
|
|||
| 'greatcircle'
|
||||
| 'wall'
|
||||
| 'simple'
|
||||
| 'line';
|
||||
| 'line'
|
||||
| 'linearline';
|
||||
|
||||
const LineModels: { [key in LineModelType]: any } = {
|
||||
arc: ArcModel,
|
||||
|
@ -23,6 +25,7 @@ const LineModels: { [key in LineModelType]: any } = {
|
|||
wall: LineWallModel,
|
||||
line: LineModel,
|
||||
simple: SimpleLineModel,
|
||||
linearline: LinearLine,
|
||||
};
|
||||
|
||||
export default LineModels;
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
import {
|
||||
AttributeType,
|
||||
gl,
|
||||
IAnimateOption,
|
||||
IEncodeFeature,
|
||||
IImage,
|
||||
ILayerConfig,
|
||||
IModel,
|
||||
IModelUniform,
|
||||
ITexture2D,
|
||||
} from '@antv/l7-core';
|
||||
|
||||
import { generateColorRamp, getMask, IColorRamp } from '@antv/l7-utils';
|
||||
import { isNumber } from 'lodash';
|
||||
import BaseModel from '../../core/BaseModel';
|
||||
import { ILineLayerStyleOptions } from '../../core/interface';
|
||||
import { LineTriangulation } from '../../core/triangulation';
|
||||
import linear_line_frag from '../shaders/linearLine/line_linear_frag.glsl';
|
||||
import linear_line_vert from '../shaders/linearLine/line_linear_vert.glsl';
|
||||
|
||||
export default class LinearLineModel extends BaseModel {
|
||||
protected colorTexture: ITexture2D;
|
||||
public getUninforms(): IModelUniform {
|
||||
const {
|
||||
opacity,
|
||||
vertexHeightScale = 20.0,
|
||||
raisingHeight = 0,
|
||||
heightfixed = false,
|
||||
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
|
||||
|
||||
if (this.rendererService.getDirty()) {
|
||||
this.colorTexture.bind();
|
||||
}
|
||||
|
||||
if (this.dataTextureTest && this.dataTextureNeedUpdate({ opacity })) {
|
||||
this.judgeStyleAttributes({ opacity });
|
||||
const encodeData = this.layer.getEncodedData();
|
||||
const { data, width, height } = this.calDataFrame(
|
||||
this.cellLength,
|
||||
encodeData,
|
||||
this.cellProperties,
|
||||
);
|
||||
this.rowCount = height; // 当前数据纹理有多少行
|
||||
|
||||
this.dataTexture =
|
||||
this.cellLength > 0 && data.length > 0
|
||||
? this.createTexture2D({
|
||||
flipY: true,
|
||||
data,
|
||||
format: gl.LUMINANCE,
|
||||
type: gl.FLOAT,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
: this.createTexture2D({
|
||||
flipY: true,
|
||||
data: [1],
|
||||
format: gl.LUMINANCE,
|
||||
type: gl.FLOAT,
|
||||
width: 1,
|
||||
height: 1,
|
||||
});
|
||||
}
|
||||
return {
|
||||
u_dataTexture: this.dataTexture, // 数据纹理 - 有数据映射的时候纹理中带数据,若没有任何数据映射时纹理是 [1]
|
||||
u_cellTypeLayout: this.getCellTypeLayout(),
|
||||
|
||||
u_opacity: isNumber(opacity) ? opacity : 1.0,
|
||||
// 纹理支持参数
|
||||
u_texture: this.colorTexture, // 贴图
|
||||
|
||||
// 是否固定高度
|
||||
u_heightfixed: Number(heightfixed),
|
||||
|
||||
// 顶点高度 scale
|
||||
u_vertexScale: vertexHeightScale,
|
||||
u_raisingHeight: Number(raisingHeight),
|
||||
};
|
||||
}
|
||||
|
||||
public initModels(): IModel[] {
|
||||
this.updateTexture();
|
||||
return this.buildModels();
|
||||
}
|
||||
|
||||
public clearModels() {
|
||||
this.colorTexture?.destroy();
|
||||
this.dataTexture?.destroy();
|
||||
}
|
||||
|
||||
public buildModels(): IModel[] {
|
||||
const {
|
||||
mask = false,
|
||||
maskInside = true,
|
||||
depth = false,
|
||||
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
|
||||
const { frag, vert, type } = this.getShaders();
|
||||
this.layer.triangulation = LineTriangulation;
|
||||
return [
|
||||
this.layer.buildLayerModel({
|
||||
moduleName: 'line_' + type,
|
||||
vertexShader: vert,
|
||||
fragmentShader: frag,
|
||||
triangulation: LineTriangulation,
|
||||
primitive: gl.TRIANGLES,
|
||||
blend: this.getBlend(),
|
||||
depth: { enable: depth },
|
||||
stencil: getMask(mask, maskInside),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据参数获取不同的 shader 代码
|
||||
* @returns
|
||||
*/
|
||||
public getShaders(): { frag: string; vert: string; type: string } {
|
||||
return {
|
||||
frag: linear_line_frag,
|
||||
vert: linear_line_vert,
|
||||
type: 'linear_rampColors',
|
||||
};
|
||||
}
|
||||
|
||||
protected registerBuiltinAttributes() {
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
name: 'distanceAndIndex',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_DistanceAndIndex',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.STATIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 2,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
normal: number[],
|
||||
vertexIndex?: number,
|
||||
) => {
|
||||
return vertexIndex === undefined
|
||||
? [vertex[3], 10]
|
||||
: [vertex[3], vertexIndex];
|
||||
},
|
||||
},
|
||||
});
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
name: 'total_distance',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Total_Distance',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.STATIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 1,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
return [vertex[5]];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
name: 'size',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Size',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.DYNAMIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 2,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
const { size = 1 } = feature;
|
||||
return Array.isArray(size) ? [size[0], size[1]] : [size as number, 0];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// point layer size;
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
name: 'normal',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Normal',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.STATIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 3,
|
||||
// @ts-ignore
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
normal: number[],
|
||||
) => {
|
||||
return normal;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
name: 'miter',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Miter',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.STATIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 1,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
return [vertex[4]];
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private updateTexture = () => {
|
||||
const { createTexture2D } = this.rendererService;
|
||||
if (this.colorTexture) {
|
||||
this.colorTexture.destroy();
|
||||
}
|
||||
const {
|
||||
rampColors,
|
||||
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
|
||||
const imageData = generateColorRamp(rampColors as IColorRamp);
|
||||
this.colorTexture = createTexture2D({
|
||||
data: new Uint8Array(imageData.data),
|
||||
width: imageData.width,
|
||||
height: imageData.height,
|
||||
wrapS: gl.CLAMP_TO_EDGE,
|
||||
wrapT: gl.CLAMP_TO_EDGE,
|
||||
min: gl.NEAREST,
|
||||
mag: gl.NEAREST,
|
||||
flipY: false,
|
||||
});
|
||||
};
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
uniform float u_opacity : 1.0;
|
||||
uniform sampler2D u_texture;
|
||||
|
||||
#pragma include "picking"
|
||||
|
||||
varying mat4 styleMappingMat;
|
||||
|
||||
void main() {
|
||||
float opacity = styleMappingMat[0][0];
|
||||
float d_distance_ratio = styleMappingMat[3].r; // 当前点位距离占线总长的比例
|
||||
|
||||
gl_FragColor = texture2D(u_texture, vec2(d_distance_ratio, 0.5));
|
||||
|
||||
gl_FragColor.a *= opacity; // 全局透明度
|
||||
gl_FragColor = filterColor(gl_FragColor);
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
|
||||
attribute float a_Miter;
|
||||
attribute vec2 a_Size;
|
||||
attribute vec3 a_Normal;
|
||||
attribute vec3 a_Position;
|
||||
|
||||
// dash line
|
||||
attribute float a_Total_Distance;
|
||||
attribute vec2 a_DistanceAndIndex;
|
||||
|
||||
uniform mat4 u_ModelMatrix;
|
||||
uniform mat4 u_Mvp;
|
||||
|
||||
uniform float u_heightfixed: 0.0;
|
||||
uniform float u_vertexScale: 1.0;
|
||||
uniform float u_raisingHeight: 0.0;
|
||||
|
||||
uniform float u_opacity: 1.0;
|
||||
|
||||
#pragma include "projection"
|
||||
#pragma include "picking"
|
||||
|
||||
varying mat4 styleMappingMat; // 用于将在顶点着色器中计算好的样式值传递给片元
|
||||
|
||||
#pragma include "styleMapping"
|
||||
#pragma include "styleMappingCalOpacity"
|
||||
|
||||
void main() {
|
||||
// cal style mapping - 数据纹理映射部分的计算
|
||||
styleMappingMat = mat4(
|
||||
0.0, 0.0, 0.0, 0.0, // opacity - strokeOpacity - strokeWidth - empty
|
||||
0.0, 0.0, 0.0, 0.0, // strokeR - strokeG - strokeB - strokeA
|
||||
0.0, 0.0, 0.0, 0.0, // offsets[0] - offsets[1]
|
||||
0.0, 0.0, 0.0, 0.0 // distance_ratio/distance/pixelLen/texV
|
||||
);
|
||||
|
||||
float rowCount = u_cellTypeLayout[0][0]; // 当前的数据纹理有几行
|
||||
float columnCount = u_cellTypeLayout[0][1]; // 当看到数据纹理有几列
|
||||
float columnWidth = 1.0/columnCount; // 列宽
|
||||
float rowHeight = 1.0/rowCount; // 行高
|
||||
float cellCount = calCellCount(); // opacity - strokeOpacity - strokeWidth - stroke - offsets
|
||||
float id = a_vertexId; // 第n个顶点
|
||||
float cellCurrentRow = floor(id * cellCount / columnCount) + 1.0; // 起始点在第几行
|
||||
float cellCurrentColumn = mod(id * cellCount, columnCount) + 1.0; // 起始点在第几列
|
||||
|
||||
// cell 固定顺序 opacity -> strokeOpacity -> strokeWidth -> stroke ...
|
||||
// 按顺序从 cell 中取值、若没有则自动往下取值
|
||||
float textureOffset = 0.0; // 在 cell 中取值的偏移量
|
||||
|
||||
vec2 opacityAndOffset = calOpacityAndOffset(cellCurrentRow, cellCurrentColumn, columnCount, textureOffset, columnWidth, rowHeight);
|
||||
styleMappingMat[0][0] = opacityAndOffset.r;
|
||||
textureOffset = opacityAndOffset.g;
|
||||
// cal style mapping - 数据纹理映射部分的计算
|
||||
|
||||
vec3 size = a_Miter * setPickingSize(a_Size.x) * reverse_offset_normal(a_Normal);
|
||||
|
||||
vec2 offset = project_pixel(size.xy);
|
||||
|
||||
float lineDistance = a_DistanceAndIndex.x;
|
||||
float currentLinePointRatio = lineDistance / a_Total_Distance;
|
||||
|
||||
|
||||
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] = currentLinePointRatio; // 当前点位距离占线总长的比例
|
||||
|
||||
vec4 project_pos = project_position(vec4(a_Position.xy, 0, 1.0));
|
||||
|
||||
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, a_Size.y, 1.0));
|
||||
|
||||
float h = float(a_Position.z) * u_vertexScale; // 线顶点的高度 - 兼容不存在第三个数值的情况 vertex height
|
||||
float lineHeight = a_Size.y; // size 第二个参数代表的高度 [linewidth, lineheight]
|
||||
|
||||
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
|
||||
lineHeight *= 0.2; // 保持和 amap/mapbox 一致的效果
|
||||
h *= 0.2;
|
||||
if(u_heightfixed < 1.0) {
|
||||
lineHeight = project_pixel(a_Size.y);
|
||||
}
|
||||
gl_Position = u_Mvp * (vec4(project_pos.xy + offset, lineHeight + h + u_raisingHeight, 1.0));
|
||||
} else {
|
||||
// mapbox - amap
|
||||
|
||||
// 兼容 mapbox 在线高度上的效果表现基本一致
|
||||
if(u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT || u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET) {
|
||||
// mapbox
|
||||
// 保持高度相对不变
|
||||
float mapboxZoomScale = 4.0/pow(2.0, 21.0 - u_Zoom);
|
||||
h *= mapboxZoomScale;
|
||||
h += u_raisingHeight * mapboxZoomScale;
|
||||
if(u_heightfixed > 0.0) {
|
||||
lineHeight *= mapboxZoomScale;
|
||||
}
|
||||
|
||||
} else {
|
||||
// amap
|
||||
h += u_raisingHeight;
|
||||
// lineHeight 顶点偏移高度
|
||||
if(u_heightfixed < 1.0) {
|
||||
lineHeight *= pow(2.0, 20.0 - u_Zoom);
|
||||
}
|
||||
}
|
||||
|
||||
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, lineHeight + h, 1.0));
|
||||
}
|
||||
|
||||
setPickingColor(a_PickingColor);
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-ignore
|
||||
import { LineLayer, Scene } from '@antv/l7';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import * as React from 'react';
|
||||
|
@ -14,10 +15,8 @@ export default class Amap2demo_lineLinear extends React.Component {
|
|||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [120.19382669582967, 30.258134],
|
||||
pitch: 0,
|
||||
zoom: 6,
|
||||
viewMode: '3D',
|
||||
center: [105, 30.258134],
|
||||
zoom: 5,
|
||||
}),
|
||||
});
|
||||
this.scene = scene;
|
||||
|
@ -33,18 +32,21 @@ export default class Amap2demo_lineLinear extends React.Component {
|
|||
type: 'Polygon',
|
||||
coordinates: [
|
||||
[
|
||||
[113.8623046875, 30.031055426540206],
|
||||
[116.3232421875, 30.031055426540206],
|
||||
[116.3232421875, 31.090574094954192],
|
||||
[113.8623046875, 31.090574094954192],
|
||||
[113.8623046875, 30.031055426540206],
|
||||
[99.228515625, 37.43997405227057],
|
||||
[99.228515625, 35.02999636902566],
|
||||
[101.337890625, 32.99023555965106],
|
||||
[99.052734375, 30.29701788337205],
|
||||
[100.72265625, 27.994401411046148],
|
||||
[99.49218749999999, 26.352497858154024],
|
||||
[100.634765625, 23.725011735951796],
|
||||
],
|
||||
[
|
||||
[117.26806640625, 32.13840869677249],
|
||||
[118.36669921875, 32.13840869677249],
|
||||
[118.36669921875, 32.47269502206151],
|
||||
[117.26806640625, 32.47269502206151],
|
||||
[117.26806640625, 32.13840869677249],
|
||||
[108.544921875, 37.71859032558816],
|
||||
[110.74218749999999, 34.66935854524543],
|
||||
[110.21484375, 32.76880048488168],
|
||||
[112.412109375, 32.84267363195431],
|
||||
[112.1484375, 30.751277776257812],
|
||||
[114.08203125, 31.42866311735861],
|
||||
],
|
||||
],
|
||||
},
|
||||
|
@ -53,38 +55,36 @@ export default class Amap2demo_lineLinear extends React.Component {
|
|||
};
|
||||
|
||||
scene.on('loaded', () => {
|
||||
fetch(
|
||||
// 'https://gw.alipayobjects.com/os/basement_prod/40ef2173-df66-4154-a8c0-785e93a5f18e.json',
|
||||
'https://gw.alipayobjects.com/os/bmw-prod/459591a6-8aa5-4ce7-87a1-0e1e7bce3e60.json',
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
// @ts-ignore
|
||||
const layer = new LineLayer({})
|
||||
// .source(data)
|
||||
.source(geoData)
|
||||
.size(5)
|
||||
.shape('line')
|
||||
.color('#25d8b7')
|
||||
// .animate({
|
||||
// interval: 1, // 间隔
|
||||
// duration: 1, // 持续时间,延时
|
||||
// trailLength: 2, // 流线长度
|
||||
// })
|
||||
.style({
|
||||
opacity: 'testOpacity',
|
||||
// opacity: 0,
|
||||
// lineTexture: true, // 开启线的贴图功能
|
||||
// iconStep: 50, // 设置贴图纹理的间距
|
||||
// lineType: 'dash',
|
||||
// dashArray: [5, 5],
|
||||
// textureBlend: 'replace',
|
||||
// textureBlend: 'normal',
|
||||
sourceColor: '#f00',
|
||||
targetColor: '#0f0',
|
||||
});
|
||||
scene.addLayer(layer);
|
||||
// const layer = new LineLayer({})
|
||||
// .source(geoData)
|
||||
// .size(5)
|
||||
// .shape('line')
|
||||
// .color('#25d8b7')
|
||||
// .style({
|
||||
// sourceColor: '#f00',
|
||||
// targetColor: '#0f0',
|
||||
// });
|
||||
// scene.addLayer(layer);
|
||||
const layer = new LineLayer({})
|
||||
.source(geoData)
|
||||
.size(5)
|
||||
.shape('linearline')
|
||||
.style({
|
||||
rampColors: {
|
||||
colors: [
|
||||
'#FF4818',
|
||||
'#F7B74A',
|
||||
'#FFF598',
|
||||
'#91EABC',
|
||||
'#2EA9A1',
|
||||
'#206C7C',
|
||||
],
|
||||
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
// colors: [ '#f00', '#0f0', '#ff0' ],
|
||||
// positions: [0, 0.1, 1.0]
|
||||
},
|
||||
});
|
||||
scene.addLayer(layer);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue