Source: public/javascript/modules/olqv_boxes.js

import {ShapesFactory} from "./olqv_shapes.js";
import { box2DTranslator, box3DTranslator } from "./olqv_selector.js";
import {DOMAccessor} from "./domelements.js";
import { ServerApi } from "./serverApi.js";
import {highlightedFeature, standardFeature} from "./olqv_shapestyle.js";
import {BoxButton} from "./olqv_olbuttons.js";


/** A class to create and to manage rectangular boxes in the 2D part of YAFITS. A box is basically defined by a pair of opposite corners.
 * 
 * Boxes are created and manipulated with the {@link https://openlayers.org/en/v5.3.0/apidoc/|OpenLayers} API. 
 * In particular the {@link https://openlayers.org/en/v5.3.0/apidoc/module-ol_interaction_DragBox.html|DragBox} 
 * and {@link https://openlayers.org/en/v5.3.0/apidoc/module-ol_Feature-Feature.html|Feature} classes are used.
 * 
 * Boxes style is defined in ShapesFactory
 * 
 * <ul>
 * <li>Boxes have their side parallels to the image sides (No rotation possible).</li>
 * <li>A method is provided to obtain measurements associated to the collection of pixels contained in a box. </li>
 * </ul>
 *
 * @extends ShapesFactory
 */
class BoxesFactory extends ShapesFactory {

    static enter(what) {
        console.group(this.name + "." + what);
    }

    static exit() {
        console.groupEnd();
    }

    /**
     * Creates an instance.
     * @param {Viewer} viewer instance of the Viewer class that will host the boxes.
     */
    constructor(viewer) {
        super(viewer, 'box');
        BoxesFactory.enter(this.constructor.name);
        this.mode = this;
        this.lastSelectedBox = null;
        this.layer.setZIndex(30);
        this.buttonObject = new BoxButton(this.viewer);
        let self = this;

        let f = function (event) {
            if(self.isOpened){
                self.close(); 
                self.buttonObject.unsetActive();
            } else {
                self.open(self.dragBox, 'nesw-resize');
                self.buttonObject.setActive();
            }                
        };
        this.buttonObject.setClickAction(f);
        this.numOfBoxes = 0;  // The number of boxes created sbsiao far

        /*
        ** This is our dragbox interaction
        */
        //this.dragBox = new ol.interaction.DragBox({ condition: ol.events.condition.platformModifierKeyOnly });
        this.dragBox = new ol.interaction.DragBox({ condition: ol.events.condition.noModifierKeys });
        this.dragBox.on('boxend', () => {
            ShapesFactory.enter("this.dragBox.on('boxend', function() { : entering");
            const extent = this.dragBox.getGeometry().getExtent();
            this.addBox(extent[0], extent[2], extent[1], extent[3]);
            /*

            // The real work when a drag box interaction reach its end is done below ( saynchronously )
            //this.createAndMeasureBox(this.relFITSFilePath, this.viewer.getSliceRange()[0], extent[0], extent[2], extent[1], extent[3]);
            let box = this.prepareBox(extent[0], extent[2], extent[1], extent[3]);
            this.measure(box);
            this.numOfBoxes += 1;*/
            ShapesFactory.exit();
        });

        /*
        ** This the behaviour when a box has been selected.
        */
        this.selector.select['Polygon'] = (feature) => {
            ShapesFactory.enter("A box is selected");
            this.selected(feature);
            ShapesFactory.exit();
        };

        /*
        ** This the behaviour when a box has been unselected.
        */
        this.selector.unselect['Polygon'] = (feature) => {
            ShapesFactory.enter("A box is unselected");
            this.unselected(feature);
            ShapesFactory.exit();
        };

        /*
        ** This is the behaviour when a selected box is going to be removed
        */
        this.selector.remove['Polygon'] = (f) => {
            ShapesFactory.enter("A selected box is about to be removed");
            this.remove(f);
            ShapesFactory.exit();
        };

        this._setBoxTranslator();
        this.selector.setTranslateInteraction(this);

        BoxesFactory.exit();
    }

    _setBoxTranslator(){
        Object.assign(this.selector, box2DTranslator);
    }

    addBox(iRA0, iRA1, iDEC0, iDEC1){
        // The real work when a drag box interaction reach its end is done below ( saynchronously )
        //this.createAndMeasureBox(this.relFITSFilePath, this.viewer.getSliceRange()[0], extent[0], extent[2], extent[1], extent[3]);
        DOMAccessor.showLoaderAction(true);
        let box = this.prepareBox(iRA0, iRA1, iDEC0, iDEC1);
        this.measure(box);
        this.numOfBoxes += 1;
	    this.viewer.map.updateSize();
        DOMAccessor.showLoaderAction(false);
    }

    /**
     * Utility. Returns the toolbox button that will activate this instance.
     * @returns a {@link https://developer.mozilla.org/fr/docs/Web/HTML/Element/Button|button}
     */
    getButtonObject() {
        return this.buttonObject;
    }

    /**
     * Creates an OpenLayers feature that describres a box given bottom left and upper right coordinates.
     * A label is automatically generated that will be displayed beside the box.
     * @param {number} iRA0 lower 1st coordinate
     * @param {*} iRA1 upper 1st coordinate
     * @param {*} iDEC0 lower 2nd coordinate
     * @param {*} iDEC1 upper 2nd coordinate
     * @returns {Feature}
     */
    prepareBox(iRA0, iRA1, iDEC0, iDEC1) {
        ShapesFactory.enter(this.prepareBox.name);
        //DOMAccessor.getLoading().style.display = 'block';
        var tl = [iRA0, iDEC1];
        var tr = [iRA1, iDEC1];
        var br = [iRA1, iDEC0];
        var bl = [iRA0, iDEC0];

        var corners = [];
        corners.push(tl, tr, br, bl, tl);

        let feature = new ol.Feature({ geometry: new ol.geom.Polygon([corners]) });
        let properties = {};
        properties["type"] = "box";
        feature.set("properties", properties);
        feature.set("label", `b_${this.numOfBoxes}`);
        feature.setStyle(standardFeature(feature));
        ShapesFactory.exit();
        return feature;
    }

    /**
     * Calculates some measurements associated to the box described in feature.
     * The measurements are stored in an entry "measurements" of feature "properties" property.
     * 
     * > The measurements are calculated remotely by the YAFITSS server.
     *  
     * @param {Feature} feature the OpenLayers feature expected to represent a box.
     * @param {boolean} addFeature Deprecated, don't use it.
     */
    measure(feature, addFeature = true) {
        BoxesFactory.enter(this.measure.name);
        console.warn("range : " + this.viewer.getSliceRange());
        if (typeof this.viewer.getSliceRange() != "number") {
            alert("Do not know how to measure a 2D box on an image representing a 3D dataset");
        }
        else {
            let properties = feature.get("properties");
            if (properties.hasOwnProperty("type") && properties["type"] === "box") {
                //DOMAccessor.getLoading().style.display = "block";
                DOMAccessor.showLoaderAction(true);
                let corners = feature.getGeometry().getCoordinates()[0];
                let iRA0, iRA1, iDEC0, iDEC1;
                [iRA0, iRA1] = [Math.min(corners[0][0], corners[2][0]), Math.max(corners[0][0], corners[2][0])];
                [iDEC0, iDEC1] = [Math.min(corners[0][1], corners[2][1]), Math.max(corners[0][1], corners[2][1])];
                // query server
                let serverApi = new ServerApi();
                serverApi.measureBox(iRA0, iRA1, iDEC0, iDEC1, this.viewer.getSliceRange(), this.relFITSFilePath, (resp)=>{
                    properties["measurements"] = resp["result"];
                    feature.set("properties", properties);
                    if (addFeature) {
                        feature.setStyle(this.style_f(feature));
                        this.source.addFeature(feature);
                        this.viewer.map.updateSize();
                    }
                });
            }
            else {
                console.log("Unable to measure such a feature");
            }
        }
        BoxesFactory.exit();
    }

    /**
     * Behaviour when an instance is activated.
     *
     * @see {@link https://openlayers.org/en/v5.3.0/apidoc/module-ol_interaction_Interaction-Interaction.html|Interaction }
     * @see {@link https://developer.mozilla.org/fr/docs/Web/CSS/cursor | cursor}
     * @param {Interaction} interaction
     * @param {cursor} cursor
    */
    open(interaction, cursor) {
        BoxesFactory.enter(this.open.name);
        super.open(interaction, cursor);
        BoxesFactory.exit();
    }

    /**
     * Behaviour when a box is selected.
     * @param {Feature} box 
     */
    selected(box) {
        ShapesFactory.enter(this.selected.name);
        super.selected(box);
        ShapesFactory.exit();
    }
}

class Boxes3DFactory extends BoxesFactory {
    /**
     * Creates an instance.
     * @param {Viewer} viewer instance of the Viewer class that will host the boxes.
     */
    constructor(viewer) {
        super(viewer);
        this.selectAction = undefined;
    }

    _setBoxTranslator(){
        Object.assign(this.selector, box3DTranslator);
    }

    addBox(iRA0, iRA1, iDEC0, iDEC1){
        // The real work when a drag box interaction reach its end is done below ( saynchronously )
        //this.createAndMeasureBox(this.relFITSFilePath, this.viewer.getSliceRange()[0], extent[0], extent[2], extent[1], extent[3]);
        DOMAccessor.showLoaderAction(true);
        let boxFeature = this.prepareBox(iRA0, iRA1, iDEC0, iDEC1);
        boxFeature.setStyle(this.style_f(boxFeature));
        this.source.addFeature(boxFeature);
        this.numOfBoxes += 1;
        //make sure the box is immediately visible
        this.viewer.map.updateSize();
        DOMAccessor.showLoaderAction(false);
    }

    setSelectAction(action){
        this.selectAction = action;
    }

    /**
     * Behaviour when a box is selected.
     * @param {Feature} box 
     */
    selected(box) {
        ShapesFactory.enter(this.selected.name);
        super.selected(box);
        if (this.selectAction !== undefined){
            this.selectAction(box);
        }
        ShapesFactory.exit();
    }
}

export {
    BoxesFactory,
    Boxes3DFactory
}