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
 | ||||
| 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"); | ||||
| 
 | ||||
| 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 Point = require("ol/geom/Point").default; | ||||
| 
 | ||||
| const proj = require("ol/proj"); | ||||
| 
 | ||||
| const { Style, Fill, Stroke, Circle, Icon } = require("ol/style"); | ||||
| const colorModule = require("color"); | ||||
| 
 | ||||
| const mapboxToken = "pk.eyJ1IjoibWF0dGVvZGVsYWJyZSIsImEiOiJja2NxaTUyMmUwcmFhMn\ | ||||
| h0NmFsdzQ3emxqIn0.cyxF0h36emIMTk3cc4VqUw"; | ||||
| 
 | ||||
| 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 { Style, Icon } = require("ol/style"); | ||||
| const tilesLayers = require("./tiles"); | ||||
| const networkLayers = require("./network"); | ||||
| const { sizes, makeBorderColor, makeCourseColor } = require("./common"); | ||||
| const network = require("../../tam/network.json"); | ||||
| const simulation = require("../../tam/simulation"); | ||||
| 
 | ||||
| const courseStyles = {}; | ||||
| 
 | ||||
|  | @ -169,53 +59,7 @@ const getCourseStyle = lineColor => { | |||
|     return courseStyles[lineColor]; | ||||
| }; | ||||
| 
 | ||||
| const createMap = 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 create = target => { | ||||
|     const view = new View({ | ||||
|         center: proj.fromLonLat([3.88, 43.605]), | ||||
|         zoom: 14, | ||||
|  | @ -226,14 +70,14 @@ const createMap = target => { | |||
|     const map = new Map({ | ||||
|         target, | ||||
|         layers: [ | ||||
|             backgroundLayer, | ||||
|             segmentsBorderLayer, | ||||
|             segmentsInnerLayer, | ||||
|             stopsLayer | ||||
|             ...tilesLayers.getLayers(), | ||||
|             ...networkLayers.getLayers() | ||||
|         ], | ||||
|         view | ||||
|     }); | ||||
| 
 | ||||
|     const stopsLayer = map.getLayers().item(3); | ||||
| 
 | ||||
|     // Run courses simulation
 | ||||
|     const simulInstance = simulation.start(); | ||||
| 
 | ||||
|  | @ -322,4 +166,4 @@ const createMap = target => { | |||
|     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