require('ol/ol.css'); const {Map, View} = require('ol'); const TileLayer = require('ol/layer/Tile').default; const XYZSource = require('ol/source/XYZ').default; const VectorLayer = require('ol/layer/Vector').default; const VectorSource = require('ol/source/Vector').default; const Feature = require('ol/Feature').default; const Point = require('ol/geom/Point').default; const LineString = require('ol/geom/LineString').default; const proj = require('ol/proj'); const {Style, Fill, Stroke, Circle} = require('ol/style'); const color = require('color'); const mapboxToken = 'pk.eyJ1IjoibWF0dGVvZGVsYWJyZSIsImEiOiJjazUxaWNsdXcwdWhjM2\ 9tc2xndXJoNGtxIn0.xELwMerqJLFimIqU6RxnZw'; const server = window.origin; const fetchDataSources = async () => { const dataSource = new VectorSource(); const res = await fetch(`${server}/network`); const network = await res.json(); const stopPoints = Object.entries(network.stops) .map(([stopId, stop]) => new Feature({ type: 'stop', color: network.lines[stop.lines[0]].color, geometry: new Point(proj.fromLonLat([stop.lon, stop.lat])), }) ); dataSource.addFeatures(stopPoints); const segmentLines = Object.values(network.lines) .flatMap(({color, routes}) => routes.map(({segments}) => new Feature({ type: 'segment', color, geometry: new LineString(segments.flat().map( ({lat, lon}) => proj.fromLonLat([lon, lat]) )), }) ) ); dataSource.addFeatures(segmentLines); return dataSource; }; const createMap = async (target) => { // Map background const backgroundSource = new XYZSource({ url: 'https://api.mapbox.com/' + [ 'styles', 'v1', 'mapbox', 'streets-v11', 'tiles', '512', '{z}', '{x}', '{y}', ].join('/') + `?access_token=${mapboxToken}`, tileSize: [512, 512], }); const backgroundLayer = new TileLayer({ source: backgroundSource, }); // Data overlay const dataSource = await fetchDataSources(); const makeBorderColor = mainColor => { const hsl = color(mainColor).hsl(); hsl.color = Math.max(0, hsl.color[2] -= 20); return hsl.hex(); }; const dataLayer = new VectorLayer({ source: dataSource, updateWhileInteracting: true, updateWhileAnimating: true, style: feature => { if (feature.get('type') === 'stop') { return new Style({ image: new Circle({ fill: new Fill({ color: feature.get('color'), }), stroke: new Stroke({ color: makeBorderColor(feature.get('color')), width: 2, }), radius: 6, }), }); } else if (feature.get('type') === 'segment') { return [ new Style({ stroke: new Stroke({ color: makeBorderColor(feature.get('color')), width: 8, }), }), new Style({ stroke: new Stroke({ color: feature.get('color'), width: 6, }), }), ]; } }, }); // Setup map const map = new Map({ target, layers: [ backgroundLayer, dataLayer, ], view: new View({ center: proj.fromLonLat([3.88, 43.605]), zoom: 13, maxZoom: 22, constrainResolution: true, }), }); return map; }; exports.createMap = createMap;