WME Reverse Nodes

Serves to reverse the nodes A and B of a segment, in order to better manage restrictions on multiples segments.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name             WME Reverse Nodes
// @name:fr          WME Reverse Nodes
// @description      Serves to reverse the nodes A and B of a segment, in order to better manage restrictions on multiples segments.
// @description:fr   Sert à inverser les nodes A et B d'un segment, dans le but de mieux gerer les restrictions sur segments multiples.
// @match            https://beta.waze.com/*editor*
// @match            https://www.waze.com/*editor*
// @exclude          https://www.waze.com/*user/*editor/*
// @namespace        WME_Reverse_Nodes
// @version          2.09
// @grant            unsafeWindow
// ==/UserScript==

/* global $ */
/* global require */
/* global W */

var WRNAB_version = "2.09";
var debug = true;
var wazeOBJ = {};
var w;
var newversion = 0;

/* bootstrap */
function WRNAB_bootstrap(){
    if (typeof(unsafeWindow) === "undefined"){
        unsafeWindow = ( function () {
            var dummyElem = document.createElement('p');
            dummyElem.setAttribute('onclick', 'return window;');
            return dummyElem.onclick();
        }) ();
    }
    /* begin running the code! */
    log("Start");
    if (W.version.substr(0,6) > 'v2.179') { newversion = 1 }
    setTimeout(initializeWazeObjects, 1000);
}

//==========  Helper ==============================//

function getElementsByClassName(classname, node) {
    if(!node) node = document.getElementsByTagName("body")[0];
    var a = [];
    var re = new RegExp('\\b' + classname + '\\b');
    var els = node.getElementsByTagName("*");
    for (var i=0,j=els.length; i<j; i++){
        if (re.test(els[i].className)) a.push(els[i]);
    }
    return a;
}

function getId(node) {
    return document.getElementById(node);
}

function log(msg, obj){
    if (obj==null){
        console.log("WME Reverse Nodes AB v" + WRNAB_version + " - " + msg);
    }
    else if(debug){
        console.debug("WME Reverse Nodes AB v" + WRNAB_version + " - " + msg + " " ,obj);
    }
}

function cloneObj(obj){
    var copy = JSON.parse(JSON.stringify(obj));
    return copy;
}

function clone(rest){
    return[].concat(this)
}

function withReverseDirection(rest){
    if(this.isBidi())return this;
    var e=this.isForward()?w.REV:w.FWD;
    return this.with({direction:e})
}

function getByID(obj, id){
    if (typeof(obj.getObjectById) == "function"){
        return obj.getObjectById(id);
    }else if (typeof(obj.getObjectById) == "undefined"){
        return obj.get(id);
    }
}

//==========  /Helper ==============================//

function initializeWazeObjects(){

    log("init");
    var objectToCheck = [
        {o: "W", s: "waze"},
        {o: "W.model", s: "wazeModel"},
        {o: "W.map", s: "wazeMap"},
        {o: "W.loginManager", s: "WazeLoginManager"},
        {o: "W.selectionManager", s: "WazeSelectionManager"},
        {o: "Waze/Action/UpdateObject", s: "WazeActionUpdateObject"},
        {o: "Waze/Action/UpdateSegmentGeometry", s: "WazeUpdateSegmentGeometry"},
        {o: "Waze/Action/ConnectSegment", s: "WazeActionConnectSegment"},
        {o: "Waze/Action/DisconnectSegment", s: "WazeActionDisconnectSegment"},
        {o: "Waze/Action/MultiAction", s: "WazeActionMultiAction"},
        {o: "Waze/Model/Graph/Actions/SetTurn", s: "WazeModelGraphActionsSetTurn"},
        //{o: "Waze/Model/Graph/Turn",              s: "WazeModelGraphTurn"},
        {o: "Waze/Model/Graph/TurnData", s: "WazeModelGraphTurnData"},
        //{o: "Waze/Model/Graph/TurnGraph",         s: "WazeModelGraphTurnGraph"},
        //{o: "Waze/Model/Graph/Vertex",            s: "WazeModelGraphVertex"},
        {o: "W.loginManager.user", s: "me"},
        {o: "localStorage", s: null}
    ];

    if (newversion) {
        objectToCheck.push({o: "W.loginManager.user.attributes.rank", s: "ul"})
        objectToCheck.push({o: "W.loginManager.user.attributes.isAreaManager", s: "uam"})
    } else {
        objectToCheck.push({o: "W.loginManager.user.rank", s: "ul"})
        objectToCheck.push({o: "W.loginManager.user.isAreaManager", s: "uam"})
    }

    for (var i=0; i<objectToCheck.length; i++){
        if (objectToCheck[i].o.indexOf("/") != -1) {
            if (objectToCheck[i].s != null) wazeOBJ[objectToCheck[i].s] = require(objectToCheck[i].o);
        } else {
            var path = objectToCheck[i].o.split(".");
            var object = unsafeWindow;
            for (var j = 0; j < path.length; j++) {
                object = object[path[j]];
                if (typeof object == "undefined" || object == null) {
                    window.setTimeout(initializeWazeObjects, 1000);
                    return;
                }else{ if (objectToCheck[i].s != null) wazeOBJ[objectToCheck[i].s] = object;}
            }
        }
    }
    log("wazeOBJ :",wazeOBJ);
    initializeWazeUI();
}

function initializeWazeUI(){
    var userInfo = getId('user-info');
    if (userInfo==null)
    {
        window.setTimeout(initializeWazeUI, 500);
        return;
    }

    var navTabs=userInfo.getElementsByTagName('ul');
    if (navTabs.length==0)
    {
        window.setTimeout(initializeWazeUI, 500);
        return;
    }
    if (typeof(navTabs[0])==='undefined')
    {
        window.setTimeout(initializeWazeUI, 500);
        return;
    }

    var tabContents=userInfo.getElementsByTagName('div');
    if (tabContents.length==0)
    {
        window.setTimeout(initializeWazeUI, 500);
        return;
    }
    if (typeof(tabContents[0])==='undefined')
    {
        window.setTimeout(initializeWazeUI, 500);
        return;
    }

    WRNAB_Init();
}

function WRNAB_newSelectionAvailable(){
    //log('wazeOBJ.WazeSelectionManager',wazeOBJ.WazeSelectionManager);
    if (wazeOBJ.WazeSelectionManager.getSelectedFeatures() !== undefined){
        var selection = wazeOBJ.WazeSelectionManager.getSelectedFeatures();
    }
    if (wazeOBJ.WazeSelectionManager.selectedItems !== undefined){
        selection = wazeOBJ.WazeSelectionManager.selectedItems;
    }
    log('selection', selection);

    if (selection === undefined || selection.length !=1 ){
        return;
    }
    var selectedObject = W.version.substr(1,3) > '2.1' ? selection[0]._wmeObject : selection[0].attributes.wazeFeature._wmeObject;
    if (selectedObject.type!="segment"){
        return;
    }
    log('wazeOBJ.uam', wazeOBJ.uam);
    if (!wazeOBJ.uam) return;

    var editPanel=getId('edit-panel');
    log(editPanel)
    if (editPanel.firstElementChild.style.display=='none'){
        window.setTimeout(WRNAB_newSelectionAvailable, 100);
    }

    // ok: 1 selected item and pannel is shown
    append_WRNAB_Controle();
}

function append_WRNAB_Controle() { // wait for 'sidebar'
    var WRNAB_Controle1 = create_WRNAB_Controle

    if (document.getElementById('segment-edit-general')!=null) {
//        if (!document.getElementById('WRNAB_Controle')) {
            $("#segment-edit-general").append(WRNAB_Controle1);
//    }
    }
    else {
        setTimeout (function () {append_WRNAB_Controle();}, 1001);
    }
}

function create_WRNAB_Controle () {
    var WRNAB_Controle = $ ('<div id="WRNAB_Controle" style="border: none;"/>');
    var btn1 = $('<button style="display: block; margin: auto; background-color: #0099FF; color: white; border: none; border-radius: 40px; box-sizing: border-box; line-height: 20px;">Reverse Nodes A/B</button>');
    btn1.click	(invertNodeAB);
    var cnt1 = $('<section id="WRNAB_cnt1" style="margin:2px; display:inline;"/>'); cnt1.append(btn1);
    WRNAB_Controle.append(cnt1);
    return WRNAB_Controle;
}

function WRNAB_Init(){
    wazeOBJ.WazeSelectionManager.events.register("selectionchanged", null, WRNAB_newSelectionAvailable);
    log('init done.');
}

function onScreen(obj){
    if (obj.geometry)
    {
        return(wazeOBJ.wazeMap.getExtent().intersectsBounds(obj.geometry.getBounds()));
    }
    return false;
}

function isSegmentEditable(seg){
    var rep = true;
    if (seg==null) return false;
    //if (wazeOBJ.Waze.loginManager.user.isCountryManager() ) rep = true;
    if (!wazeOBJ.uam) rep = false;
    var ndA = getByID(wazeOBJ.wazeModel.nodes,seg.attributes.fromNodeID);
    var ndB = getByID(wazeOBJ.wazeModel.nodes,seg.attributes.toNodeID);

    if ((seg.attributes.permissions == 0) || (seg.attributes.hasClosures)){
        rep = false;
    }
/*    if ((ndA.attributes.permissions == 0) || (seg.attributes.hasClosures)){
        rep = false;
    }
    if ((ndB.attributes.permissions == 0) || (seg.attributes.hasClosures)){
        rep = false;
    }
*/
    return rep;
}

function invertNodeAB(){
    if (wazeOBJ.WazeSelectionManager.getSelectedFeatures() !== undefined){
        var selection = wazeOBJ.WazeSelectionManager.getSelectedFeatures();
    }
    if (wazeOBJ.WazeSelectionManager.selectedItems !== undefined){
        selection = wazeOBJ.WazeSelectionManager.selectedItems;
    }
    log('selection', selection);

    if (selection === undefined || selection.length !=1 ){
        return;
    }
    var seg = getByID(wazeOBJ.wazeModel.segments,W.version.substr(1,3) > '2.1' ? selection[0]._wmeObject.attributes.id : selection[0].attributes.wazeFeature._wmeObject.attributes.id);
    if (seg.type!="segment"){
        return;
    }
    var attr = seg.attributes;
    log("seg",seg);
    log("attr",attr);

    var sid = attr.id;

    // if unnamed, don't touch
    if (attr.primaryStreetID==null){
        log("Unnamed segment. Do not put my name on it!");
        return;
    }

    // if locked, don't touch
    /*if ((attr.lockRank==null && attr.rank>usrRank) ||
        (attr.lockRank!=null && attr.lockRank>usrRank)){
        log("locked: " + attr.rank + " " + attr.lockRank);
        continue;
    }
    */

    var ndA = getByID(wazeOBJ.wazeModel.nodes,attr.fromNodeID);
    var ndB = getByID(wazeOBJ.wazeModel.nodes,attr.toNodeID);
    log("ndA", ndA);
    log("ndB", ndB);
    // verification de pbs sur nodes (unterminated roads par ex)
    // check if bad node (unterminated roads or over...)
    if (!ndA || !ndB){
        log("Bad nodes: A=" + ndA + " or B=" + ndB);
        return;
    }

    // On verifie que le segment est affiche a l'ecran
    if (onScreen(ndA) && onScreen(ndB)){
        log("IN Screen!");
    }
    else{
        log("OUT of Screen");
        return;
    }

    // On verifie que le segment est editable
    console.log(seg)
    if (!isSegmentEditable(seg)){
        log("Not editable!");
        return;
    }

    // Memorisation des parametre du segment pour les reporter apres changement de sens
    // Storing segment parameters for restore them after changing direction
    var newAttr={};
    //var fromConnections = attr.toConnections.clone();
    //var toConnections = attr.fromConnections.clone();
    newAttr.fwdDirection = attr.revDirection;
    newAttr.revDirection = attr.fwdDirection;
    newAttr.fwdTurnsLocked = attr.revTurnsLocked;
    newAttr.revTurnsLocked = attr.fwdTurnsLocked;
    newAttr.fwdMaxSpeed = attr.revMaxSpeed;
    newAttr.revMaxSpeed = attr.fwdMaxSpeed;
    newAttr.fwdMaxSpeedUnverified = attr.revMaxSpeedUnverified;
    newAttr.revMaxSpeedUnverified = attr.fwdMaxSpeedUnverified;
    newAttr.fwdLaneCount = attr.revLaneCount;
    newAttr.revLaneCount = attr.fwdLaneCount;
    newAttr.restrictions = [];

    for (var i=0; i<attr.restrictions.length; i++){
        newAttr.restrictions[i] = attr.restrictions[i].withReverseDirection();
    }

    log("newAttr", newAttr);

    /*----------------------------------------------------------------------------

     New editor:
     f = forward direction (A>B)
     r = reverse direction (B>A)

     W.model.turnGraph._adjacencyList["280125026r"]
     --> {295413973f: {instructionOpcode: null,
                      restrictions:Array[0],
                      state:1 }

     W.model.turnGraph.getTurn(W.model.segments.objects["280125026"],W.model.segments.objects["280125024"])
     --> {fromVertex, toVertex , turnData}

     turn = W.model.getTurnGraph().getTurnThroughNode(W.model.nodes.get("232242442"),W.model.segments.get("280125026"),W.model.segments.get("280125024"))
           = W.model.turnGraph.getTurn( turn.getFromVertex(), turn.getToVertex())
           --> {fromVertex, toVertex , turnData}

     turn.getTurnData().getInstructionOpcode()

     W.model.getRoadGraph().getVertexNodeID(W.model.getTurnGraph().getTurnThroughNode(W.model.nodes.get("232242442"),W.model.segments.get("280125026"),W.model.segments.get("280125024")).getFromVertex())
     e {fromVertex: e, toVertex: e, turnData: e}
            fromVertex:e
                direction:true    --> A>B
                segmentID:69264747
            toVertex:e
                direction:false    --> B>A
                segmentID:69264745
            turnData:e
                instructionOpcode:null
                restrictions:Array[0]
                state:2

     -------------------------------------
     CLASS_NAME: "Waze.Action.SetTurn" --> require("Waze/Model/Graph/Actions/SetTurn");

    -----------------------------------------------------------------------------*/
    var sconA={};
    var sconB={};
    var fromConnections = {};
    var toConnections = {};

    // pour les segments connecte à la node A

    for (var s=0; s<ndA.attributes.segIDs.length; s++){
        var scon=ndA.attributes.segIDs[s];

        // On memorise les directions entrantes
        if (scon != attr.id){
            if (debug){
                log("sconA; mem "+scon+" --> ndA: "+ndA.attributes.id+" --> " + attr.id);
                log("turn",wazeOBJ.wazeModel.getTurnGraph().getTurnThroughNode(ndA, getByID(wazeOBJ.wazeModel.segments,scon), getByID(wazeOBJ.wazeModel.segments,attr.id)));
            }
            sconA[scon]= wazeOBJ.wazeModel.getTurnGraph().getTurnThroughNode(ndA, getByID(wazeOBJ.wazeModel.segments,scon),getByID(wazeOBJ.wazeModel.segments,attr.id));
            sconA[scon].toVertex.direction = sconA[scon].toVertex.direction == "fwd" ? "rev" : "fwd"
        }

        // On memorise les directions sortantes
        if (debug){
            log("toConnections; mem "+attr.id+" -->  ndA: "+ndA.attributes.id+" --> " + scon);
            log("turn",wazeOBJ.wazeModel.getTurnGraph().getTurnThroughNode(ndA,getByID(wazeOBJ.wazeModel.segments,attr.id),getByID(wazeOBJ.wazeModel.segments,scon)));
        }
        toConnections[scon] = wazeOBJ.wazeModel.getTurnGraph().getTurnThroughNode(ndA,getByID(wazeOBJ.wazeModel.segments,attr.id),getByID(wazeOBJ.wazeModel.segments,scon));
        toConnections[scon].fromVertex.direction = toConnections[scon].fromVertex.direction == "fwd" ? "rev" : "fwd"
        if (scon == attr.id){ //u-turn
            toConnections[scon].toVertex.direction = toConnections[scon].toVertex.direction == "fwd" ? "rev" : "fwd"
        }
    }
    // pour les segments connecte à la node B
    for (s=0; s<ndB.attributes.segIDs.length; s++){
        scon=ndB.attributes.segIDs[s];
        // On memorise les directions entrantes
        if (scon != attr.id){
            if (debug){
                log("sconB; mem "+scon+" -->  ndB: "+ndB.attributes.id+" --> " + attr.id);
                log("turn",wazeOBJ.wazeModel.getTurnGraph().getTurnThroughNode(ndB,getByID(wazeOBJ.wazeModel.segments,scon),getByID(wazeOBJ.wazeModel.segments,attr.id)));
            }
            sconB[scon]= wazeOBJ.wazeModel.getTurnGraph().getTurnThroughNode(ndB,getByID(wazeOBJ.wazeModel.segments,scon),getByID(wazeOBJ.wazeModel.segments,attr.id));
            sconB[scon].toVertex.direction = sconB[scon].toVertex.direction == "fwd" ? "rev" : "fwd"
        }
        // On memorise les directions sortantes
        if (debug){
            log("fromConnections; mem "+attr.id+" --> ndB: "+ndB.attributes.id+" --> " + scon);
            log("turn",wazeOBJ.wazeModel.getTurnGraph().getTurnThroughNode(ndB,getByID(wazeOBJ.wazeModel.segments,attr.id),getByID(wazeOBJ.wazeModel.segments,scon)));
        }
        fromConnections[scon] = wazeOBJ.wazeModel.getTurnGraph().getTurnThroughNode(ndB,getByID(wazeOBJ.wazeModel.segments,attr.id),getByID(wazeOBJ.wazeModel.segments,scon));
        fromConnections[scon].fromVertex.direction = fromConnections[scon].fromVertex.direction == "fwd" ? "rev" : "fwd"
        if (scon == attr.id){ //u-turn
            fromConnections[scon].toVertex.direction = fromConnections[scon].toVertex.direction == "fwd" ? "rev" : "fwd"
        }
    }

    //log("wazeOBJ.wazeModel.getTurnGraph()._adjacencyList["+attr.id+"f]", wazeOBJ.wazeModel.getTurnGraph()._adjacencyList[attr.id+"f"]);
    //log("wazeOBJ.wazeModel.getTurnGraph()._adjacencyList["+attr.id+"r]", wazeOBJ.wazeModel.getTurnGraph()._adjacencyList[attr.id+"r"]);

    log("sconA",sconA);
    log("sconB",sconB);
    log("fromConnections", fromConnections);
    log("toConnections", toConnections);

    //on inverse la geometrie du segment
    var geo = { ... seg.getGeometry() };
    geo.coordinates.reverse();

    // controle de position
    var nbPoints = geo.coordinates.length-1;
    if (!(JSON.stringify(geo.coordinates[0]) == JSON.stringify(ndB.getGeometry().coordinates))){
        if (debug) log("point 0 et dif de node A");
        var delta = {x:0, y:0};
        delta.x =ndB.attributes.geoJSONGeometry.coordinates[0] - geo.coordinates[0][0];
        delta.y = ndB.attributes.geoJSONGeometry.coordinates[1] - geo.coordinates[0][1];
        geo.coordinates[0][0] += delta.x;
        geo.coordinates[0][1] += delta.y;
    }
    if (!(JSON.stringify(geo.coordinates[nbPoints])) == (JSON.stringify(ndA.getOLGeometry().coordinates))){
        if (debug) log("point "+nbPoints+ " est dif de node B");
        delta = {x:0, y:0};
        delta.x = ndA.attributes.geoJSONGeometry.coordinates[0] - geo.coordinates[nbPoints][0];
        delta.y = ndA.attributes.geoJSONGeometry.coordinates[1] - geo.coordinates[nbPoints][1];
        geo.coordinates[nbPoints][0] += delta.x;
        geo.coordinates[nbPoints][1] += delta.y;
    }

    // On deconnecte le segment
    wazeOBJ.wazeModel.actionManager.add(new wazeOBJ.WazeActionMultiAction([new wazeOBJ.WazeActionDisconnectSegment(seg, ndA),new wazeOBJ.WazeActionDisconnectSegment(seg, ndB)]));

    // maj de la geo du seg
    wazeOBJ.wazeModel.actionManager.add(new wazeOBJ.WazeUpdateSegmentGeometry (seg,seg.getGeometry(),geo));

    //on replace les parametre inverse
    wazeOBJ.wazeModel.actionManager.add(new wazeOBJ.WazeActionUpdateObject(seg, newAttr));

    // On reconnecte le segment
    wazeOBJ.wazeModel.actionManager.add(new wazeOBJ.WazeActionMultiAction([new wazeOBJ.WazeActionConnectSegment(ndB, seg),new wazeOBJ.WazeActionConnectSegment(ndA, seg)]));

    //on replace les autorisations sortantes

    var actions = [];
    for (sid in fromConnections){
        var segId = fromConnections[sid].toVertex.segmentID;
        log("attr.id: " + attr.id + " --> NodeA: " + ndB.attributes.id + " --> segId: " + segId + " ; state = "+fromConnections[sid].turnData.state);
        if (debug) log("fromConnections["+sid+"] = ", fromConnections[sid]);
        switch (fromConnections[sid].turnData.state){
            case 0 :
            case 1 :
                //var turn = new wazeOBJ.WazeModelGraphTurnData(wazeOBJ.WazeModelGraphTurnData.State.ALLOWED);
                //var turn = new wazeOBJ.WazeModelGraphTurnData.createAllowed;
                var turn = new wazeOBJ.WazeModelGraphTurnData;
                turn = turn.withState(fromConnections[sid].turnData.state);
                turn = turn.withRestrictions(fromConnections[sid].turnData.restrictions);
                turn = turn.withInstructionOpcode(fromConnections[sid].turnData.instructionOpcode);
                turn = turn.withLanes(fromConnections[sid].turnData.lanes);
                if (debug) log("turn ", turn);
                actions.push(new wazeOBJ.WazeModelGraphActionsSetTurn(wazeOBJ.wazeModel.getTurnGraph(), fromConnections[sid].withTurnData(turn)));
                break;
        }
    }

    for (sid in toConnections){
        segId = toConnections[sid].toVertex.segmentID;
        log("attr.id: " + attr.id + " --> NodeB: " + ndA.attributes.id + " --> segId: " + segId + " ; state = "+toConnections[sid].turnData.state);
        if (debug) log("toConnections["+sid+"] = ", toConnections[sid]);
        switch (toConnections[sid].turnData.state){
            case 0 :
            case 1 :
                turn = new wazeOBJ.WazeModelGraphTurnData;
                turn = turn.withState(toConnections[sid].turnData.state);
                turn = turn.withRestrictions(toConnections[sid].turnData.restrictions);
                turn = turn.withInstructionOpcode(toConnections[sid].turnData.instructionOpcode);
                turn = turn.withLanes(toConnections[sid].turnData.lanes);
                if (debug) log("turn ", turn);
                actions.push(new wazeOBJ.WazeModelGraphActionsSetTurn(wazeOBJ.wazeModel.getTurnGraph(), toConnections[sid].withTurnData(turn)));
                break;
        }
    }
    wazeOBJ.wazeModel.actionManager.add(new wazeOBJ.WazeActionMultiAction(actions));
    actions=[];
    //on replace les autorisations entrantes
    log("ndB", ndB);
    for (sid in sconB){
        log("sconB: "+sid+" --> nodeA: " + ndB.attributes.id + "--> attr.id: " + attr.id + " ; state = "+sconB[sid].turnData.state);
        if (debug) log("sconB["+sid+"]:",sconB[sid]);
        switch (sconB[sid].turnData.state){
            case 0 :
            case 1 :
                turn = new wazeOBJ.WazeModelGraphTurnData;
                turn = turn.withState(sconB[sid].turnData.state);
                turn = turn.withRestrictions(sconB[sid].turnData.restrictions);
                turn = turn.withInstructionOpcode(sconB[sid].turnData.instructionOpcode);
                turn = turn.withLanes(sconB[sid].turnData.lanes);
                if (debug) log("turn ", turn);
                actions.push(new wazeOBJ.WazeModelGraphActionsSetTurn(wazeOBJ.wazeModel.getTurnGraph(), sconB[sid].withTurnData(turn)));
                break;
        }
    }
    log("ndA", ndA);
    for (sid in sconA){
        log("sconA : "+sid+" --> nodeB: " + ndA.attributes.id + " --> attr.id: " + attr.id+ " ; state = "+sconA[sid].turnData.state);
        if (debug) log("sconA["+sid+"]:",sconA[sid]);
        switch (sconA[sid].turnData.state){
            case 0 :
            case 1 :
                turn = new wazeOBJ.WazeModelGraphTurnData;
                turn = turn.withState(sconA[sid].turnData.state);
                turn = turn.withRestrictions(sconA[sid].turnData.restrictions);
                turn = turn.withInstructionOpcode(sconA[sid].turnData.instructionOpcode);
                turn = turn.withLanes(sconA[sid].turnData.lanes);
                if (debug) log("turn ", turn);
                actions.push(new wazeOBJ.WazeModelGraphActionsSetTurn(wazeOBJ.wazeModel.getTurnGraph(), sconA[sid].withTurnData(turn)));
                break;
        }
    }
    wazeOBJ.wazeModel.actionManager.add(new wazeOBJ.WazeActionMultiAction(actions));
    log('Invert node for segment '+attr.id+': Ok');
}

WRNAB_bootstrap();