Spatial Joins¶
Goal: Master the spatial join — the operation that combines two layers based on where they are, not on a shared key.
What you'll learn
- What a spatial join is and how it differs from a regular join
- The match options (intersects, contains, within, closest)
- One-to-one vs one-to-many joins
- Common spatial join patterns
Regular join vs spatial join¶
| Type | How it matches |
|---|---|
| Regular (attribute) join | Matches rows that share a value in a field (e.g., FIPS = FIPS) |
| Spatial join | Matches rows based on geographic relationship (e.g., this point falls in this polygon) |
You'd use a spatial join when there's no shared ID — but the location says they belong together.
Classic example¶
"I have crime points and police precinct polygons. I want to count crimes per precinct."
flowchart LR
Crimes[Crime points<br/>1,200 features] --> SJ((Spatial Join<br/>INTERSECT))
Precincts[Precinct polygons<br/>22 features] --> SJ
SJ --> Out[Precincts +<br/>crime count]
classDef p fill:#fef3c7,stroke:#f59e0b,color:#92400e
classDef pol fill:#dbeafe,stroke:#1e40af,color:#1e3a8a
classDef o fill:#dcfce7,stroke:#10b981,color:#065f46
class Crimes p
class Precincts pol
class Out o Match options¶
In ArcGIS Pro, Spatial Join asks how the two layers should relate:
| Match option | Meaning |
|---|---|
| Intersect | Any overlap counts (most common) |
| Within | Target geometry is fully inside the join geometry |
| Contains | Target fully contains the join geometry |
| Closest | Picks the nearest feature; can record distance |
| Within a distance | Match if within X meters |
| Have their center in | Polygon centroid in target |
| Identical to | Exact geometric match |
One-to-one vs one-to-many¶
This is the trickiest setting.
One-to-one (default)¶
If multiple features match a single target feature, the output summarizes them.
You can choose how to aggregate each field: SUM, MEAN, MIN, MAX, FIRST, COUNT, etc.
One-to-many¶
Each match becomes its own row.
Use one-to-many when you need each individual relationship preserved.
Field aggregation¶
When doing a one-to-one join with multiple matches, choose how each field merges:
| Operation | Use for |
|---|---|
Count | Number of matches (e.g., crimes per precinct) |
Sum | Add values (e.g., total damage) |
Mean | Average values (e.g., average income) |
Min / Max | Extreme values |
First / Last | Just grab one |
Join (text) | Concatenate strings |
Common spatial join patterns¶
-
Points in polygons
Count points per area. How many crimes per precinct?
-
Polygons in polygons
Calculate overlap. What % of each county is forested? (Use Intersect first for area-weighting.)
-
:material-distance-variant: Closest feature
Which fire station is closest to each school? — Use Closest match option.
-
Within X meters
Which restaurants are within 500 m of the new transit station?
Spatial join vs Summarize Within¶
Esri also has a Summarize Within tool that's a streamlined version of the points-in-polygon spatial join. It's often easier to use for "how many of X are in each Y, with stats" questions.
| Tool | When |
|---|---|
| Spatial Join | You need all matched fields in the output |
| Summarize Within | You only need counts and statistics, cleaner output |
A common mistake¶
Coordinate system mismatch
Spatial joins use coordinates. If your two layers are in different CRSs, results may look right but distances and areas will be wrong.
Fix: Project both layers to the same projected CRS (UTM, State Plane) before any spatial join that involves distance.
Practice¶
Try this
- Download Atlanta neighborhoods (polygons) and Atlanta crime incidents (points).
- Project both to NAD83 / Georgia State Plane West (EPSG:2240).
- Run Spatial Join:
- Target = Neighborhoods
- Join = Crimes
- Match option = Intersect
- Output = One-to-one
- Field map: Count of
INCIDENT_TYPE
- Symbolize neighborhoods by
Join_Countto see crime hotspots. - Now run a closest-feature spatial join from
SchoolstoFire stations. Compute the distance.
Next up¶
→ Geoprocessing Tools — buffer, clip, intersect, dissolve.