Skip to content

Commit a1800ef

Browse files
Feat marker (#2790)
* chore: 更新dumi antv theme 版本 * feat: 升级聚合marker 样式 * fix(component): attach extData to cluster markers so getExtData() returns feature properties * feat(component): add MarkerLayer.markerOption default marker color/style/class * feat(component): add marker show/hide API and implement visibility control in Marker and MarkerLayer * fix(component): add/remove marker behavior * fix(component): marker camera handlers + cluster events * Update packages/component/src/marker-layer.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update examples/demos/components/marker-cluster.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update packages/component/src/marker-layer.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: 移除空的try catch --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent b919f0b commit a1800ef

File tree

19 files changed

+650
-387
lines changed

19 files changed

+650
-387
lines changed

examples/demos/components/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export { layerPopup } from './layer-popup';
22
export { marker } from './marker';
3+
export { markerCluster } from './marker-cluster';
4+
export { markerClusterVerify } from './marker-cluster-verify';
35
export { popup } from './popup';
46
export { swipe } from './swipe';
57
export { zoom } from './zoom';
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { Marker, MarkerLayer, Popup } from '@antv/l7';
2+
import type { TestCase } from '../../types';
3+
import { CaseScene } from '../../utils';
4+
5+
export const markerClusterVerify: TestCase = async (options) => {
6+
const scene = await CaseScene({
7+
...options,
8+
mapConfig: {
9+
center: [105.79, 30],
10+
zoom: 3,
11+
},
12+
});
13+
14+
// create a layer with markerOption defaults to verify defaults application
15+
const markerLayer = new MarkerLayer({
16+
cluster: true,
17+
markerOption: {
18+
color: '#ff5722',
19+
style: { width: '26px', height: '26px', borderRadius: '50%' },
20+
className: 'demo-marker-default',
21+
},
22+
});
23+
24+
// sample points: three clustered points and one isolated point
25+
const coords = [
26+
[105.79, 30.0],
27+
[105.795, 30.002],
28+
[105.788, 29.998],
29+
// isolated single-point far away
30+
[110.0, 35.0],
31+
];
32+
33+
const markers: Marker[] = [];
34+
35+
coords.forEach((c, i) => {
36+
const m = new Marker({
37+
// do not pass element so markerOption defaults get applied
38+
extData: { id: `m-${i}`, idx: i },
39+
} as any).setLnglat({ lng: c[0], lat: c[1] });
40+
41+
// attach a simple listener to validate event binding and show a popup
42+
m.on('click', (ev: any) => {
43+
console.log('[marker click]', i, ev.data, 'lngLat:', ev.lngLat);
44+
try {
45+
const popup = new Popup({
46+
lngLat: ev.lngLat,
47+
html: `<div style="padding:6px">id: ${ev.data?.id || 'unknown'}<br/>idx: ${ev.data?.idx}</div>`,
48+
className: `marker-popup-${i}`,
49+
});
50+
m.setPopup(popup);
51+
m.openPopup();
52+
} catch (e) {
53+
// ignore popup errors in environments without Popup implementation
54+
void e;
55+
}
56+
});
57+
58+
markers.push(m);
59+
markerLayer.addMarker(m);
60+
});
61+
62+
scene.addMarkerLayer(markerLayer);
63+
// layer-level event for cluster markers
64+
markerLayer.on &&
65+
markerLayer.on('marker:click', (ev: any) => {
66+
console.log('[layer marker click]', ev && ev.data, ev && ev.lngLat);
67+
try {
68+
const data = ev && ev.data;
69+
const popup = new Popup({
70+
lngLat: ev && ev.lngLat,
71+
html: `<div style="padding:6px">cluster: ${JSON.stringify(data)}</div>`,
72+
});
73+
ev.marker && ev.marker.setPopup && ev.marker.setPopup(popup);
74+
ev.marker && ev.marker.openPopup && ev.marker.openPopup();
75+
} catch (e) {
76+
void e;
77+
}
78+
});
79+
80+
// GUI helpers to verify behaviors
81+
markerClusterVerify.extendGUI = (gui) => {
82+
return [
83+
gui.add(
84+
{
85+
setZoom: () => scene.setZoom(6),
86+
hideFirst: () => {
87+
const m = markers[0];
88+
m.hide();
89+
},
90+
showFirst: () => {
91+
const m = markers[0];
92+
m.show();
93+
},
94+
removeFirst: () => {
95+
const m = markers[0];
96+
markerLayer.removeMarker(m);
97+
},
98+
addNew: () => {
99+
const i = markers.length;
100+
const nm = new Marker({ extData: { id: `m-${i}` } } as any).setLnglat({
101+
lng: 105.79 + Math.random() * 0.01,
102+
lat: 30 + Math.random() * 0.01,
103+
});
104+
nm.on('click', (e: any) => console.log('[new marker click]', e.data));
105+
markers.push(nm);
106+
markerLayer.addMarker(nm);
107+
},
108+
},
109+
'setZoom',
110+
),
111+
];
112+
};
113+
114+
scene.render();
115+
116+
// Debug helper: wait until cluster markers are rendered, then attach raw DOM listeners
117+
const attachDomListeners = () => {
118+
const items = markerLayer.getMarkers();
119+
if (!items || items.length === 0) return false;
120+
items.forEach((mm: any, idx: number) => {
121+
try {
122+
const el = mm.getElement && mm.getElement();
123+
if (el) {
124+
// avoid duplicate listeners
125+
if (!(el as any).__verify_click_attached) {
126+
el.addEventListener('click', () => {
127+
console.log('[DOM click]', idx, mm.getExtData && mm.getExtData());
128+
});
129+
(el as any).__verify_click_attached = true;
130+
}
131+
}
132+
} catch (e) {
133+
void e;
134+
}
135+
});
136+
return true;
137+
};
138+
139+
const maxTry = 20;
140+
let tryCount = 0;
141+
const interval = setInterval(() => {
142+
tryCount += 1;
143+
const ok = attachDomListeners();
144+
if (ok || tryCount >= maxTry) {
145+
clearInterval(interval);
146+
}
147+
}, 300);
148+
149+
return scene;
150+
};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Marker, MarkerLayer } from '@antv/l7';
2+
import type { TestCase } from '../../types';
3+
import { CaseScene } from '../../utils';
4+
5+
export const markerCluster: TestCase = async (options) => {
6+
const scene = await CaseScene({
7+
...options,
8+
mapConfig: {
9+
center: [105.790327, 30],
10+
zoom: 2,
11+
},
12+
});
13+
14+
const resp = await fetch(
15+
'https://gw.alipayobjects.com/os/basement_prod/d3564b06-670f-46ea-8edb-842f7010a7c6.json',
16+
);
17+
const data = await resp.json();
18+
19+
const markerLayer = new MarkerLayer({
20+
cluster: true,
21+
});
22+
23+
for (let i = 0; i < data.features.length; i++) {
24+
const { coordinates } = data.features[i].geometry;
25+
// create marker with configurable style and color
26+
const color = i % 2 === 0 ? '#ff5722' : '#5B8FF9';
27+
const marker = new Marker({
28+
color,
29+
style: {
30+
width: '28px',
31+
height: '28px',
32+
},
33+
}).setLnglat({
34+
lng: coordinates[0],
35+
lat: coordinates[1],
36+
});
37+
markerLayer.addMarker(marker);
38+
}
39+
40+
scene.addMarkerLayer(markerLayer);
41+
42+
scene.render();
43+
44+
markerCluster.extendGUI = (gui) => {
45+
return [
46+
gui.add(
47+
{
48+
setZoom: () => {
49+
scene.setZoom(5);
50+
},
51+
},
52+
'setZoom',
53+
),
54+
];
55+
};
56+
57+
return scene;
58+
};

packages/component/src/interface.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,13 @@ export interface IMarkerStyleOption {
1515
export interface IMarkerLayerOption {
1616
cluster: boolean;
1717
clusterOption: Partial<IMarkerStyleOption>;
18+
/**
19+
* Default marker options applied to markers added to this layer when not overridden per-marker.
20+
* Example: { color: '#ff0000', style: { width: '24px', height: '24px' }, className: 'my-marker' }
21+
*/
22+
markerOption?: Partial<{
23+
color?: string;
24+
style?: { [key: string]: any };
25+
className?: string;
26+
}>;
1827
}

0 commit comments

Comments
 (0)