Skip to content

Add hurricane tracker code #1207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions PyPortal_Hurricane_Tracker/hurricane_tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import time
import math
import board
import displayio
import terminalio
from simpleio import map_range
import adafruit_imageload
from adafruit_pyportal import PyPortal
from adafruit_display_text.label import Label
from adafruit_display_shapes.line import Line

# --| User Config |---------------------------------------------------
UPDATE_RATE = 60 # minutes
MAX_STORMS = 3 # limit storms
NAME_COLOR = 0xFFFFFF # label text color
NAME_BG_COLOR = 0x000000 # label background color
ARROW_COLOR = 0x0000FF # movement direction arrow color
ARROW_LENGTH = 15 # movement direction arrow length
LAT_RANGE = (45, 5) # set to match map
LON_RANGE = (-100, -40) # set to match map
# --------------------------------------------------------------------

# setup pyportal
pyportal = PyPortal(
url="https://www.nhc.noaa.gov/CurrentStorms.json",
json_path=["activeStorms"],
status_neopixel=board.NEOPIXEL,
default_bg="/map.bmp",
)

# setup display group for storms
icons_bmp, icons_pal = adafruit_imageload.load(
"/storm_icons.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette
)
for i, c in enumerate(icons_pal):
if c == 0xFFFF00:
icons_pal.make_transparent(i)
storm_icons = displayio.Group(max_size=MAX_STORMS)
pyportal.splash.append(storm_icons)
STORM_CLASS = ("TD", "TS", "HU")

# setup info label
info_update = Label(
terminalio.FONT,
text="1984-01-01T00:00:00.000Z",
color=NAME_COLOR,
background_color=NAME_BG_COLOR,
)
info_update.anchor_point = (0.0, 1.0)
info_update.anchored_position = (10, board.DISPLAY.height - 10)
pyportal.splash.append(info_update)

# these are need for lat/lon to screen x/y mapping
VIRTUAL_WIDTH = board.DISPLAY.width * 360 / (LON_RANGE[1] - LON_RANGE[0])
VIRTUAL_HEIGHT = board.DISPLAY.height * 360 / (LAT_RANGE[0] - LAT_RANGE[1])
Y_OFFSET = math.radians(LAT_RANGE[0])
Y_OFFSET = math.tan(math.pi / 4 + Y_OFFSET / 2)
Y_OFFSET = math.log(Y_OFFSET)
Y_OFFSET = (VIRTUAL_WIDTH * Y_OFFSET) / (2 * math.pi)
Y_OFFSET = VIRTUAL_HEIGHT / 2 - Y_OFFSET


def update_display():
# clear out existing icons
while len(storm_icons):
_ = storm_icons.pop()

# get latest storm data
try:
storm_data = pyportal.fetch()
except RuntimeError:
return
print("Number of storms:", len(storm_data))

# parse the storm data
for storm in storm_data:
# don't exceed max
if len(storm_icons) >= MAX_STORMS:
continue
# get lat/lon
lat = storm["latitudeNumeric"]
lon = storm["longitudeNumeric"]
# check if on map
if (
not LAT_RANGE[0] >= lat >= LAT_RANGE[1]
or not LON_RANGE[0] <= lon <= LON_RANGE[1]
):
continue
# OK, let's make a group for all the graphics
storm_gfx = displayio.Group(max_size=3) # icon + label + arrow
# convert to sreen coords
x = int(map_range(lon, LON_RANGE[0], LON_RANGE[1], 0, board.DISPLAY.width - 1))
y = math.radians(lat)
y = math.tan(math.pi / 4 + y / 2)
y = math.log(y)
y = (VIRTUAL_WIDTH * y) / (2 * math.pi)
y = VIRTUAL_HEIGHT / 2 - y
y = int(y - Y_OFFSET)
# icon type
if storm["classification"] in STORM_CLASS:
storm_type = STORM_CLASS.index(storm["classification"])
else:
storm_type = 0
# create storm icon
icon = displayio.TileGrid(
icons_bmp,
pixel_shader=icons_pal,
width=1,
height=1,
tile_width=16,
tile_height=16,
default_tile=storm_type,
x=x - 8,
y=y - 8,
)
# add storm icon
storm_gfx.append(icon)
# add a label
name = Label(
terminalio.FONT,
text=storm["name"],
color=NAME_COLOR,
background_color=NAME_BG_COLOR,
)
name.anchor_point = (0.0, 1.0)
name.anchored_position = (x + 8, y - 8)
storm_gfx.append(name)
# add direction arrow
angle = math.radians(storm["movementDir"])
xd = x + int(ARROW_LENGTH * math.sin(angle))
yd = y - int(ARROW_LENGTH * math.cos(angle))
arrow = Line(x, y, xd, yd, color=ARROW_COLOR)
storm_gfx.append(arrow)
# add the storm graphics
storm_icons.append(storm_gfx)
# update time
info_update.text = storm["lastUpdate"]
# debug
print(
"{} @ {},{}".format(
storm["name"], storm["latitudeNumeric"], storm["longitudeNumeric"]
)
)

# no storms? at least say something
if not len(storm_icons):
print("No storms in map area.")
storm_icons.append(
Label(
terminalio.FONT,
scale=4,
x=50,
y=110,
text="NO STORMS\n IN AREA",
color=NAME_COLOR,
background_color=NAME_BG_COLOR,
)
)


# --------------------------------------------------------------------
# M A I N
# --------------------------------------------------------------------
update_display()
last_update = time.monotonic()
while True:
now = time.monotonic()
if now - last_update > UPDATE_RATE * 60:
print("Updating...")
update_display()
last_update = now
Binary file added PyPortal_Hurricane_Tracker/map.bmp
Binary file not shown.
Binary file added PyPortal_Hurricane_Tracker/storm_icons.bmp
Binary file not shown.