Migrate to OpenLayers
This commit is contained in:
parent
6c252413d5
commit
248d746a67
170
front/index.js
170
front/index.js
|
@ -1,36 +1,76 @@
|
||||||
const L = require('leaflet');
|
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 color = require('color');
|
||||||
|
|
||||||
require('leaflet/dist/leaflet.css');
|
// Map background
|
||||||
|
const mapboxToken = `pk.eyJ1IjoibWF0dGVvZGVsYWJyZSIsImEiOiJjazUxaWNsdXcwdWhjM2\
|
||||||
|
9tc2xndXJoNGtxIn0.xELwMerqJLFimIqU6RxnZw`;
|
||||||
|
|
||||||
// MAP
|
const backgroundSource = new XYZSource({
|
||||||
const map = L.map('map').setView(
|
url: 'https://api.mapbox.com/' + [
|
||||||
[43.605, 3.88],
|
'styles', 'v1', 'mapbox', 'streets-v11',
|
||||||
/* zoomLevel = */ 13
|
'tiles', '256', '{z}', '{x}', '{y}',
|
||||||
);
|
].join('/') + `?access_token=${mapboxToken}`,
|
||||||
|
});
|
||||||
|
|
||||||
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
|
const backgroundLayer = new TileLayer({
|
||||||
attribution: [
|
source: backgroundSource,
|
||||||
// TODO: Attribution des données TaM
|
});
|
||||||
`Données cartographiques © Contributeurs d’<a href="https://www.open\
|
|
||||||
streetmap.org/">OpenStreetMap</a>, <a href="https://creativecommons.org/\
|
|
||||||
licenses/by-sa/2.0/">CC-BY-SA</a>`,
|
|
||||||
'Imagerie © <a href="https://www.mapbox.com/">Mapbox</a>',
|
|
||||||
].join(' | '),
|
|
||||||
|
|
||||||
id: 'mapbox/streets-v11',
|
// Data overlay
|
||||||
tileSize: 512,
|
const dataSource = new VectorSource();
|
||||||
|
|
||||||
accessToken: 'pk.eyJ1IjoibWF0dGVvZGVsYWJyZSIsImEiOiJjazUxaWNsdXcwdWhjM29tc2xndXJoNGtxIn0.xELwMerqJLFimIqU6RxnZw',
|
const SERVER = window.origin;
|
||||||
|
|
||||||
maxZoom: 19,
|
fetch(SERVER + '/network').then(res => res.json()).then(network =>
|
||||||
zoomSnap: 0,
|
{
|
||||||
zoomOffset: -1,
|
const stopPoints = Object.entries(network.stops)
|
||||||
}).addTo(map);
|
.map(([stopId, stop]) =>
|
||||||
|
{
|
||||||
|
const feature = new Feature({
|
||||||
|
type: 'stop',
|
||||||
|
color: network.lines[stop.lines[0]].color,
|
||||||
|
geometry: new Point(proj.fromLonLat([stop.lon, stop.lat])),
|
||||||
|
});
|
||||||
|
|
||||||
// </MAP>
|
feature.setId(stopId);
|
||||||
//
|
return feature;
|
||||||
// TAM
|
});
|
||||||
|
|
||||||
|
const segmentLines = Object.entries(network.segments)
|
||||||
|
.map(([segmentId, segment]) =>
|
||||||
|
{
|
||||||
|
const feature = new Feature({
|
||||||
|
type: 'segment',
|
||||||
|
color: network.lines[segment.lines[0]].color,
|
||||||
|
geometry: new LineString(segment.nodes.map(
|
||||||
|
({lat, lon}) => proj.fromLonLat([lon, lat])
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
|
||||||
|
feature.setId(segmentId);
|
||||||
|
return feature;
|
||||||
|
});
|
||||||
|
|
||||||
|
dataSource.addFeatures(
|
||||||
|
stopPoints.concat(segmentLines)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const makeBorderColor = mainColor =>
|
const makeBorderColor = mainColor =>
|
||||||
{
|
{
|
||||||
|
@ -39,41 +79,55 @@ const makeBorderColor = mainColor =>
|
||||||
return hsl.hex();
|
return hsl.hex();
|
||||||
};
|
};
|
||||||
|
|
||||||
const SERVER = window.origin;
|
const dataLayer = new VectorLayer({
|
||||||
|
source: dataSource,
|
||||||
fetch(SERVER + '/network').then(res => res.json()).then(network =>
|
style: (feature, resolution) =>
|
||||||
{
|
|
||||||
Object.values(network.segments).forEach(segment =>
|
|
||||||
{
|
{
|
||||||
const color = network.lines[segment.lines[0]].color;
|
if (feature.get('type') === 'stop')
|
||||||
const borderColor = makeBorderColor(color);
|
|
||||||
const nodes = segment.nodes.map(({lat, lon}) => [lat, lon]);
|
|
||||||
|
|
||||||
L.polyline(nodes, {weight: 8, color: borderColor}).addTo(map);
|
|
||||||
const line = L.polyline(nodes, {weight: 6, color}).addTo(map);
|
|
||||||
|
|
||||||
line.bindPopup(`${segment.length} m`);
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.entries(network.stops).forEach(([stopId, stop]) =>
|
|
||||||
{
|
{
|
||||||
const color = network.lines[stop.lines[0]].color;
|
return new Style({
|
||||||
const borderColor = makeBorderColor(color);
|
image: new Circle({
|
||||||
|
fill: new Fill({
|
||||||
const stopMarker = L.circleMarker(
|
color: feature.get('color'),
|
||||||
[stop.lat, stop.lon],
|
}),
|
||||||
{
|
stroke: new Stroke({
|
||||||
fillColor: color,
|
color: makeBorderColor(feature.get('color')),
|
||||||
|
width: 2,
|
||||||
|
}),
|
||||||
radius: 6,
|
radius: 6,
|
||||||
fillOpacity: 1,
|
}),
|
||||||
color: borderColor,
|
|
||||||
weight: 2
|
|
||||||
}
|
|
||||||
).addTo(map);
|
|
||||||
|
|
||||||
stopMarker.bindPopup(`<strong>${stop.name}</strong><br>
|
|
||||||
Arrêt n°${stopId}`);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
console.log(network);
|
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: 'map',
|
||||||
|
layers: [
|
||||||
|
backgroundLayer,
|
||||||
|
dataLayer,
|
||||||
|
],
|
||||||
|
view: new View({
|
||||||
|
center: proj.fromLonLat([3.88, 43.605]),
|
||||||
|
zoom: 13,
|
||||||
|
maxZoom: 22,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/**
|
||||||
|
* Based on https://github.com/mikhailshilkov/leaflet-corridor
|
||||||
|
*/
|
||||||
|
|
||||||
|
const leaflet = require('leaflet');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polyline whose weight is based on a fixed number of meters of thickness
|
||||||
|
* rather than a static number of pixels.
|
||||||
|
*/
|
||||||
|
const Corridor = leaflet.Polyline.extend({
|
||||||
|
initialize(latlngs, options)
|
||||||
|
{
|
||||||
|
leaflet.Polyline.prototype.initialize.call(this, latlngs, options);
|
||||||
|
|
||||||
|
this.corridor = options.corridor;
|
||||||
|
},
|
||||||
|
|
||||||
|
onAdd(map)
|
||||||
|
{
|
||||||
|
leaflet.Polyline.prototype.onAdd.call(this, map);
|
||||||
|
map.on('zoomend', () => this._updateWeight(map));
|
||||||
|
this._updateWeight(map);
|
||||||
|
},
|
||||||
|
|
||||||
|
onRemove(map)
|
||||||
|
{
|
||||||
|
map.off('zoomend', () => this._updateWeight(map));
|
||||||
|
leaflet.Polyline.prototype.onRemove.call(this, map);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the actual line weight to span the requested number of meters
|
||||||
|
* based on the current meter density.
|
||||||
|
*/
|
||||||
|
_updateWeight(map)
|
||||||
|
{
|
||||||
|
this.setStyle({
|
||||||
|
weight: this.corridor / this._getMetersPerPixel(map)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate how many meters are in a pixel at the current zoom level
|
||||||
|
* around the current center point.
|
||||||
|
*
|
||||||
|
* @param map Map to take the zoom level from.
|
||||||
|
* @return Number of meters per pixel.
|
||||||
|
*/
|
||||||
|
_getMetersPerPixel(map)
|
||||||
|
{
|
||||||
|
const centerLatLng = map.getCenter();
|
||||||
|
const centerPixels = map.latLngToContainerPoint(centerLatLng);
|
||||||
|
|
||||||
|
// Place two points left and right of the center
|
||||||
|
const point1 = leaflet.point(centerPixels.x + 5, centerPixels.y);
|
||||||
|
const point2 = leaflet.point(centerPixels.x - 5, centerPixels.y);
|
||||||
|
|
||||||
|
// Convert back to latitude and longitude then compute distance
|
||||||
|
const point1LatLng = map.containerPointToLatLng(point1);
|
||||||
|
const point2LatLng = map.containerPointToLatLng(point2);
|
||||||
|
|
||||||
|
console.log(point1LatLng.distanceTo(point2LatLng) / 10, 'mètres par pixel');
|
||||||
|
return point1LatLng.distanceTo(point2LatLng) / 10;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = (latlngs, options) => new Corridor(
|
||||||
|
latlngs,
|
||||||
|
options || { corridor: 100 }
|
||||||
|
);
|
|
@ -908,6 +908,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
|
||||||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
||||||
},
|
},
|
||||||
|
"@openlayers/pepjs": {
|
||||||
|
"version": "0.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@openlayers/pepjs/-/pepjs-0.5.3.tgz",
|
||||||
|
"integrity": "sha512-Bgvi5c14BS0FJWyYWWFstNEnXsB30nK8Jt8hkAAdqr7E0gDdBBWVDglF3Ub19wTxvgJ/CVHyTY6VuCtnyRzglg=="
|
||||||
|
},
|
||||||
"@parcel/fs": {
|
"@parcel/fs": {
|
||||||
"version": "1.11.0",
|
"version": "1.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-1.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-1.11.0.tgz",
|
||||||
|
@ -5128,6 +5133,17 @@
|
||||||
"has": "^1.0.3"
|
"has": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ol": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ol/-/ol-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-0dL3i3eJqgOpqIjDKEY3grkeQnjAYfV5L/JCxhOu4SxiaizRwFrFgeas6LILRoxKa03jhQFbut2r2bbgcLGQeA==",
|
||||||
|
"requires": {
|
||||||
|
"@openlayers/pepjs": "^0.5.3",
|
||||||
|
"pbf": "3.2.1",
|
||||||
|
"pixelworks": "1.1.0",
|
||||||
|
"rbush": "^3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"on-finished": {
|
"on-finished": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
|
@ -5393,6 +5409,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||||
},
|
},
|
||||||
|
"pbf": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==",
|
||||||
|
"requires": {
|
||||||
|
"ieee754": "^1.1.12",
|
||||||
|
"resolve-protobuf-schema": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pbkdf2": {
|
"pbkdf2": {
|
||||||
"version": "3.0.17",
|
"version": "3.0.17",
|
||||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
|
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
|
||||||
|
@ -5427,6 +5452,11 @@
|
||||||
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
|
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"pixelworks": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pixelworks/-/pixelworks-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-Hwla1I3Ki/ihyCWOAJIDGkTyLKU="
|
||||||
|
},
|
||||||
"pn": {
|
"pn": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
|
||||||
|
@ -5909,6 +5939,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||||
},
|
},
|
||||||
|
"protocol-buffers-schema": {
|
||||||
|
"version": "3.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.3.3.tgz",
|
||||||
|
"integrity": "sha512-jiszJ9Nzo8glRgP+PQ+QdQ/WqoolZLTIGBEG2PBwGWVGbTmq+j4S/3NR1kpoGE+pYXV2HfS8ukxXGKkBnMx7eA=="
|
||||||
|
},
|
||||||
"proxy-addr": {
|
"proxy-addr": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
|
||||||
|
@ -6001,6 +6036,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
|
||||||
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
|
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
|
||||||
},
|
},
|
||||||
|
"quickselect": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
|
||||||
|
},
|
||||||
"quote-stream": {
|
"quote-stream": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz",
|
||||||
|
@ -6044,6 +6084,14 @@
|
||||||
"unpipe": "1.0.0"
|
"unpipe": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rbush": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==",
|
||||||
|
"requires": {
|
||||||
|
"quickselect": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"rc": {
|
"rc": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||||
|
@ -6270,6 +6318,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
|
||||||
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
|
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
|
||||||
},
|
},
|
||||||
|
"resolve-protobuf-schema": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
|
||||||
|
"requires": {
|
||||||
|
"protocol-buffers-schema": "^3.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"resolve-url": {
|
"resolve-url": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"geolib": "^3.2.1",
|
"geolib": "^3.2.1",
|
||||||
"leaflet": "^1.6.0",
|
"leaflet": "^1.6.0",
|
||||||
|
"ol": "^6.1.1",
|
||||||
"parcel-bundler": "^1.12.4",
|
"parcel-bundler": "^1.12.4",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"request-promise-native": "^1.0.8",
|
"request-promise-native": "^1.0.8",
|
||||||
|
|
Loading…
Reference in New Issue