Skip to content

Commit db3ce04

Browse files
committed
feat: add event system for wind layer
- Add event system for data and options changes - Add WindLayerEventType and WindLayerEventCallback types - Implement addEventListener and removeEventListener methods - Add event dispatching for data and options updates - Improve texture recreation when updating wind data
1 parent 5df7a99 commit db3ce04

File tree

9 files changed

+169
-39
lines changed

9 files changed

+169
-39
lines changed

example/CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# example
22

3+
## 0.4.0
4+
5+
### Minor Changes
6+
7+
- feat: add event system for wind layer
8+
9+
- Add event system for data and options changes
10+
- Add WindLayerEventType and WindLayerEventCallback types
11+
- Implement addEventListener and removeEventListener methods
12+
- Add event dispatching for data and options updates
13+
- Improve texture recreation when updating wind data
14+
15+
### Patch Changes
16+
17+
- Updated dependencies
18+
- cesium-wind-layer@0.6.0
19+
320
## 0.3.7
421

522
### Patch Changes

example/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "example",
33
"private": true,
4-
"version": "0.3.7",
4+
"version": "0.4.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

example/public/wind2.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

example/src/components/SpeedQuery.tsx

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useEffect } from 'react';
1+
import React, { useState, useEffect, useRef } from 'react';
22
import { Typography, Space, Button } from 'antd';
33
import styled from 'styled-components';
44
import { WindDataAtLonLat, WindLayer } from 'cesium-wind-layer';
@@ -140,6 +140,7 @@ export const SpeedQuery: React.FC<SpeedQueryProps> = ({ windLayer, viewer }) =>
140140
const [queryResult, setQueryResult] = useState<WindData | null>(null);
141141
const [location, setLocation] = useState<{ lon: number; lat: number } | null>(null);
142142
const [showInterpolated, setShowInterpolated] = useState(true);
143+
const lastLocationRef = useRef<{ lon: number; lat: number } | null>(null);
143144

144145
const calculateWindDirection = (u: number, v: number): number => {
145146
// 使用 atan2 计算角度,注意参数顺序:atan2(y, x)
@@ -162,20 +163,12 @@ export const SpeedQuery: React.FC<SpeedQueryProps> = ({ windLayer, viewer }) =>
162163
};
163164

164165
useEffect(() => {
165-
if (!viewer || !windLayer) return;
166+
if (!windLayer) return;
166167

167-
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
168-
const handleClick = (movement: any) => {
169-
const cartesian = viewer.camera.pickEllipsoid(movement.position);
170-
if (cartesian) {
171-
const cartographic = Cartographic.fromCartesian(cartesian);
172-
const lon = CesiumMath.toDegrees(cartographic.longitude);
173-
const lat = CesiumMath.toDegrees(cartographic.latitude);
174-
168+
const handleDataChange = () => {
169+
if (lastLocationRef.current) {
175170
try {
176-
const result = windLayer.getDataAtLonLat(lon, lat);
177-
setLocation({ lon, lat });
178-
171+
const result = windLayer.getDataAtLonLat(lastLocationRef.current.lon, lastLocationRef.current.lat);
179172
if (result) {
180173
const data = showInterpolated ? result.interpolated : result.original;
181174
const direction = calculateWindDirection(data.u, data.v);
@@ -190,13 +183,56 @@ export const SpeedQuery: React.FC<SpeedQueryProps> = ({ windLayer, viewer }) =>
190183
}
191184
};
192185

186+
// Add event listener for data changes
187+
windLayer.addEventListener('dataChange', handleDataChange);
188+
189+
return () => {
190+
// Remove event listener when component unmounts or windLayer changes
191+
windLayer.removeEventListener('dataChange', handleDataChange);
192+
};
193+
}, [windLayer, showInterpolated]);
194+
195+
useEffect(() => {
196+
if (!location || !windLayer) return;
197+
198+
lastLocationRef.current = location;
199+
200+
try {
201+
const result = windLayer.getDataAtLonLat(location.lon, location.lat);
202+
if (result) {
203+
const data = showInterpolated ? result.interpolated : result.original;
204+
const direction = calculateWindDirection(data.u, data.v);
205+
setQueryResult({ ...result, direction });
206+
} else {
207+
setQueryResult(null);
208+
}
209+
} catch (error) {
210+
console.error('Failed to get wind data:', error);
211+
setQueryResult(null);
212+
}
213+
}, [windLayer, location, showInterpolated]);
214+
215+
useEffect(() => {
216+
if (!viewer || !windLayer) return;
217+
218+
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
219+
const handleClick = (movement: any) => {
220+
const cartesian = viewer.camera.pickEllipsoid(movement.position);
221+
if (cartesian) {
222+
const cartographic = Cartographic.fromCartesian(cartesian);
223+
const lon = CesiumMath.toDegrees(cartographic.longitude);
224+
const lat = CesiumMath.toDegrees(cartographic.latitude);
225+
setLocation({ lon, lat });
226+
}
227+
};
228+
193229
handler.setInputAction(handleClick, ScreenSpaceEventType.LEFT_CLICK);
194230
handler.setInputAction(handleClick, ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
195231

196232
return () => {
197233
handler.destroy();
198234
};
199-
}, [viewer, windLayer, showInterpolated]);
235+
}, [viewer, windLayer]);
200236

201237
const currentData = queryResult ? (showInterpolated ? queryResult.interpolated : queryResult.original) : null;
202238

example/src/pages/earth.tsx

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const defaultOptions: Partial<WindLayerOptions> = {
2727
dropRate: 0.003,
2828
particleHeight: 1000,
2929
dropRateBump: 0.01,
30-
speedFactor: 1.0,
30+
speedFactor: 0.5,
3131
lineWidth: 10.0,
3232
colors: colorSchemes[3].colors,
3333
flipY: true,
@@ -37,8 +37,10 @@ const defaultOptions: Partial<WindLayerOptions> = {
3737
export function Earth() {
3838
const viewerRef = useRef<Viewer | null>(null);
3939
const windLayerRef = useRef<WindLayer | null>(null);
40-
// Add state to trigger re-render when windLayer is ready
4140
const [, setIsWindLayerReady] = useState(false);
41+
const dataIndexRef = useRef(0);
42+
const windDataFiles = ['/wind.json', '/wind2.json'];
43+
const isFirstLoadRef = useRef(true);
4244

4345
useEffect(() => {
4446
let isComponentMounted = true;
@@ -75,18 +77,11 @@ export function Earth() {
7577
viewerRef.current.scene.verticalExaggeration = 2;
7678
// viewerRef.current.sceneModePicker.viewModel.duration = 0;
7779

78-
// Load wind data
79-
const loadWindData = async () => {
80-
// Skip if wind layer already exists or viewer is not initialized
81-
if (windLayerRef.current || !viewerRef.current) {
82-
return;
83-
}
84-
80+
const initWindLayer = async () => {
8581
try {
86-
const res = await fetch('/wind.json');
82+
const res = await fetch(windDataFiles[0]);
8783
const data = await res.json();
8884

89-
// Check if component is still mounted
9085
if (!isComponentMounted || !viewerRef.current) return;
9186

9287
const windData: WindData = {
@@ -99,7 +94,7 @@ export function Earth() {
9994
}
10095
};
10196

102-
if (windData.bounds) {
97+
if (isFirstLoadRef.current && windData.bounds) {
10398
const rectangle = Rectangle.fromDegrees(
10499
windData.bounds.west,
105100
windData.bounds.south,
@@ -110,25 +105,66 @@ export function Earth() {
110105
destination: rectangle,
111106
duration: 0,
112107
});
108+
isFirstLoadRef.current = false;
113109
}
114110

115111
const layer = new WindLayer(viewerRef.current, windData, defaultOptions);
116-
console.log('initailize windlayer', layer);
112+
113+
// Add event listeners
114+
layer.addEventListener('dataChange', (data) => {
115+
console.log('Wind data updated:', data);
116+
// Handle data change
117+
});
118+
119+
layer.addEventListener('optionsChange', (options) => {
120+
console.log('Options updated:', options);
121+
// Handle options change
122+
});
123+
117124
windLayerRef.current = layer;
118-
// Trigger re-render when windLayer is ready
119125
setIsWindLayerReady(true);
120126
} catch (error) {
121-
console.error('Failed to load wind data:', error);
127+
console.error('Failed to initialize wind layer:', error);
128+
}
129+
};
130+
131+
const updateWindData = async () => {
132+
try {
133+
const nextIndex = (dataIndexRef.current + 1) % windDataFiles.length;
134+
const res = await fetch(windDataFiles[nextIndex]);
135+
const data = await res.json();
136+
137+
if (!isComponentMounted || !windLayerRef.current) return;
138+
139+
const windData: WindData = {
140+
...data,
141+
bounds: {
142+
west: data.bbox[0],
143+
south: data.bbox[1],
144+
east: data.bbox[2],
145+
north: data.bbox[3],
146+
}
147+
};
148+
149+
windLayerRef.current.updateWindData(windData);
150+
dataIndexRef.current = nextIndex;
151+
} catch (error) {
152+
console.error('Failed to update wind data:', error);
122153
}
123154
};
124155

125-
loadWindData();
156+
// Initialize wind layer
157+
initWindLayer();
158+
159+
// Set up interval to update data
160+
const intervalId = setInterval(updateWindData, 3000);
126161

127162
return () => {
128163
isComponentMounted = false;
164+
isFirstLoadRef.current = true;
165+
clearInterval(intervalId);
129166

130167
if (windLayerRef.current) {
131-
console.log('destroy windlayer');
132168
windLayerRef.current.destroy();
133169
windLayerRef.current = null;
134170
setIsWindLayerReady(false);

packages/cesium-wind-layer/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# cesium-wind-layer
22

3+
## 0.6.0
4+
5+
### Minor Changes
6+
7+
- feat: add event system for wind layer
8+
9+
- Add event system for data and options changes
10+
- Add WindLayerEventType and WindLayerEventCallback types
11+
- Implement addEventListener and removeEventListener methods
12+
- Add event dispatching for data and options updates
13+
- Improve texture recreation when updating wind data
14+
315
## 0.5.3
416

517
### Patch Changes

packages/cesium-wind-layer/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cesium-wind-layer",
3-
"version": "0.5.3",
3+
"version": "0.6.0",
44
"publishConfig": {
55
"access": "public"
66
},

packages/cesium-wind-layer/src/index.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import { WindParticleSystem } from './windParticleSystem';
1212

1313
export * from './types';
1414

15+
type WindLayerEventType = 'dataChange' | 'optionsChange';
16+
type WindLayerEventCallback = (data: WindData | WindLayerOptions) => void;
17+
1518
export class WindLayer {
1619
private _show: boolean = true;
1720
private _resized: boolean = false;
@@ -52,6 +55,7 @@ export class WindLayer {
5255
};
5356
private _isDestroyed: boolean = false;
5457
private primitives: any[] = [];
58+
private eventListeners: Map<WindLayerEventType, Set<WindLayerEventCallback>> = new Map();
5559

5660
/**
5761
* WindLayer class for visualizing wind field data with particle animation in Cesium.
@@ -294,6 +298,8 @@ export class WindLayer {
294298
this.windData = this.processWindData(data);
295299
this.particleSystem.computing.updateWindData(this.windData);
296300
this.viewer.scene.requestRender();
301+
// Dispatch data change event
302+
this.dispatchEvent('dataChange', this.windData);
297303
}
298304

299305
/**
@@ -304,6 +310,8 @@ export class WindLayer {
304310
this.options = { ...this.options, ...options };
305311
this.particleSystem.changeOptions(options);
306312
this.viewer.scene.requestRender();
313+
// Dispatch options change event
314+
this.dispatchEvent('optionsChange', this.options);
307315
}
308316

309317
zoomTo(duration: number = 0): void {
@@ -343,6 +351,8 @@ export class WindLayer {
343351
this.remove();
344352
this.removeEventListeners();
345353
this.particleSystem.destroy();
354+
// Clear all event listeners
355+
this.eventListeners.clear();
346356
this._isDestroyed = true;
347357
}
348358

@@ -353,6 +363,21 @@ export class WindLayer {
353363
});
354364
}
355365

366+
addEventListener(type: WindLayerEventType, callback: WindLayerEventCallback) {
367+
if (!this.eventListeners.has(type)) {
368+
this.eventListeners.set(type, new Set());
369+
}
370+
this.eventListeners.get(type)?.add(callback);
371+
}
372+
373+
removeEventListener(type: WindLayerEventType, callback: WindLayerEventCallback) {
374+
this.eventListeners.get(type)?.delete(callback);
375+
}
376+
377+
private dispatchEvent(type: WindLayerEventType, data: WindData | WindLayerOptions) {
378+
this.eventListeners.get(type)?.forEach(callback => callback(data));
379+
}
380+
356381
}
357382

358-
export type { WindLayerOptions, WindData };
383+
export type { WindLayerOptions, WindData, WindLayerEventType, WindLayerEventCallback };

packages/cesium-wind-layer/src/windParticlesComputing.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,19 +208,22 @@ export class WindParticlesComputing {
208208
};
209209
}
210210

211+
private reCreateWindTextures() {
212+
this.windTextures.U.destroy();
213+
this.windTextures.V.destroy();
214+
this.createWindTextures();
215+
}
216+
211217
updateWindData(data: Required<WindData>) {
212218
this.windData = data;
213-
this.windTextures.U.copyFrom({ source: data.u.array });
214-
this.windTextures.V.copyFrom({ source: data.v.array });
219+
this.reCreateWindTextures();
215220
}
216221

217222
updateOptions(options: Partial<WindLayerOptions>) {
218223
const needUpdateWindTextures = options.flipY !== this.options.flipY;
219224
this.options = { ...this.options, ...options };
220225
if (needUpdateWindTextures) {
221-
this.windTextures.U.destroy();
222-
this.windTextures.V.destroy();
223-
this.createWindTextures();
226+
this.reCreateWindTextures();
224227
}
225228
}
226229

0 commit comments

Comments
 (0)