Timeline map (UK)

A modern 3D map with time slider

Updated 6 years ago by Duncan Clark

How to use this template

A template for creating fast and beautiful maps showing events over time. Useful for any data with timestamps and locations, such as website traffic or financial transactions. Events are shown as pulses on a three-dimensional WebGL map, optionally scaled and coloured based on the data.

Panning the map

Pan the map by dragging; hold down Alt while dragging to change the angle of view.

Data Requirements

At a minimum, you need a spreadsheet of data with columns for:

  • Latitude: the latitude of the event.
  • Longitude: the longitude of the event.
  • Timestamp: the start time of the event. Be sure to set the time format for your data in the settings panel!

In addition you set columns for:

  • End timestamp: the end time of the event in the same format as the main timestamp.
  • Scale: the scale at which to draw the event.
  • Colour: a column of categories to determine the color of the event. Colors are listed in the colours spreadsheet.

Colouring events

Colours for the events are listed in a separate spreadsheet with two columns:

  • Category: should contain categories matching those in the main data sheet.
  • Colour: the desired colour, in any standard format, such as “red” (i.e. named colours), “#ffeeaa”, ”rgb(255, 255, 255)”, “hsla(120, 50%, 20%, 0.4)”.

If a matching color isn't found in the spreadsheet, a fallback colour will be used. This can be set in the settings panel.

Counter

The event counter tracks the number of events of each category over time. This also doubles as a legend. In the settings panel you can decide whether the counter should measure just the number of events or the total value of those events as specified in the “Size” column. For example, if your dataset showed financial transactions, you could scale the dots by the value of the transactions and have the counter show the cumulative value of transactions in each category.

Tips

  • Try experimenting the “Terrain” and “Ocean” colour settings to change the look of the map
  • You can drag the time slider back and forward to quickly scan through the data
  • Try making a Flourish story to capture stunning animations between different views

Credits

Created by Hugh Kennedy, inspired by Kiln’s shipmap.org.

This section documents API usage specific to this template, so for an introduction we suggest you refer to the generic API documentation instead.

template: _233

version: _1

Template data

There are three different formats in which you can supply data to this template. The most convenient for you to use likely depends on the source of your data, as described below.

1. Array of arrays, and a bindings object

You can supply arrays of arrays to opts.data, which might look like:

{
    data: {
        events: [
            [ "EventsColumn1Value1", "EventsColumn2Value1",
            [ "EventsColumn1Value2", "EventsColumn2Value2",
            [ "EventsColumn1Value3", "EventsColumn2Value3",
            ...
        ],
        colors: [
            [ "ColorsColumn1Value1", "ColorsColumn2Value1",
            [ "ColorsColumn1Value2", "ColorsColumn2Value2",
            [ "ColorsColumn1Value3", "ColorsColumn2Value3",
            ...
        ]
    }
}

where each array of arrays represents the rows in a data sheet.

To tell the API how the values from each column should be associated with the keys that the template is expecting, you must also supply an object attached to opts.bindings. (The meanings of the keys in the bindings object are documented below.) The minimal bindings you can supply for this template are as shown in this example:

{
    template: "_233",
    version: "_1",
    bindings: {
        events: {
            lat: 0, // index of a column in your data
            lon: 1, // index of a column in your data
        },
        colors: {
            category: 0, // index of a column in your data
            color: 1, // index of a column in your data
        }
    },
    data: {
        events: [
            [ "EventsColumn1Value1", "EventsColumn2Value1",
            [ "EventsColumn1Value2", "EventsColumn2Value2",
            [ "EventsColumn1Value3", "EventsColumn2Value3",
            ...
        ],
        colors: [
            [ "ColorsColumn1Value1", "ColorsColumn2Value1",
            [ "ColorsColumn1Value2", "ColorsColumn2Value2",
            [ "ColorsColumn1Value3", "ColorsColumn2Value3",
            ...
        ]
    }
}

All possible bindings that you can supply are shown in this example:

{
    template: "_233",
    version: "_1",
    bindings: {
        events: {
            lat: 0, // index of a column in your data
            lon: 1, // index of a column in your data
            start_timestamp: 2, // index of a column in your data
            end_timestamp: 3, // index of a column in your data
            metadata: [4, 5, ...], // index(es) of column(s) in your data
            category: 6, // index of a column in your data
            scale: 7, // index of a column in your data
        },
        colors: {
            category: 0, // index of a column in your data
            color: 1, // index of a column in your data
        }
    },
    data: {
        events: [
            [ "EventsColumn1Value1", "EventsColumn2Value1",
            [ "EventsColumn1Value2", "EventsColumn2Value2",
            [ "EventsColumn1Value3", "EventsColumn2Value3",
            ...
        ],
        colors: [
            [ "ColorsColumn1Value1", "ColorsColumn2Value1",
            [ "ColorsColumn1Value2", "ColorsColumn2Value2",
            [ "ColorsColumn1Value3", "ColorsColumn2Value3",
            ...
        ]
    }
}

2. Array of objects with arbitrary keys, and a bindings object

This format is most likely useful when you have data from an external source, such as CSV data loaded from d3-dsv. You should supply this attached to the opts.data, which might look like:

{
        events: [
            { "EventsHeader1": ..., "EventsHeader2": ..., ... },
            { "EventsHeader1": ..., "EventsHeader2": ..., ... },
            { "EventsHeader1": ..., "EventsHeader2": ..., ... },
            ...
        ],
        colors: [
            { "ColorsHeader1": ..., "ColorsHeader2": ..., ... },
            { "ColorsHeader1": ..., "ColorsHeader2": ..., ... },
            { "ColorsHeader1": ..., "ColorsHeader2": ..., ... },
            ...
        ]
    }

... but with the keys being the column headers from your source data instead. You must also supply an object attached to opts.bindings. The minimal bindings you can supply for this template are as shown in this example:

{
    template: "_233",
    version: "_1",
    bindings: {
        events: {
            lat: "EventsHeader1",
            lon: "EventsHeader2",
        },
        colors: {
            category: "ColorsHeader1",
            color: "ColorsHeader2",
        }
    },
    data: {
        events: [
            { "EventsHeader1": ..., "EventsHeader2": ..., ... },
            { "EventsHeader1": ..., "EventsHeader2": ..., ... },
            { "EventsHeader1": ..., "EventsHeader2": ..., ... },
            ...
        ],
        colors: [
            { "ColorsHeader1": ..., "ColorsHeader2": ..., ... },
            { "ColorsHeader1": ..., "ColorsHeader2": ..., ... },
            { "ColorsHeader1": ..., "ColorsHeader2": ..., ... },
            ...
        ]
    }
}

All possible bindings that you can supply are shown in this example:

{
    template: "_233",
    version: "_1",
    bindings: {
        events: {
            lat: "EventsHeader1",
            lon: "EventsHeader2",
            start_timestamp: "EventsHeader3",
            end_timestamp: "EventsHeader4",
            metadata: ["EventsHeader5", "EventsHeader6", ...],
            category: "EventsHeader7",
            scale: "EventsHeader8",
        },
        colors: {
            category: "ColorsHeader1",
            color: "ColorsHeader2",
        }
    },
    data: {
        events: [
            { "EventsHeader1": ..., "EventsHeader2": ..., ... },
            { "EventsHeader1": ..., "EventsHeader2": ..., ... },
            { "EventsHeader1": ..., "EventsHeader2": ..., ... },
            ...
        ],
        colors: [
            { "ColorsHeader1": ..., "ColorsHeader2": ..., ... },
            { "ColorsHeader1": ..., "ColorsHeader2": ..., ... },
            { "ColorsHeader1": ..., "ColorsHeader2": ..., ... },
            ...
        ]
    }
}

(As before, the keys containing "Header" would be replaced by column names from your data source.)

3. Array of objects with template-defined keys

There is an alternative format you can use, which is likely to be easier to use if your data is not from a spreadsheet source. With this alternative format you supply your data to the template as an array of objects, attached to opts.data, where the keys must be those used by the template, as documented below. In this case there is no need to supply a bindings object, since the key names are already those expected by the template. The required properties in the data object are as follows (scroll down for a description of what each property is):

{
    template: "_233",
    version: "_1",
    data: {
    events: [
        {
            lat: ...,
            lon: ...,
            metadata: [...]
        },
        ...
    ],
    colors: [
        {
            category: ...,
            color: ...
        },
        ...
    ]
},
    ...
}

And the full list of all possible properties is as follows:

{
    template: "_233",
    version: "_1",
    data: {
    events: [
        {
            lat: ...,
            lon: ...,
            start_timestamp: ...,
            end_timestamp: ...,
            metadata: [...],
            category: ...,
            scale: ...
        },
        ...
    ],
    colors: [
        {
            category: ...,
            color: ...
        },
        ...
    ]
},
    ...
}

Meanings of the template data keys:

  • events.lat: lat
  • events.lon: lon
  • events.start_timestamp: Determines when the circles appear. If left blank all the events will appear at once. If selected, creates a time slider and shows dots over time. The date format must be specified in the settings panel.
  • events.end_timestamp: Determines when the circles disappear. If not specified the circle remains for the default duration specified in the settings panel.
  • events.metadata: One or more columns to show in the popups
  • events.category: Used to determine the colours of the circles, as configured in the separate data sheet
  • events.scale: Determines the size of the circles.
  • colors.category: The category of the event. Should match the categories specified in the main data sheet.
  • colors.color: Can be a hex value such as “#FF7E5D” or an HTML colour name such as “red”.

Template settings

Options for opts.state.

Debug Settings

debug_show_overlay_buffer boolean

Display overlay buffer.

debug_show_picking_buffer boolean

Display picking nuffer.

events_pulse_standalone boolean

Draw standalone events.

location string

Map location. Should be either "world" or "uk"

heightmap_url url

Heightmap URL. Greyscale image file to use as the heightmap. Should be 1024x1024, 2048x2048 or 4096x4096.

heightmap_height number

Height. The total height of the terrain covered by the heightmap. Scales terrain vertically with higher values.

heightmap_sea_level number

Sea level.

Layer: Events (Pulse)

events_pulse_enabled boolean

Show events.

events_pulse_simple boolean

Allowed values:

  • false (Pulses)
  • true (Circles)

events_pulse_fallback_color color

Default colour. Circle colour when no category colour specified

events_pulse_default_scale number

Default size. The size of the circle when the “Scale” column is not selected or contains an invalid number for the given event

events_pulse_min_scale number

Min size. The smallest circle size allowed; usually best left at zero so that the sizes of the circles are proportional to the values in the data

events_pulse_max_scale number

Max size. The size of the largest circle

events_pulse_on_ocean boolean

Show events over ocean.

events_pulse_min_latitude number

Min lat . The minimum latitude bounds in your dataset. Used to change the position of events relative to the heightmap.

Min: -90

Max: 90

events_pulse_max_latitude number

Max lat. The maximum latitude bounds in your dataset. Used to change the position of events relative to the heightmap.

Min: -90

Max: 90

events_pulse_min_longitude number

Min lng. The minimum longitude bounds in your dataset. Used to change the position of events relative to the heightmap.

Min: -180

Max: 180

events_pulse_max_longitude number

Max lng. The maximum longitude bounds in your dataset. Used to change the position of events relative to the heightmap.

Min: -180

Max: 180

Event counter and graph

counter_type string

Count events. Enables the events counter and graph. Use “By count” to display the number of events, or “By size” to display the number scaled by the “Scale” column.

Allowed values:

  • off (Off)
  • count (By count)
  • scale (By size)

counter_other boolean

Include.

counter_other_label string

Label.

counter_enabled boolean

Show counter.

counter_labels boolean

Category labels.

graph_enabled boolean

Show graph.

graph_height number

Graph height (%).

label_cumulative string

“Total” label.

label_sampled string

“Rate” label.

number_prefix string

Prefix.

number_multiplier number

Multiplier.

number_decimals number

Decimals.

number_suffix string

Suffix.

timeline_bg_opacity number

Opacity of shading behind graph.

Max: 1

controls_bg_opacity number

Opacity of shading behind counter.

Max: 1

Time

events_pulse_date_format string

Date/time format in data. The date/time format in your data sheet. If it isn’t in the list, you can enter a custom format using d3-time-format syntax. See npmjs.com/package/d3-time-format for details.

Predefined values:

  • %Y-%m-%dT%H:%M:%S.%LZ (1986-01-28T11:39:13.000Z)
  • %Y-%m-%d (1986-01-28)
  • %m/%d/%Y (01/28/1986)
  • %d/%m/%Y (28/01/1986)
  • %d-%b-%y (28-Jan-86)
  • %m/%Y (01/1986)
  • %b %Y (Jan 1986)
  • %B %d (January 28)
  • %d %b (28 Jan)
  • %Y (1986)
  • %B (January)
  • %b (Jan)
  • %A (Tuesday)
  • %a (Tue)
  • %H:%M:%S (11:39:13)
  • %I:%M %p (11:39 AM)
  • %H:%M (11:39)

events_pulse_date_format_display string

Date/time format on slider. The date/time format in your data sheet. If it isn’t in the list, you can enter a custom format using d3-time-format syntax. See npmjs.com/package/d3-time-format for details.

Predefined values:

  • (Auto)
  • %Y-%m-%d (1986-01-28)
  • %m/%d/%Y (01/28/1986)
  • %d/%m/%Y (28/01/1986)
  • %d-%b-%y (28-Jan-86)
  • %m/%Y (01/1986)
  • %b %Y (Jan 1986)
  • %B %d (January 28)
  • %d %b (28 Jan)
  • %Y (1986)
  • %B (January)
  • %b (Jan)
  • %A (Tuesday)
  • %a (Tue)
  • %H:%M:%S (11:39:13)
  • %I:%M %p (11:39 AM)
  • %H:%M (11:39)

timeline_playing boolean

Play on load.

events_pulse_transition_duration number

Pulse duration. Duration in seconds of the pulse animation

Max: 5

timeline_duration number

Timeline duration. How long in seconds it takes to play through the timeline

Min: 5

timeline_speed number

Time multiplier. Speeds up or slows down the time. Affects the animations as well as the timeline. Set to 1 for normal time.

camera_transition_speed number

Pan/zoom duration. How long to spend moving the camera when changing slides in story mode, in seconds.

Popups

events_pulse_tooltips boolean

Enable popups. Display tooltips when the user hovers over an event circle, based on the column assigned for Labels

events_pulse_tooltips_custom boolean

Custom popups content. Allows you to create bespoke popups using HTML

events_pulse_tooltips_content text

Enable popups. The text to appear in the popup. You can use {{column_name}} to add a value from any selected column. You can add extra columns to “Popups” if needed. Advanced users can include HTML to apply layouts, formatting, images, etc.

Layer: Flat Map

box_terrain_enabled boolean

Show map.

box_terrain_mode string

Mode.

Allowed values:

  • brick (3D Block)
  • circle (Feathered Circle)
  • square (Feathered Square)

box_terrain_color color

Colorize map.

box_terrain_height number

Block height.

box_terrain_textured boolean

Use texture on map.

box_terrain_src url

Custom map file.

Layer: Terrain (Mesh)

mesh_terrain_color_ground color

Ground.

mesh_terrain_color_coast color

Coast.

mesh_terrain_color_cliff color

Cliffs.

mesh_terrain_color_peak color

Peaks.

mesh_terrain_enable_lighting boolean

Enable lighting.

mesh_terrain_enable_noise boolean

Enable noise.

Fog and background

fog_color color

Colour.

fog_density number

Density.

Light

lighting_light1_color color

Light #1: Colour.

lighting_light1_polar number

Polar angle.

Min: -90

Max: 90

lighting_light1_azimuth number

Azimuth angle.

Min: -180

Max: 180

lighting_light2_color color

Light #2: Colour.

lighting_light2_polar number

Polar angle.

Min: -90

Max: 90

lighting_light2_azimuth number

Azimuth angle.

Min: -180

Max: 180

Layer: Terrain (Level of Detail)

lod_terrain_enabled boolean

Enabled.

lod_terrain_color_ground color

Ground colour.

lod_terrain_color_cliff color

Cliff colour.

lod_terrain_color_peak color

Peak colour.

Layer: Terrain (Low Poly)

poly_enabled boolean

Enabled.

poly_color1 color

Colour 1.

poly_color2 color

Colour 2.

poly_shading number

Shading.

poly_surface_specularity number

Surface specularity.

poly_surface_shininess number

Surface shininess.

poly_resolution number

Resolution. Determines the total number of polygons used to draw the terrain. Higher values will create more polygons and more detail, but will run more slowly.

Min: 2

Max: 1024

Layer: Ocean (Realistic)

ocean_enabled boolean

Show ocean.

ocean_clip boolean

Clip ocean.

ocean_color color

Water.

border_color color

Border.

ocean_stripe_color color

Stripe.

ocean_specular number

Water shininess.

border_thickness number

Border thickness.

Min: -0.05

Max: 1

ocean_stripe_opacity number

Stripe opacity.

Max: 1

ocean_stripe_thickness number

Stripe thickness.

Layer: Ocean (Low Poly)

poly_ocean_enabled boolean

Enabled.

poly_ocean_color color

Water colour.