Creating a Basemap Switcher – Part 1

The latest release of the ArcGIS Runtime SDK for WPF and Windows Phone and ArcGIS API for Silverlight includes support for the ArcGIS Portal REST API.   Using the Portal library (ESRI.ArcGIS.Client.Portal.dll) you can easily search, browse, and open web maps available on ArcGIS Online or your own portal.

Each web map has a basemap, which contains one or more layers.  A basemap is often displayed below other layers to provide a spatial context for operational data.

Unlike a web map, the Map control in the SDK\API does not have a basemap. While the first layer in the layer collection may be considered a basemap, but the Map control by itself does not technically define basemap layers.  However, when creating a Map from a web map, the Portal API’s Document class provides an attached property IsBaseMapProperty to tag layers within a Map as basemap layers.  Using the Document.GetIsBaseMap() method to check this attached property you can determine which layers are basemap layers.

The following code downloads a web map from ArcGIS Online, creates a Map control and iterates through the layers to find basemap layers:

using ESRI.ArcGIS.Client.WebMap; 

[...] 
Document doc = new Document(); 
doc.GetMapCompleted += (s, e) => 
{ 
	var map = e.Map; 
	foreach (var layer in map.Layers) 
	{ 
		if(Document.GetIsBaseMap(layer)) 
		{ //This layer is a basemap } 
	} 
}; 
doc.GetMapAsync("a847f1f4cfa34cb487f6b17f151b9362");
Note: The code above only works for layers created from a web map.

Once basemap layers have been determined, you can change them to provide a different perspective.  The remainder of this post will demonstrate how to find and present basemaps in an application and enable a user to select and switch a basemap in a Map control.   Where can you find basemaps?   ArcGIS Online and other portals can define a gallery of basemaps for use by client applications.  The following code returns the first 20 basemaps in a gallery hosted by ArcGIS Online and binds the result to a listbox.   Each basemap is really just a web map with basemap layers.

ArcGISPortal portal = new ArcGISPortal(); 
portal.InitializeAsync("http://www.arcgis.com/sharing/rest", (s, e) => 
{ 
    	if(portal.ArcGISPortalInfo != null) 
	{ 
		SearchParameters parameters = new SearchParameters() { Limit = 20 }; 
		portal.ArcGISPortalInfo.SearchBasemapGalleryAsync(parameters, (result, error) => 
		{ 
			if (error == null && result != null) 
			{ basemapList.ItemsSource = result.Results;} 
		}); 
	} 
});

SearchBasemapGalleryAsync returns a list of ArcGISPortalItems, one for each basemap (web map).   Display the list in a listbox with thumbnails and a title using the following XAML template:

<ItemsControl x:Name="basemapList"> 
 <ItemsControl.ItemTemplate> 
  <DataTemplate> 
   <Button> 
    <StackPanel> 
     <Image Source="{Binding ThumbnailUri}" Margin="3" Width="100" Height="67" /> 
     <TextBlock Text="{Binding Title}" /> 
    </StackPanel> 
   </Button> 
  </DataTemplate> 
 </ItemsControl.ItemTemplate> 
</ItemsControl>

basemap switcher
Now that the basemaps are displayed, we need to add a click-handler to each button.  On click we replace the basemap layers in the current Map control with layers from the basemap clicked.

First, get the ArcGISPortalItem bound to the button clicked, and get the web map associated with it:

private void BaseMapButton_Click(object sender, RoutedEventArgs e) 
{ 
	var item = (sender as Button).DataContext as ArcGISPortalItem; 
	ESRI.ArcGIS.Client.WebMap.WebMap.FromPortalItemAsync(item, 
        (webmap, err) => 
	{ 
		var basemaps = webmap.BaseMap; 
		//Create a map instance from the webmap 
		Document doc = new Document(); 
		doc.GetMapCompleted += (s, result) => { } 
		doc.GetMapAsync(webmap); 
	}); 
}

To finish, we’ll return all the basemap layers from the chosen web map and add them to the existing Map. One nuance in this process is that when adding new layers, some layers in the basemap are “Reference Layers” which go on top of all other layers in the Map (e.g. label layers):

var newBaseLayers = result.Map.Layers.Where(l => Document.GetIsBaseMap(l)).ToList(); 
result.Map.Layers.Clear(); 
int idx = 0; 
foreach (var layer in newBaseLayers) 
{ 
	// Check if layer is a reference layer 
	var data = layer.GetValue(Document.WebMapDataProperty) as IDictionary; 
	if (!data.ContainsKey("isReference")) 
		map.Layers.Insert(idx++, layer); 
	else { 
		// Reference layers go on top 
		map.Layers.Add(layer); 
	} 
}

Use the links below to view this sample application in action and download the source code. The sample application includes an operational layer that contains United States federal land holdings. Switching basemaps enables a user to quickly visualize relationships between federal ownership, human infrastructure, and natural features.

View the live app
Download the source

In the next blogpost we will package this code into  a reusable custom control that you can quickly add and restyle for any application.

Morten Nielsen
Senior Software Engineer

This entry was posted in ArcGIS Online, Developer and tagged , , , . Bookmark the permalink.

Leave a Reply

One Comment

  1. seabrooke says:

    Is there a second blogpost with the reusable custom control?