Book Image

Mastering ArcGIS Server Development with JavaScript

By : Raymond Kenneth Doman
Book Image

Mastering ArcGIS Server Development with JavaScript

By: Raymond Kenneth Doman

Overview of this book

Table of Contents (18 chapters)
Mastering ArcGIS Server Development with JavaScript
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

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.