Source: routing/routing.js

let axios = require('axios');
let RoutingResult = require('./types/directionResult');
let RoutingInteraction = require('./interactors/commonInteraction');

/**
 * Класс Routing предназначер для вычисления маршрутов
 */
class Routing {
    /**
     * @param params {object} - опции создания экземпляра класса
     * @param params.locations {array} - стартовый массив путевых точек, по умолчанию пустой массив
     */
    constructor(params) {
        this.params = params;
        this.store = {};
        this.locations = [];
        this.events = {};
        this.started = false;
        this.interaction = params.interaction || new RoutingInteraction(params);
        this.initEvents();
    }

    /**
     * Запуск работы модуля маршрутизации
     */
    start() {
        let self = this;
        if (self.started) return self.started;
        self.started = true;

        let locations = self.locations;
        if (self.interaction) self.interaction.start({locations: locations});

        /**
         * Cобытие, возникающее при старте (инициализации) модуля роутинга
         * @event Routing#routing-started
         * @return {object}
         */
        self.emit("routing-started", {
            status: true
        });
    }

    /**
     * Остановка работы модуля
     */
    stop() {
        let self = this;
        if (self.started === false) return;
        self.started = false;

        self.interaction.stop();

        /**
         * @event Routing#routing-stopped - Событие, возникающее при остановке действий с роутингом
         */
        self.emit("routing-stopped", {
            status: true
        })
    }

    /**
     * Метод, вычисляющий маршрут
     * @param params {object} - опции поиска
     * @param params.locations {array} - массив путевых точек
     * @return {Promise.<*>}
     */
    async calc(params) {
        let self = this;
        if (params.coordinates.length < 2) {
            /**
             * @event Routing#route-locations-not-enough
             * Возникает в случае, если параметров для расчета маршрута недостаточно (0 или 1 точка)
             */
            self.emit("route-locations-not-enough", {
                locationsCount: params.coordinates.length,
                route: undefined,
                status: false
            });

            return {
                route: undefined,
                locationsCount: params.coordinates.length,
                status: false
            };
        }
        let locations = params.coordinates.map((item) => {
            return "loc=" + item[1] + "," + item[0];
        }).join("&");
        const zParam = "z=50";
        const outParam = "output=json";
        const instructionsParam = "instructions=true";
        const compressionParam = "compression=false";
        let query = [zParam, outParam, instructionsParam, compressionParam, locations].join("&");
        const requestUrl = self.params.url + "/viaroute?" + query;
        let action = (await axios(requestUrl)).data;

        self.store.route = new RoutingResult(action);

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

        return self.store.route;
    }

    /**
     * Возврашает текущий набор маршуртных точек
     * @return {Array|*}
     */
    getLocations() {
        return this.locations;
    }

    /**
     * Добавляет маршуртную точку
     * @param {RoutingLocation} data
     */
    addLocation(data) {
        let location = data;
        this.locations.push(location);

        /**
         * @event Routing#route-location-api-added
         * Возникает при программном добавлении маршрутной точки
         */
        this.emit("route-location-api-added", {
            location: location
        });

        if (this.interaction) this.interaction.addLocation(location);
    }

    /**
     * Удаляет маршрутную точку по ее индексу
     * @param index
     */
    removeLocation(index) {
        this.locations = this.locations.filter((item, idx) => {
            if (index === idx) return false;
            return true;
        });

        /**
         * @event Routing#route-location-api-added
         * Возникает при программном удалении маршрутной точки
         */
        this.emit("route-location-api-removed", {
            locationId: index
        });

        if (this.interaction) this.interaction.removeLocation(index);
        if (this.locations.length < 2) this.interaction.clearRoute();
    }

    updateLocations(locations) {
        this.locations = locations;
    }

    clear() {
        this.clearRoute();
        this.clearLocations();
    }

    clearRoute() {
        if (this.interaction) this.interaction.clearRoute();
    }

    clearLocations() {
        if (this.interaction) this.interaction.clearLocations();
    }

    /**
     * @param params {object}
     * @param params.map {Map} - ссылка на экземпляр карты
     */
    setOptions(params) {
        if (params.map && this.interaction !== undefined) this.interaction.setMap(params.map);
    }

    initEvents() {
        let self = this;
        self.events = {};

        if (self.interaction) {
            self.interaction.on("route-location-map-added", async function (data) {
                let route = await self.calc({coordinates: data.coordinates});
                self.updateLocations(data.locations);

                if (self.interaction.isValid()) self.interaction.drawRoute(route);
            });

            self.interaction.on("route-locations-changed", async function (data) {
                let route = await self.calc({coordinates: data.coordinates});
                self.updateLocations(data.locations);

                if (self.interaction.isValid()) self.interaction.drawRoute(route);
            });
        }
    }

    /**
     * Подписка на события
     * @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];
        if (listener && typeof listener === "function") {
            listener(context);
        }
    }
}

module.exports = Routing;