Mapalizer® Toolbar Documentation

Install the toolbar, integrate it with your preferred map library, tune its behavior with configuration, customize its appearance with CSS variables, and understand the public API.

Try the Configurator → View API Demo Current release: OSM data:

How Mapalizer Works

Mapalizer overlays location-quality scoring on real estate maps without showing raw POI clutter. The toolbar lets visitors weight categories such as transit, dining, parks, shopping, health, and daily essentials, then visualizes the result directly on the map.

  • The toolbar reads the current viewport and selected user preferences.
  • Mapalizer requests score layers and supporting locale data from the Mapalizer CDN.
  • The map displays score layers that can be toggled on or off.
  • The same state can be queried programmatically through the public JavaScript API.

How scoring works

Each real-estate-relevant place (selected from OpenStreetMap Features) belongs to a subcategory, and each subcategory has its own maximum score and its own maximum distance. As distance increases, that contribution decreases linearly until it reaches zero at the configured maximum distance.

For example, two different restaurants standing side-by-side will not have a greater impact than the maximum score that can be obtained from the "Restaurant" subcategory. In this way, different subcategories are added together to determine the main category score between 0 and 1.

When calculating the score of a point on the map, the average score of the categories selected from the Mapalizer Toolbar at that point is taken into account. By default, all selected categories are weighted the same when calculating the average score.

Getting Started

A simple launch checklist for your Mapalizer setup.

Create account and register your sites

Create your account at portal.mapalizer.com, then add the websites where Mapalizer is allowed to load.

Create API tokens

Generate publishable API tokens for each site at portal.mapalizer.com/tokens and keep test and production usage separate.

Install Mapalizer

Add the async loader in your page <head>. See Installation for setup details.

Use Mapalizer

Choose your integration path — toolbar on a map, score widget, or scores only. See Use Mapalizer for details.

Try Configurator and Toolbar Themes

Use the Configurator to define your starting settings, then explore Toolbar Themes to find the style that fits your website best.

Try API examples

Open API Examples to see programmatic scoring and filtering in action. See Programmatic Scoring for details.

Installation

The recommended installation pattern is an asynchronous loader in the page <head>. It creates the global mapalizer object immediately and queues calls until the runtime bundle finishes loading.

<script type="text/javascript">
(function (w, d, methods, url) {
    const m = w.mapalizer = w.mapalizer || { _queue: [] };
    methods.forEach(fn => { m[fn] = (...args) => m._queue.push({ fn, args }); });
    const s = d.createElement('script');
    s.async = true;
    s.src = url;
    s.onload = function () {
        if (w.mapalizer._flushQueue) w.mapalizer._flushQueue();
    };
    d.head.appendChild(s);
})(window, document,
    ['addToMap', 'configure', 'removeFromMap', 'getState', 'getScores', 'renderWidget', 'initWidgets'],
    '');
</script>

Content Security Policy (CSP)

Directive Value Purpose
script-src https://cdn.mapalizer.com Main JavaScript bundle
style-src https://cdn.mapalizer.com Toolbar stylesheet loaded by the runtime
connect-src https://cdn.mapalizer.com, https://data.mapalizer.com, https://api.mapalizer.com Locale JSON, score tile JSON, and session token requests

Use Mapalizer

Choose the setup path that matches your use case:

Toolbar on a map

Interactive score overlay directly onto your map:

Score widget (no map required)

Embed a self-contained score badge on any page:

  • HTML embed — add the attribute to any container for zero-JS auto-init. Coordinates and options live in data-* attributes.
  • mapalizer.renderWidget(element, point, options) — call from JavaScript to render a score badge into any container element. Works standalone with a site token or map-attached.

Scores only (no map required)

Score arbitrary coordinates without any toolbar UI or map instance:

Public API Methods

The toolbar exposes a small public API designed for site integration and state-aware workflows. Each method below has its own anchor, input contract, and return shape.

Method Purpose
addToMap(map, config?) Inject the toolbar into a supported map instance.
removeFromMap(map) Remove toolbar UI and detach related resources.
configure(map, partialConfig) Live-update language, theme, toolbar state, layer settings, or category selection without reloading the toolbar.
getState(map?) Read the current toolbar state.
getScores(points, options?) Score arbitrary coordinates. Pass options.map to read from a live toolbar, or omit it for map-free standalone scoring.
renderWidget(element, point, options?) Render a self-contained score-badge widget into a container element. Works with a live map or standalone with a site token.
HTML embed ([data-mpl-widget]) Render the same widget from static markup using data-* attributes. No per-widget JavaScript call required.
initWidgets(root?) Scan root (or the full document) for [data-mpl-widget] elements and initialise any that have not been rendered yet.

mapalizer.addToMap(map, config?)

Initializes Mapalizer on a supported map instance and injects the toolbar UI.

ParameterTypeRequiredDescription
mapObjectYesGoogle Maps, Leaflet, or Mapbox map instance.
configObjectNoConfiguration object. Include auth.siteToken for test and production websites. See Configuration Object.

Returns: Promise<{ ok: boolean, error?: string, status?: number }>. The toolbar is only inserted into the page after authentication succeeds. Use await or .then() to react to the outcome.

  • { ok: true } — toolbar inserted successfully.
  • { ok: false, error: 'auth_failed', status: 403 } — site token rejected.
  • { ok: false, error: 'timeout' } — required resources did not load in time.
  • { ok: false, error: 'browser_not_supported' } — browser lacks required APIs (no toolbar inserted).

When using the async loader, you can listen for the mapalizer:ready or mapalizer:failed window events.

Supported map libraries

  • Google Maps JavaScript API
  • Leaflet
  • Mapbox JS
// Google Maps
const map = new google.maps.Map(document.getElementById('map'), {
    center: { lat: 52.505, lng: 13.389 },
    zoom: 13,
});
const result = await mapalizer.addToMap(map, {
    auth: {
        siteToken: 'mpl_pk_live_REPLACE_ME'
    }
});
if (!result.ok) console.error('Mapalizer failed:', result.error);
// Leaflet
const map = L.map('map').setView([52.505, 13.389], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">OpenStreetMap contributors</a>'
}).addTo(map);
const result = await mapalizer.addToMap(map, {
    auth: {
        siteToken: 'mpl_pk_live_REPLACE_ME'
    }
});
if (!result.ok) console.error('Mapalizer failed:', result.error);
// Mapbox JS (Leaflet-based)
L.mapbox.accessToken('your-mapbox-token');
const map = L.mapbox.map('map', 'mapbox.streets').setView([52.505, 13.389], 13);
const result = await mapalizer.addToMap(map, {
    auth: {
        siteToken: 'mpl_pk_live_REPLACE_ME'
    }
});
if (!result.ok) console.error('Mapalizer failed:', result.error);

mapalizer.removeFromMap(map)

Removes Mapalizer from a specific map instance.

ParameterTypeRequiredDescription
mapObjectYesThe map instance from which the toolbar should be removed.

Returns: Boolean indicating whether removal succeeded.

mapalizer.removeFromMap(map);

mapalizer.configure(map, partialConfig)

Live-updates settings for an active toolbar. Only the keys you include in partialConfig are applied — all other settings remain unchanged.

ParameterTypeRequiredDescription
mapObjectYesThe map instance the toolbar is attached to.
partialConfigObjectYesPartial config object. Any subset of the fields below.

partialConfig fields

FieldTypeDescription
languageStringSwitch the toolbar UI language. Also updates the inherited default for future addToMap() calls. Supported language code list: .
themeStringSwitch the toolbar theme. Supported values: 'light', 'dark'.
toolbar.activeStateBooleanProgrammatically enable (true) or disable (false) the toolbar. Does not fire mapalizer:statechange — programmatic toggles carry no side effects; the event is reserved for user-initiated actions (which also trigger tile prefetching).
toolbar.verticalStringAnchor edge: 'top' or 'bottom'.
toolbar.horizontalStringAnchor edge: 'left' or 'right'.
toolbar.desktopObjectDesktop positioning override. Shape: { style: { top, right, bottom, left } }.
toolbar.mobileObjectMobile positioning override. Shape: { maxWidth: Number, hidden: Boolean, style: { … } }.
layer.opacityNumberHeatmap layer opacity (0–1).
layer.contourThresholdNumberContour visibility threshold (0–1). Same path as the drag handle in the colour bar.
layer.contourColorsArray<String>Array of hex colour values for contour lines. Triggers a layer and colour-bar redraw.
layer.transitionDurationNumberSVG tile fade duration in milliseconds. Takes effect on the next tile transition — no immediate redraw.
categoriesObjectSelectively update category selection state. Only isSelected is accepted per key — weight, icon, and sortOrder are init-only. Keys are case-insensitive; unknown keys are skipped with a warning. Fires mapalizer:statechange (type CATEGORIES) only when at least one isSelected value actually changes — passing the same selection state is a no-op and dispatches no event. See category key map.

Returns: Boolean. true if the toolbar was found and updated, false if no toolbar is attached to the given map.

// Change language
mapalizer.configure(map, { language: 'de' });

// Switch to dark theme and disable toolbar
mapalizer.configure(map, { theme: 'dark', toolbar: { activeState: false } });

// Reposition toolbar to bottom-left
mapalizer.configure(map, {
  toolbar: {
    vertical: 'bottom',
    horizontal: 'left',
    desktop: { style: { bottom: '20px', left: '10px' } },
    mobile:  { maxWidth: 768, hidden: false, style: { bottom: '20px', left: '10px' } }
  }
});

// Update layer settings and category selection together
mapalizer.configure(map, {
  layer: {
    opacity: 0.6,
    contourThreshold: 0.3,
    contourColors: ['#00ff99', '#33cc88', '#66bb55', '#99aa33', '#ccaa00', '#ffcc00', '#ff9900', '#ff6600', '#ff3300', '#cc0000'],
    transitionDuration: 400
  },
  categories: {
    TRANSIT:  { isSelected: false },
    DINING:   { isSelected: true },
    SHOPPING: { isSelected: false }
  }
});

mapalizer.getState(map?)

Returns the current runtime state of the toolbar as a plain object.

ParameterTypeRequiredDescription
mapObjectNoIf omitted, returns the state of the first active toolbar.

Returns: Object | null. Returns null if no toolbar is found.

{
  toolbar: {
    activeState: true
  },
  layer: {
    contourThreshold: 0.4
  },
  categories: {
    TRANSIT: { isSelected: true },
    ESSENTIALS: { isSelected: true },
    HEALTH: { isSelected: false },
    NATURE: { isSelected: false },
    DINING: { isSelected: true },
    SPORTS: { isSelected: false },
    SHOPPING: { isSelected: true },
    CULTURE: { isSelected: false }
  }
}

mapalizer.getScores(points, options?)

Scores arbitrary geographic coordinates. Pass options.map to read from a live toolbar attached to that map instance (map-attached mode), or omit it to score without any map or toolbar (standalone mode). Tiles are fetched in parallel, deduplicated by tile, and cached in memory for subsequent calls.

ParameterTypeRequiredDescription
pointsArray<{ lat: number, lng: number }>YesCoordinates to score, returned in the same order as the input array.
optionsObjectNoOptions object. Omit entirely for standalone mode, or pass at minimum { map } for map-attached mode.

options fields

FieldTypeModeDescription
mapObjectMap-attachedThe map instance the toolbar is attached to. When present, all other options are ignored; toolbar state is read live.
verticalKeyStringStandaloneData vertical to score against. Currently only 're' (real estate) is supported. Defaults to 're'.
auth.siteTokenStringStandalonePublishable site token from portal.mapalizer.com. Required everywhere. Use a mpl_pk_test_ token on localhost and a mpl_pk_live_ token on real domains.
languageStringStandaloneLanguage code for localizedTitle values in the response (e.g. 'de', 'fr'). Defaults to English. Ignored in map-attached mode (toolbar language is used instead). Supported language code list: .
categoriesObjectStandaloneSelectively exclude categories from the totalScore calculation. In standalone mode all categories start as isSelected: true (there is no toolbar UI to drive selection), so totalScore reflects every category by default. Pass { [key]: { isSelected: false } } to opt specific categories out of the weighted total. Has no effect on individual category scores — only on which categories contribute to totalScore. See category key map for available keys.

Returns: Promise<GetScoresResult>.

GetScoresResult

{
  ok: Boolean,
  error: String | undefined,   // only present when ok is false
  toolbar: {
    activeState: Boolean | null,
    contourThreshold: Number | null
  },
  categories: {
    TRANSIT: {
      title: String,
      localizedTitle: String,
      isSelected: Boolean,
      weight: Number,
      sortOrder: Number
    }
    // ESSENTIALS … CULTURE
  },
  results: [
    {
      lat: Number,
      lng: Number,
      dataFound: Boolean,
      scores: { TRANSIT: Number /* …CULTURE */ } | null,
      totalScore: Number | null,
      aboveThreshold: Boolean | null
    }
  ]
}
FieldTypeDescription
okBooleantrue when scoring succeeded; false on auth or configuration failure. Check this before reading results.
errorString | undefinedError code when ok is false. Values: 'toolbox_not_found', 'browser_not_supported', 'invalid_settings', 'missing_site_token', 'auth_failed'.
toolbar.activeStateBoolean | nullWhether the toolbar is currently on. Always null in standalone mode.
toolbar.contourThresholdNumber | nullCurrent threshold in the 0-1 range. Always null in standalone mode.
categoriesObjectCurrent category metadata and selection state keyed by title (e.g. TRANSIT, ESSENTIALS). See category key map.
resultsArray<Object>One result per input point, in the same order as the input. Each item uses the structure below.

results[] item

FieldTypeDescription
latNumberEchoed-back latitude from the input point.
lngNumberEchoed-back longitude from the input point.
dataFoundBooleanfalse means there is no tile coverage for this coordinate.
scoresObject | nullPer-category raw scores keyed by title (e.g. TRANSIT, ESSENTIALS), each in the 0-1 range.
totalScoreNumber | nullWeighted aggregate score in the 0-1 range, rounded to 2 decimals.
aboveThresholdBoolean | nulltrue, false, or null when threshold comparison is inactive (standalone mode or toolbar off).

Map-attached mode

// Only selected categories contribute to totalScore
const response = await mapalizer.getScores([
    { lat: 52.505, lng: 13.389 },
    { lat: 48.858, lng: 2.295 }
], { map });

if (response.ok) {
    response.results.forEach(function (result) {
        if (!result.dataFound) {
            console.log(result.lat, result.lng, '— no data coverage');
        } else {
            console.log(result.lat, result.lng, '— score:', result.totalScore);
        }
    });
}

Standalone mode

// All categories contribute to totalScore (default)
const response = await mapalizer.getScores([
    { lat: 52.505, lng: 13.389 },
    { lat: 48.858, lng: 2.295 }
], {
    auth: {
        siteToken: 'mpl_pk_live_REPLACE_ME'
    }
});
...

mapalizer:statechange event

Listen for state changes when you want your application to react to threshold updates, category selection, or toolbar on/off toggles. Category keys use the title-based format shown in the category key map (e.g. TRANSIT, ESSENTIALS).

{
  eventType: 'CATEGORIES',
  map: [map instance],
  state: {
    toolbar: {
      activeState: true
    },
    layer: {
      contourThreshold: 0.4
    },
    categories: {
      TRANSIT: { isSelected: true },
      ESSENTIALS: { isSelected: true },
      HEALTH: { isSelected: false },
      NATURE: { isSelected: false },
      DINING: { isSelected: true },
      SPORTS: { isSelected: false },
      SHOPPING: { isSelected: true },
      CULTURE: { isSelected: false }
    }
  }
}
eventTypeMeaning
ACTIVE_STATEThe toolbar was turned on or off.
CONTOUR_THRESHOLDThe contour threshold slider changed.
CATEGORIESThe selected category set changed.

mapalizer:ready event

Dispatched on window when the async loader has finished loading the runtime bundle and flushed all queued calls. This is the recommended signal to use when you need the return value from addToMap() or renderWidget() — both methods return meaningful results only after the library is fully loaded.

window.addEventListener('mapalizer:ready', function () {
    mapalizer.addToMap(map, { auth: { siteToken: 'mpl_pk_live_REPLACE_ME' } })
        .then(function (result) {
            if (!result.ok) console.error('Mapalizer failed:', result.error);
        });
});

mapalizer:failed event

Dispatched on window when the async loader fails to initialise — for example if the bundle script fails to load or the browser is not supported. Use this to show a fallback or suppress further Mapalizer calls.

window.addEventListener('mapalizer:failed', function (e) {
    console.warn('Mapalizer could not load:', e.detail);
});

mapalizer.renderWidget(element, point, options?)

Renders a self-contained score-badge widget into element for a single geographic coordinate. Works in two modes: map-attached (inherits auth and settings from a live toolbar) or standalone (no map required — supply a siteToken).

Returns a Promise that always resolves (never rejects). On success the promise value includes a destroy() function to remove the widget and the raw scores object.

// Map-attached — inherits auth from the toolbar on googleMapInst
const { ok, scores, destroy } = await mapalizer.renderWidget(
    document.getElementById('my-widget'),
    { lat: 52.52, lng: 13.405 },
    { map: googleMapInst }
);

// Standalone — no map needed
const result = await mapalizer.renderWidget(
    document.getElementById('my-widget'),
    { lat: 52.52, lng: 13.405 },
    { auth: { siteToken: 'mpl_pk_live_…' } }
);
if (result.ok) console.log(result.scores);

Options

OptionTypeDefaultDescription
mapMap instanceMap-attached mode. Inherits auth, vertical, language, and theme from the toolbar already added to this map.
auth.siteTokenstringRequired in standalone mode. Your publishable site token.
verticalKeystring're'Standalone mode only. Data vertical (e.g. 're' for real estate).
languagestring'en'BCP 47 language code for category labels (e.g. 'de', 'fr'). Supported language code list: .
theme'light' | 'dark''light'Visual theme. In map-attached mode defaults to the toolbar theme.
categoriesstring[]allRestrict which category badges are shown. E.g. ['TRANSIT','ESSENTIALS','HEALTH'].
excludeCategoriesstring[]noneExclude specific category keys before rendering. This is especially meaningful when used with showTopCategories.
showTopCategoriesinteger ≥ 1Show only the top N categories sorted by score descending.
titlestring | nulllocale defaultWidget heading. Pass null or '' to suppress the title.

You can exclude lessly used categories like Health and Culture with excludeCategories: ['HEALTH', 'CULTURE']. This is especially meaningful when used together with showTopCategories.

Return value

PropertyTypeDescription
okbooleantrue on success.
errorstring?Error key when ok is false: invalid_element, invalid_settings, missing_site_token, auth_failed, no_data, toolbox_not_found, browser_not_supported, superseded.
scoresobject?Raw 0–1 scores keyed by category (e.g. { TRANSIT: 0.87, ESSENTIALS: 0.61, … }). Present only when ok is true.
destroy()functionRemoves the widget node from element. Idempotent. Safe to call even if a newer render has already replaced this one.

Prefer static markup over JavaScript calls? See HTML embed ([data-mpl-widget]) for the zero-JS integration path.

HTML embed ([data-mpl-widget])

For zero-JavaScript integration, add the data-mpl-widget attribute to any container element. The widget auto-initialises on DOMContentLoaded:

<div data-mpl-widget
     data-token="mpl_pk_live_…"
     data-lat="52.52"
     data-lng="13.405"
     data-language="en"
     data-theme="light"
     data-show-top="3"
     data-exclude-categories="HEALTH,CULTURE"></div>
AttributeRequiredDescription
data-mpl-widgetYesMarks the element for auto-init.
data-tokenYesYour publishable site token (same as auth.siteToken).
data-latYesLatitude of the location to score.
data-lngYesLongitude of the location to score.
data-languageNoBCP 47 language code for labels (e.g. de, fr). Defaults to en. Supported language code list: .
data-themeNolight or dark. Defaults to light.
data-verticalNoData vertical key. Defaults to re (real estate).
data-categoriesNoComma-separated category keys to show (e.g. TRANSIT,ESSENTIALS,HEALTH). Defaults to all categories.
data-show-topNoShow only the top N categories sorted by score. E.g. 4.
data-exclude-categoriesNoComma-separated category keys to exclude. Especially meaningful together with data-show-top; for example HEALTH,CULTURE excludes Health and Culture.
data-titleNoWidget heading. Omit for locale default. Set to "" to suppress the title entirely.

For elements rendered after page load (SPA route changes, CMS hydration), call mapalizer.initWidgets(root?) to initialise any new [data-mpl-widget] elements inside root.

CSS customisation

The widget root carries class mpl mpl-widget. All visual tokens are CSS custom properties on .mpl and should be overridden on .mpl itself, or via a selector that targets it.

Font family: The Mapalizer stylesheet sets the font on .mpl via --mpl-font-family with !important. Overriding font-family directly on .mpl-widget will not work — set the CSS variable on .mpl instead:

/* Override font for one widget */
#my-widget .mpl {
    --mpl-font-family: 'Lato', sans-serif;
    --mpl-font-size-base: 13px;
}

/* Or globally */
body .mpl {
    --mpl-font-family: var(--app-font-family, Arial, sans-serif);
}

The unminified reference stylesheet is available at . Use it as a starting point when you need full control — disable automatic CSS injection with loadCss: false and host your own copy:

<link rel="stylesheet" href="/your-path/mapalizer.css">

⚠️ Ring colours and badge geometry are intentionally not customisable. They encode score meaning and should remain consistent with the Mapalizer design language.

🌙 Dark mode is applied automatically when you pass theme: 'dark' — the .mpl.mpl-theme-dark modifier is added to the widget root. No extra CSS is needed from the host page.

mapalizer.initWidgets(root?)

Scans root (or the full document when omitted) for [data-mpl-widget] elements and calls renderWidget() for each one that has not been initialised yet. Already-rendered elements are skipped — calling initWidgets() multiple times is safe.

This method is called automatically on DOMContentLoaded, so elements present in the initial HTML require no manual call. You only need to call it explicitly for elements added to the DOM after page load.

When to use initWidgets() vs renderWidget()

renderWidget()initWidgets()
Integration style JavaScript-driven. You pass the element, coordinates, and options explicitly in code. HTML-driven. Coordinates and options live in data-* attributes; no per-widget JavaScript is needed.
When to call Any time — on page load, after a user action, or when coordinates arrive from an API. After adding new [data-mpl-widget] elements to the DOM (SPA route change, CMS hydration, infinite scroll). For elements in the initial HTML, it is called automatically.
Return value Promise<{ ok, scores, destroy }> — lets you handle success/error and control the widget lifecycle. undefined — fire and forget.
Best for Dynamic coordinates, result handling, or when you need the destroy() handle. CMS pages, static HTML templates, or any setup where widget markup is generated server-side.

Scoping to a subtree

Pass a DOM element as root to limit the scan to that subtree. This is more efficient than scanning the whole document and avoids re-checking already-rendered content.

// SPA route change — only scan the newly mounted view
function onRouteChange(newView) {
    document.getElementById('app').replaceChildren(newView);
    mapalizer.initWidgets(document.getElementById('app'));
}

// Full-document scan — rarely needed; runs automatically on DOMContentLoaded
mapalizer.initWidgets();

Programmatic Scoring

Use getScores() to score arbitrary coordinates. Pass options.map to read from a live toolbar attached to that map, or omit it for standalone scoring — useful for custom marker filtering, ranked side panels, or synchronizing map state with listing cards.

💡 Try it live: open the API Examples page, drop some markers, then open your browser DevTools (F12) and watch the Console tab — each getScores call prints the exact parameters used and the full response object so you can explore the result shape interactively.


Filtering markers on toolbar state changes

A common listing-page pattern is to grey out markers that fall below the active score threshold whenever the user adjusts the toolbar, and to restore them when the toolbar is turned off. The technique combines two APIs:

  • mapalizer:statechange — dispatched on window for every user-initiated change (toolbar toggle, threshold slider, category selection).
  • getScores(points, { map }) — returns threshold-aware results that reflect the current live toolbar state.

Debouncing is essential. The threshold slider fires mapalizer:statechange on every drag step. Use a short setTimeout / clearTimeout pair so that only one getScores() call is issued after the user finishes adjusting, rather than one per pixel of slider movement.

Always check response.toolbar.activeState before reading aboveThreshold. When the toolbar is off, every result.aboveThreshold is null — clear all grayed styling in that state rather than treating every marker as below-threshold.

Programmatic scoring example — markers filtered by toolbar threshold

Strategy A — Score each marker at its own position

Pass every visible listing coordinate to getScores() and apply the result directly to the corresponding marker. Best suited for pages with up to a few hundred visible markers and no clustering layer. This is the approach used in the API Examples demo.

// ── Step 1: Listing data ──────────────────────────────────────────────────
// One entry per visible listing. getScores() results are returned in the
// same order as the input array, so results[i] maps to listings[i].
const listings = [
    { id: 'listing-1', marker: markerA, lat: 52.505, lng: 13.389 },
    { id: 'listing-2', marker: markerB, lat: 52.508, lng: 13.401 },
    // …
];

// ── Step 2: Marker appearance ─────────────────────────────────────────────
// Toggle a CSS class or swap the icon to apply / clear the grayed state.
// Adapt this to your map library and icon implementation.
function setMarkerGrayed(marker, grayed) {
    // Google Maps AdvancedMarkerElement:
    marker.content.classList.toggle('marker--grayed', grayed);
    // Leaflet:
    // marker.getElement()?.classList.toggle('marker--grayed', grayed);
}

// ── Step 3: Apply scoring results ─────────────────────────────────────────
// Grays a marker when the toolbar is active AND the location falls below
// the current contourThreshold (aboveThreshold === false).
function applyThresholdFilter(response) {
    const active = response.toolbar.activeState;
    response.results.forEach(function (result, i) {
        const grayed = active && result.aboveThreshold === false;
        setMarkerGrayed(listings[i].marker, grayed);
    });
}

// ── Step 4: Debounced scoring request ─────────────────────────────────────
// Cancels any pending request and waits delayMs before issuing a new one.
// A generation counter discards stale async responses that arrive out of order.
let refreshTimer = null;
let requestGen   = 0;

function scheduleFilterRefresh(delayMs) {
    clearTimeout(refreshTimer);
    refreshTimer = setTimeout(function () {
        const gen = ++requestGen;
        const points = listings.map(function (l) { return { lat: l.lat, lng: l.lng }; });
        mapalizer.getScores(points, { map: map }).then(function (response) {
            if (gen !== requestGen) return; // discard if a newer request has started
            if (response.ok) applyThresholdFilter(response);
        });
    }, delayMs);
}

// ── Step 5: React to toolbar state changes ────────────────────────────────
// mapalizer:statechange fires for ACTIVE_STATE, CONTOUR_THRESHOLD, and
// CATEGORIES events. Use a longer debounce for the threshold slider
// (fires continuously during drag) than for instant toggle/click events.
window.addEventListener('mapalizer:statechange', function (e) {
    if (e.detail.map !== map) return; // ignore events from other map instances
    const delay = e.detail.eventType === mapalizer.EventTypes.CONTOUR_THRESHOLD ? 180 : 80;
    scheduleFilterRefresh(delay);
});

Strategy B — Score by cluster or group representative

When your map groups nearby markers into cluster bubbles, scoring one representative position per cluster is far more efficient than scoring every individual listing. A map with 1,000 markers spread across 80 clusters requires only 80 scoring points instead of 1,000. The listing demo uses this as its primary strategy, with a Strategy A fallback for when the clusterer is not yet active.

Only currently visible markers inside each cluster should be included — exclude any markers hidden by your own listing filters. For multi-marker clusters, score the cluster centre. For unclustered single markers, score the marker's own position for accuracy. Since getScores() returns results in the same order as the input, results[i] aligns directly with units[i] — no ID mapping needed.

// ── Step 1: Helpers ───────────────────────────────────────────────────────
// Normalize a map-library position to a plain { lat, lng } object.
// Google Maps uses LatLng objects with .lat()/.lng() methods; Leaflet uses plain objects.
function toPoint(pos) {
    return typeof pos.lat === 'function' ? { lat: pos.lat(), lng: pos.lng() } : pos;
}

// Replace with your own visibility predicate — return false for markers
// currently hidden by listing filters (price, rooms, property type, etc.).
function isMarkerVisible(marker) { return !marker.hidden; }

// ── Step 2: Build cluster scoring units ───────────────────────────────────
// getClusters() must return the live cluster snapshot from your clustering
// library. Each cluster must expose:
//   cluster.position  — cluster centre (plain { lat, lng } or a LatLng object)
//   cluster.markers   — all markers inside the cluster
let refreshTimer = null;
let requestGen   = 0;

function scheduleClusterFilterRefresh(getClusters, delayMs) {
    clearTimeout(refreshTimer);
    refreshTimer = setTimeout(function () {
        const gen = ++requestGen;
        const clusters = getClusters();
        if (!clusters || !clusters.length) return;

        // Collect one scoring unit per cluster, skipping hidden markers.
        // Single visible marker → score its own position (accurate).
        // Multiple visible markers → score the cluster centre (representative).
        const units = [];
        clusters.forEach(function (cluster) {
            const visible = cluster.markers.filter(isMarkerVisible);
            if (!visible.length) return; // skip fully-hidden clusters
            units.push({
                point:   toPoint(visible.length > 1 ? cluster.position : visible[0].position),
                markers: visible // keep direct references — results align by index
            });
        });
        if (!units.length) return;

        // ── Step 3: Score representative points and apply results ─────────────
        // One getScores() call for all clusters instead of one per listing.
        // response.results[i] aligns with units[i] by sequence.
        const points = units.map(function (u) { return u.point; });
        mapalizer.getScores(points, { map: map }).then(function (response) {
            if (gen !== requestGen) return; // discard if a newer request has started
            if (!response.ok) return;
            const active = response.toolbar.activeState;
            units.forEach(function (unit, i) {
                const grayed = active && response.results[i].aboveThreshold === false;
                // Propagate the cluster score to every marker inside it.
                unit.markers.forEach(function (m) { setMarkerGrayed(m, grayed); });
            });
        });
    }, delayMs);
}

// ── Step 4: React to toolbar state changes ────────────────────────────────
// Same pattern as Strategy A. Fall back to Strategy A when getClusters()
// returns null (e.g. the clusterer has not initialised yet).
window.addEventListener('mapalizer:statechange', function (e) {
    if (e.detail.map !== map) return;
    const delay = e.detail.eventType === mapalizer.EventTypes.CONTOUR_THRESHOLD ? 180 : 80;
    if (getClusters()) scheduleClusterFilterRefresh(getClusters, delay);
    else scheduleFilterRefresh(delay); // Strategy A fallback
});

Configuration Object

The optional second argument to addToMap() lets you tune the toolbar to your layout, language, and product requirements.

Pass an object like this as the second argument to mapalizer.addToMap(map, config). For test and production websites, include auth.siteToken from portal.mapalizer.com.

Reference JSON for the current website release: .

Loading current default settings…

toolbar

Property Default Description
activeState Show the toolbar as active on initialization.
horizontal Horizontal toolbar position: left or right.
vertical Vertical toolbar position: top or bottom.
desktop.style See example above Desktop CSS positioning rules such as top, right, and z-index.
mobile.maxWidth Breakpoint below which mobile behavior activates.
mobile.hidden Hide the toolbar entirely on mobile if needed.
mobile.style See example above Mobile-specific positioning overrides.

desktop.style and mobile.style are filtered by the chosen horizontal and vertical positions. For example, with horizontal: 'right' and vertical: 'top', only the right and top offsets are applied; the opposite side values are ignored.

layer

Property Default Description
opacity Heat layer transparency from 0.0 to 1.0.
contourThreshold Hide low-scoring contours below this default threshold.
contourColors Low-to-high contour colors. Array length also limits how many contour levels are drawn on the map.
transitionDuration Duration in milliseconds for the opacity cross-fade when contour tiles appear or disappear. Set to 0 to disable the transition.

Top-level options

Property Default Description
language Sets the initial UI language. It can also be changed later with mapalizer.configure(). Supported language code list: .
theme Toolbar theme. Currently light and dark are supported.
verticalKey CDN namespace used for toolbar assets, locale files, category metadata, and score data.
loadCss Auto-load the distributed toolbar stylesheet. Set false if you want to host the CSS yourself. See CSS Customization.
localeUrl Derived from release Override the base URL for locale JSON files. See Supported Languages.
categories {} Optional. Override category ordering, icon, weight, and default selection state. See category key map.

categories

Each category key maps to a built-in category. You can override sort order, icon, weight, and default selection state.

{
  "categories": {
    "TRANSIT": {
      "sortOrder": 10,
      "weight": 2.0,
      "isSelected": true
    },
    "SHOPPING": {
      "icon": "<svg>...</svg>",
      "weight": 0.5,
      "isSelected": false
    },
    "CULTURE": {
      "weight": 0
    }
  }
}

The current default category definitions are available at .

Key Category Key Category
TRANSITTransitESSENTIALSEssentials
HEALTHHealthNATURENature
DININGDiningSPORTSSports
SHOPPINGShoppingCULTURECulture
Override Property Type Description
sortOrder Number Controls the category position in the toolbar. Lower values appear earlier.
icon String Custom SVG markup used instead of the default category icon.
weight Number Weight multiplier for the category. Higher values increase that category’s contribution to the final score. Set 0 to hide the category from the toolbar list.
isSelected Boolean Default selection state when the toolbar initializes.

These overrides let you shape the initial category experience without changing the underlying built-in category set.

The live Configurator is the fastest way to experiment and generate a production-ready configuration object.

auth

Controls session-token authentication for tile access.

Property Type Default Description
siteToken String undefined Publishable site token from portal.mapalizer.com. Required everywhere. Use mpl_pk_test_ on localhost and mpl_pk_live_ on real domains.

Authentication flow: On addToMap() Mapalizer posts your siteToken to the session API and receives a short-lived token. The toolbar DOM is only inserted after this request succeeds. Each tile request appends ?t=<token> to the URL (no CORS preflight). The token is automatically refreshed 60 s before expiry. On a 401/403 response Mapalizer silently refreshes once and retries; if the retry also fails, a mapalizer:autherror event is dispatched.

const result = await mapalizer.addToMap(map, {
    auth: {
        siteToken: 'mpl_pk_live_REPLACE_ME'
    }
});
if (!result.ok) console.error('Mapalizer failed:', result.error, result.status);

Customising with CSS Custom Properties

Mapalizer exposes visual tokens on the .mpl root. Override those properties in your own stylesheet to match your product brand without editing the distributed bundle.

.mpl.mpl-sidebar {
    --mpl-color-accent: #e05c3a;
    --mpl-radius-panel: 4px;
    --mpl-font-size-label: 13px;
}

Use a selector such as .mpl.mpl-sidebar so your overrides beat the runtime stylesheet even when it loads later in the document.

Host The Stylesheet Yourself

If you need full control, disable automatic CSS injection with loadCss: false and host your own copy of the stylesheet.

The unminified reference stylesheet for the current website release is available at .

<link rel="stylesheet" href="/your-path/mapalizer.css">

For visual inspiration, see the Toolbar Themes gallery.

Supported Languages

The toolbar supports 100+ locales. The initial language can be set through the configuration object and changed later with mapalizer.configure().

Locale assets are served from the Mapalizer CDN alongside the toolbar release and are loaded on demand.

Custom Locale Host

localeUrl is resolved as {localeUrl}/{langCode}.json. Override it only when you want to host custom or corrected translations.

The English reference locale for the current website release is available at .

{
  "language": "ja",
  "localeUrl": "https://example.com/mapalizer-locales"
}

Supported language code list: .

Licensing

Mapalizer is commercial software with a free tier for smaller sites and a registration-based evaluation path for higher-traffic websites.

  • Under 10,000 monthly pageviews: free to use.
  • 10,000+ monthly pageviews: registration required, followed by a 3-month evaluation period.
  • After evaluation: paid commercial license required.

See the full Terms and License for the authoritative language.