ThinkGeo.com    |     Documentation    |     Premium Support

Canceling a Spatial Query

Hi people,


I have a small issue with spatial queries that sometimes take long to be completed. As I have implemented a progress bar/progress dialog box for my process with a cancel button, I need to give the ability to the user to cancel the spatial query half way through. Now I guess that if I create a delegate and start working with "BeginInvoke" and "Endnvoke" techniques I will achieve this. I am just wondering if there is a "best practice" approach. My starting point in the meanwhile is as follows:


1) BeginInvoke(addressof DelegateThatPerformsSpatialQuery)


2) My Delegate will catch the exception of a Closed Layer as follows:



try
aLayeer.Open();
Collection<Feature> features = aLayer.QueryTools.GetFeaturesByColumnValue("Country_Name", "US");
aLayeer.Close();
catch ex Exception
'Here I handle any exception but MAINLY a closed layer one
End Catch

3) My cancel botton just does a aLayer.Close() That should be enough to cause the exception to be thrown and my BeginInvoke to return at its handler.


Do you have anything better to suggest?


Thanks in advance


Yiannis



Hi people,


I just though of providing you the solution to my question, after a long night of experimentations, hoping to poke some further discussions/ideas. So here it is:


Protected selFeatures As Collection(Of Feature)
Private Delegate Function SpatialQuery(ByVal searchArea As BaseShape, ByVal columns As ReturningColumnsType) As Collection(Of Feature)
Private cancelPendingjob As Boolean = False
Private aResetEvent As New ManualResetEvent(False)

Public Function GetTrackInfo(ByVal searchArea As ThinkGeo.MapSuite.Core.BaseShape) As System.Collections.ObjectModel.Collection(Of ThinkGeo.MapSuite.Core.Feature)
cancelPendingjob = False
aResetEvent.Reset()
Try
If Not aTrackLayer Is Nothing Then
aTrackLayer.Open()
RaiseEvent queryProgress(0, "About to select tracks. Running spatial query locally.")
 Dim aSpatialQuery As New SpatialQuery(AddressOf aTrackLayer.QueryTools.GetFeaturesIntersecting)
 Dim aSpatialQueryAsyncState = aSpatialQuery.BeginInvoke(searchArea, ReturningColumnsType.AllColumns, AddressOf InvokeAsyncStateHandlerSpatialQuery, aSpatialQuery)
Console.WriteLine("About to block")
aResetEvent.WaitOne()
Console.WriteLine("Unblocked")
aTrackLayer.Close()
.....
End Function

Public Sub CancelRunningQuery() Implements IAISAnalisysElement.CancelRunningQuery
aResetEvent.Set()
cancelPendingjob = True
End Sub

Private Sub InvokeAsyncStateHandlerSpatialQuery(ByVal ar As IAsyncResult)
Try
If ar.AsyncState IsNot Nothing AndAlso TypeOf ar.AsyncState Is SpatialQueryThen
 Dim invocation_results = CTypeDynamic(Of SpatialQuery)(ar.AsyncState).EndInvoke(ar)
 If invocation_results IsNot Nothing Then
 selFeatures = invocation_results
 aResetEvent.Set()
 End If
 End If
 Catch ex As Exception
 Console.WriteLine("Opps")
 End Try
End Sub


The way it works is pretty simple. The GetTrackInfo Function needs to run a Spatial Query on a layer. Instead of calling the spatial query directly it creates a new delegate of type SpatialQuery that has of course the same signature with the spatial query we need to run. Then it calls the BeginInvoke of the delegate, passing as parameters the parameters of the actual SpatialQuery, the address of a Sub that will handle the response, InvokeAsyncStateHandlerSpatialQuery in our case, and of course the Delegate instance itself. This has to happen because when the InvokeAsyncStateHandlerSpatialQuery returns we must be able to get back our results as we will see later.  In a normal run, the GetTrackInfo will BLOCK at aResetEvent.WaitOne() waiting for the delegate to return at its handler i.e at the InvokeAsyncStateHandlerSpatialQuery. This is the "catch" since now we have delegated the actual Query to another thread and our code is waiting for a signal that we control i.e the aResetEvent.  When the InvokeAsyncStateHandlerSpatialQuery returns we simply call the EndIvoke where we obtain our results. In this case the collection of features and we pass it to the variable the is visible to the whole class i.e Protected selFeatures As Collection(Of Feature). Finally we signal our waiting Function to wake up by "setting" the "aResetEvent"  In the scenario that we want to simply Cancel the query we call the Sub CancelRunningQuery() which also signal our ResetEvent, thus causing the blocked function to proceed. In this scenario though, the function (missing code) will have to handle the empty features of exit because the cancelPendingjob boolean is also set to false.  I wish I had not used the reset event and simply Close the Feature Layer causing the Query to fail with an exception within the InvokeAsyncStateHandlerSpatialQuery. That would give me a cleaner implementation. Unfortunately when I tried to close the layer from the Sub CancelRunningQuery() nothing happened. I guess it must have something to do with the threading model. So the only "gray" area in the code is there where the InvokeAsyncStateHandlerSpatialQuery will return long after a cancellation has happened. Nevertheless I have yet to see "such" a return so I assume that some kind of garbage collector kills it silently.  A final point regards the actual SpatialQuery function that I tried to use. When I created the delegate, it appeared that its signature was:  GetFeaturesIntersecting(ByVal searchArea As BaseShape, ByVal columns As ReturningColumnsType) As Collection(Of Feature)  Wouldn't this be better be ByRef for the BaseShape as coping a whole geometry is time and memory expensive?   Anyway I hope that my bubbling was of some use. I hope that the ThinkGeo people will look into my snippet and will suggest better tweaks. I would really like to kill the running Query with some kind of signal or by closing the layer.  Kind Regards Yiannis



Yiannis,


Can you use the Background worker to instead of the thread in your application? Here is the link about how to use Background worker below:


msdn.microsoft.com/en-us/lib...s.95).aspx


Also it supports the progresses report  for the progress bar and can be canced easily, you just need to have a research on the link above,


Thanks,


Scott,



Hi Scott, 
  
 Thank you for this. Perhaps the Background worker would have make the code simpler but in essence the same result would have been produced. Cancelling the background worker or cancelling the way I describe earlier will still leave the "grey" area of "what happens" to the running spatial query though as exiting the background worker would somehow leave the query running? Unless if I miss something :) 
  
 Thanks a lot 
 Yiannis

Hi Yinnis, 
  
 Thanks for your questions.  
 We are not sure, this is a more general threading question. What my suggestion is you can create a sample to do a quick test.  
 Try to do a long time query or do the spatial query again and again in the Background worker, then exit the background worker see what happens 
  
 It will be great if you want to share what you found. 
  
 Thanks, 
 Lee 


Lee, 
  
 The best solution I believe to this topic is to think about the possibility of creating some progress event for the spatial queries. Something like “percentage” complete or status notification i.e “doing this, doing that etc”. Also a proper cancel method that can be called and stop your propriety code running. Those events will be very helpful in a progress monitoring process or a background worker as now a background worker is giving us the ability to “background” the spatial query but the spatial query status is anybody’s guess. For example I am running a spatial query like the one that follows that takes ages: 
  
 Dim cpaFeatures As Collection(Of Feature) = layerToAnalyse.QueryTools.GetFeaturesIntersecting((cpaPointShape.Buffer(GetLenthInMercator(wgs84Point.X, wgs84Point.Y, distanceNMS * NAUTICALMILE_KILOMETER), GeographyUnit.Meter, DistanceUnit.Kilometer)), ReturningColumnsType.AllColumns) 
  
 The layer is a shapefilelayer for your info. 
  
 Thanks in advance 
 Yiannis

Yiannis,


Thanks for your post and response; I appreciate your suggestions very much.
 
While after some investigations into the code, I found that it is very hard to get this implemented as we are utilizing the third party library (Net topology suite) to do the query, it does not provide this kind of interface to show the status progress of query, sorry for the inconvenience for now.
 
If I am not clear please feel free to let me know.
 
Thanks.
 
Yale