2 Developed in Python 2.7.15 :: Anaconda 4.5.10, on Windows
3 Lauren Hearn (lauren@robotlauren.com) 2018-10
5 from netCDF4
import Dataset
6 from datetime
import datetime
, timedelta
8 import matplotlib
.pyplot
as plt
9 from mpl_toolkits
.basemap
import Basemap
10 from pyproj
import Proj
16 # Seconds since 2000-01-01 12:00:00
17 add_seconds
=ncf
.variables
['t'][0]
18 # Datetime of image scan
19 dt
=datetime(2000, 1, 1, 12)+timedelta(seconds
=float(add_seconds
.data
))
23 # Detections in RGB array
25 R
=ncf
.variables
['CMI_C07'][:].data
26 G
=ncf
.variables
['CMI_C06'][:].data
27 B
=ncf
.variables
['CMI_C05'][:].data
28 # Turn empty values into nans
32 # Apply range limits for each channel (mostly important for Red channel)
39 # Normalize each channel by the appropriate range of values
43 # Apply the gamma correction to Red channel.
46 # The final RGB array :)
47 RGB
=np
.dstack([R
, G
, B
])
53 sat_h
=ncf
.variables
['goes_imager_projection'].perspective_point_height
57 sat_lon
=ncf
.variables
['goes_imager_projection'].longitude_of_projection_origin
61 sat_sweep
=ncf
.variables
['goes_imager_projection'].sweep_angle_axis
64 # The projection x and y coordinates equals
65 # the scanning angle (in radians) multiplied by the satellite height (https://proj4.org/operations/projections/geos.html)
66 X
=ncf
.variables
['x'][:] * sat_h
67 Y
=ncf
.variables
['y'][:] * sat_h
73 # Visualize using pyproj (https://github.com/pyproj4/pyproj/blob/master/pyproj/proj.py)
74 p
=Proj(proj
='geos', h
=sat_h
, lon_0
=sat_lon
, sweep
=sat_sweep
)
75 # Convert map points to latitude and longitude with the magic provided by Pyproj
76 XX
, YY
=np
.meshgrid(X
, Y
)
77 lons
, lats
=p(XX
, YY
, inverse
=True)
78 lats
[np
.isnan(R
)]=np
.nan
79 lons
[np
.isnan(R
)]=np
.nan
81 # create new netCDF file with lat/lon info
83 newfile = "appended" + file
84 w_ncf = Dataset(newfile, 'w')
85 with Dataset(file) as src, w_ncf as dst:
87 for name in src.ncattrs():
88 dst.setncattr(name, src.getncattr(name))
90 for name, dimension in src.dimensions.iteritems():
92 name, (len(dimension) if not dimension.isunlimited else None))
93 # copy all file data from source file
94 for name, variable in src.variables.iteritems():
95 x = dst.createVariable(name, variable.datatype, variable.dimensions)
96 dst.variables[name][:] = src.variables[name][:]
98 # add new lat/lon info
99 dst.createDimension('longitude', None)
100 dst.createDimension('lattitude', None)
101 # Assign the dimension data to the new NetCDF file.
102 dst.createVariable('longitude', X.dtype, ('longitude',))
103 dst.createVariable('lattitude', Y.dtype, ('lattitude',))
104 dst.variables['longitude'][:] = lons
105 dst.variables['lattitude'][:] = lats
110 # Make a new map object for the HRRR model domain map projection
111 mH
= Basemap(resolution
='l', projection
='lcc', area_thresh
=5000, \
112 width
=1800*3000, height
=1060*3000, \
113 lat_1
=38.5, lat_2
=38.5, \
114 lat_0
=38.5, lon_0
=-97.5)
115 # Create a color tuple for pcolormesh
116 rgb
= RGB
[:,:-1,:] # Using one less column is very important, else your image will be scrambled! (This is the stange nature of pcolormesh)
117 colorTuple
= rgb
.reshape((rgb
.shape
[0] * rgb
.shape
[1]), 3) # flatten array, becuase that's what pcolormesh wants.
118 colorTuple
= np
.insert(colorTuple
, 3, 1.0, axis
=1) # adding an alpha channel will plot faster, according to stackoverflow. Not sure why.
119 # Now we can plot the GOES data on the HRRR map domain and projection
120 plt
.figure(figsize
=[10,8])
121 # The values of R are ignored becuase we plot the color in colorTuple, but pcolormesh still needs its shape.
122 newmap
= mH
.pcolormesh(lons
, lats
, R
, color
=colorTuple
, linewidth
=0, latlon
=True)
123 newmap
.set_array(None) # without this line the linewidth is set to zero, but the RGB colorTuple is ignored. I don't know why.
124 mH
.drawcoastlines(color
='w')
125 mH
.drawcountries(color
='w')
126 mH
.drawstates(color
='w')
127 plt
.title('GOES-16 Fire Temperature', fontweight
='semibold', fontsize
=15)
128 plt
.title('%s' % dt
.strftime('%H:%M UTC %d %B %Y'), loc
='right')
131 # location of South Sugarloaf fire
132 l
= {'latitude': 41.812,
133 'longitude': -116.324}
135 #l = {'latitude': 39.2827513,
136 # 'longitude':-76.6214623}
138 #l = {'latitude': 33.724,
139 # 'longitude': -108.538}
141 mZ
= Basemap(projection
='lcc', resolution
='i',
142 width
=3E5
, height
=3E5
,
143 lat_0
=l
['latitude'], lon_0
=l
['longitude'],)
145 # Now we can plot the GOES data on a zoomed in map centered on the Sugarloaf wildfire
146 plt
.figure(figsize
=[10, 10])
147 newmap
= mZ
.pcolormesh(lons
, lats
, R
, color
=colorTuple
, linewidth
=0, latlon
=True)
148 newmap
.set_array(None)
150 #mZ.scatter(l['longitude'], l['latitude'], marker='o', s=1000, facecolor='none', edgecolor='greenyellow') # circle fire area
151 mZ
.drawcoastlines(color
='w')
152 mZ
.drawcountries(color
='w')
153 mZ
.drawstates(color
='w')
155 plt
.title('GOES-16 Fire Temperature\n', fontweight
='semibold', fontsize
=15)
156 plt
.title('%s' % dt
.strftime('%H:%M UTC %d %B %Y'), loc
='right')
157 plt
.title('South Sugarloaf Fire', loc
='left')