Arenarium Maps

Introduction

@arenarium/maps is a high performance TypeScript library designed for applications that need to display a large number of complex markers without cluttering the map. Its purpose is to simplify visualizing and searching map related information.
To use the library, you will need an access token. First go to the sign in page and sign in to your account. After signing in, go to the dashboard and create a new token.

Installation

To install the arenarium/maps library for Google Maps using npm, run the following command in your project directory:
bash
npm install @arenarium/maps @arenarium/maps-integration-google @googlemaps/js-api-loader

Setup

To initialize the library, first add a container element to your HTML where the map will be rendered.
app.html
<div id="map"></div>
Import the MapManager and the required css from the main library.
app.ts
import { MapManager } from '@arenarium/maps'; import '@arenarium/maps/style.css';
To use Google Maps, you'll need to load the Google Maps JavaScript API and create a Google Maps provider instance. Next, create a GoogleMapsProvider instance which requires a google.maps.Map class, a google.maps.Marker.AdvancedMarkerElement class, and a google.maps.MapOptions object. Use the GoogleMapsProvider instance, along with the token key, to initialize the map manager.
app.ts
import { GoogleMapsProvider } from '@arenarium/maps-integration-google'; import * as GoogleMaps from '@googlemaps/js-api-loader'; // Load Google Maps API GoogleMaps.setOptions({ key: 'YOUR_GOOGLE_MAPS_API_KEY', v: 'weekly' }); // Import required libraries const mapsLibrary = await GoogleMaps.importLibrary('maps'); const markerLibrary = await GoogleMaps.importLibrary('marker'); // Create a Google Maps provider instance const mapElement = document.getElementById('map')!; const mapGoogleProvider = new GoogleMapsProvider(mapsLibrary.Map, markerLibrary.AdvancedMarkerElement, mapElement, { // For enabling advanced marker elements mapId: 'YOUR_GOOGLE_MAPS_MAP_ID', // Other google maps options... }); // Initialize the map manager with the provider const mapManager = await MapManager.create('YOUR_TOKEN_KEY', mapGoogleProvider); // Access the Google Maps instance for direct map interactions const mapGoogle = mapGoogleProvider.getMap();
Map features like rotation or tilting are disabled in order for the plugin to work correctly.
You can change the map's visual appearance by using the predefined styles in combination with custom StyledMapType:
app.ts
import { GoogleMapDarkStyle, GoogleMapLightStyle } from '@arenarium/maps-integration-google'; const mapTypeLight = new google.maps.StyledMapType(GoogleMapLightStyle, { name: 'Light Map' }); const mapTypeDark = new google.maps.StyledMapType(GoogleMapDarkStyle, { name: 'Dark Map' }); mapGoogle.mapTypes.set("light-id", mapTypeLight); mapGoogle.mapTypes.set("dark-id", mapTypeDark); mapGoogle.setMapTypeId("light-id"); // or "dark-id" for dark theme

Markers

A marker consists of a pin, a tooltip and an optional popup:
  • The pin is an element that should convey the marker's location and basic information like an icon or a color.
  • The tooltip is an element that should provide additional information that needs to be readable — it could be a small or large amount of information depending on the application.
  • The popup is an element that is displayed when the user clicks on the marker. It should contain additional information that is not necessary or too large for a tooltip.
Markers toggle between the pin and the tooltip elements as the user zooms in. The pin is displayed when there is no room for the tooltip. When the user zooms in and more space is available, more tooltips are displayed. Which tooltips are displayed first is determined primarily by the ranking of the markers — the higher the rank, the sooner the tooltip is displayed.
To add markers to the map, you first need to define an array of MapMarkerData objects. Provide the base marker data and the configuration for the tooltip, pin and popup. The configurations have body callbacks which should return a HTMLElement.
Use the updateMarkers method on the map manager to update the map with new markers. If some of the provided markers already exist on the map with the same id, it will be assumed that the data is the same (width, height, body...). This approach is designed for continuous updates of map markers.
When updating the markers, you can let the library try to automatically measure the sizes of the provided elements, or use the dimensions property to manually set the sizes. Using it will also slightly improve loading performance when working with a large number of complex elements.
app.ts
import { type MapMarkerProperties } from '@arenarium/maps'; const count = 100; const radius = 10; const lat = 39.8283; const lng = -98.5795; // Create markers const markers: MapMarkerProperties[] = []; for (let i = 0; i < count; i++) { const id = i.toString(); markers.push({ // A unique identifier for the marker id: id, // The rank of the marker, used for prioritization rank: i, // The latitude of the marker's location lat: lat + (Math.random() - 0.5) * radius, // The longitude of the marker's location lng: lng + (Math.random() - 0.5) * radius, // The tooltip configuration of the marker (required) tooltip: { // Callback function that returns the HTMLElement object for the tooltip body element: getTooltipElement(id), // (Optional) The desired dimensions of the marker's tooltip area dimensions: { height: 32 + (i % 16), width: 64 + (i % 16), padding: 6 }, // (Optional) The tooltip-specific style style: { background: '#ffffff', filter: 'drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3))', radius: 8 } // (Optional) The tooltip initialze callback, called when visible for the first time // initialize: async (id, element) => { } }, // (Optional) The pin configuration of the marker pin: { // (Optional) Callback function that returns the HTMLElement object for the pin body element: getPinElement(), // (Optional) The desired dimensions of the marker's pin area dimensions: { radius: 8, stroke: 1 }, // (Optional) The pin-specific colors style: { background: '#008800', stroke: '#000000' } // (Optional) The pin initialze callback, called when visible for the first time // initialize: async (id, element) => { } }, // (Optional) The popup configuration of the marker popup: { // Callback function that returns the HTMLElement object for the popup body element: getPopupElement(id), // (Optional) The desired dimensions of the marker's popup area dimensions: { height: 96 + (i % 16), width: 128 + (i % 16), padding: 6 }, // (Optional) The popup-specific colors style: { background: '#ffffff', filter: 'drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3))', radius: 8 } // (Optional) The popup initialze callback, called when visible for the first time // initialize: async (id, element) => { } } }); } mapManager.updateMarkers(markers);
The tooltip and popup body dimensions must abide by the following rules:
  • The width, height and radius of the body must be greater than zero.
  • The padding must be less than a fourth of the minimum of the width or height of the body.
The following are body callback functions for the current demo. Use them to return the HTMLElement object for the tooltip, pin and popup. You can create them manually or by using a specific frontend library such as React, Vue, Svelte, or any other library that supports creating and manipulating DOM elements.
app.ts
const getTooltipElement = (id: string) => { const element = document.createElement('div'); element.style = 'width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: #000000; font-family: Outfit, sans-serif;'; element.innerHTML = 'Tooltip ' + id; return element; }; const getPinElement = () => { const element = document.createElement('div'); element.style = 'width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; border-radius: 50%;'; return element; }; const getPopupElement = (id: string) => { const element = document.createElement('div'); element.style = 'width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: #000000; font-family: Outfit, sans-serif;'; element.innerHTML = 'Popup ' + id; return element; };
To remove all markers from the map, use the removeMarkers method:
app.ts
mapManager.removeMarkers();
To manually toggle the popup of a marker, use the showPopup and hidePopup methods:
app.ts
mapManager.showPopup(id); mapManager.hidePopup();

Configuration

MapConfiguration allows you to customize various aspects of the map and marker behavior when initializing the MapManager. You can control settings such as pin fading, popup panning, event handlers and more.
To use, simply pass your configuration object when creating the map:
app.ts
const mapConfiguration = { // (Optional) pin: { // Values for pin fadeout effect. They define how much the pin fades per zoom level fadeout: { scale: 0.1, opacity: 0.15 color: 0.2 }, // Zoom level delta in which the pin will remain visible depth: 3 }, // (Optional) popup: { // Whether to pan the map when the popup is opened pan: true }, // (Optional) events: { error: (message, err) => { console.error('Map error:', message, err); } } // And more... }; const mapManager = await MapManager.create('YOUR_TOKEN_KEY', mapProvider, mapConfiguration);
Refer to the MapConfiguration schema for all available options and further customize your map experience.

Usage

Usage is proportional to the number of marker updates. Every time you update map markers, some computation is required to calculate the optimal position of the markers. The compute is provided by an API endpoint which is used by the library internally.
Usage pricing is scales per million marker updates per month.
Usage does not increase on cache hits. For example if you use the same markers on your map multiple times, subsequent compute requests will hit the cache and the usage is not increased. The cache is invalidated after a certain time.