Learn how to use style a feature layer.
A feature layer is a dataset in a feature service hosted in an ArcGIS portal. Each feature layer contains features with a single geometry type (point, line, or polygon), and a set of attributes. A feature layer can be styled in MapLibre GL JS with a layer connected to a GeoJSON source.
In this tutorial, you use symbol, line, and fill layers to style three hosted feature layers 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 and follow the steps to create a new app.
Choose API key authentication if you:
- Want the easiest way to get started.
- Want to build public applications that access ArcGIS Location Services and secure items.
- Have an ArcGIS Location Platform or ArcGIS Online account.
Choose user authentication if you:
- Want to build private applications.
- Require application users to sign in with their own ArcGIS account and access resources their behalf.
- Have an ArcGIS Online account.
To learn more about both types of authentication, go to Authentication.
Set up authentication
Set developer credentials
Use the API key or OAuth developer credentials so your application can access ArcGIS services.
Load a hiker icon
To use an image as an icon, you must first load the image into the map style using map.load and map.add.
-
Call
map.loadwithImage 'httpsas the first parameter. The second parameter is a callback taking an error and the image.://static.arcgis.com/images/ Symbols/ NP S/nps Pictograph _0231b.png' See the MapLibre GL JS documentation for more information.
Use dark colors for code blocks 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.loadImage("https://static.arcgis.com/images/Symbols/NPS/npsPictograph_0231b.png").then((resp) => { }); -
Call
map.addto defineImage hiker-iconas the provided image.A MapLibre style can contain a number of images as part of its sprite file.
map.addis required to display images that are not included in its sprite file.Image See the MapLibre GL JS documentation for more information.
Use dark colors for code blocks map.loadImage("https://static.arcgis.com/images/Symbols/NPS/npsPictograph_0231b.png").then((resp) => { map.addImage("hiker-icon", resp.data); });
Style and display trailheads with a hiker icon and labels
To display an icon of a hiker for each trailhead, along with a label, you can use a single symbol layer. Properties for the label start with text- and properties for the icon start with icon-.
-
Create an asynchronous function called
show. Inside, use the plugin to create a newTrailheads Featureobject that references the Trailheads feature service, then add the sources to the map.Layer Use dark colors for code blocks map.loadImage("https://static.arcgis.com/images/Symbols/NPS/npsPictograph_0231b.png").then((resp) => { map.addImage("hiker-icon", resp.data); }); async function showTrailheads() { const trailheads = await maplibreArcGIS.FeatureLayer.fromUrl("https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0"); trailheads.addSourcesTo(map); }); } -
Add a symbol layer for the hiker icons. Use
icon-imageto reference the icon you loaded.Use dark colors for code blocks async function showTrailheads() { const trailheads = await maplibreArcGIS.FeatureLayer.fromUrl("https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0"); trailheads.addSourcesTo(map); map.addLayer({ ...trailheads.layer, type: "symbol", layout: { "icon-image": "hiker-icon", "icon-size": 0.3, "icon-allow-overlap": true, } }); } -
Add additional label properties. Set a
text-anchorofbottom, with atext-offsetof[0, -2]to position the label above the icon. Use theTRLattribute as the text field._NAME Use dark colors for code blocks map.addLayer({ ...trailheads.layer, type: "symbol", layout: { "icon-image": "hiker-icon", "icon-size": 0.3, "icon-allow-overlap": true, "text-font": ["Arial Italic"], "text-field": ["get", "TRL_NAME"], "text-size": 12, "text-anchor": "bottom", "text-offset": [0, -2] }, paint: { "text-color": "white", "text-halo-color": "seagreen", "text-halo-width": 2 }
Add a load event handler
It is important to wait for the MapLibre GL JS map to finish loading before adding any layers.
-
Add an event handler to the map
loadevent. Inside, call theshowfunction.Trailheads Use dark colors for code blocks }, paint: { "text-color": "white", "text-halo-color": "seagreen", "text-halo-width": 2 } }); } map.on("load", async () => { await showTrailheads(); });
Style and display all trails
-
Create an asynchronous function called
show. Inside, use the plugin to create a newTrails Featureobject that references Trails feature service, then add the sources to the map.Layer Use dark colors for code blocks }, paint: { "text-color": "white", "text-halo-color": "seagreen", "text-halo-width": 2 } }); } async function showTrails() { const trails = await maplibreArcGIS.FeatureLayer.fromUrl("https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0"); trails.addSourcesTo(map); -
Add a line layer with id
trails-lineto display thetrailssource.Use dark colors for code blocks async function showTrails() { const trails = await maplibreArcGIS.FeatureLayer.fromUrl("https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0"); trails.addSourcesTo(map); map.addLayer({ ...trails.layer, paint: { "line-color": "hsl(291, 44%, 60%)", "line-opacity": 0.75 } }); -
To show trails that allow bikes with a dashed line, create a new layer that sits above the other layer. You can reuse the existing source with a different id of
biketrails-line. Use a filter to only show trails whereUSEhas a value of_BIKE YES. Setline-dasharrayto[1, 2]to create short dashes.Use dark colors for code blocks map.addLayer({ ...trails.layer, paint: { "line-color": "hsl(291, 44%, 60%)", "line-width": ["interpolate", ["linear"], ["get", "ELEV_GAIN"], 0, 3, 2300, 7], "line-opacity": 0.75 } }); map.addLayer({ ...trails.layer, id: "bike-trails", filter: ["==", ["get", "USE_BIKE"], "Yes"], paint: { "line-dasharray": [1, 2], "line-width": 2, "line-color": "hsl(300, 100%, 78.4%)" } }); }
Style trail width by elevation gain
To make the width of the trail line reflect the elevation gain, you can use a MapLibre GL expression as the value of the line-width. You use the ['interpolate'] and ['get'] expressions to achieve this.
['interpolate', ['linear'], ... creates a linear mapping from one range of input values (the number of feet of elevation gain) to a range of output values (the number of pixels of line width). You provide "stops", such as mapping 2,300 feet to 7 pixels. See the MapLibre Style Specification for details.
['get', ' accesses the ELEV property. See the MapLibre Style Specification for details.
-
Update the
trails-linelayer definition that calculatesline-widthfrom theELEVattribute._GAIN Use dark colors for code blocks map.addLayer({ ...trails.layer, paint: { "line-color": "hsl(291, 44%, 60%)", "line-width": ["interpolate", ["linear"], ["get", "ELEV_GAIN"], 0, 3, 2300, 7], "line-opacity": 0.75 } });
Display park areas with different colors
You can use a different style for each unique attribute value in a feature layer. Use a ['match', ...] expression to display park polygons with different colors depending on the TYPE field.
A ['match', ...] expression choose between several different output values depending on the input value. See the MapLibre Style Specification for details.
-
Create an asynchronous function called
show. Inside, use the plugin to create a newParks Featureobject that references the Parks feature service, then add the sources to the map.Layer Use dark colors for code blocks map.addLayer({ ...trails.layer, id: "bike-trails", filter: ["==", ["get", "USE_BIKE"], "Yes"], paint: { "line-dasharray": [1, 2], "line-width": 2, "line-color": "hsl(300, 100%, 78.4%)" } }); } async function showParks() { const parks = await maplibreArcGIS.FeatureLayer.fromUrl("https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Parks_and_Open_Space/FeatureServer/0"); parks.addSourcesTo(map); } -
Add a fill layer with id
parks-filland sourceparks. For the fill color, use amatchexpression for differentTYPEvalues.Use dark colors for code blocks async function showParks() { const parks = await maplibreArcGIS.FeatureLayer.fromUrl("https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Parks_and_Open_Space/FeatureServer/0"); parks.addSourcesTo(map); map.addLayer({ ...parks.layer, // The layer property contains a preformatted maplibre layer, including a layer ID and source. paint: { // Override default paint properties provided by parks.layer "fill-color": [ "match", ["get", "TYPE"], "Natural Areas", "#9E559C", "Regional Open Space", "#A7C636", "Local Park", "#149ECE", "Regional Recreation Park", "#ED5151", "black" ], "fill-opacity": 0.2 } }); } -
Back inside the load event handler, call the
showfunctions. Place it before the other functions so that the parks layer is placed under the trails and trailheads.Parks Use dark colors for code blocks map.on("load", async () => { await showParks(); await showTrails(); await showTrailheads(); });
Run the app
Run the app.
You should now see trailheads with icons and labels, trails whose width varies by elevation, dashed lines for trails that allow bikes, and parks of different colors.What's next?
Learn how to use additional location services in these tutorials:


