Mastering ArcGIS Server Development with JavaScript

4.8 (5 reviews total)
By Ken Doman
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Your First Mapping Application

About this book

ESRI and its ArcGIS line of software have been an industry leader in digital map production and publication for over 30 years. ArcGIS Server lets you design, configure, and publish maps that can be viewed and edited through the Internet.

After designing basic maps, you may want to find out new and innovative ways to represent information using these maps. In this book, you'll work through practical examples, experiencing the pitfalls and successes of creating desktop and mobile map applications for a web browser using the ArcGIS Server platform.

The book begins by introducing you to ArcGIS Server and ESRI's JavaScript API. You'll work with your first web map and then move on to learn about ESRI's building blocks. A Dojo AMS style widget will help you create your own widgets for a map and then see how to collect geographic data.

Furthermore, you will learn different techniques such as using Dojo Charts to create charts and graphs to represent your data. Then you will see how to use ESRI JavaScript API with other JavaScript libraries and different styling methods to make your map stand out. By the end of the book, you will discover how to make your application compatible with different devices and platforms and test it using testing libraries.

Publication date:
September 2015
Publisher
Packt
Pages
366
ISBN
9781784396459

 

Chapter 1. Your First Mapping Application

Let's say you have a map. You've digitized it using ArcGIS Desktop, a sort of desktop mapping and analysis software provided by ESRI. You've gone through the painstaking process of plotting points, connecting lines, and checking the boundaries of polygons. You've added nice background aerial imagery, and you've applied all the parts that make a map readable.

How are you going to share this map with the general public? You could post it in the public information office, but citizens have complained about that location being too remote, and down too many flights of stairs underground. You could make a thousand printed copies, but that would be terribly expensive.

If you have the ArcGIS Server software running and connected to a web server, you can publish your map online, and serve it through a website running with the ArcGIS JavaScript API (http://developers.arcgis.com/javascript).

The ArcGIS JavaScript API is a JavaScript library that works with ArcGIS Server to connect the map maker with the general public. The map maker can use an ESRI product, such as ArcMap, to generate a map document. That map maker can then publish the map document through ArcGIS Server. From there, a web page that has been loaded with the ArcGIS JavaScript API can draw the map in the browser, and let the general public pan, identify, and interact with the map.

In this chapter, we'll be covering the following topics:

  • The requirements for creating a web mapping application using ArcGIS Server and the ArcGIS API for JavaScript

  • The HTML head and body content necessary to serve maps with the JavaScript API

  • How to create a map with the ArcGIS JavaScript API and add content

  • How to make a map interactive

 

Features of the API


The ArcGIS JavaScript API provides many of the tools necessary to build robust web map applications. It can generate slippy maps, interactive maps that let the user pan and zoom in. The behavior is similar to Google or Bing Maps, but with your data. You're in control of the content, from background imagery to markers and popup content. With ArcGIS Server, you have control over how the maps are laid out, and which colors, styles, and fonts you use. The API also comes with custom elements that let you do everything from drawing on the map, searching for data, measuring things on the map, and printing the map in multiple formats.

The ArcGIS JavaScript API is built on top of the Dojo framework (www.dojotoolkit.org).You also have access to an extensive package of free HTML form elements, controls, and layout elements for your web applications because Dojo comes packaged with the API. These Dojo user controls have been tested in multiple browsers, and include an entire library of items that can be used to make a mobile application. While the ArcGIS JavaScript API is built with Dojo, it also works well with other libraries such as jQuery and AngularJS.

The ArcGIS JavaScript API was designed and built, along with an ArcGIS API, for Flash and Silverlight. Unlike other APIs which require specialized compilers, plugins, and related software, the ArcGIS JavaScript API can be written with a simple text editor and viewed on most common browsers without any special plugins. Since mobile browsers, such as Safari for iPad and Chrome for Android, do not support third party plugins, the ArcGIS JavaScript API is the preferred choice for creating interactive map websites for the mobile platform.

Tip

Now, it is possible to code a website using Windows Notepad, just like it's possible to hike up Mount Everest without a guide. But when things go wrong, you will probably want to use a free text editor with syntax highlighting and other features, such as NotePad++ (http://notepad-plus-plus.org/), Aptana Studio 3 (http://www.aptana.com/products/studio3.html), or Visual Studio Code (http://code.visualstudio.com) for Windows, Brackets (http://brackets.io) or Textmate (http://macromates.com/) for Mac, or Kate (http://kate-editor.org/), Emacs (http://www.gnu.org/software/emacs/), or vim (http://www.vim.org/) for Linux. If you want text editors that aren't free, but offer more features and support, you can check out Sublime Text (http://www.sublimetext.com/) or Webstorm (http://www.jetbrains.com/webstorm/).

 

The ArcGIS JavaScript API community


The ArcGIS JavaScript API has an active community of developers who are willing to help you along the way. ESRI has blogs where they post updates, and host meetups in various cities across the country and around the globe. Many ArcGIS JavaScript developers, both inside and outside of ESRI, are active on Twitter and other social media outlets.

Note

You can find many resources to help you learn about the ArcGIS JavaScript API through books and the web. For books, you can check out Building Web and Mobile ArcGIS Server Applications with JavaScript by Eric Pimpler, Building Web Applications with ArcGIS by Hussein Nasser, and ArcGIS Web Development by Rene Rubalcava. For online resources, you can visit ESRI GeoNet (https://geonet.esri.com/community/developers/content), view the arcgis-javascript-api tag on GIS StackExchange (http://gis.stackexchange.com/questions/tagged/arcgis-javascript-api), or visit the ESRI GitHub page (https://github.com/esri).

 

Our first Web Map


Now that the introductions are out of the way, we should begin working with the API. In this chapter, we're going to look at some code that will make a simple, interactive map. The example will be a single-page application, with all the styling and coding on the same page. In the real world, we would want to separate those into separate files. For this example, this is what the project is comprised of:

  • Setting up an HTML5 web page

  • Adding the necessary styling and the ArcGIS JavaScript library

  • Framing out our HTML content

  • Setting up a script to create a map

  • Loading a layer file

  • Adding a click event that collects data from the map service

  • Displaying that data on the map

Our assignment

We've just been asked by the Y2K historical society to make an interactive map application of the United States around the year 2000. They want the application to show the U.S. demographics including gender, age, and ethnicity, during that year. After reviewing the request from the client, we determined that the 2000 census data would provide all the mapping and demographics data we were looking for.

After a bit of research, we found an ArcGIS Server map service that serves the 2000 census data. We can use the ArcGIS JavaScript API to show that data on an HTML document. The user will be able to click on the map, and the application will display census data by state, census tract, and census block group.

 

Setting up the HTML document


Let's open our favorite text editor and create an HTML document. Since we're working with census data, let's call it census.html. We'll start with an HTML5 template. Browsers will recognize it as HTML5 by the appropriate document type at the top of the page. Our HTML5 page starts out as follows:

<!DOCTYPE html>
<html>
<head></head>
<body></body>
</html>

Starting from the head

The head of an HTML document contains information about the page including the title, metadata about the page content, Cascading Style Sheet (CSS) links to tell the browser how to render the output, and any scripts that the developer needs the browser to run before it reads the rest of the page. Here is an example of a simple webpage:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
  <title>Census Map</title>
  <link rel="stylesheet" href="http://js.arcgis.com/3.13/esri/css/esri.css" />
  <style>
    html, body {
      border: 0;
      margin: 0;
      padding: 0;
      height: 100%;
      width: 100%;
    }
  </style>
  <script type="text/javascript">
    dojoConfig = {parseOnLoad: true, debug: true};
  </script>
  <script type="text/javascript" src="http://js.arcgis.com/3.13/" ></script>
</head>
…

Tip

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Let's take a look at each of the items separately.

Meta tags and title tags

The title and meta tags in the head of the document tell browsers and search engines more about the page. See the following code for an example:

<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
  <title>Census Map</title>

Some tags tell search engines how to read and categorize the content of your website. Others, like the meta tags in this file, tell the browser how to display and manipulate the page. In the preceding code the first meta tag, we're establishing the character set used to render text. The second meta tag tells Internet Explorer browsers to load the page using the latest version available. The third meta tag tells mobile browsers that the content is scaled to the correct size, disabling the ability to pinch or spread your fingers on the screen in order to zoom the text in and out. This is different from zooming the map scale in and out, and this tag is required in most mobile browsers to zoom in and out of the map.

Cascading style sheets

The look of a website is determined by the CSS. These style sheets tell the browser how to lay elements on the page, what colors to make each element, how to space them out, and so on. You can see how they are arranged in the current document:

<link rel="stylesheet" href="http://js.arcgis.com/3.13/esri/css/esri.css" />
  <style>
    html, body {
      border: 0;
      margin: 0;
      padding: 0;
      height: 100%;
      width: 100%;
    }
  </style>

Here are three ways to organize and control the styling of elements by using CSS:

  • First, we can apply styles to individual elements on the page with inline styling, by adding a style attribute to the HTML element (<div style="..."></div> for instance).

  • Second, we can apply styling to the whole page by using an internal style sheet, denoted by the <style></style> tags.

  • Third, we can apply styles to multiple sheets by referring to an external style sheet, denoted by <link rel="stylesheet" … />. For our single page application, we'll use both our own internal style sheet, and an external one provided by ESRI.

The ArcGIS JavaScript library requires its own style sheet to properly position the maps on the screen. Without this file, the map will not render correctly, and thus show the expected results. To load the required style sheet for the library, add a link tag and set the href attribute to point to the esri.css file.

If you're using version 3.2 or later, and your map appears on the page in a checkerboard pattern with the map tiles showing on every other square, the most likely problem is that the esri.css style sheet did not load. Make sure you reference the correct esri.css style sheet for the library version you're using. The following image shows an example of this behavior:

The Dojo configuration script

Our JavaScript code adds a variable that tells Dojo how it should load, in the first script tags. From this script, we can tell the browser how to interpret specially defined HTML elements in our document, whether we want the browser to cache all files, and even how to load packages and other libraries into the Dojo build system:

  <script type="text/javascript">
    dojoConfig = {
      parseOnLoad: true, 
      cacheBust: true
    };
  </script>

In this case, we're telling Dojo to parse any specially decorated HTML elements in the body, and replace them with the appropriate Dojo widgets as the page is loaded. Using the cacheBust parameter, we are also asking the browser to use a timestamp when it loads the files, so that the files aren't cached in the browser. Adding timestamps forces the browser to load a fresh copy of the JavaScript file, rather than relying on a cached copy. Cached scripts under active development may not show the most recent changes you made, slowing development and increasing troubleshooting time.

Tip

The script that loads the dojoConfig object must come before you load the ArcGIS JavaScript API. If the dojoConfig object is created after the API script reference, the dojoConfig contents will be ignored.

The ArcGIS JavaScript API script

The ArcGIS JavaScript library is the main tool you'll use to render, manipulate, and interact with geographic data from the ArcGIS Server:

<script type="text/javascript" src="http://js.arcgis.com/3.13/" ></script>

This application, as well as other applications in the book, use version 3.13 of the ArcGIS JavaScript API. It was the most current version available at the time the book was written. As you maintain these applications, be aware of version number updates. ESRI often releases new versions to add new features, fix bugs in previous versions, and to keep the API compliant with the latest browsers.

Moving from the head to the body

With our HTML head set up, we can focus on the body of the application. We'll add HTML elements to the body where the map and other information should go. We'll style those features from an inline style sheet. Finally, we'll write a script to handle the map creation, census data retrieval, and reacting to map events.

Framing the HTML body

Our client specified that they would like the app to show two panels, a main map panel, and a separate panel that explains what the user is supposed to do. We're going to fulfill the request by blocking off the sections using HTML div elements. The div elements are generic blocks of content in HTML. In the first div, we'll add a styling class of instructions, and fill it with the appropriate instructions. In the second div, we'll apply the specific element id of map, to tell ourselves and the ArcGIS JavaScript API where to put the map:

<body>
  <div class="instructions">
    <h1>U.S. Census for 2000</h1>
    <p>Click on the map to view census data for the state, census tract, and block.</p>
  </div>
  <div id="map"></div>
</body>

Adding a little style

We need to add some style to the new elements we added. To do this, we'll modify the original internal style sheet in the head portion of the application. Our client wants the map to take up the whole screen, with a little space in the upper right-hand corner for the map title and the instructions. The client hasn't decided on colors yet, but they have requested the rounded corners that everybody's putting on their websites today.

So, after reviewing the requirements, and looking up how to style the elements, let's add the following within the <style></style> element. The changes have been highlighted to help you see what has changed in the following code snippet:

<style>
  html, body, #map {
 border: 0;
 margin: 0;
 padding: 0;
 width: 100%;
 height: 100%;
  }
  .instructions {
      position: absolute;
      top: 0;
      right: 0;
      width: 25%;
      height: auto;
      z-index: 100;
      border-radius: 0 0 0 8px;
      background: white;
      padding: 0 5px;
  }
  h1 {
      text-align: center;
      margin: 4px 0;
  }
</style>

Here's an explanation of the style we've added. We want the HTML document, the map <div> to have no margin, border, or padding, and take up the full height of the page. We also want the <div> elements with the instructions class to be precisely positioned in the top right corner, taking up twenty-five percent of the page's width, and then its height will be determined automatically. The instructions block will be floating 100 z-index units towards the user (putting it in front of our map), and its bottom-left corner will have an 8 pixel radius curve in the lower left corner. It will have a white background, and a little padding on the right and left side. Finally, the title <h1> will be centered horizontally, with a little padding above and below it.

Adding a script at the end

If we look at our web page now, we won't see much. Just a title and instructions in the upper right-hand corner. We need to turn this plain page into a fully powered map application. To do that, we'll need to instruct the browser, by using JavaScript, on how to transform our map <div> into a map.

Just before the end of our body tag, we'll add a script tag where we'll write our JavaScript code. We put our script near the end of our HTML document because, unlike images and style sheets, browsers load script tags one at a time. While the browser is loading the script, it doesn't load anything else. If you put it at the beginning of the page, the user might notice a bit of latency, or a delay before the page loads. When we put a script at the end, the user is too distracted by the images and other elements on the page to notice when your script loads. This gives it the appearance of loading faster:

  <div id="map"></div>
  <script type="text/javascript"></script>
</body>

So, why didn't we load the ArcGIS JavaScript library at the end of the page? There will be times, especially if you are using other parts of Dojo, when we'll need the library to manipulate the page as it loads in the browser. In that case, we put the library reference at the head of the HTML document.

Now that we have a script tag to write some JavaScript, we can code up an interactive map. But, before we start writing code, we need to understand how to use the ArcGIS JavaScript API and Dojo. We'll start with a quick history lesson.

Back in the good old days of web development, and even continuing today, JavaScript libraries tried to avoid colliding into one another by creating a single global object (such as JQuery's $ or Yahoo's YUI). All of the library's features would be built into that object. If you've used the Google Maps API, you've probably used google.maps.Map() to create a map, and google.maps.LatLng() to load a point on that map. Each subpart of the library is separated by a dot (.).

Older versions of the ArcGIS JavaScript library were no different. All of ESRI's mapping libraries were loaded into the main "esri" object. You could create a map using esri.map, and load it with an esri.layer.FeatureLayer to show some data, for instance. The Dojo framework was similar, using the dojo, dijit, and dojox global objects.

But this tree-like approach to JavaScript library design has its downsides. As the library expands and matures, it builds up a lot of parts that developers didn't always use. We might use a library for one or two specific features, but we may not use every single tool and function the library offers. The parts of the library we don't use waste client bandwidth, bloat the memory, and make our app appear slower to load.

 

Asynchronous Module Definition


Both the ArcGIS JavaScript API and Dojo decided to handle the bloated library crisis by incorporating the concept of Asynchronous Module Definition (AMD). In AMD, a library is broken down into modular components. The developer can pick and choose which parts of library they want to include in the application. By loading only the parts we need, we reduce download times, free the browser memory of unused functionality, and improve performance.

Another advantage of AMD is name collision avoidance or the names of the variables where the libraries load are controlled by the developer. Also, the scope of the loaded libraries is limited to within the calling function, much like a self-executing statement.

In an AMD based application, we make a list of the library modules we want to use, usually in an array of strings that the library knows how to interpret. We then follow it up with a function that loads most or all of those modules into JavaScript objects. We can then use those modules within the function to get the results we want.

 

Loading required modules


To take advantage of the Dojo's AMD style, we're going to use Dojo's require function. In older examples, we would create multiple dojo.require("") statements that would load the parts of the ArcGIS JavaScript library we needed (and hopefully by the time we wanted to use them). But with AMD style, we use a single require function that requests the list of libraries we ask for, and loads them within a function that runs after all the libraries are loaded in the browser:

  <div id="map"></div>
  <script type="text/javascript">
    require([], function () {});
  </script>
</body>

The require function takes two arguments, an array of strings that corresponds to folder locations in our library, and a function that runs after those libraries load. In the second function, we add arguments (variables within the parentheses after functions) that correspond to the libraries loaded in the list.

So, for this application, we'll need a few modules from the ArcGIS JavaScript API. We'll need to create an empty map and add data to the map in a form we call layers. We'll need to identify things on the map, retrieve the census data we need from the place we clicked on the map, and then display it:

<div id="map"></div>
<script type="text/javascript">
  require([
    "esri/map",
    "esri/layers/ArcGISDynamicMapServiceLayer",
    "esri/tasks/IdentifyParameters",
    "esri/tasks/IdentifyTask",
    "esri/InfoTemplate",
    "dojo/_base/array",
    "dojo/domReady!"
  ], function (
    Map, ArcGISDynamicMapServiceLayer,
    IdentifyParameters, IdentifyTask, InfoTemplate,
    arrayUtils
  ) {
    // code goes here
  });
</script>

Notice the order of the libraries loaded in the require statement, and the arguments in the following function. The first item in the list corresponds to the first argument in the function. It is a common error when creating more complex applications to mix up the order of the elements, especially if it has been revised multiple times. Make sure the items correspond as you go down the list.

You may have noticed that, while there are seven libraries loaded, there are only six arguments. The last library that loaded, the dojo/domReady! library, tells the require statement's second function not to run until all the HTML elements have loaded and are rendered in the browser.

 

The map object


Now that we have the components, we need to create an interactive map; let's put them together. We'll start by constructing a map object, which provides the basis and interaction platform that we'll need to work with. The map constructor takes two arguments. The first parameter, either the HTML node or the id string of a node, indicates where we want to put our map. The second parameter of the map constructor is an options object, where we add the configurable options that make our map work as expected:

function (
    Map, ArcGISDynamicMapServiceLayer,
    IdentifyParameters, IdentifyTask, InfoTemplate,
    arrayUtils
  ) {
    var map = new Map("map", {
      basemap: "national-geographic",
      center: [-95, 45],
      zoom: 3
    });
  });

In the preceding code, we're creating a map in the div element with an id of map. In the map, we're adding a basemap, or a background reference map, in the style of National Geographic. We're centering the map at 45°N and 95°W, at a zoom level of three. We'll go into greater depth concerning these configurations in later chapters. If you're using a desktop browser to view the results, you should see the United States, including Alaska, and Hawaii, as in the following image:

Tip

If you have experience working with Dojo, most element constructors have the options first, followed by the node or the id of the node. This is the reverse of how we construct a map. Remember, order is important.

 

The layers


Years ago, mapping departments drew maps on transparent Mylar sheets. Painstaking work went into drawing those sheets at the same scale. When the Mylar sheets were stacked on top of each other, and corresponding points on each layer were lined up, they would provide a visual mashup of overlapping map layers.

Today, the same effect can be created with a browser-based map application. Instead of clear Mylar sheets, the application takes advantage of layering image files and vector graphics to create the same effect. In the ArcGIS JavaScript API, we refer to these stackable map data sources as layers.

The ArcGIS JavaScript API can accept multiple types of layer files from different sources. Some layers are very flexible, and can be realigned and reprojected to line up with other map sources. These are commonly referred to as dynamic layers. Other layers are made up of images drawn at specific scales, and are not designed to be so flexible. These are commonly referred to as tiled layers.

In our current application, the National Geographic background we added is considered a tiled layer. Its pre-rendered content loads quickly in the browser, which is one of the advantages of using a tiled layer. On the other hand, our data source for the census data is a dynamic layer provided by an ArcGIS Server map service. Its dynamic nature helps it to stretch and line up with our tiled background. Here's the code we'll use to add the layer:

var censusUrl = "http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/";
var map = new Map("map", {
      basemap: "national-geographic",
      center: [-95, 45],
      zoom: 3
    });

var layer = new ArcGISDynamicMapServiceLayer(censusUrl);

map.addLayer(layer);

So, if we take a look at the page at this point, we should see a map of the world with black lines surrounding each state, census tract, and block. We can zoom in to see more detail in the map. Your map should look something like the following image:

 

Adding some action


At this point, we have a map that draws all the state and census block and track data. We need more than that. We need a way for the user to interact with the site. The ArcGIS JavaScript API incorporates many tools provided by the native JavaScript language, plus new tools provided by Dojo.

Events

When many of us learned traditional programming, we learned that programs followed a linear design. They started on the first line and ended on the last line. The computer performed each computation in an ordered fashion, and would not go on to the next line until the current line had finished.

But JavaScript adds something different. JavaScript adds an event loop that monitors for specific website interactions, such as a change in a text blank. We can attach a function, commonly called an event listener, to a known element event. When that event is triggered, the event listener runs.

For example, buttons have a click event, and if we attach an event listener to the button's click event, that function will respond every time that button is clicked. We can also get information about the button through the data passed through the click event.

For our application, we want the map to do something when we click on it. If we look up the list of supported map events, we can attach an event listener using the .on() method. The .on() method takes two parameters, a string description of the event, and the event listener function we want to be called when the event occurs. A list of supported events can be found in the ArcGIS JavaScript API documentation:

map.addLayer(layer);
function onMapClick (event) {
}
map.on("click", onMapClick);

Before we can do anything with the map, we need to know if it has loaded yet. If we're running an updated browser on our fastest computer with a high-speed internet connection, the map may load immediately, but if we're testing from a mobile browser on an older smartphone, with less than stellar internet download speeds, the map may not load very quickly.

Before we assign the click event, we're going to test whether the map has loaded. If so, we'll add the onMapClick function as an event listener to the map's click event. If not, we'll wait until the map fires its load event to set the click event. Just so we don't have to repeat ourselves when adding the map click event, we'll enclose that assignment in another function:

function onMapClick(event) {
}
function onMapLoad() {
  map.on("click", onMapClick);
}
if (map.loaded) {
  onMapLoad();
} else {
  map.on("load", onMapLoad);
}

Tasks

While developers can write code that does a lot with JavaScript, there are some tasks that are best left to a server. The ArcGIS JavaScript API sends requests to the server through task objects. There are tasks for calculating the area and perimeter of complex shapes, tasks for querying and returning spatial and non-spatial data on features in a map, and tasks for creating .pdf documents containing the map we're looking at, among many others. These tasks take the burden off the browser to perform complex calculations that could be very slow on mobile devices. They also allow the library to be lighter in weight, so the library doesn't have to load every single set of conversion factors between one coordinate system and another, for example.

Most tasks have three parts: the task object, the task parameters, and the task results. The task object lets us send requests to the ArcGIS Server for specific tasks, and includes the constants that may need to be referenced for some tasks. The task object typically takes in a URL string parameter that tells the task to which ArcGIS Server service endpoint to send its requests. The task parameters object defines what information we need to get information from the task. Finally, after we execute the task with the task parameters, and we receive the response from the server, we get a structured object, or a list of objects known as the task's result. Formats for the task, task parameters, and task results can be found in the ArcGIS JavaScript API documentation at https://developers.arcgis.com/javascript/jsapi/.

In our code, we're going to use a task called an IdentifyTask method. We'll tell the IdentifyTask method to contact ArcGIS Server through our census URL. Inside the map click event handler, we'll create a task parameter object called an IdentifyParameter object. We'll configure it with the data about the point we clicked, and data on the map. Finally, we'll execute the IdentifyTask method, passing in the IdentifyParameters object, to retrieve census data from the location we clicked:

layer = new ArcGISDynamicMapServiceLayer(censusUrl),
   iTask = new IdentifyTask(censusUrl);

function onMapClick (event) {
  var params = new IdentifyParameters();
  params.geometry = event.mapPoint;
  params.layerOption = IdentifyParameters.LAYER_OPTION_ALL;
  params.mapExtent = map.extent;
  params.returnGeometry = true;
  params.width = map.width;
  params.height= map.height;
  params.spatialReference = map.spatialReference;
  params.tolerance = 3;
  iTask.execute(params);
}

Deferreds and promises

Years ago, when FORTRAN ruled the computer programming world, there was one statement that drove developers crazy when it came time to troubleshoot their code: GOTO. With this line of code, disrupting the flow of the application, the application would jump to another line of code. Jumping from one section of code to another made following the application's logic difficult at best.

With modern JavaScript and asynchronous development, following the logic of some asynchronous applications can be difficult, too. The app fires one event when the user clicks a button, which triggers an AJAX request for data. On the successful return of that data, another event fires, which makes the map do something that takes a little time. After the map finishes, it fires off another event, and so on and so forth.

Dojo responded to this by creating Deferreds objects. Deferred objects return a promise that a result from an asynchronous process will be coming soon. The function waiting for the result will not be called until that promise is fulfilled. With functions returning Deferred results, the developer can chain functions together using a .then() statement. The .then() statement launches the first function in its parameters only after the result is fulfilled. Multiple .then() statements can be chained together with functions that return Deferred objects, leading to an orderly, and more easily readable coding logic.

In our onMapClick function, the IdentifyTask object's execute method returns a Deferred object. We'll store that deferred result in a variable, so that it can be used by another tool later:

function onMapClick (event) {
  var params = new IdentifyParameters(),
      defResults;
  …
  defResults = iTask.execute(params);
}
 

Showing the results


Once we have received our data, we should show the results to the user. If we look at the format of the data passed over the network, we'll see a list of complicated JavaScript Object Notation (JSON) objects. That data, in its raw form, would be useless in the hands of the average user. Thankfully, the ArcGIS JavaScript API provides tools and methods for turning this data into something more user-friendly.

The Map's infoWindow

In the day of modern map applications such as Google Maps, Bing Maps, and OpenStreetmaps, users have been taught that if you click on something important on a map, a little box should pop up and tell you more about that item. The ArcGIS JavaScript API provides a similar popup control for the map called an infoWindow control. The infoWindow highlights feature shapes on the map, and overlays a popup window to show the features-related attributes.

The infoWindow can be accessed as a property of the map (for example, map.infoWindow). From this point, we can hide or show the popup. We can tell the infoWindow which features to highlight. The infoWindow provides a number of configurable and control points to help create a better user experience.

In our application's map click handler, we will need to convert the search results into a form that can be used by the infoWindow. We'll do that by adding an .addCallback() call to the IdentifyTask object's execute function. We'll pull out the features from IdentifyResults, and make a list of them. From that point, we can pass the processed results into the infoWindow object's list of selected features. We'll also prompt the infoWindow to show where the user clicked:

function onIdentifyComplete (results) {
  // takes in a list of results and return the feature parameter 
  // of each result.
  return arrayUtils.map(results, function (result) {
    return result.feature;
  });
}

function onMapClick (event) {
…
  defResults = iTask.execute(params).addCallback(onIdentifyComplete);
  map.infoWindow.setFeatures([defResults]);
  map.infoWindow.show(event.mapPoint);
}

You can run the application now from your browser, and try clicking on one of the black-outlined features on the map. You should see a shape outlined in cyan (light blue), and a popup pointing to where you clicked. The popup will tell you that there is at least one record there (probably more). You can click the small back and forward arrows on the popup to flip through the selected results (if there is more than one).

The InfoTemplate object

As of this point, we can see the shape of our data. The problem is, we can't see what's inside. There is important tabular data associated with the shapes we're seeing in the results, but the popup hasn't been told how to show the information. For that, we can use an InfoTemplate object. An InfoTemplate object tells the popup how to format the data for display, including what title to use, and how we want the search results displayed. An InfoTemplate object is connected to the feature data, along with the feature's geometry and attributes.

An InfoTemplate object can be constructed in different ways, but most commonly with a string to describe the title, and another string to show the content. The content can contain any valid HTML, including tables, links, and images. Since the title and content are templates, you can insert feature attributes within the template string. Surround the field names from your results with ${fieldname}, where "fieldname" is the name of the field you want to use. If you want to show all the field names and values in the content, without any special formatting, set the content value of the InfoTemplate object to ${*}.

For our application, we'll need to add InfoTemplates to the IdentifyTask results. We'll work with the onIdentifyComplete callback and insert them there. We'll start by inserting the following code:

function onIdentifyComplete (results) {
  return arrayUtils.map(results, function (result) {
    var feature = result.feature,
        title = result.layerName;
    feature.infoTemplate = new InfoTemplate(title, "${*}");
    return feature;
  });
}

In this bit of code, we're extracting the layer name of the results, and using that for the title. For the content, we're using the "show everything" template to show all fields and values. If you run the web page in your browser now, and click on a feature, you should see something like the following image:

Now, there are a lot of unreadable field names, and field values we may not be able to understand. We could apply different content formats, based on the layer names of the features. Looking at the preceding example, we're mostly interested in the population, the number of households, and the number of housing units:

function onIdentifyComplete (results) {
  return arrayUtils.map(results, function (result) {
    var feature = result.feature,
        title = result.layerName,
        content;
    
    switch(title) {
      case "Census Block Points":
        content = "Population: ${POP2000}<br />Households: ${HOUSEHOLDS}<br />Housing Units: ${HSE_UNITS}";
        break;
      default:
        content = "${*}";
    }
    feature.infoTemplate = new InfoTemplate(title, content);
    return feature;
  });
}

If you run the page again in your browser, you should see more readable results, at least for the census block points. Other features, such as the states, counties, and block groups, will show a list of field names and their corresponding values, separated by a colon (:). I'll leave the templates for the other fields as a homework exercise for you.

In the end, your code should read as follows:

<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
  <title>Census Map</title>
  <link rel="stylesheet" href="http://js.arcgis.com/3.13/esri/css/esri.css">
  <style>
    html, body, #map {
      border: 0;
      margin: 0;
      padding: 0;
      height: 100%;
    }
    .instructions {
      position: absolute;
      top: 0;
      right: 0;
      width: 25%;
      height: auto;
      z-index: 100;
      border-radius: 0 0 0 8px;
      background: white;
      padding: 0 5px;
    }
    h1 {
      text-align: center;
      margin: 4px 0;
    }
  </style>
  <script type="text/javascript">
    dojoConfig = { parseOnLoad: true, isDebug: true };
  </script>
  <script src="http://js.arcgis.com/3.13/"></script>
</head>
<body>
  <div class="instructions">
    <h1>U.S. Census for 2000</h1>
    <p>Click on the map to view census data for the state, census tract, and block.</p>
  </div>
  <div id="map"></div>
  <script type="text/javascript">
    require([
      "esri/map",
      "esri/layers/ArcGISDynamicMapServiceLayer",
      "esri/tasks/IdentifyParameters",
      "esri/tasks/IdentifyTask",
      "esri/InfoTemplate",
      "dojo/_base/array",
      "dojo/domReady!"
    ], function (
      Map, ArcGISDynamicMapServiceLayer,
      IdentifyParameters, IdentifyTask, InfoTemplate,
      arrayUtils
    ) {
      var censusUrl = "http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/",
          map = new Map("map", { 
            basemap: "national-geographic",
            center: [-95, 45],
            zoom: 3
          }),
          layer = new ArcGISDynamicMapServiceLayer(censusUrl),
          iTask = new IdentifyTask(censusUrl);

      function onIdentifyComplete (results) {
        return arrayUtils.map(results, function (result) {
          var feature = result.feature,
              title = result.layerName,
              content;
          switch(title) {
            case "Census Block Points":
              content = "Population: ${POP2000}<br />Households: ${HOUSEHOLDS}<br />Housing Units: ${HSE_UNITS}";
              break;
            default:
              content = "${*}";
          }
          feature.infoTemplate = new InfoTemplate(title, content);
          return feature;
        });
      }

      function onMapClick (event) {
        var params = new IdentifyParameters(),
            defResults;
        params.geometry = event.mapPoint;
        params.layerOption = IdentifyParameters.LAYER_OPTION_ALL;
        params.mapExtent = map.extent;
        params.returnGeometry = true;
        params.width = map.width;
        params.height= map.height;
        params.spatialReference = map.spatialReference;
        params.tolerance = 3;

        defResults = iTask.execute(params).addCallback(onIdentifyComplete);
        map.infoWindow.setFeatures([defResults]);
        map.infoWindow.show(event.mapPoint);
      }

      function onMapLoad() {
        map.on("click", onMapClick);
      }

     map.addLayer(layer);

      if (map.loaded) {
        onMapLoad();
      } else {
        map.on("load", onMapLoad);
      }

    });
    </script>
  </body>
</html>

Congratulations, if this is your first web map application using ArcGIS Server and JavaScript. You've worked through multiple steps to produce a working, interactive map application. This application should look great on all the latest browsers. However, older browsers may not connect with the server properly, and may require a proxy.

 

A note on proxies


If you've worked with the ArcGIS JavaScript API for any length of time, or had to support older browsers like Internet Explorer 9 or less, then you've probably come across proxies. Proxies are server-side applications that make web requests on behalf of the web browser, often transmitting and receiving data the browser could not collect on its own. There are three primary reasons why a browser would require a proxy to communicate with a server. They are as follows:

  1. The browser is an older browser that does not support Cross Origin Resource Sharing (CORS), and the server requests will be made to a different server from the one the application sits on.

  2. The proxy provides additional security keys to access specific data, which might include secured tokens the developer doesn't want to release to the public.

  3. The web request is much longer than the 2048+ character maximum for GET requests.

The first example is common with older browsers, including Internet Explorer 9 or lower. They can't grab data from third party servers that are separate from the web server, because of a security restraint. With the HTML5 specification for CORS, newer browsers can check to see if an application is allowed to be requested from a script not on the server.

The second example is common in large secure environments, with many security hoops to jump through. Department portal websites could access proxies with tokens unique to the department, providing an extra layer of security.

The third example is common when the user is passing large geometries with many irregular vertices. For instance, if you were to use a drawing tool to draw an irregular shape with the free-handed drawer, its vertices are added as you move around on the map. With so many points, and those points requiring a lot of characters to show their latitude and longitude, it's no wonder a shape request might exceed the browser's maximum character length.

The ArcGIS Server proxy is free to download from GitHub (https://github.com/Esri/resource-proxy). They have versions that work with Java, .NET, and PHP. Please use the most recent version, and make sure it's properly configured for all the services you'll be using.

Tip

If both your computer and your ArcGIS Server are behind a network firewall, and the ArcGIS Server has unique public and private IP addresses, your network firewall may block proxy connections to your ArcGIS Server. If you see network traffic failing only on older browsers, such as Internet Explorer 8, and only for internal requests, the firewall might be the issue. Contact your network administrator to work out the issue.

 

Summary


In this chapter, we have learnt the basics of writing a simple web application using the ArcGIS JavaScript API, and put together a map-based application. We learned how to set up the application in the head of the HTML document. We learned how to create a map on a page and how to add layers so that we can view the data. We learned how to retrieve feature shapes and attributes through a task, and how to show that data to the user.

In the next chapter, we'll look more in depth at the tools available in the ArcGIS JavaScript API.

About the Author

  • Ken Doman

    Ken Doman has worked in the geographic information system field since 2007. In his first GIS job, he was given a stack of maps, some AutoCAD data, and a shoe box full of address notes and was told to make a web-accessible GIS application out of them. Since then, he has strived to make geographic data available and easily accessible over the Internet. He is an ESRI certified web application developer and has created many public web applications using ArcGIS software.

    Currently, Ken works for Bruce Harris & Associates, Inc., a mapping and custom development company that specializes in land records and ESRI software solutions. There, he creates custom web, desktop, and mobile applications using JavaScript, .NET, and ArcGIS software.

    He has also been a book reviewer for Building Web and Mobile ArcGIS Server Applications with JavaScript, Packt Publishing by Eric Pimpler and ArcGIS for Desktop Cookbook, Packt Publishing by Daniela Cristiana Docan. You can learn more about Ken at http://raykendo.com.

    Browse publications by this author

Latest Reviews

(5 reviews total)
All you need to know to get up and running with the ArcGIS API. Very well written and well organized.
Excellent
Good
Book Title
Access this book, plus 7,500 other titles for FREE
Access now