Benutzer:Erik/Extension:Häfen in der Nähe
Hier befindet sich ein Vorschlag für eine Extension zur Anzeige von Häfen in der Nähe eines gebenen Punkts. Kommentare, Bugs und Verbesserungsvorschläge bitte auf der Diskussionsseite
Nach dem Erstellen der Navigationsleisten, habe ich überlegt, wie man sinnvolle Navigationsleisten auch automatisch erstellen kann. Eine Möglichkeit habe ich einmal in der folgenden Extension ausprobiert. Sie arbeitet als Parser Hook und listet alle Häfen in der Nähe einer gegebenen geografischen Position auf. Die Position, sowie die maximale Distanz und die maximale Anzahl der anzuzeigenden Häfen kann als Parameter übergeben werden. Die bekannten Häfen werden auf das Zutreffen der Bedingungen untersucht und dann nach Entfernung und Alphabet geordnet angezeigt. Die Extension greift auf das kml-File zurück, welches von der GoogleEarthExport Extension erzeugt wird.
Beispiel
Im folgenden ein Beispiel, wie man die Extension in Verbindung mit der Vorlage Navigationsleiste nutzen kann:
{{Navigationsleiste |TITEL=Häfen in der Umgebung |INHALT= <nearbyplaces lat="54.333333" lon="10.133333" dist="100" max="10"/> }}
Bedienung
Das Aufrufen der Orte geschieht mit dem Tag <nearbyplaces/>
oder auch <nearbyplaces></nearbyplaces>
. Ohne die Angabe weiterer Parameter werden die zehn am nächsten an Kiel gelegenen Häfen, die maximal 100sm von Kiel entfernt sein dürfen. Diese Darstellung kann durch die folgenden Parameter verändert werden:
- lon
- Angabe der geografischen Länge des Ausgangspunkts
- lat
- Angabe der geografischen Breit des Ausgsngspunkts
- dist
- Der maximal Abstand vom Ausgangspunkt, den ein Hafen haben darf
- showdist
- Mit showdist="yes" wird die Anzeige der Distanzen aktiviert
- max
- Die maximale Anzahl von angezeigten Häfen
Diese Werte werden als Attribute des Tags gesetzt. Beispiel:
<nearbyplaces lat="54.333333" lon="10.133333" dist="100" max="10"/>
Die Reihenfolge der Parameter ist dabei egal. Der Wert sollte immer in Anführungsstriche gesetzt sein. Wird ein Parameter ausgelassen, so wird der Default-Wert (s.o.) genommen.
Eine lauffähige Installation des Scripts gibt es hier. Dort kann auch mit den Einstellungen experimentiert werden.
Versionen
- 2007-03-04
- Die Distanzanzeige ist nun per default deaktiviert. Sie kann über das Attribut showdist aktiviert werden.
- 2007-03-03
- Fix für PHP4. Die benutzen DOM-Funktionen von PHP5 waren nicht rückwärtskompatibel. Die Umstellung auf DOM XML war zu aufwendig. Daher die 'saubere' Lösung: Erstellen eines Parsers.
- 2007-03-02
- Initiale Version
Installation
Das folgende Script (NearbyPlacesExtension.php) muss im Extensions-Verzeichnis abgelegt und die Zeile require_once("./extensions/NearbyPlacesExtension.php");
muss zu der LocalSettings.php hinzugefügt werden.
<?php // HISTORY // 2007-03-04 : The display of the distance is no deactivated by default. // It can be activated using the showdist attribute. // 2007-03-04 : Fix for PHP 4 // Instead of DOM or DOM XML, the xml parser functions are used // 2007-03-02 : Initial implemtation // TODO // - debug output // - testing // - lat and lon in degrees and minutes // - improved retrival of places // - error handling // - config file // - documentation // - output data to be selected by attributes // - error handling // register the extension $wgExtensionFunctions[] = "wfNearbyPlacesExtension"; // set the credit information $wgExtensionCredits['parserhook'][] = array( 'name' => 'Nearby Places', 'author' => 'Erik Hansen', 'url' => 'http://www.skipperguide.de/wiki/Benutzer:Erik/Extension:H%C3%A4fen_in_der_N%C3%A4he', 'description' => 'Shows a list of places near a given place', ); // initialize this extension // register nearbyplaces as parser hook. function wfNearbyPlacesExtension() { global $wgParser; $wgParser->setHook( "nearbyplaces", "npeShowNearbyPlaces" ); } // the comparation function for sorting the places array // this sort all places first by distance then by their name function npeCmpPlacesByDist($a, $b) { if ($a["dist"] == $b["dist"]) { return strcmp($a["name"], $b["name"]); } return ($a["dist"] < $b["dist"]) ? -1 : 1; } // XML Parser Class class npeCParser { var $places; var $tag; var $description; var $placename; var $coordinates; var $parser; // constructor function npeCParser() { $this->places = array(); $this->tag = ""; $this->description = ""; $this->placename = ""; $this->coordinates = ""; } // initialize variables for every new placemark // store the current tag for use within die characterData function function startElement($parser, $name, $attrs) { if ($name == "PLACEMARK") { $this->description = ""; $this->placename = ""; $this->coordinates = ""; } $this->tag = $name; } // store the data from the closed placemark function endElement($parser, $name) { if ($name == "PLACEMARK") { $this->places[] = array ("name" => trim($this->placename), "description" => trim($this->description), "coordinates" => trim($this->coordinates)); } } // store the data of the tags function characterData($parser, $data) { switch ($this->tag) { case "NAME": $this->placename .= $data; break; case "DESCRIPTION": $this->description .= $data; break; case "COORDINATES": $this->coordinates .= $data; break; } } // parse the xml data function parse($xml) { $this->parser = xml_parser_create(); xml_set_object($this->parser, $this); xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1); xml_set_element_handler($this->parser, "startElement", "endElement"); xml_set_character_data_handler($this->parser, "characterData"); xml_parse($this->parser, $xml); xml_parser_free($this->parser); return $this->places; } } // this function function npeShowNearbyPlaces( $input, $argv, &$parser ) { // this is needed to use wiki syntax in the output global $wgOut; // get the supplied arguments if (isset($argv["lat"])) { $req_lat = $argv["lat"]; } else { $req_lat = 54.333333; } if (isset($argv["lon"])) { $req_lon = $argv["lon"]; } else { $req_lon = 10.133333; } if (isset($argv["dist"])) { $max_dist = $argv["dist"]; } else { $max_dist = 100; } if (isset($argv["max"])) { $max_cnt = $argv["max"]; } else { $max_cnt = 10; } $show_distance = false; if (isset($argv["showdist"])) { if ($argv["showdist"] == "yes") { $show_distance = true; } } // initialize the array for the found places $nearby_places = array(); // create a new dom instance and load the kml file into it $xml = file_get_contents("http://www.skipperguide.de/extension/GoogleEarthExport.php"); // create a new Parser and parse the retrieved kml file $parser = new npeCParser(); $places = $parser->parse($xml); // pick all placemarks from the kml-File // In case none is found we are done already. if (count($places) == 0) return ("Es wurden leider keine Häfen gefunden (0)."); // parse through all palces from the kml file foreach ($places as $place) { list($lon, $lat) = sscanf($place["coordinates"], "%f,%f"); // in case the difference in nm between the latitudes is // lager than the distance, we do not hat to proceed any // further with this place if (abs($lat - $req_lat) >> ($max_dist / 3600.0)) { continue; } // calculate the distance between the two places // this is done by calculating the loxodromic distance // using the (Besteckrechnung nach Mittelbreite). // This should be sufficient for distances shorter than // 500nm. // In case the distance is larger than max_dist, // this place will be discarded. $b = $lat - $req_lat; $l = $lon - $req_lon; $pm = ($lat + $req_lat) / 2; $a = $l * cos($pm); $al = atan ($a / $b); $dist = abs(60 * $b / cos ($al)); if ($dist > $max_dist) { continue; } // in case we have come that far, the place will be added // to the list of places within the requested range list($link) = sscanf($place["description"], "http://www.skipperguide.de/wiki/%s"); $nearby_places[] = array ("lat" => $lat, "lon" => $lon, "dist" => $dist, "name" => $place["name"], "link" => $link); } // check wether any places have been found. if (count($nearby_places) == 0) return ("Es wurden leider keine Häfen gefunden (1)."); // sort all places using npeCmpPlacesByDist for comparation // this sorts all places by distance. Places with the same // distancs are sorted by their name usort($nearby_places, "npeCmpPlacesByDist"); // initialize the output string $output = ""; // output all places up to max_cnt places ordered by distance an name $cnt = 0; foreach ($nearby_places as $place) { $output .= "[[" . $place["link"] . "|" . $place["name"] . "]]"; if ($show_distance == true) { $output .= " (" . number_format($place["dist"], 0) . "sm)"; } $output .= " |\n"; $cnt++; if ($cnt >= $max_cnt) { break; } } // parse the output with the wiki parser, then return the result return ($wgOut->parse($output)); } ?>
ToDo
- Verbesserte Abfrage der Orte
- Bisher ruft das Script über http die KML-Datei des GoogleEarthExports ab. Da die Daten dafür allerdings ja auch auf dem Server liegen, ist es wohl besser direkt auf diese Daten zuzugreifen. Ist irgendwo dokumentiert, wie die Daten abgelegt sind?
- Parameter
- Die Usability bei den Parametern könnte deutlich erhöht werden:
- Lon und Lat in Grad und Minuten übergeben
- Plasibilitätstests auf die Parameter
- Test mit verschiedenen Wiki-Versionen
- Bisher wurde die Extension nur mit 1.6.0, 1.7.1, 1.9.3 getestet. Andere Versionen müssen noch getestet werden. Auch wurde bei den Tests nur PHP 5 verwendet. Läuft das Script auch mit PHP 4?
- m.E. uninteressant. php7 ist eher die Frage.--Argonaut (Diskussion) 17:00, 8. Nov. 2017 (UTC)
- Bisherige Testumgebungen:
- MediaWiki: 1.6.0, PHP: 5.1.2 (apache2handler), MySQL: 5.0.18
- MediaWiki: 1.7.1, PHP: 5.1.6 (cgi), MySQL: 4.0.24
- MediaWiki: 1.9.3, PHP: 5.1.6 (apache2handler), MySQL: 5.0.27-log (Läuft noch nicht. Statt des Texts erscheinen wirre Zeichen beginnend mit UINQ)
- Debugging
- Zum besseren Debuggen müssten Debug-Ausgaben eingebaut werden.
- Internationalisierung
- Die Bildschirmausgaben im Fehlerfall, sowie die 'sm' sollten internationalisiert werden. Entsprechende Übersetzungen müssten eingefügt werden.
- Config-File
- Bisher ist alles hart rein gecodet. Veränderliche Einstellungen (z.B. der Linkt zum Downloaden der KML-Datei) sollten über Variablen in der LocalConfig.php oder über eine eigene Config-Datei einstellbar sein.
- Dokumentation
- Wie immer könnte die besser sein ;)
- Fehlerbehandlungen
- Müssen noch hinzu gefügt werden. Sind bisher noch nicht vorhanden.
Mathematisches
Verfahren
Breitenunterschied (b) in nm sind zunächst recht einfach zu berechnen. Er ergibt sich aus der Differenz (Δφ) von der Breiten von Ausgangspunkt zu Endpunkt. Der Längenunterschied (a) ist nicht mehr ganz so einfach zu berechnen, da eine Bogenminute nur am Äquator einer sm entpricht. Hier muss der Abweitung (Δλ) zwischen den beiden Punkten je nach Breite skaliert werden. Als Skalierungsfaktor ergibt sich der Cosinus der Breite. Da die beiden Punkte auf verschiedenen Breiten liegen können wird die mittlere Breite (Mittelbreite φM) dafür genommen. Somit ergibt sich aus der Abweitung (Δλ) der Längenunterschied (a). Längen- (a) und Breitenunterschied (b) bilden Gegenkathete (a) und Ankathete (b) eines rechtwinkligen Dreiecks. Der zu steuernde Kurs (α) ist der arctan von Längenunterschied (a) geteilt durch den Breitenunterschied (b). Die Distanz (Länge der Hypothenuse) ergibt sich dann aus dem Breitenunterschied (b) geteilt durch den cosinus der mittleren Breite (φM) skaliert mit 60, um von Grad auf sm umzurechnen.
Dieses Verfahren funktioniert nur bei kleineren Distanzen, bzw. Breitenunterschieden.
Formeln
- Breitenunterschied
- b = Δφ = φB - φA
- Abweitung
- Δλ = λB - λA
- Mittelbreite
- φM = ½ (φA + φB)
- Längenunterschied
- a = Δλ * cos(φM)
- Winkel
- α = arctan(a/b) = arctan((Δλ/Δφ) * cos(φM))
- Distanz
- d = 60 * b / cos(α)
Zum Nachlesen
- Gunter Herdam: Astronomische Navigation, 3. Auflage September 2001, Eingenverlag, S. 101ff [1]
- Werner Kumm, Hans-Dieter Lübbers, Harald Schultz: Sporthochseeschifferschein, 2. Auflage 1999, Delius Klasing, S. 16ff [2]
- Karl-Richard Albrand, Hermann Junge: Formelsammlung Navigation, DSV Verlag, S. 14 [3]
- Karl-Richard Albrand, Walter Stein: Nautische Tafeln und Formeln, DSV Verlag, S. 73 [4]