• Stars
    star
    209
  • Rank 187,222 (Top 4 %)
  • Language
    JavaScript
  • Created almost 6 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A utility library that makes Mapbox GL JS or Maplibre GL a bit more convenient to work with.

Map-GL-Utils (formerly Mapbox-GL-Utils) adds a number of utility functions and syntactic sugar to a Mapbox GL JS or Maplibre GL JS map instance. If you write a lot of interactive map code, you may appreciate the more concise form, and simpler API.

Full documentation: https://stevage.github.io/map-gl-utils

Major features:

  • No need to distinguish between paint, layout and other properties.
  • All properties can be expressed as camelCase rather than kebab-case.
  • Layer operations can act on multiple layers (given by array, regex or filter function), not just one.
  • Source types, layer types and property names are incorporated into function names: addGeoJSON(), addCircleLayer(), setCircleRadius(), getTextFont()...
  • Adding layers and sources is idempotent: call addLineLayer() multiple times to create, then update the layer.
  • Some other convenience functions: show(), hide(), onLoad(), setData(), fontsInUse()
  • Better click and hover functions: hoverPointer(), hoverFeatureState(), hoverPopup(), clickLayer()
  • Some functions behave better: removeLayer() (not an error if layer doesn't exist), removeSource() (removes attached layers automatically), setFilter() (works on multiple layers at once), setData() clears data if no GeoJSON provided.

Usage

To use without any build process:

<script src="https://unpkg.com/map-gl-utils"></script>

then

U.init(map)

With Webpack etc:

const mapgl = require('maplibre-gl'); // or require('mapbox-gl');
const map = new mapgl.Map({ ... });

// or:
import U from 'map-gl-utils';
U.init(map);

// A small number of methods (eg hoverPopup) require access to the maplibre-gl/mapbox-gl library itself, in order to instantiate other objects.
require('map-gl-utils').init(map, mapgl);

The default distribution is an ES2015 module with no transpiling. If you experience any syntax issues (such as using older JavaScript versions), use the UMD bundle instead:

// Adds U property to map, containing these methods.
require('map-gl-utils/umd').init(map);

If you want to use Flow types:

import type MapGlUtils from 'map-gl-utils/src/index'

Guide

Working with layers

The props object passed when adding a layer can freely mix paint, layout and other properties. Property keys can be specified in camelCase or kebab-case:

map.U.addCircleLayer('trees-circle', 'trees', {
    circleColor: 'green', // paint property
    circleRadius: ['interpolate', ['zoom'], 12, 3, 15, 5], // paint property
    circleSortKey: ['get', 'tree-sort-key'], // layout property
    filter: ['!=', 'type', 'stump'], // other property
});

Almost every method that works with existing layers (eg, show()) can work with multiple layers. There are four ways to specify the layer(s) you want to modify:

  • string: map.U.show('trees-label'); map.U.show('trees-circle');
  • array of strings: map.U.show(['trees-label', 'trees-circle']);
  • regular expression: map.U.show(/^trees-/);
  • function that takes a layer, and returns truthy: map.U.show(layer => layer.source === 'trees');

Adding sources

Methods that add sources return an object ("SourceBoundUtils" in this documentation) that can be chained to allow layers to be added to it:

map.U.addGeoJSONSource('properties')
.addCircleLayer('properties-line', { lineWidth: 3 })
.addSymbolLayer('properties-fill', { fillColor: 'hsla(30,30%,60%,0.5)' })

Adding and removing layers

// Conveniently add a line feature, mixing paint, layout and other properties.
// Notice you can use camelCase for all property names.
map.U.addLineLayer('mylines', 'mysource', {
    lineWidth: 3,
    lineCap: 'round',
    minzoom: 11
});

// Also addFillLayer, addFillExtrusionLayer, addRasterLayer, addVideoLayer, addSymbolLayer, addHillshadeLayer, addHeatmapLayer
map.U.addCircleLayer('mycircles', 'mysource', { circleStrokeColor: 'red' });
// if the layer already exists, calling add*Layer simply updates any of the properties
map.U.addCircleLayer('mycircles', 'mysource', { circleStrokeColor: 'red', circleRadius: 4, filter: ['==', 'type', 'active'});


// and of course add the layer "before" another layer if needed:
map.U.addLineLayer('mylayer', 'mysource', { lineColor: 'red' }, 'toplayer');

// removeLayer() doesn't throw errors if the layers don't exist
map.U.removeLayer(['towns','town-labels']);

Adding and removing sources

// Simpler way to create GeoJSON source:
map.U.addGeoJSON('mysource', geojson);

// Or create a GeoJSON source with initially blank data. This is very convenient if you're loading
// the data separately and will call .setData() later.
map.U.addGeoJSON('mysource');

// Simpler ways to create a vector tile source:
map.U.addVector('mysource', 'mapbox://foo.blah');
map.U.addVector('mysource', 'https://example.com/tiles/{z}/{x}/{y}.pbf');

// Additional properties still work
map.U.addVector('mysource', 'https://example.com/tiles/{z}/{x}/{y}.pbf', { maxzoom: 13 });

// There's also addRaster(), addRasterDem(), addImage(), addVideo()
// Calling any of the add* functions simply updates the source definition if it exists already.

// Automatically removes any layers using these sources. Not an error if sources don't exist.
map.U.removeSource(['buildings', 'roads']);

// You can also use the returned object to add layers conveniently:
map.U.addGeoJSON('buildings', 'data/buildings.geojson')
    .addFillExtrusion('buildings-3d', {
        fillExtrusionHeight: 100,
        fillExtrusionColor: 'grey'
    }).addLineLayer('buildings-footprint', {
        lineColor: 'lightblue'
    });

// Replace the source on an existing layer. (Actually removes and re-adds it.)
map.U.setLayerSource('buildings', 'newsource');
map.U.setLayerSource(['buildings-3d', 'buildings-outline]', 'newsource', 'newsourcelayer');

// To change the source layer, pass a third argument, or null to clear it (if switching from vector tiles to geojson)
map.U.setLayerSource('buildings', 'mylocalbuildings', null);

Setting properties and updating data

// Every property has a setXxx() form:
map.U.setTextSize('mylayer', 12);

// And they all work on multiple layers at once:
map.U.setLineWidth(['mylayer', 'mylayer-highlight'], 4);
map.U.setLineOpacity(/^border-/, 0);
map.U.setFillColor(layer => layer.source === 'farms', 'green');

// There's also a more familiar setProperty() form.
map.U.setProperty('mylayer', 'line-width', 3);
// Existing properties aren't touched
map.U.setProperty('mylayer', {
    textSize: 12,
    textColor: 'red'
});

// There's a `get...` version of every function, too.
map.U.getFillColor('water')

// Simpler way to update source data:
map.U.setData('mysource', data);

// you can leave out the data parameter to clear out a GeoJSON source:
map.U.setData('mysource');

// Easier to remember way to turn layers on and off:
map.U.show('mylayer');
map.U.hide('mylayer');
map.U.toggle(['mylayer', 'myotherlayer'], isVisible);

// To avoid name clashes such as with 'raster', you can use a longer form ending
// with either ...Layer() or ...Source()

map.U.addRasterSource('myrastersource', { type: 'raster', url: 'mapbox://mapbox.satellite', tileSize: 256 });
map.U.addRasterLayer('myrasterlayer', 'myrastersource', { rasterSaturation: 0.5 });

Hovering and clicking

// Use the mouse 'finger' cursor when hovering over this layer.
map.U.hoverPointer('mylayer');

// If you pass several layers, it correctly handles moving from one layer to another
// Use the mouse 'finger' cursor when hovering over this layer.
map.U.hoverPointer(['regions-border', 'regions-fill']);

// Sets a "hover" feature-state to be true or false as the mouse moves over features in this layer.
// Requires that features have an `id`.
map.U.hoverFeatureState('mylayer');

// Want to apply the hover feature-state to a different source?
// For instance, you hover over a label, but want to highlight the surrounding boundary.
map.U.hoverFeatureState('town-labels', 'boundaries', 'town-boundaries');

// You can also add additional event handlers:
map.U.hoverFeatureState('mylayer', 'mysource', 'mysourcelayer',
    e => console.log(`Entered ${e.features[0].id}`),
    e => console.log(`Left ${e.oldFeatureid}`);

// Shows a popup when a feature is hovered over or clicked.
// The third argument is an options object, passed to the Popup constructor.
// callback is called as: (feature, popup) => htmlString
// Make sure you passed the mapboxgl library itself when initialising: U.init(map, mapboxgl).
map.U.hoverPopup('mylayer', f => `<h3>${f.properties.Name}</h3> ${f.properties.Description}`, { anchor: 'left' });
map.U.clickPopup('mylayer', f => `<h3>${f.properties.Name}</h3> ${f.properties.Description}`, { maxWidth: 500 });

// clickLayer() is like .on('click)', but can take an array and adds a 'features' member
// to the event, for what got clicked on.
map.U.clickLayer(['towns', 'town-labels'], e => panel.selectedId = e.features[0].id);

// clickOneLayer tests multiple layers in order, firing callback on the first one that
// is hit. The callback is passed { feature, features, layer, event }.
map.U.clickOneLayer(['town-labels', 'state-boundaries'], e => {
    if (e.layer === 'town-labels') {
        setView('town');
        panel.selectedId = e.features[0].id;
    } else if (e.layer === 'state-boundaries') {
        setView('state');
        panel.selectedId = e.features[0].id;
    }
});

// Optionally pass in an extra callback which is fired for clicks that miss all layers:
map.U.clickOneLayer(['town-labels', 'state-boundaries'], e => {...}, e => {
    console.log('Missed everything');
});

// All these functions return an "undo" function that removes the handlers added:
const remove = map.U.hoverPopup('mylayer', showPopupFunc);
//...
remove(); // no more hover popup

Other functions

// Like on('load') but fires immediately (and reliably) any time after map already loaded.
map.U.onLoad(callback);
// returns a promise if no callback:
await map.U.onLoad();

// Gets the layer definition. Mapbox's `getLayer()` has weird paint and layout properties.
const layer = map.U.getLayerStyle('mylayer');

// Resets all other properties to default first. Ignores non-paint, non-layout properties.
map.setLayerStyle('mylayer', {
    lineWidth: 3
});

// properties() converts an object to a layer object accepted by Mapbox-GL-JS
map.addLayer(map.U.properties({
    id: 'mylayer',
    source: 'mysource',
    type: 'line',
    lineWidth: 3,
    lineCap: 'round',
    minzoom: 11,
    filter: ['==', 'status', 'confirmed']
}));

// layerStyle() is flexible, pass as many or as few of id, source, and type (in that order) as you like:
map.U.layerStyle('mylayer', 'mysource', 'line', { ... })
map.U.layerStyle('mylayer', 'mysource', { ... })
map.U.layerStyle('mylayer', { ... })
map.U.layerStyle({ ... })


// Hide/show/toggle all the layers attached to this source
map.U.hideSource('buildings');
map.U.showSource('buildings');
map.U.toggleSource('buildings', true);

// Update several filters at once.
map.U.setFilter(['buildings-fill', 'buildings-outline', 'buildings-label'], [...]);

// Conveniently load an image into the map in one step
map.U.loadImage('marker', '/assets/marker-pin.png');
map.U.loadImage('marker', '/assets/[email protected]', { pixelRatio: 2}).then(/* ... */;


// Update the map style's root "transition" property
map.U.setTransition({ delay: 1000, delay: 0});

// Get a list of fonts used in symbol layers with fontsUsed(). Useful for quickly getting some text displaying.
const fonts = map.U.fontsInUse();
map.U.addSymbolLayer('labels', 'mysource', { textFont: fonts[0], textField: '{label}' });

Contrived example

map.U.onload(() => {
    map.U.addGeoJSON('towns');
    map.U.addCircleLayer('small-towns', 'towns', { circleColor: 'green', filter: ['==', 'size', 'small']});
    map.U.addCircleLayer('large-towns', 'towns', {
        circleColor: 'red',
        filter: ['==', 'size', ['large']],
        circleStrokeWidth: ['case', ['to-boolean', ['feature-state', 'hover']], 5, 1]
    );
    map.U.setCircleRadius(['small-towns', 'large-towns'], 12);
    map.U.hoverPointer(['small-towns', 'large-towns']);
    map.U.hoverFeatureState('large-towns');
    // update the source layer when data is available
    d3.json('http://example.com/towns.json', data => map.U.setData('towns', data));
});

Credits

Map-GL-Utils was written by, and maintained, by Steve Bennett, a freelance map developer.

Documentation built with documentation.js.

Packaging uses rollup.js and Babel.

Flow is used internally, including types from Mapbox GL JS.

Tests are run using Jest.

More Repositories

1

OpenTrees

Front end for opentrees.org, a data visualisation of millions of publicly maintained trees around the world.
SCSS
165
star
2

mapbox-choropleth

Generate choropleth layers in Mapbox-GL-JS
JavaScript
40
star
3

opentrees-data

Generate vector tiles from open data for opentrees.org
JavaScript
37
star
4

saltymill

Tillmill scripts in SaltStack
SaltStack
31
star
5

which-geocoder

getlon.lat: A website to help you decide which geocoding service might meet your needs.
JavaScript
31
star
6

vector-inspector

Visually inspect the contents of arbitrary vector tile sources!
Vue
30
star
7

portable-vector-style

Converts Mapbox-GL-JS style files between three popular OSM vector tile schemas.
JavaScript
25
star
8

tilemill-server

Scripts supporting a tilemill server deployment (see http://steveko.wordpress.com/2013/05/08/tilemill-server/ )
Shell
21
star
9

epsg

EPSG definitions for proj4js
JavaScript
18
star
10

ptvpy

Python API for the Public Transport Victoria API
Python
12
star
11

mapbox-gl-draw

Adds snapping
JavaScript
12
star
12

WhatTheProj

Website to guess the projection for a given point.
Vue
12
star
13

normalize-crosstab

A Google Sheets Script for normalizing cross-tab spreadsheets, so they work better with Tableau.
JavaScript
11
star
14

tinymap

A tiny collaborative map platform.
Vue
11
star
15

geojson2ndjson

Convert a GeoJSON file into newline-delimited JSON output, one line per feature.
JavaScript
10
star
16

mbsprite

Command line utilities for exploding/bundling Mapbox GL spritesheets
JavaScript
10
star
17

node-tippecanoe

A thin Node shell around Mapbox's Tippecanoe.
JavaScript
9
star
18

trainle

Vue
7
star
19

community-map

Vue
7
star
20

tileinfo

Get tilejson info from an mbtiles file
JavaScript
6
star
21

PTV-API-doc

Unofficial mirror of the Public Transport Victoria Timetable API documentation
HTML
6
star
22

figmasset

JavaScript
5
star
23

ptv-api-js

NodeJS client library for PTV API, based on their OpenAPI specification.
JavaScript
4
star
24

cycletour-style

CartoCSS
4
star
25

hipster-melbourne-old

The Hipster Map of Melbourne. What it sounds like!
CSS
4
star
26

geojson-spec

A human readable version of the GeoJSON spec
HTML
4
star
27

which-boundary

Find which LGA boundary a given lat-lon is within.
JavaScript
3
star
28

AusTrails

Trail aggregator site for GovHack 2015
HTML
3
star
29

map-effects

Playing with visual effects on Mapbox-GL maps
Vue
2
star
30

QueryRemoteTiles

Look up a latitude/longitude in a remotely hosted vector tile source.
JavaScript
2
star
31

hipster-melbourne

New version of Hipster Map of Melbourne (late 2019)
Vue
2
star
32

BikeTrafficCounts

Bike traffic count data derived from https://cdmresearch.shinyapps.io/bike_counts/
JavaScript
2
star
33

bushfire-areas

Demonstration of bushfire areas spatial services from EMSINA
Vue
2
star
34

meshlium-soda

Aggregate Meshlium sensor readings then publish to SODA API.
Python
2
star
35

MicroTardis-Harvest

Shell scripts that harvest data for the MicroTardis project
Shell
2
star
36

atom-provider-chef

Basic chef configuration for setting up Atom dataset provider. Supports 'dev' and 'prod' environments.
Ruby
2
star
37

geohash-map

JavaScript
2
star
38

query-mbtiles

JavaScript
2
star
39

vector-tile-scripts

Processing OSM data into chunks for Mapbox Studio
Shell
1
star
40

tunnelator

JavaScript
1
star
41

e2demo

Vue
1
star
42

mapbox-csv-geo-au

Visualising csv-geo-au data using Mapbox-GL-JS
Vue
1
star
43

tilemill-portability

Portability tool for Tilemill projects
1
star
44

turf-voronoi-polygons

Unofficial Turf module to generate Voronoi polygons.
JavaScript
1
star
45

DataSourceGuide

A guidebook for custodians of data sources used by National Map or other TerriaJS installations.
CSS
1
star
46

throttle-end

Another throttle/debounce function with some particular properties.
JavaScript
1
star
47

mapgl-expression

JavaScript
1
star
48

ndgeojson

Moved to subfolder of https://github.com/stevage/geojson-spec
HTML
1
star
49

mapswell

Vue
1
star
50

vue-map

Template for Vue based maps
Vue
1
star
51

fake-town-name

JavaScript
1
star
52

VMap

Matching requests and offers for medical equipment in French Polynesia
Vue
1
star
53

sheets2geojson

Convert a Google Sheet to GeoJSON
JavaScript
1
star
54

get-vector-tile

JavaScript
1
star