Split front/map.js
This commit is contained in:
		
							parent
							
								
									079fbcf310
								
							
						
					
					
						commit
						dd950a95e8
					
				|  | @ -1,6 +1,6 @@ | ||||||
| // eslint-disable-next-line node/no-extraneous-require
 | // eslint-disable-next-line node/no-extraneous-require
 | ||||||
| require("regenerator-runtime/runtime"); | require("regenerator-runtime/runtime"); | ||||||
| 
 | 
 | ||||||
| const { createMap } = require("./map"); | const map = require("./map/index.js"); | ||||||
| 
 | 
 | ||||||
| createMap(/* map = */ "map"); | map.create(/* map = */ "map"); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,42 @@ | ||||||
|  | const colorModule = require("color"); | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Turn the main color of a line into a color suitable for using as a border. | ||||||
|  |  * @param {string} mainColor Original color. | ||||||
|  |  * @returns {string} Hexadecimal representation of the border color. | ||||||
|  |  */ | ||||||
|  | const makeBorderColor = mainColor => { | ||||||
|  |     const hsl = colorModule(mainColor).hsl(); | ||||||
|  | 
 | ||||||
|  |     hsl.color = Math.max(0, hsl.color[2] -= 20); | ||||||
|  |     return hsl.hex(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | exports.makeBorderColor = makeBorderColor; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Turn the main color of a line into a color suitable for using as a border. | ||||||
|  |  * @param {string} mainColor Original color. | ||||||
|  |  * @returns {string} Hexadecimal representation of the border color. | ||||||
|  |  */ | ||||||
|  | const makeCourseColor = mainColor => { | ||||||
|  |     const hsl = colorModule(mainColor).hsl(); | ||||||
|  | 
 | ||||||
|  |     hsl.color = Math.max(0, hsl.color[2] += 10); | ||||||
|  |     return hsl.hex(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | exports.makeCourseColor = makeCourseColor; | ||||||
|  | 
 | ||||||
|  | const sizes = { | ||||||
|  |     segmentOuter: 8, | ||||||
|  |     segmentInner: 6, | ||||||
|  |     stopRadius: 6, | ||||||
|  |     stopBorder: 1.5, | ||||||
|  |     courseSize: 15, | ||||||
|  |     courseOuterBorder: 13, | ||||||
|  |     courseBorder: 10, | ||||||
|  |     courseInnerBorder: 7 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | exports.sizes = sizes; | ||||||
|  | @ -1,125 +1,15 @@ | ||||||
| require("ol/ol.css"); | require("ol/ol.css"); | ||||||
| 
 | 
 | ||||||
| const { Map, View } = require("ol"); | const { Map, View } = require("ol"); | ||||||
| 
 |  | ||||||
| const GeoJSON = require("ol/format/GeoJSON").default; |  | ||||||
| const reader = new GeoJSON({ featureProjection: "EPSG:3857" }); |  | ||||||
| 
 |  | ||||||
| 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 { getVectorContext } = require("ol/render"); | const { getVectorContext } = require("ol/render"); | ||||||
| 
 |  | ||||||
| const Point = require("ol/geom/Point").default; | const Point = require("ol/geom/Point").default; | ||||||
| 
 |  | ||||||
| const proj = require("ol/proj"); | const proj = require("ol/proj"); | ||||||
| 
 | const { Style, Icon } = require("ol/style"); | ||||||
| const { Style, Fill, Stroke, Circle, Icon } = require("ol/style"); | const tilesLayers = require("./tiles"); | ||||||
| const colorModule = require("color"); | const networkLayers = require("./network"); | ||||||
| 
 | const { sizes, makeBorderColor, makeCourseColor } = require("./common"); | ||||||
| const mapboxToken = "pk.eyJ1IjoibWF0dGVvZGVsYWJyZSIsImEiOiJja2NxaTUyMmUwcmFhMn\ | const network = require("../../tam/network.json"); | ||||||
| h0NmFsdzQ3emxqIn0.cyxF0h36emIMTk3cc4VqUw"; | const simulation = require("../../tam/simulation"); | ||||||
| 
 |  | ||||||
| const simulation = require("../tam/simulation"); |  | ||||||
| const network = require("../tam/network.json"); |  | ||||||
| 
 |  | ||||||
| const lineFeaturesOrder = (feature1, feature2) => { |  | ||||||
|     const lines1 = feature1.get("lines"); |  | ||||||
| 
 |  | ||||||
|     if (lines1.length === 0) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const lines2 = feature2.get("lines"); |  | ||||||
| 
 |  | ||||||
|     if (lines2.length === 0) { |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return Math.min(...lines1) - Math.min(...lines2); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const makeDataSources = () => { |  | ||||||
|     const segmentsSource = new VectorSource(); |  | ||||||
|     const stopsSource = new VectorSource(); |  | ||||||
| 
 |  | ||||||
|     const readFeatures = hash => Object.values(hash).map(json => { |  | ||||||
|         json.properties.lines = json.properties.routes.filter( |  | ||||||
| 
 |  | ||||||
|             // Only consider normal routes (excluding alternate routes)
 |  | ||||||
|             ([lineRef, routeRef]) => |  | ||||||
|                 network.lines[lineRef].routes[routeRef].state === "normal" |  | ||||||
|         ).map(([lineRef]) => lineRef); |  | ||||||
| 
 |  | ||||||
|         if (json.properties.lines.length >= 1) { |  | ||||||
|             json.properties.colors = json.properties.lines.map( |  | ||||||
|                 lineRef => network.lines[lineRef].color |  | ||||||
|             ); |  | ||||||
|         } else { |  | ||||||
|             json.properties.colors = ["#FFFFFF"]; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return reader.readFeature(json); |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     segmentsSource.addFeatures(readFeatures(network.segments)); |  | ||||||
|     stopsSource.addFeatures(readFeatures(network.stops)); |  | ||||||
|     return { segmentsSource, stopsSource }; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const makeBorderColor = mainColor => { |  | ||||||
|     const hsl = colorModule(mainColor).hsl(); |  | ||||||
| 
 |  | ||||||
|     hsl.color = Math.max(0, hsl.color[2] -= 20); |  | ||||||
|     return hsl.hex(); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const makeCourseColor = mainColor => { |  | ||||||
|     const hsl = colorModule(mainColor).hsl(); |  | ||||||
| 
 |  | ||||||
|     hsl.color = Math.max(0, hsl.color[2] += 10); |  | ||||||
|     return hsl.hex(); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const sizes = { |  | ||||||
|     segmentOuter: 8, |  | ||||||
|     segmentInner: 6, |  | ||||||
|     stopRadius: 6, |  | ||||||
|     stopBorder: 1.5, |  | ||||||
|     courseSize: 15, |  | ||||||
|     courseOuterBorder: 13, |  | ||||||
|     courseBorder: 10, |  | ||||||
|     courseInnerBorder: 7 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const segmentBorderStyle = feature => new Style({ |  | ||||||
|     stroke: new Stroke({ |  | ||||||
|         color: makeBorderColor(feature.get("colors")[0]), |  | ||||||
|         width: sizes.segmentOuter |  | ||||||
|     }) |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| const segmentInnerStyle = feature => new Style({ |  | ||||||
|     stroke: new Stroke({ |  | ||||||
|         color: feature.get("colors")[0], |  | ||||||
|         width: sizes.segmentInner |  | ||||||
|     }) |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| const stopStyle = feature => new Style({ |  | ||||||
|     image: new Circle({ |  | ||||||
|         fill: new Fill({ |  | ||||||
|             color: feature.get("colors")[0] |  | ||||||
|         }), |  | ||||||
|         stroke: new Stroke({ |  | ||||||
|             color: makeBorderColor(feature.get("colors")[0]), |  | ||||||
|             width: sizes.stopBorder |  | ||||||
|         }), |  | ||||||
|         radius: sizes.stopRadius |  | ||||||
|     }) |  | ||||||
| }); |  | ||||||
| 
 | 
 | ||||||
| const courseStyles = {}; | const courseStyles = {}; | ||||||
| 
 | 
 | ||||||
|  | @ -169,53 +59,7 @@ const getCourseStyle = lineColor => { | ||||||
|     return courseStyles[lineColor]; |     return courseStyles[lineColor]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const createMap = target => { | const create = 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 |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     // Static data overlay
 |  | ||||||
|     const { segmentsSource, stopsSource } = makeDataSources(); |  | ||||||
| 
 |  | ||||||
|     const segmentsBorderLayer = new VectorLayer({ |  | ||||||
|         source: segmentsSource, |  | ||||||
|         renderOrder: lineFeaturesOrder, |  | ||||||
|         style: segmentBorderStyle, |  | ||||||
| 
 |  | ||||||
|         updateWhileInteracting: true, |  | ||||||
|         updateWhileAnimating: true |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     const segmentsInnerLayer = new VectorLayer({ |  | ||||||
|         source: segmentsSource, |  | ||||||
|         renderOrder: lineFeaturesOrder, |  | ||||||
|         style: segmentInnerStyle, |  | ||||||
| 
 |  | ||||||
|         updateWhileInteracting: true, |  | ||||||
|         updateWhileAnimating: true |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     const stopsLayer = new VectorLayer({ |  | ||||||
|         source: stopsSource, |  | ||||||
|         renderOrder: lineFeaturesOrder, |  | ||||||
|         style: stopStyle, |  | ||||||
| 
 |  | ||||||
|         minZoom: 13, |  | ||||||
|         updateWhileInteracting: true, |  | ||||||
|         updateWhileAnimating: true |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     // Setup map
 |  | ||||||
|     const view = new View({ |     const view = new View({ | ||||||
|         center: proj.fromLonLat([3.88, 43.605]), |         center: proj.fromLonLat([3.88, 43.605]), | ||||||
|         zoom: 14, |         zoom: 14, | ||||||
|  | @ -226,14 +70,14 @@ const createMap = target => { | ||||||
|     const map = new Map({ |     const map = new Map({ | ||||||
|         target, |         target, | ||||||
|         layers: [ |         layers: [ | ||||||
|             backgroundLayer, |             ...tilesLayers.getLayers(), | ||||||
|             segmentsBorderLayer, |             ...networkLayers.getLayers() | ||||||
|             segmentsInnerLayer, |  | ||||||
|             stopsLayer |  | ||||||
|         ], |         ], | ||||||
|         view |         view | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     const stopsLayer = map.getLayers().item(3); | ||||||
|  | 
 | ||||||
|     // Run courses simulation
 |     // Run courses simulation
 | ||||||
|     const simulInstance = simulation.start(); |     const simulInstance = simulation.start(); | ||||||
| 
 | 
 | ||||||
|  | @ -322,4 +166,4 @@ const createMap = target => { | ||||||
|     return map; |     return map; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| exports.createMap = createMap; | exports.create = create; | ||||||
|  | @ -0,0 +1,132 @@ | ||||||
|  | const network = require("../../tam/network.json"); | ||||||
|  | const { makeBorderColor, sizes } = require("./common"); | ||||||
|  | 
 | ||||||
|  | const GeoJSON = require("ol/format/GeoJSON").default; | ||||||
|  | const VectorLayer = require("ol/layer/Vector").default; | ||||||
|  | const VectorSource = require("ol/source/Vector").default; | ||||||
|  | 
 | ||||||
|  | const { Style, Fill, Stroke, Circle } = require("ol/style"); | ||||||
|  | 
 | ||||||
|  | const geojsonReader = new GeoJSON({ featureProjection: "EPSG:3857" }); | ||||||
|  | 
 | ||||||
|  | // Style used for the border of line segments
 | ||||||
|  | const segmentBorderStyle = feature => new Style({ | ||||||
|  |     stroke: new Stroke({ | ||||||
|  |         color: makeBorderColor(feature.get("colors")[0]), | ||||||
|  |         width: sizes.segmentOuter | ||||||
|  |     }) | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // Style used for the inner part of line segments
 | ||||||
|  | const segmentInnerStyle = feature => new Style({ | ||||||
|  |     stroke: new Stroke({ | ||||||
|  |         color: feature.get("colors")[0], | ||||||
|  |         width: sizes.segmentInner | ||||||
|  |     }) | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // Style used for line stops
 | ||||||
|  | const stopStyle = feature => new Style({ | ||||||
|  |     image: new Circle({ | ||||||
|  |         fill: new Fill({ | ||||||
|  |             color: feature.get("colors")[0] | ||||||
|  |         }), | ||||||
|  |         stroke: new Stroke({ | ||||||
|  |             color: makeBorderColor(feature.get("colors")[0]), | ||||||
|  |             width: sizes.stopBorder | ||||||
|  |         }), | ||||||
|  |         radius: sizes.stopRadius | ||||||
|  |     }) | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Order for features related to a network line inside the same layer. | ||||||
|  |  * @param {Feature} feature1 First feature to order. | ||||||
|  |  * @param {Feature} feature2 Second feature to order. | ||||||
|  |  * @returns {number} -1 if `feature1` comes before `feature2`, 1 if | ||||||
|  |  * `feature2` comes before `feature1`, 0 if the order is irrelevant. | ||||||
|  |  */ | ||||||
|  | const lineFeaturesOrder = (feature1, feature2) => { | ||||||
|  |     // Place features with no lines attributed on the background
 | ||||||
|  |     const lines1 = feature1.get("lines"); | ||||||
|  | 
 | ||||||
|  |     if (lines1.length === 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const lines2 = feature2.get("lines"); | ||||||
|  | 
 | ||||||
|  |     if (lines2.length === 0) { | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Draw lines with a lower numeric value first
 | ||||||
|  |     return Math.max(...lines2) - Math.max(...lines1); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Create the list of layers for displaying the transit network. | ||||||
|  |  * @returns {Array.<Layer>} List of map layers. | ||||||
|  |  */ | ||||||
|  | const getLayers = () => { | ||||||
|  |     const segmentsSource = new VectorSource(); | ||||||
|  |     const stopsSource = new VectorSource(); | ||||||
|  | 
 | ||||||
|  |     // Turn GeoJSON stops list and segments list into vector sources
 | ||||||
|  |     const readFeatures = hash => Object.values(hash).map(json => { | ||||||
|  |         json.properties.lines = json.properties.routes.filter( | ||||||
|  | 
 | ||||||
|  |             // Only consider normal routes (excluding alternate routes)
 | ||||||
|  |             ([lineRef, routeRef]) => | ||||||
|  |                 network.lines[lineRef].routes[routeRef].state === "normal" | ||||||
|  |         ).map(([lineRef]) => lineRef); | ||||||
|  | 
 | ||||||
|  |         if (json.properties.lines.length >= 1) { | ||||||
|  |             json.properties.colors = json.properties.lines.map( | ||||||
|  |                 lineRef => network.lines[lineRef].color | ||||||
|  |             ); | ||||||
|  |         } else { | ||||||
|  |             json.properties.colors = ["#FFFFFF"]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return geojsonReader.readFeature(json); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     segmentsSource.addFeatures(readFeatures(network.segments)); | ||||||
|  |     stopsSource.addFeatures(readFeatures(network.stops)); | ||||||
|  | 
 | ||||||
|  |     // Background layer on which the darker borders of line segments are drawn
 | ||||||
|  |     const segmentsBorderLayer = new VectorLayer({ | ||||||
|  |         source: segmentsSource, | ||||||
|  |         renderOrder: lineFeaturesOrder, | ||||||
|  |         style: segmentBorderStyle, | ||||||
|  | 
 | ||||||
|  |         updateWhileInteracting: true, | ||||||
|  |         updateWhileAnimating: true | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // Foreground layer on which the lighter inner part of line segments are
 | ||||||
|  |     // drawn. The two layers are separated so that forks blend nicely together
 | ||||||
|  |     const segmentsInnerLayer = new VectorLayer({ | ||||||
|  |         source: segmentsSource, | ||||||
|  |         renderOrder: lineFeaturesOrder, | ||||||
|  |         style: segmentInnerStyle, | ||||||
|  | 
 | ||||||
|  |         updateWhileInteracting: true, | ||||||
|  |         updateWhileAnimating: true | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     const stopsLayer = new VectorLayer({ | ||||||
|  |         source: stopsSource, | ||||||
|  |         renderOrder: lineFeaturesOrder, | ||||||
|  |         style: stopStyle, | ||||||
|  | 
 | ||||||
|  |         minZoom: 13, | ||||||
|  |         updateWhileInteracting: true, | ||||||
|  |         updateWhileAnimating: true | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return [segmentsBorderLayer, segmentsInnerLayer, stopsLayer]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | exports.getLayers = getLayers; | ||||||
|  | @ -0,0 +1,25 @@ | ||||||
|  | const TileLayer = require("ol/layer/Tile").default; | ||||||
|  | const XYZSource = require("ol/source/XYZ").default; | ||||||
|  | 
 | ||||||
|  | const mapboxToken = "pk.eyJ1IjoibWF0dGVvZGVsYWJyZSIsImEiOiJja2NxaTUyMmUwcmFhMn\ | ||||||
|  | h0NmFsdzQ3emxqIn0.cyxF0h36emIMTk3cc4VqUw"; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Create the list of layers for displaying the background map. | ||||||
|  |  * @returns {Array.<Layer>} List of map layers. | ||||||
|  |  */ | ||||||
|  | const getLayers = () => { | ||||||
|  |     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] | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return [new TileLayer({ | ||||||
|  |         source: backgroundSource | ||||||
|  |     })]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | exports.getLayers = getLayers; | ||||||
		Loading…
	
		Reference in New Issue