Add NHL arenas layer
This commit is contained in:
parent
6afaa43911
commit
a0a5b5bf42
7
layers/nhl-arenas/README.md
Normal file
7
layers/nhl-arenas/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
https://en.wikipedia.org/wiki/Template:NHL_arenas
|
||||||
|
|
||||||
|
https://en.wikipedia.org/wiki/Template:NHL_arenas_map
|
||||||
|
|
||||||
|
https://en.wikipedia.org/wiki/List_of_National_Hockey_League_arenas
|
||||||
|
|
||||||
|
https://statsapi.web.nhl.com/api/v1/venues
|
97
layers/nhl-arenas/get_data.py
Executable file
97
layers/nhl-arenas/get_data.py
Executable file
|
@ -0,0 +1,97 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Tuple
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
BASE_URL="https://en.wikipedia.org/w/api.php"
|
||||||
|
|
||||||
|
# A previous attempt at this script used the NHL api...except for some reason
|
||||||
|
# they don't include all the arenas! Only 16 are included on this page:
|
||||||
|
#
|
||||||
|
# venueData = requests.get("https://statsapi.web.nhl.com/api/v1/venues").json()
|
||||||
|
#
|
||||||
|
# venues = [] for venue in venueData["venues"]: # Special-case a few entries if
|
||||||
|
# venue["name"] == "NASSAU LIVE CENTER": # As of 2021 the islanders now play out
|
||||||
|
# of UBS Arena. Not sure why this # is still in the list. continue if
|
||||||
|
# venue["name"] == "Prudential Center Map & Info": # not sure why they call
|
||||||
|
# it that venue["name"] = "Prudential Center" ...
|
||||||
|
|
||||||
|
def wikipedia_request(page_title: str) -> str:
|
||||||
|
params = {
|
||||||
|
"action": "parse",
|
||||||
|
"page": f"{page_title}",
|
||||||
|
"prop": "wikitext",
|
||||||
|
"formatversion": 2,
|
||||||
|
"format": "json",
|
||||||
|
}
|
||||||
|
return requests.get(url=BASE_URL, params=params).json()['parse']['wikitext']
|
||||||
|
|
||||||
|
def get_wikipedia_coords_for_arena(arena: str) -> Tuple[float, float]:
|
||||||
|
raw_arena_page = wikipedia_request(arena)
|
||||||
|
# print(raw_arena_page)
|
||||||
|
|
||||||
|
# e.g. `coordinates = {{coord|40.712094|N|73.727157|W|...}}`
|
||||||
|
match = re.search(r"[Cc]oord\|([0-9.]*)\|N\|([0-9.]*)\|W\|", raw_arena_page)
|
||||||
|
if match:
|
||||||
|
return (float(match[1]), -float(match[2]))
|
||||||
|
|
||||||
|
# e.g. `coordinates = {{Coord|47.622|-122.354|...}}`
|
||||||
|
match = re.search(r"[Cc]oord\|([0-9.]*)\|(-[0-9.]*)\|[^\d]", raw_arena_page)
|
||||||
|
if match:
|
||||||
|
return (float(match[1]), float(match[2]))
|
||||||
|
|
||||||
|
# e.g. `coordinates = {{coord|44|56|41|N|93|6|4|W|...}}`
|
||||||
|
match = re.search(r"[Cc]oord\|([0-9.]*)\|([0-9.]*)\|([0-9.]*)\|N\|([0-9.]*)\|([0-9.]*)\|([0-9.]*)\|W\|", raw_arena_page) # Assuming northern and western hemispheres; currently safe
|
||||||
|
lat_deg = match[1]
|
||||||
|
lat_min = match[2]
|
||||||
|
lat_sec = match[3]
|
||||||
|
lon_deg = match[4]
|
||||||
|
lon_min = match[5]
|
||||||
|
lon_sec = match[6]
|
||||||
|
lat = float(lat_deg) + float(lat_min) / 60 + float(lat_sec) / 3600
|
||||||
|
lon = float(lon_deg) + float(lon_min) / 60 + float(lon_sec) / 3600
|
||||||
|
return (lat, -lon)
|
||||||
|
|
||||||
|
|
||||||
|
print("Retrieving arena list...", flush=True)
|
||||||
|
raw_arenas_list = wikipedia_request("Template:NHL arenas")
|
||||||
|
arena_names = re.findall(r"\* +\[\[ ?(.*?)(?:\|.*)? ?\]\]", raw_arenas_list)
|
||||||
|
arenas = []
|
||||||
|
for arena in arena_names:
|
||||||
|
print(f"Retrieving data for {arena}...", flush=True)
|
||||||
|
nominatim_params = {
|
||||||
|
'q': arena,
|
||||||
|
'format': "json",
|
||||||
|
'addressdetails': 1,
|
||||||
|
}
|
||||||
|
if arena == "SAP Center":
|
||||||
|
nominatim_params['q'] = "SAP Center at San Jose" # https://en.wikipedia.org/w/index.php?title=SAP_Center&oldid=690907747
|
||||||
|
nominatim_result = requests.get(url="https://nominatim.openstreetmap.org/search", params=nominatim_params).json()[0]
|
||||||
|
|
||||||
|
# confirm it matches what wikipedia claims
|
||||||
|
wiki_lat, wiki_lon = get_wikipedia_coords_for_arena(arena)
|
||||||
|
if wiki_lat - float(nominatim_result["lat"]) > 0.1 or wiki_lon - float(nominatim_result["lon"]) > 0.1:
|
||||||
|
raise Exception(f"Data mismatch for {arena}: {wiki_lat} vs {nominatim_result['lat']}; {wiki_lon} vs {nominatim_result['lon']}")
|
||||||
|
|
||||||
|
arenas.append({
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [float(nominatim_result["lon"]), float(nominatim_result["lat"])], # yes, [lon, lat] since it's [x, y]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"name": arena,
|
||||||
|
"osm_id": nominatim_result["osm_id"],
|
||||||
|
"address": nominatim_result["address"], # requires &addressdetails=1 (https://nominatim.org/release-docs/latest/api/Search/#output-details)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
geojson = {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": arenas,
|
||||||
|
}
|
||||||
|
|
||||||
|
with open("nhl-arenas-data.geojson", "w") as f:
|
||||||
|
f.write(json.dumps(geojson))
|
30
layers/nhl-arenas/layer.js
Normal file
30
layers/nhl-arenas/layer.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import VectorLayer from 'ol/layer/Vector';
|
||||||
|
import {Vector as VectorSource} from 'ol/source.js';
|
||||||
|
import GeoJSON from 'ol/format/GeoJSON.js';
|
||||||
|
|
||||||
|
import {Style, Stroke, Circle, Fill} from 'ol/style.js';
|
||||||
|
|
||||||
|
import arenaURL from '/data/nhl-arenas-data.geojson?url'; // TODO: remove `?url`?
|
||||||
|
|
||||||
|
import visitedArenas from './visited.js'
|
||||||
|
|
||||||
|
const vectorLayer = new VectorLayer({
|
||||||
|
source: new VectorSource({
|
||||||
|
url: arenaURL,
|
||||||
|
format: new GeoJSON,
|
||||||
|
}),
|
||||||
|
// TODO: use '✓' and '✗' (or maybe '✔' and '✘') (from https://en.wikipedia.org/wiki/List_of_Unicode_characters#Dingbats)
|
||||||
|
// TODO: popups with Arena information (name, photo, date visited, score from that day)
|
||||||
|
style: function(feature, resolution) {
|
||||||
|
const base_color = visitedArenas.some(a => a.name == feature.get('name')) ? '#008800' : '#FF0000';
|
||||||
|
return new Style({
|
||||||
|
image: new Circle({
|
||||||
|
radius: 50/Math.pow(resolution, 1/4),
|
||||||
|
fill: new Fill({color: base_color + '33'}),
|
||||||
|
stroke: new Stroke({color: base_color, width: 1}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default vectorLayer;
|
10
layers/nhl-arenas/visited.js
Normal file
10
layers/nhl-arenas/visited.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
name: "Xcel Energy Center",
|
||||||
|
date: new Date('2022-04-28'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Enterprise Center",
|
||||||
|
date: new Date('2023-03-15'),
|
||||||
|
},
|
||||||
|
]
|
6
main.js
6
main.js
|
@ -5,6 +5,7 @@ import OSM from 'ol/source/OSM';
|
||||||
import {fromLonLat} from 'ol/proj.js';
|
import {fromLonLat} from 'ol/proj.js';
|
||||||
|
|
||||||
import amtrakLayer from './layers/amtrak/layer.js';
|
import amtrakLayer from './layers/amtrak/layer.js';
|
||||||
|
import arenasLayer from './layers/nhl-arenas/layer.js';
|
||||||
|
|
||||||
const map = new Map({
|
const map = new Map({
|
||||||
target: 'map',
|
target: 'map',
|
||||||
|
@ -19,4 +20,7 @@ const map = new Map({
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
map.addLayer(amtrakLayer);
|
map.getLayers().extend([
|
||||||
|
amtrakLayer,
|
||||||
|
arenasLayer,
|
||||||
|
]);
|
||||||
|
|
Loading…
Reference in a new issue