Docs »

Workspaces »

Maps

Create interactive map visualizations with geospatial data

Cerb can create interactive map visualizations with geospatial data from any source using the standard GeoJSON format.

A map schema is defined using KATA, which may include map resources, additional properties to fetch and merge, region colorization, point datasets, point sizing and colorization, labels, click interactions, and more.

GeoJSON

GeoJSON1 is a text format for describing geometric (latitude/longitude) features for use in maps.

Each feature contains geometry to describe its shape, and a list of key/value properties to describe its attributes. This geometry is vector-based, so it can be sharply scaled to any size.

Individual locations, like cities, are represented as points. Perimeters – such as continents, countries, states, roads, buildings, or geofences – are represented as polygons (a sequence of points).

For instance, a GeoJSON document can contain geometry that describes the shape and position of every country. The same document can also include points for populous cities of those countries. Properties can include localized names, population, continent, identifiers, and so on.

Resources

Detailed map data can be several megabytes in size, which would be cumbersome and redundant to include directly in a widget. Instead, map data is stored in reusable resource records. Each resource has a unique name, like map.world.countries or map.country.germany, which map KATA refers to. To improve performance, resources are cached in a client’s web browser and automatically updated with changes.

There are three resource types for maps:

Type Description
Map Features that describe regions in GeoJSON (e.g. countries, states, counties). These are base maps that other features are drawn on top of.
Map Points Points of interest to display on a base map in GeoJSON (e.g. cities, business locations).
Map Properties Additional JSON datasets (that can be merged into a base map (e.g. election results by state/county, COVID-19 cases per country).

Cerb includes several default resources that can be remixed into thousands of combinations:

Name Description
map.world.countries The countries of the world
map.country.usa.states The states of the United States
map.country.usa.counties The counties of the United States
mapPoints.worldCapitalCities The capital city for every country
mapPoints.usaStateCapitals The capital city of each U.S. state

Resources can be file-based, with an uploaded file that infrequently changes, or automation-based, with a dynamically generated resource. For instance, an automation-based resource can fetch live data from any HTTP endpoint, transform it, and output the expected JSON format.

One map resource can form the basis of numerous visualizations. You can have hundreds of map widget variations, and a client’s browser will only fetch the distinct resources once.

With map KATA, you can filter the map.world.countries resource to only display a single continent (e.g. Europe), a single country (e.g. Germany), or any combination of countries.

Similarly, the map.country.usa.counties resource can be filtered to display the counties of a single state or region. This one resource replaces 50+ individual U.S. state maps.

These are just the built-in examples. The same approach can be used to create resources for any map, of any region, at any level of detail.

Typically, you would download a “shapefile” from a government agency and convert it to the GeoJSON format, with a tool like shp2json.

However, if you just need a state/province level map for any country, we’ve already done that work for you in the cerb/geojson-maps repository. Countries are organized by their two-letter code (iso_a2). The repository also includes data sets for populated cities by country or continent. This is much more detail than provided in the default resources.

These resources are derived from Natural Earth public domain data. They do not require attribution and do not have any limitations on their usage.

Maps KATA

As mentioned earlier, map schemas use KATA, our human-friendly text format for structured data.

Resource:

A map starts with a map:resource::



map:
  resource:
    uri: cerb:resource:map.country.usa.states


Notice that the resource is referred to by a map:resource:uri: (uniform resource identifier). This always starts with a cerb: prefix, then a record type, another colon :, and ends with the unique identifier for a record of that type (an alias or record ID).

In the map editor, you can hold Command (Mac) or Control (Windows/Linux) to click on a resource URI and open its card popup.

Projection:

Our planet is spherical, but you’re probably used to looking at flat, rectangular maps.

A projection converts geospatial coordinates (latitude/longitude) into a flat plane (2D screen coordinates).

type:

Cerb supports the following projection types:

mercator

Mercator has been the general purpose standard projection for nearly 500 years.



map:
  projection:
    type: mercator
    scale: 90


naturalEarth

Natural Earth is a relatively new projection, suitable for showing worldwide maps at a small scale.



map:
  projection:
    type: naturalEarth
    scale: 90


albersUsa

AlbersUSA is a “composite” projection for displaying a more compact map of the United States, with resized Alaska/Hawaii moved near the continental southwest.



map:
  projection:
    type: albersUsa
    scale: 650


scale:

The map:projection:scale: key determines the initial size of the rendered map features. A larger number results in bigger shapes.

You can determine the ideal scale by panning and zooming the map, then looking at the current values in the bottom right.



map:
  projection:
    type: mercator
    scale: 90


center:

The map:projection:center: key determines the default location at the center of the map’s viewport. If omitted, the default is 0º Latitude, 0º Longitude (off the west coast of Africa).



map:
  projection:
    type: mercator
    scale: 90
    center:
        latitude: 0
        longitude: 0


The albersUsa projection does not support this option, and is instead always centered on the United States by default. You can use zoom: to change the focus.

zoom:

While map:projection:center: determines the baseline map position, the map:projection:zoom: key allows you to procedurally pan and zoom the map. These changes can be reset back to the baseline by the user.

Interactions can also use the zoom: option to move around an existing map.



map:
  projection:
    type: mercator
    scale: 90
    center:
        latitude: 0
        longitude: 0
    zoom:
        latitude: 37.98
        longitude: -97.74
        scale: 2


The map:projection:zoom:scale: key is a zoom multiplier from 1 to 40.

Regions:

The shapes in the base map are regions – countries, states, etc.

When you click on a region on the map, you’ll see a list of its key/value properties in the top left.

This makes it easy to explore the available properties while creating your map.

properties:

To improve reusability, you can use map:regions:properties: to merge additional properties into the existing regions on a map.

data:

Use the map:regions:properties:data: key to specify additional properties to merge into the existing map regions.


map:
  resource:
    uri: cerb:resource:map.country.usa.states
  projection:
    type: albersUsa
    scale: 650
  regions:
    properties:
      data:
        CA:
          party: D
        TX:
          party: R
      join:
        property: postal
        case: upper

resource:

Use the map:regions:properties:resource: key to fetch a ‘Map Properties’ resource and merge it into the existing map regions.


map:
  resource:
    uri: cerb:resource:map.country.usa.states
  projection:
    type: albersUsa
    scale: 650
  regions:
    properties:
      resource:
        uri: cerb:resource:mapProperties.usaStateAnimals
      join:
        property: name
        case: upper

The map properties resource must have the following JSON object format:


{
    "VALUE_TO_MATCH": {
        "new_property": "some value",
        "another_new_property": "another value"
    },
    ...
}

For instance:


{
    "CALIFORNIA": {
        "state_animal": "California grizzly bear" 
    },
    "TEXAS": {
        "state_animal": "Nine-banded armadillo"
    }
}

join:

The top-level keys should uniquely identify a property of the base map (e.g. country/state). This is referenced in the map:regions:properties:join:property: key.

The join property must exactly match an existing property for each feature. You can use the map:regions:properties:join:case: key with a value of upper (upper case) or lower (lower case) to normalize the data. For instance, case: upper will match camelCaps using CAMELCAPS for both the join property and the feature property.

With this option you can join countless datasets to an existing map.

It's a good idea to use standard properties like iso_a2 to join properties to your map. These are already uniform and used by many third-party datasets.

label:

It’s useful to be able to see all of the properties on a feature when designing your map. However, you’ll likely eventually want to display only the data related to your visualization.

You can use the map:regions:label: key to control the contents of the floating label.


map:
  resource:
    uri: cerb:resource:map.country.usa.states
  projection:
    type: albersUsa
    scale: 650
  regions:
    properties:
      resource:
        uri: cerb:resource:mapProperties.usaStateAnimals
      join:
        property: name
        case: upper
    label:
      title: name
      properties:
        state_animal:
          label: Animal
        pop_est:
          label: Population
          format: number

filter:

Use map:regions:filter: to extract or omit certain features from a larger map resource.

is:

Use is: to match a single property value:


map:
  resource:
    uri: cerb:resource:map.country.usa.states
  projection:
    type: albersUsa
    scale: 650
  regions:
    filter:
      property: name
      is: California

is@list:

Use is@list: or is@csv: to match multiple values in a single property:


map:
  resource:
    uri: cerb:resource:map.world.countries
  projection:
    type: mercator
    scale: 790
    center:
      latitude: 48.4088
      longitude: 8.2271
  regions:
    filter:
      property: name
      is@list:
        Austria
        Belgium
        France
        Germany
        Switzerland

not:

Use not: to exclude a single property value:


map:
  resource:
    uri: cerb:resource:map.world.countries
  projection:
    type: mercator
    scale: 90
    center:
      latitude: 0
      longitude: 0
  regions:
    filter:
      property: continent
      not: North America

not@list:

Use not@list: or not@csv: to exclude multiple values in a single property:


map:
  resource:
    uri: cerb:resource:map.world.countries
  projection:
    type: mercator
    scale: 90
    center:
      latitude: 0
      longitude: 0
  regions:
    filter:
      property: name
      not@csv: Australia, New Zealand, Antarctica

fill:

Map regions can be dynamically colorized based on property values.

Colors should be specified using web colors (CSS) syntax:

Type Examples
Color names aquamarine, red, black
Hexadecimal #FF0000, #646464
RGB triplets rgb(255,0,0), rgba(0,200,0,0.5)

color_key:

Use map:regions:fill:color_key: to select colors directly from a property.


map:
  regions:
    fill:
      color_key:
        property: country_color

color_map:

Use map:regions:fill:color_map: to associate colors with specific property values.


map:
  regions:
    fill:
      color_map:
        property: scalerank
        colors:
          1: gray
          2: blue
          3: green
          4: orange
          5: red

choropleth:

Use map:regions:fill:choropleth: to interpolate color intensity on a scale based on a numeric property.


map:
  regions:
    fill:
      choropleth:
        property: mapcolor9
        classes: 9

Points:

Points of interest can be plotted on a map using geospatial coordinates (latitude/longitude).

resource:

Like map data, points can be loaded from a resource URI using map:points:resource::


map:
  points:
    resource:
      uri: cerb:resource:mapPoints.worldCapitalCities

data:

You can also use map:points:data: to manually specify points and their properties. This is useful for map widgets; for instance, displaying an organization’s location on its profile page, or displaying the approximate location of an IP address from an interaction.

If you specify both map:points:resource: and map:points:data:, the points will be merged into a single dataset.


map:
  points:
    data:
      point/berlin:
        latitude: 52.549636074382285
        longitude: 13.403320312499998
        properties:
          name: Berlin
          country: Germany
          continent: Europe
      point/los_angeles:
        latitude: 34.08906131584994
        longitude: 241.69921874999997
        properties:
          name: Los Angeles
          country: United States of America
          continent: North America
      point/sydney:
        latitude: -33.7243396617476
        longitude: 151.259765625
        properties:
          name: Sydney
          country: Australia
          continent: Oceania

The website http://geojson.io/ is very useful for finding point coordinates, or drawing simple shapes in GeoJSON.

label:

Like with regions, clicking on a point displays its properties. You can use map:points:label: to select which properties to show.


map:
  points:
    resource:
      uri: cerb:resource:mapPoints.worldCapitalCities
    label:
      title: name
      properties:
        adm1name:
          label: State
        adm0name:
          label: Country
        pop_max:
          label: Population
          format: number
        latitude:
          label: Latitude
        longitude:
          label: Longitude

filter:

Use map:points:filter: to extract or omit certain points from a resource.

is:

Use is: to match a single property value:


map:
  points:
    resource:
      uri: cerb:resource:mapPoints.worldCapitalCities
    filter:
      property: continent
      is: Europe

is@list:

Use is@list: or is@csv: to match multiple values in a single property:


map:
  points:
    resource:
      uri: cerb:resource:mapPoints.worldCapitalCities
    filter:
      property: name
      is@list:
        Beijing
        Berlin
        London
        Moscow
        Washington, D.C.

not:

Use not: to exclude a single property value:


map:
  points:
    resource:
      uri: cerb:resource:mapPoints.worldCapitalCities
    filter:
      property: megacity
      not: 0

not@list:

Use not@list: or not@csv: to exclude multiple values in a single property:


map:
  points:
    resource:
      uri: cerb:resource:mapPoints.worldCapitalCities
    filter:
      property: continent
      not@list:
        Asia
        Oceania

size:

Use map:points:size: to configure the size of each point.

default:

The map:points:size:default: key configures the default size for points which don’t match any other rules. The size should be specified as a number.


map:
  points:
    resource:
      uri: cerb:resource:mapPoints.worldCapitalCities
    size:
      default: 2.5

value_map:

Use map:points:size:value_map: to associate point sizes based on the value of a property.


map:
  points:
    resource:
      uri: cerb:resource:mapPoints.worldCapitalCities
    size:
      default: 2.5
      value_map:
        property: worldcity
        values:
          1: 5.0

fill:

Use the map:points:fill: key to configure the color for each point.

Colors should be specified using web colors (CSS) syntax:

Type Examples
Color names aquamarine, red, black
Hexadecimal #FF0000, #646464
RGB triplets rgb(255,0,0), rgba(0,200,0,0.5)

default:

The map:points:fill:default: key configures the default color for points which don’t match any other rules.


map:
  points:
    resource:
      uri: cerb:resource:mapPoints.worldCapitalCities
    fill:
      default: red

color_map:

Use map:points:fill:color_map: to colorize points based on the value of a property.


map:
  points:
    resource:
      uri: cerb:resource:mapPoints.worldCapitalCities
    fill:
      default: #646464
      color_map:
        property: worldcity
        colors:
          1: red

Zooming and panning

Maps can be zoomed with a mouse-wheel, or with the ‘+’ and ‘-‘ buttons in the top right of the widget.

You can pan a map by clicking on any region or point. Maps can’t be panned from whitespace (e.g. ocean), which makes it easier to scroll past a widget without unintentionally zooming.

Handling clicks with automations

On map widgets, a ‘Map clicked’ (map.clicked) automation can be added using event handler KATA.

The first enabled automation receives the event with inputs for the feature_type (region or point), feature_properties (from the GeoJSON), an optional widget_* dictionary, and the current worker_*.

The return dictionary may include a sheet: key with a schema to display for the clicked feature, using the feature properties.

Example: COVID-19 cases in the U.S.

Suppose we want to visualize COVID-19 cases in the United States by the severity of each state’s incident rate (i.e. cases per 100K population). We’ll call this “Covid-19 Incident Rate per 100K”.

Choosing our resource and projection

We’ve learned that we can use the built-in map.country.usa.states resource to display a map of the United States.

Visualizations of the United States often use a compact format with a smaller Alaska, and larger Hawaii, shown closer to the continental states than their true locations. We’ll use the albersUsa projection to achieve this effect.

Our map KATA begins with:



map:
  resource:
    uri: cerb:resource:map.country.usa.states
  projection:
    type: albersUsa
    scale: 650


This shows all 50 states in a compact format, and scaled up to fill the widget:

Creating a properties resource

Now we want to colorize the states based on their incident rate. The map doesn’t include this data by default, so we need to find some data for our new ‘Map Properties’ resource.

A quick search discovers a GitHub repository with daily reports of COVID-19 levels in the U.S.: https://github.com/CSSEGISandData/COVID-19/tree/master/csse_covid_19_data/csse_covid_19_daily_reports_us

We’ll grab the latest report (2020-11-23, at the time of writing).

This is a comma-separated value file (.csv). We need to convert it to our ‘Map Properties’ JSON format.

For demonstration, we’ll do the conversion manually, so you’re better prepared to create your own resources.

You could write a simple script for this in any programming language; but you can also use the Setup » Developers » Bot Scripting Tester and write a quick and disposable script in Cerb’s bot scripting language.



{# https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_daily_reports_us/11-23-2020.csv #}
{% set csv %}
Alabama,US,2020-11-24 05:30:26,32.3182,-86.9023,234080,3459,90702.0,139919.0,1.0,4774.039731317501,1533628.0,,1.4776999316473,84000001,USA,31278.199782386342,
Alaska,US,2020-11-24 05:30:26,61.3707,-152.4044,28273,103,7165.0,21005.0,2.0,3864.834015679145,933016.0,,0.3643051674742687,84000002,USA,127540.47939634607,
American Samoa,US,2020-11-24 05:30:26,-14.271,-170.132,0,0,,0.0,60.0,0.0,1988.0,,,16,ASM,3572.904872306393,
Arizona,US,2020-11-24 05:30:26,33.7298,-111.4312,302324,6464,48807.0,247053.0,4.0,4153.534201151109,2124873.0,,2.138103491618264,84000004,USA,29192.960792403388,
Arkansas,US,2020-11-24 05:30:26,34.9697,-92.3731,146190,2387,127059.0,16744.0,5.0,4844.250985153441,1605173.0,,1.6328066215199395,84000005,USA,53190.101146396526,
California,US,2020-11-24 05:30:26,36.1162,-119.6816,1128219,18753,,1109466.0,6.0,2855.3670594539817,22457544.0,,1.662177290047411,84000006,USA,56836.95397244544,
Colorado,US,2020-11-24 05:30:26,39.0598,-105.3111,202289,2810,11201.0,188278.0,8.0,3512.732655221563,2901740.0,,1.3891017306922275,84000008,USA,50388.48802931755,
Connecticut,US,2020-11-24 05:30:26,41.5978,-72.7554,106740,4871,9800.0,92069.0,9.0,2993.868375813785,3038489.0,,4.5634251452126655,84000009,USA,85224.24702415263,
Delaware,US,2020-11-24 05:30:26,39.3185,-75.5071,32211,752,16073.0,15386.0,10.0,3307.8856889348963,687482.0,,2.33460619043184,84000010,USA,70600.47403683027,
Diamond Princess,US,2020-11-24 05:30:26,,,49,0,,49.0,88888.0,,,,0.0,84088888,USA,,
District of Columbia,US,2020-11-24 05:30:26,38.8974,-77.0268,20290,672,14935.0,4683.0,11.0,2874.959794487842,639461.0,,3.3119763430261213,84000011,USA,90607.42558615033,
Florida,US,2020-11-24 05:30:26,27.7663,-81.6868,944745,18085,,926660.0,12.0,4398.7176116366445,11740720.0,,1.9142731636579184,84000012,USA,54664.60456238942,
Georgia,US,2020-11-24 05:30:26,33.0406,-83.6431,451056,9215,,441841.0,13.0,4248.262502115626,4139509.0,,2.0429835763186834,84000013,USA,38987.88811560018,
Grand Princess,US,2020-11-24 05:30:26,,,103,3,,100.0,99999.0,,,,2.9126213592233015,84099999,USA,,
Guam,US,2020-11-24 05:30:26,13.4443,144.7937,6452,103,4846.0,1503.0,66.0,3928.6605897862128,80999.0,,1.5964042157470553,316,GUM,49320.76551644351,
Hawaii,US,2020-11-24 05:30:26,21.0943,-157.4983,17596,233,11958.0,5405.0,15.0,1242.767707815396,636113.0,,1.3241645828597408,84000015,USA,44927.29568774577,
Idaho,US,2020-11-24 05:30:26,44.2405,-114.4788,93090,866,38025.0,54199.0,16.0,5209.099836883382,453872.0,,0.9302825222902568,84000016,USA,25397.6212392946,
Illinois,US,2020-11-24 05:30:26,40.3495,-88.9861,664620,12111,,652509.0,17.0,5244.86575370659,9892981.0,,1.8222442899702085,84000017,USA,78070.71296224908,
Indiana,US,2020-11-24 05:30:26,39.8494,-86.2583,300913,5332,169211.0,126370.0,18.0,4469.7446711106695,3939645.0,,1.771940727053999,84000018,USA,58519.2638563897,
Iowa,US,2020-11-24 05:30:26,42.0115,-93.2105,214792,2222,116837.0,95733.0,19.0,6807.836276215741,1040373.0,,1.0344891802301759,84000019,USA,32974.6408162101,
Kansas,US,2020-11-24 05:30:26,38.5266,-96.7265,144792,1348,3189.0,140255.0,20.0,4970.010098465185,784288.0,,0.9309906624675396,84000020,USA,26920.819383011923,
Kentucky,US,2020-11-24 05:30:26,37.6681,-84.6701,160232,1792,26611.0,131829.0,21.0,3586.475554500072,2441630.0,,1.1183783513904837,84000021,USA,54651.04540999309,
Louisiana,US,2020-11-24 05:30:26,31.1695,-91.8678,221160,6284,185960.0,28916.0,22.0,4757.3628773397995,3262192.0,,2.8413818050280337,84000022,USA,70172.86633909782,
Maine,US,2020-11-24 05:30:26,44.6939,-69.3819,10544,177,7986.0,2381.0,23.0,784.4000797493253,825349.0,,1.678679817905918,84000023,USA,61400.21068105329,
Maryland,US,2020-11-24 05:30:26,39.0639,-76.8021,183797,4448,8511.0,170838.0,24.0,3040.137751253788,4171674.0,,2.420061263241511,84000024,USA,69002.56050601421,
Massachusetts,US,2020-11-24 05:30:26,42.2302,-71.5301,208623,10531,145682.0,52410.0,25.0,3026.8104344677113,7920042.0,,5.047861453435144,84000025,USA,114908.06750464963,
Michigan,US,2020-11-24 05:30:26,43.3266,-84.5361,340964,8940,152267.0,179757.0,26.0,3414.1271873623505,6263536.0,,2.621977686793913,84000026,USA,62717.789991385675,
Minnesota,US,2020-11-24 05:30:26,45.6945,-93.9002,276500,3321,227311.0,45868.0,27.0,4902.80216865214,3756777.0,,1.2010849909584087,84000027,USA,66613.86771335434,
Mississippi,US,2020-11-24 05:30:26,32.7416,-89.6787,143879,3676,121637.0,18566.0,28.0,4834.4017722230965,1091151.0,,2.5549246241633594,84000028,USA,36663.184538139714,
Missouri,US,2020-11-24 05:30:26,38.4561,-92.2884,280645,3580,,277065.0,29.0,4572.680934098127,2942201.0,,1.2756329170304122,84000029,USA,47938.664209176866,
Montana,US,2020-11-24 05:30:26,46.9219,-110.4544,56381,614,39450.0,16317.0,30.0,5275.2769985909135,617974.0,,1.0890193504904135,84000030,USA,57820.61382251506,
Nebraska,US,2020-11-24 05:30:26,41.1254,-98.2681,115921,934,58057.0,56930.0,31.0,5992.58274366111,1262563.0,,0.8057211376713451,84000031,USA,65268.70236268668,
Nevada,US,2020-11-24 05:30:26,38.3135,-117.0554,136227,2023,,134204.0,32.0,4422.730537024748,1536076.0,,1.4850213247006834,84000032,USA,49870.07151585829,
New Hampshire,US,2020-11-24 05:30:26,43.4525,-71.5639,18082,512,13226.0,4344.0,33.0,1329.841414830063,770791.0,,2.831545183054972,84000033,USA,56687.85499271537,
New Jersey,US,2020-11-24 05:30:26,40.2989,-74.521,309588,16772,41550.0,251266.0,34.0,3485.4917537229,5675266.0,,5.4175226429964995,84000034,USA,63894.89529046328,
New Mexico,US,2020-11-24 05:30:26,34.8405,-106.2485,84148,1400,29183.0,53565.0,35.0,4013.1074112385895,1464452.0,,1.6637353234776822,84000035,USA,69841.26984126984,
New York,US,2020-11-24 05:30:26,42.1657,-74.9481,602120,34339,83307.0,484474.0,36.0,3095.1659698705034,18187840.0,,5.703016010097655,84000036,USA,93493.62823598208,
North Carolina,US,2020-11-24 05:30:26,35.6301,-79.8064,339194,5039,293555.0,40600.0,37.0,3234.089276935615,4983768.0,,1.4855805232403876,84000037,USA,47518.383720038866,
North Dakota,US,2020-11-24 05:30:26,47.5289,-99.784,73397,852,62697.0,9848.0,38.0,9631.368576310064,1033394.0,,1.1608103873455318,84000038,USA,135604.9770228669,
Northern Mariana Islands,US,2020-11-24 05:30:26,15.0979,145.6739,104,2,29.0,73.0,69.0,188.59712752067315,16851.0,,1.9230769230769231,580,MNP,30558.174960104454,
Ohio,US,2020-11-24 05:30:26,40.3888,-82.7649,363304,6020,230678.0,126606.0,39.0,3108.0579343148747,5683989.0,,1.6570145112632946,84000039,USA,48626.404085857765,
Oklahoma,US,2020-11-24 05:30:26,35.5653,-96.9289,177874,1649,142381.0,33844.0,40.0,4495.206055338793,1967519.0,,0.927060728380764,84000040,USA,49722.856194801534,
Oregon,US,2020-11-24 05:30:26,44.572,-122.0709,66333,826,5870.0,59637.0,41.0,1572.7154158734884,1016282.0,,1.2452323881024527,84000041,USA,24095.43316712256,
Pennsylvania,US,2020-11-24 05:30:26,40.5908,-77.2098,320158,9846,198072.0,112240.0,42.0,2500.8457670132348,3019571.0,,3.0753565427070386,84000042,USA,23586.733280273867,
Puerto Rico,US,2020-11-24 05:30:26,18.2208,-66.5901,48200,1032,39214.0,7954.0,72.0,1509.224114771171,354172.0,,2.1410788381742742,630,PRI,11089.728696612763,
Rhode Island,US,2020-11-24 05:30:26,41.6809,-71.5118,50573,1309,3436.0,45828.0,44.0,4773.915596288706,1468082.0,,2.588337650524984,84000044,USA,138581.84320547953,
South Carolina,US,2020-11-24 05:30:26,33.8569,-80.945,207552,4288,108469.0,94795.0,45.0,4031.1425338443737,2267605.0,,2.065988282454517,84000045,USA,44042.16276141965,
South Dakota,US,2020-11-24 05:30:26,44.2998,-99.4388,73848,819,55679.0,17350.0,46.0,8347.623208490502,313297.0,,1.1090347741306468,84000046,USA,35414.436522999255,
Tennessee,US,2020-11-24 05:30:26,35.7478,-86.6923,344550,4301,296592.0,43657.0,47.0,5045.2660892810745,4334644.0,,1.248294877376288,84000047,USA,63472.44923031687,
Texas,US,2020-11-24 05:30:26,31.0545,-97.5635,1162135,21049,917739.0,221075.0,48.0,4000.0957377359914,10123856.0,,1.814783297682571,84000048,USA,34914.807382469255,
Utah,US,2020-11-24 05:30:26,40.15,-111.8624,179420,797,117104.0,61519.0,49.0,5596.4551001603895,1735824.0,,0.44420911826998105,84000049,USA,54143.69121491922,
Vermont,US,2020-11-24 05:30:26,44.0459,-72.7107,3714,63,2300.0,1351.0,50.0,595.2028000493599,523227.0,,1.6962843295638126,84000050,USA,83851.95892876317,
Virgin Islands,US,2020-11-24 05:30:26,18.3358,-64.8963,1507,23,1407.0,77.0,78.0,1404.8924189879556,27479.0,,1.5262110152621102,850,VIR,25617.145840325167,
Virginia,US,2020-11-24 05:30:26,37.7693,-78.17,221038,3942,23253.0,193843.0,51.0,2589.625774367089,3161059.0,,1.7834037586297378,84000051,USA,37034.174488979515,
Washington,US,2020-11-24 05:30:26,47.4009,-121.4905,147537,2655,,144882.0,53.0,1937.4796205278265,2894367.0,,1.7995485878118709,84000053,USA,38009.29310497206,
West Virginia,US,2020-11-24 05:30:26,38.4912,-80.9545,41114,667,26769.0,13678.0,54.0,2294.1198461956524,1038846.0,,1.6223184316777741,84000054,USA,57966.56189475529,
Wisconsin,US,2020-11-24 05:30:26,44.2685,-89.6165,379693,3158,280358.0,96177.0,55.0,6521.207453789943,4196070.0,,0.8317245774876019,84000055,USA,72067.28320149271,
Wyoming,US,2020-11-24 05:30:26,42.756,-107.3025,29431,202,17452.0,11777.0,56.0,5085.190899839138,161219.0,,0.6863511263633584,84000056,USA,27855.98150525521,
{% endset %}
{% set results = [] %}
{% for line in csv|split_crlf %}
{% set data = line|split_csv %}
{# Province_State,Country_Region,Last_Update,Lat,Long_,Confirmed,Deaths,Recovered,Active,FIPS,Incident_Rate,Total_Test_Results,People_Hospitalized,Case_Fatality_Ratio,UID,ISO3,Testing_Rate,Hospitalization_Rate #}
{% set results = dict_set(results, data.0, {
	covid_incident_rate: data.10|round(2),
	covid_testing_rate: data.16|round(2),
	covid_cases_confirmed: data.5|round,
	covid_deaths: data.6|round,
	covid_recovered: data.7|round,
	covid_active: data.8|round,
	covid_fips: data.9|round,
}) %}
{% endfor %}
{{results|json_encode|json_pretty}}


Omitting |json_pretty gives more compact output, and will often produce a considerably smaller file for large data sets.

Don’t worry if the above script doesn’t make sense to you. It’s not important for this example.

This script gives us the following JSON output:


{
  "Alabama": {
    "covid_incident_rate": 4774.04,
    "covid_testing_rate": 31278.2,
    "covid_cases_confirmed": 234080,
    "covid_deaths": 3459,
    "covid_recovered": 90702,
    "covid_active": 139919,
    "covid_fips": 1
  },
  "Alaska": {
    "covid_incident_rate": 3864.83,
    "covid_testing_rate": 127540.48,
    "covid_cases_confirmed": 28273,
    "covid_deaths": 103,
    "covid_recovered": 7165,
    "covid_active": 21005,
    "covid_fips": 2
  },
  "American Samoa": {
    "covid_incident_rate": 0,
    "covid_testing_rate": 3572.9,
    "covid_cases_confirmed": 0,
    "covid_deaths": 0,
    "covid_recovered": 0,
    "covid_active": 0,
    "covid_fips": 60
  },
  "Arizona": {
    "covid_incident_rate": 4153.53,
    "covid_testing_rate": 29192.96,
    "covid_cases_confirmed": 302324,
    "covid_deaths": 6464,
    "covid_recovered": 48807,
    "covid_active": 247053,
    "covid_fips": 4
  },
  "Arkansas": {
    "covid_incident_rate": 4844.25,
    "covid_testing_rate": 53190.1,
    "covid_cases_confirmed": 146190,
    "covid_deaths": 2387,
    "covid_recovered": 127059,
    "covid_active": 16744,
    "covid_fips": 5
  },
  "California": {
    "covid_incident_rate": 2855.37,
    "covid_testing_rate": 56836.95,
    "covid_cases_confirmed": 1128219,
    "covid_deaths": 18753,
    "covid_recovered": 0,
    "covid_active": 1109466,
    "covid_fips": 6
  },
  "Colorado": {
    "covid_incident_rate": 3512.73,
    "covid_testing_rate": 50388.49,
    "covid_cases_confirmed": 202289,
    "covid_deaths": 2810,
    "covid_recovered": 11201,
    "covid_active": 188278,
    "covid_fips": 8
  },
  "Connecticut": {
    "covid_incident_rate": 2993.87,
    "covid_testing_rate": 85224.25,
    "covid_cases_confirmed": 106740,
    "covid_deaths": 4871,
    "covid_recovered": 9800,
    "covid_active": 92069,
    "covid_fips": 9
  },
  "Delaware": {
    "covid_incident_rate": 3307.89,
    "covid_testing_rate": 70600.47,
    "covid_cases_confirmed": 32211,
    "covid_deaths": 752,
    "covid_recovered": 16073,
    "covid_active": 15386,
    "covid_fips": 10
  },
  "Diamond Princess": {
    "covid_incident_rate": 0,
    "covid_testing_rate": 0,
    "covid_cases_confirmed": 49,
    "covid_deaths": 0,
    "covid_recovered": 0,
    "covid_active": 49,
    "covid_fips": 88888
  },
  "District of Columbia": {
    "covid_incident_rate": 2874.96,
    "covid_testing_rate": 90607.43,
    "covid_cases_confirmed": 20290,
    "covid_deaths": 672,
    "covid_recovered": 14935,
    "covid_active": 4683,
    "covid_fips": 11
  },
  "Florida": {
    "covid_incident_rate": 4398.72,
    "covid_testing_rate": 54664.6,
    "covid_cases_confirmed": 944745,
    "covid_deaths": 18085,
    "covid_recovered": 0,
    "covid_active": 926660,
    "covid_fips": 12
  },
  "Georgia": {
    "covid_incident_rate": 4248.26,
    "covid_testing_rate": 38987.89,
    "covid_cases_confirmed": 451056,
    "covid_deaths": 9215,
    "covid_recovered": 0,
    "covid_active": 441841,
    "covid_fips": 13
  },
  "Grand Princess": {
    "covid_incident_rate": 0,
    "covid_testing_rate": 0,
    "covid_cases_confirmed": 103,
    "covid_deaths": 3,
    "covid_recovered": 0,
    "covid_active": 100,
    "covid_fips": 99999
  },
  "Guam": {
    "covid_incident_rate": 3928.66,
    "covid_testing_rate": 49320.77,
    "covid_cases_confirmed": 6452,
    "covid_deaths": 103,
    "covid_recovered": 4846,
    "covid_active": 1503,
    "covid_fips": 66
  },
  "Hawaii": {
    "covid_incident_rate": 1242.77,
    "covid_testing_rate": 44927.3,
    "covid_cases_confirmed": 17596,
    "covid_deaths": 233,
    "covid_recovered": 11958,
    "covid_active": 5405,
    "covid_fips": 15
  },
  "Idaho": {
    "covid_incident_rate": 5209.1,
    "covid_testing_rate": 25397.62,
    "covid_cases_confirmed": 93090,
    "covid_deaths": 866,
    "covid_recovered": 38025,
    "covid_active": 54199,
    "covid_fips": 16
  },
  "Illinois": {
    "covid_incident_rate": 5244.87,
    "covid_testing_rate": 78070.71,
    "covid_cases_confirmed": 664620,
    "covid_deaths": 12111,
    "covid_recovered": 0,
    "covid_active": 652509,
    "covid_fips": 17
  },
  "Indiana": {
    "covid_incident_rate": 4469.74,
    "covid_testing_rate": 58519.26,
    "covid_cases_confirmed": 300913,
    "covid_deaths": 5332,
    "covid_recovered": 169211,
    "covid_active": 126370,
    "covid_fips": 18
  },
  "Iowa": {
    "covid_incident_rate": 6807.84,
    "covid_testing_rate": 32974.64,
    "covid_cases_confirmed": 214792,
    "covid_deaths": 2222,
    "covid_recovered": 116837,
    "covid_active": 95733,
    "covid_fips": 19
  },
  "Kansas": {
    "covid_incident_rate": 4970.01,
    "covid_testing_rate": 26920.82,
    "covid_cases_confirmed": 144792,
    "covid_deaths": 1348,
    "covid_recovered": 3189,
    "covid_active": 140255,
    "covid_fips": 20
  },
  "Kentucky": {
    "covid_incident_rate": 3586.48,
    "covid_testing_rate": 54651.05,
    "covid_cases_confirmed": 160232,
    "covid_deaths": 1792,
    "covid_recovered": 26611,
    "covid_active": 131829,
    "covid_fips": 21
  },
  "Louisiana": {
    "covid_incident_rate": 4757.36,
    "covid_testing_rate": 70172.87,
    "covid_cases_confirmed": 221160,
    "covid_deaths": 6284,
    "covid_recovered": 185960,
    "covid_active": 28916,
    "covid_fips": 22
  },
  "Maine": {
    "covid_incident_rate": 784.4,
    "covid_testing_rate": 61400.21,
    "covid_cases_confirmed": 10544,
    "covid_deaths": 177,
    "covid_recovered": 7986,
    "covid_active": 2381,
    "covid_fips": 23
  },
  "Maryland": {
    "covid_incident_rate": 3040.14,
    "covid_testing_rate": 69002.56,
    "covid_cases_confirmed": 183797,
    "covid_deaths": 4448,
    "covid_recovered": 8511,
    "covid_active": 170838,
    "covid_fips": 24
  },
  "Massachusetts": {
    "covid_incident_rate": 3026.81,
    "covid_testing_rate": 114908.07,
    "covid_cases_confirmed": 208623,
    "covid_deaths": 10531,
    "covid_recovered": 145682,
    "covid_active": 52410,
    "covid_fips": 25
  },
  "Michigan": {
    "covid_incident_rate": 3414.13,
    "covid_testing_rate": 62717.79,
    "covid_cases_confirmed": 340964,
    "covid_deaths": 8940,
    "covid_recovered": 152267,
    "covid_active": 179757,
    "covid_fips": 26
  },
  "Minnesota": {
    "covid_incident_rate": 4902.8,
    "covid_testing_rate": 66613.87,
    "covid_cases_confirmed": 276500,
    "covid_deaths": 3321,
    "covid_recovered": 227311,
    "covid_active": 45868,
    "covid_fips": 27
  },
  "Mississippi": {
    "covid_incident_rate": 4834.4,
    "covid_testing_rate": 36663.18,
    "covid_cases_confirmed": 143879,
    "covid_deaths": 3676,
    "covid_recovered": 121637,
    "covid_active": 18566,
    "covid_fips": 28
  },
  "Missouri": {
    "covid_incident_rate": 4572.68,
    "covid_testing_rate": 47938.66,
    "covid_cases_confirmed": 280645,
    "covid_deaths": 3580,
    "covid_recovered": 0,
    "covid_active": 277065,
    "covid_fips": 29
  },
  "Montana": {
    "covid_incident_rate": 5275.28,
    "covid_testing_rate": 57820.61,
    "covid_cases_confirmed": 56381,
    "covid_deaths": 614,
    "covid_recovered": 39450,
    "covid_active": 16317,
    "covid_fips": 30
  },
  "Nebraska": {
    "covid_incident_rate": 5992.58,
    "covid_testing_rate": 65268.7,
    "covid_cases_confirmed": 115921,
    "covid_deaths": 934,
    "covid_recovered": 58057,
    "covid_active": 56930,
    "covid_fips": 31
  },
  "Nevada": {
    "covid_incident_rate": 4422.73,
    "covid_testing_rate": 49870.07,
    "covid_cases_confirmed": 136227,
    "covid_deaths": 2023,
    "covid_recovered": 0,
    "covid_active": 134204,
    "covid_fips": 32
  },
  "New Hampshire": {
    "covid_incident_rate": 1329.84,
    "covid_testing_rate": 56687.85,
    "covid_cases_confirmed": 18082,
    "covid_deaths": 512,
    "covid_recovered": 13226,
    "covid_active": 4344,
    "covid_fips": 33
  },
  "New Jersey": {
    "covid_incident_rate": 3485.49,
    "covid_testing_rate": 63894.9,
    "covid_cases_confirmed": 309588,
    "covid_deaths": 16772,
    "covid_recovered": 41550,
    "covid_active": 251266,
    "covid_fips": 34
  },
  "New Mexico": {
    "covid_incident_rate": 4013.11,
    "covid_testing_rate": 69841.27,
    "covid_cases_confirmed": 84148,
    "covid_deaths": 1400,
    "covid_recovered": 29183,
    "covid_active": 53565,
    "covid_fips": 35
  },
  "New York": {
    "covid_incident_rate": 3095.17,
    "covid_testing_rate": 93493.63,
    "covid_cases_confirmed": 602120,
    "covid_deaths": 34339,
    "covid_recovered": 83307,
    "covid_active": 484474,
    "covid_fips": 36
  },
  "North Carolina": {
    "covid_incident_rate": 3234.09,
    "covid_testing_rate": 47518.38,
    "covid_cases_confirmed": 339194,
    "covid_deaths": 5039,
    "covid_recovered": 293555,
    "covid_active": 40600,
    "covid_fips": 37
  },
  "North Dakota": {
    "covid_incident_rate": 9631.37,
    "covid_testing_rate": 135604.98,
    "covid_cases_confirmed": 73397,
    "covid_deaths": 852,
    "covid_recovered": 62697,
    "covid_active": 9848,
    "covid_fips": 38
  },
  "Northern Mariana Islands": {
    "covid_incident_rate": 188.6,
    "covid_testing_rate": 30558.17,
    "covid_cases_confirmed": 104,
    "covid_deaths": 2,
    "covid_recovered": 29,
    "covid_active": 73,
    "covid_fips": 69
  },
  "Ohio": {
    "covid_incident_rate": 3108.06,
    "covid_testing_rate": 48626.4,
    "covid_cases_confirmed": 363304,
    "covid_deaths": 6020,
    "covid_recovered": 230678,
    "covid_active": 126606,
    "covid_fips": 39
  },
  "Oklahoma": {
    "covid_incident_rate": 4495.21,
    "covid_testing_rate": 49722.86,
    "covid_cases_confirmed": 177874,
    "covid_deaths": 1649,
    "covid_recovered": 142381,
    "covid_active": 33844,
    "covid_fips": 40
  },
  "Oregon": {
    "covid_incident_rate": 1572.72,
    "covid_testing_rate": 24095.43,
    "covid_cases_confirmed": 66333,
    "covid_deaths": 826,
    "covid_recovered": 5870,
    "covid_active": 59637,
    "covid_fips": 41
  },
  "Pennsylvania": {
    "covid_incident_rate": 2500.85,
    "covid_testing_rate": 23586.73,
    "covid_cases_confirmed": 320158,
    "covid_deaths": 9846,
    "covid_recovered": 198072,
    "covid_active": 112240,
    "covid_fips": 42
  },
  "Puerto Rico": {
    "covid_incident_rate": 1509.22,
    "covid_testing_rate": 11089.73,
    "covid_cases_confirmed": 48200,
    "covid_deaths": 1032,
    "covid_recovered": 39214,
    "covid_active": 7954,
    "covid_fips": 72
  },
  "Rhode Island": {
    "covid_incident_rate": 4773.92,
    "covid_testing_rate": 138581.84,
    "covid_cases_confirmed": 50573,
    "covid_deaths": 1309,
    "covid_recovered": 3436,
    "covid_active": 45828,
    "covid_fips": 44
  },
  "South Carolina": {
    "covid_incident_rate": 4031.14,
    "covid_testing_rate": 44042.16,
    "covid_cases_confirmed": 207552,
    "covid_deaths": 4288,
    "covid_recovered": 108469,
    "covid_active": 94795,
    "covid_fips": 45
  },
  "South Dakota": {
    "covid_incident_rate": 8347.62,
    "covid_testing_rate": 35414.44,
    "covid_cases_confirmed": 73848,
    "covid_deaths": 819,
    "covid_recovered": 55679,
    "covid_active": 17350,
    "covid_fips": 46
  },
  "Tennessee": {
    "covid_incident_rate": 5045.27,
    "covid_testing_rate": 63472.45,
    "covid_cases_confirmed": 344550,
    "covid_deaths": 4301,
    "covid_recovered": 296592,
    "covid_active": 43657,
    "covid_fips": 47
  },
  "Texas": {
    "covid_incident_rate": 4000.1,
    "covid_testing_rate": 34914.81,
    "covid_cases_confirmed": 1162135,
    "covid_deaths": 21049,
    "covid_recovered": 917739,
    "covid_active": 221075,
    "covid_fips": 48
  },
  "Utah": {
    "covid_incident_rate": 5596.46,
    "covid_testing_rate": 54143.69,
    "covid_cases_confirmed": 179420,
    "covid_deaths": 797,
    "covid_recovered": 117104,
    "covid_active": 61519,
    "covid_fips": 49
  },
  "Vermont": {
    "covid_incident_rate": 595.2,
    "covid_testing_rate": 83851.96,
    "covid_cases_confirmed": 3714,
    "covid_deaths": 63,
    "covid_recovered": 2300,
    "covid_active": 1351,
    "covid_fips": 50
  },
  "Virgin Islands": {
    "covid_incident_rate": 1404.89,
    "covid_testing_rate": 25617.15,
    "covid_cases_confirmed": 1507,
    "covid_deaths": 23,
    "covid_recovered": 1407,
    "covid_active": 77,
    "covid_fips": 78
  },
  "Virginia": {
    "covid_incident_rate": 2589.63,
    "covid_testing_rate": 37034.17,
    "covid_cases_confirmed": 221038,
    "covid_deaths": 3942,
    "covid_recovered": 23253,
    "covid_active": 193843,
    "covid_fips": 51
  },
  "Washington": {
    "covid_incident_rate": 1937.48,
    "covid_testing_rate": 38009.29,
    "covid_cases_confirmed": 147537,
    "covid_deaths": 2655,
    "covid_recovered": 0,
    "covid_active": 144882,
    "covid_fips": 53
  },
  "West Virginia": {
    "covid_incident_rate": 2294.12,
    "covid_testing_rate": 57966.56,
    "covid_cases_confirmed": 41114,
    "covid_deaths": 667,
    "covid_recovered": 26769,
    "covid_active": 13678,
    "covid_fips": 54
  },
  "Wisconsin": {
    "covid_incident_rate": 6521.21,
    "covid_testing_rate": 72067.28,
    "covid_cases_confirmed": 379693,
    "covid_deaths": 3158,
    "covid_recovered": 280358,
    "covid_active": 96177,
    "covid_fips": 55
  },
  "Wyoming": {
    "covid_incident_rate": 5085.19,
    "covid_testing_rate": 27855.98,
    "covid_cases_confirmed": 29431,
    "covid_deaths": 202,
    "covid_recovered": 17452,
    "covid_active": 11777,
    "covid_fips": 56
  }
}

We can copy the output above and save it to a file with a name like usa-covid-2020-11-23.json.txt.

Now we can create a new resource in Cerb from Search » Resources by clicking the (+) icon above the list.

After saving the file, we can refer to it by name in our map KATA.


map:
  resource:
    uri: cerb:resource:map.country.usa.states
  projection:
    type: albersUsa
    scale: 650
  regions:
    properties:
      join:
        property: name
      resource:
        uri: cerb:resource:mapProperties.usaCovidByState

The map doesn’t look any different yet, but if you click on any state then you’ll see that the new covid_ properties have been merged into the bottom of the list:

We can use the map:regions:fill:choropleth feature and the covid_incident_rate property to colorize the map.


map:
  resource:
    uri: cerb:resource:map.country.usa.states
  projection:
    type: albersUsa
    scale: 650
  regions:
    properties:
      join:
        property: name
      resource:
        uri: cerb:resource:mapProperties.usaCovidByState
    fill:
      choropleth:
        property: covid_incident_rate
        classes: 8

We can now easily see that the darker states have a higher incidence rate of the virus:

Simplifying the labels

Now we can configure the labels to only show the virus-related properties:


map:
  resource:
    uri: cerb:resource:map.country.usa.states
  projection:
    type: albersUsa
    scale: 650
  regions:
    properties:
      join:
        property: name
      resource:
        uri: cerb:resource:mapProperties.usaCovidByState
    fill:
      choropleth:
        property: covid_incident_rate
        classes: 8
    label:
      title: name_en
      properties:
        covid_incident_rate:
          label: Cases/100K
          format: number
        covid_testing_rate:
          label: Tests/100K
          format: number
        covid_cases_confirmed:
          label: Cases Confirmed
          format: number
        covid_deaths:
          label: Deaths
          format: number
        covid_recovered:
          label: Recovered
          format: number
        covid_active:
          label: Active
          format: number

And we see a much more readable label:

Conclusion

You can use the same process to gather and visualize data from anywhere.

In production, we’d create a resource automation to pull the latest report via HTTP and automatically convert it from CSV to JSON. We’d only need to do that once, and we’d always have the latest data in the widget.

A resource automation could also fetch properties using a data query.

For instance, you could colorize countries or regions based on the number of clients you have in that area, with the darker regions having a higher density of clients.

References