Benutzer:Erik/Extension:Häfen in der Nähe

Version vom 5. März 2007, 08:43 Uhr von Erik (Diskussion | Beiträge) (Dokumentation und Revisionsliste aktualisiert.)

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?
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.