Working with geometries
The turf.js library is recommended for working with GeoJSON. The sections below describe using this library together with MapGL JS API.
Bounding box (bbox)
The turf.bbox
method calculates a rectangular area that contains given objects or a group of objects.
const line = turf.lineString([
[34, 40],
[36, 41],
[41, 37],
[48, 42],
[42, 35],
]);
const bbox = turf.bbox(line); // [minX, minY, maxX, maxY]
Method documentation: @turf/bbox
Usage examples
-
Position the map so that all objects are fit into the screen using the map.fitBounds method.
map.fitBounds({ southWest: bbox.slice(0, 2), northEast: bbox.slice(2, 4) });
-
Request additional data. For example, request nearest construction objects when you have municipal area polygons.
Bounding box is the most common and fast way to define boundaries of objects location. The downside is that the boundaries are defined roughly. If you need higher precision, use a convex hull.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Using @turf/bbox with MapGL</title>
<meta name="description" content="Using @turf/bbox with MapGL" />
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://unpkg.com/@turf/turf@6.5.0/turf.min.js"></script>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const line = turf.lineString([[34, 40], [36, 41], [41, 37], [48, 42], [42, 35]]);
const bbox = turf.bbox(line);
const geojson = turf.featureCollection([line, turf.bboxPolygon(bbox)]);
const map = new mapgl.Map('container', {
center: turf.getCoord(turf.center(geojson)),
zoom: 5,
key: 'Your API access key',
});
new mapgl.GeoJsonSource(map, {
data: geojson,
attributes: {
foo: 'bar'
}
});
map.on('styleload', () => {
map.addLayer({
id: 'polygons',
type: 'polygon',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DD50'
}
});
map.addLayer({
id: 'lines',
type: 'line',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DDA0',
width: 3
}
});
map.addLayer({
id: 'points',
type: 'point',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
iconImage: 'ent_i',
iconWidth: 16,
iconPriority: 100
}
});
})
</script>
</body>
</html>
Convex hull
The turf.convex
method takes an object or a group of objects and creates a convex hull based on them.
const line = turf.lineString([
[34, 40],
[36, 41],
[41, 37],
[48, 42],
[42, 35],
]);
const convexHull = turf.convex(line);
Method documentation: @turf/convex
Usage example
Convex hull is a more precise (compared to the bounding box) method of defining object boundaries. The downside is high computational complexity of this method.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Using @turf/convex with MapGL</title>
<meta name="description" content="Using @turf/convex with MapGL" />
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://unpkg.com/@turf/turf@6.5.0/turf.min.js"></script>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const line = turf.lineString([[34, 40], [36, 41], [41, 37], [48, 42], [42, 35]]);
const convexHull = turf.convex(line);
const geojson = turf.featureCollection([line, convexHull]);
const map = new mapgl.Map('container', {
center: turf.getCoord(turf.center(geojson)),
zoom: 5,
key: 'Your API access key',
});
new mapgl.GeoJsonSource(map, {
data: geojson,
attributes: {
foo: 'bar'
}
});
map.on('styleload', () => {
map.addLayer({
id: 'polygons',
type: 'polygon',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DD50'
}
});
map.addLayer({
id: 'lines',
type: 'line',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DDA0',
width: 3
}
});
map.addLayer({
id: 'points',
type: 'point',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
iconImage: 'ent_i',
iconWidth: 16,
iconPriority: 100
}
});
})
</script>
</body>
</html>
Buffer around an object
The turf.buffer
method enables enlarging or reducing an object proportionally.
const line = turf.lineString([
[34, 40],
[36, 41],
[41, 37],
[48, 42],
[42, 35],
]);
const buffer = turf.buffer(line, 40);
Method documentation: @turf/buffer
Usage examples
- Display various types of security zones, isolation areas, and others.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Using @turf/buffer and MapGL</title>
<meta name="description" content="Using @turf/buffer and MapGL" />
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://unpkg.com/@turf/turf@6.5.0/turf.min.js"></script>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const line = turf.lineString([[34, 40], [36, 41], [41, 37], [48, 42], [42, 35]]);
const buffer = turf.buffer(line, 40);
const geojson = turf.featureCollection([line, buffer]);
const map = new mapgl.Map('container', {
center: turf.getCoord(turf.center(geojson)),
zoom: 5,
key: 'Your API access key',
});
new mapgl.GeoJsonSource(map, {
data: geojson,
attributes: {
foo: 'bar'
}
});
map.on('styleload', () => {
map.addLayer({
id: 'polygons',
type: 'polygon',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DD50'
}
});
map.addLayer({
id: 'lines',
type: 'line',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DDA0',
width: 3
}
});
map.addLayer({
id: 'points',
type: 'point',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
iconImage: 'ent_i',
iconWidth: 16,
iconPriority: 100
}
});
})
</script>
</body>
</html>
-
Display places equally distant from an object on the map.
However, this works good only for small distances (up to 50-100 m). Calculation of larger distances is affected by the method of
turf.buffer
calculations. Distances are calculated purely geometrically while in reality the shortest geometric way usually contains obstacles: houses, rivers, fences, which must be bypassed. In this case, you should use isochrones as a more precise and specialized tool.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Drawing buffers around points with @turf/buffer and MapGL</title>
<meta name="description" content="Drawing buffers around points with @turf/buffer and MapGL" />
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://unpkg.com/@turf/turf@6.5.0/turf.min.js"></script>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const points = [
[33.63825,34.917647],
[33.638238,34.914459],
[33.634526,34.912492],
[33.628846,34.914821],
[33.628997,34.917202],
[33.625677,34.916229]
];
const geojson = turf.featureCollection([]);
for (const pt of points) {
const feature = turf.point(pt);
geojson.features.push(feature);
geojson.features.push(turf.buffer(feature, 0.1));
}
const map = new mapgl.Map('container', {
center: turf.getCoord(turf.center(geojson)),
zoom: 15.5,
key: 'Your API access key',
});
new mapgl.GeoJsonSource(map, {
data: geojson,
attributes: {
foo: 'bar'
}
});
map.on('styleload', () => {
map.addLayer({
id: 'polygons',
type: 'polygon',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DD50'
}
});
map.addLayer({
id: 'lines',
type: 'line',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DDA0',
width: 3
}
});
map.addLayer({
id: 'points',
type: 'point',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
iconImage: 'ent_i',
iconWidth: 16,
iconPriority: 100
}
});
})
</script>
</body>
</html>
Getting a center (centroid)
The turf.center
, turf.centroid
, and turf.centerOfMass
methods enable getting a particular center of an object or a group of objects.
const polygon = turf.polygon([
[
[34, 40],
[36, 41],
[41, 37],
[48, 42],
[42, 35],
[34, 40],
],
]);
const center = turf.center(polygon);
const centroid = turf.centroid(polygon);
const centerOfMass = turf.centerOfMass(polygon);
Method documentation:
Usage examples
You can get a center in three ways:
center
gets the center of a bounding box of an object or a group.centroid
calculates a barycenter of an object or a group.centerOfMass
gets the center of mass of a figure.
On convex and symmetric polygons, these methods show almost no difference. In case of non-convex or other irregular polygons, centerOfMass
works best.
Using these methods is convenient for placing object captions.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Using @turf/[center,centroid,centerOfMass] and MapGL</title>
<meta name="description" content="Using @turf/[center,centroid,centerOfMass] and MapGL" />
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://unpkg.com/@turf/turf@6.5.0/turf.min.js"></script>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const polygon = turf.polygon([[[34, 40], [36, 41], [41, 37], [48, 42], [42, 35], [34, 40]]]);
const center = turf.center(polygon, { properties: { name: 'Center' } });
const centroid = turf.centroid(polygon, { properties: { name: 'Centeroid' } });
const centerOfMass = turf.centerOfMass(polygon, { properties: { name: 'Center of mass' } });
const geojson = turf.featureCollection([polygon, center, centroid, centerOfMass]);
const map = new mapgl.Map('container', {
center: turf.getCoord(turf.center(geojson)),
zoom: 7.5,
key: 'Your API access key',
});
new mapgl.GeoJsonSource(map, {
data: geojson,
attributes: {
foo: 'bar'
}
});
map.on('styleload', () => {
map.addLayer({
id: 'polygons',
type: 'polygon',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DD50'
}
});
map.addLayer({
id: 'lines',
type: 'line',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DDA0',
width: 3
}
});
map.addLayer({
id: 'points',
type: 'point',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
iconImage: 'ent_i',
iconWidth: 16,
iconPriority: 100,
textFont: 'Noto_Sans',
textFontSize: 12,
textField: ['get', 'name'],
textPriority: 100
}
});
})
</script>
</body>
</html>
Nearest point
The turf.nearestPoint
method gets the nearest point from the set of points.
const line = turf.lineString([
[34, 40],
[36, 41],
[41, 37],
[48, 42],
[42, 35],
]);
const target = turf.point([38, 38]);
const points = turf.explode(line);
const nearest = turf.nearestPoint(target, points);
Method documentation: @turf/nearestPoint
The method feature is working only with a set of points. To transform an object into a set of points, use the @turf/explode method.
Usage example
Find the nearest object to the given one.
Nearest point on a line
The turf.nearestPointOnLine
method gets the nearest point to the given one on a line or a group of lines.
const line = turf.lineString([
[34, 40],
[36, 41],
[41, 37],
[48, 42],
[42, 35],
]);
const target = turf.point([38, 38], { name: 'Target' });
const nearestOnLine = turf.nearestPointOnLine(line, target);
Method documentation: @turf/nearestPointOnLine
Usage example
Find the nearest point on a route or a road. Like the turf.buffer method, it works in the simplest cases only when considering obstacles is not required. In other cases, use the Routing API.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Finding nearest with Turf.js and MapGL</title>
<meta name="description" content="Finding nearest with Turf.js and MapGL" />
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://unpkg.com/@turf/turf@6.5.0/turf.min.js"></script>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const line = turf.lineString([[34, 40], [36, 41], [41, 37], [48, 42], [42, 35]]);
const target = turf.point([38, 38], { name: 'Target' });
const nearestOnLine = turf.nearestPointOnLine(line, target);
nearestOnLine.properties = { name: 'Nearest Point On Line' };
const points = turf.explode(line);
const nearest = turf.nearestPoint(target, points);
nearest.properties = { name: 'Nearest Point' };
const geojson = turf.featureCollection([line, target, nearest, nearestOnLine]);
const map = new mapgl.Map('container', {
center: turf.getCoord(turf.center(geojson)),
zoom: 7,
key: 'Your API access key',
});
new mapgl.GeoJsonSource(map, {
data: geojson,
attributes: {
foo: 'bar'
}
});
map.on('styleload', () => {
map.addLayer({
id: 'polygons',
type: 'polygon',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DD50'
}
});
map.addLayer({
id: 'lines',
type: 'line',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DDA0',
width: 3
}
});
map.addLayer({
id: 'points',
type: 'point',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
iconImage: 'ent_i',
iconWidth: 16,
iconPriority: 100,
textFont: 'Noto_Sans',
textFontSize: 12,
textField: ['get', 'name'],
textPriority: 100
}
});
})
</script>
</body>
</html>
Union
The turf.union
method combines multiple figures into one.
const points = [
[10, 10],
[12, 10],
];
const circleA = turf.buffer(turf.point([10, 10]), 300);
const circleB = turf.buffer(turf.point([12, 10]), 300);
const united = turf.union(circleA, circleB);
Method documentation: @turf/union
Usage examples
- Display an area formed by multiple objects as one object on the map.
- Assemble overly fragmented objects, which often happens during geodata handling.
Intersection
The turf.intersect
method find a figure formed by an intersection of given figures.
const points = [
[10, 10],
[12, 10],
];
const circleA = turf.buffer(turf.point([10, 10]), 300);
const circleB = turf.buffer(turf.point([12, 10]), 300);
const intersected = turf.intersect(circleA, circleB);
Method documentation: @turf/intersect
Usage example
Display an area formed by an intersection of other areas on the map. For example, a free parking lot within a given radius from an object.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Using @turf/union and @turf/intersect with MapGL</title>
<meta name="description" content="Using @turf/union and @turf/intersect with MapGL" />
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://unpkg.com/@turf/turf@6.5.0/turf.min.js"></script>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const points = [[10, 10], [12, 10]];
const circleA = turf.buffer(turf.point([10, 10]), 300);
const circleB = turf.buffer(turf.point([12, 10]), 300);
const united = turf.transformTranslate(turf.union(circleA, circleB), 1200, 90);
const intersected = turf.transformTranslate(turf.intersect(circleA, circleB), 1200, -90);
const geojson = turf.featureCollection([circleA, circleB, united, intersected]);
const map = new mapgl.Map('container', {
center: turf.getCoord(turf.center(geojson)),
zoom: 5,
key: 'Your API access key',
});
new mapgl.GeoJsonSource(map, {
data: geojson,
attributes: {
foo: 'bar'
}
});
map.on('styleload', () => {
map.addLayer({
id: 'polygons',
type: 'polygon',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DD50'
}
});
map.addLayer({
id: 'lines',
type: 'line',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DDA0',
width: 3
}
});
map.addLayer({
id: 'points',
type: 'point',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
iconImage: 'ent_i',
iconWidth: 16,
iconPriority: 100
}
});
})
</script>
</body>
</html>
Mask
The turf.mask
method enables turning a polygon "inside out": turning its surrounding area into a polygon.
const poly = turf.polygon([
[
[0, 0],
[0, 2],
[2, 2],
[2, 0],
[0, 0],
],
]);
turf.mask(poly);
Method documentation: @turf/mask
Usage example
Draw attention to one or multiple areas on the map and obscure the remaining space.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Using @turf/mask with MapGL</title>
<meta name="description" content="Using @turf/mask with MapGL" />
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://unpkg.com/@turf/turf@6.5.0/turf.min.js"></script>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const aPlace = turf.polygon([[
[105.92429031266958, -5.262701049754213],
[106.28471551378776, -5.195701693969994],
[106.64994638418318, -5.214845098030779],
[107.03920560108905, -5.305768259535483],
[107.58705190638044, -5.4158151922751046],
[107.74083332350821, -5.721926046845098],
[107.8369467063646, -6.371857054158838],
[107.82252969773305, -6.558086239663282],
[106.65475205317988, -7.569142136398696],
[106.01079236773299, -7.5834332864075975],
[105.86181661939993, -7.535794279073713],
[105.49178008359819, -7.430970061978684],
[104.78488126325243, -6.791043334337019],
[104.8233266147578, -6.456892553102861],
[105.0299703919453, -6.026947405124446],
[105.16933480114257, -5.816626441233922],
[105.30389354084993, -5.644487005567058],
[105.92429031266958, -5.262701049754213],
]]);
const bbox = turf.bbox(aPlace);
const map = new mapgl.Map('container', {
center: turf.getCoord(turf.center(aPlace)),
zoom: 7.5,
key: 'Your API access key',
});
new mapgl.GeoJsonSource(map, {
data: turf.mask(aPlace),
attributes: {
foo: 'bar'
}
});
map.on('styleload', () => {
map.addLayer({
id: 'polygons',
type: 'polygon',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: '#00B2DD50'
}
});
})
</script>
</body>
</html>
Simplification
The turf.simplify
method simplifies a geometry using the Ramer–Douglas–Peucker algorithm.
const gpsTrack = turf.lineString([
[0, 0],
[0, 0.1],
[0.5, 0.5],
[1, 0.5],
]);
const simplifiedTrack = turf.simplify(gpsTrack, { tolerance: 0.2 });
// { ... geometry: { ..., coordinates: [[0, 0], [0.5, 0.5], [1, 0.5]] } }
The simplification degree is defined by the tolerance
parameter, which specifies the maximum error that the algorithm can make during simplification. The tolerance
value has the same dimension as the source data: if geocoordinates are specified in degrees, tolerance
must be in degrees too.
Method documentation: @turf/simplify
Usage examples
- Get less complicated geometries, which are rendered faster.
- Filter data when it is known that their precision must not exceed specific value.
- Display precise data on a large scale: simplified geometries have more aesthetic outlook and smoother lines without visual noise.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Using @turf/simplify with MapGL</title>
<meta name="description" content="Using @turf/simplify with MapGL" />
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://unpkg.com/@turf/turf@6.5.0/turf.min.js"></script>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const gpsTrack = turf.lineString([[0, 0], [0, 0.1], [0.5, 0.5], [1, 0.5]]);
const simplifiedTrack = turf.simplify(gpsTrack, { tolerance: 0.2 });
simplifiedTrack.properties.simplified = true;
const bbox = turf.bbox(gpsTrack);
console.log(gpsTrack, simplifiedTrack);
const geojson = turf.featureCollection([gpsTrack, simplifiedTrack]);
const map = new mapgl.Map('container', {
center: turf.getCoord(turf.center(geojson)),
zoom: 5,
key: 'Your API access key',
});
map.fitBounds(
{ southWest: bbox.slice(0, 2), northEast: bbox.slice(2, 4) },
{ padding: { left: 20, top: 20, right: 20, bottom: 20 } }
);
new mapgl.GeoJsonSource(map, {
data: geojson,
attributes: {
foo: 'bar'
}
});
map.on('styleload', () => {
map.addLayer({
id: 'lines',
type: 'line',
filter: ['==', ['sourceAttr', 'foo'], 'bar'],
style: {
color: ['match',
['get', 'simplified'],
[true], '#FF9387',
'#00B2DDA0'
],
width: ['match',
['get', 'simplified'],
[true], 3,
5
]
}
});
})
</script>
</body>
</html>