Category: Web

Listen for updates to a FeatureLayer

The ArcGIS Silverlight/WPF API provides a FeatureLayer as an efficient means for referencing a layer that contains features (geometry and attributes) and resides on a server.  You can add a FeatureLayer to a Map in XAML, define the URL to the layer on the server, add symbology and the feature layer contents will be rendered in the Map as graphic features.  Technically the FeatureLayer is a GraphicsLayer that wraps a QueryTask which queries either a feature layer in an ArcGIS Server map service or a spatial table via the MapIt Spatial Data Service. 

Unfortunately the FeatureLayer does not explicitly expose any events dealing with the query operation, such as when the query has completed and all features have been returned.   This may be necesssary if you need to define renderers on the fly, determine query progress, etc.  To determine when a FeatureLayer query is complete you can listen to the collection changed event on the FeatureLayer.Graphics property.  However this event will be triggered each time a graphic is added, so you need to wait until all graphics have been added.  Enter the Dispatcher class.  Dispatcher enables you to invoke a delegate on the UI (main) thread.  In this case, the completed event on the QueryTask inside the FeatureLayer is executing on the UI thread.  You can call the Dispatcher.BeginInvoke method and delegate a call to code of your choice.  Since this delegate must execute on the UI thread, it must also wait for the completed event on QueryTask to finish before being called.  So in effect, the delegate in BeginInvoke is waiting in a queue.  Also, so the delegate isn’t called each time the graphics collection changes, remove the collection changed event handler upon the first call.  You can readd the handler in your code logic if you need to continue listening to changes in a FeatureLayer’s graphics collection. A basic code example is provided below.  It assumes a FeatureLayer named “MyFeatureLayer” has been defined in XAML:

ESRI.ArcGIS.Client.FeatureLayer _featureLayer;

public FeatureLayerEventingDemo()
{
    InitializeComponent();

    _featureLayer = MyMap.Layers["MyFeatureLayer"] as FeatureLayer;
    _featureLayer.Graphics.CollectionChanged += Graphics_CollectionChanged;
}

void Graphics_CollectionChanged(object sender,
    System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    // Remove event method on the first call, then invoke delegate
    _featureLayer.Graphics.CollectionChanged -= Graphics_CollectionChanged;
    System.Windows.Application.Current.RootVisual.Dispatcher.BeginInvoke(
	delegate {
	    doSomething();
	});
}

private void doSomething()
{
    // Do something... then readd event method to listen for another update
    _featureLayer.Graphics.CollectionChanged += Graphics_CollectionChanged;
}
Update: As of version 1.2, FeatureLayer maintains an UpdateCompleted to listen for when features have been retrieved from a service and an UpdateFailed event to determine if an error occurred during feature retrieval.

Rex Hansen
ESRI Product Engineer
ArcGIS Server .NET, Silverlight/WPF, MapIt

 

Posted in Web | Tagged , , | 2 Comments

Sort query results using LINQ

Sorting data on values in a column is a common requirement in many applications.  Whether defining a more intuitive presentation of information  or merely providing a more aesthetic user experience, an organized presentation of attribute data is ideal. Unfortunately many data sources used by the Silverlight/WPF API do not inherently support ordering query results, or provide limited support.  For example, queries against layers in ArcGIS Server services will only support ORDER BY if hosted in ArcSDE.  Even if supported by the layer, the REST API does not expose the ability to add the ORDER BY clause.  Note that it is supported in the SOAP API using the QueryFilter.PostfixClause property.  For SQL Server tables hosted as layers via the MapIt Spatial Data Service, ordered data can be provided in a view which must be created beforehand.  The Spatial Data Service does not support ordering on the fly.  In either case, sorting query results cannot be done in a service request so the sorting operation is left up to the client.  In Silverlight/WPF querying layer contents usually involves the QueryTask which returns a FeatureSet (IEnumerable<Graphic>).  While there are a few techniques for sorting data in Silverlight, LINQ provides an quick and easy solution.  Here’s an example which queries a layer in an ArcGIS Server map service and uses LINQ to sort results in ascending order using the NAME field:

public void SortGraphicsDemo()
{
    QueryTask queryTask =
      new QueryTask("http://services.arcgisonline.com/ArcGIS/rest/services"
                    + "/Demographics/USA_Population_Density/MapServer/4");
    Query query = new Query();
    query.Where = "1 = 1";
    query.OutFields.Add("*");

    queryTask.ExecuteCompleted += queryTask_ExecuteCompleted;
    queryTask.ExecuteAsync(query);
}

void queryTask_ExecuteCompleted(object sender, QueryEventArgs e)
{
    var enumGraphics = from g in e.FeatureSet
                       orderby (g.Attributes["NAME"] as string) ascending
                       select g;

    foreach (ESRI.ArcGIS.Client.Graphic gn in enumGraphics)
    {
        System.Diagnostics.Debug.WriteLine
	(string.Format("{0},{1}", gn.Attributes["NAME"], gn.Attributes["TOTPOP_CY"]));
    }
}  

Rex Hansen
ESRI Product Engineer
ArcGIS Server .NET, Silverlight/WPF, MapIt

 

Posted in Web | Tagged , | Leave a comment

Deploy an ArcGIS Silverlight application to Windows Azure

At the end of 2008, Microsoft unveiled the Windows Azure platform as a set of cloud computing services which provide hosted operating system, database, web application, and security solutions.  For the past year Microsoft invited users to access to the Azure platform for testing purposes in an effort to optimize performance, scalability, and functionality.  At the 2009 Professional Developers Conference in mid-November, Microsoft announced that Azure will go live in January 2010 and begin charging customers on February 1, 2010.   The pending production-ready release of the Azure platform presents a question for Silverlight developers, let alone those working with the ArcGIS Silverlight/WPF API: does Windows Azure provide an effective platform to host Silverlight applications?  To put it simply, yes.  Basically you can use Azure to host, scale, and secure Silverlight Web applications instead of purchasing and maintaining hardware/software solutions in-house.  This same argument applies to other features of the Azure platform ranging from secure off-site data storage, to accessing data in SQL Server, to hosting business logic via Web services.   With this in mind, I’d like to provide a brief walkthrough of how to create and deploy and ArcGIS Silverlight application into the Windows Azure cloud.  

Note, to complete these steps, you will need to have access to a Windows Azure hosted service.   If you do not have access to a hosted service, you will need to request an invitation token via http://www.microsoft.com/windowsazure/account/ (requires a Live ID account).   When you receive the token, login to http://windows.azure.com/ and claim it to create a hosted service.   Note, as Windows Azure moves closer to production in early 2010, this process may change. 

Let’s get started… 

  1. Install the ArcGIS API for Microsoft Silverlight/WPF

    Assuming you have Visual Studio 2008 (with Silverlight 3 Tools) or 2010, download and install the ArcGIS API for Microsoft Silverlight/WPF

  2. Install Windows Azure Tools for Visual Studio

    Download Windows Azure Tools for the Visual Studio 2008/2010.  The download contains a set of Visual Studio projects and the Windows Azure SDK which includes the Development Fabric to test your Windows Azure application prior to deployment.
  3. Open Visual Studio and create a Windows Azure Cloud Service

    When creating the cloud service, add a single ASP.NET Web Role.  

  4. Add a Silverlight application to the solution

    When adding a Silverlight application in Visual Studio use the ASP.NET Web Application created as a Web Role in the previous step as a host for the Silverlight application.

  5. Add a reference to the core ArcGIS Silverlight assembly in the Silverlight application

    Add a reference in the Silverlight application to the ESRI.ArcGIS.Client.dll.  References will be available in the .NET tab in Visual Studio 2008.  Navigate to the library in Visual Studio 2010.  By default, the ArcGIS Silverlight assemblies are installed into C:Program FilesESRI SDKsSilverlightv1.1.  If installed on a 64-bit operating system, ArcGIS Silverlight assemblies are installed in the 32-bit (x86) program files directory.

  6. Add a Map, base map layer, and feature layer for SQL Azure spatial data

    First add a namespace reference to ESRI.ArcGIS.Client, then add a Map control to the root container (Grid) in the page (UserControl).  Add a base map layer to provide a geographic context.  Optionally, you can add a dynamic map service or feature layer to overlay on the base layer.  In the following example, the base layer references an ArcGIS Online tiled (cached) map service and the feature layer references a table in a SQL Azure Database accessed via an ESRI MapIt Spatial Data Service deployed in a Windows Azure Web Role.  Map tips have also been configured on the feature layer.  Note that output fields for a feature layer must include the field(s) that will be used to display attribute values in map tips.    
    <UserControl x:Class="SilverlightApplication1.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:esri="clr-namespace:ESRI.ArcGIS.Client;assembly=ESRI.ArcGIS.Client"
        xmlns:esriConverters="clr-namespace:ESRI.ArcGIS.Client.ValueConverters;assembly=ESRI.ArcGIS.Client"
        xmlns:sys="clr-namespace:System;assembly=mscorlib">
    
        <UserControl.Resources>
            <esriConverters:DictionaryConverter x:Name="MyDictionaryConverter" />
        </UserControl.Resources>
    
        <Grid x:Name="LayoutRoot" Background="White">
            <esri:Map>
                <esri:ArcGISTiledMapServiceLayer
                     Url="http://services.arcgisonline.com/ArcGIS/rest/services/NGS_Topo_US_2D/MapServer"/>
                <esri:FeatureLayer
                     Url="http://esri.cloudapp.net/databases/Demo/dbo.WorldCities_Geographic"
                     Color="#99FF0000" >
                    <esri:FeatureLayer.OutFields>
                        <sys:String>NAME</sys:String>
                    </esri:FeatureLayer.OutFields>
                    <esri:FeatureLayer.MapTip>
                        <TextBlock
                             Text="{Binding Converter={StaticResource MyDictionaryConverter},
                             ConverterParameter=NAME, Mode=OneWay}" FontSize="14"
                             Foreground="White" FontWeight="Bold">
                            <TextBlock.Effect>
                                <DropShadowEffect BlurRadius="10" Color="Black"/>
                            </TextBlock.Effect>
                        </TextBlock>
                    </esri:FeatureLayer.MapTip>
                </esri:FeatureLayer>
            </esri:Map>
        </Grid>
    </UserControl>
    
    
  7. Run the cloud service

    Run the cloud service locally using the Azure SDKs Development Fabric.  The Development Fabric provides a Windows Azure simulation environment for you to debug and test an application before deployment. You will need to run Visual Studio in administrator mode to use the Development Fabric.  The ASP.NET Web application configured as a Web Role should host the Silverlight application on a local Web server.  In this example the map control should fill the browser window and display the base and feature layer.

  8. Optional.  Configure the default document for the Web Role

    The ASP.NET Web application configured as a Web Role will host the Silverlight application in an html or aspx page.  If test pages (aspx and html) were created the file name will contain the name of the Silverlight application and the text “TestPage”.  In the Visual Studio IDE you can define the default start page for the Web application in Solution Explorer or in project properties.  However this does not define the default start page for a Windows Azure Web Role.  To define a start page for Windows Azure you can either 1) change the name of the host page (aspx or html) to a standard default document name, such as Default.aspx or 2) add the default document in the web.config (example below).  Note, if you add a default document in the web.config to use as a start page, make sure you clear the default document list to remove any existing document names.  To clear the list use the <clear /> tag illustrated in the example. 

    <system.webServer>
       <defaultDocument>
          <files>
            <clear />
            <add value="Default.htm"/>
          </files>
        </defaultDocument>
    

    If a default document is not available, you must specify the full path to the document hosting the Silverlight application to access it in a browser. 

  9. Publish the cloud service project for Windows Azure

    In Visual Studio, right click on the cloud service solution and select Publish.  This operation will generate a cloud service package (.cspkg) and application settings file (.cscfg) in the build output directory in a folder named “Publish”.


     

  10. Deploy the cloud service project files to Windows Azure

    Login to Windows Azure (http://windows.azure.com/) and create or select a hosted service.  Deploy the cloud service to the Staging or Production site.  Note, deploying, starting, or stopping a service may take longer than 5 minutes to complete.  

    Run the service (if necessary) and the Web Role containing the Silverlight application will initialize and start.  If desired, use the Staging site to test your application.  The Staging site url contains a unique GUID as a subdomain.   You can deploy the Staging application to Production or deploy directly to the Production site if you choose.  The Production site url was defined when the hosted service was created in Windows Azure.    

  11. View the application

    Open a browser and navigate to the Production or Staging site url.  The ArcGIS Silverlight application should load and display.   In this example, the Production site url to the application is:  http://arcgis.cloudapp.net/simple.htm.  To view a styled application with the same data, navigate to the root site http://arcgis.cloudapp.net/.   To enhance the ArcGIS Silverlight application with additional functionality and content use the ArcGIS Silverlight/WPF API Resource Center for further reference: http://resources.esri.com/arcgisserver/apis/silverlight

For additional information on current and future actions with respect to Windows Azure, see the Windows Azure FAQ.

Rex Hansen
ESRI Product Engineer
ArcGIS Server .NET, Silverlight/WPF, MapIt

 

Posted in Web | Tagged , , | 4 Comments

Improve startup time of the map control

By default, the map control will not render until all layers have initialized.   This process ensures that the map will startup at the full extent of all layers and use the preferred spatial reference discovered at runtime.  However, if you have many layers in your map or if one layer takes a while to initialize, users may have wait a significant amount of time to see the first map.   To improve startup time simply define the initial extent and spatial reference.  Then the map will not need to discover this information at runtime and as a result individual layers will start loading as soon as they initialize.

You can define the startup extent in your page as follows:

<esri:Map>
      <esri:Map.Extent>
            <esriGeometry:Envelope XMin="-180" YMin="-90" XMax="180" YMax="90" >
                  <esriGeometry:Envelope.SpatialReference>
                        <esriGeometry:SpatialReference WKID="4326"/>
                  </esriGeometry:Envelope.SpatialReference>
            </esriGeometry:Envelope>
      </esri:Map.Extent>
...
</esri:Map>

If you have multiple tiled layers in your map, make sure their spatial reference matches the one defined for the initial extent or they will not render.   Also see the interactive SDK sample for an example of this in action.
 
Morten Nielsen
Senior Software Engineer
ArcGIS Server.NET, Silverlight/WPF, MapIt

 

Posted in Web | Tagged , , | 4 Comments

Use a Bing Maps key in your ArcGIS Silverlight/WPF application

In November 2009, Bing Maps introduced the ability to generate keys which assign an id to an application that uses Bing Maps services.  A Bing Maps key is similar to a token, but it does not expire and it is associated with a specific application name and URL.  Like a token, the key is included in each request to a Bing Maps service to authorize access. 

The ArcGIS Silverlight/WPF API uses Bing Maps imagery, geocoding, and routing services via a set of proxy classes in the ESRI.ArcGIS.Client.Bing library – namely the TileLayer, Geocoder, and Routing classes, respectively.  Each class has a Token property which must be set to a valid token generated by a Bing Maps token service for either the production or staging environment.   The Token property can also be set to a Bing Maps key.  Keys can only be used with Bing Maps services in the production environment.   

To create a Bing Maps key, do the following:

  1. In a browser, navigate to http://www.bingmapsportal.com/.
  2. Create or use a Windows Live ID to log in.
  3. On the “Create and view Bing Maps keys” page enter an application name and URL to create a key.  Currently the name and URL are not validated. 
  4. Copy the key, available on the same page, into your ArcGIS Silverlight or WPF application.  Set the Token property or apply the token in the proxy class constructor.
  5. Run your application.  It will function without requiring you to generate new tokens and update the Token property on a regular basis.  As a result, you can remove any client or server logic to generate and apply Bing Maps tokens.

This begs the question, why would you use a token instead of a key?

Since the name or URL associated with a key is not validated, it can be used by multiple applications.  The key itself is present and visible in Web requests from your application to Bing Maps services.  As a result, someone can discover and use your key.  If this is a concern, you can avoid this scenario by using a token which has a maximum expiration time of 8 hours.   Validation of a Bing Maps key will be easier once the Silverlight platform supports the Referer header – which should be available in Silverlight 4 due to be released early next year.   

Rex Hansen
ESRI Product Engineer
ArcGIS Server .NET, Silverlight/WPF, MapIt

Posted in Web | Tagged , , | 2 Comments

Using multiple subdomains with a tiled service layer

Since many Web browsers do not allow more than two requests to a given domain at the same time, some users map multiple subdomains to the same server. This often improves performance for client applications that make many requests to the same server. Currently, ArcGIS Server and the ArcGIS Silverlight/WPF API do not support multiple subdomains for the same service.  However, you can enhance the ArcGIS Silverlight/WPF API to take advantage of a tiled map service hosted on multiple subdomains.  This post will demonstrate how to add support for multiple subdomains to a tiled map service layer to improve map initialization, navigation, and overall performance.  

The approach involves creating a custom tiled service layer that inherits from ArcGISTiledMapServiceLayer and overriding the GetTileUrl method. In the method simply replace the subdomain part of the URL with any available subdomain hosting the map tiles. You can randomize which subdomain to use, but to ensure that the browser will cache the tiles you’ll need to associate a given row/column/level with the same subdomain. To do this use a simple modulus expression that evenly distributes that load on all subdomains and guarantees that a specific row/column/level will always map to the same subdomain.

The custom tiled service layer looks like this:

public class ArcGISMultiDomainTileLayer : ArcGISTiledMapServiceLayer
{
      string[] subDomains = new string[] {
         "http://subdomain1.",
         "http://subdomain2.",
         "http://subdomain3."
      };

      public override string GetTileUrl(int level, int row, int col)
      {
            string url = base.GetTileUrl(level, row, col);
            string subdomain = subDomains[(level + col + row) % subDomains.Length];
            return url.Replace("http://subdomain1.", subdomain);
      }
}

Now you can use this layer in your XAML in place of an ArcGISTiledMapServiceLayer:

<esri:ArcGISMultiDomainTileLayer				
Url="http://subdomain1.myserver.com/ArcGIS/REST/MyMapService/MapServer/" />

Morten Nielsen
Senior Software Engineer
ArcGIS Server.NET, Silverlight/WPF, MapIt

Posted in Web | Tagged , | 1 Comment

The ArcGIS API for Microsoft Silverlight/WPF version 1.1 is available!

Version 1.1 of the ArcGIS API for Microsoft Silverlight/WPF is now available for download on the ArcGIS Server Resource Center.   The API includes a set of feature, functionality, and integration enhancments.  One notable difference; you’ll download a setup executable which will install and configure Silverlight and WPF assemblies, components, and templates with Expression Blend and Visual Studio.  Here are a few highlights of what’s new:

  • Silverlight 3 is now required with version 1.1.  Silverlight 2 is no longer supported. 
  • Silverlight 3 supports element binding, which means you can bind the Map property of a Navigation control to a Map using XAML – no code behind.
  • A complete, interactive design-time experience in Expression Blend 3.  You can drag, drop, and configure ArcGIS Silverlight and WPF controls on the artboard.
  • A set of Silverlight templates are integrated with Expression Blend 3 and Visual Studio 2008.  The templates provide a pre-configured, pre-styled, customizable architecture that enables you to create production worthy mapping applications quickly and easily.
  • Expression Blend 3 introduced behaviors, which are reusable pieces of packaged code that define interactive relationships between controls using XAML.  A new ArcGIS Silverlight and WPF library, ESRI.ArcGIS.Client.Behaviors.dll, includes a set of behaviors and actions to define interactive relationships between user input and Map behavior and content. 

We invite you to try the new Interactive SDK to see the new features and functionality in action.  In addition, we’ve created an interactive Symbol Gallery for you to peruse and copy marker, line, and fill symbols for use in your application.  

Enjoy!

The ArcGIS Silverlight/WPF Development Team

Posted in Web | Tagged , , , , | 4 Comments

Deep linking to map extents

With the Silverlight 3 release, we get a new Navigation API that allows you to deep-link into your Silverlight controls, as well as enable the back/forward buttons in the browser. This blog post will show you how you can use the navigation API to link to specific extents in the map, and allow the user to go back and forward in the extent history user the browser history.

The Navigation API allows you to use parts of the URI in the browser to load specific user controls in your project. To do this, let’s first link to a specific user control in our a new default Silverlight + website application. First add a reference to the navigation assembly “System.Windows.Controls.Navigation”.

In our main page control, we create a Frame element, which maps to another “page”, in this case Map.xaml. Map.xaml will be the page that holds our map. The MainPage.xaml should look something like this:

<UserControl x:Class="MapNavigation.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:navigationCtrl="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls.Navigation"
    xmlns:navigation="clr-namespace:System.Windows.Navigation;
assembly=System.Windows.Controls.Navigation" >
  <Grid>
       <navigationCtrl:Frame>
              <navigationCtrl:Frame.UriMapper>
                   <navigation:UriMapper>
                        <navigation:UriMapping Uri="" MappedUri="/Map.xaml" /> 
                   </navigation:UriMapper>
              </navigationCtrl:Frame.UriMapper>
       </navigationCtrl:Frame>
  </Grid>
</UserControl>

The Frame uses the UriMapping entry to map the empty (root) URI to the user control “/Map.xaml”.  Since the Frame’s Source property is not set, the Map.xaml associated with the empty URI is loaded by a default at runtime.    

Next, right-click your Silverlight project in Visual Studio, and select to add a new User Control. Name it Map.xaml. This will be the sub-page that will be hosted by MainPage. This must be a of type System.Windows.Controls.Page, so the first step is to change the superclass. Go to Map.xaml’s header and change the UserControl tag to navigation:Page and add the navigation prefix. While we are at it we also add the ESRI namespace and a simple map control:

<navigation:Page x:Class="MapNavigation.Map"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:esri="clr-namespace:ESRI.ArcGIS.Client;assembly=ESRI.ArcGIS.Client"
    xmlns:navigation="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls.Navigation">
       <Grid x:Name="LayoutRoot">
              <esri:Map x:Name="myMap">
                     <esri:ArcGISTiledMapServiceLayer ID="StreetMap"
       Url="http://server.arcgisonline.com/ArcGIS/rest/services/
ESRI_StreetMap_World_2D/MapServer" />
              </esri:Map>
       </Grid>
</navigation:Page>

In the code-behind of Map.xaml.cs, change the class declaration to inherit from “Page” instead of “UserControl” :  public partial class Map : Page

If you run the application now, the application should load up Map.xaml. Notice the browser URI does not contain any parameters and the back button in the browser is disabled.

So far so good. So now to allow the users to go back and forth when they zoom in/out we first need to tell the navigation service that the state has changed and provide it with a the link to the current extent that changed. We do this by listening to the ExtentChanged event of the map, and in the event handler call NavigationService.Navigate:

<esri:Map x:Name="myMap" ExtentChanged="myMap_ExtentChanged" >

private void myMap_ExtentChanged(object sender, ESRI.ArcGIS.Client.ExtentEventArgs e)
{
       NavigationService.Navigate(new Uri(String.Format(CultureInfo.InvariantCulture,
     "#extent={0},{1},{2},{3}", e.NewExtent.XMin, e.NewExtent.YMin,
e.NewExtent.XMax, e.NewExtent.YMax), UriKind.Relative));
}

The format we use can be anything, but we prefix it with “#” to tell the service that this is a “fragment” within the Map URI. If we didn’t do this, the service would try and load a new instance of Map.xaml causing the map to reset. If we run the application now, everytime you change the extent, the browser URI will update:

Notice the “$” symbol which signifies a fragment that contains extent parameters.  The fragment is handled by the Map page which is mapped to the default or root URI. 

The back and forward buttons now work, but nothing happens when we click them. We still need to handle when the fragment changes and set the map extent according. We do this by overriding Page.OnFragmentNavigation. This method is called every time the browser fragment after “#$” has changed. In this method we will parse out the extent and set the extent of the map. Notice that we check if the extent really has changed. This is because the code in myMap_ExtentChanged also causes this method to get called, but in those cases there is no need to update the extent, since it already happened. Doing that could cause an infinite loop. Similar we ignore the code in the extent changed handler if we update the extent in OnFragmentNavigation using a simple boolean flag:

protected override void OnFragmentNavigation(FragmentNavigationEventArgs e)
{
       if(e.Fragment.StartsWith("extent="))
       {
              try
              {
                     string[] vals = e.Fragment.Substring(7).Split(new char[] { ',' });
                     double xmin = Double.Parse(vals[0]);
                     double ymin = Double.Parse(vals[1]);
                     double xmax = Double.Parse(vals[2]);
                     double ymax = Double.Parse(vals[3]);

                     Envelope mapExtent = myMap.Extent;
                     Envelope newExtent = new Envelope(xmin, ymin, xmax, ymax);

                     if (mapExtent==null || !AreEqual(mapExtent, newExtent, 0.000000001))
                     {
                           flag = true;
                           myMap.Extent = newExtent;
                     }
              }
              catch { }
       }
       base.OnFragmentNavigation(e);
}

bool flag = true;
private void myMap_ExtentChanged(object sender, ESRI.ArcGIS.Client.ExtentEventArgs e)
{
       if (flag) flag = false;
       else
            NavigationService.Navigate(new Uri(String.Format(CultureInfo.InvariantCulture,
     "#extent={0},{1},{2},{3}", e.NewExtent.XMin, e.NewExtent.YMin,
e.NewExtent.XMax, e.NewExtent.YMax), UriKind.Relative));
}


Now when you run the application, you can use the back and forward buttons to visit previous extents, you can copy/paste the URL into a different browser or email it, and the map will start up at the same location.

Download a sample from our development server – view the sample live to see it in action.

You can learn more about the navigation framework on the following links:
Silverlight Navigation framework screencast
Tim Heuer’s blog on the Navigation framework
Tim Heuer’s blog on Navigation behavior 
 

Morten Nielsen
Senior Software Engineer
ArcGIS Server.NET, Silverlight/WPF, MapIt

Posted in Web | 2 Comments

Using services across schemes

A number of questions have surfaced recently regarding support for ArcGIS Server services over a scheme different than a host application that uses the ArcGIS API for Microsoft Silverlight.  For example, when hosting an ArcGIS Silverlight API application on https that accesses ArcGIS Server services over http, the service fails to display as a layer in the map.  In this post I’ll discuss a set of common issues encountered with cross scheme restrictions that explain this behavior – and present a few expectations for what should work.  Before we dive into this topic though, let’s cover some background.  For security reasons, the Silverlight runtime restricts access to data and services for specific classes across schemes, domains and zones.   These restrictions may impact your use of data and services in the ArcGIS Silverlight API.  Scheme defines the protocol by which you access a resource.   This includes http, https, file, ftp, etc.   From the Silverlight perspective, scheme applies to the host application and resources it will use.  Usually Silverlight applications are hosted in a Web page, thus the host scheme is http or https.   Likewise, resources such as services and images are usually exposed over http or https.  

The ArcGIS Silverlight API requires that applications be hosted on a Web server, thus the host scheme will be either http or https.  Resources used by components of the ArcGIS Silverlight API are often consumed as services or images on a Web server, so they share the same scheme requirements (http or https).  The use of services and images in the ArcGIS Silverlight API is impacted by cross-scheme restrictions in two ways:  1) when using services to retrieve metadata about a layer to display in a map or query and 2) when displaying map service layers in a map requires retrieving a dynamic map image or a set of map image tiles.  In short, when working across different schemes #1 is possible, #2 is not.       

Since cross-scheme access to a service is allowed, how do you enable it?
 
The solution is specific to the Silverlight platform.  You need to create a client access policy file on the services Web server that enables full access.  For example, the client access policy file on ArcGIS Online allows full access to http and https services on ArcGIS Online from Silverlight applications hosted on http or https. You can view the file in a browser via the url:  http://services.arcgisonline.com/clientaccesspolicy.xml

The file contains two lines that affect scheme support.  The following line in the client access policy file allows access from http hosted apps to http services -and- allows access from https hosted apps to http and https services:

domain uri="*"

It does not allow access from http hosted apps to https services. This is handled by the next line:

domain uri="http://*" 

Both entries work together to allow full access.

Note, the Silverlight 3 runtime will recognize a domain uri equal to ”https://*”.   This is only necessary if you want to restrict access to Silverlight applications hosted on https.  See the Network Security Access Restrictions in Silverlight topic on MSDN for more information.

Even with cross-scheme access to services enabled, you cannot load an image across schemes in a Silverlight application.  It is a limitation imposed by the Silverlight platform.  Take a look at the document URL Access Restrictions in Silverlight on MSDN for more details.  Note the cross-scheme entry for the Image class is “Not Allowed”.  This restriction is often to blame when a layer, accessed across schemes, fails to display in the ArcGIS Silverlight API Map control.   

So, with this in mind, if the client access policy file allows complete access to site resources (as the ArcGIS Online and serverapps.esri.com do), what should you expect to work in the ArcGIS Silverlight API?  

Silverlight Hosted
Scheme

Service
Scheme

Service
access

Image
access

HTTP

HTTP

Yes

Yes

HTTP

HTTPS

Yes

No

HTTPS

HTTP

Yes

No

HTTPS

HTTPS

Yes

Yes

The problematic combinations are clear – cross scheme access to map images (dynamic or tile) are not accessible. So how can you resolve this?  For ArcGIS Server services the solution involves changes on the server.   

For dynamic (non-cached) map services the ArcGIS Silverlight API sends a request to export a map image to the Url defined on an ArcGISDynamicMapServiceLayer. The response format is JSON, which includes a url to the output image. The output virtual directory, which includes the scheme, is defined by the service. Unfortunately you cannot have multiple output virtual directories or define the scheme for the output image url.

So how can you solve this on the server? Create two services from the same map config file (mxd, msd): one that generates output in a virtual directory accessible via https and another for http. For example, two services are available on http://serverapps.esri.com: California – which outputs to an http directory; California_SSLOUT – which outputs to an https directory.  The following table highlights the results of different scheme combinations for the host and service:

Silverlight Hosted Scheme

Scheme and Service access

Result

HTTP

HTTP – California

Works

HTTP

HTTP – California_SSLOUT

Does not work (cross scheme image request)

HTTP

HTTPS – California

Works

HTTP

HTTPS – California_SSLOUT

Does not work (cross scheme image request)

HTTPS

HTTP – California

Does not work (cross scheme image request)

HTTPS

HTTP – California_SSLOUT

Works (prompt to display mixed content – http service request)

HTTPS

HTTPS – California

Does not work (cross scheme image request)

HTTPS

HTTPS – California_SSLOUT

Works

For tiled (cached) map services, the ArcGIS Silverlight API constructs requests for tiles. The scheme in the tile requests is defined by the scheme in the Url for the ArcGISTiledMapServiceLayer.  In this case the service cannot be modified to return a url with a specific scheme, thus the REST endpoint must be available via a scheme that matches the host application.  This limitation is frequently encountered when working with “free” ArcGIS Online tiled map services.   If you need to host your ArcGIS Silverlight API application over https, thus far you’ve been unable to access ArcGIS Online free services directly because they were not available over https.  This will change – very soon ArcGIS Online will provide access to “free” services over https.

Update: In version 1.1 of the API, ArcGIS dynamic map services were changed to use use mime data instead of URLs for map image output.   The request for mime output does not depend on the output directory set on the server, so creating an explicit output folder for http and https access is unnecessary.  The scheme defined for the layer URL will match the export image request that returns mime data.   Basically, when working with images (map or otherwise) in Silverlight, the scheme of the hosted application should match the scheme of the URL to image data.

Rex Hansen
ESRI Product Engineer
ArcGIS Server .NET, Silverlight/WPF, MapIt

Posted in Web | Tagged , , | 1 Comment

Troubleshooting blank layers

The Silverlight API is a client application dependent on services that might fail or be unavailable. In general, the Map control will not throw an exception if one service fails. This means that if one service is down, the application will continue to run and access the remaining layers. However, it also means that you don’t automatically get any error reporting, and if all your services fail, you will just end up with a blank map.

When looking at the ESRI Silverlight forums, several users have reported issues with getting their layers to display or encountering blank maps. This blog post tries to outline the common reasons why a service does not display in the map, and how to remedy it.

Most of the problems with a missing layer are related to browser security issues, so to trouble shoot client applications, there is one tool that I will recommend anyone to get familiar with: Fiddler. This is a free HTTP tracing tool that tracks the requests and responses between the browser and the web server, and logs if any requests failed. You can download this tool from http://www.fiddler2.com/.

Once installed and launched, any network traffic initiated by Internet Explorer will be tracked by Fiddler. Note that any traffic to http://localhost/ is not tracked by default, and requires extra configuration. Other browsers might also require explicit configuration to work with Fiddler. I will be using Fiddler several times in this blogpost to track down issues you may experience.

Cross-domain limitations

Let’s for sake of argument say you installed ArcGIS Server on your own server, and you are trying to use one of its services. You first navigate to a service at your REST endpoint and copy/paste the URL to a layer in your Silverlight application, but the layer never shows. Let’s see what happens in Fiddler:

We see two requests. Both are highlighted in red, meaning an error occurred, and from the result code “404″ we can see that none of these files were found. So what are these files it tries to get from the root of my map server?

Silverlight does not allow you to make requests to servers hosted on a different host than your web application. In this case I was running my application on http://localhost/, and trying to access a service on http://myserver/.  Silverlight will first verify that the server allows other applications to perform requests against that server. It does this by trying to download a clientaccesspolicy.xml file from the domain. This is a Silverlight access configuration file telling the client who can call the server and which parts it can request. Flex has a similar pattern in place using a crossdomain.xml file. If it fails to find the clientaccesspolicy.xml file, Silverlight will try and search for Flex’s crossdomain.xml file and use that instead. If both fail, services on the server will be unavailable, the layer will not be able to initialize itself, and it will not render in the map. You can read more about this Silverlight security feature here, and you can look at the policy file that ArcGIS Online uses here.  If your services and application are hosted on the same domain AND port number, this should not be an issue. It’s worth noting the port constraint, since Visual Studio by default will run the autogenerated test-website on a different port on localhost than your ArcGIS REST services (not to mention that you shouldn’t be using localhost in the Url for layer since ‘localhost’ would mean something different to each client hitting your Silverlight application).

The cross domain restriction is by far the number one reason for a service failing to load.

Cross-scheme access

Another security feature in Silverlight is one preventing you from requesting data across different schemes, like http:// https:// and file://.  If you try to run a Silverlight application with a Map control using file:// in your browser URL, you might see this in your Map control:

 

This is because the Map control will need to be in an application hosted on a http or https website, but you are running the application from the file system (scheme).  To resolve this, make sure your Silverlight application hosted by a Web server and make sure the Web site is set as the startup project in Visual Studio. When you start a new Silverlight application project in Visual Studio, make sure you say yes to create a Web site to host your application in:

Restrictions also apply to http and https schemes. If your application is hosted on https and your service is hosted on http, or visa versa, you can access the service if explicitly permitted by the clientaccesspolicy.xml.  You cannot access images across schemes though, so while a request for service metadata may succeed, the request for a dynamic map image or cache tile will fail.   An upcoming blog post will cover this scenario in more detail. 

Hopefully at this point you’re able to see your map, and Fiddler will show something like:

In sequence you will see one successful request for clientaccesspolicy.xml, then a request for the service metadata, next a request for an image url, and lastly for the actual map image that was generated. Depending on which service type you are using, the last two requests may be different. The above example is using a dynamic map service.

Service output folder configuration

If you still have problems displaying the map service and you see something like below in Fiddler, you might have a configuration issue on your server end:

Notice how it requests images from “myserver”, but in the end tries to request an image from “localhost” (or some other domain/website unknown to the Silverlight client). If this is the case, the output folder of your ArcGIS Server service is not set up correctly.  Use Manager or Catalog to define the appropriate url to the output folder

Unsupported file format (GIF)

Silverlight does not support the GIF image format. If you are using a tiled service that has the tiles cached as GIF, it will not work with the ArcGIS Silverlight API.

Tiled layers – Wrong spatial reference

ArcGISTiledMapServiceLayer does not support re-projecting on the fly. This means that if you try and mix cached layers of different spatial reference, one or more services will not display. The first layer in the ESRI Map Control that has a spatial reference set will define the spatial reference for the entire map*, and any cached layer not matching this spatial reference will not load. You will see a request for the service metadata in Fiddler, but no requests for any image tiles. 

*Note that you can override the default map projection by explicitly setting a startup extent with a spatial reference set. See the sample in the Interactive SDK. 

Handling service errors

In the beginning I mentioned that some servers might be unavailable, and the Map control will continue to run with the remaining layers. If this is the case, you may want to let the user know that there was a problem loading a layer.  All layers have an “InitializationFailed” event you can subscribe to, that will fire in the event of a layer failing to initialize. You can also use this yourself to track down any initialization issues as well.
To listen for it, add the highlighted code to your layer definition:

<esri:ArcGISDynamicMapServiceLayer
 ID="StreetMap"
 InitializationFailed="layer_InitializationFailed"
 Url="http://myserver/ArcGIS/rest/services/StreetMapUSA/MapServer" />

In the event handler you can check the InitializationFailure exception to determine what the problem was, and display a message to the user. Example:

private void layer_InitializationFailed(object sender, EventArgs e)
{
  Layer layer = sender as Layer;
  MessageBox.Show(string.Format("Layer '{0}' is currently unavailable. Error: {1}",
     layer.ID, layer.InitializationFailure.Message));
}

Morten Nielsen
Senior Software Engineer
ArcGIS Server.NET, Silverlight/WPF, MapIt

Posted in Web | Tagged , , , | 10 Comments