Question

I have geography field of irregular shapes. Geography field can vary from hundred to thousands of Lat/Long points that define that shape. In regards to size it could be from several US. Postal Codes to a size of entire US State. In order to have increased performance I have build Spacial index on that field. On frequent basis I have to find vehicles based on Lat/Long point that are within specific zone.

My original approach was this.

WITH    LastP
          AS ( SELECT vlp.ID
                   ,GEOGRAPHY::STPointFromText('POINT(' + CAST(vlp.Long AS VARCHAR(20)) + ' '
                                               + CAST(vlp.Lat AS VARCHAR(20)) + ')', 4326) AS LastKnownPoint
                FROM LastPosition AS vlp )
    SELECT lp.ID
           ,zn.ZONE
        FROM dbo.GeogZone AS zn WITH ( NOLOCK )
        JOIN @zones AS z
            ON zn.Zone = z.Zone
        JOIN LastP AS lp
            ON lp.LastKnownPoint.STWithin(zn.ZoneGeog) = 1

I was getting all records from my table LastPosition and than I converted Lat/Long into Geography point and later JOIN using STWithin function. This process works great but can be very slow. I have tried to adjust Spacial indexes but it did not make big changed.

To increase performance I want to introduce the following process.

From Geography type I will extract NorthLat, SouthLat, EastLong, WestLong Now I can limit the number of results before I do compare in the following matter.

WITH    LastP
          AS ( SELECT vlp.ID
                   ,GEOGRAPHY::STPointFromText('POINT(' + CAST(vlp.Long AS VARCHAR(20)) + ' '
                                               + CAST(vlp.Lat AS VARCHAR(20)) + ')', 4326) AS LastKnownPoint
                FROM LastPosition AS vlp 
                WHERE (vlp.Long BETWEEN @WestLong and @EastLong) AND (vlp.Lat BETWEEN @SouthLat AND @NorthLat))
    SELECT lp.ID
           ,zn.ZONE
        FROM dbo.GeogZone AS zn 
        JOIN @zones AS z
            ON zn.Zone = z.Zone
        JOIN LastP AS lp
            ON lp.LastKnownPoint.STWithin(zn.ZoneGeog) = 1

Here is the code for building the box.

DECLARE @geomenvelope GEOMETRY;

DECLARE @BoundingBox AS TABLE
    (
     SouthLat DECIMAL(10, 8)
    ,NorthLat DECIMAL(10, 8)
    ,EastLong DECIMAL(10, 8)
    ,WestLong DECIMAL(10, 8)
    );

SELECT @geomenvelope = GEOMETRY::STGeomFromWKB(zn.ZoneGeog.STAsBinary(), zn.ZoneGeog.STSrid).STEnvelope()
    FROM dbo.GeogZone AS zn
    WHERE zn.Zone = 'CA-1'

INSERT INTO @BoundingBox (SouthLat,NorthLat,EastLong,WestLong)
        SELECT @geomenvelope.STPointN(1).STY 
               ,@geomenvelope.STPointN(3).STY 
               ,@geomenvelope.STPointN(1).STX 
               ,@geomenvelope.STPointN(3).STX 

SELECT *
    FROM @BoundingBox

My question: Is there an alternative (easier) way to get East, West, North, South Points from my Geography Field?

Was it helpful?

Solution

Sorry for the late reply, but hope I can add something.

Firstly, the conversion into LastKnownPoint, you should be able to declare it as follows:

GEOGRAPHY::Point(vlp.Lat, vlp.Long, 4326) AS LastKnownPoint

It works just the same, but is so must easier to read and doesn't require the casts.

To get better performance, you wouldn't have to do the conversion if you can store the Lat / Long as a Geography column in itself which if you're searching regularly is a lot of overhead. Doing this would also allow you to use the Zone directly as a filter and using a spatial index and I couldn't recommend it highly enough. Not to mention no longer needing to create the bounding box.

If you can't do all of that, at least the reduction in CAST'ing and Concatenation should gain you a fair few milliseconds here and there.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top