Umkreissuche per SQL Query

Umkreissuche per SQL Query

Umkreissuchen sind ne feine Sache, aber keine ganz so triviale Angelegenheit. Es braucht ne Masse an Koordinaten in Verbindung mit, sagen wir mal, leichter zu ermittelnden ortspezifischen Daten als GPS Koordinaten oder dem Längen und Breitengrad der derzeitigen Position.

Postleitzahlen würden sich da beispielsweise anbieten. Für eine anständige Datenbasis gibt es da verschiedene Möglichkeiten.
Von kostenfrei bis wahnsinnig teuer ist im Prinzip alles dabei. Zwei kostenlose Datenquellen für Geodaten und eine Umkreissuche möchte ich im folgenden Vorstellen.

Datenquellen

Für den auf avanzu angebotenen Webservice habe ich die Daten von OpenGeoDB verwendet. Die Daten sind für eine herkömmliche Anwendung präzise genug aber leider nur für eine begrenzte Fläche. Der Vorteil, den ich bei dieser Datenquelle sehe liegt darin, dass nicht nur Städte sondern auch Stadtteile aufgeführt sind. Die Stadtteile haben zwar die selben Koordinaten wie die Stadt im ganzen, aber dafür hat man auch nichts dafür bezahlt.

MaxMind – GeoIP bietet einen etwas weniger präzisen aber dafür globalen Dump gratis an, hierzu kann ich nicht wirklich viel mehr sagen, da ich da die Daten für die hier angebotene Umkreissuche nicht ganz so passend waren. Der – wie der Name schon sagt – sehr große vorteil ist der Bezug von Geodaten zu IP Adressen und das sogar relativ exakt. Der monatliche Update-Zyklus hat natürlich auch was für sich.

Die Umkreissuche

Woher auch immer man seine Daten bezieht und in eine Datenbank knallt sei jedem selbst überlassen, jetzt stellt sich natürlich noch die Frage wie man Orte im Umkreis findet. Da die Abfrage selbst der Datenbank relativ viel Rechnerei aufbürdet sollte man etwas vorarbeit leisten.
Die Lägen- und Breitenangaben sind normalerweise in Grad angegeben. Was wir aber brauchen sind Radianten. Die könnte man sich zwar auch on the fly berechnen, aber das kostet Performance die man nicht notwendiger Weise ausgeben muss.

Radiant aus Grad berechnen:

$rad  = $grad * (pi()/180);

Für die Abfrage selbst brauchen wir eine Konstante und drei Variablen

  • Erdumfang (6371 km – hier $earth)
  • Länge (in Rad – hier $lng)
  • Breite (in Rad – hier $lat)
  • Umkreis (in km – hier $size)

die wir folgendermaßen verwursten:

select 
    alle,
    felder,
    die,
    wir,
    haben,
    wollen,
    $earth 
    * ( 2 
      * asin(
        sqrt(
          power(sin(($lng - lonRad) / 2), 2) 
          + cos($lat) 
          * cos(latRad) 
          * power(sin(($lat - latRad) / 2), 2)
          )
        )
      ) as distance
from
    geodata
having distance < $size
order by distance
limit 20

Die Stellschrauben, die bei dieser Abfrage zur Verfügung stehen sind die Umkreisgröße und das Limit. Je nachdem wie sehr man seine Datenbank peinigen will oder darf kann man hier das Feintuning betreiben.

 

Kommentare: 1

Deinen Kommentar hinzufügen

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.