ThinkGeo.com    |     Documentation    |     Premium Support

Problem With Distance Queries

Hi,


I am having trouble with two methods, GetFeaturesWithinDistanceOf and GetFeaturesNearestTo. If no projection is applied to the layer these methods seem to work fine. However, if I project a layer in UTM83 Zone 16N into a geographic projection (NAD83) I receive an error every time I try to use them. Here is a code snippet:



   Private Sub Map1_MapClick(ByVal sender As Object, ByVal e As ThinkGeo.MapSuite.DesktopEdition.MapClickWinformsMapEventArgs) Handles Map1.MapClick
      Dim oFeatures As Collection(Of Feature)
      Dim oClickPoint As New ScreenPointF(e.ScreenX, e.ScreenY)
      Dim oDistancePoint As New ScreenPointF(e.ScreenY + 15, e.ScreenY)

      Dim dDistance As Double = ExtentHelper.GetWorldDistanceBetweenTwoScreenPoints(Map1.CurrentExtent, oClickPoint, oDistancePoint, Map1.Width, Map1.Height, Map1.MapUnit, DistanceUnit.Meter)

      oFeatures = moTestUTMLayer.FeatureSource.GetFeaturesWithinDistanceOf(
         New PointShape(e.WorldX, e.WorldY),
         Map1.MapUnit,
         DistanceUnit.Meter,
         dDistance,
         ReturningColumnsType.AllColumns)

      oFeatures = moTestUTMLayer.FeatureSource.GetFeaturesNearestTo(New PointShape(e.WorldX, e.WorldY), Map1.MapUnit, 1, New String() {""}, dDistance, DistanceUnit.Meter)

   End Sub

And here is the error I get:


System.ArgumentOutOfRangeException was unhandled

  Message=The decimal degree latitude value you provided was out of range.

Parameter name: latitude

  ParamName=latitude

  Source=MapSuiteCore

  StackTrace:

       at ThinkGeo.MapSuite.Core.x6d719af406ea4c2c.xcaea77dcf8ddb91c(Double x68af109c02c3871b, String x34decc57f0820440)

       at ThinkGeo.MapSuite.Core.DecimalDegreesHelper.GetLongitudeDifferenceFromDistance(Double distance, DistanceUnit distanceUnit, Double latitude)

       at ThinkGeo.MapSuite.Core.BaseShape.BufferCore(Double distance, Int32 quadrantSegments, BufferCapType bufferCapType, GeographyUnit shapeUnit, DistanceUnit distanceUnit)

       at ThinkGeo.MapSuite.Core.BaseShape.Buffer(Double distance, GeographyUnit shapeUnit, DistanceUnit distanceUnit)

       at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesWithinDistanceOfCore(BaseShape targetShape, GeographyUnit unitOfData, DistanceUnit distanceUnit, Double distance, IEnumerable`1 returningColumnNames)

       at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesWithinDistanceOf(BaseShape targetShape, GeographyUnit unitOfData, DistanceUnit distanceUnit, Double distance, IEnumerable`1 returningColumnNames)

       at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesWithinDistanceOf(BaseShape targetShape, GeographyUnit unitOfData, DistanceUnit distanceUnit, Double distance, ReturningColumnsType returningColumnNamesType)

       at GetFeaturesWithinDistanceOf.Form1.Map1_MapClick(Object sender, MapClickWinformsMapEventArgs e) in C:\Users\steller\documents\visual studio 2010\Projects\ThinkGeo\GetFeaturesWithinDistanceOf\Form1.vb:line 44

       at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.OnMapClick(MapClickWinformsMapEventArgs e)

       at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.x1e882ae98c96da8b(InteractionArguments x195facd4ef5d753d)

       at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.xfeca3317d3c75bbb(Object xd9272088e65bd176, x6a8380ab1a7ebb4c xc2fd4c0ed406cdb7)

       at ThinkGeo.MapSuite.DesktopEdition.x5cd462d41be2f68a.OnMouseEvent(x6a8380ab1a7ebb4c e)

       at ThinkGeo.MapSuite.DesktopEdition.x5cd462d41be2f68a.x9726bb53ed7f1ead(Object xd9272088e65bd176, EventArgs xc2fd4c0ed406cdb7)

       at System.Windows.Forms.Timer.OnTick(EventArgs e)

       at System.Windows.Forms.Timer.TimerNativeWindow.WndProc(Message& m)

       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)

       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)

       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)

       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)

       at System.Windows.Forms.Application.Run(ApplicationContext context)

       at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()

       at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()

       at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)

       at GetFeaturesWithinDistanceOf.My.MyApplication.Main(String[] Args) in 17d14f5c-a337-4978-8281-53493378c1071.vb:line 81

       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)

       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)

       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()

       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)

       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)

       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

       at System.Threading.ThreadHelper.ThreadStart()


I am not sure whether the problem is because I don't understand how to use these methods, or if it's a bug, or if they just don't work on projected data. I can provide a project that demonstrates this problem if necessary. Can you help me with this issue?


Thanks,


Steve



Steve, 
  
 I think you set wrong map unit, if it’s DecimalDegree, the x coordinate represents latitude so that it must between -90 and 90, however your is another projection that is not DecimalDegree unit, you can set Map1.MapUnit = Meter and try again. 
  
 Let me know is it still has problem. 
  
 Thanks 
 James 
  
  
  
  
  


James,


Thanks very much for responing. I don't think the problem is incorrect MapUnit. Here is the code that loads the layer and sets up the map.



   Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

      Dim sTestUTM16Path As String = "..\..\Data\Test_UTM_Zone16N.shp"
      Dim oOverlay As New LayerOverlay()

      moTestUTMLayer = New ShapeFileFeatureLayer(sTestUTM16Path)
      moTestUTMLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle =
         AreaStyles.CreateSimpleAreaStyle(GeoColor.SimpleColors.Transparent, GeoColor.SimpleColors.Black)
      moTestUTMLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20

      Dim oProj4 As New ManagedProj4Projection(ManagedProj4Projection.GetEsriParameters(26916), ManagedProj4Projection.GetEsriParameters(4269))
      ShapeFileFeatureLayer.BuildIndexFile(sTestUTM16Path, BuildIndexMode.DoNotRebuild)
      moTestUTMLayer.FeatureSource.Projection = oProj4
      oOverlay.Layers.Add(moTestUTMLayer)
      Map1.Overlays.Add(oOverlay)

      moTestUTMLayer.Open()
      Map1.CurrentExtent = moTestUTMLayer.GetBoundingBox
      moTestUTMLayer.Close()

      Map1.MapUnit = GeographyUnit.DecimalDegree
      Map1.Refresh()
   End Sub

Any other ideas?


 



Steven,


I reviewed this thread, and my option is also the same with James, could you take a real try to change the unit in the Map1_MapClick event. Following is the code snippet you can reference:
 

Private Sub Map1_MapClick(ByVal sender As Object, ByVal e As ThinkGeo.MapSuite.DesktopEdition.MapClickWinformsMapEventArgs) Handles Map1.MapClick
      Dim oFeatures As Collection(Of Feature)
      Dim oClickPoint As New ScreenPointF(e.ScreenX, e.ScreenY)
      Dim oDistancePoint As New ScreenPointF(e.ScreenY + 15, e.ScreenY)
 
      Dim dDistance As Double = ExtentHelper.GetWorldDistanceBetweenTwoScreenPoints(Map1.CurrentExtent, oClickPoint, oDistancePoint, Map1.Width, Map1.Height, GeographyUnit.Meter, DistanceUnit.Meter)
 
      oFeatures = moTestUTMLayer.FeatureSource.GetFeaturesWithinDistanceOf(
         New PointShape(e.WorldX, e.WorldY),
         Map1.MapUnit,
         DistanceUnit.Meter,
         dDistance,
         ReturningColumnsType.AllColumns)
 
      oFeatures = moTestUTMLayer.FeatureSource.GetFeaturesNearestTo(New PointShape(e.WorldX, e.WorldY), Map1.MapUnit, 1, New String() {""}, dDistance, DistanceUnit.Meter)
 
End Sub

 
If you still have problem, could you send us the data you are using(Test_UTM_Zone16N.shp)? If you want, you can send to our support(support@thinkgeo.com) and asked to forword to James or Yale.
 
Any more questions or concerns please do not hesitate to let me know.
 
Thanks.
 
Yale

James, Yale, 



No luck, I still get the error when I click on the map. I have emailed a sample project with the data.



Steven, 
  
 Thanks for your updates and sample provided. 
  
 I am sorry to say that I have not received the sample yet. I am afraid it will need a few more days because our support is now on vacation for now, sorry for the inconvenience.   
  
 Thanks. 
  
 Yale 


Steven,


I got your sample from support today and I can reproduce your problem by testing the sample.


Your original shp file is not decimal degree, so when you GetFeaturesWithinDistanceOf, you need to use GeographicUnit.Meter. However, you convert shp file temporary and display on the screen map, the screen map's unit is decimal degree that make sense, so when you GetWorldDistanceBetweenTwoScreenPoints, you need to use Map.MapUnit. I have updated some code and it works without any exception.


Dim dDistance As Double = ExtentHelper.GetWorldDistanceBetweenTwoScreenPoints(
           Map1.CurrentExtent,
           oClickPoint,
           oDistancePoint,
           Map1.Width,
           Map1.Height,
           Map1.MapUnit,
           DistanceUnit.Meter)

        oFeatures = moTestUTMLayer.FeatureSource.GetFeaturesWithinDistanceOf(
           New PointShape(e.WorldX, e.WorldY),
           GeographyUnit.Meter,
           DistanceUnit.Meter,
           dDistance,
           ReturningColumnsType.AllColumns)

Thanks


James


 



Thanks James, 
  
 I was unclear on which units to provide when using the function.  If I understand correctly, GetWorldDistanceBetweenTwoScreenPoints always requires the Geography Units of the Map.  The Distance Queries (GetFeaturesWithinDistanceOf, GetFeaturesNearestTo, etc) always require the Geography units that correspond to the Internal projection of the layer in the map.  Is that correct? 
  
 Thanks, 
  
 Steve

No, you always pass the GeographyUnit of what the layer is in its output coordinates. If your layer has been projected to some projection, then you use the GeographyUnit of that external projection for the Distance Queries functions such as GetFeaturesWithinDistanceOf, GetFeatureNearestTo etc. 
  Always think in what Geography Unit  my layer is in the end. If If it has been projected, do the Distance Query in the GeographyUnit of the external projection. If no projection is applied to it, do the DistanceQuery in the GeographyUnit of the internal projection (In that case, the internal and external projections are the same actually).  
  For the ExtentHelper, the GeographyUnit always has to be the same as the MapUnit. 
  I hope that makes sense.

Hi Val, 



What your are saying is exactly what I assumed originally.  Once you set the MapUnit on the map, that is the unit that should be used in all distance queries.  However that is not what James' code example demonstrates.


If you look back at my original question and trace through the thread you will see that I added a layer that was in UTM Zone 16 meters (SRID=46916).  I then applied a projection to the layer so that it would be displayed in the geographic coordinate system NAD83 (4269).  I set the MapUnit to Decimal Degrees.  In my original code, when I called GetWorldDistanceBetweenTwoScreenPoints I used the following arguments: 


Dim dDistance As Double = ExtentHelper.GetWorldDistanceBetweenTwoScreenPoints(
          Map1.CurrentExtent, 
          oClickPoint, 
          oDistancePoint, 
          Map1.Width, 
          Map1.Height, 
          Map1.MapUnit, 
          DistanceUnit.Meter) 
 

And this is exactly the way James said to do it.  However, James also said to call GetFeaturesWithinDistanceOf like this: 




oFeatures = moTestUTMLayer.FeatureSource.GetFeaturesWithinDistanceOf(
           New PointShape(e.WorldX, e.WorldY),
           GeographyUnit.Meter,
           DistanceUnit.Meter,
           dDistance,
           ReturningColumnsType.AllColumns)
 

Note that instead of using Map1.MapUnit for the GeographyUnit he uses GeographyUnit.Meter. So what you are saying does not agree with what James is saying.


Thanks for your help.


Steve


 



I did some tests on some real data with projected layers for the Distance Queries (GetFeaturesWithinDistanceOf) and I think that there is a bug. It is counterintuitive and illogical to have to pass the Geography Unit of the internal projection. It should be in the Geography unit the layer is in. I created an issue to track for the development team. I am waiting their response on this to update you. Thank you.

Steve,  
  
 This issue is fixed today. Please get the latest build (4.5.53.0 or later) (auto build starts now, maybe take another couple hours to finish) and have another try. Thanks again for point this out.  
  
 Thanks, 
  
 Ben

Hi Ben,


Thanks, the latest version no longer creates the error.  Things seem to be working OK with method GetFeaturesWithinDistanceOf. However the method GetFeaturesNearestTo is either not working or I don't understand how it is supposed to work.  Using this code:



      Dim oFeatures As Collection(Of Feature)
      Dim oClickPoint As New ScreenPointF(e.ScreenX, e.ScreenY)
      Dim oDistancePoint As New ScreenPointF(e.ScreenX + 10, e.ScreenY)

      Dim dDistance As Double = ExtentHelper.GetWorldDistanceBetweenTwoScreenPoints(
         Map1.CurrentExtent,
         oClickPoint,
         oDistancePoint,
         Map1.Width,
         Map1.Height,
         Map1.MapUnit,
         DistanceUnit.Meter)

      oFeatures = moTestUTMLayer.FeatureSource.GetFeaturesNearestTo(
         New PointShape(e.WorldX, e.WorldY),
          Map1.MapUnit,
          1,
          New String() {""},
          dDistance,
          DistanceUnit.Meter)

The method sometimes returns no features, sometimes returns 2 or more features even though I have set the limit to 1.  I am clcking diectly on a feature at all times so at no time should it ever return zero features.  This is my understanding of how this method should work.  Can you offer any guidance?


Thanks!


Steve


 



Steven, 
  
   We are still working on some issues involving the Distance Queries on projected layers. We will let you know the final fix. Thank you.

Steven, 
  
 There is another bug in the overload of GetFeaturesNearestTo you were using, now it has been fixed. Please get the latest build (4.5.54.0 or later) and have another try. 
  
 Thanks, 
  
 Bai

Ben & others, 
  
 Thanks for your work.  GetFeauturesNearestTo is working fine now.  Thanks again for this forum! 
  
 Steve 
  


Steven,


 You are welcome. And I let you know that we also made a Code Community sample on that topic to make everyone aware of this Distance Query issue on projected layers. You can check it out if you want wiki.thinkgeo.com/wiki/Map_Suite_De...ted_Layers