Learn how to query a feature layer with SQL.
A feature layer
In this tutorial, you perform server-side SQL queries to return a subset of the features
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
A public application is an application that allows anonymous access without requiring users to sign in with an ArcGIS account. It supports API key or app authentication. that access ArcGIS Location ServicesArcGIS Location Services, also referred to as Location Services, are services hosted by Esri that provide geospatial functionality for developing mapping applications. They include the ArcGIS Basemap Styles service, ArcGIS Static Basemap Tiles service, ArcGIS Places service, ArcGIS Geocoding service, ArcGIS Routing service, ArcGIS GeoEnrichment service, and ArcGIS Elevation service. An ArcGIS Location Platform or ArcGIS Online account is required to use the services. and secure itemsAn item, also known as a content item, is a resource stored in a portal such as a web map, hosted layer, style, script tool, file, or notebook. . - 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
An ArcGIS account is an identity with a user type and set of privileges that can access specific ArcGIS products, tools, APIs, services, and resources. The main account types that can be used for development are an ArcGIS Location Platform account, ArcGIS Online account, and ArcGIS Enterprise account. ArcGIS Location Platform and ArcGIS Online accounts are also associated with a subscription. 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
Add script references
This tutorial uses ArcGIS REST JS to query the feature layer.
-
In the
<headelement, add references to the ArcGIS REST JS library.> Use dark colors for code blocks <!-- Load MapLibre GL JS from CDN --> <script src="https://unpkg.com/maplibre-gl@5.23.0/dist/maplibre-gl.js"></script> <link href="https://unpkg.com/maplibre-gl@5.23.0/dist/maplibre-gl.css" rel="stylesheet"> <!-- Load MapLibre ArcGIS from CDN --> <script src="https://unpkg.com/@esri/maplibre-arcgis@1.1.0/dist/umd/maplibre-arcgis.min.js"></script> <script src="https://unpkg.com/@esri/arcgis-rest-request@4/dist/bundled/request.umd.js"></script> <script src="https://unpkg.com/@esri/arcgis-rest-feature-service@4/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>
Create a SQL selector
Hosted feature layers support a standard SQL query where clause. Use an HTML <select element to provide a list of SQL queries for the LA County Parcel feature layer.
To add the <select HTML element as a Map control, you create an object that implements the MapLibre GL JS IControl interface.
Creating an IControl allows MapLibre GL JS to position it, which prevents your control overlapping other MapLibre GL JS controls.
-
Create a
Queryclass with anControl onfunction. Inside, create aAdd <divelement with a> <selectinside, with options for each of the SQL> whereclauses. Return this element.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.80543, 34.03] // starting location [longitude, latitude] }); const basemapStyle = maplibreArcGIS.BasemapStyle.applyStyle(map, { style: "arcgis/outdoor", token: accessToken }); class QueryControl { onAdd(map) { const template = document.createElement("template"); template.innerHTML = `<div class="maplibregl-ctrl maplibregl-ctrl-group" style="margin:20px;"> <select style="font-size:16px; padding:4px 8px;"> <option value="">Choose a WHERE clause...</option> <option>UseType = 'Residential'</option> <option>UseType = 'Government'</option> <option>UseType = 'Irrigated Farm'</option> <option>TaxRateArea = 10853</option> <option>TaxRateArea = 10860</option> <option>TaxRateArea = 08637</option> <option>Roll_LandValue > 1000000</option> <option>Roll_LandValue < 1000000</option> </select> </div>`; return template.content; } } -
Create a
Queryand add it to the Map withControl map.add.Control Use dark colors for code blocks return template.content; } } const queryControl = new QueryControl(); map.addControl(queryControl);
Add parcel layer
Parcels returned by the query are simple GeoJSON
-
Create an
addfunction. Inside, add a GeoJSON source and a fill layer. Add the layer before the first symbol layer.Parcel Layer By inserting the polygon layer beneath the icon and text layers, it keeps the labels readable. In MapLibre GL JS, there is no distinction between "basemap" layers and "overlay layers", so you can insert the layer anywhere.
To determine the first symbol layer, you can use
map.getto get the style, then iterate to find the first layer whoseStyle typeissymbol.See the MapLibre GL JS documentation for details.
Use dark colors for code blocks const queryControl = new QueryControl(); map.addControl(queryControl); function addParcelLayer() { map.addSource("parcels", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); const firstSymbolLayer = map.getStyle().layers.find((l) => l.type === "symbol"); map.addLayer( { id: "parcels-fill", source: "parcels", type: "fill", paint: { "fill-color": "hsla(200, 80%,50%, 0.5)", "fill-outline-color": "hsl(360, 100%, 100%)" } }, firstSymbolLayer.id ); // insert new layer before this one map.addLayer({ id: "parcels-line", source: "parcels", type: "line", paint: { "line-width": 0.5, "line-color": "hsl(360, 100%,100%)" } }); // set outline style for parcels } -
Add the data attribution
Data attribution is the requirement to display the names of data source providers in all applications when accessing ArcGIS content, layers, or services. for the feature layer source.- Go to the LA County Parcels item
An item, also known as a content item, is a resource stored in a portal such as a web map, hosted layer, style, script tool, file, or notebook. . - Scroll down to the Acknowledgments section and copy its value.
- Paste the copied value to the
attributionproperty.Use dark colors for code blocks const queryControl = new QueryControl(); map.addControl(queryControl); function addParcelLayer() { map.addSource("parcels", { type: "geojson", data: { type: "FeatureCollection", features: [] }, // Attribution text retrieved from https://arcgis.com/home/item.html?id=a6fdf2ee0e454393a53ba32b9838b303 attribution: "LA County Parcels" }); const firstSymbolLayer = map.getStyle().layers.find((l) => l.type === "symbol"); map.addLayer( { id: "parcels-fill", source: "parcels", type: "fill", paint: { "fill-color": "hsla(200, 80%,50%, 0.5)", "fill-outline-color": "hsl(360, 100%, 100%)" } }, firstSymbolLayer.id ); // insert new layer before this one map.addLayer({ id: "parcels-line", source: "parcels", type: "line", paint: { "line-width": 0.5, "line-color": "hsl(360, 100%,100%)" } }); // set outline style for parcels }
- Go to the LA County Parcels item
Add load handler
To add layers to the map, you need to use the load event to ensure the map is fully loaded.
-
Add an event handler for the
loadevent. Inside, calladd.Parcel Layer Use dark colors for code blocks const firstSymbolLayer = map.getStyle().layers.find((l) => l.type === "symbol"); map.addLayer( { id: "parcels-fill", source: "parcels", type: "fill", paint: { "fill-color": "hsla(200, 80%,50%, 0.5)", "fill-outline-color": "hsl(360, 100%, 100%)" } }, firstSymbolLayer.id ); // insert new layer before this one map.addLayer({ id: "parcels-line", source: "parcels", type: "line", paint: { "line-width": 0.5, "line-color": "hsl(360, 100%,100%)" } }); // set outline style for parcels } map.on("load", () => { addParcelLayer(); });
Execute the query
Use the ArcGIS REST JS query method to find features in the LA County Parcels feature layerwhere clause.
To limit the query results to the visible extentgeometry property. This confines the SQL query to the geographic bounds (extent) of the map.
When the matching parcels are returned, call set on the parcels source
-
Create a function that calls
arcgis. Specify GeoJSONRest.query Features GeoJSON is an open standard for representing geographic information in a JSON file. It is commonly used for sharing data between web services or manipulating vector data in the browser. as the return type, requestingreturnand specificGeometry out. All of the featuresFields A feature is a single record, also known as a row, that represents a real-world entity. It typically contains a geometry (point, multipoint, polyline, or polygon) and attributes but it can also contain just attributes. within the geometryA geometry is a geometric shape, such as a point, polyline, or polygon, that contains one or more coordinates and a spatial reference. will be returned with attributeAttributes are fields and values for a single feature or non-spatial record. They are typically stored in a database or service such as a feature service. information set by theoutproperty.Fields Use dark colors for code blocks function executeQuery(whereClause, geometry) { arcgisRest .queryFeatures({ url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/LA_County_Parcels/FeatureServer/0", geometry: geometry, geometryType: "esriGeometryEnvelope", inSR: 4326, // EPSG:4326 uses latitudes and longitudes spatialRel: "esriSpatialRelIntersects", where: whereClause, f: "geojson", returnGeometry: true, outFields: ["APN", "UseType", "TaxRateCity", " Roll_LandValue"] }) } -
Add a response handler. Inside, set the returned parcels as the data for the parcels source.
Use dark colors for code blocks arcgisRest .queryFeatures({ url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/LA_County_Parcels/FeatureServer/0", geometry: geometry, geometryType: "esriGeometryEnvelope", inSR: 4326, // EPSG:4326 uses latitudes and longitudes spatialRel: "esriSpatialRelIntersects", where: whereClause, f: "geojson", returnGeometry: true, outFields: ["APN", "UseType", "TaxRateCity", " Roll_LandValue"] }) .then((response) => { map.getSource("parcels").setData(response); }); -
Update your
Queryto add aControl changeevent handler to theselectelement. Inside, calculate the viewport extent and callexecutewith the extent andQuery whereclause.Use dark colors for code blocks class QueryControl { onAdd(map) { const template = document.createElement("template"); template.innerHTML = `<div class="maplibregl-ctrl maplibregl-ctrl-group" style="margin:20px;"> <select style="font-size:16px; padding:4px 8px;"> <option value="">Choose a WHERE clause...</option> <option>UseType = 'Residential'</option> <option>UseType = 'Government'</option> <option>UseType = 'Irrigated Farm'</option> <option>TaxRateArea = 10853</option> <option>TaxRateArea = 10860</option> <option>TaxRateArea = 08637</option> <option>Roll_LandValue > 1000000</option> <option>Roll_LandValue < 1000000</option> </select> </div>`; const select = template.content.querySelector("select"); select.addEventListener("change", () => { // Do nothing for the "Choose a WHERE clause..." option if (select.value !== "") { // Get bounds in [minx, miny, maxx, maxy] format const bounds = map.getBounds().toArray().flat(); executeQuery(select.value, bounds); } }); return template.content; } } -
At the top right, click Run. When you select a where clause using the toolbox, a query will run against the feature layer
A feature layer (client-side) is a data layer that can access and display features from a feature service that has the same type of geometry and attribute fields. and display all land parcels within the current viewport matching the where clause.
Add a pop-up
You can add a Popup to view attributes of a parcel when the user clicks on it.
-
Add a
clickevent handler to theparcels-filllayer. Inside, create a popup that display the attributes of the clicked parcel. Set the position of the pop-up and add it to the map.Use dark colors for code blocks .then((response) => { map.getSource("parcels").setData(response); }); } map.on("click", "parcels-fill", (e) => { const parcel = e.features[0].properties; const landValue = parcel.Roll_LandValue != "null" ? `$${parcel.Roll_LandValue.toLocaleString()}` : "N/A"; const message = `<b>Parcel ${parcel.APN}</b></br>` + `Type: ${parcel.UseType} <br>` + `Land value: ${landValue} <br>` + `Tax Rate City: ${parcel.TaxRateCity}`; new maplibregl.Popup().setHTML(message).setLngLat(e.lngLat).addTo(map); });
Run the app
Run the app.
When the map displays, you should be able to use the query selector to display parcels. Click on a parcel to show a pop-up with the feature attributes.What's next?
Learn how to use additional location services in these tutorials:

Query a feature layer (spatial)
Execute a spatial query to access polygon features from a feature service.

Get global data
Query demographic information for locations around the world.

Get local data
Query regional facts and spending trends for a study area in the United States.

Add a feature layer as GeoJSON
Display and style GeoJSON features from a feature service.

Style a feature layer
Use data-driven styling to apply symbol colors and styles to feature layers.