Skip to content
Snippets Groups Projects
FireGpkg.py 10.9 KiB
Newer Older
Yang Chen's avatar
Yang Chen committed
""" FireGpkg
Module for creating regional geopackage summary at each time step
List of geojson types
---------------------
* fperim(''): fire basic attributes and perimeter geometry
* fline('FL'): active fire line geometry
* NFP('NFP'): new fire pixels
List of functions
-----------------
* make_gdf_fperim
* make_gdf_fline
* make_gdf_NFP
* save_gdf_1t
* save_gdf_trng
Modules required
----------------
* FireObj
* FireIO
* FireConsts
"""

Yang's avatar
Yang committed

def make_gdf_snapshot(allfires, regnm, layer="perimeter"):
    """ Create gpd DataFrame for fire basic attributes and fire perimeter.
Yang Chen's avatar
Yang Chen committed
    Method:
    -------
    Extract attributes rom the allfires obj input, and create a gdf DataFrame.
    In order to save the running time, if the gpkg file for previous time step
    is available, read the data and only modify properties of previously active
    fires.
    Parameters
    ----------
    allfires : Allfires object
        the Allfires object to be used to modify gdf
    Returns
    -------
    gdf : geopandas DataFrame
        the gdf containing half daily fire basic attributes and fire perimeter
Yang's avatar
Yang committed
    """
Yang Chen's avatar
Yang Chen committed
    import geopandas as gpd
Yang Chen's avatar
Yang Chen committed
    # from FireConsts import dd
Yang's avatar
Yang committed
    import FireIO, FireObj, FireTime
Yang's avatar
Yang committed
    from FireConsts import epsg
Yang Chen's avatar
Yang Chen committed

    # diagnostic data name and types saved in geojson files (in addition to geometries)
Yang's avatar
Yang committed
    if layer == "perimeter":
Yang's avatar
Yang committed
        # dd1: group of attributes that can change with time (even without expansion or merging)
        dd1 = {
Yang's avatar
Yang committed
            "isactive": "int",  # active status
            "isdead": "int",  # dead status
            "t_inactive": "int",  # how long has it been inactive
            "isignition": "int",  # is this a new ignition?
            "mayreactivate": "int",  # sleeper status
            "t": "datetime64",
        }
Yang's avatar
Yang committed

        # dd2: group of attributes that can only change with fire expansion or merging
        dd2 = {
Yang's avatar
Yang committed
            "mergeid": "int",  # this is the id in the large fire database
            "ftype": "int",  # fire type
            # 'invalid':'int',              # invalid status
            "n_pixels": "int",  # number of total pixels
            "n_newpixels": "int",  # number of new pixels
            "farea": "float",  # fire size
            "fperim": "float",  # fire perimeter length
            "flinelen": "float",  # active fire front line length
            "duration": "float",  # fire duration
            "pixden": "float",  # fire pixel density
            "meanFRP": "float",  # mean FRP of the new fire pixels
            "t_st": "datetime64",
            "t_ed": "datetime64",
        }
    elif layer == "fireline":
Yang's avatar
Yang committed
        dd1 = {
Yang's avatar
Yang committed
            "t": "datetime64",
        }
Yang's avatar
Yang committed
        dd2 = {
            "mergeid": "int",
            "t_st": "datetime64",
            "t_ed": "datetime64",# this is the id in the large fire database
Yang's avatar
Yang committed
        }
    elif layer == "newfirepix":
Yang's avatar
Yang committed
        dd1 = {
Yang's avatar
Yang committed
            "t": "datetime64",
        }
Yang's avatar
Yang committed
        dd2 = {
            "mergeid": "int",
            "t_st": "datetime64",
            "t_ed": "datetime64",# this is the id in the large fire database
Yang's avatar
Yang committed
        }
Yang's avatar
Yang committed

    dd = {**dd1, **dd2}  # or (dd1 | dd2) for Python 3.9.0 or higher
Yang Chen's avatar
Yang Chen committed
    # read or initialize the gdf
Yang's avatar
Yang committed
    t_pt = FireTime.t_nb(allfires.t, nb="previous")
Eli Orland's avatar
Eli Orland committed
    gdf = FireIO.load_gpkgobj(t_pt, regnm, layer=layer)  # try to read data at t_pt

    if gdf is None:  # when no previous time step data, initialize the GeoDataFrame
        gdf = gpd.GeoDataFrame(
                columns=(list(dd.keys()) + ["fireID"]), crs="epsg:" + str(epsg), geometry=[]
            )
        gdf = gdf.set_index("fireID")  # set fid column as the index column
Yang's avatar
Yang committed

    # 1. drop rows for fires that are newly invalidated or dead

    # - for newly invalidated fire objects, drop the row
Yang's avatar
Yang committed
    # if 'invalid' in dd.keys():
    #     for fid in allfires.fids_invalid:  # set newly invalidated object
    #         gdf.loc[fid,'invalid'] = 1
    #     gdf.drop(gdf.index[gdf['invalid'] == 1], inplace = True)  # drop the row
Yang's avatar
Yang committed
    gdf.drop(allfires.fids_invalid, inplace=True)
Yang's avatar
Yang committed
    # - for newly dead fires, drop the row
Yang's avatar
Yang committed
    # if 'isdead' in dd.keys():
    #     for fid,f in allfires.deadfires.items():  # set dead fire object
    #         gdf.loc[fid,'isdead'] = 1
    #     gdf.drop(gdf.index[gdf['isdead'] == 1], inplace = True)  # drop the row
Yang's avatar
Yang committed
    gdf.drop(allfires.fids_dead, inplace=True)

    # - drop fires with fid not in allfires object anymore (mostly fires turning from mayactive to dead)
    fids_remove = list(set(gdf.index) - set(allfires.fids))
    gdf.drop(fids_remove, inplace=True)

Yang's avatar
Yang committed
    # 2. modify dd2 for fires with possible modification

    # - for mayactive fires (active+sleeper), copy attributes from fire object to gdf
Yang's avatar
Yang committed
    for fid, f in allfires.mayactivefires.items():  # loop over active fires
        for k, tp in dd2.items():
            if tp == "datetime64":
                gdf.loc[fid, k] = FireTime.t2dt(getattr(f, k))
Yang's avatar
Yang committed
            else:
Yang's avatar
Yang committed
                gdf.loc[fid, k] = getattr(f, k)
Yang Chen's avatar
Yang Chen committed

    # update the hull of each active fire as the geometry column
Yang's avatar
Yang committed
    if layer == "perimeter":
        for fid, f in allfires.mayactivefires.items():
Yang's avatar
Yang committed
            fhull = f.hull
Yang's avatar
Yang committed
            if fhull.geom_type == "MultiPolygon":
                gdf.loc[fid, "geometry"] = gpd.GeoDataFrame(
                    geometry=[fhull]
                ).geometry.values
Yang Chen's avatar
Yang Chen committed
            else:
Yang's avatar
Yang committed
                gdf.loc[fid, "geometry"] = fhull
Yang's avatar
Yang committed
    elif layer == "fireline":
        for fid, f in allfires.activefires.items():  # loop over active fires
Yang's avatar
Yang committed
            fline = f.fline
            if fline is not None:
Yang's avatar
Yang committed
                if fline.geom_type == "MultiLineString":
                    gdf.loc[fid, "geometry"] = gpd.GeoDataFrame(
                        geometry=[fline]
                    ).geometry.values
Yang's avatar
Yang committed
                else:
Yang's avatar
Yang committed
                    gdf.loc[fid, "geometry"] = fline
Yang's avatar
Yang committed

Yang's avatar
Yang committed
    elif layer == "newfirepix":
        for fid, f in allfires.activefires.items():  # loop over active fires
Yang's avatar
Yang committed
            if f.n_newpixels > 0:
Yang's avatar
Yang committed
                gdf.loc[fid, "geometry"] = gpd.GeoDataFrame(
                    geometry=[f.newlocsMP]
                ).geometry.values
Yang's avatar
Yang committed

    # 3. modify dd1 for all fires
Yang's avatar
Yang committed
    for fid, f in allfires.fires.items():  # loop over all fires
        for k, tp in dd1.items():
            if tp == "datetime64":
                gdf.loc[fid, k] = FireTime.t2dt(getattr(f, k))
Yang Chen's avatar
Yang Chen committed
            else:
Yang's avatar
Yang committed
                gdf.loc[fid, k] = getattr(f, k)
Yang's avatar
Yang committed
    # 4. force the correct dtypes
Yang's avatar
Yang committed
    for k, tp in dd.items():
Yang's avatar
Yang committed
        gdf[k] = gdf[k].astype(tp)
Yang's avatar
Yang committed
    # # 5. drop the columns with no use
    # if 'invalid' in dd.keys(): gdf = gdf.drop(columns='invalid')
Yang Chen's avatar
Yang Chen committed

    return gdf

Yang's avatar
Yang committed

def save_gdf_1t(t, regnm):
    """ Creat gdf using one Allfires object and save it to a geopackage file at 1 time step.
Yang Chen's avatar
Yang Chen committed
            This can be used  for fperim, fline, and NFP files.
    Parameters
    ----------
    allfires : Allfires object
        the Allfires object to be used to create gdf
Yang's avatar
Yang committed
    """
Yang Chen's avatar
Yang Chen committed
    import FireIO

Yang's avatar
Yang committed
    # read Allfires object from the saved pkl file
Yang's avatar
Yang committed
    allfires = FireIO.load_fobj(t, regnm, activeonly=True)
#     allfires = FireIO.load_fobj(t, regnm, activeonly=False)
Yang's avatar
Yang committed

Yang Chen's avatar
Yang Chen committed
    # create gdf using previous time step gdf values and new allfires object
Yang's avatar
Yang committed
    gdf_fperim = make_gdf_snapshot(allfires, regnm, layer="perimeter")
    gdf_fline = make_gdf_snapshot(allfires, regnm, layer="fireline")
    gdf_nfp = make_gdf_snapshot(allfires, regnm, layer="newfirepix")

    FireIO.save_gpkgobj(
        allfires.t, regnm, gdf_fperim=gdf_fperim, gdf_fline=gdf_fline, gdf_nfp=gdf_nfp
    )
Yang's avatar
Yang committed
def save_gdf_trng(tst, ted, regnm):
    """ Wrapper to create and save gpkg files for a time period
Yang Chen's avatar
Yang Chen committed
    Parameters
    ----------
    tst : tuple, (int,int,int,str)
        the year, month, day and 'AM'|'PM' at start time
    ted : tuple, (int,int,int,str)
        the year, month, day and 'AM'|'PM' at end time
Yang's avatar
Yang committed
    """
    import FireObj, FireIO, FireTime
Yang Chen's avatar
Yang Chen committed

    # loop over all days during the period
Yang's avatar
Yang committed
    endloop = False  # flag to control the ending olf the loop
Yang's avatar
Yang committed
    t = list(tst)  # t is the time (year,month,day,ampm) for each step
Yang Chen's avatar
Yang Chen committed
    while endloop == False:
Yang's avatar
Yang committed
        print("Snapshot making", t)
Yang Chen's avatar
Yang Chen committed

        # create and save gdfs according to input options
Yang's avatar
Yang committed
        save_gdf_1t(t, regnm)
Yang Chen's avatar
Yang Chen committed

        # time flow control
        #  - if t reaches ted, set endloop to True to stop the loop
Yang's avatar
Yang committed
        if FireTime.t_dif(t, ted) == 0:
Yang Chen's avatar
Yang Chen committed
            endloop = True

        #  - update t with the next time stamp
Yang's avatar
Yang committed
        t = FireTime.t_nb(t, nb="next")
Yang's avatar
Yang committed

Yang's avatar
Yang committed

def save_gdf_uptonow(t, regnm):
    """ Create and save all valid fires (active, sleeper, and dead) up to now
    """
    import FireIO, FireTime
Yang's avatar
Yang committed
    import geopandas as gpd
Yang's avatar
Yang committed
    from FireConsts import epsg
Yang's avatar
Yang committed

    # read allfires object
Yang's avatar
Yang committed
    allfires = FireIO.load_fobj(t, regnm, activeonly=False)
Yang's avatar
Yang committed

    # initialize gdf
Yang's avatar
Yang committed
    dd = {
        "isactive": "int",  # active status
        "isdead": "int",  # dead status
        "t_inactive": "int",  # how long has it been inactive
        "isignition": "int",  # is this a new ignition?
        "mayreactivate": "int",  # sleeper status
        "t": "datetime64",
        "mergeid": "int",  # this is the id in the large fire database
        "ftype": "int",  # fire type
        "n_pixels": "int",  # number of total pixels
        "farea": "float",  # fire size
        "fperim": "float",  # fire perimeter length
        "duration": "float",  # fire duration
        "pixden": "float",  # fire pixel density
        "t_st": "datetime64",
        "t_ed": "datetime64",
    }
    gdf = gpd.GeoDataFrame(
        columns=(list(dd.keys()) + ["fireID"]), crs="epsg:" + str(epsg), geometry=[]
    )
    gdf = gdf.set_index("fireID")
Yang's avatar
Yang committed

    # loop over valid fires
Yang's avatar
Yang committed
    for fid, f in allfires.validfires.items():
Yang's avatar
Yang committed
        # assign attributes
Yang's avatar
Yang committed
        for k, tp in dd.items():
            if tp == "datetime64":
                gdf.loc[fid, k] = FireTime.t2dt(getattr(f, k))
Yang's avatar
Yang committed
            else:
Yang's avatar
Yang committed
                gdf.loc[fid, k] = getattr(f, k)
Yang's avatar
Yang committed
        # assign geometry
        fhull = f.hull
Yang's avatar
Yang committed
        if fhull.geom_type == "MultiPolygon":
            gdf.loc[fid, "geometry"] = gpd.GeoDataFrame(
                geometry=[fhull]
            ).geometry.values
Yang's avatar
Yang committed
        else:
Yang's avatar
Yang committed
            gdf.loc[fid, "geometry"] = fhull
Yang's avatar
Yang committed

    # make sure the data types are correct
Yang's avatar
Yang committed
    for k, tp in dd.items():
Yang's avatar
Yang committed
        gdf[k] = gdf[k].astype(tp)

    # save
Yang's avatar
Yang committed
    FireIO.save_gpkgobj(allfires.t, regnm, gdf_uptonow=gdf)
Yang's avatar
Yang committed
    # return gdf
Yang Chen's avatar
Yang Chen committed
if __name__ == "__main__":
Yang's avatar
Yang committed
    """ The main code to record daily geojson data for a time period
    """
Yang Chen's avatar
Yang Chen committed

    import time
Yang Chen's avatar
Yang Chen committed
    t1 = time.time()
    # set the start and end time
Yang's avatar
Yang committed
    # tst=(2021,7,13,'AM')
    # ted=(2021,9,15,'PM')
    #
    # # for each day during the period, create and save geojson summary file
    # save_gdf_trng(tst=tst,ted=ted,regnm='Dixie')

    # tst=(2020,10,29,'PM')
    tst = (2020, 1, 1, "PM")
    ted = (2020, 12, 31, "PM")
Yang's avatar
Yang committed
    # save_gdf_trng(tst=tst,ted=ted,regnm='Creek')
    save_gdf_trng(tst=tst, ted=ted, regnm='California32610')
#     save_gdf_uptonow(ted, "Creek")



Yang Chen's avatar
Yang Chen committed

    t2 = time.time()
Eli Orland's avatar
Eli Orland committed
    print(f"{(t2-t1)/60.} minutes used to run code")