# Spatial Joins

Collections can be joined by spatial location as well as by property values. To join based
on spatial location, use a `withinDistance()` filter with `.geo` join
fields specified. The `.geo` field indicates that the item's
geometry is to be used to compute the distance metric. For example, consider the task of
finding all
[power plants](https://developers.google.com/earth-engine/datasets/catalog/WRI_GPPD_power_plants) within 100 kilometers
of Yosemite National Park, USA. For that purpose, use a filter on the geometry
fields, with the maximum distance set to 100 kilometers using the `distance`
parameter:

### Code Editor (JavaScript)

```javascript
// Load a primary collection: protected areas (Yosemite National Park).
var primary = ee.FeatureCollection("WCMC/WDPA/current/polygons")
  .filter(ee.Filter.eq('NAME', 'Yosemite National Park'));

// Load a secondary collection: power plants.
var powerPlants = ee.FeatureCollection('WRI/GPPD/power_plants');

// Define a spatial filter, with distance 100 km.
var distFilter = ee.Filter.withinDistance({
  distance: 100000,
  leftField: '.geo',
  rightField: '.geo',
  maxError: 10
});

// Define a saveAll join.
var distSaveAll = ee.Join.saveAll({
  matchesKey: 'points',
  measureKey: 'distance'
});

// Apply the join.
var spatialJoined = distSaveAll.apply(primary, powerPlants, distFilter);

// Print the result.
print(spatialJoined);
```
Python setup

See the [Python Environment](https://developers.google.com/earth-engine/guides/python_install) page for information on the Python API and using
`geemap` for interactive development.

```python
import ee
import geemap.core as geemap
```

### Colab (Python)

```python
# Load a primary collection: protected areas (Yosemite National Park).
primary = ee.FeatureCollection('WCMC/WDPA/current/polygons').filter(
    ee.Filter.eq('NAME', 'Yosemite National Park')
)

# Load a secondary collection: power plants.
power_plants = ee.FeatureCollection('WRI/GPPD/power_plants')

# Define a spatial filter, with distance 100 km.
dist_filter = ee.Filter.withinDistance(
    distance=100000, leftField='.geo', rightField='.geo', maxError=10
)

# Define a saveAll join.
dist_save_all = ee.Join.saveAll(matchesKey='points', measureKey='distance')

# Apply the join.
spatial_joined = dist_save_all.apply(primary, power_plants, dist_filter)

# Print the result.
display(spatial_joined)
```

Note that the previous example joins a `FeatureCollection` to another
`FeatureCollection`. The `saveAll()` join sets a property
(`points`) on each feature in the `primary` collection which
stores a list of the points within 100 km of the feature. The distance of each point to
the feature is stored in the `distance` property of each joined point.

Spatial joins can also be used to identify which features
in one collection intersect those in another. For example, consider two feature
collections: a `primary` collection containing polygons representing the
boundaries of US states, a `secondary` collection containing point locations
representing power plants. Suppose there is need to determine the number intersecting each
state. This can be accomplished with a spatial join as follows:

### Code Editor (JavaScript)

```javascript
// Load the primary collection: US state boundaries.
var states = ee.FeatureCollection('TIGER/2018/States');

// Load the secondary collection: power plants.
var powerPlants = ee.FeatureCollection('WRI/GPPD/power_plants');

// Define a spatial filter as geometries that intersect.
var spatialFilter = ee.Filter.intersects({
  leftField: '.geo',
  rightField: '.geo',
  maxError: 10
});

// Define a save all join.
var saveAllJoin = ee.Join.saveAll({
  matchesKey: 'power_plants',
});

// Apply the join.
var intersectJoined = saveAllJoin.apply(states, powerPlants, spatialFilter);

// Add power plant count per state as a property.
intersectJoined = intersectJoined.map(function(state) {
  // Get "power_plant" intersection list, count how many intersected this state.
  var nPowerPlants = ee.List(state.get('power_plants')).size();
  // Return the state feature with a new property: power plant count.
  return state.set('n_power_plants', nPowerPlants);
});

// Make a bar chart for the number of power plants per state.
var chart = ui.Chart.feature.byFeature(intersectJoined, 'NAME', 'n_power_plants')
  .setChartType('ColumnChart')
  .setSeriesNames({n_power_plants: 'Power plants'})
  .setOptions({
    title: 'Power plants per state',
    hAxis: {title: 'State'},
    vAxis: {title: 'Frequency'}});

// Print the chart to the console.
print(chart);
```

In the previous example, note that the `intersects()` filter doesn't store
a distance as the `withinDistance()` filter does. The output should look
something like Figure 1.
![CA WRS2 join](https://developers.google.com/static/earth-engine/images/Join_example_us_power_plants.png) Figure 1. Bar chart showing the number of power plants intersecting each US state.