From ff77bc31e776ce72e509cc22d194c9a7cf45cec0 Mon Sep 17 00:00:00 2001 From: Chandler Swift Date: Thu, 1 Feb 2024 01:10:18 -0600 Subject: [PATCH] Add Viarail layer --- layers/index.js | 5 +++++ layers/viarail/get_data.py | 43 ++++++++++++++++++++++++++++++++++++++ layers/viarail/layer.js | 26 +++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100755 layers/viarail/get_data.py create mode 100644 layers/viarail/layer.js diff --git a/layers/index.js b/layers/index.js index 109133c..09b0ef9 100644 --- a/layers/index.js +++ b/layers/index.js @@ -4,6 +4,7 @@ import Stamen from 'ol/source/Stamen.js'; import chandlerLayer from './chandler/layer.js'; import amtrakLayer from './amtrak/layer.js'; +import viarailLayer from './viarail/layer.js'; import arenasLayer from './nhl-arenas/layer.js'; import bikepackingLayer from './bikepacking/layer.js'; import chains from './chains/index.js'; @@ -68,6 +69,10 @@ const layerCategories = [ name: "Amtrak Routes", layer: amtrakLayer, }, + { + name: "Via Rail Routes", + layer: viarailLayer, + }, { name: "NHL Arenas", layer: arenasLayer, diff --git a/layers/viarail/get_data.py b/layers/viarail/get_data.py new file mode 100755 index 0000000..38c122f --- /dev/null +++ b/layers/viarail/get_data.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 + +import csv +import io +import json +import zipfile +import urllib.request + +shapes = {} + +# https://stackoverflow.com/a/5711095 +resp = urllib.request.urlopen("https://www.viarail.ca/sites/all/files/gtfs/viarail.zip") +with zipfile.ZipFile(io.BytesIO(resp.read())).open('shapes.txt') as f: + reader = csv.DictReader(io.TextIOWrapper(f)) + for row in reader: + # they look like they're probably all in order, but the spec doesn't + # actually say that they have to be, so we're going to bucket them by + # route and then sort each bucket's contents to be on the safe side. + # It's not that much data, and I don't run this download frequently, so + # the extra CPU cost shouldn't be too outrageous :) + if row['shape_id'] not in shapes: + shapes[row['shape_id']] = [] + shapes[row['shape_id']].append(row) + +geojson = { + "type": "FeatureCollection", + "features": [], +} + +for _, shape in shapes.items(): + shape.sort(key=lambda c: int(c['shape_pt_sequence'])) + geojson['features'].append({ + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [float(l['shape_pt_lon']), float(l['shape_pt_lat'])] for l in shape + ] + }, + }) + +with open('data.geojson', 'w') as f: + f.write(json.dumps(geojson)) diff --git a/layers/viarail/layer.js b/layers/viarail/layer.js new file mode 100644 index 0000000..63b4d99 --- /dev/null +++ b/layers/viarail/layer.js @@ -0,0 +1,26 @@ +import VectorLayer from 'ol/layer/Vector'; +import {Vector as VectorSource} from 'ol/source.js'; +import GeoJSON from 'ol/format/GeoJSON.js'; + +import {Style, Stroke} from 'ol/style.js'; + +import url from './data.geojson?url'; // TODO: remove `?url`? + +const colors = '255,0,0'; // "Canadian red"...is just red? https://en.wikipedia.org/wiki/National_colours_of_Canada#Reproduction + +const vectorLayer = new VectorLayer({ + source: new VectorSource({ + url: url, + format: new GeoJSON, + }), + style: function(feature, resolution){ + return new Style({ + stroke: new Stroke({ + color: `rgba(${colors},${Math.min(1, Math.pow(resolution/10, 1/4))})`, + width: 10/Math.pow(resolution, 1/4), + }) + }); + }, +}); + +export default vectorLayer;