Compare commits

...

29 Commits

Author SHA1 Message Date
Râu Cao
bedb066407
1.10.1 2022-09-17 09:38:35 +02:00
Râu Cao
27c7067c04
Link logo to main page 2022-09-17 09:37:59 +02:00
Râu Cao
6ee5254780
1.10.0 2022-09-16 12:16:54 +02:00
Râu Cao
8bd7535163
Add donation widget 2022-09-16 12:16:25 +02:00
Râu Cao
7a1e4232f4
1.9.1 2022-09-16 11:05:42 +02:00
Râu Cao
cf2c5c0021
Add logo 2022-09-16 11:05:18 +02:00
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
Râu Cao
e00b5576e6
1.2.0 2022-09-06 21:09:39 +02:00
Râu Cao
e06b9d5109
MVP 2022-09-06 21:09:06 +02:00
Râu Cao
b527049acb
1.1.0 2022-09-04 20:55:44 +02:00
Râu Cao
18ea53313f
WIP tracked POIs 2022-09-04 20:53:37 +02:00
Râu Cao
9e839def7a
1.0.1 2022-09-03 11:54:42 +02:00
Râu Cao
47f1fce82d
Use public dir for static assets 2022-09-03 11:54:15 +02:00
21 changed files with 229220 additions and 52776 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

View File

@ -1 +1 @@
.ol-box{box-sizing:border-box;border-radius:2px;border:1.5px solid rgb(179,197,219);background-color:#fff6}.ol-mouse-position{top:8px;right:8px;position:absolute}.ol-scale-line{background:rgba(0,60,136,.3);border-radius:4px;bottom:8px;left:8px;padding:2px;position:absolute}.ol-scale-line-inner{border:1px solid #eee;border-top:none;color:#eee;font-size:10px;text-align:center;margin:1px;will-change:contents,width;transition:all .25s}.ol-scale-singlebar-even{background-color:#000}.ol-scale-singlebar-odd{background-color:#fff}.ol-scale-bar{position:absolute;bottom:8px;left:8px}.ol-scale-step-marker{width:1px;height:15px;background-color:#000;float:right;z-index:10}.ol-scale-step-text{position:absolute;bottom:-5px;font-size:12px;z-index:11;color:#000;text-shadow:-2px 0 #FFFFFF,0 2px #FFFFFF,2px 0 #FFFFFF,0 -2px #FFFFFF}.ol-scale-text{position:absolute;font-size:14px;text-align:center;bottom:25px;color:#000;text-shadow:-2px 0 #FFFFFF,0 2px #FFFFFF,2px 0 #FFFFFF,0 -2px #FFFFFF}.ol-scale-singlebar{position:relative;height:10px;z-index:9;box-sizing:border-box;border:1px solid black}.ol-unsupported{display:none}.ol-viewport,.ol-unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.ol-viewport canvas{all:unset}.ol-selectable{-webkit-touch-callout:default;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ol-grabbing{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.ol-grab{cursor:move;cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.ol-control{position:absolute;background-color:#fff6;border-radius:4px;padding:2px}.ol-control:hover{background-color:#fff9}.ol-zoom{top:.5em;left:.5em}.ol-rotate{top:.5em;right:.5em;transition:opacity .25s linear,visibility 0s linear}.ol-rotate.ol-hidden{opacity:0;visibility:hidden;transition:opacity .25s linear,visibility 0s linear .25s}.ol-zoom-extent{top:4.643em;left:.5em}.ol-full-screen{right:.5em;top:.5em}.ol-control button{display:block;margin:1px;padding:0;color:#fff;font-weight:700;text-decoration:none;font-size:inherit;text-align:center;height:1.375em;width:1.375em;line-height:.4em;background-color:#003c8880;border:none;border-radius:2px}.ol-control button::-moz-focus-inner{border:none;padding:0}.ol-zoom-extent button{line-height:1.4em}.ol-compass{display:block;font-weight:400;font-size:1.2em;will-change:transform}.ol-touch .ol-control button{font-size:1.5em}.ol-touch .ol-zoom-extent{top:5.5em}.ol-control button:hover,.ol-control button:focus{text-decoration:none;background-color:#003c88b3}.ol-zoom .ol-zoom-in{border-radius:2px 2px 0 0}.ol-zoom .ol-zoom-out{border-radius:0 0 2px 2px}.ol-attribution{text-align:right;bottom:.5em;right:.5em;max-width:calc(100% - 1.3em);display:flex;flex-flow:row-reverse;align-items:center}.ol-attribution a{color:#003c88b3;text-decoration:none}.ol-attribution ul{margin:0;padding:1px .5em;color:#000;text-shadow:0 0 2px #fff;font-size:12px}.ol-attribution li{display:inline;list-style:none}.ol-attribution li:not(:last-child):after{content:" "}.ol-attribution img{max-height:2em;max-width:inherit;vertical-align:middle}.ol-attribution button{flex-shrink:0}.ol-attribution.ol-collapsed ul{display:none}.ol-attribution:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-attribution.ol-uncollapsible{bottom:0;right:0;border-radius:4px 0 0}.ol-attribution.ol-uncollapsible img{margin-top:-.2em;max-height:1.6em}.ol-attribution.ol-uncollapsible button{display:none}.ol-zoomslider{top:4.5em;left:.5em;height:200px}.ol-zoomslider button{position:relative;height:10px}.ol-touch .ol-zoomslider{top:5.5em}.ol-overviewmap{left:.5em;bottom:.5em}.ol-overviewmap.ol-uncollapsible{bottom:0;left:0;border-radius:0 4px 0 0}.ol-overviewmap .ol-overviewmap-map,.ol-overviewmap button{display:block}.ol-overviewmap .ol-overviewmap-map{border:1px solid #7b98bc;height:150px;margin:2px;width:150px}.ol-overviewmap:not(.ol-collapsed) button{bottom:2px;left:2px;position:absolute}.ol-overviewmap.ol-collapsed .ol-overviewmap-map,.ol-overviewmap.ol-uncollapsible button{display:none}.ol-overviewmap:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-overviewmap-box{border:2px dotted rgba(0,60,136,.7)}.ol-overviewmap .ol-overviewmap-box:hover{cursor:move}html,body{margin:0;height:100%}#map{position:absolute;top:0;bottom:0;width:100%} .ol-box{box-sizing:border-box;border-radius:2px;border:1.5px solid rgb(179,197,219);background-color:#fff6}.ol-mouse-position{top:8px;right:8px;position:absolute}.ol-scale-line{background:rgba(0,60,136,.3);border-radius:4px;bottom:8px;left:8px;padding:2px;position:absolute}.ol-scale-line-inner{border:1px solid #eee;border-top:none;color:#eee;font-size:10px;text-align:center;margin:1px;will-change:contents,width;transition:all .25s}.ol-scale-singlebar-even{background-color:#000}.ol-scale-singlebar-odd{background-color:#fff}.ol-scale-bar{position:absolute;bottom:8px;left:8px}.ol-scale-step-marker{width:1px;height:15px;background-color:#000;float:right;z-index:10}.ol-scale-step-text{position:absolute;bottom:-5px;font-size:12px;z-index:11;color:#000;text-shadow:-2px 0 #FFFFFF,0 2px #FFFFFF,2px 0 #FFFFFF,0 -2px #FFFFFF}.ol-scale-text{position:absolute;font-size:14px;text-align:center;bottom:25px;color:#000;text-shadow:-2px 0 #FFFFFF,0 2px #FFFFFF,2px 0 #FFFFFF,0 -2px #FFFFFF}.ol-scale-singlebar{position:relative;height:10px;z-index:9;box-sizing:border-box;border:1px solid black}.ol-unsupported{display:none}.ol-viewport,.ol-unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.ol-viewport canvas{all:unset}.ol-selectable{-webkit-touch-callout:default;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ol-grabbing{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.ol-grab{cursor:move;cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.ol-control{position:absolute;background-color:#fff6;border-radius:4px;padding:2px}.ol-control:hover{background-color:#fff9}.ol-zoom{top:.5em;left:.5em}.ol-rotate{top:.5em;right:.5em;transition:opacity .25s linear,visibility 0s linear}.ol-rotate.ol-hidden{opacity:0;visibility:hidden;transition:opacity .25s linear,visibility 0s linear .25s}.ol-zoom-extent{top:4.643em;left:.5em}.ol-full-screen{right:.5em;top:.5em}.ol-control button{display:block;margin:1px;padding:0;color:#fff;font-weight:700;text-decoration:none;font-size:inherit;text-align:center;height:1.375em;width:1.375em;line-height:.4em;background-color:#003c8880;border:none;border-radius:2px}.ol-control button::-moz-focus-inner{border:none;padding:0}.ol-zoom-extent button{line-height:1.4em}.ol-compass{display:block;font-weight:400;font-size:1.2em;will-change:transform}.ol-touch .ol-control button{font-size:1.5em}.ol-touch .ol-zoom-extent{top:5.5em}.ol-control button:hover,.ol-control button:focus{text-decoration:none;background-color:#003c88b3}.ol-zoom .ol-zoom-in{border-radius:2px 2px 0 0}.ol-zoom .ol-zoom-out{border-radius:0 0 2px 2px}.ol-attribution{text-align:right;bottom:.5em;right:.5em;max-width:calc(100% - 1.3em);display:flex;flex-flow:row-reverse;align-items:center}.ol-attribution a{color:#003c88b3;text-decoration:none}.ol-attribution ul{margin:0;padding:1px .5em;color:#000;text-shadow:0 0 2px #fff;font-size:12px}.ol-attribution li{display:inline;list-style:none}.ol-attribution li:not(:last-child):after{content:" "}.ol-attribution img{max-height:2em;max-width:inherit;vertical-align:middle}.ol-attribution button{flex-shrink:0}.ol-attribution.ol-collapsed ul{display:none}.ol-attribution:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-attribution.ol-uncollapsible{bottom:0;right:0;border-radius:4px 0 0}.ol-attribution.ol-uncollapsible img{margin-top:-.2em;max-height:1.6em}.ol-attribution.ol-uncollapsible button{display:none}.ol-zoomslider{top:4.5em;left:.5em;height:200px}.ol-zoomslider button{position:relative;height:10px}.ol-touch .ol-zoomslider{top:5.5em}.ol-overviewmap{left:.5em;bottom:.5em}.ol-overviewmap.ol-uncollapsible{bottom:0;left:0;border-radius:0 4px 0 0}.ol-overviewmap .ol-overviewmap-map,.ol-overviewmap button{display:block}.ol-overviewmap .ol-overviewmap-map{border:1px solid #7b98bc;height:150px;margin:2px;width:150px}.ol-overviewmap:not(.ol-collapsed) button{bottom:2px;left:2px;position:absolute}.ol-overviewmap.ol-collapsed .ol-overviewmap-map,.ol-overviewmap.ol-uncollapsible button{display:none}.ol-overviewmap:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-overviewmap-box{border:2px dotted rgba(0,60,136,.7)}.ol-overviewmap .ol-overviewmap-box:hover{cursor:move}html,body{margin:0;height:100%}#map{position:absolute;top:0;bottom:0;width:100%}img#logo{position:absolute;top:10px;right:10px;width:256px;height:auto}lightning-widget{display:block;position:absolute;bottom:10px;left:10px;width:200px}@media only screen and (max-width: 600px){lightning-widget{display:block;position:absolute;right:10px;width:auto}}

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

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
dist/img/r2b-logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

13
dist/index.html vendored
View File

@ -6,12 +6,21 @@
<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.7920774d.js"></script> <script defer data-domain="r2b22.kip.pe" src="https://plausible.io/js/plausible.js"></script>
<link rel="stylesheet" href="/assets/index.eed9f443.css"> <script type="module" crossorigin src="/assets/index.b6f9bc24.js"></script>
<link rel="stylesheet" href="/assets/index.a04fc84d.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>
<a href="https://teamsatoshi.org/wiki/Road2Bitcoin_2022" title="Road2Bitcoin 2022"><img id="logo" src="/img/r2b-logo.png"></a>
<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>
<lightning-widget name=""
accent="#e01b24"
to="team-satoshi@kosmos.org"
image="" />
<script src="/js/lightning-widget.js"></script>
</body> </body>
</html> </html>

2
dist/js/lightning-widget.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -6,10 +6,19 @@
<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>
<a href="https://teamsatoshi.org/wiki/Road2Bitcoin_2022" title="Road2Bitcoin 2022"><img id="logo" src="/img/r2b-logo.png"></a>
<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>
<lightning-widget name=""
accent="#e01b24"
to="team-satoshi@kosmos.org"
image="" />
<script src="/js/lightning-widget.js"></script>
</body> </body>
</html> </html>

438
main.js
View File

@ -8,175 +8,287 @@ 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} 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();
const styles = { async function main() {
lineOrange: new Style({ const styles = {
stroke: new Stroke({ lineOrange: new Style({
color: '#FF9900',
// lineDash: [8],
width: 5,
}),
}),
lineGrey: new Style({
stroke: new Stroke({
color: '#555555',
// lineDash: [8],
width: 5,
}),
}),
iconStop: new Style({
image: new Icon({
anchor: [0.5, 46],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: 'data/icon.png',
}),
}),
iconVan: new Style({
image: new Icon({
anchor: [0.5, 16],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: 'data/van-100px.png',
}),
}),
circleBlack: new Style({
image: new CircleStyle({
radius: 7,
fill: new Fill({color: '#FF9900'}),
stroke: new Stroke({ stroke: new Stroke({
color: 'white', color: '#FF9900',
width: 2, // lineDash: [8],
width: 5,
}), }),
})
})
}
//
// Route
//
const lastStageFinished = 4;
const stagesCompleted = geojsonRoute.features.slice(0, lastStageFinished);
const stagesAhead = geojsonRoute.features.slice(lastStageFinished);
const vectorSourceStagesCompleted = new VectorSource();
const vectorSourceStagesAhead = new VectorSource();
for (const stage of stagesCompleted) {
vectorSourceStagesCompleted.addFeature(new GeoJSON().readFeature(stage));
}
for (const stage of stagesAhead) {
vectorSourceStagesAhead.addFeature(new GeoJSON().readFeature(stage));
}
const stagesCompletedLayer = new VectorLayer({
source: vectorSourceStagesCompleted,
style: styles.lineOrange
});
const stagesAheadLayer = new VectorLayer({
source: vectorSourceStagesAhead,
style: styles.lineGrey
});
//
// Points of Interest
//
const vectorSourcePOI = new VectorSource({
features: new GeoJSON().readFeatures(geojsonPOI),
});
const poiLayer = new VectorLayer({
source: vectorSourcePOI,
style: styles.circleBlack,
});
const vectorSourceVan = new VectorSource();
const vanFeature= new Feature({
geometry: new Point([ 12.498556, 45.780383 ]),
name: 'Support Van'
});
vectorSourceVan.addFeature(vanFeature);
const vanLayer = new VectorLayer({
source: vectorSourceVan,
style: styles.iconVan
});
//
// Map initialization
//
const view = new View({
center: [10.6, 46.9],
zoom: 6.6
})
const map = new Map({
target: 'map',
layers: [
new TileLayer({
source: new OSM()
}), }),
stagesCompletedLayer, lineGrey: new Style({
stagesAheadLayer, stroke: new Stroke({
poiLayer, color: '#555555',
vanLayer // lineDash: [8],
], width: 5,
view: view }),
}); }),
iconStop: new Style({
// image: new Icon({
// Popups anchor: [0.5, 46],
// anchorXUnits: 'fraction',
const popupEl = document.getElementById('popup'); anchorYUnits: 'pixels',
src: '/img/icon.png',
const popup = new Overlay({ }),
element: popupEl, }),
positioning: 'bottom-center', iconVan: new Style({
stopEvent: false, image: new Icon({
}); anchor: [0.5, 16],
map.addOverlay(popup); anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
let popover; src: '/img/van-100px.png',
function disposePopover() { }),
if (popover) { }),
popover.dispose(); circleBlack: new Style({
popover = undefined; image: new CircleStyle({
radius: 7,
fill: new Fill({color: '#FF9900'}),
stroke: new Stroke({
color: 'white',
width: 2,
}),
})
})
} }
//
// Route
//
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 stagesAhead = geojsonRoute.features.slice(lastStageFinished);
const vectorSourceStagesCompleted = new VectorSource();
const vectorSourceStagesAhead = new VectorSource();
for (const stage of stagesCompleted) {
vectorSourceStagesCompleted.addFeature(new GeoJSON().readFeature(stage));
}
for (const stage of stagesAhead) {
vectorSourceStagesAhead.addFeature(new GeoJSON().readFeature(stage));
}
const stagesCompletedLayer = new VectorLayer({
source: vectorSourceStagesCompleted,
style: styles.lineOrange
});
const stagesAheadLayer = new VectorLayer({
source: vectorSourceStagesAhead,
style: styles.lineGrey
});
//
// Points of Interest
//
const vectorSourcePOI = new VectorSource({
features: new GeoJSON().readFeatures(geojsonPOI),
});
const poiLayer = new VectorLayer({
source: vectorSourcePOI,
style: styles.circleBlack,
});
const vectorSourceTrackedPoints = new VectorSource();
const vanFeature= new Feature({
geometry: new Point([8.918618, 44.407408]),
name: 'Support Van'
});
vectorSourceTrackedPoints.addFeature(vanFeature);
const trackedPointsLayer = new VectorLayer({
source: vectorSourceTrackedPoints,
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
//
const view = new View({
center: [10.6, 46.9],
zoom: 6.6
})
window.view = view;
const map = new Map({
target: 'map',
layers: [
new TileLayer({
source: new OSM()
}),
stagesCompletedLayer,
stagesAheadLayer,
legacyLayer,
poiLayer,
trackedPointsLayer
],
view: view
});
//
// Center map on current/next stage
//
setTimeout(() => {
const nextStageFeature = new GeoJSON().readFeature(stagesAhead[0]);
view.fit(nextStageFeature.getGeometry(), {
maxZoom: 10,
duration: 1000
});
}, 3000);
//
// Popups
//
const popupEl = document.getElementById('popup');
const popup = new Overlay({
element: popupEl,
positioning: 'bottom-center',
stopEvent: false,
});
map.addOverlay(popup);
let popover;
function disposePopover() {
if (popover) {
popover.dispose();
popover = undefined;
}
}
function createPopoverHtml(feature) {
const container = document.createElement('div');
const title = document.createElement('div');
title.textContent = feature.get('name');
container.append(title);
return container.innerHTML;
}
// display popup on click
map.on('click', function (evt) {
const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
return feature;
});
disposePopover();
if (!feature) return;
popup.setPosition(evt.coordinate);
popover = new bootstrap.Popover(popupEl, {
placement: 'top',
html: true,
content: createPopoverHtml(feature)
});
popover.show();
});
// change mouse cursor when over marker
map.on('pointermove', function (evt) {
map.getTargetElement().style.cursor = map.hasFeatureAtPixel(evt.pixel)
? 'pointer'
: '';
});
// Close the popup when the map is moved
map.on('movestart', disposePopover);
//
// Tracking
//
const updateInterval = 10000;
const peopleOverlays = {};
function createParticipantHTML (name) {
if (document.getElementById(`user-${name}`)) return;
const el = document.createElement('img');
el.src = `https://r2b22.kip.pe/avatars/${name}.png`;
el.id = `user-${name}`;
el.style = 'width: 40px; height: 40px; border-radius: 20px; cursor: pointer';
document.getElementById('people').append(el);
}
function createParticipantOverlay (name) {
if (peopleOverlays[name]) return;
const overlayElement = new Overlay({
stopEvent: false,
positioning: 'center-center',
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) {
fetch('https://r2b22.kip.pe/last.json')
.then(response => response.json())
.then(data => {
const vanData = data.find(i => i.name == 'satoshithevan');
const vanCoords = [vanData.lon, vanData.lat];
vanFeature.getGeometry().setCoordinates(vanCoords);
for (const item of data) {
if (!tourStatus.participants.includes(item.name)) continue;
if (!isRecentTimestamp(item.tst)) continue;
createParticipantHTML(item.name);
createParticipantOverlay(item.name);
const overlay = peopleOverlays[item.name];
overlay.setPosition([item.lon, item.lat]);
function clickHandler () {
disposePopover();
popup.setPosition([item.lon, item.lat]);
popover = new bootstrap.Popover(popupEl, {
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) {
setInterval(updateData, updateInterval);
}
}
updateData(true);
} }
// display popup on click main();
map.on('click', function (evt) {
const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
return feature;
});
disposePopover();
if (!feature) {
return;
}
popup.setPosition(evt.coordinate);
popover = new bootstrap.Popover(popupEl, {
placement: 'top',
html: true,
content: feature.get('name'),
});
popover.show();
});
// change mouse cursor when over marker
map.on('pointermove', function (evt) {
map.getTargetElement().style.cursor = map.hasFeatureAtPixel(evt.pixel)
? 'pointer'
: '';
});
// Close the popup when the map is moved
map.on('movestart', disposePopover);

4
package-lock.json generated
View File

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

View File

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

BIN
public/img/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
public/img/r2b-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/img/van-100px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

File diff suppressed because one or more lines are too long

View File

@ -4,9 +4,35 @@ html, body {
margin: 0; margin: 0;
height: 100%; height: 100%;
} }
#map { #map {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 100%; width: 100%;
} }
img#logo {
position: absolute;
top: 10px;
right: 10px;
width: 256px;
height: auto;
}
lightning-widget {
display: block;
position: absolute;
bottom: 10px;
left: 10px;
width: 200px;
}
@media only screen and (max-width: 600px) {
lightning-widget {
display: block;
position: absolute;
right: 10px;
width: auto;
}
}