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.

 

This entry was posted in Services and tagged , , . Bookmark the permalink.

Leave a Reply

8 Comments

  1. sterlingdq says:

    From Brent S on May 8, 2007: Is there any way to have the full completed class file and/or dll of the control (with both blog post code) available as a download?

  2. jvickrey79 says:

    Bryan or anyone that knows the answer- The QueryAttribute Task works great but is there a way I could get total linear feet of all selected features it returns?? Maybe the linear footage could be added in the results task or just some other textbox? Any ideas on code for this please..? Thanks for any help.

    Regards,
    Josh

  3. josh5000 says:

    Bryan,

    I am a novice programmer(obviously). How can I go about getting the custom tool you developed to only display the draw polygon tool and not the other selection tools? Also, if possible, how do I get the user drawn shape to remain persist on the screen amid the selected features?

    Is it possible for me to edit some of the .cs files in the source code and then make my own build?

    Thank you.

  4. josh5000 says:

    Bryan,

    Another question, is it possible to incorporate into your selecttool task, the feature you did in this example where all features selected are checked in the results tree and highlighted?

    Thank you.

  5. jyoe says:

    I have 2 ddls.First ddlcontains hospitals,restaurants etc(point features).Now when i select an item in first ddl, the second ddl should get populated with data like different restaurant names or hospital names etc from oracle database.
    For the first ddl i have got the data from oracle database.How will i populate items in 2nd ddl dynamically using c#,asp.net and visual studio2005.

    After selecting the feature from 2nd ddl, the feature must be zoomed.

    Plz can anybody help me.

  6. bcales says:

    This doesn’t seem to work with GridView (http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2007/10/24/Displaying-task-results-in-a-table.aspx), and errors out.

    In addition, I am trying to move my results pane below the map (full width) so the users have less scrolling to do. I want to have it be like the side pane (expand/collapse)

  7. dp3lw says:

    I notice that the task window appears without being selected from the task menu. Any idea how to turn this off?

    Larry.

    P.S. Superb blog

  8. dp3lw says:

    Oh! Never mind I discovered the “visible=false” tag.