Source: public/javascript/modules/olqv_boxes.js


import {ShapesFactory} from "./olqv_shapes.js";
import {getLoading} from "./domelements.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.
 * 
 * <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(12);
        this.button = document.createElement("button");
        this.button.setAttribute("type", "button");
        this.button.setAttribute("class", "btn btn-primary btn-sm");
        this.button.setAttribute("data-tooltip", "tooltip");
        this.button.setAttribute("title", "Work with boxes");
        this.button.append("B");
        let f = function (event) {if (this.isOpened) this.close(); else this.open(this.dragBox, 'nesw-resize'); };
        this.button.onclick = f.bind(this);
        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.on('boxend', () => {
            ShapesFactory.enter("this.dragBox.on('boxend', function() { : entering");
            var extent = this.dragBox.getGeometry().getExtent();

            // 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();
        };

        BoxesFactory.exit();
    }

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

    /**
     * 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);
        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}`);
        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);
        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") {
                getLoading().style.display = "block";
                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])];
                console.log(iRA0, iRA1, iDEC0, iDEC1);
                $.post("measureBox", {
                    'relFITSFilePath': this.relFITSFilePath,
                    'iFREQ': this.viewer.getSliceRange(),
                    'iRA0': iRA0,
                    'iRA1': iRA1,
                    'iDEC0': iDEC0,
                    'iDEC1': iDEC1
                },
                    (resp) => {
                        ShapesFactory.enter('measure callback: entering');
                        getLoading().style.display = "none";
                        if (resp["status"] == false) {
                            alert(`Something went wrong with the measurements of the box [[${iRA0}, ${iDEC0}], [${iRA1},${iDEC1}]]. The message was '${resp["message"]}'`);
                        }
                        else {
                            properties["measurements"] = resp["result"];
                            feature.set("properties", properties);
                            if (addFeature) {
                                feature.setStyle(this.style_f(feature));
                                this.source.addFeature(feature);
                            }
                        }
                        ShapesFactory.exit();
                    });
            }
            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();
    }
}

export {
    BoxesFactory
}