feat: 修复bug,新增图片瓦片的像素映射 (#1257)

* feat: 弧线图层/大圆弧线图层 虚线效果优化

* style: lint style

* fix: 修复 simpleLine linear 失效

* style: lint style

* feat: 优化 simple line 网格构建

* style: lint style

* style: lint style

* style: lint style

* feat: 新增图片图层的像素映射

* feat: 图片瓦片图层支持像素的颜色映射

* style: lint style
This commit is contained in:
YiQianYao 2022-07-28 19:05:51 +08:00 committed by GitHub
parent 3015e05b42
commit 043d30ecfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 954 additions and 1556 deletions

View File

@ -21,8 +21,9 @@ addParameters({
});
// automatically import all files ending in *.stories.tsx
const req = require.context('../stories', true, /\.stories\.tsx$/);
// const req = require.context('../stories', true, /\.stories\.tsx$/);
// const req = require.context('../stories/Map', true, /\.stories\.tsx$/);
const req = require.context('../stories/tile', true, /\.stories\.tsx$/);
function loadStories() {
req.keys().forEach(req);

View File

@ -152,6 +152,12 @@ export interface ISubLayerInitOptions {
// 在初始化的时候使用
rampColorsData?: ImageData | IImagedata;
pixelConstant?: number;
pixelConstantR?: number;
pixelConstantG?: number;
pixelConstantB?: number;
pixelConstantRGB?: number;
coords?: string;
sourceLayer?: string;
featureId?: string;

View File

@ -158,12 +158,27 @@ export interface IWindLayerStyleOptions {
[key: number]: string;
};
sizeScale?: number;
mask?: boolean;
maskInside?: boolean;
}
export interface IImageLayerStyleOptions {
opacity: number;
mask?: boolean;
maskInside?: boolean;
domain?: [number, number];
noDataValue?: number;
clampLow?: boolean;
clampHigh?: boolean;
rampColors?: IColorRamp;
rampColorsData?: ImageData | IImagedata;
pixelConstant?: number;
pixelConstantR?: number;
pixelConstantG?: number;
pixelConstantB?: number;
pixelConstantRGB?: number;
}
export interface IGeometryLayerStyleOptions {

View File

@ -15,6 +15,7 @@ import {
calculateCentroid,
calculatePointsCenterAndRadius,
} from '../utils/geo';
import SimpleLine from '../utils/simpleLine';
import extrudePolygon, {
extrude_PolygonNormal,
fillPolygon,
@ -145,32 +146,9 @@ export function LineTriangulation(feature: IEncodeFeature) {
}
export function SimpleLineTriangulation(feature: IEncodeFeature) {
const { coordinates, originCoordinates, version } = feature;
const { coordinates } = feature;
const line = new ExtrudePolyline({
dash: true,
join: 'bevel',
});
if (version === 'GAODE2.x') {
// 处理高德2.0几何体构建
let path1 = coordinates as number[][][] | number[][]; // 计算位置
if (!Array.isArray(path1[0][0])) {
path1 = [coordinates] as number[][][];
}
let path2 = originCoordinates as number[][][] | number[][]; // 计算法线
if (!Array.isArray(path2[0][0])) {
path2 = [originCoordinates] as number[][][];
}
for (let i = 0; i < path1.length; i++) {
// 高德2.0在计算线时,需要使用经纬度计算发现,使用 customCoords.lnglatToCoords 计算的数据来计算顶点的位置
const item1 = path1[i];
const item2 = path2[i];
line.simpleExtrude_gaode2(item1 as number[][], item2 as number[][]);
}
} else {
// 处理非高德2.0的几何体构建
const line = new SimpleLine();
let path = coordinates as number[][][] | number[][];
if (path[0] && !Array.isArray(path[0][0])) {
path = [coordinates] as number[][][];
@ -178,13 +156,11 @@ export function SimpleLineTriangulation(feature: IEncodeFeature) {
path.forEach((item: any) => {
line.simpleExtrude(item as number[][]);
});
}
const linebuffer = line.complex;
return {
vertices: linebuffer.positions, // [ x,y,z, distance, miter,total ]
vertices: linebuffer.positions, // [ x,y,z, distance, miter, total ]
indices: linebuffer.indices,
normals: linebuffer.normals,
size: 6,
};
}

View File

@ -26,11 +26,22 @@ export default class ImageLayer extends BaseLayer<IImageLayerStyleOptions> {
const type = this.getModelType();
const defaultConfig = {
image: {},
dataImage: {},
};
return defaultConfig[type];
}
protected getModelType(): ImageModelType {
const shapeAttribute = this.styleAttributeService.getLayerStyleAttribute(
'shape',
);
const shape = shapeAttribute?.scale?.field as ImageModelType;
if (shape === 'dataImage') {
return 'dataImage';
} else if (shape === 'image') {
return 'image';
} else {
return 'image';
}
}
}

View File

@ -0,0 +1,186 @@
import {
AttributeType,
gl,
IEncodeFeature,
IModelUniform,
ITexture2D,
} from '@antv/l7-core';
import { generateColorRamp, getMask, IColorRamp, isMini } from '@antv/l7-utils';
import { isEqual } from 'lodash';
import BaseModel from '../../core/BaseModel';
import { IImageLayerStyleOptions } from '../../core/interface';
import { RasterImageTriangulation } from '../../core/triangulation';
import ImageFrag from '../shaders/dataImage_frag.glsl';
import ImageVert from '../shaders/image_vert.glsl';
export default class ImageDataModel extends BaseModel {
protected texture: ITexture2D;
protected colorTexture: ITexture2D;
private rampColors: any;
public getUninforms(): IModelUniform {
const {
opacity,
clampLow = true,
clampHigh = true,
noDataValue = -9999999,
domain = [0, 1],
rampColors,
pixelConstant = 0.0,
pixelConstantR = 256 * 256,
pixelConstantG = 256,
pixelConstantB = 1,
pixelConstantRGB = 0.1,
} = this.layer.getLayerConfig() as IImageLayerStyleOptions;
if (!isEqual(this.rampColors, rampColors)) {
this.updateColorTexure();
this.rampColors = rampColors;
}
return {
u_opacity: opacity || 1,
u_texture: this.texture,
u_pixelConstant: pixelConstant,
u_pixelConstantR: pixelConstantR,
u_pixelConstantG: pixelConstantG,
u_pixelConstantB: pixelConstantB,
u_pixelConstantRGB: pixelConstantRGB,
u_domain: domain,
u_clampLow: clampLow,
u_clampHigh: typeof clampHigh !== 'undefined' ? clampHigh : clampLow,
u_noDataValue: noDataValue,
u_colorTexture: this.colorTexture,
};
}
public initModels() {
const {
mask = false,
maskInside = true,
rampColorsData,
rampColors,
} = this.layer.getLayerConfig() as IImageLayerStyleOptions;
const source = this.layer.getSource();
const { createTexture2D } = this.rendererService;
this.texture = createTexture2D({
height: 0,
width: 0,
});
if (isMini) {
// @ts-ignore
const canvas = this.layerService.sceneService.getSceneConfig().canvas;
const img = canvas.createImage();
img.crossOrigin = 'anonymous';
img.src = source.data.originData;
img.onload = () => {
this.texture = createTexture2D({
data: img,
width: img.width,
height: img.height,
});
this.layerService.updateLayerRenderList();
this.layerService.renderLayers();
};
} else {
source.data.images.then(
(imageData: Array<HTMLImageElement | ImageBitmap>) => {
this.texture = createTexture2D({
data: imageData[0],
width: imageData[0].width,
height: imageData[0].height,
});
this.layerService.updateLayerRenderList();
this.layerService.renderLayers();
},
);
}
const rampImageData = rampColorsData
? rampColorsData
: generateColorRamp(rampColors as IColorRamp);
this.colorTexture = createTexture2D({
data: rampImageData.data,
width: rampImageData.width,
height: rampImageData.height,
flipY: false,
});
return [
this.layer.buildLayerModel({
moduleName: 'RasterImage',
vertexShader: ImageVert,
fragmentShader: ImageFrag,
triangulation: RasterImageTriangulation,
primitive: gl.TRIANGLES,
depth: { enable: false },
blend: this.getBlend(),
stencil: getMask(mask, maskInside),
}),
];
}
public clearModels(): void {
this.texture?.destroy();
this.colorTexture?.destroy();
}
public buildModels() {
return this.initModels();
}
protected getConfigSchema() {
return {
properties: {
opacity: {
type: 'number',
minimum: 0,
maximum: 1,
},
},
};
}
protected registerBuiltinAttributes() {
// point layer size;
this.styleAttributeService.registerStyleAttribute({
name: 'uv',
type: AttributeType.Attribute,
descriptor: {
name: 'a_Uv',
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,
) => {
return [vertex[3], vertex[4]];
},
},
});
}
private updateColorTexure() {
const { createTexture2D } = this.rendererService;
const {
rampColors,
} = this.layer.getLayerConfig() as IImageLayerStyleOptions;
const imageData = generateColorRamp(rampColors as IColorRamp);
this.colorTexture = createTexture2D({
data: imageData.data,
width: imageData.width,
height: imageData.height,
flipY: false,
});
}
}

View File

@ -77,6 +77,11 @@ export default class ImageModel extends BaseModel {
}),
];
}
public clearModels(): void {
this.texture?.destroy();
}
public buildModels() {
return this.initModels();
}

View File

@ -1,8 +1,10 @@
import DataImageModel from './dataImage';
import ImageModel from './image';
export type ImageModelType = 'image';
export type ImageModelType = 'image' | 'dataImage';
const ImageModels: { [key in ImageModelType]: any } = {
image: ImageModel,
dataImage: DataImageModel,
};
export default ImageModels;

View File

@ -0,0 +1,38 @@
precision mediump float;
uniform float u_opacity: 1.0;
uniform sampler2D u_texture;
uniform sampler2D u_colorTexture;
varying vec2 v_texCoord;
uniform vec2 u_domain;
uniform float u_noDataValue;
uniform bool u_clampLow: true;
uniform bool u_clampHigh: true;
uniform float u_pixelConstant;
uniform float u_pixelConstantR;
uniform float u_pixelConstantG;
uniform float u_pixelConstantB;
uniform float u_pixelConstantRGB;
void main() {
vec4 baseColor = texture2D(u_texture, vec2(v_texCoord.x, v_texCoord.y));
float r = baseColor.r;
float g = baseColor.g;
float b = baseColor.b;
float value = u_pixelConstant + ((r * u_pixelConstantR + g * u_pixelConstantG + b * u_pixelConstantB) * u_pixelConstantRGB);
if (value == u_noDataValue) {
gl_FragColor = vec4(0.0, 0, 0, 0.0);
} else if ((!u_clampLow && value < u_domain[0]) || (!u_clampHigh && value > u_domain[1])) {
gl_FragColor = vec4(0, 0, 0, 0);
} else {
float normalisedValue =(value - u_domain[0]) / (u_domain[1] - u_domain[0]);
vec4 color = texture2D(u_colorTexture, vec2(normalisedValue, 0));
gl_FragColor = color;
gl_FragColor.a = gl_FragColor.a * u_opacity ;
}
}

View File

@ -99,6 +99,7 @@ export default class ArcModel extends BaseModel {
u_cellTypeLayout: this.getCellTypeLayout(),
u_thetaOffset: isNumber(thetaOffset) ? thetaOffset : 0.0,
// u_thetaOffset: 0.0,
u_opacity: isNumber(opacity) ? opacity : 1.0,
u_textureBlend: textureBlend === 'normal' ? 0.0 : 1.0,
segmentNumber,
@ -180,6 +181,7 @@ export default class ArcModel extends BaseModel {
const { frag, vert, type } = this.getShaders();
return [
this.layer.buildLayerModel({
// primitive: gl.POINTS,
moduleName: 'arc2dline' + type,
vertexShader: vert,
fragmentShader: frag,

View File

@ -1,9 +1,7 @@
import {
AttributeType,
gl,
IAnimateOption,
IEncodeFeature,
ILayerConfig,
IModel,
IModelUniform,
} from '@antv/l7-core';
@ -12,10 +10,10 @@ import { isNumber } from 'lodash';
import BaseModel from '../../core/BaseModel';
import { ILineLayerStyleOptions } from '../../core/interface';
import { SimpleLineTriangulation } from '../../core/triangulation';
import simple_line_frag from '../shaders/simple/simpleline_frag.glsl';
// linear simple line shader
import simle_linear_frag from '../shaders/linear/simpleline_linear_frag.glsl';
import simple_line_frag from '../shaders/simpleline_frag.glsl';
import simple_line_vert from '../shaders/simpleline_vert.glsl';
import simle_linear_frag from '../shaders/simple/simpleline_linear_frag.glsl';
import simple_line_vert from '../shaders/simple/simpleline_vert.glsl';
export default class SimpleLineModel extends BaseModel {
public getUninforms(): IModelUniform {
const {
@ -79,13 +77,6 @@ export default class SimpleLineModel extends BaseModel {
u_vertexScale: vertexHeightScale,
};
}
public getAnimateUniforms(): IModelUniform {
const { animateOption } = this.layer.getLayerConfig() as ILayerConfig;
return {
u_aimate: this.animateOption2Array(animateOption as IAnimateOption),
u_time: this.layer.getLayerAnimateTime(),
};
}
public initModels(): IModel[] {
return this.buildModels();
@ -205,54 +196,5 @@ export default class SimpleLineModel extends BaseModel {
},
},
});
// 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]];
},
},
});
}
}

View File

@ -48,7 +48,7 @@ vec2 midPoint(vec2 source, vec2 target, float arcThetaOffset) {
// return mid;
}
float getSegmentRatio(float index) {
return smoothstep(0.0, 1.0, index / (segmentNumber - 1.));
return index / (segmentNumber - 1.);
}
vec2 interpolate (vec2 source, vec2 target, float t, float arcThetaOffset) {
// if the angularDist is PI, linear interpolation is applied. otherwise, use spherical interpolation
@ -123,9 +123,7 @@ void main() {
t = unProjCustomCoord(target);
}
float total_Distance = pixelDistance(s, t) / 2.0 * PI;
v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / (total_Distance / segmentNumber * segmentIndex);
v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / total_Distance;
styleMappingMat[3].b = segmentIndex / segmentNumber;
@ -145,5 +143,6 @@ void main() {
} else {
gl_Position = project_common_position_to_clipspace(vec4(curr.xy + offset, 0, 1.0));
}
gl_PointSize = 5.0;
setPickingColor(a_PickingColor);
}

View File

@ -18,14 +18,14 @@ void main() {
float d_distance_ratio = styleMappingMat[3].r; // 当前点位距离占线总长的比例
gl_FragColor = v_color;
gl_FragColor.a *= opacity; // 全局透明度
// dash line
float flag = 0.;
float dashLength = mod(d_distance_ratio, v_dash_array.x + v_dash_array.y + v_dash_array.z + v_dash_array.w);
if(dashLength < v_dash_array.x || (dashLength > (v_dash_array.x + v_dash_array.y) && dashLength < v_dash_array.x + v_dash_array.y + v_dash_array.z)) {
flag = 1.;
// 实线部分
} else {
// 虚线部分
discard
}
gl_FragColor.a *=flag;
gl_FragColor = filterColor(gl_FragColor);
}

View File

@ -48,18 +48,19 @@ void main() {
// float blur = smoothstep(1.0, u_blur, length(v_normal.xy));
gl_FragColor.a *= opacity;
if(u_line_type == LineTypeDash) {
float flag = 0.;
float dashLength = mod(v_distance_ratio, v_dash_array.x + v_dash_array.y + v_dash_array.z + v_dash_array.w);
if(dashLength < v_dash_array.x || (dashLength > (v_dash_array.x + v_dash_array.y) && dashLength < v_dash_array.x + v_dash_array.y + v_dash_array.z)) {
flag = 1.;
// 实线部分
} else {
// 虚线部分
discard;
}
gl_FragColor.a *=flag;
}
// 设置弧线的动画模式
if(u_aimate.x == Animate) {
animateSpeed = u_time / u_aimate.y;
float alpha =1.0 - fract( mod(1.0- smoothstep(0.0, 1.0, v_distance_ratio), u_aimate.z)* (1.0/ u_aimate.z) + u_time / u_aimate.y);
float alpha =1.0 - fract( mod(1.0- v_distance_ratio, u_aimate.z)* (1.0/ u_aimate.z) + u_time / u_aimate.y);
alpha = (alpha + u_aimate.w -1.0) / u_aimate.w;
alpha = smoothstep(0., 1., alpha);
gl_FragColor.a *= alpha;

View File

@ -40,7 +40,7 @@ float maps (float value, float start1, float stop1, float start2, float stop2) {
}
float getSegmentRatio(float index) {
return smoothstep(0.0, 1.0, index / (segmentNumber - 1.));
return index / (segmentNumber - 1.);
}
float paraboloid(vec2 source, vec2 target, float ratio) {
@ -157,6 +157,7 @@ void main() {
float segmentIndex = a_Position.x;
float segmentRatio = getSegmentRatio(segmentIndex);
float indexDir = mix(-1.0, 1.0, step(segmentIndex, 0.0));
if(u_line_type == LineTypeDash) {
v_distance_ratio = segmentIndex / segmentNumber;
vec2 s = source;
@ -167,13 +168,14 @@ void main() {
t = unProjCustomCoord(target);
}
float total_Distance = pixelDistance(s, t) / 2.0 * PI;
total_Distance = total_Distance*8.0;
// float total_Distance = pixelDistance(a_Instance.rg, a_Instance.ba);
v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / (total_Distance / segmentNumber * segmentIndex);
total_Distance = total_Distance*16.0; // total_Distance*16.0 调整默认的效果
v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / total_Distance;
}
if(u_aimate.x == Animate) {
v_distance_ratio = segmentIndex / segmentNumber;
}
float nextSegmentRatio = getSegmentRatio(segmentIndex + indexDir);
v_distance_ratio = segmentIndex / segmentNumber;
vec4 curr = project_position(vec4(degrees(interpolate(source, target, angularDist, segmentRatio)), 0.0, 1.0));

View File

@ -4,8 +4,8 @@ uniform vec4 u_targetColor;
varying mat4 styleMappingMat;
void main() {
float opacity = styleMappingMat[0][0];
float d_distance_ratio = styleMappingMat[3].r; // 当前点位距离占线总长的比例
gl_FragColor = mix(u_sourceColor, u_targetColor, d_distance_ratio);
// styleMappingMat[3][0] 当前点位距离占线总长的比例
gl_FragColor = mix(u_sourceColor, u_targetColor, styleMappingMat[3][0]);
gl_FragColor.a *= opacity; // 全局透明度
}

View File

@ -47,8 +47,9 @@ void main() {
textureOffset = opacityAndOffset.g;
// cal style mapping - 数据纹理映射部分的计算
float d_distance_ratio; // 当前点位距离占线总长的比例
v_color = a_Color;
styleMappingMat[3][0] = a_Distance / a_Total_Distance;
vec4 project_pos = project_position(vec4(a_Position.xy, 0, 1.0));

View File

@ -151,6 +151,12 @@ export class TileLayerManager implements ITileLayerManager {
},
featureId = 'id',
sourceLayer,
pixelConstant = 0,
pixelConstantR = 256 * 256,
pixelConstantG = 256,
pixelConstantB = 1,
pixelConstantRGB = 0.1,
} = this.parent.getLayerConfig() as ISubLayerInitOptions;
const colorValue = this.tileConfigManager.getAttributeScale(
@ -191,6 +197,12 @@ export class TileLayerManager implements ITileLayerManager {
domain,
rampColors,
rampColorsData: this.rampColorsData,
pixelConstant,
pixelConstantR,
pixelConstantG,
pixelConstantB,
pixelConstantRGB,
};
}
@ -210,6 +222,27 @@ export class TileLayerManager implements ITileLayerManager {
this.tileConfigManager.setConfig('domain', this.initOptions.domain);
this.tileConfigManager.setConfig('clampHigh', this.initOptions.clampHigh);
this.tileConfigManager.setConfig('clampLow', this.initOptions.clampLow);
this.tileConfigManager.setConfig(
'pixelConstant',
this.initOptions.pixelConstant,
);
this.tileConfigManager.setConfig(
'pixelConstantR',
this.initOptions.pixelConstantR,
);
this.tileConfigManager.setConfig(
'pixelConstantG',
this.initOptions.pixelConstantG,
);
this.tileConfigManager.setConfig(
'pixelConstantB',
this.initOptions.pixelConstantB,
);
this.tileConfigManager.setConfig(
'pixelConstantRGB',
this.initOptions.pixelConstantRGB,
);
} else {
// Vector Tile Layer Need Listen
this.tileConfigManager.setConfig('stroke', this.initOptions.stroke);

View File

@ -147,62 +147,6 @@ export default class ExtrudePolyline {
complex.startIndex = complex.positions.length / 6;
return complex;
}
public simpleExtrude_gaode2(points: number[][], originPoints: number[][]) {
const complex = this.complex;
if (points.length <= 1) {
return complex;
}
this.lastFlip = -1;
this.started = false;
this.normal = null;
this.totalDistance = 0;
// 去除数组里重复的点
// points = getArrayUnique(points);
const total = points.length;
let count = complex.startIndex;
for (let i = 1; i < total; i++) {
const last = points[i - 1];
last.push(originPoints[i - 1][2] ?? 0);
// @ts-ignore
const originLast = originPoints[i - 1] as vec3;
const cur = points[i];
cur.push(originPoints[i][2] ?? 0);
// @ts-ignore
const originCur = originPoints[i] as vec3;
const next =
i < points.length - 1
? [...points[i + 1], originPoints[i + 1][2] ?? 0]
: null;
const originNext =
i < originPoints.length - 1 ? originPoints[i + 1] : null;
const amt = this.simpleSegment(
complex,
count,
// @ts-ignore
last as vec3,
// @ts-ignore
cur as vec3,
// @ts-ignore
next as vec3,
// @ts-ignore
originLast,
originCur,
// @ts-ignore
originNext as vec3,
);
count += amt;
}
if (this.dash) {
for (let i = 0; i < complex.positions.length / 6; i++) {
complex.positions[i * 6 + 5] = this.totalDistance;
}
}
complex.startIndex = complex.positions.length / 6;
return complex;
}
public extrude(points: number[][]) {
const complex = this.complex;
if (points.length <= 1) {
@ -231,34 +175,6 @@ export default class ExtrudePolyline {
complex.startIndex = complex.positions.length / 6;
return complex;
}
public simpleExtrude(points: number[][]) {
const complex = this.complex;
if (points.length <= 1) {
return complex;
}
this.lastFlip = -1;
this.started = false;
this.normal = null;
this.totalDistance = 0;
const total = points.length;
let count = complex.startIndex;
for (let i = 1; i < total; i++) {
const last = points[i - 1] as vec3;
const cur = points[i] as vec3;
const next = i < points.length - 1 ? points[i + 1] : null;
const amt = this.simpleSegment(complex, count, last, cur, next as vec3);
count += amt;
}
if (this.dash) {
for (let i = 0; i < complex.positions.length / 6; i++) {
complex.positions[i * 6 + 5] = this.totalDistance;
}
}
complex.startIndex = complex.positions.length / 6;
return complex;
}
private segment_gaode2(
complex: any,
index: number,
@ -497,111 +413,6 @@ export default class ExtrudePolyline {
}
return count;
}
private simpleSegment(
complex: any,
index: number,
last: vec3,
cur: vec3,
next: vec3,
) {
let count = 0;
const indices = complex.indices;
const positions = complex.positions;
const normals = complex.normals;
const flatCur = aProjectFlat([cur[0], cur[1]]) as [number, number];
const flatLast = aProjectFlat([last[0], last[1]]) as [number, number];
// @ts-ignore
direction(lineA, flatCur, flatLast);
let segmentDistance = 0;
if (this.dash) {
// @ts-ignore
segmentDistance = this.lineSegmentDistance(flatCur, flatLast);
this.totalDistance += segmentDistance;
}
if (!this.normal) {
this.normal = vec2.create();
computeNormal(this.normal, lineA);
}
if (!this.started) {
this.started = true;
this.extrusions(
positions,
normals,
last,
this.normal,
this.thickness,
this.totalDistance - segmentDistance,
);
}
indices.push(index + 0, index + 1, index + 2);
if (!next) {
computeNormal(this.normal, lineA);
this.extrusions(
positions,
normals,
cur,
this.normal,
this.thickness,
this.totalDistance,
);
indices.push(
...(this.lastFlip === 1
? [index, index + 2, index + 3]
: [index + 2, index + 1, index + 3]),
);
count += 2;
} else {
const flatNext = aProjectFlat([next[0], next[1]]) as [number, number];
if (isPointEqual(flatCur, flatNext)) {
vec2.add(
flatNext,
flatCur,
vec2.normalize(flatNext, vec2.subtract(flatNext, flatCur, flatLast)),
);
}
direction(lineB, flatNext, flatCur);
// stores tangent & miter
const [miterLen, miter] = computeMiter(
tangent,
vec2.create(),
lineA,
lineB,
this.thickness,
);
// normal(tmp, lineA)
// get orientation
let flip = vec2.dot(tangent, this.normal) < 0 ? -1 : 1;
this.extrusions(
positions,
normals,
cur,
miter,
miterLen,
this.totalDistance,
);
indices.push(
...(this.lastFlip === 1
? [index, index + 2, index + 3]
: [index + 2, index + 1, index + 3]),
);
flip = -1;
// the miter is now the normal for our next join
vec2.copy(this.normal, miter);
count += 2;
this.lastFlip = flip;
}
return count;
}
private segment(
complex: any,
index: number,

View File

@ -0,0 +1,112 @@
import { aProjectFlat } from '@antv/l7-utils';
import { vec2, vec3 } from 'gl-matrix';
const lineA = vec2.create();
export function direction(out: vec2, a: vec2, b: vec2) {
vec2.sub(out, a, b);
vec2.normalize(out, out);
return out;
}
export default class ExtrudePolyline {
public complex: {
positions: number[];
indices: number[];
startIndex: number;
indexes: number[];
};
private started: boolean = false;
private totalDistance: number = 0;
private currentIndex: number = 0;
constructor() {
this.complex = {
positions: [],
indices: [],
startIndex: 0,
indexes: [],
};
}
public simpleExtrude(points: number[][]) {
const complex = this.complex;
if (points.length <= 1) {
return complex;
}
this.started = false;
this.totalDistance = 0;
const total = points.length;
let count = complex.startIndex;
for (let i = 1; i < total; i++) {
const last = points[i - 1] as vec3;
const cur = points[i] as vec3;
const amt = this.simpleSegment(complex, count, last, cur);
count += amt;
}
for (let i = 0; i < complex.positions.length / 6; i++) {
complex.positions[i * 6 + 5] = this.totalDistance;
}
return complex;
}
private simpleSegment(complex: any, index: number, last: vec3, cur: vec3) {
let count = 0;
const indices = complex.indices;
const positions = complex.positions;
const flatCur = aProjectFlat([cur[0], cur[1]]) as [number, number];
const flatLast = aProjectFlat([last[0], last[1]]) as [number, number];
direction(lineA, flatCur, flatLast);
const segmentDistance = this.lineSegmentDistance(flatCur, flatLast);
this.totalDistance += segmentDistance;
if (!this.started) {
this.started = true;
this.extrusions(positions, last, this.totalDistance - segmentDistance);
}
this.extrusions(positions, cur, this.totalDistance);
indices.push(index + 0, index + 1, index + 2);
indices.push(index + 2, index + 1, index + 3);
count += 2;
return count;
}
private extrusions(
positions: number[],
point: vec3, // 顶点
distanceRadio: number,
) {
positions.push(
point[0],
point[1],
point[2] | 0,
distanceRadio,
0,
point[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
positions.push(
point[0],
point[1],
point[2] | 0,
distanceRadio,
0,
point[2] | 0,
);
this.complex.indexes.push(this.currentIndex);
this.currentIndex++;
}
private lineSegmentDistance(b1: [number, number], a1: [number, number]) {
const dx = a1[0] - b1[0];
const dy = a1[1] - b1[1];
return Math.sqrt(dx * dx + dy * dy);
}
}

View File

@ -7,7 +7,7 @@ import {
ITexture2D,
Point,
} from '@antv/l7-core';
import { FrequencyController } from '@antv/l7-utils';
import { FrequencyController, getMask } from '@antv/l7-utils';
import BaseModel from '../../core/BaseModel';
import { IWindLayerStyleOptions } from '../../core/interface';
import { RasterImageTriangulation } from '../../core/triangulation';
@ -28,7 +28,6 @@ const defaultRampColors = {
export default class WindModel extends BaseModel {
protected texture: ITexture2D;
private colorModel: IModel;
private wind: IWind;
private imageCoords: [Point, Point];
@ -51,18 +50,6 @@ export default class WindModel extends BaseModel {
}
public initModels() {
const { createTexture2D } = this.rendererService;
const source = this.layer.getSource();
this.texture = createTexture2D({
height: 0,
width: 0,
});
const glContext = this.rendererService.getGLContext();
this.imageCoords = source.data.dataArray[0].coordinates as [Point, Point];
source.data.images.then((imageData: HTMLImageElement[]) => {
const {
uMin = -21.32,
uMax = 26.8,
@ -74,7 +61,21 @@ export default class WindModel extends BaseModel {
dropRateBump = 0.01,
rampColors = defaultRampColors,
sizeScale = 0.5,
// mask
mask = false,
maskInside = true,
} = this.layer.getLayerConfig() as IWindLayerStyleOptions;
const { createTexture2D } = this.rendererService;
const source = this.layer.getSource();
this.texture = createTexture2D({
height: 0,
width: 0,
});
const glContext = this.rendererService.getGLContext();
this.imageCoords = source.data.dataArray[0].coordinates as [Point, Point];
source.data.images.then((imageData: HTMLImageElement[]) => {
this.sizeScale = sizeScale;
const { imageWidth, imageHeight } = this.getWindSize();
@ -119,6 +120,7 @@ export default class WindModel extends BaseModel {
primitive: gl.TRIANGLES,
depth: { enable: false },
blend: this.getBlend(),
// stencil: getMask(mask, maskInside),
});
return [this.colorModel];

View File

@ -53,8 +53,8 @@ export interface IImagedata {
export function generateColorRamp(
colorRamp: IColorRamp,
): ImageData | IImagedata {
const canvas = $window.document.createElement('canvas');
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
let canvas = $window.document.createElement('canvas');
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
canvas.width = 256;
canvas.height = 1;
const gradient = ctx.createLinearGradient(0, 0, 256, 1);
@ -83,9 +83,17 @@ export function generateColorRamp(
imageData.data[i + 2] = data[i + 2];
imageData.data[i + 3] = data[i + 3];
}
// @ts-ignore
canvas = null;
// @ts-ignore
ctx = null;
return imageData;
} else {
data = new Uint8ClampedArray(ctx.getImageData(0, 0, 256, 1).data);
// @ts-ignore
canvas = null;
// @ts-ignore
ctx = null;
return { data, width: 256, height: 1 };
}
}

View File

@ -1,5 +1,5 @@
import { LineLayer, Scene } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
import { GaodeMap, Mapbox, GaodeMapV2 } from '@antv/l7-maps';
import * as React from 'react';
export default class Amap2demo_arcLineDir extends React.Component {
@ -31,20 +31,20 @@ export default class Amap2demo_arcLineDir extends React.Component {
lat2: 37.373799,
testOpacity: 0.4,
},
{
lng1: 116.98242187499999,
lat1: 43.004647127794435,
lng2: 105.64453124999999,
lat2: 28.998531814051795,
testOpacity: 0.4,
},
{
lng1: 75.76171875,
lat1: 36.31512514748051,
lng2: 46.23046874999999,
lat2: 52.802761415419674,
testOpacity: 0.8,
},
// {
// lng1: 116.98242187499999,
// lat1: 43.004647127794435,
// lng2: 105.64453124999999,
// lat2: 28.998531814051795,
// testOpacity: 0.4,
// },
// {
// lng1: 75.76171875,
// lat1: 36.31512514748051,
// lng2: 46.23046874999999,
// lat2: 52.802761415419674,
// testOpacity: 0.8,
// },
];
let data2 = [
{
@ -74,75 +74,78 @@ export default class Amap2demo_arcLineDir extends React.Component {
y1: 'lat2',
},
})
.size(10)
.size(5)
.shape('arc')
.color('#8C1EB2')
.style({
forward: false,
opacity: 'testOpacity',
lineType: 'dash',
dashArray: [25, 5],
// forward: false,
// opacity: 'testOpacity',
// segmentNumber: 5
});
scene.addLayer(layer);
const layer2 = new LineLayer({ blend: 'normal' })
.source(data, {
parser: {
type: 'json',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})
.size(10)
.shape('arc')
.color('#8C1EB2')
.style({
opacity: 'testOpacity',
});
scene.addLayer(layer2);
// const layer2 = new LineLayer({ blend: 'normal' })
// .source(data, {
// parser: {
// type: 'json',
// x: 'lng1',
// y: 'lat1',
// x1: 'lng2',
// y1: 'lat2',
// },
// })
// .size(10)
// .shape('arc')
// .color('#8C1EB2')
// .style({
// opacity: 'testOpacity',
// });
// scene.addLayer(layer2);
const layer3 = new LineLayer({ blend: 'normal' })
.source(data2, {
parser: {
type: 'json',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})
.size(10)
.shape('arc')
.color('#8C1EB2')
.style({
opacity: 'testOpacity',
})
.animate(true);
scene.addLayer(layer3);
// const layer3 = new LineLayer({ blend: 'normal' })
// .source(data2, {
// parser: {
// type: 'json',
// x: 'lng1',
// y: 'lat1',
// x1: 'lng2',
// y1: 'lat2',
// },
// })
// .size(10)
// .shape('arc')
// .color('#8C1EB2')
// .style({
// opacity: 'testOpacity',
// })
// .animate(true);
// scene.addLayer(layer3);
const layer4 = new LineLayer({ blend: 'normal' })
.source(data2, {
parser: {
type: 'json',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})
.size(10)
.shape('arc')
.color('#8C1EB2')
.style({
forward: false,
opacity: 'testopacity',
})
.animate({
duration: 4,
interval: 0.3,
trailLength: 0.5,
});
scene.addLayer(layer4);
// const layer4 = new LineLayer({ blend: 'normal' })
// .source(data2, {
// parser: {
// type: 'json',
// x: 'lng1',
// y: 'lat1',
// x1: 'lng2',
// y1: 'lat2',
// },
// })
// .size(10)
// .shape('arc')
// .color('#8C1EB2')
// .style({
// forward: false,
// opacity: 'testopacity',
// })
// .animate({
// duration: 4,
// interval: 0.3,
// trailLength: 0.5,
// });
// scene.addLayer(layer4);
});
}

View File

@ -43,12 +43,14 @@ export default class Amap2demo_arcLine_greatCircle extends React.Component {
},
},
)
.size(1)
.size(5)
.shape('greatcircle')
.color('#ff0000')
.style({
opacity: 0.8,
blur: 0.99,
lineType: 'dash',
dashArray: [5, 5],
// opacity: 0.8,
// blur: 0.99,
});
scene.addLayer(layer);
});

View File

@ -1,4 +1,4 @@
import { RasterLayer, Scene } from '@antv/l7';
import { RasterLayer, Scene, PointLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
import * as React from 'react';
// tslint:disable-next-line:no-submodule-imports
@ -45,11 +45,34 @@ export default class Amap2demo_rasterLayer extends React.Component {
});
this.scene = scene;
const point = new PointLayer()
.source(
[
{
lng: 120,
lat: 30,
},
],
{
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
},
)
.shape('circle')
.size(10)
.color('#f00');
scene.addLayer(point);
const tiffdata = await this.getTiffData();
console.log('tiffdata', tiffdata);
const layer = new RasterLayer({});
const mindata = -0;
const maxdata = 8000;
layer
.source(tiffdata.data, {
parser: {
@ -57,12 +80,6 @@ export default class Amap2demo_rasterLayer extends React.Component {
width: tiffdata.width,
height: tiffdata.height,
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
// extent: [
// 73.4766000000000048,
// 18.1054999999999993,
// 135.1066187,
// 57.630046,
// ],
},
})
.style({
@ -80,6 +97,7 @@ export default class Amap2demo_rasterLayer extends React.Component {
positions: [0, 0.25, 0.5, 0.75, 1.0],
},
});
scene.addLayer(layer);
}

View File

@ -1,225 +0,0 @@
import {
LineLayer,
Scene,
PointLayer,
flow,
MarkerLayer,
Marker,
} from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
import * as React from 'react';
import { animate, linear } from 'popmotion';
interface IObject {
[key: string]: any;
}
function parseCSV(data: string) {
let arr = data.split('\n');
if (arr && arr[0]) {
let columns = arr[0].replace('\r', '').split(',');
let rows = arr.slice(1).map((d: string) => d.split(','));
let json = rows.map((row: string[]) => {
let object: IObject = {};
row.map((e: string, i: number) => {
let str = e.replace('\r', '');
let key = columns[i].replace('\n', '');
// console.log('key', key)
object[key] = str;
return '';
});
return object;
});
return json;
}
return [];
}
function getLngLat(data: any) {
return data.map((d: any) => {
return [+d.lng, +d.lat];
});
}
export default class Amap2demo_road 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({
center: [116.35, 40],
zoom: 12,
// zoom: 3
}),
});
this.scene = scene;
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/bmw-prod/89aa8682-2245-448b-be0d-710308cd63a6.csv',
)
.then((res) => res.text())
.then((data) => {
const jsonData = parseCSV(data);
const lnglatData = getLngLat(jsonData);
const sd = {
type: 'FeatureCollection',
name: 'dl2',
crs: {
type: 'name',
properties: { name: 'urn:ogc:def:crs:OGC:1.3:CRS84' },
},
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'MultiLineString',
coordinates: [
[
// [120.10236740112303, 30.25469303741397],
// [120.104341506958, 30.252542952311455],
// [120.10622978210449, 30.250096246503063],
// [120.10974884033202, 30.248613364842253],
// [120.11017799377441, 30.24661143909856],
// [120.11069297790527, 30.244387029323946],
// [120.11120796203613, 30.24245916678936],
// [120.11404037475586, 30.24156937132543],
// [120.11773109436037, 30.24194012041444],
// [120.12099266052248, 30.241865970708517],
// [120.12073516845703, 30.24053126643564],
// [120.12219429016112, 30.238455023761645],
// [120.12537002563475, 30.240086360983426],
// [120.12837409973145, 30.242533316047716],
// [120.12760162353517, 30.24661143909856],
// [120.12605667114256, 30.249503096524485],
// [120.12639999389648, 30.2540999151896],
// [120.12579917907715, 30.25521201642245],
// [120.12339591979979, 30.25521201642245],
// [120.12219429016112, 30.253729211980726],
// [120.11979103088379, 30.253877493432157],
// [120.11893272399901, 30.251282535717067],
// [120.11773109436037, 30.249280664359304],
// [120.11507034301759, 30.249058231690526],
...lnglatData,
// [
// 120.11507034301759,
// 30.249058231690526
// ],
// [
// 120.12,
// 30.249058231690526
// ]
],
],
},
},
],
};
const linelayer = new LineLayer({ blend: 'normal' })
.source(sd)
.size(2)
.shape('line')
.color('#8C1EB2')
// .animate({
// interval: 1, // 间隔
// duration: 1, // 持续时间,延时
// trailLength: 2, // 流线长度
// })
.style({
// opacity: 'opacity',
sourceColor: '#f00', // 起点颜色
targetColor: '#0f0', // 终点颜色
});
linelayer.on('inited', () => {
const source = linelayer.getSource();
const coords = source?.data?.dataArray[0]?.coordinates;
const path = flow(coords, 50000);
runPath(path, 0);
// @ts-ignore
function runPath(pathData: any, startIndex: number) {
const path = pathData[startIndex];
if (!path) return;
const { start, end, duration, rotation } = path;
let startRotation = 360 - scene.getRotation();
let timer0 = animate({
from: {
rotation: startRotation,
},
to: {
rotation: rotation,
},
ease: linear,
duration: 600,
onUpdate: (o) => {
scene.setRotation(o.rotation);
},
onComplete: () => {
timer0.stop();
// @ts-ignore
timer0 = null;
let timer = animate({
from: {
lng: start[0],
lat: start[1],
},
to: {
lng: end[0],
lat: end[1],
},
ease: linear,
duration,
onUpdate: (o) => {
scene.setCenter([o.lng, o.lat]);
},
onComplete: () => {
timer.stop();
// @ts-ignore
timer = null;
setTimeout(() => {
runPath(pathData, startIndex + 1);
}, 500);
},
});
},
});
}
});
scene.addLayer(linelayer);
});
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -77,7 +77,15 @@ export default class WindMap extends React.Component {
sizeScale: 0.5,
};
const layer = new WindLayer({});
fetch(
'https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json',
)
.then((res) => res.json())
.then((maskData) => {
const layer = new WindLayer({
mask: true,
maskfence: maskData,
});
layer
.source(
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*wcU8S5xMEDYAAAAAAAAAAAAAARQnAQ',
@ -179,6 +187,7 @@ export default class WindMap extends React.Component {
});
});
});
});
}
public render() {

View File

@ -0,0 +1,84 @@
// @ts-ignore
import { ImageLayer, Scene } from '@antv/l7';
import { GaodeMap, Mapbox } from '@antv/l7-maps';
import * as React from 'react';
export default class Amap2demo_imageLayer 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({
center: [121.268, 30.3628],
pitch: 0,
style: 'normal',
zoom: 10,
viewMode: '3D',
}),
});
this.scene = scene;
scene.on('loaded', () => {
const layer = new ImageLayer({});
layer
.source(
// 'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',
'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*Al3JR5Hdg0cAAAAAAAAAAAAAARQnAQ',
{
parser: {
type: 'image',
extent: [121.168, 30.2828, 121.384, 30.4219],
},
},
)
.shape('dataImage')
.style({
// clampLow: false,
// clampHigh: false,
// opacity: 0.8,
domain: [0, 8000],
rampColors: {
colors: [
'#FF4818',
'#F7B74A',
'#FFF598',
'#91EABC',
'#2EA9A1',
'#206C7C',
].reverse(),
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
},
// float value = u_pixelConstant + ((r * u_pixelConstantR + g * u_pixelConstantG + b * u_pixelConstantB) * u_pixelConstantRGB);
pixelConstant: 0.0,
pixelConstantR: 256 * 256,
pixelConstantG: 256,
pixelConstantB: 1,
pixelConstantRGB: 0.1,
});
scene.addLayer(layer);
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -1,320 +0,0 @@
import { LineLayer, Scene, PointLayer, Marker, MarkerLayer } from '@antv/l7';
import { GaodeMap, GaodeMapV2, Map } from '@antv/l7-maps';
import * as React from 'react';
import { animate, easeInOut } from 'popmotion';
function getImageData(img: HTMLImageElement) {
let canvas: HTMLCanvasElement = document.createElement('canvas');
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
const { width, height } = img;
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height);
return imageData;
}
function getLatData(data: number[]) {
const size = Math.floor(Math.sqrt(data.length));
const arr = [];
let startLng = 110,
lngStep = 5 / (size - 1);
let startLat = 30,
latStep = -5 / (size - 1);
for (let i = 0; i < size; i++) {
let arr2 = [];
for (let j = 0; j < size; j++) {
let index = i + j * size;
let x = startLng + lngStep * i;
let y = startLat + latStep * j;
arr2.push([x, y, data[index]]);
}
arr.push(arr2);
}
return arr;
}
function getLngData(data: number[]) {
const size = Math.floor(Math.sqrt(data.length));
const arr = [];
let startLng = 110,
lngStep = 5 / (size - 1);
let startLat = 30,
latStep = -5 / (size - 1);
for (let i = 0; i < size; i++) {
let arr2 = [];
for (let j = 0; j < size; j++) {
let index = i * size + j;
let x = startLng + lngStep * j;
let y = startLat + latStep * i;
arr2.push([x, y, data[index]]);
}
arr.push(arr2);
}
return arr;
}
function getR(data: Uint8ClampedArray) {
const arr = [];
for (let i = 0; i < data.length; i += 4) {
if (data[i] < 25) {
// console.log(data[i])
arr.push(20);
} else {
arr.push(data[i]);
}
}
return arr;
}
export default class GridTile2 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({
center: [112, 27.5],
// pitch: 75,
// zoom: 8.2,
// rotation: -30,
style: 'blank',
}),
});
this.scene = scene;
scene.setBgColor('#000');
const airPorts = [
{
name: '常德桃花源机场',
lng: 111.641101,
lat: 28.91165,
},
{
name: '芷江机场',
lng: 109.709699,
lat: 27.442172,
},
{
name: '铜仁凤凰机场',
lng: 109.313971,
lat: 27.880629,
},
{
name: '永州零陵机场',
lng: 111.616049,
lat: 26.335053,
},
{
name: '桂林两江国际机场',
lng: 110.049256,
lat: 25.210065,
},
{
name: '长沙黄花国际机场',
lng: 113.216412,
lat: 28.183613,
},
{
name: '井冈山机场',
lng: 114.745845,
lat: 26.852646,
},
];
scene.addImage(
'plane',
'https://gw.alipayobjects.com/zos/bmw-prod/96327aa6-7fc5-4b5b-b1d8-65771e05afd8.svg',
);
const img: HTMLImageElement = new Image();
img.crossOrigin = 'none';
img.src =
'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*dPgXQJ9eUtoAAAAAAAAAAAAAARQnAQ';
img.onload = function() {
const data = getImageData(img);
const rData = getR(data.data);
let d1 = getLngData(rData);
let d2 = getLatData(rData);
const geoData = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: d1,
},
},
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: d2,
},
},
],
};
const layer = new LineLayer({})
.source(geoData)
.size(1)
.shape('simple')
.color('rgb(22, 119, 255)')
.style({
vertexHeightScale: 2000,
sourceColor: '#f00',
// targetColor: '#0f0',
});
scene.addLayer(layer);
};
scene.addImage(
'start',
'https://gw.alipayobjects.com/zos/bmw-prod/ebb0af57-4a8a-46e0-a296-2d51f9fa8007.svg',
);
const imageLayer = new PointLayer()
.source(
[
{
lng: 111.641101,
lat: 28.91165,
cityData: '城市数据',
},
],
{
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
},
)
.shape('cityData', 'text')
.size(16)
.style({
textAnchor: 'left', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直]
spacing: 2, // 字符间距
padding: [1, 1], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
stroke: '#ffffff', // 描边颜色
strokeWidth: 0.3, // 描边宽度
strokeOpacity: 1.0,
});
const pointLayer = new PointLayer()
.source(
[
{
lng: 111.641101,
lat: 28.91165,
},
],
{
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
},
)
.shape('circle')
.size(35)
.color('#fff')
.style({});
const height = 200;
const dom = document.createElement('div');
dom.innerHTML = `
<div style="width:100px;height:${height}px;position:relative;">
<div style="position: absolute;width: 8px;height: 8px;top: 10px;border-radius:5px;background: rgba(150, 238, 150, 1.0);"></div>
<div style="position: absolute;width: 2px;height: 100%;top: 10px;left: 3px;background-image: linear-gradient(rgba(150, 238, 150, 0.4), rgba(150, 238, 150, 0))"></div>
<div style="
position: absolute;
width: 100px;
height: 20px;
left: 15px;top: 5px;
background-image: linear-gradient(to right, rgba(150, 238, 150, 0.4), rgba(150, 238, 150, 0));
color: #fff;
padding-left: 15px;
line-height: 20px;
font-size: 12px;
">
</div>
</div>
`;
const markerLayer = new MarkerLayer({});
const marker = new Marker()
.setLnglat({
lng: 111.641101,
lat: 28.91165,
})
.setElement(dom);
markerLayer.addMarker(marker);
scene.on('loaded', () => {
// scene.addLayer(pointLayer);
// scene.addLayer(imageLayer);
scene.addMarkerLayer(markerLayer);
// scene.addLayer(waveLayer);
// scene.addLayer(barLayer);
// scene.addLayer(airPrtsLayer);
// scene.addLayer(airLineLayer);
// scene.addLayer(airPlaneLayer);
animate({
from: {
pitch: 0,
rotation: 0,
zoom: 6,
},
to: {
pitch: 75,
rotation: -30,
zoom: 8.2,
},
ease: easeInOut,
duration: 1000,
onUpdate: ({ pitch, rotation, zoom }) => {
scene.setPitch(pitch);
scene.setRotation(rotation);
scene.setZoom(zoom);
},
onComplete: () => {},
});
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -1,204 +0,0 @@
import {
LineLayer,
Scene,
PointLayer,
flow,
MarkerLayer,
Marker,
} from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
import * as React from 'react';
import { cloneDeep } from 'lodash';
interface IObject {
[key: string]: any;
}
function parseCSV(data: string) {
let arr = data.split('\n');
if (arr && arr[0]) {
let columns = arr[0].replace('\r', '').split(',');
let rows = arr.slice(1).map((d: string) => d.split(','));
let json = rows.map((row: string[]) => {
let object: IObject = {};
row.map((e: string, i: number) => {
let str = e.replace('\r', '');
let key = columns[i].replace('\n', '');
object[key] = str;
return '';
});
return object;
});
return json;
}
return [];
}
function getLngLat2(data: any) {
let res = [];
for (let i = 0; i < data.length - 1; i++) {
let start = data[i];
let end = data[i + 1];
res.push({
lng1: start.lng,
lat1: start.lat,
lng2: end.lng,
lat2: end.lat,
opacity: 0.6,
});
}
return res;
}
function createMarker(
lng: number,
lat: number,
time: string,
loc: string,
event: string,
) {
const dom = document.createElement('div');
dom.innerHTML = `
<div class="infoPlane" style="
zIndex: 99;
padding: 5px;
border-radius: 6px;
background: rgba(173,216,230, 0.8);
color: #DC143C;
">
<div style="
font-size: 12px;
font-weight: 800;
border-bottom: 1px #DC143C;
">${time}</div>
<div style="
height: 1px;
width: 95%;
margin: 0 auto;
background: #DC143C;
"></div>
<div style="
font-size: 12px;
">${loc}</div>
<div style="
font-size: 12px;
">${event}</div>
</div>
`;
const marker = new Marker().setLnglat({ lng, lat }).setElement(dom);
return marker;
}
export default class Amap2demo_road 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({
center: [116.35, 40],
zoom: 3,
style: 'dark',
}),
});
this.scene = scene;
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/bmw-prod/89aa8682-2245-448b-be0d-710308cd63a6.csv',
)
.then((res) => res.text())
.then((data) => {
const jsonData = parseCSV(data);
const lnglatData2 = getLngLat2(jsonData);
const pointLayers = new PointLayer()
.source(jsonData, {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
})
.shape('circle')
.size(5)
.color('#DC143C');
scene.addLayer(pointLayers);
const linelayer = new LineLayer({ blend: 'normal', autoFit: true })
.source(lnglatData2, {
parser: {
type: 'json',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})
.size(2)
.shape('arc')
.color('#DC143C')
.animate({
interval: 1, // 间隔
duration: 1, // 持续时间,延时
trailLength: 2, // 流线长度
})
.style({
opacity: 'opacity',
});
scene.addLayer(linelayer);
setTimeout(() => {
addMarker(jsonData, 0);
}, 600);
function addMarker(data: any[], index: number) {
let d = data[index];
if (!d) {
return;
}
setTimeout(() => {
let m = createMarker(d.lng, d.lat, d.time, d.loc, d.do);
scene.addMarker(m);
let lineData = cloneDeep(lnglatData2);
if (lineData[index]) {
lineData[index].opacity = 1;
linelayer.setData(lineData);
}
addMarker(data, index + 1);
}, 400);
}
});
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -1,205 +0,0 @@
import {
LineLayer,
Scene,
PointLayer,
flow,
MarkerLayer,
Marker,
} from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
import * as React from 'react';
import { cloneDeep } from 'lodash';
interface IObject {
[key: string]: any;
}
function parseCSV(data: string) {
let arr = data.split('\n');
if (arr && arr[0]) {
let columns = arr[0].replace('\r', '').split(',');
let rows = arr.slice(1).map((d: string) => d.split(','));
let json = rows.map((row: string[]) => {
let object: IObject = {};
row.map((e: string, i: number) => {
let str = e.replace('\r', '');
let key = columns[i].replace('\n', '');
object[key] = str;
return '';
});
return object;
});
return json;
}
return [];
}
function getLngLat2(data: any) {
let res = [];
for (let i = 0; i < data.length - 1; i++) {
let start = data[i];
let end = data[i + 1];
res.push({
lng1: start.lng,
lat1: start.lat,
lng2: end.lng,
lat2: end.lat,
opacity: 0.4,
});
}
return res;
}
export default class Amap2demo_road 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({
center: [116.35, 40],
zoom: 3,
style: 'dark',
}),
});
this.scene = scene;
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/bmw-prod/89aa8682-2245-448b-be0d-710308cd63a6.csv',
)
.then((res) => res.text())
.then((data) => {
const jsonData = parseCSV(data);
jsonData.map((d: any, i: number) => (d.index = i + 1));
const lnglatData2 = getLngLat2(jsonData);
const pointLayers = new PointLayer()
.source([jsonData[0]], {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
})
.shape('circle')
.size(5)
.color('#DC143C')
.style({
opacity: 0.5,
});
scene.addLayer(pointLayers);
const pointLayer2 = new PointLayer()
.source([jsonData[0]], {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
})
.shape('circle')
.size(14)
.color('rgba(255, 0, 0, 0.01)')
// .color('')
.style({
strokeWidth: 2,
stroke: '#f00',
opacity: 1,
offsets: [21, 2],
});
scene.addLayer(pointLayer2);
const pointText = new PointLayer()
.source([jsonData[0]], {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
})
.shape('index', 'text')
.size(18)
.color('#f00')
.style({
textAnchor: 'center',
textOffset: [28, 0],
textAllowOverlap: true,
});
scene.addLayer(pointText);
const linelayer = new LineLayer({ blend: 'normal', autoFit: true })
.source(lnglatData2, {
parser: {
type: 'json',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})
.size(2)
.shape('arc')
.color('#DC143C')
.animate({
interval: 1, // 间隔
duration: 1, // 持续时间,延时
trailLength: 2, // 流线长度
})
.style({
opacity: 'opacity',
});
scene.addLayer(linelayer);
setTimeout(() => {
addPoint(0);
}, 600);
function addPoint(index: number) {
let d = data[index];
if (!d) {
return;
}
setTimeout(() => {
let pointData = cloneDeep(jsonData);
pointLayers.setData(pointData.slice(0, index + 1));
pointLayer2.setData(pointData.slice(0, index + 1));
pointText.setData(pointData.slice(0, index + 1));
let lineData = cloneDeep(lnglatData2);
if (lineData[index]) {
lineData[index].opacity = 1;
linelayer.setData(lineData);
}
addPoint(index + 1);
}, 400);
}
});
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -1,88 +0,0 @@
// @ts-ignore
import { PointLayer, Scene } from '@antv/l7';
import { GaodeMap, GaodeMapV2, Mapbox } from '@antv/l7-maps';
import * as React from 'react';
export default class PointUV 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({
center: [121.107846, 30.267069],
pitch: 40,
// style: 'normal',
zoom: 20,
}),
});
// normal = 'normal',
// additive = 'additive',
// subtractive = 'subtractive',
// min = 'min',
// max = 'max',
// none = 'none',
const layer = new PointLayer({ depth: false })
.source(
[
{
lng: 121.107846,
lat: 30.267069,
},
{
lng: 121.107,
lat: 30.267069,
},
],
{
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
},
)
.shape('cylinder')
.color('#0ff')
.size([5, 5, 100])
.style({
opacity: 0.6,
sourceColor: 'red',
targetColor: 'yellow',
opacityLinear: {
enable: true, // true - false
dir: 'up', // up - down
},
lightEnable: false,
});
layer.on('click', () => console.log('click'));
scene.addLayer(layer);
scene.render();
this.scene = scene;
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -0,0 +1,89 @@
import { LineLayer, Scene, PointLayer, Marker, MarkerLayer } from '@antv/l7';
import { GaodeMap, GaodeMapV2, Mapbox, Map } from '@antv/l7-maps';
import * as React from 'react';
export default class SimpleLine extends React.Component {
// @ts-ignore
private scene: Scene;
public componentWillUnmount() {
this.scene.destroy();
}
public async componentDidMount() {
const scene = new Scene({
id: 'map',
map: new GaodeMapV2({
center: [96.328125, 38.685509760012],
// pitch: 75,
zoom: 4,
// rotation: -30,
// style: 'blank',
}),
});
this.scene = scene;
const layer = new LineLayer({})
.source({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [
[90.791015625, 38.75408327579141],
[90.87890625, 33.797408767572485],
[95.185546875, 32.76880048488168],
[95.537109375, 34.08906131584994],
[96.328125, 38.685509760012],
[100.107421875, 38.34165619279595],
[101.953125, 36.80928470205937],
[100.107421875, 34.95799531086792],
],
},
},
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [
[90.791015625, 38.75408327579141],
[95, 45],
],
},
},
],
})
.size(1)
.shape('simple')
.color('rgb(22, 119, 255)')
.style({
sourceColor: '#f00',
targetColor: '#0f0',
});
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

@ -28,7 +28,6 @@ import Amap2demo_arcLineLinear from './components/amap2demo_arcLineLinear';
import Amap2demo_arcLine3DTex from './components/amap2demo_arcLine3DTex';
import Amap2demo_lineStreet from './components/amap2demo_lineStreet';
import Amap2demo_lineLinear from './components/amap2demo_lineLinear';
import Amap2demo_road from './components/amap2demo_road';
import Amap2demo_road2 from './components/amap2demo_road2';
import Amap2demo_heatmap from "./components/amap2demo_heatmap"
@ -64,7 +63,6 @@ import Amap2demo_textOffset from "./components/amap2demo_textOffset"
import ShapeUpdate from './components/shapeUpdate'
import BusLine from './components/busline'
import AmapPlugin from './components/plugin'
import PointUV from './components/pointUV'
import DestroyClear from './components/destroyClear'
import PlaneLine from './components/planeLine'
import Slider from './components/slider'
@ -72,12 +70,11 @@ import WindMap from './components/amap2demo_wind'
import SimplePoint from './components/simplePoint';
import LineWall from './components/linewall'
import GridTile from './components/gridTile'
import GridTile2 from './components/gridTile2'
import SimpleLine from './components/simpleLine'
import Cluster from './components/cluster'
import Hot from './components/hot'
import Hot2 from './components/hot2'
import Mask from './components/mask'
import PolygonExteudeTex from './components/polygon_extrudeTex';
import DataImagelayer from './components/dataImagelayer';
import BugFix from './components/bugfix'
// @ts-ignore
@ -110,7 +107,6 @@ storiesOf('地图方法', module)
.add('高德地图2.0 line_winds', () => <Amap2demo_winds />)
.add('高德地图2.0 line_Street', () => <Amap2demo_lineStreet />)
.add('高德地图2.0 line_Linear', () => <Amap2demo_lineLinear />)
.add('高德地图2.0 road', () => <Amap2demo_road />)
.add('高德地图2.0 road2', () => <Amap2demo_road2 />)
.add('高德地图2.0 heatmap', () => <Amap2demo_heatmap />)
@ -147,7 +143,6 @@ storiesOf('地图方法', module)
.add('WindMap', () => <WindMap/>)
.add('AmapPlugin', () => <AmapPlugin/>)
.add('PointUV', () => <PointUV/>)
.add('DestroyClear', () => <DestroyClear/>)
.add('PlaneLine', () => <PlaneLine/>)
.add('Slider', () => <Slider/>)
@ -155,10 +150,9 @@ storiesOf('地图方法', module)
.add('LineWall', () => <LineWall/>)
.add('BusLine', () => <BusLine/>)
.add('GridTile', () => <GridTile/>)
.add('GridTile2', () => <GridTile2/>)
.add('SimpleLine', () => <SimpleLine/>)
.add('Cluster', () => <Cluster/>)
.add('Hot1', () => <Hot/>)
.add('Hot2', () => <Hot2/>)
.add('Mask', () => <Mask/>)
.add('PolygonExteudeTex', () => <PolygonExteudeTex/>)
.add('DataImagelayer', () => <DataImagelayer/>)
.add('BugFix', () => <BugFix/>)

View File

@ -0,0 +1,86 @@
import * as React from 'react';
import * as turf from '@turf/turf';
import { RasterLayer, Scene, LineLayer, ILayer } from '@antv/l7';
import { GaodeMap, GaodeMapV2, Map, Mapbox } from '@antv/l7-maps';
export default class OsmRasterTile extends React.Component {
private scene: Scene;
private gridLayer: ILayer;
public componentWillUnmount() {
this.scene.destroy();
}
public async componentDidMount() {
this.scene = new Scene({
id: 'map',
map: new Map({
center: [130, 30],
pitch: 0,
style: 'normal',
zoom: 1.5,
}),
});
// this.scene.on('mapchange', this.updateGridLayer);
this.scene.on('loaded', () => {
const layer = new RasterLayer({})
.source(
'http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
{
parser: {
type: 'rasterTile',
tileSize: 256,
zoomOffset: 0,
updateStrategy: 'overlap',
},
},
)
.shape('dataImage')
.style({
clampLow: false,
clampHigh: false,
// opacity: 0.8,
domain: [0, 8000],
rampColors: {
colors: [
'#FF4818',
'#F7B74A',
'#FFF598',
'#91EABC',
'#2EA9A1',
'#206C7C',
],
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
},
// float value = u_pixelConstant + ((r * u_pixelConstantR + g * u_pixelConstantG + b * u_pixelConstantB) * u_pixelConstantRGB);
pixelConstant: 0.0,
pixelConstantR: 256 * 20,
pixelConstantG: 256,
pixelConstantB: 1,
pixelConstantRGB: 1,
});
this.scene.addLayer(layer);
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -3,6 +3,7 @@ import * as React from 'react';
import RasterTile from './components/RasterTile';
import OsmRasterTile from './components/OsmRasterTile';
import RasterImageDataTile from './components/RasterImageDataTile';
import RasterArrayBuffer from './components/RasterArrayBuffer';
import DemFilter from './components/demFilter';
@ -35,6 +36,7 @@ import Quxian from './components/QuXian';
storiesOf('瓦片', module)
.add('RasterTile', () => <RasterTile />)
.add('OsmRasterTile', () => <OsmRasterTile />)
.add('RasterImageDataTile', () => <RasterImageDataTile />)
.add('America', () => <America />)
.add('Lerc', () => <Lerc />)