Tag Archives: .NET

Hyperlinking in task results

At the User Conference, some of you asked how to include hyperlinks in your task results. This is useful if your features have corresponding web sites. You want users to be able to click directly on a hyperlink from a field in the results panel instead of typing the URL in a browser.

There are two ways to approach hyperlinking. If you don’t want to add any custom code to your application, you can add a field to the feature class that stores the complete HTML for the hyperlink. You should include the tag, attributes, and text for the hyperlink, like this:

<a href=http://www.esri.com target=_blank>ESRI</a>

This is how it would look in the ArcMap table view:

Hyperlink HTML in ArcMap table view 

This will create the following result in the ADF for a task result. You don’t have to do anything extra to create the link; the HTML gets interpreted automatically:

Task results with link to ESRI website

Clicking this link will open http://www.esri.com.

The second way to create a hyperlink in the task results is to construct the hyperlink programmatically with existing attribute content. The example below shows one way to do this with the Query Attributes task. You’ll need to subclass the out-of-the-box query task and override the ExecuteTask method to get access to the result dataset from the query (available via the Results property).

   public class CustomQueryTask: QueryAttributesTask

    {

        public override void ExecuteTask()

        {

            base.ExecuteTask();

            if (Results is System.Data.DataSet)

            {

                System.Data.DataSet dataset = (System.Data.DataSet)Results;

                System.Data.DataTable datatable = dataset.Tables[0];

                foreach (System.Data.DataRow datarow in datatable.Rows)

                {

                    string val = (string)datarow["CNTRY_NAME"];

                    string updatedval = "<a target='_blank' href='http://www.google.com/search?q=" + val + "' >" + val + "</a>";

                    datarow["CNTRY_NAME"] = updatedval;

                }

            }

        }

    }

The results of the above code might appear like this in the TaskResults control at runtime:

Task results with links to countries

For an additional example of hyperlinking in task results, see Tom Brenneman’s sample on ArcScripts.

-Sterling Quinn, with Derek Weatherbe and Rex Hansen

Posted in Services | Tagged , , , | 5 Comments

GeoRSS feed with map tips in the Web ADF

In this post, guest author Sam Berg of ESRI Boston explains how to link map tips to a GeoRSS feed using the Web ADF:

The ArcGIS Server 9.2 Web ADF provides a very useful capability in its support of web-tier graphics resources. Because the underlying map services in an application are not therefore required to draw all content, the web application instead can not only add dynamic data directly, but also more efficiently manage this custom content of the web map. The ArcGIS Server framework will take care of rendering all the data- we just need to customize the application to load into the web-tier graphics resources the content that our users will be interested in seeing.

When you combine adding data to your map at run time along with utilizing the out of the box MapTips web control, there is a real opportunity to build some easy-to-use web maps, complete with pop-up map tips for the added content.

Perhaps one of the most natural use cases for this kind of approach would be to plot GeoRSS feeds from a published resource on the web. GeoRSS feeds contain what you might expect: XML content encoded with associated geographic coordinates. There are a variety of sources for this content, and one great example is one from the USGS Earthquake Center that contains the location and magnitude information for worldwide earthquake activity over the past 24 hours (http://earthquake.usgs.gov/eqcenter/recenteqsww/catalogs/eqs1day-M2.5.xml).

There are some good GeoRSS feeds out there, but what about all the RSS news feeds that you are currently viewing with your favorite online RSS aggregator? Actually, there are a few web services out there that will read a designated RSS feed and return for you a GeoRSS version with <geo:lat> and <geo:lon> tags. Nice!

The web service I use in this example is from GeoNames.org. Their web service, described here, simply takes a “feedURL” parameter and returns for you a version of the XML with coordinates for the matches it finds.

For example, sending the BBC News World Edition Front Page feed which includes an item about heavy rains in Australia:

<item>

<title>Heavy rains hit parched Australia</title>

<link>http://news.bbc.co.uk/go/rss/-/2/hi/asia-pacific/6670333.stm</link>

<description>Farmers get welcome relief from a six-year drought, as the heaviest rain for years soaks south-eastern Australia.</description>

</item>

…to the GeoNames.org web services by using this format:

http://ws.geonames.org/rssToGeoRSS?feedUrl=http://news.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml

…will result in a feed being returned which includes the updated item with GeoRSS point tags added:

<item>

<title>Heavy rains hit parched Australia</title>

<link>http://news.bbc.co.uk/go/rss/-/2/hi/asia-pacific/6670333.stm</link>

<description>Farmers get welcome relief from a six-year drought, as the heaviest rain for years soaks south-eastern Australia.</description>

<geo:lat xmlns:geo=”http://www.w3.org/2003/01/geo/wgs84_pos#”>-25.0</geo:lat>

<geo:long xmlns:geo=”http://www.w3.org/2003/01/geo/wgs84_pos#”>135.0</geo:long>

</item>

So I have now in this feed spatially referenced items, along with <TITLE>, <LINK>, and <DESCRIPTION> tags from which to generate map tips. Starting with a simple web map page, what I’ll do next is write some code to dynamically populate a graphics resource with “graphic feature elements” derived from the designated GeoRSS feed, and configure the map to generate map tips for the new data. Along the way I’ll also wire up a few AJAX callbacks to support the application communication.

First, I’ll set up a simple web map with a dropdown for a few GeoRSS feeds, a MapResourceManager configured with a basemap resource and a graphics resource, a Toolbar, and a MapTips control. Finally, I have also added a “BuddiedCallbackButton”. This control is based on Ryan Olson’s excellent CallbackButton post, and I added to the code to be able to pass a collection of values to the callback handler from associated buddy controls, allowing in this case the URL of the selected GeoRSS feed in the dropdown to be used.

Example application layout in Visual Studio

When the “Add” command is clicked, I’ll set up a graphics layer in the “GeoRSSFeed” graphics resource. As described in the online EDN documentation, there are two graphics layer types, ElementGraphicsLayers, and FeatureGraphicsLayers. I’ll choose to generate a FeatureGraphicsLayer since these are best suited for storing attributes with the elements, which I’ll need to configure the map tips with. Since I know the GeoRSS feeds in this example all contain <TITLE>, <LINK>, and <DESCRIPTION> tags, I’ll set up the graphics layer to also maintain these attributes.

Next, I’ll use the .NET System.XML assembly to read and parse the selected GeoRSS feed. For each item in the feed, I’ll try to find the <geo:lat> and <geo:lon> tags, as well as the <TITLE>, <LINK>, and <DESCRIPTION> tags and store these in a StringDictionary.

As long as a coordinate was found for the item, I’ll add a new graphic element to the graphics layer and populate its attributes from the StringDictionary that was populated from the feed items.

Here’s the main code block to read the GeoRSS feed, parse for coordinates and attributes, and populate a graphics layer from the results:

    //note that the application could expose different values for the URL and maptips format tag:

    string sURL = “http://earthquake.usgs.gov/eqcenter/recenteqsww/catalogs/eqs1day-M2.5.xml”;

    string sBodyFormat = “Earthquake:<BR>{DESCRIPTION}<BR> <a target=_new href= {LINK} > {LINK} </A> <BR> <img src=’http://earthquake.usgs.gov/te
mplate/images/header_graphic_usgsIdentifier_white.jpg’>”
;

    #region load RSS feed

    XmlDocument _doc = new XmlDocument();

    String UrlString;

    //Build RSS Request String

    UrlString = sURL;

    //Load RSS document

    _doc.Load(UrlString);

    //Navigator and Iterator used to parse RSS

    System.Xml.XPath.XPathNavigator nav = _doc.CreateNavigator();

    System.Xml.XPath.XPathNodeIterator nodeItrItems = nav.Select(“//item”);

    int iCount = 0;

    //Loop through each item witin RSS

    while (nodeItrItems.MoveNext())

    {

      StringDictionary pValues = new StringDictionary();

      System.Xml.XPath.XPathNodeIterator nodeItrChildren = nodeItrItems.Current.SelectChildren(System.Xml.XPath.XPathNodeType.Element);

      ESRI.ArcGIS.ADF.Web.Geometry.Point pIncident = new ESRI.ArcGIS.ADF.Web.Geometry.Point();

      pIncident.Z = 100;

      ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleMarkerSymbol pSymbol = new ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleMarkerSymbol();

      ESRI.ArcGIS.ADF.Web.Display.Symbol.TextMarkerSymbol pText = new ESRI.ArcGIS.ADF.Web.Display.Symbol.TextMarkerSymbol();

      System.Drawing.Color pColor = new System.Drawing.Color();

      pColor = System.Drawing.Color.Red;

      pSymbol.Type = ESRI.ArcGIS.ADF.Web.Display.Symbol.MarkerSymbolType.Star;

      pSymbol.Width = 20;

      pSymbol.Color = pColor;

      //Loop through each child node and extract information

      while (nodeItrChildren.MoveNext())

      {

        string str = nodeItrChildren.Current.Name;

       
if (str == “geo:lat” || str == “LATITUDE”)

        {

          pIncident.Y = Convert.ToDouble(nodeItrChildren.Current.InnerXml);

          pIncident.Z = 0;

        }

        else if (str == “geo:long” || str == “LONGITUDE”)

        {

          pIncident.X = Convert.ToDouble(nodeItrChildren.Current.InnerXml);

          pIncident.Z = 0;

        }

        else if (pFields.Contains(str.ToUpper()))

        {

          string s = nodeItrChildren.Current.InnerXml;

          s = HttpUtility.HtmlDecode(s);

          pValues.Add(str.ToUpper(), s);

        }

      }

      if ((pIncident.Z != 100))//just using Z = 100 as code for unlocated

      {

        DataRow row = glayer.Add(pIncident);

        for (int ii = 0; ii < pFields.Count; ii++)

        {

          row[pFields[ii].ToString()] = pValues[pFields[ii].ToString()].ToString();

        }

        iCount++;

      }

      if (iCount >= 100)//stop after loading 100 feed items

      {

        break;

      }

    }

    #endregion

At this point, I have a graphics layer populated with point elements derived from the specified GeoRSS feed. I need to now configure the MapTips control to use this layer:

 

 #region update MapTips

    MapTips1.Features = null;

    MapTips1.Layer = “MapResourceManager1::GeoRSSFeed::GeoRSS Results”;

    MapTips1.Map = this.Map1.ID;

    MapTips1.FeatureLimit = 100;

    MapTips1.HeaderFormatString = “{TITLE}”;

    MapTips1.BodyFormatString = sBodyFormat;

    MapTips1.Refresh();

    #endregion

A very useful property of the graphics layer is that we can ask it for its geographic bounding box. Since I want to automatically zoom the map to this extent, I can use this property instead of having to write code to loop through all the coordinates of the feed items which were added.

Finally, I’ll add the MapTips and Map controls’ CallbackResults collections to the CallbackButton. In these callback results collections will be a lot of instructions to the processCallbackResult ESRI javascript handler on how to generate MapTips for the graphics layer and what in the map needs updating.

Since the MapTips’ BodyFormat property will honor both fields from the associated layer, as well as standard HTML formatting tags, we can mix and match these to come up with some nicely formatted MapTip pop-up windows when a feed point is clicked.

switch (sURL)

    {

      //USGS Recent Earthquakes:

      case “http://earthquake.usgs.gov/eqcenter/recenteqsww/catalogs/eqs1day-M2.5.xml”:

        sBodyFormat = “Earthquake:<BR>{DESCRIPTION}<BR> <a target=_new href= {LINK} > {LINK} </A> <BR> <img src=’http://earthquake.usgs.gov/template/images/header_graphic_usgsIdentifier_white.jpg’>”;

        break;

      //Google News Headlines:

      case “http://ws.geonames.org/rssToGeoRSS?feedUrl=http://news.google.com/nwshp?output=rss”:

        sBodyFormat = “{DESCRIPTION}”;

        break;

      //BBC World News:

      case “http://ws.geonames.org/rssToGeoRSS?feedUrl=http://news.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml”:

        sBodyFormat = “{TITLE}:<BR><a target=_new href= {LINK} > {LINK} </A> <BR>”;

        break;

    }

News item from a map tips layer linked to a GeoRSS feed 

So that’s about it. Using web-tier graphics layers in the ArcGIS Server ADF can be a great way of adding dynamic data to your web map application, whether it be from existing XML-based internet feeds, or your own custom data generated by your application.

Posted in Services | Tagged , , , | 5 Comments

Determining layer IDs at runtime

In this post, Rex Hansen provides a tip for determining the ID for a given layer: 

The SearchAttibutesTask and QueryAttributesTask controls are designed to use a layer ID to define the layer within a resource on which a query will execute. When both tasks are configured at design-time the layer ID is stored in the definition of the query. The layer IDs are determined by the order of the layers in a resource (i.e. map service).

The layer order can change when existing layers are reordered or removed, or when new layers are added. In this situation, the layer IDs defined when you configured the SearchAttibutesTask and QueryAttributesTask become invalid. However, if the layer names will remain constant and unique you can discover the layer ID at runtime and update the query definition for both tasks. Although this pattern will add some overhead to each request, the layer ID will be dynamically updated without having to manually change and recompile the web application. The following code provides an example of this technique.

    protected void Page_Load(object sender, EventArgs e)

    {

        ESRI.ArcGIS.ADF.Web.DataSources.IGISFunctionality gisfunctionality = 

            Map1.GetFunctionality("MapResourceItem0");

 

        ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality mf = 

            (ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality)gisfunctionality;

 

        string[] layerids;

        string[] layernames;

        mf.GetLayers(out layerids, out layernames);

 

        for (int i = 0; i < layerids.Length; i++)

        {

            string layername = layernames[i];

            if (layername == "counties")

            {

                SearchAttributesTask1.SearchFields = 

                    string.Format("MapResourceManager1:::MapResourceItem0:::{0}:::NAME", layerids[i]);

                QueryAttributesTask1.PredefinedQuery.LayerID = layerids[i];

            }

        }

    }

Posted in Services | Tagged , , , | 1 Comment

User Conference 2007: Java and .NET Special Interest Groups (SIGs)

Special Interest Group meeting

Jay Theodore and Art Haddad contributed the following previews of the Java and .NET SIGs, respectively, at the ESRI User Conference. All attendees are welcome to attend the SIGs.

Java Developers SIG

Wednesday, June 20, 12:00PM – 1:00PM (Room 31 A/B)

Interested in Java and emerging technologies? You can hear Jon Ferraiolo discuss OpenAjax while enjoying a complementary lunch. Jon is a member of IBM’s Emerging Technology group and the lead for the OpenAjax initiative.

Here’s what you’ll experience at the SIG:

  • OpenAjax briefing by Jon Ferraiolo
  • Scott Morehouse (ESRI Director of Software Development) shares his vision for GIS and Web 2.0
  • ArcGIS Server 9.3 JavaScript and REST API demos by the development team
  • Audience participation in Q & A / panel discussion

.NET Developers SIG

Wednesday, June 20, 2007, 5:30 PM – 7:00 PM (Room 6F)

Calling all .NET developers! This is the place to be Wednesday evening for the third annual .NET Developers SIG. The agenda includes discussions about .NET and ESRI technologies, product giveaways, and food and drink served promptly after the SIG.

Here is what you can expect at the meeting:

  • Fred Aubry and Jeff Shaner from the ArcGIS Mobile Team will speak about Mobile technologies
  • Euan Cameron and team will discuss the ArcGIS Explorer SDK
  • General panel discussion / Q & A with ESRI leads

See you there!

Posted in Services | Tagged , , | Leave a comment

Passing URL parameters with the Web ADF

We received a question about whether you can pass parameters back and forth in a URL using the Web ADF (many folks have done this previously in their ArcIMS applications). The answer is yes, and there is a sample on the ESRI Developer Network (EDN) website that shows how to pass URL parameters in your Web mapping applications.

This sample was created by our team, so let us know if you have any questions about it.

Posted in Services | Tagged , , | 6 Comments

Print Task Sample

In this post, Bryan Baker comments on a new sample you can use to add a Print task to your Web ADF applications: 

A sample print task for the Web ADF for the Microsoft .NET Framework is now available at the ESRI Developer Network website. This sample allows users to do simple printing of a map and task results. ESRI plans to include printing in the upcoming 9.3 version, but this sample should get you started with printing from the Web ADF. The sample works in the Web ADFs for both ArcGIS Server and ArcIMS.

The sample task prints all resources and services in the Map control, including graphics layers and scalebar. It merges the resources into a single image that the browser displays in a new window. If the user has generated task results, such as with a query or identify, the user may also choose to print those results as a table below the map. The sample also allows entering a custom title. It does not currently support printing other items in the website, such as the table of contents or overview map.

In the website, the Map may be zoomed to any location, and other map properties may be set, such as turning layers on/off or displaying highlights from task results. If other tasks, such as a QueryAttributes task, have been added, the print task displays a list of results from those tasks to print. From the Print task dialog, the user may set a print title and task results to print (if any have been generated in the website). Clicking Print in the dialog pops up a new browser window with the title, map and task results (if any). The user may then print the page with their usual printing methods, to any available printer.

Some additional highlights:

  • The user may choose print size, resolution and scale. Resolution (e.g., 300 dpi) is accomplished via standard HTML in the browser.
  • The administrator can configure properties of the task, such as the default print page title, the maximum print size, whether to allow printing task results, and whether to print the scale bar on the map.

Installing the sample Print Task

You can install and use the sample task as-is, or customize it for your own use. To install the task you need to:

  1. Make sure the Web ADF is already installed, with Service Pack 1 or higher applied.
  2. Unzip the downloaded file.
  3. Register the task with the .NET global assembly cache.
  4. (Optional) Add the task to the Visual Studio toolbox.
  5. (Optional) Add the task to Manager.

Details for these steps are in the instructions at the EDN website, and also in the Readme.doc file accompanying the sample. The sample includes the source code for the task, which can be customized. (Note: If you downloaded the EDN sample before the date of this post, you should revisit the EDN site for updated instructions for adding the task to Manager.)

The instructions on EDN include the path to the ArcGIS Server Manager. To add the task to the ArcIMS Web Manager, follow the same instructions, except add the appropriate entry into the Tasks.xml file for the ArcIMS Web Manager, which by default is in C:InetpubwwwrootArcIMSManagerApplicationsApp_Data.

If you install a service pack for the Web ADF, you will need to reinstall the task into Manager. Uninstalling the task must be done manually; see the readme document for details.

Adding the sample Print Task to a Website

After you install the task, you can add it to a Web ADF web page, using either Manager or Visual Studio. In Manager, you should see the Print task in the list of available tasks when you create or edit a website. Add it to the list of tasks in the website, then configure it (you must click Configure for any task, even if you just keep the default settings). The configuration settings allows you to set properties such as the maximum allowed size and resolution and default title for the print page. When you save the website, the print task should appear in the menu of tasks in the website.

The Print task appears in the left menu of the Web mapping application

From Visual Studio, add the Print task like other tasks, usually into a TaskManager. Set the Print task's results container to a TaskResults control on the page, using the "smart tag" in the upper right of the task. You can set other properties in the Properties window of Visual Studio. You can set the task's Visible property to false if desired, and make sure a Menu or TreeView control is buddied to the TaskManager so the user sees a list of tasks to open.

The task currently requires that the web page include a Map control. If no Map control is present, an error will occur. The code could be customized to allow for a page with only tasks, such as a query task, with no map. Also, please note that the task will not work with ArcGIS Online services (currently in beta), since those services currently deliver maps only as cached tiles (this task requests new map images for printing).

This task does not use the layout of any ArcGIS Server service in the website. If you want to print using an MXD-based layout, you might want to check out Ismael Chivite's sample posted at ArcScripts that prints layouts.

We'd welcome your comments on this sample. The download page for the sample includes a user comments section. We'll also post periodic updates to the sample based on feedback.

You can try out the task yourself by printing a map from this example web application.

Posted in Services | Tagged , , , , | 12 Comments

Map rotation support in the Web ADF at SP 2

Rex Hansen wrote this thorough post about the new support for map rotation in version 9.2, Service Pack 2 of the Web ADF for the Microsoft® .NET Framework:

ArcGIS Server map services support the ability to serve a rotated data frame (map). In 9.2 SP 2, the .NET Web ADF has been enhanced to support map rotation. This involved a number of changes in many sections of the Web ADF. Each section is discussed below:

Map control

The Map control has a new Rotation property that can be used to specify, in degrees, the rotation of resources in the Map. A value of NaN means that the rotation of the PrimaryMapResource will be applied; 0 will unrotate rotated resources and leave other resources alone; values greater or less than 0 will rotate resources, throwing an exception if unrotatable resources are present. Map rotation is supported for ArcGIS Server (both Internet and Local) and Web ADF Graphics data sources.

If the Map control has a rotation value greater or less than 0:

  1. The presence of non-rotatable resources will return an exception in the Web page at runtime. The exception will indicate which resource does not support rotation.
  2. The ContinuousCallback and EnableTileCaching properties on the Map control will be disabled.

If the Map Rotation property is NaN, the rotation of the PrimaryMapResource (PMR) can be used. Since a Web ADF Graphics resource cannot be a PMR, only a rotation value specified for a data frame in an ArcGIS Server map service can be used.

Map rotation can also be set on the fly at runtime. As long as the resources in the Map control support rotation, Map contents will be updated. Again, if a resource does not support rotation, an exception will returned in the Web page indicating which resource.

The following code snippet illustrates how to change the Rotation property on a Map control:

            Map1.Reset();

            Map1.Rotation = 45;

Rotating an ArcGIS Server cached map service requires that dynamic map images are generated – the cached map tiles will not be used. As a result, many of the performance benefits of a cached map service are negated.

Web ADF controls that work with the Map have also been updated to support rotation, such as the Navigation, Magnifier, and OverviewMap controls.

TransformationParams and RotationParams classes

Two new classes have been introduced to support parameters used when transforming and rotating map content: TransformationParams and RotationParams.

TransformationParams stores parameters needed to convert coordinates between screen and map units. The Map control has a GetTransformationParams() method to return a TransformationParams instance set up using Map properties. The TransformationDirection enumeration allows you to select which direction the transformation will take place: either screen to map units (ToMap) or map to screen units (ToScreen).

The RotationParams class stores parameters associated with a rotated map extent. The TransformationParams class uses the RotatedParams class to account for rotation when converting coordinates between screen and map units.

In most cases, you’ll use an instance of TransformationParams when converting geometry to and from screen and map units. This is discussed in more detail in the next section.

Geometry conversion

A set of static methods on the Web ADF Geometry, Point and Envelope classes (in ESRI.ArcGIS.ADF.Web.Geometry) used to convert to and from screen and map units have been overloaded to support map rotation. Previous methods of the same name in 9.2 final and 9.2 sp1 have been marked obsolete because they do not support rotated maps. Obsolete methods will still work with unrotated map content. The new overloads provide a more efficient pattern for transforming Web ADF geometry. A single TransformationParams instance can be created from scratch or retrieved from a Map control and used in multiple geometry conversion calls. TransformationDirection.ToMap should be used for ToMap_ methods while TransformationDirection.ToScreen should be used with ToScreen_ methods. The overloaded methods are listed below:

In the ESRI.ArcGIS.ADF.Web.Geometry.Geometry class (inherited by concrete Web ADF geometry classes):

TransformGeometryToScreen(ESRI.ArcGIS.ADF.Web.Geometry.Geometry,

  ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)

In the ESRI.ArcGIS.ADF.Web.Geometry.Point class:

ToMapPoint(ESRI.ArcGIS.ADF.Web.Geometry.Point, ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)
 

ToMapPoint(int, int, ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)

ToScreenPoint(ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)

In the ESRI.ArcGIS.ADF.Web.Geometry.Envelope class:

ToMapEnvelope(System.Drawing.Rectangle, ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)

ToScreenRectangle(ESRI.ArcGIS.ADF.Web.Geometry.TransformationParams)

The following code snippet illustrates how to convert from a point in screen units to a Web ADF Point object in map units:

ESRI.ArcGIS.ADF.Web.Geometry.Point adfPoint = ESRI.ArcGIS.ADF.Web.Geometry.Point.ToMapPoint(screenPoint.X, screenPoint.Y, Map1.GetTransformationParams(TransformationDirection.ToMap);

Manager

When building a Web application in Manager, the Map rotation property is set to a value of NaN and cannot be changed explicitly. Instead, the Primary Map Resource (PMR) can be used to define a rotation value. In Manager, the PMR is set in the Preview Layers dialog by selecting a resource in the drop down list under “Use coordinate system, rotation and extent of:”. If the PMR is rotated, you cannot use non-rotatable service types, such as ArcIMS and WMS, in the same map. Manager will provide an informative warning if this situation occurs. If you select a non-rotatable service as the PMR, the Web ADF will unrotate any rotated service so that it lines up with the non-rotatable service.

Posted in Services | Tagged , , | 4 Comments

Follow-up to Extending the QueryAttributes Task: Zooming to selected features

From Bryan Baker, a product engineer working on the .NET SDK: 

I wrote an earlier post that showed how to extend the QueryAttributes task so that all features are immediately highlighted. Several users have asked about also zooming to the selected features. I'll show that here, though keep in mind that the user can also zoom to the selected features by right-clicking on the node for the layer (Cities in the graphic at the top of the earlier post) and choosing to zoom to selected features.

It turns out that it's a little more difficult to zoom to the features than I originally thought, because the FullExtent property of the graphics layer is null for queries like this. Instead, we have to construct our own envelope around all the features by looping through them. This isn't that difficult, though of course it does require more processing.

I’ve included code below that does the zooming. This code should be added to near the bottom of the existing code in the earlier post. If for some reason you didn’t want to highlight all the features, you could omit or comment out the line in the loop that sets the selectedCol to true.

The code below first creates an envelope to use, then in the existing loop that selects each feature, it widens the envelope to surround each feature. Once it has the envelope, it gets a reference to the Map control so it can set the Map’s extent. This takes some work, since the task itself has no reference to the Map. We have to get the ID of the Map and then search the page’s control tree. Since the Map control could be nested within another control, such as in a FloatingPanel, we search for it recursively using a custom function (found at the bottom of this listing).

One more thing before we zoom the map: the task could be querying a point layer, and if only one point is found, the “envelope” around all features is a point. We can’t zoom to a point, so instead we set the envelope to a percentage of the full extent of the Map (five percent—this value is hard-coded here, and you can change it depending on tightly you want to zoom in this one-point case).

Finally, we’ve got an envelope that will work, and we set the Map to this extent, refresh the Map, and copy its CallbackResults to the task’s CallbackResults. This last step is necessary because a callback only works with one control (the task in this case), and we need to tell another control (the Map) to update its contents.

    ' Set up the items to hold the extent of all features

    Dim geom As ESRI.ArcGIS.ADF.Web.Geometry.Geometry

    Dim layerEnv As New _

       ESRI.ArcGIS.ADF.Web.Geometry.Envelope( _

       Double.MaxValue, Double.MaxValue, _

       Double.MinValue, Double.MinValue)

    ' Set each feature to selected (this loop is

    ' at the end the code in my previous blog post)

For Each row As DataRow In graphicsLayer.Rows

   row(selectedCol) = True

    ' Enlarge the overall envelope to

    '  include the current feature

   geom = graphicsLayer.GeometryFromRow(row)

   layerEnv.Union(geom)

Next

    ' If any records found, zoom to them

If graphicsLayer.Rows.Count > 0 Then

    ' Get a reference to the Map – have to search the Page since

    '  task itself has no direct reference to the Map

    ' (the task's TaskResults does have the ID of the map)

    Dim mapCtrl As Map = Nothing

   Dim taskResultsId As String =

      Me.TaskResultsContainers(0).Name

    Dim taskResults As TaskResults = _

       CType(FindControlRecursive( _

       Me.Page, taskResultsId), TaskResults)

   If Not TaskResults Is Nothing Then

      mapCtrl = CType(FindControlRecursive( _

         Me.Page, taskResults.Map), Map)

   End If

   If Not mapCtrl Is Nothing Then

    ' If only one point found, envelope will be a point

    '   – set to a percentage of the full extent

      If layerEnv.XMin = layerEnv.XMax AndAlso _

         layerEnv.YMin = layerEnv.YMax AndAlso _

         Not IsNothing(mapCtrl) Then

    ' Percentage of the full extent to use when zooming to point

    Dim zoomToPointPercentage As Integer = 5

    Dim NewWidth As Double = mapCtrl.GetFullExtent().Width _

       * (zoomToPointPercentage / 100)

    Dim NewHeight As Double = mapCtrl.GetFullExtent().Height _

       * (zoomToPointPercentage / 100)

         layerEnv.XMin -= NewWidth / 2

         layerEnv.XMax += NewWidth / 2

         layerEnv.YMin -= NewHeight / 2

         layerEnv.YMax += NewHeight / 2

      End If

    ' Now we can zoom the map to the extent of the features

      mapCtrl.Extent = layerEnv

      mapCtrl.Refresh()

    ' We have to tell the client to refresh, using CallbackResults

      Me.CallbackResults.CopyFrom(mapCtrl.CallbackResults)

   End If

End If

Below is the function called in the above code. This searches the page and its child controls for the control with the given ID. Put this after the end of the Execute method (End Sub), but inside the Class (before the End Class statement).

    ' Finds the control in the Page's control tree

    Public Function FindControlRecursive(ByVal root As _

       Control, ByVal id As String) As Control

        If root.ID = id Then

            Return root

        End If

        Dim c As Control

        For Each c In root.Controls

            Dim t As Control = FindControlRecursive(c, id)

            If Not t Is Nothing Then

                Return t

            End If

        Next

        Return Nothing

    End Function

If you add the code above to the custom task as outlined in the earlier blog post, it should automatically zoom to the extent of all features found by the task.

 

Posted in Services | Tagged , , | 8 Comments

Customizing the Web Editor task

 James Goodrich, a developer on the .NET Web ADF team, contributed this information about some Service Pack 2 enhancements to the Editor task:

 When the Editor task was released at version 9.2 of the Web ADF for the .NET Framework, a common question was “How can I customize the Editor task?”. Service Pack 2 provides an answer to this question. You can now customize the Editor task with custom tools and Editor Panels and we have added more events that allow you to hook into the Editor task.

In addition to these new customization options for developers, the Editor task has another key change at Service Pack 2 which it allows it to be configured with a pooled map service. You can edit non-versioned data using a pooled map service.

See the Editor Task control discussion in the Developer Help for samples and instructions.

Customized Editor Task with added tools

Posted in Services | Tagged , , , | 8 Comments

How to track pending tiles and display a busy indicator in a Web mapping application

Rex Hansen contributed this post about how to use some of the enhanced JavaScript in Service Pack 2 to track pending tiles and display a busy indicator (such as an animated “Loading” graphic) over the Web ADF’s Map control: 

As a Web ADF developer working in an asynchronous communication environment, it is often beneficial to provide an end user with some indication that a user action is being processed. Since most Web ADF applications are centered on working with a map, the ability of an end user to effectively interact with map contents is essential. The Web ADF has the ability to asynchronously retrieve map data from multiple sources and consolidate it in a single map control. In general, data sources often differ in the time it takes to respond to a request. Since the Web ADF Map control is capable of rendering map data as it is returned to the browser, it’s possible that some portion of data in the map is visible and accessible before another portion. In this case, it will likely be important to let the end user know when the map control has finished loading map data from any and all sources.

To support this capability, 9.2 service pack 2 includes an enhanced Web ADF JavaScript Map object. The JavaScript Map object has a set of “event handlers” on the pendingTiles property. The pendingTiles property references an array of map image tiles to be rendered. The array is updated when the map needs new image tiles based on the current extent. Events on the pendingTiles property are listed below:

Event Description
add_onRequestsPending Triggered when the number of items in the pendingTiles array changes from 0 to a higher value
add_onRequestsRemove Triggered when an item is removed from the pendingTiles array
add_onRequestsCompleted Triggered when the number of item in the pendingTiles array changes to 0

Use these handlers on the Map object’s pendingTiles property to register a JavaScript function with the event. For example:

map.pendingTiles.add_onRequestsPending(showBusyIndicator)

where map is the Map object and showBusyIndicator is a JavaScript function to call when the number of items in the pendingTiles array changes from 0 to a higher value.

The JavaScript function showBusyIndicator may appear as follows.

function showBusyIndicator(sender) {

            showLayer(“BusyIndicator”);

            if (sender!=null) {

                window.status = “Pending Tiles: “ + sender.pendingTiles.length;

            }

The argument to the function is a reference to the JavaScript Map object. This argument can be used to gain access to map properties, such as the number of map image tiles left in the pendingTiles array. In this example, the number of pending tiles is output to the browser window’s status bar. If the argument is null, the pendingTiles array contains 0 items. The Web ADF includes two convenient JavaScript functions to show or hide a layer (div) based on its id, named showLayer and hideLayer, respectively. The functions are contained in the display_common.js file which is by default embedded with the Web ADF controls. In this example, the showLayer function is used to make the contents in the div tag with an id of “BusyIndicator” visible.

You can show the number of pending tiles and a "busy indicator" in a Web mapping application
 

Included below is a simple Web page with a MapResourceManager, Map, and a div tag containing an image. The JavaScript Map object events are handled after the form to let the content of the form load before interacting with it.
 

<%@ Page Language=”C#” AutoEventWireup=”true”  CodeFile=”Default.aspx.cs” Inherits=”_Default” %>

<%@ Register Assembly=”ESRI.ArcGIS.ADF.Web.UI.WebControls, Version=9.2.2.1350, Culture=neutral, PublicKeyToken=8fc3cc631e44ad86″

    Namespace=”ESRI.ArcGIS.ADF.Web.UI.WebControls” TagPrefix=”esri” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

<html xmlns=”http://www.w3.org/1999/xhtml” >

<head runat=”server”>

    <title>Untitled Page</title>

</head>

<body>

    <form id=”form1″ runat=”server”>

    <div>

        <esri:MapResourceManager ID=”MapResourceManager1″ runat=”server”>

            <ResourceItems>

            </ResourceItems>

        </esri:MapResourceManager>

        <esri:Map ID=”Map1″ runat=”server” Height=”453px” Width=”556px” MapResourceManager=”MapResourceManager1″>

        </esri:Map>   

    </div>        

    

     <div id=”BusyIndicator” style=”z-index: 1000; left: 25px; width: 100px; position: absolute; top: 422px;height: 100px”>

        <img src=”images/CircleThickbox.gif” />

     </div>

   </form>

   <script language=”javascript” type=”text/javascript”>

           

        function showBusyIndicator(sender) {

            showLayer(“BusyIndicator”);

            if (sender!=null) {

                window.status = “Pending Tiles: “ + sender.pendingTiles.length;

            } 

        }

       

        function showPendingTiles(sender) {

            if (sender!=null) {

                window.status = “Pending Tiles: “ + sender.pendingTiles.length;

            } 

        }

       

        function hideBusyIndicator(sender) {

            hideLayer(“BusyIndicator”);

            if (sender!=null) {

                window.status = “”;

            } 

        }

       

        // add busy indicator functions to the map

        map = Maps["Map1"];

        map.pendingTiles.add_onRequestsPending(showBusyIndicator);

        map.pendingTiles.add_onRequestsRemove(showPendingTiles);

        map.pendingTiles.add_onRequestsCompleted(hideBusyIndicator);   

       

   </script>

</body>

</html>

Posted in Services | Tagged , , | 6 Comments