Learn how to add features as GeoJSON to a map .
A feature layer is a dataset in a hosted feature service . Each feature layer contains features with a single geometry type (point, line, or polygon), and a set of attributes . You can access and display features by making query requests to the feature service and displaying them in a map.
In this tutorial, you access the Trailheads feature layer to get GeoJSON and display the features as clusters. Working with GeoJSON is often useful for dynamically generated data, but not suitable for large datasets.
Prerequisites You need an ArcGIS account to access the developer dashboard and create an API key .
Steps Create a new pen To get started, either complete the Display a map tutorial or use this pen . Set the API key To access location services , you need an API key or OAuth 2.0 access token . To learn how to create and scope your key, visit the Create an API key tutorial.
Go to your dashboard to get an API key. The API key must be scoped to access the services used in this tutorial.
In CodePen , update apiKey
to use your key.
Use dark colors for code blocks
Change line
1
2
3
4
5
6
7
8
9
10
//
const apiKey = "YOUR_API_KEY" ;
const basemapEnum = "ArcGIS:Streets" ;
const map = new mapboxgl.Map({
container : "map" , // the id of the div element
style : `https://basemaps-api.arcgis.com/arcgis/rest/services/styles/ ${basemapEnum} ?type=style&token= ${apiKey} ` ,
zoom : 12 , // starting zoom
center : [- 118.805 , 34.027 ] // starting location [longitude, latitude]
});
Add a load event handler You need to wait for the map to be completely loaded before adding any layers.
Add an event handler to the map load
event.
Use dark colors for code blocks
Show more lines
Add line. Add line. Add line. Add line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<!DOCTYPE html >
< html >
< head >
< meta charset = "utf-8" />
< meta name = "viewport" content = "initial-scale=1,maximum-scale=1,user-scalable=no" />
< script src = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js" > </ script >
< link href = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" rel = "stylesheet" />
< script src = "https://unpkg.com/@esri/arcgis-rest-request@4.0.0/dist/bundled/request.umd.js" > </ script >
< script src = "https://unpkg.com/@esri/arcgis-rest-feature-service@4.0.0/dist/bundled/feature-service.umd.js" > </ script >
< style >
html ,
body ,
#map {
padding : 0 ;
margin : 0 ;
height : 100% ;
width : 100% ;
font-family : Arial, Helvetica, sans-serif;
font-size : 14px ;
color : #323232 ;
}
</ style >
</ head >
< body >
< div id = "map" > </ div >
< script >
const apiKey = "YOUR_API_KEY" ;
const basemapEnum = "ArcGIS:Streets" ;
const map = new mapboxgl.Map({
container : "map" , // the id of the div element
style : `https://basemaps-api.arcgis.com/arcgis/rest/services/styles/ ${basemapEnum} ?type=style&token= ${apiKey} ` ,
zoom : 12 , // starting zoom
center : [- 118.805 , 34.027 ] // starting location [longitude, latitude]
});
map.once( "load" , () => {
// This code runs once the base style has finished loading.
map.addSource( "trailheads" , {
type : "geojson" ,
data : "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0/query?f=pgeojson&where=1=1" ,
cluster : true ,
clusterRadius : 20 , // cluster two trailheads if less than 20 pixels apart
clusterMaxZoom : 14 // display all trailheads individually from zoom 14 up
});
map.addLayer({
id : "trailheads-circle" ,
type : "circle" ,
source : "trailheads" ,
paint : {
"circle-color" : "hsla(0,0%,0%,0.75)" ,
"circle-stroke-width" : 1.5 ,
"circle-stroke-color" : "white" ,
"circle-radius" : [ "case" , [ "get" , "cluster" ], 10 , 5 ] // 10 pixels for clusters, 5 pixels otherwise
}
});
map.addLayer({
id : "trailheads-cluster-count" ,
type : "symbol" ,
source : "trailheads" ,
layout : {
"text-font" : [ "Arial Bold" ],
"text-field" : [ "get" , "point_count" ],
"text-offset" : [ 0 , 0.1 ] // move the label vertically downwards slightly to improve centering
},
paint : {
"text-color" : "white"
}
});
});
</ script >
</ body >
</ html >
Show more lines
Add a GeoJSON source To add GeoJSON from a feature service , you need to define a source of type geojson
and reference the feature layer . The source tells Mapbox GL JS how to access the data for the layer, but does not visually add it to the map. To get GeoJSON features from a feature layer, you provide a URL to query the feature service and return features in GeoJSON format. Features can then be displayed by adding the source to a layer .
More info While there are several types of sources, the two most common are geojson
(for a set of features represented as GeoJSON) and vector
(for vector tiles). For more information, see the Mapbox GL JS Style Specification .
Inside the load event handler, add a source
. Set the id
to trailheads
and type
to geojson
. Set data
to the URL for the trailheads
feature layer. Append query?f=pgeojson&where=1=1
to the URL to request all of the features as GeoJSON.
Use dark colors for code blocks
Show more lines
Add line. Add line. Add line. Add line. Add line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<!DOCTYPE html >
< html >
< head >
< meta charset = "utf-8" />
< meta name = "viewport" content = "initial-scale=1,maximum-scale=1,user-scalable=no" />
< script src = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js" > </ script >
< link href = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" rel = "stylesheet" />
< script src = "https://unpkg.com/@esri/arcgis-rest-request@4.0.0/dist/bundled/request.umd.js" > </ script >
< script src = "https://unpkg.com/@esri/arcgis-rest-feature-service@4.0.0/dist/bundled/feature-service.umd.js" > </ script >
< style >
html ,
body ,
#map {
padding : 0 ;
margin : 0 ;
height : 100% ;
width : 100% ;
font-family : Arial, Helvetica, sans-serif;
font-size : 14px ;
color : #323232 ;
}
</ style >
</ head >
< body >
< div id = "map" > </ div >
< script >
const apiKey = "YOUR_API_KEY" ;
const basemapEnum = "ArcGIS:Streets" ;
const map = new mapboxgl.Map({
container : "map" , // the id of the div element
style : `https://basemaps-api.arcgis.com/arcgis/rest/services/styles/ ${basemapEnum} ?type=style&token= ${apiKey} ` ,
zoom : 12 , // starting zoom
center : [- 118.805 , 34.027 ] // starting location [longitude, latitude]
});
map.once( "load" , () => {
// This code runs once the base style has finished loading.
map.addSource( "trailheads" , {
type : "geojson" ,
data : "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0/query?f=pgeojson&where=1=1" ,
cluster : true ,
clusterRadius : 20 , // cluster two trailheads if less than 20 pixels apart
clusterMaxZoom : 14 // display all trailheads individually from zoom 14 up
});
map.addLayer({
id : "trailheads-circle" ,
type : "circle" ,
source : "trailheads" ,
paint : {
"circle-color" : "hsla(0,0%,0%,0.75)" ,
"circle-stroke-width" : 1.5 ,
"circle-stroke-color" : "white" ,
"circle-radius" : [ "case" , [ "get" , "cluster" ], 10 , 5 ] // 10 pixels for clusters, 5 pixels otherwise
}
});
map.addLayer({
id : "trailheads-cluster-count" ,
type : "symbol" ,
source : "trailheads" ,
layout : {
"text-font" : [ "Arial Bold" ],
"text-field" : [ "get" , "point_count" ],
"text-offset" : [ 0 , 0.1 ] // move the label vertically downwards slightly to improve centering
},
paint : {
"text-color" : "white"
}
});
});
</ script >
</ body >
</ html >
Show more lines
Add a circle layer A layer in Mapbox GL JS is a visual representation of the data within one source. Use a layer of type circle
to display the trailheads.
Use addLayer
to add a circle
layer with id trailheads-circle
. Set source
to trailheads
to reference the source you just created. Add paint
properties to make the circles black.
More info The type
property defines how it will be displayed. Commonly used layer types include circle
, line
, fill
and symbol
(used for text and icons).
The id
property is an identifier you choose. You will need it if you want to manipulate the layer, such as hiding it or changing its properties dynamically.
The source
property references the id
property of the source you just created.
The paint
properties control the visual attributes of the layer and are specific to the type of layer.
For more information, see the Mapbox GL JS Style Specification .
Use dark colors for code blocks
Show more lines
Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<!DOCTYPE html >
< html >
< head >
< meta charset = "utf-8" />
< meta name = "viewport" content = "initial-scale=1,maximum-scale=1,user-scalable=no" />
< script src = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js" > </ script >
< link href = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" rel = "stylesheet" />
< script src = "https://unpkg.com/@esri/arcgis-rest-request@4.0.0/dist/bundled/request.umd.js" > </ script >
< script src = "https://unpkg.com/@esri/arcgis-rest-feature-service@4.0.0/dist/bundled/feature-service.umd.js" > </ script >
< style >
html ,
body ,
#map {
padding : 0 ;
margin : 0 ;
height : 100% ;
width : 100% ;
font-family : Arial, Helvetica, sans-serif;
font-size : 14px ;
color : #323232 ;
}
</ style >
</ head >
< body >
< div id = "map" > </ div >
< script >
const apiKey = "YOUR_API_KEY" ;
const basemapEnum = "ArcGIS:Streets" ;
const map = new mapboxgl.Map({
container : "map" , // the id of the div element
style : `https://basemaps-api.arcgis.com/arcgis/rest/services/styles/ ${basemapEnum} ?type=style&token= ${apiKey} ` ,
zoom : 12 , // starting zoom
center : [- 118.805 , 34.027 ] // starting location [longitude, latitude]
});
map.once( "load" , () => {
// This code runs once the base style has finished loading.
map.addSource( "trailheads" , {
type : "geojson" ,
data : "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0/query?f=pgeojson&where=1=1" ,
cluster : true ,
clusterRadius : 20 , // cluster two trailheads if less than 20 pixels apart
clusterMaxZoom : 14 // display all trailheads individually from zoom 14 up
});
map.addLayer({
id : "trailheads-circle" ,
type : "circle" ,
source : "trailheads" ,
paint : {
"circle-color" : "hsla(0,0%,0%,0.75)" ,
"circle-stroke-width" : 1.5 ,
"circle-stroke-color" : "white" ,
"circle-radius" : [ "case" , [ "get" , "cluster" ], 10 , 5 ] // 10 pixels for clusters, 5 pixels otherwise
}
});
map.addLayer({
id : "trailheads-cluster-count" ,
type : "symbol" ,
source : "trailheads" ,
layout : {
"text-font" : [ "Arial Bold" ],
"text-field" : [ "get" , "point_count" ],
"text-offset" : [ 0 , 0.1 ] // move the label vertically downwards slightly to improve centering
},
paint : {
"text-color" : "white"
}
});
});
</ script >
</ body >
</ html >
Show more lines
At the top right, click Run to display the basemap with trailheads as black dots.
Use clusters to display points One benefit of using GeoJSON to load points in Mapbox GL JS is you can use clustering. This technique replaces a number of overlapping points with a single point that represents the cluster. It simplifies the visual appearance of the map by reducing detail, particularly at lower zoom levels.
To enable clustering, you pass additional parameters when defining the source : cluster
: true
and the optional parameters clusterRadius
and clusterMaxZoom
.
Points that represent clusters have a cluster
property that is set to true
. You can use this in your trailheads-circle
layer to make those points larger, by using a data-driven expression for circle-radius
. You use the case
and get
expressions for this.
They also have a point_count
attribute which contains the number of points in the cluster. You can display this as a text label, using a symbol layer .
Add cluster
, clusterRadius
and clusterMaxZoom
attributes to the definition of the trailheads
source.
Use dark colors for code blocks
Show more lines
Add line. Add line. Add line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<!DOCTYPE html >
< html >
< head >
< meta charset = "utf-8" />
< meta name = "viewport" content = "initial-scale=1,maximum-scale=1,user-scalable=no" />
< script src = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js" > </ script >
< link href = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" rel = "stylesheet" />
< script src = "https://unpkg.com/@esri/arcgis-rest-request@4.0.0/dist/bundled/request.umd.js" > </ script >
< script src = "https://unpkg.com/@esri/arcgis-rest-feature-service@4.0.0/dist/bundled/feature-service.umd.js" > </ script >
< style >
html ,
body ,
#map {
padding : 0 ;
margin : 0 ;
height : 100% ;
width : 100% ;
font-family : Arial, Helvetica, sans-serif;
font-size : 14px ;
color : #323232 ;
}
</ style >
</ head >
< body >
< div id = "map" > </ div >
< script >
const apiKey = "YOUR_API_KEY" ;
const basemapEnum = "ArcGIS:Streets" ;
const map = new mapboxgl.Map({
container : "map" , // the id of the div element
style : `https://basemaps-api.arcgis.com/arcgis/rest/services/styles/ ${basemapEnum} ?type=style&token= ${apiKey} ` ,
zoom : 12 , // starting zoom
center : [- 118.805 , 34.027 ] // starting location [longitude, latitude]
});
map.once( "load" , () => {
// This code runs once the base style has finished loading.
map.addSource( "trailheads" , {
type : "geojson" ,
data : "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0/query?f=pgeojson&where=1=1" ,
cluster : true ,
clusterRadius : 20 , // cluster two trailheads if less than 20 pixels apart
clusterMaxZoom : 14 // display all trailheads individually from zoom 14 up
});
map.addLayer({
id : "trailheads-circle" ,
type : "circle" ,
source : "trailheads" ,
paint : {
"circle-color" : "hsla(0,0%,0%,0.75)" ,
"circle-stroke-width" : 1.5 ,
"circle-stroke-color" : "white" ,
"circle-radius" : [ "case" , [ "get" , "cluster" ], 10 , 5 ] // 10 pixels for clusters, 5 pixels otherwise
}
});
map.addLayer({
id : "trailheads-cluster-count" ,
type : "symbol" ,
source : "trailheads" ,
layout : {
"text-font" : [ "Arial Bold" ],
"text-field" : [ "get" , "point_count" ],
"text-offset" : [ 0 , 0.1 ] // move the label vertically downwards slightly to improve centering
},
paint : {
"text-color" : "white"
}
});
});
</ script >
</ body >
</ html >
Show more lines
Make each point larger if it represents a cluster.
Use dark colors for code blocks
Show more lines
Add line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<!DOCTYPE html >
< html >
< head >
< meta charset = "utf-8" />
< meta name = "viewport" content = "initial-scale=1,maximum-scale=1,user-scalable=no" />
< script src = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js" > </ script >
< link href = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" rel = "stylesheet" />
< script src = "https://unpkg.com/@esri/arcgis-rest-request@4.0.0/dist/bundled/request.umd.js" > </ script >
< script src = "https://unpkg.com/@esri/arcgis-rest-feature-service@4.0.0/dist/bundled/feature-service.umd.js" > </ script >
< style >
html ,
body ,
#map {
padding : 0 ;
margin : 0 ;
height : 100% ;
width : 100% ;
font-family : Arial, Helvetica, sans-serif;
font-size : 14px ;
color : #323232 ;
}
</ style >
</ head >
< body >
< div id = "map" > </ div >
< script >
const apiKey = "YOUR_API_KEY" ;
const basemapEnum = "ArcGIS:Streets" ;
const map = new mapboxgl.Map({
container : "map" , // the id of the div element
style : `https://basemaps-api.arcgis.com/arcgis/rest/services/styles/ ${basemapEnum} ?type=style&token= ${apiKey} ` ,
zoom : 12 , // starting zoom
center : [- 118.805 , 34.027 ] // starting location [longitude, latitude]
});
map.once( "load" , () => {
// This code runs once the base style has finished loading.
map.addSource( "trailheads" , {
type : "geojson" ,
data : "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0/query?f=pgeojson&where=1=1" ,
cluster : true ,
clusterRadius : 20 , // cluster two trailheads if less than 20 pixels apart
clusterMaxZoom : 14 // display all trailheads individually from zoom 14 up
});
map.addLayer({
id : "trailheads-circle" ,
type : "circle" ,
source : "trailheads" ,
paint : {
"circle-color" : "hsla(0,0%,0%,0.75)" ,
"circle-stroke-width" : 1.5 ,
"circle-stroke-color" : "white" ,
"circle-radius" : [ "case" , [ "get" , "cluster" ], 10 , 5 ] // 10 pixels for clusters, 5 pixels otherwise
}
});
map.addLayer({
id : "trailheads-cluster-count" ,
type : "symbol" ,
source : "trailheads" ,
layout : {
"text-font" : [ "Arial Bold" ],
"text-field" : [ "get" , "point_count" ],
"text-offset" : [ 0 , 0.1 ] // move the label vertically downwards slightly to improve centering
},
paint : {
"text-color" : "white"
}
});
});
</ script >
</ body >
</ html >
Show more lines
Add a symbol layer, trailheads-cluster-count
. Use the attribute point_count
as the value for text-field
.
Use dark colors for code blocks
Show more lines
Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<!DOCTYPE html >
< html >
< head >
< meta charset = "utf-8" />
< meta name = "viewport" content = "initial-scale=1,maximum-scale=1,user-scalable=no" />
< script src = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js" > </ script >
< link href = "https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" rel = "stylesheet" />
< script src = "https://unpkg.com/@esri/arcgis-rest-request@4.0.0/dist/bundled/request.umd.js" > </ script >
< script src = "https://unpkg.com/@esri/arcgis-rest-feature-service@4.0.0/dist/bundled/feature-service.umd.js" > </ script >
< style >
html ,
body ,
#map {
padding : 0 ;
margin : 0 ;
height : 100% ;
width : 100% ;
font-family : Arial, Helvetica, sans-serif;
font-size : 14px ;
color : #323232 ;
}
</ style >
</ head >
< body >
< div id = "map" > </ div >
< script >
const apiKey = "YOUR_API_KEY" ;
const basemapEnum = "ArcGIS:Streets" ;
const map = new mapboxgl.Map({
container : "map" , // the id of the div element
style : `https://basemaps-api.arcgis.com/arcgis/rest/services/styles/ ${basemapEnum} ?type=style&token= ${apiKey} ` ,
zoom : 12 , // starting zoom
center : [- 118.805 , 34.027 ] // starting location [longitude, latitude]
});
map.once( "load" , () => {
// This code runs once the base style has finished loading.
map.addSource( "trailheads" , {
type : "geojson" ,
data : "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0/query?f=pgeojson&where=1=1" ,
cluster : true ,
clusterRadius : 20 , // cluster two trailheads if less than 20 pixels apart
clusterMaxZoom : 14 // display all trailheads individually from zoom 14 up
});
map.addLayer({
id : "trailheads-circle" ,
type : "circle" ,
source : "trailheads" ,
paint : {
"circle-color" : "hsla(0,0%,0%,0.75)" ,
"circle-stroke-width" : 1.5 ,
"circle-stroke-color" : "white" ,
"circle-radius" : [ "case" , [ "get" , "cluster" ], 10 , 5 ] // 10 pixels for clusters, 5 pixels otherwise
}
});
map.addLayer({
id : "trailheads-cluster-count" ,
type : "symbol" ,
source : "trailheads" ,
layout : {
"text-font" : [ "Arial Bold" ],
"text-field" : [ "get" , "point_count" ],
"text-offset" : [ 0 , 0.1 ] // move the label vertically downwards slightly to improve centering
},
paint : {
"text-color" : "white"
}
});
});
</ script >
</ body >
</ html >
Show more lines
Run the app In CodePen , run your code to display the map.
The map should display the trailheads as clusters with a number on a large circle. Zoom in to see the clusters split into smaller clusters, and eventually split into single trailhead features.
What's next? Learn how to use additional ArcGIS location services in these tutorials: