Monday, March 26, 2012

ARCGIS JavaScript API – ADDING A LAYER SELECTION – PART III


1.     Introduction


Before going to implementing different tools and commands from the toolbars for doing different GIS functions I add a list that contains all the layers that  can be used for doing spatial and attribute queries.  This list is similar to what can be found in the ArcGis desktop application AcMap on the second tab.  As is the case of the legend module I used a jQuery extension in combination with a template to create a layer selection list. The actions needed for creation the layer selection list consists of the following steps

·         Create a jQuery HTML template that is hosted on the web server and will serve as the blueprint for a list item. The template is retrieved through an AJAX request.

·         Create a simple jQuery extension that will call the jQuery template plug-in.

·         Build an array of layers with for each layer a number of useful attribute information. The creation of the array will consists of a call of a rest service for each layer. In the example outlined here, I will limit the array of layers to feature layers. This could easy be extended to all layers having a query capability and not necessary having attributes exposed.

To make the layer selection list available to all components, the list will be maintained in the gisOperation class.

2.     Layer selection template


The template I use is much simpler than the one used in the legend. It can easy be customized by CSS to enhance the presentation of the list. In my example, the template looks like this:

<li>

  <input type="checkbox" style="float:left" id="${$item.getCheckboxID()}" checked="${$item.getChecked()}" />

  <h3 class="agsSelect" style="margin:0 0 0 25px;padding:0;" >${title}</h3>

</li>

<div style="clear:both"/>

In this template I use two functions  to calculate an unique ID and event handling of the checkbox checked.

3.     Layer selection jQuery extension


The most important is the update of the array list with the selection set or reset. This is done by the code below that is linked to the click event of the checkbox.

// Handle the select toggle click event
function checkSelectLayer(ev) {
// We'll find the layer based on the select id, so capture it
  var selectId = this.id,select =this.checked;
  $.each(iGisOperation.getLayersSelectionInfos(), function (index, selectLayer) {
     if (selectLayer.selectId === selectId){
        iGisOperation.getLayersSelectionInfos()[index].selected = select; 
     }
     return;
  });                                       
}

4.     Layer selection list creation


The creation of the layer selection list is largely done in a the layer selection module. In this module I put some initialization into it that will be responsible for the maintenance of the list. For its creation the layer selection list depends on the output of the legend module. To synchronize the maintenance of the layer selection list with the legend module, I used the event aggregator  class to subscribe to the end of the legend build. After the legend is build, all extra information needed will be available.
The init of the layer selection module is responsible for the subscription process.

var layerSelectionModule = (function () {
    var i, j, k, l, index;
    var totalLayers2Check = 0;
    var totalLayersChecked = 0;
    var urlGet;
    var divLegend;
    var selectionInfo;

….

    return {
        init: function (divElement) {
            this.divLegend = divElement;
            iEventAggregator.subscribe("legendBuild", selectBuild, divElement);
        }
    }
});

The selectBuild function will actual create the layer selection list upon the end of the build of the legend.

function selectBuild() {
// Calculate number or layers
  for (i = 0; i < iGisOperation.getLayersLegendInfos().length; i++) {
    if (iGisOperation.getLayersLegendInfos()[i].layers != null)
      totalLayers2Check += iGisOperation.getLayersLegendInfos()[i].layers.length;
  }
  // Start building the selection layer list
  for (i = 0; i < iGisOperation.getLayersLegendInfos().length; i++) {
    if (iGisOperation.getLayersLegendInfos()[i].layers != null) {
     for (j = 0; j < iGisOperation.getLayersLegendInfos()[i].layers.length; j++) {
     // Skip group layer
       if (iGisOperation.getLayersLegendInfos()[i].layers[j].parentLayerId > -1) {
         urlGet = iGisOperation.getLayersLegendInfos()[i].url + "/" + iGisOperation.getLayersLegendInfos()[i].layers[j].id + "?f=json&callback=?";
         $.getJSON(urlGet, function (data) {
            if (data.type == 'Feature Layer') {
              index = -1;
              for (k = 0; k < iGisOperation.getLayersLegendInfos().length; k++) {
                if (iGisOperation.getLayersLegendInfos()[k].layers != null) {
                  for (l = 0; l < iGisOperation.getLayersLegendInfos()[k].layers.length; l++) {
                    if (iGisOperation.getLayersLegendInfos()[k].layers[l].name == data.name) {
                       index = k;
                       break;
                     }
                   }
                   if (index > -1)
                     break;
                 }
               }
               if (index > -1) {
                 selectionInfo = 
                   new layerSelectionInfo(iGisOperation.getLayersLegendInfos()[index].url, data.id, data.name, data.fields, data.displayField, iGisOperation.getLayersSelectionInfos().length);
                   iGisOperation.getLayersSelectionInfos().push(selectionInfo);
               }
             }
             terminateLayerInitialize();
           });
         }
         else
           terminateLayerInitialize();
       }
     }
   }
 }
 function terminateLayerInitialize() {
   totalLayersChecked += 1;
   if (totalLayersChecked == totalLayers2Check) {
   // All layers has been retrieved, ready to move the contents to the list
     var selectLayers = $("#selectRegion").agsSelect({ autoLoadTemplates: true });
   }
 }

I use a simple counter system to know when all rest services has been processed. When this is done, the jQuery extension is called with the template plugin. During the processing of the rest services important information as fields are included into the layer selection list. This will be useful during the query command and tools. I also maintain the layer selection array into the gisOperation class for common use.

The final result looks like this:

Tuesday, March 13, 2012

ARCGIS JavaScript API – BUILDING A LEGEND COMPONENT – PART II


1.     Introduction


As we see in the ArcGis Desktop application (ArcMap), the use of the legend consists of more than just displaying the rendering information. Behind the legend extra functionality is exposed through context menus. In the Silverlight desktop application I wrote some of this functionality was added to the Silverlight API Legend. This extra functionality was done through extending the Silverlight API legend control with extra methods and attached in the XAML through commands. The Microsoft Silverlight framework and MVVM makes this possible.

In the JavaScript API however, the legend control has only limited functionality making it very difficult to create extended functionality. To solve this issue, a custom legend control must be created. A solution could be creating the legend with the help of the Dojo library, but for a generic solution this is not a simple job. Another solution is using jQuery templates. The jQuery templates is an extension created by Microsoft  and added to the community. The interesting aspect is the use of html templates to define how a control is shown using all the power of CSS and a build-in processing syntax. There was an interesting session  at ESRI in 2011 explaining this technique of building a custom legend control.  To have a flexible solution, the template is saved on the server in a file that is downloaded and added to the legend region. The initial template I used has the following contents.

<li>
  <input type="checkbox" style="float:left" id="${$item.getCheckboxID()}" checked="${$item.getChecked()}" />
      <h3 class="agsLegendOpen" style="margin:0 0 0 25px;padding:0;" >${title}</h3>
     {{if serviceType == 4}}
       {{if renderer.type=="simple"}}
          {{if renderer.symbol.type=='esriPMS'}}
            <div style="display:table; margin:10px 5px;" >
              <img  style="padding-right:10px;
              float:left;
              display:inline-block;
              vertical-align:middle;"
              src="data:${renderer.symbol.contentType};base64,${renderer.symbol.imageData}"/>
            </div>
          {{else renderer.symbol.type=='esriSFS'}} 
             {{if renderer.symbol.color == null}}
                  <div style="display:table-cell;
                    vertical-align:middle;
                    width:20px;
                    height:20px;
                    background-color:rgba(151,219,242,255);
                    border:${$item.getBorder(renderer.symbol.outline)};">
                  </div>
                {{else}}
                  <div style="display:table-cell;
                    vertical-align:middle;
                    width:20px;
                    height:20px;
                    background-color:${$item.getColor(renderer.symbol.color)};
                    border:${$item.getBorder(renderer.symbol.outline)};">
                  </div>
             {{/if}}
          {{else renderer.symbol.type=='esriSMS'}}
            {{if renderer.symbol.style=="esriSMSCircle"}}
            {{/if}}
          {{/if}}
        {{else renderer.type=='uniqueValue'}}
          <ul>
          {{each renderer.uniqueValueInfos}}                
                  {{if $value.symbol.type=='esriPMS'}}
                          <div style="display:table;margin:5px 5px;">                        
                          <img src="data:${$value.symbol.contentType};base64,${$value.symbol.imageData}" style="vertical-align:middle;"/>
                          <span style="padding:0px 5px 0px 0px;display:table-cell;vertical-align:middle;">${$value.label}</span>
                          </div>
                  {{else $value.symbol.type=='esriSFS'}}
                          <li style="display:table; margin:5px 5px;" >                               
                                  <div style="display:table-cell;vertical-align:middle;width:20px;height:20px;background:${$item.getColor($value.symbol.color)};border:${$item.getBorder($value.symbol.outline)};" ></div>
                                  <span style="padding:0px 5px 0px 5px;display:table-cell;vertical-align:middle;">${$value.label}</span>
                          </li>
                  {{else $value.symbol.type=='esriSLS'}}
                    <li style="display:table;" >
                      <div style="margin:8px 5px -15px 5px;
                        width:20px;
                        height:${$value.symbol.width}px;
                        background:${$item.getColor($value.symbol.color)};">
                      </div>
                      <span style="padding:0px 5px 0px 5px;
                        display:table-cell;
                        vertical-align:middle;">
                        ${$value.label}
                      </span>
                    </li>
                  {{/if}}
          {{/each}}
        </ul>
       {{/if}}
     {{/if}}
</li>
<div style="clear:both"/>

As you can see, there are conditional statements and loop statements. To finalize this approach I encapsulate the template class into a custom jQuery plugin. All these classes makes part of the legend module.

2.     Legend in action

To handle this approach of program logic, I have to retrieve extra information of the ArcGis server using the REST API. This extra information is retrieved through the legend module and not during the map initialization process. Doing so we can loosely couple the map module from the legend module,  allowing this module to handle all data needed for the legend. The communication between the map module and the legend module is done through our class eventAggregator that will inform the legend module when the map is initialized (loaded). During the initialization method of the legend module a callback functions is hooked to the map loaded event  as you can see in the code below.
var legendModule = (function () {
    var divLegend;
    var layersCount;
    var layersTotal
    function legendBuild(eventName, eventData, tagData) {

       }
    return {
        initLegend: function (divElement) {
            this.divLegend = divElement;
            iEventAggregator.subscribe("mapLoaded", legendBuild, divElement);
        }
    }

Because the retrieving of all the legend information coming from the REST services is asynchronous, I use a simple counter to know when the last information is retrieved and all the legend information is available to be applied in the template. The code below shows how the build of the legend is started after the last legend has been processed. We simply called the custom legend  jQuery plug-in
    function buildLegend() {
        if (layersCount == layersTotal) {
            var legend = $("#legendRegion").agsLegend({ });
        }

To activate the templates, this can be done through this method in the jQuery plug-in.
    $.fn.agsLegend = function (options) {
        opts = $.extend({}, $.fn.agsLegend.defaults, options);     
        if (opts.autoLoadTemplates) {
            getTemplate();
        }
        return this.each(function () {
                $this = $(this);
                $this.delegate("input[type='checkbox']""click", checkLayerVisibility);  
                $this.delegate("h3","mouseup",rightClick);
                if (opts.isCollapsible) { makeCollapsible(this); }   
        }

Upload of the template from the server is done by this code through a AJAX call.
    function getTemplate() {
        $.ajax({
            url: opts.templateFileURL,
            success: opts.onTemplateLoaded || handleTemplateResponse
        });
    }

To apply the template, you can do this by this function below. You simply insert the template into a script section of the head section. Using the tmpl method of the jquery template plug-in the data is inserted into the template.

    function handleTemplateResponse(data) {
        script = document.createElement("script");
        script.style.display = "none";
        script.type = "text/x-jquery-tmpl";
        script.id = opts.templateDOMId;
        script.text = data;
        $('head').append(script);                  
        $("#"+opts.templateDOMId).tmpl(iGisOperation.GetLayersLegendInfos(),
                {
                    getColor: getColor,
                    getBorder: getBorder,
                    getChecked: getChecked,
                    getCheckboxID: getCheckboxID
                }).appendTo($this);
    }

Some methods are needed to format the values in the template.The final result of all this, is a legend that looks like this.

The legend build here is a first simple implementation. I will show later how we can enhance the look and feel of the legend by adding concept as grouping using more advanced rendering support. All legend information will be maintained in the gisOperation class so that other modules can benefit from this data.

In the coming weeks I will add a toolbar to the application to allow different GIS operations on the map as zooming and selecting.

Wednesday, March 7, 2012

ARCGIS JavaScript API – CREATION OF AN APPLICATION FRAMEWORK – PART I.


1.     Architecture


In previous documents I explained how you can encapsulate the ArcGis Silverlight API’s into a component library to simplify the creation of application based on some high level functionality. At the same time there was also a strict separation between GUI and the functionality of tools and commands. In JavaScript I will outline how we can use the same patterns to create a more high level JavaScript component library and also make code free HTML pages.

The  exercise I will do is the same as with the Silverlight application, namely I will produce a small general ArcGis Desktop Editor based on some of the functionality of ArcGis Desktop 10. To make the application ready for the future, a HTML5 environment is used. The configuration file is the same XML file as used in the Silverlight application.

The migration of the Silverlight components and classed consist of the following jobs to be done:

·         Migrate the GIS services into a JavaScript class (function) implementing all methods that are defined in the interfaces. Because there is almost one to one relation between the ArcGis Silverlight API functionality and the ArcGis JavaScript API functionality, the migration path is relatively trivial,  the latter is mainly because the two API’s are based on the REST services of ArcGis Server.

·         Configuration of the application is based on the same XML configuration file of the Silverlight Application. The only difference is that I use a WCF service to convert the XML file into a JSON object. This is a very simple WCF service and can also be easy written into other languages as JAVA. You could also use a JSON file that is transferred to the client.

·         In Silverlight I use a framework PRISM and a pattern MVVM that separates GUI and business logic and also implements IoC (inversion of control) and DI (dependency injection). MVVM is built into Silverlight, PRISM is an additional library. In JavaScript we can use MVVM through a library KNOCKOUTJS. The library will mainly be used to handle dependency properties (text and events) on the HTML pages. By stripping of the code from HTML pages, it will be a lot easier to use the same pages in other devices like mobiles. I hope later to illustrate this using some specific JavaScript libraries that will help us to achieve this. I will not use IoC and DI because we do not have the concept of modules as in Silverlight, and that it is also  in JavaScript to make the GIS libraries global over the different modules.  However some patterns found in PRISM will be implemented in JavaScript.

·         In tise exercise,  the different parts that makes up a solution will be implemented as module libraries. Examples are the map module, legend module and different toolbar modules. As in the Silverlight solution, the modules must be independent from each other, no reference is made to each other. A PRISM pattern will be used to communicate between the different modules.

·         Mandatory JavaScript libraries used are jquery, DOJO and KNOCKOUTJS

·         Use of CCS3 can be used to format the application. Because web design is not the focus of these series of documents, little attention will be done to the CSS formatting.

2.     Application development structure


The application architecture consists of different JavaScript library organized in different folders as outlined below :

Gis                           gisCommonTasks.js
                                       gisEditing.js
                                       gisGeoProcessing.js
                                       gisOperation.js

Helper                    dataMapping.js
                                       eventAggregator.js

Modules                 layerSelectionModule.js
                                       legendModule.js
                                       mapModule.js
                                       toolbarEditModule.js
                                       toolbarSimpleModule.js

ViewModels                mainViewModel.js

 The GIS libraries corresponds to what I did in Silverlight. The main function is to encapsulate the ArcGis JavaScript API into more usable high level methods. Doing so, less experienced ArcGis developers can step into the GIS development team.  The GIS libraries use the JavaScript pattern ‘enclosure’ to expose only the methods, similar to using interfaces in the Silverlight C# implementation of the GIS classes.  By using this pattern it becomes more simple to migrate the Silverlight libraries I developed .
The Helper libraries are support libraries for GIS and modules. ‘dataMapping’ contains classes we need  in the application.

Module libraries consists of an important part of the application. Because de ArcGis components must be created in JavaScript through DOJO, the main job will be the building of these visual components. Some hooks on events will be needed as we cannot use the view model to bind to these components. The only module that is needed, is the map module, all other modules are optional.
ViewModels consist of the view model needed for the HTML page. A big part of the view model will be interfacing with the different GIS services. Because there is no specific data handling, no models are defined. All interactions with the REST services happens through the GIS services as its nature is generic.

3.     The application template


As previous explained, a lot of JavaScript libraries are needed at the startup of the page. Below you see an overview of the current JavaScript libraries used.

<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.2.min.js"></script>

<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.14/jquery-ui.min.js"></script>
<script type="text/javascript" src="scripts/knockout-2.0.0.js"></script>
<script type="text/javascript" src="scripts/Helper/dataMapping.js"></script>
<script type="text/javascript" src="scripts/Helper/eventAggregator.js"></script>
<script type="text/javascript" src="scripts/Gis/gisOperation.js"></script>
<script type="text/javascript" src="scripts/Modules/legendModule.js"></script>
<script type="text/javascript" src="scripts/Modules/mapModule.js"></script>
<script type="text/javascript" src="scripts/globalVars.js"></script>
<script type="text/javascript" src="scripts/ViewModels/mainViewModel.js"></script>
<script type="text/javascript" src="scripts/startup.js"></script>

This comes after we loaded the Dojo and ArcGis API libraries.
The body has a very simple layout. The layout consist of Dojo containers organized in a classical way with a header, two columns consisting of a tab container and a simple container, followed by a footer.

The header will host different toolbars as it was the case in the Silverlight application. The tab container will host our legend, search layers and a query information. The right column will host the map. The footer will display different informations.
<body class="claro">
         <div id="appLayout" class="arcgisLayout" 
                          data-dojo-type="dijit.layout.BorderContainer" 
                          data-dojo-props="design: 'headline'">
                 <div class="edgePanel" id="header"
                          data-dojo-type="dijit.layout.ContentPane"                
                          data-dojo-props="region: 'top'">
                          <div data-bind="html: applicationIcon"></div>
                          <label data-bind="text: applicationName"></label>
                 </div>
                 <div id="tocPanel" class="leftPane" 
                          data-dojo-type="dijit.layout.TabContainer" 
                          data-dojo-props="region: 'left', tabPosition: 'bottom'">
                          <div data-dojo-type="dijit.layout.ContentPane" title="Legend">
                                   <div id="legendRegion">
                                   </div>
                          </div>
                          <div data-dojo-type="dijit.layout.ContentPane" title="Select">
                                   <div id="selectRegion">
                                   </div>
                          </div>
                          <div data-dojo-type="dijit.layout.ContentPane" title="Query">
                                   <div id="queryRegion">
                                   </div>
                          </div>
                 </div>
                 <div id="mapPanel" class="edgePanel"
                          data-dojo-type="dijit.layout.ContentPane"            
                          data-dojo-props="region: 'center'">
                          <div id="mapRegion">
                          </div>
                 </div>
                 <div class="edgePanel" id="footer"
                          data-dojo-type="dijit.layout.ContentPane" 
                          data-dojo-props="region: 'bottom'">bottom
                 </div>
         </div>
</body>

The result will look something  like this:

4.     Startup


In Silverlight we use a bootstrapper class to start the application. The bootstrapper is part of PRISM an is responsible for the setup of the MEF container that will be used for IoC and DI. In JavaScript I will use a startup JavaScript that will kick off the application.
The first step in the startup is getting the configuration from the server. This will be done through  an AJAX call to the server that hosts a WCF service. At return we will have the full configuration into a JSON object ‘configuration’. This is a global variable, making it easy to access configuration data through the whole project.

Next we need to create the GIS objects. Because the GIS objects are global, we can use them within modules and view models. Only the methods are exposed in the GIS objects.
Finally, the module objects are created and initialized. The map module must be initialized as last, this is because of the publish / subscribe pattern of the eventAggregator class used to communicate when the map has been loaded and the other modules.

5.     Configuration


Through the ready function of jquery the configuration is retrieved doing an Ajax call. In the success method we get the configuration as a JSON object.  Once this is done, we can start building the map dynamically from the layers defined in the configuration XML file. If modules have no dependency of the map, you can place them everywhere after the configuration assignment otherwise you must initialize and subscribe to the map loaded event before the map initialization.

$(document).ready(
         function () {
             try {
                 GetConfiguration();
             } catch (exception) { }
         }
);
 
function GetConfiguration() {
    Type = "GET";
    Url = GetConfigURl;
    DataType = "jsonp";
    Data = 'application=' + applic;
    ProcessData = false;
    method = "GetAConfig";
    CallService();
}
 
function ServiceSucceeded(result) {
    if (DataType == "jsonp") {
        if (method == "GetAConfig") {
            configuration = result;
            // Create the Gis classes, making its interface available           
            iGisOperation = new gisOperation();
            // Initialise the modules, start the map module as last
            legendModule = new legendModule();
            legendModule.initLegend("legendRegion");
            mapModule = new mapModule();            
            // Configuration done, start creation of the map
            mapModule.initMap("mapRegion", mapLayersInitialized);
        }
        else {
            resultObject = result.GetEmployeeResult;
            var string = result.Name + " \n " + result.Address;
            alert(string);
        }
    }
}

6.     EventAggregator


To communicate between modules, without having a tight connection between the modules, I implemented some functionality in JavaScript similar to the EventAggregator class of PRISM in Silverlight.
This class uses two methods to realize the communication:

·         Subscribe – This method will add to a list of event handlers a callback function that will be called when an event is triggered.  Optional a tag can be saved at the same moment for use in the callback function. The event that is triggered, is defined by a name and can return data to the callback function.

·         Publish - When this method executes, all callback functions related to the event are fired and optional data is passed.

This simple implementation makes it easier to communicate between modules, without  the publisher having knowledge of who needed attention when the event is fired . A good example is the map module, which publish the map loaded event. Modules as the legend will only start populating the labels after the map has been loaded and all information about the layers is available. The same is true for the view model which can only update information related to the configuration or GIS information.

De eventAggregator class implementation :

var eventAggregator = (function () {
    var events = new Array();
    return {
        subscribe: function (eventName, eventAction, tagData) {
            events.push(new eventElement(eventName, eventAction, tagData));
        },
        publish: function (eventName, eventData) {
            for (var i = 0; i < events.length; i++) {
                if (eventName == events[i].eventName) {
                    if (events[i].eventAction != null)
                        events[i].eventAction(eventName, eventData, events[i].tagData);
                }
            }
        }
    }
});

7.     GIS Services


Migrating the Silverlight C# application towards a JavaScript application consist mostly of migrating the C# code of the services into JavaScript code. Lucky,  because the Silverlight API and JavaScript API are an encapsulation of the REST services provided by the ArcGis server, much of the code is similar. In some cases data has to be retrieved in a different way with JavaScript API than in the case of the Silverlight  API.  Sometimes LINQ is used  to replace loops in C#, resulting in more code to be written in JavaScript.
Let’s see how I convert the C# GisOperations.cs class in to a JavaScript Class.

First create a JavaScript class based on the ‘enclosure’ pattern like this :
var gisOperation = (function () {
         // Private variables
         var map;
         var ArcGISServiceType = { Unknown: 0, Cached: 1, Dynamic: 2, Image: 3, Feature: 4 };
         var geometryService = null;
         var layerCount = 0;
         var layerInitialised = 0;
         var mapInitialiseComplete = null;
         var baseMapLayerInfos;
         var featureLayerInfos;
         var layersData;
         var graphicsLayerSelect = null;
         var isFeatureLayersLoaded = false;
         var selectSymbol;
         var flInfo;
         return {
                 setMap: function (mapControl) {
                 
                 },
                 getMap: function () {
                                           },
                 initialize: function (geometryServiceUrl) {
                          
                 },
                 getSelectLayer: function () {
                          
                 },
                 getFeatureLayer: function (layerName) {
                 },

               

         }
});

 As you can see, it is very simple to use the interfaces I use in Silverlight C#, and put them into a JavaScript class. Only the functions in the return are exposed in the application, similar as interfaces are used in the Silverlight Application.
In Silverlight we had an interface IGisOperations that’s looks like

       public interface IGisOperations
       {
             Map GetMap();
             void Initialize(string geometryServiceUrl);
             GraphicsLayer GetRoutingLayer();
             GraphicsLayer GetSelectLayer();
             FeatureLayer GetFeatureLayer(string layerName);
             void SetLayers(Map mapControl);
             void AddNewDynamicLayer(string layerUrl, bool visible, string layerName, ArcGISServiceType serviceType);
             void AddNewDynamicLayer(string layerUrl, bool visible, string layerName, int index, ArcGISServiceType serviceType);
             void AddNewFeatureLayer(string layerUrl, bool visible, string layerName);
             void RemoveDynamicLayer(string layerName);
             void AttributeQueryTask_Async(string whereValue, string whereField, string layerName, string fieldType, ESRI.ArcGIS.Client.Geometry.Geometry geometry);
             void AttributeQueryTask_ExecuteCompleted(object sender, QueryEventArgs args);
             void AttributeQueryTask_Failed(object sender, TaskFailedEventArgs args);
             void SetFinishedEvent(ResultsHandler finishedOperation);
             void SetCompleteDrawEvent(DrawCompleteHandeler drawComplete);
             void ResetCompleteDrawEvent(DrawCompleteHandeler drawComplete);
             void SetMapInitialiseCompleteEvent(InitialiseCompleteHandler mapInitialiseComplete);
             MarkerSymbol GetSelectionMarkerSymbol();
             LineSymbol GetSelectionLineSymbol();
             FillSymbol GetSelectionFillSymbol();
             void SetSelectionMarkerSymbol(MarkerSymbol symbol);
             void SetSelectionLineSymbol(LineSymbol symbol);
             void SetSelectionFillSymbol(FillSymbol symbol);
             void SetDrawMode(DrawMode drawMode);
             void SetDrawModeContinuous(DrawMode drawMode);
             void DisableDrawMode();
             void EnableDrawMode();
             void MapZoom(string action, Geometry geometry);
             FeatureLayerInfo GetFeatureLayerInfo(int id);
             FeatureLayerInfo GetFeatureLayerInfo(string layerName);
             GeometryService GetGeometryService();
             void CenterAndZoom(MapPoint point, double resolution);
             IList<LayerData> GetLayersData();
             double Resolution2Scale(double resolution);
             double Scale2Resolution(double scale);
             IList<FeatureLayerInfo> GetFeatureLayerInfos();
             IList<BaseMapLayerInfo> GetBaseMapLayerInfos();
             void CreateGeometry(DrawMode drawMode, DrawCompleteHandeler drawComplete);
             void SetMapSize(int offset);
             void SetTabControlVisible(bool tabControlVisible);
             int GetLayerTocSelected();
             void SetLayerTocSelected(int layerIndex);
             void ZoomTo(Geometry geometry);
             Envelope GetNormalizedExtent(Envelope extent);
       }

All this methods with the corresponding parameters must be added in the return of the gisOperation function. Functions used before the return statement are private functions in the class like the variables.

8.     Migration of adding a dynamic layer


In this section you can see how the functionality for adding a dynamic layer in Silverlight / C# is transformed into JavaScript. The method that we will implement is AddNewDynamicLayer.

In Silverlight/C# we have :

public void AddNewDynamicLayer(string layerUrl, bool visible, string layerName, ArcGISServiceType serviceType)
{
      if (serviceType == ArcGISServiceType.Dynamic)
      {
            ArcGISDynamicMapServiceLayer mapLayer = new ArcGISDynamicMapServiceLayer()
            {
                  Url = layerUrl,
                  DisableClientCaching = true,
                  ID = layerName,
                  Visible = visible
            };
            mapLayer.Initialized += InitializedDynamicLayer;
            mapLayer.InitializationFailed += layer_InitializationFailed;
            mapControl.Layers.Add(mapLayer);
      }
      else if (serviceType == ArcGISServiceType.Image)
      {
            ArcGISTiledMapServiceLayer mapLayer = new ArcGISTiledMapServiceLayer()
            {
                  Url = layerUrl,
                  ID = layerName,
                  Visible = visible
            };
            mapLayer.Initialized += InitializedDynamicLayer;
            mapLayer.InitializationFailed += layer_InitializationFailed;
            mapControl.Layers.Add(mapLayer);
      }  
}

In JavaScript we the code looks like that:

function _addNewDynamicLayer(layerUrl, visible, layerName, serviceType) {
         var mapLayer;
         if (serviceType == ArcGISServiceType.Dynamic) {
                  mapLayer = new esri.layers.ArcGISDynamicMapServiceLayer(layerUrl,
                         id: layerName,
                          visible: visible
                  });
                 dojo.connect(mapLayer, "onLoad"function (layer) {
                          layer_Initialized(layer);
                 });
                 dojo.connect(mapLayer, "onError"function (err) {
                          layer_InitializationFailed(err);
                 });
         }
         else if (serviceType == ArcGISServiceType.Image) {
                 mapLayer = new esri.layers.ArcGISTiledMapServiceLayer(layerUrl,
                        id: layerName,
                          visible: visible
                 });
                 dojo.connect(mapLayer, "onLoad"function (layer) {
                          layer_Initialized(layer);
                 });
                 dojo.connect(mapLayer, "onError"function (err) {
                          layer_InitializationFailed(err);
                 });
         }
}

As you can see in the example above, there is not much difference between the C# and JavaScript  development.  As we see later, the major difference of the API’s is in the widgets.

In the next document I will go deeper how after the map is loaded, all information about layers is available and can be used to build a custom legend. As in the Silverlight application, once the map is loaded, all information needed for  building a custom or editing widgets is available and can be exposed through our view model.