Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/actions/map-boundaries-cache/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Cache map boundaries
description: >-
Restores the map boundaries generated by web-common/scripts/generate-map-boundaries.ts,
so builds skip downloading Natural Earth data when the inputs are unchanged.
On a cache miss, the build generates them and they are saved at the end of the job.
Requires Node.js; must run before the web-local or web-admin build.
runs:
using: composite
steps:
- name: Compute map boundaries cache key
id: key
shell: bash
# The script and the mapshaper version determine the output, matching the script's own stamp.
# The version is read from the lockfile, since this may run before `npm install`.
run: |-
version=$(node -p 'Object.entries(require("./package-lock.json").packages).find(([path]) => path.endsWith("node_modules/mapshaper"))[1].version')
echo "key=map-boundaries-mapshaper-${version}-${{ hashFiles('web-common/scripts/generate-map-boundaries.ts') }}" >> "$GITHUB_OUTPUT"

- name: Cache map boundaries
uses: actions/cache@v4
with:
path: web-common/static/map-boundaries
key: ${{ steps.key.outputs.key }}
3 changes: 3 additions & 0 deletions .github/workflows/cli-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ jobs:
with:
node-version-file: '.nvmrc'

- name: Cache map boundaries
uses: ./.github/actions/map-boundaries-cache

- name: Build and embed static UI
run: make cli.prepare
env:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/rill-ui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ jobs:
echo "DOMAIN=rilldata.in" >> $GITHUB_ENV
echo "RILL_VERSION=${GITHUB_SHA}" >> $GITHUB_ENV

- name: Cache map boundaries
uses: ./.github/actions/map-boundaries-cache

- name: Build Cloud UI
run: |-
npm ci
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/web-test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ jobs:
node-version-file: '.nvmrc'
cache: 'npm'

- name: Cache map boundaries
uses: ./.github/actions/map-boundaries-cache

- name: NPM Install
run: npm ci

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ node_modules
/cli/pkg/examples/embed/dist
/cli/pkg/installscript/embed/install.sh
/cli/pkg/web/embed/dist
/web-common/static/map-boundaries
/runtime/drivers/duckdb/extensions/embed/*/*
!/runtime/drivers/duckdb/extensions/embed/**/.gitkeep
/dev-project*
Expand Down
102 changes: 95 additions & 7 deletions docs/docs/developers/build/dashboards/canvas-widgets/map.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import ImageCodeToggle from '@site/src/components/ImageCodeToggle';

The map widget plots a metrics view on an interactive basemap. Use it to show how a measure is distributed geographically: stores, sensors, or customers as points, or states, countries, or sales territories as shaded regions. For more information, refer to our [Components reference doc](/reference/project-files/component#map).

Each map needs a **geo dimension** that holds a location for every row, and a **color measure**. Points can also be sized by a second measure.
A map has up to two layers:

- A **geo dimension** layer, which draws a location stored in every row as a point or polygon, colored by a measure. Points can also be sized by a second measure.
- A **regions** layer, which fills built-in country or US state boundaries by a measure, matched through the values of a regular dimension such as `country` or `state`. It needs no geographic data in your model. See [Built-in regions](#built-in-regions).

<ImageCodeToggle
image="/img/build/dashboard/canvas/components/map-points.png"
Expand Down Expand Up @@ -70,7 +73,7 @@ Alternatively, `ST_Point2D(longitude, latitude) AS location` stores a DuckDB `PO

### Regions from a GeoJSON file

To shade regions, load their boundaries as polygons. The following model reads a GeoJSON `FeatureCollection` file from your project's `data` folder and returns one row per feature, with the feature's geometry as a GeoJSON string:
For most regions, a regions layer is simpler: use [built-in regions](#built-in-regions) for countries and US states, or [custom regions](#custom-regions) for others, such as DMAs, postal codes, or sales territories. Alternatively, load the boundaries as polygons in a geo dimension. The following model reads a GeoJSON `FeatureCollection` file from your project's `data` folder and returns one row per feature, with the feature's geometry as a GeoJSON string:

```sql
-- models/state_population.sql
Expand Down Expand Up @@ -124,7 +127,9 @@ See [Metrics view YAML](/reference/project-files/metrics-views) for all dimensio
4. Under **Color**, pick the measure and a color scheme or gradient.
5. Optionally, add a **Size measure** and a **Tooltip dimension**.

The map draws as soon as it has a metrics view, a geo dimension, and a color measure. You can also define the widget directly in the canvas YAML, as in the examples on this page.
To shade countries or US states instead, pick a dimension under **Regions**; see [Built-in regions](#built-in-regions).

The map draws as soon as it has a metrics view and either a geo dimension with a color measure or a complete regions layer. You can also define the widget directly in the canvas YAML, as in the examples on this page.

## Point maps

Expand Down Expand Up @@ -162,16 +167,98 @@ When the geo dimension holds polygons, each region is filled with the color of i

Colors are spread linearly between the lowest and highest value on the map. If a few regions have much larger values than the rest, most regions end up in the same color. In the example above, the measure is `LN(AVG(population_density))` for that reason; a log-scaled or ratio measure (such as a per-capita value) often spreads colors more evenly.

## Built-in regions

To shade countries, or the states or provinces of a country, add a `regions` layer. It matches the values of a regular dimension to built-in boundaries, so your model only needs a column with country or state names or codes:

```yaml
- map:
metrics_view: sales_metrics
title: Revenue by country
regions:
dimension: country
boundaries: countries
color:
measure: total_revenue
colorRange:
mode: scheme
scheme: blues
```

To shade states, set `boundaries` to `states` and `country` to the country's ISO 3166-1 alpha-2 code:

```yaml
regions:
dimension: state
boundaries: states
country: DE
color:
measure: total_revenue
```

In the visual editor, pick the dimension under **Regions**, then choose the **Boundaries** and, for states, the **Country**. When you add a map to a metrics view without a geo dimension, the map starts with a regions layer for the first dimension whose name contains `country`, `state`, or `province`.

| `boundaries` | Covers | `key` values |
|--------------|--------|--------------|
| `countries` | Every country | `iso_a2` (`US`), `iso_a3` (`USA`), `iso_n3` (`840`), `name` (`United States`) |
| `states` | The first-level subdivisions of `country`, such as states, provinces, or regions | `code` (`CA`), `iso_3166_2` (`US-CA`), `fips` (`06`, US only), `name` (`California`) |
| `us_states` | Same as `states` with `country: US`: the US states, the District of Columbia, and Puerto Rico | `postal` (`CA`), `iso_3166_2` (`US-CA`), `fips` (`06`), `name` (`California`) |

`states` is available for Argentina, Australia, Austria, Belgium, Brazil, Canada, Chile, China, Colombia, Czechia, Denmark, Egypt, Finland, France, Germany, Greece, Hungary, India, Ireland, Israel, Italy, Japan, Malaysia, Mexico, the Netherlands, New Zealand, Nigeria, Peru, the Philippines, Poland, Portugal, Romania, Russia, Saudi Arabia, Singapore, South Africa, South Korea, Spain, Sweden, Switzerland, Thailand, Turkey, Ukraine, the United Arab Emirates, the United Kingdom, and the United States. Regions follow each country's first-level subdivisions in ISO 3166-2: for example, the regions of France and Italy, the autonomous communities of Spain, and England, Scotland, Wales, and Northern Ireland for the United Kingdom.

By default, values are matched against every key, so a column that mixes `US`, `USA`, and `United States` still works. Set `key` to match one key only. Matching ignores case, accents, and punctuation, and `name` also matches common alternative names, such as `UK` or `Ivory Coast`. The legend reports how many values didn't match a region; hover over the message to see them.

Regions without data are drawn in gray. Regions are drawn below the basemap's labels and below a geo dimension layer, so you can combine both, for example to show stores as points over states shaded by revenue. The `color`, `size_measure`, and `tooltip_dimension` properties only apply to the geo dimension layer; the regions layer has its own `color`.

Boundaries come from [Natural Earth](https://www.naturalearthdata.com/), which draws de facto borders for disputed territories. For other regions, such as DMAs, use [custom regions](#custom-regions).

## Custom regions

To shade regions that aren't built in, such as Nielsen DMAs, sales territories, or store trade areas, add a GeoJSON or TopoJSON boundary file to your project's `public` folder and point `boundaries` to it:

```yaml
- map:
metrics_view: ad_metrics
title: Impressions by DMA
regions:
dimension: dma_code
boundaries:
file: public/dma.topojson
id: dma_code
name: dma_name
color:
measure: impressions
```

| Property | Required | Description |
|----------|----------|-------------|
| `file` | Yes | Path of the boundary file from the project root. It must be directly inside one of the project's [`public_paths`](/reference/project-files/rill-yaml#public_paths), which default to `public`; files in subfolders aren't served. |
| `id` | Yes | Feature property that identifies each region. The values of `dimension` are matched against it, ignoring case, punctuation, and leading zeros. Features without this property use their top-level GeoJSON `id` instead. |
| `name` | No | Feature property shown as the region name in tooltips. Defaults to the `id` property. |

The file must be a GeoJSON `FeatureCollection` or a TopoJSON file with a single object. Only `Polygon` and `MultiPolygon` features with an id are drawn; if several features share an `id`, the first one is used. Your metrics view only needs a dimension whose values match the `id` property; no joins or geo dimensions are required. `key` doesn't apply to custom regions.

In the visual editor, choose **Custom file** under **Boundaries**, then enter the file and properties.

Every viewer's browser downloads the whole file, so keep it small. Simplifying the shapes and converting them to TopoJSON, which shares borders between neighboring regions, typically makes the file several times smaller:

```sh
npx mapshaper dma.geojson -simplify 10% keep-shapes -o format=topojson public/dma.topojson
```

The file is served only to viewers of your project, who receive it in full, including any viewers of [embedded dashboards](/developers/embed). Shapes aren't subject to [security policies](/developers/build/metrics-view/security): regions a viewer can't see data for still draw, in gray. Boundary sets such as DMAs are often licensed; you are responsible for ensuring your license permits displaying the shapes to your dashboard viewers, including embed users.

## Properties

| Property | Required | Description |
|----------|----------|-------------|
| `metrics_view` | Yes | Metrics view to query. |
| `geo_dimension.field` | Yes | Dimension that holds each row's location. See [Prepare geographic data](#prepare-geographic-data). |
| `color.measure` | Yes | Measure that sets the color of each point or region. |
| `geo_dimension.field` | One of `geo_dimension` or `regions` | Dimension that holds each row's location. See [Prepare geographic data](#prepare-geographic-data). |
| `color.measure` | With `geo_dimension` | Measure that sets the color of each point or polygon. |
| `color.colorRange` | No | Color scale. Defaults to the `tealblues` scheme. See [Colors](#colors). |
| `size_measure.field` | No | Measure that scales the radius of each point. Points only. |
| `tooltip_dimension.field` | No | Dimension shown as the heading of the hover tooltip, such as a city or state name. |
| `regions` | One of `geo_dimension` or `regions` | Regions layer with `dimension`, `boundaries`, optional `key`, and `color` (`measure` and optional `colorRange`). See [Built-in regions](#built-in-regions) and [Custom regions](#custom-regions). |
| `initial_view` | No | `longitude`, `latitude`, and optional `zoom` the map opens with. See [Set the initial view](#set-the-initial-view). |
| `title`, `description` | No | Title and description shown in the widget header. |
| `time_filters`, `dimension_filters` | No | Filters that apply to this widget only, as on other [canvas widgets](/developers/build/dashboards/canvas#filters). |
Expand Down Expand Up @@ -217,15 +304,16 @@ initial_view:
## Interacting with a map

- **Pan and zoom:** Drag to pan, scroll or pinch to zoom, and use the zoom and compass buttons in the top-right corner. A scale bar is shown in the bottom-left corner.
- **Tooltips:** Hover over a point or region to see its tooltip dimension value, the color measure, and the size measure.
- **Tooltips:** Hover over a point or polygon to see its tooltip dimension value, the color measure, and the size measure. Hover over a region to see its name and value.
- **Legend:** The top-left corner shows the color scale of each layer.
- **Filters:** The map follows the canvas time range and filters. Changing a filter updates the data without moving the map, so the area you zoomed into stays in view.
- **Dark mode:** The basemap switches between a light and a dark style with the dashboard's theme mode.

## Limitations

- Coordinates must be longitude and latitude in degrees. Projected coordinates, such as meters in a local coordinate system, aren't supported. Reproject them in your model first, for example with DuckDB's `ST_Transform`.
- Line geometries (`LineString`, `MultiLineString`) aren't drawn.
- The map loads up to 5,000 rows, one for each combination of geo dimension and tooltip dimension values. Choose a tooltip dimension that has one value per location, and aggregate very large point sets, for example to a grid or to regions, before mapping them.
- Each layer loads up to 5,000 rows. The geo dimension layer loads one row for each combination of geo dimension and tooltip dimension values. Choose a tooltip dimension that has one value per location, and aggregate very large point sets, for example to a grid or to regions, before mapping them.
- The basemap is loaded from [Mapbox](https://www.mapbox.com/) in the viewer's browser, so viewers need access to `api.mapbox.com`.

:::note Self-hosted builds
Expand Down
32 changes: 29 additions & 3 deletions docs/docs/reference/project-files/component.md
Original file line number Diff line number Diff line change
Expand Up @@ -536,17 +536,17 @@ See [Custom Viz](/developers/build/dashboards/canvas-widgets/custom-viz).

### `map`

_[object]_ - Map of points or regions from a geo dimension, colored by a measure and, for points, sized by a second measure
_[object]_ - Map with up to two layers: points or polygons from a geo dimension, colored by a measure and, for points, sized by a second measure; and regions such as countries or US states, filled by a measure and matched through the values of a regular dimension. At least one of `geo_dimension` and `regions` is required.

- **`metrics_view`** - _[string]_ - Reference to the metrics view to use _(required)_

- **`geo_dimension`** - _[object]_ - Dimension that holds the location of each row, as a GeoJSON geometry string or a DuckDB `POINT_2D` or `POLYGON_2D` value, in longitude and latitude. Only dimensions with `type: geo` are offered in the visual editor. _(required)_
- **`geo_dimension`** - _[object]_ - Dimension that holds the location of each row, as a GeoJSON geometry string or a DuckDB `POINT_2D` or `POLYGON_2D` value, in longitude and latitude. Only dimensions with `type: geo` are offered in the visual editor. `color`, `size_measure` and `tooltip_dimension` apply to this layer.

- **`field`** - _[string]_ - Name of the dimension _(required)_

- **`type`** - _[string]_ - Field type, always `nominal`

- **`color`** - _[object]_ - Measure that colors each point or region _(required)_
- **`color`** - _[object]_ - Measure that colors each point or polygon of the `geo_dimension` layer. Required with `geo_dimension`.

- **`measure`** - _[string]_ - Name of the measure _(required)_

Expand All @@ -572,6 +572,32 @@ _[object]_ - Map of points or regions from a geo dimension, colored by a measure

- **`type`** - _[string]_ - Field type, always `nominal`

- **`regions`** - _[object]_ - Regions layer that fills region boundaries by a measure, joined to the rows through the values of `dimension`. Boundaries are either a built-in set, such as countries or the states of a country, or a GeoJSON or TopoJSON file in the project. Drawn below the base map's labels and the `geo_dimension` layer.

- **`dimension`** - _[string]_ - Name of the dimension whose values identify regions, such as country codes or state names _(required)_

- **`boundaries`** - _[oneOf]_ - Boundaries to draw, either the name of a built-in set or a mapping that points to a boundary file in the project. _(required)_

- **option 1** - _[string]_ - Built-in boundary set to draw. `countries` covers every country; `states` covers the first-level subdivisions of the country in `country`, such as states, provinces or regions; `us_states` is the same as `states` with `country: US`.

- **option 2** - _[object]_ - Custom boundaries from a GeoJSON `FeatureCollection` or a TopoJSON file with a single object, such as Nielsen DMAs or sales territories. The file must be under one of the project's `public_paths` (`public/` by default); every dashboard viewer downloads it, so make sure its license allows that.

- **`file`** - _[string]_ - Path of the GeoJSON or TopoJSON file, relative to the project root, such as `public/dma.topojson` _(required)_

- **`id`** - _[string]_ - Feature property that identifies each region, matched against the values of `dimension`. Features without this property use their top-level GeoJSON `id` instead. Only polygon features with an id are drawn. _(required)_

- **`name`** - _[string]_ - Feature property shown as the region name in tooltips. Defaults to the `id` property.

- **`country`** - _[string]_ - Country whose states to draw, as an ISO 3166-1 alpha-2 code such as `DE`. Required with `boundaries: states`, and not supported with other boundaries.

- **`key`** - _[string]_ - Region property the dimension values are matched against, for built-in boundaries only. For `countries`: `iso_a2` (`US`), `iso_a3` (`USA`), `iso_n3` (`840`) or `name`. For `states`: `code` (`CA`), `iso_3166_2` (`US-CA`), `fips` (`06`, US only) or `name`. For `us_states`: `postal` (`CA`), `iso_3166_2` (`US-CA`), `fips` (`06`) or `name`. `name` also matches common alternative names. Defaults to `auto`, which tries every key. Matching ignores case, accents and punctuation.

- **`color`** - _[object]_ - Measure that fills each region _(required)_

- **`measure`** - _[string]_ - Name of the measure _(required)_

- **`colorRange`** - _[object]_ - Color scale for the measure; same options as the top-level `color.colorRange`

- **`initial_view`** - _[object]_ - Camera position the map opens with. When set, the map does not zoom to fit the data.

- **`longitude`** - _[number]_ - Longitude of the map center _(required)_
Expand Down
Loading
Loading