Split network into segments and report distance
This commit is contained in:
		
							parent
							
								
									082270831d
								
							
						
					
					
						commit
						8e98819c38
					
				|  | @ -12,7 +12,8 @@ | |||
|  * the `script/update-network` script. | ||||
|  */ | ||||
| 
 | ||||
| const {choosePlural, joinSentence} = require('../util'); | ||||
| const geolib = require('geolib'); | ||||
| const util = require('../util'); | ||||
| const osm = require('./sources/osm'); | ||||
| const tam = require('./sources/tam'); | ||||
| 
 | ||||
|  | @ -163,6 +164,9 @@ out body qt; | |||
|     // All transport lines of the network
 | ||||
|     const lines = {}; | ||||
| 
 | ||||
|     // All segments leading from one stop to another
 | ||||
|     const segments = {}; | ||||
| 
 | ||||
|     for (let routeMaster of routeMasters) | ||||
|     { | ||||
|         const lineRef = routeMaster.tags.ref; | ||||
|  | @ -236,9 +240,9 @@ URI: ${osm.viewNode(stop.id)} | |||
|                             { | ||||
|                                 console.warn(`\
 | ||||
|     — Stop ${candidate.stopRef} with name “${candidate.name}” used by \ | ||||
| ${choosePlural(candidate.lines.length, 'line', '.s')} \ | ||||
| ${joinSentence(Array.from(candidate.lines), ', ', ' and ')} going to \ | ||||
| ${joinSentence(Array.from(candidate.directions), ', ', ' or ')} | ||||
| ${util.choosePlural(candidate.lines.length, 'line', '.s')} \ | ||||
| ${util.joinSentence(Array.from(candidate.lines), ', ', ' and ')} going to \ | ||||
| ${util.joinSentence(Array.from(candidate.directions), ', ', ' or ')} | ||||
|       Apply in JOSM: ${osm.addTagsToNode(stop.id, ['ref=' + candidate.stopRef])} | ||||
| `);
 | ||||
|                             } | ||||
|  | @ -368,22 +372,65 @@ ${name} is one-way and cannot be used in reverse.`); | |||
|             } | ||||
| 
 | ||||
|             // Split the path into segments between stops
 | ||||
|             const segments = []; | ||||
| 
 | ||||
|             for (let stopIndex = 0; stopIndex + 1 < stops.length; ++stopIndex) | ||||
|             { | ||||
|                 segments.push(path.slice( | ||||
|                 const begin = elements[stops[stopIndex]].tags.ref; | ||||
|                 const end = elements[stops[stopIndex + 1]].tags.ref; | ||||
| 
 | ||||
|                 const id = `${begin}-${end}`; | ||||
|                 const nodes = path.slice( | ||||
|                     path.indexOf(stops[stopIndex]), | ||||
|                     path.indexOf(stops[stopIndex + 1]) + 1, | ||||
|                 ).map(id => ({ | ||||
|                     lat: elements[id].lat, | ||||
|                     lon: elements[id].lon | ||||
|                 }))); | ||||
|                 ); | ||||
| 
 | ||||
|                 if (id in segments) | ||||
|                 { | ||||
|                     if (!util.arraysEqual(nodes, segments[id].nodes)) | ||||
|                     { | ||||
|                         throw new Error(`Segment ${id} is defined as a
 | ||||
| different sequence of nodes in two or more lines.`);
 | ||||
|                     } | ||||
| 
 | ||||
|                     segments[id].lines.add(lineRef); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     const points = nodes.map(id => ({ | ||||
|                         lat: elements[id].lat, | ||||
|                         lon: elements[id].lon | ||||
|                     })); | ||||
| 
 | ||||
|                     if (points.length) | ||||
|                     { | ||||
|                         // Augment each point with the distance to the start
 | ||||
|                         points[0].distance = 0; | ||||
| 
 | ||||
|                         for (let i = 1; i < points.length; ++i) | ||||
|                         { | ||||
|                             const len = geolib.getPreciseDistance( | ||||
|                                 ...[points[i - 1], points[i]] | ||||
|                                     .map(({lat, lon}) => ({ | ||||
|                                         latitude: lat, | ||||
|                                         longitude: lon | ||||
|                                     })), | ||||
|                             ); | ||||
| 
 | ||||
|                             points[i].distance = points[i - 1].distance + len; | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     segments[id] = { | ||||
|                         // Keep track of the original sequence of nodes to
 | ||||
|                         // compare with duplicates
 | ||||
|                         nodes, | ||||
|                         points, | ||||
|                         lines: new Set([lineRef]), | ||||
|                     }; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             routes.push({ | ||||
|                 from, to, name, | ||||
|                 segments, | ||||
|                 stops: stops.map(id => elements[id].tags.ref), | ||||
|             }); | ||||
|         } | ||||
|  | @ -394,7 +441,13 @@ ${name} is one-way and cannot be used in reverse.`); | |||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     return {stops, lines}; | ||||
|     // Remove OSM nodes from segments that were only used for checking validity
 | ||||
|     for (let segment of Object.values(segments)) | ||||
|     { | ||||
|         delete segment.nodes; | ||||
|     } | ||||
| 
 | ||||
|     return {stops, lines, segments}; | ||||
| }; | ||||
| 
 | ||||
| exports.fetch = fetch; | ||||
|  |  | |||
							
								
								
									
										55142
									
								
								back/data/network.json
								
								
								
								
							
							
						
						
									
										55142
									
								
								back/data/network.json
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -22,8 +22,10 @@ const {isObject} = require('../../util'); | |||
| const runQuery = ( | ||||
|     query, | ||||
|     endpoint = 'https://lz4.overpass-api.de/api/interpreter' | ||||
| ) => axios.post(endpoint, 'data=' + query) | ||||
|     .then(res => res.data); | ||||
| ) => ( | ||||
|     axios.post(endpoint, 'data=' + query) | ||||
|     .then(res => res.data) | ||||
| ); | ||||
| 
 | ||||
| exports.runQuery = runQuery; | ||||
| 
 | ||||
|  | @ -37,12 +39,13 @@ exports.runQuery = runQuery; | |||
|  * @param tags List of tags to add, in the `key=value` format. | ||||
|  * @return Link for remotely adding the tags. | ||||
|  */ | ||||
| const addTagsToNode = (id, tags) => | ||||
| const addTagsToNode = (id, tags) => ( | ||||
|     'http://127.0.0.1:8111/load_object?' + [ | ||||
|         `objects=n${id}`, | ||||
|         'new_layer=false', | ||||
|         'addtags=' + tags.join('%7C'), | ||||
|     ].join('&'); | ||||
|     ].join('&') | ||||
| ); | ||||
| 
 | ||||
| exports.addTagsToNode = addTagsToNode; | ||||
| 
 | ||||
|  | @ -64,11 +67,12 @@ exports.viewNode = viewNode; | |||
|  * @param tags Set of tags of the way. | ||||
|  * @return True iff. the way is one-way. | ||||
|  */ | ||||
| const isOneWay = object => | ||||
| const isOneWay = object => ( | ||||
|     object.type === 'way' | ||||
|     && isObject(object.tags) | ||||
|     && (object.tags.oneway === 'yes' || object.tags.junction === 'roundabout' | ||||
|         || object.tags.highway === 'motorway'); | ||||
|         || object.tags.highway === 'motorway') | ||||
| ); | ||||
| 
 | ||||
| exports.isOneWay = isOneWay; | ||||
| 
 | ||||
|  | @ -81,9 +85,10 @@ exports.isOneWay = isOneWay; | |||
|  * @param object OSM object. | ||||
|  * @return True iff. the relation is a public transport line. | ||||
|  */ | ||||
| const isTransportLine = object => | ||||
| const isTransportLine = object => ( | ||||
|     object.type === 'relation' | ||||
|     && isObject(object.tags) | ||||
|     && object.tags.type === 'route_master'; | ||||
|     && object.tags.type === 'route_master' | ||||
| ); | ||||
| 
 | ||||
| exports.isTransportLine = isTransportLine; | ||||
|  |  | |||
							
								
								
									
										22
									
								
								back/util.js
								
								
								
								
							
							
						
						
									
										22
									
								
								back/util.js
								
								
								
								
							|  | @ -52,9 +52,11 @@ const joinSentence = (array, separator, lastSeparator) => | |||
|         return array.join(lastSeparator); | ||||
|     } | ||||
| 
 | ||||
|     return array.slice(0, -1).join(separator) | ||||
|         + lastSeparator | ||||
|         + array[array.length - 1]; | ||||
|     return ( | ||||
|         array.slice(0, -1).join(separator) | ||||
|             + lastSeparator | ||||
|             + array[array.length - 1] | ||||
|     ); | ||||
| }; | ||||
| 
 | ||||
| exports.joinSentence = joinSentence; | ||||
|  | @ -68,3 +70,17 @@ exports.joinSentence = joinSentence; | |||
| const isObject = value => value !== null && typeof value === 'object'; | ||||
| 
 | ||||
| exports.isObject = isObject; | ||||
| 
 | ||||
| /** | ||||
|  * Check if two arrays are equal in a shallow manner. | ||||
|  * | ||||
|  * @param array1 First array. | ||||
|  * @param array2 Second array. | ||||
|  * @return True iff. the two arrays are equal. | ||||
|  */ | ||||
| const arraysEqual = (array1, array2) => ( | ||||
|     array1.length === array2.length | ||||
|     && array1.every((elt1, index) => elt1 === array2[index]) | ||||
| ); | ||||
| 
 | ||||
| exports.arraysEqual = arraysEqual; | ||||
|  |  | |||
							
								
								
									
										30
									
								
								front/map.js
								
								
								
								
							
							
						
						
									
										30
									
								
								front/map.js
								
								
								
								
							|  | @ -29,23 +29,21 @@ const makeDataSources = async () => | |||
|     const stopsSource = new VectorSource(); | ||||
| 
 | ||||
|     segmentsSource.addFeatures( | ||||
|         Object.values(network.lines).flatMap(({color, routes}) => | ||||
|             routes.map(({segments}) => | ||||
|                 new Feature({ | ||||
|                     color, | ||||
|                     geometry: new LineString(segments.flat().map( | ||||
|                         ({lat, lon}) => proj.fromLonLat([lon, lat]) | ||||
|                     )), | ||||
|                 }) | ||||
|             ) | ||||
|         Object.values(network.segments).map(({lines, points}) => | ||||
|             new Feature({ | ||||
|                 colors: lines.map(line => network.lines[line].color), | ||||
|                 geometry: new LineString(points.map( | ||||
|                     ({lat, lon}) => proj.fromLonLat([lon, lat]) | ||||
|                 )), | ||||
|             }) | ||||
|         ) | ||||
|     ); | ||||
| 
 | ||||
|     stopsSource.addFeatures( | ||||
|         Object.entries(network.stops).map(([stopId, stop]) => | ||||
|         Object.values(network.stops).map(({lines, lon, lat}) => | ||||
|             new Feature({ | ||||
|                 color: network.lines[stop.lines[0]].color, | ||||
|                 geometry: new Point(proj.fromLonLat([stop.lon, stop.lat])), | ||||
|                 colors: lines.map(line => network.lines[line].color), | ||||
|                 geometry: new Point(proj.fromLonLat([lon, lat])), | ||||
|             }) | ||||
|         ) | ||||
|     ); | ||||
|  | @ -62,14 +60,14 @@ const makeBorderColor = mainColor => | |||
| 
 | ||||
| const segmentsBorderStyle = feature => new Style({ | ||||
|     stroke: new Stroke({ | ||||
|         color: makeBorderColor(feature.get('color')), | ||||
|         color: makeBorderColor(feature.get('colors')[0]), | ||||
|         width: 8, | ||||
|     }), | ||||
| }); | ||||
| 
 | ||||
| const segmentsInnerStyle = feature => new Style({ | ||||
|     stroke: new Stroke({ | ||||
|         color: feature.get('color'), | ||||
|         color: feature.get('colors')[0], | ||||
|         width: 6, | ||||
|     }), | ||||
| }); | ||||
|  | @ -77,10 +75,10 @@ const segmentsInnerStyle = feature => new Style({ | |||
| const stopsStyle = feature => new Style({ | ||||
|     image: new Circle({ | ||||
|         fill: new Fill({ | ||||
|             color: feature.get('color'), | ||||
|             color: feature.get('colors')[0], | ||||
|         }), | ||||
|         stroke: new Stroke({ | ||||
|             color: makeBorderColor(feature.get('color')), | ||||
|             color: makeBorderColor(feature.get('colors')[0]), | ||||
|             width: 1.5, | ||||
|         }), | ||||
|         radius: 6, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue