Quickstart: Patterns and Best Practices


Installation

To install Highcharts Maps for Python, just execute:

$ pip install highcharts-maps

Importing Highcharts Maps for Python Objects

Tip

Best Practice!

This method of importing Highcharts Maps for Python objects yields the fastest performance for the import statement. However, it is more verbose and requires you to navigate the extensive Highcharts Maps for Python API.

# Import classes using precise module indications. For example:
from highcharts_maps.chart import Chart
from highcharts_maps.global_options.shared_options import SharedMapsOptions
from highcharts_maps.options import HighchartsMapsOptions
from highcharts_maps.options.plot_options.map import MapOptions
from highcharts_maps.options.series.map import MapSeries

Standardizing Your Charts

Tip

Best practice!

We really like to use JS literals written as separate files in our codebase. It makes it super simple to instantiate a SharedMapsOptions instance with one method call.

Let’s say you organize your files like so:

my_repository/
| — docs/
| — my_project/
| —— project_resources/
| ——— image_files/
| ——— data_files/
| ———— data-file-01.csv
| ———— data-file-02.csv
| ———— data-file-03.csv
| ——— highcharts_config/
| ———— shared_options.js
| ———— bar-template-01.js
| ———— bar-template-02.js
| ———— line-template.js
| ———— packed-bubble-template.js
| —— some_package/
| ——— __init__.py
| ——— package_module.py
| ——— another_module.py
| —— __init__.py
| —— __version__.py
| —— some_module.py
| — tests/
| — .gitignore
| — requirements.txt

You’ll notice that the organization has a project_resources folder. This is where you would put the various files that your application wlil reference, like your static images, or the files that contain data you might be using in your application. It also contains a highcharts_config folder, which contains several files with a .js extension. Of particular note is the file in bold, shared_options.js. This file should contain a JavaScript object literal version of the configuration settings you want to apply to all of your visualizations. This file might look something like this:

{
  chart: {
        backgroundColor: {
            linearGradient: {
              x1: 0,
              x2: 0,
              y1: 1,
              y2: 1
            },
            stops: [
                [0, 'rgb(255, 255, 255)'],
                [1, 'rgb(240, 240, 255)']
            ]
        },
        borderWidth: 2,
        plotBackgroundColor: 'rgba(255, 255, 255, .9)',
        plotBorderWidth: 1
  },
  caption: {
      align: 'center',
      floating: true,
      margin: 20,
      verticalAlign: 'top'
  },
  credits: {
      enabled: true,
      href: 'https://www.somewhere.com',
      style: {
          color: '#cccccc',
          fontSize: '8px'
      },
      text: 'Highcharts for Python'
  }
}

Now with this file, you can easily create a SharedMapsOptions instance by executing:

from highcharts_maps.highcharts import SharedMapsOptions

my_shared_options = SharedMapsOptions.from_js_literal('../../project_resources/highcharts_config/shared_options.js')

And that’s it! Now you have a SharedMapsOptions instance that can be used to apply your configuration standards to all of your charts. You can do that by delivering its JavaScript output to your front-end by calling:

js_code_snippet = my_shared_options.to_js_literal()

which will produce a string as follows:

Highcharts.setOptions({
  caption: {
    align: 'center',
    floating: true,
    margin: 20,
    verticalAlign: 'top'
  },
  chart: {
    backgroundColor: {
      linearGradient: {
        x1: 0.0,
        x2: 0.0,
        y1: 1.0,
        y2: 1.0
      },
      stops: [
        [0, 'rgb(255, 255, 255)'],
        [1, 'rgb(240, 240, 255)']
      ]
    },
    borderWidth: 2,
    plotBackgroundColor: 'rgba(255, 255, 255, .9)',
    plotBorderWidth: 1
  },
  credits: {
    enabled: true,
    href: 'https://www.somewhere.com',
    style: {
      color: '#cccccc',
      fontSize: '8px'
    },
    text: 'Highcharts for Python'
  }
});

And now you can deliver js_code_snippet to your HTML template or wherever it will be rendered.


Populating Series with Data

Configuring Your Map Data

When configuring your visualization, you can set your chart’s basic configuration settings in the Chart.options option, specifically in the Chart.options.chart property. There, you will find the ChartOptions.map property which is where you supply your map definition.

This property accepts either a MapData instance or an AsyncMapData instance which contains the GeoJSON, TopoJSON, or Shapefile definition of your map geometry.

The map defined in this property will be the default map used for all series rendered on your chart. Since most map visualizations will be rendering all series on one map, this is the most common use case.

Tip

Best practice!

It is recommended to use options.chart.map to configure your visualization’s map. This is because laying out a single visualization that has multiple series represented on multiple maps is a very complicated configuration, and is rarely necessary.

Populating the Series Data

my_series = LineSeries()

# EXAMPLE 1
# A simple array of numerical values which correspond to the Y value of the data
# point

my_series.data = [0, 5, 3, 5]

# EXAMPLE 2
# An array containing 2-member arrays (corresponding to the X and Y values of the
# data point)

my_series.data = [
    [0, 0],
    [1, 5],
    [2, 3],
    [3, 5]
]

# EXAMPLE 3
# An array of dict with named values

my_series.data = [
  {
      'x': 0,
      'y': 0,
      'name': 'Point1',
      'color': '#00FF00'
  },
  {
      'x': 1,
      'y': 5,
      'name': 'Point2',
      'color': '#CCC'
  },
  {
      'x': 2,
      'y': 3,
      'name': 'Point3',
      'color': '#999'
  },
  {
      'x': 3,
      'y': 5,
      'name': 'Point4',
      'color': '#000'
  }
]

Assembling Your Chart and Options

Using Keyword Arguments

Note

The keyword pattern outlined below is supported by both the Chart and HighchartsOptions classes

from highcharts_maps.chart import Chart
from highcharts_maps.options.series.area import LineSeries

# EXAMPLE 1. Indicating data and series_type.
my_chart = Chart(data = [[0, 1], [1, 2], [2, 3]],
                 series_type = 'line')

# EXAMPLE 2. Supplying the Series instance(s) directly.
my_chart = Chart(series = LineSeries(data = [
                                          [0, 1],
                                          [1, 2],
                                          [2, 3]
                                    ]))

Note

.add_series() is supported by both the Chart and HighchartsStockOptions classes

my_chart = Chart()
my_chart.add_series(my_series1, my_series2)

my_series = LineSeries()
my_chart.add_series(my_series)
Method Signature
.add_series(self, *series)

Adds series to the Chart.options.series property.

Parameters:

series (SeriesBase or coercable) – One or more series instances (descended from SeriesBase) or an instance (e.g. dict, str, etc.) coercable to one


Rendering Your Visualizations

from highcharts_maps.chart import Chart
from highcharts_maps.options.series.hlc import HLCSeries

my_chart = Chart(container = 'target_div',
                 options = {
                     'series': [
                         HLCSeries(data = [
                             [2, 0, 4],
                             [4, 2, 8],
                             [3, 9, 3]
                         ])
                     ]
                 },
                 variable_name = 'myChart',
                 is_maps_chart = True)

as_js_literal = my_chart.to_js_literal()

# This will produce a string equivalent to:
#
# document.addEventListener('DOMContentLoaded', function() {
#   const myChart = Highcharts.stockChart('target_div', {
#      series: {
#          type: 'hlc',
#          data: [
#            [2, 0, 4],
#            [4, 2, 8],
#            [3, 9, 3]
#          ]
#      }
#   });
# });

Downloading a Rendered Highcharts Visualization

from highcharts_maps.chart import Chart

my_chart = Chart(data = [0, 5, 3, 5],
                 series_type = 'line')

# Download a PNG version of the chart in memory within your Python code.
my_png_image = my_chart.download_chart(format = 'png')

# Download a PNG version of the chart and save it the file "/images/my-chart-file.png"
my_png_image = my_chart.download_chart(
    format = 'png',
    filename = '/images/my-chart-file.png'
)
Method Signature
.download_chart(self, filename=None, format='png', server_instance=None, scale=1, width=None, auth_user=None, auth_password=None, timeout=0.5, global_options=None, **kwargs)

Export a downloaded form of the chart using a Highcharts Export Server.

Parameters:
  • filename (Path-like or None) – The name of the file where the exported chart should (optionally) be persisted. Defaults to None.

  • server_instance (ExportServer or None) – Provide an already-configured ExportServer instance to use to programmatically produce the exported chart. Defaults to None, which causes Highcharts for Python to instantiate a new ExportServer instance with all applicable defaults.

  • format (str) –

    The format in which the exported chart should be returned. Defaults to 'png'.

    Accepts:

    • 'png'

    • 'jpeg'

    • 'pdf'

    • 'svg'

  • scale (numeric) –

    The scale factor by which the exported chart image should be scaled. Defaults to 1.

    Tip

    Use this setting to improve resolution when exporting PNG or JPEG images. For example, setting scale = 2 on a chart whose width is 600px will produce an image file with a width of 1200px.

    Warning

    If width is explicitly set, this setting will be overridden.

  • width (numeric or None) –

    The width that the exported chart should have. Defaults to None.

    Warning

    If explicitly set, this setting will override scale.

  • auth_user (str or None) – The username to use to authenticate against the Export Server, using basic authentication. Defaults to None.

  • auth_password (str or None) – The password to use to authenticate against the Export Server (using basic authentication). Defaults to None.

  • timeout (numeric or None) – The number of seconds to wait before issuing a timeout error. The timeout check is passed if bytes have been received on the socket in less than the timeout value. Defaults to 0.5.

  • global_options (HighchartsMapsOptions, HighchartsOptions or None) – The global options which will be passed to the (JavaScript) Highcharts.setOptions() method, and which will be applied to the exported chart. Defaults to None.

Note

All other keyword arguments are as per the ExportServer constructor.

Returns:

The exported chart image, either as a bytes binary object or as a base-64 encoded string (depending on the use_base64 keyword argument).

Return type:

bytes or str


Using Highcharts Maps Features

When configuring your map visualization, obviously you need to configure the actual “map” your visualization will be rendering. All maps are defined by their geometries, which is a fancy way of saying they are defined by a very precise definition of the lines and shapes that make up the map.

Typically, your map definition will be stored in either GeoJSON, TopoJSON, or ESRI Shapefile files. Highcharts for Maps natively supports these formats, automatically rendering the maps defined by their content.

The map used in your visualization can be defined in two separate places:

When configuring your visualization, you can set your chart’s basic configuration settings in the Chart.options option, specifically in the Chart.options.chart property. There, you will find the ChartOptions.map property which is where you supply your map definition.

This property accepts either a MapData instance or an AsyncMapData instance which contains the GeoJSON, TopoJSON, or Shapefile definition of your map geometry.

The map defined in this property will be the default map used for all series rendered on your chart. Since most map visualizations will be rendering all series on one map, this is the most common use case.

Tip

Best practice!

It is recommended to use options.chart.map to configure your visualization’s map. This is because laying out a single visualization that has multiple series represented on multiple maps is a very complicated configuration, and is rarely necessary.

Your map itself is defined using either GeoJSON, Topojson, or Shapefiles formats. The most important decision you will need to make is whether you wish to load your map data synchronously within Highcharts Maps for Python and then supply the chart definition and the map definition to your (JavaScript) client, or whether you would prefer to load the map definition asynchronously from your (JavaScript) client:

Tip

Best practice!

Because map data can be verbose and relatively large on the wire, we prefer to rely on the asynchronous method, but there are plenty of valid use cases where the synchronous approach is the best choice.

You can configure your visualization to load your map data asynchronously by supplying an AsyncMapData instance to either .options.chart.map or .map_data as described above.

The AsyncMapData instance contains a configuration that tells Highcharts Maps for Python how to have your (JavaScript) client download (using JavaScript’s fetch()) your map data.

The AsyncMapData instance is configured by supplying it with three pieces of information:

  • The url from where your map data should be downloaded. This should be the URL to a single file which contains either GeoJSON, Topojson, or Shapefile data.

  • An optional selector (JavaScript) function which you can use to have your (JavaScript) code modify, change, or sub-select data from your asynchronously fetched map file before rendering your chart.

  • An optional fetch_configuration which you can use to configure the details of how your (JavaScript) code will execute the (JavaScript) fetch() request from the url (typically used to supply credentials against a backend API, for example).

If you have configured an asynchronous map, Highcharts Maps for Python will automatically serialize it to JavaScript (when calling Chart.to_js_literal()) using (JavaScript) async/await and the fetch() API.

Tip

Best practice!

This approach is recommended because - in practice - it minimizes the amount of data transferred over the wire between your Python backend and your (JavaScript) client. This is particularly helpful because map geometries can be verbose and occupy a (relatively) large amount of space on the wire.