WME Place NavPoints

Add place entry point indicators to the map

  1. // ==UserScript==
  2. // @name WME Place NavPoints
  3. // @namespace WazeDev
  4. // @version 2024.09.20.000
  5. // @description Add place entry point indicators to the map
  6. // @author MapOMatic
  7. // @include /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
  8. // @require https://gf.qytechs.cn/scripts/24851-wazewrap/code/WazeWrap.js
  9. // @require https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js
  10. // @grant GM_xmlhttpRequest
  11. // @connect gf.qytechs.cn
  12. // ==/UserScript==
  13.  
  14. /* global W */
  15. /* global OpenLayers */
  16. /* global WazeWrap */
  17. /* global turf */
  18.  
  19. (function main() {
  20. 'use strict';
  21.  
  22. const SCRIPT_NAME = GM_info.script.name;
  23. const SCRIPT_VERSION = GM_info.script.version;
  24. const DOWNLOAD_URL = 'https://update.gf.qytechs.cn/scripts/387498/WME%20Place%20NavPoints.user.js';
  25.  
  26. const _settings = {
  27. visible: true,
  28. plaVisible: true
  29. };
  30.  
  31. let _layer;
  32.  
  33. // NOTE: There are occasions where the street is not loaded in the model yet, and
  34. // the WazeWrap getStreetName function will throw an error. This function will
  35. // just return null instead.
  36. // function getStreetName(primaryStreetID) {
  37. // const street = W.model.streets.getObjectById(primaryStreetID);
  38. // if (street) {
  39. // return street.name;
  40. // }
  41. // return null;
  42. // }
  43.  
  44. function getOLMapExtent() {
  45. let extent = new OpenLayers.Bounds(W.map.getExtent());
  46. extent = extent.transform('EPSG:4326', 'EPSG:3857');
  47. return extent;
  48. }
  49.  
  50. function drawLines() {
  51. _layer.removeAllFeatures();
  52. if (!_settings.visible) return;
  53.  
  54. const features = [];
  55. const bounds = getOLMapExtent().scale(2.0);
  56. const zoom = W.map.getZoom();
  57. W.model.venues.getObjectArray()
  58. .filter(venue => (
  59. _settings.plaVisible || !venue.isParkingLot())
  60. && bounds.intersectsBounds(venue.getOLGeometry().getBounds())
  61. && (zoom >= 6 || (venue.isResidential() && !venue.attributes.entryExitPoints.length)))
  62. .forEach(venue => {
  63. const pts = [];
  64. let mainColor = venue.isPoint() ? '#0FF' : '#0FF';
  65. let endPoint;
  66.  
  67. // Get the places location.
  68. const placePoint = venue.getOLGeometry().getCentroid();
  69. pts.push(placePoint);
  70.  
  71. // Get the main entry/exit point, if it exists.
  72. let entryExitPoint;
  73. if (venue.attributes.entryExitPoints.length) {
  74. entryExitPoint = W.userscripts.toOLGeometry(venue.attributes.entryExitPoints[0].getPoint());
  75. endPoint = entryExitPoint;
  76. pts.push(entryExitPoint);
  77. } else {
  78. endPoint = placePoint;
  79. }
  80.  
  81. const geoJsonEndPoint = W.userscripts.toGeoJSONGeometry(endPoint);
  82. const closestSegment = findClosestSegment(geoJsonEndPoint, false, false);
  83. if (closestSegment) {
  84. // Find the closest point on the closest segment (the stop point).
  85. const stopPoint = turf.nearestPointOnLine(closestSegment.getGeometry(), geoJsonEndPoint).geometry;
  86. pts.push(W.userscripts.toOLGeometry(stopPoint));
  87.  
  88. const placeStreetID = venue.attributes.streetID;
  89. if (placeStreetID) {
  90. // The intent here was to highlight places that route to a street with a name
  91. // other than the place's street name, but I believe that is too common
  92. // of a scenario and distracting. Leaving this code here in case we
  93. // can tweak it to be more useful somehow.
  94.  
  95. // const segmentStreetID = closestSegment.attributes.primaryStreetID;
  96. // const segmentStreetName = getStreetName(segmentStreetID);
  97. // const placeStreetName = getStreetName(placeStreetID);
  98. // if (segmentStreetName !== placeStreetName) {
  99. // mainColor = '#FFA500';
  100. // }
  101. } else {
  102. // If the place has no street listed, make the lines red.
  103. mainColor = '#F00';
  104. }
  105.  
  106. // Draw the lines.
  107. features.push(new OpenLayers.Feature.Vector(
  108. new OpenLayers.Geometry.LineString(pts),
  109. { isNavLine: true },
  110. {
  111. strokeColor: mainColor,
  112. strokeWidth: 2,
  113. strokeDashstyle: '6 4'
  114. }
  115. ));
  116.  
  117. // Draw the stop point.
  118. features.push(
  119. new OpenLayers.Feature.Vector(
  120. pts[pts.length - 1],
  121. { isNavLine: true },
  122. {
  123. pointRadius: 4,
  124. strokeWidth: 2,
  125. fillColor: '#A00',
  126. strokeColor: mainColor,
  127. fillOpacity: 1
  128. }
  129. )
  130. );
  131.  
  132. // Draw the entry/exit point, if it exists.
  133. if (entryExitPoint) {
  134. features.push(
  135. new OpenLayers.Feature.Vector(
  136. entryExitPoint,
  137. { isNavLine: true },
  138. {
  139. pointRadius: 4,
  140. strokeWidth: 2,
  141. strokeColor: mainColor,
  142. fillColor: '#FFF',
  143. fillOpacity: 1
  144. }
  145. )
  146. );
  147. }
  148. }
  149. });
  150.  
  151. _layer.addFeatures(features);
  152. }
  153.  
  154. function findClosestSegment(mygeometry, ignorePLR, ignoreUnnamedPR) {
  155. const segments = W.model.segments.getObjectArray();
  156. let minDistance = Infinity;
  157. let closestSegment;
  158.  
  159. segments.forEach(segment => {
  160. const { roadType } = segment.attributes;
  161. const segmentStreetID = segment.attributes.primaryStreetID;
  162.  
  163. if (!segment.isDeleted()
  164. && ![10, 16, 18, 19].includes(roadType) // 10 ped boardwalk, 16 stairway, 18 railroad, 19 runway, 3 freeway
  165. && !(ignorePLR && roadType === 20) // PLR
  166. && !(ignoreUnnamedPR && roadType === 17 && WazeWrap.Model.getStreetName(segmentStreetID) === null)) { // PR
  167. const distanceToSegment = W.userscripts.toOLGeometry(mygeometry).distanceTo(segment.getOLGeometry(), { details: true });
  168. if (distanceToSegment.distance < minDistance) {
  169. minDistance = distanceToSegment.distance;
  170. closestSegment = segment;
  171. }
  172. }
  173. });
  174. return closestSegment;
  175. }
  176.  
  177. function saveSettings() {
  178. localStorage.setItem('wme_place_navpoints', JSON.stringify(_settings));
  179. }
  180.  
  181. function errorHandler(callback) {
  182. try {
  183. callback();
  184. } catch (ex) {
  185. console.error(ex);
  186. }
  187. }
  188.  
  189. function onPlacesLayerCheckedChanged(checked) {
  190. _settings.visible = checked;
  191. $('#layer-switcher-item_pla_navpoints').attr('disabled', checked ? null : true);
  192. saveSettings();
  193. drawLines();
  194. }
  195.  
  196. function onPlaLayerCheckedChanged(checked) {
  197. _settings.plaVisible = checked;
  198. saveSettings();
  199. drawLines();
  200. }
  201.  
  202. function loadScriptUpdateMonitor() {
  203. try {
  204. const updateMonitor = new WazeWrap.Alerts.ScriptUpdateMonitor(SCRIPT_NAME, SCRIPT_VERSION, DOWNLOAD_URL, GM_xmlhttpRequest);
  205. updateMonitor.start();
  206. } catch (ex) {
  207. // Report, but don't stop if ScriptUpdateMonitor fails.
  208. console.error('WME Place NavPoints:', ex);
  209. }
  210. }
  211.  
  212. function init() {
  213. loadScriptUpdateMonitor();
  214. const loadedSettings = JSON.parse(localStorage.getItem('wme_place_navpoints'));
  215. $.extend(_settings, loadedSettings);
  216. const drawLinesFunc = () => errorHandler(drawLines);
  217. W.model.events.register('mergeend', null, drawLinesFunc);
  218. W.map.events.register('zoomend', null, drawLinesFunc);
  219. W.model.venues.on('objectschanged', drawLinesFunc);
  220. W.model.venues.on('objectsadded', drawLinesFunc);
  221. W.model.venues.on('objectsremoved', drawLinesFunc);
  222. W.model.segments.on('objectschanged', drawLinesFunc);
  223. W.model.segments.on('objectsadded', drawLinesFunc);
  224. W.model.segments.on('objectsremoved', drawLinesFunc);
  225. _layer = new OpenLayers.Layer.Vector('Place NavPoints Layer', {
  226. uniqueName: '__PlaceNavPointsLayer',
  227. displayInLayerSwitcher: false
  228. });
  229. W.map.addLayer(_layer);
  230. drawLines();
  231. WazeWrap.Interface.AddLayerCheckbox('Display', 'Place NavPoints', _settings.visible, onPlacesLayerCheckedChanged, null);
  232. WazeWrap.Interface.AddLayerCheckbox('Display', 'PLA NavPoints', _settings.visible, onPlaLayerCheckedChanged, null);
  233. $('#layer-switcher-item_pla_navpoints').attr('disabled', _settings.visible ? null : true).parent().css({ 'margin-left': '10px' });
  234. }
  235.  
  236. function onWmeReady() {
  237. if (WazeWrap && WazeWrap.Ready) {
  238. init();
  239. } else {
  240. setTimeout(onWmeReady, 100);
  241. }
  242. }
  243.  
  244. function bootstrap() {
  245. if (typeof W === 'object' && W.userscripts?.state.isReady) {
  246. onWmeReady();
  247. } else {
  248. document.addEventListener('wme-ready', onWmeReady, { once: true });
  249. }
  250. }
  251.  
  252. bootstrap();
  253. })();

QingJ © 2025

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