WAZEPT Segments

Facilitates the standardization of segments

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         WAZEPT Segments
// @version      2024.06.30
// @description  Facilitates the standardization of segments
// @author       J0N4S13 ([email protected])
// @include 	   /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
// @exclude        https://www.waze.com/user/*editor/*
// @exclude        https://www.waze.com/*/user/*editor/*
// @grant        none
// @namespace https://greasyfork.org/users/218406
// ==/UserScript==

(function() {
    var version = GM_info.script.version;
    var roads_id = [3,4,6,7,2,1,22,8,20,17,15,18,19];
    var pedonal_id = [5,10,16];
    var array_config_script = {};
    var array_config_country = {};
    var array_language_original = {};
    var array_language_country = {};
    var language = {};
    var indexselected = "";
    var valueselected = "";
    var array_roads = {};
    var last_node_A = null;
    var last_node_B = null;
    var last_coord_esquerda_first = null;
    var last_coord_esquerda_last = null;
    var last_coord_direita_first = null;
    var last_coord_direita_last = null;
    var sentido_base = null;

    function bootstrap() {
        if (typeof W === 'object' && W.userscripts?.state.isReady) {
            init();
        } else {
            document.addEventListener('wme-ready', init, { once: true });
        }
    }

    async function init() {
        var result = await getLanguages();

        result = await getConfigsScript();
        if(array_config_script[W.model.getTopCountry().attributes.abbr] !== undefined)
            result = await getConfigsCountry(array_config_script[W.model.getTopCountry().attributes.abbr]);

        setTimeout(() => {
            W.selectionManager.events.register('selectionchanged', null, selectedFeature);
            selectedFeature();
        }, 250);

    }

    function selectedFeature(){
        var typeData = null;
        setTimeout(() => {
            if(typeof W.selectionManager.getSelectedFeatures()[0] != 'undefined')
                typeData = W.selectionManager.getSelectedFeatures()[0]._wmeObject.type;
            if (typeData == "segment")
            {
                myTimer();
                if(W.loginManager.getUserRank() >= 3)
                    insertButtons();
            }
        }, 100)
    }

    function getConfigsScript() {
        return new Promise(resolve => {

            fetch('https://docs.google.com/spreadsheets/d/1s_vcC-ENS7JdkVmVH2QceGMA058tan2UUPlQGd6AhwU/gviz/tq?tqx=out:json&sheet=Config')
                .then(res => res.text())
                .then(text => {
                const json = JSON.parse(text.substr(47).slice(0, -2))

                let first = false;
                $(json.table.rows).each(function(){
                    if(first == false)
                    {
                        first = true;
                        return;
                    }
                    array_config_script[verifyNull(this["c"][0])] = verifyNull(this["c"][1]);
                });
            })

            var timer = setInterval(check_data, 100);

            function check_data() {
                if(Object.keys(array_config_script).length > 0 && W.model.getTopCountry() != null)
                {
                    clearInterval(timer);
                    resolve('true');
                }
            }
        });
    }

    function getConfigsCountry(link) {
        let timeout = 0;
        return new Promise(resolve => {

            fetch(link)
                .then(res => res.text())
                .then(text => {
                const json = JSON.parse(text.substr(47).slice(0, -2))

                $(json.table.rows).each(function(){
                    if(verifyNull(this["c"][0]) == true)
                    {
                        var elem = [verifyNull(this["c"][4]), verifyNull(this["c"][5]), verifyNull(this["c"][6]), verifyNull(this["c"][7]), verifyNull(this["c"][8])];
                        array_config_country[verifyNull(this["c"][1])] = elem;
                        array_roads[verifyNull(this["c"][1])] = [verifyNull(this["c"][2]), verifyNull(this["c"][3])];
                    }
                });
            })

            var timer = setInterval(check_data, 100);

            function check_data() {
                if(Object.keys(array_config_country).length > 0 || timeout >= 20)
                {
                    clearInterval(timer);
                    resolve('true');
                }
                timeout = timeout + 1;
            }
        });
    }

    function getLanguages() {
        let timeout = 0;
        return new Promise(resolve => {

            fetch('https://docs.google.com/spreadsheets/d/1qkWpUItMINWmtn7HtrpmZbRPtAnz0x0zjrIWB7lnOqI/gviz/tq?tqx=out:json')
                .then(res => res.text())
                .then(text => {
                const json = JSON.parse(text.substr(47).slice(0, -2))

                let first = false;
                $(json.table.rows).each(function(){
                    if(first == false)
                    {
                        first = true;
                        return;
                    }

                    if(verifyNull(this["c"][0]) == "Original String")
                    {
                        array_language_original["btnSplit"] = verifyNull(this["c"][1]);
                        array_language_original["strMeters"] = verifyNull(this["c"][2]);
                        array_language_original["strDistance"] = verifyNull(this["c"][3]);
                        array_language_original["strSelMoreSeg"] = verifyNull(this["c"][4]);
                    }
                    if(verifyNull(this["c"][0]).toLowerCase() == JSON.parse(localStorage.getItem("editorLocation"))["locale"].toLowerCase())
                    {
                        array_language_country["btnSplit"] = verifyNull(this["c"][1]);
                        array_language_country["strMeters"] = verifyNull(this["c"][2]);
                        array_language_country["strDistance"] = verifyNull(this["c"][3]);
                        array_language_country["strSelMoreSeg"] = verifyNull(this["c"][4]);
                    }
                });

            })

            var timer = setInterval(check_data, 100);

            function check_data() {
                if(Object.keys(array_language_original).length > 0 || timeout >= 20)
                {
                    if(Object.keys(array_language_country).length == 0)
                        language = array_language_original;
                    $.each(array_language_country, function(code, string) {
                        if(string == "")
                            language[code] = array_language_original[code];
                        else
                            language[code] = array_language_country[code];
                    });
                    clearInterval(timer);
                    resolve('true');
                }
                timeout = timeout + 1;
            }
        });
    }

    function myTimer() {

        var n_bloqueio;
        var nivel;
        var lvl_atual;
        var lvl_max;

            if (!$("#signsroad").length) {
                var signsroad = document.createElement("div");
                signsroad.id = 'signsroad';

                $.each(array_roads, function(func , emoji) {

                    // The sign background
                    var addsign = document.createElement("div");
                    addsign.id = 'sign_' + func;

                    // Get width/height of sign background img
                    addsign.style.cssText = 'cursor:pointer;float:left;width:34px;height:34px;';
                    // Credits for some of these parts go to t0cableguy & Rickzabel
                    addsign.onclick = function() {

                        indexselected = func;

                        if(indexselected == 'LD_ROUNDABOUT')
                        {
                            if(W.selectionManager.getSelectedFeatures()[0]._wmeObject.isInRoundabout())
                            {
                                let coords = [];
                                let segsfeitos = [];
                                let segmentos = W.selectionManager.getSelectedFeatures()[0]._wmeObject.getRoundabout().attributes.segIDs;
                                let primeiro = W.selectionManager.getSelectedFeatures()[0]._wmeObject.attributes.id;
                                let segmento = primeiro;
                                let feitos = 0;

                                let polyPoints = [];

                                do{
                                    let seg = W.selectionManager.model.segments.getObjectById(segmento);

                                    if(seg.attributes.fwdDirection)
                                    {
                                        for (let i=0; i<seg.geometry.getVertices().length - 1; i++) {
                                            polyPoints.push(seg.geometry.getVertices()[i])
                                        }
                                    }
                                    else
                                    {
                                        for (let i=0; i<seg.geometry.getVertices().length - 1; i++) {
                                            polyPoints.push(seg.geometry.getVertices().reverse()[i])
                                        }
                                    }

                                    if(seg.attributes.fwdDirection)
                                    {
                                        W.selectionManager.model.nodes.getObjectById(seg.attributes.toNodeID).attributes.segIDs.forEach(function(aux){
                                            if(aux != segmento && segmentos.includes(aux) && !segsfeitos.includes(aux))
                                            {
                                                segsfeitos.push(segmento);
                                                segmento = aux;
                                            }
                                        });
                                    }
                                    else
                                    {
                                        W.selectionManager.model.nodes.getObjectById(seg.attributes.fromNodeID).attributes.segIDs.forEach(function(aux){
                                            if(aux != segmento && segmentos.includes(aux) && !segsfeitos.includes(aux))
                                            {
                                                segsfeitos.push(segmento);
                                                segmento = aux;
                                            }
                                        });
                                    }
                                    feitos++;
                                } while (feitos < segmentos.length)

                                var polygon=new OpenLayers.Geometry.Polygon(new OpenLayers.Geometry.LinearRing(polyPoints));
                                polygon.calculateBounds();

                                let AddLandmark= require("Waze/Action/AddLandmark");
                                var wazefeatureVectorLandmark = require("Waze/Feature/Vector/Landmark");

                                var poi = new wazefeatureVectorLandmark({geoJSONGeometry:W.userscripts.toGeoJSONGeometry(polygon)});

                                var TabLine2 = [];

                                poi.attributes.categories = new Array("JUNCTION_INTERCHANGE");

                                let bloquear;
                                if(W.selectionManager.getSelectedFeatures()[0]._wmeObject.attributes.lockRank == null)
                                    bloquear = null;
                                else
                                {
                                    let rank = W.selectionManager.getSelectedFeatures()[0]._wmeObject.attributes.lockRank + 1;
                                    rank--;
                                    if(W.loginManager.user.rank >= rank)
                                        bloquear = rank;
                                    else
                                        bloquear = W.loginManager.user.rank;
                                }
                                poi.attributes.lockRank = bloquear;

                                W.model.actionManager.add(new AddLandmark(poi));

                                W.selectionManager.setSelectedModels(poi);
                            }

                            return;

                        }

                        if(indexselected == 'DATA_TOLLS')
                        {

                            let myRoad = W.selectionManager.getSelectedFeatures()[0]._wmeObject.attributes;

                            var from = "";
                            var to = "";
                            var sentido = "";

                            if(myRoad.fwdDirection == true && myRoad.revDirection == true)
                            {
                                $( "#divSentidos" ).show();
                            }
                            else
                            {
                                if(myRoad.fwdDirection == true) //A to B
                                {
                                    from = myRoad.fromNodeID;
                                    to = myRoad.toNodeID;
                                    sentido = "TRUE";
                                }

                                else if(myRoad.revDirection == true)//B to A
                                {
                                    from = myRoad.toNodeID;
                                    to = myRoad.fromNodeID;
                                    sentido = "FALSE";
                                }
                                let center = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat)
                                .transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
                                var $temp = $("<input>");
                                $("body").append($temp);
                                $temp.val(center.lon.toString().slice(0,8) + "," + center.lat.toString().slice(0,8) + "|" + W.selectionManager.getSelectedFeatures()[0]["attributes"]["wazeFeature"]["_wmeObject"]["attributes"]["id"] + "|" + sentido + "|" + getPermalink() + "|" + from + "|" + to).select();
                                document.execCommand("copy");
                                $temp.remove();
                            }

                            return;

                        }

                        if(indexselected == 'REMOVE_INSTRUCTIONS')
                        {
                            RemoveInstructions();
                        }

                        $.each(W.selectionManager.getSelectedFeatures(), function(i, segment) {
                            let seg = segment._wmeObject;
                            let address = true;
                            if(pedonal_id.includes(seg.attributes.roadType) && roads_id.includes(parseInt(array_config_country[indexselected][2])))
                            {
                                seg = convertSegmentType(seg);
                                address = false;
                            }
                            else
                                if(roads_id.includes(seg.attributes.roadType) && pedonal_id.includes(parseInt(array_config_country[indexselected][2])))
                                {
                                    convertSegmentType(seg);
                                    return;
                                }

                            if(array_config_country[indexselected][2] != "")
                                defineRoadType(seg, parseInt(array_config_country[indexselected][2]));
                            if(array_config_country[indexselected][3] != "")
                                defineSpeed(seg, array_config_country[indexselected][3]);
                            if(array_config_country[indexselected][1] != "")
                                defineLockRankRoad(seg, array_config_country[indexselected][1]);
                            if(array_config_country[indexselected][4] && address && seg.getAddress().attributes.isEmpty)
                                defineAddress(seg, "");
                        });
                    }

                    if(W.loginManager.getUserRank() >= array_config_country[func][0] - 1)
                    {
                        var emojivalue = document.createElement("div");
                        emojivalue.id = 'emoji_' + func;
                        emojivalue.style.cssText = 'text-align:center;font-size:14px;visibility:visible;';
                        emojivalue.innerHTML = emoji[0];
                        emojivalue.title = emoji[1];
                        addsign.appendChild(emojivalue);
                        signsroad.appendChild(addsign);
                    }
                });

                var btnAB = document.createElement("button");
                btnAB.innerHTML = 'A->B';
                btnAB.id = 'btnAB';
                btnAB.style.cssText = 'height: 20px;font-size:11px';

                btnAB.onclick = function() {
                    let myRoad = W.selectionManager.getSelectedFeatures()[0]._wmeObject.attributes;
                    if(myRoad.fwdDirection == true) //A to B
                    {
                        let center = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat)
                        .transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
                        var $temp = $("<input>");
                        $("body").append($temp);
                        $temp.val(center.lon.toString().slice(0,8) + "," + center.lat.toString().slice(0,8) + "|" + W.selectionManager.getSelectedFeatures()[0]["attributes"]["wazeFeature"]["_wmeObject"]["attributes"]["attributes"]["id"] + "|" + "TRUE" + "|" + getPermalink() + "|" + myRoad.fromNodeID + "|" + myRoad.toNodeID).select();
                        document.execCommand("copy");
                        $temp.remove();
                    }
                }

                var btnBA = document.createElement("button");
                btnBA.innerHTML = 'B->A';
                btnBA.id = 'btnBA';
                btnBA.style.cssText = 'height: 20px;font-size:11px';

                btnBA.onclick =  function() {
                    let myRoad = W.selectionManager.getSelectedFeatures()[0]._wmeObject.attributes;
                    if(myRoad.revDirection == true) //B to A
                    {
                        let center = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat)
                        .transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
                        var $temp = $("<input>");
                        $("body").append($temp);
                        $temp.val(center.lon.toString().slice(0,8) + "," + center.lat.toString().slice(0,8) + "|" + W.selectionManager.getSelectedFeatures()[0]["attributes"]["wazeFeature"]["_wmeObject"]["attributes"]["id"] + "|" + "FALSE" + "|" + getPermalink() + "|" + myRoad.toNodeID + "|" + myRoad.fromNodeID).select();
                        document.execCommand("copy");
                        $temp.remove();
                    }
                }

                var divSentidos = document.createElement("div");
                divSentidos.id = 'divSentidos';
                divSentidos.appendChild(btnAB);
                divSentidos.appendChild(btnBA);

                var divLandmarkScript = document.createElement("div");
                divLandmarkScript.id = 'divLandmarkScript';
                divLandmarkScript.style.cssText = 'float:left;';
                divLandmarkScript.appendChild(signsroad);
                divLandmarkScript.appendChild(divSentidos);

                $("div #segment-edit-general").prepend(divLandmarkScript);
                $("div #segment-edit-general .address-edit").css("display", "inline-block");
                $( "#divSentidos" ).hide();
            }
    }

    function defineSpeed (segment, speed) {
        let UpdateObject= require("Waze/Action/UpdateObject");
        if(segment.attributes.fwdMaxSpeed == null && segment.attributes.fwdMaxSpeed == null)
            W.model.actionManager.add(new UpdateObject(segment, {'fwdMaxSpeed': speed, 'revMaxSpeed': speed}));
        else if(segment.attributes.fwdMaxSpeed == null)
            W.model.actionManager.add(new UpdateObject(segment, {'fwdMaxSpeed': speed}));
        else if(segment.attributes.fwdMaxSpeed == null)
            W.model.actionManager.add(new UpdateObject(segment, {'revMaxSpeed': speed}));
    }

    function defineRoadType (segment, type) {
        let UpdateObject= require("Waze/Action/UpdateObject");
        W.model.actionManager.add(new UpdateObject(segment, {'roadType': type}));
    }

    function defineLockRankRoad (segment, rank) {
        let UpdateObject= require("Waze/Action/UpdateObject");
        rank--;
        var bloquear;
        if(W.loginManager.user.rank >= rank)
            bloquear = rank;
        else
            bloquear = W.loginManager.user.rank;
        let lock = segment.attributes.lockRank;
        if(lock < bloquear)
            W.model.actionManager.add(new UpdateObject(segment, {'lockRank': bloquear}));
    }

    function defineAddress(segment, street) {
        let UpdateFeatureAddress = require("Waze/Action/UpdateFeatureAddress");
        var seg_id = segment.getID();
        if(parseInt(seg_id) < 0)
        {
            document.querySelector('#segment-edit-general > div.address-edit > div.address-edit-view > div.preview > wz-card > div.full-address-container > span.full-address').click();
            setTimeout(() => {
                document.querySelector('#segment-edit-general > div.address-edit > div.address-edit-view > wz-card.address-edit-card > form.address-form > div:nth-child(2) > wz-label > div.toggle-empty > wz-checkbox.empty-street').click();
                document.querySelector('#segment-edit-general > div.address-edit > div.address-edit-view > wz-card.address-edit-card > form.address-form > div:nth-child(3) > wz-label > div.toggle-empty > wz-checkbox.empty-city').click();
            }, 50);
            setTimeout(() => {
                document.querySelector('#segment-edit-general > div.address-edit > div.address-edit-view > wz-card.address-edit-card > form.address-form > div.action-buttons > wz-button.save-button').click();
            }, 100);
        }
        else
        {
            var seg = W.model.segments.getObjectById(seg_id);
            var address = segment.getAddress().attributes;
            var newAddressAtts = {
                streetName: street,
                emptyStreet: street==""?true:false,
                cityName: "",
                emptyCity: true,
                countryID: address.country.id
            };
            W.model.actionManager.add(new UpdateFeatureAddress(seg, newAddressAtts,{streetIDField:"primaryStreetID"}));
        }
    }

    function convertSegmentType(segment) {
        let AddSegment = require("Waze/Action/AddSegment");
        let FeatureVectorSegment = require("Waze/Feature/Vector/Segment");
        let DeleteSegment = require("Waze/Action/DeleteSegment");
        let ModifyAllConnections = require("Waze/Action/ModifyAllConnections");
        let UpdateObject = require("Waze/Action/UpdateObject");
        let ConnectSegment = require("Waze/Action/ConnectSegment");

        var newseg1=new FeatureVectorSegment({geoJSONGeometry:W.userscripts.toGeoJSONGeometry(segment.attributes.geometry)});

        newseg1.copyAttributes(segment);

        newseg1.attributes.roadType=parseInt(array_config_country[indexselected][2]);
        newseg1.attributes.lockRank=null;
        newseg1.setID(null);

        W.model.actionManager.add(new DeleteSegment(segment));

        let action = new AddSegment(newseg1);
        W.model.actionManager.add(action);

        let seg = W.model.segments.getObjectById(action.segment.attributes.id);
        if(roads_id.includes(seg.attributes.roadType))
        {
            W.model.actionManager.add(new UpdateObject(seg,{fwdTurnsLocked:true,revTurnsLocked:true}))
            if(seg.getFromNode() != null)
                W.model.actionManager.add(new ConnectSegment(seg.getFromNode(),newseg1));
            if(seg.getToNode() != null)
                W.model.actionManager.add(new ConnectSegment(seg.getToNode(),newseg1));
            if(seg.getFromNode() != null)
                W.model.actionManager.add(new ModifyAllConnections(seg.getFromNode(),true));
            if(seg.getToNode() != null)
                W.model.actionManager.add(new ModifyAllConnections(seg.getToNode(),true));
        }

        return seg;
    }

    // Split Segments

    function insertButtons() {

        if (typeof W.loginManager != 'undefined' && !W.loginManager.isLoggedIn()) {
            return;
        }

        if (W.selectionManager.getSelectedFeatures().length === 0)
            return;

        let exit = false;
        $.each(W.selectionManager.getSelectedFeatures(), function(i, segment) {
            if(segment._wmeObject.attributes.fwdLaneCount != 0 || segment._wmeObject.attributes.revLaneCount != 0)
                exit = true;
            if(segment._wmeObject.attributes.fwdDirection == false || segment._wmeObject.attributes.revDirection == false)
                exit = true;
            if(pedonal_id.includes(segment._wmeObject.attributes.roadType))
                exit = true;
        });

        if(exit)
            return;

        try {
            if (document.getElementById('split-segment') !== null)
                return;
        } catch (e) {}

        var btn1 = $('<wz-button color="secondary" size="sm" style="float:right;margin-top: 5px;">' + language.btnSplit + '</wz-button>');
        btn1.click(mainSplitSegments);

        var strMeters = language.strMeters;

        var selSegmentsDistance = $('<wz-select id="segmentsDistance" data-type="numeric" value="5" style="width: 45%;float:left;" />');
        selSegmentsDistance.append($('<wz-option value="5">5 ' + strMeters + '</wz-option>'));
        selSegmentsDistance.append($('<wz-option value="7">7 ' + strMeters + '</wz-option>'));
        selSegmentsDistance.append($('<wz-option value="9">9 ' + strMeters + '</wz-option>'));
        selSegmentsDistance.append($('<wz-option value="11">11 ' + strMeters + '</wz-option>'));
        selSegmentsDistance.append($('<wz-option value="13">13 ' + strMeters + '</wz-option>'));
        selSegmentsDistance.append($('<wz-option value="15">15 ' + strMeters + '</wz-option>'));
        selSegmentsDistance.append($('<wz-option value="17">17 ' + strMeters + '</wz-option>'));
        selSegmentsDistance.append($('<wz-option value="19">19 ' + strMeters + '</wz-option>'));
        selSegmentsDistance.append($('<wz-option value="21">21 ' + strMeters + '</wz-option>'));
        selSegmentsDistance.append($('<wz-option value="23">23 ' + strMeters + '</wz-option>'));
        selSegmentsDistance.append($('<wz-option value="25">25 ' + strMeters + '</wz-option>'));

        var cnt = $('<div id="split-segment" class="form-group" style="display: flex;" />');

        var divGroup1 = $('<div/>');
        divGroup1.append($('<wz-label>' + language.strDistance + '</wz-label>'));
        divGroup1.append(selSegmentsDistance);
        divGroup1.append(btn1);
        //var divControls1 = $('<div class="controls-container" />');
        //divGroup1.append(divControls1);
        cnt.append(divGroup1);

        /*var divGroup2 = $('<div/>');
        var divControls2 = $('<div class="btn-toolbar" />');
        divControls2.append(btn1);
        divGroup2.append(divControls2);
        cnt.append(divGroup2);*/

        $(cnt).insertAfter("#segment-edit-general .attributes-form");

        $("#segmentsDistance").val(localStorage.getItem("metersSplitSegment"));

        $('#segmentsDistance').change(function(){
            localStorage.setItem("metersSplitSegment", $("#segmentsDistance").val());
        });
    }

    function orderSegments() {
        var segmentosOrdenados = [];
        var nos = [];
        var noSeguinte = null;
        $.each(W.selectionManager.getSelectedFeatures(), function(i1, segment) {
            if(nos.length > 0)
            {
                let fromExiste = false;
                let toExiste = false;
                $.each(nos, function(i2, no1) {
                    let no = null;
                    if(no1[0] == segment._wmeObject.attributes.fromNodeID)
                    {
                        no1[1] = 2;
                        fromExiste = true;
                    }

                    if(no1[0] == segment._wmeObject.attributes.toNodeID)
                    {
                        no1[1] = 2;
                        toExiste = true;
                    }
                });
                if(!fromExiste)
                    nos.push([segment._wmeObject.attributes.fromNodeID,1]);
                if(!toExiste)
                    nos.push([segment._wmeObject.attributes.toNodeID,1]);
            }
            else
            {
                nos.push([segment._wmeObject.attributes.fromNodeID,1]);
                nos.push([segment._wmeObject.attributes.toNodeID,1]);
            }
        });

        let segmentos = W.selectionManager.getSelectedFeatures().length;

        $.each(nos, function(i, no) {
            if(no[1] == 1)
            {
                noSeguinte = no[0];
                return false;
            }
        });

        while(segmentos > 0)
        {
            $.each(W.selectionManager.getSelectedFeatures(), function(i, segment) {
                if(segment._wmeObject.attributes.fromNodeID == noSeguinte)
                {
                    segmentosOrdenados.push(segment._wmeObject.attributes.id);
                    noSeguinte = segment._wmeObject.attributes.toNodeID;
                    segmentos--;
                }
                else if(segment._wmeObject.attributes.toNodeID == noSeguinte)
                {
                    segmentosOrdenados.push(segment._wmeObject.attributes.id);
                    noSeguinte = segment._wmeObject.attributes.fromNodeID;
                    segmentos--;
                }
            });
        }

        return segmentosOrdenados;
    }

    function mainSplitSegments() {

        if (W.selectionManager.getSelectedFeatures().length > 1)
            if(!confirm(language.strSelMoreSeg))
                return;

        var AddNode= require("Waze/Action/AddNode");
        var UpdateObject= require("Waze/Action/UpdateObject");
        var ModifyAllConnections= require("Waze/Action/ModifyAllConnections");

        var distancia = $("#segmentsDistance").val();
        var no = null;
        var seg_esquerda = [];
        var seg_direita = [];

        last_node_A = null;
        last_node_B = null;
        last_coord_esquerda_first = null;
        last_coord_esquerda_last = null;
        last_coord_direita_first = null;
        last_coord_direita_last = null;
        sentido_base = null;

        let segmentosOrdenados = orderSegments();

        $.each(segmentosOrdenados, function(i, idsegment) {
            var segment = W.model.segments.getObjectById(idsegment);
            let action_esquerda = null;
            let action_direita = null;
            if(last_node_A != null && last_node_B != null)
            {
                if(last_node_A == segment.getToNode())
                    no = "AB";
                if(last_node_B == segment.getFromNode())
                    no = "BA";
                if(last_node_A == segment.getFromNode())
                    no = "AA";
                if(last_node_B == segment.getToNode())
                    no = "BB";
                if(i == 1)
                {
                    if(no == "AB" || no == "AA")
                        sentido_base = "BA";
                    if(no == "BA" || no == "BB")
                        sentido_base = "AB";
                }
            }
            if(no == "AA" || no == "BB")
            {
                last_node_A = segment.getToNode();
                last_node_B = segment.getFromNode();
            }
            else
            {
                last_node_A = segment.getFromNode();
                last_node_B = segment.getToNode();
            }
            var segments = createSegments(segment, distancia, no);

            if(i > 0)
            {
                if(no == "BA")
                {
                    action_esquerda = new AddNode(W.userscripts.toGeoJSONGeometry(W.model.segments.getObjectById(segments[0]).attributes.geometry.components[0]),[W.model.segments.getObjectById(seg_esquerda[seg_esquerda.length - 1]), W.model.segments.getObjectById(segments[0])]);
                    W.model.actionManager.add(action_esquerda);

                    action_direita = new AddNode(W.userscripts.toGeoJSONGeometry(W.model.segments.getObjectById(segments[1]).attributes.geometry.components[0]),[W.model.segments.getObjectById(seg_direita[seg_direita.length - 1]), W.model.segments.getObjectById(segments[1])]);
                    W.model.actionManager.add(action_direita);
                }
                if(no == "BB")
                {
                    action_esquerda = new AddNode(W.userscripts.toGeoJSONGeometry(W.model.segments.getObjectById(segments[0]).attributes.geometry.components[0]),[W.model.segments.getObjectById(seg_esquerda[seg_esquerda.length - 1]), W.model.segments.getObjectById(segments[0])]);
                    W.model.actionManager.add(action_esquerda);

                    action_direita = new AddNode(W.userscripts.toGeoJSONGeometry(W.model.segments.getObjectById(segments[1]).attributes.geometry.components[0]),[W.model.segments.getObjectById(seg_direita[seg_direita.length - 1]), W.model.segments.getObjectById(segments[1])]);
                    W.model.actionManager.add(action_direita);
                }
                if(no == "AB")
                {
                    action_esquerda = new AddNode(W.userscripts.toGeoJSONGeometry(W.model.segments.getObjectById(segments[0]).attributes.geometry.components[W.model.segments.getObjectById(segments[0]).attributes.geometry.components.length - 1]),[W.model.segments.getObjectById(seg_esquerda[seg_esquerda.length - 1]), W.model.segments.getObjectById(segments[0])]);
                    W.model.actionManager.add(action_esquerda);

                    action_direita = new AddNode(W.userscripts.toGeoJSONGeometry(W.model.segments.getObjectById(segments[1]).attributes.geometry.components[W.model.segments.getObjectById(segments[1]).attributes.geometry.components.length - 1]),[W.model.segments.getObjectById(seg_direita[seg_direita.length - 1]), W.model.segments.getObjectById(segments[1])]);
                    W.model.actionManager.add(action_direita);
                }
                if(no == "AA")
                {
                    action_esquerda = new AddNode(W.userscripts.toGeoJSONGeometry(W.model.segments.getObjectById(segments[0]).attributes.geometry.components[W.model.segments.getObjectById(segments[0]).attributes.geometry.components.length - 1]),[W.model.segments.getObjectById(seg_esquerda[seg_esquerda.length - 1]), W.model.segments.getObjectById(segments[0])]);
                    W.model.actionManager.add(action_esquerda);

                    action_direita = new AddNode(W.userscripts.toGeoJSONGeometry(W.model.segments.getObjectById(segments[1]).attributes.geometry.components[W.model.segments.getObjectById(segments[1]).attributes.geometry.components.length - 1]),[W.model.segments.getObjectById(seg_direita[seg_direita.length - 1]), W.model.segments.getObjectById(segments[1])]);
                    W.model.actionManager.add(action_direita);
                }
            }
            W.model.actionManager.add(new UpdateObject(W.model.segments.getObjectById(segments[0]),{fwdTurnsLocked:true,revTurnsLocked:true}))
            W.model.actionManager.add(new UpdateObject(W.model.segments.getObjectById(segments[1]),{fwdTurnsLocked:true,revTurnsLocked:true}))
            seg_esquerda.push(segments[0]);
            seg_direita.push(segments[1]);
        });

        $.each(seg_esquerda, function(i, segmentos_esquerda) {
            if(i < seg_esquerda.length - 1)
            {
                let segment = W.model.segments.getObjectById(segmentos_esquerda)
                if(sentido_base == "AB")
                    W.model.actionManager.add(new ModifyAllConnections(segment.getToNode(),true))
                if(sentido_base == "BA")
                    W.model.actionManager.add(new ModifyAllConnections(segment.getFromNode(),true))
            }
        });
        $.each(seg_direita, function(i, segmentos_direita) {
            if(i > 0)
            {
                let segment = W.model.segments.getObjectById(segmentos_direita)
                if(sentido_base == "AB")
                    W.model.actionManager.add(new ModifyAllConnections(segment.getFromNode(),true))
                if(sentido_base == "BA")
                    W.model.actionManager.add(new ModifyAllConnections(segment.getToNode(),true))
            }
        });

    }

    function createSegments(sel, displacement, no) {
        var wazefeatureVectorSegment = require("Waze/Feature/Vector/Segment");
        var UpdateSegmentGeometry= require("Waze/Action/UpdateSegmentGeometry");
        var UpdateObject= require("Waze/Action/UpdateObject");

        var streetVertices = sel.geometry.simplify(0.001).getVertices();
        var esquerdaPoints = null;
        var direitaPoints = null;

        var i;
        var leftPa,
            rightPa,
            leftPb,
            rightPb;
        var prevLeftEq,
            prevRightEq;

        var first = 0;

        for (i = first; i < streetVertices.length - 1; i++) {
            var pa = streetVertices[i];
            var pb = streetVertices[i + 1];

            var points = [pa, pb];
            var ls = new OpenLayers.Geometry.LineString(points);
            var len = ls.getGeodesicLength(W.map.getProjectionObject());
            var scale = (len + displacement / 2) / len;

            leftPa = pa.clone();
            leftPa.resize(scale, pb, 1);
            rightPa = leftPa.clone();
            leftPa.rotate(90, pa);
            rightPa.rotate(-90, pa);

            leftPb = pb.clone();
            leftPb.resize(scale, pa, 1);
            rightPb = leftPb.clone();
            leftPb.rotate(-90, pb);
            rightPb.rotate(90, pb);

            var leftEq = getEquation({
                'x1': leftPa.x,
                'y1': leftPa.y,
                'x2': leftPb.x,
                'y2': leftPb.y
            });
            var rightEq = getEquation({
                'x1': rightPa.x,
                'y1': rightPa.y,
                'x2': rightPb.x,
                'y2': rightPb.y
            });
            if (esquerdaPoints === null && direitaPoints === null) {
                esquerdaPoints = [leftPa];
                direitaPoints = [rightPa];
            } else {
                var li = intersectX(leftEq, prevLeftEq);
                var ri = intersectX(rightEq, prevRightEq);
                if (li && ri) {
                    if (i >= 0) {
                        esquerdaPoints.unshift(li);
                        direitaPoints.push(ri);

                        if (i == 0) {
                            esquerdaPoints = [li];
                            direitaPoints = [ri];
                        }
                    }
                } else {
                    if (i >= 0) {
                        esquerdaPoints.unshift(leftPb.clone());
                        direitaPoints.push(rightPb.clone());

                        if (i == 0) {
                            esquerdaPoints = [leftPb];
                            direitaPoints = [rightPb];
                        }
                    }
                }
            }

            prevLeftEq = leftEq;
            prevRightEq = rightEq;

        }
        esquerdaPoints.push(leftPb);
        direitaPoints.push(rightPb);

        esquerdaPoints.unshift(esquerdaPoints[esquerdaPoints.length-1]);
        esquerdaPoints.pop();

        var newSegEsq = sel.attributes.geometry.clone();
        var newSegDir = sel.attributes.geometry.clone();

        var segmentos = SplitSegment(sel);

        esquerdaPoints = esquerdaPoints.reverse();

        if(no == "AA" || no == "BB")
        {
            let aux = esquerdaPoints.reverse();
            esquerdaPoints = direitaPoints.reverse();
            direitaPoints = aux;
        }

        if(last_coord_esquerda_first != null && last_coord_esquerda_last != null && last_coord_direita_last != null && last_coord_direita_first != null)
        {
            if(no == "AB")
            {
                esquerdaPoints.pop();
                esquerdaPoints.push(last_coord_esquerda_first);
                direitaPoints.pop();
                direitaPoints.push(last_coord_direita_first);
            }
            if(no == "BA")
            {
                esquerdaPoints.shift();
                esquerdaPoints.unshift(last_coord_esquerda_last);
                direitaPoints.shift();
                direitaPoints.unshift(last_coord_direita_last);
            }
            if(no == "AA")
            {
                esquerdaPoints.pop();
                esquerdaPoints.push(last_coord_esquerda_first);
                direitaPoints.pop();
                direitaPoints.push(last_coord_direita_first);
            }
            if(no == "BB")
            {
                esquerdaPoints.shift();
                esquerdaPoints.unshift(last_coord_esquerda_last);
                direitaPoints.shift();
                direitaPoints.unshift(last_coord_direita_last);
            }
        }

        newSegEsq.components = esquerdaPoints;
        newSegDir.components = direitaPoints;

        last_coord_esquerda_first = esquerdaPoints[0];
        last_coord_direita_first = direitaPoints[0];
        last_coord_esquerda_last = esquerdaPoints[esquerdaPoints.length - 1];
        last_coord_direita_last = direitaPoints[direitaPoints.length - 1];

        var segmentoEsquerdo = W.model.segments.getObjectById(segmentos[0]);
        var segmentoDireito = W.model.segments.getObjectById(segmentos[1]);

        W.model.actionManager.add(new UpdateSegmentGeometry(segmentoEsquerdo,segmentoEsquerdo.attributes.geoJSONGeometry,W.userscripts.toGeoJSONGeometry(newSegEsq)));
        W.model.actionManager.add(new UpdateSegmentGeometry(segmentoDireito,segmentoDireito.attributes.geoJSONGeometry,W.userscripts.toGeoJSONGeometry(newSegDir)));

        if(no == "AA" || no == "BB")
        {
            W.model.actionManager.add(new UpdateObject(segmentoEsquerdo, {'revDirection': false, 'fwdMaxSpeed': segmentoEsquerdo.attributes.revMaxSpeed, 'revMaxSpeed': segmentoEsquerdo.attributes.fwdMaxSpeed}));
            W.model.actionManager.add(new UpdateObject(segmentoDireito, {'fwdDirection': false, 'fwdMaxSpeed': segmentoDireito.attributes.revMaxSpeed, 'revMaxSpeed': segmentoDireito.attributes.fwdMaxSpeed}));
        }
        else
        {
            W.model.actionManager.add(new UpdateObject(segmentoEsquerdo, {'revDirection': false}));
            W.model.actionManager.add(new UpdateObject(segmentoDireito, {'fwdDirection': false}));
        }

        return segmentos;

    }

    function getEquation(segment) {
        if (segment.x2 == segment.x1)
            return {
                'x': segment.x1
            };

        var slope = (segment.y2 - segment.y1) / (segment.x2 - segment.x1);
        var offset = segment.y1 - (slope * segment.x1);
        return {
            'slope': slope,
            'offset': offset
        };
    }


    function intersectX(eqa, eqb, defaultPoint) {
        if ("number" == typeof eqa.slope && "number" == typeof eqb.slope) {
            if (eqa.slope == eqb.slope)
                return null;

            var ix = (eqb.offset - eqa.offset) / (eqa.slope - eqb.slope);
            var iy = eqa.slope * ix + eqa.offset;
            return new OpenLayers.Geometry.Point(ix, iy);
        } else if ("number" == typeof eqa.x) {
            return new OpenLayers.Geometry.Point(eqa.x, eqb.slope * eqa.x + eqb.offset);
        } else if ("number" == typeof eqb.y) {
            return new OpenLayers.Geometry.Point(eqb.x, eqa.slope * eqb.x + eqa.offset);
        }
        return null;
    }


    function SplitSegment(road)
    {
        let SplitSegments= require("Waze/Action/SplitSegments");
        let UpdateSegmentGeometry= require("Waze/Action/UpdateSegmentGeometry");

        if(road.arePropertiesEditable())
        {
            var geo=road.geometry.clone();
            var action=null;
            if(geo.components.length<2)
            {
                return undefined;
            }
            if(geo.components.length==2)
            {
                geo.components.splice(1,0,new OpenLayers.Geometry.Point(((geo.components[1].x+geo.components[0].x)/2),((geo.components[1].y+geo.components[0].y)/2)));
                W.model.actionManager.add(new UpdateSegmentGeometry(road,road.attributes.geoJSONGeometry,W.userscripts.toGeoJSONGeometry(geo)));
            }
            action=new SplitSegments(road,{splitAtPoint:W.userscripts.toGeoJSONGeometry(road.attributes.geometry.components[Math.ceil(road.attributes.geometry.components.length/2-1)])});
            W.model.actionManager.add(action);
            var RoadIds=new Array();
            if(action.splitSegmentPair!==null)
            {
                for(var i=0;i<action.splitSegmentPair.length;i++)
                {
                    RoadIds.push(action.splitSegmentPair[i].attributes.id);
                }
            }
            return RoadIds;
        }
    }

    function RemoveInstructions()
    {
        let SetTurn= require("Waze/Model/Graph/Actions/SetTurn");

        var nos = {};

        /*$.each(W.model.nodes.objects, function(id, no) {
            if(no.attributes.segIDs.length > 2)
                nos[id] = no.attributes.segIDs;
        });*/

        $.each(W.selectionManager.getSelectedFeatures(), function(id, segmento) {
            if(nos[segmento._wmeObject.attributes.fromNodeID] === undefined)
            {
                let aux = W.selectionManager.model.nodes.getObjectById(segmento._wmeObject.attributes.fromNodeID);
                if(aux.attributes.segIDs.length > 2)
                    nos[aux.attributes.id] = aux.attributes.segIDs;
            }
            if(nos[segmento._wmeObject.attributes.toNodeID] === undefined)
            {
                let aux = W.selectionManager.model.nodes.getObjectById(segmento._wmeObject.attributes.toNodeID);
                if(aux.attributes.segIDs.length > 2)
                    nos[aux.attributes.id] = aux.attributes.segIDs;
            }
        });

        $.each(nos, function(id, segmentos) {
            $.each(segmentos, function(index, segmento1) {
                $.each(segmentos, function(index, segmento2) {
                    if(segmento1 != segmento2)
                    {
                        let node = W.selectionManager.model.nodes.getObjectById(id);
                        let seg1 = W.selectionManager.model.segments.getObjectById(segmento1);
                        let seg2 = W.selectionManager.model.segments.getObjectById(segmento2);
                        if(W.model.getTurnGraph().getTurnThroughNode(node,seg1,seg2).getTurnData().getInstructionOpcode() != null)
                        {
                            var turn=W.model.getTurnGraph().getTurnThroughNode(node,seg1,seg2);
                            W.model.actionManager.add(new SetTurn(W.model.getTurnGraph(),turn.withTurnData(turn.getTurnData().withInstructionOpcode(null))));
                        }
                    }
                });
            });
        });
    }

    function getPermalink() {
        let PL = "";
        let center = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat)
        .transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));

        PL += window.location.origin;
        PL += window.location.pathname;
        PL += '?env=';
        PL += W.app.getAppRegionCode();
        PL += '&lon=';
        PL += center.lon.toString().slice(0,8);
        PL += '&lat=';
        PL += center.lat.toString().slice(0,8);
        PL += '&zoomLevel=';
        PL += W.map.getZoom();
        PL += '&segments=';
        $.each(W.selectionManager.getSelectedFeatures(), function(id, segmento) {
            PL += segmento["attributes"]["wazeFeature"]["_wmeObject"]["attributes"]["id"] + ",";
        });
        return PL.slice(0,-1);
    }

    function verifyNull(variable)
    {
        if(variable === null)
            return "";
        return variable["v"];
    }

    bootstrap();
})();