Simple Calculations
Learn to perform basic mathematical operations and calculate spectral indices in Earth Engine.
What You’ll Learn
Basic arithmetic operations on images
Calculating common spectral indices
Working with image bands and band math
Understanding Earth Engine expressions
Conditional operations and masking
Prerequisites
Completed first script and image display examples
Understanding of remote sensing concepts
Basic knowledge of spectral indices
Familiarity with Python programming
Basic Mathematical Operations
Earth Engine supports standard mathematical operations:
import ee
# Initialize Earth Engine
ee.Initialize(project='your-project-id')
# Load a Landsat 8 image
image = ee.Image('LANDSAT/LC08/C02/T1_L2/LC08_044034_20140318')
# Apply scaling factors
image = image.select('SR_B.').multiply(0.0000275).add(-0.2)
# Basic arithmetic
red = image.select('SR_B4')
nir = image.select('SR_B5')
# Addition
sum_bands = red.add(nir)
# Subtraction
difference = nir.subtract(red)
# Multiplication
product = red.multiply(nir)
# Division
ratio = nir.divide(red)
Common Spectral Indices
NDVI (Normalized Difference Vegetation Index):
# NDVI = (NIR - Red) / (NIR + Red)
ndvi = image.normalizedDifference(['SR_B5', 'SR_B4']).rename('NDVI')
print("NDVI range: -1 to 1")
print("Values > 0.3 typically indicate vegetation")
NDWI (Normalized Difference Water Index):
# NDWI = (Green - NIR) / (Green + NIR)
ndwi = image.normalizedDifference(['SR_B3', 'SR_B5']).rename('NDWI')
print("NDWI > 0 typically indicates water")
EVI (Enhanced Vegetation Index):
# EVI = 2.5 * ((NIR - Red) / (NIR + 6 * Red - 7.5 * Blue + 1))
evi = image.expression(
'2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))',
{
'NIR': image.select('SR_B5'),
'RED': image.select('SR_B4'),
'BLUE': image.select('SR_B2')
}
).rename('EVI')
Using ee.Image.expression()
For complex calculations, use expressions:
# Custom vegetation index
custom_vi = image.expression(
'(NIR * 2.5 - RED * 1.5) / (NIR + RED + 1)',
{
'NIR': image.select('SR_B5'),
'RED': image.select('SR_B4')
}
).rename('Custom_VI')
# Atmospheric visibility
visibility = image.expression(
'BLUE / RED',
{
'BLUE': image.select('SR_B2'),
'RED': image.select('SR_B4')
}
).rename('Visibility')
Conditional Operations
Create masks and conditional values:
# Create vegetation mask
vegetation_mask = ndvi.gt(0.3)
# Create water mask
water_mask = ndwi.gt(0.2)
# Conditional assignment
land_cover = ee.Image(0) # Start with zeros
land_cover = land_cover.where(water_mask, 1) # Water = 1
land_cover = land_cover.where(vegetation_mask, 2) # Vegetation = 2
# Apply mask to preserve only high vegetation
high_veg_only = ndvi.updateMask(ndvi.gt(0.5))
Statistical Analysis
Calculate statistics over regions:
# Define area of interest
point = ee.Geometry.Point([-122.4, 37.8])
region = point.buffer(1000) # 1km buffer
# Calculate statistics
stats = ndvi.reduceRegion(
reducer=ee.Reducer.mean().combine(
reducer2=ee.Reducer.minMax(),
sharedInputs=True
).combine(
reducer2=ee.Reducer.stdDev(),
sharedInputs=True
),
geometry=region,
scale=30,
maxPixels=1e9
)
# Print results
ndvi_mean = stats.get('NDVI_mean').getInfo()
ndvi_min = stats.get('NDVI_min').getInfo()
ndvi_max = stats.get('NDVI_max').getInfo()
ndvi_std = stats.get('NDVI_stdDev').getInfo()
print(f"NDVI Statistics:")
print(f" Mean: {ndvi_mean:.3f}")
print(f" Range: {ndvi_min:.3f} to {ndvi_max:.3f}")
print(f" Std Dev: {ndvi_std:.3f}")
Complete Example
Here’s a complete script combining all concepts:
1"""
2Basic Example 3: Simple Calculations and Spectral Indices
3=========================================================
4
5This example demonstrates:
6- Basic mathematical operations on images
7- Calculating common spectral indices (NDVI, NDWI, EVI)
8- Working with image bands and band math
9- Understanding Earth Engine data types
10- Creating custom calculations and functions
11
12Prerequisites:
13- Authenticated Earth Engine environment
14- Basic understanding of remote sensing concepts
15- Familiarity with spectral indices
16"""
17
18import ee
19import matplotlib.pyplot as plt
20import pandas as pd
21import numpy as np
22
23def demonstrate_basic_math():
24 """
25 Demonstrate basic mathematical operations on Earth Engine images.
26 """
27 print("🧮 Basic Mathematical Operations")
28 print("-" * 40)
29
30 # Load a Landsat 8 image
31 image = (ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
32 .filterDate('2023-06-01', '2023-09-01')
33 .filterBounds(ee.Geometry.Point([-122.4, 37.8])) # San Francisco
34 .sort('CLOUD_COVER')
35 .first())
36
37 print(f"Working with image: {image.get('LANDSAT_PRODUCT_ID').getInfo()}")
38
39 # Scale factors for Landsat Collection 2
40 def apply_scale_factors(image):
41 """Apply scale factors to Landsat Collection 2 data."""
42 optical_bands = image.select('SR_B.').multiply(0.0000275).add(-0.2)
43 thermal_bands = image.select('ST_B.*').multiply(0.00341802).add(149.0)
44 return image.addBands(optical_bands, None, True).addBands(thermal_bands, None, True)
45
46 # Apply scaling
47 image = apply_scale_factors(image)
48
49 # Basic arithmetic operations
50 print("\n📊 Basic Arithmetic Operations:")
51
52 # Addition
53 red_plus_nir = image.select('SR_B4').add(image.select('SR_B5'))
54 print("✓ Addition: Red + NIR")
55
56 # Subtraction
57 nir_minus_red = image.select('SR_B5').subtract(image.select('SR_B4'))
58 print("✓ Subtraction: NIR - Red")
59
60 # Multiplication
61 red_times_nir = image.select('SR_B4').multiply(image.select('SR_B5'))
62 print("✓ Multiplication: Red × NIR")
63
64 # Division
65 nir_div_red = image.select('SR_B5').divide(image.select('SR_B4'))
66 print("✓ Division: NIR ÷ Red")
67
68 # Constants
69 red_plus_constant = image.select('SR_B4').add(0.1)
70 red_times_constant = image.select('SR_B4').multiply(2.0)
71 print("✓ Operations with constants")
72
73 # Power operations
74 red_squared = image.select('SR_B4').pow(2)
75 red_sqrt = image.select('SR_B4').sqrt()
76 print("✓ Power operations: square and square root")
77
78 return image
79
80def calculate_spectral_indices(image):
81 """
82 Calculate common spectral indices for vegetation and water analysis.
83
84 Args:
85 image: Landsat 8 image (scaled)
86
87 Returns:
88 ee.Image: Image with spectral indices added as bands
89 """
90 print("\n🌱 Calculating Spectral Indices")
91 print("-" * 40)
92
93 # NDVI (Normalized Difference Vegetation Index)
94 # NDVI = (NIR - Red) / (NIR + Red)
95 ndvi = image.normalizedDifference(['SR_B5', 'SR_B4']).rename('NDVI')
96 print("✓ NDVI calculated")
97
98 # NDWI (Normalized Difference Water Index)
99 # NDWI = (Green - NIR) / (Green + NIR)
100 ndwi = image.normalizedDifference(['SR_B3', 'SR_B5']).rename('NDWI')
101 print("✓ NDWI calculated")
102
103 # NDBI (Normalized Difference Built-up Index)
104 # NDBI = (SWIR1 - NIR) / (SWIR1 + NIR)
105 ndbi = image.normalizedDifference(['SR_B6', 'SR_B5']).rename('NDBI')
106 print("✓ NDBI calculated")
107
108 # EVI (Enhanced Vegetation Index)
109 # EVI = 2.5 * ((NIR - Red) / (NIR + 6 * Red - 7.5 * Blue + 1))
110 evi = image.expression(
111 '2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))',
112 {
113 'NIR': image.select('SR_B5'),
114 'RED': image.select('SR_B4'),
115 'BLUE': image.select('SR_B2')
116 }
117 ).rename('EVI')
118 print("✓ EVI calculated")
119
120 # SAVI (Soil Adjusted Vegetation Index)
121 # SAVI = ((NIR - Red) / (NIR + Red + L)) * (1 + L), where L = 0.5
122 savi = image.expression(
123 '((NIR - RED) / (NIR + RED + 0.5)) * (1.5)',
124 {
125 'NIR': image.select('SR_B5'),
126 'RED': image.select('SR_B4')
127 }
128 ).rename('SAVI')
129 print("✓ SAVI calculated")
130
131 # MNDWI (Modified Normalized Difference Water Index)
132 # MNDWI = (Green - SWIR1) / (Green + SWIR1)
133 mndwi = image.normalizedDifference(['SR_B3', 'SR_B6']).rename('MNDWI')
134 print("✓ MNDWI calculated")
135
136 # NBR (Normalized Burn Ratio)
137 # NBR = (NIR - SWIR2) / (NIR + SWIR2)
138 nbr = image.normalizedDifference(['SR_B5', 'SR_B7']).rename('NBR')
139 print("✓ NBR calculated")
140
141 # Add all indices to the image
142 indices_image = image.addBands([ndvi, ndwi, ndbi, evi, savi, mndwi, nbr])
143
144 return indices_image
145
146def create_custom_calculations(image):
147 """
148 Demonstrate custom calculations using ee.Image.expression().
149
150 Args:
151 image: Input Earth Engine image
152
153 Returns:
154 ee.Image: Image with custom calculated bands
155 """
156 print("\n🔬 Custom Calculations with ee.Image.expression()")
157 print("-" * 50)
158
159 # Custom vegetation index combining multiple bands
160 custom_vi = image.expression(
161 '(NIR * 2.5 - RED * 1.5 - BLUE * 0.8) / (NIR + RED + BLUE)',
162 {
163 'NIR': image.select('SR_B5'),
164 'RED': image.select('SR_B4'),
165 'BLUE': image.select('SR_B2')
166 }
167 ).rename('Custom_VI')
168 print("✓ Custom vegetation index")
169
170 # Atmospheric visibility index
171 # Uses the ratio of blue to red for atmospheric clarity
172 visibility_index = image.expression(
173 'BLUE / RED',
174 {
175 'BLUE': image.select('SR_B2'),
176 'RED': image.select('SR_B4')
177 }
178 ).rename('Visibility_Index')
179 print("✓ Atmospheric visibility index")
180
181 # Soil brightness index
182 # Average of visible bands
183 soil_brightness = image.expression(
184 '(BLUE + GREEN + RED) / 3',
185 {
186 'BLUE': image.select('SR_B2'),
187 'GREEN': image.select('SR_B3'),
188 'RED': image.select('SR_B4')
189 }
190 ).rename('Soil_Brightness')
191 print("✓ Soil brightness index")
192
193 # Shadow index (using multiple bands)
194 shadow_index = image.expression(
195 '(BLUE + GREEN) / (NIR + SWIR1)',
196 {
197 'BLUE': image.select('SR_B2'),
198 'GREEN': image.select('SR_B3'),
199 'NIR': image.select('SR_B5'),
200 'SWIR1': image.select('SR_B6')
201 }
202 ).rename('Shadow_Index')
203 print("✓ Shadow index")
204
205 # Add custom calculations
206 custom_image = image.addBands([custom_vi, visibility_index, soil_brightness, shadow_index])
207
208 return custom_image
209
210def analyze_image_statistics(image, region=None):
211 """
212 Calculate comprehensive statistics for image bands.
213
214 Args:
215 image: Earth Engine image
216 region: Optional region for analysis (uses image bounds if None)
217
218 Returns:
219 dict: Dictionary containing statistics
220 """
221 print("\n📈 Statistical Analysis")
222 print("-" * 30)
223
224 if region is None:
225 region = image.geometry()
226
227 # Define bands to analyze
228 spectral_bands = ['SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B6', 'SR_B7']
229 index_bands = ['NDVI', 'NDWI', 'NDBI', 'EVI', 'SAVI']
230
231 all_bands = spectral_bands + index_bands
232
233 # Calculate comprehensive statistics
234 stats = image.select(all_bands).reduceRegion(
235 reducer=ee.Reducer.minMax().combine(
236 reducer2=ee.Reducer.mean(),
237 sharedInputs=True
238 ).combine(
239 reducer2=ee.Reducer.stdDev(),
240 sharedInputs=True
241 ).combine(
242 reducer2=ee.Reducer.percentile([25, 75]),
243 sharedInputs=True
244 ),
245 geometry=region,
246 scale=30,
247 maxPixels=1e9
248 )
249
250 stats_dict = stats.getInfo()
251
252 # Format and display statistics
253 print("Band Statistics Summary:")
254 print("-" * 60)
255
256 for band in all_bands:
257 if f'{band}_mean' in stats_dict:
258 print(f"\n{band}:")
259 print(f" Mean: {stats_dict[f'{band}_mean']:.4f}")
260 print(f" StdDev: {stats_dict[f'{band}_stdDev']:.4f}")
261 print(f" Min: {stats_dict[f'{band}_min']:.4f}")
262 print(f" Max: {stats_dict[f'{band}_max']:.4f}")
263
264 if f'{band}_p25' in stats_dict:
265 print(f" Q1 (25%): {stats_dict[f'{band}_p25']:.4f}")
266 print(f" Q3 (75%): {stats_dict[f'{band}_p75']:.4f}")
267
268 return stats_dict
269
270def demonstrate_conditional_operations(image):
271 """
272 Demonstrate conditional operations and masking.
273
274 Args:
275 image: Input Earth Engine image
276
277 Returns:
278 ee.Image: Image with conditional results
279 """
280 print("\n🎯 Conditional Operations and Masking")
281 print("-" * 40)
282
283 # Create vegetation mask (NDVI > 0.3)
284 vegetation_mask = image.select('NDVI').gt(0.3)
285 print("✓ Vegetation mask created (NDVI > 0.3)")
286
287 # Create water mask (NDWI > 0.2)
288 water_mask = image.select('NDWI').gt(0.2)
289 print("✓ Water mask created (NDWI > 0.2)")
290
291 # Create urban mask (NDBI > 0.1)
292 urban_mask = image.select('NDBI').gt(0.1)
293 print("✓ Urban mask created (NDBI > 0.1)")
294
295 # Combined land cover classification using conditions
296 land_cover = (ee.Image(0) # Start with zeros
297 .where(water_mask, 1) # Water = 1
298 .where(vegetation_mask, 2) # Vegetation = 2
299 .where(urban_mask, 3)) # Urban = 3
300
301 land_cover = land_cover.rename('Land_Cover')
302 print("✓ Simple land cover classification")
303
304 # Conditional value assignment
305 # High vegetation areas get NDVI value, others get 0
306 high_vegetation = image.select('NDVI').where(
307 image.select('NDVI').lt(0.5), 0
308 ).rename('High_Vegetation_Only')
309 print("✓ Conditional value assignment")
310
311 # Multiple conditions using ee.Image.expression()
312 complex_condition = image.expression(
313 '(NDVI > 0.4) && (NDWI < 0.1) && (EVI > 0.3) ? 1 : 0',
314 {
315 'NDVI': image.select('NDVI'),
316 'NDWI': image.select('NDWI'),
317 'EVI': image.select('EVI')
318 }
319 ).rename('Healthy_Vegetation')
320 print("✓ Complex conditional expression")
321
322 # Add conditional results
323 conditional_image = image.addBands([
324 land_cover, high_vegetation, complex_condition,
325 vegetation_mask.rename('Vegetation_Mask'),
326 water_mask.rename('Water_Mask'),
327 urban_mask.rename('Urban_Mask')
328 ])
329
330 return conditional_image
331
332def create_visualization_parameters():
333 """
334 Create visualization parameters for different types of data.
335
336 Returns:
337 dict: Dictionary of visualization parameters
338 """
339 vis_params = {
340 'true_color': {
341 'bands': ['SR_B4', 'SR_B3', 'SR_B2'],
342 'min': 0.0,
343 'max': 0.3,
344 'gamma': 1.2
345 },
346 'false_color': {
347 'bands': ['SR_B5', 'SR_B4', 'SR_B3'],
348 'min': 0.0,
349 'max': 0.3,
350 'gamma': 1.2
351 },
352 'ndvi': {
353 'bands': ['NDVI'],
354 'min': -0.2,
355 'max': 0.8,
356 'palette': ['blue', 'white', 'green']
357 },
358 'ndwi': {
359 'bands': ['NDWI'],
360 'min': -0.3,
361 'max': 0.5,
362 'palette': ['white', 'blue']
363 },
364 'land_cover': {
365 'bands': ['Land_Cover'],
366 'min': 0,
367 'max': 3,
368 'palette': ['gray', 'blue', 'green', 'red']
369 }
370 }
371
372 return vis_params
373
374def main():
375 """
376 Main function demonstrating simple calculations and spectral indices.
377 """
378 # Initialize Earth Engine
379 try:
380 ee.Initialize(project='your-project-id')
381 print("✓ Earth Engine initialized successfully!")
382 except Exception as e:
383 print(f"✗ Error initializing Earth Engine: {e}")
384 return
385
386 print("\n" + "="*70)
387 print("🧮 EARTH ENGINE SIMPLE CALCULATIONS AND SPECTRAL INDICES")
388 print("="*70)
389
390 # Step 1: Basic mathematical operations
391 image = demonstrate_basic_math()
392
393 # Step 2: Calculate spectral indices
394 indices_image = calculate_spectral_indices(image)
395
396 # Step 3: Custom calculations
397 custom_image = create_custom_calculations(indices_image)
398
399 # Step 4: Statistical analysis
400 san_francisco_region = ee.Geometry.Rectangle([-122.5, 37.7, -122.3, 37.9])
401 stats = analyze_image_statistics(custom_image, san_francisco_region)
402
403 # Step 5: Conditional operations
404 final_image = demonstrate_conditional_operations(custom_image)
405
406 # Step 6: Visualization setup
407 print("\n🎨 Visualization Parameters")
408 print("-" * 30)
409 vis_params = create_visualization_parameters()
410
411 for vis_name, params in vis_params.items():
412 print(f"✓ {vis_name}: {params['bands']}")
413
414 # Step 7: Export example
415 print("\n📤 Export Configuration Example")
416 print("-" * 35)
417
418 # Select key bands for export
419 export_image = final_image.select([
420 'SR_B4', 'SR_B3', 'SR_B2', # RGB bands
421 'NDVI', 'NDWI', 'EVI', # Vegetation indices
422 'Land_Cover' # Classification
423 ])
424
425 export_config = {
426 'image': export_image,
427 'description': 'landsat_analysis_with_indices',
428 'folder': 'EarthEngine_Exports',
429 'region': san_francisco_region,
430 'scale': 30,
431 'crs': 'EPSG:4326',
432 'maxPixels': 1e13
433 }
434
435 print("Export parameters configured:")
436 for key, value in export_config.items():
437 if key != 'image':
438 print(f" {key}: {value}")
439
440 # Step 8: Summary of calculations performed
441 print("\n📋 Summary of Calculations Performed")
442 print("-" * 40)
443
444 all_bands = final_image.bandNames().getInfo()
445
446 spectral_bands = [b for b in all_bands if b.startswith('SR_B')]
447 index_bands = [b for b in all_bands if b in ['NDVI', 'NDWI', 'NDBI', 'EVI', 'SAVI', 'MNDWI', 'NBR']]
448 custom_bands = [b for b in all_bands if 'Custom' in b or 'Visibility' in b or 'Soil' in b or 'Shadow' in b]
449 mask_bands = [b for b in all_bands if 'Mask' in b or 'Cover' in b or 'Vegetation' in b]
450
451 print(f"Original spectral bands ({len(spectral_bands)}): {spectral_bands}")
452 print(f"Standard indices ({len(index_bands)}): {index_bands}")
453 print(f"Custom calculations ({len(custom_bands)}): {custom_bands}")
454 print(f"Masks and classifications ({len(mask_bands)}): {mask_bands}")
455 print(f"Total bands: {len(all_bands)}")
456
457 print("\n✅ Simple Calculations Example Completed!")
458 print("\n🎓 Key Concepts Demonstrated:")
459 print("• Basic arithmetic operations on images")
460 print("• Standard spectral index calculations")
461 print("• Custom expressions and formulas")
462 print("• Statistical analysis and summaries")
463 print("• Conditional operations and masking")
464 print("• Land cover classification basics")
465 print("• Visualization parameter setup")
466
467 print("\n📚 Next Steps:")
468 print("• Experiment with different spectral indices")
469 print("• Try custom mathematical expressions")
470 print("• Apply calculations to different sensors")
471 print("• Explore time series calculations")
472 print("• Learn about image collection processing")
473
474if __name__ == "__main__":
475 main()
Visualization Parameters
Visualize your calculated indices:
# NDVI visualization
ndvi_vis = {
'bands': ['NDVI'],
'min': -0.2,
'max': 0.8,
'palette': ['blue', 'white', 'green']
}
# Water index visualization
ndwi_vis = {
'bands': ['NDWI'],
'min': -0.3,
'max': 0.5,
'palette': ['white', 'blue']
}
# Land cover visualization
landcover_vis = {
'bands': ['classification'],
'min': 0,
'max': 2,
'palette': ['gray', 'blue', 'green']
}
Common Applications
Agriculture Monitoring: * NDVI for crop health assessment * EVI for biomass estimation * SAVI for soil-adjusted vegetation monitoring
Water Resources: * NDWI for water body mapping * MNDWI for water extent monitoring * Turbidity indices for water quality
Urban Planning: * NDBI for built-up area detection * Urban heat island analysis * Green space monitoring
Best Practices
Scale Considerations: * Use appropriate scale for your analysis * Consider sensor resolution limits * Match scale to feature size
Data Quality: * Apply cloud masking before calculations * Check for valid data ranges * Handle no-data values appropriately
Computational Efficiency: * Calculate indices only when needed * Use appropriate data types * Optimize expressions for performance
Common Issues
Scale Factor Problems:
# Wrong - using raw DN values
ndvi_wrong = image.normalizedDifference(['SR_B5', 'SR_B4'])
# Correct - apply scale factors first
scaled = image.select('SR_B.').multiply(0.0000275).add(-0.2)
ndvi_correct = scaled.normalizedDifference(['SR_B5', 'SR_B4'])
Invalid Data Handling:
# Mask invalid values
valid_ndvi = ndvi.updateMask(ndvi.gte(-1).And(ndvi.lte(1)))
Memory Issues:
# For large areas, use appropriate scale
stats = ndvi.reduceRegion(
reducer=ee.Reducer.mean(),
geometry=large_region,
scale=100, # Use coarser resolution for large areas
maxPixels=1e9
)
Next Steps
Try calculating different spectral indices
Experiment with custom expressions
Learn about time series calculations
Explore classification applications
Next: Intermediate Examples