tracktracker/src/front/map/network.js

147 lines
4.5 KiB
JavaScript

import network from "../../data/network.json";
import * as routing from "../../data/routing.js";
import { cacheStyle, makeBorderColor, sizes } from "./common";
import GeoJSON from "ol/format/GeoJSON";
import VectorImageLayer from "ol/layer/VectorImage";
import VectorSource from "ol/source/Vector";
import { Style, Fill, Stroke, Circle } from "ol/style";
const geojsonReader = new GeoJSON({ featureProjection: "EPSG:3857" });
// Style used for the border of line segments
const segmentBorderStyle = cacheStyle(
color => new Style({
stroke: new Stroke({
color: makeBorderColor(color),
width: sizes.segmentOuter
})
}),
feature => feature.get("colors")[0],
);
// Style used for the inner part of line segments
const segmentInnerStyle = cacheStyle(
color => new Style({
stroke: new Stroke({
color,
width: sizes.segmentInner
})
}),
feature => feature.get("colors")[0],
);
// Style used for line stops
const stopStyle = cacheStyle(
color => new Style({
image: new Circle({
fill: new Fill({ color }),
stroke: new Stroke({
color: makeBorderColor(color),
width: sizes.stopBorder
}),
radius: sizes.stopRadius
})
}),
feature => feature.get("colors")[0],
);
/**
* 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.
*/
export const getLayers = () => {
const segmentsSource = new VectorSource();
const stopsSource = new VectorSource();
// Turn GeoJSON stops list into a vector source
const readStops = hash => Object.values(hash).map(json => {
json.properties.lines = json.properties.routes.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);
});
stopsSource.addFeatures(readStops(network.stops));
// Link stops with segments
const makeSegments = function* (lines) {
for (const [lineRef, line] of Object.entries(network.lines)) {
for (const route of line.routes) {
for (let i = 0; i + 1 < route.stops.length; ++i) {
const stop1 = network.stops[route.stops[i]].id;
const stop2 = network.stops[route.stops[i + 1]].id;
const segment = routing.findSegment(stop1, stop2);
segment.properties.lines = [lineRef];
segment.properties.colors = [line.color];
yield geojsonReader.readFeature(segment);
}
}
}
};
segmentsSource.addFeatures([...makeSegments(network.lines)]);
// Background layer on which the darker borders of line segments are drawn
const segmentsBorderLayer = new VectorImageLayer({
source: segmentsSource,
renderOrder: lineFeaturesOrder,
style: segmentBorderStyle,
imageRatio: 2,
});
// 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 VectorImageLayer({
source: segmentsSource,
renderOrder: lineFeaturesOrder,
style: segmentInnerStyle,
imageRatio: 2,
});
const stopsLayer = new VectorImageLayer({
source: stopsSource,
renderOrder: lineFeaturesOrder,
style: stopStyle,
imageRatio: 2,
minZoom: 13,
});
return [segmentsBorderLayer, segmentsInnerLayer, stopsLayer];
};