5

Mit den folgenden Daten wäre es möglich, und wenn ja, welche wäre die effizienteste Methode zur Bestimmung, ob die Position 'Shurdington' in der ersten Tabelle enthalten ist gegebene Radius von irgendeinem der Orte in der zweiten Tabelle.Ist ein Punkt innerhalb eines geographischen Radius - SQL Server 2008

Die GeoData-Spalte hat den Typ "geography". Daher sind die Verwendung von SQL Server-räumlichen Features eine Option sowie die Verwendung von Längen- und Breitengrad.

Location  GeoData  Latitude Longitude 
=========================================================== 
Shurdington XXXXXXXXXX 51.8677979 -2.113189 

ID Location   GeoData  Latitude Longitude Radius 
============================================================================== 
1000 Gloucester  XXXXXXXXXX 51.8907127 -2.274598 10 
1001 Leafield  XXXXXXXXXX 51.8360519 -1.537438 10 
1002 Wotherton  XXXXXXXXXX 52.5975151 -3.061798 5 
1004 Nether Langwith XXXXXXXXXX 53.2275276 -1.212108 20 
1005 Bromley   XXXXXXXXXX 51.4152069 0.0292294 10 

Jede Hilfe wird sehr geschätzt.

Antwort

8

Erstellen von Daten

CREATE TABLE #Data (
    Id int, 
    Location nvarchar(50), 
    Latitude decimal(10,5), 
    Longitude decimal(10,5), 
    Radius int 
) 

INSERT #Data (Id,Location,Latitude,Longitude,Radius) VALUES 
(1000,'Gloucester', 51.8907127 ,-2.274598 , 20), -- Increased to 20 
(1001,'Leafield', 51.8360519 , -1.537438 , 10), 
(1002,'Wotherton', 52.5975151, -3.061798 , 5), 
(1004,'Nether Langwith', 53.2275276 , -1.212108 , 20), 
(1005,'Bromley', 51.4152069 , 0.0292294 , 10) 

-Test

Erklären Sie Ihren Punkt von Interesse als POINT

DECLARE @p GEOGRAPHY = GEOGRAPHY::STGeomFromText('POINT(-2.113189 51.8677979)', 4326); 

Um herauszufinden, ob es im Umkreis von einem anderen Punkt ist:

-- First create a Point. 
DECLARE @point GEOGRAPHY = GEOGRAPHY::STGeomFromText('POINT(-2.27460 51.89071)', 4326); 
-- Buffer the point (meters) and check if the 1st point intersects 
SELECT @point.STBuffer(50000).STIntersects(@p) 

sie alle in einer einzigen Abfrage Kombination:

select *, 
     GEOGRAPHY::STGeomFromText('POINT('+ 
      convert(nvarchar(20), Longitude)+' '+ 
      convert(nvarchar(20), Latitude)+')', 4326) 
     .STBuffer(Radius * 1000).STIntersects(@p) as [Intersects] 
from #Data 

Gibt:

Id   Location Latitude Longitude Radius Intersects 
1000 Gloucester 51.89071 -2.27460 20 1 
1001 Leafield 51.83605 -1.53744 10 0 
1002 Wotherton 52.59752 -3.06180 5 0 
1004 Nether Langwith 53.22753 -1.21211 20 0 
1005 Bromley   51.41521 0.02923   10 0 

Re: Effizienz. Mit einiger korrekter Indexierung scheint es, dass die räumlichen Indizes von SQL sehr schnell sein können

+0

Ich liebe dieses Beispiel, obwohl seltsamerweise die Entfernungen um ein gutes Stück zu sein scheinen - vielleicht hat dies mit meinem Mangel an Wissen über die räumlichen Eigenschaften von SQL Server zu tun, aber trotzdem hätte ich angenommen, dass durch Hinzufügen eines Spalte '.STDistance (@p)/1609.34' würde es die Entfernung in Meilen zurückgeben, scheint aber weit weg zu sein - oder bin ich das? – Nathan

+0

Bei der Erstellung eines "POINT" aus "STGeomFromText" ist es erforderlich, dass lat/long anders herum eingegeben wird, z. lang/lat. – Nathan

+0

@Nathan Sie haben recht, ich werde meine Antwort entsprechend aktualisieren – Paddy

1

Sie berechnen den Abstand zwischen den zwei Punkten und vergleichen diese Entfernung mit dem gegebenen Radius.

Für die Berechnung kurzer Entfernungen können Sie die Formel unter Wikipedia - Geographical distance - Spherical Earth projected to a plane verwenden, die "sehr schnell ist und ein ziemlich genaues Ergebnis für kleine Entfernungen liefert".

Nach der Formel, müssen Sie den Unterschied in der Längen- und Breitengrade und die mittlere Breite

with geo as (select g1.id, g1.latitude as lat1, g1.longitude as long1, g1.radius, 
        g2.latitude as lat2, g2.longitude as long2 
      from geography g1 
      join geography g2 on g2.location = 'shurdington' 
           and g1.location <> 'shurdington') 
    base as (select id, 
        (radians(lat1) - radians(lat2)) as dlat, 
        (radians(long1) - radians(long2)) as dlong, 
        (radians(lat1) + radians(lat2))/2 as mlat, radius 
       from geo) 
    dist as (select id, 
        6371.009 * sqrt(square(dlat) + square(cos(mlat) * dlong)) as distance, 
        radius 
       from base) 
select id, distance 
from dist 
where distance <= radius 

Ich benutzte die with select s als Zwischenschritte, die Berechnungen „lesbar“ zu halten.

+0

Warum selbst, wenn man bedenkt, dass "die Verwendung von SQL-Servern räumliche Funktionen anbietet"? – AakashM

+0

@AakashM Weil es eine interessante Übung war. –

1

Wenn Sie die Berechnungen selbst durchführen möchten, können Sie eine auf Pythagoras basierende äquirektangulare Approximation verwenden. Die Formel lautet:

var x = (lon2-lon1) * Math.cos ((lat1 + lat2)/2); var y = (lat2-lat1); var d = Math.sqrt (x * x + y * y) * R;

In Bezug auf den SQL, diese sollten diese Standorte in Ihrer zweiten Tabelle geben, die Ihren Eintrag in den ersten in ihrem Radius enthalten:

SELECT * 
FROM Table2 t2 
WHERE EXISTS (
SELECT 1 FROM Table1 t1 
WHERE 
    ABS (
    SQRT (
    (SQUARE((RADIANS(t2.longitude) - RADIANS(t1.longitude)) * COS((RADIANS(t2.Latitude) + RADIANS(t1.Latitude))/2))) + 
    (SQUARE(RADIANS(t1.Latitude) - RADIANS(t2.Latitude))) 
    ) * 6371 --Earth radius in km, use 3959 for miles 
    ) 
    <= t2.Radius 
) 

Beachten Sie, dass dies aber wahrscheinlich gut nicht die genaueste Methode zur Verfügung genug. Wenn Sie Entfernungen auf der ganzen Welt betrachten, möchten Sie vielleicht Google 'haversine' Formel.

Es könnte sich lohnen, dies mit Paddys Lösung zu vergleichen, um zu sehen, wie gut sie übereinstimmen und welche am besten funktioniert.

Verwandte Themen