GEOG 863:
Web Application Development for Geospatial Professionals

8.2 Executing a synchronous task


Have a look at this example app built around the drive-time service we’ve been discussing.  The app will display 5-, 10-, and 15-minute drive times emanating from a point clicked by the user.  While the map is zoomed in to State College, PA, it should work anywhere in the U.S.

Preparing the input parameters

Lines 29-32 define a couple of variables to hold information critical to consuming the service: the REST endpoint of the CreateDriveTimePolygons task and the drive time of interest.

Line 55 creates a new empty GraphicsLayer that will hold the drive-time polygons returned by the service.

A SimpleMarkerSymbol (line 58) is created to show the location of the user’s click.

An array of SimpleFillSymbols is created to depict the drive-time polygons.  Note that each symbol is added to the array with an index that corresponds to the drive time the symbols is intended to depict (i.e., one symbol is given an index of 5, one an index of 10, and the last an index of 15).

The first step in executing a geoprocessing service task itself is to create a new Geoprocessor object.  Here a Geoprocessor object is instantiated through setting just its url property (lines 87-92). 

Line 93 sets up a handler for clicks on the view, causing execution of the findDriveTimePolys() function that begins on line 95. 

Earlier in the course we saw that a promise passes along an object or value (its "payload") to the callback function that the developer attaches to it.  Similarly, when you create a handler for the MapView’s click event, as seen here, its callback function has access to an event object.  In this case, the object is stored in a variable called evt.  The Event Details section of the MapView class page in the SDK documents the properties of this event object.  The one used most typically is mapPoint, which returns a Point object representing the location of the user’s click.  The latitude and longitude properties of this object are used to create a new Point object.  That object is in turn used to set the geometry property of a new Graphic.  The Graphic is then added to the GraphicsLayer to remind the user where he/she clicked on the map.  

Recall that when looking at the documentation of the CreateDriveTimePolygons task in the REST Service Directory, we saw that its Input_Location parameter should be set using a FeatureSet object.  Thus, line 114 creates a new empty FeatureSet; line 115 sets its features property.  That property must be set using an array of Graphic objects.  Here the Graphic that was created from the user’s click on the map is used to set the property by putting it in brackets (making it a one-item array). 

With the FeatureSet created and the driveTimes variable defined earlier, we have what we need to execute the task.  The two parameters are stored in an object variable called params.  Note that the keys of this object match the names of the input parameters we saw in the task's documentation: Input_Location and Drive_Times.

Running the task

At this point, we’re ready to run the task, passing along the two parameters.  We saw in the task's documentation that its execution type was synchronous, which leads us to call on the Geoprocessor object’s execute() method.  (If the task was asynchronous, we’d use submitJob instead.)

The execute() method returns a promise, so we can use the then() method to specify a function to execute when the promise has resolved.  Here that function is called drawPolys().

As shown in the execute() method’s documentation, when its promise has been resolved, it returns an object (stored here in a variable called gpResponse).  This object has two properties: messages and results.  The messages can be useful in trying to debug a task that isn't producing the expected outcome.  But if you’ve supplied the right inputs, you’ll want to read the results property to get at the task's outputs.  This property returns an array of ParameterValue objects.   

Processing the task’s response

Because the CreateDriveTimePolys task has just one output parameter, we can retrieve that parameter using the expression results[0].  We then need to read the parameter’s value property to get at the actual output data.  What we get back could be a string, double, or many other things, depending on the parameter's data type.  As was discussed earlier, this task’s Output_Drive_Time_Polygons parameter returns a GPFeatureRecordSetLayer, which translates to a FeatureSet in the JS API.  As we saw when working with query results, we read the features property to get at the array of Graphics in a FeatureSet.  Given that 3 drive times were supplied to the task, we can expect the drivePolys variable on line 125 to store an array of 3 polygon Graphics.

Lines 127-131 use the now familiar map() method to create a new array of polygon Graphics, in which each one has a symbol assigned to it.  Let’s talk about how that symbol is assigned.

Another thing we learned from the Services Directory was that the polygons returned had fields FromBreak and ToBreak.  Using console.log(), I determined that the 5-minute polygon had a FromBreak value of 1 and a ToBreak value of 5.  Likewise, the other polygons had values of 6 and 10, and 11 and 15, respectively.  Thus, the expression poly.attributes.ToBreak will return a value of 5, 10 or 15. That value is plugged inside the brackets to specify which SimpleFillSymbol in the fillSymbols array to use for the Graphic being processed by the map() method. 

With the new symbolized Graphics array created, it is added to the GraphicsLayer (line 133).  It’s then also used as the target of the MapView’s goTo() method to zoom to the extent of the polygons.

Implementing the Spinner widget

While synchronous tasks don’t take too long, they can still cause enough of a delay that the user might wonder if there’s a problem, click again on the map, etc.  A good practice to follow in such cases is to provide some sort of indicator that the app is "thinking."  The Spinner widget was added to Esri's API, though not yet documented, at version 4.4 to provide this sort of indicator.  

In this example, a Spinner is created on lines 46-49, with its container set to a new empty div and its view set to the MapView that was just created.

The Spinner's parent div element is then placed within the MapView's root element, but importantly, only after the MapView has finished loading.  

The Spinner is then made visible at the very beginning of the findDriveTimePolys() function.  Using its show() method, it is displayed at the view's center. 

Recall that the findDriveTimePolys() function finishes with a call to the Geoprocessor's execute() method.  Chained to the call to execute() is a call to the then() method, which specifies that the drawPolys() function should be run after the geoprocessing task has completed.  It is at the end of drawPolys(), after all the polygons have been added, that the Spinner's hide() method is used to make it invisible.

Now let's have a look at how an asyncronous task is run.