Migrate to OpenLayers
This commit is contained in:
		
							parent
							
								
									6c252413d5
								
							
						
					
					
						commit
						248d746a67
					
				
							
								
								
									
										176
									
								
								front/index.js
								
								
								
								
							
							
						
						
									
										176
									
								
								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'); | ||||
| 
 | ||||
| require('leaflet/dist/leaflet.css'); | ||||
| // Map background
 | ||||
| const mapboxToken = `pk.eyJ1IjoibWF0dGVvZGVsYWJyZSIsImEiOiJjazUxaWNsdXcwdWhjM2\
 | ||||
| 9tc2xndXJoNGtxIn0.xELwMerqJLFimIqU6RxnZw`;
 | ||||
| 
 | ||||
| // MAP
 | ||||
| const map = L.map('map').setView( | ||||
|     [43.605, 3.88], | ||||
|     /* zoomLevel = */ 13 | ||||
| ); | ||||
| const backgroundSource = new XYZSource({ | ||||
|     url: 'https://api.mapbox.com/' + [ | ||||
|         'styles', 'v1', 'mapbox', 'streets-v11', | ||||
|         '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}', { | ||||
|     attribution: [ | ||||
|         // 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(' | '), | ||||
| const backgroundLayer = new TileLayer({ | ||||
|     source: backgroundSource, | ||||
| }); | ||||
| 
 | ||||
|     id: 'mapbox/streets-v11', | ||||
|     tileSize: 512, | ||||
| // Data overlay
 | ||||
| const dataSource = new VectorSource(); | ||||
| 
 | ||||
|     accessToken: 'pk.eyJ1IjoibWF0dGVvZGVsYWJyZSIsImEiOiJjazUxaWNsdXcwdWhjM29tc2xndXJoNGtxIn0.xELwMerqJLFimIqU6RxnZw', | ||||
| const SERVER = window.origin; | ||||
| 
 | ||||
|     maxZoom: 19, | ||||
|     zoomSnap: 0, | ||||
|     zoomOffset: -1, | ||||
| }).addTo(map); | ||||
| fetch(SERVER + '/network').then(res => res.json()).then(network => | ||||
| { | ||||
|     const stopPoints = Object.entries(network.stops) | ||||
|         .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>
 | ||||
| //
 | ||||
| // TAM
 | ||||
|             feature.setId(stopId); | ||||
|             return feature; | ||||
|         }); | ||||
| 
 | ||||
|     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 => | ||||
| { | ||||
|  | @ -39,41 +79,55 @@ const makeBorderColor = mainColor => | |||
|     return hsl.hex(); | ||||
| }; | ||||
| 
 | ||||
| const SERVER = window.origin; | ||||
| 
 | ||||
| fetch(SERVER + '/network').then(res => res.json()).then(network => | ||||
| { | ||||
|     Object.values(network.segments).forEach(segment => | ||||
| const dataLayer = new VectorLayer({ | ||||
|     source: dataSource, | ||||
|     style: (feature, resolution) => | ||||
|     { | ||||
|         const color = network.lines[segment.lines[0]].color; | ||||
|         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; | ||||
|         const borderColor = makeBorderColor(color); | ||||
| 
 | ||||
|         const stopMarker = L.circleMarker( | ||||
|             [stop.lat, stop.lon], | ||||
|             { | ||||
|                 fillColor: color, | ||||
|                 radius: 6, | ||||
|                 fillOpacity: 1, | ||||
|                 color: borderColor, | ||||
|                 weight: 2 | ||||
|             } | ||||
|         ).addTo(map); | ||||
| 
 | ||||
|         stopMarker.bindPopup(`<strong>${stop.name}</strong><br>
 | ||||
| Arrêt n°${stopId}`);
 | ||||
|     }); | ||||
| 
 | ||||
|     console.log(network); | ||||
|         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: '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", | ||||
|       "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": { | ||||
|       "version": "1.11.0", | ||||
|       "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-1.11.0.tgz", | ||||
|  | @ -5128,6 +5133,17 @@ | |||
|         "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": { | ||||
|       "version": "2.3.0", | ||||
|       "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", | ||||
|       "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": { | ||||
|       "version": "3.0.17", | ||||
|       "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", | ||||
|  | @ -5427,6 +5452,11 @@ | |||
|       "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "pixelworks": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/pixelworks/-/pixelworks-1.1.0.tgz", | ||||
|       "integrity": "sha1-Hwla1I3Ki/ihyCWOAJIDGkTyLKU=" | ||||
|     }, | ||||
|     "pn": { | ||||
|       "version": "1.1.0", | ||||
|       "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", | ||||
|       "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": { | ||||
|       "version": "2.0.5", | ||||
|       "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", | ||||
|       "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": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", | ||||
|  | @ -6044,6 +6084,14 @@ | |||
|         "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": { | ||||
|       "version": "1.2.8", | ||||
|       "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", | ||||
|       "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": { | ||||
|       "version": "0.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
|     "express": "^4.17.1", | ||||
|     "geolib": "^3.2.1", | ||||
|     "leaflet": "^1.6.0", | ||||
|     "ol": "^6.1.1", | ||||
|     "parcel-bundler": "^1.12.4", | ||||
|     "request": "^2.88.0", | ||||
|     "request-promise-native": "^1.0.8", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue