WME RA Util

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

目前为 2023-06-06 提交的版本。查看 最新版本

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