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.
|
* 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;
|
||||||
|
|
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 = (
|
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;
|
||||||
|
|
22
back/util.js
22
back/util.js
|
@ -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;
|
||||||
|
|
30
front/map.js
30
front/map.js
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue