Got Maps? - 30 Oct 2009
Develop a Map-centric Web Application in Silverlight
October 30, 2009
INETA Community Voices
LANGUAGES: C#
ASP.NETVERSIONS: 3.5
Got Maps?
Develop a Map-centric Web Application in Silverlight
By Al Pascual
One routine task for developers is the creation of reportsto analyze data. One of the shortcomings of this task, however, is theinability to analyze the information in different formats to fulfill differentrequirements. The analysis of variable data requires that some information bepresented to the user within a formatted table or graphical chart. Ideally,data should be visualized or represented in a geographic context so it can enablequeries and be manipulated. There is huge value in visualizing tabular data ona map. For example, population data of the UScould be viewed in a table or chart, but the data could be overwhelming. Maps,on the other hand, allow the user to examine the geographic context of the dataand easily identify trends.
The best way to present the user with a huge amount ofinformation is in a geographic context. To achieve these results, one needsMicrosoft technologies ASP.NET, the .NET 3.5 Framework, Silverlight, and JavaScript combined with ArcGIS Server 9.3and the REST API for visualization of the data.
To demonstrate how to build a map viewer in Silverlight,this article will use the MultiScaleImage provided with Silverlight 2 beta2.0.30523. With the Silverlight map viewer, users will be able to create a Webpage and add their local georeferenced data or live data, creating a GeoRSSfeed using GeoTwitter. This guide will also help users extend the control usingC# and interact with the Silverlight map viewer control using JavaScript.
What Is Geographic Information System Technology?
Geographic Information System (GIS) technology allows usto view, understand, question, interpret, and visualize data in many ways thatreveal relationships, patterns, and trends in the form of maps, globes,reports, and charts. GIS helps answer questions and solve problems by lookingat data in a way that is quickly understood and easily shared.
Configuring Solutions through Mashups
In the past few years, mashup has become a word familiarto most developers (and to a lesser extent, the general public). A mashup isthe keystone to service-oriented architecture, allowing developers to publishcontent using one or more remote service. The mashup became famous when Googlepublished its online mapping API. Overnight, anyone with a glimmer ofJavaScript talent could publish their data on a map on their Web site in just afew lines of code. In the past few years, APIs have been used to displayinformation on top of free visualization map applications, such as VirtualEarth and Google Maps.
Introduction to the ESRI ArcGIS Server REST API
Because I am most familiar with ArcGIS Server, this iswhat I used for this article. However, other products are available, such asGoogle Maps (http://maps.google.com) andMicrosoft Virtual Earth (http://maps.live.com).
ArcGIS Server 9.3 comes with a .NET API for ASP.NET andthe flexible REST API that allows you to develop your own viewer; this articlewill use the latest build of the Silverlight map viewer. The Silverlight mapviewer and JavaScript API take advantage of the REST API in ArcGIS Server toconsume and query map data. The REST API is stateless and does not storetransaction requests. To retrieve information you can use the ArcGIS online at http://sampleserver1.arcgisonline.com/ArcGIS/rest/services.
ArcGIS Online is a cluster of servers, available on theInternet, with cached base maps and resources. You can see the resources inHTML, JSON, or KML. Sampleserver1.arcgisonline will be used for all the samplesin this guide.
The world map extent will be referenced often throughoutthis guide to describe the four points that describe the four corners of themap, using the most common latitude/longitude measurement for maps. For helpand documentation of the REST API for ArcGIS Server 9.3, visit http://resources.esri.com/help/9.3/arcgisserver/apis/rest/index.html.
Share Geographic Information Using GeoRSS
There are many formats to publish map services, most ofwhich are image-based to provide a geographic base map. Because sharinginformation is easier because of expanded bandwidth and storage, many newformats allow users to share geographic information that can be displayed inany base map. There is a downside to these new formats: being file-based, usersmust fetch the file again to update the map. RSS feeds eliminate this hassle. GeoRSS(Geographic Really Simple Syndication) provides access to an up-to-date sourceof geographically tagged information.
With RSS, users subscribe to a feed, which is time sorted,and the client or aggregator is the one requesting the updated feed using thelast one fetched. RSS is a format of XML. GeoRSS works the same way, extendingthe format to include a point or collection of points to generate a polyline orpolygon. There are different formats of GeoRSS; this article will onlyreference Simple GeoRSS.
Subscribing to a GeoRSS feed means receiving to yourapplication the updated geometry in real time and creating a layer of thatgeometry that any map viewer supporting GeoRSS can consume in real time. Forexample, you can keep track of the changes in a hurricane or car accidents inreal time. Figure 1 demonstrates how to generate a GeoRSS feed and consume itin Google Maps. There are a few GeoRSS editors online for you to quickly sketchyour geographic information. I recommend using an editor like the GeoTwitterEditor (see Figure 2; http://geotwitter.net/editor.aspx).
Figure 1: Generate a GeoRSS feed andconsume it in Google Maps.
Figure 2: The GeoTwitter Editor.
Although there are many ways to parse RSS in .NET, in thisarticle we ll parse GeoRSS. To convert it into points, this guide will parseGeoRSS in C#, VB.NET, and JavaScript. Any of these will be useful to add thepoints to the Silverlight map viewer.
The Silverlight Map Viewer Using MultiScaleImage
Richie Carmichael has posted a Silverlight 2.0 beta 2viewer proof of concept; download the source code from http://resources.esri.com/arcgisserver/adf/dotnet/index.cfm?fa=codeGalleryDetails&scriptID=15746.
The control was built using the MultiScaleImage control,also known as DeepZoom. The control uses the REST API to construct URLs toindividual map image tiles and pass them to the MultiScaleImage control.DeepZoom provides panning, zoom in, and zoom out capability, as well as the spring animation feature, an animation effect that makes the DeepZoom controlappealing to most users. In the first version of the MultiScaleImage, developershad to create a zoomable image using the DeepZoom Composer. With the release ofSilverlight 2.0 beta 2, developers can create a provider to consume any otherimages not prepared by the DeepZoom Composer.
In these exercises, we are going to consume GeoRSS and addit as points to the Silverlight map control. The sample will also contain alocation service.
We then can add the control to an HTML page or .NET Web Form,as Silverlight downloads into your browser from the server as a browser plug-in(see Figure 3). In Listing One, the control is addedto an HTML page and provides a map of the world, with pan and zoom in/out,attaching to the mouse events in the browser. You can download the code from http://alpascual.com/files/folders/2776/download.aspx.
Figure 3: The Silverlight map viewercontrol on an HTML page.
With the code from Listing One, it is very easy to add thecontrol to an HTML page. The object for Silverlight is simply dropped into thepage, and, if installed in your browser, the plug-in will be initialized; ifnot, you ll be asked to download the Silverlight plug-in from Microsoft beforerunning the xap file. (Adding the Silverlight map viewer to your Web page isvery straightforward. If Silverlight is not installed on the target computer, theuser will be prompted to install Silverlight from the Microsoft Web site.)
If we look into the Silverlight control, we can see howMultiScaleImage requests that the different map images tile upon the mouseevents using the ESRI REST API. Keeping track of the map extent is done in theSilverlight control rather than the server; the REST API, as previously discussed,is stateless and does not know the last extent of the map. The Silverlight mapcontrol includes only the MultiScaleImage control, which provides the springingeffect when panning out of the box, as well as a smooth transition betweentiles. To do a mashup of two or more map services, you can use multipleMultiScaleImage controls with different transparencies in the image. All themaps must use the same projection.
In the code-behind we attached all the mouse events toretrieve the correct image from the ArcGIS server 9.3 using the REST API. Thecontrol will take care of the image transitions, as well as the zoom in/outanimation. The server returns JSON strings that are being desterilized to C#classes with all the image attributes and properties. To demonstrate thistechnique, the code in Figure 4 shows how to create an instance of the control.
MultiScaleImage msi = this.map.Children[0] as MultiScaleImage;
if (msi == null) {return; }
MapTileSource mts =msi.Source as MapTileSource;
if (mts == null) {return; }
ArcGISMapServiceLayerlayer = (ArcGISMapServiceLayer)mts.Layer;
double widthDegrees =layer.FullExtent.XMax - layer.FullExtent.XMin;
Point origin = newPoint();
origin.X = (value.XMin -layer.FullExtent.XMin) / widthDegrees;
origin.Y = (value.YMax -layer.FullExtent.YMax) / widthDegrees;
double width =(value.XMax - value.XMin) / widthDegrees;
msi.ViewportWidth = width;
msi.ViewportOrigin =origin;
Figure 4: Createan instance of the MultiScaleImage control.
Silverlight Communication with ASP.NET and JavaScript
A key advantage of using the Silverlight control insteadof a map div to display a map is that the plug-in does all the work with theimage. This means the image will be rendered and processed faster using theBackground worker thread from the Silverlight plug-in. It also means you canstore images in the user s hard disk for later use, or to avoid a round tripwith Silverlight isolated storage to the server to download it again.
Now that you ve included the Silverlight control in an HTMLpage, we re going to use the ArcGIS 9.3 JavaScript API as the SDK forSilverlight. We ll create a page to geocode any point from the Silverlightcontrol, as well as use GeoRSS to place information in the Silverlight map.
JavaScript can easily communicate with Silverlight byusing the method attribute (ScriptableMember) on the method you want to exposeto JavaScript from inside Silverlight 2.0 beta 2. That wasn t true in otherprevious versions of Silverlight. Normally, C# methods start with a uppercaseletter, and JavaScript methods begin with a lowercase letter. You can use theproper ScriptAlias to expose the method for each platform to correct this case:
[ScriptableMember(ScriptAlias="graphics")]
public Graphics Graphics
{
get { return _graphics; }
set{ _graphics = value; }
}
The sample shown in Listing Two willuse the GeoRSS parser to add points to the map. Using the JavaScript GeoRSSparser with a timer, the map viewer can always be up to date with the liveGeoRSS feed and convert them to geometry points in Silverlight. You candownload the complete code from http://alpascual.com/files/folders/2776/download.aspx.
The JavaScript to parse GeoRSS is shown in Listing Three; the complete file can be downloadedfrom http://alpascual.com/files/folders/2776/download.aspx.
To use the JavaScript API from ArcGIS online, you need onlyto include the script, as shown in the sample below (the script will be downloadedto the browser from ArcGIS Online; the examples all use the JavaScript APIversion 1.1):
The JavaScript API is very robust and helps you develop amap viewer and execute tasks faster. The results are ideal for displaying ahefty amount of information in a simple geographic environment. Tool tipsdisplay the information of the item added into the map, making it veryintuitive. GeoRSS information can be formatted in HTML and style sheets can becreated to make a better presentation layer when the mouse is moved over theitem. A Deep Look into the Silverlight Map Control You should download the code from the resource page at http://resources.esri.com (RichieCarmichael did a great job commenting the code). The Map.xaml contains theSilverlight UserControl where the MultiScaleImage would be created. The code inFigure 5 shows you the Silverlight UserControl containing the MultiScaleImageand the canvas to display the map. x:Class="ESRI.PrototypeLab.Silverlight.Map" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > Grid.Row="0"Grid.RowSpan="2" /> Grid.Row="0"Grid.RowSpan="2" /> Grid.Column="1" Grid.Row="1" Margin="10,10,10,10" Background="Black" CornerRadius="20" Opacity="0.5"> HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Trebuchet MS" Foreground="White" /> Figure 5: Use the SilverlightUserControl with the MultiScaleImage and a canvas. Looking at the code-behind, the control needs to hook toall mouse events needed for panning and zooming. You need to set the mapextent, the part of the map you want to see, and the service to use. TheWebClient class communicates with the server using JSON. You can download the completecode from http://resources.esri.com/arcgisserver/adf/dotnet/index.cfm?fa=codeGalleryDetails&scriptID=15746.Use the code shown in Figure 6 to initialize the events on the MultiScaleImage. public Map() { InitializeComponent(); // Clear Items this.grid.Height =double.NaN; this.grid.Width =double.NaN; // this.coordinates.Text =Map.BLANK_COORDINATE; // Handle MapInteraction this.map.MouseLeftButtonDown += new MouseButtonEventHandler(this.Map_MouseLeftButtonDown); this.map.MouseLeftButtonUp += new MouseButtonEventHandler(this.Map_MouseLeftButtonUp); this.map.MouseMove +=new MouseEventHandler(this.Map_MouseMove); this.map.MouseLeave +=new MouseEventHandler(this.Map_MouseLeave); this.map.ViewportChanged += new RoutedEventHandler(Map_ViewportChanged); // Handle Browser Event MouseWheelHelper mouseWheelHelper= new MouseWheelHelper(this.map); mouseWheelHelper.Moved+= new EventHandler (this.MouseWheelHelper_Moved); // this.Loaded += newRoutedEventHandler(this.Map_Loaded); } Figure 6: The Silverlight controlconstructor used to initialize all events in the map. Use Geolocating to Display Results Geolocating is the term used when you provide an addressand the geolocator finds the coordinates that correspond to that address. Conversely,reverse geolocating is when the user provides a point (latitude and longitude)and the geolocator finds the address. In the sample shown in Figure 7, we re going to add abutton to the HTML page. Right-click the mouse to grab the clicked point of themap to find the address of those coordinates. As discussed previously, we lluse the ArcGIS Server 9.3 JavaScript API to construct the query to the serverand return the results from the point in the Silverlight control that will bedisplayed in a JavaScript alert box to show the bidirectional communicationbuilt in to Silverlight 2.0 beta 2. function ReveseGeocoder() { map.OnClick ="LocationOnClick"; var infoTemplate = newesri.InfoTemplate("Address", "Street: ${Address} City: ${City} State:${State} Zip: ${Zip}"); var symbol = newesri.symbol.SimpleMarkerSymbol( esri.symbol.SimpleMarkerSymbol.STYLE_CIRCLE,15, newesri.symbol.SimpleLineSymbol(esri.symbol. SimpleLineSymbol.STYLE_SOLID,new dojo.Color([0, 0, 255]), 2), new dojo.Color([0,0, 255])); dojo.connect(locator,"onLocationToAddressComplete", function(candidate) { if(candidate.address) { vargraphic = new esri.Graphic(candidate.location, symbol, candidate.address, infoTemplate); map.graphics.add(graphic); alert(graphic.getTitle() +graphic.getContent()); //Create a infoWindow class map.infoWindow.setTitle(graphic.getTitle()); map.infoWindow.setContent(graphic.getContent()); var screenPnt =map.toScreen(candidate.location); map.infoWindow.show(screenPnt, map.getInfoWindowAnchor(screenPnt)); } }); } Figure 7: Reversegeocoding an address. The JavaScript locator method will use the REST API to processthe point into the server; you can manually browse the REST API using the endpoint to test the locator. You can download the rest of the code from http://alpascual.com/files/folders/2776/download.aspx. Conclusion This article presents a simple way to add rich maps toyour application and provide your users with a fast and rich interface tovisualize your data in a geographic context. With a few simple lines, you canadd to any Web page the Silverlight map with a few base maps from the ArcGISonline server and add dynamic data using other map services or GeoRSS forstreaming live data to your Silverlight control. Even while having an extended JavaScript API, as well as a.NET API that you can use out of the box with ArcGIS Server 9.3, if you wouldadditionally like to replace your map for a Silverlight map, you can go to theresources page and download the code (http://resources.esri.com)or only the control to add it to your pages. There are many resources online toextend the Silverlight control to include many animations, events, and effectson the map control. If you want to start using the Silverlight controlimmediately, I recommend you begin by downloading the code and compiling it onyour computer. Source code accompanyingthis article is available for download. Al Pascual is asenior software engineer at ESRI headquarters, located in Redlands, CA, wherehe works in the Professional Services division. Al s latest personal hack,GeoTwitter, makes extensive use of the advantage of visualizing Twitter on amap. Al is a Microsoft ASP.NET MVP. Keep up with Al by visiting his blog at http://silverlightme.netor send an e-mail to mailto:[email protected]. References GeoTwitter: http://geotwitter.net GeoRSS: http://georss.org Google Maps: http://maps.google.com ESRI: http://esri.com Resource Page: http://resources.esri.com Richie Carmichael s blog: http://mrrichie.spaces.live.com ArcGIS online REST API information: http://resources.esri.com/help/9.3/arcgisserver/apis/rest/index.html Al Pascual s blog: http://silverlightme.net Silverlight Forums and Resources: http://silverlight.net Begin Listing One Adding theSilverlight control to an HTML page End Listing One Begin Listing Two Add a GeoRSS feedto your Silverlight map dojo.require("esri.map"); dojo.require("esri.tasks.query"); dojo.require("esri.tasks.gp"); var map; functiononSilverlightLoad(sender, args) { map =document.getElementById( "silverlightMap").Content.Map; //map.onMouseDown =onMouseDown; //map.onLayerAdded =onLayerAdded; var layer =document.getElementById( "silverlightMap").Content.services.createObject( "ArcGISTiledMapServiceLayer"); layer.url ="http://services.arcgisonline.com/ArcGIS/ rest/services/ESRI_StreetMap_World_2D/MapServer"; map.addLayer(layer); } functionMapMouseMove(sender, args) { var label =document.getElementById("label1"); // Display MouseCoordinates label.innerHTML =args.X + " " + args.Y; } function AddFeed() { var geoRssFeed = document.getElementById("georssname"); var georss = newGeoRss(map); georss.AddGeoRSS(geoRssFeed.value); } End Listing Two Begin Listing Three A JavaScriptGeoRSS parserfunction GeoRSSItem(itemtags) { this.feedType = 1; this.lat=""; this.lon=""; this.fType="point"; // by default the feature //type is point this.coords=""; this.title=""; this.description=""; this.pubDate=""; this.link=""; this.id=""; for ( var j = 0; j var v=""; if(itemtags[j].firstChild) v=itemtags[j].firstChild.data; var tag =itemtags[j].nodeName; if (tag == 'geo:lat'){ this.lat = v; } else if (tag =='geo:long') { this.lon = v; } else if (tag =='georss:point') { varptArr=v.split(" "); this.lat =ptArr[0]; this.lon =ptArr[1]; } else if (tag =='gml:pos') { var ptArr=v.split(""); this.lat =ptArr[0]; this.lon =ptArr[1]; } else if (tag =='georss:line') { this.coords=v; this.fType="line" } else if (tag =='georss:polygon') { this.coords=v; this.fType="polygon" } else if (tag =='title') { this.title=(v==null? "":v); } else if (tag =='description') { if (v!=null) { if(v.lastIndexOf(".")>-1) { varfExt=v.substr(v.lastIndexOf( ".")+1).toLowerCase(); if(fExt=="gif" || fExt=="jpg" || fExt=="png") { v=""; } } } else { v=""; } this.description=v; } else if (tag == 'link') { if (v!=null) { if(v.lastIndexOf(".")>-1) { varfExt=v.substr(v.lastIndexOf( ".")+1).toLowerCase(); if(fExt=="gif" || fExt=="jpg" || fExt=="png") { v=""; } } } else { v=""; } this.link=v; } else if (tag =='pubDate') { this.pubDate=(v==null? "":v); } else if (tag =='ems:sym') { if(itemtags[j].firstChild.firstChild) v=itemtags[j].firstChild.firstChild.data; if (v!=null) { this.sym=v.substring(0,v.indexOf(".")); } } else if (tag =='guid') { if(v.indexOf(":")>0) { this.id=v.substring(v.indexOf(":")+1); } else { this.id=v; } } } //end for}End Listing Three
About the Author
You May Also Like