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.
*/
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;

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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;

View File

@ -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,