UjjwalKGupta commited on
Commit
08b3e9a
1 Parent(s): 4072fc2

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +375 -191
app.py CHANGED
@@ -1,191 +1,375 @@
1
- import os
2
- import ee
3
- import geemap
4
- import json
5
- import geopandas as gpd
6
- import streamlit as st
7
- import pandas as pd
8
- import geojson
9
- from shapely.geometry import Polygon, MultiPolygon, shape, Point
10
- from io import BytesIO
11
-
12
-
13
- # Enable fiona driver
14
- gpd.io.file.fiona.drvsupport.supported_drivers['KML'] = 'rw'
15
-
16
- #Intialize EE library
17
- # Error in EE Authentication
18
- ee_credentials = os.environ.get("EE")
19
- os.makedirs(os.path.expanduser("~/.config/earthengine/"), exist_ok=True)
20
- with open(os.path.expanduser("~/.config/earthengine/credentials"), "w") as f:
21
- f.write(ee_credentials)
22
- ee.Initialize()
23
-
24
- # Functions
25
- def convert_to_2d_geometry(geom): #Handles Polygon Only
26
- if geom is None:
27
- return None
28
- elif geom.has_z:
29
- # Extract exterior coordinates and convert to 2D
30
- exterior_coords = geom.exterior.coords[:] # Get all coordinates of the exterior ring
31
- exterior_coords_2d = [(x, y) for x, y, *_ in exterior_coords] # Keep only the x and y coordinates, ignoring z
32
-
33
- # Handle interior rings (holes) if any
34
- interior_coords_2d = []
35
- for interior in geom.interiors:
36
- interior_coords = interior.coords[:]
37
- interior_coords_2d.append([(x, y) for x, y, *_ in interior_coords])
38
-
39
- # Create a new Polygon with 2D coordinates
40
- return type(geom)(exterior_coords_2d, interior_coords_2d)
41
- else:
42
- return geom
43
-
44
- def validate_KML_file(gdf):
45
- # try:
46
- # gdf = gpd.read_file(BytesIO(uploaded_file.read()), driver='KML')
47
- # except Exception as e:
48
- # ValueError("Input must be a valid KML file.")
49
-
50
- if gdf.empty:
51
- return {
52
- 'corner_points': None,
53
- 'area': None,
54
- 'perimeter': None,
55
- 'is_single_polygon': False}
56
-
57
- polygon_info = {}
58
-
59
- # Check if it's a single polygon or multipolygon
60
- if isinstance(gdf.iloc[0].geometry, Polygon):
61
- polygon_info['is_single_polygon'] = True
62
-
63
- polygon = convert_to_2d_geometry(gdf.geometry.iloc[0])
64
-
65
- # Calculate corner points in GCS projection
66
- polygon_info['corner_points'] = [
67
- (polygon.bounds[0], polygon.bounds[1]),
68
- (polygon.bounds[2], polygon.bounds[1]),
69
- (polygon.bounds[2], polygon.bounds[3]),
70
- (polygon.bounds[0], polygon.bounds[3])
71
- ]
72
-
73
- # Calculate Centroids in GCS projection
74
- polygon_info['centroid'] = polygon.centroid.coords[0]
75
-
76
- # Calculate area and perimeter in EPSG:7761 projection
77
- # It is a local projection defined for Gujarat as per NNRMS
78
- polygon = gdf.to_crs(epsg=7761).geometry.iloc[0]
79
- polygon_info['area'] = polygon.area
80
- polygon_info['perimeter'] = polygon.length
81
-
82
- else:
83
- polygon_info['is_single_polygon'] = False
84
- polygon_info['corner_points'] = None
85
- polygon_info['area'] = None
86
- polygon_info['perimeter'] = None
87
- polygon_info['centroid'] = None
88
- ValueError("Input must be a single Polygon.")
89
-
90
- return polygon_info
91
-
92
- # Calculate NDVI as Normalized Index
93
- def reduce_zonal_ndvi(image, ee_object):
94
- ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
95
- image = image.addBands(ndvi)
96
- image = image.select('NDVI')
97
- reduced = image.reduceRegion(
98
- reducer=ee.Reducer.mean(),
99
- geometry=ee_object.geometry(),
100
- scale=10,
101
- maxPixels=1e12
102
- )
103
- return image.set(reduced)
104
-
105
- # Get Zonal NDVI
106
- def get_zonal_ndvi(collection, geom_ee_object):
107
- reduced_collection = collection.map(lambda image: reduce_zonal_ndvi(image, ee_object=geom_ee_object))
108
- stats_list = reduced_collection.aggregate_array('NDVI').getInfo()
109
- filenames = reduced_collection.aggregate_array('system:index').getInfo()
110
- dates = [f.split("_")[0].split('T')[0] for f in reduced_collection.aggregate_array('system:index').getInfo()]
111
- df = pd.DataFrame({'NDVI': stats_list, 'Date': dates, 'Imagery': filenames})
112
- return df
113
-
114
- # put title in center
115
- st.markdown("""
116
- <style>
117
- h1 {
118
- text-align: center;
119
- }
120
- </style>
121
- """, unsafe_allow_html=True)
122
-
123
- st.title("Mean NDVI Calculator")
124
-
125
- # get the start and end date from the user
126
- col = st.columns(2)
127
- start_date = col[0].date_input("Start Date", value=pd.to_datetime('2021-01-01'))
128
- end_date = col[1].date_input("End Date", value=pd.to_datetime('2021-01-30'))
129
- start_date = start_date.strftime("%Y-%m-%d")
130
- end_date = end_date.strftime("%Y-%m-%d")
131
-
132
- max_cloud_cover = st.number_input("Max Cloud Cover", value=20)
133
-
134
- # Get the geojson file from the user
135
- uploaded_file = st.file_uploader("Upload KML/GeoJSON file", type=["geojson", "kml"])
136
-
137
-
138
-
139
- if uploaded_file is not None:
140
- try:
141
- if uploaded_file.name.endswith("kml"):
142
- gdf = gpd.read_file(BytesIO(uploaded_file.read()), driver='KML')
143
- elif uploaded_file.name.endswith("geojson"):
144
- gdf = gpd.read_file(uploaded_file)
145
- except Exception as e:
146
- st.write('ValueError: "Input must be a valid KML file."')
147
- st.stop()
148
-
149
- # Validate KML File
150
- polygon_info = validate_KML_file(gdf)
151
-
152
- if polygon_info["is_single_polygon"]==True:
153
- st.write("Uploaded KML file has single geometry.")
154
- st.write("It has bounds as {0:.6f}, {1:.6f}, {2:.6f}, and {3:.6f}.".format(
155
- polygon_info['corner_points'][0][0],
156
- polygon_info['corner_points'][0][1],
157
- polygon_info['corner_points'][2][0],
158
- polygon_info['corner_points'][2][1]
159
- ))
160
- st.write("It has centroid at ({0:.6f}, {1:.6f}).".format(polygon_info['centroid'][0], polygon_info['centroid'][1]))
161
- st.write("It has area of {:.2f} meter squared.".format(polygon_info['area']))
162
- st.write("It has perimeter of {:.2f} meters.".format(polygon_info['perimeter']))
163
-
164
- # # Read KML file
165
- # geom_ee_object = ee.FeatureCollection(json.loads(gdf.to_json()))
166
-
167
- # # Add buffer of 100m to ee_object
168
- # buffered_ee_object = geom_ee_object.map(lambda feature: feature.buffer(100))
169
-
170
- # # Filter data based on the date, bounds, cloud coverage and select NIR and Red Band
171
- # 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'])
172
-
173
- # # Get Zonal NDVI based on collection and geometries (Original KML and Buffered KML)
174
- # df_geom = get_zonal_ndvi(collection, geom_ee_object)
175
- # df_buffered_geom = get_zonal_ndvi(collection, buffered_ee_object)
176
-
177
- # # Merge both Zonalstats and create resultant dataframe
178
- # resultant_df = pd.merge(df_geom, df_buffered_geom, on='Date', how='inner')
179
- # resultant_df = resultant_df.rename(columns={'NDVI_x': 'AvgNDVI_Inside', 'NDVI_y': 'Avg_NDVI_Buffer', 'Imagery_x': 'Imagery'})
180
- # resultant_df['Ratio'] = resultant_df['AvgNDVI_Inside'] / resultant_df['Avg_NDVI_Buffer']
181
- # resultant_df.drop(columns=['Imagery_y'], inplace=True)
182
-
183
- # # Re-order the columns of the resultant dataframe
184
- # resultant_df = resultant_df[['Date', 'Imagery', 'AvgNDVI_Inside', 'Avg_NDVI_Buffer', 'Ratio']]
185
-
186
- # st.write(resultant_df)
187
-
188
- else:
189
- st.write('ValueError: "Input must have single polygon geometry"')
190
- st.write(gdf)
191
- st.stop()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import ee
3
+ import geemap
4
+ import json
5
+ import geopandas as gpd
6
+ import streamlit as st
7
+ import pandas as pd
8
+ <<<<<<< HEAD
9
+ from fastkml import kml
10
+ import geojson
11
+ from shapely.geometry import Polygon, MultiPolygon, shape, Point
12
+
13
+ =======
14
+ import geojson
15
+ from shapely.geometry import Polygon, MultiPolygon, shape, Point
16
+ from io import BytesIO
17
+
18
+
19
+ # Enable fiona driver
20
+ gpd.io.file.fiona.drvsupport.supported_drivers['KML'] = 'rw'
21
+
22
+ #Intialize EE library
23
+ # Error in EE Authentication
24
+ >>>>>>> 4072fc2e14e38a014c456666d781a212750773a4
25
+ ee_credentials = os.environ.get("EE")
26
+ os.makedirs(os.path.expanduser("~/.config/earthengine/"), exist_ok=True)
27
+ with open(os.path.expanduser("~/.config/earthengine/credentials"), "w") as f:
28
+ f.write(ee_credentials)
29
+ <<<<<<< HEAD
30
+
31
+ ee.Initialize()
32
+
33
+ def convert_3d_to_2d(geometry):
34
+ """
35
+ Recursively convert any 3D coordinates in a geometry to 2D.
36
+ """
37
+ if geometry.is_empty:
38
+ return geometry
39
+
40
+ if geometry.geom_type == 'Polygon':
41
+ return geojson.Polygon([[(x, y) for x, y, *_ in ring] for ring in geometry.coordinates])
42
+
43
+ elif geometry.geom_type == 'MultiPolygon':
44
+ return geojson.MultiPolygon([
45
+ [[(x, y) for x, y, *_ in ring] for ring in poly]
46
+ for poly in geometry.coordinates
47
+ ])
48
+
49
+ elif geometry.geom_type == 'LineString':
50
+ return geojson.LineString([(x, y) for x, y, *_ in geometry.coordinates])
51
+
52
+ elif geometry.geom_type == 'MultiLineString':
53
+ return geojson.MultiLineString([
54
+ [(x, y) for x, y, *_ in line]
55
+ for line in geometry.coordinates
56
+ ])
57
+
58
+ elif geometry.geom_type == 'Point':
59
+ x, y, *_ = geometry.coordinates
60
+ return geojson.Point((x, y))
61
+
62
+ elif geometry.geom_type == 'MultiPoint':
63
+ return geojson.MultiPoint([(x, y) for x, y, *_ in geometry.coordinates])
64
+
65
+ return geometry # Return unchanged if not a supported geometry type
66
+
67
+ =======
68
+ ee.Initialize()
69
+
70
+ # Functions
71
+ >>>>>>> 4072fc2e14e38a014c456666d781a212750773a4
72
+ def convert_to_2d_geometry(geom): #Handles Polygon Only
73
+ if geom is None:
74
+ return None
75
+ elif geom.has_z:
76
+ # Extract exterior coordinates and convert to 2D
77
+ exterior_coords = geom.exterior.coords[:] # Get all coordinates of the exterior ring
78
+ exterior_coords_2d = [(x, y) for x, y, *_ in exterior_coords] # Keep only the x and y coordinates, ignoring z
79
+
80
+ # Handle interior rings (holes) if any
81
+ interior_coords_2d = []
82
+ for interior in geom.interiors:
83
+ interior_coords = interior.coords[:]
84
+ interior_coords_2d.append([(x, y) for x, y, *_ in interior_coords])
85
+
86
+ # Create a new Polygon with 2D coordinates
87
+ return type(geom)(exterior_coords_2d, interior_coords_2d)
88
+ else:
89
+ return geom
90
+
91
+ <<<<<<< HEAD
92
+ def kml_to_geojson(kml_string):
93
+ k = kml.KML()
94
+ k.from_string(kml_string.encode('utf-8')) # Convert the string to bytes
95
+ features = list(k.features())
96
+
97
+ geojson_features = []
98
+ for feature in features:
99
+ geometry_2d = convert_3d_to_2d(feature.geometry)
100
+ geojson_features.append(geojson.Feature(geometry=geometry_2d))
101
+
102
+ geojson_data = geojson.FeatureCollection(geojson_features)
103
+ return geojson_data
104
+
105
+ # Calculate NDVI as Normalized Index
106
+ def reduce_zonal_ndvi(image, ee_object):
107
+ ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
108
+ image = image.addBands(ndvi)
109
+ image = image.select('NDVI')
110
+ reduced = image.reduceRegion(
111
+ reducer=ee.Reducer.mean(),
112
+ geometry=ee_object.geometry(),
113
+ scale=10,
114
+ maxPixels=1e12
115
+ )
116
+ return image.set(reduced)
117
+
118
+ # Validate KML File for Single Polygon and return polygon information
119
+ def validate_KML_file(kml_file):
120
+ try:
121
+ gdf = gpd.read_file(kml_file)
122
+ except Exception as e:
123
+ ValueError("Input must be a valid KML file.")
124
+ =======
125
+ def validate_KML_file(gdf):
126
+ # try:
127
+ # gdf = gpd.read_file(BytesIO(uploaded_file.read()), driver='KML')
128
+ # except Exception as e:
129
+ # ValueError("Input must be a valid KML file.")
130
+ >>>>>>> 4072fc2e14e38a014c456666d781a212750773a4
131
+
132
+ if gdf.empty:
133
+ return {
134
+ 'corner_points': None,
135
+ 'area': None,
136
+ 'perimeter': None,
137
+ 'is_single_polygon': False}
138
+
139
+ polygon_info = {}
140
+ <<<<<<< HEAD
141
+
142
+ # Check if it's a single polygon or multipolygon
143
+ if isinstance(gdf.iloc[0].geometry, Polygon):
144
+ polygon_info['is_single_polygon'] = True
145
+
146
+ polygon = gdf.geometry.iloc[0]
147
+ =======
148
+
149
+ # Check if it's a single polygon or multipolygon
150
+ if isinstance(gdf.iloc[0].geometry, Polygon):
151
+ polygon_info['is_single_polygon'] = True
152
+
153
+ polygon = convert_to_2d_geometry(gdf.geometry.iloc[0])
154
+ >>>>>>> 4072fc2e14e38a014c456666d781a212750773a4
155
+
156
+ # Calculate corner points in GCS projection
157
+ polygon_info['corner_points'] = [
158
+ (polygon.bounds[0], polygon.bounds[1]),
159
+ (polygon.bounds[2], polygon.bounds[1]),
160
+ (polygon.bounds[2], polygon.bounds[3]),
161
+ (polygon.bounds[0], polygon.bounds[3])
162
+ ]
163
+
164
+ # Calculate Centroids in GCS projection
165
+ polygon_info['centroid'] = polygon.centroid.coords[0]
166
+
167
+ <<<<<<< HEAD
168
+ # Calculate area and perimeter in EPSG:7761 projection
169
+ # It is a local projection defined for Gujarat as per NNRMS
170
+ =======
171
+ # Calculate area and perimeter in EPSG:7761 projection
172
+ # It is a local projection defined for Gujarat as per NNRMS
173
+ >>>>>>> 4072fc2e14e38a014c456666d781a212750773a4
174
+ polygon = gdf.to_crs(epsg=7761).geometry.iloc[0]
175
+ polygon_info['area'] = polygon.area
176
+ polygon_info['perimeter'] = polygon.length
177
+
178
+ else:
179
+ polygon_info['is_single_polygon'] = False
180
+ polygon_info['corner_points'] = None
181
+ polygon_info['area'] = None
182
+ polygon_info['perimeter'] = None
183
+ polygon_info['centroid'] = None
184
+ ValueError("Input must be a single Polygon.")
185
+
186
+ return polygon_info
187
+
188
+ <<<<<<< HEAD
189
+ =======
190
+ # Calculate NDVI as Normalized Index
191
+ def reduce_zonal_ndvi(image, ee_object):
192
+ ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
193
+ image = image.addBands(ndvi)
194
+ image = image.select('NDVI')
195
+ reduced = image.reduceRegion(
196
+ reducer=ee.Reducer.mean(),
197
+ geometry=ee_object.geometry(),
198
+ scale=10,
199
+ maxPixels=1e12
200
+ )
201
+ return image.set(reduced)
202
+
203
+ >>>>>>> 4072fc2e14e38a014c456666d781a212750773a4
204
+ # Get Zonal NDVI
205
+ def get_zonal_ndvi(collection, geom_ee_object):
206
+ reduced_collection = collection.map(lambda image: reduce_zonal_ndvi(image, ee_object=geom_ee_object))
207
+ stats_list = reduced_collection.aggregate_array('NDVI').getInfo()
208
+ filenames = reduced_collection.aggregate_array('system:index').getInfo()
209
+ dates = [f.split("_")[0].split('T')[0] for f in reduced_collection.aggregate_array('system:index').getInfo()]
210
+ df = pd.DataFrame({'NDVI': stats_list, 'Date': dates, 'Imagery': filenames})
211
+ return df
212
+
213
+ <<<<<<< HEAD
214
+ def geojson_to_ee(geojson_data):
215
+ ee_object = ee.FeatureCollection(geojson_data)
216
+ return ee_object
217
+
218
+ def kml_to_gdf(kml_file):
219
+ try:
220
+ gdf = gpd.read_file(kml_file)
221
+ for i in range(len(gdf)):
222
+ geom = gdf.iloc[i].geometry
223
+ new_geom = convert_to_2d_geometry(geom)
224
+ gdf.loc[i, 'geometry'] = new_geom
225
+ print(gdf.iloc[i].geometry)
226
+ print(f"KML file '{kml_file}' successfully read")
227
+ except Exception as e:
228
+ print(f"Error: {e}")
229
+ return gdf
230
+
231
+ =======
232
+ >>>>>>> 4072fc2e14e38a014c456666d781a212750773a4
233
+ # put title in center
234
+ st.markdown("""
235
+ <style>
236
+ h1 {
237
+ text-align: center;
238
+ }
239
+ </style>
240
+ """, unsafe_allow_html=True)
241
+
242
+ st.title("Mean NDVI Calculator")
243
+
244
+ # get the start and end date from the user
245
+ col = st.columns(2)
246
+ start_date = col[0].date_input("Start Date", value=pd.to_datetime('2021-01-01'))
247
+ end_date = col[1].date_input("End Date", value=pd.to_datetime('2021-01-30'))
248
+ start_date = start_date.strftime("%Y-%m-%d")
249
+ end_date = end_date.strftime("%Y-%m-%d")
250
+
251
+ max_cloud_cover = st.number_input("Max Cloud Cover", value=20)
252
+
253
+ # Get the geojson file from the user
254
+ uploaded_file = st.file_uploader("Upload KML/GeoJSON file", type=["geojson", "kml"])
255
+
256
+ <<<<<<< HEAD
257
+ # Read the KML file
258
+ if uploaded_file is None:
259
+ file_name = "Bhankhara_Df_11_he_5_2020-21.geojson"
260
+ st.write(f"Using default file: {file_name}")
261
+ data = gpd.read_file(file_name)
262
+ with open(file_name) as f:
263
+ str_data = f.read()
264
+ else:
265
+ st.write(f"Using uploaded file: {uploaded_file.name}")
266
+ file_name = uploaded_file.name
267
+ bytes_data = uploaded_file.getvalue()
268
+ str_data = bytes_data.decode("utf-8")
269
+
270
+
271
+ if file_name.endswith(".geojson"):
272
+ geojson_data = json.loads(str_data)
273
+ elif file_name.endswith(".kml"):
274
+ geojson_data = json.loads(kml_to_gdf(str_data).to_json())
275
+
276
+ # Read Geojson File
277
+ ee_object = geojson_to_ee(geojson_data)
278
+
279
+ # Filter data based on the date, bounds, cloud coverage and select NIR and Red Band
280
+ collection = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterBounds(ee_object).filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', max_cloud_cover)).filter(ee.Filter.date(start_date, end_date)).select(['B4', 'B8'])
281
+
282
+ polygon_info = validate_KML_file(str_data)
283
+
284
+ if polygon_info['is_single_polygon']:
285
+ # Read KML file
286
+ geom_ee_object = ee.FeatureCollection(geojson_data)
287
+
288
+ # Add buffer of 100m to ee_object
289
+ buffered_ee_object = geom_ee_object.map(lambda feature: feature.buffer(100))
290
+
291
+ # Filter data based on the date, bounds, cloud coverage and select NIR and Red Band
292
+ collection = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED").filterBounds(geom_ee_object).filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20)).filter(ee.Filter.date('2022-01-01', '2023-01-01')).select(['B4', 'B8'])
293
+
294
+ # Get Zonal NDVI based on collection and geometries (Original KML and Buffered KML)
295
+ df_geom = get_zonal_ndvi(collection, geom_ee_object)
296
+ df_buffered_geom = get_zonal_ndvi(collection, buffered_ee_object)
297
+
298
+ # Merge both Zonalstats and create resultant dataframe
299
+ resultant_df = pd.merge(df_geom, df_buffered_geom, on='Date', how='inner')
300
+ resultant_df = resultant_df.rename(columns={'NDVI_x': 'AvgNDVI_Inside', 'NDVI_y': 'Avg_NDVI_Buffer'})
301
+ resultant_df['Ratio'] = resultant_df['AvgNDVI_Inside'] / resultant_df['Avg_NDVI_Buffer']
302
+ resultant_df.drop(columns=['Imagery_y'], inplace=True)
303
+
304
+ # Re-order the columns of the resultant dataframe
305
+ resultant_df = resultant_df[['Date', 'Imagery_x', 'AvgNDVI_Inside', 'Avg_NDVI_Buffer', 'Ratio']]
306
+
307
+ # Map = geemap.Map(center=(polygon_info['centroid'][1],polygon_info['centroid'][0]) , zoom=12)
308
+ # Map.addLayer(geom_ee_object, {}, 'Layer1')
309
+ # Map.addLayer(buffered_ee_object, {}, 'Layer2')
310
+
311
+ # plot the time series
312
+ st.write("Time Series Plot")
313
+ st.line_chart(resultant_df.set_index('Date'))
314
+
315
+ #st.write(f"Overall Mean NDVI: {resultant_df['Mean NDVI'].mean():.2f}")
316
+
317
+ else:
318
+ print("Input must be a single Polygon.")
319
+ =======
320
+
321
+
322
+ if uploaded_file is not None:
323
+ try:
324
+ if uploaded_file.name.endswith("kml"):
325
+ gdf = gpd.read_file(BytesIO(uploaded_file.read()), driver='KML')
326
+ elif uploaded_file.name.endswith("geojson"):
327
+ gdf = gpd.read_file(uploaded_file)
328
+ except Exception as e:
329
+ st.write('ValueError: "Input must be a valid KML file."')
330
+ st.stop()
331
+
332
+ # Validate KML File
333
+ polygon_info = validate_KML_file(gdf)
334
+
335
+ if polygon_info["is_single_polygon"]==True:
336
+ st.write("Uploaded KML file has single geometry.")
337
+ st.write("It has bounds as {0:.6f}, {1:.6f}, {2:.6f}, and {3:.6f}.".format(
338
+ polygon_info['corner_points'][0][0],
339
+ polygon_info['corner_points'][0][1],
340
+ polygon_info['corner_points'][2][0],
341
+ polygon_info['corner_points'][2][1]
342
+ ))
343
+ st.write("It has centroid at ({0:.6f}, {1:.6f}).".format(polygon_info['centroid'][0], polygon_info['centroid'][1]))
344
+ st.write("It has area of {:.2f} meter squared.".format(polygon_info['area']))
345
+ st.write("It has perimeter of {:.2f} meters.".format(polygon_info['perimeter']))
346
+
347
+ # # Read KML file
348
+ # geom_ee_object = ee.FeatureCollection(json.loads(gdf.to_json()))
349
+
350
+ # # Add buffer of 100m to ee_object
351
+ # buffered_ee_object = geom_ee_object.map(lambda feature: feature.buffer(100))
352
+
353
+ # # Filter data based on the date, bounds, cloud coverage and select NIR and Red Band
354
+ # 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'])
355
+
356
+ # # Get Zonal NDVI based on collection and geometries (Original KML and Buffered KML)
357
+ # df_geom = get_zonal_ndvi(collection, geom_ee_object)
358
+ # df_buffered_geom = get_zonal_ndvi(collection, buffered_ee_object)
359
+
360
+ # # Merge both Zonalstats and create resultant dataframe
361
+ # resultant_df = pd.merge(df_geom, df_buffered_geom, on='Date', how='inner')
362
+ # resultant_df = resultant_df.rename(columns={'NDVI_x': 'AvgNDVI_Inside', 'NDVI_y': 'Avg_NDVI_Buffer', 'Imagery_x': 'Imagery'})
363
+ # resultant_df['Ratio'] = resultant_df['AvgNDVI_Inside'] / resultant_df['Avg_NDVI_Buffer']
364
+ # resultant_df.drop(columns=['Imagery_y'], inplace=True)
365
+
366
+ # # Re-order the columns of the resultant dataframe
367
+ # resultant_df = resultant_df[['Date', 'Imagery', 'AvgNDVI_Inside', 'Avg_NDVI_Buffer', 'Ratio']]
368
+
369
+ # st.write(resultant_df)
370
+
371
+ else:
372
+ st.write('ValueError: "Input must have single polygon geometry"')
373
+ st.write(gdf)
374
+ st.stop()
375
+ >>>>>>> 4072fc2e14e38a014c456666d781a212750773a4