Skip to content

Commit 4d47073

Browse files
committed
Merge remote-tracking branch 'origin/master' into master
2 parents 334511e + 32d4400 commit 4d47073

90 files changed

Lines changed: 2712 additions & 514 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@antv/l7-utils': patch
3+
---
4+
5+
fix: 修复原型污染漏洞
6+
7+
在 merge 和 mergeWith 函数中添加对 **proto**、constructor 和 prototype 键的过滤,防止攻击者通过 JSON.parse 构造的恶意对象污染 Object.prototype。
8+
9+
安全报告由 Dremig 提供。

L7_API_Reference.md

Lines changed: 181 additions & 167 deletions
Large diffs are not rendered by default.

examples/demos/index.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ import * as HeatmapTestCases from './heatmap';
1010
import * as LineTestCases from './line';
1111
import * as MaskTestCases from './mask';
1212
import * as PointTestCases from './point';
13+
import { anchor } from './point/anchor';
14+
import { billboard } from './point/billboard';
15+
import { column } from './point/column';
16+
import { dot } from './point/dot';
17+
import { fill } from './point/fill';
18+
import { fillImage } from './point/fill-image';
19+
import { iconfont } from './point/iconfont';
20+
import { image } from './point/image';
21+
import { imageTextAnchor } from './point/image-text-anchor';
22+
import { radar } from './point/radar';
23+
import { text } from './point/text';
1324
import * as PolygonTestCases from './polygon';
1425
import * as RasterTestCases from './raster';
1526
import * as RefactorTestCases from './refactor';
@@ -50,3 +61,17 @@ export const TestCases = new Map<string, [string, TestCase][]>([
5061
// }));
5162
// return demo;
5263
// }
64+
65+
export const point = {
66+
...anchor,
67+
...billboard,
68+
...column,
69+
...dot,
70+
...fill,
71+
...fillImage,
72+
...iconfont,
73+
...image,
74+
...imageTextAnchor,
75+
...radar,
76+
...text,
77+
};

examples/demos/point/anchor.ts

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { PointLayer } from '@antv/l7';
2+
import type { TestCase } from '../../types';
3+
import { CaseScene } from '../../utils';
4+
5+
export const anchor: TestCase = async (options) => {
6+
const scene = await CaseScene({
7+
...options,
8+
mapConfig: {
9+
center: [121.435159, 31.256971],
10+
zoom: 14.89,
11+
},
12+
});
13+
14+
const data = await fetch(
15+
'https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json',
16+
).then((res) => res.json());
17+
18+
// 参考点位图层 - 显示原始坐标位置
19+
const referenceLayer = new PointLayer({
20+
zIndex: 10,
21+
})
22+
.source(data, {
23+
parser: {
24+
type: 'json',
25+
x: 'longitude',
26+
y: 'latitude',
27+
},
28+
})
29+
.shape('circle')
30+
.size(3)
31+
.active(false)
32+
.color('#FF4D4F')
33+
.style({
34+
opacity: 1,
35+
});
36+
37+
// 气泡图层 - 使用 anchor 属性
38+
const bubbleLayer = new PointLayer({})
39+
.source(data, {
40+
parser: {
41+
type: 'json',
42+
x: 'longitude',
43+
y: 'latitude',
44+
},
45+
})
46+
.shape('circle')
47+
.size(30)
48+
.active(false)
49+
.color('#5B8FF9')
50+
.style({
51+
stroke: '#fff',
52+
strokeWidth: 2,
53+
opacity: 0.8,
54+
anchor: 'bottom', // 气泡底部对齐到坐标位置
55+
});
56+
57+
// 文字图层 - 使用 textAnchor 属性
58+
const textLayer = new PointLayer()
59+
.source(data, {
60+
parser: {
61+
type: 'json',
62+
x: 'longitude',
63+
y: 'latitude',
64+
},
65+
})
66+
.shape('name', 'text')
67+
.color('#333')
68+
.size(14)
69+
.style({
70+
textAnchor: 'top', // 文字顶部对齐到坐标位置
71+
textOffset: [0, 0],
72+
stroke: '#fff',
73+
strokeWidth: 2,
74+
});
75+
76+
scene.addLayer(referenceLayer);
77+
scene.addLayer(bubbleLayer);
78+
scene.addLayer(textLayer);
79+
80+
// GUI 控制
81+
anchor.extendGUI = (gui) => {
82+
const config = {
83+
bubbleAnchor: 'bottom',
84+
textAnchor: 'top',
85+
};
86+
87+
const anchors = [
88+
'center',
89+
'top',
90+
'top-right',
91+
'right',
92+
'bottom-right',
93+
'bottom',
94+
'bottom-center',
95+
'bottom-left',
96+
'left',
97+
'top-left',
98+
];
99+
100+
const bubbleController = gui
101+
.add(config, 'bubbleAnchor', anchors)
102+
.name('气泡 Anchor')
103+
.onChange((value: string) => {
104+
bubbleLayer.style({
105+
anchor: value,
106+
});
107+
scene.render();
108+
});
109+
110+
const textController = gui
111+
.add(config, 'textAnchor', anchors)
112+
.name('文字 TextAnchor')
113+
.onChange((value: string) => {
114+
textLayer.style({
115+
textAnchor: value,
116+
});
117+
scene.render();
118+
});
119+
120+
return [bubbleController, textController];
121+
};
122+
123+
return scene;
124+
};

examples/demos/point/iconfont.ts

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import { PointLayer } from '@antv/l7';
2+
import type { TestCase } from '../../types';
3+
import { CaseScene } from '../../utils';
4+
5+
export const iconfont: TestCase = async (options) => {
6+
const scene = await CaseScene({
7+
...options,
8+
mapConfig: {
9+
center: [120.5, 30.45],
10+
style: 'dark',
11+
zoom: 8.1,
12+
},
13+
});
14+
15+
const config = {
16+
iconSize: 28,
17+
iconFill: 'white' as 'black' | 'white' | 'red' | 'blue' | 'green',
18+
backgroundColor: '#E81A1A',
19+
backgroundPadding: 2,
20+
backgroundRadius: 4,
21+
backgroundShape: 'circle' as 'rect' | 'circle' | 'circle-rect',
22+
backgroundFill: 'red' as 'red' | 'green' | 'blue' | 'yellow' | 'white',
23+
stroke: '#F8A3A3',
24+
strokeWidth: 0,
25+
};
26+
27+
const backgroundColorMap = {
28+
red: '#E81A1A',
29+
green: '#18C964',
30+
blue: '#1677FF',
31+
yellow: '#F5C400',
32+
white: '#FFFFFF',
33+
} as const;
34+
35+
const iconColorMap = {
36+
black: '#000000',
37+
white: '#FFFFFF',
38+
red: '#F5222D',
39+
blue: '#1677FF',
40+
green: '#18C964',
41+
} as const;
42+
43+
const fontFamily = 'iconfont';
44+
// 从 iconfont.cn 上拷贝的字体文件链接
45+
const fontPath = 'https://at.alicdn.com/t/font_2534097_ao9soua2obv.woff2?t=1622021146076';
46+
47+
// 注册字体
48+
const fontLoaded = new Promise<void>((resolve) => {
49+
scene.once('fontloaded', () => {
50+
resolve();
51+
});
52+
});
53+
scene.addFontFace(fontFamily, fontPath);
54+
55+
// 注册图标
56+
scene.addIconFonts([
57+
['smallRain', '&#xe6f7;'],
58+
['middleRain', '&#xe61c;'],
59+
['hugeRain', '&#xe6a6;'],
60+
['sun', '&#xe6da;'],
61+
['cloud', '&#xe8da;'],
62+
]);
63+
64+
// 数据
65+
const originData = [
66+
{
67+
lng: 120.5,
68+
lat: 30.2,
69+
iconType: 'sun',
70+
iconColorKey: 'icon',
71+
temperature: '22℃',
72+
weather: '晴',
73+
},
74+
{
75+
lng: 120.2,
76+
lat: 30.5,
77+
iconType: 'cloud',
78+
iconColorKey: 'icon',
79+
temperature: '24℃',
80+
weather: '多云',
81+
},
82+
{
83+
lng: 120.6,
84+
lat: 30.8,
85+
iconType: 'smallRain',
86+
iconColorKey: 'icon',
87+
temperature: '21℃',
88+
weather: '小雨',
89+
},
90+
];
91+
92+
const pointParser = {
93+
parser: {
94+
type: 'json' as const,
95+
x: 'lng',
96+
y: 'lat',
97+
},
98+
};
99+
100+
const referenceLayer = new PointLayer({ zIndex: 10 })
101+
.source(originData, pointParser)
102+
.shape('circle')
103+
.size(3)
104+
.active(false)
105+
.color('#FF4D4F')
106+
.style({ opacity: 1 });
107+
108+
const pointIconFontLayer = new PointLayer({})
109+
.source(originData, pointParser)
110+
.shape('iconType', 'text') // 使用 iconType 字段来指定图标名称
111+
.size(config.iconSize)
112+
.color('iconColorKey', [iconColorMap[config.iconFill]])
113+
.style({
114+
textAnchor: 'bottom', // 图标中心点对齐到坐标位置
115+
fontFamily,
116+
iconfont: true, // 开启 iconfont 模式
117+
backgroundColor: config.backgroundColor,
118+
backgroundPadding: config.backgroundPadding,
119+
backgroundRadius: config.backgroundRadius,
120+
backgroundShape: config.backgroundShape,
121+
stroke: config.stroke,
122+
strokeWidth: config.strokeWidth,
123+
textAllowOverlap: true,
124+
});
125+
126+
scene.addLayer(referenceLayer);
127+
128+
await fontLoaded;
129+
scene.addLayer(pointIconFontLayer);
130+
131+
iconfont.extendGUI = (gui) => {
132+
const sizeController = gui
133+
.add(config, 'iconSize', 18, 48, 1)
134+
.name('图标大小')
135+
.onChange((value: number) => {
136+
pointIconFontLayer.size(value);
137+
scene.render();
138+
});
139+
140+
const iconColorController = gui
141+
.add(config, 'iconFill', Object.keys(iconColorMap))
142+
.name('图标颜色')
143+
.onChange((value: keyof typeof iconColorMap) => {
144+
config.iconFill = value;
145+
pointIconFontLayer.color('iconColorKey', [iconColorMap[value]]);
146+
scene.render();
147+
});
148+
149+
const paddingController = gui
150+
.add(config, 'backgroundPadding', 0, 24, 1)
151+
.name('背景 Padding')
152+
.onChange((value: number) => {
153+
pointIconFontLayer.style({ backgroundPadding: value });
154+
scene.render();
155+
});
156+
157+
const radiusController = gui
158+
.add(config, 'backgroundRadius', 0, 24, 1)
159+
.name('背景圆角')
160+
.onChange((value: number) => {
161+
pointIconFontLayer.style({ backgroundRadius: value });
162+
scene.render();
163+
});
164+
165+
const shapeController = gui
166+
.add(config, 'backgroundShape', ['rect', 'circle', 'circle-rect'])
167+
.name('背景形状')
168+
.onChange((value: 'rect' | 'circle' | 'circle-rect') => {
169+
pointIconFontLayer.style({ backgroundShape: value });
170+
scene.render();
171+
});
172+
173+
const fillController = gui
174+
.add(config, 'backgroundFill', Object.keys(backgroundColorMap))
175+
.name('背景填充色')
176+
.onChange((value: keyof typeof backgroundColorMap) => {
177+
const backgroundColor = backgroundColorMap[value];
178+
config.backgroundColor = backgroundColor;
179+
pointIconFontLayer.style({ backgroundColor });
180+
scene.render();
181+
});
182+
183+
const strokeWidthController = gui
184+
.add(config, 'strokeWidth', 0, 4, 0.5)
185+
.name('描边宽度')
186+
.onChange((value: number) => {
187+
pointIconFontLayer.style({ strokeWidth: value });
188+
scene.render();
189+
});
190+
191+
return [
192+
sizeController,
193+
iconColorController,
194+
paddingController,
195+
radiusController,
196+
shapeController,
197+
fillController,
198+
strokeWidthController,
199+
];
200+
};
201+
202+
return scene;
203+
};

0 commit comments

Comments
 (0)