Source: specific/mixins/ol-map-layers.js

/**
 * @desc A mixin object containing methods related to map layers
 * @module MapLayersMixin
 */
import { OSM, BingMaps, Tile, ImageTile } from 'ol/source';
import { transform } from "ol/proj";
import { GeoTIFF as GeoTIFFSource } from 'ol/source';
import { get as getProjection } from 'ol/proj';
import { Style, Fill, Stroke } from 'ol/style';
import { Vector as VectorLayer } from 'ol/layer';
import WebGLVectorLayer from 'ol/layer/WebGLVector';
//import {createXYZ} from 'ol/tilegrid';

export const MapLayersMixin = {
    methods: {

        /**
         * Sets the base layer of the map based on the selectedBaseLayer value.
         * If selectedBaseLayer is 1, sets the base layer source to OpenStreetMap (OSM).
         * If selectedBaseLayer is not 1, sets the base layer source to Bing Maps with the specified key and imagerySet.
         * Updates the map size and forces a component update.
         * @async
         * @returns {Promise<void>}
         */
        async setBaseLayer() {
            if (this.selectedBaseLayer.value == 1) {
                this.baseLayer.setSource(new OSM());
            } else {
                this.baseLayer.setSource(new BingMaps({
                    key: 'AnNHHAjCQ3arTU0FoTUB1zoCdKC2png2M2pstqoV5uFhCHorOx_aUz1Yt8RA6Xly',
                    imagerySet: 'AerialWithLabels',
                }));
            }
            this.baseLayer.getSource().changed();
            this.map.updateSize();
            this.$forceUpdate();
        },

        async setCustomRestrictedLayer() {
            if (this.customRestrictedAPI && this.customRestrictedLayer && this.zoom >= this.customRestrictedLayer.getMinZoom() && this.zoom <= this.customRestrictedLayer.getMaxZoom()) {
                 let res = await this.api(this.axios.API[this.customRestrictedAPI.method ?? "get"], this.customRestrictedAPI.url, 
                    { Srid: this.srid, 
                      Extent: this.extent.join(','),
                      Zoom: Math.round(this.zoom)
                    });  
                 this.customRestrictedSource.clear();
                 this.customRestrictedSource.addFeatures(this.format.readFeatures(res)); 
            }
        },

        /**
         * Clears the shape source and adds features to it based on the selected shape.
         * @async
         * @function setShapeLayer
         * @memberof module:mixins/ol-map-layers
         * @returns {Promise<void>} A promise that resolves when the shape layer is set.
         */
        // async setShapeLayer() {
        //     this.shapeSource.clear();
        //     if (this.selectedSite) {
        //         let ret = await this.get("Shape/GetGeometryOf", {
        //             Id: this.selectedSite.value,
        //             Srid: this.srid,
        //             GeometryTypeId: '1',
        //         });
        //         if (ret && ret.features) {
        //             let features = this.format.readFeatures(ret);
        //             this.shapeSource.addFeatures(features);
        //             this.animateToExtent(this.shapeSource);
        //         }
        //     }
        // },

        /**
         * Creates a point collection feature from the given data.
         *
         * @param {Object} data - The data used to create the point collection.
         * @param {string} projection - The projection used for transforming coordinates.
         * @returns {Object} - The created point collection feature.
         */
        createPointCollection(data, projection) {
            let self = this;
            var featureCollection = {
                type: 'FeatureCollection',
                features: data.data.map(function(row) {
                    return {
                        type: 'Feature',
                        geometry: {
                            type: 'Point',
                            coordinates: transform([row[0], row[1]], 'EPSG:4326', projection)
                        },
                        properties: {
                            value: row[2],
                            date: row[3],
                            color_code: row[4] ?? '#ff0000',
                            name: row[8] || row[5],
                            nuts_0: row[6],
                            description: row[7],
                            point_id: row[9],
                            sample: row[10],
                            has_spectrum: row[11],
                            data_source_order_no: self.selectedDataSourceIds.indexOf(row[12]),
                        },
                    };
                })
            };  
            return featureCollection;
        },

                /**
         * Fetches and sets the indicator values for the map.
         * 
         * @returns {Promise<void>} A promise that resolves when all asynchronous operations are complete.
         */
        async getIndicatorValues() {
            this.$store.working = true;
            await this.$nextTick(); // Wait for the DOM to update before proceeding
            if (this.selectedIndicator) {
                this.legend = await this.get("User/GetIndicatorLegend", { indicatorId: this.selectedIndicator.value });
                if (!this.legend) this.legend = [];
            } else {
                this.legend = [];
            }
            
            await this.setPointLayer();
            await this.setBoundariesLayer();
            this.$store.working = false;
        },

        /**
         * Sets the point layer on the map.
         * @async
         * @returns {Promise<void>} A promise that resolves when the point layer is set.
         */
        async setPointLayer() {
            this.pointSource.clear();
            this.pointFeatures = [];

            if (!this.showPoints) return;
            if (!this.selectedIndicator) return;
            let data = await this.get("User/GetIndicatorValues", { 
                indicatorId: this.selectedIndicator.value, 
                dataSourceId: this.selectedDataSourceIds.join(","), 
                valueFrom: this.valueFrom, 
                valueTo: this.valueTo,
                depthId: this.selectedIndicator.depth.value });   
            let pointCollection = this.createPointCollection(data, this.projection);
            this.pointFeatures = this.format.readFeatures(pointCollection);
            this.pointSource.addFeatures(this.pointFeatures);
         },
        
        async setMapTilerLayer() {
            if (!this.mapTilerSource) return;
            let mapTiler = this.mapTilerSource.toString().replaceAll ("'", '"');
            this.mapTilerLayer.setSource(new ImageTile({url: mapTiler}));
            this.mapTilerLayer.changed();
            this.mapTilerLayer.setVisible(true);
        },

        /**
         * Sets the tile layer containig GeoTiff sources for the map.
         * @async
         * @returns {Promise<void>} A promise that resolves when the tile layer is set.
         */
        async setTileLayer(selectedTitle, selectedCatalog, tiffLayer, tiffLegend, alt) {
            this.$store.working = true;
            // create appropriate source
            if (selectedCatalog) {
                let source = new GeoTIFFSource({
                    normalize: false,
                    sources: [{ url: selectedCatalog.source }],
                    projection: getProjection("EPSG:" + selectedTitle.srid),
                    //convertToRGB: true,
                    interpolate: false
                });               
                tiffLayer.setSource(source);  
                source.on("change", () => {
                    if (source.getState() === "error") {
                        this.showError(this.$t("Error loading GeoTIFF") + ": " + source.getError());
                        return;
                    } else if (source.getState() === "ready") {
                        console.log("source ready", source);
                        selectedTitle.tiffLayer = tiffLayer;             
                        this.createLegend(selectedTitle, tiffLegend);
                        // tiffLayer.setStyle({
                        //     color: JSON.parse(selectedTitle.color_map)
                        // }); 
                        tiffLayer.changed();
                        tiffLayer.setVisible(true);
                    }
                });

                if (selectedTitle.max_zoom) {	
                    tiffLayer.setMaxZoom(selectedTitle.max_zoom);	
                } else {
                    tiffLayer.setMaxZoom(30);
                }
                if (selectedTitle.custom_restricted_api) {
                    this.customRestrictedAPI = selectedTitle.custom_restricted_api;
                    this.customRestrictedLayer.setMinZoom(this.customRestrictedAPI.minZoom ?? 0);
                }     

            } else {
                tiffLayer.setSource(null);
                tiffLayer.setVisible(false);
            }
            this.$store.working = false;
        },

        /**
         * Sets the boundaries layer on the map based on the selected NUTS level.
         * If the NUTS level is greater than or equal to 0, it makes an asynchronous request to retrieve the boundaries data.
         * The retrieved data is then added to the boundaries source of the map.
         * If the NUTS level is less than 0, the boundaries source is cleared.
         * @returns {Promise<void>} A promise that resolves when the boundaries layer is set.
         */
        async setBoundariesLayer() {
            if (this.nutsLevel && this.nutsLevel.value >= 0) {
                let featureCollection = await this.get("User/GetNutsBoundaries", {
                    level: this.nutsLevel.value, 
                    srid: this.srid, 
                    zoom: Math.round(this.zoom), 
                    extent: this.extent.join(","), 
                    indicatorId: this.selectedIndicator ? this.selectedIndicator.value : null, 
                    dataSourceId: this.selectedDataSourceIds.join(","),
                    depthId: this.selectedIndicator ? this.selectedIndicator.depth.value : null,
                });
                this.boundariesSource.clear();
                if (featureCollection.features) {
                    this.boundariesSource.addFeatures(this.format.readFeatures(featureCollection));
                }
            } else {
                this.boundariesSource.clear();
            }
        },


        async setShapeLayer() {
            if (this.shapeAPI) {
                this.$store.working = true;
                this.shapeAPI.params = this.shapeAPI.params ?? {};
                this.shapeAPI.params.Zoom = Math.round(this.zoom);
                this.shapeAPI.params.Extent = this.extent.join(",");
                let shapes = await this.api(this.axios.API[this.shapeAPI.method ?? "get"], this.shapeAPI.url, this.shapeAPI.params);
                this.shapeSource.clear();
                if (shapes && shapes.features) {
                    this.shapeSource.addFeatures(this.format.readFeatures(shapes));
                    // this.shapeSource.forEachFeature(feature => {
                    //     feature.setStyle(
                    //         new Style({
                    //             stroke: new Stroke({
                    //                 color: feature.get('color_code') || '#000000', // Default to black if no color_code
                    //                 width: 1,
                    //             }),
                    //             fill: new Fill({
                    //                 color: feature.get('fill_color_code') || 'rgba(255, 255, 255, 0.5)', // Default to semi-transparent white
                    //             }),
                    //         })
                    //     );
                    // });
                }
                this.$store.working = false;
            }
        }
    },
}