Vector Operations
Working with vector data and spatial operations in Earth Engine.
This page documents the vector operations example.
1"""
2Intermediate Example 3: Vector Operations
3=========================================
4
5This example demonstrates:
6- Working with Earth Engine vector data (FeatureCollections)
7- Geometric operations and spatial analysis
8- Zonal statistics and aggregation
9- Vector-raster interactions
10- Feature filtering and manipulation
11- Creating custom geometries
12
13Prerequisites:
14- Understanding of vector data concepts
15- Basic knowledge of spatial analysis
16- Familiarity with Earth Engine data structures
17"""
18
19import ee
20import json
21import pandas as pd
22
23class VectorOperations:
24 """Class for Earth Engine vector operations and spatial analysis."""
25
26 def __init__(self, project_id):
27 """Initialize with Earth Engine project."""
28 self.project_id = project_id
29 self.initialize_ee()
30
31 def initialize_ee(self):
32 """Initialize Earth Engine."""
33 try:
34 ee.Initialize(project=self.project_id)
35 print("✓ Earth Engine initialized successfully!")
36 except Exception as e:
37 print(f"✗ Error initializing Earth Engine: {e}")
38 raise
39
40 def load_vector_datasets(self):
41 """Load various vector datasets from Earth Engine catalog."""
42 print("🗺️ Loading Vector Datasets")
43 print("-" * 35)
44
45 # Administrative boundaries
46 countries = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017')
47 us_states = ee.FeatureCollection('TIGER/2018/States')
48 us_counties = ee.FeatureCollection('TIGER/2018/Counties')
49
50 print(f"✓ Countries loaded: {countries.size().getInfo()} features")
51 print(f"✓ US States loaded: {us_states.size().getInfo()} features")
52 print(f"✓ US Counties loaded: {us_counties.size().getInfo()} features")
53
54 # Protected areas
55 protected_areas = ee.FeatureCollection('WCMC/WDPA/current/polygons')
56 print(f"✓ Protected areas loaded: {protected_areas.size().getInfo()} features")
57
58 # Ecoregions
59 ecoregions = ee.FeatureCollection('RESOLVE/ECOREGIONS/2017')
60 print(f"✓ Ecoregions loaded: {ecoregions.size().getInfo()} features")
61
62 return {
63 'countries': countries,
64 'states': us_states,
65 'counties': us_counties,
66 'protected_areas': protected_areas,
67 'ecoregions': ecoregions
68 }
69
70 def basic_feature_operations(self, datasets):
71 """Demonstrate basic feature operations."""
72 print("\n🔧 Basic Feature Operations")
73 print("-" * 35)
74
75 # Filter features by attribute
76 print("1. Attribute Filtering:")
77 california = datasets['states'].filter(ee.Filter.eq('NAME', 'California'))
78 print(f" California feature: {california.size().getInfo()} feature(s)")
79
80 # Filter by multiple attributes
81 western_states = datasets['states'].filter(
82 ee.Filter.inList('NAME', ['California', 'Oregon', 'Washington'])
83 )
84 print(f" Western states: {western_states.size().getInfo()} features")
85
86 # Filter by numeric property
87 large_counties = datasets['counties'].filter(
88 ee.Filter.gt('ALAND', 1e10) # > 10,000 km²
89 )
90 print(f" Large counties: {large_counties.size().getInfo()} features")
91
92 # Spatial filtering
93 print("\n2. Spatial Filtering:")
94
95 # Features intersecting California
96 ca_counties = datasets['counties'].filterBounds(california)
97 print(f" Counties in California: {ca_counties.size().getInfo()} features")
98
99 # Point in polygon
100 point = ee.Geometry.Point([-122.4, 37.8]) # San Francisco
101 point_county = datasets['counties'].filterBounds(point)
102 county_name = point_county.first().get('NAME').getInfo()
103 print(f" County containing SF: {county_name}")
104
105 return {
106 'california': california,
107 'western_states': western_states,
108 'ca_counties': ca_counties
109 }
110
111 def geometric_operations(self, features):
112 """Demonstrate geometric operations on features."""
113 print("\n📐 Geometric Operations")
114 print("-" * 30)
115
116 california = features['california']
117 ca_counties = features['ca_counties']
118
119 # Area calculations
120 print("1. Area Calculations:")
121
122 def add_area_km2(feature):
123 """Add area in km² to feature."""
124 area_m2 = feature.geometry().area()
125 area_km2 = area_m2.divide(1e6)
126 return feature.set('area_km2', area_km2)
127
128 ca_counties_with_area = ca_counties.map(add_area_km2)
129
130 # Get largest county
131 largest_county = ca_counties_with_area.sort('area_km2', False).first()
132 largest_name = largest_county.get('NAME').getInfo()
133 largest_area = largest_county.get('area_km2').getInfo()
134 print(f" Largest CA county: {largest_name} ({largest_area:.0f} km²)")
135
136 # Centroid calculation
137 print("\n2. Centroid Operations:")
138
139 def add_centroid(feature):
140 """Add centroid coordinates to feature."""
141 centroid = feature.geometry().centroid()
142 coords = centroid.coordinates()
143 return feature.set({
144 'centroid_lon': coords.get(0),
145 'centroid_lat': coords.get(1)
146 })
147
148 counties_with_centroids = ca_counties.map(add_centroid)
149
150 # Buffer operations
151 print("3. Buffer Operations:")
152
153 # Create buffer around California
154 ca_buffer_50km = california.geometry().buffer(50000) # 50 km buffer
155 ca_buffer_100km = california.geometry().buffer(100000) # 100 km buffer
156
157 print(" ✓ Created 50km and 100km buffers around California")
158
159 # Simplification
160 print("\n4. Geometry Simplification:")
161
162 def simplify_geometry(feature):
163 """Simplify feature geometry."""
164 simplified = feature.geometry().simplify(maxError=1000) # 1km tolerance
165 return feature.setGeometry(simplified)
166
167 simplified_counties = ca_counties.map(simplify_geometry)
168 print(" ✓ Simplified county geometries (1km tolerance)")
169
170 return {
171 'counties_with_area': ca_counties_with_area,
172 'counties_with_centroids': counties_with_centroids,
173 'ca_buffer_50km': ca_buffer_50km,
174 'simplified_counties': simplified_counties
175 }
176
177 def spatial_relationships(self, datasets, processed_features):
178 """Demonstrate spatial relationship operations."""
179 print("\n🌐 Spatial Relationships")
180 print("-" * 30)
181
182 california = processed_features['counties_with_area'].first().geometry()
183 ca_counties = processed_features['counties_with_area']
184
185 # Intersection operations
186 print("1. Intersection Operations:")
187
188 # Protected areas in California
189 ca_protected = datasets['protected_areas'].filterBounds(california)
190 print(f" Protected areas in CA: {ca_protected.size().getInfo()} areas")
191
192 # Ecoregions intersecting California
193 ca_ecoregions = datasets['ecoregions'].filterBounds(california)
194 print(f" Ecoregions in CA: {ca_ecoregions.size().getInfo()} regions")
195
196 # Union operations
197 print("\n2. Union Operations:")
198
199 # Create union of all California counties
200 ca_union = ca_counties.geometry().dissolve()
201 print(" ✓ Created union of all CA counties")
202
203 # Difference operations
204 print("\n3. Difference Operations:")
205
206 # Land area (counties minus protected areas)
207 def calculate_unprotected_area(county):
208 """Calculate unprotected area within county."""
209 county_geom = county.geometry()
210
211 # Get protected areas in this county
212 county_protected = ca_protected.filterBounds(county_geom)
213
214 if county_protected.size().gt(0):
215 protected_union = county_protected.geometry().dissolve()
216 unprotected = county_geom.difference(protected_union)
217 unprotected_area = unprotected.area().divide(1e6) # km²
218 else:
219 unprotected_area = county_geom.area().divide(1e6)
220
221 return county.set('unprotected_area_km2', unprotected_area)
222
223 # Apply to a subset for demonstration
224 sample_counties = ca_counties.limit(5)
225 counties_with_unprotected = sample_counties.map(calculate_unprotected_area)
226
227 print(" ✓ Calculated unprotected areas for sample counties")
228
229 # Distance operations
230 print("\n4. Distance Operations:")
231
232 # Create point features from county centroids
233 def create_centroid_feature(county):
234 """Create point feature from county centroid."""
235 centroid = county.geometry().centroid()
236 return ee.Feature(centroid, county.toDictionary())
237
238 county_centroids = ca_counties.map(create_centroid_feature)
239
240 # Distance to coast (using a coastal point)
241 coast_point = ee.Geometry.Point([-122.5, 37.5]) # Pacific coast
242
243 def add_distance_to_coast(feature):
244 """Add distance to coast."""
245 distance = feature.geometry().distance(coast_point)
246 return feature.set('distance_to_coast_m', distance)
247
248 centroids_with_distance = county_centroids.map(add_distance_to_coast)
249
250 print(" ✓ Calculated distances to coast for county centroids")
251
252 return {
253 'ca_protected': ca_protected,
254 'ca_ecoregions': ca_ecoregions,
255 'ca_union': ca_union,
256 'counties_with_unprotected': counties_with_unprotected,
257 'centroids_with_distance': centroids_with_distance
258 }
259
260 def zonal_statistics(self, vector_features):
261 """Demonstrate zonal statistics using raster data."""
262 print("\n📊 Zonal Statistics")
263 print("-" * 25)
264
265 # Load raster datasets
266 elevation = ee.Image('USGS/SRTMGL1_003')
267 population = ee.Image('CIESIN/GPWv411/GPW_Population_Density/gpw_v4_population_density_rev11_2020_30_sec')
268
269 print("Loaded datasets:")
270 print(" ✓ SRTM elevation data")
271 print(" ✓ Population density data")
272
273 # Get California counties for analysis
274 ca_counties = vector_features['counties_with_area']
275
276 # Calculate zonal statistics for elevation
277 print("\n1. Elevation Statistics:")
278
279 def calculate_elevation_stats(feature):
280 """Calculate elevation statistics for a feature."""
281 stats = elevation.reduceRegion(
282 reducer=ee.Reducer.minMax().combine(
283 reducer2=ee.Reducer.mean(),
284 sharedInputs=True
285 ).combine(
286 reducer2=ee.Reducer.stdDev(),
287 sharedInputs=True
288 ),
289 geometry=feature.geometry(),
290 scale=90, # SRTM resolution
291 maxPixels=1e9
292 )
293
294 return feature.set({
295 'elevation_min': stats.get('elevation_min'),
296 'elevation_max': stats.get('elevation_max'),
297 'elevation_mean': stats.get('elevation_mean'),
298 'elevation_stdDev': stats.get('elevation_stdDev')
299 })
300
301 # Apply to sample counties
302 sample_counties = ca_counties.limit(10)
303 counties_with_elevation = sample_counties.map(calculate_elevation_stats)
304
305 print(" ✓ Calculated elevation statistics for sample counties")
306
307 # Calculate population statistics
308 print("\n2. Population Statistics:")
309
310 def calculate_population_stats(feature):
311 """Calculate population statistics for a feature."""
312 # Population density (people per km²)
313 pop_stats = population.select('population_density').reduceRegion(
314 reducer=ee.Reducer.mean().combine(
315 reducer2=ee.Reducer.sum(),
316 sharedInputs=True
317 ),
318 geometry=feature.geometry(),
319 scale=1000,
320 maxPixels=1e9
321 )
322
323 area_km2 = feature.geometry().area().divide(1e6)
324 total_population = pop_stats.get('population_density_sum')
325
326 return feature.set({
327 'pop_density_mean': pop_stats.get('population_density_mean'),
328 'total_population_est': total_population,
329 'area_km2': area_km2
330 })
331
332 counties_with_population = sample_counties.map(calculate_population_stats)
333
334 print(" ✓ Calculated population statistics for sample counties")
335
336 # Vegetation statistics using NDVI
337 print("\n3. Vegetation Statistics (NDVI):")
338
339 # Get recent Landsat image
340 landsat = (ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
341 .filterDate('2023-06-01', '2023-09-01')
342 .filterBounds(ca_counties.geometry())
343 .sort('CLOUD_COVER')
344 .first())
345
346 # Calculate NDVI
347 ndvi = landsat.normalizedDifference(['SR_B5', 'SR_B4']).rename('NDVI')
348
349 def calculate_ndvi_stats(feature):
350 """Calculate NDVI statistics for a feature."""
351 ndvi_stats = ndvi.reduceRegion(
352 reducer=ee.Reducer.mean().combine(
353 reducer2=ee.Reducer.stdDev(),
354 sharedInputs=True
355 ).combine(
356 reducer2=ee.Reducer.percentile([25, 75]),
357 sharedInputs=True
358 ),
359 geometry=feature.geometry(),
360 scale=30,
361 maxPixels=1e9
362 )
363
364 return feature.set({
365 'ndvi_mean': ndvi_stats.get('NDVI_mean'),
366 'ndvi_stdDev': ndvi_stats.get('NDVI_stdDev'),
367 'ndvi_p25': ndvi_stats.get('NDVI_p25'),
368 'ndvi_p75': ndvi_stats.get('NDVI_p75')
369 })
370
371 counties_with_ndvi = sample_counties.map(calculate_ndvi_stats)
372
373 print(" ✓ Calculated NDVI statistics for sample counties")
374
375 return {
376 'elevation_stats': counties_with_elevation,
377 'population_stats': counties_with_population,
378 'ndvi_stats': counties_with_ndvi
379 }
380
381 def create_custom_geometries(self):
382 """Demonstrate creating custom geometries and features."""
383 print("\n🎨 Custom Geometry Creation")
384 print("-" * 35)
385
386 # Create points
387 print("1. Creating Points:")
388 points = [
389 ee.Geometry.Point([-122.4, 37.8]), # San Francisco
390 ee.Geometry.Point([-118.2, 34.1]), # Los Angeles
391 ee.Geometry.Point([-121.5, 38.6]) # Sacramento
392 ]
393
394 point_features = ee.FeatureCollection([
395 ee.Feature(points[0], {'city': 'San Francisco', 'population': 884000}),
396 ee.Feature(points[1], {'city': 'Los Angeles', 'population': 3980000}),
397 ee.Feature(points[2], {'city': 'Sacramento', 'population': 525000})
398 ])
399
400 print(f" ✓ Created {point_features.size().getInfo()} city points")
401
402 # Create lines
403 print("\n2. Creating Lines:")
404
405 # Highway route (simplified)
406 highway_coords = [
407 [-122.4, 37.8], # San Francisco
408 [-121.9, 37.3], # San Jose
409 [-120.7, 36.7], # Fresno
410 [-118.2, 34.1] # Los Angeles
411 ]
412
413 highway = ee.Geometry.LineString(highway_coords)
414 highway_feature = ee.Feature(highway, {
415 'name': 'California Highway Route',
416 'length_km': highway.length().divide(1000)
417 })
418
419 print(" ✓ Created highway route line")
420
421 # Create polygons
422 print("\n3. Creating Polygons:")
423
424 # Study area polygon
425 study_area_coords = [[
426 [-123.0, 37.0],
427 [-122.0, 37.0],
428 [-122.0, 38.0],
429 [-123.0, 38.0],
430 [-123.0, 37.0]
431 ]]
432
433 study_area = ee.Geometry.Polygon(study_area_coords)
434 study_area_feature = ee.Feature(study_area, {
435 'name': 'San Francisco Bay Study Area',
436 'area_km2': study_area.area().divide(1e6)
437 })
438
439 print(" ✓ Created study area polygon")
440
441 # Create buffer polygons around points
442 print("\n4. Creating Buffers:")
443
444 def create_city_buffer(feature):
445 """Create buffer around city based on population."""
446 population = ee.Number(feature.get('population'))
447 # Buffer radius based on population (scaled)
448 radius = population.sqrt().multiply(10)
449 buffered_geom = feature.geometry().buffer(radius)
450 return feature.setGeometry(buffered_geom).set('buffer_radius', radius)
451
452 city_buffers = point_features.map(create_city_buffer)
453
454 print(" ✓ Created population-based buffers around cities")
455
456 # Create regular grid
457 print("\n5. Creating Regular Grid:")
458
459 def create_grid(bounds, cell_size):
460 """Create a regular grid over the bounds."""
461 # Get bounding box
462 coords = ee.List(bounds.coordinates().get(0))
463
464 # Extract min/max coordinates
465 xs = coords.map(lambda item: ee.List(item).get(0))
466 ys = coords.map(lambda item: ee.List(item).get(1))
467
468 min_x = xs.reduce(ee.Reducer.min())
469 max_x = xs.reduce(ee.Reducer.max())
470 min_y = ys.reduce(ee.Reducer.min())
471 max_y = ys.reduce(ee.Reducer.max())
472
473 # Create grid
474 x_range = ee.List.sequence(min_x, max_x, cell_size)
475 y_range = ee.List.sequence(min_y, max_y, cell_size)
476
477 def create_cell(x):
478 def create_row(y):
479 x_coord = ee.Number(x)
480 y_coord = ee.Number(y)
481
482 cell = ee.Geometry.Rectangle([
483 x_coord, y_coord,
484 x_coord.add(cell_size), y_coord.add(cell_size)
485 ])
486
487 return ee.Feature(cell, {
488 'grid_x': x_coord,
489 'grid_y': y_coord,
490 'cell_id': x_coord.format('%.2f').cat('_').cat(y_coord.format('%.2f'))
491 })
492
493 return y_range.map(create_row)
494
495 grid_features = x_range.map(create_cell).flatten()
496 return ee.FeatureCollection(grid_features)
497
498 # Create 0.1 degree grid over study area
499 grid = create_grid(study_area, 0.1)
500 grid_count = grid.size().getInfo()
501 print(f" ✓ Created regular grid with {grid_count} cells")
502
503 return {
504 'cities': point_features,
505 'highway': highway_feature,
506 'study_area': study_area_feature,
507 'city_buffers': city_buffers,
508 'grid': grid
509 }
510
511 def export_vector_data(self, feature_collection, description):
512 """Demonstrate vector data export."""
513 print(f"\n📤 Exporting Vector Data: {description}")
514 print("-" * 40)
515
516 # Export to Google Drive
517 export_task = ee.batch.Export.table.toDrive(
518 collection=feature_collection,
519 description=description,
520 folder='EarthEngine_Exports',
521 fileFormat='SHP' # Shapefile format
522 )
523
524 print(f"✓ Export task created: {description}")
525 print(f" Task ID: {export_task.id}")
526 print(f" Status: Ready to start")
527 print(f" Format: Shapefile")
528 print(f" Destination: Google Drive/EarthEngine_Exports/")
529
530 # Note: To actually start the export, uncomment the line below
531 # export_task.start()
532
533 return export_task
534
535def main():
536 """Main function demonstrating vector operations."""
537
538 # Initialize vector operations system
539 vector_ops = VectorOperations('your-project-id')
540
541 print("="*70)
542 print("🗺️ EARTH ENGINE VECTOR OPERATIONS GUIDE")
543 print("="*70)
544
545 # Step 1: Load vector datasets
546 datasets = vector_ops.load_vector_datasets()
547
548 # Step 2: Basic feature operations
549 basic_features = vector_ops.basic_feature_operations(datasets)
550
551 # Step 3: Geometric operations
552 geometric_results = vector_ops.geometric_operations(basic_features)
553
554 # Step 4: Spatial relationships
555 spatial_results = vector_ops.spatial_relationships(datasets, geometric_results)
556
557 # Step 5: Zonal statistics
558 zonal_results = vector_ops.zonal_statistics(geometric_results)
559
560 # Step 6: Create custom geometries
561 custom_geometries = vector_ops.create_custom_geometries()
562
563 # Step 7: Export examples
564 print("\n📤 Export Examples")
565 print("-" * 25)
566
567 # Export California counties with statistics
568 counties_export = vector_ops.export_vector_data(
569 geometric_results['counties_with_area'].limit(5),
570 'california_counties_with_area'
571 )
572
573 # Export custom city features
574 cities_export = vector_ops.export_vector_data(
575 custom_geometries['cities'],
576 'california_cities'
577 )
578
579 # Summary
580 print("\n" + "="*70)
581 print("📊 VECTOR OPERATIONS SUMMARY")
582 print("="*70)
583
584 print("\n🎯 Operations Demonstrated:")
585 print("• Loading Earth Engine vector datasets")
586 print("• Attribute and spatial filtering")
587 print("• Geometric calculations (area, centroid, buffer)")
588 print("• Spatial relationship analysis")
589 print("• Zonal statistics with raster data")
590 print("• Custom geometry creation")
591 print("• Vector data export")
592
593 print("\n📈 Key Results:")
594 print(f"• California counties analyzed: {basic_features['ca_counties'].size().getInfo()}")
595 print(f"• Protected areas in CA: {spatial_results['ca_protected'].size().getInfo()}")
596 print(f"• Custom grid cells created: {custom_geometries['grid'].size().getInfo()}")
597
598 print("\n🏆 Best Practices Applied:")
599 print("• Efficient spatial filtering")
600 print("• Appropriate scale selection for analysis")
601 print("• Combining vector and raster operations")
602 print("• Proper error handling and validation")
603
604 print("\n✅ Vector Operations Guide Complete!")
605
606if __name__ == "__main__":
607 main()