Loading noctua/api/fits_image.py +2 −2 Original line number Diff line number Diff line Loading @@ -313,8 +313,8 @@ async def image_tile(station, camera): h, w = data.shape[:2] cx = request.args.get('cx', default=w // 2, type=int) cy = request.args.get('cy', default=h // 2, type=int) rx = request.args.get('rx', default=15, type=int) # Default 15 half-width (30 total) ry = request.args.get('ry', default=10, type=int) # Default 10 half-height (20 total) rx = request.args.get('rx', default=50, type=int) # Default 15 half-width (30 total) ry = request.args.get('ry', default=50, type=int) # Default 10 half-height (20 total) x0 = max(0, cx - rx) x1 = min(w, cx + rx) Loading noctua/web/pages/viewer.html +16 −25 Original line number Diff line number Diff line Loading @@ -37,7 +37,7 @@ style="background:#000; overflow:hidden; border-radius:6px;"> <canvas class="cv-principale d-block" width="800" height="800" style="width:100%; aspect-ratio:1/1; cursor:crosshair; style="width:100%; cursor:crosshair; transform-origin:0 0;"> </canvas> Loading Loading @@ -95,23 +95,14 @@ <div class="col-xl-3 col-lg-4 d-flex flex-column gap-2"> {# b1 — Panoramic #} <div> <div class="text-muted small mb-1" style="font-size:.65rem; letter-spacing:.08em; text-transform:uppercase;"> Panoramic </div> <div class="position-relative" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-panoramic d-block" width="256" height="256" style="width:100%; height:auto;"></canvas> </div> </div> <div class="row"> {# b2 — Explore (zoom crop) #} <div> <div class="text-muted small mb-1" style="font-size:.65rem; letter-spacing:.08em; text-transform:uppercase;"> Explore <div class="col-6 ps-2" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-explore d-block" width="256" height="256" style="width:100%; image-rendering:pixelated;"></canvas> </div> <div style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-explore d-block" width="256" height="256" style="width:100%; image-rendering:pixelated;"></canvas> <div class="col-6 ps-2" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-panoramic d-block" width="256" height="256" style="width:100%; height:auto;"></canvas> </div> <!-- NEW: Input for Explore size --> Loading @@ -121,7 +112,7 @@ style="font-size:.65rem; padding: 2px 6px; background:#1a1a1a; border-color:#333; color:#aaa;">Box</span> <input type="number" class="ctrl-explore-w form-control form-control-sm" <input type="number" class="ctrl-explore-w form-control form-control-sm" value="70" style="background:#111; border-color:#333; font-size:.7rem; color:#b0ffb0;" box="100" step="10"> </div> </div> Loading @@ -136,7 +127,7 @@ <div class="position-relative" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-teccam d-block" width="256" height="256" style="width:100%; aspect-ratio:1/1;"></canvas> style="width:100%"></canvas> <svg class="overlay-teccam position-absolute top-0 start-0 w-100 h-100" style="pointer-events:none; overflow:visible;" xmlns="http://www.w3.org/2000/svg"> </svg> Loading noctua/web/static/js/viewer/fits-viewer.js +57 −24 Original line number Diff line number Diff line Loading @@ -54,7 +54,8 @@ export class FitsViewer { infoSci: root.querySelector('.info-sci'), infoTec: root.querySelector('.info-tec'), crosshair: root.querySelector('.cv-crosshair'), canvasExploreW: root.querySelector('.ctrl-explore-w'), expW: root.querySelector('.ctrl-explore-w'), expH: root.querySelector('.ctrl-explore-h'), }; this._sci = { Loading Loading @@ -325,39 +326,71 @@ export class FitsViewer { * Resolves when the tile is fetched and drawn. */ async _fetchExplore(imgX, imgY) { async _fetchExplore(x, y) { const camId = 'scicam'; const url = this._url(`/viewer/${this.station}/${camId}/tile`, { cx: imgX, cy: imgY, r: TILE_HALF }); try { const { data, w, h } = await this._fetchTile(url); // Extract the center pixel value from the Float32Array const centerIdx = Math.floor(h / 2) * w + Math.floor(w / 2); const pixelValue = data[centerIdx]; const w = parseInt(this._el.expW.value); const h = w // parseInt(this._el.expH.value); const url = `${this.api}/viewer/${this.station}/${camId}/tile?cx=${x}&cy=${y}&rx=${w/2}&ry=${h/2}`; this._updatePixelInfo(imgX, imgY, pixelValue); const resp = await fetch(url); const buf = await resp.arrayBuffer(); const head = new Int32Array(buf, 0, 2); const data = new Float32Array(buf, 8); if (!this._exploreGL) { this._exploreGL = new GLRenderer(this._el.canvasExplore); this._exploreGL.setColormap(COLORMAPS[this._el.selColormap?.value || 'viridis']); } // if (!this._exploreGL) this._exploreGL = new GLRenderer(this._el.cvExp); this._exploreGL.upload(data, w, h); this._exploreGL.setRange(this._sci.vmin, this._sci.vmax); this._exploreGL.render(true); } catch { // Ignore if FITS tile is not available } this._el.infoSci.textContent = `X: ${x} Y: ${y} | Val: ${data[Math.floor(data.length/2)].toFixed(1)}`; } // async _fetchExplore(imgX, imgY) { // const w = parseInt(this._el.expW.value); // const h = w // /// const h = parseInt(this._el.expH.value); // const camId = 'scicam'; // const url = this._url(`/viewer/${this.station}/${camId}/tile`, { // cx: imgX, // cy: imgY, // rx: w/2, // ry: h/2, // //r: TILE_HALF // }); // try { // const { data, w, h } = await this._fetchTile(url); // // Extract the center pixel value from the Float32Array // const centerIdx = Math.floor(h / 2) * w + Math.floor(w / 2); // const pixelValue = data[centerIdx]; // this._updatePixelInfo(imgX, imgY, pixelValue); // if (!this._exploreGL) { // this._exploreGL = new GLRenderer(this._el.canvasExplore); // this._exploreGL.setColormap(COLORMAPS[this._el.selColormap?.value || 'viridis']); // } // this._exploreGL.upload(data, w, h); // this._exploreGL.setRange(this._sci.vmin, this._sci.vmax); // this._exploreGL.render(true); // } catch { // // Ignore if FITS tile is not available // } // } /** * Update the UI badge with image dimensions, cursor position, and pixel value. * Loading Loading
noctua/api/fits_image.py +2 −2 Original line number Diff line number Diff line Loading @@ -313,8 +313,8 @@ async def image_tile(station, camera): h, w = data.shape[:2] cx = request.args.get('cx', default=w // 2, type=int) cy = request.args.get('cy', default=h // 2, type=int) rx = request.args.get('rx', default=15, type=int) # Default 15 half-width (30 total) ry = request.args.get('ry', default=10, type=int) # Default 10 half-height (20 total) rx = request.args.get('rx', default=50, type=int) # Default 15 half-width (30 total) ry = request.args.get('ry', default=50, type=int) # Default 10 half-height (20 total) x0 = max(0, cx - rx) x1 = min(w, cx + rx) Loading
noctua/web/pages/viewer.html +16 −25 Original line number Diff line number Diff line Loading @@ -37,7 +37,7 @@ style="background:#000; overflow:hidden; border-radius:6px;"> <canvas class="cv-principale d-block" width="800" height="800" style="width:100%; aspect-ratio:1/1; cursor:crosshair; style="width:100%; cursor:crosshair; transform-origin:0 0;"> </canvas> Loading Loading @@ -95,23 +95,14 @@ <div class="col-xl-3 col-lg-4 d-flex flex-column gap-2"> {# b1 — Panoramic #} <div> <div class="text-muted small mb-1" style="font-size:.65rem; letter-spacing:.08em; text-transform:uppercase;"> Panoramic </div> <div class="position-relative" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-panoramic d-block" width="256" height="256" style="width:100%; height:auto;"></canvas> </div> </div> <div class="row"> {# b2 — Explore (zoom crop) #} <div> <div class="text-muted small mb-1" style="font-size:.65rem; letter-spacing:.08em; text-transform:uppercase;"> Explore <div class="col-6 ps-2" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-explore d-block" width="256" height="256" style="width:100%; image-rendering:pixelated;"></canvas> </div> <div style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-explore d-block" width="256" height="256" style="width:100%; image-rendering:pixelated;"></canvas> <div class="col-6 ps-2" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-panoramic d-block" width="256" height="256" style="width:100%; height:auto;"></canvas> </div> <!-- NEW: Input for Explore size --> Loading @@ -121,7 +112,7 @@ style="font-size:.65rem; padding: 2px 6px; background:#1a1a1a; border-color:#333; color:#aaa;">Box</span> <input type="number" class="ctrl-explore-w form-control form-control-sm" <input type="number" class="ctrl-explore-w form-control form-control-sm" value="70" style="background:#111; border-color:#333; font-size:.7rem; color:#b0ffb0;" box="100" step="10"> </div> </div> Loading @@ -136,7 +127,7 @@ <div class="position-relative" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-teccam d-block" width="256" height="256" style="width:100%; aspect-ratio:1/1;"></canvas> style="width:100%"></canvas> <svg class="overlay-teccam position-absolute top-0 start-0 w-100 h-100" style="pointer-events:none; overflow:visible;" xmlns="http://www.w3.org/2000/svg"> </svg> Loading
noctua/web/static/js/viewer/fits-viewer.js +57 −24 Original line number Diff line number Diff line Loading @@ -54,7 +54,8 @@ export class FitsViewer { infoSci: root.querySelector('.info-sci'), infoTec: root.querySelector('.info-tec'), crosshair: root.querySelector('.cv-crosshair'), canvasExploreW: root.querySelector('.ctrl-explore-w'), expW: root.querySelector('.ctrl-explore-w'), expH: root.querySelector('.ctrl-explore-h'), }; this._sci = { Loading Loading @@ -325,39 +326,71 @@ export class FitsViewer { * Resolves when the tile is fetched and drawn. */ async _fetchExplore(imgX, imgY) { async _fetchExplore(x, y) { const camId = 'scicam'; const url = this._url(`/viewer/${this.station}/${camId}/tile`, { cx: imgX, cy: imgY, r: TILE_HALF }); try { const { data, w, h } = await this._fetchTile(url); // Extract the center pixel value from the Float32Array const centerIdx = Math.floor(h / 2) * w + Math.floor(w / 2); const pixelValue = data[centerIdx]; const w = parseInt(this._el.expW.value); const h = w // parseInt(this._el.expH.value); const url = `${this.api}/viewer/${this.station}/${camId}/tile?cx=${x}&cy=${y}&rx=${w/2}&ry=${h/2}`; this._updatePixelInfo(imgX, imgY, pixelValue); const resp = await fetch(url); const buf = await resp.arrayBuffer(); const head = new Int32Array(buf, 0, 2); const data = new Float32Array(buf, 8); if (!this._exploreGL) { this._exploreGL = new GLRenderer(this._el.canvasExplore); this._exploreGL.setColormap(COLORMAPS[this._el.selColormap?.value || 'viridis']); } // if (!this._exploreGL) this._exploreGL = new GLRenderer(this._el.cvExp); this._exploreGL.upload(data, w, h); this._exploreGL.setRange(this._sci.vmin, this._sci.vmax); this._exploreGL.render(true); } catch { // Ignore if FITS tile is not available } this._el.infoSci.textContent = `X: ${x} Y: ${y} | Val: ${data[Math.floor(data.length/2)].toFixed(1)}`; } // async _fetchExplore(imgX, imgY) { // const w = parseInt(this._el.expW.value); // const h = w // /// const h = parseInt(this._el.expH.value); // const camId = 'scicam'; // const url = this._url(`/viewer/${this.station}/${camId}/tile`, { // cx: imgX, // cy: imgY, // rx: w/2, // ry: h/2, // //r: TILE_HALF // }); // try { // const { data, w, h } = await this._fetchTile(url); // // Extract the center pixel value from the Float32Array // const centerIdx = Math.floor(h / 2) * w + Math.floor(w / 2); // const pixelValue = data[centerIdx]; // this._updatePixelInfo(imgX, imgY, pixelValue); // if (!this._exploreGL) { // this._exploreGL = new GLRenderer(this._el.canvasExplore); // this._exploreGL.setColormap(COLORMAPS[this._el.selColormap?.value || 'viridis']); // } // this._exploreGL.upload(data, w, h); // this._exploreGL.setRange(this._sci.vmin, this._sci.vmax); // this._exploreGL.render(true); // } catch { // // Ignore if FITS tile is not available // } // } /** * Update the UI badge with image dimensions, cursor position, and pixel value. * Loading