Trade Area Analysis
Builds CARTO Workflows that define catchment areas around candidate locations, enrich them with data, and score/rank locations for site selection, billboard placement, or coverage analysis.
Prerequisites: Load
for the development process, JSON structure, and validation commands.
Instructions
A trade area workflow always follows this pipeline:
Candidate Locations → (Filter) → Catchment Areas → Spatial Index → Enrich → Aggregate → Score/Rank → Save
Step 1: Load Candidate Locations
Use
to load stores, billboards, POIs, or any point dataset representing candidate locations.
Success: Node outputs a table with a geometry column (e.g.
) and a unique location identifier.
Step 2: Filter/Limit Candidates (optional)
Use
to narrow candidates (e.g. specific city, category). Use
+
for top-N by a metric (e.g. top 50 by revenue).
Success: Output contains only the candidate subset relevant to the analysis. Reducing candidates early prevents combinatorial explosion downstream.
Step 3: Generate Catchment Areas
Choose the catchment method based on the use case:
| Method | Component | When to use | Key params |
|---|
| Isochrones | | Site selection, retail — realistic road-network areas | : car/walk; : time/distance; : seconds or meters |
| Buffers | | Billboard coverage, proximity — simple geometric circles | : meters |
Isochrones vs Buffers: Isochrones follow the road network and produce realistic catchment shapes but require a CARTO LDS API call. Buffers are purely geometric circles — simpler and faster, suitable for proximity analysis or billboard coverage.
Success: Each candidate location has an associated polygon representing its catchment area.
Step 4: Convert Catchment to Spatial Index
Use
or
to tessellate catchment polygons into grid cells. The resolution must match the enrichment dataset you plan to use.
Success: Output contains one row per grid cell per location, with both the spatial index column and the location identifier preserved.
Step 5: Enrich with Data
Join grid cells with a spatial features dataset or use
:
- JOIN approach: or with a pre-indexed enrichment table
- Enrich approach: to pull variables from CARTO's Data Observatory
Success: Each grid cell row has enrichment variables (e.g. population, income, foot traffic) attached.
Step 6: Aggregate Back to Locations
Use
to collapse grid-cell rows back to one row per location:
- Group by: the location identifier column
- Aggregation:
population,sum,income,avg
(comma-separated column,method pairs)
Success: Output has one row per candidate location with aggregated enrichment metrics.
Step 7: Score and Rank
Three-part scoring pattern:
- Normalize each variable to [0,1] using (one call per variable, or chain multiple)
- Composite score via : weighted addition of normalized variables, e.g.
normalized_population * 0.4 + normalized_income * 0.3 + normalized_traffic * 0.3
- Rank using (descending by composite score) + (top N)
Success: Output is a ranked list of candidate locations with a composite score and the contributing normalized variables.
Step 8: Save
Use
to persist the ranked results.
Success: Validated workflow that can be uploaded via
.
Gotchas
- Provider casing & SQL dialect. This skill uses lowercase column names (, , , , etc.) — BigQuery / Databricks / Postgres / Redshift convention. On Snowflake, unquoted identifiers surface UPPERCASE — reference them as , , , . See
carto-create-workflow/references/providers/<provider>.md
for casing rules and SQL dialect equivalents.
- Isochrone and route components call CARTO's LDS API — they require a valid connection with API access enabled. Buffers do not.
- Cross Join for distance matrices can explode with many locations x many grid cells. Filter and limit candidates first (Step 2) to keep the pipeline manageable.
- H3 resolution must match the enrichment dataset resolution. Check the enrichment table's index column before choosing resolution.
- Buffer distance is in meters. Isoline range is in seconds (for ) or meters (for ).
- The
native.commercialhotspots
component expects as a Python-style list string () and as comma-separated values — inconsistent with other components.
- When aggregating enrichment back to locations (Step 6), ensure the GROUP BY uses the location identifier, not the grid cell index. Grouping by the grid cell produces per-cell results instead of per-location.
- When using for competitor proximity, the output is grid-based. Join it back to the location table to get per-location distance metrics.
Reference Templates
These files are working examples in this skill directory:
| File | Description |
|---|
isochrones_from_points.json
| Retail stores in Boston — 5-min walk-time isochrones via LDS API |
identify_best_billboards.json
| Billboard site scoring — buffer, enrich, normalize, weighted composite score, top-N |
| Commercial hotspot detection — H3 distance to competitors, weighted hotspot analysis |
Common Variations
| Variant | How |
|---|
| Retail site selection | Isochrones (walk/drive) -> H3 polyfill -> enrich with demographics -> score by population + income |
| Billboard/OOH placement | Buffers -> H3 polyfill -> enrich with audience/traffic -> normalize + weighted score -> top-N |
| Commercial hotspot detection | H3 grid -> for competitor proximity -> native.commercialhotspots
with weights and p-value threshold |
| Drive-time coverage analysis | Isochrones (car, multiple ranges) -> union -> dissolve to find total coverage area |
| Walk-time catchment comparison | Isochrones (walk, 5/10/15 min) -> enrich each band -> compare population captured per band |
| Franchise territory planning | Isochrones per candidate -> check overlap -> filter non-overlapping set -> score remaining |