Query features with SQL

Learn how to execute a SQL query to access polygon features from a feature layer.

A feature layer can contain a large number of features stored in ArcGIS. To access a subset of the features, you can execute a SQL or spatial query, or both at the same time. You can also return the attributes, geometry, or both attributes and geometry for each record. SQL and spatial queries are useful when a feature layer is very large and you just want to access a subset of the data.

In this tutorial, you perform server-side SQL queries to return a subset of the features from the LA County Parcel hosted feature layer. The feature layer contains over 2.4 million features. The resulting features are displayed as graphics on the map. A pop-up is also used to display feature attributes.

Prerequisites

You need a free ArcGIS developer account to access your dashboard and API keys. The API key must be scoped to access the services used in this tutorial.

Steps

Create a new pen

  1. To get started, either complete the Display a map tutorial or .

Set the API key

To access ArcGIS location services, you need an API key.

  1. Go to your dashboard to get an API key.

  2. In CodePen, update apiKey to use your key.

    Change line
        
    const apiKey = "YOUR-API-KEY";
    const basemapId = "ArcGIS:Streets";
    const basemapURL = "https://basemaps-api.arcgis.com/arcgis/rest/services/styles/" + basemapId + "?type=style&apiKey=" + apiKey;
    olms(map, basemapURL);
    

Add references

This tutorial uses ArcGIS REST JS to query the feature layer, and the ol-popup library to display pop-ups.

  1. In the <head> element, add references to the ArcGIS REST JS and ol-popup libraries.

    Add line.Add line.Add line.Add line.
                                                                                                                                                       
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
        <title>OpenLayers Tutorials: Display a map</title>
    
    
          <style>
          html,
          body,
          #map {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 14px;
            color: #323232;
          }
    
          #query-select {
            position: absolute;
            top: 20px;
            right: 20px;
            padding: 4px 8px;
            font-size: 16px;
            border-radius: 0; /* Causes more uniform appearance across browsers. */
          }
    
        </style>
    
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" type="text/css" />
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/ol-mapbox-style@6.1.4/dist/olms.js" type="text/javascript"></script>
    
        <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
        <script src="https://unpkg.com/@esri/arcgis-rest-feature-layer@3.0.0/dist/umd/feature-layer.umd.js"></script>
        <script src="https://unpkg.com/ol-popup@4.0.0"></script>
        <link rel="stylesheet" href="https://unpkg.com/ol-popup@4.0.0/src/ol-popup.css" />
    
      </head>
    
      <body>
        <div id="map"></div>
    
          <select id="query-select">
            <option value="" selected>Choose a WHERE clause...</option>
            <option value="UseType = 'Residential'">UseType = 'Residential'</option>
            <option value="UseType = 'Government'">UseType = 'Government'</option>
            <option value="UseType = 'Irrigated Farm'">UseType = 'Irrigated Farm'</option>
            <option value="TaxRateArea = 10853">TaxRateArea = 10853</option>
            <option value="TaxRateArea = 10860">TaxRateArea = 10860</option>
            <option value="TaxRateArea = 08637">TaxRateArea = 08637</option>
            <option value="Roll_LandValue > 1000000">Roll_LandValue &gt; 1000000</option>
            <option value="Roll_LandValue < 1000000">Roll_LandValue &lt; 1000000</option>
          </select>
    
        <script>
    
          const apiKey = "YOUR-API-KEY";
    
          const map = new ol.Map({
            target: "map"
          });
    
          map.setView(
            new ol.View({
              center: ol.proj.fromLonLat([-118.805, 34.027]),
              zoom: 12
            })
          );
    
          const basemapId = "ArcGIS:Streets";
          const basemapURL = "https://basemaps-api.arcgis.com/arcgis/rest/services/styles/" + basemapId + "?type=style&apiKey=" + apiKey;
    
          const parcelLayer = new ol.layer.Vector();
          olms(map, basemapURL).then((map) => {
    
            map.addLayer(parcelLayer);
    
          });
    
          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"]
              })
    
              .then((response) => {
                const geojson = new ol.format.GeoJSON({
                    defaultDataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:3857'
                  })
    
                let newSource = new ol.source.Vector({
                  features: geojson.readFeatures(response)
                });
                parcelLayer.setSource(newSource);
              });
    
    
          }
    
          // Listen for changes
          const select = document.getElementById("query-select");
          select.addEventListener("change", () => {
            const whereClause = select.value;
    
            const extent3857 = map.getView().calculateExtent(map.getSize());
            const extent4326 = ol.proj.transformExtent(extent3857, 'EPSG:3857', 'EPSG:4326');
            executeQuery(whereClause, extent4326);
          });
    
          const popup = new Popup();
          map.addOverlay(popup);
    
          map.on("click", (event) => {
            const parcel = map.getFeaturesAtPixel(event.pixel, {
              layerFilter: l => l === parcelLayer // don't query layers from the basemap
            })[0];
            if (parcel) {
              const message =
                `<b>Parcel ${parcel.get("APN")}</b>` +
                `<br>Type: ${parcel.get("UseType")} <br>` +
                `Land value: ${parcel.get("Roll_LandValue")} <br>` +
                `Tax Rate City: ${parcel.get("TaxRateCity")}`;
              popup.show(event.coordinate, message);
            } else {
              popup.hide();
            }
          });
    
        </script>
    
      </body>
    </html>

Create a SQL selector

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

  1. Create a select element containing an option element for each where clause.

    Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.
                                                                                                                                                       
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
        <title>OpenLayers Tutorials: Display a map</title>
    
    
          <style>
          html,
          body,
          #map {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 14px;
            color: #323232;
          }
    
          #query-select {
            position: absolute;
            top: 20px;
            right: 20px;
            padding: 4px 8px;
            font-size: 16px;
            border-radius: 0; /* Causes more uniform appearance across browsers. */
          }
    
        </style>
    
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" type="text/css" />
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/ol-mapbox-style@6.1.4/dist/olms.js" type="text/javascript"></script>
    
        <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
        <script src="https://unpkg.com/@esri/arcgis-rest-feature-layer@3.0.0/dist/umd/feature-layer.umd.js"></script>
        <script src="https://unpkg.com/ol-popup@4.0.0"></script>
        <link rel="stylesheet" href="https://unpkg.com/ol-popup@4.0.0/src/ol-popup.css" />
    
      </head>
    
      <body>
        <div id="map"></div>
    
          <select id="query-select">
            <option value="" selected>Choose a WHERE clause...</option>
            <option value="UseType = 'Residential'">UseType = 'Residential'</option>
            <option value="UseType = 'Government'">UseType = 'Government'</option>
            <option value="UseType = 'Irrigated Farm'">UseType = 'Irrigated Farm'</option>
            <option value="TaxRateArea = 10853">TaxRateArea = 10853</option>
            <option value="TaxRateArea = 10860">TaxRateArea = 10860</option>
            <option value="TaxRateArea = 08637">TaxRateArea = 08637</option>
            <option value="Roll_LandValue > 1000000">Roll_LandValue &gt; 1000000</option>
            <option value="Roll_LandValue < 1000000">Roll_LandValue &lt; 1000000</option>
          </select>
    
        <script>
    
          const apiKey = "YOUR-API-KEY";
    
          const map = new ol.Map({
            target: "map"
          });
    
          map.setView(
            new ol.View({
              center: ol.proj.fromLonLat([-118.805, 34.027]),
              zoom: 12
            })
          );
    
          const basemapId = "ArcGIS:Streets";
          const basemapURL = "https://basemaps-api.arcgis.com/arcgis/rest/services/styles/" + basemapId + "?type=style&apiKey=" + apiKey;
    
          const parcelLayer = new ol.layer.Vector();
          olms(map, basemapURL).then((map) => {
    
            map.addLayer(parcelLayer);
    
          });
    
          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"]
              })
    
              .then((response) => {
                const geojson = new ol.format.GeoJSON({
                    defaultDataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:3857'
                  })
    
                let newSource = new ol.source.Vector({
                  features: geojson.readFeatures(response)
                });
                parcelLayer.setSource(newSource);
              });
    
    
          }
    
          // Listen for changes
          const select = document.getElementById("query-select");
          select.addEventListener("change", () => {
            const whereClause = select.value;
    
            const extent3857 = map.getView().calculateExtent(map.getSize());
            const extent4326 = ol.proj.transformExtent(extent3857, 'EPSG:3857', 'EPSG:4326');
            executeQuery(whereClause, extent4326);
          });
    
          const popup = new Popup();
          map.addOverlay(popup);
    
          map.on("click", (event) => {
            const parcel = map.getFeaturesAtPixel(event.pixel, {
              layerFilter: l => l === parcelLayer // don't query layers from the basemap
            })[0];
            if (parcel) {
              const message =
                `<b>Parcel ${parcel.get("APN")}</b>` +
                `<br>Type: ${parcel.get("UseType")} <br>` +
                `Land value: ${parcel.get("Roll_LandValue")} <br>` +
                `Tax Rate City: ${parcel.get("TaxRateCity")}`;
              popup.show(event.coordinate, message);
            } else {
              popup.hide();
            }
          });
    
        </script>
    
      </body>
    </html>
  2. Add styling to position the select element in the top right.

    Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.
                                                                                                                                                       
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
        <title>OpenLayers Tutorials: Display a map</title>
    
    
          <style>
          html,
          body,
          #map {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 14px;
            color: #323232;
          }
    
          #query-select {
            position: absolute;
            top: 20px;
            right: 20px;
            padding: 4px 8px;
            font-size: 16px;
            border-radius: 0; /* Causes more uniform appearance across browsers. */
          }
    
        </style>
    
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" type="text/css" />
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/ol-mapbox-style@6.1.4/dist/olms.js" type="text/javascript"></script>
    
        <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
        <script src="https://unpkg.com/@esri/arcgis-rest-feature-layer@3.0.0/dist/umd/feature-layer.umd.js"></script>
        <script src="https://unpkg.com/ol-popup@4.0.0"></script>
        <link rel="stylesheet" href="https://unpkg.com/ol-popup@4.0.0/src/ol-popup.css" />
    
      </head>
    
      <body>
        <div id="map"></div>
    
          <select id="query-select">
            <option value="" selected>Choose a WHERE clause...</option>
            <option value="UseType = 'Residential'">UseType = 'Residential'</option>
            <option value="UseType = 'Government'">UseType = 'Government'</option>
            <option value="UseType = 'Irrigated Farm'">UseType = 'Irrigated Farm'</option>
            <option value="TaxRateArea = 10853">TaxRateArea = 10853</option>
            <option value="TaxRateArea = 10860">TaxRateArea = 10860</option>
            <option value="TaxRateArea = 08637">TaxRateArea = 08637</option>
            <option value="Roll_LandValue > 1000000">Roll_LandValue &gt; 1000000</option>
            <option value="Roll_LandValue < 1000000">Roll_LandValue &lt; 1000000</option>
          </select>
    
        <script>
    
          const apiKey = "YOUR-API-KEY";
    
          const map = new ol.Map({
            target: "map"
          });
    
          map.setView(
            new ol.View({
              center: ol.proj.fromLonLat([-118.805, 34.027]),
              zoom: 12
            })
          );
    
          const basemapId = "ArcGIS:Streets";
          const basemapURL = "https://basemaps-api.arcgis.com/arcgis/rest/services/styles/" + basemapId + "?type=style&apiKey=" + apiKey;
    
          const parcelLayer = new ol.layer.Vector();
          olms(map, basemapURL).then((map) => {
    
            map.addLayer(parcelLayer);
    
          });
    
          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"]
              })
    
              .then((response) => {
                const geojson = new ol.format.GeoJSON({
                    defaultDataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:3857'
                  })
    
                let newSource = new ol.source.Vector({
                  features: geojson.readFeatures(response)
                });
                parcelLayer.setSource(newSource);
              });
    
    
          }
    
          // Listen for changes
          const select = document.getElementById("query-select");
          select.addEventListener("change", () => {
            const whereClause = select.value;
    
            const extent3857 = map.getView().calculateExtent(map.getSize());
            const extent4326 = ol.proj.transformExtent(extent3857, 'EPSG:3857', 'EPSG:4326');
            executeQuery(whereClause, extent4326);
          });
    
          const popup = new Popup();
          map.addOverlay(popup);
    
          map.on("click", (event) => {
            const parcel = map.getFeaturesAtPixel(event.pixel, {
              layerFilter: l => l === parcelLayer // don't query layers from the basemap
            })[0];
            if (parcel) {
              const message =
                `<b>Parcel ${parcel.get("APN")}</b>` +
                `<br>Type: ${parcel.get("UseType")} <br>` +
                `Land value: ${parcel.get("Roll_LandValue")} <br>` +
                `Tax Rate City: ${parcel.get("TaxRateCity")}`;
              popup.show(event.coordinate, message);
            } else {
              popup.hide();
            }
          });
    
        </script>
    
      </body>
    </html>

Add parcel layer

Parcels returned by the query are simple GeoJSON polygons. You will create a Vector layer to display them.

  1. Add a load event handler to the olms function call. Inside, create a Vector layer and save it to a parcelLayer variable. Add it to the map with map.addLayer.

    As you are not passing a style parameter, the default styling for polygons will be used. See the OpenLayers documentation for more details.

    Add line.Add line.Add line.Add line.
                                                                                                                                                       
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
        <title>OpenLayers Tutorials: Display a map</title>
    
    
          <style>
          html,
          body,
          #map {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 14px;
            color: #323232;
          }
    
          #query-select {
            position: absolute;
            top: 20px;
            right: 20px;
            padding: 4px 8px;
            font-size: 16px;
            border-radius: 0; /* Causes more uniform appearance across browsers. */
          }
    
        </style>
    
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" type="text/css" />
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/ol-mapbox-style@6.1.4/dist/olms.js" type="text/javascript"></script>
    
        <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
        <script src="https://unpkg.com/@esri/arcgis-rest-feature-layer@3.0.0/dist/umd/feature-layer.umd.js"></script>
        <script src="https://unpkg.com/ol-popup@4.0.0"></script>
        <link rel="stylesheet" href="https://unpkg.com/ol-popup@4.0.0/src/ol-popup.css" />
    
      </head>
    
      <body>
        <div id="map"></div>
    
          <select id="query-select">
            <option value="" selected>Choose a WHERE clause...</option>
            <option value="UseType = 'Residential'">UseType = 'Residential'</option>
            <option value="UseType = 'Government'">UseType = 'Government'</option>
            <option value="UseType = 'Irrigated Farm'">UseType = 'Irrigated Farm'</option>
            <option value="TaxRateArea = 10853">TaxRateArea = 10853</option>
            <option value="TaxRateArea = 10860">TaxRateArea = 10860</option>
            <option value="TaxRateArea = 08637">TaxRateArea = 08637</option>
            <option value="Roll_LandValue > 1000000">Roll_LandValue &gt; 1000000</option>
            <option value="Roll_LandValue < 1000000">Roll_LandValue &lt; 1000000</option>
          </select>
    
        <script>
    
          const apiKey = "YOUR-API-KEY";
    
          const map = new ol.Map({
            target: "map"
          });
    
          map.setView(
            new ol.View({
              center: ol.proj.fromLonLat([-118.805, 34.027]),
              zoom: 12
            })
          );
    
          const basemapId = "ArcGIS:Streets";
          const basemapURL = "https://basemaps-api.arcgis.com/arcgis/rest/services/styles/" + basemapId + "?type=style&apiKey=" + apiKey;
    
          const parcelLayer = new ol.layer.Vector();
          olms(map, basemapURL).then((map) => {
    
            map.addLayer(parcelLayer);
    
          });
    
          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"]
              })
    
              .then((response) => {
                const geojson = new ol.format.GeoJSON({
                    defaultDataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:3857'
                  })
    
                let newSource = new ol.source.Vector({
                  features: geojson.readFeatures(response)
                });
                parcelLayer.setSource(newSource);
              });
    
    
          }
    
          // Listen for changes
          const select = document.getElementById("query-select");
          select.addEventListener("change", () => {
            const whereClause = select.value;
    
            const extent3857 = map.getView().calculateExtent(map.getSize());
            const extent4326 = ol.proj.transformExtent(extent3857, 'EPSG:3857', 'EPSG:4326');
            executeQuery(whereClause, extent4326);
          });
    
          const popup = new Popup();
          map.addOverlay(popup);
    
          map.on("click", (event) => {
            const parcel = map.getFeaturesAtPixel(event.pixel, {
              layerFilter: l => l === parcelLayer // don't query layers from the basemap
            })[0];
            if (parcel) {
              const message =
                `<b>Parcel ${parcel.get("APN")}</b>` +
                `<br>Type: ${parcel.get("UseType")} <br>` +
                `Land value: ${parcel.get("Roll_LandValue")} <br>` +
                `Tax Rate City: ${parcel.get("TaxRateCity")}`;
              popup.show(event.coordinate, message);
            } else {
              popup.hide();
            }
          });
    
        </script>
    
      </body>
    </html>

Create the query

Use the ArcGIS REST JS queryFeatures method to find features in the LA County Parcels feature layer that match the selected where clause.

To limit the query results to the visible extent of the Map, you can set the queryFeatures geometry property. To do so your function will take a geometry object in the required esriGeometryEnvelope format of [minx, miny, maxx, maxy]. This confines the SQL query to the geographic bounds (extent) of the map.

When the matching parcels are returned, create a new Vector source using a GeoJSON feature format, to update your parcelsLayer Vector layer.

  1. Create a function called executeQuery with whereClause and geometry parameters. Inside, create a new arcgisRest.ApiKey to access the feature service. Call arcgisRest.queryFeatures. Pass the geometry and geometryType. Specify GeoJSON as the return type, requesting returnGeometry and specific outFields. All of the features within the geometry will be returned with attribute information set by the outFields property.

    There are many other spatial relationships that you can specify with spatialRel. For example, you can use esriSpatialRelContains to only return parcels completely within the viewport. See the ArcGIS REST API documentation for details.

    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.Add line.Add line.Add line.Add line.
                                                                                                                                                       
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
        <title>OpenLayers Tutorials: Display a map</title>
    
    
          <style>
          html,
          body,
          #map {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 14px;
            color: #323232;
          }
    
          #query-select {
            position: absolute;
            top: 20px;
            right: 20px;
            padding: 4px 8px;
            font-size: 16px;
            border-radius: 0; /* Causes more uniform appearance across browsers. */
          }
    
        </style>
    
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" type="text/css" />
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/ol-mapbox-style@6.1.4/dist/olms.js" type="text/javascript"></script>
    
        <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
        <script src="https://unpkg.com/@esri/arcgis-rest-feature-layer@3.0.0/dist/umd/feature-layer.umd.js"></script>
        <script src="https://unpkg.com/ol-popup@4.0.0"></script>
        <link rel="stylesheet" href="https://unpkg.com/ol-popup@4.0.0/src/ol-popup.css" />
    
      </head>
    
      <body>
        <div id="map"></div>
    
          <select id="query-select">
            <option value="" selected>Choose a WHERE clause...</option>
            <option value="UseType = 'Residential'">UseType = 'Residential'</option>
            <option value="UseType = 'Government'">UseType = 'Government'</option>
            <option value="UseType = 'Irrigated Farm'">UseType = 'Irrigated Farm'</option>
            <option value="TaxRateArea = 10853">TaxRateArea = 10853</option>
            <option value="TaxRateArea = 10860">TaxRateArea = 10860</option>
            <option value="TaxRateArea = 08637">TaxRateArea = 08637</option>
            <option value="Roll_LandValue > 1000000">Roll_LandValue &gt; 1000000</option>
            <option value="Roll_LandValue < 1000000">Roll_LandValue &lt; 1000000</option>
          </select>
    
        <script>
    
          const apiKey = "YOUR-API-KEY";
    
          const map = new ol.Map({
            target: "map"
          });
    
          map.setView(
            new ol.View({
              center: ol.proj.fromLonLat([-118.805, 34.027]),
              zoom: 12
            })
          );
    
          const basemapId = "ArcGIS:Streets";
          const basemapURL = "https://basemaps-api.arcgis.com/arcgis/rest/services/styles/" + basemapId + "?type=style&apiKey=" + apiKey;
    
          const parcelLayer = new ol.layer.Vector();
          olms(map, basemapURL).then((map) => {
    
            map.addLayer(parcelLayer);
    
          });
    
          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"]
              })
    
              .then((response) => {
                const geojson = new ol.format.GeoJSON({
                    defaultDataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:3857'
                  })
    
                let newSource = new ol.source.Vector({
                  features: geojson.readFeatures(response)
                });
                parcelLayer.setSource(newSource);
              });
    
    
          }
    
          // Listen for changes
          const select = document.getElementById("query-select");
          select.addEventListener("change", () => {
            const whereClause = select.value;
    
            const extent3857 = map.getView().calculateExtent(map.getSize());
            const extent4326 = ol.proj.transformExtent(extent3857, 'EPSG:3857', 'EPSG:4326');
            executeQuery(whereClause, extent4326);
          });
    
          const popup = new Popup();
          map.addOverlay(popup);
    
          map.on("click", (event) => {
            const parcel = map.getFeaturesAtPixel(event.pixel, {
              layerFilter: l => l === parcelLayer // don't query layers from the basemap
            })[0];
            if (parcel) {
              const message =
                `<b>Parcel ${parcel.get("APN")}</b>` +
                `<br>Type: ${parcel.get("UseType")} <br>` +
                `Land value: ${parcel.get("Roll_LandValue")} <br>` +
                `Tax Rate City: ${parcel.get("TaxRateCity")}`;
              popup.show(event.coordinate, message);
            } else {
              popup.hide();
            }
          });
    
        </script>
    
      </body>
    </html>
  2. Add a response handler. Inside, create a GeoJSON feature format. Use it to read features from the response to a new Vector source.

    Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.
                                                                                                                                                       
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
        <title>OpenLayers Tutorials: Display a map</title>
    
    
          <style>
          html,
          body,
          #map {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 14px;
            color: #323232;
          }
    
          #query-select {
            position: absolute;
            top: 20px;
            right: 20px;
            padding: 4px 8px;
            font-size: 16px;
            border-radius: 0; /* Causes more uniform appearance across browsers. */
          }
    
        </style>
    
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" type="text/css" />
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/ol-mapbox-style@6.1.4/dist/olms.js" type="text/javascript"></script>
    
        <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
        <script src="https://unpkg.com/@esri/arcgis-rest-feature-layer@3.0.0/dist/umd/feature-layer.umd.js"></script>
        <script src="https://unpkg.com/ol-popup@4.0.0"></script>
        <link rel="stylesheet" href="https://unpkg.com/ol-popup@4.0.0/src/ol-popup.css" />
    
      </head>
    
      <body>
        <div id="map"></div>
    
          <select id="query-select">
            <option value="" selected>Choose a WHERE clause...</option>
            <option value="UseType = 'Residential'">UseType = 'Residential'</option>
            <option value="UseType = 'Government'">UseType = 'Government'</option>
            <option value="UseType = 'Irrigated Farm'">UseType = 'Irrigated Farm'</option>
            <option value="TaxRateArea = 10853">TaxRateArea = 10853</option>
            <option value="TaxRateArea = 10860">TaxRateArea = 10860</option>
            <option value="TaxRateArea = 08637">TaxRateArea = 08637</option>
            <option value="Roll_LandValue > 1000000">Roll_LandValue &gt; 1000000</option>
            <option value="Roll_LandValue < 1000000">Roll_LandValue &lt; 1000000</option>
          </select>
    
        <script>
    
          const apiKey = "YOUR-API-KEY";
    
          const map = new ol.Map({
            target: "map"
          });
    
          map.setView(
            new ol.View({
              center: ol.proj.fromLonLat([-118.805, 34.027]),
              zoom: 12
            })
          );
    
          const basemapId = "ArcGIS:Streets";
          const basemapURL = "https://basemaps-api.arcgis.com/arcgis/rest/services/styles/" + basemapId + "?type=style&apiKey=" + apiKey;
    
          const parcelLayer = new ol.layer.Vector();
          olms(map, basemapURL).then((map) => {
    
            map.addLayer(parcelLayer);
    
          });
    
          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"]
              })
    
              .then((response) => {
                const geojson = new ol.format.GeoJSON({
                    defaultDataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:3857'
                  })
    
                let newSource = new ol.source.Vector({
                  features: geojson.readFeatures(response)
                });
                parcelLayer.setSource(newSource);
              });
    
    
          }
    
          // Listen for changes
          const select = document.getElementById("query-select");
          select.addEventListener("change", () => {
            const whereClause = select.value;
    
            const extent3857 = map.getView().calculateExtent(map.getSize());
            const extent4326 = ol.proj.transformExtent(extent3857, 'EPSG:3857', 'EPSG:4326');
            executeQuery(whereClause, extent4326);
          });
    
          const popup = new Popup();
          map.addOverlay(popup);
    
          map.on("click", (event) => {
            const parcel = map.getFeaturesAtPixel(event.pixel, {
              layerFilter: l => l === parcelLayer // don't query layers from the basemap
            })[0];
            if (parcel) {
              const message =
                `<b>Parcel ${parcel.get("APN")}</b>` +
                `<br>Type: ${parcel.get("UseType")} <br>` +
                `Land value: ${parcel.get("Roll_LandValue")} <br>` +
                `Tax Rate City: ${parcel.get("TaxRateCity")}`;
              popup.show(event.coordinate, message);
            } else {
              popup.hide();
            }
          });
    
        </script>
    
      </body>
    </html>

Execute the query

The executeQuery function needs to execute when you select an option from the select control. You achieve this by adding a change event handler. You can access the chosen where clause with the value property of the select element.

You need to calculate the extent of the current map view in latitude and longitude. You can use View.calculateExtent to get the extent, and proj.transformExtent to convert it to the EPSG:4326 spatial reference.

  1. Add a change event handler to the select element. Inside, use View.calculateExtent and ol.proj.transformExtent to calculate the viewport extent. Call executeQuery with the extent and where clause.

    Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.
                                                                                                                                                       
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
        <title>OpenLayers Tutorials: Display a map</title>
    
    
          <style>
          html,
          body,
          #map {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 14px;
            color: #323232;
          }
    
          #query-select {
            position: absolute;
            top: 20px;
            right: 20px;
            padding: 4px 8px;
            font-size: 16px;
            border-radius: 0; /* Causes more uniform appearance across browsers. */
          }
    
        </style>
    
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" type="text/css" />
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/ol-mapbox-style@6.1.4/dist/olms.js" type="text/javascript"></script>
    
        <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
        <script src="https://unpkg.com/@esri/arcgis-rest-feature-layer@3.0.0/dist/umd/feature-layer.umd.js"></script>
        <script src="https://unpkg.com/ol-popup@4.0.0"></script>
        <link rel="stylesheet" href="https://unpkg.com/ol-popup@4.0.0/src/ol-popup.css" />
    
      </head>
    
      <body>
        <div id="map"></div>
    
          <select id="query-select">
            <option value="" selected>Choose a WHERE clause...</option>
            <option value="UseType = 'Residential'">UseType = 'Residential'</option>
            <option value="UseType = 'Government'">UseType = 'Government'</option>
            <option value="UseType = 'Irrigated Farm'">UseType = 'Irrigated Farm'</option>
            <option value="TaxRateArea = 10853">TaxRateArea = 10853</option>
            <option value="TaxRateArea = 10860">TaxRateArea = 10860</option>
            <option value="TaxRateArea = 08637">TaxRateArea = 08637</option>
            <option value="Roll_LandValue > 1000000">Roll_LandValue &gt; 1000000</option>
            <option value="Roll_LandValue < 1000000">Roll_LandValue &lt; 1000000</option>
          </select>
    
        <script>
    
          const apiKey = "YOUR-API-KEY";
    
          const map = new ol.Map({
            target: "map"
          });
    
          map.setView(
            new ol.View({
              center: ol.proj.fromLonLat([-118.805, 34.027]),
              zoom: 12
            })
          );
    
          const basemapId = "ArcGIS:Streets";
          const basemapURL = "https://basemaps-api.arcgis.com/arcgis/rest/services/styles/" + basemapId + "?type=style&apiKey=" + apiKey;
    
          const parcelLayer = new ol.layer.Vector();
          olms(map, basemapURL).then((map) => {
    
            map.addLayer(parcelLayer);
    
          });
    
          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"]
              })
    
              .then((response) => {
                const geojson = new ol.format.GeoJSON({
                    defaultDataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:3857'
                  })
    
                let newSource = new ol.source.Vector({
                  features: geojson.readFeatures(response)
                });
                parcelLayer.setSource(newSource);
              });
    
    
          }
    
          // Listen for changes
          const select = document.getElementById("query-select");
          select.addEventListener("change", () => {
            const whereClause = select.value;
    
            const extent3857 = map.getView().calculateExtent(map.getSize());
            const extent4326 = ol.proj.transformExtent(extent3857, 'EPSG:3857', 'EPSG:4326');
            executeQuery(whereClause, extent4326);
          });
    
          const popup = new Popup();
          map.addOverlay(popup);
    
          map.on("click", (event) => {
            const parcel = map.getFeaturesAtPixel(event.pixel, {
              layerFilter: l => l === parcelLayer // don't query layers from the basemap
            })[0];
            if (parcel) {
              const message =
                `<b>Parcel ${parcel.get("APN")}</b>` +
                `<br>Type: ${parcel.get("UseType")} <br>` +
                `Land value: ${parcel.get("Roll_LandValue")} <br>` +
                `Tax Rate City: ${parcel.get("TaxRateCity")}`;
              popup.show(event.coordinate, message);
            } else {
              popup.hide();
            }
          });
    
        </script>
    
      </body>
    </html>
  2. At the top right, click Run.

When you select a where clause using the select control, a query will run against the feature layer and display all land parcels within the current viewport matching the where clause.

Add a pop-up

The parcels returned from the query contain many attributes, such as land value and land use type. You can make the parcels interactive by showing a pop-up when the user clicks on a parcel. You create a Popup to display parcel attributes. It is a type of Overlay so you add it to the map with map.addOverlay.

  1. Create a Popup and save it to a popup variable. Add it to the map with map.addOverlay.

    Add line.Add line.
                                                                                                                                                       
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
        <title>OpenLayers Tutorials: Display a map</title>
    
    
          <style>
          html,
          body,
          #map {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 14px;
            color: #323232;
          }
    
          #query-select {
            position: absolute;
            top: 20px;
            right: 20px;
            padding: 4px 8px;
            font-size: 16px;
            border-radius: 0; /* Causes more uniform appearance across browsers. */
          }
    
        </style>
    
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" type="text/css" />
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/ol-mapbox-style@6.1.4/dist/olms.js" type="text/javascript"></script>
    
        <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
        <script src="https://unpkg.com/@esri/arcgis-rest-feature-layer@3.0.0/dist/umd/feature-layer.umd.js"></script>
        <script src="https://unpkg.com/ol-popup@4.0.0"></script>
        <link rel="stylesheet" href="https://unpkg.com/ol-popup@4.0.0/src/ol-popup.css" />
    
      </head>
    
      <body>
        <div id="map"></div>
    
          <select id="query-select">
            <option value="" selected>Choose a WHERE clause...</option>
            <option value="UseType = 'Residential'">UseType = 'Residential'</option>
            <option value="UseType = 'Government'">UseType = 'Government'</option>
            <option value="UseType = 'Irrigated Farm'">UseType = 'Irrigated Farm'</option>
            <option value="TaxRateArea = 10853">TaxRateArea = 10853</option>
            <option value="TaxRateArea = 10860">TaxRateArea = 10860</option>
            <option value="TaxRateArea = 08637">TaxRateArea = 08637</option>
            <option value="Roll_LandValue > 1000000">Roll_LandValue &gt; 1000000</option>
            <option value="Roll_LandValue < 1000000">Roll_LandValue &lt; 1000000</option>
          </select>
    
        <script>
    
          const apiKey = "YOUR-API-KEY";
    
          const map = new ol.Map({
            target: "map"
          });
    
          map.setView(
            new ol.View({
              center: ol.proj.fromLonLat([-118.805, 34.027]),
              zoom: 12
            })
          );
    
          const basemapId = "ArcGIS:Streets";
          const basemapURL = "https://basemaps-api.arcgis.com/arcgis/rest/services/styles/" + basemapId + "?type=style&apiKey=" + apiKey;
    
          const parcelLayer = new ol.layer.Vector();
          olms(map, basemapURL).then((map) => {
    
            map.addLayer(parcelLayer);
    
          });
    
          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"]
              })
    
              .then((response) => {
                const geojson = new ol.format.GeoJSON({
                    defaultDataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:3857'
                  })
    
                let newSource = new ol.source.Vector({
                  features: geojson.readFeatures(response)
                });
                parcelLayer.setSource(newSource);
              });
    
    
          }
    
          // Listen for changes
          const select = document.getElementById("query-select");
          select.addEventListener("change", () => {
            const whereClause = select.value;
    
            const extent3857 = map.getView().calculateExtent(map.getSize());
            const extent4326 = ol.proj.transformExtent(extent3857, 'EPSG:3857', 'EPSG:4326');
            executeQuery(whereClause, extent4326);
          });
    
          const popup = new Popup();
          map.addOverlay(popup);
    
          map.on("click", (event) => {
            const parcel = map.getFeaturesAtPixel(event.pixel, {
              layerFilter: l => l === parcelLayer // don't query layers from the basemap
            })[0];
            if (parcel) {
              const message =
                `<b>Parcel ${parcel.get("APN")}</b>` +
                `<br>Type: ${parcel.get("UseType")} <br>` +
                `Land value: ${parcel.get("Roll_LandValue")} <br>` +
                `Tax Rate City: ${parcel.get("TaxRateCity")}`;
              popup.show(event.coordinate, message);
            } else {
              popup.hide();
            }
          });
    
        </script>
    
      </body>
    </html>
  2. Create a map click event handler. Inside, use map.getFeaturesAtPixel to get the clicked parcel, if any. If there is one, use popup.show to display a pop-up containing the parcel's APN, use type, land value and taxation city.

    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.Add line.Add line.
                                                                                                                                                       
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
        <title>OpenLayers Tutorials: Display a map</title>
    
    
          <style>
          html,
          body,
          #map {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
            font-family: Arial, Helvetica, sans-serif;
            font-size: 14px;
            color: #323232;
          }
    
          #query-select {
            position: absolute;
            top: 20px;
            right: 20px;
            padding: 4px 8px;
            font-size: 16px;
            border-radius: 0; /* Causes more uniform appearance across browsers. */
          }
    
        </style>
    
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" type="text/css" />
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/ol-mapbox-style@6.1.4/dist/olms.js" type="text/javascript"></script>
    
        <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
        <script src="https://unpkg.com/@esri/arcgis-rest-feature-layer@3.0.0/dist/umd/feature-layer.umd.js"></script>
        <script src="https://unpkg.com/ol-popup@4.0.0"></script>
        <link rel="stylesheet" href="https://unpkg.com/ol-popup@4.0.0/src/ol-popup.css" />
    
      </head>
    
      <body>
        <div id="map"></div>
    
          <select id="query-select">
            <option value="" selected>Choose a WHERE clause...</option>
            <option value="UseType = 'Residential'">UseType = 'Residential'</option>
            <option value="UseType = 'Government'">UseType = 'Government'</option>
            <option value="UseType = 'Irrigated Farm'">UseType = 'Irrigated Farm'</option>
            <option value="TaxRateArea = 10853">TaxRateArea = 10853</option>
            <option value="TaxRateArea = 10860">TaxRateArea = 10860</option>
            <option value="TaxRateArea = 08637">TaxRateArea = 08637</option>
            <option value="Roll_LandValue > 1000000">Roll_LandValue &gt; 1000000</option>
            <option value="Roll_LandValue < 1000000">Roll_LandValue &lt; 1000000</option>
          </select>
    
        <script>
    
          const apiKey = "YOUR-API-KEY";
    
          const map = new ol.Map({
            target: "map"
          });
    
          map.setView(
            new ol.View({
              center: ol.proj.fromLonLat([-118.805, 34.027]),
              zoom: 12
            })
          );
    
          const basemapId = "ArcGIS:Streets";
          const basemapURL = "https://basemaps-api.arcgis.com/arcgis/rest/services/styles/" + basemapId + "?type=style&apiKey=" + apiKey;
    
          const parcelLayer = new ol.layer.Vector();
          olms(map, basemapURL).then((map) => {
    
            map.addLayer(parcelLayer);
    
          });
    
          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"]
              })
    
              .then((response) => {
                const geojson = new ol.format.GeoJSON({
                    defaultDataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:3857'
                  })
    
                let newSource = new ol.source.Vector({
                  features: geojson.readFeatures(response)
                });
                parcelLayer.setSource(newSource);
              });
    
    
          }
    
          // Listen for changes
          const select = document.getElementById("query-select");
          select.addEventListener("change", () => {
            const whereClause = select.value;
    
            const extent3857 = map.getView().calculateExtent(map.getSize());
            const extent4326 = ol.proj.transformExtent(extent3857, 'EPSG:3857', 'EPSG:4326');
            executeQuery(whereClause, extent4326);
          });
    
          const popup = new Popup();
          map.addOverlay(popup);
    
          map.on("click", (event) => {
            const parcel = map.getFeaturesAtPixel(event.pixel, {
              layerFilter: l => l === parcelLayer // don't query layers from the basemap
            })[0];
            if (parcel) {
              const message =
                `<b>Parcel ${parcel.get("APN")}</b>` +
                `<br>Type: ${parcel.get("UseType")} <br>` +
                `Land value: ${parcel.get("Roll_LandValue")} <br>` +
                `Tax Rate City: ${parcel.get("TaxRateCity")}`;
              popup.show(event.coordinate, message);
            } else {
              popup.hide();
            }
          });
    
        </script>
    
      </body>
    </html>

Run the app

In CodePen, run your code to display the map.

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's attributes.

What's next?

Learn how to use additional ArcGIS location services in these tutorials: