2323 < input id ="fits-url " type ="url " placeholder ="https://data.nasa.gov/sample.fits " aria-label ="FITS file URL " />
2424 < button type ="button " id ="load-url "> Load URL</ button >
2525 </ div >
26- < button type ="button " id ="add-marker " class ="marker-control "> Add marker</ button >
26+ < div class ="marker-actions ">
27+ < button type ="button " id ="add-marker " class ="marker-control "> Add marker</ button >
28+ < button type ="button " id ="remove-marker " class ="marker-control "> Remove marker</ button >
29+ </ div >
2730 </ div >
2831 < div class ="samples ">
2932 < span class ="samples-label "> Try a sample dataset:</ span >
@@ -93,6 +96,7 @@ <h2 id="marker-modal-title">Add marker</h2>
9396 const urlInput = document . getElementById ( 'fits-url' ) ;
9497 const loadUrlButton = document . getElementById ( 'load-url' ) ;
9598 const addMarkerButton = document . getElementById ( 'add-marker' ) ;
99+ const removeMarkerButton = document . getElementById ( 'remove-marker' ) ;
96100 const sampleButtons = Array . from ( document . querySelectorAll ( '[data-fits-url]' ) ) ;
97101 const markerModal = document . getElementById ( 'marker-modal' ) ;
98102 const markerForm = markerModal ? markerModal . querySelector ( 'form' ) : null ;
@@ -104,7 +108,9 @@ <h2 id="marker-modal-title">Add marker</h2>
104108 let aladinInstance = null ;
105109 let currentRequestId = 0 ;
106110 const markerLayersByColor = new Map ( ) ;
111+ const markerCatalogMap = new WeakMap ( ) ;
107112 let isAddingMarker = false ;
113+ let isRemovingMarker = false ;
108114 let storedStatusText = statusMessage . textContent ;
109115 let resolveMarkerDialog = null ;
110116 const MARKER_DEFAULT_COLOR = '#60A5FA' ;
@@ -398,12 +404,17 @@ <h2 id="marker-modal-title">Add marker</h2>
398404 showError ( 'The viewer is not ready yet. Please wait a moment and try again.' ) ;
399405 return ;
400406 }
407+ if ( active ) {
408+ toggleRemovalMode ( false ) ;
409+ }
401410 isAddingMarker = active ;
402- addMarkerButton . classList . toggle ( 'marker-control--active' , isAddingMarker ) ;
411+ if ( addMarkerButton ) {
412+ addMarkerButton . classList . toggle ( 'marker-control--active' , isAddingMarker ) ;
413+ }
403414 if ( isAddingMarker ) {
404415 storedStatusText = statusMessage . textContent ;
405416 statusMessage . textContent = 'Click anywhere on the map to place a marker. Press Esc to cancel.' ;
406- } else {
417+ } else if ( ! isRemovingMarker ) {
407418 statusMessage . textContent = storedStatusText ;
408419 }
409420 if ( ! isAddingMarker ) {
@@ -413,6 +424,30 @@ <h2 id="marker-modal-title">Add marker</h2>
413424 }
414425 }
415426
427+ function toggleRemovalMode ( active ) {
428+ if ( ! aladinInstance ) {
429+ showError ( 'The viewer is not ready yet. Please wait a moment and try again.' ) ;
430+ return ;
431+ }
432+ if ( active ) {
433+ toggleMarkerMode ( false ) ;
434+ }
435+ isRemovingMarker = active ;
436+ if ( removeMarkerButton ) {
437+ removeMarkerButton . classList . toggle ( 'marker-control--active' , isRemovingMarker ) ;
438+ }
439+ if ( isRemovingMarker ) {
440+ storedStatusText = statusMessage . textContent ;
441+ statusMessage . textContent = 'Click an existing marker to remove it. Press Esc to cancel.' ;
442+ window . addEventListener ( 'keydown' , cancelRemovalModeOnEscape ) ;
443+ } else {
444+ window . removeEventListener ( 'keydown' , cancelRemovalModeOnEscape ) ;
445+ if ( ! isAddingMarker ) {
446+ statusMessage . textContent = storedStatusText ;
447+ }
448+ }
449+ }
450+
416451 function cancelMarkerModeOnEscape ( event ) {
417452 if ( event . key === 'Escape' ) {
418453 if ( isMarkerDialogOpen ( ) ) {
@@ -422,6 +457,12 @@ <h2 id="marker-modal-title">Add marker</h2>
422457 }
423458 }
424459
460+ function cancelRemovalModeOnEscape ( event ) {
461+ if ( event . key === 'Escape' ) {
462+ toggleRemovalMode ( false ) ;
463+ }
464+ }
465+
425466 async function createMarkerAt ( ra , dec ) {
426467 const defaultTitle = `Marker @ RA ${ ra . toFixed ( 3 ) } , Dec ${ dec . toFixed ( 3 ) } ` ;
427468 const defaultDescription = `Coordinates: RA ${ ra . toFixed ( 3 ) } , Dec ${ dec . toFixed ( 3 ) } ` ;
@@ -444,6 +485,7 @@ <h2 id="marker-modal-title">Add marker</h2>
444485 color : validColor
445486 } ;
446487 const marker = A . marker ( ra , dec , markerOptions ) ;
488+ markerCatalogMap . set ( marker , targetLayer ) ;
447489 targetLayer . addSources ( [ marker ] ) ;
448490 aladinInstance . gotoRaDec ( ra , dec ) ;
449491 const confirmation = `${ markerOptions . popupTitle } saved at RA ${ ra . toFixed ( 3 ) } , Dec ${ dec . toFixed ( 3 ) } ` ;
@@ -479,6 +521,33 @@ <h2 id="marker-modal-title">Add marker</h2>
479521 }
480522 toggleMarkerMode ( false ) ;
481523 } ) ;
524+
525+ aladinInstance . on ( 'objectClicked' , ( source ) => {
526+ if ( ! isRemovingMarker || ! source ) {
527+ return ;
528+ }
529+ let catalog = markerCatalogMap . get ( source ) ;
530+ if ( ! catalog ) {
531+ for ( const candidate of markerLayersByColor . values ( ) ) {
532+ if ( candidate && typeof candidate . remove === 'function' ) {
533+ candidate . remove ( source ) ;
534+ catalog = candidate ;
535+ break ;
536+ }
537+ }
538+ } else {
539+ catalog . remove ( source ) ;
540+ }
541+ if ( ! catalog ) {
542+ return ;
543+ }
544+ const title = ( source . data && source . data . popupTitle ) || 'Marker' ;
545+ const confirmation = `${ title } removed.` ;
546+ statusMessage . textContent = confirmation ;
547+ storedStatusText = confirmation ;
548+ markerCatalogMap . delete ( source ) ;
549+ toggleRemovalMode ( false ) ;
550+ } ) ;
482551 } ) ;
483552
484553 initialiseSampleButtons ( ) ;
@@ -508,6 +577,11 @@ <h2 id="marker-modal-title">Add marker</h2>
508577 addMarkerButton . addEventListener ( 'click' , ( ) => {
509578 toggleMarkerMode ( ! isAddingMarker ) ;
510579 } ) ;
580+ if ( removeMarkerButton ) {
581+ removeMarkerButton . addEventListener ( 'click' , ( ) => {
582+ toggleRemovalMode ( ! isRemovingMarker ) ;
583+ } ) ;
584+ }
511585
512586 initialiseViewer ( ) ;
513587 </ script >
0 commit comments