13 Commits

Author SHA1 Message Date
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
11 changed files with 46600 additions and 52773 deletions
-11
View File
File diff suppressed because one or more lines are too long
+11
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+2 -1
View File
@@ -6,7 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Road2Bitcoin Live Map</title>
<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.1d1993b1.js"></script>
<script defer data-domain="r2b22.kip.pe" src="https://plausible.io/js/plausible.js"></script>
<script type="module" crossorigin src="/assets/index.892cac62.js"></script>
<link rel="stylesheet" href="/assets/index.eed9f443.css">
</head>
<body>
+46326 -52595
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Road2Bitcoin Live Map</title>
<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>
<body>
<div id="map"><div id="popup"></div></div>
+256 -162
View File
@@ -7,176 +7,270 @@ import Point from 'ol/geom/Point';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import {Circle as CircleStyle, Fill, Icon, Stroke, Style} from 'ol/style';
import {OSM, Vector as VectorSource} from 'ol/source';
import {useGeographic} from 'ol/proj';
import {useGeographic, fromLonLat} from 'ol/proj';
import geojsonRoute from './geo/route.json'
import geojsonPOI from './geo/poi.json'
useGeographic();
const styles = {
lineOrange: new Style({
stroke: new Stroke({
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: '/img/icon.png',
}),
}),
iconVan: new Style({
image: new Icon({
anchor: [0.5, 16],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: '/img/van-100px.png',
}),
}),
circleBlack: new Style({
image: new CircleStyle({
radius: 7,
fill: new Fill({color: '#FF9900'}),
async function main() {
const styles = {
lineOrange: new Style({
stroke: new Stroke({
color: 'white',
width: 2,
color: '#FF9900',
// 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,
stagesAheadLayer,
poiLayer,
vanLayer
],
view: view
});
//
// 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;
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: '/img/icon.png',
}),
}),
iconVan: new Style({
image: new Icon({
anchor: [0.5, 16],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: '/img/van-100px.png',
}),
}),
circleBlack: new Style({
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',
trackable: true
});
vectorSourceTrackedPoints.addFeature(vanFeature);
const trackedPointsLayer = new VectorLayer({
source: vectorSourceTrackedPoints,
style: styles.iconVan
});
//
// 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,
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;
// 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
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 = 5000;
// let followedFeature = vanFeature;
// let followedZoomed = false;
// let followedFeature = null;
function startFollowing(feature, followLink) {
followedFeature = feature;
followLink.textContent = 'Stop following';
// followLink.removeEventListener('click', startFollowing);
followLink.addEventListener('click', stopFollowing(feature, followLink));
}
function stopFollowing(feature, followLink) {
followedFeature = null;
followedZoomed = false;
followLink.textContent = 'Stop following';
// followLink.removeEventListener('click', stopFollowing);
followLink.addEventListener('click', startFollowing(feature, followLink));
}
function updateData(startInterval=false) {
fetch('https://r2b22.kip.pe/last.json')
.then(response => response.json())
.then(data => {
console.log(data);
const van_data = data.find(i => i.name == 'satoshithevan');
const van_coords = [van_data.lon, van_data.lat];
vanFeature.getGeometry().setCoordinates(van_coords);
// let zoomLevel;
// if (!followedZoomed) {
// zoomLevel = 13;
// followedZoomed = true;
// }
// if (followedFeature) {
// view.animate({
// center: followedFeature.getGeometry().getCoordinates(),
// duration: 500,
// zoom: zoomLevel
// });
// }
});
if (startInterval) {
setInterval(updateData, updateInterval);
}
}
updateData(true);
}
// 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: 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);
main();
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "ol-vite",
"version": "1.0.1",
"version": "1.5.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ol-vite",
"version": "1.0.1",
"version": "1.5.0",
"dependencies": {
"ol": "latest"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "map",
"version": "1.0.1",
"version": "1.5.0",
"scripts": {
"start": "vite",
"build": "vite build",