Mit einem fertigen Modul kann ich leider nicht dienen, aber mit einem Auszug. Hier ein Code-Snippet, der eine fertige Umkreissuche (OPenGeoDB) für Drupal bereitstellt.
Mit ein wenig Arbeit läßt sich hieraus ein komplettes Modul machen. Die eigentlichen Suchroutinen (class Umkreissuche) habe ich selber aus dem Internet. Weiß nicht mehr woher. Im Zweifel Google befragen.
Benötigt wird natürlich noch die OpenGeoDB, die händisch in die DB geladen werden muss.
http://opengeodb.hoppe-media.com/<code>
<?php
class Umkreissuche {
// Erdradius in Kilometern
private $Erdradius = 6371;
// mysql link identifier
private $db;
// Datentabelle
private $table = false;
// Fehler zeigen?
public $zeigeFehler = true;
public function __construct($db, $table = 'user_location') {
if (!is_resource($db) || get_resource_type($db) != 'mysql link') {
trigger_error('Keine MySQL-Ressource übergeben', E_USER_ERROR);
}
$this->db = $db;
$this->table = $table;
// leere Koordinaten in Tabelle füllen
$sql = 'SELECT ID, PLZ
FROM {user_location}
WHERE
KoordX = 0
AND KoordY = 0
AND KoordZ = 0
';
$re = db_query($sql);
while ($rd = db_fetch_object($re)) {
if (!$this->Plz2Koord($rd->PLZ, $lon, $lat)) {
if ($this->zeigeFehler) {
trigger_error('Postleitzahl ' . $rd->PLZ . ' konnte nicht zugeordnet werden', E_USER_NOTICE);
}
continue;
}
$this->Kugel2Kartesisch($lon, $lat, $x, $y, $z);
$sql = 'UPDATE {user_location}
SET
Longitude = %f,
Latitude = %f,
KoordX = %f,
KoordY = %f,
KoordZ = %f
WHERE
ID = '. (int)$rd->ID .'
LIMIT 1
';
db_query($sql, $lon, $lat, $x, $y, $z);
}
}
public function Kugel2Kartesisch($lon, $lat, &$x, &$y, &$z) {
$lambda = $lon * pi() / 180;
$phi = $lat * pi() / 180;
$x = $this->Erdradius * cos($phi) * cos($lambda);
$y = $this->Erdradius * cos($phi) * sin($lambda);
$z = $this->Erdradius * sin($phi);
return true;
}
public function Plz2Koord($PLZ, &$lon, &$lat) {
$sql = 'SELECT
coo.lon,
coo.lat
FROM geodb_coordinates AS coo
INNER JOIN geodb_textdata AS textdata
ON textdata.loc_id = coo.loc_id
WHERE
textdata.text_val = "' . mysql_real_escape_string($PLZ, $this->db) . '"
AND textdata.text_type = "500300000"
LIMIT 1';
$re = mysql_query($sql, $this->db);
if (mysql_num_rows($re) != 1) {
return false;
}
list($lon, $lat) = mysql_fetch_row($re);
return true;
}
public function Suche($PLZ, $Radius, $Spalten = array(), $Reihenfolge = false, $Richtung = 'ASC') {
if (!is_array($Spalten) || count($Spalten) == 0) {
$Spalten = '*';
} else {
$Spalten = '`' . implode('`, `', $Spalten) . '`';
}
if (!$this->Plz2Koord($PLZ, $lon, $lat)) {
if ($this->zeigeFehler) {
trigger_error('Postleitzahl ' . $PLZ . ' konnte nicht zugeordnet werden', E_USER_NOTICE);
}
return false;
}
$this->Kugel2Kartesisch($lon, $lat, $UrsprungX, $UrsprungY, $UrsprungZ);
$sql = 'SELECT ' . $Spalten . '
FROM {user_location}
WHERE
KoordX >= ' . ($UrsprungX - $Radius) . '
AND KoordX <= ' . ($UrsprungX + $Radius) . '
AND KoordY >= ' . ($UrsprungY - $Radius) . '
AND KoordY <= ' . ($UrsprungY + $Radius) . '
AND KoordZ >= ' . ($UrsprungZ - $Radius) . '
AND KoordZ <= ' . ($UrsprungZ + $Radius) . '
AND POWER(' . $UrsprungX .' - KoordX, 2)
+ POWER(' . $UrsprungY .' - KoordY, 2)
+ POWER(' . $UrsprungZ .' - KoordZ, 2)
<= "' . pow(2 * $this->Erdradius * sin($Radius / (2 * $this->Erdradius)), 2) . '"';
if ($Reihenfolge && strpos($Spalten, $Reihenfolge) !== false) {
$Richtung = (strtoupper($Richtung) == 'DESC') ? 'DESC' : 'ASC';
$sql .= "\n" . 'ORDER BY `' . $Reihenfolge . '` ' . $Richtung;
}
$re = db_query($sql);
$result = array();
while ($rd = db_fetch_object($re)) {
$result[] = $rd;
}
return $result;
}
}
#Für den User ein Profilfeld "PLZ" anlegen und diese bei der Verwendung an diese Funktion übergeben.
function mymodule_fetchDistanceByPLZ($plz_1, $plz_2)
{
$database = 'geodb';
#Unterstützung von 4- und 5-stelligen PLZ (z.B. Schweiz)
if((strlen($plz_1) == 5 || strlen($plz_1) == 4) && (strlen($plz_2) == 5 || strlen($plz_2) == 4)){
// Statement to query the Distance from the OpenGeoDB database
// 1.85201 is the conversion factor from nautical miles to kilometers
$sql = "
SELECT
DEGREES(
ACOS(
SIN(RADIANS(c1.lat)) * SIN(RADIANS(c2.lat))
+ COS(RADIANS(c1.lat)) * COS(RADIANS(c2.lat))
* COS(RADIANS(c1.lon - c2.lon))
) * 60 * 1.85201
) AS distance
FROM
geodb_textdata t1,
geodb_textdata t2,
geodb_coordinates c1,
geodb_coordinates c2
WHERE
t1.text_type = 500300000
AND t2.text_type = 500300000
AND t1.text_val = \"{$plz_1}\"
AND t2.text_val = \"{$plz_2}\"
AND c1.loc_id = t1.loc_id
AND c2.loc_id = t2.loc_id
";
$query = mysql_query($sql);
if($row = mysql_fetch_assoc($query))
{
// get the database result and round it
if($row['distance'] < 100)
// return distance (in km) round to one position after decimal point
return round($row['distance'], 1);
else
// round to no position after decimal point from 100km and more
return round($row['distance'], 0);
}
else
{
// No result found
return -1;
}
}elseif($plz_1 == $plz_2){
return 0; // Same country codes - distance is null
}else{
return -1; // At least one of both codes is invalid
}
}
/*
* Beim Aktualisieren des Profils wird die Funktion aufgerufen, um die PLZ zu syncronisieren. In einem eigenen Modul sollte diese Funktion in hook_user($op = 'update') aufgerufen werden.
*/
function mymodule_update_user_location($uid){
$zip = _get_users_zip($uid); #Liest PLZ aus Userprofil aus.
$sql = 'SELECT
coo.lon,
coo.lat
FROM geodb_coordinates AS coo
INNER JOIN geodb_textdata AS textdata
ON textdata.loc_id = coo.loc_id
WHERE
textdata.text_val = "'.$zip.'"
AND textdata.text_type = "500300000"
LIMIT 1';
$re = db_query($sql);
if (mysql_num_rows($re) != 1) {
return false;
}
while($data = db_fetch_object($re)){
$lon = $data->lon;
$lat = $data->lat;
}
$is_set = db_result(db_query("SELECT ID FROM {user_location} WHERE uid = %d",$uid));
$sql = "INSERT INTO {user_location} (uid, PLZ, Longitude, Latitude) VALUES (%d, %d, %f, %f)";
if(!$is_set){
$res = db_query($sql, $uid, $zip, $lon, $lat);
}else{
#TODO: Update query
}
}
?>
</code>
Gruß
Alex