Skip to content
Snippets Groups Projects
Commit e40069c1 authored by screbec's avatar screbec
Browse files

cahnges related to spread rate computation and metric distances

parent 5edf2d5a
No related branches found
No related tags found
No related merge requests found
...@@ -18,6 +18,7 @@ This module include: ...@@ -18,6 +18,7 @@ This module include:
# a. Project modules used # a. Project modules used
import numpy as np import numpy as np
from pyproj import Geod
import FireIO import FireIO
import FireVector import FireVector
import FireClustering import FireClustering
...@@ -230,6 +231,7 @@ class Allfires: ...@@ -230,6 +231,7 @@ class Allfires:
# cumulative recordings # cumulative recordings
self.heritages = [] # a list of fire heritage relationships (source, target) self.heritages = [] # a list of fire heritage relationships (source, target)
self.mergedates = [] # a list of merge dates
self.id_dict = [] # this list relates the list position of the fire in the allfires object to the fire id self.id_dict = [] # this list relates the list position of the fire in the allfires object to the fire id
# (list_index, fireid) (list position can be variable when writing out only active fires) # (list_index, fireid) (list position can be variable when writing out only active fires)
...@@ -418,7 +420,22 @@ class Allfires: ...@@ -418,7 +420,22 @@ class Allfires:
''' '''
for f in self.activefires: for f in self.activefires:
f.spreadrate_ts.append(f.spreadavg) f.spreadrate_ts.append(f.spreadavg)
def compute_geom_stats(self):
'''this computes and sets the geod version of farea and perim
for each active fire'''
for f in self.activefires:
# get hull
fhull = f.hull
if fhull is None: # if no hull, return zero
f.perim = 0
self.farea = f.n_pixels * area_VI
else: # otherwise, use the hull length
geod = Geod(ellps="WGS84")
farea, perim = geod.geometry_area_perimeter(fhull)
f.farea = max(abs(farea)/1000000,area_VI) # geod.geometry_area_perimeter returns signed area
f.fperim = perim/1000
# b. Object - Fire # b. Object - Fire
class Fire: class Fire:
...@@ -461,7 +478,9 @@ class Fire: ...@@ -461,7 +478,9 @@ class Fire:
self.fline_prior = None # fline of latest active timestep, used for sleeper threshold self.fline_prior = None # fline of latest active timestep, used for sleeper threshold
self.prior_hull = None # hull of prior time step, used for spread rate computation self.prior_hull = None # hull of prior time step, used for spread rate computation
self.spreadrate_ts = [] # time series of average spread rates for the fire self.spreadrate_ts = [] # time series of average spread rates for the fire
self.farea = 0
self.fperim = 0
# initialize the exterior pixels (pixels within the inward extbuffer of # initialize the exterior pixels (pixels within the inward extbuffer of
# the hull; used for saving time for hull calculation of large fires) # the hull; used for saving time for hull calculation of large fires)
self.extpixels = FireVector.cal_extpixels(fpixels,hull) self.extpixels = FireVector.cal_extpixels(fpixels,hull)
...@@ -599,21 +618,6 @@ class Fire: ...@@ -599,21 +618,6 @@ class Fire:
''' '''
return len(self.ignpixels) return len(self.ignpixels)
@property
def farea(self):
''' Fire spatail size of the fire event (km2)
'''
# get hull
fhull = self.hull
# If no hull, return area calculated from number of pixels
if fhull is None:
return self.n_pixels * area_VI
# otherwise, use calConcHarea to calculate area,
# but no smaller than area_VI (sometimes calculated hull area is very mall)
else:
return max(FireVector.calConcHarea(fhull),area_VI)
@property @property
def pixden(self): def pixden(self):
''' Fire pixel density (number of pixels per km2 fire area) ''' Fire pixel density (number of pixels per km2 fire area)
...@@ -641,7 +645,7 @@ class Fire: ...@@ -641,7 +645,7 @@ class Fire:
''' '''
# get hull # get hull
fhull = self.hull fhull = self.hull
if fhull is not None: # centroid of the hull if fhull is not None: # centroid of the hull
cent = (fhull.centroid.y,fhull.centroid.x) cent = (fhull.centroid.y,fhull.centroid.x)
else: # when no hull, use the centroid of all pixels else: # when no hull, use the centroid of all pixels
...@@ -672,46 +676,44 @@ class Fire: ...@@ -672,46 +676,44 @@ class Fire:
from FireConsts import FTYP from FireConsts import FTYP
return FTYP[self.ftype] return FTYP[self.ftype]
@property
def fperim(self):
''' Perimeter length of fire hull
'''
# get hull
fhull = self.hull
if fhull is None: # if no hull, return zero
perim = 0
else: # otherwise, use the hull length
perim = fhull.length
return perim
@property @property
def flinepixels(self): def flinepixels(self):
''' List of all fire pixels near the fire perimeter (fine line pixels) ''' List of all fire pixels near the fire perimeter (fine line pixels)
''' '''
from shapely.geometry import Point, MultiLineString from shapely.geometry import Point, MultiLineString
from FireConsts import valpha
# get pixels of last active fire detection # get pixels of last active fire detection
nps = self.newpixels nps = self.newpixels
# get hull # get hull
fhull = self.hull fhull = self.hull
if fhull is None: # if no hull, return empty list if fhull is None: # if no hull, return empty list
return [] return []
else: # otherwise, extract the pixels nearl the hull else: # otherwise, extract the pixels nearl the hull
# nps_buf = [FireVector.addbuffer(Point(p.lon,p.lat), valpha+fpbuffer) for p in nps]
# if hull is a polygon, return new pixels near the hull # if hull is a polygon, return new pixels near the hull
if fhull.type == 'Polygon': if fhull.type == 'Polygon':
# lr = fhull.exterior.buffer(fpbuffer) lr = fhull.exterior
lr = FireVector.addbuffer(fhull.exterior, fpbuffer) # return [p for i,p in enumerate(nps) if lr.intersects(nps_buf[i])]
return [p for p in nps if lr.contains(Point(p.loc[1],p.loc[0]))]
# if hull is a multipolygon, return new pixels near the hull # if hull is a multipolygon, return new pixels near the hull
elif fhull.type == 'MultiPolygon': elif fhull.type == 'MultiPolygon':
# mlr = MultiLineString([x.exterior for x in fhull]).buffer(fpbuffer) lr = MultiLineString([x.exterior for x in fhull])
mlr = MultiLineString([x.exterior for x in fhull]) # mlr = FireVector.addbuffer(mlr,fpbuffer)
mlr = FireVector.addbuffer(mlr,fpbuffer) # return [p for i,p in enumerate(nps) if lr.intersects(nps_buf[i])]
return [p for p in nps if mlr.contains(Point(p.loc[1],p.loc[0]))]
# instead of buffer: compute all distances
dist = [lr.distance(Point(p.lat,p.lon)) for p in nps]
# compute distance in m (this should probably go in a separate function)
lats = [p.loc[0] for p in nps]
ldeg = (EARTH_RADIUS_KM*np.cos(np.deg2rad(lats))*1000*2*np.pi/360)
dist_m = np.array(dist)*np.array(ldeg) # convert dist in deg to m
return [p for i,p in enumerate(nps) if dist_m[i] <= fpbuffer+valpha]
@property @property
def fline(self): def fline(self):
...@@ -719,17 +721,17 @@ class Fire: ...@@ -719,17 +721,17 @@ class Fire:
''' '''
from shapely.geometry import MultiLineString#,Polygon,Point,MultiPoint from shapely.geometry import MultiLineString#,Polygon,Point,MultiPoint
from FireConsts import valpha from FireConsts import valpha
if len(self.flinepixels)==0: # if for whatever reason we don't have an active fire line if len(self.flinepixels)==0: # if for whatever reason we don't have an active fire line
return None return None
# get fireline pixel locations # get fireline pixel locations
flinelocs = [p.loc for p in self.flinepixels] flinelocs = [p.loc for p in self.flinepixels]
flinelocsMP = FireVector.doMultP(flinelocs,valpha) flinelocsMP = FireVector.doMultP(flinelocs,valpha+flbuffer)
# get the hull # get the hull
fhull = self.hull fhull = self.hull
# calculate the fire line # calculate the fire line
if fhull is None: # if no hull, return None if fhull is None: # if no hull, return None
return None return None
...@@ -739,17 +741,17 @@ class Fire: ...@@ -739,17 +741,17 @@ class Fire:
mls = MultiLineString([plg.exterior for plg in fhull]) mls = MultiLineString([plg.exterior for plg in fhull])
# return the part which intersects with bufferred flinelocsMP # return the part which intersects with bufferred flinelocsMP
# return mls.intersection(flinelocsMP.buffer(flbuffer)) # return mls.intersection(flinelocsMP.buffer(flbuffer))
flinelocsMP_buf = FireVector.addbuffer(flinelocsMP,flbuffer) # flinelocsMP_buf = FireVector.addbuffer(flinelocsMP,flbuffer)
fline = mls.intersection(flinelocsMP_buf) fline = mls.intersection(flinelocsMP)
elif fhull.type == 'Polygon': elif fhull.type == 'Polygon':
mls = fhull.exterior mls = fhull.exterior
# return mls.intersection(flinelocsMP.buffer(flbuffer)) # return mls.intersection(flinelocsMP.buffer(flbuffer))
flinelocsMP_buf = FireVector.addbuffer(flinelocsMP,flbuffer) # flinelocsMP_buf = FireVector.addbuffer(flinelocsMP,flbuffer)
fline = mls.intersection(flinelocsMP_buf) fline = mls.intersection(flinelocsMP)
else: # if fhull type is not 'MultiPolygon' or 'Polygon', return flinelocsMP else: # if fhull type is not 'MultiPolygon' or 'Polygon', return flinelocsMP
fline = flinelocsMP fline = flinelocsMP
# we save the fire line to a new property (this is only updated when fline not None) # we save the fire line to a new property (this is only updated when fline not None)
self.fline_prior = fline self.fline_prior = fline
...@@ -760,10 +762,11 @@ class Fire: ...@@ -760,10 +762,11 @@ class Fire:
''' The length of active fire line ''' The length of active fire line
''' '''
try: try:
flinelen = self.fline.length geod = Geod(ellps="WGS84")
flinelen = geod.geometry_length(self.fline)/1000 # in km
except: except:
flinelen = 0 flinelen = 0
return flinelen return flinelen
@property @property
...@@ -780,7 +783,7 @@ class Fire: ...@@ -780,7 +783,7 @@ class Fire:
# compute distance in m (this should probably go in a separate function) # compute distance in m (this should probably go in a separate function)
lats = [p.loc[0] for p in nps] lats = [p.loc[0] for p in nps]
ldeg = (EARTH_RADIUS_KM*np.cos(np.deg2rad(lats))*1000*2*np.pi/360) ldeg = (EARTH_RADIUS_KM*np.cos(np.deg2rad(lats))*1000*2*np.pi/360)
dist_m = np.array(dist)*np.array(ldeg) # convert dist in m to degs dist_m = np.array(dist)*np.array(ldeg) # convert dist in deg to m
return dist_m return dist_m
else: # this is the ignition time step else: # this is the ignition time step
...@@ -813,6 +816,21 @@ class Fire: ...@@ -813,6 +816,21 @@ class Fire:
return spread return spread
else: # this is the ignition time step else: # this is the ignition time step
return np.nan return np.nan
def add_prior_hull(self,src_hull):
'''Adds a prior_hull of a merged fire
for accurate spread rate calculation'''
from shapely.ops import unary_union
tgt_hull = self.prior_hull
if src_hull is None:
self.prior_hull = tgt_hull
elif tgt_hull is None:
self.prior_hull = src_hull
else:
self.prior_hull = unary_union([tgt_hull, src_hull])
# c. Object - Cluster # c. Object - Cluster
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment