Plugin: Bullets

"Bullets" is a collection of configurable shapes (stars, pins, flags, etc.) that you can use as chart bullets, map markers, and anywhere else.

Availability

Bullets plugin is available since amCharts 4 version 4.5.7.

Including the module

Before you can use this plugin you need to make sure you include it.

If you are using it in a TypeScript/ES6 application, you can import it as module. For pure JavaScript applications you'll need to include a .js vesion

import * as am4plugins_bullets from "@amcharts/amcharts4/plugins/bullets"; 
<script src="//cdn.amcharts.com/lib/4/plugins/bullets.js"></script>

Bullet types

There are currently 4 complex bullet types included (with more planned to add in the future) with Bullets plugin:

ClassExampleComment
FlagBullet
Draws a flag shape with optional text inside it. Flag is configurable down to colors, pole length, flag "waveness", etc.
PinBullet
Draws a pin shape with optional image and/or text inside it.
PointedCircle
Simple version of PinBullet: same shape but no option to add text or image.
ShapeBulletA universal bullet that can take a few basic shapes as circle, diamond, square, etc. It's useful when you need to connect shape of a bullet to data or simplify your bullet code.
Star
Draws a star shape with configurable number of parameters, including number of points, radius, rounding, etc.

You can click on the link for each class for a reference of all available properties. Or you can continue reading and we'll look at some of those later in the article, as well as present you with a bullet configurator.

Using

In a nutshell, a bullet from Bullets plugin is an advanced shape that can be used virtually anywhere, even as a standalone element.

We're going to look into few common usage cases shortly. But first, let's see how we instantiate a bullet shape.

Just like any other visual class that derives from Sprite, we can instantiate it using new syntax. The name scope for items in this plugin is as follows: am4plugins_bullets.. E.g.:

new am4plugins_bullets.FlagBullet();
new am4plugins_bullets.FlagBullet();

As series bullets

The most common usage of bullets is enhancing Series - mostly LineSeries.

In the course of this chapter we'll add and configure advanced bullets to a LineSeries, tied them to data, and even make them react to heat rules.

Adding a bullet to series

MORE INFO Adding bullets to series is covered in great detail here. For advanced techniques of controlling bullets, visit the article linked. We are going to provide simplified code snippets here.

Let's add a Star bullet to a simple LineSeries:

let bullet = series.bullets.push(new am4plugins_bullets.Star());
bullet.radius = 20;
bullet.pointCount = 8;
var bullet = series.bullets.push(new am4plugins_bullets.Star());
bullet.radius = 20;
bullet.pointCount = 8;
{
  // ...
  "series": [{
    // ...
    "bullets": [{
      "type": "Star",
      "radius": 20,
      "pointCount": 8
    }]
  }]
}

As you can see, we are not just creating a bullet instance. We're also setting some shape-specific settings, like in this instance we're making our star have 20 pixel radius, and 8 points.

Let's see how it turned out:

See the Pen PMoraX by amCharts team (@amcharts) on CodePen.24419

Tying to data

For purpose of this task, let's use a more complicated shape - a flag.

As it happens we have a FlagBullet in our Bullets plugin.

It's not just a shape, though. It can also contain text label.

To make it more interesting, let's selectively put flag bullets on only a few data points with unique text in them.

Here's the code (follow comments in code for breakdown):

// Creating a bullet
let bullet = series.bullets.push(new am4plugins_bullets.FlagBullet());

// Setting label to display values from data
bullet.label.text = "{bulletText}\n[bold]{valueY}[/]";
bullet.label.textAlign = "middle";

// Disabling all bullets, except ones that are explicitly enabled via data
bullet.disabled = true;
bullet.propertyFields.disabled = "hideBullet";

// Allowing controlling pole height via data (negative height means upside down flag)
// We also instruct pole to draw its color from "bulletColor" in data
bullet.propertyFields.poleHeight = "height";
bullet.pole.propertyFields.stroke = "bulletColor"; 

// Background is a WavedRectangle, which we configure, as well as instruct
// it to get its fill and border color from data field "bulletColor"
bullet.background.waveLength = 15;
bullet.background.fillOpacity = 0.5;
bullet.background.propertyFields.fill = "bulletColor";
bullet.background.propertyFields.stroke = "bulletColor";

// Add a circle to pole base.
// Bullet is a Container, so we can add there anything.
let circle = bullet.createChild(am4core.Circle);
circle.radius = 4;
circle.strokeWidth = 2;
circle.stroke = am4core.color("#fff");
circle.propertyFields.fill = "bulletColor";
// Creating a bullet
var bullet = series.bullets.push(new am4plugins_bullets.FlagBullet());

// Setting label to display values from data
bullet.label.text = "{bulletText}\n[bold]{valueY}[/]";
bullet.label.textAlign = "middle";

// Disabling all bullets, except ones that are explicitly enabled via data
bullet.disabled = true;
bullet.propertyFields.disabled = "hideBullet";

// Allowing controlling pole height via data (negative height means upside down flag)
// We also instruct pole to draw its color from "bulletColor" in data
bullet.propertyFields.poleHeight = "height";
bullet.pole.propertyFields.stroke = "bulletColor"; 

// Background is a WavedRectangle, which we configure, as well as instruct
// it to get its fill and border color from data field "bulletColor"
bullet.background.waveLength = 15;
bullet.background.fillOpacity = 0.5;
bullet.background.propertyFields.fill = "bulletColor";
bullet.background.propertyFields.stroke = "bulletColor";

// Add a circle to pole base.
// Bullet is a Container, so we can add there anything.
var circle = bullet.createChild(am4core.Circle);
circle.radius = 4;
circle.strokeWidth = 2;
circle.stroke = am4core.color("#fff");
circle.propertyFields.fill = "bulletColor";
{
  // ...
  "series": [{
    // ...
    "bullets": [{
      "type": "FlagBullet",

      // Setting label to display values from data
      "label": {
        "text": "{bulletText}\n[bold]{valueY}[/]",
        "textAlign": "middle"
      },

      // Disabling all bullets, except ones that are explicitly enabled via data
      "disabled": true,
      "propertyFields": {
        "disabled": "hideBullet"
      },

      // Allowing controlling pole height via data (negative height means upside down flag)
      // We also instruct pole to draw its color from "bulletColor" in data
      "background": {
        "waveLength": 15,
        "fillOpacity": 0.5,
        "propertyFields": {
          "fill": "bulletColor",
          "stroke": "bulletColor"
        }
      },

      // Add a circle to pole base.
      // Bullet is a Container, so we can add there anything.
      "children": [{
        "type": "Circle",
        "radius": 4,
        "strokeWidth": 2,
        "stroke": "#fff",
        "propertyFields": {
          "fill": "bulletColor"
        }
      }]
    }]
  }]
}

See the Pen amCharts 4: Using FlagBullet by amCharts team (@amcharts) on CodePen.24419

Using ShapeBullet

This type of bullet deserves a special mention.

It's unique feature is that it can take multiple basic shapes, e.g. circle, diamond, triangle, etc.

Creating a shape bullet

The shape type is set via string-based parameter shape.

For example, if we'd like to add diamond-shaped bullets to a LineSeries we could do this:

let bullet = series.bullets.push(new am4plugins_bullets.ShapeBullet());
bullet.shape = "diamond";
var bullet = series.bullets.push(new am4plugins_bullets.ShapeBullet());
bullet.shape = "diamond";
{
  // ...
  "series": [{
    // ...
    "bullets": [{
      "type": "ShapeBullet",
      "shape": "diamond"
    }]
  }]
}

Setting size of the ShapeBullet

Another convenient aspect of a ShapeBullet is that we don't have to care about a number of different size-related settings like width, height, scale, radius, etc.

There's a single property for that: size. Its value is in pixels.

Setting a size for such bullet is as easy as it sounds:

let bullet = series.bullets.push(new am4plugins_bullets.ShapeBullet());
bullet.shape = "diamond";
bullet.size = 20;
var bullet = series.bullets.push(new am4plugins_bullets.ShapeBullet());
bullet.shape = "diamond";
bullet.size = 20;
{
  // ...
  "series": [{
    // ...
    "bullets": [{
      "type": "ShapeBullet",
      "shape": "diamond",
      "size": 20
    }]
  }]
}

Binding shape and size to data

Having both shape and size as basic properties allows us to easily bind them to data via property fields and even heat rules.

let bullet = series.bullets.push(new am4plugins_bullets.ShapeBullet());
bullet.propertyFields.shape = "bullet";

series.heatRules.push({
  "target": bullet,
  "property": "size",
  "min": 15,
  "max": 30,
  "dataField": "valueY"
});
var bullet = series.bullets.push(new am4plugins_bullets.ShapeBullet());
bullet.propertyFields.shape = "bullet";

series.heatRules.push({
  "target": bullet,
  "property": "size",
  "min": 15,
  "max": 30,
  "dataField": "valueY"
});
{
  // ...
  "series": [{
    // ...
    "heatRules": [{
      "target": "bullets[0]",
      "property": "size",
      "min": 15,
      "max": 30,
      "dataField": "valueY"
    }],
    "bullets": [{
      "type": "ShapeBullet",
      "shape": "diamond",
      "size": 20
    }]
  }]
}

Here's a complete example:

See the Pen Using ShapeBullet with property fields and heat rules by amCharts team (@amcharts) on CodePen.24419

As map markers

Now, let's try these out on a Map, but go even deeper.

We are going to use PinBullet here. We'll also display an image URL for which we'll feed from data.

We'll also use "heat rules" to dynamically set radius of the bullet to indicate value set in data. Again, please follow commends in code for pointers on what we are doing.

// Image series
let imageSeries = chart.series.push(new am4maps.MapImageSeries());
let imageTemplate = imageSeries.mapImages.template;
imageTemplate.propertyFields.longitude = "longitude";
imageTemplate.propertyFields.latitude = "latitude";
imageTemplate.nonScaling = true;

// Creating a pin bullet
let pin = imageTemplate.createChild(am4plugins_bullets.PinBullet);

// Set what to display on rollover tooltip
pin.tooltipText = "{title}";
imageSeries.tooltip.pointerOrientation = "right";

// Configuring pin appearance
pin.background.fill = chart.colors.getIndex(0);
pin.background.fillOpacity = 0.7;
pin.background.pointerAngle = 120;
pin.background.pointerBaseWidth = 10;

// Adding an image with its "href" attribute tied to values in data
pin.image = new am4core.Image();
pin.image.propertyFields.href = "imageURL";

// Creating a "heat rule" to modify "radius" of the bullet based
// on value in data
imageSeries.heatRules.push({
  "target": pin.background,
  "property": "radius",
  "min": 20,
  "max": 40,
  "dataField": "value"
});

// Add a circle to pin base.
// Bullet is a Container, so we can add there anything.
let circle = pin.createChild(am4core.Ellipse);
circle.radius = 6;
circle.radiusY = 3;
circle.strokeWidth = 0;
circle.fillOpacity = 0.1;
circle.zIndex = -1;
// Image series
var imageSeries = chart.series.push(new am4maps.MapImageSeries());
var imageTemplate = imageSeries.mapImages.template;
imageTemplate.propertyFields.longitude = "longitude";
imageTemplate.propertyFields.latitude = "latitude";
imageTemplate.nonScaling = true;

// Creating a pin bullet
var pin = imageTemplate.createChild(am4plugins_bullets.PinBullet);

// Set what to display on rollover tooltip
pin.tooltipText = "{title}";
imageSeries.tooltip.pointerOrientation = "right";

// Configuring pin appearance
pin.background.fill = chart.colors.getIndex(0);
pin.background.fillOpacity = 0.7;
pin.background.pointerAngle = 120;
pin.background.pointerBaseWidth = 10;

// Adding an image with its "href" attribute tied to values in data
pin.image = new am4core.Image();
pin.image.propertyFields.href = "imageURL";

// Creating a "heat rule" to modify "radius" of the bullet based
// on value in data
imageSeries.heatRules.push({
  "target": pin.background,
  "property": "radius",
  "min": 20,
  "max": 40,
  "dataField": "value"
});

// Add a circle to pin base.
// Bullet is a Container, so we can add there anything.
var circle = pin.createChild(am4core.Ellipse);
circle.radius = 6;
circle.radiusY = 3;
circle.strokeWidth = 0;
circle.fillOpacity = 0.1;
circle.zIndex = -1;
{
  // ...
  "series": [{
    // Image series
    "type": "MapImageSeries",

    // Set what to display on rollover tooltip
    "tooltipText": "{title}",
    "tooltip": {
      "pointerOrientation": "right"
    },

    // Creating a "heat rule" to modify "radius" of the bullet based
    // on value in data
    "heatRules": [{
      "target": "children[0].background",
      "property": "radius",
      "min": 20,
      "max": 40,
      "dataField": "value"
    }],

    "mapImages": [{
      "propertyFields": {
        "longitude": "longitude",
        "latitude": "latitude"
      },
      "nonScaling": true

      "children": [{
        // Creating a pin bullet
        "type": "PinBullet",

        // Configuring pin appearance
        "background": {
          "fill": "#c00",
          "fillOpacity": 0.7,
          "pointerAngle": 120,
          "pointerBaseWidth": 10,
        },

        // Adding an image with its "href" attribute tied to values in data
        "image": {
          "type": "Image",
          "propertyFields":{
            "href": "imageURL"
          }
        },

        // Add a circle to pole base.
        // Bullet is a Container, so we can add there anything.
        "children": [{
          "type": "Ellipse",
          "radius": 6,
          "radiusY": 3,
          "strokeWidth": 0,
          "fillOpacity": 0.1,
          "zIndex": -1
        }]
      }]
    }]
  }]
}

And, here we go:

See the Pen amCharts 4: Map with PinBullet by amCharts team (@amcharts) on CodePen.24419

Bullet configuration tool

The below tool will help you visually select the best options for one of your bullets.

See the Pen amCharts 4: Bullet configurator by amCharts team (@amcharts) on CodePen.24419