WME Select Same Type Roads

This script add functionnality to select and modify roads

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name WME Select Same Type Roads
// @author buchet37
// @namespace https://greasyfork.org/users/4062
// @description This script add functionnality to select and modify roads
// @match https://world.waze.com/editor*
// @match https://www.waze.com/editor*
// @match https://world.waze.com/map-editor*
// @match https://www.waze.com/map-editor*
// @match https://*.waze.com/editor*
// @match https://*.waze.com/*/editor*
// @match https://*.waze.com/map-editor*
// @match https://*.waze.com/beta_editor*
// @match https://descarte*.waze.com/beta*
// @match https://editor-beta.waze.com*
// @version         6.0.1
// ==/UserScript==

// Based on Street to River ( http://userscripts.org/scripts/show/122049 )
// Thanks to alcolo47 (some functions are based on WME Extented Tools)
// Thanks to gdu1971, bgodette, Timbones for part of code
// Adapted by buchet37 for "Select Same Type Road"

// Mini howto:
// 1) install this script as greasemonkey script or chrome extension
// 2) Select 2 segments
// 3) Click the "Select Roads A<=>B" button
// The script will select all same type road between A and B with a limit of 50 segments

/* global require */
/* global $ */
/* global OpenLayers */
/* global W */
/* global I18n */
/* global SDK_INITIALIZED */

var WME_SSTR_version = "6.0.1" ;


// *************
// **  INIT   **
// *************

//function initScript() {
    // initialize the sdk with your script id and script name
//    const wmeSDK = getWmeSdk(
        //		  {scriptId: "your-userscript-id", scriptName: "Script Display Name"});
//        {scriptId: "SSTR", scriptName: "Select Same Type Road"});
//}

function SSTR_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("starting");
    selectSameTypeRoad();
}

function selectSameTypeRoad() {
    if (W && W.map && W.model && SDK_INITIALIZED && 'undefined' != typeof require ) {
        //       alert ("le SDK n'est pas encore chargé");
        //        window.SDK_INITIALIZED.then(initScript);
        unsafeWindow.SDK_INITIALIZED.then(start_selectSameTypeRoad);
    }
    else { setTimeout(selectSameTypeRoad , 1000); }
}

function start_selectSameTypeRoad() {
    const wmeSDK = getWmeSdk({scriptId: "SSTR", scriptName: "Select Same Type Road"});
    var defaultWidth = "15 m"; //Default Width is equal to 15m

    // *****************   COMPATIBILITY WITH NEW EDITOR     ***********
    //  var WazeActionAddLandmark = require("Waze/Action/AddLandmark");
    //  var WazeActionAddOrGetCity = require("Waze/Action/AddOrGetCity");
    //  var WazeActionAddOrGetStreet = require("Waze/Action/AddOrGetStreet");
    //  var WazeActionCreateObject = require("Waze/Action/CreateObject");
    //  var WazeActionCreateRoundabout = require ("Waze/Action/CreateRoundabout");
    //  var WazeActionDeleteSegment = require ("Waze/Action/DeleteSegment");
    //  var WazeActionModifyConnection = require ("Waze/Action/ModifyConnection");
    var WazeActionMultiAction = require ("Waze/Action/MultiAction");
    // var WazeActionUpdateObject = require("Waze/Action/UpdateObject");
    var WazeActionUpdateSegmentGeometry = require("Waze/Action/UpdateSegmentGeometry");
    //  var WazeFeatureVectorLandmark = require("Waze/Feature/Vector/Landmark");
    var WazeActionMoveNode = require("Waze/Action/MoveNode");

    // *****************************************************************

    setTimeout (function () {insertButton();}, 4001);		//tempo

    function insertButton() {
        if(document.getElementById('WME_SSTR_All') != null) return;
        var WME_SSTR_ALL1 = create_WME_SSTR_ALL ();

        // ******* Mise en place des buttons ****
        var WME_SSTR_ALL_Flag = false, myDialogBoxFlag = false;

        function put_WME_SSTR_ALL() { // wait for 'sidebar'
            if (document.getElementById('segment-edit-general')!=null) {
                $("#segment-edit-general").append(WME_SSTR_ALL1);
                WME_SSTR_ALL_Flag = true;
            }
            else {
                setTimeout (function () {put_WME_SSTR_ALL();}, 1001);
            }
        }

        put_WME_SSTR_ALL();

        function start_init_WME_SSTR() { // si tous les boutons sont chargés on démarre le script
            if (WME_SSTR_ALL_Flag) {
                init_WME_SSTR();
            }
            else {setTimeout(function () {start_init_WME_SSTR();}, 501);}
        }
        start_init_WME_SSTR();
        return;
    }

    function put_WME_SSTR_button () {
        if(document.getElementById('WME_SSTR_All') != null) return ;
        var selectedItems = getSelectedSegmentsIds();
        if (selectedItems.length != 0) { // s'il y aune selection de segment active
            var WME_SSTR_ALL1 = create_WME_SSTR_ALL ();
            if (document.getElementById('segment-edit-general')!=null) {
                $("#segment-edit-general").append(WME_SSTR_ALL1); //on met le menu et on intilise les check box
                if (localStorage['WME_SSTR_enable']=='true') {	// restaure old Values (if exist)
                    document.getElementById ('WME_SSTR_enable').checked = 1;}
                if (localStorage['WME_SSTR_Smth']=='true') {
                    document.getElementById ('WME_SSTR_SmthRvr').checked = 1;}
            }
            else {
                setTimeout (function () {put_WME_SSTR_button();}, 1001); //autrement on attend
            }
        }
        return;
    }

    function create_WME_SSTR_ALL () {
        var chk1 = $('<Label style="font-weight:normal"><input type="checkbox"; style="vertical-align: middle;margin:0px;" id="WME_SSTR_enable"	title="Enable or Disable WME SSTR">On-Off    </input></Label>');
        var chk2 = $('<Label style="font-weight:normal;margin:0px 5px 0px 0px"><input type="checkbox"; style="vertical-align: middle;margin:0px;" id="WME_SSTR_SmthRvr" title="Check for smoothing">Smooth</input></Label>');
        var url1 = $('<div style="font-size:12px;display: inline;"> <u><i><a href="https://greasyfork.org/scripts/4715-wme-select-same-type-roads" target="_blank">Select Same Type Road ' + WME_SSTR_version+ '</a></i></u>');

        var btn1 = $('<button class="waze-btn waze-btn-small waze-btn-white" style="padding:0px 6px; height:20px;" title="Select 1 or more segments and click this button">Select Same Type Roads</button>');
        var btn2 = $('<button class="waze-btn waze-btn-small waze-btn-white" style="padding:0px 6px; height:20px; margin-right:5px;" title="Select adjacent segment from node A">A =></button>');
        var btn3 = $('<button class="waze-btn waze-btn-small waze-btn-white" style="padding:0px 6px; height:20px;" title="Select adjacent segment from node B">B =></button>');
        var btn4 = $('<button class="waze-btn waze-btn-small waze-btn-white" style="padding:0px 6px; height:20px;" title="Start from segment 1 to join Segment 2 (if possible)">1 => 2</button>');
        var btn7 = $('<button class="waze-btn waze-btn-small waze-btn-white" style="padding:0px 6px; height:20px; margin-right:5px; " title="Create a River from Street Geometry">Street => River</button>');
        var btn8 = $('<button class="waze-btn waze-btn-small waze-btn-white" style="padding:0px 6px; height:20px;" title="Select road(s) to make an Overall Landmark">Do Landmark</button>');
        var btn10= $('<button class="waze-btn waze-btn-small waze-btn-white" style="padding:0px 6px; height:20px; margin:2px;width:250px;" title="Make a new roundabout from 1 segment of an old one">Redo Roundabout</button>');
        var btn12= $('<button class="waze-btn waze-btn-small waze-btn-white" style="padding:0px 6px; height:20px; margin-right:5px; " title="click this button to suppress road geometry">Clear Road Geometry</button>');
        var btn14= $('<button class="waze-btn waze-btn-small waze-btn-white" style="font-size:20px; padding:0px 6px; height:auto; margin: 2px;" >&#11013</button>');
        var btn15= $('<button class="waze-btn waze-btn-small waze-btn-white" style="font-size:20px; padding:0px 6px; height:auto; margin: 2px;" >&#10145</button>');
        var btn16= $('<button class="waze-btn waze-btn-small waze-btn-white" style="font-size:20px; padding:0px 6px; height:auto; margin: 2px;" >&#11015</button>');
        var btn17= $('<button class="waze-btn waze-btn-small waze-btn-white" style="font-size:20px; padding:0px 6px; height:auto; margin: 2px;" >&#11014</button>');
        var btn18= $('<button class="waze-btn waze-btn-small waze-btn-white" style="font-size:20px; padding:0px 6px; height:auto; margin: 2px;" > -- </button>');
        var btn19= $('<button class="waze-btn waze-btn-small waze-btn-white" style="font-size:20px; padding:0px 6px; height:auto; margin: 2px;" > ++ </button>');
        var btn20= $('<button class="waze-btn waze-btn-small waze-btn-white" style="font-size:20px; padding:0px 6px; height:auto; margin: 2px;" >&#11119</button>');
        var btn21= $('<button class="waze-btn waze-btn-small waze-btn-white" style="font-size:20px; padding:0px 6px; height:auto; margin: 2px;" >&#11118</button>');

        btn1.click	(select_same_type_roads);
        btn2.click	(Side_A);
        btn3.click	(Side_B);
        btn4.click	(select_AB);
        btn7.click	(Street_River);
        btn8.click	(Roads_to_Interchange);
        btn12.click (Clear_Road_Geometry);
        chk1.click  (manage_WME_SSTR);
        chk2.click  (manageSmoothRiver);
        btn14.click (Redo_Rdt_Xmoins);btn15.click (Redo_Rdt_Xplus);btn16.click (Redo_Rdt_Ymoins);btn17.click (Redo_Rdt_Yplus);
        btn18.click (Redo_Rdt_Rmoins);btn19.click (Redo_Rdt_Rplus);btn20.click (Redo_Rdt_Rotmoins);btn21.click (Redo_Rdt_Rotplus);

        var WME_SSTR_ALL = $ ('<div id="WME_SSTR_All"  style="height: auto;  padding:2px 2px 2px 5px;margin:5px 0px 0px -5px;width:295px; border-width:3px; border-style:double;border-color: SkyBlue; border-radius:10px"/>');

        var cnt0 = $('<section id="WME_SSTR_lnk" 	style="padding-top:2px; margin:2px;"/>'); cnt0.append(chk1);cnt0.append(" ");cnt0.append(url1);
        var cnt1 = $('<section id="WME_SSTR"		style="padding-top:2px; margin:2px; display:inline;"/>'); cnt1.append(btn1);
        var cnt2 = $('<section id="WME_SSTR_Side"	style="padding-top:2px; margin:2px;"/>'); cnt2.append(btn2);cnt2.append(btn3);
        var cnt3 = $('<section id="WME_SSTR_12"		style="padding-top:2px; margin:2px;"/>'); cnt3.append(btn4);
        var cnt4 = $('<section id="WME_SSTR_River"	style="padding-top:2px; margin:2px;"/>'); cnt4.append(btn7); cnt4.append(chk2);
        var cnt6 = $('<section id="WME_SSTR_Ldmk"	style="padding-top:2px; margin:2px;"/>'); cnt6.append(btn8);
        var cnt7_1 = $('<div style="padding-top:2px; margin:2px;"/>'); cnt7_1.append("&ensp;Move&emsp;",btn14,btn15,btn16,btn17);
        var cnt7_2 = $('<div style="padding-top:2px; margin:2px;"/>'); cnt7_2.append("&ensp;Strech&ensp;",btn18,btn19,btn20,btn21);
        var cnt7 = $('<section id="WME_SSTR_Rdt"	style="padding-top:2px; margin:2px;border-width:1px; border-style:solid;border-color: SkyBlue; border-radius:6px; background-color:#ebebeb"/>');
        cnt7.append(cnt7_1,cnt7_2);
        var cnt8 = $('<section id="WME_SSTR_CrgAds"	style="padding-top:2px; margin:2px;"/>'); cnt8.append(btn12);

        WME_SSTR_ALL.append(cnt0);
        WME_SSTR_ALL.append(cnt1);
        WME_SSTR_ALL.append(cnt2);
        WME_SSTR_ALL.append(cnt3);
        WME_SSTR_ALL.append(cnt4);
        WME_SSTR_ALL.append(cnt6);
        WME_SSTR_ALL.append(cnt7);
        WME_SSTR_ALL.append(cnt8);

        return WME_SSTR_ALL;
    }

    function Clear_Road_Geometry(ev) {    // ******* PASS TO SDK ********
        var selectedItems = getSelectedSegmentsIds();
        if (selectedItems.length!=0) {
            if (confirm ("Do you want to clear the geometry for selected segments") ) {
                for (var i = 0; i < selectedItems.length; i++) {
                    var seg = getRoadFromId(selectedItems[i]);
                    var geo = clone(seg.geometry);
                    geo.coordinates.splice(1,geo.coordinates.length-2); // on garde le 1er et le dernier point
                    wmeSDK.DataModel.Segments.updateSegment ({geometry: geo, segmentId:seg.id});}
            }
        }
    }

    function areTwice (myArray) {
        var myNewArray = [];
        if (myArray.length > 0) {
            for (var i = 0; i < myArray.length-1; i++) {
                for (var j = i+1; j < myArray.length; j++) {
                    if (myArray [i] == myArray[j]) {
                        myNewArray.push(myArray [i]);
                    }
                }
            }
            return delete_multi_Ids(myNewArray);
        }
        else {
            return (myArray);
        }
    }

    function Redo_Rdt_Xmoins (ev) {moveRoundAbout(-1, 0, 0, 0)};
    function Redo_Rdt_Xplus (ev)  {moveRoundAbout( 1, 0, 0, 0)};
    function Redo_Rdt_Ymoins (ev) {moveRoundAbout( 0,-1, 0, 0)};
    function Redo_Rdt_Yplus (ev)  {moveRoundAbout( 0, 1, 0, 0)};
    function Redo_Rdt_Rmoins (ev) {moveRoundAbout( 0, 0,-1, 0)};
    function Redo_Rdt_Rplus (ev)  {moveRoundAbout( 0, 0, 1, 0)};
    function Redo_Rdt_Rotmoins(ev){moveRoundAbout( 0, 0, 0, 1)};
    function Redo_Rdt_Rotplus (ev){moveRoundAbout( 0, 0, 0,-1)};

    function moveRoundAbout(xdecal,ydecal,rstr,rot) { // ******* PASS TO SDK ********
        var rdt = getOldRdt();
        if (rdt) {
            var multiaction = new WazeActionMultiAction();
            var decal_X = xdecal * 0.05 * rdt.width;
            var decal_Y = ydecal * 0.05 * rdt.height;
            for (var i = 0; i < rdt.nodes.length; i++) { // on decale les noeuds
                var node = getNodeFromId(rdt.nodes[i]);
                var geo = clone(node.geometry);
                var decal_R = decalageRadial(geo.coordinates[0],geo.coordinates[1],rdt,rstr,rot); // décalage radial (etire - repli
                geo.coordinates[0]= decal_R.x + decal_X;
                geo.coordinates[1]= decal_R.y + decal_Y;

                multiaction.doSubAction (W.model,oldMoveNode(rdt.nodes[i],geo));  // conservation du mode multiaction
                //   wmeSDK.DataModel.Nodes.moveNode({geometry: geo, id: node.id }); // ready for SDK (with many unique actions )
            }
            for (var j = 0; j < rdt.segs.length; j++) { // on decale les segments
                var seg = getRoadFromId(rdt.segs[j]);
                var geo1 = clone(seg.geometry);
                for (var k = 1; k < geo1.coordinates.length-1; k++){
                    var decal_R1 = decalageRadial(geo1.coordinates[k][0],geo1.coordinates[k][1],rdt,rstr,rot); // décalage radial (etire - repli
                    geo1.coordinates[k][0]= decal_R1.x + decal_X;
                    geo1.coordinates[k][1]= decal_R1.y + decal_Y;
                }

                 multiaction.doSubAction (W.model,oldMoveSeg(rdt.segs[j],geo1));
               // wmeSDK.DataModel.Segments.updateSegment({geometry: geo1, segmentId:rdt.segs[j]});
            }
            W.model.actionManager.add (multiaction);
        }
    }

    function oldMoveNode(nodeID,newGeo) {
        var node = W.model.nodes.objects[nodeID];
        var connectedSegs = [];
        for(let m=0;m<node.attributes.segIDs.length;m++){
            var segID = node.attributes.segIDs[m];
            connectedSegs[segID] = wmeSDK.DataModel.Segments.getById({segmentId: segID}).geometry;
        }
        return new WazeActionMoveNode (node, node.getGeometry(), newGeo, connectedSegs,{});
    }

    function oldMoveSeg (segID,newGeo){
        var seg = W.model.segments.objects[segID];
        return new WazeActionUpdateSegmentGeometry (seg,seg.getGeometry(),newGeo)
    }

    function decalageRadial (x,y,rdt,rstr,rot) { // avec passage en projection linéaire pour non deformation
        var center = openLayerTransform (rdt.center.x,rdt.center.y,"EPSG:4326","EPSG:900913");
        var point = openLayerTransform (x,y,"EPSG:4326","EPSG:900913");
        var deltaX = point.lon - center.lon;
        var deltaY = point.lat - center.lat;
        var angle = angleDeg (deltaX,deltaY) + 5 * rot; // rotation de +-5 °
        var oldRayon = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
        var newRayon = oldRayon + oldRayon * 0.05 * rstr; // etirement ou contraction
        var newPt = {};
        newPt.lon = center.lon + newRayon * Math.cos(convertDegRad(angle));
        newPt.lat = center.lat + newRayon * Math.sin(convertDegRad(angle));
        var point2 = openLayerTransform (newPt.lon,newPt.lat,"EPSG:900913","EPSG:4326");
        var pt = {}; pt.x = point2.lon; pt.y = point2.lat;
        return pt;
    }

    function openLayerTransform (x,y,from,to) {
        var point = new OpenLayers.LonLat(x,y);
        var projFrom = new OpenLayers.Projection(from);
        var projTo = new OpenLayers.Projection(to);
        return point.transform(projFrom, projTo);
    }

    function getOldRdt() { // ******* PASS TO SDK ********
        var selectedItems = getSelectedSegmentsIds();
        if (selectedItems.length==0) {return null;}
        var junctionID = [];
        for (var i = 0; i < selectedItems.length; i++) {
            var road = getRoadFromId(selectedItems[i]);
            if (road.junctionId !=null) {
                junctionID.push(road.junctionId);
            }
        }
        junctionID = delete_multi_Ids(junctionID);
        if (junctionID.length!=1) {return null;} // il ya un rdt et un seul de selectionné
        var rdt ={}
        rdt.junctionID = junctionID[0];
        rdt.nodes = [];
        rdt.center = {};
        rdt.maxX = rdt.maxY = -10000000000000;
        rdt.minX = rdt.minY = 10000000000000;
        var junc = getJunctionFromId(rdt.junctionID);
        rdt.segs = junc.segmentIds;
        for (var j = 0; j < rdt.segs.length; j++) {
            var seg = getRoadFromId(rdt.segs[j]);
            rdt.nodes.push(seg.fromNodeId);
            rdt.nodes.push(seg.toNodeId);
            var geo = seg.geometry.coordinates;
            for (var k = 0; k < geo.length; k++) { // recup geométrie
                if (geo[k][0] > rdt.maxX) {rdt.maxX = geo[k][0];}
                if (geo[k][1] > rdt.maxY) {rdt.maxY = geo[k][1];}
                if (geo[k][0] < rdt.minX) {rdt.minX = geo[k][0];}
                if (geo[k][1] < rdt.minY) {rdt.minY = geo[k][1];}
            }
        }
        rdt.nodes = delete_multi_Ids(rdt.nodes);
        rdt.width = rdt.maxX - rdt.minX;
        rdt.height= rdt.maxY - rdt.minY;
        rdt.center.x = (rdt.maxX + rdt.minX)/2;
        rdt.center.y = (rdt.maxY + rdt.minY)/2;
        return rdt;
    }

    function Roads_to_Interchange(ev) {
        var foundSelectedSegment = false;
        var selectedItems = getSelectedSegmentsIds();
        var selectedGood = (selectedItems.length>0);
        var roadIds = [];
        for (var i = 0; i<selectedItems.length;i++) { 					// Test if selection are segment
            var sel1 = getRoadFromId(selectedItems[i]);
            if ((selectedGood) && (sel1.junctionId!=null)) {	// if it is a roundabout we add all Rdt segs
                var junc = getJunctionFromId(sel1.junctionId);
                roadIds.push.apply (roadIds,junc.segmentIds);	// we add all segment of the roundabout
            }
            if (selectedGood) {roadIds.push (sel1.id);}			// stocke les segments
        }

        if ((selectedGood) && (roadIds.length != 0)) {
            roadIds = delete_multi_Ids (roadIds);						// delete double roads
            var totalPoints = [];
            var name;
            var leftEnv = [];
            var rightEnv = [];
            var typeLandmak;
            leftEnv.push ({x: 100000000000000,y:2000000000});
            var yMax = -100000000000;

            for (var k = 0; k<roadIds.length;k++) {
                var sel = getRoadFromId(roadIds[k]);
                if (name == null) {name = getStreetName(sel.id);}
                if (typeLandmak == null) {
                    switch (sel.roadType) {
                        case 1: //"Streets"
                        case 2: //"Primary Street"
                        case 3: //"Freeways"
                        case 4: //"Ramps"
                        case 6: //"Major Highway"
                        case 7: //"Minor Highway"
                            typeLandmak = "JUNCTION_INTERCHANGE"; break;// Jonction/interchange
                        case 8: //"Dirt roads"
                        case 18: //"Railroad"
                        case 19: //"Runway/Taxiway"
                        case 20: //"Parking Lot Road"
                            typeLandmak = "PARKING_LOT"; break;						// ParkingLot
                        case 5: //"Walking Trails"
                        case 10: //"Pedestrian Bw"
                        case 16: //"Stairway"
                        case 17: //"Private Road"
                        case 21: //"Service Road"
                            typeLandmak = "PARK"; break;												// Park
                    }
                }
                var newGeo = transformGpsToLinear(sel);
                for (var j = 0; j < newGeo.length;j++) {
                    totalPoints.push (clone(newGeo[j]));
                    if (leftEnv[0].y > newGeo[j].y) {									// Stocke le Y mini
                        leftEnv[0] = clone(newGeo[j]);
                        rightEnv[0] = clone(newGeo[j]);;
                    }
                    if (newGeo[j].y > yMax) { yMax = newGeo[j].y;}
                }
            }

            while ( rightEnv[rightEnv.length-1].y <yMax) {												// traitement de la voie droite
                var anglemin = 190;
                for (var i = 0; i<totalPoints.length;i++) {
                    if (totalPoints[i].y > rightEnv[rightEnv.length-1].y) {
                        var deltaX = totalPoints[i].x - rightEnv[rightEnv.length-1].x;
                        if (deltaX !=0) {
                            var deltaY = totalPoints[i].y - rightEnv[rightEnv.length-1].y;
                            var angle = angleDeg( deltaX , deltaY);
                            if (angle < anglemin) {
                                anglemin = angle;
                                var iMin = i;
                            }
                        }
                    }
                }
                rightEnv.push (totalPoints[iMin]);
            }

            while ( leftEnv[leftEnv.length-1].y <yMax) {													// traitement de la voie droite
                var anglemax = 0;
                for (var i = 0; i<totalPoints.length;i++) {
                    if (totalPoints[i].y > leftEnv[leftEnv.length-1].y) {
                        var deltaX = totalPoints[i].x - leftEnv[leftEnv.length-1].x;
                        if (deltaX !=0) {
                            var deltaY = totalPoints[i].y - leftEnv[leftEnv.length-1].y;
                            var angle = angleDeg( deltaX , deltaY);
                            if (angle > anglemax) {
                                anglemax = angle;
                                var iMax = i;
                            }
                        }
                    }
                }
                leftEnv.push (totalPoints[iMax]);
            }

            leftEnv.shift(); leftEnv.pop();											//On ote le premier et le dernier point( communs avec droite)
            rightEnv.push.apply (rightEnv,leftEnv.reverse ());						//on ajoute la partie Gauche
            var dummy = doLandmark (rightEnv,name,typeLandmak);						// make the landmark
            alert("Successfully created Landmark");}
        else {
            alert("Incorrect Selection : \n\nOne segment must be selected \nOr It is not RoundAbout Segment");
        }
    }

    function transformGpsToLinear (sel) {
        var geo = [];
        var coord = sel.geometry.coordinates;
        for (var i = 0; i<coord.length;i++) {
            var point = openLayerTransform (coord[i][0],coord[i][1],"EPSG:4326","EPSG:900913");
            geo[i] = {}; geo[i].x = point.lon; geo[i].y = point.lat;
        }
        return geo;
    }

    function doLandmark (geometry,nameLandmak,typeLandmark) {
        var newGeo = transformLinearToGps (geometry); // on repasse en coordonnées GPS
        newGeo.coordinates[0].push(newGeo.coordinates[0][0]); // on ferme le polygone en reportant le point 0
        newGeo.type= 'Polygon';
        var newLdmkId = ''+ wmeSDK.DataModel.Venues.addVenue ({category: typeLandmark, geometry: newGeo});
        if (nameLandmak!="" && nameLandmak!= undefined) {wmeSDK.DataModel.Venues.updateVenue ({name: nameLandmak, venueId: newLdmkId});} //nommage du ldmk
        activateLayer ('layer-switcher-group_places',true);
        switch (typeLandmark) { // active les layers correspondants
            case 'JUNCTION_INTERCHANGE': activateLayer ('layer-switcher-item_venues', true);break;
            case 'PARKING_LOT': activateLayer ('layer-switcher-item_parking_places', true); break;
            case 'PARK': activateLayer ('layer-switcher-item_venues', true);break
            case 'RIVER_STREAM': activateLayer ('layer-switcher-item_natural_features', true);break;
        }
        return true;
    }

    function activateLayer (layerName, flag) {
        var layer = document.getElementById (layerName);
        var layerState = layer.checked;
        if (layerState == false && flag == true) {layer.click();};
        if (layerState == true && flag == false) {layer.click();};
    }
    function transformLinearToGps (geo) {
        var newGeo = {};
        newGeo.coordinates = [];
        newGeo.coordinates[0] = [];
        for (var i = 0; i < geo.length;i++) {
            var point = openLayerTransform (geo[i].x,geo[i].y,"EPSG:900913","EPSG:4326");
            newGeo.coordinates[0][i] = [];
            newGeo.coordinates[0][i][0] = point.lon;
            newGeo.coordinates[0][i][1] = point.lat;
        }
        return newGeo
    }

    function Street_River (ev) {
        var selectedItems = getSelectedSegmentsIds();
        var selectedGood = (selectedItems.length==1);
        var sel = getRoadFromId(selectedItems[0]);
        selectedGood = selectedGood && (sel.roadType != "18");
        if (selectedGood) {
            var offset = getDisplacement();							// valeur en mètres
            if (offset == null) {
                return;}
            var name = getStreetName(sel.id);
            var points = StreetToLandmark (sel, offset);
            var dummy = doLandmark (points,name,"RIVER_STREAM");	// river
            alert("Successfully created a River Landmark");}
        else {
            alert("Incorrect Selection : \n\nOne segment must be selected \nOr It is not Street Segment");
        }
    }

    function getDisplacement() {
        var scale = 1;				// Scale mètres => coordonnées waze
        var width = prompt("Enter new Width or leave it to old value ",defaultWidth);
        if (width == null) {
            return null; }
        else {
            if (width.match("m","g")) {
                width =parseInt(width);
                if (width < 1) {width = 1;} //minimum width equal to 1m
                if (width >100) {width = 100;} //maximum width equal to 100m
                defaultWidth=width+" m";
                return width * scale / 2;
            }
            if (width.match("ft","g")) {
                width =parseInt(width);
                if (width < 3) {width =3;}					//minimum width equal to 3 ft
                if (width > 300) {width =300;}			//maximum width equal to 300 ft
                defaultWidth=width+" ft";
                return width * 0.3048 * scale /2;
            }
            width=15;
            defaultWidth="15 m";
            return width * scale / 2;
        }
    }

    function StreetToLandmark (seg,offset) {
        var newGeo = transformGpsToLinear(seg);
        var decal = decalage (newGeo, offset);
        if (document.getElementById ('WME_SSTR_SmthRvr').checked == 1) {
            decal.dir = optGeometry (decal.dir);
            decal.sym = optGeometry (decal.sym);
            decal.dir = b_spline (decal.dir);												// creation des B - splines X & Y
            decal.sym = b_spline (decal.sym);
            decal.dir = sup_unneed (decal.dir);											// delete aligned points
            decal.sym = sup_unneed (decal.sym);											// delete aligned points
        }
        decal.dir.push.apply(decal.dir,decal.sym.reverse());		// on rajoute le trajet retour
        return decal.dir;
    }

    function sup_unneed (decal) {
        for (var phase = 0; phase < 3; phase ++) {
            var decal1 = [];
            decal1 [0] = decal [0];
            for (var i = 1; i< decal.length-2; i++) {
                if ((decal1[decal1.length-1].x != decal[i+1].x) && (decal[i+1].x != decal[i+2].x)) {															// non vertical => can calculate Atan
                    var angle1 = ((decal1[decal1.length-1].y - decal[i+1].y) / (decal1[decal1.length-1].x - decal[i+1].x));
                    var angle2 = ((decal[i+1].y - decal[i+2].y) / (decal[i+1].x - decal[i+2].x));
                    var length1 = longueur (decal1[decal1.length-1].x,decal1[decal1.length-1].y,decal[i+1].x,decal[i+1].y);
                    if (testUnneed (angle1,angle2,length1,phase)) {
                        decal1.push (decal[i+1]);
                    }
                }
                else {
                    decal1.push (decal[i+1]);
                }
            }
            decal1.push (decal[decal.length-1]);
            decal = decal1;
        }
        return decal1;
    }

    function testUnneed (angle1,angle2,longueur,phase) {
        var deltaAngle = Math.abs (AtanDeg (angle1) - AtanDeg (angle2));
        switch (phase) {
            case 0: if ((deltaAngle < 45) && (longueur < 10))	{return false;}; break;
            case 1: if ((deltaAngle < 1 ) && (longueur >= 10) && (longueur < 250)) {return false;}; break;
            case 2: if ((deltaAngle < 2 ) && (longueur >= 10) && (longueur < 50 )) {return false;}; break;
        }
        return true;
    }

    function optGeometry ( line) {
        var opt = [];
        opt[0] = clone(line[0]);
        for (var i = 1; i< line.length; i++) {
            var deltaX = line[i].x-line[i-1].x;
            var deltaY = line[i].y-line[i-1].y;
            opt.push ({x: line[i-1].x + deltaX * 0.33, y: line[i-1].y + deltaY * 0.33}); // add 2 extra control points
            opt.push ({x: line[i-1].x + deltaX * 0.66, y: line[i-1].y + deltaY * 0.66});
            opt.push ({x: line[i].x, y: line[i].y});
        }
        return opt;
    }

    function decalage (geom,offset) {
        var decal = {};
        decal.dir = [];															// décalage d'un coté
        decal.sym = [];															// décalage de l'autre
        decal.dir[0] = clone(geom[0]);
        decal.sym[0] = clone(geom[0]);
        if (Math.abs(geom[1].x - geom[0].x) < 0.1) {geom[1].x = geom[0].x+0.1;}	// traitement de la verticalité
        var deltaX = geom[1].x - geom[0].x;
        var deltaY = geom[1].y - geom[0].y;
        var angle = Math.atan (deltaY/deltaX);
        decal.dir[0].x = geom[0].x - sign (deltaX) * offset * Math.sin (angle);
        decal.dir[0].y = geom[0].y + sign (deltaX) * offset * Math.cos (angle);
        decal.sym[0].x = geom[0].x + sign (deltaX) * offset * Math.sin (angle);
        decal.sym[0].y = geom[0].y - sign (deltaX) * offset * Math.cos (angle);

        var aprev = deltaY / deltaX;
        var b = geom[0].y - aprev * geom[0].x;									// y = ax+b

        var off1 = sign(deltaX) * offset / Math.cos (angle);
        var bprev = b + off1;	var bprev1 = b - off1;
        for (var i = 1; i < geom.length-1; i++) {
            if (Math.abs(geom[i+1].x - geom[i].x)< 0.1) {geom[i+1].x = geom[i].x+0.1;}	// traitement de la verticalité
            deltaX = geom[i+1].x - geom[i].x;
            deltaY = geom[i+1].y - geom[i].y;
            var anext = deltaY / deltaX;
            b = geom[i].y - anext * geom[i].x;
            angle = Math.atan (deltaY/deltaX);
            off1 = sign(deltaX) * offset / Math.cos (angle);
            var bnext = b + off1;	var bnext1 = b - off1;

            var x1 = -(bprev - bnext) / (aprev - anext);
            var x2 = -(bprev1 - bnext1) / (aprev - anext);
            decal.dir.push ({x: x1, y: (aprev * x1 + bprev)}); // décalage d'un coté
            decal.sym.push ({x: x2, y: (aprev * x2 + bprev1)}); // décalage de l'autre coté

            aprev = anext;
            bprev = bnext;	bprev1 = bnext1;
        }
        // derniers point
        decal.dir.push ({x: (geom[i].x - sign(deltaX) * offset * Math.sin (angle)),y: (geom[i].y + sign(deltaX) * offset * Math.cos (angle))});
        decal.sym.push ({x: (geom[i].x + sign(deltaX) * offset * Math.sin (angle)),y: (geom[i].y - sign(deltaX) * offset * Math.cos (angle))});
        return decal;
    }

    function b_spline (ligne) {
        var ligne1 = [];
        ligne1 [0] = ligne [0];
        for (var j = 1; j < ligne.length-2;j++) {
            var t = 4; 									// nombre de sous-segments
            for (var i = 0; i < 1;i+=1/t) {
                var x1 = ((1-i)*(1-i)*(1-i)*ligne[j-1].x + (3*i*i*i -6*i*i +4)*ligne[j].x + (-3*i*i*i +3*i*i +3*i +1)*ligne[j+1].x + i*i*i*ligne[j+2].x)/6;
                var y1 = ((1-i)*(1-i)*(1-i)*ligne[j-1].y + (3*i*i*i -6*i*i +4)*ligne[j].y + (-3*i*i*i +3*i*i +3*i +1)*ligne[j+1].y + i*i*i*ligne[j+2].y)/6;
                ligne1.push ({x: (x1), y: (y1)});
            }
        }
        ligne1.push(ligne[ligne.length-1] );
        return ligne1;
    }

    function select_same_type_roads(ev) {
        var selectedItems = getSelectedSegmentsIds(); //  Récupère les Ids des segments sélectionnés
        var nbRoad = selectedItems.length;
        var selectedGood = true;				// selection must have 1 or 2 items
        var select_IDs =[];                     					//tableau de stockage des Routes electionnées
        for (var j = 0; j < nbRoad; j++) {
            var sel = getRoadFromId(selectedItems[j]);
            if (sel.junctionId!=null) {					            // It's un roundabout
                var junc = getJunctionFromId(sel.junctionId);       // On récupère la junction
                select_IDs.push.apply(select_IDs,junc.segmentIds);} // Add to pervious selected Ids
            else {
                var nodeFrom = getNodeFromId(sel.fromNodeId);
                var segList = searchRoad(nodeFrom,sel,"0");
                select_IDs.push.apply(select_IDs,segList.IDs);		// Add to pervious selected Ids
                var nodeTo = getNodeFromId(sel.toNodeId); // recherche à partir du deuxième noeud
                var segList1 = searchRoad(nodeTo,sel,"0");
                select_IDs.push.apply(select_IDs,segList1.IDs);		// Add to pervious selected Ids
            }
            select (select_IDs);
        }
        if (!selectedGood) {alert("You must select road(s)");}
    }

    function Side_A(ev) {
        var selectedItems = getSelectedSegmentsIds(); // Récupère les Ids des segments sélectionnés
        if ((selectedItems.length == 1) ) {
            var sel = getRoadFromId(selectedItems[0]);
            var nodeFrom = getNodeFromId(sel.fromNodeId); // Recherche à partir du noeud A
            var segList = searchRoad(nodeFrom,sel,"0");
            select (segList.IDs);
        }
        else {alert("One segment (and only one)\nmust be selected");}
    }

    function pseudoNode (nodeID) { // debug function which add old attributes
        var node = wmeSDK.DataModel.Nodes.getById({nodeId: nodeID});
        node.attributes = {};
        node.attributes.segIDs = node.connectedSegmentIds;
        node.getID = function() {return node.id;};
        var point2 = openLayerTransform (node.geometry.coordinates[0],node.geometry.coordinates[1],"EPSG:4326","EPSG:900913");
        node.attributes.geometry = {};
        node.geometry.x = point2.lon;
        node.geometry.y = point2.lat;
        node.getGeometry = function() {return (node.geometry)};
        // node.attributes.geometry.x = node.geometry.coordinates[0];
        // node.attributes.geometry.y = node.geometry.coordinates[1];
        // node.geometry.x = node.geometry.coordinates[0];
        // node.geometry.y = node.geometry.coordinates[1];
        return node;
    }

    function pseudoRoad (roadId) { // debug function which add old attributes
        var sel = wmeSDK.DataModel.Segments.getById({segmentId: roadId});
        sel.attributes = {};
        sel.attributes.roadType = sel.roadType;
        sel.attributes.fromNodeID = sel.fromNodeId;
        sel.attributes.toNodeID = sel.toNodeId;
        sel.getID = function() {return sel.id;};
        sel.attributes.junctionID = sel.junctionId;
        sel.type = "segment";
        sel.pseudoGeo = {};
        sel.pseudoGeo.components = [];
        for (var i = 0; i < sel.geometry.coordinates.length;i++) {
            var point = openLayerTransform (sel.geometry.coordinates[i][0],sel.geometry.coordinates[i][1],"EPSG:4326","EPSG:900913");
            sel.pseudoGeo.components[i] = {};
            sel.pseudoGeo.components[i] = point.lon;
            sel.pseudoGeo.components[i] = point.lat;
        }
        return sel;
    }

    function Side_B(ev) {
        var selectedItems = getSelectedSegmentsIds(); //  Récupère les Ids des segments sélectionnés
        if ((selectedItems.length == 1) ) {
            var sel = getRoadFromId(selectedItems[0]);
            var nodeTo = getNodeFromId(sel.toNodeId); // recherche à partir du noeud B
            var segList = searchRoad(nodeTo,sel,"0");
            select (segList.IDs);}
        else {alert ("One segment (and only one)\nmust be selected");}
    }

    function select_AB(ev) {
        var selectedItems = getSelectedSegmentsIds(); //  Récupère les Ids des segments sélectionnés
        var nbRoad = selectedItems.length;															// **** Validate selection *****
        var selectedGood = (nbRoad == 2);															// selection must have 2 items
        if (selectedGood) {
            var sel = getRoadFromId(selectedItems[0]);
            var sel1= getRoadFromId(selectedItems[1]);
            selectedGood = ((selectedGood) && (sel.roadType == sel1.roadType));	// Test if selection have same road Type
        }
        if (selectedGood) {
            var lengthMin = 1000000;
            var goodTrip = [];
            var select1 = select_12(sel,sel1);
            if (select1[select1.length-1] == sel1.id) { // on a trouvé un chemin dans ce sens
                goodTrip = select1;
                lengthMin = lengthTrip (select1);
            }
            var select2 = select_12(sel1,sel);

            if ((select2[select2.length-1] == sel.id) && (lengthTrip (select2) < lengthMin)){	// on a trouvé un chemin dans ce sens
                goodTrip = select2;
                lengthMin = lengthTrip (select2);
            }
            var nodeTrip1 = nodeFromTrip (select1);	// ******* search for Common Nodes
            var nodeTrip2 = nodeFromTrip (select2);
            var CommonNode = [];
            for (var m = 0; m < nodeTrip1.length; m++) {
                if (isInArray (nodeTrip1[m],nodeTrip2)) {
                    CommonNode.push (nodeTrip1[m]);
                }
            }

            if (CommonNode.length !=0) {
                for (var i = 0; i < CommonNode.length; i++) {
                    var select3 = [];
                    var road = getRoadFromId(select1[0]);
                    for (var j = 0; ((road.fromNodeId != CommonNode[i]) && (road.toNodeId != CommonNode[i])); j++) {
                        select3.push (road.id);
                        road = getRoadFromId(select1[j]);
                    }
                    select3.push (road.id);
                    road = getRoadFromId(select2[0]);
                    for (var k = 0; ((road.fromNodeId != CommonNode[i]) && (road.toNodeId != CommonNode[i])); k++) {
                        select3.push (road.id);
                        road = getRoadFromId(select2[k]);
                    }
                    select3.push (road.id);
                    select3 = delete_multi_Ids (select3);
                    if (lengthTrip (select3) <lengthMin) {
                        goodTrip = select3;
                        lengthMin = lengthTrip (goodTrip);
                    }
                }
            }

            if (lengthMin != 1000000) {								// a path was found
                goodTrip = addRoundabout (goodTrip);				// Add roundabout segments
                goodTrip = addAlternativePaths (goodTrip);			// add alternative simple way like fork in roundabaout
                select (goodTrip);}		// make the selection
            else {
                alert("No Path found");
            }
        }
        else { alert("You must select 2 roads \nwith the same type");
             }
    }

    function addAlternativePaths (trip) { // ******* PASS TO SDK ********
        var alternativeSegs = [];
        var listNodeIDs = nodeFromTrip (trip);																			// list of nodesIds of the trip
        var road = getRoadFromId(trip[0]);
        var roadtype = road.roadType;
        for (var i = 0; i < listNodeIDs.length;i++) {
            var node = getNodeFromId(listNodeIDs[i]);
            var nodeSegIdList = node.connectedSegmentIds;
            for (var j = 0; j < nodeSegIdList.length;j++) {
                var road1 = getRoadFromId(nodeSegIdList[j]);
                if ((road1 != null) && (road1.roadType == roadtype) && (isInArray (road1.fromNodeId,listNodeIDs)) && (isInArray (road1.toNodeId,listNodeIDs))) {
                    alternativeSegs.push (road1.id);
                }
            }
        }
        if (alternativeSegs.length != 0 ) {
            trip.push.apply(trip,alternativeSegs);
            trip = delete_multi_Ids (trip);
        }
        return trip;
    }

    function addRoundabout (trip) { // ******* PASS TO SDK ********
        var roundaboutSegs = [];
        for (var i = 0; i < trip.length;i++) {
            var road = getRoadFromId(trip[i]);
            if (road.junctionId!=null) {// It's un roundabout
                var junc = getJunctionFromId(road.junctionId);
                roundaboutSegs.push.apply(roundaboutSegs,junc.segmentIds);	// on ajoute les segments du RP
            }
        }
        if (roundaboutSegs.length != 0 ) {
            trip.push.apply(trip,roundaboutSegs);
            trip = delete_multi_Ids (trip);
        }
        return trip;
    }


    function nodeFromTrip (trip) {
        var node =[];
        for (var i = 0; i < trip.length; i++) {
            var road = getRoadFromId(trip[i]);
            node.push (road.fromNodeId);
            node.push (road.toNodeId);
        }
        node = delete_multi_Ids (node);
        return node;
    }

    function lengthTrip (listRoadID) {
        var length= 0;
        for (var i = 0; i < listRoadID.length;i++) {
            var road = getRoadFromId(listRoadID[i]);
            length = length + road.length;
        }
        return length;
    }

    function select_12(startRoad,endRoad) {
        var select_IDs =[];													//tableau de stockage des Routes electionnées
        var endRoadFrom;
        var endRoadTo;
        if (endRoad.fromNodeId != null) {endRoadFrom = getNodeFromId(endRoad.fromNodeId);}	// Validate node for End Road
        else {endRoadFrom = getNodeFromId(endRoad.toNodeId);}
        if (endRoad.toNodeId != null) {endRoadTo = getNodeFromId(endRoad.toNodeId);}
        else {endRoadTo = getNodeFromId (endRoad.fromNodeId);}
        var node = choiceStartNode (startRoad,endRoadFrom,endRoadTo);		        // Choix du node de depart
        var segList = searchRoad(node,startRoad,endRoad.id)
        select_IDs.push.apply(select_IDs,segList.IDs);
        while ((segList.stop == "multiRoads") && (segList.roads.length >"1") && (select_IDs.length < 50)) {	// Manage jonctions with same type road
            var BestNextNode = searchBestNextNode (segList.node, segList.roads, endRoad);
            if ( BestNextNode.id != segList.node.id ) {					// search road with best node
                for (var i = 0; i < segList.roads.length;i++) {
                    var road = getRoadFromId (segList.roads[i]);
                    if ((BestNextNode.id == road.fromNodeId) || (BestNextNode.id == road.toNodeId)) {
                        var bestRoad = road;
                    }
                }
                var segList = searchRoad (BestNextNode, bestRoad, endRoad.id);
                select_IDs.push.apply(select_IDs, segList.IDs);}
            else {
                segList.stop = "none";
            }
        }
        return (select_IDs);
    }

    function searchBestNextNode (startNode,listRoadID,endRoad) {
        var endNode,endNode1,endNode2 ;
        endNode1 = getNodeFromId(endRoad.fromNodeId);
        endNode2 = getNodeFromId(endRoad.toNodeId);
        if (distance1(startNode,endNode2) > distance1(startNode,endNode1)) {endNode = endNode1;} // determine de noeud de référence de fin
        else {endNode = endNode2;}
        var angleEnd = angle_1(startNode, endNode);
        var angleMin = 360;
        var bestNode;

        for (var i = 0; i < listRoadID.length;i++) {
            var road = getRoadFromId( listRoadID[i]);
            if (road.fromNodeId == startNode.id) {												// determine de noeud à tester pour la fin du segment
                var node = getNodeFromId(road.toNodeId);}
            else {var node = getNodeFromId(road.fromNodeId);}
            var angle1 = Math.abs(angle_1 (startNode,node) - angleEnd);
            if (angle1 > 180 ) { angle1= 360 - angle1;}															// angle complémentaire
            if ( angle1 < angleMin ) {
                angleMin = angle1;
                bestNode = node;
            }
        }
        return bestNode;
    }
    //        **** Math functions *****
    function sign (x) {return (x < 0) ? (-1) : (1);}
    function AtanDeg ( x) {return ( 180 * Math.atan (x) / Math.PI );}
    function convertDegRad (angledeg) {return (Math.PI * (angledeg) / 180 );}

    function angle (node1,node2) {
        //var deltaX = (node2.geometry.x - node1.geometry.x);
        //var deltaY = (node2.geometry.y - node1.geometry.y);
        //return angleDeg (deltaX,deltaY);
        return angleDeg ((node2.geometry.x - node1.geometry.x),(node2.geometry.y - node1.geometry.y));
    }

    function angle_1 (node1,node2) { // *** SDK ***
        var point1 = openLayerTransform (node1.geometry.coordinates[0],node1.geometry.coordinates[1],"EPSG:4326","EPSG:900913");
        var point2 = openLayerTransform (node2.geometry.coordinates[0],node2.geometry.coordinates[1],"EPSG:4326","EPSG:900913");
        return angleDeg ((point2.lon - point1.lon),(point2.lat - point1.lat));
    }

    function angleDeg (deltaX,deltaY) {
        if (deltaX == 0) { return ( sign( deltaY ) * 90);}
        if (deltaX > 0 ) { return (AtanDeg( deltaY / deltaX));}
        else { return ((sign( deltaY )* 180) + AtanDeg( deltaY / deltaX));}
    }
    function longueur (x1,y1,x2,y2) {
        return (Math.sqrt (((x1-x2)*(x1-x2))+((y1-y2)*(y1-y2))));
    }
    //        **********************

    function select (select_IDs) {
        select_IDs = delete_multi_Ids (select_IDs);	// suppression des doublons
        wmeSDK.Editing.setSelection({selection: {ids: select_IDs, objectType: 'segment'}});

    }

    function delete_multi_Ids (myArray) {
        var myNewArray = [];
        if (myArray.length >0) {
            myNewArray[0]= myArray [0];
            for (var i = 0; i < myArray.length; i++) {
                if (notInArray (myArray [i],myNewArray)) {
                    myNewArray.push(myArray [i]);
                }
            }
        }
        return myNewArray;
    }

    function minInArray (array) {
        if (array.length > 0) {
            var minimum = array [0];
            for (var i = 1; i < array.length; i++) {
                minimum = Math.min (minimum,array [i]);
            }
            return minimum;
        }
        else {return null;}
    }

    function isInArray (item,array) {return array.indexOf(item) !== -1;}
    function notInArray (item,array) {return array.indexOf(item) === -1;}

    function searchRoad(node,roadStart,roadEndID) {
        var roadtype = roadStart.roadType;
        var roadID = roadStart.id;
        var foundSegs = {};												// object for return parameters
        foundSegs.IDs = [];
        foundSegs.roads = [];												//init array
        foundSegs.stop = "none";											//init Stop cause
        foundSegs.IDs.push(roadID);
        var nbSeg = 1;														//Number of searched segments
        while ((nbSeg < 50) && (roadID != roadEndID)) {
            var nodeSegIdList = node.connectedSegmentIds;						// list of road connected to node
            var sameTypeRoad = [];
            for (var i = 0; i < nodeSegIdList.length;i++) {
                var segID = nodeSegIdList [i];
                var seg1 = getRoadFromId(segID);
                if (seg1 == null ) return foundSegs;						// le segment n'est pas chargé en mémoire
                else {
                    if ((seg1.roadType == roadtype) && (seg1.id != roadID)) {
                        sameTypeRoad.push(segID);
                    }
                }
            }

            if (sameTypeRoad.length !=1) {
                if (isInArray (roadEndID,sameTypeRoad)) {			// End Road is in the fork
                    foundSegs.IDs.push(roadEndID);					// We add it and go away
                    return foundSegs;
                }
                sameTypeRoad = validate (sameTypeRoad);				// delete cul-de-sac
            }

            if (sameTypeRoad.length !=1) {							// not an unique segment (0,2 or more)
                foundSegs.stop = "multiRoads";
                foundSegs.roads = sameTypeRoad;
                foundSegs.node = node;
                return foundSegs;}									// on retourne le tableau d'Ids s
            else {
                var roadID = sameTypeRoad[0];
                if (isInArray (roadID,foundSegs.IDs)) return foundSegs;		// we are in a lopp : we go away
                foundSegs.IDs.push(roadID);
                nbSeg = nbSeg + 1;
                var seg2 = getRoadFromId(roadID);
                if (node.id == seg2.fromNodeId) {var nodeID = seg2.toNodeId;}
                else {var nodeID = seg2.fromNodeId;}
                var node = getNodeFromId(nodeID);
                if (node == null ) {return foundSegs;}											// It's a cul-de-sac : we go away
            }
        }
        return foundSegs;
    }

    function validate (sameTypeRoad) { // *** SDK ***
        var myNewSameTypeRoad = [];
        for (var i = 0; i < sameTypeRoad.length; i++) {
            var sel = getRoadFromId(sameTypeRoad[i]);
            if ((sel.fromNodeId !=null) && (sel.toNodeId !=null)) { //it is not a cul-de-sac
                myNewSameTypeRoad.push (sameTypeRoad[i]);
            }
        }
        return myNewSameTypeRoad;
    }

    function choiceStartNode (road1,node3,node4) {
        var node1,node2;
        if (road1.fromNodeId != null) {node1 = getNodeFromId(road1.fromNodeId);}	// test of cul-de-sac & change node if it is
        else {node1 = getNodeFromId(road1.toNodeId);}
        if (road1.toNodeId != null) { node2 = getNodeFromId(road1.toNodeId);}
        else {node2 = getNodeFromId(road1.fromNodeId);}
        var nodeStart = node1;
        var dist_min = distance1 (node1,node3);
        var dist = distance1 (node1,node4);
        if (dist < dist_min ) {dist_min=dist;}
        dist = distance1 (node2,node3);
        if (dist < dist_min ) { dist_min = dist; nodeStart = node2;}
        dist = distance1 (node2,node4);
        if (dist < dist_min ) { dist_min = dist; nodeStart = node2;}
        return nodeStart;
    }

    //    function distance (node1 , node2) {
    //        var dist = (node1.geometry.x - node2.geometry.x)*(node1.geometry.x - node2.geometry.x);
    //        dist = dist + (node1.geometry.y - node2.geometry.y)*(node1.geometry.y - node2.geometry.y);
    //       return Math.sqrt(dist);
    //    }

    function distance1 (node1 , node2) { // *** SDK ***
        var point1 = openLayerTransform (node1.geometry.coordinates[0],node1.geometry.coordinates[1],"EPSG:4326","EPSG:900913");
        var point2 = openLayerTransform (node2.geometry.coordinates[0],node2.geometry.coordinates[1],"EPSG:4326","EPSG:900913");
        var dist = (point1.lon-point2.lon)*(point1.lon-point2.lon)+(point1.lat-point2.lat)*(point1.lat-point2.lat);
        return Math.sqrt(dist);
    }

    function manage_WME_SSTR(ev) {
        put_WME_SSTR_button();
        if(document.getElementById('WME_SSTR_All') != null) {
            localStorage['WME_SSTR_enable'] = document.getElementById ('WME_SSTR_enable').checked == 1;
            var road = [];
            var selectedItems = getSelectedSegmentsIds() //  Récupère les Ids des segments sélectionnés
            for (var i = 0; i<selectedItems.length;i++) {
                var seg= getRoadFromId (selectedItems[i]);
                if (seg != null) {road.push(seg);}
            }
            effaceMenu ();
            if(document.getElementById ('WME_SSTR_enable').checked == 1) {
                if (road.length == 1) {
                    document.getElementById ('WME_SSTR_Side').style.display = "inline";
                    document.getElementById ('WME_SSTR_River').style.display = "block";}
                if (road.length >= 1) {
                    document.getElementById ('WME_SSTR').style.display = "inline";
                    document.getElementById ('WME_SSTR_Ldmk').style.display = "block";
                    if (road[0].junctionId !=null) {
                        document.getElementById ('WME_SSTR_Rdt').style.display = "block";
                    }
                    if (getUserRank() >= 2) {
                        document.getElementById ('WME_SSTR_CrgAds').style.display = "block";}
                }
                if (road.length == 2) {
                    document.getElementById ('WME_SSTR_12').style.display = "inline";
                }
            }
        }
        return;
    }

    function effaceMenu () {
        document.getElementById ('WME_SSTR').style.display = "none";
        document.getElementById ('WME_SSTR_Side').style.display = "none";
        document.getElementById ('WME_SSTR_12').style.display = "none";
        document.getElementById ('WME_SSTR_River').style.display = "none";
        document.getElementById ('WME_SSTR_Ldmk').style.display = "none";
        document.getElementById ('WME_SSTR_Rdt').style.display = "none";
        document.getElementById ('WME_SSTR_CrgAds').style.display = "none";
    }

    function manageSmoothRiver () {
        localStorage['WME_SSTR_Smth'] = document.getElementById ('WME_SSTR_SmthRvr').checked == 1;
        return;
    }

    function init_WME_SSTR() {
        if (localStorage['WME_SSTR_enable']=='true') {document.getElementById ('WME_SSTR_enable').checked = 1;}	// restaure old Values (if exist)
        if (localStorage['WME_SSTR_Smth']=='true') {document.getElementById ('WME_SSTR_SmthRvr').checked = 1;}
        wmeSDK.Events.on({eventHandler: manage_WME_SSTR1 ,eventName : "wme-selection-changed"});
        effaceMenu();
        manage_WME_SSTR();
        console_log("Select Same Type Roads initialized");
    }

    function manage_WME_SSTR1 () {
        setTimeout(manage_WME_SSTR, 1001);
    }

    function endAlert() {
        var myMessage = document.getElementById ('WME_JCB_AlertTxt').innerHTML;
        var line = myMessage.split("<br>");
        line.shift();
        document.getElementById ('WME_JCB_AlertTxt').innerHTML = line.join ("<br>");
        if (line.length ==0){
            document.getElementById ('WME_JCB_AlertBox').style.display = "none";
        }
    }

    function getSelectedSegmentsIds() { //  Récupère les Ids des segments sélectionnés
        var selectedItems = wmeSDK.Editing.getSelection();
        var segmentIds = [];
        if (selectedItems && selectedItems.objectType == "segment" && selectedItems.localizedTypeName == "Segment") {segmentIds = selectedItems.ids}
        return segmentIds ;
    }

    function getStreetName(rId) {return wmeSDK.DataModel.Segments.getAddress({segmentId: rId}).street.name;}
    function getRoadFromId(rId) {return wmeSDK.DataModel.Segments.getById({segmentId: rId});}
    function getNodeFromId(nId) {return wmeSDK.DataModel.Nodes.getById({nodeId: nId});}
    function getJunctionFromId(jId) {return wmeSDK.DataModel.Junctions.getById({junctionId: jId});}
    function getUserRank() {return wmeSDK.State.getUserInfo().rank;}
    function clone(objet) {return JSON.parse(JSON.stringify(objet));}
    function console_log(msg) {if (console) {console.log(msg);}}
    function afficheObjet (objet) {for (var e in objet) {alert("objet["+e+"] ="+ objet[e]+" !");}}
}

SSTR_bootstrap();