Greasy Fork 还支持 简体中文。

WME Google Place Layer

Adds a Google Maps Place layer overlay to the Waze Map Editor. Syncs with WME’s map center and zoom level. - Shift+P: Toggle layer visibility. - Shift+L: Toggle interactivity (enable/disable pointer events).

  1. // ==UserScript==
  2. // @name WME Google Place Layer
  3. // @name:vi WME Google Place Layer
  4. // @namespace https://waze.com/minhtanz1
  5. // @version 1.9
  6. // @description Adds a Google Maps Place layer overlay to the Waze Map Editor. Syncs with WME’s map center and zoom level. - Shift+P: Toggle layer visibility. - Shift+L: Toggle interactivity (enable/disable pointer events).
  7. // @description:vi Thêm lớp phủ địa điểm của Google Maps vào Waze Map Editor. Đồng bộ với trung tâm bản đồ và mức thu phóng của WME. - Shift+P: Chuyển đổi chế độ hiển thị lớp. - Shift+L: Chuyển đổi tính tương tác (bật/tắt sự kiện con trỏ).
  8. // @author Minh Tan
  9. // @match https://*.waze.com/*/editor*
  10. // @match https://*.waze.com/editor*
  11. // @exclude https://*.waze.com/user/editor*
  12. // @grant none
  13. // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
  14. // @license MIT
  15. // ==/UserScript==
  16.  
  17. /* global W, OpenLayers, google, WazeWrap */
  18.  
  19. (function() {
  20. 'use strict';
  21. let gmap;
  22. let placeDiv;
  23. let layerEnabled = (localStorage.getItem("WMEGooglePlaceLayer-enabled") ?? false) === 'true';
  24. let interactivityEnabled = false; // Default: interactivity disabled
  25.  
  26. // Toggle the display of the Google Place Layer
  27. function toggleLayer() {
  28. layerEnabled = !layerEnabled;
  29. const checkbox = document.querySelector("#layer-switcher-item_google_place_layer");
  30. if (checkbox) {
  31. checkbox.checked = layerEnabled;
  32. }
  33. placeDiv.style.display = layerEnabled ? "block" : "none";
  34. localStorage.setItem("WMEGooglePlaceLayer-enabled", layerEnabled);
  35. }
  36.  
  37. // Toggle pointer events (i.e. interactivity) on the overlay
  38. function toggleInteractivity() {
  39. interactivityEnabled = !interactivityEnabled;
  40. placeDiv.style.pointerEvents = interactivityEnabled ? 'auto' : 'none';
  41. console.log("Google Map interactivity " + (interactivityEnabled ? "enabled" : "disabled"));
  42. }
  43.  
  44. // Initialize the Google Maps overlay
  45. function initPlaceLayer() {
  46. // const trafficLayer = new google.maps.TrafficLayer();
  47.  
  48. placeDiv = document.createElement('div');
  49. placeDiv.id = "trafficDiv";
  50. placeDiv.style.position = 'absolute';
  51. placeDiv.style.top = '0';
  52. placeDiv.style.left = '0';
  53. placeDiv.style.right = '0';
  54. placeDiv.style.bottom = '0';
  55. placeDiv.style.opacity = '0.75'; // transparent layer
  56. // Start with interactivity disabled so WME controls work
  57. placeDiv.style.pointerEvents = 'none';
  58. W.map.olMap.getViewport().appendChild(placeDiv);
  59.  
  60. const lonlat = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat);
  61. lonlat.transform(new OpenLayers.Projection('EPSG:900913'), new OpenLayers.Projection('EPSG:4326'));
  62.  
  63. gmap = new google.maps.Map(placeDiv, {
  64. zoom: W.map.getZoom(),
  65. center: { lat: lonlat.lat, lng: lonlat.lon },
  66. disableDefaultUI: true,
  67. zoomControl: false,
  68. mapTypeId: 'roadmap',
  69. styles: [
  70. // DON'T CHANGE ANYTHING FROM THIS
  71. { featureType: 'landscape', stylers: [{ visibility: 'off' }] },
  72. // TO THIS
  73.  
  74. // Link get style json: https://mapstyle.withgoogle.com/
  75. {
  76. "featureType": "administrative.province",
  77. "elementType": "geometry.stroke",
  78. "stylers": [
  79. {
  80. "color": "#9238ff"
  81. },
  82. {
  83. "visibility": "on"
  84. },
  85. {
  86. "weight": 1.5
  87. }
  88. ]
  89. },
  90. {
  91. "featureType": "administrative.province",
  92. "elementType": "labels.text",
  93. "stylers": [
  94. {
  95. "visibility": "on"
  96. }
  97. ]
  98. },
  99. {
  100. "featureType": "administrative.province",
  101. "elementType": "labels.text.fill",
  102. "stylers": [
  103. {
  104. "color": "#000000"
  105. }
  106. ]
  107. },
  108. {
  109. "featureType": "administrative.province",
  110. "elementType": "labels.text.stroke",
  111. "stylers": [
  112. {
  113. "visibility": "on"
  114. }
  115. ]
  116. },
  117. {
  118. "featureType": "poi.attraction",
  119. "elementType": "labels.icon",
  120. "stylers": [
  121. {
  122. "visibility": "simplified"
  123. }
  124. ]
  125. },
  126. {
  127. "featureType": "poi.attraction",
  128. "elementType": "labels.text.fill",
  129. "stylers": [
  130. {
  131. "color": "#209d2f"
  132. }
  133. ]
  134. },
  135. {
  136. "featureType": "poi.business",
  137. "elementType": "labels.icon",
  138. "stylers": [
  139. {
  140. "visibility": "simplified"
  141. }
  142. ]
  143. },
  144. {
  145. "featureType": "poi.business",
  146. "elementType": "labels.text",
  147. "stylers": [
  148. {
  149. "visibility": "off"
  150. }
  151. ]
  152. },
  153. {
  154. "featureType": "poi.government",
  155. "elementType": "labels.icon",
  156. "stylers": [
  157. {
  158. "color": "#b90e0e"
  159. }
  160. ]
  161. },
  162. {
  163. "featureType": "poi.government",
  164. "elementType": "labels.text.fill",
  165. "stylers": [
  166. {
  167. "color": "#b90e0e"
  168. }
  169. ]
  170. },
  171. {
  172. "featureType": "poi.medical",
  173. "elementType": "geometry.fill",
  174. "stylers": [
  175. {
  176. "color": "#f8a0a0"
  177. }
  178. ]
  179. },
  180. {
  181. "featureType": "poi.medical",
  182. "elementType": "labels",
  183. "stylers": [
  184. {
  185. "visibility": "on"
  186. }
  187. ]
  188. },
  189. {
  190. "featureType": "poi.school",
  191. "elementType": "labels.icon",
  192. "stylers": [
  193. {
  194. "color": "#218cb0"
  195. }
  196. ]
  197. },
  198. {
  199. "featureType": "road.arterial",
  200. "elementType": "geometry",
  201. "stylers": [
  202. {
  203. "color": "#e9ec18"
  204. },
  205. {
  206. "visibility": "on"
  207. },
  208. {
  209. "weight": 2
  210. }
  211. ]
  212. },
  213. {
  214. "featureType": "road.highway",
  215. "elementType": "geometry",
  216. "stylers": [
  217. {
  218. "color": "#0a68ff"
  219. },
  220. {
  221. "visibility": "on"
  222. },
  223. {
  224. "weight": 2.5
  225. }
  226. ]
  227. },
  228. {
  229. "featureType": "road.highway",
  230. "elementType": "geometry.stroke",
  231. "stylers": [
  232. {
  233. "visibility": "on"
  234. },
  235. {
  236. "weight": 2.5
  237. }
  238. ]
  239. },
  240. {
  241. "featureType": "road.local",
  242. "elementType": "geometry",
  243. "stylers": [
  244. {
  245. "color": "#ff0000"
  246. },
  247. {
  248. "visibility": "on"
  249. },
  250. {
  251. "weight": 2
  252. }
  253. ]
  254. },
  255. {
  256. "featureType": "transit.station.airport",
  257. "stylers": [
  258. {
  259. "visibility": "on"
  260. }
  261. ]
  262. },
  263. {
  264. "featureType": "transit.station.bus",
  265. "stylers": [
  266. {
  267. "visibility": "off"
  268. }
  269. ]
  270. },
  271. {
  272. "featureType": "transit.station.rail",
  273. "stylers": [
  274. {
  275. "visibility": "on"
  276. }
  277. ]
  278. },
  279. {
  280. "featureType": "water",
  281. "elementType": "labels.geometry",
  282. "stylers": [
  283. {
  284. "visibility": "off"
  285. }
  286. ]
  287. },
  288. {
  289. "featureType": "water",
  290. "elementType": "labels.text",
  291. "stylers": [
  292. {
  293. "color": "#ffffff"
  294. }
  295. ]
  296. }
  297.  
  298.  
  299. ]
  300. });
  301.  
  302. //show traffic layer
  303. // trafficLayer.setMap(gmap);
  304. if (placeDiv.firstElementChild) {
  305. placeDiv.firstElementChild.style.backgroundColor = 'rgb(0 0 0 / 0%)'; //remove white background
  306. }
  307. if (!layerEnabled) {
  308. placeDiv.style.display = "none";
  309. }
  310.  
  311. // Sync Google Maps center with WME map movements
  312. WazeWrap.Events.register('moveend', null, function() {
  313. const lonlat = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat);
  314. lonlat.transform(new OpenLayers.Projection('EPSG:900913'), new OpenLayers.Projection('EPSG:4326'));
  315. gmap.panTo({ lat: lonlat.lat, lng: lonlat.lon });
  316. });
  317.  
  318. // Sync Google Maps zoom with WME zoom events
  319. WazeWrap.Events.register('zoomend', null, function() {
  320. gmap.setZoom(W.map.getZoom());
  321. });
  322.  
  323. window.gmap = gmap; // for testing
  324.  
  325. WazeWrap.Interface.AddLayerCheckbox(
  326. "display",
  327. "Google Place Layer",
  328. layerEnabled,
  329. toggleLayer,
  330. W.map.getLayerByName("Google Place Layer")
  331. );
  332.  
  333. new WazeWrap.Interface.Shortcut(
  334. 'WMEGooglePlaceLayer',
  335. 'Toggle Place Layer',
  336. 'layers',
  337. 'layersToggleWMEGooglePlaceLayer',
  338. "Shift+P",
  339. toggleLayer,
  340. null
  341. ).add();
  342.  
  343. new WazeWrap.Interface.Shortcut(
  344. 'WMEGoogleInteractivityToggle',
  345. 'Toggle Google Map Interactivity',
  346. 'layers',
  347. 'layersToggleGoogleInteractivity',
  348. 'Shift+L',
  349. toggleInteractivity,
  350. null
  351. ).add();
  352. }
  353.  
  354. if (W && W.userscripts && W.userscripts.state && W.userscripts.state.isReady) {
  355. initPlaceLayer();
  356. } else {
  357. document.addEventListener('wme-ready', initPlaceLayer, { once: true });
  358. }
  359. })();