Learn how to perform a text-based search to find places within a bounding box.
A bounding box search finds places within an extent using the places service. An extent typically represents the visible area of a map. To perform a bounding box search, you use the places package from ArcGIS REST JS. With the results of the search, you can make another request to the service and return place attributes such as street address and telephone number.
In this tutorial, you use ArcGIS REST JS to perform a bounding box search based on the visible extent on the map and return details about each place. You also use Calcite components to create a basic search interface.
Prerequisites
An ArcGIS Location Platform account.
Steps
Get the starter app
Select a type of authentication below and follow the steps to create a new application.
Set up authentication
Create developer credentials in your portal for the type of authentication you selected.
Set developer credentials
Use the API key or OAuth developer credentials so your application can access location services.
Add script references
Reference ArcGIS REST JS request
and places
packages to perform a bounding box search operation. You also reference the Calcite libraries to create the user interface.
-
Reference the
routing
andrequest
packages from ArcGIS REST JS.Use dark colors for code blocks <!-- Load MapLibre from CDN --> <script src=https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.js></script> <link href=https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.css rel="stylesheet" /> <!-- ArcGIS REST JS: request and places --> <script src="https://unpkg.com/@esri/arcgis-rest-request@4/dist/bundled/request.umd.js"></script> <script src="https://unpkg.com/@esri/arcgis-rest-places@1/dist/bundled/places.umd.js"></script>
-
Reference the Calcite libraries.
Use dark colors for code blocks <!-- Load MapLibre from CDN --> <script src=https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.js></script> <link href=https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.css rel="stylesheet" /> <!-- Calcite components --> <script type="module" src=https://js.arcgis.com/calcite-components/2.12.1/calcite.esm.js></script> <link rel="stylesheet" type="text/css" href=https://js.arcgis.com/calcite-components/2.12.1/calcite.css /> <!-- ArcGIS REST JS: request and places --> <script src="https://unpkg.com/@esri/arcgis-rest-request@4/dist/bundled/request.umd.js"></script> <script src="https://unpkg.com/@esri/arcgis-rest-places@1/dist/bundled/places.umd.js"></script>
-
Create a REST JS
Api
using your access token.Key Manager Use dark colors for code blocks const authentication = arcgisRest.ApiKeyManager.fromKey(accessToken);
Update interface
-
Change the map's viewpoint to
[-122.32116, 47.62737]
with a zoom of13
to focus on Seattle, Washington.Use dark colors for code blocks const basemapEnum = "arcgis/streets"; const map = new maplibregl.Map({ container: "map", // the id of the div element style: `https://basemapstyles-api.arcgis.com/arcgis/rest/services/styles/v2/styles/${basemapEnum}?token=${accessToken}`, zoom: 13, // starting zoom center: [-122.32116, 47.62737] // starting location [longitude, latitude] });
-
Inside
<body
, copy and paste the following HTML to create a basic search interface.> Use dark colors for code blocks <div id="place-control"> <div class="search"> <calcite-input type="text" id="search-input" placeholder="Type in a place name or category"> <calcite-button kind="inverse" icon-start="search" id="search-button" type="submit" slot="action"></calcite-button> </calcite-input> </div> </div> <div id="map"></div>
-
Update styling to the map
div
element andbody
. Add styling for the new elements.Use dark colors for code blocks body { margin: 0; padding: 0; } #map { position: absolute; top: 0; bottom: 0; right: 0; left: 0; font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #323232; z-index: 1; } #place-control { position: absolute; top: 15px; left: 15px; display: flex; flex-direction: row; z-index: 2; } .search { margin-right: 15px; } #search-input { width: 270px; } .category-button { margin: auto 5px; }
-
Create a set of dynamic buttons for place categories (e.g.,
Restaurants
,Hotels
,Museums
) usingcalcite-button
elements.Use dark colors for code blocks const control = document.getElementById("place-control"); const input = document.getElementById("search-input") const placeKeywords = ["Restaurants", "Hotels", "Museums", "ATMs", "Breweries"]; placeKeywords.forEach(category => { const categoryButton = document.createElement("calcite-button"); categoryButton.setAttribute("class", "category-button"); categoryButton.setAttribute("round", true); categoryButton.setAttribute("scale", "s"); categoryButton.setAttribute("kind", "inverse") categoryButton.innerHTML = category; categoryButton.id = category; control.appendChild(categoryButton); }) document.getElementById(placeKeywords[0]).dispatchEvent(new Event('click'));
Add event listeners
Add event listeners to this interface to make requests to the places service on when they are clicked.
-
Create a
show
function to make requests to the places service.Places Use dark colors for code blocks function showPlaces(text) { };
-
Add an event listener to the search button that calls
show
on click.Places Use dark colors for code blocks const control = document.getElementById("place-control"); const input = document.getElementById("search-input") const placeKeywords = ["Restaurants", "Hotels", "Museums", "ATMs", "Breweries"]; document.getElementById("search-button").addEventListener("click", e => { showPlaces(input.value) }) placeKeywords.forEach(category => { const categoryButton = document.createElement("calcite-button"); categoryButton.setAttribute("class", "category-button"); categoryButton.setAttribute("round", true); categoryButton.setAttribute("scale", "s"); categoryButton.setAttribute("kind", "inverse") categoryButton.innerHTML = category; categoryButton.id = category; control.appendChild(categoryButton); }) document.getElementById(placeKeywords[0]).dispatchEvent(new Event('click'));
-
Add an event listener to each category button that calls
show
on click.Places Use dark colors for code blocks placeKeywords.forEach(category => { const categoryButton = document.createElement("calcite-button"); categoryButton.setAttribute("class", "category-button"); categoryButton.setAttribute("round", true); categoryButton.setAttribute("scale", "s"); categoryButton.setAttribute("kind", "inverse") categoryButton.innerHTML = category; categoryButton.id = category; control.appendChild(categoryButton); categoryButton.addEventListener("click", e => { input.value = category; showPlaces(category) }) }) document.getElementById(placeKeywords[0]).dispatchEvent(new Event('click'));
Find places in the map bounds
-
Calculate the current visible extent of the MapLibre map with
map.get
. Access the top right and bottom left corners.Bounds() Use dark colors for code blocks function showPlaces(text) { const bounds = map.getBounds() const topRight = bounds.getNorthEast(); const bottomLeft = bounds.getSouthWest(); };
-
Use the ArcGIS REST JS
find
function to make a request to the places service. Set thePlaces Within Extent search
parameter to your query and pass the current map bounding box to theText xmin
,xmax
,ymin
, andymax
parameters.Use dark colors for code blocks function showPlaces(text) { const bounds = map.getBounds() const topRight = bounds.getNorthEast(); const bottomLeft = bounds.getSouthWest(); arcgisRest.findPlacesWithinExtent({ xmin: bottomLeft.lng, ymin: bottomLeft.lat, xmax: topRight.lng, ymax: topRight.lat, searchText: text, authentication }) };
Display results
The response from the places service will contain a list of place results. Each result will include a place's x/y coordinates, name, category, and unique ID.
-
Create a new list to store service results. When a new places request is made, clear the list.
Use dark colors for code blocks const currentPlaces = []; function showPlaces(text) { for (let place of currentPlaces) { place.remove(); } const bounds = map.getBounds() const topRight = bounds.getNorthEast(); const bottomLeft = bounds.getSouthWest(); arcgisRest.findPlacesWithinExtent({ xmin: bottomLeft.lng, ymin: bottomLeft.lat, xmax: topRight.lng, ymax: topRight.lat, searchText: text, authentication }) };
-
Access the service results. For each result, add a MapLibre
Marker
to your map.Use dark colors for code blocks function showPlaces(text) { for (let place of currentPlaces) { place.remove(); } const bounds = map.getBounds() const topRight = bounds.getNorthEast(); const bottomLeft = bounds.getSouthWest(); arcgisRest.findPlacesWithinExtent({ xmin: bottomLeft.lng, ymin: bottomLeft.lat, xmax: topRight.lng, ymax: topRight.lat, searchText: text, authentication }) .then((response) => { response.results.forEach((result) => { const marker = new maplibregl.Marker() .setLngLat([result.location.x, result.location.y]) .addTo(map); currentPlaces.push(marker); }); }); };
-
Run the app. When you click a category button or search for a phrase, the map should display a set of points representing place results.
Display a popup
To view more information about each place result, bind a popup to each marker that displays more information about the place.
-
Use
set
to bind a popup to each result marker.Popup Use dark colors for code blocks .then((response) => { response.results.forEach((result) => { const marker = new maplibregl.Marker() .setLngLat([result.location.x, result.location.y]) .addTo(map); currentPlaces.push(marker); const popup = new maplibregl.Popup(); marker.setPopup(popup) }); }); }; function getDetails(popup, id) { };
-
Add an event listener to each marker that calls a new function
get
. Pass the current popup as well as the unique place ID from the service response.Details Use dark colors for code blocks .then((response) => { response.results.forEach((result) => { const marker = new maplibregl.Marker() .setLngLat([result.location.x, result.location.y]) .addTo(map); currentPlaces.push(marker); const popup = new maplibregl.Popup(); popup.on('open', e => { getDetails(e.target, result.placeId) }); marker.setPopup(popup) }); }); }; function getDetails(popup, id) { };
Get place address and phone number
You can access more information about a place using the unique place
associated with it. Perform a subsequent request to the places service to get the street address and phone number of a clicked POI.
-
Use the ArcGIS REST JS
get
function to get detailed information about a specific place. Pass thePlace Details place
associated with the current marker, and set theId requested
parameter to return theFields street
andAddress telephone
properties.Use dark colors for code blocks function getDetails(popup, id) { arcgisRest.getPlaceDetails(({ placeId: id, authentication, requestedFields: ["name", "address:streetAddress", "contactInfo:telephone"] })) };
-
Access the service response. Display the results in your popup if they are available.
Use dark colors for code blocks function getDetails(popup, id) { arcgisRest.getPlaceDetails(({ placeId: id, authentication, requestedFields: ["name", "address:streetAddress", "contactInfo:telephone"] })) .then((result) => { console.log(result) let popupContents = `<b>${result.placeDetails.name}</b><br>`; if (result.placeDetails.address.streetAddress) popupContents += `${result.placeDetails.address.streetAddress}<br>`; if (result.placeDetails.contactInfo.telephone) popupContents += `${result.placeDetails.contactInfo.telephone}`; popup.setHTML(popupContents); }); };
Run the app
Run the app.
The app should display a map with a search control. Upon clicking a button or entering a phrase, place results should appear on the map. Clicking a result will submit another service request to get the place address and phone number.What's next?
Learn how to use additional ArcGIS location services in these tutorials:
Find nearby places and details
Find points of interest near a location and get detailed information about them
Find place addresses
Search for coffee shops, gas stations, restaurants and other nearby places with the Geocoding service.
Find a route and directions
Find a route and directions with the route service.