deepgee 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
deepgee/__init__.py ADDED
@@ -0,0 +1,36 @@
1
+ """
2
+ DeepGEE: Earth Observation with Google Earth Engine and Deep Learning
3
+ =====================================================================
4
+
5
+ A Python package for integrating Google Earth Engine with deep learning
6
+ for advanced Earth observation analysis.
7
+
8
+ Features:
9
+ - GEE authentication and initialization
10
+ - Data download using geemap
11
+ - Spectral indices calculation
12
+ - Deep learning model training
13
+ - Land cover classification
14
+ - Change detection
15
+ - And more...
16
+ """
17
+
18
+ __version__ = "0.1.0"
19
+ __author__ = "Pulakesh Pradhan"
20
+
21
+ from .auth import authenticate_gee, initialize_gee
22
+ from .data import GEEDataDownloader, SpectralIndices
23
+ from .models import LandCoverClassifier, ChangeDetector
24
+ from .utils import load_geotiff, save_geotiff, calculate_area_stats
25
+
26
+ __all__ = [
27
+ 'authenticate_gee',
28
+ 'initialize_gee',
29
+ 'GEEDataDownloader',
30
+ 'SpectralIndices',
31
+ 'LandCoverClassifier',
32
+ 'ChangeDetector',
33
+ 'load_geotiff',
34
+ 'save_geotiff',
35
+ 'calculate_area_stats',
36
+ ]
deepgee/auth.py ADDED
@@ -0,0 +1,154 @@
1
+ """
2
+ Authentication module for Google Earth Engine
3
+ """
4
+
5
+ import ee
6
+ import os
7
+ from typing import Optional
8
+
9
+
10
+ def authenticate_gee(auth_mode: str = 'notebook') -> None:
11
+ """
12
+ Authenticate with Google Earth Engine.
13
+
14
+ Parameters:
15
+ -----------
16
+ auth_mode : str, optional
17
+ Authentication mode: 'notebook', 'gcloud', or 'service_account'
18
+ Default is 'notebook'
19
+
20
+ Examples:
21
+ ---------
22
+ >>> import deepgee
23
+ >>> deepgee.authenticate_gee()
24
+ >>> deepgee.initialize_gee(project='your-project-id')
25
+ """
26
+ try:
27
+ if auth_mode == 'notebook':
28
+ ee.Authenticate()
29
+ print("✓ GEE authentication successful!")
30
+ elif auth_mode == 'gcloud':
31
+ ee.Authenticate(auth_mode='gcloud')
32
+ print("✓ GEE authentication successful via gcloud!")
33
+ elif auth_mode == 'service_account':
34
+ print("For service account, use initialize_gee() with service_account parameter")
35
+ else:
36
+ raise ValueError(f"Unknown auth_mode: {auth_mode}")
37
+ except Exception as e:
38
+ print(f"✗ Authentication failed: {e}")
39
+ raise
40
+
41
+
42
+ def initialize_gee(
43
+ project: Optional[str] = None,
44
+ service_account: Optional[str] = None,
45
+ key_file: Optional[str] = None,
46
+ opt_url: Optional[str] = None
47
+ ) -> None:
48
+ """
49
+ Initialize Google Earth Engine.
50
+
51
+ Parameters:
52
+ -----------
53
+ project : str, optional
54
+ GEE project ID (required for most operations)
55
+ service_account : str, optional
56
+ Service account email for authentication
57
+ key_file : str, optional
58
+ Path to service account key file (JSON)
59
+ opt_url : str, optional
60
+ Optional URL for high-volume endpoint
61
+
62
+ Examples:
63
+ ---------
64
+ # Standard initialization
65
+ >>> import deepgee
66
+ >>> deepgee.initialize_gee(project='your-project-id')
67
+
68
+ # Service account initialization
69
+ >>> deepgee.initialize_gee(
70
+ ... project='your-project-id',
71
+ ... service_account='your-sa@project.iam.gserviceaccount.com',
72
+ ... key_file='path/to/key.json'
73
+ ... )
74
+ """
75
+ try:
76
+ if service_account and key_file:
77
+ # Service account authentication
78
+ credentials = ee.ServiceAccountCredentials(service_account, key_file)
79
+ ee.Initialize(credentials, project=project, opt_url=opt_url)
80
+ print(f"✓ GEE initialized with service account: {service_account}")
81
+ elif project:
82
+ # Standard authentication
83
+ ee.Initialize(project=project, opt_url=opt_url)
84
+ print(f"✓ GEE initialized with project: {project}")
85
+ else:
86
+ # Try to initialize without project (legacy)
87
+ ee.Initialize(opt_url=opt_url)
88
+ print("✓ GEE initialized (no project specified)")
89
+ except Exception as e:
90
+ print(f"✗ Initialization failed: {e}")
91
+ print("\nTroubleshooting:")
92
+ print("1. Run: deepgee.authenticate_gee()")
93
+ print("2. Ensure you have a valid GEE project")
94
+ print("3. Check your internet connection")
95
+ raise
96
+
97
+
98
+ def check_gee_status() -> dict:
99
+ """
100
+ Check if GEE is properly initialized.
101
+
102
+ Returns:
103
+ --------
104
+ dict : Status information
105
+
106
+ Examples:
107
+ ---------
108
+ >>> import deepgee
109
+ >>> status = deepgee.auth.check_gee_status()
110
+ >>> print(status)
111
+ """
112
+ try:
113
+ # Try a simple operation
114
+ test_image = ee.Image('LANDSAT/LC08/C02/T1_L2/LC08_044034_20140318')
115
+ info = test_image.getInfo()
116
+
117
+ return {
118
+ 'initialized': True,
119
+ 'status': 'OK',
120
+ 'test_image': info['id'],
121
+ 'message': 'GEE is properly initialized and working'
122
+ }
123
+ except Exception as e:
124
+ return {
125
+ 'initialized': False,
126
+ 'status': 'ERROR',
127
+ 'error': str(e),
128
+ 'message': 'GEE is not initialized or not working properly'
129
+ }
130
+
131
+
132
+ def get_project_info() -> dict:
133
+ """
134
+ Get information about the current GEE project.
135
+
136
+ Returns:
137
+ --------
138
+ dict : Project information
139
+ """
140
+ try:
141
+ # Get project assets
142
+ root_assets = ee.data.getAssetRoots()
143
+
144
+ return {
145
+ 'status': 'OK',
146
+ 'root_assets': root_assets,
147
+ 'message': 'Successfully retrieved project information'
148
+ }
149
+ except Exception as e:
150
+ return {
151
+ 'status': 'ERROR',
152
+ 'error': str(e),
153
+ 'message': 'Failed to retrieve project information'
154
+ }
deepgee/data.py ADDED
@@ -0,0 +1,413 @@
1
+ """
2
+ Data download and processing module using geemap and GEE
3
+ """
4
+
5
+ import ee
6
+ import geemap
7
+ import os
8
+ from typing import Optional, List, Dict, Tuple, Union
9
+ import numpy as np
10
+
11
+
12
+ class SpectralIndices:
13
+ """
14
+ Calculate common spectral indices for remote sensing.
15
+ """
16
+
17
+ @staticmethod
18
+ def ndvi(image: ee.Image, nir: str = 'B5', red: str = 'B4') -> ee.Image:
19
+ """Calculate Normalized Difference Vegetation Index."""
20
+ return image.normalizedDifference([nir, red]).rename('NDVI')
21
+
22
+ @staticmethod
23
+ def evi(image: ee.Image, nir: str = 'B5', red: str = 'B4', blue: str = 'B2') -> ee.Image:
24
+ """Calculate Enhanced Vegetation Index."""
25
+ evi = image.expression(
26
+ '2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))',
27
+ {
28
+ 'NIR': image.select(nir),
29
+ 'RED': image.select(red),
30
+ 'BLUE': image.select(blue)
31
+ }
32
+ ).rename('EVI')
33
+ return evi
34
+
35
+ @staticmethod
36
+ def ndwi(image: ee.Image, green: str = 'B3', nir: str = 'B5') -> ee.Image:
37
+ """Calculate Normalized Difference Water Index."""
38
+ return image.normalizedDifference([green, nir]).rename('NDWI')
39
+
40
+ @staticmethod
41
+ def ndbi(image: ee.Image, swir1: str = 'B6', nir: str = 'B5') -> ee.Image:
42
+ """Calculate Normalized Difference Built-up Index."""
43
+ return image.normalizedDifference([swir1, nir]).rename('NDBI')
44
+
45
+ @staticmethod
46
+ def nbr(image: ee.Image, nir: str = 'B5', swir2: str = 'B7') -> ee.Image:
47
+ """Calculate Normalized Burn Ratio."""
48
+ return image.normalizedDifference([nir, swir2]).rename('NBR')
49
+
50
+ @staticmethod
51
+ def ndmi(image: ee.Image, nir: str = 'B5', swir1: str = 'B6') -> ee.Image:
52
+ """Calculate Normalized Difference Moisture Index."""
53
+ return image.normalizedDifference([nir, swir1]).rename('NDMI')
54
+
55
+ @staticmethod
56
+ def ndbai(image: ee.Image, swir1: str = 'B6', swir2: str = 'B7') -> ee.Image:
57
+ """Calculate Normalized Difference Bareness Index."""
58
+ return image.normalizedDifference([swir1, swir2]).rename('NDBaI')
59
+
60
+ @staticmethod
61
+ def add_all_indices(image: ee.Image, sensor: str = 'landsat8') -> ee.Image:
62
+ """
63
+ Add all spectral indices to an image.
64
+
65
+ Parameters:
66
+ -----------
67
+ image : ee.Image
68
+ Input image
69
+ sensor : str
70
+ Sensor type: 'landsat8', 'landsat9', 'sentinel2'
71
+
72
+ Returns:
73
+ --------
74
+ ee.Image : Image with all indices added as bands
75
+ """
76
+ if sensor in ['landsat8', 'landsat9']:
77
+ indices = ee.Image([
78
+ SpectralIndices.ndvi(image, 'B5', 'B4'),
79
+ SpectralIndices.evi(image, 'B5', 'B4', 'B2'),
80
+ SpectralIndices.ndwi(image, 'B3', 'B5'),
81
+ SpectralIndices.ndbi(image, 'B6', 'B5'),
82
+ SpectralIndices.nbr(image, 'B5', 'B7'),
83
+ SpectralIndices.ndmi(image, 'B5', 'B6'),
84
+ SpectralIndices.ndbai(image, 'B6', 'B7')
85
+ ])
86
+ elif sensor == 'sentinel2':
87
+ indices = ee.Image([
88
+ SpectralIndices.ndvi(image, 'B8', 'B4'),
89
+ SpectralIndices.ndwi(image, 'B3', 'B8'),
90
+ SpectralIndices.ndbi(image, 'B11', 'B8'),
91
+ SpectralIndices.nbr(image, 'B8', 'B12'),
92
+ SpectralIndices.ndmi(image, 'B8', 'B11')
93
+ ])
94
+ else:
95
+ raise ValueError(f"Unknown sensor: {sensor}")
96
+
97
+ return image.addBands(indices)
98
+
99
+
100
+ class GEEDataDownloader:
101
+ """
102
+ Download and process data from Google Earth Engine using geemap.
103
+ """
104
+
105
+ def __init__(self, project: Optional[str] = None):
106
+ """
107
+ Initialize the data downloader.
108
+
109
+ Parameters:
110
+ -----------
111
+ project : str, optional
112
+ GEE project ID
113
+ """
114
+ self.project = project
115
+ if not self._check_ee_initialized():
116
+ print("Warning: GEE not initialized. Call deepgee.initialize_gee() first.")
117
+
118
+ @staticmethod
119
+ def _check_ee_initialized() -> bool:
120
+ """Check if Earth Engine is initialized."""
121
+ try:
122
+ ee.Image(0).getInfo()
123
+ return True
124
+ except:
125
+ return False
126
+
127
+ @staticmethod
128
+ def cloud_mask_landsat89(image: ee.Image) -> ee.Image:
129
+ """
130
+ Apply cloud mask to Landsat 8/9 Collection 2 Level 2 images.
131
+
132
+ Parameters:
133
+ -----------
134
+ image : ee.Image
135
+ Landsat 8 or 9 image
136
+
137
+ Returns:
138
+ --------
139
+ ee.Image : Cloud-masked image
140
+ """
141
+ qa = image.select('QA_PIXEL')
142
+ dilated = 1 << 1
143
+ cirrus = 1 << 2
144
+ cloud = 1 << 3
145
+ shadow = 1 << 4
146
+
147
+ mask = qa.bitwiseAnd(dilated).eq(0) \
148
+ .And(qa.bitwiseAnd(cirrus).eq(0)) \
149
+ .And(qa.bitwiseAnd(cloud).eq(0)) \
150
+ .And(qa.bitwiseAnd(shadow).eq(0))
151
+
152
+ return image.select(['SR_B.*'], ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7']) \
153
+ .updateMask(mask) \
154
+ .multiply(0.0000275) \
155
+ .add(-0.2)
156
+
157
+ @staticmethod
158
+ def cloud_mask_sentinel2(image: ee.Image) -> ee.Image:
159
+ """
160
+ Apply cloud mask to Sentinel-2 images.
161
+
162
+ Parameters:
163
+ -----------
164
+ image : ee.Image
165
+ Sentinel-2 image
166
+
167
+ Returns:
168
+ --------
169
+ ee.Image : Cloud-masked image
170
+ """
171
+ qa = image.select('QA60')
172
+ cloud_bit_mask = 1 << 10
173
+ cirrus_bit_mask = 1 << 11
174
+
175
+ mask = qa.bitwiseAnd(cloud_bit_mask).eq(0) \
176
+ .And(qa.bitwiseAnd(cirrus_bit_mask).eq(0))
177
+
178
+ return image.updateMask(mask) \
179
+ .divide(10000) \
180
+ .select(['B2', 'B3', 'B4', 'B8', 'B11', 'B12']) \
181
+ .rename(['BLUE', 'GREEN', 'RED', 'NIR', 'SWIR1', 'SWIR2'])
182
+
183
+ def create_composite(
184
+ self,
185
+ roi: Union[ee.Geometry, List[float]],
186
+ start_date: str,
187
+ end_date: str,
188
+ sensor: str = 'landsat8',
189
+ add_indices: bool = True,
190
+ add_elevation: bool = True
191
+ ) -> ee.Image:
192
+ """
193
+ Create a cloud-free composite image.
194
+
195
+ Parameters:
196
+ -----------
197
+ roi : ee.Geometry or list
198
+ Region of interest (geometry or [lon_min, lat_min, lon_max, lat_max])
199
+ start_date : str
200
+ Start date (YYYY-MM-DD)
201
+ end_date : str
202
+ End date (YYYY-MM-DD)
203
+ sensor : str
204
+ Sensor: 'landsat8', 'landsat9', 'sentinel2'
205
+ add_indices : bool
206
+ Add spectral indices
207
+ add_elevation : bool
208
+ Add elevation from SRTM
209
+
210
+ Returns:
211
+ --------
212
+ ee.Image : Composite image
213
+
214
+ Examples:
215
+ ---------
216
+ >>> downloader = GEEDataDownloader()
217
+ >>> roi = [85.0, 20.0, 87.0, 22.0]
218
+ >>> composite = downloader.create_composite(
219
+ ... roi, '2023-01-01', '2023-12-31', sensor='landsat8'
220
+ ... )
221
+ """
222
+ # Convert ROI to geometry if needed
223
+ if isinstance(roi, list):
224
+ roi = ee.Geometry.Rectangle(roi)
225
+
226
+ # Select collection and cloud mask function
227
+ if sensor == 'landsat8':
228
+ collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
229
+ cloud_mask = self.cloud_mask_landsat89
230
+ elif sensor == 'landsat9':
231
+ collection = ee.ImageCollection('LANDSAT/LC09/C02/T1_L2')
232
+ cloud_mask = self.cloud_mask_landsat89
233
+ elif sensor == 'sentinel2':
234
+ collection = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
235
+ cloud_mask = self.cloud_mask_sentinel2
236
+ else:
237
+ raise ValueError(f"Unknown sensor: {sensor}")
238
+
239
+ # Create composite
240
+ composite = collection \
241
+ .filterBounds(roi) \
242
+ .filterDate(start_date, end_date) \
243
+ .map(cloud_mask) \
244
+ .median() \
245
+ .clip(roi)
246
+
247
+ # Add spectral indices
248
+ if add_indices:
249
+ composite = SpectralIndices.add_all_indices(composite, sensor)
250
+
251
+ # Add elevation
252
+ if add_elevation:
253
+ srtm = ee.Image('USGS/SRTMGL1_003').select('elevation')
254
+ composite = composite.addBands(srtm.clip(roi))
255
+
256
+ return composite
257
+
258
+ def download_image(
259
+ self,
260
+ image: ee.Image,
261
+ output_path: str,
262
+ roi: Optional[Union[ee.Geometry, List[float]]] = None,
263
+ scale: int = 30,
264
+ crs: str = 'EPSG:4326'
265
+ ) -> str:
266
+ """
267
+ Download an Earth Engine image to local file using geemap.
268
+
269
+ Parameters:
270
+ -----------
271
+ image : ee.Image
272
+ Image to download
273
+ output_path : str
274
+ Output file path (GeoTIFF)
275
+ roi : ee.Geometry or list, optional
276
+ Region of interest
277
+ scale : int
278
+ Resolution in meters
279
+ crs : str
280
+ Coordinate reference system
281
+
282
+ Returns:
283
+ --------
284
+ str : Path to downloaded file
285
+
286
+ Examples:
287
+ ---------
288
+ >>> downloader = GEEDataDownloader()
289
+ >>> composite = downloader.create_composite(...)
290
+ >>> downloader.download_image(
291
+ ... composite, 'output.tif', scale=30
292
+ ... )
293
+ """
294
+ try:
295
+ if roi is not None:
296
+ if isinstance(roi, list):
297
+ roi = ee.Geometry.Rectangle(roi)
298
+
299
+ geemap.ee_export_image(
300
+ image,
301
+ filename=output_path,
302
+ scale=scale,
303
+ region=roi,
304
+ crs=crs,
305
+ file_per_band=False
306
+ )
307
+ else:
308
+ geemap.ee_export_image(
309
+ image,
310
+ filename=output_path,
311
+ scale=scale,
312
+ crs=crs,
313
+ file_per_band=False
314
+ )
315
+
316
+ print(f"✓ Image downloaded to: {output_path}")
317
+ return output_path
318
+ except Exception as e:
319
+ print(f"✗ Download failed: {e}")
320
+ raise
321
+
322
+ def extract_training_samples(
323
+ self,
324
+ image: ee.Image,
325
+ samples: ee.FeatureCollection,
326
+ scale: int = 30,
327
+ output_path: Optional[str] = None
328
+ ) -> Union[ee.FeatureCollection, str]:
329
+ """
330
+ Extract training samples from an image.
331
+
332
+ Parameters:
333
+ -----------
334
+ image : ee.Image
335
+ Image to sample
336
+ samples : ee.FeatureCollection
337
+ Training sample polygons/points
338
+ scale : int
339
+ Sampling resolution
340
+ output_path : str, optional
341
+ If provided, save to CSV
342
+
343
+ Returns:
344
+ --------
345
+ ee.FeatureCollection or str : Extracted samples or path to CSV
346
+
347
+ Examples:
348
+ ---------
349
+ >>> samples = ee.FeatureCollection([...])
350
+ >>> extracted = downloader.extract_training_samples(
351
+ ... composite, samples, scale=30, output_path='samples.csv'
352
+ ... )
353
+ """
354
+ extracted = image.sampleRegions(
355
+ collection=samples,
356
+ scale=scale,
357
+ geometries=True
358
+ )
359
+
360
+ if output_path:
361
+ geemap.ee_export_vector(extracted, output_path)
362
+ print(f"✓ Samples exported to: {output_path}")
363
+ return output_path
364
+ else:
365
+ return extracted
366
+
367
+ def visualize_map(
368
+ self,
369
+ image: ee.Image,
370
+ vis_params: Optional[Dict] = None,
371
+ name: str = 'Image',
372
+ center: Optional[List[float]] = None,
373
+ zoom: int = 10
374
+ ) -> geemap.Map:
375
+ """
376
+ Create an interactive map with the image.
377
+
378
+ Parameters:
379
+ -----------
380
+ image : ee.Image
381
+ Image to visualize
382
+ vis_params : dict, optional
383
+ Visualization parameters
384
+ name : str
385
+ Layer name
386
+ center : list, optional
387
+ Map center [lon, lat]
388
+ zoom : int
389
+ Zoom level
390
+
391
+ Returns:
392
+ --------
393
+ geemap.Map : Interactive map
394
+
395
+ Examples:
396
+ ---------
397
+ >>> Map = downloader.visualize_map(
398
+ ... composite,
399
+ ... vis_params={'min': 0, 'max': 0.3, 'bands': ['B5', 'B4', 'B3']},
400
+ ... name='False Color'
401
+ ... )
402
+ >>> Map
403
+ """
404
+ Map = geemap.Map()
405
+
406
+ if center:
407
+ Map.setCenter(center[0], center[1], zoom)
408
+
409
+ if vis_params is None:
410
+ vis_params = {}
411
+
412
+ Map.addLayer(image, vis_params, name)
413
+ return Map