Skip to content

Add a feature layer as GeoJSON

Learn how to add features as GeoJSON to a map.

Add a feature layer as GeoJSON using API key authentication

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, add the Trailheads feature layer as GeoJSON and display the features as clusters using the MapLibre ArcGIS plugin.

Prerequisites

You need an ArcGIS Location Platform or ArcGIS Online account.

Steps

Get the starter app

Select a type of authentication below and follow the steps to create a new application.

You can choose one of the following to create a new CodePen:

  • Option 1: Complete the Display a map tutorial; or,
  • Option 2: Start from the Display a map tutorial .

Set up authentication

Create developer credentials in your portal for the type of authentication you selected.

Create a new API key credential with the correct privileges to access the resources used in this tutorial.

  1. Go to the Create an API key tutorial to follow the steps to get an access token with these privilege(s):
    • Privileges
      • Location services > Basemaps
    • Item access
      • Note: If you are using your own custom data layer for this tutorial, you need to grant the API key credentials access to the layer item. Learn more in Item access privileges.

Set developer credentials

Use the API key or OAuth developer credentials so your application can access ArcGIS services.

  1. Update the accessToken variable to use your API key.

    Expand
    Use dark colors for code blocks
    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
        /* Use for API key authentication */
        const accessToken = "YOUR_ACCESS_TOKEN";
    
    Expand

Add a load event handler

You need to wait for the map to be completely loaded before adding any layers.

  1. Add an event handler to the map load event.

    Expand
    Use dark colors for code blocks
    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
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
      <script>
        /* Use for API key authentication */
        const accessToken = "YOUR_ACCESS_TOKEN";
    
        // or
    
        /* Use for user authentication */
        // const session = await arcgisRest.ArcGISIdentityManager.beginOAuth2({
        //   clientId: "YOUR_CLIENT_ID", // Your client ID from OAuth credentials
        //   redirectUri: "YOUR_REDIRECT_URI", // The redirect URL registered in your OAuth credentials
        //   portal: "YOUR_PORTAL_URL" // Your portal URL
        // })
    
        // const accessToken = session.token;
        const map = new maplibregl.Map({
          container: "map", // the id of the div element
          zoom: 12, // starting zoom
          center: [-118.805, 34.027] // starting location [longitude, latitude]
        });
        const basemapStyle = maplibreArcGIS.BasemapStyle.applyStyle(map, {
          style: 'arcgis/outdoor',
          token: accessToken
        });
    
        map.once("load", async () => {
    
        });
    
      </script>
    
    Expand

Add feature layer source

To add the feature layer to your map, use the MapLibre ArcGIS plugin.

  1. Inside the load event handler, use the plugin to create a new FeatureLayer object that references your feature service URL.

    Expand
    Use dark colors for code blocks
    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
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
        map.once("load", async () => {
    
          // This code runs once the base style has finished loading.
          const featureLayer = await maplibreArcGIS.FeatureLayer.fromUrl(
            "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0",
            { token: accessToken });
    
        });
    
    Expand
  2. Add the feature layer source to the map. Add cluster, clusterRadius and clusterMaxZoom attributes to the definition of the trailheads source to enable feature clustering.

    Expand
    Use dark colors for code blocks
    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
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
        map.once("load", async () => {
    
          // This code runs once the base style has finished loading.
          const featureLayer = await maplibreArcGIS.FeatureLayer.fromUrl(
            "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0",
            { token: accessToken });
    
          map.addSource(featureLayer.sourceId, {
            ...featureLayer.source,
            cluster: true,
            clusterRadius: 20, // cluster two trailheads if less than 20 pixels apart
            clusterMaxZoom: 14, // display all trailheads individually from zoom 14 up
          });
    
        });
    
    Expand

Add a circle layer

A layer in MapLibre GL JS is a visual representation of the data within one source. Use a layer of type circle to display the trailheads.

  1. 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.

    Expand
    Use dark colors for code blocks
    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
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
        map.once("load", async () => {
    
          // This code runs once the base style has finished loading.
          const featureLayer = await maplibreArcGIS.FeatureLayer.fromUrl(
            "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0",
            { token: accessToken });
    
          map.addSource(featureLayer.sourceId, {
            ...featureLayer.source,
            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({
            ...featureLayer.layer,
            type: "circle",
    
            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
    
            }
          });
    
        });
    
    Expand

Use clusters to display points

One benefit of using GeoJSON to load points in MapLibre 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.

  1. Make each point larger if it represents a cluster.

    Expand
    Use dark colors for code blocks
    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
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
          map.addLayer({
            ...featureLayer.layer,
            type: "circle",
    
            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
    
            }
          });
    
    Expand
  2. Add a symbol layer, trailheads-cluster-count. Use the attribute point_count as the value for text-field.

    Expand
    Use dark colors for code blocks
    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
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
            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({
            ...featureLayer.layer,
            id: "trailheads-cluster-count",
            type: "symbol",
            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"
            }
          });
    
    Expand

Run the app

Run the app.

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 location services in these tutorials:

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.