Map of tornado historic tornado traces
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
4.6 KiB

  1. # -*- coding: utf-8 -*-
  2. import csv
  3. from dataclasses import dataclass, field
  4. from datetime import datetime
  5. from typing import List
  6. import cartopy.crs as ccrs
  7. import matplotlib.pyplot as plt
  8. import cartopy.feature as cfeature
  9. from mpl_toolkits.axes_grid1.inset_locator import inset_axes
  10. from cartopy import geodesic
  11. import shapely.geometry as sgeom
  12. proj = ccrs.AlbersEqualArea(
  13. central_longitude=-95,
  14. central_latitude=35,
  15. )
  16. water_blue = "#7ebfd4"
  17. def phys(name, resolv):
  18. return cfeature.NaturalEarthFeature('physical', name, resolv, facecolor="none")
  19. def cultural(name, resolv):
  20. return cfeature.NaturalEarthFeature('cultural', name, resolv, facecolor="none")
  21. land = phys("land", "50m")
  22. rivers = phys("rivers_lake_centerlines", "50m")
  23. lakes = phys("lakes", "50m")
  24. countries = cultural("admin_0_countries", "50m")
  25. states = cultural("admin_1_states_provinces_lines", "50m")
  26. @dataclass
  27. class Tornado:
  28. om: str
  29. yr: str
  30. mo: str
  31. dy: str
  32. date: str
  33. time: str
  34. tz: str
  35. st: str
  36. stf: str
  37. stn: str
  38. mag: str
  39. inj: str
  40. fat: str
  41. loss: str
  42. closs: str
  43. slat: str
  44. slon: str
  45. elat: str
  46. elon: str
  47. len: str
  48. wid: str
  49. ns: str
  50. sn: str
  51. sg: str
  52. f1: str
  53. f2: str
  54. f3: str
  55. f4: str
  56. fc: str
  57. def __post_init__(self):
  58. self.id = int(self.om)
  59. self.dt = datetime.strptime(self.date, "%Y-%m-%d")
  60. self.slon = float(self.slon)
  61. self.slat = float(self.slat)
  62. self.elon = float(self.elon)
  63. self.elat = float(self.elat)
  64. self.mag = int(self.mag)
  65. def __repr__(self):
  66. return "<tornado {id} {date}>".format(id=self.id, date=self.dt)
  67. def read_data():
  68. tornados = []
  69. with open("1950-2017_actual_tornadoes.csv", newline='') as csvfile:
  70. reader = csv.DictReader(csvfile)
  71. for i, row in enumerate(reader):
  72. tornados.append(Tornado(**row))
  73. return tornados
  74. def draw_map(f_unknown, f_zero, f_one, f_two, f_three, f_four, f_five):
  75. fig = plt.figure(frameon=False, figsize=(12, 7.1))
  76. ax = fig.add_axes([0, 0, 1, 0.95], projection=proj)
  77. ax.background_patch.set_facecolor(water_blue)
  78. ax.set_extent([-122, -65, 21, 50], ccrs.Geodetic())
  79. ax.add_feature(land, facecolor="#f0f0f0")
  80. ax.add_feature(lakes, facecolor=water_blue)
  81. ax.add_feature(countries, edgecolor="grey", linewidth=0.2, alpha=0.6, dashes='--')
  82. ax.add_feature(states, edgecolor="grey", linewidth=0.2, alpha=0.4, dashes='--')
  83. ax.coastlines(resolution='50m', color="#55aacc", linewidth=0.2)
  84. geodetic = ccrs.Geodetic()
  85. ax.plot(f_unknown.lons, f_unknown.lats, 'k', lw=0.2, alpha=0.7, label="Unknown", transform=geodetic)
  86. ax.plot(f_zero.lons, f_zero.lats, '#ffbb00', lw=0.2, alpha=1, label="F0", transform=geodetic)
  87. ax.plot(f_one.lons, f_one.lats, '#ff7700', lw=0.3, alpha=1, label="F1", transform=geodetic)
  88. ax.plot(f_two.lons, f_two.lats, '#ff4400', lw=0.4, alpha=0.8, label="F2", transform=geodetic)
  89. ax.plot(f_three.lons, f_three.lats, '#ff1100', lw=0.5, alpha=0.8, label="F3", transform=geodetic)
  90. ax.plot(f_four.lons, f_four.lats, 'r', lw=0.6, alpha=0.8, label="F4", transform=geodetic)
  91. ax.plot(f_five.lons, f_five.lats, 'r', lw=0.8, alpha=0.7, label="F5", transform=geodetic)
  92. ax.axis('off')
  93. plt.title(" US Tornados 1950-2017", loc='left')
  94. ax.text(0, 0.01, " data from https://www.spc.noaa.gov/wcm/", transform=ax.transAxes, fontsize=6)
  95. plt.legend(loc=4, title="Intensity", fontsize='small')
  96. plt.savefig("tornados_us.png", dpi=190)
  97. @dataclass
  98. class Fmag:
  99. lons: List[int] = field(default_factory=list)
  100. lats: List[int] = field(default_factory=list)
  101. def append(collection, tornado):
  102. collection.lons.append(tornado.slon)
  103. collection.lats.append(tornado.slat)
  104. if tornado.elon < 0:
  105. collection.lons.append(tornado.elon)
  106. collection.lats.append(tornado.elat)
  107. else:
  108. collection.lons.append(tornado.slon)
  109. collection.lats.append(tornado.slat)
  110. collection.lons.append(None)
  111. collection.lats.append(None)
  112. if __name__ == '__main__':
  113. ts = read_data()
  114. f_unknown = Fmag()
  115. f_zero = Fmag()
  116. f_one = Fmag()
  117. f_two = Fmag()
  118. f_three = Fmag()
  119. f_four = Fmag()
  120. f_five = Fmag()
  121. for t in ts:
  122. if t.mag == 0:
  123. append(f_zero, t)
  124. elif t.mag == 1:
  125. append(f_one, t)
  126. elif t.mag == 2:
  127. append(f_two, t)
  128. elif t.mag == 3:
  129. append(f_three, t)
  130. elif t.mag == 4:
  131. append(f_four, t)
  132. elif t.mag == 5:
  133. append(f_five, t)
  134. else:
  135. append(f_unknown, t)
  136. draw_map(f_unknown, f_zero, f_one, f_two, f_three, f_four, f_five)