Spaces:
				
			
			
	
			
			
		Sleeping
		
	
	
	
			
			
	
	
	
	
		
		
		Sleeping
		
	Upload 16 files
Browse files- Dockerfile +30 -0
- Home.py +44 -0
- LICENSE +21 -0
- README.md +58 -0
- __init__.py +0 -0
- __pycache__/NBR_calculations.cpython-312.pyc +0 -0
- current_fires.py +399 -0
- historical_fires.py +429 -0
- pages/Home.py +44 -0
- pages/__init__.py +0 -0
- pages/__pycache__/NBR_calculations.cpython-312.pyc +0 -0
- pages/current_fires.py +399 -0
- pages/historical_fires.py +429 -0
- requirements.txt +6 -0
- utils/NBR_calculations.py +130 -0
- utils/__init__.py +0 -0
    	
        Dockerfile
    ADDED
    
    | @@ -0,0 +1,30 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            FROM jupyter/base-notebook:latest
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Install required packages
         | 
| 4 | 
            +
            RUN mamba install -c conda-forge leafmap geopandas localtileserver -y && \
         | 
| 5 | 
            +
                fix-permissions "${CONDA_DIR}" && \
         | 
| 6 | 
            +
                fix-permissions "/home/${NB_USER}"
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            # Copy the requirements file and install dependencies
         | 
| 9 | 
            +
            COPY requirements.txt .
         | 
| 10 | 
            +
            RUN pip install -r requirements.txt
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            # Copy the entire project directory into the container
         | 
| 13 | 
            +
            COPY . /home/${NB_USER}
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            # Set the working directory
         | 
| 16 | 
            +
            WORKDIR /home/${NB_USER}
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            # Set the PROJ_LIB environment variable
         | 
| 19 | 
            +
            ENV PROJ_LIB='/opt/conda/share/proj'
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            # Ensure the notebook user owns the home directory
         | 
| 22 | 
            +
            USER root
         | 
| 23 | 
            +
            RUN chown -R ${NB_UID} ${HOME}
         | 
| 24 | 
            +
            USER ${NB_USER}
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            # Expose the port for Solara
         | 
| 27 | 
            +
            EXPOSE 8765
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            # Run the Solara app
         | 
| 30 | 
            +
            CMD ["solara", "run", "pages", "--host=0.0.0.0"]
         | 
    	
        Home.py
    ADDED
    
    | @@ -0,0 +1,44 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import solara
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            @solara.component
         | 
| 4 | 
            +
            def Page():
         | 
| 5 | 
            +
                with solara.Column(align="center"):
         | 
| 6 | 
            +
                    markdown = """
         | 
| 7 | 
            +
                    ## Real-time wildfire burn mapping 
         | 
| 8 | 
            +
                    
         | 
| 9 | 
            +
                    ### About the project
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    **A proof of concept illustrating wildfire burn severity maps with emerging clarity while the fires progress. 
         | 
| 12 | 
            +
                    Target users are forecasters and emergency managers responding to post-fire risks including debris flows and landslides.**
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    More project description, etc, etc. 
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    **Case Studies from 2020 and 2021 Western US wildfire seasons **
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    - August Complex, CA (2020)
         | 
| 19 | 
            +
                    - Cameron Peak, CO (2020)
         | 
| 20 | 
            +
                    - Dixie Fire, CA (2021)
         | 
| 21 | 
            +
                    - North Complex, CA (2020)
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    
         | 
| 24 | 
            +
                     **Current 2024 wildfires over 10,000 acres **
         | 
| 25 | 
            +
                    
         | 
| 26 | 
            +
                    ### How to use the app
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    1. Select the fire from the drop-down menu
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    2. Export image to Google Drive as a geotiff
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    3. 
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    ### Support
         | 
| 35 | 
            +
                    
         | 
| 36 | 
            +
                    Initial funding for wildland burn scar mapping came through the NOAA JPSS/RRPG Fire and Smoke Initiative. 
         | 
| 37 | 
            +
                    This supported the initial tests of BRIDGE maps using dNDVI. Subsequent funding supported the development of dNBR mapping and an effort 
         | 
| 38 | 
            +
                    to tie support the near real-time distribution of incident-based fire detection and related satellite imagery products through the Next Generation Fire System (NGFS). 
         | 
| 39 | 
            +
                    Current funding from the NOAA Weather Program Office (WPO) is supporting the refinement of our Google Earth Engine App (GEE) 
         | 
| 40 | 
            +
                    and integration of GEE burn scar output with AWIPS (see example above) for Weather Forecast Offices, Regional Offices, and the Weather Prediction Center.
         | 
| 41 | 
            +
                    
         | 
| 42 | 
            +
                    """
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    solara.Markdown(markdown)
         | 
    	
        LICENSE
    ADDED
    
    | @@ -0,0 +1,21 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            MIT License
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Copyright (c) 2023 Open Geospatial Solutions
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
| 6 | 
            +
            of this software and associated documentation files (the "Software"), to deal
         | 
| 7 | 
            +
            in the Software without restriction, including without limitation the rights
         | 
| 8 | 
            +
            to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         | 
| 9 | 
            +
            copies of the Software, and to permit persons to whom the Software is
         | 
| 10 | 
            +
            furnished to do so, subject to the following conditions:
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            The above copyright notice and this permission notice shall be included in all
         | 
| 13 | 
            +
            copies or substantial portions of the Software.
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         | 
| 16 | 
            +
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         | 
| 17 | 
            +
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         | 
| 18 | 
            +
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         | 
| 19 | 
            +
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         | 
| 20 | 
            +
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         | 
| 21 | 
            +
            SOFTWARE.
         | 
    	
        README.md
    ADDED
    
    | @@ -0,0 +1,58 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            title: Solara Geemap
         | 
| 3 | 
            +
            emoji: 🏃
         | 
| 4 | 
            +
            colorFrom: blue
         | 
| 5 | 
            +
            colorTo: purple
         | 
| 6 | 
            +
            sdk: docker
         | 
| 7 | 
            +
            pinned: false
         | 
| 8 | 
            +
            license: mit
         | 
| 9 | 
            +
            app_port: 8765
         | 
| 10 | 
            +
            ---
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            ## Earth Engine Web Apps
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            ### Introduction
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            **A collection of Earth Engine web apps developed using [Solara](https://github.com/widgetti/solara) and geemap**
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            - Web App: <https://giswqs-solara-geemap.hf.space>
         | 
| 19 | 
            +
            - GitHub: <https://github.com/opengeos/solara-geemap>
         | 
| 20 | 
            +
            - Hugging Face: <https://huggingface.co/spaces/giswqs/solara-geemap>
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            ### How to deploy this app on Hugging Face Spaces
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            1. Go to <https://huggingface.co/spaces/giswqs/solara-geemap/tree/main> and duplicate the space to your own space.
         | 
| 25 | 
            +
             | 
| 26 | 
            +
               
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            2. You need to set `EARTHENGINE_TOKEN` in order to use Earth Engine. The token value should be copied from the following file depending on your operating system:
         | 
| 29 | 
            +
             | 
| 30 | 
            +
               ```text
         | 
| 31 | 
            +
               Windows: C:\\Users\\USERNAME\\.config\\earthengine\\credentials
         | 
| 32 | 
            +
               Linux: /home/USERNAME/.config/earthengine/credentials
         | 
| 33 | 
            +
               MacOS: /Users/USERNAME/.config/earthengine/credentials
         | 
| 34 | 
            +
               ```
         | 
| 35 | 
            +
             | 
| 36 | 
            +
               Simply open the file and copy **ALL** the content to the `EARTHENGINE_TOKEN` environment variable.
         | 
| 37 | 
            +
             | 
| 38 | 
            +
               
         | 
| 39 | 
            +
             | 
| 40 | 
            +
               
         | 
| 41 | 
            +
             | 
| 42 | 
            +
               Alternatively, you can run the following code to retrieve your Earth Engine token:
         | 
| 43 | 
            +
             | 
| 44 | 
            +
               ```python
         | 
| 45 | 
            +
               import geemap
         | 
| 46 | 
            +
               geemap.get_ee_token()
         | 
| 47 | 
            +
               ```
         | 
| 48 | 
            +
             | 
| 49 | 
            +
               Copy all the content of the printed token and set it as the `EARTHENGINE_TOKEN` environment variable.
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            3. After the space is built successfully, click the `Embed this Space` menu and find the `Direct URL` for the app, such as <https://giswqs-solara-geemap.hf.space>.
         | 
| 52 | 
            +
             | 
| 53 | 
            +
               
         | 
| 54 | 
            +
             | 
| 55 | 
            +
               
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            4. Add your own apps (\*.py) to the `pages` folder.
         | 
| 58 | 
            +
            5. Commit and push your changes to the repository. Wait for the space to be built successfully.
         | 
    	
        __init__.py
    ADDED
    
    | 
            File without changes
         | 
    	
        __pycache__/NBR_calculations.cpython-312.pyc
    ADDED
    
    | Binary file (6.87 kB). View file | 
|  | 
    	
        current_fires.py
    ADDED
    
    | @@ -0,0 +1,399 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import ee
         | 
| 2 | 
            +
            import geemap
         | 
| 3 | 
            +
            import solara
         | 
| 4 | 
            +
            import ipywidgets as widgets
         | 
| 5 | 
            +
            import datetime
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            #from NBR_calculations import GcalcNBR, VcalcNBR, LcalcNBR, ScalcNBR, GcalcCCsingle
         | 
| 8 | 
            +
            import requests
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            # Bit-masking
         | 
| 11 | 
            +
            BitMask_0 = 1 << 0
         | 
| 12 | 
            +
            BitMask_1 = 1 << 1
         | 
| 13 | 
            +
            BitMask_2 = 1 << 2
         | 
| 14 | 
            +
            BitMask_3 = 1 << 3
         | 
| 15 | 
            +
            BitMask_4 = 1 << 4
         | 
| 16 | 
            +
            BitMask_5 = 1 << 5
         | 
| 17 | 
            +
            BitMask_6 = 1 << 6
         | 
| 18 | 
            +
            BitMask_7 = 1 << 7
         | 
| 19 | 
            +
            BitMask_8 = 1 << 8
         | 
| 20 | 
            +
            BitMask_9 = 1 << 9
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            def GcalcCCsingle (goesImg):
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              fireDQF = goesImg.select('DQF').int()
         | 
| 25 | 
            +
              CMI_QF3 = goesImg.select('DQF_C03').int()
         | 
| 26 | 
            +
              CMI_QF6 = goesImg.select('DQF_C06').int()
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              #Right now, cloud mask is excluding clouds and water; active fire, bad data and fire free are unmasked. NBR mask exlcudes fire
         | 
| 29 | 
            +
              F_Mask = fireDQF.eq(0)
         | 
| 30 | 
            +
              C_Mask = (fireDQF.lt(2).Or(fireDQF.gt(2))).rename('C_Mask')
         | 
| 31 | 
            +
                   #.And(CMI_QF3.lt(2)).And(CMI_QF6.lt(2)).rename('C_Mask')
         | 
| 32 | 
            +
              QF_Mask = (fireDQF.eq(1).Or(fireDQF.gt(3)))\
         | 
| 33 | 
            +
                    .And(CMI_QF3.lt(2)).And(CMI_QF6.lt(2)).rename('QFmask')
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              GOESmasked = goesImg.select(['CMI_C03','CMI_C06']).updateMask(QF_Mask)
         | 
| 36 | 
            +
              NBRmasked = GOESmasked.normalizedDifference(['CMI_C03', 'CMI_C06']).toFloat().rename('NBR')
         | 
| 37 | 
            +
              cloudMasked = goesImg.select('CMI_C03').updateMask(C_Mask).toFloat().rename('CC')
         | 
| 38 | 
            +
              fireMasked =  goesImg.select('CMI_C03').updateMask(F_Mask).toFloat().rename('FC')
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              return goesImg.addBands([NBRmasked,cloudMasked, fireMasked,QF_Mask,C_Mask])
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            '''Parameter Array Name Value Bit(s) = Value
         | 
| 43 | 
            +
            Sun Glint           QF1 Surface Reflectance None 6-7 = 00
         | 
| 44 | 
            +
            Low Sun Mask        QF1 Surface Reflectance High 5 = 0
         | 
| 45 | 
            +
            Day/Night           QF1 Surface Reflectance Day 4 =0
         | 
| 46 | 
            +
            Cloud Detection     QF1 Surface Reflectance Confident Clear 2-3 = 00 or Problably Clear 2-3 = 01
         | 
| 47 | 
            +
            Cloud Mask Quality  QF1 Surface Reflectance High or Medium 0-1 = 10 or 11
         | 
| 48 | 
            +
            Snow/Ice            QF2 Surface Reflectance No Snow or Ice 5 = 0
         | 
| 49 | 
            +
            Cloud Shadow        QF2 Surface Reflectance No Cloud Shadow 3 = 0
         | 
| 50 | 
            +
            LandWater           QF2 Surface Reflectance Land, Snow, Arctic, Antarctic or Greenland, Desert 0-2  = 011, 100, 101, 110, 111
         | 
| 51 | 
            +
            Thin Cirrus Flag    QF7 Surface Reflectance No Thin Cirrus 4 = 0
         | 
| 52 | 
            +
            Aerosol Quantity    QF7 Surface Reflectance Climatology, Low or Medium 2-3 = 00, 01 or 10
         | 
| 53 | 
            +
            Adjacent to Cloud   QF7 Surface Reflectance Not Adjacent to Cloud 1 = 0'''
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            def VcalcNBR (VIIRSimg):
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                QF1 = VIIRSimg.select('QF1').int()
         | 
| 58 | 
            +
                QF2 = VIIRSimg.select('QF2').int()
         | 
| 59 | 
            +
                QF7 = VIIRSimg.select('QF7').int()
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                QF_Mask = (QF1.bitwiseAnd(BitMask_3).eq(0)).And\
         | 
| 62 | 
            +
                ((QF2.bitwiseAnd(BitMask_2).eq(4)).Or((QF2.bitwiseAnd(BitMask_1).eq(0)))).And\
         | 
| 63 | 
            +
                (QF2.bitwiseAnd(BitMask_5).eq(0)).rename('QFmask');
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                VIIRSm = VIIRSimg.select(['I2','M11']).updateMask(QF_Mask);
         | 
| 66 | 
            +
                NBR = VIIRSm.normalizedDifference(['I2','M11']).toFloat().rename('NBR')
         | 
| 67 | 
            +
                return VIIRSimg.addBands(NBR).addBands(QF_Mask)#.set('avgNBR', avgNBR)
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            ''' Bit 1: Dilated Cloud
         | 
| 70 | 
            +
                Bit 2: Cirrus (high confidence)
         | 
| 71 | 
            +
                Bit 3: Cloud
         | 
| 72 | 
            +
                Bit 4: Cloud Shadow
         | 
| 73 | 
            +
                Bit 5: Snow
         | 
| 74 | 
            +
                Bit 6: Clear (0: Cloud or Dilated Cloud bits are set, 1: Cloud and Dilated Cloud bits are not set)
         | 
| 75 | 
            +
                Bit 7: Water
         | 
| 76 | 
            +
                Bits 8-9: Cloud Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 77 | 
            +
                Bits 10-11: Cloud Shadow Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 78 | 
            +
                Bits 12-13: Snow/Ice Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 79 | 
            +
                Bits 14-15: Cirrus Confidence (0: None, 1: Low, 2: Medium, 3: High)'''
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            def LcalcNBR (LSimg):
         | 
| 82 | 
            +
              QApixel = LSimg.select('QA_PIXEL').int()
         | 
| 83 | 
            +
              QF_Mask =(QApixel.bitwiseAnd(BitMask_3).eq(0)).And\
         | 
| 84 | 
            +
                       (QApixel.bitwiseAnd(BitMask_5).eq(0)).And\
         | 
| 85 | 
            +
                       (QApixel.bitwiseAnd(BitMask_7).eq(0)).rename('QFmask');
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              LSmasked = LSimg.select(['SR_B5','SR_B7']).updateMask(QF_Mask);
         | 
| 88 | 
            +
              NBR = LSmasked.normalizedDifference(['SR_B5','SR_B7']).toFloat().rename('NBR')
         | 
| 89 | 
            +
              return LSimg.addBands(NBR).addBands(QF_Mask)#.set('avgNBR', avgNBR)
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            ''' 1    Saturated or defective
         | 
| 92 | 
            +
                2    Dark Area Pixels
         | 
| 93 | 
            +
                3    Cloud Shadows
         | 
| 94 | 
            +
                4    Vegetation
         | 
| 95 | 
            +
                5    Bare Soils
         | 
| 96 | 
            +
                6    Water
         | 
| 97 | 
            +
                7    Clouds Low Probability / Unclassified
         | 
| 98 | 
            +
                8    Clouds Medium Probability
         | 
| 99 | 
            +
                9    Clouds High Probability
         | 
| 100 | 
            +
                10   Cirrus
         | 
| 101 | 
            +
                11   Snow / Ice'''
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            def ScalcNBR (sentImg):
         | 
| 104 | 
            +
              SCL = sentImg.select('SCL');
         | 
| 105 | 
            +
              QF_Mask =(SCL.neq(6)).And\
         | 
| 106 | 
            +
                (SCL.neq(8)).And\
         | 
| 107 | 
            +
                (SCL.neq(9)).And\
         | 
| 108 | 
            +
                (SCL.neq(11))\
         | 
| 109 | 
            +
                .rename('QFmask');
         | 
| 110 | 
            +
              sentMasked = sentImg.select(['B8A','B12']).updateMask(QF_Mask); #B8 is another option- broadband NIR
         | 
| 111 | 
            +
              NBR = sentMasked.normalizedDifference(['B8A','B12']).toFloat().rename('NBR')
         | 
| 112 | 
            +
              return sentImg.addBands(NBR).addBands(QF_Mask).addBands(SCL)#.set('avgNBR', avgNBR)
         | 
| 113 | 
            +
             | 
| 114 | 
            +
            #createDates = NIFC_perims_716.aggregate_array('attr_Cre_1')
         | 
| 115 | 
            +
            #incidentIDs = NIFC_perims_716.aggregate_array('poly_Incid')
         | 
| 116 | 
            +
            #fireList = incidentIDs.getInfo()
         | 
| 117 | 
            +
            fireList = wildfire_names = [ "FRESNO JUNE LIGHTNING COMPLEX", "Larch Creek","Deadman","Cow Valley","0404 RV LONE ROCK",
         | 
| 118 | 
            +
                "PIONEER","South Fork", "Deer Springs","Basin","Lake","Horse Gulch","Falls","Silver King","Indios"]
         | 
| 119 | 
            +
            selected_fire = solara.reactive(fireList[6])
         | 
| 120 | 
            +
            dNBRvisParams = {'min': 0.0,'max': 0.8, 'palette': ['green', 'yellow','orange','red']}
         | 
| 121 | 
            +
            today = datetime.datetime.today().strftime('%Y-%m-%d')
         | 
| 122 | 
            +
             | 
| 123 | 
            +
            class Map(geemap.Map):
         | 
| 124 | 
            +
                def __init__(self, **kwargs):
         | 
| 125 | 
            +
                    super().__init__(**kwargs)        
         | 
| 126 | 
            +
                    self.add_basemap('OpenStreetMap')
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                    self.customize_ee_data(selected_fire.value, today)
         | 
| 129 | 
            +
                    self.add_selector()
         | 
| 130 | 
            +
                    self.add_dwnldButton()
         | 
| 131 | 
            +
                    self.add("layer_manager")
         | 
| 132 | 
            +
                    self.remove("draw_control")
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                    
         | 
| 135 | 
            +
                def customize_ee_data(self, fireID, elapDays):
         | 
| 136 | 
            +
                    NIFC_perims_716 = ee.FeatureCollection('projects/ovcrge-ssec-burn-scar-map-c116/assets/NIFC_perimeters_7-16')
         | 
| 137 | 
            +
                    fire = NIFC_perims_716.filter(ee.Filter.eq('poly_Incid',fireID)).first()
         | 
| 138 | 
            +
                    timestamp = fire.get('attr_Cre_1')
         | 
| 139 | 
            +
                    geom = fire.geometry()
         | 
| 140 | 
            +
                    
         | 
| 141 | 
            +
                    startDate = ee.Date(timestamp)#.format('YYYY-MM-dd')
         | 
| 142 | 
            +
                    endDate = ee.Date.parse('YYYY-MM-dd', str(today))
         | 
| 143 | 
            +
                    
         | 
| 144 | 
            +
                    boundingBox = ee.Geometry(geom.buffer(5000).bounds())
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                    elapDayNum = ee.Number(10)
         | 
| 147 | 
            +
                    elapDay_plusOne = elapDayNum.add(ee.Number(1))
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                    def calc_nbr(pre_start, pre_stop, post_start, post_stop, bbox, goes):
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                            def MergeBands (eachImage):
         | 
| 152 | 
            +
                                oneImage = ee.Image.cat(eachImage.get('CMI'), eachImage.get('FDC'))
         | 
| 153 | 
            +
                                return oneImage
         | 
| 154 | 
            +
                            displacementImg18 = ee.Image.load('projects/ee-losos/assets/G18-F-meter-offset_GEE')
         | 
| 155 | 
            +
                            y_dif = displacementImg18.select([1])
         | 
| 156 | 
            +
                            x_dif = displacementImg18.select([0]).multiply(-1)
         | 
| 157 | 
            +
                            displacement18 = ee.Image([x_dif, y_dif])
         | 
| 158 | 
            +
                            
         | 
| 159 | 
            +
                            displacementImg16 = ee.Image.load('projects/ee-losos/assets/G16-F-meter-offset_GEE')
         | 
| 160 | 
            +
                            y_dif = displacementImg16.select([1])
         | 
| 161 | 
            +
                            x_dif = displacementImg16.select([0]).multiply(-1)
         | 
| 162 | 
            +
                            displacement16 = ee.Image([x_dif, y_dif]);
         | 
| 163 | 
            +
                            
         | 
| 164 | 
            +
                            preCMIcol = ee.ImageCollection(f"NOAA/GOES/{goes}/MCMIPF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 165 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 166 | 
            +
                            preFDCcol = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 167 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 168 | 
            +
                            postCMIcol = ee.ImageCollection(f"NOAA/GOES/{goes}/MCMIPF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 169 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 170 | 
            +
                            postFDCcol = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 171 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                            prejoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 174 | 
            +
                            primary = preCMIcol,
         | 
| 175 | 
            +
                            secondary = preFDCcol,
         | 
| 176 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 177 | 
            +
                                difference = 10, #milliseconds
         | 
| 178 | 
            +
                                leftField = 'system:time_start',
         | 
| 179 | 
            +
                                rightField = 'system:time_start',))
         | 
| 180 | 
            +
                            preMiddayGOEScol = ee.ImageCollection(prejoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 181 | 
            +
                            preMiddayGOEScol = preMiddayGOEScol.map(GcalcCCsingle)
         | 
| 182 | 
            +
                            pre_meanNBR = preMiddayGOEScol.select(['NBR']).mean()
         | 
| 183 | 
            +
                            pre_meanNBR = pre_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                            postjoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 186 | 
            +
                            primary = postCMIcol,
         | 
| 187 | 
            +
                            secondary = postFDCcol,
         | 
| 188 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 189 | 
            +
                                difference = 10, #milliseconds
         | 
| 190 | 
            +
                                leftField = 'system:time_start',
         | 
| 191 | 
            +
                                rightField = 'system:time_start',))
         | 
| 192 | 
            +
                            postMiddayGOEScol = ee.ImageCollection(postjoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 193 | 
            +
                            postMiddayGOEScol = postMiddayGOEScol.map(GcalcCCsingle)
         | 
| 194 | 
            +
                            post_meanNBR = postMiddayGOEScol.select(['NBR']).mean()
         | 
| 195 | 
            +
                            post_meanNBR = post_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                            dNBR_goes17 = pre_meanNBR.subtract(post_meanNBR).select('NBR')
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                            
         | 
| 200 | 
            +
                            #GOES-16
         | 
| 201 | 
            +
                            preCMIcol = ee.ImageCollection("NOAA/GOES/16/MCMIPF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 202 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 203 | 
            +
                            preFDCcol = ee.ImageCollection("NOAA/GOES/16/FDCF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 204 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                            prejoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 207 | 
            +
                            primary = preCMIcol,
         | 
| 208 | 
            +
                            secondary = preFDCcol,
         | 
| 209 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 210 | 
            +
                                difference = 10, #milliseconds
         | 
| 211 | 
            +
                                leftField = 'system:time_start',
         | 
| 212 | 
            +
                                rightField = 'system:time_start',))
         | 
| 213 | 
            +
                            preMiddayGOEScol = ee.ImageCollection(prejoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 214 | 
            +
                            preMiddayGOEScol = preMiddayGOEScol.map(GcalcCCsingle)
         | 
| 215 | 
            +
                            pre_meanNBR = preMiddayGOEScol.select(['NBR']).mean()
         | 
| 216 | 
            +
                            pre_meanNBR = pre_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                            
         | 
| 219 | 
            +
                            postCMIcol = ee.ImageCollection("NOAA/GOES/16/MCMIPF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 220 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 221 | 
            +
                            postFDCcol = ee.ImageCollection("NOAA/GOES/16/FDCF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 222 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                            postjoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 225 | 
            +
                            primary = postCMIcol,
         | 
| 226 | 
            +
                            secondary = postFDCcol,
         | 
| 227 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 228 | 
            +
                                difference = 10, #milliseconds
         | 
| 229 | 
            +
                                leftField = 'system:time_start',
         | 
| 230 | 
            +
                                rightField = 'system:time_start',))
         | 
| 231 | 
            +
                            postMiddayGOEScol = ee.ImageCollection(postjoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 232 | 
            +
                            postMiddayGOEScol = postMiddayGOEScol.map(GcalcCCsingle)
         | 
| 233 | 
            +
                            post_meanNBR = postMiddayGOEScol.select(['NBR']).mean()
         | 
| 234 | 
            +
                            post_meanNBR = post_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                            dNBR_goes16 = pre_meanNBR.subtract(post_meanNBR).select('NBR')
         | 
| 237 | 
            +
                            
         | 
| 238 | 
            +
                            dNBRclip_goes17= dNBR_goes17.clip(bbox)
         | 
| 239 | 
            +
                            dNBRclip_goes16= dNBR_goes16.clip(bbox)
         | 
| 240 | 
            +
                            dNBRdisp_goes17 = dNBRclip_goes17.displace(displacement18, 'bicubic')
         | 
| 241 | 
            +
                            dNBRdisp_goes16 = dNBRclip_goes16.displace(displacement16, 'bicubic')
         | 
| 242 | 
            +
                            dNBRgoes_compos = ee.ImageCollection([dNBRdisp_goes17,dNBRdisp_goes16]).mean()
         | 
| 243 | 
            +
                            
         | 
| 244 | 
            +
                            #ACTIVE fire
         | 
| 245 | 
            +
                            activeFire18 = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 246 | 
            +
                            activeFire16 = ee.ImageCollection(f"NOAA/GOES/16/FDCF").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 247 | 
            +
                            sumFRP18 = activeFire18.select('Power').sum().rename('sumFRP')
         | 
| 248 | 
            +
                            sumFRP16 = activeFire16.select('Power').sum().rename('sumFRP')
         | 
| 249 | 
            +
                            maskNoFire18 = sumFRP18.gt(200).displace(displacement18, 'bicubic')
         | 
| 250 | 
            +
                            maskNoFire16 = sumFRP16.gt(200).displace(displacement16, 'bicubic')
         | 
| 251 | 
            +
                            maskNoFire =  ee.ImageCollection([maskNoFire18,maskNoFire16]).sum().gt(0)
         | 
| 252 | 
            +
                            
         | 
| 253 | 
            +
                            '''
         | 
| 254 | 
            +
                            activeSNPP = ee.ImageCollection("NASA/LANCE/SNPP_VIIRS/C2").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 255 | 
            +
                            activeNOAA20 = ee.ImageCollection("NASA/LANCE/NOAA20_VIIRS/C2").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 256 | 
            +
                            sumFRP_SNPP = activeSNPP.select('confidence').max().rename('sumFRP')
         | 
| 257 | 
            +
                            sumFRP_NOAA20 = activeNOAA20.select('confidence').max().rename('sumFRP')
         | 
| 258 | 
            +
                            #maskNoFire =  ee.ImageCollection([sumFRP_SNPP,sumFRP_NOAA20]).sum().gt(0)
         | 
| 259 | 
            +
                            maskNoFire = sumFRP_SNPP.gt(0)
         | 
| 260 | 
            +
                            '''
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                            #VIIRS
         | 
| 263 | 
            +
                            preVIIRSimg = ee.ImageCollection("NASA/VIIRS/002/VNP09GA").filter(ee.Filter.date(pre_start, pre_stop)).mean()
         | 
| 264 | 
            +
                            postVIIRSimgCol = ee.ImageCollection("NASA/VIIRS/002/VNP09GA").filter(ee.Filter.date(post_start, post_stop)) #TO FIX ON JUNE 18 sfork_startDate.advance(24, 'day'), sfork_startDate.advance(25,'day')
         | 
| 265 | 
            +
                            
         | 
| 266 | 
            +
                            #Landsat
         | 
| 267 | 
            +
                            prelandsat8col = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 268 | 
            +
                            postlandsat8col = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2").filterDate(post_start, post_stop).filterBounds(bbox)
         | 
| 269 | 
            +
                            prelandsat9col = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 270 | 
            +
                            postlandsat9col = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2").filterDate(post_start, post_stop).filterBounds(bbox)
         | 
| 271 | 
            +
                            prelandsatcol = prelandsat8col.merge(prelandsat9col)
         | 
| 272 | 
            +
                            postlandsatcol = postlandsat8col.merge(postlandsat9col)
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                            #Sentinel
         | 
| 275 | 
            +
                            presentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 276 | 
            +
                            postsentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(post_start, post_stop).filterBounds(bbox) #TO FIX on JULY 5: sfork_startDate.advance(32, 'day'), sfork_startDate.advance(33,'day')
         | 
| 277 | 
            +
                            #olderPostSentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(sfork_startDate.advance(37, 'day'), sfork_startDate.advance(38,'day')).filterBounds(bbox)
         | 
| 278 | 
            +
                            
         | 
| 279 | 
            +
                            #SAR
         | 
| 280 | 
            +
                            #SARimg = ee.Image('projects/ovcrge-ssec-burn-scar-map-c116/assets/burned_20200907_20200919_test')
         | 
| 281 | 
            +
                            #SARmask = SARimg.eq(1)
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                            if postVIIRSimgCol.size().getInfo() > 0:
         | 
| 284 | 
            +
                                postVIIRSimg = postVIIRSimgCol.mean()
         | 
| 285 | 
            +
                                preVIIRSimg = VcalcNBR(preVIIRSimg)
         | 
| 286 | 
            +
                                postVIIRSimg = VcalcNBR(postVIIRSimg)
         | 
| 287 | 
            +
                                dNBR_viirs = preVIIRSimg.subtract(postVIIRSimg).select('NBR')
         | 
| 288 | 
            +
                                dNBRclip_viirs = dNBR_viirs.clip(bbox)
         | 
| 289 | 
            +
                            else: 
         | 
| 290 | 
            +
                                dNBR_composite = dNBRgoes_compos
         | 
| 291 | 
            +
                            if postsentCol.size().getInfo() > 0:
         | 
| 292 | 
            +
                                    presentMean = presentCol.mean()
         | 
| 293 | 
            +
                                    postsentMean = postsentCol.mean() 
         | 
| 294 | 
            +
                                    presentImg = ScalcNBR(presentMean)
         | 
| 295 | 
            +
                                    postsentImg = ScalcNBR(postsentMean)
         | 
| 296 | 
            +
                                    dnbr_sent =  presentImg.subtract(postsentImg).multiply(1.3).add(0.05).select('NBR')
         | 
| 297 | 
            +
                                    dNBRclip_sent = dnbr_sent.clip(bbox)
         | 
| 298 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos,dNBRclip_sent]).mosaic() #dNBRclip_viirs SHOULD GO IN IF UP TO DATE
         | 
| 299 | 
            +
                            elif postlandsatcol.size().getInfo() > 0:
         | 
| 300 | 
            +
                                    prelandsat = prelandsatcol.mean()
         | 
| 301 | 
            +
                                    prelandsatImg = LcalcNBR(prelandsat)
         | 
| 302 | 
            +
                                    postlandsat = postlandsatcol.mean()
         | 
| 303 | 
            +
                                    postlandsatImg = LcalcNBR(postlandsat)
         | 
| 304 | 
            +
                                    dNBR_landsat = prelandsatImg.subtract(postlandsatImg).multiply(3.23).add(0.01).select('NBR')
         | 
| 305 | 
            +
                                    dNBRclip_ls = dNBR_landsat.clip(bbox)
         | 
| 306 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos,dNBRclip_ls]).mosaic() #dNBRclip_viirs  SHOULD GO IN IF UP TO DATE
         | 
| 307 | 
            +
                            else:
         | 
| 308 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos]).mosaic() #dNBRclip_viirs  SHOULD GO IN IF UP TO DATE
         | 
| 309 | 
            +
                            
         | 
| 310 | 
            +
                            masked_compos = dNBR_composite.updateMask(maskNoFire) #(SARmask)
         | 
| 311 | 
            +
                            #doubleMasked_compos = masked_compos.updateMask(maskNoFire)
         | 
| 312 | 
            +
                            doubleMasked_compos = masked_compos.mask(masked_compos.mask()).float()
         | 
| 313 | 
            +
                            downloadArgs = {'name': 'VIIRS_burnMap',
         | 
| 314 | 
            +
                                            'crs': 'EPSG:4326',
         | 
| 315 | 
            +
                                            'scale': 60,
         | 
| 316 | 
            +
                                            'region': bbox}
         | 
| 317 | 
            +
                            url = doubleMasked_compos.getDownloadURL(downloadArgs)
         | 
| 318 | 
            +
                            
         | 
| 319 | 
            +
                            print(url)
         | 
| 320 | 
            +
                            noDataVal = -9999
         | 
| 321 | 
            +
                            unmaskedImage = doubleMasked_compos.unmask(noDataVal, False)
         | 
| 322 | 
            +
                            
         | 
| 323 | 
            +
                            task = ee.batch.Export.image.toDrive(**{
         | 
| 324 | 
            +
                                'image': unmaskedImage,
         | 
| 325 | 
            +
                                'description': "Composite_burnMap6",
         | 
| 326 | 
            +
                                'folder': "Earth Engine Outputs",
         | 
| 327 | 
            +
                                'fileNamePrefix': "Composite_burnMap_noData_VIIRS_June18_espg3857_60m",
         | 
| 328 | 
            +
                                'region': bbox,
         | 
| 329 | 
            +
                                'crs': 'EPSG:3857',
         | 
| 330 | 
            +
                                'scale': 60,})
         | 
| 331 | 
            +
                            task.start()
         | 
| 332 | 
            +
                            return masked_compos
         | 
| 333 | 
            +
                    
         | 
| 334 | 
            +
                        
         | 
| 335 | 
            +
                    self.clear_specific_layers()
         | 
| 336 | 
            +
                    
         | 
| 337 | 
            +
                    fireImg = calc_nbr(startDate.advance(-7, 'day'), startDate, endDate.advance(-3, 'day'), endDate, boundingBox, 18) 
         | 
| 338 | 
            +
                    self.addLayer(fireImg, dNBRvisParams, fireID, True)
         | 
| 339 | 
            +
                    self.centerObject(boundingBox, 10)
         | 
| 340 | 
            +
                    file = fireImg
         | 
| 341 | 
            +
             | 
| 342 | 
            +
                
         | 
| 343 | 
            +
                def clear_specific_layers(self):
         | 
| 344 | 
            +
                    layers_to_keep = ['OpenStreetMap']
         | 
| 345 | 
            +
                    layers = list(self.layers)
         | 
| 346 | 
            +
                    for layer in layers:
         | 
| 347 | 
            +
                        if layer.name not in layers_to_keep:
         | 
| 348 | 
            +
                            self.remove_layer(layer)  
         | 
| 349 | 
            +
             | 
| 350 | 
            +
                           
         | 
| 351 | 
            +
                def add_selector(self):
         | 
| 352 | 
            +
                    selector = widgets.Dropdown(options=fireList, value=fireList[6], description='Current wildfire :', style={'description_width': '125px'}, layout=widgets.Layout(width='400px')) 
         | 
| 353 | 
            +
             | 
| 354 | 
            +
                    def on_selector_change(change):
         | 
| 355 | 
            +
                        if change['name'] == 'value': 
         | 
| 356 | 
            +
                            selected_fire.value = change['new']
         | 
| 357 | 
            +
                            self.customize_ee_data(selected_fire.value, today)
         | 
| 358 | 
            +
             | 
| 359 | 
            +
                    selector.observe(on_selector_change, names='value')
         | 
| 360 | 
            +
                    self.add_widget(selector, position="topleft")
         | 
| 361 | 
            +
             | 
| 362 | 
            +
             | 
| 363 | 
            +
                def add_dwnldButton(self):
         | 
| 364 | 
            +
                    button = widgets.Button(description='Export to Drive',icon='cloud-arrow-down')
         | 
| 365 | 
            +
             | 
| 366 | 
            +
                    #def on_button_click(change, file):
         | 
| 367 | 
            +
                    #    if change['name'] == 'value': 
         | 
| 368 | 
            +
                    #        selected_days.value = change['new']
         | 
| 369 | 
            +
                    #        self.download_ee_image(file, "trial_file.tif", scale=30)
         | 
| 370 | 
            +
                    def on_button_click(b):
         | 
| 371 | 
            +
                    # Get the currently selected fire and elapsed days
         | 
| 372 | 
            +
                        fire = selected_fire.value
         | 
| 373 | 
            +
                        elapDays = today
         | 
| 374 | 
            +
                        
         | 
| 375 | 
            +
                        # Customize the EE data and download the image
         | 
| 376 | 
            +
                        file = self.customize_ee_data(fire, elapDays)
         | 
| 377 | 
            +
                        #self.download_ee_image(file, f"{fire}_NBR_{elapDays}days.tif", scale=30)
         | 
| 378 | 
            +
                    
         | 
| 379 | 
            +
                    button.observe(on_button_click) 
         | 
| 380 | 
            +
                    self.add_widget(button, position="topleft")
         | 
| 381 | 
            +
             | 
| 382 | 
            +
                    
         | 
| 383 | 
            +
             | 
| 384 | 
            +
            @solara.component
         | 
| 385 | 
            +
            def Page():
         | 
| 386 | 
            +
                
         | 
| 387 | 
            +
                with solara.Column(align="center"):
         | 
| 388 | 
            +
                    markdown = """
         | 
| 389 | 
            +
                    ## Current 2024 wildfires over 10,000 acres"""
         | 
| 390 | 
            +
                    solara.Markdown(markdown)
         | 
| 391 | 
            +
                
         | 
| 392 | 
            +
                # Isolation is required to prevent the map from overlapping navigation (when screen width < 960px)
         | 
| 393 | 
            +
                with solara.Column(style={"isolation": "isolate"}):
         | 
| 394 | 
            +
                    map_widget = Map.element(
         | 
| 395 | 
            +
                        center=[39, -120.5],
         | 
| 396 | 
            +
                        zoom=8,
         | 
| 397 | 
            +
                        height="600px",
         | 
| 398 | 
            +
                        toolbar_ctrl=False
         | 
| 399 | 
            +
                    )
         | 
    	
        historical_fires.py
    ADDED
    
    | @@ -0,0 +1,429 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import ee
         | 
| 2 | 
            +
            import geemap
         | 
| 3 | 
            +
            import solara
         | 
| 4 | 
            +
            import ipywidgets as widgets
         | 
| 5 | 
            +
            #from NBR_calculations import GcalcNBR, VcalcNBR, LcalcNBR, ScalcNBR, GcalcCCsingle
         | 
| 6 | 
            +
            import requests
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            # Bit-masking
         | 
| 9 | 
            +
            BitMask_0 = 1 << 0
         | 
| 10 | 
            +
            BitMask_1 = 1 << 1
         | 
| 11 | 
            +
            BitMask_2 = 1 << 2
         | 
| 12 | 
            +
            BitMask_3 = 1 << 3
         | 
| 13 | 
            +
            BitMask_4 = 1 << 4
         | 
| 14 | 
            +
            BitMask_5 = 1 << 5
         | 
| 15 | 
            +
            BitMask_6 = 1 << 6
         | 
| 16 | 
            +
            BitMask_7 = 1 << 7
         | 
| 17 | 
            +
            BitMask_8 = 1 << 8
         | 
| 18 | 
            +
            BitMask_9 = 1 << 9
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            def GcalcCCsingle (goesImg):
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              fireDQF = goesImg.select('DQF').int()
         | 
| 23 | 
            +
              CMI_QF3 = goesImg.select('DQF_C03').int()
         | 
| 24 | 
            +
              CMI_QF6 = goesImg.select('DQF_C06').int()
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              #Right now, cloud mask is excluding clouds and water; active fire, bad data and fire free are unmasked. NBR mask exlcudes fire
         | 
| 27 | 
            +
              F_Mask = fireDQF.eq(0)
         | 
| 28 | 
            +
              C_Mask = (fireDQF.lt(2).Or(fireDQF.gt(2))).rename('C_Mask')
         | 
| 29 | 
            +
                   #.And(CMI_QF3.lt(2)).And(CMI_QF6.lt(2)).rename('C_Mask')
         | 
| 30 | 
            +
              QF_Mask = (fireDQF.eq(1).Or(fireDQF.gt(3)))\
         | 
| 31 | 
            +
                    .And(CMI_QF3.lt(2)).And(CMI_QF6.lt(2)).rename('QFmask')
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              GOESmasked = goesImg.select(['CMI_C03','CMI_C06']).updateMask(QF_Mask)
         | 
| 34 | 
            +
              NBRmasked = GOESmasked.normalizedDifference(['CMI_C03', 'CMI_C06']).toFloat().rename('NBR')
         | 
| 35 | 
            +
              cloudMasked = goesImg.select('CMI_C03').updateMask(C_Mask).toFloat().rename('CC')
         | 
| 36 | 
            +
              fireMasked =  goesImg.select('CMI_C03').updateMask(F_Mask).toFloat().rename('FC')
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              return goesImg.addBands([NBRmasked,cloudMasked, fireMasked,QF_Mask,C_Mask])
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            '''Parameter Array Name Value Bit(s) = Value
         | 
| 41 | 
            +
            Sun Glint           QF1 Surface Reflectance None 6-7 = 00
         | 
| 42 | 
            +
            Low Sun Mask        QF1 Surface Reflectance High 5 = 0
         | 
| 43 | 
            +
            Day/Night           QF1 Surface Reflectance Day 4 =0
         | 
| 44 | 
            +
            Cloud Detection     QF1 Surface Reflectance Confident Clear 2-3 = 00 or Problably Clear 2-3 = 01
         | 
| 45 | 
            +
            Cloud Mask Quality  QF1 Surface Reflectance High or Medium 0-1 = 10 or 11
         | 
| 46 | 
            +
            Snow/Ice            QF2 Surface Reflectance No Snow or Ice 5 = 0
         | 
| 47 | 
            +
            Cloud Shadow        QF2 Surface Reflectance No Cloud Shadow 3 = 0
         | 
| 48 | 
            +
            LandWater           QF2 Surface Reflectance Land, Snow, Arctic, Antarctic or Greenland, Desert 0-2  = 011, 100, 101, 110, 111
         | 
| 49 | 
            +
            Thin Cirrus Flag    QF7 Surface Reflectance No Thin Cirrus 4 = 0
         | 
| 50 | 
            +
            Aerosol Quantity    QF7 Surface Reflectance Climatology, Low or Medium 2-3 = 00, 01 or 10
         | 
| 51 | 
            +
            Adjacent to Cloud   QF7 Surface Reflectance Not Adjacent to Cloud 1 = 0'''
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            def VcalcNBR (VIIRSimg):
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                QF1 = VIIRSimg.select('QF1').int()
         | 
| 56 | 
            +
                QF2 = VIIRSimg.select('QF2').int()
         | 
| 57 | 
            +
                QF7 = VIIRSimg.select('QF7').int()
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                QF_Mask = (QF1.bitwiseAnd(BitMask_3).eq(0)).And\
         | 
| 60 | 
            +
                ((QF2.bitwiseAnd(BitMask_2).eq(4)).Or((QF2.bitwiseAnd(BitMask_1).eq(0)))).And\
         | 
| 61 | 
            +
                (QF2.bitwiseAnd(BitMask_5).eq(0)).rename('QFmask');
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                VIIRSm = VIIRSimg.select(['I2','M11']).updateMask(QF_Mask);
         | 
| 64 | 
            +
                NBR = VIIRSm.normalizedDifference(['I2','M11']).toFloat().rename('NBR')
         | 
| 65 | 
            +
                return VIIRSimg.addBands(NBR).addBands(QF_Mask)#.set('avgNBR', avgNBR)
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            ''' Bit 1: Dilated Cloud
         | 
| 68 | 
            +
                Bit 2: Cirrus (high confidence)
         | 
| 69 | 
            +
                Bit 3: Cloud
         | 
| 70 | 
            +
                Bit 4: Cloud Shadow
         | 
| 71 | 
            +
                Bit 5: Snow
         | 
| 72 | 
            +
                Bit 6: Clear (0: Cloud or Dilated Cloud bits are set, 1: Cloud and Dilated Cloud bits are not set)
         | 
| 73 | 
            +
                Bit 7: Water
         | 
| 74 | 
            +
                Bits 8-9: Cloud Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 75 | 
            +
                Bits 10-11: Cloud Shadow Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 76 | 
            +
                Bits 12-13: Snow/Ice Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 77 | 
            +
                Bits 14-15: Cirrus Confidence (0: None, 1: Low, 2: Medium, 3: High)'''
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            def LcalcNBR (LSimg):
         | 
| 80 | 
            +
              QApixel = LSimg.select('QA_PIXEL').int()
         | 
| 81 | 
            +
              QF_Mask =(QApixel.bitwiseAnd(BitMask_3).eq(0)).And\
         | 
| 82 | 
            +
                       (QApixel.bitwiseAnd(BitMask_5).eq(0)).And\
         | 
| 83 | 
            +
                       (QApixel.bitwiseAnd(BitMask_7).eq(0)).rename('QFmask');
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              LSmasked = LSimg.select(['SR_B5','SR_B7']).updateMask(QF_Mask);
         | 
| 86 | 
            +
              NBR = LSmasked.normalizedDifference(['SR_B5','SR_B7']).toFloat().rename('NBR')
         | 
| 87 | 
            +
              return LSimg.addBands(NBR).addBands(QF_Mask)#.set('avgNBR', avgNBR)
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            ''' 1    Saturated or defective
         | 
| 90 | 
            +
                2    Dark Area Pixels
         | 
| 91 | 
            +
                3    Cloud Shadows
         | 
| 92 | 
            +
                4    Vegetation
         | 
| 93 | 
            +
                5    Bare Soils
         | 
| 94 | 
            +
                6    Water
         | 
| 95 | 
            +
                7    Clouds Low Probability / Unclassified
         | 
| 96 | 
            +
                8    Clouds Medium Probability
         | 
| 97 | 
            +
                9    Clouds High Probability
         | 
| 98 | 
            +
                10   Cirrus
         | 
| 99 | 
            +
                11   Snow / Ice'''
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            def ScalcNBR (sentImg):
         | 
| 102 | 
            +
              SCL = sentImg.select('SCL');
         | 
| 103 | 
            +
              QF_Mask =(SCL.neq(6)).And\
         | 
| 104 | 
            +
                (SCL.neq(8)).And\
         | 
| 105 | 
            +
                (SCL.neq(9)).And\
         | 
| 106 | 
            +
                (SCL.neq(11))\
         | 
| 107 | 
            +
                .rename('QFmask');
         | 
| 108 | 
            +
              sentMasked = sentImg.select(['B8A','B12']).updateMask(QF_Mask); #B8 is another option- broadband NIR
         | 
| 109 | 
            +
              NBR = sentMasked.normalizedDifference(['B8A','B12']).toFloat().rename('NBR')
         | 
| 110 | 
            +
              return sentImg.addBands(NBR).addBands(QF_Mask).addBands(SCL)#.set('avgNBR', avgNBR)
         | 
| 111 | 
            +
             | 
| 112 | 
            +
             | 
| 113 | 
            +
            fireList = ["North Complex", "Dixie", "Cameron Peak", "August Complex", "South Fork"]
         | 
| 114 | 
            +
            selected_fire = solara.reactive(fireList[4])
         | 
| 115 | 
            +
            selected_days = solara.reactive(25) #30
         | 
| 116 | 
            +
            dNBRvisParams = {'min': 0.0,'max': 0.8, 'palette': ['green', 'yellow','orange','red']}
         | 
| 117 | 
            +
             | 
| 118 | 
            +
             | 
| 119 | 
            +
            class Map(geemap.Map):
         | 
| 120 | 
            +
                def __init__(self, **kwargs):
         | 
| 121 | 
            +
                    super().__init__(**kwargs)        
         | 
| 122 | 
            +
                    self.add_basemap('OpenStreetMap')
         | 
| 123 | 
            +
                    self.customize_ee_data(selected_fire.value, selected_days.value)
         | 
| 124 | 
            +
                    self.add_selector()
         | 
| 125 | 
            +
                    self.add_intSlider()
         | 
| 126 | 
            +
                    self.add_dwnldButton()
         | 
| 127 | 
            +
                    self.add("layer_manager")
         | 
| 128 | 
            +
                    self.remove("draw_control")
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    
         | 
| 131 | 
            +
                def customize_ee_data(self, fire, elapDays):
         | 
| 132 | 
            +
                    elapDayNum = ee.Number(elapDays)
         | 
| 133 | 
            +
                    elapDay_plusOne = elapDayNum.add(ee.Number(1))
         | 
| 134 | 
            +
                    
         | 
| 135 | 
            +
                    north_startDate = ee.Date('2020-08-16')
         | 
| 136 | 
            +
                    dixie_startDate = ee.Date('2021-07-13')
         | 
| 137 | 
            +
                    cam_startDate = ee.Date('2020-08-13')
         | 
| 138 | 
            +
                    aug_startDate = ee.Date('2020-08-15')
         | 
| 139 | 
            +
                    sfork_startDate = ee.Date('2024-05-25')
         | 
| 140 | 
            +
                    
         | 
| 141 | 
            +
                    north_complex_bb = ee.Geometry.BBox(-121.616097, 39.426723, -120.668526, 40.030845)
         | 
| 142 | 
            +
                    dixie_bb = ee.Geometry.BBox(-121.680467, 39.759303, -120.065477, 40.873387)
         | 
| 143 | 
            +
                    cam_peak_bb = ee.Geometry.BBox(-106.014784, 40.377907, -105.116651, 40.822094)
         | 
| 144 | 
            +
                    aug_complex_bb = ee.Geometry.BBox(-123.668726, 39.337654, -122.355860, 40.498304)
         | 
| 145 | 
            +
                    sfork_bb = ee.Geometry.BBox(-106.192, 33.1,  -105.065, 33.782)
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    def calc_nbr(pre_start, pre_stop, post_start, post_stop, bbox, goes):
         | 
| 148 | 
            +
                            def MergeBands (eachImage):
         | 
| 149 | 
            +
                                oneImage = ee.Image.cat(eachImage.get('CMI'), eachImage.get('FDC'))
         | 
| 150 | 
            +
                                return oneImage
         | 
| 151 | 
            +
                            displacementImg18 = ee.Image.load('projects/ee-losos/assets/G18-F-meter-offset_GEE')
         | 
| 152 | 
            +
                            y_dif = displacementImg18.select([1])
         | 
| 153 | 
            +
                            x_dif = displacementImg18.select([0]).multiply(-1)
         | 
| 154 | 
            +
                            displacement18 = ee.Image([x_dif, y_dif])
         | 
| 155 | 
            +
                            
         | 
| 156 | 
            +
                            displacementImg16 = ee.Image.load('projects/ee-losos/assets/G16-F-meter-offset_GEE')
         | 
| 157 | 
            +
                            y_dif = displacementImg16.select([1])
         | 
| 158 | 
            +
                            x_dif = displacementImg16.select([0]).multiply(-1)
         | 
| 159 | 
            +
                            displacement16 = ee.Image([x_dif, y_dif]);
         | 
| 160 | 
            +
                            
         | 
| 161 | 
            +
                            preCMIcol = ee.ImageCollection(f"NOAA/GOES/{goes}/MCMIPF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 162 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 163 | 
            +
                            preFDCcol = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 164 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 165 | 
            +
                            postCMIcol = ee.ImageCollection(f"NOAA/GOES/{goes}/MCMIPF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 166 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 167 | 
            +
                            postFDCcol = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 168 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                            prejoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 171 | 
            +
                            primary = preCMIcol,
         | 
| 172 | 
            +
                            secondary = preFDCcol,
         | 
| 173 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 174 | 
            +
                                difference = 10, #milliseconds
         | 
| 175 | 
            +
                                leftField = 'system:time_start',
         | 
| 176 | 
            +
                                rightField = 'system:time_start',))
         | 
| 177 | 
            +
                            preMiddayGOEScol = ee.ImageCollection(prejoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 178 | 
            +
                            preMiddayGOEScol = preMiddayGOEScol.map(GcalcCCsingle)
         | 
| 179 | 
            +
                            pre_meanNBR = preMiddayGOEScol.select(['NBR']).mean()
         | 
| 180 | 
            +
                            pre_meanNBR = pre_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                            postjoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 183 | 
            +
                            primary = postCMIcol,
         | 
| 184 | 
            +
                            secondary = postFDCcol,
         | 
| 185 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 186 | 
            +
                                difference = 10, #milliseconds
         | 
| 187 | 
            +
                                leftField = 'system:time_start',
         | 
| 188 | 
            +
                                rightField = 'system:time_start',))
         | 
| 189 | 
            +
                            postMiddayGOEScol = ee.ImageCollection(postjoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 190 | 
            +
                            postMiddayGOEScol = postMiddayGOEScol.map(GcalcCCsingle)
         | 
| 191 | 
            +
                            post_meanNBR = postMiddayGOEScol.select(['NBR']).mean()
         | 
| 192 | 
            +
                            post_meanNBR = post_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                            dNBR_goes17 = pre_meanNBR.subtract(post_meanNBR).select('NBR')
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                            
         | 
| 197 | 
            +
                            #GOES-16
         | 
| 198 | 
            +
                            preCMIcol = ee.ImageCollection("NOAA/GOES/16/MCMIPF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 199 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 200 | 
            +
                            preFDCcol = ee.ImageCollection("NOAA/GOES/16/FDCF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 201 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                            prejoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 204 | 
            +
                            primary = preCMIcol,
         | 
| 205 | 
            +
                            secondary = preFDCcol,
         | 
| 206 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 207 | 
            +
                                difference = 10, #milliseconds
         | 
| 208 | 
            +
                                leftField = 'system:time_start',
         | 
| 209 | 
            +
                                rightField = 'system:time_start',))
         | 
| 210 | 
            +
                            preMiddayGOEScol = ee.ImageCollection(prejoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 211 | 
            +
                            preMiddayGOEScol = preMiddayGOEScol.map(GcalcCCsingle)
         | 
| 212 | 
            +
                            pre_meanNBR = preMiddayGOEScol.select(['NBR']).mean()
         | 
| 213 | 
            +
                            pre_meanNBR = pre_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                            
         | 
| 216 | 
            +
                            postCMIcol = ee.ImageCollection("NOAA/GOES/16/MCMIPF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 217 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 218 | 
            +
                            postFDCcol = ee.ImageCollection("NOAA/GOES/16/FDCF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 219 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                            postjoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 222 | 
            +
                            primary = postCMIcol,
         | 
| 223 | 
            +
                            secondary = postFDCcol,
         | 
| 224 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 225 | 
            +
                                difference = 10, #milliseconds
         | 
| 226 | 
            +
                                leftField = 'system:time_start',
         | 
| 227 | 
            +
                                rightField = 'system:time_start',))
         | 
| 228 | 
            +
                            postMiddayGOEScol = ee.ImageCollection(postjoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 229 | 
            +
                            postMiddayGOEScol = postMiddayGOEScol.map(GcalcCCsingle)
         | 
| 230 | 
            +
                            post_meanNBR = postMiddayGOEScol.select(['NBR']).mean()
         | 
| 231 | 
            +
                            post_meanNBR = post_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                            dNBR_goes16 = pre_meanNBR.subtract(post_meanNBR).select('NBR')
         | 
| 234 | 
            +
                            
         | 
| 235 | 
            +
                            dNBRclip_goes17= dNBR_goes17.clip(bbox)
         | 
| 236 | 
            +
                            dNBRclip_goes16= dNBR_goes16.clip(bbox)
         | 
| 237 | 
            +
                            dNBRdisp_goes17 = dNBRclip_goes17.displace(displacement18, 'bicubic')
         | 
| 238 | 
            +
                            dNBRdisp_goes16 = dNBRclip_goes16.displace(displacement16, 'bicubic')
         | 
| 239 | 
            +
                            dNBRgoes_compos = ee.ImageCollection([dNBRdisp_goes17,dNBRdisp_goes16]).mean()
         | 
| 240 | 
            +
                            
         | 
| 241 | 
            +
                            #ACTIVE fire
         | 
| 242 | 
            +
                            activeFire18 = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 243 | 
            +
                            activeFire16 = ee.ImageCollection(f"NOAA/GOES/16/FDCF").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 244 | 
            +
                            sumFRP18 = activeFire18.select('Power').sum().rename('sumFRP')
         | 
| 245 | 
            +
                            sumFRP16 = activeFire16.select('Power').sum().rename('sumFRP')
         | 
| 246 | 
            +
                            maskNoFire18 = sumFRP18.gt(200).displace(displacement18, 'bicubic')
         | 
| 247 | 
            +
                            maskNoFire16 = sumFRP16.gt(200).displace(displacement16, 'bicubic')
         | 
| 248 | 
            +
                            maskNoFire =  ee.ImageCollection([maskNoFire18,maskNoFire16]).sum().gt(0)
         | 
| 249 | 
            +
                            
         | 
| 250 | 
            +
                            '''
         | 
| 251 | 
            +
                            activeSNPP = ee.ImageCollection("NASA/LANCE/SNPP_VIIRS/C2").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 252 | 
            +
                            activeNOAA20 = ee.ImageCollection("NASA/LANCE/NOAA20_VIIRS/C2").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 253 | 
            +
                            sumFRP_SNPP = activeSNPP.select('confidence').max().rename('sumFRP')
         | 
| 254 | 
            +
                            sumFRP_NOAA20 = activeNOAA20.select('confidence').max().rename('sumFRP')
         | 
| 255 | 
            +
                            #maskNoFire =  ee.ImageCollection([sumFRP_SNPP,sumFRP_NOAA20]).sum().gt(0)
         | 
| 256 | 
            +
                            maskNoFire = sumFRP_SNPP.gt(0)
         | 
| 257 | 
            +
                            '''
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                            #VIIRS
         | 
| 260 | 
            +
                            preVIIRSimg = ee.ImageCollection("NASA/VIIRS/002/VNP09GA").filter(ee.Filter.date(pre_start, pre_stop)).mean()
         | 
| 261 | 
            +
                            #postVIIRSimgCol = ee.ImageCollection("NASA/VIIRS/002/VNP09GA").filter(ee.Filter.date(post_start, post_stop))
         | 
| 262 | 
            +
                            postVIIRSimgCol = ee.ImageCollection("NASA/VIIRS/002/VNP09GA").filter(ee.Filter.date(post_start, post_stop)) #TO FIX ON JUNE 18 sfork_startDate.advance(24, 'day'), sfork_startDate.advance(25,'day')
         | 
| 263 | 
            +
                            
         | 
| 264 | 
            +
                            #Landsat
         | 
| 265 | 
            +
                            prelandsat8col = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 266 | 
            +
                            postlandsat8col = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2").filterDate(post_start, post_stop).filterBounds(bbox)
         | 
| 267 | 
            +
                            prelandsat9col = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 268 | 
            +
                            postlandsat9col = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2").filterDate(post_start, post_stop).filterBounds(bbox)
         | 
| 269 | 
            +
                            prelandsatcol = prelandsat8col.merge(prelandsat9col)
         | 
| 270 | 
            +
                            postlandsatcol = postlandsat8col.merge(postlandsat9col)
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                            #Sentinel
         | 
| 273 | 
            +
                            presentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 274 | 
            +
                            postsentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(post_start, post_stop).filterBounds(bbox) #TO FIX on JULY 5: sfork_startDate.advance(32, 'day'), sfork_startDate.advance(33,'day')
         | 
| 275 | 
            +
                            olderPostSentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(sfork_startDate.advance(37, 'day'), sfork_startDate.advance(38,'day')).filterBounds(bbox)
         | 
| 276 | 
            +
                            #SAR
         | 
| 277 | 
            +
                            #SARimg = ee.Image('projects/ovcrge-ssec-burn-scar-map-c116/assets/burned_20200907_20200919_test')
         | 
| 278 | 
            +
                            #SARmask = SARimg.eq(1)
         | 
| 279 | 
            +
                            if postVIIRSimgCol.size().getInfo() > 0:
         | 
| 280 | 
            +
                                postVIIRSimg = postVIIRSimgCol.mean()
         | 
| 281 | 
            +
                                preVIIRSimg = VcalcNBR(preVIIRSimg)
         | 
| 282 | 
            +
                                postVIIRSimg = VcalcNBR(postVIIRSimg)
         | 
| 283 | 
            +
                                dNBR_viirs = preVIIRSimg.subtract(postVIIRSimg).select('NBR')
         | 
| 284 | 
            +
                                dNBRclip_viirs = dNBR_viirs.clip(bbox)
         | 
| 285 | 
            +
                            else: 
         | 
| 286 | 
            +
                                dNBR_composite = dNBRgoes_compos
         | 
| 287 | 
            +
                            if postsentCol.size().getInfo() > 0:
         | 
| 288 | 
            +
                                    presentMean = presentCol.mean()
         | 
| 289 | 
            +
                                    postsentMean = postsentCol.mean() 
         | 
| 290 | 
            +
                                    postsent2Mean = olderPostSentCol.mean()
         | 
| 291 | 
            +
                                    presentImg = ScalcNBR(presentMean)
         | 
| 292 | 
            +
                                    postsentImg = ScalcNBR(postsentMean)
         | 
| 293 | 
            +
                                    postsentImg2 = ScalcNBR(postsent2Mean)
         | 
| 294 | 
            +
                                    postSentCombo = ee.ImageCollection([postsentImg,postsentImg2]).mosaic()
         | 
| 295 | 
            +
                                    dnbr_sent =  presentImg.subtract(postSentCombo).multiply(1.3).add(0.05).select('NBR')
         | 
| 296 | 
            +
                                    dNBRclip_sent = dnbr_sent.clip(bbox)
         | 
| 297 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos,dNBRclip_viirs,dNBRclip_sent]).mosaic()
         | 
| 298 | 
            +
                            elif postlandsatcol.size().getInfo() > 0:
         | 
| 299 | 
            +
                                    print(postlandsatcol.size().getInfo())
         | 
| 300 | 
            +
                                    prelandsat = prelandsatcol.mean()
         | 
| 301 | 
            +
                                    prelandsatImg = LcalcNBR(prelandsat)
         | 
| 302 | 
            +
                                    postlandsat = postlandsatcol.mean()
         | 
| 303 | 
            +
                                    postlandsatImg = LcalcNBR(postlandsat)
         | 
| 304 | 
            +
                                    dNBR_landsat = prelandsatImg.subtract(postlandsatImg).multiply(3.23).add(0.01).select('NBR')
         | 
| 305 | 
            +
                                    dNBRclip_ls = dNBR_landsat.clip(bbox)
         | 
| 306 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos,dNBRclip_viirs,dNBRclip_ls]).mosaic()
         | 
| 307 | 
            +
                            else:
         | 
| 308 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos,dNBRclip_viirs]).mosaic() 
         | 
| 309 | 
            +
                            
         | 
| 310 | 
            +
                            masked_compos = dNBR_composite.updateMask(maskNoFire) #(SARmask)
         | 
| 311 | 
            +
                            #doubleMasked_compos = masked_compos.updateMask(maskNoFire)
         | 
| 312 | 
            +
                            doubleMasked_compos = masked_compos.mask(masked_compos.mask()).float()
         | 
| 313 | 
            +
                            downloadArgs = {'name': 'VIIRS_burnMap',
         | 
| 314 | 
            +
                                            'crs': 'EPSG:4326',
         | 
| 315 | 
            +
                                            'scale': 60,
         | 
| 316 | 
            +
                                            'region': bbox}
         | 
| 317 | 
            +
                            url = doubleMasked_compos.getDownloadURL(downloadArgs)
         | 
| 318 | 
            +
                            
         | 
| 319 | 
            +
                            print(url)
         | 
| 320 | 
            +
                            noDataVal = -9999
         | 
| 321 | 
            +
                            unmaskedImage = doubleMasked_compos.unmask(noDataVal, False)
         | 
| 322 | 
            +
                            
         | 
| 323 | 
            +
                            task = ee.batch.Export.image.toDrive(**{
         | 
| 324 | 
            +
                                'image': unmaskedImage,
         | 
| 325 | 
            +
                                'description': "Composite_burnMap6",
         | 
| 326 | 
            +
                                'folder': "Earth Engine Outputs",
         | 
| 327 | 
            +
                                'fileNamePrefix': "Composite_burnMap_noData_VIIRS_June18_espg3857_60m",
         | 
| 328 | 
            +
                                'region': bbox,
         | 
| 329 | 
            +
                                'crs': 'EPSG:3857',
         | 
| 330 | 
            +
                                'scale': 60,})
         | 
| 331 | 
            +
                            #task.start()
         | 
| 332 | 
            +
                            return masked_compos
         | 
| 333 | 
            +
                    
         | 
| 334 | 
            +
                        
         | 
| 335 | 
            +
                    self.clear_specific_layers()
         | 
| 336 | 
            +
                    
         | 
| 337 | 
            +
                    if fire == "North Complex": 
         | 
| 338 | 
            +
                        north_complex = calc_nbr(north_startDate.advance(-7, 'day'), north_startDate, north_startDate.advance(elapDayNum, 'day'), north_startDate.advance(elapDay_plusOne,'day'), north_complex_bb, 17) 
         | 
| 339 | 
            +
                        self.addLayer(north_complex, dNBRvisParams, 'North Complex GOES NBR', True)
         | 
| 340 | 
            +
                        self.centerObject(north_complex_bb, 9)
         | 
| 341 | 
            +
                        file = north_complex
         | 
| 342 | 
            +
                    elif fire == "Dixie":
         | 
| 343 | 
            +
                        dixie = calc_nbr(dixie_startDate.advance(-7, 'day'), dixie_startDate, dixie_startDate.advance(elapDayNum, 'day'), dixie_startDate.advance(elapDay_plusOne,'day'), dixie_bb, 17)
         | 
| 344 | 
            +
                        self.addLayer(dixie, dNBRvisParams, 'Dixie Complex GOES NBR', True)
         | 
| 345 | 
            +
                        self.centerObject(dixie_bb, 9)
         | 
| 346 | 
            +
                        file = dixie
         | 
| 347 | 
            +
                    elif fire == "Cameron Peak":
         | 
| 348 | 
            +
                        cam_peak = calc_nbr(cam_startDate.advance(-7, 'day'), cam_startDate, cam_startDate.advance(elapDayNum, 'day'), cam_startDate.advance(elapDay_plusOne,'day'),   cam_peak_bb, 17)
         | 
| 349 | 
            +
                        self.addLayer(cam_peak, dNBRvisParams, 'Cameron Peak GOES NBR', True)
         | 
| 350 | 
            +
                        self.centerObject(cam_peak_bb, 9)
         | 
| 351 | 
            +
                        file = cam_peak
         | 
| 352 | 
            +
                    elif fire == "August Complex":
         | 
| 353 | 
            +
                        aug_complex = calc_nbr(aug_startDate.advance(-7, 'day'), aug_startDate, aug_startDate.advance(elapDayNum, 'day'), aug_startDate.advance(elapDay_plusOne,'day'),   aug_complex_bb, 17)
         | 
| 354 | 
            +
                        self.addLayer(aug_complex, dNBRvisParams, 'August Complex GOES NBR', True)
         | 
| 355 | 
            +
                        self.centerObject(aug_complex_bb, 9)
         | 
| 356 | 
            +
                        file = aug_complex
         | 
| 357 | 
            +
                    elif fire == "South Fork":
         | 
| 358 | 
            +
                        sfork = calc_nbr(sfork_startDate.advance(-7, 'day'), sfork_startDate, sfork_startDate.advance(elapDayNum, 'day'), sfork_startDate.advance(elapDay_plusOne,'day'),   sfork_bb, 18)
         | 
| 359 | 
            +
                        self.addLayer(sfork, dNBRvisParams, 'South Fork GOES NBR', True)
         | 
| 360 | 
            +
                        self.centerObject(sfork_bb, 9)
         | 
| 361 | 
            +
                        file = sfork
         | 
| 362 | 
            +
                
         | 
| 363 | 
            +
                def clear_specific_layers(self):
         | 
| 364 | 
            +
                    layers_to_keep = ['OpenStreetMap']
         | 
| 365 | 
            +
                    layers = list(self.layers)
         | 
| 366 | 
            +
                    for layer in layers:
         | 
| 367 | 
            +
                        if layer.name not in layers_to_keep:
         | 
| 368 | 
            +
                            self.remove_layer(layer)  
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                           
         | 
| 371 | 
            +
                def add_selector(self):
         | 
| 372 | 
            +
                    selector = widgets.Dropdown(options=fireList, value="South Fork", description='Wildfire Case Study:', style={'description_width': '125px'}, layout=widgets.Layout(width='400px')) 
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                    def on_selector_change(change):
         | 
| 375 | 
            +
                        if change['name'] == 'value': 
         | 
| 376 | 
            +
                            selected_fire.value = change['new']
         | 
| 377 | 
            +
                            self.customize_ee_data(selected_fire.value, selected_days.value)
         | 
| 378 | 
            +
             | 
| 379 | 
            +
                    selector.observe(on_selector_change, names='value')
         | 
| 380 | 
            +
                    self.add_widget(selector, position="topleft")
         | 
| 381 | 
            +
             | 
| 382 | 
            +
                def add_intSlider(self):
         | 
| 383 | 
            +
                    slider = widgets.IntSlider(value=selected_days.value,min=1,max=40,step=1,description='Elapsed days:',style={'description_width': '125px'}, layout=widgets.Layout(width='400px'))
         | 
| 384 | 
            +
                    
         | 
| 385 | 
            +
                    def on_slider_change(change):
         | 
| 386 | 
            +
                        if change['name'] == 'value': 
         | 
| 387 | 
            +
                            selected_days.value = change['new']
         | 
| 388 | 
            +
                            self.customize_ee_data(selected_fire.value, selected_days.value)
         | 
| 389 | 
            +
                        
         | 
| 390 | 
            +
                    slider.observe(on_slider_change, names='value') 
         | 
| 391 | 
            +
                    self.add_widget(slider, position="topleft")
         | 
| 392 | 
            +
             | 
| 393 | 
            +
                def add_dwnldButton(self):
         | 
| 394 | 
            +
                    button = widgets.Button(description='Download',icon='cloud-arrow-down')
         | 
| 395 | 
            +
             | 
| 396 | 
            +
                    #def on_button_click(change, file):
         | 
| 397 | 
            +
                    #    if change['name'] == 'value': 
         | 
| 398 | 
            +
                    #        selected_days.value = change['new']
         | 
| 399 | 
            +
                    #        self.download_ee_image(file, "trial_file.tif", scale=30)
         | 
| 400 | 
            +
                    def on_button_click(b):
         | 
| 401 | 
            +
                    # Get the currently selected fire and elapsed days
         | 
| 402 | 
            +
                        fire = selected_fire.value
         | 
| 403 | 
            +
                        elapDays = selected_days.value
         | 
| 404 | 
            +
                        
         | 
| 405 | 
            +
                        # Customize the EE data and download the image
         | 
| 406 | 
            +
                        file = self.customize_ee_data(fire, elapDays)
         | 
| 407 | 
            +
                        #self.download_ee_image(file, f"{fire}_NBR_{elapDays}days.tif", scale=30)
         | 
| 408 | 
            +
                    
         | 
| 409 | 
            +
                    button.observe(on_button_click) 
         | 
| 410 | 
            +
                    self.add_widget(button, position="topleft")
         | 
| 411 | 
            +
             | 
| 412 | 
            +
                    
         | 
| 413 | 
            +
             | 
| 414 | 
            +
            @solara.component
         | 
| 415 | 
            +
            def Page():
         | 
| 416 | 
            +
                
         | 
| 417 | 
            +
                with solara.Column(align="center"):
         | 
| 418 | 
            +
                    markdown = """
         | 
| 419 | 
            +
                    ## Historical Western US wildfires from 2020-2021 """
         | 
| 420 | 
            +
                    solara.Markdown(markdown)
         | 
| 421 | 
            +
                
         | 
| 422 | 
            +
                # Isolation is required to prevent the map from overlapping navigation (when screen width < 960px)
         | 
| 423 | 
            +
                with solara.Column(style={"isolation": "isolate"}):
         | 
| 424 | 
            +
                    map_widget = Map.element(
         | 
| 425 | 
            +
                        center=[39, -120.5],
         | 
| 426 | 
            +
                        zoom=8,
         | 
| 427 | 
            +
                        height="600px",
         | 
| 428 | 
            +
                        toolbar_ctrl=False
         | 
| 429 | 
            +
                    )
         | 
    	
        pages/Home.py
    ADDED
    
    | @@ -0,0 +1,44 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import solara
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            @solara.component
         | 
| 4 | 
            +
            def Page():
         | 
| 5 | 
            +
                with solara.Column(align="center"):
         | 
| 6 | 
            +
                    markdown = """
         | 
| 7 | 
            +
                    ## Real-time wildfire burn mapping 
         | 
| 8 | 
            +
                    
         | 
| 9 | 
            +
                    ### About the project
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    **A proof of concept illustrating wildfire burn severity maps with emerging clarity while the fires progress. 
         | 
| 12 | 
            +
                    Target users are forecasters and emergency managers responding to post-fire risks including debris flows and landslides.**
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    More project description, etc, etc. 
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    **Case Studies from 2020 and 2021 Western US wildfire seasons **
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    - August Complex, CA (2020)
         | 
| 19 | 
            +
                    - Cameron Peak, CO (2020)
         | 
| 20 | 
            +
                    - Dixie Fire, CA (2021)
         | 
| 21 | 
            +
                    - North Complex, CA (2020)
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    
         | 
| 24 | 
            +
                     **Current 2024 wildfires over 10,000 acres **
         | 
| 25 | 
            +
                    
         | 
| 26 | 
            +
                    ### How to use the app
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    1. Select the fire from the drop-down menu
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    2. Export image to Google Drive as a geotiff
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    3. 
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    ### Support
         | 
| 35 | 
            +
                    
         | 
| 36 | 
            +
                    Initial funding for wildland burn scar mapping came through the NOAA JPSS/RRPG Fire and Smoke Initiative. 
         | 
| 37 | 
            +
                    This supported the initial tests of BRIDGE maps using dNDVI. Subsequent funding supported the development of dNBR mapping and an effort 
         | 
| 38 | 
            +
                    to tie support the near real-time distribution of incident-based fire detection and related satellite imagery products through the Next Generation Fire System (NGFS). 
         | 
| 39 | 
            +
                    Current funding from the NOAA Weather Program Office (WPO) is supporting the refinement of our Google Earth Engine App (GEE) 
         | 
| 40 | 
            +
                    and integration of GEE burn scar output with AWIPS (see example above) for Weather Forecast Offices, Regional Offices, and the Weather Prediction Center.
         | 
| 41 | 
            +
                    
         | 
| 42 | 
            +
                    """
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    solara.Markdown(markdown)
         | 
    	
        pages/__init__.py
    ADDED
    
    | 
            File without changes
         | 
    	
        pages/__pycache__/NBR_calculations.cpython-312.pyc
    ADDED
    
    | Binary file (6.87 kB). View file | 
|  | 
    	
        pages/current_fires.py
    ADDED
    
    | @@ -0,0 +1,399 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import ee
         | 
| 2 | 
            +
            import geemap
         | 
| 3 | 
            +
            import solara
         | 
| 4 | 
            +
            import ipywidgets as widgets
         | 
| 5 | 
            +
            import datetime
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            #from NBR_calculations import GcalcNBR, VcalcNBR, LcalcNBR, ScalcNBR, GcalcCCsingle
         | 
| 8 | 
            +
            import requests
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            # Bit-masking
         | 
| 11 | 
            +
            BitMask_0 = 1 << 0
         | 
| 12 | 
            +
            BitMask_1 = 1 << 1
         | 
| 13 | 
            +
            BitMask_2 = 1 << 2
         | 
| 14 | 
            +
            BitMask_3 = 1 << 3
         | 
| 15 | 
            +
            BitMask_4 = 1 << 4
         | 
| 16 | 
            +
            BitMask_5 = 1 << 5
         | 
| 17 | 
            +
            BitMask_6 = 1 << 6
         | 
| 18 | 
            +
            BitMask_7 = 1 << 7
         | 
| 19 | 
            +
            BitMask_8 = 1 << 8
         | 
| 20 | 
            +
            BitMask_9 = 1 << 9
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            def GcalcCCsingle (goesImg):
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              fireDQF = goesImg.select('DQF').int()
         | 
| 25 | 
            +
              CMI_QF3 = goesImg.select('DQF_C03').int()
         | 
| 26 | 
            +
              CMI_QF6 = goesImg.select('DQF_C06').int()
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              #Right now, cloud mask is excluding clouds and water; active fire, bad data and fire free are unmasked. NBR mask exlcudes fire
         | 
| 29 | 
            +
              F_Mask = fireDQF.eq(0)
         | 
| 30 | 
            +
              C_Mask = (fireDQF.lt(2).Or(fireDQF.gt(2))).rename('C_Mask')
         | 
| 31 | 
            +
                   #.And(CMI_QF3.lt(2)).And(CMI_QF6.lt(2)).rename('C_Mask')
         | 
| 32 | 
            +
              QF_Mask = (fireDQF.eq(1).Or(fireDQF.gt(3)))\
         | 
| 33 | 
            +
                    .And(CMI_QF3.lt(2)).And(CMI_QF6.lt(2)).rename('QFmask')
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              GOESmasked = goesImg.select(['CMI_C03','CMI_C06']).updateMask(QF_Mask)
         | 
| 36 | 
            +
              NBRmasked = GOESmasked.normalizedDifference(['CMI_C03', 'CMI_C06']).toFloat().rename('NBR')
         | 
| 37 | 
            +
              cloudMasked = goesImg.select('CMI_C03').updateMask(C_Mask).toFloat().rename('CC')
         | 
| 38 | 
            +
              fireMasked =  goesImg.select('CMI_C03').updateMask(F_Mask).toFloat().rename('FC')
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              return goesImg.addBands([NBRmasked,cloudMasked, fireMasked,QF_Mask,C_Mask])
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            '''Parameter Array Name Value Bit(s) = Value
         | 
| 43 | 
            +
            Sun Glint           QF1 Surface Reflectance None 6-7 = 00
         | 
| 44 | 
            +
            Low Sun Mask        QF1 Surface Reflectance High 5 = 0
         | 
| 45 | 
            +
            Day/Night           QF1 Surface Reflectance Day 4 =0
         | 
| 46 | 
            +
            Cloud Detection     QF1 Surface Reflectance Confident Clear 2-3 = 00 or Problably Clear 2-3 = 01
         | 
| 47 | 
            +
            Cloud Mask Quality  QF1 Surface Reflectance High or Medium 0-1 = 10 or 11
         | 
| 48 | 
            +
            Snow/Ice            QF2 Surface Reflectance No Snow or Ice 5 = 0
         | 
| 49 | 
            +
            Cloud Shadow        QF2 Surface Reflectance No Cloud Shadow 3 = 0
         | 
| 50 | 
            +
            LandWater           QF2 Surface Reflectance Land, Snow, Arctic, Antarctic or Greenland, Desert 0-2  = 011, 100, 101, 110, 111
         | 
| 51 | 
            +
            Thin Cirrus Flag    QF7 Surface Reflectance No Thin Cirrus 4 = 0
         | 
| 52 | 
            +
            Aerosol Quantity    QF7 Surface Reflectance Climatology, Low or Medium 2-3 = 00, 01 or 10
         | 
| 53 | 
            +
            Adjacent to Cloud   QF7 Surface Reflectance Not Adjacent to Cloud 1 = 0'''
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            def VcalcNBR (VIIRSimg):
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                QF1 = VIIRSimg.select('QF1').int()
         | 
| 58 | 
            +
                QF2 = VIIRSimg.select('QF2').int()
         | 
| 59 | 
            +
                QF7 = VIIRSimg.select('QF7').int()
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                QF_Mask = (QF1.bitwiseAnd(BitMask_3).eq(0)).And\
         | 
| 62 | 
            +
                ((QF2.bitwiseAnd(BitMask_2).eq(4)).Or((QF2.bitwiseAnd(BitMask_1).eq(0)))).And\
         | 
| 63 | 
            +
                (QF2.bitwiseAnd(BitMask_5).eq(0)).rename('QFmask');
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                VIIRSm = VIIRSimg.select(['I2','M11']).updateMask(QF_Mask);
         | 
| 66 | 
            +
                NBR = VIIRSm.normalizedDifference(['I2','M11']).toFloat().rename('NBR')
         | 
| 67 | 
            +
                return VIIRSimg.addBands(NBR).addBands(QF_Mask)#.set('avgNBR', avgNBR)
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            ''' Bit 1: Dilated Cloud
         | 
| 70 | 
            +
                Bit 2: Cirrus (high confidence)
         | 
| 71 | 
            +
                Bit 3: Cloud
         | 
| 72 | 
            +
                Bit 4: Cloud Shadow
         | 
| 73 | 
            +
                Bit 5: Snow
         | 
| 74 | 
            +
                Bit 6: Clear (0: Cloud or Dilated Cloud bits are set, 1: Cloud and Dilated Cloud bits are not set)
         | 
| 75 | 
            +
                Bit 7: Water
         | 
| 76 | 
            +
                Bits 8-9: Cloud Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 77 | 
            +
                Bits 10-11: Cloud Shadow Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 78 | 
            +
                Bits 12-13: Snow/Ice Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 79 | 
            +
                Bits 14-15: Cirrus Confidence (0: None, 1: Low, 2: Medium, 3: High)'''
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            def LcalcNBR (LSimg):
         | 
| 82 | 
            +
              QApixel = LSimg.select('QA_PIXEL').int()
         | 
| 83 | 
            +
              QF_Mask =(QApixel.bitwiseAnd(BitMask_3).eq(0)).And\
         | 
| 84 | 
            +
                       (QApixel.bitwiseAnd(BitMask_5).eq(0)).And\
         | 
| 85 | 
            +
                       (QApixel.bitwiseAnd(BitMask_7).eq(0)).rename('QFmask');
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              LSmasked = LSimg.select(['SR_B5','SR_B7']).updateMask(QF_Mask);
         | 
| 88 | 
            +
              NBR = LSmasked.normalizedDifference(['SR_B5','SR_B7']).toFloat().rename('NBR')
         | 
| 89 | 
            +
              return LSimg.addBands(NBR).addBands(QF_Mask)#.set('avgNBR', avgNBR)
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            ''' 1    Saturated or defective
         | 
| 92 | 
            +
                2    Dark Area Pixels
         | 
| 93 | 
            +
                3    Cloud Shadows
         | 
| 94 | 
            +
                4    Vegetation
         | 
| 95 | 
            +
                5    Bare Soils
         | 
| 96 | 
            +
                6    Water
         | 
| 97 | 
            +
                7    Clouds Low Probability / Unclassified
         | 
| 98 | 
            +
                8    Clouds Medium Probability
         | 
| 99 | 
            +
                9    Clouds High Probability
         | 
| 100 | 
            +
                10   Cirrus
         | 
| 101 | 
            +
                11   Snow / Ice'''
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            def ScalcNBR (sentImg):
         | 
| 104 | 
            +
              SCL = sentImg.select('SCL');
         | 
| 105 | 
            +
              QF_Mask =(SCL.neq(6)).And\
         | 
| 106 | 
            +
                (SCL.neq(8)).And\
         | 
| 107 | 
            +
                (SCL.neq(9)).And\
         | 
| 108 | 
            +
                (SCL.neq(11))\
         | 
| 109 | 
            +
                .rename('QFmask');
         | 
| 110 | 
            +
              sentMasked = sentImg.select(['B8A','B12']).updateMask(QF_Mask); #B8 is another option- broadband NIR
         | 
| 111 | 
            +
              NBR = sentMasked.normalizedDifference(['B8A','B12']).toFloat().rename('NBR')
         | 
| 112 | 
            +
              return sentImg.addBands(NBR).addBands(QF_Mask).addBands(SCL)#.set('avgNBR', avgNBR)
         | 
| 113 | 
            +
             | 
| 114 | 
            +
            #createDates = NIFC_perims_716.aggregate_array('attr_Cre_1')
         | 
| 115 | 
            +
            #incidentIDs = NIFC_perims_716.aggregate_array('poly_Incid')
         | 
| 116 | 
            +
            #fireList = incidentIDs.getInfo()
         | 
| 117 | 
            +
            fireList = wildfire_names = [ "FRESNO JUNE LIGHTNING COMPLEX", "Larch Creek","Deadman","Cow Valley","0404 RV LONE ROCK",
         | 
| 118 | 
            +
                "PIONEER","South Fork", "Deer Springs","Basin","Lake","Horse Gulch","Falls","Silver King","Indios"]
         | 
| 119 | 
            +
            selected_fire = solara.reactive(fireList[6])
         | 
| 120 | 
            +
            dNBRvisParams = {'min': 0.0,'max': 0.8, 'palette': ['green', 'yellow','orange','red']}
         | 
| 121 | 
            +
            today = datetime.datetime.today().strftime('%Y-%m-%d')
         | 
| 122 | 
            +
             | 
| 123 | 
            +
            class Map(geemap.Map):
         | 
| 124 | 
            +
                def __init__(self, **kwargs):
         | 
| 125 | 
            +
                    super().__init__(**kwargs)        
         | 
| 126 | 
            +
                    self.add_basemap('OpenStreetMap')
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                    self.customize_ee_data(selected_fire.value, today)
         | 
| 129 | 
            +
                    self.add_selector()
         | 
| 130 | 
            +
                    self.add_dwnldButton()
         | 
| 131 | 
            +
                    self.add("layer_manager")
         | 
| 132 | 
            +
                    self.remove("draw_control")
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                    
         | 
| 135 | 
            +
                def customize_ee_data(self, fireID, elapDays):
         | 
| 136 | 
            +
                    NIFC_perims_716 = ee.FeatureCollection('projects/ovcrge-ssec-burn-scar-map-c116/assets/NIFC_perimeters_7-16')
         | 
| 137 | 
            +
                    fire = NIFC_perims_716.filter(ee.Filter.eq('poly_Incid',fireID)).first()
         | 
| 138 | 
            +
                    timestamp = fire.get('attr_Cre_1')
         | 
| 139 | 
            +
                    geom = fire.geometry()
         | 
| 140 | 
            +
                    
         | 
| 141 | 
            +
                    startDate = ee.Date(timestamp)#.format('YYYY-MM-dd')
         | 
| 142 | 
            +
                    endDate = ee.Date.parse('YYYY-MM-dd', str(today))
         | 
| 143 | 
            +
                    
         | 
| 144 | 
            +
                    boundingBox = ee.Geometry(geom.buffer(5000).bounds())
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                    elapDayNum = ee.Number(10)
         | 
| 147 | 
            +
                    elapDay_plusOne = elapDayNum.add(ee.Number(1))
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                    def calc_nbr(pre_start, pre_stop, post_start, post_stop, bbox, goes):
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                            def MergeBands (eachImage):
         | 
| 152 | 
            +
                                oneImage = ee.Image.cat(eachImage.get('CMI'), eachImage.get('FDC'))
         | 
| 153 | 
            +
                                return oneImage
         | 
| 154 | 
            +
                            displacementImg18 = ee.Image.load('projects/ee-losos/assets/G18-F-meter-offset_GEE')
         | 
| 155 | 
            +
                            y_dif = displacementImg18.select([1])
         | 
| 156 | 
            +
                            x_dif = displacementImg18.select([0]).multiply(-1)
         | 
| 157 | 
            +
                            displacement18 = ee.Image([x_dif, y_dif])
         | 
| 158 | 
            +
                            
         | 
| 159 | 
            +
                            displacementImg16 = ee.Image.load('projects/ee-losos/assets/G16-F-meter-offset_GEE')
         | 
| 160 | 
            +
                            y_dif = displacementImg16.select([1])
         | 
| 161 | 
            +
                            x_dif = displacementImg16.select([0]).multiply(-1)
         | 
| 162 | 
            +
                            displacement16 = ee.Image([x_dif, y_dif]);
         | 
| 163 | 
            +
                            
         | 
| 164 | 
            +
                            preCMIcol = ee.ImageCollection(f"NOAA/GOES/{goes}/MCMIPF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 165 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 166 | 
            +
                            preFDCcol = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 167 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 168 | 
            +
                            postCMIcol = ee.ImageCollection(f"NOAA/GOES/{goes}/MCMIPF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 169 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 170 | 
            +
                            postFDCcol = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 171 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                            prejoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 174 | 
            +
                            primary = preCMIcol,
         | 
| 175 | 
            +
                            secondary = preFDCcol,
         | 
| 176 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 177 | 
            +
                                difference = 10, #milliseconds
         | 
| 178 | 
            +
                                leftField = 'system:time_start',
         | 
| 179 | 
            +
                                rightField = 'system:time_start',))
         | 
| 180 | 
            +
                            preMiddayGOEScol = ee.ImageCollection(prejoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 181 | 
            +
                            preMiddayGOEScol = preMiddayGOEScol.map(GcalcCCsingle)
         | 
| 182 | 
            +
                            pre_meanNBR = preMiddayGOEScol.select(['NBR']).mean()
         | 
| 183 | 
            +
                            pre_meanNBR = pre_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                            postjoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 186 | 
            +
                            primary = postCMIcol,
         | 
| 187 | 
            +
                            secondary = postFDCcol,
         | 
| 188 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 189 | 
            +
                                difference = 10, #milliseconds
         | 
| 190 | 
            +
                                leftField = 'system:time_start',
         | 
| 191 | 
            +
                                rightField = 'system:time_start',))
         | 
| 192 | 
            +
                            postMiddayGOEScol = ee.ImageCollection(postjoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 193 | 
            +
                            postMiddayGOEScol = postMiddayGOEScol.map(GcalcCCsingle)
         | 
| 194 | 
            +
                            post_meanNBR = postMiddayGOEScol.select(['NBR']).mean()
         | 
| 195 | 
            +
                            post_meanNBR = post_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                            dNBR_goes17 = pre_meanNBR.subtract(post_meanNBR).select('NBR')
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                            
         | 
| 200 | 
            +
                            #GOES-16
         | 
| 201 | 
            +
                            preCMIcol = ee.ImageCollection("NOAA/GOES/16/MCMIPF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 202 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 203 | 
            +
                            preFDCcol = ee.ImageCollection("NOAA/GOES/16/FDCF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 204 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                            prejoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 207 | 
            +
                            primary = preCMIcol,
         | 
| 208 | 
            +
                            secondary = preFDCcol,
         | 
| 209 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 210 | 
            +
                                difference = 10, #milliseconds
         | 
| 211 | 
            +
                                leftField = 'system:time_start',
         | 
| 212 | 
            +
                                rightField = 'system:time_start',))
         | 
| 213 | 
            +
                            preMiddayGOEScol = ee.ImageCollection(prejoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 214 | 
            +
                            preMiddayGOEScol = preMiddayGOEScol.map(GcalcCCsingle)
         | 
| 215 | 
            +
                            pre_meanNBR = preMiddayGOEScol.select(['NBR']).mean()
         | 
| 216 | 
            +
                            pre_meanNBR = pre_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                            
         | 
| 219 | 
            +
                            postCMIcol = ee.ImageCollection("NOAA/GOES/16/MCMIPF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 220 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 221 | 
            +
                            postFDCcol = ee.ImageCollection("NOAA/GOES/16/FDCF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 222 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                            postjoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 225 | 
            +
                            primary = postCMIcol,
         | 
| 226 | 
            +
                            secondary = postFDCcol,
         | 
| 227 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 228 | 
            +
                                difference = 10, #milliseconds
         | 
| 229 | 
            +
                                leftField = 'system:time_start',
         | 
| 230 | 
            +
                                rightField = 'system:time_start',))
         | 
| 231 | 
            +
                            postMiddayGOEScol = ee.ImageCollection(postjoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 232 | 
            +
                            postMiddayGOEScol = postMiddayGOEScol.map(GcalcCCsingle)
         | 
| 233 | 
            +
                            post_meanNBR = postMiddayGOEScol.select(['NBR']).mean()
         | 
| 234 | 
            +
                            post_meanNBR = post_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                            dNBR_goes16 = pre_meanNBR.subtract(post_meanNBR).select('NBR')
         | 
| 237 | 
            +
                            
         | 
| 238 | 
            +
                            dNBRclip_goes17= dNBR_goes17.clip(bbox)
         | 
| 239 | 
            +
                            dNBRclip_goes16= dNBR_goes16.clip(bbox)
         | 
| 240 | 
            +
                            dNBRdisp_goes17 = dNBRclip_goes17.displace(displacement18, 'bicubic')
         | 
| 241 | 
            +
                            dNBRdisp_goes16 = dNBRclip_goes16.displace(displacement16, 'bicubic')
         | 
| 242 | 
            +
                            dNBRgoes_compos = ee.ImageCollection([dNBRdisp_goes17,dNBRdisp_goes16]).mean()
         | 
| 243 | 
            +
                            
         | 
| 244 | 
            +
                            #ACTIVE fire
         | 
| 245 | 
            +
                            activeFire18 = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 246 | 
            +
                            activeFire16 = ee.ImageCollection(f"NOAA/GOES/16/FDCF").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 247 | 
            +
                            sumFRP18 = activeFire18.select('Power').sum().rename('sumFRP')
         | 
| 248 | 
            +
                            sumFRP16 = activeFire16.select('Power').sum().rename('sumFRP')
         | 
| 249 | 
            +
                            maskNoFire18 = sumFRP18.gt(200).displace(displacement18, 'bicubic')
         | 
| 250 | 
            +
                            maskNoFire16 = sumFRP16.gt(200).displace(displacement16, 'bicubic')
         | 
| 251 | 
            +
                            maskNoFire =  ee.ImageCollection([maskNoFire18,maskNoFire16]).sum().gt(0)
         | 
| 252 | 
            +
                            
         | 
| 253 | 
            +
                            '''
         | 
| 254 | 
            +
                            activeSNPP = ee.ImageCollection("NASA/LANCE/SNPP_VIIRS/C2").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 255 | 
            +
                            activeNOAA20 = ee.ImageCollection("NASA/LANCE/NOAA20_VIIRS/C2").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 256 | 
            +
                            sumFRP_SNPP = activeSNPP.select('confidence').max().rename('sumFRP')
         | 
| 257 | 
            +
                            sumFRP_NOAA20 = activeNOAA20.select('confidence').max().rename('sumFRP')
         | 
| 258 | 
            +
                            #maskNoFire =  ee.ImageCollection([sumFRP_SNPP,sumFRP_NOAA20]).sum().gt(0)
         | 
| 259 | 
            +
                            maskNoFire = sumFRP_SNPP.gt(0)
         | 
| 260 | 
            +
                            '''
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                            #VIIRS
         | 
| 263 | 
            +
                            preVIIRSimg = ee.ImageCollection("NASA/VIIRS/002/VNP09GA").filter(ee.Filter.date(pre_start, pre_stop)).mean()
         | 
| 264 | 
            +
                            postVIIRSimgCol = ee.ImageCollection("NASA/VIIRS/002/VNP09GA").filter(ee.Filter.date(post_start, post_stop)) #TO FIX ON JUNE 18 sfork_startDate.advance(24, 'day'), sfork_startDate.advance(25,'day')
         | 
| 265 | 
            +
                            
         | 
| 266 | 
            +
                            #Landsat
         | 
| 267 | 
            +
                            prelandsat8col = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 268 | 
            +
                            postlandsat8col = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2").filterDate(post_start, post_stop).filterBounds(bbox)
         | 
| 269 | 
            +
                            prelandsat9col = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 270 | 
            +
                            postlandsat9col = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2").filterDate(post_start, post_stop).filterBounds(bbox)
         | 
| 271 | 
            +
                            prelandsatcol = prelandsat8col.merge(prelandsat9col)
         | 
| 272 | 
            +
                            postlandsatcol = postlandsat8col.merge(postlandsat9col)
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                            #Sentinel
         | 
| 275 | 
            +
                            presentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 276 | 
            +
                            postsentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(post_start, post_stop).filterBounds(bbox) #TO FIX on JULY 5: sfork_startDate.advance(32, 'day'), sfork_startDate.advance(33,'day')
         | 
| 277 | 
            +
                            #olderPostSentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(sfork_startDate.advance(37, 'day'), sfork_startDate.advance(38,'day')).filterBounds(bbox)
         | 
| 278 | 
            +
                            
         | 
| 279 | 
            +
                            #SAR
         | 
| 280 | 
            +
                            #SARimg = ee.Image('projects/ovcrge-ssec-burn-scar-map-c116/assets/burned_20200907_20200919_test')
         | 
| 281 | 
            +
                            #SARmask = SARimg.eq(1)
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                            if postVIIRSimgCol.size().getInfo() > 0:
         | 
| 284 | 
            +
                                postVIIRSimg = postVIIRSimgCol.mean()
         | 
| 285 | 
            +
                                preVIIRSimg = VcalcNBR(preVIIRSimg)
         | 
| 286 | 
            +
                                postVIIRSimg = VcalcNBR(postVIIRSimg)
         | 
| 287 | 
            +
                                dNBR_viirs = preVIIRSimg.subtract(postVIIRSimg).select('NBR')
         | 
| 288 | 
            +
                                dNBRclip_viirs = dNBR_viirs.clip(bbox)
         | 
| 289 | 
            +
                            else: 
         | 
| 290 | 
            +
                                dNBR_composite = dNBRgoes_compos
         | 
| 291 | 
            +
                            if postsentCol.size().getInfo() > 0:
         | 
| 292 | 
            +
                                    presentMean = presentCol.mean()
         | 
| 293 | 
            +
                                    postsentMean = postsentCol.mean() 
         | 
| 294 | 
            +
                                    presentImg = ScalcNBR(presentMean)
         | 
| 295 | 
            +
                                    postsentImg = ScalcNBR(postsentMean)
         | 
| 296 | 
            +
                                    dnbr_sent =  presentImg.subtract(postsentImg).multiply(1.3).add(0.05).select('NBR')
         | 
| 297 | 
            +
                                    dNBRclip_sent = dnbr_sent.clip(bbox)
         | 
| 298 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos,dNBRclip_sent]).mosaic() #dNBRclip_viirs SHOULD GO IN IF UP TO DATE
         | 
| 299 | 
            +
                            elif postlandsatcol.size().getInfo() > 0:
         | 
| 300 | 
            +
                                    prelandsat = prelandsatcol.mean()
         | 
| 301 | 
            +
                                    prelandsatImg = LcalcNBR(prelandsat)
         | 
| 302 | 
            +
                                    postlandsat = postlandsatcol.mean()
         | 
| 303 | 
            +
                                    postlandsatImg = LcalcNBR(postlandsat)
         | 
| 304 | 
            +
                                    dNBR_landsat = prelandsatImg.subtract(postlandsatImg).multiply(3.23).add(0.01).select('NBR')
         | 
| 305 | 
            +
                                    dNBRclip_ls = dNBR_landsat.clip(bbox)
         | 
| 306 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos,dNBRclip_ls]).mosaic() #dNBRclip_viirs  SHOULD GO IN IF UP TO DATE
         | 
| 307 | 
            +
                            else:
         | 
| 308 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos]).mosaic() #dNBRclip_viirs  SHOULD GO IN IF UP TO DATE
         | 
| 309 | 
            +
                            
         | 
| 310 | 
            +
                            masked_compos = dNBR_composite.updateMask(maskNoFire) #(SARmask)
         | 
| 311 | 
            +
                            #doubleMasked_compos = masked_compos.updateMask(maskNoFire)
         | 
| 312 | 
            +
                            doubleMasked_compos = masked_compos.mask(masked_compos.mask()).float()
         | 
| 313 | 
            +
                            downloadArgs = {'name': 'VIIRS_burnMap',
         | 
| 314 | 
            +
                                            'crs': 'EPSG:4326',
         | 
| 315 | 
            +
                                            'scale': 60,
         | 
| 316 | 
            +
                                            'region': bbox}
         | 
| 317 | 
            +
                            url = doubleMasked_compos.getDownloadURL(downloadArgs)
         | 
| 318 | 
            +
                            
         | 
| 319 | 
            +
                            print(url)
         | 
| 320 | 
            +
                            noDataVal = -9999
         | 
| 321 | 
            +
                            unmaskedImage = doubleMasked_compos.unmask(noDataVal, False)
         | 
| 322 | 
            +
                            
         | 
| 323 | 
            +
                            task = ee.batch.Export.image.toDrive(**{
         | 
| 324 | 
            +
                                'image': unmaskedImage,
         | 
| 325 | 
            +
                                'description': "Composite_burnMap6",
         | 
| 326 | 
            +
                                'folder': "Earth Engine Outputs",
         | 
| 327 | 
            +
                                'fileNamePrefix': "Composite_burnMap_noData_VIIRS_June18_espg3857_60m",
         | 
| 328 | 
            +
                                'region': bbox,
         | 
| 329 | 
            +
                                'crs': 'EPSG:3857',
         | 
| 330 | 
            +
                                'scale': 60,})
         | 
| 331 | 
            +
                            task.start()
         | 
| 332 | 
            +
                            return masked_compos
         | 
| 333 | 
            +
                    
         | 
| 334 | 
            +
                        
         | 
| 335 | 
            +
                    self.clear_specific_layers()
         | 
| 336 | 
            +
                    
         | 
| 337 | 
            +
                    fireImg = calc_nbr(startDate.advance(-7, 'day'), startDate, endDate.advance(-3, 'day'), endDate, boundingBox, 18) 
         | 
| 338 | 
            +
                    self.addLayer(fireImg, dNBRvisParams, fireID, True)
         | 
| 339 | 
            +
                    self.centerObject(boundingBox, 10)
         | 
| 340 | 
            +
                    file = fireImg
         | 
| 341 | 
            +
             | 
| 342 | 
            +
                
         | 
| 343 | 
            +
                def clear_specific_layers(self):
         | 
| 344 | 
            +
                    layers_to_keep = ['OpenStreetMap']
         | 
| 345 | 
            +
                    layers = list(self.layers)
         | 
| 346 | 
            +
                    for layer in layers:
         | 
| 347 | 
            +
                        if layer.name not in layers_to_keep:
         | 
| 348 | 
            +
                            self.remove_layer(layer)  
         | 
| 349 | 
            +
             | 
| 350 | 
            +
                           
         | 
| 351 | 
            +
                def add_selector(self):
         | 
| 352 | 
            +
                    selector = widgets.Dropdown(options=fireList, value=fireList[6], description='Current wildfire :', style={'description_width': '125px'}, layout=widgets.Layout(width='400px')) 
         | 
| 353 | 
            +
             | 
| 354 | 
            +
                    def on_selector_change(change):
         | 
| 355 | 
            +
                        if change['name'] == 'value': 
         | 
| 356 | 
            +
                            selected_fire.value = change['new']
         | 
| 357 | 
            +
                            self.customize_ee_data(selected_fire.value, today)
         | 
| 358 | 
            +
             | 
| 359 | 
            +
                    selector.observe(on_selector_change, names='value')
         | 
| 360 | 
            +
                    self.add_widget(selector, position="topleft")
         | 
| 361 | 
            +
             | 
| 362 | 
            +
             | 
| 363 | 
            +
                def add_dwnldButton(self):
         | 
| 364 | 
            +
                    button = widgets.Button(description='Export to Drive',icon='cloud-arrow-down')
         | 
| 365 | 
            +
             | 
| 366 | 
            +
                    #def on_button_click(change, file):
         | 
| 367 | 
            +
                    #    if change['name'] == 'value': 
         | 
| 368 | 
            +
                    #        selected_days.value = change['new']
         | 
| 369 | 
            +
                    #        self.download_ee_image(file, "trial_file.tif", scale=30)
         | 
| 370 | 
            +
                    def on_button_click(b):
         | 
| 371 | 
            +
                    # Get the currently selected fire and elapsed days
         | 
| 372 | 
            +
                        fire = selected_fire.value
         | 
| 373 | 
            +
                        elapDays = today
         | 
| 374 | 
            +
                        
         | 
| 375 | 
            +
                        # Customize the EE data and download the image
         | 
| 376 | 
            +
                        file = self.customize_ee_data(fire, elapDays)
         | 
| 377 | 
            +
                        #self.download_ee_image(file, f"{fire}_NBR_{elapDays}days.tif", scale=30)
         | 
| 378 | 
            +
                    
         | 
| 379 | 
            +
                    button.observe(on_button_click) 
         | 
| 380 | 
            +
                    self.add_widget(button, position="topleft")
         | 
| 381 | 
            +
             | 
| 382 | 
            +
                    
         | 
| 383 | 
            +
             | 
| 384 | 
            +
            @solara.component
         | 
| 385 | 
            +
            def Page():
         | 
| 386 | 
            +
                
         | 
| 387 | 
            +
                with solara.Column(align="center"):
         | 
| 388 | 
            +
                    markdown = """
         | 
| 389 | 
            +
                    ## Current 2024 wildfires over 10,000 acres"""
         | 
| 390 | 
            +
                    solara.Markdown(markdown)
         | 
| 391 | 
            +
                
         | 
| 392 | 
            +
                # Isolation is required to prevent the map from overlapping navigation (when screen width < 960px)
         | 
| 393 | 
            +
                with solara.Column(style={"isolation": "isolate"}):
         | 
| 394 | 
            +
                    map_widget = Map.element(
         | 
| 395 | 
            +
                        center=[39, -120.5],
         | 
| 396 | 
            +
                        zoom=8,
         | 
| 397 | 
            +
                        height="600px",
         | 
| 398 | 
            +
                        toolbar_ctrl=False
         | 
| 399 | 
            +
                    )
         | 
    	
        pages/historical_fires.py
    ADDED
    
    | @@ -0,0 +1,429 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import ee
         | 
| 2 | 
            +
            import geemap
         | 
| 3 | 
            +
            import solara
         | 
| 4 | 
            +
            import ipywidgets as widgets
         | 
| 5 | 
            +
            #from NBR_calculations import GcalcNBR, VcalcNBR, LcalcNBR, ScalcNBR, GcalcCCsingle
         | 
| 6 | 
            +
            import requests
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            # Bit-masking
         | 
| 9 | 
            +
            BitMask_0 = 1 << 0
         | 
| 10 | 
            +
            BitMask_1 = 1 << 1
         | 
| 11 | 
            +
            BitMask_2 = 1 << 2
         | 
| 12 | 
            +
            BitMask_3 = 1 << 3
         | 
| 13 | 
            +
            BitMask_4 = 1 << 4
         | 
| 14 | 
            +
            BitMask_5 = 1 << 5
         | 
| 15 | 
            +
            BitMask_6 = 1 << 6
         | 
| 16 | 
            +
            BitMask_7 = 1 << 7
         | 
| 17 | 
            +
            BitMask_8 = 1 << 8
         | 
| 18 | 
            +
            BitMask_9 = 1 << 9
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            def GcalcCCsingle (goesImg):
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              fireDQF = goesImg.select('DQF').int()
         | 
| 23 | 
            +
              CMI_QF3 = goesImg.select('DQF_C03').int()
         | 
| 24 | 
            +
              CMI_QF6 = goesImg.select('DQF_C06').int()
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              #Right now, cloud mask is excluding clouds and water; active fire, bad data and fire free are unmasked. NBR mask exlcudes fire
         | 
| 27 | 
            +
              F_Mask = fireDQF.eq(0)
         | 
| 28 | 
            +
              C_Mask = (fireDQF.lt(2).Or(fireDQF.gt(2))).rename('C_Mask')
         | 
| 29 | 
            +
                   #.And(CMI_QF3.lt(2)).And(CMI_QF6.lt(2)).rename('C_Mask')
         | 
| 30 | 
            +
              QF_Mask = (fireDQF.eq(1).Or(fireDQF.gt(3)))\
         | 
| 31 | 
            +
                    .And(CMI_QF3.lt(2)).And(CMI_QF6.lt(2)).rename('QFmask')
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              GOESmasked = goesImg.select(['CMI_C03','CMI_C06']).updateMask(QF_Mask)
         | 
| 34 | 
            +
              NBRmasked = GOESmasked.normalizedDifference(['CMI_C03', 'CMI_C06']).toFloat().rename('NBR')
         | 
| 35 | 
            +
              cloudMasked = goesImg.select('CMI_C03').updateMask(C_Mask).toFloat().rename('CC')
         | 
| 36 | 
            +
              fireMasked =  goesImg.select('CMI_C03').updateMask(F_Mask).toFloat().rename('FC')
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              return goesImg.addBands([NBRmasked,cloudMasked, fireMasked,QF_Mask,C_Mask])
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            '''Parameter Array Name Value Bit(s) = Value
         | 
| 41 | 
            +
            Sun Glint           QF1 Surface Reflectance None 6-7 = 00
         | 
| 42 | 
            +
            Low Sun Mask        QF1 Surface Reflectance High 5 = 0
         | 
| 43 | 
            +
            Day/Night           QF1 Surface Reflectance Day 4 =0
         | 
| 44 | 
            +
            Cloud Detection     QF1 Surface Reflectance Confident Clear 2-3 = 00 or Problably Clear 2-3 = 01
         | 
| 45 | 
            +
            Cloud Mask Quality  QF1 Surface Reflectance High or Medium 0-1 = 10 or 11
         | 
| 46 | 
            +
            Snow/Ice            QF2 Surface Reflectance No Snow or Ice 5 = 0
         | 
| 47 | 
            +
            Cloud Shadow        QF2 Surface Reflectance No Cloud Shadow 3 = 0
         | 
| 48 | 
            +
            LandWater           QF2 Surface Reflectance Land, Snow, Arctic, Antarctic or Greenland, Desert 0-2  = 011, 100, 101, 110, 111
         | 
| 49 | 
            +
            Thin Cirrus Flag    QF7 Surface Reflectance No Thin Cirrus 4 = 0
         | 
| 50 | 
            +
            Aerosol Quantity    QF7 Surface Reflectance Climatology, Low or Medium 2-3 = 00, 01 or 10
         | 
| 51 | 
            +
            Adjacent to Cloud   QF7 Surface Reflectance Not Adjacent to Cloud 1 = 0'''
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            def VcalcNBR (VIIRSimg):
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                QF1 = VIIRSimg.select('QF1').int()
         | 
| 56 | 
            +
                QF2 = VIIRSimg.select('QF2').int()
         | 
| 57 | 
            +
                QF7 = VIIRSimg.select('QF7').int()
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                QF_Mask = (QF1.bitwiseAnd(BitMask_3).eq(0)).And\
         | 
| 60 | 
            +
                ((QF2.bitwiseAnd(BitMask_2).eq(4)).Or((QF2.bitwiseAnd(BitMask_1).eq(0)))).And\
         | 
| 61 | 
            +
                (QF2.bitwiseAnd(BitMask_5).eq(0)).rename('QFmask');
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                VIIRSm = VIIRSimg.select(['I2','M11']).updateMask(QF_Mask);
         | 
| 64 | 
            +
                NBR = VIIRSm.normalizedDifference(['I2','M11']).toFloat().rename('NBR')
         | 
| 65 | 
            +
                return VIIRSimg.addBands(NBR).addBands(QF_Mask)#.set('avgNBR', avgNBR)
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            ''' Bit 1: Dilated Cloud
         | 
| 68 | 
            +
                Bit 2: Cirrus (high confidence)
         | 
| 69 | 
            +
                Bit 3: Cloud
         | 
| 70 | 
            +
                Bit 4: Cloud Shadow
         | 
| 71 | 
            +
                Bit 5: Snow
         | 
| 72 | 
            +
                Bit 6: Clear (0: Cloud or Dilated Cloud bits are set, 1: Cloud and Dilated Cloud bits are not set)
         | 
| 73 | 
            +
                Bit 7: Water
         | 
| 74 | 
            +
                Bits 8-9: Cloud Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 75 | 
            +
                Bits 10-11: Cloud Shadow Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 76 | 
            +
                Bits 12-13: Snow/Ice Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 77 | 
            +
                Bits 14-15: Cirrus Confidence (0: None, 1: Low, 2: Medium, 3: High)'''
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            def LcalcNBR (LSimg):
         | 
| 80 | 
            +
              QApixel = LSimg.select('QA_PIXEL').int()
         | 
| 81 | 
            +
              QF_Mask =(QApixel.bitwiseAnd(BitMask_3).eq(0)).And\
         | 
| 82 | 
            +
                       (QApixel.bitwiseAnd(BitMask_5).eq(0)).And\
         | 
| 83 | 
            +
                       (QApixel.bitwiseAnd(BitMask_7).eq(0)).rename('QFmask');
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              LSmasked = LSimg.select(['SR_B5','SR_B7']).updateMask(QF_Mask);
         | 
| 86 | 
            +
              NBR = LSmasked.normalizedDifference(['SR_B5','SR_B7']).toFloat().rename('NBR')
         | 
| 87 | 
            +
              return LSimg.addBands(NBR).addBands(QF_Mask)#.set('avgNBR', avgNBR)
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            ''' 1    Saturated or defective
         | 
| 90 | 
            +
                2    Dark Area Pixels
         | 
| 91 | 
            +
                3    Cloud Shadows
         | 
| 92 | 
            +
                4    Vegetation
         | 
| 93 | 
            +
                5    Bare Soils
         | 
| 94 | 
            +
                6    Water
         | 
| 95 | 
            +
                7    Clouds Low Probability / Unclassified
         | 
| 96 | 
            +
                8    Clouds Medium Probability
         | 
| 97 | 
            +
                9    Clouds High Probability
         | 
| 98 | 
            +
                10   Cirrus
         | 
| 99 | 
            +
                11   Snow / Ice'''
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            def ScalcNBR (sentImg):
         | 
| 102 | 
            +
              SCL = sentImg.select('SCL');
         | 
| 103 | 
            +
              QF_Mask =(SCL.neq(6)).And\
         | 
| 104 | 
            +
                (SCL.neq(8)).And\
         | 
| 105 | 
            +
                (SCL.neq(9)).And\
         | 
| 106 | 
            +
                (SCL.neq(11))\
         | 
| 107 | 
            +
                .rename('QFmask');
         | 
| 108 | 
            +
              sentMasked = sentImg.select(['B8A','B12']).updateMask(QF_Mask); #B8 is another option- broadband NIR
         | 
| 109 | 
            +
              NBR = sentMasked.normalizedDifference(['B8A','B12']).toFloat().rename('NBR')
         | 
| 110 | 
            +
              return sentImg.addBands(NBR).addBands(QF_Mask).addBands(SCL)#.set('avgNBR', avgNBR)
         | 
| 111 | 
            +
             | 
| 112 | 
            +
             | 
| 113 | 
            +
            fireList = ["North Complex", "Dixie", "Cameron Peak", "August Complex", "South Fork"]
         | 
| 114 | 
            +
            selected_fire = solara.reactive(fireList[4])
         | 
| 115 | 
            +
            selected_days = solara.reactive(25) #30
         | 
| 116 | 
            +
            dNBRvisParams = {'min': 0.0,'max': 0.8, 'palette': ['green', 'yellow','orange','red']}
         | 
| 117 | 
            +
             | 
| 118 | 
            +
             | 
| 119 | 
            +
            class Map(geemap.Map):
         | 
| 120 | 
            +
                def __init__(self, **kwargs):
         | 
| 121 | 
            +
                    super().__init__(**kwargs)        
         | 
| 122 | 
            +
                    self.add_basemap('OpenStreetMap')
         | 
| 123 | 
            +
                    self.customize_ee_data(selected_fire.value, selected_days.value)
         | 
| 124 | 
            +
                    self.add_selector()
         | 
| 125 | 
            +
                    self.add_intSlider()
         | 
| 126 | 
            +
                    self.add_dwnldButton()
         | 
| 127 | 
            +
                    self.add("layer_manager")
         | 
| 128 | 
            +
                    self.remove("draw_control")
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    
         | 
| 131 | 
            +
                def customize_ee_data(self, fire, elapDays):
         | 
| 132 | 
            +
                    elapDayNum = ee.Number(elapDays)
         | 
| 133 | 
            +
                    elapDay_plusOne = elapDayNum.add(ee.Number(1))
         | 
| 134 | 
            +
                    
         | 
| 135 | 
            +
                    north_startDate = ee.Date('2020-08-16')
         | 
| 136 | 
            +
                    dixie_startDate = ee.Date('2021-07-13')
         | 
| 137 | 
            +
                    cam_startDate = ee.Date('2020-08-13')
         | 
| 138 | 
            +
                    aug_startDate = ee.Date('2020-08-15')
         | 
| 139 | 
            +
                    sfork_startDate = ee.Date('2024-05-25')
         | 
| 140 | 
            +
                    
         | 
| 141 | 
            +
                    north_complex_bb = ee.Geometry.BBox(-121.616097, 39.426723, -120.668526, 40.030845)
         | 
| 142 | 
            +
                    dixie_bb = ee.Geometry.BBox(-121.680467, 39.759303, -120.065477, 40.873387)
         | 
| 143 | 
            +
                    cam_peak_bb = ee.Geometry.BBox(-106.014784, 40.377907, -105.116651, 40.822094)
         | 
| 144 | 
            +
                    aug_complex_bb = ee.Geometry.BBox(-123.668726, 39.337654, -122.355860, 40.498304)
         | 
| 145 | 
            +
                    sfork_bb = ee.Geometry.BBox(-106.192, 33.1,  -105.065, 33.782)
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    def calc_nbr(pre_start, pre_stop, post_start, post_stop, bbox, goes):
         | 
| 148 | 
            +
                            def MergeBands (eachImage):
         | 
| 149 | 
            +
                                oneImage = ee.Image.cat(eachImage.get('CMI'), eachImage.get('FDC'))
         | 
| 150 | 
            +
                                return oneImage
         | 
| 151 | 
            +
                            displacementImg18 = ee.Image.load('projects/ee-losos/assets/G18-F-meter-offset_GEE')
         | 
| 152 | 
            +
                            y_dif = displacementImg18.select([1])
         | 
| 153 | 
            +
                            x_dif = displacementImg18.select([0]).multiply(-1)
         | 
| 154 | 
            +
                            displacement18 = ee.Image([x_dif, y_dif])
         | 
| 155 | 
            +
                            
         | 
| 156 | 
            +
                            displacementImg16 = ee.Image.load('projects/ee-losos/assets/G16-F-meter-offset_GEE')
         | 
| 157 | 
            +
                            y_dif = displacementImg16.select([1])
         | 
| 158 | 
            +
                            x_dif = displacementImg16.select([0]).multiply(-1)
         | 
| 159 | 
            +
                            displacement16 = ee.Image([x_dif, y_dif]);
         | 
| 160 | 
            +
                            
         | 
| 161 | 
            +
                            preCMIcol = ee.ImageCollection(f"NOAA/GOES/{goes}/MCMIPF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 162 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 163 | 
            +
                            preFDCcol = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 164 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 165 | 
            +
                            postCMIcol = ee.ImageCollection(f"NOAA/GOES/{goes}/MCMIPF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 166 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 167 | 
            +
                            postFDCcol = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 168 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                            prejoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 171 | 
            +
                            primary = preCMIcol,
         | 
| 172 | 
            +
                            secondary = preFDCcol,
         | 
| 173 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 174 | 
            +
                                difference = 10, #milliseconds
         | 
| 175 | 
            +
                                leftField = 'system:time_start',
         | 
| 176 | 
            +
                                rightField = 'system:time_start',))
         | 
| 177 | 
            +
                            preMiddayGOEScol = ee.ImageCollection(prejoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 178 | 
            +
                            preMiddayGOEScol = preMiddayGOEScol.map(GcalcCCsingle)
         | 
| 179 | 
            +
                            pre_meanNBR = preMiddayGOEScol.select(['NBR']).mean()
         | 
| 180 | 
            +
                            pre_meanNBR = pre_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                            postjoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 183 | 
            +
                            primary = postCMIcol,
         | 
| 184 | 
            +
                            secondary = postFDCcol,
         | 
| 185 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 186 | 
            +
                                difference = 10, #milliseconds
         | 
| 187 | 
            +
                                leftField = 'system:time_start',
         | 
| 188 | 
            +
                                rightField = 'system:time_start',))
         | 
| 189 | 
            +
                            postMiddayGOEScol = ee.ImageCollection(postjoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 190 | 
            +
                            postMiddayGOEScol = postMiddayGOEScol.map(GcalcCCsingle)
         | 
| 191 | 
            +
                            post_meanNBR = postMiddayGOEScol.select(['NBR']).mean()
         | 
| 192 | 
            +
                            post_meanNBR = post_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                            dNBR_goes17 = pre_meanNBR.subtract(post_meanNBR).select('NBR')
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                            
         | 
| 197 | 
            +
                            #GOES-16
         | 
| 198 | 
            +
                            preCMIcol = ee.ImageCollection("NOAA/GOES/16/MCMIPF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 199 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 200 | 
            +
                            preFDCcol = ee.ImageCollection("NOAA/GOES/16/FDCF").filter(ee.Filter.date(pre_start, pre_stop))\
         | 
| 201 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                            prejoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 204 | 
            +
                            primary = preCMIcol,
         | 
| 205 | 
            +
                            secondary = preFDCcol,
         | 
| 206 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 207 | 
            +
                                difference = 10, #milliseconds
         | 
| 208 | 
            +
                                leftField = 'system:time_start',
         | 
| 209 | 
            +
                                rightField = 'system:time_start',))
         | 
| 210 | 
            +
                            preMiddayGOEScol = ee.ImageCollection(prejoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 211 | 
            +
                            preMiddayGOEScol = preMiddayGOEScol.map(GcalcCCsingle)
         | 
| 212 | 
            +
                            pre_meanNBR = preMiddayGOEScol.select(['NBR']).mean()
         | 
| 213 | 
            +
                            pre_meanNBR = pre_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                            
         | 
| 216 | 
            +
                            postCMIcol = ee.ImageCollection("NOAA/GOES/16/MCMIPF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 217 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour'))#10-2pm PCT, 11am-3pm MST
         | 
| 218 | 
            +
                            postFDCcol = ee.ImageCollection("NOAA/GOES/16/FDCF").filter(ee.Filter.date(post_start, post_stop))\
         | 
| 219 | 
            +
                                    .filter(ee.Filter.calendarRange(17, 21, 'hour')) #10-2pm PCT, 11am-3pm MST
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                            postjoinedGOES = ee.Join.inner('CMI','FDC').apply(
         | 
| 222 | 
            +
                            primary = postCMIcol,
         | 
| 223 | 
            +
                            secondary = postFDCcol,
         | 
| 224 | 
            +
                            condition = ee.Filter.maxDifference(
         | 
| 225 | 
            +
                                difference = 10, #milliseconds
         | 
| 226 | 
            +
                                leftField = 'system:time_start',
         | 
| 227 | 
            +
                                rightField = 'system:time_start',))
         | 
| 228 | 
            +
                            postMiddayGOEScol = ee.ImageCollection(postjoinedGOES.map(lambda object: MergeBands(object)))
         | 
| 229 | 
            +
                            postMiddayGOEScol = postMiddayGOEScol.map(GcalcCCsingle)
         | 
| 230 | 
            +
                            post_meanNBR = postMiddayGOEScol.select(['NBR']).mean()
         | 
| 231 | 
            +
                            post_meanNBR = post_meanNBR.multiply(1.18).subtract(0.12)
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                            dNBR_goes16 = pre_meanNBR.subtract(post_meanNBR).select('NBR')
         | 
| 234 | 
            +
                            
         | 
| 235 | 
            +
                            dNBRclip_goes17= dNBR_goes17.clip(bbox)
         | 
| 236 | 
            +
                            dNBRclip_goes16= dNBR_goes16.clip(bbox)
         | 
| 237 | 
            +
                            dNBRdisp_goes17 = dNBRclip_goes17.displace(displacement18, 'bicubic')
         | 
| 238 | 
            +
                            dNBRdisp_goes16 = dNBRclip_goes16.displace(displacement16, 'bicubic')
         | 
| 239 | 
            +
                            dNBRgoes_compos = ee.ImageCollection([dNBRdisp_goes17,dNBRdisp_goes16]).mean()
         | 
| 240 | 
            +
                            
         | 
| 241 | 
            +
                            #ACTIVE fire
         | 
| 242 | 
            +
                            activeFire18 = ee.ImageCollection(f"NOAA/GOES/{goes}/FDCF").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 243 | 
            +
                            activeFire16 = ee.ImageCollection(f"NOAA/GOES/16/FDCF").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 244 | 
            +
                            sumFRP18 = activeFire18.select('Power').sum().rename('sumFRP')
         | 
| 245 | 
            +
                            sumFRP16 = activeFire16.select('Power').sum().rename('sumFRP')
         | 
| 246 | 
            +
                            maskNoFire18 = sumFRP18.gt(200).displace(displacement18, 'bicubic')
         | 
| 247 | 
            +
                            maskNoFire16 = sumFRP16.gt(200).displace(displacement16, 'bicubic')
         | 
| 248 | 
            +
                            maskNoFire =  ee.ImageCollection([maskNoFire18,maskNoFire16]).sum().gt(0)
         | 
| 249 | 
            +
                            
         | 
| 250 | 
            +
                            '''
         | 
| 251 | 
            +
                            activeSNPP = ee.ImageCollection("NASA/LANCE/SNPP_VIIRS/C2").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 252 | 
            +
                            activeNOAA20 = ee.ImageCollection("NASA/LANCE/NOAA20_VIIRS/C2").filter(ee.Filter.date(pre_stop, post_stop))
         | 
| 253 | 
            +
                            sumFRP_SNPP = activeSNPP.select('confidence').max().rename('sumFRP')
         | 
| 254 | 
            +
                            sumFRP_NOAA20 = activeNOAA20.select('confidence').max().rename('sumFRP')
         | 
| 255 | 
            +
                            #maskNoFire =  ee.ImageCollection([sumFRP_SNPP,sumFRP_NOAA20]).sum().gt(0)
         | 
| 256 | 
            +
                            maskNoFire = sumFRP_SNPP.gt(0)
         | 
| 257 | 
            +
                            '''
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                            #VIIRS
         | 
| 260 | 
            +
                            preVIIRSimg = ee.ImageCollection("NASA/VIIRS/002/VNP09GA").filter(ee.Filter.date(pre_start, pre_stop)).mean()
         | 
| 261 | 
            +
                            #postVIIRSimgCol = ee.ImageCollection("NASA/VIIRS/002/VNP09GA").filter(ee.Filter.date(post_start, post_stop))
         | 
| 262 | 
            +
                            postVIIRSimgCol = ee.ImageCollection("NASA/VIIRS/002/VNP09GA").filter(ee.Filter.date(post_start, post_stop)) #TO FIX ON JUNE 18 sfork_startDate.advance(24, 'day'), sfork_startDate.advance(25,'day')
         | 
| 263 | 
            +
                            
         | 
| 264 | 
            +
                            #Landsat
         | 
| 265 | 
            +
                            prelandsat8col = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 266 | 
            +
                            postlandsat8col = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2").filterDate(post_start, post_stop).filterBounds(bbox)
         | 
| 267 | 
            +
                            prelandsat9col = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 268 | 
            +
                            postlandsat9col = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2").filterDate(post_start, post_stop).filterBounds(bbox)
         | 
| 269 | 
            +
                            prelandsatcol = prelandsat8col.merge(prelandsat9col)
         | 
| 270 | 
            +
                            postlandsatcol = postlandsat8col.merge(postlandsat9col)
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                            #Sentinel
         | 
| 273 | 
            +
                            presentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(pre_start.advance(-10, 'day'), pre_stop).filterBounds(bbox)
         | 
| 274 | 
            +
                            postsentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(post_start, post_stop).filterBounds(bbox) #TO FIX on JULY 5: sfork_startDate.advance(32, 'day'), sfork_startDate.advance(33,'day')
         | 
| 275 | 
            +
                            olderPostSentCol = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterDate(sfork_startDate.advance(37, 'day'), sfork_startDate.advance(38,'day')).filterBounds(bbox)
         | 
| 276 | 
            +
                            #SAR
         | 
| 277 | 
            +
                            #SARimg = ee.Image('projects/ovcrge-ssec-burn-scar-map-c116/assets/burned_20200907_20200919_test')
         | 
| 278 | 
            +
                            #SARmask = SARimg.eq(1)
         | 
| 279 | 
            +
                            if postVIIRSimgCol.size().getInfo() > 0:
         | 
| 280 | 
            +
                                postVIIRSimg = postVIIRSimgCol.mean()
         | 
| 281 | 
            +
                                preVIIRSimg = VcalcNBR(preVIIRSimg)
         | 
| 282 | 
            +
                                postVIIRSimg = VcalcNBR(postVIIRSimg)
         | 
| 283 | 
            +
                                dNBR_viirs = preVIIRSimg.subtract(postVIIRSimg).select('NBR')
         | 
| 284 | 
            +
                                dNBRclip_viirs = dNBR_viirs.clip(bbox)
         | 
| 285 | 
            +
                            else: 
         | 
| 286 | 
            +
                                dNBR_composite = dNBRgoes_compos
         | 
| 287 | 
            +
                            if postsentCol.size().getInfo() > 0:
         | 
| 288 | 
            +
                                    presentMean = presentCol.mean()
         | 
| 289 | 
            +
                                    postsentMean = postsentCol.mean() 
         | 
| 290 | 
            +
                                    postsent2Mean = olderPostSentCol.mean()
         | 
| 291 | 
            +
                                    presentImg = ScalcNBR(presentMean)
         | 
| 292 | 
            +
                                    postsentImg = ScalcNBR(postsentMean)
         | 
| 293 | 
            +
                                    postsentImg2 = ScalcNBR(postsent2Mean)
         | 
| 294 | 
            +
                                    postSentCombo = ee.ImageCollection([postsentImg,postsentImg2]).mosaic()
         | 
| 295 | 
            +
                                    dnbr_sent =  presentImg.subtract(postSentCombo).multiply(1.3).add(0.05).select('NBR')
         | 
| 296 | 
            +
                                    dNBRclip_sent = dnbr_sent.clip(bbox)
         | 
| 297 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos,dNBRclip_viirs,dNBRclip_sent]).mosaic()
         | 
| 298 | 
            +
                            elif postlandsatcol.size().getInfo() > 0:
         | 
| 299 | 
            +
                                    print(postlandsatcol.size().getInfo())
         | 
| 300 | 
            +
                                    prelandsat = prelandsatcol.mean()
         | 
| 301 | 
            +
                                    prelandsatImg = LcalcNBR(prelandsat)
         | 
| 302 | 
            +
                                    postlandsat = postlandsatcol.mean()
         | 
| 303 | 
            +
                                    postlandsatImg = LcalcNBR(postlandsat)
         | 
| 304 | 
            +
                                    dNBR_landsat = prelandsatImg.subtract(postlandsatImg).multiply(3.23).add(0.01).select('NBR')
         | 
| 305 | 
            +
                                    dNBRclip_ls = dNBR_landsat.clip(bbox)
         | 
| 306 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos,dNBRclip_viirs,dNBRclip_ls]).mosaic()
         | 
| 307 | 
            +
                            else:
         | 
| 308 | 
            +
                                    dNBR_composite = ee.ImageCollection([dNBRgoes_compos,dNBRclip_viirs]).mosaic() 
         | 
| 309 | 
            +
                            
         | 
| 310 | 
            +
                            masked_compos = dNBR_composite.updateMask(maskNoFire) #(SARmask)
         | 
| 311 | 
            +
                            #doubleMasked_compos = masked_compos.updateMask(maskNoFire)
         | 
| 312 | 
            +
                            doubleMasked_compos = masked_compos.mask(masked_compos.mask()).float()
         | 
| 313 | 
            +
                            downloadArgs = {'name': 'VIIRS_burnMap',
         | 
| 314 | 
            +
                                            'crs': 'EPSG:4326',
         | 
| 315 | 
            +
                                            'scale': 60,
         | 
| 316 | 
            +
                                            'region': bbox}
         | 
| 317 | 
            +
                            url = doubleMasked_compos.getDownloadURL(downloadArgs)
         | 
| 318 | 
            +
                            
         | 
| 319 | 
            +
                            print(url)
         | 
| 320 | 
            +
                            noDataVal = -9999
         | 
| 321 | 
            +
                            unmaskedImage = doubleMasked_compos.unmask(noDataVal, False)
         | 
| 322 | 
            +
                            
         | 
| 323 | 
            +
                            task = ee.batch.Export.image.toDrive(**{
         | 
| 324 | 
            +
                                'image': unmaskedImage,
         | 
| 325 | 
            +
                                'description': "Composite_burnMap6",
         | 
| 326 | 
            +
                                'folder': "Earth Engine Outputs",
         | 
| 327 | 
            +
                                'fileNamePrefix': "Composite_burnMap_noData_VIIRS_June18_espg3857_60m",
         | 
| 328 | 
            +
                                'region': bbox,
         | 
| 329 | 
            +
                                'crs': 'EPSG:3857',
         | 
| 330 | 
            +
                                'scale': 60,})
         | 
| 331 | 
            +
                            #task.start()
         | 
| 332 | 
            +
                            return masked_compos
         | 
| 333 | 
            +
                    
         | 
| 334 | 
            +
                        
         | 
| 335 | 
            +
                    self.clear_specific_layers()
         | 
| 336 | 
            +
                    
         | 
| 337 | 
            +
                    if fire == "North Complex": 
         | 
| 338 | 
            +
                        north_complex = calc_nbr(north_startDate.advance(-7, 'day'), north_startDate, north_startDate.advance(elapDayNum, 'day'), north_startDate.advance(elapDay_plusOne,'day'), north_complex_bb, 17) 
         | 
| 339 | 
            +
                        self.addLayer(north_complex, dNBRvisParams, 'North Complex GOES NBR', True)
         | 
| 340 | 
            +
                        self.centerObject(north_complex_bb, 9)
         | 
| 341 | 
            +
                        file = north_complex
         | 
| 342 | 
            +
                    elif fire == "Dixie":
         | 
| 343 | 
            +
                        dixie = calc_nbr(dixie_startDate.advance(-7, 'day'), dixie_startDate, dixie_startDate.advance(elapDayNum, 'day'), dixie_startDate.advance(elapDay_plusOne,'day'), dixie_bb, 17)
         | 
| 344 | 
            +
                        self.addLayer(dixie, dNBRvisParams, 'Dixie Complex GOES NBR', True)
         | 
| 345 | 
            +
                        self.centerObject(dixie_bb, 9)
         | 
| 346 | 
            +
                        file = dixie
         | 
| 347 | 
            +
                    elif fire == "Cameron Peak":
         | 
| 348 | 
            +
                        cam_peak = calc_nbr(cam_startDate.advance(-7, 'day'), cam_startDate, cam_startDate.advance(elapDayNum, 'day'), cam_startDate.advance(elapDay_plusOne,'day'),   cam_peak_bb, 17)
         | 
| 349 | 
            +
                        self.addLayer(cam_peak, dNBRvisParams, 'Cameron Peak GOES NBR', True)
         | 
| 350 | 
            +
                        self.centerObject(cam_peak_bb, 9)
         | 
| 351 | 
            +
                        file = cam_peak
         | 
| 352 | 
            +
                    elif fire == "August Complex":
         | 
| 353 | 
            +
                        aug_complex = calc_nbr(aug_startDate.advance(-7, 'day'), aug_startDate, aug_startDate.advance(elapDayNum, 'day'), aug_startDate.advance(elapDay_plusOne,'day'),   aug_complex_bb, 17)
         | 
| 354 | 
            +
                        self.addLayer(aug_complex, dNBRvisParams, 'August Complex GOES NBR', True)
         | 
| 355 | 
            +
                        self.centerObject(aug_complex_bb, 9)
         | 
| 356 | 
            +
                        file = aug_complex
         | 
| 357 | 
            +
                    elif fire == "South Fork":
         | 
| 358 | 
            +
                        sfork = calc_nbr(sfork_startDate.advance(-7, 'day'), sfork_startDate, sfork_startDate.advance(elapDayNum, 'day'), sfork_startDate.advance(elapDay_plusOne,'day'),   sfork_bb, 18)
         | 
| 359 | 
            +
                        self.addLayer(sfork, dNBRvisParams, 'South Fork GOES NBR', True)
         | 
| 360 | 
            +
                        self.centerObject(sfork_bb, 9)
         | 
| 361 | 
            +
                        file = sfork
         | 
| 362 | 
            +
                
         | 
| 363 | 
            +
                def clear_specific_layers(self):
         | 
| 364 | 
            +
                    layers_to_keep = ['OpenStreetMap']
         | 
| 365 | 
            +
                    layers = list(self.layers)
         | 
| 366 | 
            +
                    for layer in layers:
         | 
| 367 | 
            +
                        if layer.name not in layers_to_keep:
         | 
| 368 | 
            +
                            self.remove_layer(layer)  
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                           
         | 
| 371 | 
            +
                def add_selector(self):
         | 
| 372 | 
            +
                    selector = widgets.Dropdown(options=fireList, value="South Fork", description='Wildfire Case Study:', style={'description_width': '125px'}, layout=widgets.Layout(width='400px')) 
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                    def on_selector_change(change):
         | 
| 375 | 
            +
                        if change['name'] == 'value': 
         | 
| 376 | 
            +
                            selected_fire.value = change['new']
         | 
| 377 | 
            +
                            self.customize_ee_data(selected_fire.value, selected_days.value)
         | 
| 378 | 
            +
             | 
| 379 | 
            +
                    selector.observe(on_selector_change, names='value')
         | 
| 380 | 
            +
                    self.add_widget(selector, position="topleft")
         | 
| 381 | 
            +
             | 
| 382 | 
            +
                def add_intSlider(self):
         | 
| 383 | 
            +
                    slider = widgets.IntSlider(value=selected_days.value,min=1,max=40,step=1,description='Elapsed days:',style={'description_width': '125px'}, layout=widgets.Layout(width='400px'))
         | 
| 384 | 
            +
                    
         | 
| 385 | 
            +
                    def on_slider_change(change):
         | 
| 386 | 
            +
                        if change['name'] == 'value': 
         | 
| 387 | 
            +
                            selected_days.value = change['new']
         | 
| 388 | 
            +
                            self.customize_ee_data(selected_fire.value, selected_days.value)
         | 
| 389 | 
            +
                        
         | 
| 390 | 
            +
                    slider.observe(on_slider_change, names='value') 
         | 
| 391 | 
            +
                    self.add_widget(slider, position="topleft")
         | 
| 392 | 
            +
             | 
| 393 | 
            +
                def add_dwnldButton(self):
         | 
| 394 | 
            +
                    button = widgets.Button(description='Download',icon='cloud-arrow-down')
         | 
| 395 | 
            +
             | 
| 396 | 
            +
                    #def on_button_click(change, file):
         | 
| 397 | 
            +
                    #    if change['name'] == 'value': 
         | 
| 398 | 
            +
                    #        selected_days.value = change['new']
         | 
| 399 | 
            +
                    #        self.download_ee_image(file, "trial_file.tif", scale=30)
         | 
| 400 | 
            +
                    def on_button_click(b):
         | 
| 401 | 
            +
                    # Get the currently selected fire and elapsed days
         | 
| 402 | 
            +
                        fire = selected_fire.value
         | 
| 403 | 
            +
                        elapDays = selected_days.value
         | 
| 404 | 
            +
                        
         | 
| 405 | 
            +
                        # Customize the EE data and download the image
         | 
| 406 | 
            +
                        file = self.customize_ee_data(fire, elapDays)
         | 
| 407 | 
            +
                        #self.download_ee_image(file, f"{fire}_NBR_{elapDays}days.tif", scale=30)
         | 
| 408 | 
            +
                    
         | 
| 409 | 
            +
                    button.observe(on_button_click) 
         | 
| 410 | 
            +
                    self.add_widget(button, position="topleft")
         | 
| 411 | 
            +
             | 
| 412 | 
            +
                    
         | 
| 413 | 
            +
             | 
| 414 | 
            +
            @solara.component
         | 
| 415 | 
            +
            def Page():
         | 
| 416 | 
            +
                
         | 
| 417 | 
            +
                with solara.Column(align="center"):
         | 
| 418 | 
            +
                    markdown = """
         | 
| 419 | 
            +
                    ## Historical Western US wildfires from 2020-2021 """
         | 
| 420 | 
            +
                    solara.Markdown(markdown)
         | 
| 421 | 
            +
                
         | 
| 422 | 
            +
                # Isolation is required to prevent the map from overlapping navigation (when screen width < 960px)
         | 
| 423 | 
            +
                with solara.Column(style={"isolation": "isolate"}):
         | 
| 424 | 
            +
                    map_widget = Map.element(
         | 
| 425 | 
            +
                        center=[39, -120.5],
         | 
| 426 | 
            +
                        zoom=8,
         | 
| 427 | 
            +
                        height="600px",
         | 
| 428 | 
            +
                        toolbar_ctrl=False
         | 
| 429 | 
            +
                    )
         | 
    	
        requirements.txt
    ADDED
    
    | @@ -0,0 +1,6 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            geemap
         | 
| 2 | 
            +
            solara== 1.33.0
         | 
| 3 | 
            +
            geopandas
         | 
| 4 | 
            +
            pydantic< 2.0 
         | 
| 5 | 
            +
            ipyevents
         | 
| 6 | 
            +
            ipywidgets
         | 
    	
        utils/NBR_calculations.py
    ADDED
    
    | @@ -0,0 +1,130 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import ee
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            ''' 0   Good quality fire
         | 
| 4 | 
            +
                1   Good quality fire-free land
         | 
| 5 | 
            +
                2   Invalid due to opaque cloud
         | 
| 6 | 
            +
                3   Invalid due to surface type or sunglint or LZA threshold exceeded or off earth or missing input data
         | 
| 7 | 
            +
                4   Invalid due to bad input data
         | 
| 8 | 
            +
                5   Invalid due to algorithm failure'''
         | 
| 9 | 
            +
            # Bit-masking
         | 
| 10 | 
            +
            BitMask_0 = 1 << 0
         | 
| 11 | 
            +
            BitMask_1 = 1 << 1
         | 
| 12 | 
            +
            BitMask_2 = 1 << 2
         | 
| 13 | 
            +
            BitMask_3 = 1 << 3
         | 
| 14 | 
            +
            BitMask_4 = 1 << 4
         | 
| 15 | 
            +
            BitMask_5 = 1 << 5
         | 
| 16 | 
            +
            BitMask_6 = 1 << 6
         | 
| 17 | 
            +
            BitMask_7 = 1 << 7
         | 
| 18 | 
            +
            BitMask_8 = 1 << 8
         | 
| 19 | 
            +
            BitMask_9 = 1 << 9
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            def GcalcNBR (goesImg, aoi):
         | 
| 22 | 
            +
              #day = ee.Date(eachImg.get('system:time_start')).get('day','America/Los_Angeles')
         | 
| 23 | 
            +
              fireMode = goesImg.select('fireMode')
         | 
| 24 | 
            +
              fireMin = goesImg.select('fireMin')
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              CMI_QF3 = goesImg.select('DQF_C03').int()
         | 
| 27 | 
            +
              CMI_QF6 = goesImg.select('DQF_C06').int()
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              # To include active fire pixels - fireMin.lt(2)\ for next line
         | 
| 30 | 
            +
              QF_Mask = (fireMin.eq(1)\
         | 
| 31 | 
            +
                .Or(fireMin.gt(3)))\
         | 
| 32 | 
            +
                .And(CMI_QF3.lt(2))\
         | 
| 33 | 
            +
                .And(CMI_QF6.lt(2))\
         | 
| 34 | 
            +
                .rename('QFmask');
         | 
| 35 | 
            +
              GOESm = goesImg.select(['CMI_C03','CMI_C06']).updateMask(QF_Mask)
         | 
| 36 | 
            +
              NBR = GOESm.normalizedDifference(['CMI_C03', 'CMI_C06']).toFloat().rename('NBR')
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              return goesImg.addBands([NBR,QF_Mask])
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            def GcalcCCsingle (goesImg):
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              fireDQF = goesImg.select('DQF').int()
         | 
| 43 | 
            +
              CMI_QF3 = goesImg.select('DQF_C03').int()
         | 
| 44 | 
            +
              CMI_QF6 = goesImg.select('DQF_C06').int()
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              #Right now, cloud mask is excluding clouds and water; active fire, bad data and fire free are unmasked. NBR mask exlcudes fire
         | 
| 47 | 
            +
              F_Mask = fireDQF.eq(0)
         | 
| 48 | 
            +
              C_Mask = (fireDQF.lt(2).Or(fireDQF.gt(2))).rename('C_Mask')
         | 
| 49 | 
            +
                   #.And(CMI_QF3.lt(2)).And(CMI_QF6.lt(2)).rename('C_Mask')
         | 
| 50 | 
            +
              QF_Mask = (fireDQF.eq(1).Or(fireDQF.gt(3)))\
         | 
| 51 | 
            +
                    .And(CMI_QF3.lt(2)).And(CMI_QF6.lt(2)).rename('QFmask')
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              GOESmasked = goesImg.select(['CMI_C03','CMI_C06']).updateMask(QF_Mask)
         | 
| 54 | 
            +
              NBRmasked = GOESmasked.normalizedDifference(['CMI_C03', 'CMI_C06']).toFloat().rename('NBR')
         | 
| 55 | 
            +
              cloudMasked = goesImg.select('CMI_C03').updateMask(C_Mask).toFloat().rename('CC')
         | 
| 56 | 
            +
              fireMasked =  goesImg.select('CMI_C03').updateMask(F_Mask).toFloat().rename('FC')
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              return goesImg.addBands([NBRmasked,cloudMasked, fireMasked,QF_Mask,C_Mask])
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            '''Parameter Array Name Value Bit(s) = Value
         | 
| 61 | 
            +
            Sun Glint           QF1 Surface Reflectance None 6-7 = 00
         | 
| 62 | 
            +
            Low Sun Mask        QF1 Surface Reflectance High 5 = 0
         | 
| 63 | 
            +
            Day/Night           QF1 Surface Reflectance Day 4 =0
         | 
| 64 | 
            +
            Cloud Detection     QF1 Surface Reflectance Confident Clear 2-3 = 00 or Problably Clear 2-3 = 01
         | 
| 65 | 
            +
            Cloud Mask Quality  QF1 Surface Reflectance High or Medium 0-1 = 10 or 11
         | 
| 66 | 
            +
            Snow/Ice            QF2 Surface Reflectance No Snow or Ice 5 = 0
         | 
| 67 | 
            +
            Cloud Shadow        QF2 Surface Reflectance No Cloud Shadow 3 = 0
         | 
| 68 | 
            +
            LandWater           QF2 Surface Reflectance Land, Snow, Arctic, Antarctic or Greenland, Desert 0-2  = 011, 100, 101, 110, 111
         | 
| 69 | 
            +
            Thin Cirrus Flag    QF7 Surface Reflectance No Thin Cirrus 4 = 0
         | 
| 70 | 
            +
            Aerosol Quantity    QF7 Surface Reflectance Climatology, Low or Medium 2-3 = 00, 01 or 10
         | 
| 71 | 
            +
            Adjacent to Cloud   QF7 Surface Reflectance Not Adjacent to Cloud 1 = 0'''
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            def VcalcNBR (VIIRSimg):
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                QF1 = VIIRSimg.select('QF1').int()
         | 
| 76 | 
            +
                QF2 = VIIRSimg.select('QF2').int()
         | 
| 77 | 
            +
                QF7 = VIIRSimg.select('QF7').int()
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                QF_Mask = (QF1.bitwiseAnd(BitMask_3).eq(0)).And\
         | 
| 80 | 
            +
                ((QF2.bitwiseAnd(BitMask_2).eq(4)).Or((QF2.bitwiseAnd(BitMask_1).eq(0)))).And\
         | 
| 81 | 
            +
                (QF2.bitwiseAnd(BitMask_5).eq(0)).rename('QFmask');
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                VIIRSm = VIIRSimg.select(['I2','M11']).updateMask(QF_Mask);
         | 
| 84 | 
            +
                NBR = VIIRSm.normalizedDifference(['I2','M11']).toFloat().rename('NBR')
         | 
| 85 | 
            +
                return VIIRSimg.addBands(NBR).addBands(QF_Mask)#.set('avgNBR', avgNBR)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
            ''' Bit 1: Dilated Cloud
         | 
| 88 | 
            +
                Bit 2: Cirrus (high confidence)
         | 
| 89 | 
            +
                Bit 3: Cloud
         | 
| 90 | 
            +
                Bit 4: Cloud Shadow
         | 
| 91 | 
            +
                Bit 5: Snow
         | 
| 92 | 
            +
                Bit 6: Clear (0: Cloud or Dilated Cloud bits are set, 1: Cloud and Dilated Cloud bits are not set)
         | 
| 93 | 
            +
                Bit 7: Water
         | 
| 94 | 
            +
                Bits 8-9: Cloud Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 95 | 
            +
                Bits 10-11: Cloud Shadow Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 96 | 
            +
                Bits 12-13: Snow/Ice Confidence (0: None, 1: Low, 2: Medium, 3: High)
         | 
| 97 | 
            +
                Bits 14-15: Cirrus Confidence (0: None, 1: Low, 2: Medium, 3: High)'''
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            def LcalcNBR (LSimg):
         | 
| 100 | 
            +
              QApixel = LSimg.select('QA_PIXEL').int()
         | 
| 101 | 
            +
              QF_Mask =(QApixel.bitwiseAnd(BitMask_3).eq(0)).And\
         | 
| 102 | 
            +
                       (QApixel.bitwiseAnd(BitMask_5).eq(0)).And\
         | 
| 103 | 
            +
                       (QApixel.bitwiseAnd(BitMask_7).eq(0)).rename('QFmask');
         | 
| 104 | 
            +
             | 
| 105 | 
            +
              LSmasked = LSimg.select(['SR_B5','SR_B7']).updateMask(QF_Mask);
         | 
| 106 | 
            +
              NBR = LSmasked.normalizedDifference(['SR_B5','SR_B7']).toFloat().rename('NBR')
         | 
| 107 | 
            +
              return LSimg.addBands(NBR).addBands(QF_Mask)#.set('avgNBR', avgNBR)
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            ''' 1    Saturated or defective
         | 
| 110 | 
            +
                2    Dark Area Pixels
         | 
| 111 | 
            +
                3    Cloud Shadows
         | 
| 112 | 
            +
                4    Vegetation
         | 
| 113 | 
            +
                5    Bare Soils
         | 
| 114 | 
            +
                6    Water
         | 
| 115 | 
            +
                7    Clouds Low Probability / Unclassified
         | 
| 116 | 
            +
                8    Clouds Medium Probability
         | 
| 117 | 
            +
                9    Clouds High Probability
         | 
| 118 | 
            +
                10   Cirrus
         | 
| 119 | 
            +
                11   Snow / Ice'''
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            def ScalcNBR (sentImg):
         | 
| 122 | 
            +
              SCL = sentImg.select('SCL');
         | 
| 123 | 
            +
              QF_Mask =(SCL.neq(6)).And\
         | 
| 124 | 
            +
                (SCL.neq(8)).And\
         | 
| 125 | 
            +
                (SCL.neq(9)).And\
         | 
| 126 | 
            +
                (SCL.neq(11))\
         | 
| 127 | 
            +
                .rename('QFmask');
         | 
| 128 | 
            +
              sentMasked = sentImg.select(['B8A','B12']).updateMask(QF_Mask); #B8 is another option- broadband NIR
         | 
| 129 | 
            +
              NBR = sentMasked.normalizedDifference(['B8A','B12']).toFloat().rename('NBR')
         | 
| 130 | 
            +
              return sentImg.addBands(NBR).addBands(QF_Mask).addBands(SCL)#.set('avgNBR', avgNBR)
         | 
    	
        utils/__init__.py
    ADDED
    
    | 
            File without changes
         | 
