diff --git a/main.js b/main.js index 2339cfb..51cc516 100644 --- a/main.js +++ b/main.js @@ -13,257 +13,263 @@ 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 = 2; -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; + 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, + }), + }) + }) } -} -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); - // } -} + // + // Route + // -// display popup on click -map.on('click', function (evt) { - const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) { - return feature; - }); - disposePopover(); - if (!feature) { - return; + 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)); } - popup.setPosition(evt.coordinate); - popover = new bootstrap.Popover(popupEl, { - placement: 'top', - html: true, - content: createPopoverHtml(feature) + for (const stage of stagesAhead) { + vectorSourceStagesAhead.addFeature(new GeoJSON().readFeature(stage)); + } + + const stagesCompletedLayer = new VectorLayer({ + source: vectorSourceStagesCompleted, + style: styles.lineOrange }); - popover.show(); -}); -// change mouse cursor when over marker -map.on('pointermove', function (evt) { - map.getTargetElement().style.cursor = map.hasFeatureAtPixel(evt.pixel) - ? 'pointer' - : ''; -}); + const stagesAheadLayer = new VectorLayer({ + source: vectorSourceStagesAhead, + style: styles.lineGrey + }); -// Close the popup when the map is moved -map.on('movestart', disposePopover); + // + // Points of Interest + // + const vectorSourcePOI = new VectorSource({ + features: new GeoJSON().readFeatures(geojsonPOI), + }); -// -// Tracking -// + const poiLayer = new VectorLayer({ + source: vectorSourcePOI, + style: styles.circleBlack, + }); -const updateInterval = 5000; -let followedFeature = vanFeature; -let followedZoomed = false; -// let followedFeature = null; + const vectorSourceTrackedPoints = new VectorSource(); -function startFollowing(feature, followLink) { - followedFeature = feature; - followLink.textContent = 'Stop following'; - // followLink.removeEventListener('click', startFollowing); - followLink.addEventListener('click', stopFollowing(feature, followLink)); -} + const vanFeature= new Feature({ + geometry: new Point([8.918618, 44.407408]), + name: 'Support Van', + trackable: true + }); -function stopFollowing(feature, followLink) { - followedFeature = null; - followedZoomed = false; - followLink.textContent = 'Stop following'; - // followLink.removeEventListener('click', stopFollowing); - followLink.addEventListener('click', startFollowing(feature, followLink)); -} + vectorSourceTrackedPoints.addFeature(vanFeature); -function updateData(startInterval=false) { - fetch('https://r2b22.kip.pe/last.json') - .then(response => response.json()) - .then(data => { - console.log(data); - const coords = [data.lon, data.lat]; - vanFeature.getGeometry().setCoordinates(coords); + const trackedPointsLayer = new VectorLayer({ + source: vectorSourceTrackedPoints, + style: styles.iconVan + }); - // let zoomLevel; - // if (!followedZoomed) { - // zoomLevel = 13; - // followedZoomed = true; - // } + // + // Map initialization + // - // if (followedFeature) { - // view.animate({ - // center: followedFeature.getGeometry().getCoordinates(), - // duration: 500, - // zoom: zoomLevel - // }); - // } + 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); - if (startInterval) { - setInterval(updateData, updateInterval); + // + // 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 coords = [data.lon, data.lat]; + vanFeature.getGeometry().setCoordinates(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); } -updateData(true); +main();