WME RA Util

Providing basic utility for RA adjustment without the need to delete & recreate

当前为 2023-07-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name WME RA Util
  3. // @namespace https://greasyfork.org/users/30701-justins83-waze
  4. // @version 2023.07.18.02
  5. // @description Providing basic utility for RA adjustment without the need to delete & recreate
  6. // @include https://www.waze.com/editor*
  7. // @include https://www.waze.com/*/editor*
  8. // @include https://beta.waze.com/*
  9. // @exclude https://www.waze.com/user/editor*
  10. // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
  11. // @author JustinS83
  12. // @grant GM_xmlhttpRequest
  13. // @license GPLv3
  14. // @contributionURL https://github.com/WazeDev/Thank-The-Authors
  15. // ==/UserScript==
  16.  
  17. /* global W */
  18. /* global WazeWrap */
  19. /* global OpenLayers */
  20. /* global require */
  21. /* global $ */
  22. /* global _ */
  23. /* global I18n */
  24. /* eslint curly: ["warn", "multi-or-nest"] */
  25.  
  26. /*
  27. non-normal RA color:#FF8000
  28. normal RA color:#4cc600
  29. */
  30. (function() {
  31.  
  32. var RAUtilWindow = null;
  33. var UpdateSegmentGeometry;
  34. var MoveNode, MultiAction;
  35. var drc_layer;
  36. let wEvents;
  37. const SCRIPT_VERSION = GM_info.script.version.toString();
  38. const SCRIPT_NAME = GM_info.script.name;
  39. const DOWNLOAD_URL = GM_info.script.fileURL;
  40.  
  41. //var totalActions = 0;
  42. var _settings;
  43. const updateMessage = "Fixing Roundabout Angles portion.";
  44.  
  45. function bootstrap(tries = 1) {
  46.  
  47. if (W && W.map && W.model && require && WazeWrap.Ready){
  48. loadScriptUpdateMonitor();
  49. init();
  50. }
  51. else if (tries < 1000)
  52. setTimeout(function () {bootstrap(++tries);}, 200);
  53. }
  54.  
  55. bootstrap();
  56.  
  57. function loadScriptUpdateMonitor() {
  58. let updateMonitor;
  59. try {
  60. updateMonitor = new WazeWrap.Alerts.ScriptUpdateMonitor(SCRIPT_NAME, SCRIPT_VERSION, DOWNLOAD_URL, GM_xmlhttpRequest);
  61. updateMonitor.start();
  62. } catch (ex) {
  63. // Report, but don't stop if ScriptUpdateMonitor fails.
  64. console.error(`${SCRIPT_NAME}:`, ex);
  65. }
  66. }
  67.  
  68. function init(){
  69. injectCss();
  70. UpdateSegmentGeometry = require('Waze/Action/UpdateSegmentGeometry');
  71. MoveNode = require("Waze/Action/MoveNode");
  72. MultiAction = require("Waze/Action/MultiAction");
  73.  
  74. console.log("RA UTIL");
  75. console.log(GM_info.script);
  76. if(W.map.events)
  77. wEvents = W.map.events;
  78. else
  79. wEvents = W.map.getMapEventsListener();
  80.  
  81. RAUtilWindow = document.createElement('div');
  82. RAUtilWindow.id = "RAUtilWindow";
  83. RAUtilWindow.style.position = 'fixed';
  84. RAUtilWindow.style.visibility = 'hidden';
  85. RAUtilWindow.style.top = '15%';
  86. RAUtilWindow.style.left = '25%';
  87. RAUtilWindow.style.width = '510px';
  88. RAUtilWindow.style.zIndex = 100;
  89. RAUtilWindow.style.backgroundColor = '#FFFFFE';
  90. RAUtilWindow.style.borderWidth = '0px';
  91. RAUtilWindow.style.borderStyle = 'solid';
  92. RAUtilWindow.style.borderRadius = '10px';
  93. RAUtilWindow.style.boxShadow = '5px 5px 10px Silver';
  94. RAUtilWindow.style.padding = '4px';
  95.  
  96. var alertsHTML = '<div id="header" style="padding: 4px; background-color:#92C3D3; border-radius: 5px;-moz-border-radius: 5px;-webkit-border-radius: 5px; color: white; font-weight: bold; text-align:center; letter-spacing: 1px;text-shadow: black 0.1em 0.1em 0.2em;"><img src="https://storage.googleapis.com/wazeopedia-files/1/1e/RA_Util.png" style="float:left"></img> Roundabout Utility <a data-toggle="collapse" href="#divWrappers" id="collapserLink" style="float:right"><span id="collapser" style="cursor:pointer;padding:2px;color:white;" class="fa fa-caret-square-o-up"></a></span></div>';
  97. // start collapse // I put it al the beginning
  98. alertsHTML += '<div id="divWrappers" class="collapse in">';
  99. //***************** Round About Angles **************************
  100. alertsHTML += '<p style="margin: 10px 0px 0px 20px;"><input type="checkbox" id="chkRARoundaboutAngles">&nbsp;Enable Roundabout Angles</p>';
  101. //***************** Shift Amount **************************
  102. // Define BOX
  103. alertsHTML += '<div id="contentShift" style="text-align:center;float:left; width: 120px;max-width: 24%;height: 170px;margin: 1em 5px 0px 0px;opacity:1;border-radius: 2px;-moz-border-radius: 2px;-webkit-border-radius: 4px;border-width:1px;border-style:solid;border-color:#92C3D3;padding:2px;}">';
  104. alertsHTML += '<b>Shift amount</b></br><input type="text" name="shiftAmount" id="shiftAmount" size="1" style="float: left; text-align: center;font: inherit; line-height: normal; width: 30px; height: 20px; margin: 5px 4px; box-sizing: border-box; display: block; padding-left: 0; border-bottom-color: rgba(black,.3); background: transparent; outline: none; color: black;" value="1"/> <div style="margin: 5px 4px;">Meter(s)';
  105. // Shift amount controls
  106. alertsHTML += '<div id="controls" style="text-align:center; padding:06px 4px;width=100px; height=100px;margin: 5px 0px;border-style:solid; border-width: 2px;border-radius: 50%;-moz-border-radius: 50%;-webkit-border-radius: 50%;box-shadow: inset 0px 0px 50px -14px rgba(0,0,0,1);-moz-box-shadow: inset 0px 0px 50px -14px rgba(0,0,0,1);-webkit-box-shadow: inset 0px 0px 50px -14px rgba(0,0,0,1); background:#92C3D3;align:center;">';
  107. //Single Shift Up Button
  108. alertsHTML += '<span id="RAShiftUpBtn" style="cursor:pointer;font-size:14px;">';
  109. alertsHTML += '<i class="fa fa-angle-double-up fa-2x" style="color: white; text-shadow: black 0.1em 0.1em 0.2em; vertical-align: top;"> </i>';
  110. alertsHTML += '<span id="UpBtnCaption" style="font-weight: bold;"></span>';
  111. alertsHTML += '</span><br>';
  112. //Single Shift Left Button
  113. alertsHTML += '<span id="RAShiftLeftBtn" style="cursor:pointer;font-size:14px;margin-left:-40px;">';
  114. alertsHTML += '<i class="fa fa-angle-double-left fa-2x" style="color: white; text-shadow: black 0.1em 0.1em 0.2em; vertical-align: middle"> </i>';
  115. alertsHTML += '<span id="LeftBtnCaption" style="font-weight: bold;"></span>';
  116. alertsHTML += '</span>';
  117. //Single Shift Right Button
  118. alertsHTML += '<span id="RAShiftRightBtn" style="float: right;cursor:pointer;font-size:14px;margin-right:5px;">';
  119. alertsHTML += '<i class="fa fa-angle-double-right fa-2x" style="color: white;text-shadow: black 0.1em 0.1em 0.2em; vertical-align: middle"> </i>';
  120. alertsHTML += '<span id="RightBtnCaption" style="font-weight: bold;"></span>';
  121. alertsHTML += '</span><br>';
  122. //Single Shift Down Button
  123. alertsHTML += '<span id="RAShiftDownBtn" style="cursor:pointer;font-size:14px;margin-top:0px;">';
  124. alertsHTML += '<i class="fa fa-angle-double-down fa-2x" style="color: white;text-shadow: black 0.1em 0.1em 0.2em; vertical-align: middle"> </i>';
  125. alertsHTML += '<span id="DownBtnCaption" style="font-weight: bold;"></span>';
  126. alertsHTML += '</span>';
  127. alertsHTML += '</div></div></div>';
  128. //***************** Rotation **************************
  129. // Define BOX
  130. alertsHTML += '<div id="contentRotate" style="float:left; text-align: center;width: 120px;max-width: 24%;max-height:145px;margin: 1em auto;opacity:1;border-radius: 2px;-moz-border-radius: 2px;-webkit-border-radius: 4px;border-width:1px;border-style:solid;border-color:#92C3D3;padding:2px; display:inline-block; border-style:solid; border-width:1px; height:152px; margin-right:5px;">';
  131. alertsHTML += '<b>Rotation amount</b></br><input type="text" name="rotationAmount" id="rotationAmount" size="1" style="float: left; text-align: center;font: inherit; line-height: normal; width: 30px; height: 20px; margin: 5px 4px; box-sizing: border-box; display: block; padding-left: 0; border-bottom-color: rgba(black,.3); background: transparent; outline: none; color: black;" value="1"/> <div style="margin: 5px 4px;">Degree(s)';
  132. // Rotation controls
  133. alertsHTML += '<div id="rotationControls" style="padding: 6px 4px;width=100px; margin: 20px 0px 50px 0px;align:center;">';
  134. // Rotate Button on the Left
  135. alertsHTML += '<span id="RARotateLeftBtn" class="btnRotate" style="float: left;">';
  136. alertsHTML += '<i class="fa fa-undo fa-2x" style="color: white; text-shadow: black 0.1em 0.1em 0.2em; padding:2px;"> </i>';
  137. alertsHTML += '<span id="RotateLeftBtnCaption" style="font-weight: bold;"></span>';
  138. alertsHTML += '</span>';
  139. // Rotate button on the Right
  140. alertsHTML += '<span id="RARotateRightBtn" class="btnRotate" style="float: right;">';
  141. alertsHTML += '<i class="fa fa-repeat fa-2x" style="color: white; text-shadow: black 0.1em 0.1em 0.2em; padding:2px;"> </i>';
  142. alertsHTML += '<span id="RotateRightBtnCaption" style="font-weight: bold;"></span>';
  143. alertsHTML += '</div></div></div>';
  144. //********************* Diameter change ******************
  145. // Define BOX
  146. alertsHTML += '<div id="diameterChange" style="float:left; text-align: center;width: 120px;max-width: 24%;max-height:145px;margin: 1em auto;opacity:1;border-radius: 2px;-moz-border-radius: 2px;-webkit-border-radius: 4px;border-width:1px;border-style:solid;border-color:#92C3D3;padding:2px; display:inline-block; border-style:solid; border-width:1px; height:152px; margin-right:5px;">';
  147. alertsHTML += '<b>Change diameter</b></br></br>';
  148. // Diameter Change controls
  149. alertsHTML += '<div id="DiameterChangeControls" style="padding: 6px 4px;width=100px; margin: 5px 7px 50px 7px;align:center;">';
  150. // Decrease Button
  151. alertsHTML += '<span id="diameterChangeDecreaseBtn" style="float: left; width=45px; height=45px; background-color:#92C3D3; cursor:pointer; padding: 5px; font-size:14px; border:thin outset black; border-style:solid; border-width: 1px;border-radius: 50%;-moz-border-radius: 50%;-webkit-border-radius: 50%;box-shadow: inset 0px 0px 20px -14px rgba(0,0,0,1);-moz-box-shadow: inset 0px 0px 20px -14px rgba(0,0,0,1);-webkit-box-shadow: inset 0px 0px 20px -14px rgba(0,0,0,1);">';
  152. alertsHTML += '<i class="fa fa-compress fa-2x" style="color: white; text-shadow: black 0.1em 0.1em 0.2em; padding:2px;;"> </i>';
  153. alertsHTML += '<span id="diameterChangeDecreaseCaption" style="font-weight: bold;"></span>';
  154. alertsHTML += '</span>';
  155. // Increase Button
  156. alertsHTML += '<span id="diameterChangeIncreaseBtn" style="float: right; width=45px; height=45px; background-color:#92C3D3; cursor:pointer; padding: 5px; font-size:14px; border:thin outset black; border-style:solid; border-width: 1px;border-radius: 50%;-moz-border-radius: 50%;-webkit-border-radius: 50%;box-shadow: inset 0px 0px 20px -14px rgba(0,0,0,1);-moz-box-shadow: inset 0px 0px 20px -14px rgba(0,0,0,1);-webkit-box-shadow: inset 0px 0px 20px -14px rgba(0,0,0,1);">';
  157. alertsHTML += '<i class="fa fa-arrows-alt fa-2x" style="color: white; text-shadow: black 0.1em 0.1em 0.2em; padding:2px;"> </i>';
  158. alertsHTML += '<span id="diameterChangeIncreaseCaption" style="font-weight: bold;"></span>';
  159. alertsHTML += '</span>';
  160. alertsHTML += '</div></div>';
  161. //***************** Bump nodes **********************
  162. // Define BOX
  163. alertsHTML += '<div id="bumpNodes" style="float:left; text-align: center;width: 120px;max-width: 24%;max-height:145px;margin: 1em auto 0px auto;opacity:1;border-radius: 2px;-moz-border-radius: 2px;-webkit-border-radius: 4px;border-width:1px;border-style:solid;border-color:#92C3D3;padding:2px; display:inline-block; border-style:solid; border-width:1px; height:152px; margin-right:5px;">';
  164. alertsHTML += '<b>Move nodes</b></br>';
  165. // Move Nodes controls
  166. alertsHTML += '<div id="MoveNodesControls" style="padding: 2px;">';
  167. // Button A
  168. alertsHTML += '<div style="text-align:center; font-size:18px;">A Node';
  169. // Move node IN
  170. alertsHTML += '<p><span id="btnMoveANodeIn" class="btnMoveNode" style="color: white; font-size: 0.875em; text-shadow: black 0.1em 0.1em 0.2em; padding:3px 15px 3px 15px; margin:3px;">in</span>';
  171. // Move node OUT
  172. alertsHTML += '<span id="btnMoveANodeOut" class="btnMoveNode" class="btnMoveNode" style="color: white; font-size: 0.875em; text-shadow: black 0.1em 0.1em 0.2em; padding:3px 10px 3px 10px; margin:3px;">out</span>';
  173. alertsHTML += '</div>';
  174. // Button B
  175. alertsHTML += '<div style="text-align:center; font-size:18px;">B Node';
  176. // Move node IN
  177. alertsHTML += '<p><span id="btnMoveBNodeIn" class="btnMoveNode" style="color: white; font-size: 0.875em; text-shadow: black 0.1em 0.1em 0.2em; padding:3px 15px 3px 15px; margin:3px;">in</span>';
  178. // Move node OUT
  179. alertsHTML += '<span id="btnMoveBNodeOut" class="btnMoveNode" class="btnMoveNode" style="color: white; font-size: 0.875em; text-shadow: black 0.1em 0.1em 0.2em; padding:3px 10px 3px 10px; margin:3px;">out</span>';
  180. alertsHTML += '</div>';
  181. alertsHTML += '</div></div></div>';
  182.  
  183.  
  184. RAUtilWindow.innerHTML = alertsHTML;
  185. document.body.appendChild(RAUtilWindow);
  186.  
  187. $('#RAShiftLeftBtn').click(RAShiftLeftBtnClick);
  188. $('#RAShiftRightBtn').click(RAShiftRightBtnClick);
  189. $('#RAShiftUpBtn').click(RAShiftUpBtnClick);
  190. $('#RAShiftDownBtn').click(RAShiftDownBtnClick);
  191.  
  192. $('#RARotateLeftBtn').click(RARotateLeftBtnClick);
  193. $('#RARotateRightBtn').click(RARotateRightBtnClick);
  194.  
  195. $('#diameterChangeDecreaseBtn').click(diameterChangeDecreaseBtnClick);
  196. $('#diameterChangeIncreaseBtn').click(diameterChangeIncreaseBtnClick);
  197.  
  198. $('#btnMoveANodeIn').click(function(){moveNodeIn(WazeWrap.getSelectedFeatures()[0].WW.getObjectModel().attributes.id, WazeWrap.getSelectedFeatures()[0].WW.getObjectModel().attributes.fromNodeID);});
  199. $('#btnMoveANodeOut').click(function(){moveNodeOut(WazeWrap.getSelectedFeatures()[0].WW.getObjectModel().attributes.id, WazeWrap.getSelectedFeatures()[0].WW.getObjectModel().attributes.fromNodeID);});
  200. $('#btnMoveBNodeIn').click(function(){moveNodeIn(WazeWrap.getSelectedFeatures()[0].WW.getObjectModel().attributes.id, WazeWrap.getSelectedFeatures()[0].WW.getObjectModel().attributes.toNodeID);});
  201. $('#btnMoveBNodeOut').click(function(){moveNodeOut(WazeWrap.getSelectedFeatures()[0].WW.getObjectModel().attributes.id, WazeWrap.getSelectedFeatures()[0].WW.getObjectModel().attributes.toNodeID);});
  202.  
  203. $('#shiftAmount').keypress(function(event) {
  204. if ((event.which != 46 || $(this).val().indexOf('.') != -1) && (event.which < 48 || event.which > 57))
  205. event.preventDefault();
  206. });
  207.  
  208. $('#rotationAmount').keypress(function(event) {
  209. if ((event.which != 46 || $(this).val().indexOf('.') != -1) && (event.which < 48 || event.which > 57))
  210. event.preventDefault();
  211. });
  212.  
  213. $('#collapserLink').click(function(){
  214. if($('#collapser').attr('class') == "fa fa-caret-square-o-down"){
  215. $("#collapser").removeClass("fa-caret-square-o-down");
  216. $("#collapser").addClass("fa-caret-square-o-up");
  217. }
  218. else{
  219. $("#collapser").removeClass("fa-caret-square-o-up");
  220. $("#collapser").addClass("fa-caret-square-o-down");
  221. }
  222. saveSettingsToStorage();
  223. });
  224.  
  225. W.selectionManager.events.register("selectionchanged", null, checkDisplayTool);
  226. //W.model.actionManager.events.register("afterundoaction",null, undotriggered);
  227. //W.model.actionManager.events.register("afterclearactions",null,actionsCleared);
  228.  
  229. var loadedSettings = $.parseJSON(localStorage.getItem("WME_RAUtil"));
  230. var defaultSettings = {
  231. divTop: "15%",
  232. divLeft: "25%",
  233. Expanded: true,
  234. RoundaboutAngles: true
  235. };
  236. _settings = loadedSettings ? loadedSettings : defaultSettings;
  237.  
  238. $('#RAUtilWindow').css('left', _settings.divLeft);
  239. $('#RAUtilWindow').css('top', _settings.divTop);
  240. $("#chkRARoundaboutAngles").prop('checked', _settings.RoundaboutAngles);
  241. $("#chkRARoundaboutAngles").prop('checked', _settings.RoundaboutAngles);
  242.  
  243. if(!_settings.Expanded){
  244. $("#divWrappers").removeClass("in");
  245. $("#divWrappers").addClass("collapse");
  246. $("#collapser").removeClass("fa-caret-square-o-up");
  247. $("#collapser").addClass("fa-caret-square-o-down");
  248. }
  249.  
  250. $("#chkRARoundaboutAngles").click(function(){
  251. saveSettingsToStorage();
  252.  
  253. if($("#chkRARoundaboutAngles").is(":checked")){
  254. wEvents.register("zoomend", null, DrawRoundaboutAngles);
  255. wEvents.register("moveend", null, DrawRoundaboutAngles);
  256. DrawRoundaboutAngles();
  257. drc_layer.setVisibility(true);
  258. }
  259. else{
  260. wEvents.unregister("zoomend", null, DrawRoundaboutAngles);
  261. wEvents.unregister("moveend", null, DrawRoundaboutAngles);
  262. drc_layer.setVisibility(false);
  263. }
  264. });
  265.  
  266. if(_settings.RoundaboutAngles){
  267. wEvents.register("zoomend", null, DrawRoundaboutAngles);
  268. wEvents.register("moveend", null, DrawRoundaboutAngles);
  269. DrawRoundaboutAngles();
  270. }
  271.  
  272. WazeWrap.Interface.ShowScriptUpdate("WME RA Util", GM_info.script.version, updateMessage, "https://greasyfork.org/en/scripts/23616-wme-ra-util", "https://www.waze.com/forum/viewtopic.php?f=819&t=211079");
  273. }
  274.  
  275. function saveSettingsToStorage() {
  276. if (localStorage) {
  277. var settings = {
  278. divTop: "15%",
  279. divLeft: "25%",
  280. Expanded: true,
  281. RoundaboutAngles: true
  282. };
  283.  
  284. settings.divLeft = $('#RAUtilWindow').css('left');
  285. settings.divTop = $('#RAUtilWindow').css('top');
  286. settings.Expanded = $("#collapser").attr('class').indexOf("fa-caret-square-o-up") > -1;
  287. settings.RoundaboutAngles = $("#chkRARoundaboutAngles").is(":checked");
  288. localStorage.setItem("WME_RAUtil", JSON.stringify(settings));
  289. }
  290. }
  291.  
  292. function checkDisplayTool(){
  293. if(WazeWrap.hasSelectedFeatures() && WazeWrap.getSelectedFeatures()[0].WW.getType() === 'segment'){
  294. if(!AllSelectedSegmentsRA() || WazeWrap.getSelectedFeatures().length === 0)
  295. $('#RAUtilWindow').css({'visibility': 'hidden'});
  296. else{
  297. $('#RAUtilWindow').css({'visibility': 'visible'});
  298. if(typeof jQuery.ui !== 'undefined')
  299. $('#RAUtilWindow' ).draggable({ //Gotta nuke the height setting the dragging inserts otherwise the panel cannot collapse
  300. stop: function(event, ui) {
  301. $('#RAUtilWindow').css("height", "");
  302. saveSettingsToStorage();
  303. }
  304. });
  305. //checkSaveChanges();
  306. checkAllEditable(WazeWrap.Model.getAllRoundaboutSegmentsFromObj(WazeWrap.getSelectedFeatures()[0]));
  307. }
  308. }
  309. else{
  310. $('#RAUtilWindow').css({'visibility': 'hidden'});
  311. if(typeof jQuery.ui !== 'undefined')
  312. $('#RAUtilWindow' ).draggable({
  313. stop: function(event, ui) {
  314. $('#RAUtilWindow').css("height", "");
  315. saveSettingsToStorage();
  316. }
  317. });
  318. }
  319. }
  320.  
  321. function checkAllEditable(RASegs){
  322. var $RAEditable = $('#RAEditable');
  323. var allEditable = true;
  324. var segObj, fromNode, toNode;
  325.  
  326. for(let i=0; i<RASegs.length;i++){
  327. segObj = W.model.segments.getObjectById(RASegs[i]);
  328. fromNode = segObj.getFromNode();
  329. toNode = segObj.getToNode();
  330.  
  331. if(segObj !== "undefined"){
  332. if(fromNode && fromNode !== "undefined" && !fromNode.areConnectionsEditable())
  333. allEditable = false;
  334. else if(toNode && toNode !== "undefined" && !toNode.areConnectionsEditable())
  335. allEditable = false;
  336. var toConnected, fromConnected;
  337.  
  338. if(toNode){
  339. toConnected = toNode.attributes.segIDs;
  340. for(let j=0;j<toConnected.length;j++){
  341. if(W.model.segments.getObjectById(toConnected[j]) !== "undefined")
  342. if(W.model.segments.getObjectById(toConnected[j]).hasClosures())
  343. allEditable = false;
  344. }
  345. }
  346.  
  347. if(fromNode){
  348. fromConnected = fromNode.attributes.segIDs;
  349. for(let j=0;j<fromConnected.length;j++){
  350. if(W.model.segments.getObjectById(fromConnected[j]) !== "undefined")
  351. if(W.model.segments.getObjectById(fromConnected[j]).hasClosures())
  352. allEditable = false;
  353. }
  354. }
  355. }
  356. }
  357. if(allEditable)
  358. $RAEditable.remove();
  359. else{
  360. if($RAEditable.length === 0){
  361. $RAEditable = $('<div>', {id:'RAEditable', style:'color:red'});
  362. $RAEditable.text('One or more segments are locked above your rank or have a closure.');
  363. $('#RAUtilWindow').append($RAEditable);
  364. }
  365. }
  366. return allEditable;
  367. }
  368.  
  369. function AllSelectedSegmentsRA(){
  370. for (let i = 0; i < WazeWrap.getSelectedFeatures().length; i++){
  371. if(WazeWrap.getSelectedFeatures()[i].WW.getObjectModel().attributes.id < 0 || !WazeWrap.Model.isRoundaboutSegmentID(WazeWrap.getSelectedFeatures()[i].WW.getObjectModel().attributes.id))
  372. return false;
  373. }
  374. return true;
  375. }
  376.  
  377. function ShiftSegmentNodesLat(segObj, latOffset){
  378. var RASegs = WazeWrap.Model.getAllRoundaboutSegmentsFromObj(segObj);
  379. if(checkAllEditable(RASegs)){
  380. var gps;
  381. var newGeometry = segObj.geometry.clone();
  382. var originalLength = segObj.geometry.components.length;
  383. var multiaction = new MultiAction();
  384. multiaction.setModel(W.model);
  385.  
  386. for(let i=0; i<RASegs.length; i++){
  387. segObj = W.model.segments.getObjectById(RASegs[i]);
  388. newGeometry = segObj.geometry.clone();
  389. originalLength = segObj.geometry.components.length;
  390. for(j=1; j < originalLength-1; j++){
  391. gps = WazeWrap.Geometry.ConvertTo4326(segObj.geometry.components[j].x, segObj.geometry.components[j].y);
  392. gps.lat += latOffset;
  393. newGeometry.components.splice(j,0, new OpenLayers.Geometry.Point(segObj.geometry.components[j].x, WazeWrap.Geometry.ConvertTo900913(segObj.geometry.components[j].x,gps.lat).lat));
  394. newGeometry.components.splice(j+1,1);
  395. }
  396. newGeometry.components[0].calculateBounds();
  397. newGeometry.components[originalLength-1].calculateBounds();
  398. multiaction.doSubAction(new UpdateSegmentGeometry(segObj, segObj.geometry, newGeometry));
  399. //W.model.actionManager.add(new UpdateSegmentGeometry(segObj, segObj.geometry, newGeometry));
  400.  
  401. var node = W.model.nodes.objects[segObj.attributes.toNodeID];
  402. if(segObj.attributes.revDirection)
  403. node = W.model.nodes.objects[segObj.attributes.fromNodeID];
  404. var newNodeGeometry = node.geometry.clone();
  405. gps = WazeWrap.Geometry.ConvertTo4326(node.attributes.geometry.x, node.attributes.geometry.y);
  406. gps.lat += latOffset;
  407. newNodeGeometry.y = WazeWrap.Geometry.ConvertTo900913(node.geometry.x, gps.lat).lat;
  408. newNodeGeometry.calculateBounds();
  409.  
  410. var connectedSegObjs = {};
  411. var emptyObj = {};
  412. for(var j=0;j<node.attributes.segIDs.length;j++){
  413. var segid = node.attributes.segIDs[j];
  414. connectedSegObjs[segid] = W.model.segments.getObjectById(segid).geometry.clone();
  415. }
  416. //W.model.actionManager.add(new MoveNode(segObj, segObj.geometry, newNodeGeometry, connectedSegObjs, i));
  417. multiaction.doSubAction(new MoveNode(node, node.geometry, newNodeGeometry,connectedSegObjs,emptyObj));
  418. //W.model.actionManager.add(new MoveNode(node, node.geometry, newNodeGeometry));
  419. //totalActions +=2;
  420. }
  421. W.model.actionManager.add(multiaction);
  422. }
  423. }
  424.  
  425. function ShiftSegmentsNodesLong(segObj, longOffset){
  426. var RASegs = WazeWrap.Model.getAllRoundaboutSegmentsFromObj(segObj);
  427. if(checkAllEditable(RASegs)){
  428. var gps, newGeometry, originalLength;
  429. var multiaction = new MultiAction();
  430. multiaction.setModel(W.model);
  431.  
  432. //Loop through all RA segments & adjust
  433. for(let i=0; i<RASegs.length; i++){
  434. segObj = W.model.segments.getObjectById(RASegs[i]);
  435. newGeometry = segObj.geometry.clone();
  436. originalLength = segObj.geometry.components.length;
  437. for(let j=1; j < originalLength-1; j++){
  438. gps = WazeWrap.Geometry.ConvertTo4326(segObj.geometry.components[j].x, segObj.geometry.components[j].y);
  439. gps.lon += longOffset;
  440. newGeometry.components.splice(j,0, new OpenLayers.Geometry.Point(WazeWrap.Geometry.ConvertTo900913(gps.lon, segObj.geometry.components[j].y).lon, segObj.geometry.components[j].y));
  441. newGeometry.components.splice(j+1,1);
  442. }
  443. newGeometry.components[0].calculateBounds();
  444. newGeometry.components[originalLength-1].calculateBounds();
  445. //W.model.actionManager.add(new UpdateSegmentGeometry(segObj, segObj.geometry, newGeometry));
  446. multiaction.doSubAction(new UpdateSegmentGeometry(segObj, segObj.geometry, newGeometry));
  447.  
  448. var node = W.model.nodes.objects[segObj.attributes.toNodeID];
  449. if(segObj.attributes.revDirection)
  450. node = W.model.nodes.objects[segObj.attributes.fromNodeID];
  451.  
  452. var newNodeGeometry = node.geometry.clone();
  453. gps = WazeWrap.Geometry.ConvertTo4326(node.attributes.geometry.x, node.attributes.geometry.y);
  454. gps.lon += longOffset;
  455. newNodeGeometry.x = WazeWrap.Geometry.ConvertTo900913(gps.lon, node.geometry.y).lon;
  456. newNodeGeometry.calculateBounds();
  457.  
  458. var connectedSegObjs = {};
  459. var emptyObj = {};
  460. for(let j=0;j<node.attributes.segIDs.length;j++){
  461. var segid = node.attributes.segIDs[j];
  462. connectedSegObjs[segid] = W.model.segments.getObjectById(segid).geometry.clone();
  463. }
  464. //W.model.actionManager.add(new MoveNode(node, node.geometry, newNodeGeometry));
  465. multiaction.doSubAction(new MoveNode(node, node.geometry, newNodeGeometry, connectedSegObjs, emptyObj));
  466. //totalActions +=2;
  467. }
  468. W.model.actionManager.add(multiaction);
  469. }
  470. }
  471.  
  472. function rotatePoints(origin, points, angle){
  473. var lineFeature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points),null,null);
  474. lineFeature.geometry.rotate(angle, new OpenLayers.Geometry.Point(origin.x, origin.y));
  475. return [].concat(lineFeature.geometry.components);
  476. }
  477.  
  478. function RotateRA(segObj, angle){
  479. var RASegs = WazeWrap.Model.getAllRoundaboutSegmentsFromObj(segObj);
  480. var raCenter = W.model.junctions.objects[WazeWrap.Model.getObjectModel(segObj).attributes.junctionID].geometry;
  481.  
  482. if(checkAllEditable(RASegs)){
  483. var gps, newGeometry, originalLength;
  484. var multiaction = new MultiAction();
  485. multiaction.setModel(W.model);
  486.  
  487. //Loop through all RA segments & adjust
  488. for(let i=0; i<RASegs.length; i++){
  489. segObj = W.model.segments.getObjectById(RASegs[i]);
  490. newGeometry = segObj.geometry.clone();
  491. originalLength = segObj.geometry.components.length;
  492.  
  493. var center = raCenter; //WazeWrap.Geometry.ConvertTo900913(raCenter.x, raCenter.y);
  494. var segPoints = [];
  495. //Have to copy the points manually (can't use .clone()) otherwise the geometry rotation modifies the geometry of the segment itself and that hoses WME.
  496. for(let j=0; j<originalLength;j++)
  497. segPoints.push(new OpenLayers.Geometry.Point(segObj.geometry.components[j].x, segObj.geometry.components[j].y));
  498.  
  499. var newPoints = rotatePoints(center, segPoints, angle);
  500.  
  501. for(let j=1; j<originalLength-1;j++){
  502. newGeometry.components.splice(j, 0, new OpenLayers.Geometry.Point(newPoints[j].x, newPoints[j].y));
  503. newGeometry.components.splice(j+1,1);
  504. }
  505.  
  506. newGeometry.components[0].calculateBounds();
  507. newGeometry.components[originalLength-1].calculateBounds();
  508. //W.model.actionManager.add(new UpdateSegmentGeometry(segObj, segObj.geometry, newGeometry));
  509. multiaction.doSubAction(new UpdateSegmentGeometry(segObj, segObj.geometry, newGeometry));
  510.  
  511. //**************Rotate Nodes******************
  512. var node = W.model.nodes.objects[segObj.attributes.toNodeID];
  513. if(segObj.attributes.revDirection)
  514. node = W.model.nodes.objects[segObj.attributes.fromNodeID];
  515.  
  516. var nodePoints = [];
  517. var newNodeGeometry = node.geometry.clone();
  518.  
  519. nodePoints.push(new OpenLayers.Geometry.Point(node.attributes.geometry.x, node.attributes.geometry.y));
  520. nodePoints.push(new OpenLayers.Geometry.Point(node.attributes.geometry.x, node.attributes.geometry.y)); //add it twice because lines need 2 points
  521.  
  522. gps = rotatePoints(center, nodePoints, angle);
  523.  
  524. newNodeGeometry.x = gps[0].x;
  525. newNodeGeometry.y = gps[0].y;
  526.  
  527. newNodeGeometry.calculateBounds();
  528.  
  529. var connectedSegObjs = {};
  530. var emptyObj = {};
  531. for(let j=0;j<node.attributes.segIDs.length;j++){
  532. var segid = node.attributes.segIDs[j];
  533. connectedSegObjs[segid] = W.model.segments.getObjectById(segid).geometry.clone();
  534. }
  535. multiaction.doSubAction(new MoveNode(node, node.geometry, newNodeGeometry, connectedSegObjs, emptyObj));
  536. //totalActions +=2;
  537. }
  538. W.model.actionManager.add(multiaction);
  539. }
  540. }
  541.  
  542. function RARotateLeftBtnClick(e){
  543. e.stopPropagation();
  544. var segObj = WazeWrap.getSelectedFeatures()[0];
  545. RotateRA(segObj, $('#rotationAmount').val());
  546. }
  547.  
  548. function RARotateRightBtnClick(e){
  549. e.stopPropagation();
  550.  
  551. var segObj = WazeWrap.getSelectedFeatures()[0];
  552. RotateRA(segObj, -$('#rotationAmount').val());
  553. }
  554.  
  555. function ChangeDiameter(segObj, amount){
  556. var RASegs = WazeWrap.Model.getAllRoundaboutSegmentsFromObj(segObj);
  557. var raCenter = W.model.junctions.objects[WazeWrap.Model.getObjectModel(segObj).attributes.junctionID].geometry;
  558.  
  559. if(checkAllEditable(RASegs)){
  560. var gps, newGeometry, originalLength;
  561.  
  562. var center = raCenter; //WazeWrap.Geometry.ConvertTo900913(raCenter[0], raCenter[1]);
  563. //Loop through all RA segments & adjust
  564. for(let i=0; i<RASegs.length; i++){
  565. segObj = W.model.segments.getObjectById(RASegs[i]);
  566. newGeometry = segObj.geometry.clone();
  567. originalLength = segObj.geometry.components.length;
  568.  
  569. for(let j=1; j < originalLength-1; j++){
  570. let pt = segObj.geometry.components[j];
  571. let h = Math.sqrt(Math.abs(Math.pow((pt.x - center.x),2) + Math.pow((pt.y - center.y),2)));
  572. let ratio = (h + amount)/h;
  573. let xdelta = (pt.x - center.x) * ratio;
  574. let ydelta = (pt.y - center.y) * ratio;
  575.  
  576. newGeometry.components.splice(j,0, new OpenLayers.Geometry.Point(center.x + xdelta, center.y + ydelta));
  577. newGeometry.components.splice(j+1,1);
  578. }
  579. newGeometry.components[0].calculateBounds();
  580. newGeometry.components[originalLength-1].calculateBounds();
  581. W.model.actionManager.add(new UpdateSegmentGeometry(segObj, segObj.geometry, newGeometry));
  582.  
  583. var node = W.model.nodes.objects[segObj.attributes.toNodeID];
  584. if(segObj.attributes.revDirection)
  585. node = W.model.nodes.objects[segObj.attributes.fromNodeID];
  586.  
  587. var newNodeGeometry = node.geometry.clone();
  588. let h = Math.sqrt(Math.abs(Math.pow((newNodeGeometry.x - center.x),2) + Math.pow((newNodeGeometry.y - center.y),2)));
  589. let ratio = (h + amount)/h;
  590. let xdelta = (newNodeGeometry.x - center.x) * ratio;
  591. let ydelta = (newNodeGeometry.y - center.y) * ratio;
  592. newNodeGeometry.x = center.x + xdelta;
  593. newNodeGeometry.y = center.y + ydelta;
  594. newNodeGeometry.calculateBounds();
  595. var connectedSegObjs = {};
  596. var emptyObj = {};
  597. for(let j=0;j<node.attributes.segIDs.length;j++){
  598. var segid = node.attributes.segIDs[j];
  599. connectedSegObjs[segid] = W.model.segments.getObjectById(segid).geometry.clone();
  600. }
  601. W.model.actionManager.add(new MoveNode(node, node.geometry, newNodeGeometry, connectedSegObjs, emptyObj));
  602. }
  603. if(_settings.RoundaboutAngles)
  604. DrawRoundaboutAngles();
  605. }
  606. }
  607.  
  608. function diameterChangeDecreaseBtnClick(e){
  609. e.stopPropagation();
  610. var segObj = WazeWrap.getSelectedFeatures()[0];
  611. ChangeDiameter(segObj, -1);
  612. }
  613.  
  614. function diameterChangeIncreaseBtnClick(e){
  615. e.stopPropagation();
  616. var segObj = WazeWrap.getSelectedFeatures()[0];
  617. ChangeDiameter(segObj, 1);
  618. }
  619.  
  620. function moveNodeIn(sourceSegID, nodeID){
  621. let isANode = true;
  622. let curSeg = W.model.segments.getObjectById(sourceSegID);
  623. if(curSeg.geometry.components.length > 2){
  624. if(nodeID === curSeg.attributes.toNodeID)
  625. isANode = false;
  626. //Add geo point on the other segment
  627. let node = W.model.nodes.getObjectById(nodeID);
  628. let currNodePOS = node.geometry.clone();
  629. let otherSeg; //other RA segment that we are adding a geo point to
  630. let nodeSegs = [...W.model.nodes.getObjectById(nodeID).attributes.segIDs];
  631. nodeSegs = _.without(nodeSegs, sourceSegID); //remove the source segment from the node Segs - we need to find the segment that is a part of the RA that is prior to our source seg
  632. for(let i=0; i<nodeSegs.length; i++){
  633. let s = W.model.segments.getObjectById(nodeSegs[i]);
  634. if(s.attributes.junctionID){
  635. otherSeg = s;
  636. break;
  637. }
  638. }
  639.  
  640. var multiaction = new MultiAction();
  641. multiaction.setModel(W.model);
  642. //note and remove first geo point, move junction node to this point
  643. var newNodeGeometry = curSeg.geometry.components[(isANode ? 1 : curSeg.geometry.components.length - 2)].clone();
  644. newNodeGeometry.calculateBounds();
  645.  
  646. let newSegGeo = curSeg.geometry.clone();
  647. newSegGeo.components.splice((isANode ? 1 : newSegGeo.components.length - 2),1);
  648. //delete the geo point
  649. multiaction.doSubAction(new UpdateSegmentGeometry(curSeg, curSeg.geometry, newSegGeo));
  650.  
  651. //move the node
  652. var connectedSegObjs = {};
  653. var emptyObj = {};
  654. for(var j=0;j<node.attributes.segIDs.length;j++){
  655. var segid = node.attributes.segIDs[j];
  656. connectedSegObjs[segid] = W.model.segments.getObjectById(segid).geometry.clone();
  657. }
  658. multiaction.doSubAction(new MoveNode(node, node.geometry, newNodeGeometry,connectedSegObjs,emptyObj));
  659.  
  660. if((otherSeg.attributes.revDirection && !curSeg.attributes.revDirection) || (!otherSeg.attributes.revDirection && curSeg.attributes.revDirection))
  661. isANode = !isANode;
  662.  
  663. let newGeo = otherSeg.geometry.clone();
  664. let originalLength = otherSeg.geometry.components.length;
  665.  
  666. newGeo.components.splice((isANode ? -1 : 1),0, new OpenLayers.Geometry.Point(currNodePOS.x, currNodePOS.y));
  667. newGeo.components[0].calculateBounds();
  668. newGeo.components[originalLength].calculateBounds();
  669.  
  670. multiaction.doSubAction(new UpdateSegmentGeometry(otherSeg, otherSeg.geometry, newGeo));
  671.  
  672.  
  673. W.model.actionManager.add(multiaction);
  674.  
  675. if(_settings.RoundaboutAngles)
  676. DrawRoundaboutAngles();
  677. }
  678. }
  679.  
  680. function moveNodeOut(sourceSegID, nodeID){
  681. let isANode = true;
  682. let curSeg = W.model.segments.getObjectById(sourceSegID);
  683. if(nodeID === curSeg.attributes.toNodeID)
  684. isANode = false;
  685. //Add geo point on the other segment
  686. let node = W.model.nodes.getObjectById(nodeID);
  687. let currNodePOS = node.geometry.clone();
  688. let otherSeg; //other RA segment that we are adding a geo point to
  689. let nodeSegs = [...W.model.nodes.getObjectById(nodeID).attributes.segIDs];
  690. nodeSegs = _.without(nodeSegs, sourceSegID); //remove the source segment from the node Segs - we need to find the segment that is a part of the RA that is after our source seg
  691. for(let i=0; i<nodeSegs.length; i++){
  692. let s = W.model.segments.getObjectById(nodeSegs[i]);
  693. if(s.attributes.junctionID){
  694. otherSeg = s;
  695. break;
  696. }
  697. }
  698. if(otherSeg.geometry.components.length > 2){
  699. let origNodeSegs = [...W.model.nodes.getObjectById(nodeID).attributes.segIDs];
  700. let originalLength = otherSeg.geometry.components.length;
  701.  
  702. let newSegGeo = curSeg.geometry.clone();
  703. newSegGeo.components.splice((isANode ? 1 : newSegGeo.components.length - 1),0, new OpenLayers.Geometry.Point(currNodePOS.x, currNodePOS.y));
  704. //delete the geo point
  705. var multiaction = new MultiAction();
  706. multiaction.setModel(W.model);
  707. multiaction.doSubAction(new UpdateSegmentGeometry(curSeg, curSeg.geometry, newSegGeo));
  708. if((otherSeg.attributes.revDirection && !curSeg.attributes.revDirection) || (!otherSeg.attributes.revDirection && curSeg.attributes.revDirection))
  709. isANode = !isANode;
  710.  
  711. //note and remove first geo point, move junction node to this point
  712. var newNodeGeometry = otherSeg.geometry.components[(isANode ? otherSeg.geometry.components.length - 2 : 1)].clone();
  713. newNodeGeometry.calculateBounds();
  714. let newGeo = otherSeg.geometry.clone();
  715. newGeo.components.splice((isANode ? -2 : 1),1);
  716. newGeo.components[0].calculateBounds();
  717. newGeo.components[originalLength-2].calculateBounds();
  718.  
  719. multiaction.doSubAction(new UpdateSegmentGeometry(otherSeg, otherSeg.geometry, newGeo));
  720.  
  721. //move the node
  722. var connectedSegObjs = {};
  723. var emptyObj = {};
  724. for(var j=0; j < origNodeSegs.length;j++){
  725. var segid = origNodeSegs[j];
  726. connectedSegObjs[segid] = W.model.segments.getObjectById(segid).geometry.clone();
  727. }
  728. multiaction.doSubAction(new MoveNode(node, node.geometry, newNodeGeometry,connectedSegObjs,emptyObj));
  729. W.model.actionManager.add(multiaction);
  730.  
  731. if(_settings.RoundaboutAngles)
  732. DrawRoundaboutAngles();
  733. }
  734. }
  735.  
  736.  
  737. //Left
  738. function RAShiftLeftBtnClick(e){
  739. // this traps the click to prevent it falling through to the underlying area name element and potentially causing the map view to be relocated to that area...
  740. e.stopPropagation();
  741.  
  742. //if(!pendingChanges){
  743. var segObj = WazeWrap.getSelectedFeatures()[0];
  744. var convertedCoords = WazeWrap.Geometry.ConvertTo4326(segObj.geometry.components[0].x, segObj.geometry.components[0].y);
  745. var gpsOffsetAmount = WazeWrap.Geometry.CalculateLongOffsetGPS(-$('#shiftAmount').val(), convertedCoords.lon, convertedCoords.lat);
  746. ShiftSegmentsNodesLong(segObj, gpsOffsetAmount);
  747. //}
  748. }
  749. //Right
  750. function RAShiftRightBtnClick(e){
  751. // this traps the click to prevent it falling through to the underlying area name element and potentially causing the map view to be relocated to that area...
  752. e.stopPropagation();
  753.  
  754. //if(!pendingChanges){
  755. var segObj = WazeWrap.getSelectedFeatures()[0];
  756. var convertedCoords = WazeWrap.Geometry.ConvertTo4326(segObj.geometry.components[0].x, segObj.geometry.components[0].y);
  757. var gpsOffsetAmount = WazeWrap.Geometry.CalculateLongOffsetGPS($('#shiftAmount').val(), convertedCoords.lon, convertedCoords.lat);
  758. ShiftSegmentsNodesLong(segObj, gpsOffsetAmount);
  759. //}
  760. }
  761. //Up
  762. function RAShiftUpBtnClick(e){
  763. // this traps the click to prevent it falling through to the underlying area name element and potentially causing the map view to be relocated to that area...
  764. e.stopPropagation();
  765.  
  766. //if(!pendingChanges){
  767. var segObj = WazeWrap.getSelectedFeatures()[0];
  768. var gpsOffsetAmount = WazeWrap.Geometry.CalculateLatOffsetGPS($('#shiftAmount').val(), WazeWrap.Geometry.ConvertTo4326(segObj.geometry.components[0].x, segObj.geometry.components[0].y));
  769. ShiftSegmentNodesLat(segObj, gpsOffsetAmount);
  770. //}
  771. }
  772. //Down
  773. function RAShiftDownBtnClick(e){
  774. // this traps the click to prevent it falling through to the underlying area name element and potentially causing the map view to be relocated to that area...
  775. e.stopPropagation();
  776.  
  777. //if(!pendingChanges){
  778. var segObj = WazeWrap.getSelectedFeatures()[0];
  779. var gpsOffsetAmount = WazeWrap.Geometry.CalculateLatOffsetGPS(-$('#shiftAmount').val(), WazeWrap.Geometry.ConvertTo4326(segObj.geometry.components[0].x, segObj.geometry.components[0].y));
  780. ShiftSegmentNodesLat(segObj, gpsOffsetAmount);
  781. //}
  782. }
  783.  
  784. //*************** Roundabout Angles **********************
  785. function DrawRoundaboutAngles(){
  786. //---------get or create layer
  787. var layers = W.map.getLayersBy("uniqueName","__DrawRoundaboutAngles");
  788.  
  789. if(layers.length > 0)
  790. drc_layer = layers[0];
  791. else {
  792. var drc_style = new OpenLayers.Style({
  793. fillOpacity: 0.0,
  794. strokeOpacity: 1.0,
  795. fillColor: "#FF40C0",
  796. strokeColor: "${strokeColor}",
  797. strokeWidth: 10,
  798. fontWeight: "bold",
  799. pointRadius: 0,
  800. label : "${labelText}",
  801. fontFamily: "Tahoma, Courier New",
  802. labelOutlineColor: "#FFFFFF",
  803. labelOutlineWidth: 3,
  804. fontColor: "${labelColor}",
  805. fontSize: "10px"
  806. });
  807.  
  808. drc_layer = new OpenLayers.Layer.Vector("Roundabout Angles", {
  809. displayInLayerSwitcher: true,
  810. uniqueName: "__DrawRoundaboutAngles",
  811. styleMap: new OpenLayers.StyleMap(drc_style)
  812. });
  813.  
  814. I18n.translations[I18n.currentLocale()].layers.name["__DrawRoundaboutAngles"] = "Roundabout Angles";
  815. W.map.addLayer(drc_layer);
  816.  
  817. drc_layer.setVisibility(true);
  818. }
  819.  
  820. localStorage.WMERAEnabled = drc_layer.visibility;
  821.  
  822. if (drc_layer.visibility == false) {
  823. drc_layer.removeAllFeatures();
  824. return;
  825. }
  826.  
  827. if (W.map.getZoom() < 1) {
  828. drc_layer.removeAllFeatures();
  829. return;
  830. }
  831.  
  832. //---------collect all roundabouts first
  833. var rsegments = {};
  834.  
  835. for (var iseg in W.model.segments.objects) {
  836. let isegment = W.model.segments.getObjectById(iseg);
  837. var iattributes = isegment.attributes;
  838. var iline = isegment.geometry.id;
  839.  
  840. let irid = iattributes.junctionID;
  841.  
  842. if (iline !== null && irid != undefined) {
  843. var rsegs = rsegments[irid];
  844. if (rsegs == undefined)
  845. rsegments[irid] = rsegs = new Array();
  846. rsegs.push(isegment);
  847. }
  848. }
  849.  
  850. var drc_features = [];
  851.  
  852. //-------for each roundabout do...
  853. for (let irid in rsegments) {
  854. let rsegs = rsegments[irid];
  855.  
  856. let isegment = rsegs[0];
  857.  
  858. var nodes = [];
  859. var nodes_x = [];
  860. var nodes_y = [];
  861.  
  862. nodes = rsegs.map(seg => seg.attributes.fromNodeID); //get from nodes
  863. nodes = [...nodes, ...rsegs.map(seg => seg.attributes.toNodeID)]; //get to nodes add to from nodes
  864. nodes = _.uniq(nodes); //remove duplicates
  865.  
  866. var node_objects = W.model.nodes.getByIds(nodes);
  867. nodes_x = node_objects.map(n => n.geometry.x); //get all x locations
  868. nodes_y = node_objects.map(n => n.geometry.y); //get all y locations
  869.  
  870. var sr_x = 0;
  871. var sr_y = 0;
  872. var radius = 0;
  873. var numNodes = nodes_x.length;
  874.  
  875. if (numNodes >= 1) {
  876. var ax = nodes_x[0];
  877. var ay = nodes_y[0];
  878.  
  879. var junction = W.model.junctions.getObjectById(irid);
  880. var junction_coords = junction && junction.geometry && junction.geometry.coordinates;
  881.  
  882. if (junction_coords && junction_coords.length == 2) {
  883. //---------- get center point from junction model
  884. let lonlat = new OpenLayers.LonLat(junction_coords[0], junction_coords[1]);
  885. lonlat.transform(W.Config.map.projection.remote, W.Config.map.projection.local);
  886. let pt = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
  887. sr_x = pt.x;
  888. sr_y = pt.y;
  889. }
  890. else if (numNodes >= 3) {
  891. //-----------simple approximation of centre point calculated from three first points
  892. let bx = nodes_x[1];
  893. let by = nodes_y[1];
  894. let cx = nodes_x[2];
  895. let cy = nodes_y[2];
  896.  
  897. let x1 = (bx + ax) * 0.5;
  898. let y11 = (by + ay) * 0.5;
  899. let dy1 = bx - ax;
  900. let dx1 = -(by - ay);
  901. let x2 = (cx + bx) * 0.5;
  902. let y2 = (cy + by) * 0.5;
  903. let dy2 = cx - bx;
  904. let dx2 = -(cy - by);
  905. sr_x = (y11 * dx1 * dx2 + x2 * dx1 * dy2 - x1 * dy1 * dx2 - y2 * dx1 * dx2)/ (dx1 * dy2 - dy1 * dx2);
  906. sr_y = (sr_x - x1) * dy1 / dx1 + y11;
  907. }
  908. else {
  909. //---------- simple bounds-based calculation of center point
  910. var rbounds = new OpenLayers.Bounds();
  911. rbounds.extend(isegment.geometry.bounds);
  912.  
  913. var center = rbounds.getCenterPixel();
  914. sr_x = center.x;
  915. sr_y = center.y;
  916. }
  917.  
  918. var angles = [];
  919. var rr = -1;
  920. var r_ix;
  921.  
  922. for(let i=0; i<nodes_x.length; i++) {
  923.  
  924. var dx = nodes_x[i] - sr_x;
  925. var dy = nodes_y[i] - sr_y;
  926.  
  927. var rr2 = dx*dx + dy*dy;
  928. if (rr < rr2) {
  929. rr = rr2;
  930. r_ix = i;
  931. }
  932.  
  933. var angle = Math.atan2(dy, dx);
  934. angle = (360.0 + (angle * 180.0 / Math.PI));
  935. if (angle < 0.0) angle += 360.0;
  936. if (angle > 360.0) angle -= 360.0;
  937. angles.push(angle);
  938. }
  939.  
  940. radius = Math.sqrt(rr);
  941.  
  942. //---------sorting angles for calulating angle difference between two segments
  943. angles = angles.sort(function(a,b) { return a - b; });
  944. angles.push( angles[0] + 360.0);
  945. angles = angles.sort(function(a,b) { return a - b; });
  946.  
  947. var drc_color = (numNodes <= 4) ? "#0040FF" : "#002080";
  948.  
  949. var drc_point = new OpenLayers.Geometry.Point(sr_x, sr_y );
  950. var drc_circle = new OpenLayers.Geometry.Polygon.createRegularPolygon( drc_point, radius, 10 * W.map.getZoom() );
  951. var drc_feature = new OpenLayers.Feature.Vector(drc_circle, {labelText: "", labelColor: "#000000", strokeColor: drc_color, });
  952. drc_features.push(drc_feature);
  953.  
  954.  
  955. if (numNodes >= 2 && numNodes <= 4 && W.map.getZoom() >= 5) {
  956. for(let i=0; i<nodes_x.length; i++) {
  957. let ix = nodes_x[i];
  958. let iy = nodes_y[i];
  959. let startPt = new OpenLayers.Geometry.Point( sr_x, sr_y );
  960. let endPt = new OpenLayers.Geometry.Point( ix, iy );
  961. let line = new OpenLayers.Geometry.LineString([startPt, endPt]);
  962. let style = {strokeColor:drc_color, strokeWidth:2};
  963. let fea = new OpenLayers.Feature.Vector(line, {}, style);
  964. drc_features.push(fea);
  965. }
  966.  
  967. var angles_int = [];
  968. var angles_float = [];
  969. var angles_sum = 0;
  970.  
  971. for(let i=0; i<angles.length - 1; i++) {
  972.  
  973. var ang = angles[i+1] - angles[i+0];
  974. if (ang < 0) ang += 360.0;
  975. if (ang < 0) ang += 360.0;
  976.  
  977. if (ang < 135.0)
  978. ang = ang - 90.0;
  979. else
  980. ang = ang - 180.0;
  981.  
  982. angles_sum += parseInt(ang);
  983.  
  984. angles_float.push( ang );
  985. angles_int.push( parseInt(ang) );
  986. }
  987.  
  988. if (angles_sum > 45) angles_sum -= 90;
  989. if (angles_sum > 45) angles_sum -= 90;
  990. if (angles_sum > 45) angles_sum -= 90;
  991. if (angles_sum > 45) angles_sum -= 90;
  992. if (angles_sum < -45) angles_sum += 90;
  993. if (angles_sum < -45) angles_sum += 90;
  994. if (angles_sum < -45) angles_sum += 90;
  995. if (angles_sum < -45) angles_sum += 90;
  996. if (angles_sum != 0) {
  997. for(let i=0; i<angles_int.length; i++) {
  998. let a = angles_int[i];
  999. let af = angles_float[i] - angles_int[i];
  1000. if ( (a < 10 || a > 20) && (af < -0.5 || af > 0.5)){
  1001. angles_int[i] += -angles_sum;
  1002.  
  1003. break;
  1004. }
  1005. }
  1006. }
  1007.  
  1008. if (numNodes == 2) {
  1009. angles_int[1] = -angles_int[0];
  1010. angles_float[1] = -angles_float[0];
  1011. }
  1012.  
  1013. for(let i=0; i<angles.length - 1; i++) {
  1014. let arad = (angles[i+0] + angles[i+1]) * 0.5 * Math.PI / 180.0;
  1015. let ex = sr_x + Math.cos (arad) * radius * 0.5;
  1016. let ey = sr_y + Math.sin (arad) * radius * 0.5;
  1017.  
  1018. //*** Angle Display Rounding ***
  1019. let angint = Math.round(angles_float[i] * 100)/100;
  1020.  
  1021. let kolor = "#004000";
  1022. if (angint <= -15 || angint >= 15) kolor = "#FF0000";
  1023. else if (angint <= -13 || angint >= 13) kolor = "#FFC000";
  1024.  
  1025. let pt = new OpenLayers.Geometry.Point(ex, ey);
  1026. drc_features.push(new OpenLayers.Feature.Vector( pt, {labelText: (angint + "°"), labelColor: kolor } ));
  1027. //drc_features.push(new OpenLayers.Feature.Vector( pt, {labelText: (+angles_float[i].toFixed(2) + "°"), labelColor: kolor } ));
  1028. }
  1029. }
  1030. else {
  1031. for(let i=0; i < nodes_x.length; i++) {
  1032. let ix = nodes_x[i];
  1033. let iy = nodes_y[i];
  1034. let startPt = new OpenLayers.Geometry.Point( sr_x, sr_y );
  1035. let endPt = new OpenLayers.Geometry.Point( ix, iy );
  1036. let line = new OpenLayers.Geometry.LineString([startPt, endPt]);
  1037. let style = {strokeColor:drc_color, strokeWidth:2};
  1038. let fea = new OpenLayers.Feature.Vector(line, {}, style);
  1039. drc_features.push(fea);
  1040. }
  1041. }
  1042.  
  1043. let p1 = new OpenLayers.Geometry.Point( nodes_x[r_ix], nodes_y[r_ix] );
  1044. let p2 = new OpenLayers.Geometry.Point( sr_x, sr_y );
  1045. let line = new OpenLayers.Geometry.LineString([p1, p2]);
  1046. let geo_radius = line.getGeodesicLength(W.map.getProjectionObject());
  1047.  
  1048. let diam = geo_radius * 2.0;
  1049. let pt = new OpenLayers.Geometry.Point(sr_x, sr_y);
  1050. drc_features.push(new OpenLayers.Feature.Vector( pt, {labelText: (diam.toFixed(0) + "m"), labelColor: "#000000" } ));
  1051.  
  1052. }
  1053.  
  1054. }
  1055.  
  1056. drc_layer.removeAllFeatures();
  1057. drc_layer.addFeatures(drc_features);
  1058. }
  1059.  
  1060. function injectCss() {
  1061. var css = [
  1062. '.btnMoveNode {width=25px; height=25px; background-color:#92C3D3; cursor:pointer; padding:5px; font-size:14px; border:thin outset black; border-style:solid; border-width: 1px;border-radius:50%; -moz-border-radius:50%; -webkit-border-radius:50%; box-shadow:inset 0px 0px 20px -14px rgba(0,0,0,1); -moz-box-shadow:inset 0px 0px 20px -14px rgba(0,0,0,1); -webkit-box-shadow: inset 0px 0px 20px -14px rgba(0,0,0,1);}',
  1063. '.btnRotate { width=45px; height=45px; background-color:#92C3D3; cursor:pointer; padding: 5px; font-size:14px; border:thin outset black; border-style:solid; border-width: 1px;border-radius: 50%;-moz-border-radius: 50%;-webkit-border-radius: 50%;box-shadow: inset 0px 0px 20px -14px rgba(0,0,0,1);-moz-box-shadow: inset 0px 0px 20px -14px rgba(0,0,0,1);-webkit-box-shadow: inset 0px 0px 20px -14px rgba(0,0,0,1);}'
  1064. ].join(' ');
  1065. $('<style type="text/css">' + css + '</style>').appendTo('head');
  1066. }
  1067.  
  1068. })();
  1069.