VisiOmatic web client

source

control/SnapshotUI.js

/**
 #	This file part of:	VisiOmatic
 * @file User Interface for taking snapshots of the current screen/image.

 * @requires util/VUtil.js
 * @requires control/UI.js

 * @copyright (c) 2015-2024 CNRS/IAP/CFHT/SorbonneU/CEA/UParisSaclay
 * @author Emmanuel Bertin <bertin@cfht.hawaii.edu>
*/
import html2canvas from 'html2canvas';

import {Util} from 'leaflet';

import {VUtil} from '../util';
import {UI} from './UI';


export const SnapshotUI = UI.extend( /** @lends SnapshotUI */ {
	options: {
		title: 'Snapshots',
		collapsed: true,
		position: 'topleft'
	},

	/**
	 * Create a VisiOmatic dialog for taking snapshots of the current
	 screen/image.

	 * @extends UI
	 * @memberof module:control/SnapshotUI.js
	 * @constructs
	 * @param {object} [options] - Options.

	 * @param {string} [options.title='Snapshots']
	   Title of the dialog window or panel.

	 * @see {@link UI} for additional control options.

	 * @returns {SnapshotUI} Instance of a VisiOmatic snapshot interface.
	 */
	initialize: function (options) {
		Util.setOptions(this, options);

		this._className = 'visiomatic-control';
		this._id = 'visiomatic-snapshot';
		this._sideClass = 'snapshot';
	},

	/**
	 * Initialize the snapshot dialog.
	 * @method
	 * @static
	 * @private
	 */
	_initDialog: function () {
		const	className = this._className,
			layer = this._layer,
			visio = layer.visio,
			map = this._map;

		// Image snapshot
		const	line = this._addDialogLine('Image:', this._dialog),
			elem = this._addDialogElement(line),
			items = ['current', 'native'];

		this._snapType = 0;
		this._snapSelect =  this._addSelectMenu(
			this._className + '-select',
			elem,
			items,
			undefined,
			this._snapType,
			'Select snapshot resolution',
			function () {
				this._snapType = parseInt(this._snapSelect.selectedIndex - 1, 10);
			}
		);

		// Set up a hidden link and trigger download using the HTML5 attribute.
		const	hiddenlink = document.createElement('a'),
			elem2 = this._addDialogElement(line),
			button = this._addButton(
				className + '-button',
				elem2,
				'snapimage',
				'Snap the image within the current frame',
				function (event) {
					const	latlng = map.getCenter(),
						bounds = map.getPixelBounds(),
						wcs = map.options.crs,
						z = map.getZoom(),
						binfac = Math.pow(2, visio.maxZoom - z),
						bin = binfac > 1 ? binfac : 1;
					fetch(
						layer.getTileSettingsURL() + '&RGN=' +
						(binfac * bounds.min.x).toFixed(0) + ',' +
						(binfac * bounds.min.y).toFixed(0) + ':' +
						(binfac * bounds.max.x).toFixed(0) + ',' +
						(binfac * bounds.max.y).toFixed(0) +
						'&BIN=' + (this._snapType ? 1 : bin.toFixed(0))
					).then((res) => res.status === 200 ?
						res.blob() : Promise.reject(res)
					).then((blob) => {
						hiddenlink.href = window.URL.createObjectURL(
							new Blob([blob], {type: "image/jpg"})
						);
						hiddenlink.download = visio.imageName.replace(
							/(\.fits)|(\.fit)|(\.fz)/g,
							''
						) + '_' + wcs.latLngToHMSDMS(latlng).replace(
							/[\s\:\.]/g,
							'') + '.jpg';
						hiddenlink.click();
					}).catch(async (res) => {
						const	json = await res.json();
						alert('Error ' + res.status + ': ' +
							json.detail[0].msg + '.');
					});
				}
			);

		document.body.appendChild(hiddenlink);

		// Screen snapshot
		const	line2 = this._addDialogLine('Screen:', this._dialog),
			elem3 = this._addDialogElement(line2);

		this._addButton(
			className + '-button',
			elem3,
			'printscreen',
			'Print current screen',
			function (event) {
				var	control = document.querySelector(
					'#map > .leaflet-control-container'
				);
				control.style.display = 'none';
				window.print();
				control.style.display = 'unset';
			}
		);

		// Set up a hidden link and trigger download using the HTML5 attribute
		// and an output element for the screenshot.
		const	hiddenlink2 = document.createElement('a'),
			canvasoutput = document.createElement('div');

		this._addButton(
			className + '-button',
			elem3,
			'snapscreen',
			'Snap current screen',
			function (event) {
				const	control = document.querySelector(
						'#map > .leaflet-control-container'
					),
					popup = document.querySelector(
						'.leaflet-popup-content-wrapper'
					);
				control.style.display = 'none';
				// Backup popup box shadow style, as it is not
				// supported by html2canvas yet.
				if (popup) {
					var	popup_style = popup.style.boxShadow;
					popup.style.boxShadow = 'none';
				}
				// Re-render to canvas (for best results map option
				// preferCanvas should be set to true)
				html2canvas(
					document.querySelector('#map'),
					{backgroundColor: '#000000'}
				).then(
					function (canvas) {
						const	latlng = map.getCenter(),
							wcs = map.options.crs;
						hiddenlink2.href = canvas.toDataURL();
						hiddenlink2.download = visio.imageName.replace(
							/(\.fits)|(\.fit)|(\.fz)/g, ''
						) + '_' + wcs.latLngToHMSDMS(latlng).replace(
							/[\s\:\.]/g, '') +'.jpg';
						hiddenlink2.click();
					}
				);
				if (popup) {
					popup.style.boxShadow = popup_style;
				}
				control.style.display = 'unset';
			}
		);

		document.body.appendChild(hiddenlink2);
	}

});


/**
 * Instantiate a VisiOmatic dialog for taking snapshots.
 * @function
 * @param {object} [options] - Options: see {@link SnapshotUI}
 * @returns {SnapshotUI} Instance of a VisiOmatic snapshot interface.
 */
export const snapshotUI = function (options) {
	return new SnapshotUI(options);
};