Source: routing/interactors/commonInteraction.js

let ol = require('openlayers');
let Location = require('./../types/location');

/**
 * Класс RoutingInteraction предназначен для реализации взаимодействия пользователя и карты, при построении маршрутов
 */
class RoutingInteraction {
    constructor(params) {
        this.map = params.map;
        this.events = {};
    }

    setMap(map) {
        this.map = map;
    }

    addLocation(location) {
        let self = this;
        let feature = new ol.Feature({
            id: location.id,
            title: location.title,
            address: location.address,
            geometry: new ol.geom.Point(location.coordinate)
        });
        let source = self.getSource();
        source.addFeature(feature);
        source.on("addfeature", () => {
            let coordinates = self.getCoordinates();
            let features = self.getSource().getFeatures();
            let locations = self.featuresToLocations(features);
            self.emit("route-location-map-added", {
                status: true,
                coordinates: coordinates,
                locations: locations
            });
        })
    }

    removeLocation(index) {
        let self = this;
        let features = self.getSource().getFeatures();
        let feature = features[index];
        this.getSource().removeFeature(feature);
        this.getSource().on("removefeature", function () {
            let coordinates = self.getCoordinates();
            let features = self.getSource().getFeatures();
            let locations = self.featuresToLocations(features);
            self.emit("route-locations-changed", {
                status: true,
                coordinates: coordinates,
                locations: locations
            });
        });
    }

    isValid() {
        let flag = this.map === undefined ? false : true;
        return flag;
    }

    getSource() {
        return this.stopsLayer.getSource();
    }

    getMap() {
        return this.map.map;
    }

    initInteractions() {
        let self = this;

        let {stopsSource, map} = this.initLayers();

        self.modify = new ol.interaction.Modify({source: stopsSource});
        map.addInteraction(self.modify);

        self.draw = new ol.interaction.Draw({
            source: stopsSource,
            type: "Point"
        });
        map.addInteraction(self.draw);

        self.snap = new ol.interaction.Snap({source: stopsSource});
        map.addInteraction(self.snap);

        self.draw.on("drawend", async function (evt) {
            let features = self.getSource().getFeatures();
            features.push(evt.feature);

            let coordinates = features.map((item) => {
                return ol.proj.toLonLat(item.getGeometry().getCoordinates());
            });
            let locations = self.featuresToLocations(features);

            /**
             * Событие возникает при добавлнии нового маршрутного пункта пользователем с карты
             * @event RoutingInteraction#route-location-user-added
             */
            self.emit("route-location-map-added", {
                status: true,
                coordinates: coordinates,
                locations: locations
            });
        });

        self.modify.on("modifyend", async function () {
            let coordinates = self.getCoordinates();
            let locations = self.featuresToLocations(self.getSource().getFeatures());

            /**
             * Событие возникает при изменении местоположения маршрутного пункта пользователем с карты
             * @event RoutingInteraction#route-locations-changed
             */
            self.emit("route-locations-changed", {
                status: true,
                coordinates: coordinates,
                locations: locations
            });
        });
    }

    initLayers() {
        let self = this;
        let stopsSource = new ol.source.Vector();
        let routeSource = new ol.source.Vector();
        let alternativeRouteSource = new ol.source.Vector();

        self.stopsLayer = new ol.layer.Vector({
            source: stopsSource,
            style: new ol.style.Style({
                fill: new ol.style.Fill({
                    color: '#27dd38'
                }),
                stroke: new ol.style.Stroke({
                    color: 'rgba(100,100,255, 0.7)',
                    width: 10
                }),
                image: new ol.style.Circle({
                    radius: 8,
                    fill: new ol.style.Fill({
                        color: '#ffffff'
                    }),
                    stroke: new ol.style.Stroke({
                        color: "#ff1ee9",
                        width: 3
                    })
                })
            })
        });

        self.routeLayer = new ol.layer.Vector({
            source: routeSource,
            style: new ol.style.Style({
                stroke: new ol.style.Stroke({
                    color: 'rgba(100,100,255, 0.7)',
                    width: 10
                }),
                image: new ol.style.Circle({
                    radius: 8,
                    fill: new ol.style.Fill({
                        color: '#ffffff'
                    }),
                    stroke: new ol.style.Stroke({
                        color: "#ff1ee9",
                        width: 3
                    })
                })
            })
        });

        self.alternativeRouteLayer = new ol.layer.Vector({
            source: alternativeRouteSource,
            style: new ol.style.Style({
                stroke: new ol.style.Stroke({
                    color: 'rgba(255,0,200, 0.7)',
                    width: 8
                }),
                image: new ol.style.Circle({
                    radius: 8,
                    fill: new ol.style.Fill({
                        color: '#ffffff'
                    }),
                    stroke: new ol.style.Stroke({
                        color: "#ff1ee9",
                        width: 3
                    })
                })
            })
        });

        let map = self.getMap();
        map.addLayer(self.routeLayer);
        map.addLayer(self.alternativeRouteLayer);
        map.addLayer(self.stopsLayer);
        return {stopsSource, map};
    }

    /**
     * Метод, перерисовывющий маршрут на карте
     */
    drawRoute(data) {
        let self = this;
        let routeFeature;
        let alternativeRouteFeatures;
        let layer = self.routeLayer;
        let alternativeLayer = self.alternativeRouteLayer;
        if (data === false || data.status === false) return data;

        self.clearRoute();

        let routeGeometry = this.getRouteGeometry(data);
        if (routeGeometry) {
            routeFeature = new ol.Feature({
                geometry: routeGeometry
            });
            layer.getSource().addFeature(routeFeature);
        }

        let alternativeGeometries = this.getAlternativeRouteGeometry(data);
        if (alternativeGeometries) {
            alternativeRouteFeatures = alternativeGeometries.map((item) => {
                return new ol.Feature({geometry: item});
            });
            alternativeLayer.getSource().addFeatures(alternativeRouteFeatures);
        }

        /**
         * Событие возникает перерисовке маршрута на карте
         * @event RoutingInteraction#route-drawend
         */
        self.emit("route-drawend", {
            status: true,
            feature: routeFeature
        });
    }

    getRouteGeometry(data) {
        try {
            let coordinates = data.result.route.geometry.map((item) => {
                let coordinate = ol.proj.transform([item[1], item[0]], 'EPSG:4326', 'EPSG:3857');
                return coordinate;
            });

            let geometry = new ol.geom.LineString(coordinates);
            return geometry;
        }
        catch (e) {

        }
        return undefined;
    }

    getAlternativeRouteGeometry(data) {
        let result = [];
        try {
            for (let i = 0; i < data.result.alternative.geometry.length; i++) {
                let geom = data.result.alternative.geometry[i];
                let coordinates = geom.map((item) => {
                    let coordinate = ol.proj.transform([item[1], item[0]], 'EPSG:4326', 'EPSG:3857');
                    return coordinate;
                });

                let geometry = new ol.geom.LineString(coordinates);
                result.push(geometry);
            }
        }
        catch (e) {

        }
        return result;
    }

    /**
     * Метод, приводящий к очистке путевых точек
     */
    clearLocations() {
        let self = this;
        self.locations = [];
        self.stopsLayer.getSource().clear();

        /**
         * Событие, возникающее при очистке маршрутных точек
         * @event RoutingInteraction#route-clear-locations
         */
        self.emit("route-clear-locations", {
            locations: []
        });
    }

    /**
     * Метод очищает все построенные линии маршрутов, но не очищает маршрутные точки.
     */
    clearRoute() {
        let self = this;
        let source = self.routeLayer.getSource();
        source.clear();

        source = self.alternativeRouteLayer.getSource();
        source.clear();

        /**
         * Событие, возникающее при очистке маршрута
         * @event RoutingInteraction#route-cleared
         */
        self.emit("route-cleared", {
            route: undefined
        })
    }

    getCoordinates() {
        let locations = this.getSource().getFeatures();
        let coordinates = locations.map((item) => {
            return ol.proj.toLonLat(item.getGeometry().getCoordinates());
        });
        return coordinates;
    }

    featuresToLocations(data) {
        let locations = data.map((item) => {
            let location = new Location({
                id: item.get("id"),
                title: item.get("title"),
                address: item.get("address"),
                coordinates: item.getGeometry().getCoordinates(),
            });
            return location;
        });
        return locations;
    }

    start() {
        this.initInteractions();
    }

    stop() {
        let self = this;
        let map = self.map.map;
        map.removeInteraction(self.draw);
        map.removeInteraction(self.modify);
        map.removeInteraction(self.snap);
        map.removeLayer(self.stopsLayer);
        map.removeLayer(self.routeLayer);
        map.removeLayer(self.alternativeRouteLayer);
    }

    /**
     * Подписка на события
     * @param eventName - имя события
     * @param listener - callback
     */
    on(eventName, listener) {
        let self = this;
        let eventStore = self.events;
        eventStore[eventName] = listener;
    }

    /**
     * Выключение подписки на событие
     * @param eventName - имя события
     */
    off(eventName) {
        let self = this;
        let eventStore = self.events;
        if (eventStore[eventName]) delete eventStore[eventName];
    }

    /**
     * Инициация события с контекстом
     * @param eventName - имя события
     * @param context - контекст события
     */
    emit(eventName, context) {
        let self = this;
        let eventStore = self.events;
        let listener = eventStore[eventName];
        console.log(eventName, context);
        if (listener && typeof listener === "function") {
            listener(context);
        }
    }
}

module.exports = RoutingInteraction;