@@ -357,6 +357,8 @@ class MapManager {
357357 mapManager . hideLoader ( ) ;
358358 mapManager . state . loading = false ;
359359 }
360+ const acePanel = document . getElementById ( "ace-results" ) ;
361+ if ( acePanel ) acePanel . classList . add ( "hidden" ) ;
360362 }
361363
362364 handleCustomMapUpload ( event ) {
@@ -552,6 +554,50 @@ function normalizeLongitude(lng) {
552554 return ( ( lng + 180 ) % 360 + 360 ) % 360 - 180 ;
553555}
554556
557+ // ACE helpers
558+ function computeACEByStorm ( points ) {
559+ const groups = points . reduce ( ( acc , p ) => {
560+ const k = p . name || "Unnamed" ;
561+ ( acc [ k ] ??= [ ] ) . push ( p ) ;
562+ return acc ;
563+ } , { } ) ;
564+ const storms = [ ] ;
565+ let total = 0 ;
566+
567+ Object . entries ( groups ) . forEach ( ( [ name , arr ] ) => {
568+ let sum = 0 , pts = 0 , tsPts = 0 ;
569+ arr . forEach ( p => {
570+ const v = Number ( p . speed ) ;
571+ if ( ! Number . isFinite ( v ) ) return ;
572+ pts ++ ;
573+ if ( v >= 34 ) {
574+ const v5 = Math . round ( v / 5 ) * 5 ; // NOAA convention
575+ sum += v5 * v5 ;
576+ tsPts ++ ;
577+ }
578+ } ) ;
579+ const ace = + ( sum / 10000 ) . toFixed ( 2 ) ;
580+ storms . push ( { name, ace, points : pts , tsPoints : tsPts } ) ;
581+ total += ace ;
582+ } ) ;
583+
584+ storms . sort ( ( a , b ) => b . ace - a . ace ) ;
585+ return { totalAce : + total . toFixed ( 2 ) , storms } ;
586+ }
587+
588+ function renderACEResults ( ace ) {
589+ const container = document . getElementById ( "ace-results" ) ;
590+ if ( ! container ) return ;
591+ container . classList . remove ( "hidden" ) ;
592+ container . innerHTML = `
593+ <h3 style="margin:.25rem 0;">ACE</h3>
594+ <div class="ace-total">Total: ${ ace . totalAce } </div>
595+ <ul class="ace-list">
596+ ${ ace . storms . map ( s => `<li>${ s . name || "Unnamed" } : ${ s . ace } <span>(pts: ${ s . tsPoints } /${ s . points } )</span></li>` ) . join ( "" ) }
597+ </ul>
598+ ` ;
599+ }
600+
555601// determine point type (tropical/subtropical/extratropical) from available fields
556602function getPointType ( point ) {
557603 const normalizeType = ( t ) => {
@@ -575,6 +621,10 @@ function createMap(data, accessible) {
575621 const imageContainer = elements . imageContainer ;
576622 const smallerDotsCheckbox = document . getElementById ( "smaller-dots" ) ;
577623
624+ // Hide ACE panel while generating to avoid stale values
625+ const acePanel = document . getElementById ( "ace-results" ) ;
626+ if ( acePanel ) acePanel . classList . add ( "hidden" ) ;
627+
578628 closeButton . classList . remove ( "hidden" ) ;
579629 output . classList . add ( "hidden" ) ;
580630 loader . classList . remove ( "hidden" ) ;
@@ -803,6 +853,13 @@ function createMap(data, accessible) {
803853 loader . classList . add ( "hidden" ) ;
804854 output . classList . remove ( "hidden" ) ;
805855
856+ try {
857+ const ace = computeACEByStorm ( data ) ;
858+ renderACEResults ( ace ) ;
859+ } catch ( e ) {
860+ console . warn ( "ACE calculation failed:" , e ) ;
861+ }
862+
806863 // if map generation is successful, hide the loader icon
807864 mapManager . hideLoader ( ) ;
808865 mapManager . updateStatus ( 'success' ) ;
@@ -815,5 +872,8 @@ function createMap(data, accessible) {
815872 mapManager . hideLoader ( ) ;
816873 mapManager . updateStatus ( 'error' ) ;
817874 mapManager . state . loading = false ;
875+
876+ const acePanel = document . getElementById ( "ace-results" ) ;
877+ if ( acePanel ) acePanel . classList . add ( "hidden" ) ;
818878 } ) ;
819879}
0 commit comments