Spaces:
Sleeping
Sleeping
File size: 7,101 Bytes
79b3dbd 4072fc2 79b3dbd 4072fc2 79b3dbd 4072fc2 79b3dbd 4072fc2 79b3dbd 4072fc2 79b3dbd 4072fc2 79b3dbd 4072fc2 79b3dbd 4072fc2 79b3dbd 4072fc2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
import os
import ee
import geemap
import json
import geopandas as gpd
import streamlit as st
import pandas as pd
import geojson
from shapely.geometry import Polygon, MultiPolygon, shape, Point
from io import BytesIO
# Enable fiona driver
gpd.io.file.fiona.drvsupport.supported_drivers['KML'] = 'rw'
#Intialize EE library
# Error in EE Authentication
ee_credentials = os.environ.get("EE")
os.makedirs(os.path.expanduser("~/.config/earthengine/"), exist_ok=True)
with open(os.path.expanduser("~/.config/earthengine/credentials"), "w") as f:
f.write(ee_credentials)
ee.Initialize()
# Functions
def convert_to_2d_geometry(geom): #Handles Polygon Only
if geom is None:
return None
elif geom.has_z:
# Extract exterior coordinates and convert to 2D
exterior_coords = geom.exterior.coords[:] # Get all coordinates of the exterior ring
exterior_coords_2d = [(x, y) for x, y, *_ in exterior_coords] # Keep only the x and y coordinates, ignoring z
# Handle interior rings (holes) if any
interior_coords_2d = []
for interior in geom.interiors:
interior_coords = interior.coords[:]
interior_coords_2d.append([(x, y) for x, y, *_ in interior_coords])
# Create a new Polygon with 2D coordinates
return type(geom)(exterior_coords_2d, interior_coords_2d)
else:
return geom
def validate_KML_file(gdf):
# try:
# gdf = gpd.read_file(BytesIO(uploaded_file.read()), driver='KML')
# except Exception as e:
# ValueError("Input must be a valid KML file.")
if gdf.empty:
return {
'corner_points': None,
'area': None,
'perimeter': None,
'is_single_polygon': False}
polygon_info = {}
# Check if it's a single polygon or multipolygon
if isinstance(gdf.iloc[0].geometry, Polygon):
polygon_info['is_single_polygon'] = True
polygon = convert_to_2d_geometry(gdf.geometry.iloc[0])
# Calculate corner points in GCS projection
polygon_info['corner_points'] = [
(polygon.bounds[0], polygon.bounds[1]),
(polygon.bounds[2], polygon.bounds[1]),
(polygon.bounds[2], polygon.bounds[3]),
(polygon.bounds[0], polygon.bounds[3])
]
# Calculate Centroids in GCS projection
polygon_info['centroid'] = polygon.centroid.coords[0]
# Calculate area and perimeter in EPSG:7761 projection
# It is a local projection defined for Gujarat as per NNRMS
polygon = gdf.to_crs(epsg=7761).geometry.iloc[0]
polygon_info['area'] = polygon.area
polygon_info['perimeter'] = polygon.length
else:
polygon_info['is_single_polygon'] = False
polygon_info['corner_points'] = None
polygon_info['area'] = None
polygon_info['perimeter'] = None
polygon_info['centroid'] = None
ValueError("Input must be a single Polygon.")
return polygon_info
# Calculate NDVI as Normalized Index
def reduce_zonal_ndvi(image, ee_object):
ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
image = image.addBands(ndvi)
image = image.select('NDVI')
reduced = image.reduceRegion(
reducer=ee.Reducer.mean(),
geometry=ee_object.geometry(),
scale=10,
maxPixels=1e12
)
return image.set(reduced)
# Get Zonal NDVI
def get_zonal_ndvi(collection, geom_ee_object):
reduced_collection = collection.map(lambda image: reduce_zonal_ndvi(image, ee_object=geom_ee_object))
stats_list = reduced_collection.aggregate_array('NDVI').getInfo()
filenames = reduced_collection.aggregate_array('system:index').getInfo()
dates = [f.split("_")[0].split('T')[0] for f in reduced_collection.aggregate_array('system:index').getInfo()]
df = pd.DataFrame({'NDVI': stats_list, 'Date': dates, 'Imagery': filenames})
return df
# put title in center
st.markdown("""
<style>
h1 {
text-align: center;
}
</style>
""", unsafe_allow_html=True)
st.title("Mean NDVI Calculator")
# get the start and end date from the user
col = st.columns(2)
start_date = col[0].date_input("Start Date", value=pd.to_datetime('2021-01-01'))
end_date = col[1].date_input("End Date", value=pd.to_datetime('2021-01-30'))
start_date = start_date.strftime("%Y-%m-%d")
end_date = end_date.strftime("%Y-%m-%d")
max_cloud_cover = st.number_input("Max Cloud Cover", value=20)
# Get the geojson file from the user
uploaded_file = st.file_uploader("Upload KML/GeoJSON file", type=["geojson", "kml"])
if uploaded_file is not None:
try:
if uploaded_file.name.endswith("kml"):
gdf = gpd.read_file(BytesIO(uploaded_file.read()), driver='KML')
elif uploaded_file.name.endswith("geojson"):
gdf = gpd.read_file(uploaded_file)
except Exception as e:
st.write('ValueError: "Input must be a valid KML file."')
st.stop()
# Validate KML File
polygon_info = validate_KML_file(gdf)
if polygon_info["is_single_polygon"]==True:
st.write("Uploaded KML file has single geometry.")
st.write("It has bounds as {0:.6f}, {1:.6f}, {2:.6f}, and {3:.6f}.".format(
polygon_info['corner_points'][0][0],
polygon_info['corner_points'][0][1],
polygon_info['corner_points'][2][0],
polygon_info['corner_points'][2][1]
))
st.write("It has centroid at ({0:.6f}, {1:.6f}).".format(polygon_info['centroid'][0], polygon_info['centroid'][1]))
st.write("It has area of {:.2f} meter squared.".format(polygon_info['area']))
st.write("It has perimeter of {:.2f} meters.".format(polygon_info['perimeter']))
# # Read KML file
# geom_ee_object = ee.FeatureCollection(json.loads(gdf.to_json()))
# # Add buffer of 100m to ee_object
# buffered_ee_object = geom_ee_object.map(lambda feature: feature.buffer(100))
# # Filter data based on the date, bounds, cloud coverage and select NIR and Red Band
# collection = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', max_cloud_cover)).filter(ee.Filter.date(start_date, end_date)).select(['B4', 'B8'])
# # Get Zonal NDVI based on collection and geometries (Original KML and Buffered KML)
# df_geom = get_zonal_ndvi(collection, geom_ee_object)
# df_buffered_geom = get_zonal_ndvi(collection, buffered_ee_object)
# # Merge both Zonalstats and create resultant dataframe
# resultant_df = pd.merge(df_geom, df_buffered_geom, on='Date', how='inner')
# resultant_df = resultant_df.rename(columns={'NDVI_x': 'AvgNDVI_Inside', 'NDVI_y': 'Avg_NDVI_Buffer', 'Imagery_x': 'Imagery'})
# resultant_df['Ratio'] = resultant_df['AvgNDVI_Inside'] / resultant_df['Avg_NDVI_Buffer']
# resultant_df.drop(columns=['Imagery_y'], inplace=True)
# # Re-order the columns of the resultant dataframe
# resultant_df = resultant_df[['Date', 'Imagery', 'AvgNDVI_Inside', 'Avg_NDVI_Buffer', 'Ratio']]
# st.write(resultant_df)
else:
st.write('ValueError: "Input must have single polygon geometry"')
st.write(gdf)
st.stop() |