Skip to content

Commit 151bac0

Browse files
committed
Fix "Add marker" button
1 parent 888d43d commit 151bac0

1 file changed

Lines changed: 247 additions & 4 deletions

File tree

web/front_end.js

Lines changed: 247 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,30 @@ const fileInput = document.getElementById('fits-file');
1111
const urlInput = document.getElementById('fits-url');
1212
const loadUrlButton = document.getElementById('load-url');
1313
const sampleButtons = Array.from(document.querySelectorAll('[data-fits-url]'));
14+
const addMarkerButton = document.getElementById('add-marker');
15+
const removeMarkerButton = document.getElementById('remove-marker');
16+
const markerModal = document.getElementById('marker-modal');
17+
const markerForm = markerModal ? markerModal.querySelector('form') : null;
18+
const markerCancelButton = markerModal ? markerModal.querySelector('[data-action="cancel"]') : null;
19+
const markerTitleInput = document.getElementById('marker-title');
20+
const markerDescriptionInput = document.getElementById('marker-description');
21+
const markerColorInput = document.getElementById('marker-color');
1422

1523
let aladinInstance = null;
1624
let currentRequestId = 0;
25+
let markerLayer = null;
26+
let markerMode = 'idle';
27+
let pendingMarkerPosition = null;
28+
let storedStatusText = null;
29+
const placedMarkers = [];
30+
let lastStatusSnapshot = { label: null, fov: null };
31+
32+
if (addMarkerButton) {
33+
addMarkerButton.disabled = true;
34+
}
35+
if (removeMarkerButton) {
36+
removeMarkerButton.disabled = true;
37+
}
1738

1839
const DEFAULT_SAMPLE = sampleButtons.length
1940
? {
@@ -34,6 +55,10 @@ function clearError() {
3455
}
3556

3657
function updateStatus(label, fov) {
58+
lastStatusSnapshot = {
59+
label: label ?? null,
60+
fov: Number.isFinite(fov) ? fov : null
61+
};
3762
const parts = [];
3863
if (label) {
3964
parts.push(`Source: ${label}`);
@@ -45,6 +70,22 @@ function updateStatus(label, fov) {
4570
statusMessage.textContent = parts.join(' · ') || 'Ready to load a FITS image.';
4671
}
4772

73+
function setTemporaryStatus(message) {
74+
if (storedStatusText === null) {
75+
storedStatusText = statusMessage.textContent;
76+
}
77+
statusMessage.textContent = message;
78+
}
79+
80+
function restoreStatus() {
81+
if (storedStatusText !== null) {
82+
statusMessage.textContent = storedStatusText;
83+
storedStatusText = null;
84+
} else {
85+
updateStatus(lastStatusSnapshot.label, lastStatusSnapshot.fov);
86+
}
87+
}
88+
4889
function focusOnImage(ra, dec, fov) {
4990
if (Number.isFinite(ra) && Number.isFinite(dec)) {
5091
aladinInstance.gotoRaDec(ra, dec);
@@ -81,6 +122,173 @@ function deriveLabel(source) {
81122
return 'Custom FITS';
82123
}
83124

125+
function ensureMarkerLayer() {
126+
if (!aladinInstance || markerLayer) {
127+
return markerLayer;
128+
}
129+
markerLayer = A.catalog({
130+
name: 'Annotations',
131+
shape: 'circle',
132+
sourceSize: 18,
133+
color: '#60A5FA'
134+
});
135+
aladinInstance.addCatalog(markerLayer);
136+
return markerLayer;
137+
}
138+
139+
function updateRemoveMarkerState() {
140+
if (removeMarkerButton) {
141+
removeMarkerButton.disabled = placedMarkers.length === 0;
142+
}
143+
}
144+
145+
function exitMarkerFlow() {
146+
markerMode = 'idle';
147+
if (addMarkerButton) {
148+
addMarkerButton.classList.remove('marker-control--active');
149+
addMarkerButton.disabled = !aladinInstance;
150+
}
151+
pendingMarkerPosition = null;
152+
restoreStatus();
153+
}
154+
155+
function openMarkerModal() {
156+
if (!markerModal || !markerForm) {
157+
return;
158+
}
159+
markerModal.classList.remove('hidden');
160+
if (markerTitleInput) {
161+
markerTitleInput.focus();
162+
}
163+
document.addEventListener('keydown', handleModalKeydown);
164+
}
165+
166+
function resetMarkerForm() {
167+
if (!markerForm) {
168+
return;
169+
}
170+
markerForm.reset();
171+
if (markerColorInput) {
172+
markerColorInput.value = '#60A5FA';
173+
}
174+
}
175+
176+
function closeMarkerModal() {
177+
if (!markerModal) {
178+
return;
179+
}
180+
markerModal.classList.add('hidden');
181+
document.removeEventListener('keydown', handleModalKeydown);
182+
resetMarkerForm();
183+
exitMarkerFlow();
184+
}
185+
186+
function handleModalKeydown(event) {
187+
if (event.key === 'Escape') {
188+
event.preventDefault();
189+
closeMarkerModal();
190+
}
191+
}
192+
193+
function extractCoordinates(event) {
194+
const candidates = [event, event?.data];
195+
for (const candidate of candidates) {
196+
if (!candidate) {
197+
continue;
198+
}
199+
const ra = Number(candidate.ra ?? candidate.lon ?? candidate.lng ?? candidate.alpha);
200+
const dec = Number(candidate.dec ?? candidate.lat ?? candidate.beta);
201+
if (Number.isFinite(ra) && Number.isFinite(dec)) {
202+
return { ra, dec };
203+
}
204+
}
205+
return null;
206+
}
207+
208+
function handleSkyClick(event) {
209+
if (markerMode !== 'armed') {
210+
return;
211+
}
212+
const coordinates = extractCoordinates(event);
213+
if (!coordinates) {
214+
return;
215+
}
216+
markerMode = 'pending';
217+
pendingMarkerPosition = coordinates;
218+
if (addMarkerButton) {
219+
addMarkerButton.classList.remove('marker-control--active');
220+
addMarkerButton.disabled = true;
221+
}
222+
openMarkerModal();
223+
}
224+
225+
function startMarkerPlacement() {
226+
if (!aladinInstance) {
227+
return;
228+
}
229+
ensureMarkerLayer();
230+
markerMode = 'armed';
231+
if (addMarkerButton) {
232+
addMarkerButton.classList.add('marker-control--active');
233+
addMarkerButton.disabled = false;
234+
}
235+
setTemporaryStatus('Click on the sky map to choose where the new marker should be placed.');
236+
}
237+
238+
function handleMarkerSubmission(event) {
239+
event.preventDefault();
240+
if (!pendingMarkerPosition || !markerLayer) {
241+
closeMarkerModal();
242+
return;
243+
}
244+
const title = markerTitleInput?.value.trim() || `Marker ${placedMarkers.length + 1}`;
245+
const description = markerDescriptionInput?.value.trim() || '';
246+
const color = markerColorInput?.value || '#60A5FA';
247+
const markerOptions = {
248+
popupTitle: title,
249+
popupDesc: description
250+
};
251+
if (color) {
252+
markerOptions.color = color;
253+
}
254+
try {
255+
const marker = A.marker(pendingMarkerPosition.ra, pendingMarkerPosition.dec, markerOptions);
256+
markerLayer.addSources([marker]);
257+
placedMarkers.push(marker);
258+
updateRemoveMarkerState();
259+
} catch (error) {
260+
console.error('Unable to add marker', error);
261+
showError('We could not create the marker. Please try again.');
262+
}
263+
closeMarkerModal();
264+
}
265+
266+
function removeLatestMarker() {
267+
if (!markerLayer || placedMarkers.length === 0) {
268+
return;
269+
}
270+
const marker = placedMarkers.pop();
271+
try {
272+
if (typeof markerLayer.remove === 'function') {
273+
markerLayer.remove(marker);
274+
} else if (typeof markerLayer.removeSources === 'function') {
275+
markerLayer.removeSources([marker]);
276+
} else if (typeof markerLayer.removeSource === 'function') {
277+
markerLayer.removeSource(marker);
278+
} else if (typeof markerLayer.removeAll === 'function') {
279+
markerLayer.removeAll();
280+
placedMarkers.length = 0;
281+
}
282+
} catch (error) {
283+
console.warn('Unable to remove marker individually, clearing all markers', error);
284+
if (typeof markerLayer.removeAll === 'function') {
285+
markerLayer.removeAll();
286+
placedMarkers.length = 0;
287+
}
288+
}
289+
updateRemoveMarkerState();
290+
}
291+
84292
function loadFits(source, options = {}) {
85293
if (!aladinInstance || !source) {
86294
return;
@@ -198,10 +406,14 @@ async function initialiseViewer() {
198406
target: 'M 31'
199407
});
200408

201-
var marker1 = A.marker(0, 0, {popupTitle: "Test", popupDesc: "TEST"});
202-
var markerLayer = A.catalog();
203-
aladinInstance.addCatalog(markerLayer);
204-
markerLayer.addSources([marker1]);
409+
ensureMarkerLayer();
410+
if (addMarkerButton) {
411+
addMarkerButton.disabled = false;
412+
}
413+
updateRemoveMarkerState();
414+
if (typeof aladinInstance.on === 'function') {
415+
aladinInstance.on('click', handleSkyClick);
416+
}
205417
});
206418

207419
initialiseSampleButtons();
@@ -234,4 +446,35 @@ openComparisonButton.addEventListener('click', () => {
234446
console.log(aladinInstance.getBaseImageLayer());
235447
});
236448

449+
if (addMarkerButton) {
450+
addMarkerButton.addEventListener('click', () => {
451+
if (!aladinInstance) {
452+
showError('The FITS viewer is still initialising. Please wait a moment and try again.');
453+
return;
454+
}
455+
if (markerMode === 'armed') {
456+
exitMarkerFlow();
457+
return;
458+
}
459+
startMarkerPlacement();
460+
});
461+
}
462+
463+
if (markerCancelButton) {
464+
markerCancelButton.addEventListener('click', (event) => {
465+
event.preventDefault();
466+
closeMarkerModal();
467+
});
468+
}
469+
470+
if (markerForm) {
471+
markerForm.addEventListener('submit', handleMarkerSubmission);
472+
}
473+
474+
if (removeMarkerButton) {
475+
removeMarkerButton.addEventListener('click', () => {
476+
removeLatestMarker();
477+
});
478+
}
479+
237480
initialiseViewer();

0 commit comments

Comments
 (0)