~phroa/boat-map

c86157419c7fd529bafca0c3c07e3bef0f78cdd5 — Jack Stratton 8 months ago 7aed69f
Include timetable
2 files changed, 83 insertions(+), 14 deletions(-)

M make-map.py
M requirements.txt
M make-map.py => make-map.py +81 -13
@@ 1,3 1,7 @@
import time
from collections import defaultdict

from lxml import html
import requests
import simdjson
from PIL import Image, ImageDraw


@@ 11,6 15,7 @@ MAP = [
]

VESSELS = "https://www.wsdot.com/ferries/vesselwatch/Vessels.ashx"
SCHEDULE = "https://www.wsdot.com/ferries/schedule/scheduledetailbyroute.aspx?route=ana-sj"
OVERPASS = "https://overpass-api.de/api/interpreter"

MIN_LON = min(MAP, key=lambda coord: coord[0])[0]


@@ 83,19 88,7 @@ def paste(src: Image, dest_x, dest_y):
                DRAW.point((dest_x + x - center_x, dest_y + y - center_y))


def main():
    filename = f"coastlines-{hash((MIN_LAT, MIN_LON, MAX_LAT, MAX_LON))}.json"
    try:
        with open(filename) as f:
            coastlines = simdjson.load(f)
    except FileNotFoundError:
        print("Downloading coastlines")
        coastlines = requests.get(OVERPASS, {"data": OVERPASS_QUERY}).json()
        with open(filename, "w") as f:
            simdjson.dump(coastlines, f)

    draw_coastlines(coastlines)

def draw_boats():
    boats = requests.get(VESSELS).json()["vessellist"]
    boats = filter(lambda boat: boat["route"] == "ANA-SJ", boats)
    boats = filter(lambda boat: boat["aterm_id"] != "", boats)


@@ 116,6 109,81 @@ def main():
        DRAW.point((x, y))
        DRAW.text((x, y), label, anchor="mm")


def draw_schedule():
    response = requests.get(SCHEDULE)
    if not response.ok:
        return

    root = html.fromstring(response.content)
    schedule_lines = defaultdict(list)
    current_label = None
    table = root.xpath('//*[@class="schedgridbyroute"]')[0]

    current_time = time.localtime()

    for row in table.xpath('tr'):
        header = row.xpath('td/h4[@id="sailingDescription"]/text()')
        # Section header
        if len(header) > 0:
            current_label = header[0]
            continue

        # Terminal names
        headers = row.xpath('td/h4[@id="sailingTerminal"]')
        if len(headers) > 0:
            continue

        # Filter out non-body rows
        if 'HighlightOnHover' not in row.attrib['class']:
            continue

        line = []
        times = []
        for col in row.xpath('td'):
            cell_contents = ''.join(col.xpath('descendant::*/text()')).replace('\xa0', '').strip()
            if '-' in cell_contents:
                cell_contents = ''
            if len(col.xpath('div/div[@class="pm"]')) > 0 and ':' in cell_contents:
                hour, minute = map(int, cell_contents.split(':'))
                times.append(60 * (hour + 12) + minute)
                cell_contents += 'p'
            elif ':' in cell_contents:
                hour, minute = map(int, cell_contents.split(':'))
                times.append(60 * hour + minute)
            elif 'Noon' in cell_contents:
                times.append(60 * 12)
            elif 'Midnight' in cell_contents:
                times.append(60 * 24)
            line.append(cell_contents)
        if current_time.tm_hour * 60 + current_time.tm_min < max(times):
            schedule_lines[current_label].append('{}  {:6} {:6} {:6} {:6} {:6}'.format(*line))

    mf_or_satsun = 'Saturday and Sunday' if time.localtime().tm_wday > 4 else 'Monday through Friday'
    left_col = f'Leave Westbound ({mf_or_satsun})'
    right_col = f'Leave Eastbound ({mf_or_satsun})'

    left_lines = '   Anac.  Lopez  Shaw   Orcas  Friday\n' + '\n'.join(schedule_lines[left_col])
    right_lines = '   Friday Orcas  Shaw   Lopez  Anac.\n' + '\n'.join(schedule_lines[right_col])

    DRAW.multiline_text((2, MAP_HEIGHT + 2), left_lines)
    DRAW.multiline_text((WIDTH / 2 + 2, MAP_HEIGHT + 2), right_lines)

def main():
    filename = f"coastlines-{hash((MIN_LAT, MIN_LON, MAX_LAT, MAX_LON))}.json"
    try:
        with open(filename) as f:
            coastlines = simdjson.load(f)
    except FileNotFoundError:
        print("Downloading coastlines")
        coastlines = requests.get(OVERPASS, {"data": OVERPASS_QUERY}).json()
        with open(filename, "w") as f:
            simdjson.dump(coastlines, f)

    draw_coastlines(coastlines)
    draw_boats()
    draw_schedule()

    SCREEN.save("map.bmp", "BMP")



M requirements.txt => requirements.txt +2 -1
@@ 2,4 2,5 @@ pillow
pysimdjson
requests
RPi.GPIO
spidev
\ No newline at end of file
spidev
lxml
\ No newline at end of file