WME RA Util

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

当前为 2019-10-30 提交的版本,查看 最新版本

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