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).

当前为 2025-04-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name WME Google Place Layer
  3. // @name:vi WME Google Place Layer
  4. // @namespace https://waze.com/minhtanz1
  5. // @version 1.8
  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. {
  77. "featureType": "administrative.province",
  78. "elementType": "geometry.stroke",
  79. "stylers": [
  80. {
  81. "color": "#9238ff"
  82. },
  83. {
  84. "visibility": "on"
  85. },
  86. {
  87. "weight": 1.5
  88. }
  89. ]
  90. },
  91. {
  92. "featureType": "administrative.province",
  93. "elementType": "labels.text",
  94. "stylers": [
  95. {
  96. "visibility": "on"
  97. }
  98. ]
  99. },
  100. {
  101. "featureType": "administrative.province",
  102. "elementType": "labels.text.fill",
  103. "stylers": [
  104. {
  105. "color": "#000000"
  106. }
  107. ]
  108. },
  109. {
  110. "featureType": "administrative.province",
  111. "elementType": "labels.text.stroke",
  112. "stylers": [
  113. {
  114. "visibility": "on"
  115. }
  116. ]
  117. },
  118. {
  119. "featureType": "poi.attraction",
  120. "elementType": "labels.icon",
  121. "stylers": [
  122. {
  123. "visibility": "simplified"
  124. }
  125. ]
  126. },
  127. {
  128. "featureType": "poi.attraction",
  129. "elementType": "labels.text.fill",
  130. "stylers": [
  131. {
  132. "color": "#209d2f"
  133. }
  134. ]
  135. },
  136. {
  137. "featureType": "poi.business",
  138. "elementType": "labels.icon",
  139. "stylers": [
  140. {
  141. "visibility": "simplified"
  142. }
  143. ]
  144. },
  145. {
  146. "featureType": "poi.business",
  147. "elementType": "labels.text",
  148. "stylers": [
  149. {
  150. "visibility": "off"
  151. }
  152. ]
  153. },
  154. {
  155. "featureType": "poi.government",
  156. "elementType": "labels.icon",
  157. "stylers": [
  158. {
  159. "color": "#b90e0e"
  160. }
  161. ]
  162. },
  163. {
  164. "featureType": "poi.government",
  165. "elementType": "labels.text.fill",
  166. "stylers": [
  167. {
  168. "color": "#b90e0e"
  169. }
  170. ]
  171. },
  172. {
  173. "featureType": "poi.medical",
  174. "elementType": "geometry.fill",
  175. "stylers": [
  176. {
  177. "color": "#f8a0a0"
  178. }
  179. ]
  180. },
  181. {
  182. "featureType": "poi.medical",
  183. "elementType": "labels",
  184. "stylers": [
  185. {
  186. "visibility": "on"
  187. }
  188. ]
  189. },
  190. {
  191. "featureType": "poi.school",
  192. "elementType": "labels.icon",
  193. "stylers": [
  194. {
  195. "color": "#218cb0"
  196. }
  197. ]
  198. },
  199. {
  200. "featureType": "road.arterial",
  201. "elementType": "geometry",
  202. "stylers": [
  203. {
  204. "color": "#3fbf36"
  205. },
  206. {
  207. "visibility": "on"
  208. },
  209. {
  210. "weight": 1.5
  211. }
  212. ]
  213. },
  214. {
  215. "featureType": "road.highway",
  216. "elementType": "geometry",
  217. "stylers": [
  218. {
  219. "visibility": "on"
  220. },
  221. {
  222. "weight": 2
  223. }
  224. ]
  225. },
  226. {
  227. "featureType": "road.local",
  228. "elementType": "geometry",
  229. "stylers": [
  230. {
  231. "color": "#ff6d2e"
  232. },
  233. {
  234. "visibility": "on"
  235. },
  236. {
  237. "weight": 1
  238. }
  239. ]
  240. },
  241. {
  242. "featureType": "transit.station.airport",
  243. "stylers": [
  244. {
  245. "visibility": "on"
  246. }
  247. ]
  248. },
  249. {
  250. "featureType": "transit.station.bus",
  251. "stylers": [
  252. {
  253. "visibility": "off"
  254. }
  255. ]
  256. },
  257. {
  258. "featureType": "transit.station.rail",
  259. "stylers": [
  260. {
  261. "visibility": "on"
  262. }
  263. ]
  264. },
  265. {
  266. "featureType": "water",
  267. "elementType": "geometry",
  268. "stylers": [
  269. {
  270. "visibility": "off"
  271. }
  272. ]
  273. },
  274. {
  275. "featureType": "water",
  276. "elementType": "labels.text",
  277. "stylers": [
  278. {
  279. "color": "#ffffff"
  280. }
  281. ]
  282. }
  283.  
  284.  
  285. ]
  286. });
  287.  
  288. //show traffic layer
  289. // trafficLayer.setMap(gmap);
  290. if (placeDiv.firstElementChild) {
  291. placeDiv.firstElementChild.style.backgroundColor = 'rgb(0 0 0 / 0%)'; //remove white background
  292. }
  293. if (!layerEnabled) {
  294. placeDiv.style.display = "none";
  295. }
  296.  
  297. // Sync Google Maps center with WME map movements
  298. WazeWrap.Events.register('moveend', null, function() {
  299. const lonlat = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat);
  300. lonlat.transform(new OpenLayers.Projection('EPSG:900913'), new OpenLayers.Projection('EPSG:4326'));
  301. gmap.panTo({ lat: lonlat.lat, lng: lonlat.lon });
  302. });
  303.  
  304. // Sync Google Maps zoom with WME zoom events
  305. WazeWrap.Events.register('zoomend', null, function() {
  306. gmap.setZoom(W.map.getZoom());
  307. });
  308.  
  309. window.gmap = gmap; // for testing
  310.  
  311. WazeWrap.Interface.AddLayerCheckbox(
  312. "display",
  313. "Google Place Layer",
  314. layerEnabled,
  315. toggleLayer,
  316. W.map.getLayerByName("Google Place Layer")
  317. );
  318.  
  319. new WazeWrap.Interface.Shortcut(
  320. 'WMEGooglePlaceLayer',
  321. 'Toggle Place Layer',
  322. 'layers',
  323. 'layersToggleWMEGooglePlaceLayer',
  324. "Shift+P",
  325. toggleLayer,
  326. null
  327. ).add();
  328.  
  329. new WazeWrap.Interface.Shortcut(
  330. 'WMEGoogleInteractivityToggle',
  331. 'Toggle Google Map Interactivity',
  332. 'layers',
  333. 'layersToggleGoogleInteractivity',
  334. 'Shift+L',
  335. toggleInteractivity,
  336. null
  337. ).add();
  338. }
  339.  
  340. if (W && W.userscripts && W.userscripts.state && W.userscripts.state.isReady) {
  341. initPlaceLayer();
  342. } else {
  343. document.addEventListener('wme-ready', initPlaceLayer, { once: true });
  344. }
  345. })();