WME RA Util

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

当前为 2019-05-03 提交的版本,查看 最新版本

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