Internet Roadtrip Map History

Shows more history on the Internet Roadtrip minimap (such as past honks)

// ==UserScript==
// @name        Internet Roadtrip Map History
// @namespace   zptr.cc
// @author      zeroptr
// @match       https://neal.fun/internet-roadtrip/*
// @version     0.1.1
// @license     MIT
// @description Shows more history on the Internet Roadtrip minimap (such as past honks)
// @icon        https://neal.fun/favicons/internet-roadtrip.png
// @grant       unsafeWindow
// @run-at      document-end
// @require     https://cdn.jsdelivr.net/npm/[email protected]
// ==/UserScript==

(async() => {
  const lib = await IRF.modules.maplibre;
  const map = (await IRF.vdom.map).data.map;

  const data = {
    type: "FeatureCollection",
    features: []
  };

  async function loadPastMarkers() {
    const req = await fetch(
      "https://roadtrip.zptr.cc/data/honks"
    );
    
    const markers = await req.json();
    markers.sort((a, b) => a.ts - b.ts);

    for (const marker of markers) {
      addMarker(marker.ts, marker.lat, marker.lon, marker.count || 1, marker.players);
    }
  }

  function addMarker(time, lat, lon, count=1, players=0) {
    data.features.push({
      type: "Feature",
      properties: { count, time, players },
      geometry: {
        type: "Point",
        coordinates: [lon, lat]
      }
    });
  }

  async function initEvents() {
    const container = await IRF.vdom.container;
    const fn = container.state.changeStop;
    container.state.changeStop = function (_, e) {
      if (e == -2) {
        const { lat, lng } = container.data.currentCoords;
        addMarker(Math.floor(Date.now() / 1000), lat, lng, 1, container.data.totalUsers);
        map.getSource("honks").setData(data);
      }

      fn.apply(this, arguments);
    };
  }

  initEvents();
  await loadPastMarkers();

  const img = await map.loadImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAWJAAAFiQFtaJ36AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAVWSURBVHgB7VrrdfJGEB3n5L/lCqxUAB0YV2CoAFKBSQWQCsAViFTg4wogFWBXIKUBCyrYzBWz35GJdrVaPT4Uc8+ZIx7Sal57Z7RaoiuuuOI744Z6AqVUyIew4K/Dzc3NO3niV+oPVu/v7+PD4ZB9CYKAhsMhPk5YvB3QG3AGxGywwkfIfr/nn9SSvgOQ/mma/jB+sVjA+FfyAMZiGVGfwAqPt9ttZvzz8zOMj4UTqo4TyrUz6hNY4fVyuVRhGKrPz8/U0/iAZY9MyjvgF+oHBnEcE2cB3d3d/cmsn1B1RC8vL8O3tzfqHThiKaaAL+nxdavX19dsCm02G9X6FADJ4CYiY5YheQLXqhP25AG+boGKwWWz0AGN9gEwlg8R1+uAJftN12v+L+GvG5a/KqYwnIfzJ1QRMD5JkuVkMiHdP7QGEBPPUzUajX6Uq7yghsP7QkKRK5HxeRtP0ptCHxBnXo/WpgAUnU6nhcbnBQqJEjGUdBh3RBWBa4qMp5YdkBbdkCyOgJJVssFRjxClMt81UtsOOO/Uqgjqu29jU6QHxprNZv+5D6amVJJUNd0JYkDdqfnIfD7XDc6YPKGNx1h6XDA/2mbhHViPNjKgpoH6jEiSpwNISFKmxJQqQrq8WOtwFu21arv3R40uYn/Mc1EkMy6KImXjiRwvVHIC7r9arbqJdsHNs/JHZsJZ6vNQKXDueDy2OkGMGDneP5JGqZtoFyhgLH9iSHB2Pjq7GNEiy3QQThiW3HvRabQLFAhN9ZYMDtDXlTkBTG6rDuoSnusRfcxrMhgh8zk0XJs9ntqcICVyS5cIiYC1+REOmFvGCMoyQUh0RZcGzD9b9EnKUVkE9XQw8QhqeRVS7ARaaZfWVyI4KhlvCNIzjYeqIXzQPdEVwSX6VCELZMwZOEM/t58LFjTUJawCV4k+VcgCGXuNhobsUyGkn4kq0aevWRA7jJ2RoqlRQp9fpyqo00oVmqUn8oEL85M9C+YO9xgh0qapIKV1RI7IGZ2tK8r1M/JBWd23CZwm7WopkdmmQhmnSBZ9MVovqeNv7/WAsq7PRaSxiRzuBSNS0/KaiVPUaQE2+xPAYuh6vc5epmCs3GrUjKoC0a/7yIu0dk1hKGlaY3DsL4aSCc+SDVvJwOoOULLGRjWMP1M+dpwKW1MW+DZHkl3V+glbp+YjSEuX9lZZVppkOq2pbSCF9FuVpiQ3FUqXwExZgDFcSdUbPk2Pq+CZXwwIS3QYm7JAyNCvprugCeKziTQ2+xIdsopQ1BfI9RG1ASW9ObVkvBbp8VcluqyLAqF7C2oaOvVNDNyk5PjAtm5gJEPboos3kPpg6jLlm5KyhVCZBoXtceNvelTJY2lbUrYQaiJj4YFmymGXqV8ktoVQUzmUayJqAm2zfkUnTHN6haZK4Lrg4mL8uAvWd5HcGyU4AmVibwpMXQfkd4gEu92OLgFJktDj4yN2loQQfG9Lt4veKottNnqrjQmyXfaDPNGXbXJGPDw84OC1gQrotQOYK4g5AB//Jk/02gG80oNdaBvPjZMZOucAbJvTggjieHt7ix2g2f/39/dfzj8ej4Rdoh8fHxkfYLsbruHXacRNUMKn/EE10LgDtGEgJxiGo94rqA2n076/Q+54lKP+L49blt9YBnTaMxjIuTuW3zn6tTYA1nKAnoMwDpHDkX/ThoG+j3I86GNdhZuGswMQORgLGQwGOqIJnSIB4/7Bsc58/BkwOkCnLb+lyQxmw3UUwbg7Ohl7UdH0wRcHwGjuxenp6SmLNH/fkRjMxu7o/wx12tmdyvr5TF3Kq+iuIE9c38voK6644tvjX5iDcFFA2WFzAAAAAElFTkSuQmCC");
  map.addImage("honk-image", img.data);

  map.addSource("honks", {
    type: "geojson",
    data,
    cluster: true,
    clusterMaxZoom: 8,
    clusterRadius: 5,
    clusterMinPoints: 10,
    clusterProperties: {
      "sum": ["+", ["get", "count"]]
    }
  });

  map.addLayer({
    id: "points",
    type: "symbol",
    source: "honks",
    filter: [">", ["zoom"], 5],
    layout: {
      "icon-image": "honk-image",
      "icon-size": 0.5
    },
  });

  let openedPopup;
  map.on("mouseenter", "points", (e) => {
    const { count, time, players } = e.features[0].properties;
    const date = new Date(time * 1000);

    if (openedPopup) {
      openedPopup.remove();
    }

    openedPopup = new lib.Popup()
      .setLngLat(e.features[0].geometry.coordinates.slice())
      .setHTML(`${date.toLocaleString()}<br>${count} honk${count == 1 ? "" : "s"} | ${players} players`)
      .addTo(map);
  });

  map.on("mouseleave", "points", () => {
    if (openedPopup) {
      openedPopup.remove();
      openedPopup = null;
    }
  });

  setTimeout(() => {
    map.moveLayer("points");
  }, 1000);
})();

QingJ © 2025

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