WME Wide-Angle Lens Locks

Find segments that don't match lock levels

  1. /// <reference path="../typescript-typings/globals/openlayers/index.d.ts" />
  2. /// <reference path="../typescript-typings/waze.d.ts" />
  3. /// <reference path="../typescript-typings/globals/jquery/index.d.ts" />
  4. /// <reference path="WME Wide-Angle Lens.user.ts" />
  5. /// <reference path="../typescript-typings/greasyfork.d.ts" />
  6. // ==UserScript==
  7. // @name WME Wide-Angle Lens Locks
  8. // @namespace https://gf.qytechs.cn/en/users/19861-vtpearce
  9. // @description Find segments that don't match lock levels
  10. // @author vtpearce and crazycaveman
  11. // @match https://*.waze.com/*editor*
  12. // @exclude https://*.waze.com/user/editor*
  13. // @exclude https://www.waze.com/discuss/*
  14. // @version 2025.04.10.001
  15. // @grant GM_xmlhttpRequest
  16. // @copyright 2020 vtpearce
  17. // @license CC BY-SA 4.0
  18. // @require https://gf.qytechs.cn/scripts/24851-wazewrap/code/WazeWrap.js
  19. // @connect gf.qytechs.cn
  20. // ==/UserScript==
  21. // @updateURL https://gf.qytechs.cn/scripts/418295-wme-wide-angle-lens-locks-beta/code/WME%20Wide-Angle%20Lens%20Locks.meta.js
  22. // @downloadURL https://gf.qytechs.cn/scripts/418295-wme-wide-angle-lens-locks-beta/code/WME%20Wide-Angle%20Lens%20Locks.user.js
  23. /*global W, OL, $, WazeWrap, WMEWAL, OpenLayers */
  24. var WMEWAL_Locks;
  25. (function (WMEWAL_Locks) {
  26. const SCRIPT_NAME = GM_info.script.name;
  27. const SCRIPT_VERSION = GM_info.script.version.toString();
  28. const DOWNLOAD_URL = GM_info.script.downloadURL;
  29. const updateText = '<ul>'
  30. + '<li>Update for plugin status.</li>'
  31. + '</ul>';
  32. const greasyForkPage = 'https://gf.qytechs.cn/scripts/40643';
  33. const wazeForumThread = 'https://www.waze.com/forum/viewtopic.php?t=206376';
  34. const ctlPrefix = `_wmewalLocks`;
  35. const minimumWALVersionRequired = "2025.04.10.001";
  36. let IncludeInOutput;
  37. (function (IncludeInOutput) {
  38. IncludeInOutput[IncludeInOutput["Low"] = 1] = "Low";
  39. IncludeInOutput[IncludeInOutput["High"] = 2] = "High";
  40. })(IncludeInOutput || (IncludeInOutput = {}));
  41. let Operation;
  42. (function (Operation) {
  43. Operation[Operation["Equal"] = 1] = "Equal";
  44. Operation[Operation["NotEqual"] = 2] = "NotEqual";
  45. })(Operation || (Operation = {}));
  46. const pluginName = "WMEWAL-Locks";
  47. WMEWAL_Locks.Title = "Locks";
  48. WMEWAL_Locks.MinimumZoomLevel = 14;
  49. WMEWAL_Locks.SupportsSegments = true;
  50. WMEWAL_Locks.SupportsVenues = false;
  51. const settingsKey = "WMEWALLocksSettings";
  52. const savedSettingsKey = "WMEWALLocksSavedSettings";
  53. let settings = null;
  54. let savedSettings = [];
  55. let streets = null;
  56. let state;
  57. let stateName;
  58. let nameRegex = null;
  59. let cityRegex = null;
  60. let initCount = 0;
  61. let savedSegments;
  62. function onWmeReady() {
  63. initCount++;
  64. if (WazeWrap && WazeWrap.Ready && typeof (WMEWAL) !== 'undefined' && WMEWAL && WMEWAL.RegisterPlugIn) {
  65. log('debug', 'WazeWrap and WMEWAL ready.');
  66. init();
  67. }
  68. else {
  69. if (initCount < 60) {
  70. log('debug', 'WazeWrap or WMEWAL not ready. Trying again...');
  71. setTimeout(onWmeReady, 1000);
  72. }
  73. else {
  74. log('error', 'WazeWrap or WMEWAL not ready. Giving up.');
  75. }
  76. }
  77. }
  78. function bootstrap() {
  79. if (W?.userscripts?.state.isReady) {
  80. onWmeReady();
  81. }
  82. else {
  83. document.addEventListener('wme-ready', onWmeReady, { once: true });
  84. }
  85. }
  86. async function init() {
  87. // Check to see if WAL is at the minimum verson needed
  88. if (!(typeof WMEWAL.IsAtMinimumVersion === "function" && WMEWAL.IsAtMinimumVersion(minimumWALVersionRequired))) {
  89. log('log', "WAL not at required minimum version.");
  90. WazeWrap.Alerts.info(GM_info.script.name, "Cannot load plugin because WAL is not at the required minimum version.&nbsp;" +
  91. "You might need to manually update it from <a href='https://gf.qytechs.cn/scripts/40641' target='_blank'>Greasy Fork镜像</a>.", true, false);
  92. return;
  93. }
  94. if (typeof Storage !== "undefined") {
  95. if (localStorage[settingsKey]) {
  96. settings = JSON.parse(localStorage[settingsKey]);
  97. }
  98. if (localStorage[savedSettingsKey]) {
  99. try {
  100. savedSettings = JSON.parse(WMEWAL.LZString.decompressFromUTF16(localStorage[savedSettingsKey]));
  101. }
  102. catch (e) { }
  103. if (typeof savedSettings === "undefined" || savedSettings === null || savedSettings.length === 0) {
  104. log('debug', "decompressFromUTF16 failed, attempting decompress");
  105. localStorage[savedSettingsKey + "Backup"] = localStorage[savedSettingsKey];
  106. try {
  107. savedSettings = JSON.parse(WMEWAL.LZString.decompress(localStorage[savedSettingsKey]));
  108. }
  109. catch (e) { }
  110. if (typeof savedSettings === "undefined" || savedSettings === null) {
  111. log('warn', "decompress failed, savedSettings unrecoverable. Using blank");
  112. savedSettings = [];
  113. }
  114. updateSavedSettings();
  115. }
  116. }
  117. }
  118. if (settings == null) {
  119. settings = {
  120. RoadTypeMask: WMEWAL.RoadType.Freeway,
  121. State: null,
  122. Regex: null,
  123. RegexIgnoreCase: true,
  124. ExcludeRoundabouts: false,
  125. ExcludeJunctionBoxes: true,
  126. EditableByMe: true,
  127. StreetLockLevel: 1,
  128. PrimaryStreetLockLevel: 2,
  129. MinorHighwayLockLevel: 3,
  130. MajorHighwayLockLevel: 4,
  131. FreewayLockLevel: 5,
  132. RampLockLevel: 7,
  133. IncludeInOutput: IncludeInOutput.Low | IncludeInOutput.High,
  134. PlusOneWayMask: 0,
  135. CityRegex: null,
  136. CityRegexIgnoreCase: true,
  137. StateOperation: Operation.Equal,
  138. RailroadLockLevel: 2,
  139. IncludeAltNames: false
  140. };
  141. }
  142. else {
  143. if (updateProperties()) {
  144. updateSettings();
  145. }
  146. }
  147. log('log', "Initialized");
  148. WazeWrap.Interface.ShowScriptUpdate(SCRIPT_NAME, SCRIPT_VERSION, updateText, greasyForkPage, wazeForumThread);
  149. WMEWAL.RegisterPlugIn(WMEWAL_Locks);
  150. }
  151. function GetTab() {
  152. let html = "<table style='border-collapse: separate; border-spacing:0px 1px;'>";
  153. html += "<tbody>";
  154. html += "<tr><td class='wal-heading'>Saved Filters</td></tr>";
  155. html += "<tr><td class='wal-indent' style='padding-bottom: 8px'>" +
  156. `<select id='${ctlPrefix}SavedSettings'></select><br/>` +
  157. `<button class='btn btn-primary' id='${ctlPrefix}LoadSetting' title='Load'>Load</button>` +
  158. `<button class='btn btn-primary' style='margin-left: 4px;' id='${ctlPrefix}SaveSetting' title='Save'>Save</button>` +
  159. `<button class='btn btn-primary' style='margin-left: 4px;' id='${ctlPrefix}DeleteSetting' title='Delete'>Delete</button></td></tr>`;
  160. html += "<tr><td class='wal-heading' style='border-top: 1px solid'>Output Options</td></tr>";
  161. html += `<tr><td class='wal-indent'><input type='checkbox' class='wal-check' id='${ctlPrefix}IncludeAlt' name='${ctlPrefix}IncludeAlt'>` +
  162. `<label for='${ctlPrefix}IncludeAlt' class='wal-label'>Include Alt Names</label></td></tr>`;
  163. html += "<tr><td class='wal-indent'><b>Include:</b>" +
  164. `<select id='${ctlPrefix}IncludeInOutput'>` +
  165. `<option value='${IncludeInOutput.Low}'>Locked too low</option>` +
  166. `<option value='${IncludeInOutput.High}'>Locked too high</option>` +
  167. `<option value='${(IncludeInOutput.Low | IncludeInOutput.High)}'>Locked incorrectly</option></select></td></tr>`;
  168. html += "<tr><td class='wal-heading' style='border-top: 1px solid; padding-top: 4px;'><b>Lock Levels</b></td></tr>";
  169. html += "<tr><td><table style='border-collapse: separate; border-spacing: 0px'>";
  170. html += `<tr><td>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.Street))}</td><td><select id='${ctlPrefix}Street'>` +
  171. "<option value='1' selected='selected'>1</option>" +
  172. "<option value='2'>2</option>" +
  173. "<option value='3'>3</option>" +
  174. "<option value='4'>4</option>" +
  175. "<option value='5'>5</option>" +
  176. "<option value='6'>6</option></select>" +
  177. `<br/><input id='${ctlPrefix}PlusOneWayStreet' type='checkbox' class='wal-check'/><label for='${ctlPrefix}PlusOneWayStreet' class='wal-label'>+1 for One-Way</label>` +
  178. "</td></tr>";
  179. html += `<tr><td>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.PrimaryStreet))}</td><td><select id='${ctlPrefix}PrimaryStreet'>` +
  180. "<option value='1'>1</option>" +
  181. "<option value='2' selected='selected'>2</option>" +
  182. "<option value='3'>3</option>" +
  183. "<option value='4'>4</option>" +
  184. "<option value='5'>5</option>" +
  185. "<option value='6'>6</option></select>" +
  186. `<br/><input id='${ctlPrefix}PlusOneWayPS' type='checkbox' class='wal-check'/><label for='${ctlPrefix}PlusOneWayPS' class='wal-label'>+1 for One-Way</label></td></tr>`;
  187. html += `<tr><td>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.MinorHighway))}</td><td><select id='${ctlPrefix}MinorHighway'>` +
  188. "<option value='1'>1</option>" +
  189. "<option value='2'>2</option>" +
  190. "<option value='3' selected='selected'>3</option>" +
  191. "<option value='4'>4</option>" +
  192. "<option value='5'>5</option>" +
  193. "<option value='6'>6</option></select>" +
  194. `<br/><input id='${ctlPrefix}PlusOneWayMinorH' type='checkbox' class='wal-check'/><label for='${ctlPrefix}PlusOneWayMinorH' class='wal-label'>+1 for One-Way</label></td></tr>`;
  195. html += `<tr><td>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.MajorHighway))}</td><td><select id='${ctlPrefix}MajorHighway'>` +
  196. "<option value='1'>1</option>" +
  197. "<option value='2'>2</option>" +
  198. "<option value='3'>3</option>" +
  199. "<option value='4' selected='selected'>4</option>" +
  200. "<option value='5'>5</option>" +
  201. "<option value='6'>6</option></select>" +
  202. `<br/><input id='${ctlPrefix}PlusOneWayMajorH' type='checkbox' class='wal-check'/><label for='${ctlPrefix}PlusOneWayMajorH' class='wal-label'>+1 for One-Way</label></td></tr>`;
  203. html += `<tr><td>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.Freeway))}</td><td><select id='${ctlPrefix}Freeway'>` +
  204. "<option value='1'>1</option>" +
  205. "<option value='2'>2</option>" +
  206. "<option value='3'>3</option>" +
  207. "<option value='4'>4</option>" +
  208. "<option value='5' selected='selected'>5</option>" +
  209. "<option value='6'>6</option></select>" +
  210. `<br/><input id='${ctlPrefix}PlusOneWayFW' type='checkbox' class='wal-check'/><label for='${ctlPrefix}PlusOneWayFW' class='wal-label'>+1 for One-Way</label></td></tr>`;
  211. html += `<tr><td>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.Ramp))}</td><td><select id='${ctlPrefix}Ramp'>` +
  212. "<option value='7' selected='selected'>Highest connection</option>" +
  213. "<option value='1'>1</option>" +
  214. "<option value='2'>2</option>" +
  215. "<option value='3'>3</option>" +
  216. "<option value='4'>4</option>" +
  217. "<option value='5'>5</option>" +
  218. "<option value='6'>6</option></select>" +
  219. `<br/><input id='${ctlPrefix}PlusOneWayRamp' type='checkbox' class='wal-check'/><label for='${ctlPrefix}PlusOneWayRamp' class='wal-label'>+1 for One-Way</label></td></tr>`;
  220. html += `<tr><td>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.Railroad))}</td><td><select id='${ctlPrefix}Railroad'>` +
  221. "<option value='1'>1</option>" +
  222. "<option value='2' selected='selected'>2</option>" +
  223. "<option value='3'>3</option>" +
  224. "<option value='4'>4</option>" +
  225. "<option value='5'>5</option>" +
  226. "<option value='6'>6</option></select>" +
  227. "</td></tr>";
  228. html += "</table></td></tr>";
  229. html += "<tr><td class='wal-heading' style='border-top: 1px solid; padding-top: 4px;'><b>Filters</b></td></tr>";
  230. html += "<tr><td><b>Name RegEx:</b></td></tr>";
  231. html += `<tr><td class='wal-indent'><input type='text' id='${ctlPrefix}Name' class='wal-textbox'/><br/>` +
  232. `<input id='${ctlPrefix}IgnoreCase' type='checkbox' class='wal-check'/>` +
  233. `<label for='${ctlPrefix}IgnoreCase' class='wal-label'>Ignore case</label></td></tr>`;
  234. html += "<tr><td><b>City RegEx:</b></td></tr>";
  235. html += `<tr><td class='wal-indent'><input type='text' id='${ctlPrefix}City' class='wal-textbox'/><br/>` +
  236. `<input id='${ctlPrefix}CityIgnoreCase' type='checkbox' class='wal-check'/>` +
  237. `<label for='${ctlPrefix}CityIgnoreCase' class='wal-label'>Ignore case</label></td></tr>`;
  238. html += "<tr><td><b>State:</b></td></tr>";
  239. html += "<tr><td class='wal-indent'>" +
  240. `<select id='${ctlPrefix}StateOp'>` +
  241. `<option value='${Operation.Equal}' selected='selected'>=</option>` +
  242. `<option value='${Operation.NotEqual}'>&lt;&gt;</option></select>` +
  243. `<select id='${ctlPrefix}State'></select></td></tr>`;
  244. html += "<tr><td><b>Road Type:</b></td></tr>";
  245. html += "<tr><td class='wal-indent'>" +
  246. `<button id='${ctlPrefix}RoadTypeAny' class='btn btn-primary' style='margin-right: 8px' title='Any'>Any</button>` +
  247. `<button id='${ctlPrefix}RoadTypeClear' class='btn btn-primary' title='Clear'>Clear</button><br/>` +
  248. `<input type='checkbox' class='wal-check' id='${ctlPrefix}RoadTypeStreet' name='${ctlPrefix}RoadType' value='${WMEWAL.RoadType.Street}'/>` +
  249. `<label for='${ctlPrefix}RoadTypeStreet' class='wal-label'>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.Street))}</label><br/>` +
  250. `<input type='checkbox' class='wal-check' id='${ctlPrefix}RoadTypePrimary' name='${ctlPrefix}RoadType' value='${WMEWAL.RoadType.PrimaryStreet}'/>` +
  251. `<label for='${ctlPrefix}RoadTypePrimary' class='wal-label'>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.PrimaryStreet))}</label><br/>` +
  252. `<input type='checkbox' class='wal-check' id='${ctlPrefix}RoadTypeMinorHighway' name='${ctlPrefix}RoadType' value='${WMEWAL.RoadType.MinorHighway}'/>` +
  253. `<label for='${ctlPrefix}RoadTypeMinorHighway' class='wal-label'>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.MinorHighway))}</label><br/>` +
  254. `<input type='checkbox' class='wal-check' id='${ctlPrefix}RoadTypeMajorHighway' name='${ctlPrefix}RoadType' value='${WMEWAL.RoadType.MajorHighway}'/>` +
  255. `<label for='${ctlPrefix}RoadTypeMajorHighway' class='wal-label'>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.MajorHighway))}</label><br/>` +
  256. `<input type='checkbox' class='wal-check' id='${ctlPrefix}RoadTypeRamp' name='${ctlPrefix}RoadType' value='${WMEWAL.RoadType.Ramp}'/>` +
  257. `<label for='${ctlPrefix}RoadTypeRamp' class='wal-label'>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.Ramp))}</label><br/>` +
  258. `<input type='checkbox' class='wal-check' checked='checked' id='${ctlPrefix}RoadTypeFreeway' name='${ctlPrefix}RoadType' value='${WMEWAL.RoadType.Freeway}'/>` +
  259. `<label for='${ctlPrefix}RoadTypeFreeway' class='wal-label'>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.Freeway))}</label><br/>` +
  260. `<input type='checkbox' class='wal-check' checked='checked' id='${ctlPrefix}RoadTypeRailroad' name='${ctlPrefix}RoadType' value='${WMEWAL.RoadType.Railroad}'/>` +
  261. `<label for='${ctlPrefix}RoadTypeRailroad' class='wal-label'>${WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(WMEWAL.RoadType.Railroad))}</label>` +
  262. "</td></tr>";
  263. html += `<tr><td><input id='${ctlPrefix}Editable' type='checkbox' class='wal-check'/>` +
  264. `<label for='${ctlPrefix}Editable' class='wal-label'>Editable by me</label></td></tr>`;
  265. html += `<tr><td><input id='${ctlPrefix}ExcludeRoundabouts' type='checkbox' class='wal-check'/>` +
  266. `<label for='${ctlPrefix}ExcludeRoundabouts' class='wal-label'>Exclude Roundabouts</label></td></tr>`;
  267. html += `<tr><td><input id='${ctlPrefix}ExcludeJunctionBoxes' type='checkbox' class='wal-check' checked='checked'/>` +
  268. `<label for='${ctlPrefix}ExcludeJunctionBoxes' class='wal-label'>Exclude Junction Boxes</label></td></tr>`;
  269. html += "</tbody></table>";
  270. return html;
  271. }
  272. WMEWAL_Locks.GetTab = GetTab;
  273. function TabLoaded() {
  274. loadScriptUpdateMonitor();
  275. updateStates();
  276. updateUI();
  277. updateSavedSettingsList();
  278. $(`#${ctlPrefix}State`).on("focus", updateStates);
  279. $(`#${ctlPrefix}RoadTypeAny`).on("click", function () {
  280. $(`input[name=${ctlPrefix}RoadType]`).prop("checked", true);
  281. });
  282. $(`#${ctlPrefix}RoadTypeClear`).on("click", function () {
  283. $(`input[name=${ctlPrefix}RoadType]`).prop("checked", false);
  284. });
  285. $(`#${ctlPrefix}LoadSetting`).on("click", loadSetting);
  286. $(`#${ctlPrefix}SaveSetting`).on("click", saveSetting);
  287. $(`#${ctlPrefix}DeleteSetting`).on("click", deleteSetting);
  288. }
  289. WMEWAL_Locks.TabLoaded = TabLoaded;
  290. function updateStates() {
  291. const selectState = $(`#${ctlPrefix}State`);
  292. // Preserve current selection
  293. const currentId = parseInt(selectState.val());
  294. selectState.empty();
  295. const stateObjs = [];
  296. stateObjs.push({ id: null, name: "" });
  297. for (let s in W.model.states.objects) {
  298. if (W.model.states.objects.hasOwnProperty(s)) {
  299. const st = W.model.states.getObjectById(parseInt(s));
  300. if (st.getAttribute('id') !== 1 && st.getAttribute('name').length > 0) {
  301. stateObjs.push({ id: st.getAttribute('id'), name: st.getAttribute('name') });
  302. }
  303. }
  304. }
  305. stateObjs.sort(function (a, b) {
  306. if (a.id == null) {
  307. return -1;
  308. }
  309. else {
  310. return a.name.localeCompare(b.name);
  311. }
  312. });
  313. for (let ix = 0; ix < stateObjs.length; ix++) {
  314. const so = stateObjs[ix];
  315. const stateOption = $("<option/>").text(so.name).attr("value", so.id);
  316. if (currentId != null && so.id === currentId) {
  317. stateOption.attr("selected", "selected");
  318. }
  319. selectState.append(stateOption);
  320. }
  321. }
  322. function updateSavedSettingsList() {
  323. const s = $(`#${ctlPrefix}SavedSettings`);
  324. s.empty();
  325. for (let ixSaved = 0; ixSaved < savedSettings.length; ixSaved++) {
  326. const opt = $("<option/>").attr("value", ixSaved).text(savedSettings[ixSaved].Name);
  327. s.append(opt);
  328. }
  329. }
  330. function updateUI() {
  331. // $(`#${ctlPrefix}OutputTo`).val(settings.OutputTo);
  332. $(`#${ctlPrefix}IncludeInOutput`).val(settings.IncludeInOutput);
  333. $(`#${ctlPrefix}Street`).val(settings.StreetLockLevel);
  334. $(`#${ctlPrefix}PrimaryStreet`).val(settings.PrimaryStreetLockLevel);
  335. $(`#${ctlPrefix}MinorHighway`).val(settings.MinorHighwayLockLevel);
  336. $(`#${ctlPrefix}MajorHighway`).val(settings.MajorHighwayLockLevel);
  337. $(`#${ctlPrefix}Freeway`).val(settings.FreewayLockLevel);
  338. $(`#${ctlPrefix}Ramp`).val(settings.RampLockLevel);
  339. $(`#${ctlPrefix}Railroad`).val(settings.RailroadLockLevel);
  340. $(`#${ctlPrefix}Name`).val(settings.Regex || "");
  341. $(`#${ctlPrefix}IgnoreCase`).prop("checked", settings.RegexIgnoreCase);
  342. $(`#${ctlPrefix}City`).val(settings.CityRegex || "");
  343. $(`#${ctlPrefix}CityIgnoreCase`).prop("checked", settings.CityRegexIgnoreCase);
  344. $(`#${ctlPrefix}State`).val(settings.State);
  345. $(`#${ctlPrefix}RoadTypeStreet`).prop("checked", settings.RoadTypeMask & WMEWAL.RoadType.Street);
  346. $(`#${ctlPrefix}RoadTypePrimary`).prop("checked", settings.RoadTypeMask & WMEWAL.RoadType.PrimaryStreet);
  347. $(`#${ctlPrefix}RoadTypeMinorHighway`).prop("checked", settings.RoadTypeMask & WMEWAL.RoadType.MinorHighway);
  348. $(`#${ctlPrefix}RoadTypeMajorHighway`).prop("checked", settings.RoadTypeMask & WMEWAL.RoadType.MajorHighway);
  349. $(`#${ctlPrefix}RoadTypeRamp`).prop("checked", settings.RoadTypeMask & WMEWAL.RoadType.Ramp);
  350. $(`#${ctlPrefix}RoadTypeFreeway`).prop("checked", settings.RoadTypeMask & WMEWAL.RoadType.Freeway);
  351. $(`#${ctlPrefix}RoadTypeRailroad`).prop("checked", settings.RoadTypeMask & WMEWAL.RoadType.Railroad);
  352. $(`#${ctlPrefix}Editable`).prop("checked", settings.EditableByMe);
  353. $(`#${ctlPrefix}ExcludeRoundabouts`).prop("checked", settings.ExcludeRoundabouts);
  354. $(`#${ctlPrefix}ExcludeJunctionBoxes`).prop("checked", settings.ExcludeJunctionBoxes);
  355. $(`#${ctlPrefix}PlusOneWayStreet`).prop("checked", settings.PlusOneWayMask & WMEWAL.RoadType.Street);
  356. $(`#${ctlPrefix}PlusOneWayPS`).prop("checked", settings.PlusOneWayMask & WMEWAL.RoadType.PrimaryStreet);
  357. $(`#${ctlPrefix}PlusOneWayMinorH`).prop("checked", settings.PlusOneWayMask & WMEWAL.RoadType.MinorHighway);
  358. $(`#${ctlPrefix}PlusOneWayMajorH`).prop("checked", settings.PlusOneWayMask & WMEWAL.RoadType.MajorHighway);
  359. $(`#${ctlPrefix}PlusOneWayFW`).prop("checked", settings.PlusOneWayMask & WMEWAL.RoadType.Freeway);
  360. $(`#${ctlPrefix}PlusOneWayRamp`).prop("checked", settings.PlusOneWayMask & WMEWAL.RoadType.Ramp);
  361. $(`#${ctlPrefix}StateOp`).val(settings.StateOperation || Operation.Equal.toString());
  362. $(`#${ctlPrefix}IncludeAlt`).prop("checked", settings.IncludeAltNames);
  363. }
  364. function loadSetting() {
  365. const selectedSetting = parseInt($(`#${ctlPrefix}SavedSettings`).val());
  366. if (selectedSetting == null || isNaN(selectedSetting) || selectedSetting < 0 || selectedSetting > savedSettings.length) {
  367. return;
  368. }
  369. const savedSetting = savedSettings[selectedSetting].Setting;
  370. // settings.OutputTo = $(`#${ctlPrefix}OutputTo`).val();
  371. for (let name in savedSetting) {
  372. if (settings.hasOwnProperty(name)) {
  373. settings[name] = savedSetting[name];
  374. }
  375. }
  376. updateUI();
  377. }
  378. function validateSettings() {
  379. function addMessage(error) {
  380. message += ((message.length > 0 ? "\n" : "") + error);
  381. }
  382. let message = "";
  383. const s = getSettings();
  384. let mask = 0;
  385. $(`input[name=${ctlPrefix}RoadType]:checked`).each(function (ix, e) {
  386. mask = mask | parseInt(e.value);
  387. });
  388. if (mask === 0) {
  389. addMessage("Please select at least one road type.");
  390. }
  391. const selectedState = $(`#${ctlPrefix}State`).val();
  392. if (nullif(selectedState, "") !== null && s.State === null) {
  393. addMessage("Invalid state selection");
  394. }
  395. let r;
  396. if (nullif(s.Regex, "") !== null) {
  397. try {
  398. r = (s.RegexIgnoreCase ? new RegExp(s.Regex, "i") : new RegExp(s.Regex));
  399. }
  400. catch (error) {
  401. addMessage("Name RegEx is invalid");
  402. }
  403. }
  404. if (nullif(s.CityRegex, "") !== null) {
  405. try {
  406. r = (s.CityRegexIgnoreCase ? new RegExp(s.CityRegex, "i") : new RegExp(s.CityRegex));
  407. }
  408. catch (error) {
  409. addMessage("City RegEx is invalid");
  410. }
  411. }
  412. if (message.length > 0) {
  413. alert(pluginName + ": " + message);
  414. return false;
  415. }
  416. return true;
  417. }
  418. function saveSetting() {
  419. if (validateSettings()) {
  420. const s = getSettings();
  421. const sName = prompt("Enter a name for this setting");
  422. if (sName == null) {
  423. return;
  424. }
  425. // Check to see if there is already a name that matches this
  426. for (let ixSetting = 0; ixSetting < savedSettings.length; ixSetting++) {
  427. if (savedSettings[ixSetting].Name === sName) {
  428. if (confirm("A setting with this name already exists. Overwrite?")) {
  429. savedSettings[ixSetting].Setting = s;
  430. updateSavedSettings();
  431. }
  432. else {
  433. alert("Please pick a new name.");
  434. }
  435. return;
  436. }
  437. }
  438. const savedSetting = {
  439. Name: sName,
  440. Setting: s
  441. };
  442. savedSettings.push(savedSetting);
  443. updateSavedSettings();
  444. }
  445. }
  446. function getSettings() {
  447. const s = {
  448. RoadTypeMask: null,
  449. State: null,
  450. Regex: null,
  451. RegexIgnoreCase: $(`#${ctlPrefix}IgnoreCase`).prop("checked"),
  452. ExcludeJunctionBoxes: $(`#${ctlPrefix}ExcludeJunctionBoxes`).prop("checked"),
  453. ExcludeRoundabouts: $(`#${ctlPrefix}ExcludeRoundabouts`).prop("checked"),
  454. EditableByMe: $(`#${ctlPrefix}Editable`).prop("checked"),
  455. StreetLockLevel: parseInt($(`#${ctlPrefix}Street`).val()),
  456. PrimaryStreetLockLevel: parseInt($(`#${ctlPrefix}PrimaryStreet`).val()),
  457. MinorHighwayLockLevel: parseInt($(`#${ctlPrefix}MinorHighway`).val()),
  458. MajorHighwayLockLevel: parseInt($(`#${ctlPrefix}MajorHighway`).val()),
  459. FreewayLockLevel: parseInt($(`#${ctlPrefix}Freeway`).val()),
  460. RampLockLevel: parseInt($(`#${ctlPrefix}Ramp`).val()),
  461. IncludeInOutput: parseInt($(`#${ctlPrefix}IncludeInOutput`).val()),
  462. PlusOneWayMask: 0,
  463. CityRegex: null,
  464. CityRegexIgnoreCase: $(`#${ctlPrefix}CityIgnoreCase`).prop("checked"),
  465. StateOperation: parseInt($(`#${ctlPrefix}StateOp`).val()),
  466. RailroadLockLevel: parseInt($(`#${ctlPrefix}Railroad`).val()),
  467. IncludeAltNames: $(`#${ctlPrefix}IncludeAlt`).prop("checked")
  468. };
  469. s.RoadTypeMask = 0;
  470. $(`input[name=${ctlPrefix}RoadType]:checked`).each(function (ix, e) {
  471. s.RoadTypeMask = s.RoadTypeMask | parseInt(e.value);
  472. });
  473. if ($(`#${ctlPrefix}PlusOneWayStreet`).prop("checked")) {
  474. s.PlusOneWayMask = s.PlusOneWayMask | WMEWAL.RoadType.Street;
  475. }
  476. if ($(`#${ctlPrefix}PlusOneWayPS`).prop("checked")) {
  477. s.PlusOneWayMask = s.PlusOneWayMask | WMEWAL.RoadType.PrimaryStreet;
  478. }
  479. if ($(`#${ctlPrefix}PlusOneWayMinorH`).prop("checked")) {
  480. s.PlusOneWayMask = s.PlusOneWayMask | WMEWAL.RoadType.MinorHighway;
  481. }
  482. if ($(`#${ctlPrefix}PlusOneWayMajorH`).prop("checked")) {
  483. s.PlusOneWayMask = s.PlusOneWayMask | WMEWAL.RoadType.MajorHighway;
  484. }
  485. if ($(`#${ctlPrefix}PlusOneWayFW`).prop("checked")) {
  486. s.PlusOneWayMask = s.PlusOneWayMask | WMEWAL.RoadType.Freeway;
  487. }
  488. if ($(`#${ctlPrefix}PlusOneWayRamp`).prop("checked")) {
  489. s.PlusOneWayMask = s.PlusOneWayMask | WMEWAL.RoadType.Ramp;
  490. }
  491. const selectedState = $(`#${ctlPrefix}State`).val();
  492. if (nullif(selectedState, "") !== null) {
  493. const state = W.model.states.getObjectById(parseInt(selectedState));
  494. if (state !== null) {
  495. s.State = state.getID();
  496. }
  497. }
  498. let pattern = $(`#${ctlPrefix}Name`).val();
  499. if (nullif(pattern, "") !== null) {
  500. s.Regex = pattern;
  501. }
  502. pattern = $(`#${ctlPrefix}City`).val();
  503. if (nullif(pattern, "") !== null) {
  504. s.CityRegex = pattern;
  505. }
  506. return s;
  507. }
  508. function deleteSetting() {
  509. const selectedSetting = parseInt($(`#${ctlPrefix}SavedSettings`).val());
  510. if (selectedSetting == null || isNaN(selectedSetting) || selectedSetting < 0 || selectedSetting > savedSettings.length) {
  511. return;
  512. }
  513. if (confirm("Are you sure you want to delete this saved setting?")) {
  514. savedSettings.splice(selectedSetting, 1);
  515. updateSavedSettings();
  516. }
  517. }
  518. function ScanStarted() {
  519. let allOk = validateSettings();
  520. streets = [];
  521. savedSegments = [];
  522. if (allOk) {
  523. settings = getSettings();
  524. if (settings.State !== null) {
  525. state = W.model.states.getObjectById(settings.State);
  526. stateName = state.getAttribute('name');
  527. }
  528. else {
  529. state = null;
  530. stateName = null;
  531. }
  532. if (settings.Regex !== null) {
  533. nameRegex = (settings.RegexIgnoreCase ? new RegExp(settings.Regex, "i") : new RegExp(settings.Regex));
  534. }
  535. else {
  536. nameRegex = null;
  537. }
  538. if (settings.CityRegex !== null) {
  539. cityRegex = (settings.CityRegexIgnoreCase ? new RegExp(settings.CityRegex, "i") : new RegExp(settings.CityRegex));
  540. }
  541. else {
  542. cityRegex = null;
  543. }
  544. if (settings.RoadTypeMask & 1 || settings.RoadTypeMask & 4096) {
  545. WMEWAL_Locks.MinimumZoomLevel = 16;
  546. }
  547. else {
  548. WMEWAL_Locks.MinimumZoomLevel = 14;
  549. }
  550. updateSettings();
  551. }
  552. return allOk;
  553. }
  554. WMEWAL_Locks.ScanStarted = ScanStarted;
  555. function isOneWay(segment) {
  556. return segment.getAttribute('fwdDirection') !== segment.getAttribute('revDirection') && (segment.getAttribute('fwdDirection') || segment.getAttribute('revDirection'));
  557. }
  558. function ScanExtent(segments, venues) {
  559. return new Promise(resolve => {
  560. setTimeout(function () {
  561. const count = scan(segments);
  562. resolve({ ID: 'Lock', count });
  563. });
  564. });
  565. }
  566. WMEWAL_Locks.ScanExtent = ScanExtent;
  567. function scan(segments) {
  568. const extentStreets = [];
  569. function addSegment(s, rId) {
  570. if (savedSegments.indexOf(s.getID()) === -1) {
  571. savedSegments.push(s.getID());
  572. const sid = s.getAttribute('primaryStreetID');
  573. const address = s.getAddress(W.model);
  574. let thisStreet = null;
  575. if (sid != null) {
  576. // let street = W.model.streets.getObjectById(sid);
  577. thisStreet = extentStreets.find(function (e) {
  578. let matches = (e.id === sid && (e.lockLevel === (s.getAttribute('lockRank') || 0) + 1) && e.roundaboutId === rId && e.roadType === s.getAttribute('roadType'));
  579. if (matches && (nameRegex != null || cityRegex != null)) {
  580. // Test for alt names
  581. for (let ixAlt = 0; ixAlt < e.altStreets.length && matches; ixAlt++) {
  582. matches = false;
  583. for (let ixSegAlt = 0; ixSegAlt < address.attributes.altStreets.length && !matches; ixSegAlt++) {
  584. //if (e.altStreets[ixAlt].id === address.attributes.altStreets[ixSegAlt].getAttribute('id')) {
  585. if (e.altStreets[ixAlt].id === address.getStreet().getID()) {
  586. matches = true;
  587. }
  588. }
  589. }
  590. }
  591. return matches;
  592. });
  593. }
  594. if (thisStreet == null) {
  595. thisStreet = {
  596. id: sid,
  597. city: ((address && !address.attributes.isEmpty && address.attributes.city.hasName()) ? address.attributes.city.getAttribute('name') : "No City"),
  598. state: ((address && !address.attributes.isEmpty) ? address.attributes.state.getAttribute('name') : "No State"),
  599. name: ((address && !address.attributes.isEmpty && !address.attributes.street.getAttribute('isEmpty')) ? address.attributes.street.getAttribute('name') : "No street"),
  600. geometries: new OpenLayers.Geometry.Collection(),
  601. lockLevel: (s.getAttribute('lockRank') || 0) + 1,
  602. segments: [],
  603. roundaboutId: rId,
  604. altStreets: [],
  605. roadType: s.getAttribute('roadType')
  606. };
  607. if (settings.IncludeAltNames) {
  608. if (s.getAttribute('streetIDs') != null) {
  609. for (let ixAlt = 0; ixAlt < s.getAttribute('streetIDs').length; ixAlt++) {
  610. if (s.getAttribute('streetIDs')[ixAlt] != null) {
  611. const altStreet = W.model.streets.getObjectById(s.getAttribute('streetIDs')[ixAlt]);
  612. if (altStreet != null) {
  613. let altCityName = null;
  614. if (altStreet.getAttribute('cityID') != null) {
  615. const altCity = W.model.cities.getObjectById(altStreet.getAttribute('cityID'));
  616. if (altCity != null) {
  617. altCityName = altCity.hasName() ? altCity.getAttribute('name') : "No city";
  618. }
  619. }
  620. thisStreet.altStreets.push({
  621. id: s.getAttribute('streetIDs')[ixAlt],
  622. name: altStreet.getAttribute('name'),
  623. city: altCityName
  624. });
  625. }
  626. }
  627. }
  628. }
  629. }
  630. extentStreets.push(thisStreet);
  631. }
  632. thisStreet.segments.push({
  633. id: s.getAttribute('id'),
  634. center: s.getAttribute('geometry').getCentroid()
  635. });
  636. thisStreet.geometries.addComponents([s.getAttribute('geometry').clone()]);
  637. }
  638. }
  639. for (let ix = 0; ix < segments.length; ix++) {
  640. const segment = segments[ix];
  641. if (segment != null) {
  642. if ((WMEWAL.WazeRoadTypeToRoadTypeBitmask(segment.getAttribute('roadType')) & settings.RoadTypeMask) &&
  643. (!settings.EditableByMe || segment.arePropertiesEditable()) &&
  644. (!settings.ExcludeJunctionBoxes || !segment.isInBigJunction())) {
  645. const address = segment.getAddress(W.model);
  646. if (state != null) {
  647. if (address != null && address.attributes != null && !address.attributes.isEmpty && address.attributes.state != null) {
  648. if (settings.StateOperation === Operation.Equal && address.attributes.state.getAttribute('id') !== state.getAttribute('id') ||
  649. settings.StateOperation === Operation.NotEqual && address.attributes.state.getAttribute('id') === state.getAttribute('id')) {
  650. continue;
  651. }
  652. }
  653. else if (settings.StateOperation === Operation.Equal) {
  654. continue;
  655. }
  656. }
  657. const plusOne = (isOneWay(segment) && (WMEWAL.WazeRoadTypeToRoadTypeBitmask(segment.getAttribute('roadType')) & settings.PlusOneWayMask)) ? 1 : 0;
  658. let incorrectLock = false;
  659. let expectedLockRank = 0;
  660. switch (segment.getAttribute('roadType')) {
  661. case 1:
  662. if ((settings.IncludeInOutput & IncludeInOutput.Low && (segment.getAttribute('lockRank') || 0) + 1 < settings.StreetLockLevel + plusOne) ||
  663. (settings.IncludeInOutput & IncludeInOutput.High && (segment.getAttribute('lockRank') || 0) + 1 > settings.StreetLockLevel + plusOne)) {
  664. incorrectLock = true;
  665. }
  666. break;
  667. case 2:
  668. if ((settings.IncludeInOutput & IncludeInOutput.Low && (segment.getAttribute('lockRank') || 0) + 1 < settings.PrimaryStreetLockLevel + plusOne) ||
  669. (settings.IncludeInOutput & IncludeInOutput.High && (segment.getAttribute('lockRank') || 0) + 1 > settings.PrimaryStreetLockLevel + plusOne)) {
  670. incorrectLock = true;
  671. }
  672. break;
  673. case 3:
  674. if ((settings.IncludeInOutput & IncludeInOutput.Low && (segment.getAttribute('lockRank') || 0) + 1 < settings.FreewayLockLevel + plusOne) ||
  675. (settings.IncludeInOutput & IncludeInOutput.High && (segment.getAttribute('lockRank') || 0) + 1 > settings.FreewayLockLevel + plusOne)) {
  676. incorrectLock = true;
  677. }
  678. break;
  679. case 4:
  680. expectedLockRank = 0;
  681. if (settings.RampLockLevel === 7) {
  682. // Find lock rank of every connected segment
  683. const fromSegments = segment.getConnectedSegments(W.model, "from");
  684. for (let ix = 0; ix < fromSegments.length; ix++) {
  685. if (fromSegments[ix].getAttribute('id') !== segment.getAttribute('id') && (fromSegments[ix].getAttribute('lockRank') || 0) + 1 > expectedLockRank) {
  686. expectedLockRank = (fromSegments[ix].getAttribute('lockRank') || 0) + 1;
  687. }
  688. }
  689. const toSegments = segment.getConnectedSegments(W.model, "to");
  690. for (let ix = 0; ix < toSegments.length; ix++) {
  691. if (toSegments[ix].getAttribute('id') !== segment.getAttribute('id') && (toSegments[ix].getAttribute('lockRank') || 0) + 1 > expectedLockRank) {
  692. expectedLockRank = (toSegments[ix].getAttribute('lockRank') || 0) + 1;
  693. }
  694. }
  695. }
  696. else {
  697. expectedLockRank = settings.RampLockLevel;
  698. }
  699. expectedLockRank += plusOne;
  700. if ((settings.IncludeInOutput & IncludeInOutput.Low && (segment.getAttribute('lockRank') || 0) + 1 < expectedLockRank) ||
  701. (settings.IncludeInOutput & IncludeInOutput.High && (segment.getAttribute('lockRank') || 0) + 1 > expectedLockRank)) {
  702. incorrectLock = true;
  703. }
  704. break;
  705. case 6:
  706. if ((settings.IncludeInOutput & IncludeInOutput.Low && (segment.getAttribute('lockRank') || 0) + 1 < settings.MajorHighwayLockLevel + plusOne) ||
  707. (settings.IncludeInOutput & IncludeInOutput.High && (segment.getAttribute('lockRank') || 0) + 1 > settings.MajorHighwayLockLevel + plusOne)) {
  708. incorrectLock = true;
  709. }
  710. break;
  711. case 7:
  712. if ((settings.IncludeInOutput & IncludeInOutput.Low && (segment.getAttribute('lockRank') || 0) + 1 < settings.MinorHighwayLockLevel + plusOne) ||
  713. (settings.IncludeInOutput & IncludeInOutput.High && (segment.getAttribute('lockRank') || 0) + 1 > settings.MinorHighwayLockLevel + plusOne)) {
  714. incorrectLock = true;
  715. }
  716. break;
  717. case 18:
  718. if ((settings.IncludeInOutput & IncludeInOutput.Low && (segment.getAttribute('lockRank') || 0) + 1 < settings.RailroadLockLevel + plusOne) ||
  719. (settings.IncludeInOutput & IncludeInOutput.High && (segment.getAttribute('lockRank') || 0) + 1 > settings.RailroadLockLevel + plusOne)) {
  720. incorrectLock = true;
  721. }
  722. break;
  723. default:
  724. break;
  725. }
  726. if (!incorrectLock) {
  727. continue;
  728. }
  729. if (nameRegex != null || cityRegex != null) {
  730. let nameMatched = false;
  731. if (address != null && address.attributes != null && !address.attributes.isEmpty) {
  732. if (nameRegex != null && address.attributes.street != null) {
  733. nameMatched = nameRegex.test(address.attributes.street.getAttribute('name'));
  734. }
  735. if (!nameMatched && cityRegex != null && address.attributes.city != null && address.attributes.city.hasName()) {
  736. nameMatched = cityRegex.test(address.attributes.city.getAttribute('name'));
  737. }
  738. if (!nameMatched && segment.getAttribute('streetIDs') != null) {
  739. for (let streetIx = 0; streetIx < segment.getAttribute('streetIDs').length && !nameMatched; streetIx++) {
  740. if (segment.getAttribute('streetIDs')[streetIx] != null) {
  741. const street = W.model.streets.getObjectById(segment.getAttribute('streetIDs')[streetIx]);
  742. if (street != null) {
  743. if (nameRegex != null) {
  744. nameMatched = nameRegex.test(street.getAttribute('name'));
  745. }
  746. if (!nameMatched && cityRegex != null && street.getAttribute('cityID') != null) {
  747. const city = W.model.cities.getObjectById(street.getAttribute('cityID'));
  748. if (city != null && city.hasName()) {
  749. nameMatched = cityRegex.test(city.getAttribute('name'));
  750. }
  751. }
  752. }
  753. }
  754. }
  755. }
  756. }
  757. if (!nameMatched) {
  758. continue;
  759. }
  760. }
  761. if (!WMEWAL.IsSegmentInArea(segment)) {
  762. continue;
  763. }
  764. if (!segment.isInRoundabout()) {
  765. addSegment(segment, null);
  766. }
  767. else if (!settings.ExcludeRoundabouts) {
  768. const r = segment.getRoundabout().attributes;
  769. for (let rIx = 0; rIx < r.segIDs.length; rIx++) {
  770. addSegment(W.model.segments.getObjectById(r.segIDs[rIx]), r.id);
  771. }
  772. }
  773. }
  774. }
  775. }
  776. for (let ix = 0; ix < extentStreets.length; ix++) {
  777. extentStreets[ix].center = extentStreets[ix].geometries.getCentroid(true);
  778. delete extentStreets[ix].geometries;
  779. streets.push(extentStreets[ix]);
  780. }
  781. return streets.length;
  782. }
  783. function ScanComplete() {
  784. if (streets.length === 0) {
  785. alert(pluginName + ": No streets found.");
  786. }
  787. else {
  788. streets.sort(function (a, b) {
  789. let cmp = getStreetName(a).localeCompare(getStreetName(b));
  790. if (cmp !== 0) {
  791. return cmp;
  792. }
  793. cmp = a.state.localeCompare(b.state);
  794. if (cmp !== 0) {
  795. return cmp;
  796. }
  797. cmp = a.city.localeCompare(b.city);
  798. if (cmp !== 0) {
  799. return cmp;
  800. }
  801. if (a.lockLevel < b.lockLevel) {
  802. return -1;
  803. }
  804. else if (a.lockLevel > b.lockLevel) {
  805. return 1;
  806. }
  807. return 0;
  808. });
  809. const isCSV = (WMEWAL.outputTo & WMEWAL.OutputTo.CSV);
  810. const isTab = (WMEWAL.outputTo & WMEWAL.OutputTo.Tab);
  811. const addBOM = WMEWAL.addBOM ?? false;
  812. const outputFields = WMEWAL.outputFields ?? ['CreatedEditor', 'LastEditor', 'LockLevel', 'Lat', 'Lon'];
  813. const includeLat = outputFields.indexOf('Lat') > -1;
  814. const includeLon = outputFields.indexOf('Lon') > -1;
  815. let lineArray;
  816. let columnArray;
  817. let w;
  818. let fileName;
  819. if (isCSV) {
  820. lineArray = [];
  821. columnArray = ["Name"];
  822. if (settings.IncludeAltNames) {
  823. columnArray.push("Alt Names");
  824. }
  825. columnArray.push('City', 'State', 'Road Type', 'Lock Level');
  826. if (includeLat) {
  827. columnArray.push("Latitude");
  828. }
  829. if (includeLon) {
  830. columnArray.push("Longitude");
  831. }
  832. columnArray.push("Permalink");
  833. lineArray.push(columnArray);
  834. fileName = "Locks_" + WMEWAL.areaName;
  835. for (let rt in WMEWAL.RoadType) {
  836. if (WMEWAL.RoadType.hasOwnProperty(rt)) {
  837. const mask = parseInt(rt);
  838. if (!isNaN(mask) && settings.RoadTypeMask & mask) {
  839. fileName += "_" + WMEWAL.RoadType[mask.toString()];
  840. }
  841. }
  842. }
  843. fileName += ".csv";
  844. }
  845. if (isTab) {
  846. w = window.open();
  847. w.document.write("<html><head><title>Locks</title></head><body>");
  848. w.document.write("<h3>Area: " + WMEWAL.areaName + "</h3>");
  849. w.document.write("<b>Filters</b>");
  850. w.document.write("<br/>Road Type(s): ");
  851. let comma = "";
  852. for (let rt in WMEWAL.RoadType) {
  853. if (WMEWAL.RoadType.hasOwnProperty(rt)) {
  854. const mask = parseInt(rt);
  855. if (!isNaN(mask) && settings.RoadTypeMask & mask) {
  856. w.document.write(comma + WMEWAL.TranslateRoadType(WMEWAL.RoadTypeBitmaskToWazeRoadType(mask)));
  857. if (settings.PlusOneWayMask & mask) {
  858. w.document.write(" (+1 for one-way)");
  859. }
  860. comma = ", ";
  861. }
  862. }
  863. }
  864. if (stateName != null) {
  865. w.document.write("<br/>State " + (settings.StateOperation === Operation.NotEqual ? "does not equal " : "equals ") + stateName);
  866. }
  867. if (nameRegex != null) {
  868. w.document.write("<br/>Name matches " + nameRegex.source);
  869. if (settings.RegexIgnoreCase) {
  870. w.document.write(" (ignoring case)");
  871. }
  872. }
  873. if (cityRegex != null) {
  874. w.document.write("</br/>City name matches " + cityRegex.source);
  875. if (settings.CityRegexIgnoreCase) {
  876. w.document.write(" (ignoring case)");
  877. }
  878. }
  879. if (settings.ExcludeRoundabouts) {
  880. w.document.write("<br/>Roundabouts excluded");
  881. }
  882. if (settings.ExcludeJunctionBoxes) {
  883. w.document.write("<br/>Junction boxes excluded");
  884. }
  885. if (settings.EditableByMe) {
  886. w.document.write("<br/>Editable by me");
  887. }
  888. w.document.write("</p><table style='border-collapse: separate; border-spacing: 8px 0px'><tr><th>Name</th>");
  889. if (settings.IncludeAltNames) {
  890. w.document.write("<th>Alt Names</th>");
  891. }
  892. w.document.write("<th>City</th><th>State</th><th>Road Type</th><th>Lock Level</th>");
  893. if (includeLat) {
  894. w.document.write("<th>Latitude</th>");
  895. }
  896. if (includeLon) {
  897. w.document.write("<th>Longitude</th>");
  898. }
  899. w.document.write("<th>Permalink</th></tr>");
  900. }
  901. for (let ixStreet = 0; ixStreet < streets.length; ixStreet++) {
  902. const street = streets[ixStreet];
  903. const roadTypeText = WMEWAL.TranslateRoadType(street.roadType);
  904. if (street.name == null && street.roundaboutId == null) {
  905. for (let ixSeg = 0; ixSeg < street.segments.length; ixSeg++) {
  906. const segment = street.segments[ixSeg];
  907. const latlon = OpenLayers.Layer.SphericalMercator.inverseMercator(segment.center.x, segment.center.y);
  908. const plSeg = getSegmentPL(segment);
  909. if (isCSV) {
  910. columnArray = [getStreetName(street)];
  911. if (settings.IncludeAltNames) {
  912. columnArray.push("");
  913. }
  914. columnArray.push(`"${street.city}"`);
  915. columnArray.push(`"${street.state}"`);
  916. columnArray.push(`"${roadTypeText}"`);
  917. columnArray.push(street.lockLevel.toString());
  918. if (includeLat) {
  919. columnArray.push(latlon.lat.toString());
  920. }
  921. if (includeLon) {
  922. columnArray.push(latlon.lon.toString());
  923. }
  924. columnArray.push(`"${plSeg}"`);
  925. lineArray.push(columnArray);
  926. }
  927. if (isTab) {
  928. w.document.write(`<tr><td>${getStreetName(street)}</td>`);
  929. if (settings.IncludeAltNames) {
  930. w.document.write("<td>&nbsp;</td>");
  931. }
  932. w.document.write(`<td>${street.city}</td>`);
  933. w.document.write(`<td>${street.state}</td>`);
  934. w.document.write(`<td>${roadTypeText}</td><td>${street.lockLevel}</td>`);
  935. if (includeLat) {
  936. w.document.write(`<td>${latlon.lat.toString()}</td>`);
  937. }
  938. if (includeLon) {
  939. w.document.write(`<td>${latlon.lon.toString()}</td>`);
  940. }
  941. w.document.write(`<td><a href='${plSeg}' target='_blank'>Permalink</a></td></tr>`);
  942. }
  943. }
  944. }
  945. else {
  946. const latlon = OpenLayers.Layer.SphericalMercator.inverseMercator(street.center.x, street.center.y);
  947. const plStreet = getStreetPL(street);
  948. let altNames = "";
  949. for (let ixAlt = 0; ixAlt < street.altStreets.length; ixAlt++) {
  950. if (ixAlt > 0) {
  951. altNames += "; ";
  952. }
  953. altNames += street.altStreets[ixAlt].name;
  954. if (street.altStreets[ixAlt].city != null) {
  955. altNames += ", " + street.altStreets[ixAlt].city;
  956. }
  957. }
  958. if (isCSV) {
  959. columnArray = [`"${getStreetName(street)}"`];
  960. if (settings.IncludeAltNames) {
  961. columnArray.push(`"${altNames}"`);
  962. }
  963. columnArray.push(`"${street.city}"`, `"${street.state}"`, `"${roadTypeText}"`, street.lockLevel.toString());
  964. if (includeLat) {
  965. columnArray.push(latlon.lat.toString());
  966. }
  967. if (includeLon) {
  968. columnArray.push(latlon.lon.toString());
  969. }
  970. columnArray.push(`"${plStreet}"`);
  971. lineArray.push(columnArray);
  972. }
  973. if (isTab) {
  974. w.document.write(`<tr><td>${getStreetName(street)}</td>`);
  975. if (settings.IncludeAltNames) {
  976. w.document.write(`<td>${altNames}</td>`);
  977. }
  978. w.document.write(`<td>${street.city}</td><td>${street.state}</td><td>${roadTypeText}</td><td>${street.lockLevel}</td>`);
  979. if (includeLat) {
  980. w.document.write(`<td>${latlon.lat.toString()}</td>`);
  981. }
  982. if (includeLon) {
  983. w.document.write(`<td>${latlon.lon.toString()}</td>`);
  984. }
  985. w.document.write(`<td><a href='${plStreet}' target='_blank'>Permalink</a></td></tr>`);
  986. }
  987. }
  988. }
  989. if (isCSV) {
  990. const csvContent = lineArray.join("\n") + "\n" + WMEWAL.getErrCsvText();
  991. const blobContent = [];
  992. if (addBOM) {
  993. blobContent.push('\uFEFF');
  994. }
  995. blobContent.push(csvContent);
  996. const blob = new Blob(blobContent, { type: "data:text/csv;charset=utf-8" });
  997. const link = document.createElement("a");
  998. const url = URL.createObjectURL(blob);
  999. link.setAttribute("href", url);
  1000. link.setAttribute("download", fileName);
  1001. const node = document.body.appendChild(link);
  1002. link.click();
  1003. document.body.removeChild(node);
  1004. }
  1005. if (isTab) {
  1006. WMEWAL.writeErrText(w);
  1007. w.document.write("</table></body></html>");
  1008. w.document.close();
  1009. w = null;
  1010. }
  1011. }
  1012. savedSegments = null;
  1013. streets = null;
  1014. }
  1015. WMEWAL_Locks.ScanComplete = ScanComplete;
  1016. function ScanCancelled() {
  1017. ScanComplete();
  1018. }
  1019. WMEWAL_Locks.ScanCancelled = ScanCancelled;
  1020. function getStreetPL(street) {
  1021. const latlon = OpenLayers.Layer.SphericalMercator.inverseMercator(street.center.x, street.center.y);
  1022. let url = WMEWAL.GenerateBasePL(latlon.lat, latlon.lon, WMEWAL.zoomLevel) + "&segments=";
  1023. for (let ix = 0; ix < street.segments.length; ix++) {
  1024. if (ix > 0) {
  1025. url += ",";
  1026. }
  1027. url += street.segments[ix].id;
  1028. }
  1029. return url;
  1030. }
  1031. function getSegmentPL(segment) {
  1032. const latlon = OpenLayers.Layer.SphericalMercator.inverseMercator(segment.center.x, segment.center.y);
  1033. return WMEWAL.GenerateBasePL(latlon.lat, latlon.lon, 5) + segment.id;
  1034. }
  1035. function getStreetName(street) {
  1036. return street.name || "No street";
  1037. }
  1038. function updateProperties() {
  1039. let upd = false;
  1040. if (settings !== null) {
  1041. if (!settings.hasOwnProperty("RailroadLockLevel")) {
  1042. settings.RailroadLockLevel = 2;
  1043. upd = true;
  1044. }
  1045. if (!settings.hasOwnProperty("IncludeAltNames")) {
  1046. settings.IncludeAltNames = false;
  1047. upd = true;
  1048. }
  1049. if (settings.hasOwnProperty("OutputTo")) {
  1050. delete settings["OutputTo"];
  1051. upd = true;
  1052. }
  1053. if (settings.hasOwnProperty("Version")) {
  1054. delete settings["Version"];
  1055. upd = true;
  1056. }
  1057. }
  1058. return upd;
  1059. }
  1060. function updateSavedSettings() {
  1061. if (typeof Storage !== "undefined") {
  1062. localStorage[savedSettingsKey] = WMEWAL.LZString.compressToUTF16(JSON.stringify(savedSettings));
  1063. }
  1064. updateSavedSettingsList();
  1065. }
  1066. function updateSettings() {
  1067. if (typeof Storage !== "undefined") {
  1068. localStorage[settingsKey] = JSON.stringify(settings);
  1069. }
  1070. }
  1071. function log(level, ...args) {
  1072. switch (level.toLocaleLowerCase()) {
  1073. case "debug":
  1074. case "verbose":
  1075. console.debug(`${SCRIPT_NAME}:`, ...args);
  1076. break;
  1077. case "info":
  1078. case "information":
  1079. console.info(`${SCRIPT_NAME}:`, ...args);
  1080. break;
  1081. case "warning":
  1082. case "warn":
  1083. console.warn(`${SCRIPT_NAME}:`, ...args);
  1084. break;
  1085. case "error":
  1086. console.error(`${SCRIPT_NAME}:`, ...args);
  1087. break;
  1088. case "log":
  1089. console.log(`${SCRIPT_NAME}:`, ...args);
  1090. break;
  1091. default:
  1092. break;
  1093. }
  1094. }
  1095. function nullif(s, nullVal) {
  1096. if (s !== null && s === nullVal) {
  1097. return null;
  1098. }
  1099. return s;
  1100. }
  1101. function loadScriptUpdateMonitor() {
  1102. let updateMonitor;
  1103. try {
  1104. updateMonitor = new WazeWrap.Alerts.ScriptUpdateMonitor(SCRIPT_NAME, SCRIPT_VERSION, DOWNLOAD_URL, GM_xmlhttpRequest);
  1105. updateMonitor.start();
  1106. }
  1107. catch (ex) {
  1108. log('error', ex);
  1109. }
  1110. }
  1111. bootstrap();
  1112. })(WMEWAL_Locks || (WMEWAL_Locks = {}));

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址