17 Commits

Author SHA1 Message Date
Râu Cao
e74e460480 1.9.0 2022-09-15 10:02:34 +02:00
Râu Cao
0cb10203c7 Only show riders when tracking timestamp is newer than 3 hours ago 2022-09-15 10:02:15 +02:00
Râu Cao
a8c0aefbd6 1.8.0 2022-09-14 19:05:41 +02:00
Râu Cao
9c18cc19b7 Add popover for rider markers 2022-09-14 19:05:15 +02:00
Râu Cao
1dc218ca8a 1.7.0 2022-09-14 18:50:38 +02:00
Râu Cao
bfd9b4fdf6 Add riders to map 2022-09-14 18:50:02 +02:00
Râu Cao
67707b7ded 1.6.0 2022-09-14 10:18:29 +02:00
Râu Cao
9c96037e32 Add legacy routes 2022-09-14 10:17:55 +02:00
Râu Cao
fc4c63b519 1.5.0 2022-09-13 07:55:53 +02:00
Râu Cao
fe1c9f6300 Adjust for multiple tracked POIs 2022-09-13 07:55:09 +02:00
Râu Cao
0d66bc1266 1.4.0 2022-09-11 12:31:26 +02:00
Râu Cao
ba3982d9a6 Add plausible analytics script 2022-09-11 12:30:52 +02:00
Râu Cao
f2cc9a9783 1.3.0 2022-09-09 20:29:41 +02:00
Râu Cao
0c1aeebdf7 Load current tour status from server 2022-09-09 20:29:18 +02:00
Râu Cao
3324a57206 Update route 2022-09-09 20:29:00 +02:00
Râu Cao
cf8e6882f8 1.2.1 2022-09-08 09:49:18 +02:00
Râu Cao
6c4959deda Hackety hack, no time 2022-09-08 09:48:51 +02:00
12 changed files with 229159 additions and 52845 deletions

182553
data/legacy-route.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

11
dist/assets/index.b6f9bc24.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/index.html vendored
View File

@@ -6,11 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Road2Bitcoin Live Map</title> <title>Road2Bitcoin Live Map</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css">
<script type="module" crossorigin src="/assets/index.098b1da4.js"></script> <script defer data-domain="r2b22.kip.pe" src="https://plausible.io/js/plausible.js"></script>
<script type="module" crossorigin src="/assets/index.b6f9bc24.js"></script>
<link rel="stylesheet" href="/assets/index.eed9f443.css"> <link rel="stylesheet" href="/assets/index.eed9f443.css">
</head> </head>
<body> <body>
<div id="map"><div id="popup"></div></div> <div id="map"><div id="popup"></div></div>
<div id="people"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"></script>
</body> </body>

View File

@@ -6,9 +6,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Road2Bitcoin Live Map</title> <title>Road2Bitcoin Live Map</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css">
<script defer data-domain="r2b22.kip.pe" src="https://plausible.io/js/plausible.js"></script>
</head> </head>
<body> <body>
<div id="map"><div id="popup"></div></div> <div id="map"><div id="popup"></div></div>
<div id="people"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"></script>
<script type="module" src="./main.js"></script> <script type="module" src="./main.js"></script>
</body> </body>

123
main.js
View File

@@ -7,12 +7,14 @@ import Point from 'ol/geom/Point';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer'; import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import {Circle as CircleStyle, Fill, Icon, Stroke, Style} from 'ol/style'; import {Circle as CircleStyle, Fill, Icon, Stroke, Style} from 'ol/style';
import {OSM, Vector as VectorSource} from 'ol/source'; import {OSM, Vector as VectorSource} from 'ol/source';
import {useGeographic, fromLonLat} from 'ol/proj'; import {useGeographic} from 'ol/proj';
import geojsonRoute from './geo/route.json' import geojsonRoute from './data/r2b22-route.json'
import geojsonPOI from './geo/poi.json' import geojsonPOI from './data/r2b22-poi.json';
import geojsonLegacy from './data/legacy-route.json';
useGeographic(); useGeographic();
async function main() {
const styles = { const styles = {
lineOrange: new Style({ lineOrange: new Style({
stroke: new Stroke({ stroke: new Stroke({
@@ -60,7 +62,9 @@ const styles = {
// Route // Route
// //
const lastStageFinished = 1; const tourStatus = await fetch('https://r2b22.kip.pe/status.json').then(res => res.json());
const lastStageFinished = tourStatus.lastStageFinished;
const stagesCompleted = geojsonRoute.features.slice(0, lastStageFinished); const stagesCompleted = geojsonRoute.features.slice(0, lastStageFinished);
const stagesAhead = geojsonRoute.features.slice(lastStageFinished); const stagesAhead = geojsonRoute.features.slice(lastStageFinished);
const vectorSourceStagesCompleted = new VectorSource(); const vectorSourceStagesCompleted = new VectorSource();
@@ -100,8 +104,7 @@ const vectorSourceTrackedPoints = new VectorSource();
const vanFeature= new Feature({ const vanFeature= new Feature({
geometry: new Point([8.918618, 44.407408]), geometry: new Point([8.918618, 44.407408]),
name: 'Support Van', name: 'Support Van'
trackable: true
}); });
vectorSourceTrackedPoints.addFeature(vanFeature); vectorSourceTrackedPoints.addFeature(vanFeature);
@@ -111,6 +114,18 @@ const trackedPointsLayer = new VectorLayer({
style: styles.iconVan style: styles.iconVan
}); });
//
// Legacy routes
//
const vectorSourceLegacy = new VectorSource();
vectorSourceLegacy.addFeatures(new GeoJSON().readFeatures(geojsonLegacy));
const legacyLayer = new VectorLayer({
source: vectorSourceLegacy,
style: styles.lineOrange
});
// //
// Map initialization // Map initialization
// //
@@ -130,6 +145,7 @@ const map = new Map({
}), }),
stagesCompletedLayer, stagesCompletedLayer,
stagesAheadLayer, stagesAheadLayer,
legacyLayer,
poiLayer, poiLayer,
trackedPointsLayer trackedPointsLayer
], ],
@@ -174,15 +190,6 @@ function createPopoverHtml(feature) {
title.textContent = feature.get('name'); title.textContent = feature.get('name');
container.append(title); container.append(title);
return container.innerHTML; return container.innerHTML;
// if (feature.get('trackable')) {
// const linkParent = document.createElement('div');
// const followLink = document.createElement('a');
// followLink.textContent = 'Follow';
// followLink.href = '#';
// followLink.addEventListener('click', startFollowing(feature, followLink));
// linkParent.append(followLink);
// container.append(linkParent);
// }
} }
// display popup on click // display popup on click
@@ -191,9 +198,7 @@ map.on('click', function (evt) {
return feature; return feature;
}); });
disposePopover(); disposePopover();
if (!feature) { if (!feature) return;
return;
}
popup.setPosition(evt.coordinate); popup.setPosition(evt.coordinate);
popover = new bootstrap.Popover(popupEl, { popover = new bootstrap.Popover(popupEl, {
placement: 'top', placement: 'top',
@@ -213,52 +218,69 @@ map.on('pointermove', function (evt) {
// Close the popup when the map is moved // Close the popup when the map is moved
map.on('movestart', disposePopover); map.on('movestart', disposePopover);
// //
// Tracking // Tracking
// //
const updateInterval = 5000; const updateInterval = 10000;
let followedFeature = vanFeature; const peopleOverlays = {};
let followedZoomed = false;
// let followedFeature = null;
function startFollowing(feature, followLink) { function createParticipantHTML (name) {
followedFeature = feature; if (document.getElementById(`user-${name}`)) return;
followLink.textContent = 'Stop following'; const el = document.createElement('img');
// followLink.removeEventListener('click', startFollowing); el.src = `https://r2b22.kip.pe/avatars/${name}.png`;
followLink.addEventListener('click', stopFollowing(feature, followLink)); el.id = `user-${name}`;
el.style = 'width: 40px; height: 40px; border-radius: 20px; cursor: pointer';
document.getElementById('people').append(el);
} }
function stopFollowing(feature, followLink) { function createParticipantOverlay (name) {
followedFeature = null; if (peopleOverlays[name]) return;
followedZoomed = false; const overlayElement = new Overlay({
followLink.textContent = 'Stop following'; stopEvent: false,
// followLink.removeEventListener('click', stopFollowing); positioning: 'center-center',
followLink.addEventListener('click', startFollowing(feature, followLink)); element: document.getElementById(`user-${name}`)
});
peopleOverlays[name] = overlayElement;
map.addOverlay(overlayElement);
}
function isRecentTimestamp (tst) {
// newer than 2 hours ago?
return (tst * 1000) > (Date.now() - 2*60*60*1000);
} }
function updateData(startInterval=false) { function updateData(startInterval=false) {
fetch('https://r2b22.kip.pe/last.json') fetch('https://r2b22.kip.pe/last.json')
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
console.log(data); const vanData = data.find(i => i.name == 'satoshithevan');
const coords = [data.lon, data.lat]; const vanCoords = [vanData.lon, vanData.lat];
vanFeature.getGeometry().setCoordinates(coords); vanFeature.getGeometry().setCoordinates(vanCoords);
// let zoomLevel; for (const item of data) {
// if (!followedZoomed) { if (!tourStatus.participants.includes(item.name)) continue;
// zoomLevel = 13; if (!isRecentTimestamp(item.tst)) continue;
// followedZoomed = true; createParticipantHTML(item.name);
// } createParticipantOverlay(item.name);
const overlay = peopleOverlays[item.name];
overlay.setPosition([item.lon, item.lat]);
// if (followedFeature) { function clickHandler () {
// view.animate({ disposePopover();
// center: followedFeature.getGeometry().getCoordinates(), popup.setPosition([item.lon, item.lat]);
// duration: 500, popover = new bootstrap.Popover(popupEl, {
// zoom: zoomLevel placement: 'top',
// }); html: true,
// } content: `Rider: ${item.name}`
});
popover.show();
}
const avatarEl = document.getElementById(`user-${item.name}`);
avatarEl.removeEventListener('click', clickHandler);
avatarEl.addEventListener('click', clickHandler);
}
}); });
if (startInterval) { if (startInterval) {
@@ -267,3 +289,6 @@ function updateData(startInterval=false) {
} }
updateData(true); updateData(true);
}
main();

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "ol-vite", "name": "ol-vite",
"version": "1.2.0", "version": "1.9.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ol-vite", "name": "ol-vite",
"version": "1.2.0", "version": "1.9.0",
"dependencies": { "dependencies": {
"ol": "latest" "ol": "latest"
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "map", "name": "map",
"version": "1.2.0", "version": "1.9.0",
"scripts": { "scripts": {
"start": "vite", "start": "vite",
"build": "vite build", "build": "vite build",

View File

@@ -4,6 +4,7 @@ html, body {
margin: 0; margin: 0;
height: 100%; height: 100%;
} }
#map { #map {
position: absolute; position: absolute;
top: 0; top: 0;