Split network into segments and report distance

This commit is contained in:
Mattéo Delabre 2020-07-17 23:48:32 +02:00
parent 082270831d
commit 8e98819c38
Signed by: matteo
GPG Key ID: AE3FBD02DC583ABB
5 changed files with 28029 additions and 27265 deletions

View File

@ -12,7 +12,8 @@
* the `script/update-network` script. * the `script/update-network` script.
*/ */
const {choosePlural, joinSentence} = require('../util'); const geolib = require('geolib');
const util = require('../util');
const osm = require('./sources/osm'); const osm = require('./sources/osm');
const tam = require('./sources/tam'); const tam = require('./sources/tam');
@ -163,6 +164,9 @@ out body qt;
// All transport lines of the network // All transport lines of the network
const lines = {}; const lines = {};
// All segments leading from one stop to another
const segments = {};
for (let routeMaster of routeMasters) for (let routeMaster of routeMasters)
{ {
const lineRef = routeMaster.tags.ref; const lineRef = routeMaster.tags.ref;
@ -236,9 +240,9 @@ URI: ${osm.viewNode(stop.id)}
{ {
console.warn(`\ console.warn(`\
Stop ${candidate.stopRef} with name ${candidate.name} used by \ Stop ${candidate.stopRef} with name ${candidate.name} used by \
${choosePlural(candidate.lines.length, 'line', '.s')} \ ${util.choosePlural(candidate.lines.length, 'line', '.s')} \
${joinSentence(Array.from(candidate.lines), ', ', ' and ')} going to \ ${util.joinSentence(Array.from(candidate.lines), ', ', ' and ')} going to \
${joinSentence(Array.from(candidate.directions), ', ', ' or ')} ${util.joinSentence(Array.from(candidate.directions), ', ', ' or ')}
Apply in JOSM: ${osm.addTagsToNode(stop.id, ['ref=' + candidate.stopRef])} 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 // Split the path into segments between stops
const segments = [];
for (let stopIndex = 0; stopIndex + 1 < stops.length; ++stopIndex) 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]),
path.indexOf(stops[stopIndex + 1]) + 1, 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({ routes.push({
from, to, name, from, to, name,
segments,
stops: stops.map(id => elements[id].tags.ref), 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; exports.fetch = fetch;

File diff suppressed because it is too large Load Diff

View File

@ -22,8 +22,10 @@ const {isObject} = require('../../util');
const runQuery = ( const runQuery = (
query, query,
endpoint = 'https://lz4.overpass-api.de/api/interpreter' 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; exports.runQuery = runQuery;
@ -37,12 +39,13 @@ exports.runQuery = runQuery;
* @param tags List of tags to add, in the `key=value` format. * @param tags List of tags to add, in the `key=value` format.
* @return Link for remotely adding the tags. * @return Link for remotely adding the tags.
*/ */
const addTagsToNode = (id, tags) => const addTagsToNode = (id, tags) => (
'http://127.0.0.1:8111/load_object?' + [ 'http://127.0.0.1:8111/load_object?' + [
`objects=n${id}`, `objects=n${id}`,
'new_layer=false', 'new_layer=false',
'addtags=' + tags.join('%7C'), 'addtags=' + tags.join('%7C'),
].join('&'); ].join('&')
);
exports.addTagsToNode = addTagsToNode; exports.addTagsToNode = addTagsToNode;
@ -64,11 +67,12 @@ exports.viewNode = viewNode;
* @param tags Set of tags of the way. * @param tags Set of tags of the way.
* @return True iff. the way is one-way. * @return True iff. the way is one-way.
*/ */
const isOneWay = object => const isOneWay = object => (
object.type === 'way' object.type === 'way'
&& isObject(object.tags) && isObject(object.tags)
&& (object.tags.oneway === 'yes' || object.tags.junction === 'roundabout' && (object.tags.oneway === 'yes' || object.tags.junction === 'roundabout'
|| object.tags.highway === 'motorway'); || object.tags.highway === 'motorway')
);
exports.isOneWay = isOneWay; exports.isOneWay = isOneWay;
@ -81,9 +85,10 @@ exports.isOneWay = isOneWay;
* @param object OSM object. * @param object OSM object.
* @return True iff. the relation is a public transport line. * @return True iff. the relation is a public transport line.
*/ */
const isTransportLine = object => const isTransportLine = object => (
object.type === 'relation' object.type === 'relation'
&& isObject(object.tags) && isObject(object.tags)
&& object.tags.type === 'route_master'; && object.tags.type === 'route_master'
);
exports.isTransportLine = isTransportLine; exports.isTransportLine = isTransportLine;

View File

@ -52,9 +52,11 @@ const joinSentence = (array, separator, lastSeparator) =>
return array.join(lastSeparator); return array.join(lastSeparator);
} }
return array.slice(0, -1).join(separator) return (
+ lastSeparator array.slice(0, -1).join(separator)
+ array[array.length - 1]; + lastSeparator
+ array[array.length - 1]
);
}; };
exports.joinSentence = joinSentence; exports.joinSentence = joinSentence;
@ -68,3 +70,17 @@ exports.joinSentence = joinSentence;
const isObject = value => value !== null && typeof value === 'object'; const isObject = value => value !== null && typeof value === 'object';
exports.isObject = isObject; 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;

View File

@ -29,23 +29,21 @@ const makeDataSources = async () =>
const stopsSource = new VectorSource(); const stopsSource = new VectorSource();
segmentsSource.addFeatures( segmentsSource.addFeatures(
Object.values(network.lines).flatMap(({color, routes}) => Object.values(network.segments).map(({lines, points}) =>
routes.map(({segments}) => new Feature({
new Feature({ colors: lines.map(line => network.lines[line].color),
color, geometry: new LineString(points.map(
geometry: new LineString(segments.flat().map( ({lat, lon}) => proj.fromLonLat([lon, lat])
({lat, lon}) => proj.fromLonLat([lon, lat]) )),
)), })
})
)
) )
); );
stopsSource.addFeatures( stopsSource.addFeatures(
Object.entries(network.stops).map(([stopId, stop]) => Object.values(network.stops).map(({lines, lon, lat}) =>
new Feature({ new Feature({
color: network.lines[stop.lines[0]].color, colors: lines.map(line => network.lines[line].color),
geometry: new Point(proj.fromLonLat([stop.lon, stop.lat])), geometry: new Point(proj.fromLonLat([lon, lat])),
}) })
) )
); );
@ -62,14 +60,14 @@ const makeBorderColor = mainColor =>
const segmentsBorderStyle = feature => new Style({ const segmentsBorderStyle = feature => new Style({
stroke: new Stroke({ stroke: new Stroke({
color: makeBorderColor(feature.get('color')), color: makeBorderColor(feature.get('colors')[0]),
width: 8, width: 8,
}), }),
}); });
const segmentsInnerStyle = feature => new Style({ const segmentsInnerStyle = feature => new Style({
stroke: new Stroke({ stroke: new Stroke({
color: feature.get('color'), color: feature.get('colors')[0],
width: 6, width: 6,
}), }),
}); });
@ -77,10 +75,10 @@ const segmentsInnerStyle = feature => new Style({
const stopsStyle = feature => new Style({ const stopsStyle = feature => new Style({
image: new Circle({ image: new Circle({
fill: new Fill({ fill: new Fill({
color: feature.get('color'), color: feature.get('colors')[0],
}), }),
stroke: new Stroke({ stroke: new Stroke({
color: makeBorderColor(feature.get('color')), color: makeBorderColor(feature.get('colors')[0]),
width: 1.5, width: 1.5,
}), }),
radius: 6, radius: 6,