WME Map Overlay (Google + OSM + Traffic + Apple Maps + Waze Live Map)

Sobreposição de mapas no WME: Google, OSM, Trânsito, Apple Maps e Waze Live Map!

  1. // ==UserScript==
  2. // @name WME Map Overlay (Google + OSM + Traffic + Apple Maps + Waze Live Map)
  3. // @namespace https://waze.com
  4. // @version 2.9
  5. // @description Sobreposição de mapas no WME: Google, OSM, Trânsito, Apple Maps e Waze Live Map!
  6. // @author ChatGPT
  7. // @match https://www.waze.com/*editor*
  8. // @grant none
  9. // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. let gmap, trafficDiv, sliderContainer, isSliderVisible = true;
  17. let wazeLiveLayer;
  18.  
  19. function initOverlay() {
  20. if (typeof W === 'undefined' || typeof W.map === 'undefined') {
  21. setTimeout(initOverlay, 1000);
  22. return;
  23. }
  24.  
  25. const map = W.map;
  26.  
  27. const googleBaseLayer = new OpenLayers.Layer.XYZ(
  28. "Google Maps (Base)",
  29. "https://mt.google.com/vt/lyrs=m&x=${x}&y=${y}&z=${z}",
  30. { isBaseLayer: false, opacity: 0.01, visibility: true }
  31. );
  32.  
  33. const osmLayer = new OpenLayers.Layer.XYZ(
  34. "OpenStreetMap",
  35. "https://tile.openstreetmap.org/${z}/${x}/${y}.png",
  36. { isBaseLayer: false, opacity: 0.01, visibility: true }
  37. );
  38.  
  39. // Atualizando a camada do Apple Maps conforme solicitado
  40. const appleLayer = new OpenLayers.Layer.XYZ(
  41. "Apple Maps",
  42. "https://maps.apple.com/frame?map=explore&center=${lat},${lon}&span=${spanX},${spanY}",
  43. { isBaseLayer: false, opacity: 0.01, visibility: true }
  44. );
  45.  
  46. wazeLiveLayer = new OpenLayers.Layer.XYZ(
  47. "Waze Live Map",
  48. "https://worldtiles1.waze.com/tiles/${z}/${x}/${y}.png",
  49. { isBaseLayer: false, opacity: 0.01, visibility: true }
  50. );
  51.  
  52. map.addLayer(wazeLiveLayer);
  53. map.addLayer(googleBaseLayer);
  54. map.addLayer(osmLayer);
  55. map.addLayer(appleLayer);
  56.  
  57. sliderContainer = document.createElement("div");
  58. sliderContainer.style.position = "absolute";
  59. sliderContainer.style.top = "80px";
  60. sliderContainer.style.left = "50%";
  61. sliderContainer.style.transform = "translateX(-50%)";
  62. sliderContainer.style.zIndex = "1000";
  63. sliderContainer.style.padding = "8px";
  64. sliderContainer.style.background = "rgba(10, 25, 50, 0.95)";
  65. sliderContainer.style.borderRadius = "10px";
  66. sliderContainer.style.border = "1px solid white";
  67. sliderContainer.style.boxShadow = "0 2px 6px rgba(0,0,0,0.3)";
  68. sliderContainer.style.display = "flex";
  69. sliderContainer.style.gap = "10px";
  70. sliderContainer.style.fontFamily = "sans-serif";
  71. sliderContainer.style.transition = "all 0.3s ease";
  72.  
  73. const layers = [
  74. {
  75. name: "Waze Live",
  76. icon: "https://cdn-images-1.medium.com/max/1200/1*3kS1iOOTBrvtkecae3u2aA.png",
  77. initial: 0.01,
  78. onChange: value => wazeLiveLayer.setOpacity(value)
  79. },
  80. {
  81. name: "Google Maps",
  82. icon: "https://static.vecteezy.com/system/resources/previews/016/716/478/non_2x/google-maps-icon-free-png.png",
  83. initial: 0.01,
  84. onChange: value => googleBaseLayer.setOpacity(value)
  85. },
  86. {
  87. name: "Traffic",
  88. icon: "https://cdn-icons-png.flaticon.com/256/2228/2228204.png",
  89. initial: 0.00,
  90. onChange: value => {
  91. if (trafficDiv) trafficDiv.style.opacity = value;
  92. }
  93. },
  94. {
  95. name: "OSM",
  96. icon: "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Openstreetmap_logo.svg/2048px-Openstreetmap_logo.svg.png",
  97. initial: 0.01,
  98. onChange: value => osmLayer.setOpacity(value)
  99. },
  100. {
  101. name: "Apple Maps",
  102. icon: "https://th.bing.com/th/id/R.7bbe9deedfa162a68a224e18fbad2dfc?rik=nsbux2xtDIgyUw&pid=ImgRaw&r=0",
  103. initial: 0.01,
  104. onChange: value => appleLayer.setOpacity(value)
  105. },
  106. {
  107. name: "ArcGIS",
  108. icon: "https://th.bing.com/th/id/OIP.e5Qa0gpEBMxdY1zIgJu5PQHaHa?w=164&h=180&c=7&r=0&o=5&pid=1.7",
  109. initial: 0.00,
  110. onChange: () => {},
  111. isButton: true
  112. }
  113. ];
  114.  
  115. layers.forEach(layer => {
  116. const wrapper = document.createElement("div");
  117. wrapper.style.width = "60px";
  118. wrapper.style.textAlign = "center";
  119.  
  120. const img = document.createElement("img");
  121. img.src = layer.icon;
  122. img.alt = layer.name;
  123. img.title = layer.name;
  124. img.style.width = "60px";
  125. img.style.height = "60px";
  126. img.style.borderRadius = "12px";
  127. img.style.border = "2px solid white";
  128. img.style.transition = "transform 0.3s ease, border 0.3s ease";
  129. img.style.display = "block";
  130. img.style.marginBottom = "4px";
  131. img.style.cursor = "pointer";
  132.  
  133. img.addEventListener("mouseenter", () => {
  134. img.style.transform = "scale(1.1)";
  135. img.style.border = "2px solid gold";
  136. });
  137.  
  138. img.addEventListener("mouseleave", () => {
  139. img.style.transform = "scale(1)";
  140. img.style.border = "2px solid white";
  141. });
  142.  
  143. img.addEventListener("click", () => {
  144. const center = W.map.getCenter();
  145. const zoom = W.map.getZoom();
  146.  
  147. const lonlat = new OpenLayers.LonLat(center.lon, center.lat);
  148. lonlat.transform(
  149. new OpenLayers.Projection("EPSG:900913"),
  150. new OpenLayers.Projection("EPSG:4326")
  151. );
  152.  
  153. const lat = parseFloat(lonlat.lat.toFixed(6));
  154. const lon = parseFloat(lonlat.lon.toFixed(6));
  155. const span = 360 / Math.pow(2, zoom);
  156.  
  157. if (layer.name === "Google Maps") {
  158. const gmapsZoom = Math.min(Math.max(zoom, 0), 21);
  159. const gmapsUrl = `https://www.google.com/maps/@${lat},${lon},${gmapsZoom}z`;
  160. window.open(gmapsUrl, '_blank');
  161. }
  162.  
  163. if (layer.name === "OSM") {
  164. const osmUrl = `https://www.openstreetmap.org/#map=${zoom}/${lat}/${lon}`;
  165. window.open(osmUrl, '_blank');
  166. }
  167.  
  168. if (layer.name === "ArcGIS") {
  169. const arcgisZoom = Math.min(Math.max(zoom, 0), 20);
  170. const arcgisUrl = `https://www.arcgis.com/apps/Viewer/index.html?appid=3801d24f76d246adb134d43a7a222b2c&center=${lon},${lat}&level=${arcgisZoom}`;
  171. window.open(arcgisUrl, '_blank');
  172. }
  173.  
  174. if (layer.name === "Apple Maps") {
  175. const appleUrl = `https://maps.apple.com/frame?map=hybrid&center=${lat}%2C${lon}&span=${span * 0.5}%2C${span}`;
  176. window.open(appleUrl, '_blank');
  177. }
  178.  
  179. if (layer.name === "Waze Live") {
  180. const wazeZoom = Math.min(Math.max(zoom, 3), 18);
  181. const wazeUrl = `https://www.waze.com/livemap?lat=${lat}&lon=${lon}&zoom=${wazeZoom}&utm_medium=wme_footer&overlay=false&utm_campaign=default&utm_source=waze_website`;
  182. window.open(wazeUrl, '_blank');
  183. }
  184. });
  185.  
  186. wrapper.appendChild(img);
  187.  
  188. if (!layer.isButton && layer.name !== "Apple Maps") { // Removido o slider do Apple Maps
  189. const slider = document.createElement("input");
  190. slider.type = "range";
  191. slider.min = "0";
  192. slider.max = "1";
  193. slider.step = "0.01";
  194. slider.value = layer.initial;
  195. slider.style.width = "100%";
  196. slider.addEventListener("input", () => {
  197. layer.onChange(parseFloat(slider.value));
  198. });
  199. wrapper.appendChild(slider);
  200. }
  201.  
  202. sliderContainer.appendChild(wrapper);
  203. });
  204.  
  205. const toggleButton = document.createElement("button");
  206. toggleButton.textContent = isSliderVisible ? "Hide Sliders" : "Show Sliders";
  207. toggleButton.style.position = "absolute";
  208. toggleButton.style.top = "30px";
  209. toggleButton.style.left = "50%";
  210. toggleButton.style.transform = "translateX(-50%)";
  211. toggleButton.style.zIndex = "2000";
  212. toggleButton.style.padding = "5px 10px";
  213. toggleButton.style.backgroundColor = "rgba(10, 25, 50, 0.8)";
  214. toggleButton.style.color = "white";
  215. toggleButton.style.border = "1px solid white";
  216. toggleButton.style.borderRadius = "5px";
  217. toggleButton.style.cursor = "pointer";
  218. toggleButton.style.transition = "all 0.3s ease";
  219. toggleButton.addEventListener("click", () => {
  220. isSliderVisible = !isSliderVisible;
  221. sliderContainer.style.display = isSliderVisible ? "flex" : "none";
  222. toggleButton.textContent = isSliderVisible ? "Hide Sliders" : "Show Sliders";
  223. });
  224.  
  225. document.body.appendChild(toggleButton);
  226. document.body.appendChild(sliderContainer);
  227.  
  228. initTrafficLayer();
  229. }
  230.  
  231. function initTrafficLayer() {
  232. const trafficLayer = new google.maps.TrafficLayer();
  233.  
  234. trafficDiv = document.createElement('div');
  235. trafficDiv.id = "trafficDiv";
  236. trafficDiv.style.position = 'absolute';
  237. trafficDiv.style.top = '0';
  238. trafficDiv.style.left = '0';
  239. trafficDiv.style.right = '0';
  240. trafficDiv.style.bottom = '0';
  241. trafficDiv.style.zIndex = '500';
  242. trafficDiv.style.opacity = '0';
  243. trafficDiv.style.pointerEvents = 'none';
  244.  
  245. W.map.olMap.getViewport().appendChild(trafficDiv);
  246.  
  247. const lonlat = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat);
  248. lonlat.transform(new OpenLayers.Projection('EPSG:900913'), new OpenLayers.Projection('EPSG:4326'));
  249.  
  250. gmap = new google.maps.Map(trafficDiv, {
  251. zoom: W.map.getZoom(),
  252. center: { lat: lonlat.lat, lng: lonlat.lon },
  253. disableDefaultUI: true,
  254. zoomControl: false,
  255. styles: [
  256. { elementType: 'labels', stylers: [{ visibility: 'off' }] },
  257. { featureType: 'road', elementType: 'geometry', stylers: [{ lightness: 100 }] },
  258. { featureType: 'road', elementType: 'labels', stylers: [{ visibility: 'off' }] },
  259. { featureType: 'transit', stylers: [{ visibility: 'off' }] },
  260. { featureType: 'poi', stylers: [{ visibility: 'off' }] },
  261. { featureType: 'administrative', stylers: [{ visibility: 'off' }] },
  262. { featureType: 'landscape', stylers: [{ visibility: 'off' }] },
  263. { featureType: 'water', stylers: [{ visibility: 'off' }] }
  264. ]
  265. });
  266.  
  267. trafficLayer.setMap(gmap);
  268. trafficDiv.firstElementChild.style.backgroundColor = 'rgba(0, 0, 0, 0)';
  269.  
  270. WazeWrap.Events.register('moveend', null, () => {
  271. const lonlat = new OpenLayers.LonLat(W.map.getCenter().lon, W.map.getCenter().lat);
  272. lonlat.transform(new OpenLayers.Projection('EPSG:900913'), new OpenLayers.Projection('EPSG:4326'));
  273. gmap.panTo({ lat: lonlat.lat, lng: lonlat.lon });
  274. });
  275.  
  276. WazeWrap.Events.register('zoomend', null, () => {
  277. gmap.setZoom(W.map.getZoom());
  278. });
  279. }
  280.  
  281. function waitReady() {
  282. if (W && WazeWrap && WazeWrap.Ready) {
  283. initOverlay();
  284. } else {
  285. setTimeout(waitReady, 500);
  286. }
  287. }
  288.  
  289. waitReady();
  290. })();