@@ -17,7 +17,7 @@ interface CyclonePoint extends StormLifecycleRead {
1717
1818function createCycloneIcon ( status : "past" | "forecast" ) {
1919 const iconHtml = ReactDOMServer . renderToString (
20- < Icon path = { mdiWeatherHurricaneOutline } size = { 1 } color = "white" /> ,
20+ < Icon path = { mdiWeatherHurricaneOutline } size = { 1 } color = "white" />
2121 ) ;
2222 const backgroundColor = status === "forecast" ? "#FF2D55D0" : "#757474D0" ;
2323
@@ -44,7 +44,18 @@ function createCycloneIcon(status: "past" | "forecast") {
4444export function TropicalCyclonePage ( ) {
4545 const [ cycloneData , setCycloneData ] = useState < CyclonePoint [ ] > ( [ ] ) ;
4646 const [ loading , setLoading ] = useState ( true ) ;
47- const { selectedDate, setSelectedDate, selectedHour, setSliderMarks, selectedStormId, setSelectedStormId, setIsStormSelectorOpen } = useWeatherMapStore ( ) ;
47+
48+ const {
49+ selectedDate,
50+ setSelectedDate,
51+ selectedHour,
52+ setSliderMarks,
53+ selectedStormId,
54+ setSelectedStormId,
55+ setIsStormSelectorOpen,
56+ setMapCenter,
57+ } = useWeatherMapStore ( ) ;
58+
4859 const [ data , setData ] = useState < StormLifecycleRead [ ] > ( [ ] ) ;
4960
5061 useEffect ( ( ) => {
@@ -66,12 +77,16 @@ export function TropicalCyclonePage() {
6677 storm_ids : [ selectedStormId || 0 ] ,
6778 } ) ;
6879
69- const data = stormLifecycles . data . sort ( ( a , b ) => {
70- return new Date ( a . timestamp ) . getTime ( ) - new Date ( b . timestamp ) . getTime ( ) ;
71- } ) . map ( ( u ) => {
72- u . timestamp = `${ u . timestamp } Z` ;
73- return u ;
74- } ) ;
80+ const data = stormLifecycles . data
81+ . sort ( ( a , b ) => {
82+ return (
83+ new Date ( a . timestamp ) . getTime ( ) - new Date ( b . timestamp ) . getTime ( )
84+ ) ;
85+ } )
86+ . map ( ( u ) => {
87+ u . timestamp = `${ u . timestamp } Z` ;
88+ return u ;
89+ } ) ;
7590
7691 if ( data . length === 0 ) {
7792 setSelectedDate ( new Date ( ) ) ;
@@ -80,18 +95,18 @@ export function TropicalCyclonePage() {
8095 }
8196
8297 const startDate = new Date ( data [ 0 ] . timestamp ) ;
83- startDate . setDate ( startDate . getDate ( ) - 0 ) ;
8498
8599 setSelectedDate ( startDate || new Date ( ) ) ;
86100
101+ setMapCenter ( [ data [ 0 ] . latitude , data [ 0 ] . longitude ] ) ;
102+
87103 setData ( data ) ;
88104 } ;
89105 fetchCycloneData ( ) ;
90- } , [ selectedStormId , setSelectedDate ] ) ;
106+ } , [ selectedStormId , setSelectedDate , setMapCenter ] ) ;
91107
92108 useEffect ( ( ) => {
93- if ( data . length === 0 )
94- return ;
109+ if ( data . length === 0 ) return ;
95110
96111 // const maxDate = dayjs(data[data.length - 1].timestamp);
97112 // const minDate = dayjs(data[0].timestamp);
@@ -100,47 +115,51 @@ export function TropicalCyclonePage() {
100115 // return;
101116 // }
102117
103- const sliderMarks = data . filter ( ( item : StormLifecycleRead ) => {
104- return dayjs ( item . timestamp ) . isSame ( selectedDate , "day" ) ;
105- } ) . reduce ( ( marks : Record < number , string > , item : StormLifecycleRead ) => {
106- const hours = dayjs ( item . timestamp ) . hour ( ) ;
107- marks [ hours ] = `${ hours } :00` ;
108- return marks ;
109- } , { } ) ;
118+ const sliderMarks = data
119+ . filter ( ( item : StormLifecycleRead ) => {
120+ return dayjs ( item . timestamp ) . isSame ( selectedDate , "day" ) ;
121+ } )
122+ . reduce ( ( marks : Record < number , string > , item : StormLifecycleRead ) => {
123+ const hours = dayjs ( item . timestamp ) . hour ( ) ;
124+ marks [ hours ] = `${ hours } :00` ;
125+ return marks ;
126+ } , { } ) ;
110127
111128 setSliderMarks ( sliderMarks ) ;
112129
113- // Determine the reference time based on selectedDate and selectedHour
114130 let referenceTime : Date ;
115131 if ( selectedDate ) {
116132 referenceTime = new Date ( selectedDate ) ;
117133 referenceTime . setHours ( selectedHour , 0 , 0 , 0 ) ;
118- }
119- else {
134+ } else {
120135 referenceTime = new Date ( ) ;
121136 }
122137
123138 // Transform data and determine status
124- const transformedData : CyclonePoint [ ] = data . map ( ( item : StormLifecycleRead , index : number ) => {
125- const timestamp = new Date ( item . timestamp ) ;
126- const isForecast = timestamp > referenceTime ;
127-
128- // Calculate radius for forecast points with increment
129- let radius = 0 ;
130- if ( isForecast ) {
131- // Base radius of 50km, increasing by 20km for each subsequent forecast point
132- const forecastIndex = data . slice ( 0 , index + 1 ) . filter ( ( d : StormLifecycleRead ) =>
133- new Date ( d . timestamp ) > referenceTime ,
134- ) . length ;
135- radius = 8000 + 20000 * Math . log ( forecastIndex + 1 ) ;
136- }
139+ const transformedData : CyclonePoint [ ] = data . map (
140+ ( item : StormLifecycleRead , index : number ) => {
141+ const timestamp = new Date ( item . timestamp ) ;
142+ const isForecast = timestamp > referenceTime ;
137143
138- return {
139- ...item ,
140- status : isForecast ? "forecast" : "past" ,
141- radius,
142- } ;
143- } ) ;
144+ // Calculate radius for forecast points with increment
145+ let radius = 0 ;
146+ if ( isForecast ) {
147+ // Base radius of 50km, increasing by 20km for each subsequent forecast point
148+ const forecastIndex = data
149+ . slice ( 0 , index + 1 )
150+ . filter (
151+ ( d : StormLifecycleRead ) => new Date ( d . timestamp ) > referenceTime
152+ ) . length ;
153+ radius = 8000 + 20000 * Math . log ( forecastIndex + 1 ) ;
154+ }
155+
156+ return {
157+ ...item ,
158+ status : isForecast ? "forecast" : "past" ,
159+ radius,
160+ } ;
161+ }
162+ ) ;
144163
145164 // Filter data based on selected time frame
146165 // Show past points up to the reference time and forecast points after
@@ -156,8 +175,7 @@ export function TropicalCyclonePage() {
156175 // Show past points that are close to the reference time (within a reasonable range)
157176 // This helps show the cyclone's path leading up to the selected time
158177 return itemTime <= referenceTime ;
159- }
160- else {
178+ } else {
161179 // Show forecast points after the reference time
162180 return itemTime > referenceTime ;
163181 }
@@ -171,10 +189,10 @@ export function TropicalCyclonePage() {
171189 }
172190
173191 const allPositions = cycloneData . map (
174- p => [ p . latitude , p . longitude ] as [ number , number ] ,
192+ ( p ) => [ p . latitude , p . longitude ] as [ number , number ]
175193 ) ;
176194 const forecastStartIndex = cycloneData . findIndex (
177- p => p . status === "forecast" ,
195+ ( p ) => p . status === "forecast"
178196 ) ;
179197
180198 // Xử lý trường hợp selectedDate nằm ngoài khoảng dữ liệu
@@ -184,12 +202,10 @@ export function TropicalCyclonePage() {
184202 if ( forecastStartIndex === - 1 ) {
185203 // Tất cả là past (selectedDate sau tất cả các điểm)
186204 pastPath = allPositions ;
187- }
188- else if ( forecastStartIndex === 0 ) {
205+ } else if ( forecastStartIndex === 0 ) {
189206 // Tất cả là forecast (selectedDate trước tất cả các điểm)
190207 forecastPath = allPositions ;
191- }
192- else {
208+ } else {
193209 // Trường hợp bình thường: có cả past và forecast
194210 pastPath = allPositions . slice ( 0 , forecastStartIndex + 1 ) ;
195211 forecastPath = allPositions . slice ( forecastStartIndex ) ;
@@ -222,7 +238,6 @@ export function TropicalCyclonePage() {
222238 fillOpacity : 0.4 ,
223239 fillRule : "evenodd" ,
224240 } }
225-
226241 />
227242 ) }
228243
0 commit comments