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 +36 -0
- deepgee/auth.py +154 -0
- deepgee/data.py +413 -0
- deepgee/models.py +491 -0
- deepgee/utils.py +401 -0
- deepgee-0.1.0.dist-info/METADATA +325 -0
- deepgee-0.1.0.dist-info/RECORD +10 -0
- deepgee-0.1.0.dist-info/WHEEL +5 -0
- deepgee-0.1.0.dist-info/licenses/LICENSE +21 -0
- deepgee-0.1.0.dist-info/top_level.txt +1 -0
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
|