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()