Friday, February 17, 2012

BUILD JAVASCRIPT APPLICATION FRAMEWORK WITH ARCGIS API

In previous documents I explained how you can create a powerful framework in Silverlight to make life easier of doing  GIS development around the ArcGis Silverlight API. The framework was achieved by using some key technologies from Microsoft.

Last week I attended the Microsoft TechDays 2012 at Brussels, and it becomes clear that with the introduction of Windows 8 the future is uncertain about  using plug-ins (Silverlight and Flex)  in web browsers. In Windows 8 Microsoft makes the development with JavaScript with HTML5 a first class development platform that will be covered in Visual Studio 2011.

In the Silverlight framework I used the MVVM pattern together with PRISM to use the patterns IoC and DI.  The Silverlight application was a somewhat light ArcGis desktop based on dynamically adding and editing layers based on new technology introduced in the version 10. I also illustrated how you can add some advanced editing functionality in an easy way.

In the JavaScript application that I introduce, I will start from the same application functionality allowing dynamic map to be used and feature editing on an ArcGis server.

The requirement for this applications will be as followed:

·         Configuration: The basic configuration file used is exactly the same as for the Silverlight application. That means I will use an XML file that will contain all the configuration data for the application. However a REST service will be used to expose the contents as a JSON object to the client. Secondary  XML configuration files will also be exposed as JSON objects. I use WCF services in .NET that will take into account this transformations. You can do the same in JAVA or create the JSON files yourself before using them at the client side.

·         Libraries: To make programming in JavaScript a lot easier, some important libraries will be used to help the conversion of some Silverlight patterns into JavaScript:

o   JQuery: This important library will be used as support for handling of the DOM and some utility functions.

o   KnockoutJS: This library will handle the MVVM. Because a lot of the Silverlight application is based on the use of MVVM, this will make the transitions of the viewmodels a lot easier.

·         GIS classes: The core of the Silverlight application consists of services that are implemented using Silverlight Classes and encapsulate the ArcGis Silverlight API functionality.  The ArcGis JavaScript API consist of the same functionality of the Silverlight API because a lot of functionality is based on the REST services at the ArcGis Server. Converting these services in  JavaScript classes is a lot simplified because of the almost one to one relation in functionality. The GIS classes will be split into different JavaScript files.

·         Components: In the JavaScript application, the notion of separate components is not used. The whole application will be implemented on one HTML page. The viewmodels are implemented in separate JavaScript files. There will be communication mechanism used between different viewmodels. In Silverlight I used the eventaggregator class of the Prism framework, in JavaScript I create an equivalent of this functionality (publish and subscribe of events).

·         Bootstrapper: An important role was attributed to the bootstrapper class of the PRISM framework. In JavaScript I will use the ready function of JQuery to achieve this pattern. The bootstrapper will be responsible for the creation of all GIS and non-GIS objects used in the application by using some kind of IoC.

·         HTML page: The idea is to have a page with very little JavaScript code embedded on the page as I did with the Silverlight application. Doing so, you can change the look and feel without having to change something in the viewmodel code or in the GIS classes. By limiting the creative construction of a HTML page, it is much easier supporting all kind of web enabled devices.

The application architecture should look similar to the next picture.
The viewmodel JavaScript code should contain no references to the ArcGis JavaScript API. By doing so, less experienced ArcGis developers could program with this concept. The GIS libraries hides the more complex functionality of the asynchronous processing and presents the results in a more workable way to the viewmodel.

This is the road book for the development of a JavaScript implementation of a simple ArcGis Desktop application for viewing an editing of GIS data. In the next document I will show you the creation of the map with a legend using the above architecture.

Tuesday, January 17, 2012

SIMPLE EDIT COMMANDS AND TOOLS - PART VII

Introduction


With the release of ArcGis Server 10, ESRI Inc. introduced a new service that can be activated for a map service : the feature service. This new service makes it a lot easier to do web editing over the internet. Through the ESRI Silverlight API you can now perform easy editing, and together with the ‘Geometry service’ you can create some of the editing operations found in the ArcGis Desktop application. In the next sections I will illustrate how you can create your own editing widgets, replacing the fixed edit widgets delivered by the ArcGis Silverlight API. In the sample code you will see the use of resources to make the application Multilanguage. In a later blog I will explain how you can use the Multilanguage functionality of Silverlight in cooperation with the MVVM pattern to make XAML and your C# code Multilanguage ready.

If you want to see how the application looks, you can find an example at the URL :


Editor control


When creating your own editor commands and tools, you will need the editor control found in the ArcGis JavaScript API. When looking at the API of this control, you will see that it contains the major CRUD methods needed for doing web editing. In this blog I explain the edit tools available in the Editor class.

To use the editor class of the ArcGis Silverlight API I created a specific ‘GisEditing’ service that will encapsulate all the edit operations needed. Because the editor control is needed to perform feature editing, it will instantiated in this custom class. The ViewModel involved in editing will not need creating the editor control. As with the other GIS services, an interface will be used to access editor functionality. The interface is available in the ViewModel through dependency injection.

GisEditing class


In this library, all editing functionality of our application will be implemented. This class is a singleton and is created in the boots trapper.  In the constructor we will instantiated the editor class. I add also a link to the GisOperation class to be able to have access to the map and the geometry services.

/// <summary>
/// Use IGisOperation interface to access the GisOperation service.
/// </summary>
/// <param name="gisOperations"></param>
public GisEditing(IGisOperations gisOperations, IMessageBoxCustom messageBoxCustom)
{
         editorTool = new Editor();
         this.gisOperations = gisOperations;
         this.messageBoxCustom = messageBoxCustom;
}


Through this class, we don’t need to add an editor component to the XAML files, all will be directed from this GisEditor service.

The functions that I implemented can be found in the interface below.

namespace Silverlight.Helper.Interfaces
{
  public delegate void GeometryServiceCompleteHandler(object sender, GraphicsEventArgs args);
  public delegate void MultipleResultOperationComplete(IList<Graphic> results);
  public delegate void SingleResultOperationComplete(Geometry result);
  public delegate void EditOperationComplete(int status,string message);
  public interface IGisEditing
  {
  Editor GetEditorTool();
  ObservableCollection<SymbolMarkerInfo> GetMarkerInfo(string layerId);
  IList<EditLayerData> GetEditLayers();
  ObservableCollection<TemplateData> GetTemplates(string layerId);
  void Initialize();
  void StartEditOperation(string operation, EditOperationComplete editOperationComplete);
  void EndEditOperation(int status, string message);
  void Intersect(IList<Graphic> targetGraphics, Geometry intersectGeometry);
 
  void CutOperation(IList<Graphic> polygons, Polyline polyline, 
    MultipleResultOperationComplete cutOperationComplete);
  void CreateConvexHull(IList<Graphic> pointList, 
    SingleResultOperationComplete singleResultOperationComplete);
  bool SplitPolygon(string layerName, MultipleResultOperationComplete cutOperationComplete);
  void SnapPolygons(string layerName, MultipleResultOperationComplete multipleResultOperationComplete);
  void UnionGeometries(IList<Graphic> geometries);
  void SaveAll();
  }
}


Most of these methods are an encapsulation of geometry services, hiding the asynchronous operation needed to execute the tool

Simple CRUD operation


To create a simple create / update or delete operation with the MVVM model you can do the following in XAML and ViewModel :

XAML

<Button  Style="{StaticResource ActionButton}" Name="btnAddGeometry" Command="{Binding AddCommand}" 
         CommandParameter="{Binding AddGeometryParameter}" >
  <Image Source="/Silverlight.UI.Esri.JTToolbarEditGeneral;component/Images/EditingPolygonTool32.png">
    <ToolTipService.ToolTip>                                                            <TextBox Text="{Binding Source={StaticResource LocalizedStrings},Path=AddGeometryTip}" 
                  BorderThickness="0" />
    </ToolTipService.ToolTip>
  </Image>
</Button>

A command is used to start the adding of a feature.

ViewModel

private ICommand _addCommand;
private ICommand _addCommand;
private ICommand _clearSelectionCommand;
private ICommand _deleteSelectedCommand;
private ICommand _editVerticesCommand;
 
 
public ICommand AddCommand
{
         get
         {
                  return _addCommand;
         }
         set
         {
                 _addCommand = value;
                 this.RaisePropertyChanged(() => this.AddCommand);
         }
}



/// <summary>
/// Add a new feature to a feature layer
/// </summary>
/// <param name="arg"></param>
protected virtual void OnAddCommandClicked(object arg)
{
  try
  {
    if (gisEditing.GetEditorTool() != null)
    {
      if (currentEditLayer == null)
      {
        ShowMessagebox.Raise(new Notification
         {
            Content = Silverlight.Helper.Resources.Helper.NoEditLayerSelected,
            Title = Silverlight.Helper.Resources.Helper.Warning
         }, confirmation =>
            {
             // No action required
            });
         return;
       }
 
       IList<string> layerIDs = new List<string>();
       layerIDs.Add(currentEditLayer.LayerName);
       gisEditing.GetEditorTool().LayerIDs = layerIDs;
       gisEditing.GetEditorTool().Freehand = false;
       Silverlight.Helper.DataMapping.FeatureLayerInfo layerInfo =
         gisOperations.GetFeatureLayerInfo(currentEditLayer.LayerName);
                 
       if (layerInfo.FeatureTemplates != null && layerInfo.FeatureTemplates.Count > 0)
         gisEditing.GetEditorTool().Add.Execute(
            layerInfo.FeatureTemplates.First(l => l.Key.Length > 0).Value);
       else
       {
         if (layerInfo.FeatureTypes != null && layerInfo.FeatureTypes.Count > 0)
         {
            FeatureType featureType = 
              layerInfo.FeatureTypes.FirstOrDefault(l => l.Key != null).Value as FeatureType;                                        gisEditing.GetEditorTool().Add.Execute(featureType.Id);
         }
         else
           gisEditing.GetEditorTool().Add.Execute(null);
       }
       EditOperationStarted("Add");
     }
   }
   catch (Exception ex)
   {
     ShowErrorMessagebox.Raise(new Notification
       {
         Content = String.Format("OnAddCommandClicked-{0}[{1}]", ex.Message, ex.StackTrace),
         Title = "System error"
       });
   }
}
 
protected virtual bool CanAddCommandClicked(object arg)
{
         return IsMapLoaded && !editActionActive;
}
 
 

The add command is built around the add method of the editor class. If you want to create a ViewModel  that has a more clear separation from the editor class, you could move this functionality towards the GisEditing service with the necessary parameters.

I found the documentation of ArcGis Silverlight API not always complete when it comes to detail the use of parameters.

In the add command I support subtypes and feature templates. Experimenting on the parameters of  the execute method of the add method of the editor class gave me the above result.  I hope in the future that ESRI will document in more detail the parameters.

The other simple edit commands (delete, vertices’ update)  are derived from the editor class and has very simple implementations.

Field subtypes and feature templates


If you want to use field subtypes creating a list of symbols where a user can choose, you must retrieve these symbols from the ArcGis Server feature service. The best way for doing this is when a feature class is initialized during the built of the map. In our GisOperation class each feature class initialized event is handled and at that moment all necessary information for a feature is available and can be saved in the GisOperation object. So in this way subtypes and feature templates are retrieved for later use. But also at the same time the name of the object id field and geometry type of the feature class are retrieved.



void InitializedFtLayer(object sender, EventArgs e)
{
  try
  {
         FeatureLayer layer = (FeatureLayer)sender;
         featureLayerInfos.Add(new Helper.DataMapping.FeatureLayerInfo()
         {
                 FeatureTemplates = layer.LayerInfo.Templates,
                 Url = layer.Url,
                 Name = layer.LayerInfo.Name,
                 Id = layer.ID,
                 FeatureTypes = layer.LayerInfo.FeatureTypes,
                 LayerGeometryType = layer.LayerInfo.GeometryType,
                 ObjectId = layer.LayerInfo.ObjectIdField
         });
         layersData.Add(new LayerData()
         {
                 ID = layer.LayerInfo.Id,
                 LayerName = layer.LayerInfo.Name,
                 Selection = true
         });
         VerifyInitialisationMap();
  }                                              
  catch (Exception ex)
  {
  messageBoxCustom.Show(String.Format("InitializedFtLayer-{0}/{1}", sender, ex.Message), GisTexts.SevereError, MessageBoxCustomEnum.MessageBoxButtonCustom.Ok);
  }
}



The XAML code for creating an edit widget looks like this: 

<ListBox Name="ListPoints"
                     helper:Selected.Command="{Binding SymbolSelected}"
                     ItemsSource="{Binding SymbolMarkers}"
                     Visibility="{Binding SubTypeVisibility}">
   <ListBox.ItemsPanel>
      <ItemsPanelTemplate>
         <StackPanel Orientation="Horizontal"/>
      </ItemsPanelTemplate>
   </ListBox.ItemsPanel>
   <ListBox.ItemTemplate>
      <DataTemplate>
         <StackPanel Background="LightGray" Orientation="Horizontal">
            <esriToolkitPrimitives:SymbolDisplay Width="25"
                               Height="25"
                               VerticalAlignment="Center"
                               Symbol="{Binding SymbolMarker}">
                <ToolTipService.ToolTip>
                    <TextBox BorderThickness="0" Text="{Binding Name}"/>
                </ToolTipService.ToolTip>
            </esriToolkitPrimitives:SymbolDisplay>
         </StackPanel>
      </DataTemplate>
   </ListBox.ItemTemplate>
</ListBox>

The item source used in the ViewModel for displaying the symbols is :

// Feature Types
private ObservableCollection<SymbolMarkerInfo> _symbolMarkers;
public ObservableCollection<SymbolMarkerInfo> SymbolMarkers
{
  get
  {
         return _symbolMarkers;
  }
  set
  {
         _symbolMarkers = value;
         this.RaisePropertyChanged(() => this.SymbolMarkers);
  }
}



Where the SymbolMarkerInfo has the following structure:

/// <summary>
/// Symbol information class
/// </summary>
public class SymbolMarkerInfo
{
         public Symbol SymbolMarker { getset; }
         public string Name { getset; }
         public object ObjectFeatureType { getset; }
         public string LayerId { getset; }
}

Because feature  templates have no symbol defined, in the edit toolbar I implemented templates using a combo box.

<ComboBox Name="templates"
          Width="250"
          Height="25"
          Margin="10,0,0,0"
          DisplayMemberPath="Description"
          IsEnabled="True"
          ItemsSource="{Binding EditTemplates}"
          Visibility="{Binding TemplateVisibility}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
           <i:InvokeCommandAction Command="{Binding TemplateSelectCommand}" CommandParameter="{Binding SelectedItem, ElementName=templates, Mode=OneWay}" />
        </i:EventTrigger>
     </i:Interaction.Triggers>
</ComboBox>

See the use of the invoke command to trigger the selection. This is the standard way of MVVM for link combo box events to the ViewModel.

private ObservableCollection<TemplateData> _editTemplates = new ObservableCollection<TemplateData>();
public ObservableCollection<TemplateData> EditTemplates
{
  get
  {
         return _editTemplates;
  }
  set
  {
         _editTemplates = value;
  }
}

With TemplateData defined as

public class TemplateData
{
         public string Description { getset; }
         public FeatureTemplate EditTemplate { getset; }
         public string LayerId { getset; }
}