diff --git a/.gitignore b/.gitignore index 6a18ad4..d490afe 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,4 @@ ENV/ # Rope project settings .ropeproject +*.csv diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..726a8eb --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +all: tornados_us.png + +1950-2017_actual_tornadoes.csv: + curl -O https://www.spc.noaa.gov/wcm/data/1950-2017_actual_tornadoes.csv + +%.png: %.py 1950-2017_actual_tornadoes.csv + pipenv run python $^ + +clean: + rm -f *.png + +.PHONY: all clean diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..0c32838 --- /dev/null +++ b/Pipfile @@ -0,0 +1,14 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +matplotlib = "*" +cartopy = "*" +scipy = "*" + +[requires] +python_version = "3.7" diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..b5686a4 --- /dev/null +++ b/README.markdown @@ -0,0 +1,17 @@ +Map of tornado historic tornado traces +====================================== + +Data from https://www.spc.noaa.gov/wcm + + +## All US tornados 1950-20017 + +![map of US with traces of tornado paths](tornados_us.png) + + +### Dev + +Using pipenv + + $ pipenv install + $ make diff --git a/README.md b/README.md deleted file mode 100644 index 51da5ac..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# tornado-map - -Map of tornado historic tornado traces \ No newline at end of file diff --git a/tornados_us.png b/tornados_us.png new file mode 100644 index 0000000..8481ce1 Binary files /dev/null and b/tornados_us.png differ diff --git a/tornados_us.py b/tornados_us.py new file mode 100644 index 0000000..faf1bb4 --- /dev/null +++ b/tornados_us.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- + +import csv +from dataclasses import dataclass +from datetime import datetime + +import cartopy.crs as ccrs +import matplotlib.pyplot as plt +import cartopy.feature as cfeature +from mpl_toolkits.axes_grid1.inset_locator import inset_axes +from cartopy import geodesic +import shapely.geometry as sgeom + + +proj = ccrs.AlbersEqualArea( + central_longitude=-95, + central_latitude=40, +) +water_blue = "#7ebfd4" + + +def phys(name, resolv): + return cfeature.NaturalEarthFeature('physical', name, resolv, facecolor="none") + + +def cultural(name, resolv): + return cfeature.NaturalEarthFeature('cultural', name, resolv, facecolor="none") + + +land = phys("land", "50m") +rivers = phys("rivers_lake_centerlines", "50m") +lakes = phys("lakes", "50m") +countries = cultural("admin_0_countries", "50m") +states = cultural("admin_1_states_provinces_lines", "50m") + + +@dataclass +class Tornado: + om: str + yr: str + mo: str + dy: str + date: str + time: str + tz: str + st: str + stf: str + stn: str + mag: str + inj: str + fat: str + loss: str + closs: str + slat: str + slon: str + elat: str + elon: str + len: str + wid: str + ns: str + sn: str + sg: str + f1: str + f2: str + f3: str + f4: str + fc: str + + def __post_init__(self): + self.id = int(self.om) + self.dt = datetime.strptime(self.date, "%Y-%m-%d") + self.slon = float(self.slon) + self.slat = float(self.slat) + self.elon = float(self.elon) + self.elat = float(self.elat) + + def __repr__(self): + return "".format(id=self.id, date=self.dt) + + +def read_data(): + tornados = [] + with open("1950-2017_actual_tornadoes.csv", newline='') as csvfile: + reader = csv.DictReader(csvfile) + for i, row in enumerate(reader): + tornados.append(Tornado(**row)) + return tornados + + +def draw_map(data): + fig = plt.figure(frameon=False) + ax = fig.add_subplot(111, projection=proj) + ax.background_patch.set_facecolor(water_blue) + ax.set_extent([-125, -65, 20, 50], ccrs.Geodetic()) + ax.add_feature(land, facecolor="#f0f0f0") + ax.add_feature(rivers, edgecolor=water_blue) + ax.add_feature(lakes, facecolor=water_blue) + + ax.add_feature(countries, edgecolor="grey", linewidth=0.2, alpha=0.6, dashes='--') + ax.add_feature(states, edgecolor="grey", linewidth=0.2, alpha=0.4, dashes='--') + + for i, t in enumerate(data): + print(i) + if t.elon < 0: + ax.plot([t.slon, t.elon], [t.slat, t.elat], 'r', lw=0.3, transform=ccrs.Geodetic()) + else: + ax.plot([t.slon, t.slon], [t.slat, t.slat], 'r', lw=0.3, transform=ccrs.Geodetic()) + + fig.tight_layout() + plt.savefig("tornados_us.png", dpi=320) + + +if __name__ == '__main__': + ts = read_data() + draw_map(ts)