Learn how to perform a hot spot feature analysis with the spatial analysis service.
A feature analysis is the process of using the spatial analysis service to perform server-side geometric and analytic operations on feature data. All feature analysis requests are job requests. The easiest way to programmatically run an analysis request to the spatial analysis service is to use ArcGIS REST JS, which provides a Job
class that handles long- running operations.
In this tutorial, you use ArcGIS REST JS to perform a hot spot analysis to find statistically significant clusters of parking violations.
Prerequisites
You need the following to access the spatial analysis service and perform feature analysis operations:
- An ArcGIS Online account.
- An OAuth2.0 credential to obtain its Client ID.
- The redirect URL of the registered application to use for authentication in the format
"https
as in::// <server >[ :port]" https
.://localhost :8080
Steps
Get the starter code
- Download the tutorial starter code zip file from the portal.
- Unzip the folder and open it in a text editor of your choice, such as Visual Studio Code. The starter app includes the following:
- callback.html: This contains callback as part of the authentication process.
- index.html: This contains app logic and the OAuth 2.0 code necessary to perform the authentication.
Set up authentication
Create a new OAuth credential to register the application.
- Go to the Create OAuth credentials for user authentication tutorial to get an OAuth credential.
- Copy the Client ID and Redirect URL from your OAuth credentials item and paste them to a safe location. They will be used in a later step.
All users that access this application need an ArcGIS account with privileges to access the basemap styles service and spatial analysis service.
Set developer credentials
-
In both the
index.html
andcallback.html
files, replaceYOUR
and_CLIENT _ID YOUR
with the client ID and redirect URL of your OAuth credentials._REDIRECT _URL index.htmlUse dark colors for code blocks // Your client ID from OAuth credentials const clientId = "YOUR_CLIENT_ID"; // The redirect URL registered in your OAuth credentials const redirectUri = "YOUR_REDIRECT_URL";
callback.htmlUse dark colors for code blocks // Your client ID from OAuth credentials const clientId = "YOUR_CLIENT_ID"; // The redirect URL registered in your OAuth credentials const redirectUri = "YOUR_REDIRECT_URL";
-
Run the app and ensure you can sign in successfully.
If you are unable to sign in, make sure you have the correct redirect URL and port. This URL varies based on your application and typically takes the format of
https
or:// <server >[ :port]/callback.html http
. For example, if you are running an application on://my-arcgis-app :/auth http
, add://127.0.0.1 :5500/ http
as your redirect URL.://127.0.0.1 :5500/callback.html
Add script references
In addition to ArcGIS REST JS Request
, you also reference the Portal
helper class from ArcGIS REST JS to obtain the spatial analysis service URL.
-
Add references to the ArcGIS REST JS
Portal
helper class.Use dark colors for code blocks <!-- ArcGIS REST JS used for user authentication. --> <script src="https://unpkg.com/@esri/arcgis-rest-request@4/dist/bundled/request.umd.js"></script> <!-- MapLibre GL JS--> <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 used for spatial analysis URL --> <script src="https://unpkg.com/@esri/arcgis-rest-portal@4/dist/bundled/portal.umd.js"></script>
Update the map
-
Update the map's viewpoint to
[-122.445, 37.760]
and a zoom level of11
to focus in San Fransisco, CA. Then, update the basemap toarcgis/human-geography
.Use dark colors for code blocks const basemapEnum = "arcgis/human-geography"; 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: 11, // starting zoom center: [-122.445, 37.760] });
Display parking violations
To perform feature analysis, you need to provide feature data as input. In this tutorial, you use the SF parking violations hosted feature layer as input data for the hot spot analysis.
-
Add the parking violation feature layer and the corresponding vector tile layer to the
points
andpoints
variables.VTL Use dark colors for code blocks const points = 'https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/sf_traffic_parking_violations_sa_osapi/FeatureServer/0'; const pointsVTL = 'https://vectortileservices3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/sf_traffic_parking_violations_sa_osapi/VectorTileServer';
-
Add the SF parking violations vector tile layer and its style definition to the map.
Use dark colors for code blocks map.once('load', () => { map.addSource('violations', { type: 'vector', tiles: [`${pointsVTL}/tile/{z}/{y}/{x}.pbf`], }); map.addLayer({ id: 'vtlLayer', type: 'circle', source: 'violations', 'source-layer': 'DeriveNewLocationsOutput', paint: { 'circle-color': 'rgb(0,0,0)', 'circle-radius': 1.5, }, }); });
-
Add the data attribution for the vector tile layer source.
- Go to the sf_traffic_parking_violations_sa_osapi_vtl item.
- Scroll down to the Credits (Attribution) section and copy its value.
- Create an
attribution
property and paste the attribution value from the item.Use dark colors for code blocks map.once('load', () => { map.addSource('violations', { type: 'vector', tiles: [`${pointsVTL}/tile/{z}/{y}/{x}.pbf`], // Attribution text retrieved from https://arcgis.com/home/item.html?id=a9b4d69b02ee4365a0fcb9610c69db9b attribution: "San Francisco 311, City and County of San Francisco" }); map.addLayer({ id: 'vtlLayer', type: 'circle', source: 'violations', 'source-layer': 'DeriveNewLocationsOutput', paint: { 'circle-color': 'rgb(0,0,0)', 'circle-radius': 1.5, }, }); });
-
Run the application and navigate to your localhost, for example
https
. After you sign in, you will see the vector tile layer rendered on the map.://localhost :8080
Get the analysis URL
To make a request to the spatial analysis service, you need to get the URL first. The analysis service URL is unique to your organization.
-
Call the
get
operation from ArcGIS REST JS to obtain the analysis URL.Self Use dark colors for code blocks const getAnalysisUrl = async (session) => { const portalSelf = await arcgisRest.getSelf({ authentication: session, }); return portalSelf.helperServices.analysis.url; };
Make the request
Use the Job
class and set the operation
and params
required for the selected analysis.
-
Create a function that submits a
Job
request to the spatial analysis service using your organization's analysis URL. Set theparams
required for a hot spot analysis and authenticate usingsession
.Use dark colors for code blocks const runAnalysis = async () => { const analysisUrl = await getAnalysisUrl(session); const operationUrl = `${analysisUrl}/FindHotSpots/submitJob`; const params = { analysisLayer: { url: points }, shapeType: 'Hexagon', outputName: { serviceProperties: { name: `MapLibreGLJS_find_hot_spots_${new Date().getTime()}`, }, }, //Outputs results as a hosted feature serivce. }; const jobReq = await arcgisRest.Job.submitJob({ url: operationUrl, params: params, authentication: session, }); // get all the results, this will start monitoring and trigger events const jobResp = await jobReq.getAllResults(); // jobResp.aggregatedLayer.value.url return jobResp; };
-
Create an HTML
div
element calledstatus
and a loader to display the job status.Use dark colors for code blocks <body> <div id="status"> <span class="loader"></span> <div id="info"></div> </div> <div id="map"></div>
-
Apply CSS styling to the
status
and the loader.Use dark colors for code blocks #status { width: 400px; background-color: #303336; color: #edffff; z-index: 0; position: absolute; font-family: Arial, Helvetica, sans-serif; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; flex-direction: row; justify-content: center; padding: 5px; border-radius: 5px; gap: 10px; align-content: center; align-items: center; margin: auto; } .loader { width: 24px; height: 24px; border: 5px solid #edffff; border-bottom-color: #303336; border-radius: 50%; display: inline-block; box-sizing: border-box; animation: rotation 2s linear infinite; } @keyframes rotation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
-
Create a function called
show
to control the visibility and content of the status element.Info Use dark colors for code blocks const showInfo = (visible, msg) => { const statusUi = document.getElementById("status"); !visible ? (statusUi.style.zIndex = 0) : (statusUi.style.zIndex = 1001); const infoUi = document.getElementById("info"); infoUi.innerText = msg; }; const runAnalysis = async () => { const analysisUrl = await getAnalysisUrl(session); const operationUrl = `${analysisUrl}/FindHotSpots/submitJob`; const params = { analysisLayer: { url: points }, shapeType: 'Hexagon', outputName: { serviceProperties: { name: `MapLibreGLJS_find_hot_spots_${new Date().getTime()}`, }, }, //Outputs results as a hosted feature serivce. }; const jobReq = await arcgisRest.Job.submitJob({ url: operationUrl, params: params, authentication: session, }); // get all the results, this will start monitoring and trigger events const jobResp = await jobReq.getAllResults(); // jobResp.aggregatedLayer.value.url return jobResp; };
-
Use the function to dynamically update the status element with the current status of the analysis.
Use dark colors for code blocks const runAnalysis = async () => { const analysisUrl = await getAnalysisUrl(session); const operationUrl = `${analysisUrl}/FindHotSpots/submitJob`; const params = { analysisLayer: { url: points }, shapeType: 'Hexagon', outputName: { serviceProperties: { name: `MapLibreGLJS_find_hot_spots_${new Date().getTime()}`, }, }, //Outputs results as a hosted feature serivce. }; const jobReq = await arcgisRest.Job.submitJob({ url: operationUrl, params: params, authentication: session, }); showInfo(true, "Initializing analysis..."); // listen to the status event to get updates every time the job status is checked. jobReq.on(arcgisRest.JOB_STATUSES.Status, (jobInfo) => { showInfo(true, "Hot spot analysis running..."); console.log(jobInfo.status); }); // get all the results, this will start monitoring and trigger events const jobResp = await jobReq.getAllResults(); // jobResp.aggregatedLayer.value.url return jobResp; };
-
Call the
run
function and store its response inAnalysis job
.Response Use dark colors for code blocks const showInfo = (visible, msg) => { const statusUi = document.getElementById("status"); !visible ? (statusUi.style.zIndex = 0) : (statusUi.style.zIndex = 1001); const infoUi = document.getElementById("info"); infoUi.innerText = msg; }; const runAnalysis = async () => { const analysisUrl = await getAnalysisUrl(session); const operationUrl = `${analysisUrl}/FindHotSpots/submitJob`; const params = { analysisLayer: { url: points }, shapeType: 'Hexagon', outputName: { serviceProperties: { name: `MapLibreGLJS_find_hot_spots_${new Date().getTime()}`, }, }, //Outputs results as a hosted feature serivce. }; const jobReq = await arcgisRest.Job.submitJob({ url: operationUrl, params: params, authentication: session, }); showInfo(true, "Initializing analysis..."); // listen to the status event to get updates every time the job status is checked. jobReq.on(arcgisRest.JOB_STATUSES.Status, (jobInfo) => { showInfo(true, "Hot spot analysis running..."); console.log(jobInfo.status); }); // get all the results, this will start monitoring and trigger events const jobResp = await jobReq.getAllResults(); // jobResp.aggregatedLayer.value.url return jobResp; }; const jobResponse = await runAnalysis();
Display results
The results of a feature analysis are returned as feature data.
-
Add a new layer to contain analysis results with its style definition to your map.
Use dark colors for code blocks map.once('load', () => { map.addSource('violations', { type: 'vector', tiles: [`${pointsVTL}/tile/{z}/{y}/{x}.pbf`], // Attribution text retrieved from https://arcgis.com/home/item.html?id=a9b4d69b02ee4365a0fcb9610c69db9b attribution: "San Francisco 311, City and County of San Francisco" }); map.addLayer({ id: 'vtlLayer', type: 'circle', source: 'violations', 'source-layer': 'DeriveNewLocationsOutput', paint: { 'circle-color': 'rgb(0,0,0)', 'circle-radius': 1.5, }, }); map.addSource('analysisResults', { type: 'geojson', data: { type: "FeatureCollection", features: [] }, }); map.addLayer({ id: 'results', type: 'fill', source: 'analysisResults', paint: { 'fill-color': [ 'match', ['get', 'Gi_Text'], 'Hot Spot with 99% Confidence', '#d62f27', 'Hot Spot with 95% Confidence', '#ed7551', 'Hot Spot with 90% Confidence', '#fab984', 'Not Significant', '#f7f7f2', 'Cold Spot with 90% Confidence', '#c0ccbe', 'Cold Spot with 95% Confidence', '#849eba', 'Cold Spot with 99% Confidence', '#4575b5', 'blue', ], }, }); });
-
Access the URL of the resulting hosted feature layer from the job's response and pass it to the analysis result layer. Then, hide the status element.
Use dark colors for code blocks map.getSource("analysisResults").setData(`${jobResponse.hotSpotsResultLayer.value.url}/query?f=geojson&where=Gi_Bin<>0&outFields=Gi_Text&token=${accessToken}`); showInfo(false, "");
Run the app
Run the application and navigate to your localhost, for example https
.
After signing in, a request is sent to the spatial analysis service to perform a hot spot analysis. When the job is complete, the results of the analysis will display on the map. The analysis can take up to a minute to complete.
What's next?
To learn how to perform other types of feature analysis, go to the related tutorials in the Spatial analysis guide:
Find and extract data
Find data with attribute and spatial queries using find analysis operations.
Combine data
Overlay, join, and dissolve features using combine analysis operations.
Summarize data
Aggregate and summarize features using summarize analysis operations.
Discover patterns in data
Find patterns and trends in data using spatial analysis operations.