official page →
Better and customizable markers for MapTiler Cloud and MapTiler SDK JS
MapTiler Marker layout for MapTiler SDK
The Marker Layout is a helper to create non-coliding marker overlays on top of MapTiler SDK. Fed by a vector layer from a tileset or from a GeoJSON source, it can be tuned with plenty of options.
Thanks to its non opinionated and logic-only approach, it lets you bind any kind of rendering you wish for your markers: vanilla HTML Divs, React components, floating canvas, etc. as it computes the position and size of the markers but lets you take handle the rendering part.
Some Examples
Here is only a few examples of what's possible with fairly basic HTML markers.
With markers anchored to the city layers, directly fueled by the streets-v2
style from MapTiler Cloud:
![](https://github.com/maptiler/maptiler-marker-layout/raw/HEAD/images/cities.png)
Displaying weather data is also a nice usecase. For this, we anchor the markers to cities, towns and villages from an official MapTiler Cloud style and then we asynchronously fetch the weather data using MapTiler Weather library, for each vector features using their coordinates:
![](https://github.com/maptiler/maptiler-marker-layout/raw/HEAD/images/weather_usa_large.png)
But markers don't need to look like markers! Smaller markers with transparent background are a nice way to avoid cluter. Icons are SVG animated:
![](https://github.com/maptiler/maptiler-marker-layout/raw/HEAD/images/weather_minimal.png)
Since markers are overlaying on top of a map, it's generally a good practice to keep them small, so that the basemap remains readable, but Marker Layout does not technically enforce that.
Some Concepts
The Marker Layout...
- computes screen-space bounding box logic
- can be provided the desired marker size and relative anchor point
- is fed with one or multiple vector layer
- can only use point features
- create non-overlapping bounding boxes
- can filter and sort features based on vector feature properties
- sorting can be done with a function, so that rank can come from an external source
- can group multiple vector features into each marker
- when updated will retrieve three lists of markers relative to the previous state: the new, the removed and the moved markers
- does not enforce how the the actual visual markers (eg. divs) should be created, cached, pooled, reused or deleted
Usage
To install it:
npm install @maptiler/marker-layout
Then, import it:
import { MarkerLayout } from "@maptiler/marker-layout";
...
const markerLayout = new MarkerLayout(map, options);
Or it can be used from MapTiler Cloud CDN with vanilla JS:
<script src="https://cdn.maptiler.com/maptiler-marker-layout/v1.0.0/maptiler-marker-layout.umd.js"></script>
And then be address as such:
const markerLayout = new maptilermarkerlayout.MarkerLayout(map, options);
Options
Here are all the options available:
{
layers?: Array<string>;
markerSize?: [number, number];
max?: number;
markerAnchor?: MarkerAnchor;
offset?: [number, number];
filter?: (feature: MapGeoJSONFeature) => boolean;
sortingProperty?: string,
sortingProperty?: string | ((feature: MapGeoJSONFeature) => number);
groupBy?: string,
maxNbFeaturesPerMarker?: number,
maxRatioUnitSize?: number,
}
type MarkerAnchor = "center" | "top" | "bottom" | "left" | "right";
API
Appart from the constructor, there are a few things to get familiar with.
AbstractMarker
They are simple data structure that hold informations about a marker (position, size)
and the list of vector features it is supposed to contain. Here is how it looks like:
type AbstractMarker = {
id: number;
position: [number, number];
size: [number, number];
features: MapGeoJSONFeature[];
internalElementSize: [number, number],
};
Again, an abstract marker is not an actual visual marker. It only aims at providing the information to help making an actual graphic representation of a marker.
MarkerMap
The type MarkerMap
is simply a JS Map of AbstractMarker
. The key of this map (number) is a hash specific to one of multiple vector features contained by a marker. If you want to add an extra caching logic, you may want to track this ID, otherwise it's only used internaly and is of little interest at application level.
type MarkerMap = Map<number, AbstractMarker>;
MarkerStatus
An object of type markerStatus is a simple data structure that contain li
type MarkerStatus = {
new: MarkerMap;
updated: MarkerMap;
removed: MarkerMap;
};
Methods
As we interact with the map (pan, zoom, rotation, etc.) we need to know which markers are now visible, disapeared outside the viewport or are still visible but at a different (screen space) location.
To compute this, a MarkerLayout
instance has two methods:
-
.update()
compute a complete new status of markers, returning a MarkerStatus
.
In case many vector features are found in the specified layers
with the provided filter
, this may have an impact on performances and may not be suitable to call from a map.on("move", () => { ... })
event.
-
.softUpdateAbstractMarker(am)
only update a single AbstractMarker
with a new screenspace position.
This is convenient to use when there are hundreds of vector features found but we only want to update the position, say, of the ones retrieved with the previous full .update()
call. In this performance-wise conservative mode, one would typically bind .update()
to the Map
event "idle"
and bind .softUpdateAbstractMarker(am)
to the Map
event "move"
.
We can also reset the internal MarkerStatus
if we need to restart from a blank slate without creating a new MarkerLayout
instance:
Examples
You can find two examples in this repo:
- demo using only
.update()
here - demo using both
.update()
and .softUpdateAbstractMarker()
here
License
See license.md