Learn how to perform a text-based search to find places within a bounding box.
Click on any place graphic to see its details in a popup.
The ArcGIS Places service allows you to search for places within a geographic bounding box, typically the visible area of a map. After performing a search, you can request additional details for each place, such as street address and telephone number.
In this tutorial, you’ll use the ArcGIS Maps SDK for JavaScript to:
- Perform a bounding box search for places based on the current map extent
- Display results as interactive graphics on the map
- Use the Calcite Design System to build a category-based search interface
- Show detailed place information in a popup when a result is clicked
By the end, you’ll have a functional app that lets users explore places within the map view and see details for each location.
Prerequisites
Steps
Create a new pen
- To get started, either complete the Display a map tutorial or
.
Get an access token
You need an
- Go to the Create an API key tutorial and create an
API key with the followingprivilege(s) :
- Privileges
- Location services > Basemaps
- Location services > Places
- Privileges
- In CodePen, set the
apiKeyproperty on the globalesriConfigvariable to your access token.var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};
To learn about other ways to get an access token, go to Types of authentication.
Update the HTML
-
Set up the main layout for your app:
- Use the Calcite Shell component to define the application container and enable dark mode.
- Add the Map component to display the interactive map.
- Set the initial center and zoom.
- Disable the default popup, since you will display custom place details in your own popup component.
- Insert the Zoom component in the top-left slot for map navigation.
- Add the Calcite Chip Group component in the top-right slot to let users select a place category (e.g., Restaurants, Hotels, Museums, ATMs, Breweries).
- Place the Popup component in the popup slot to show details when a place is clicked.
Ensure each UI element is placed in the correct slot to provide a clear and intuitive user experience.
<body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell></body>
Update the CSS
-
The Calcite Shell component automatically fills the available space, so you do not need extra CSS to size the map container. Remove any previous CSS that set the height or margin for the html or body.
<style>html,body {height: 100%;margin: 0;}</style> -
Add custom CSS to style the Calcite Chip components so they stand out clearly against the basemap. Use the
--calcite-color-foreground-1CSS variable for the background color to match the Zoom component and ensure visual consistency.16 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}33 collapsed lines</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell></body></html>
Add JavaScript
-
In a new
<script>at the bottom of the<body>, import the following modules from the ArcGIS Maps SDK for JavaScript:These modules are required to create the map, display place results, and interact with the places service.
24 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);</script></body>3 collapsed lines</html> -
Create a state object to store the current map extent and the selected search category. This object will be updated whenever the map view changes or a new category is selected, and will be used for querying places within the bounding box.
68 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};7 collapsed lines</script></body></html> -
Query the DOM for the key UI components you created in the HTML step, such as the Calcite Chip Group, Popup, and Map, so you can interact with them in your JavaScript code (e.g., to listen for events or update their properties).
74 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");7 collapsed lines</script></body></html> -
Create a new GraphicsLayer to store the graphics representing place results on the map. This layer will be added to the map and updated whenever a new places search is performed.
83 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});7 collapsed lines</script></body></html> -
Initialize a new Map with your chosen basemap (e.g., “arcgis/navigation”) and add the GraphicsLayer you created.
89 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});// Initialize the map and add the places layerviewElement.map = new Map({basemap: "arcgis/navigation",layers: [placesLayer],});7 collapsed lines</script></body></html> -
To ensure the bounding box stays within service limits, configure Map’s zoom constraints to prevent zooming out too far.
95 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});// Initialize the map and add the places layerviewElement.map = new Map({basemap: "arcgis/navigation",layers: [placesLayer],});// Configure the maps's zoom constraintsawait viewElement.viewOnReady();viewElement.constraints.maxZoom = 20;viewElement.constraints.minZoom = 12;7 collapsed lines</script></body></html>
Find places in the map extent
-
Create an asynchronous function named
queryPlacesto search for places within the current map extent:- Check that both the map extent and search text are defined in your state object. If either is missing, exit the function early.
- The
searchTextparameter allows you to search across multiple attributes of a place, including its name and category.
- The
- If both are present, call the queryPlacesWithinExtent method, passing the current extent and search text, and request icons for each place.
- Pass the response to the
addGraphicsfunction to display the results on the map. - Include error handling to log any issues that occur during the query.
100 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});// Initialize the map and add the places layerviewElement.map = new Map({basemap: "arcgis/navigation",layers: [placesLayer],});// Configure the maps's zoom constraintsawait viewElement.viewOnReady();viewElement.constraints.maxZoom = 20;viewElement.constraints.minZoom = 12;// Function to query places within the current extentasync function queryPlaces() {// Ensure extent and search text are definedif (!state.extent || !state.searchText) {return;}// Perform the places queryPlacesWithinExtent requesttry {const response = await places.queryPlacesWithinExtent(new PlacesQueryParameters({extent: state.extent,icon: "png",searchText: state.searchText,}),);addGraphics(response);} catch (error) {console.error("Error querying places within extent:", error);}}7 collapsed lines</script></body></html> - Check that both the map extent and search text are defined in your state object. If either is missing, exit the function early.
-
Create a function named
addGraphicsto display place results on the map:- Take the places search response as an argument.
- Use the JavaScript array map method to iterate through the results and create a new Graphic for each place.
- For each place:
- Build a categories string from the array of categories in the result.
- Add other relevant attributes (e.g., name, distance, placeId).
- Set the geometry to the place’s location.
- Create a PictureMarkerSymbol using the icon URL and set its size.
- Clear any existing graphics from the GraphicsLayer, then add the new graphics to the layer.
100 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});// Initialize the map and add the places layerviewElement.map = new Map({basemap: "arcgis/navigation",layers: [placesLayer],});// Configure the maps's zoom constraintsawait viewElement.viewOnReady();viewElement.constraints.maxZoom = 20;viewElement.constraints.minZoom = 12;// Function to add graphics to the places layer based on query resultsfunction addGraphics(response) {// Using the JavaScript map method, convert each place result to a graphicconst graphics = response.results.map((result) => {const categories = result.categories.map((category) => `${category.label}`).join(", ");const graphic = new Graphic({attributes: {categories: categories,distance: result.distance,name: result.name,placeId: result.placeId,},geometry: result.location,symbol: new PictureMarkerSymbol({url: result.icon.url,width: 24,height: 24,}),});return graphic;});// Remove existing graphics and add the new ones to the places layerplacesLayer.removeAll();placesLayer.addMany(graphics);}29 collapsed lines// Function to query places within the current extentasync function queryPlaces() {// Ensure extent and search text are definedif (!state.extent || !state.searchText) {return;}// Perform the places queryPlacesWithinExtent requesttry {const response = await places.queryPlacesWithinExtent(new PlacesQueryParameters({extent: state.extent,icon: "png",searchText: state.searchText,}),);addGraphics(response);} catch (error) {console.error("Error querying places within extent:", error);}}</script></body></html>
Add event listeners
-
Add an event listener to the Calcite Chip Group’s
calciteChipGroupSelectevent:- When a user selects a new category chip, update the search category in your state object with the selected value.
- If the popup is currently visible, hide it to avoid showing outdated information.
- Call the
queryPlacesfunction to perform a new search and update the map with results for the selected category.
100 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});// Initialize the map and add the places layerviewElement.map = new Map({basemap: "arcgis/navigation",layers: [placesLayer],});// Configure the maps's zoom constraintsawait viewElement.viewOnReady();viewElement.constraints.maxZoom = 20;viewElement.constraints.minZoom = 12;// Event listener for chip group selection changes to update search text, close any visible popup and query placeschipGroup.addEventListener("calciteChipGroupSelect", () => {if (chipGroup.selectedItems.length > 0) {state.searchText = chipGroup.selectedItems[0].value;if (popupElement.visible) {popupElement.visible = false;}queryPlaces();}});57 collapsed lines// Function to add graphics to the places layer based on query resultsfunction addGraphics(response) {// Using the JavaScript map method, convert each place result to a graphicconst graphics = response.results.map((result) => {const categories = result.categories.map((category) => `${category.label}`).join(", ");const graphic = new Graphic({attributes: {categories: categories,distance: result.distance,name: result.name,placeId: result.placeId,},geometry: result.location,symbol: new PictureMarkerSymbol({url: result.icon.url,width: 24,height: 24,}),});return graphic;});// Remove existing graphics and add the new ones to the places layerplacesLayer.removeAll();placesLayer.addMany(graphics);}// Function to query places within the current extentasync function queryPlaces() {// Ensure extent and search text are definedif (!state.extent || !state.searchText) {return;}// Perform the places queryPlacesWithinExtent requesttry {const response = await places.queryPlacesWithinExtent(new PlacesQueryParameters({extent: state.extent,icon: "png",searchText: state.searchText,}),);addGraphics(response);} catch (error) {console.error("Error querying places within extent:", error);}}</script></body></html> -
Add an event listener to the Map’s
arcgisViewChangeevent:- When the map view changes (e.g., pan or zoom), update the extent property in your state object with the new map extent.
- Call the
queryPlacesfunction to perform a new search and update the map with results for the new visible extent.
113 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});// Initialize the map and add the places layerviewElement.map = new Map({basemap: "arcgis/navigation",layers: [placesLayer],});// Configure the maps's zoom constraintsawait viewElement.viewOnReady();viewElement.constraints.maxZoom = 20;viewElement.constraints.minZoom = 12;// Event listener for chip group selection changes to update search text, close any visible popup and query placeschipGroup.addEventListener("calciteChipGroupSelect", () => {if (chipGroup.selectedItems.length > 0) {state.searchText = chipGroup.selectedItems[0].value;if (popupElement.visible) {popupElement.visible = false;}queryPlaces();}});// Event listener for map view changes to update extent and query placesviewElement.addEventListener("arcgisViewChange", () => {state.extent = viewElement.extent;queryPlaces();});57 collapsed lines// Function to add graphics to the places layer based on query resultsfunction addGraphics(response) {// Using the JavaScript map method, convert each place result to a graphicconst graphics = response.results.map((result) => {const categories = result.categories.map((category) => `${category.label}`).join(", ");const graphic = new Graphic({attributes: {categories: categories,distance: result.distance,name: result.name,placeId: result.placeId,},geometry: result.location,symbol: new PictureMarkerSymbol({url: result.icon.url,width: 24,height: 24,}),});return graphic;});// Remove existing graphics and add the new ones to the places layerplacesLayer.removeAll();placesLayer.addMany(graphics);}// Function to query places within the current extentasync function queryPlaces() {// Ensure extent and search text are definedif (!state.extent || !state.searchText) {return;}// Perform the places queryPlacesWithinExtent requesttry {const response = await places.queryPlacesWithinExtent(new PlacesQueryParameters({extent: state.extent,icon: "png",searchText: state.searchText,}),);addGraphics(response);} catch (error) {console.error("Error querying places within extent:", error);}}</script></body></html> -
Test your app: Select a category chip and verify that place results appear on the map within the current extent. Pan or zoom the map to confirm that results update automatically for the new visible area.
Display a popup
-
Create a new asynchronous function named
fetchPlaceDetailsto retrieve detailed information for a selected place:- Accept a place ID as an argument.
- Use the places service’s fetchPlace method to request additional details, such as address and phone number, for the specified place.
- Return the response so it can be used to enrich the graphic’s attributes and display in the popup.
147 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});// Initialize the map and add the places layerviewElement.map = new Map({basemap: "arcgis/navigation",layers: [placesLayer],});// Configure the maps's zoom constraintsawait viewElement.viewOnReady();viewElement.constraints.maxZoom = 20;viewElement.constraints.minZoom = 12;// Event listener for chip group selection changes to update search text, close any visible popup and query placeschipGroup.addEventListener("calciteChipGroupSelect", () => {if (chipGroup.selectedItems.length > 0) {state.searchText = chipGroup.selectedItems[0].value;if (popupElement.visible) {popupElement.visible = false;}queryPlaces();}});// Event listener for map view changes to update extent and query placesviewElement.addEventListener("arcgisViewChange", () => {state.extent = viewElement.extent;queryPlaces();});// Function to add graphics to the places layer based on query resultsfunction addGraphics(response) {// Using the JavaScript map method, convert each place result to a graphicconst graphics = response.results.map((result) => {const categories = result.categories.map((category) => `${category.label}`).join(", ");const graphic = new Graphic({attributes: {categories: categories,distance: result.distance,name: result.name,placeId: result.placeId,},geometry: result.location,symbol: new PictureMarkerSymbol({url: result.icon.url,width: 24,height: 24,}),});return graphic;});// Remove existing graphics and add the new ones to the places layerplacesLayer.removeAll();placesLayer.addMany(graphics);}// Function to fetch detailed information about a placeasync function fetchPlaceDetails(placeId) {const response = await places.fetchPlace(new FetchPlaceParameters({placeId: placeId,requestedFields: ["address", "contactInfo:telephone"],}),);return response;}29 collapsed lines// Function to query places within the current extentasync function queryPlaces() {// Ensure extent and search text are definedif (!state.extent || !state.searchText) {return;}// Perform the places queryPlacesWithinExtent requesttry {const response = await places.queryPlacesWithinExtent(new PlacesQueryParameters({extent: state.extent,icon: "png",searchText: state.searchText,}),);addGraphics(response);} catch (error) {console.error("Error querying places within extent:", error);}}</script></body></html> -
Add an
enrichGraphicAttributesfunction to update the graphic with detailed place information:- Accept a Graphic and the details returned from the
fetchPlaceDetailsfunction as arguments. - Update the graphic’s attributes with the address and phone number fields from the details response.
- This ensures the popup displays complete information when a place is selected.
147 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});// Initialize the map and add the places layerviewElement.map = new Map({basemap: "arcgis/navigation",layers: [placesLayer],});// Configure the maps's zoom constraintsawait viewElement.viewOnReady();viewElement.constraints.maxZoom = 20;viewElement.constraints.minZoom = 12;// Event listener for chip group selection changes to update search text, close any visible popup and query placeschipGroup.addEventListener("calciteChipGroupSelect", () => {if (chipGroup.selectedItems.length > 0) {state.searchText = chipGroup.selectedItems[0].value;if (popupElement.visible) {popupElement.visible = false;}queryPlaces();}});// Event listener for map view changes to update extent and query placesviewElement.addEventListener("arcgisViewChange", () => {state.extent = viewElement.extent;queryPlaces();});// Function to add graphics to the places layer based on query resultsfunction addGraphics(response) {// Using the JavaScript map method, convert each place result to a graphicconst graphics = response.results.map((result) => {const categories = result.categories.map((category) => `${category.label}`).join(", ");const graphic = new Graphic({attributes: {categories: categories,distance: result.distance,name: result.name,placeId: result.placeId,},geometry: result.location,symbol: new PictureMarkerSymbol({url: result.icon.url,width: 24,height: 24,}),});return graphic;});// Remove existing graphics and add the new ones to the places layerplacesLayer.removeAll();placesLayer.addMany(graphics);}// Function to enrich graphic attributes with detailed place informationfunction enrichGraphicAttributes(graphic, details) {graphic.attributes.locality = details?.address?.locality;graphic.attributes.postcode = details?.address?.postcode;graphic.attributes.region = details?.address?.region;graphic.attributes.streetAddress = details?.address?.streetAddress;graphic.attributes.telephone = details?.contactInfo?.telephone;}40 collapsed lines// Function to fetch detailed information about a placeasync function fetchPlaceDetails(placeId) {const response = await places.fetchPlace(new FetchPlaceParameters({placeId: placeId,requestedFields: ["address", "contactInfo:telephone"],}),);return response;}// Function to query places within the current extentasync function queryPlaces() {// Ensure extent and search text are definedif (!state.extent || !state.searchText) {return;}// Perform the places queryPlacesWithinExtent requesttry {const response = await places.queryPlacesWithinExtent(new PlacesQueryParameters({extent: state.extent,icon: "png",searchText: state.searchText,}),);addGraphics(response);} catch (error) {console.error("Error querying places within extent:", error);}}</script></body></html> - Accept a Graphic and the details returned from the
-
Add a function named
createPopupTemplateto format the popup content:- Set the popup’s title to the place name.
- Build an content string using the graphic’s attributes (such as name, address, phone number, and categories).
- Return the popup template object to be used when displaying place details.
156 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});// Initialize the map and add the places layerviewElement.map = new Map({basemap: "arcgis/navigation",layers: [placesLayer],});// Configure the maps's zoom constraintsawait viewElement.viewOnReady();viewElement.constraints.maxZoom = 20;viewElement.constraints.minZoom = 12;// Event listener for chip group selection changes to update search text, close any visible popup and query placeschipGroup.addEventListener("calciteChipGroupSelect", () => {if (chipGroup.selectedItems.length > 0) {state.searchText = chipGroup.selectedItems[0].value;if (popupElement.visible) {popupElement.visible = false;}queryPlaces();}});// Event listener for map view changes to update extent and query placesviewElement.addEventListener("arcgisViewChange", () => {state.extent = viewElement.extent;queryPlaces();});// Function to add graphics to the places layer based on query resultsfunction addGraphics(response) {// Using the JavaScript map method, convert each place result to a graphicconst graphics = response.results.map((result) => {const categories = result.categories.map((category) => `${category.label}`).join(", ");const graphic = new Graphic({attributes: {categories: categories,distance: result.distance,name: result.name,placeId: result.placeId,},geometry: result.location,symbol: new PictureMarkerSymbol({url: result.icon.url,width: 24,height: 24,}),});return graphic;});// Remove existing graphics and add the new ones to the places layerplacesLayer.removeAll();placesLayer.addMany(graphics);}// Function to enrich graphic attributes with detailed place informationfunction enrichGraphicAttributes(graphic, details) {graphic.attributes.locality = details?.address?.locality;graphic.attributes.postcode = details?.address?.postcode;graphic.attributes.region = details?.address?.region;graphic.attributes.streetAddress = details?.address?.streetAddress;graphic.attributes.telephone = details?.contactInfo?.telephone;}// Function to create a popup template for a placefunction createPopupTemplate() {return {title: "{name}",content: `<strong>Address:</strong> {streetAddress}, {locality}, {region} {postcode}<br/><strong>Phone:</strong> {telephone}<br/><strong>Categories:</strong> {categories}`,};}40 collapsed lines// Function to fetch detailed information about a placeasync function fetchPlaceDetails(placeId) {const response = await places.fetchPlace(new FetchPlaceParameters({placeId: placeId,requestedFields: ["address", "contactInfo:telephone"],}),);return response;}// Function to query places within the current extentasync function queryPlaces() {// Ensure extent and search text are definedif (!state.extent || !state.searchText) {return;}// Perform the places queryPlacesWithinExtent requesttry {const response = await places.queryPlacesWithinExtent(new PlacesQueryParameters({extent: state.extent,icon: "png",searchText: state.searchText,}),);addGraphics(response);} catch (error) {console.error("Error querying places within extent:", error);}}</script></body></html> -
Add an event listener to the Map’s
arcgisViewClickevent to display place details in a popup:- In the event handler, use a hitTest to check if a places graphic was clicked.
- For each graphic returned by the hit test:
- Use
fetchPlaceDetailsto request additional details for the place. - Use
enrichGraphicAttributesto update the graphic with the new details. - Use
createPopupTemplateto format the popup content for the graphic.
- Use
- Use
Promise.allandmapto process all graphics asynchronously. - If any errors occur, log them to the console and set the graphic to null.
- Use
filterto remove any null graphics from the array. - If there are valid graphics, set the popup’s location to the clicked map point and display the enriched graphic’s attributes in the popup.
119 collapsed lines<html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><title>ArcGIS Maps SDK for JavaScript Tutorials: Display a map</title><script>var esriConfig = {apiKey: "YOUR_ACCESS_TOKEN",};</script><!-- Load the ArcGIS Maps SDK for JavaScript from CDN --><script type="module" src="https://js.arcgis.com/5.0/"></script><style>calcite-chip {--calcite-chip-background-color: var(--calcite-color-foreground-1);}</style></head><body><calcite-shell class="calcite-mode-auto"><arcgis-map center="-122.33, 47.61" popup-disabled zoom="14"><arcgis-zoom slot="top-left"></arcgis-zoom><calcite-chip-group id="search-chip-group" label="Category" selection-mode="single-persist" slot="top-right"><calcite-chip appearance="outline-fill" label="Restaurants" selected value="Restaurant">Restaurants</calcite-chip><calcite-chip appearance="outline-fill" label="Hotels" value="Hotel">Hotels</calcite-chip><calcite-chip appearance="outline-fill" label="Museums" value="Museum">Museums</calcite-chip><calcite-chip appearance="outline-fill" label="ATMs" value="ATM"> ATMs </calcite-chip><calcite-chip appearance="outline-fill" label="Breweries" value="Brewery">Breweries</calcite-chip></calcite-chip-group><arcgis-popup slot="popup"></arcgis-popup></arcgis-map></calcite-shell><script type="module">const [Graphic,GraphicsLayer,Map,places,FetchPlaceParameters,PlacesQueryParameters,PictureMarkerSymbol,] = await $arcgis.import(["@arcgis/core/Graphic.js","@arcgis/core/layers/GraphicsLayer.js","@arcgis/core/Map.js","@arcgis/core/rest/places.js","@arcgis/core/rest/support/FetchPlaceParameters.js","@arcgis/core/rest/support/PlacesQueryParameters.js","@arcgis/core/symbols/PictureMarkerSymbol.js",]);// Application stateconst state = {extent: null,searchText: "Restaurant",};// Query the Calcite Chip Group componentconst chipGroup = document.querySelector("#search-chip-group");// Query the Popup componentconst popupElement = document.querySelector("arcgis-popup");// Query the Map componentconst viewElement = document.querySelector("arcgis-map");// Create a graphics layer to display placesconst placesLayer = new GraphicsLayer({id: "places-layer",title: "Places Layer",});// Initialize the map and add the places layerviewElement.map = new Map({basemap: "arcgis/navigation",layers: [placesLayer],});// Configure the maps's zoom constraintsawait viewElement.viewOnReady();viewElement.constraints.maxZoom = 20;viewElement.constraints.minZoom = 12;// Event listener for chip group selection changes to update search text, close any visible popup and query placeschipGroup.addEventListener("calciteChipGroupSelect", () => {if (chipGroup.selectedItems.length > 0) {state.searchText = chipGroup.selectedItems[0].value;if (popupElement.visible) {popupElement.visible = false;}queryPlaces();}});// Event listener for map view changes to update extent and query placesviewElement.addEventListener("arcgisViewChange", () => {state.extent = viewElement.extent;queryPlaces();});// Event listener for map clicks to show place details in a popupviewElement.addEventListener("arcgisViewClick", async (event) => {// Perform a hit test to identify clicked place graphicsconst hitTestResult = await viewElement.hitTest(event.detail, {include: [placesLayer],});// Process each hit test result to fetch and enrich place detailsconst graphics = (await Promise.all(hitTestResult.results.map(async (result) => {if ("graphic" in result && result.graphic) {try {const details = await fetchPlaceDetails(result.graphic.attributes.placeId);enrichGraphicAttributes(result.graphic, details.placeDetails);result.graphic.popupTemplate = createPopupTemplate();return result.graphic;} catch (error) {console.error("Error fetching place details:", error);return null;}}return null;}),)).filter((graphic) => graphic !== null);// Open the popup if there are any graphics with detailsif (graphics.length > 0) {popupElement.open({location: event.detail.mapPoint,features: graphics,});}});89 collapsed lines// Function to add graphics to the places layer based on query resultsfunction addGraphics(response) {// Using the JavaScript map method, convert each place result to a graphicconst graphics = response.results.map((result) => {const categories = result.categories.map((category) => `${category.label}`).join(", ");const graphic = new Graphic({attributes: {categories: categories,distance: result.distance,name: result.name,placeId: result.placeId,},geometry: result.location,symbol: new PictureMarkerSymbol({url: result.icon.url,width: 24,height: 24,}),});return graphic;});// Remove existing graphics and add the new ones to the places layerplacesLayer.removeAll();placesLayer.addMany(graphics);}// Function to enrich graphic attributes with detailed place informationfunction enrichGraphicAttributes(graphic, details) {graphic.attributes.locality = details?.address?.locality;graphic.attributes.postcode = details?.address?.postcode;graphic.attributes.region = details?.address?.region;graphic.attributes.streetAddress = details?.address?.streetAddress;graphic.attributes.telephone = details?.contactInfo?.telephone;}// Function to create a popup template for a placefunction createPopupTemplate() {return {title: "{name}",content: `<strong>Address:</strong> {streetAddress}, {locality}, {region} {postcode}<br/><strong>Phone:</strong> {telephone}<br/><strong>Categories:</strong> {categories}`,};}// Function to fetch detailed information about a placeasync function fetchPlaceDetails(placeId) {const response = await places.fetchPlace(new FetchPlaceParameters({placeId: placeId,requestedFields: ["address", "contactInfo:telephone"],}),);return response;}// Function to query places within the current extentasync function queryPlaces() {// Ensure extent and search text are definedif (!state.extent || !state.searchText) {return;}// Perform the places queryPlacesWithinExtent requesttry {const response = await places.queryPlacesWithinExtent(new PlacesQueryParameters({extent: state.extent,icon: "png",searchText: state.searchText,}),);addGraphics(response);} catch (error) {console.error("Error querying places within extent:", error);}}</script></body></html>
Run the app
In CodePen, run your code to display the map.
Your app should now be fully functional:
- Select a category chip to search for places in that category within the current map extent.
- Pan or zoom the map to update the search results automatically for the new visible area.
- Click a place graphic to display a popup with detailed information, which is fetched only for the selected place to minimize requests.
What’s next?
Learn how to use additional SDK features and ArcGIS services in these tutorials: