js-adv-markers-utils

Advanced Markers Utility Library

The @googlemaps/adv-markers-utils package is a library to simplify common patterns for using Advanced Markers in the Maps JavaScript API.

It combines all features from the google.maps.marker.AdvancedMarkerElement and google.maps.marker.PinElement classes into a single interface and adds some useful features like automatic color selection, handling for icon-fonts, and automatic handling of small to medium datasets.

To see some examples of what it can do, head over to our marker playground and test it for yourself.

Installation

If you’re using a bundler (like webpack, rollup, vite, etc.) for your project, you can install the package using npm or yarn:

npm install --save @googlemaps/adv-markers-utils

And use it in your project:

import {Marker} from '@googlemaps/adv-markers-utils';

const domElement = document.querySelector('#map');

async function main() {
  // load Google Maps API and it's Marker library
  // (see https://developers.google.com/maps/documentation/javascript/dynamic-loading)
  const {Map} = await google.maps.importLibrary('maps');
  await google.maps.importLibrary('marker');

  // create the map
  const map = new Map(domElement, {center: {lat: 53.5, lng: 10.05}, zoom: 12});

  // create a marker and add it to the map.
  const marker = new Marker({
    position: {lat: 53.5, lng: 10.05},
    map
  });
}
main();

We also support usage with native ESM and UMD, see the examples here for more information.

API Documentation

The API documentation generated by typedoc is available here:

API Documentation

Core Concepts

One key design choice made here is to use dynamic properties instead of individual getter/setter methods. So instead of marker.setMap(map); we’ll use the marker.map = map; syntax. This applies to all attributes of the marker.

Options and Attributes

Marker attributes are all the different values that make up the marker’s appearance. Attributes are generally optional and can be accessed as object-properties of the marker (e.g. marker.position or marker.color) or passed to the constructor or the setAttributes-function as an object (e.g. new Marker({color: ‘red’})).

Other values that can be specified to the Marker-constructor are called options, right now this is only the map option, but this might change in the future.

Attribute values can be specified either as static values, or as a function that will return the final value (dynamic attributes). This function has access to viewport-parameters of the map (center, zoom, heading, tilt and bounds), metadata about the marker and arbitrary user-provided data.

// static attributes
marker.color = 'lightblue';
marker.position = {lng: 34, lat: 23};

// dynamic attributes
marker.scale = ({map}) => Math.max(1, Math.pow(1.45, map.zoom) / 64);
marker.color = ({data}) => (data.someValue > 10 ? 'red' : 'blue');

The following attributes and options are implemented, all of them can both be passed to the constructor or set on the marker-object at any time:

Options

AdvancedMarkerElement Properties

These properties are directly forwarded to the AdvancedMarkerElement instance and work for all markers. See the official documentation for details.

PinElement Properties

These properties are forwarded to the PinElement instance or passed into the HTML via CSS-variables. See the official documentation for details.

HTML Marker Attributes

The remaing two attributes are used to replace the default map-pin marker with arbitrary html-elements.

Dynamic Attributes

Dynamic attributes are updated with every change to the state. This state contains information about the map (all camera parameters and current map bounds), the marker (interaction state), the current value of all other attributes and user-specified data. The parameter for a dynamic attribute function contains the following properties:

Data

All markers can be further customized using arbitrary data, which is especially useful when creating custom marker classes or when using marker-collections. Every dynamic attribute can use the user-data specified via marker.setData(myData) to change the marker styling.

MarkerCollection

Marker Collections provide a way to create markers for small to medium sized datasets with a minimal amount of code. A Marker Collection uses a very similar interface to single markers, that is it also uses static and dynamic attributes and user-supplied data. A collection is created with a single call and the markers are added to the map all at once.

const data = await loadData();

const markers = new MarkerCollection(data, {
  position: ({data}) => data?.position,
  icon: ({data}) => data?.category
});

markers.map = map;

Here is a quick example for how this looks with a simple array:

import {MarkerCollection} from '@googlemaps/adv-marker-utils';

const fieldIndex = {id: 0, latitude: 1, longitude: 2 /* ... */};
const data = [
  [1, 53.555, 10.014]
  // ...
];

const collection = new MarkerCollection(data, {
  position: ({data}) =>
    data && {
      lat: data[fieldIndex.latitude],
      lng: data[fieldIndex.longitude]
    }
});
collection.map = map;

This example will create an Advanced Marker with the specified attributes (the second parameter of the MarkerCollection constructor) for every entry in the data-array.

Marker collections also work well with data that is updated regularly. Here for example, we continually update a marker-collection with data loaded from a server:

const markers = new MarkerCollection({
  key: data => data.id,
  position: ({data}) => data?.position,
  icon: ({data}) => data?.category
});

markers.map = map;

// assuming apiStream is an async generator that yields updated data
// at a certain interval
for await (let data of apiStream) {
  markers.setData(data);
}

This also allows users to implement filtering by updating the data-array for the collection with a reduced set of records and simple configurable data-visualizations by updating the dynamic attributes.

By using the special key property, the marker-collection knows which Advanced Markers belong to which record in the data, allowing it to efficiently update Advanced Marker instances even with larger datasets.