Search code examples
sql-server-2008nhibernatehqlspatial

Get spatial points within radius using NHibernate Spatial


I'm currently trying to naivly get the k-nearest neighbors of a set of points, given a value k, a coordinate to use as center and a radius serving as the max distance to find points within. I'm using geographical points (SRID 4326) on a MSSQL 2008 database.

The neighbors are naivly found ordering the query by the distance to the point and limiting the result. My trouble starts at limiting the points by the given radius. The distances returned by the Distance function are much larger than expected, which I understand to be a caused by the SRID 4326. This is OK as along as it is only used for ordering, but when I have to compare these values to a relative distance, say 200 metres, these large numbers wont do.

My question then is: is there a smarter way of limiting the points by a radius using NHibernate Spatial queries, or is there a way to convert this radius into the some meassurement similar to that used by the Distance function?

This is my query as it looks now:

output = NHibernateSession.GetSession().CreateQuery(@"select p from POI p 
                                        where NHSP.Distance(p.PointCoord, :coord) <= :maxDistance 
                                        order by NHSP.Distance(p.PointCoord, :coord)")
                                    .SetParameter("coord", coord,
                                        NHibernateUtil.Custom(typeof(Wgs84GeographyType)))
                                    .SetParameter<double>("maxDistance", radius)
                                    .SetMaxResults(k)
                                    .Enumerable<POI>();

Just as an example I have these two points: POINT(7 1) POINT(7 3)

My expected distance is 2, but the distance calculated by the mssql STDistance function gives 221151.479533501 as a result. I just cant get my mind to make sense about this.


Solution

  • this happens because of the difference between the geography data-type and geometry data type.

    Best explained with an example.

    declare @point1 as geography
    declare @point2 as geography
    
    set @point1 = geography::STGeomFromText('POINT (7 1)', 4326)
    set @point2 = geography::STGeomFromText('POINT (7 3)', 4326)
    select @point1.STDistance(@point2)
    
    declare @point3 as geometry
    declare @point4 as geometry
    
    set @point3 = geometry::STGeomFromText('POINT (7 1)', 4326)
    set @point4 = geometry::STGeomFromText('POINT (7 3)', 4326)
    select @point3.STDistance(@point4)
    

    If you run this directly in SQL Server Management Studio you get 221151.479533501 in the first result and 2 in the second.

    This is basically because in the geography data type the unit is chosen according to the supplied SRID. In your case, being 4326, its in meters. So, you're asking for the distance, in meters, between the coordinates (lon:7; lat:1) and (lon:7; lat:3). It returns about 221 Km.

    When using the geometry type (second example), it's a planar projection where the distance works as you would expect, thus returning 2.

    Regarding your NH Spatial code, seems ok. Just supply the maxDistance parameter in meters and you should be fine.