[PHP] Implementierung der Mitgliederkarte auf ComputerBase

Steffen

Technische Leitung
Administrator
Registriert
März 2001
Beiträge
17.205
November 2005: Anleitung angepasst an vBulletin 3.5

Da im Feedback und per E-Mail Fragen eingetroffen sind wie wir die Mitgliederkarte verwirklicht haben, wollen wir hier einige Infos geben. Dies ist jedoch keinesfalls eine komplette Anleitung! Wir haben den Membermap-Hack von vBulletin-Germany erweitert und an unsere Bedürfnisse angepasst und auch nur um diese Anpassungen soll es hier gehen.

Der in unseren Auge zentrale Schwachpunkt dieses Hacks ist, dass die Kartengrafik bei jedem Seitenaufruf neu erstellt wird. Dies kann bei großen Boards (viele Kartenaufrufe und Einträge) zu Performance-Problemen führen. Daher generieren wir die Mitgliederkarte alle paar Minuten per Cronjob neu, aber dazu später mehr.

Als zweite Sache ist uns aufgefallen, dass die Zuordnung der Postleitzahlen zu einem Ort eher suboptimal ist. Die Postleitzahlen werden in der geodb_locations-Tabelle durch Kommas getrennt in der Spalte plz vom Typ text gespeichert und dann folgende Query zum Ermitteln eines Ortes eingesetzt:
Code:
$sql = 'SELECT * FROM geodb_locations WHERE plz LIKE "%'.$plzf.'%" AND adm0="'.$land.'"';
Diese Query kann MySQL aufgrund der Platzhalter nicht durch Verwendung eines Indizes optimieren und wird deshalb zum zweiten Performance-Problem.

Wir haben uns daher für ein anderes Datenbank-Layout entschieden, das zudem nur die Daten beinhaltet, die auch für die Mitgliederkarte verwendet werden:

Code:
CREATE TABLE `geo_locations` (
  `locationid` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(255) NOT NULL default '',
  `latitude` float NOT NULL default '0',
  `longitude` float NOT NULL default '0',
  PRIMARY KEY  (`locationid`)
) TYPE=MyISAM;

CREATE TABLE `geo_postcodes` (
  `postcode` mediumint(10) unsigned NOT NULL default '0',
  `country` enum('DE','AT','CH','FL') NOT NULL default 'DE',
  `locationid` int(10) unsigned NOT NULL default '0',
  KEY `postcode` (`postcode`,`country`)
) TYPE=MyISAM;
Die Tabelle geo_postcodes nimmt also zu Zuordnung von Land+PLZ zu einem Ort vor, dessen Name sowie Breiten- und Längenangaben dann in geo_locations zu finden sind (Verknüpfung erfolgt über die Spalte locationid).

Zunächst braucht man jedoch logischerweise die Daten aus der OpenGeoDB. Wir haben uns die Datei opengeodb-0.2.3b-mysql-0.1.3-layout.zip heruntergeladen und entpackt. Zunächst haben wir in der Datei 01_create_tables.sql jedoch alle Vorkommnisse von " TYPE=InnoDB CHARACTER SET utf8" entfernt, da wir vorerst lieber ISO-8859-x als Zeichensatz anstatt UTF-8 und MyISAM anstatt InnoDB als MySQL-Table-Handler verwenden wollen. Dann haben wir die Datenbankstruktur importiert:
Code:
mysql DATENBANKNAME<01_create_tables.sql -u BENUTZERNAME -p
Danach steht man vor dem Problem, dass sämtliche Ortsnamen etc in den die eigentlichen Daten beinhaltenden Dateien wie DE.sql und AT.sql ebenfalls im Zeichensatz UTF-8 vorliegen. Daher haben wir diese zunächst mit Hilfe des Tools iconv konvertiert, z.B. für die Datei mit den deutschen Orten:
Code:
iconv -f utf-8 -t iso-8859-1 ./DE.sql > DE_iso8859-1.sql
Danach kann man diese Daten in MySQL importieren.
Code:
mysql DATENBANKNAME<DE_iso8859-1.sql -u BENUTZERNAME -p

Nun hat man die OpenGeoDB-Datenbank in MySQL-Tabellen vom Typ MyISAM und mit dem Zeichensatz ISO-8859-1. Wie eingangs erwähnt wollen wir jedoch ein für die Membermap optimiertes Datenbank-Layout verwenden. Zum Konvertieren der Daten haben wir folgendes Script verwendet:
PHP:
#!/usr/bin/php
<?php
mysql_connect('__SERVER__', '__BENUTZER__', '__PASSWORT__');
mysql_select_db('__DATENBANK__');

/*
// Konfiguration
*/
$countries = array('DE', 'AT', 'CH', 'FL');


/*
// Konvertierung
*/
mysql_query("TRUNCATE TABLE geo_locations") or die(mysql_error());
mysql_query("TRUNCATE TABLE geo_postcodes") or die(mysql_error());

$sql = "SELECT
	name AS location,
	adm0 AS country,
	breite AS latitude,
	laenge AS longitude,
	plz AS postcodes
	FROM geodb_locations
	WHERE adm0 IN ('" . implode("','", $countries) . "')";
$result = mysql_query($sql) or die(mysql_error());

while ($row = mysql_fetch_assoc($result)) {
	if ($row['postcodes'] == '?') {
		continue;
	}

	$sql = "INSERT INTO geo_locations SET
		name='" . addslashes($row['location']) . "',
		latitude='" . addslashes($row['latitude']) . "',
		longitude='" . addslashes($row['longitude']) . "'";
	mysql_query($sql) or die(mysql_error());
	$locationid = mysql_insert_id();

	$postcodes = explode(',', $row['postcodes']);
	foreach ($postcodes as $postcode) {
		$sql = "INSERT INTO geo_postcodes SET
			postcode='" . addslashes($postcode) . "',
			country='" . addslashes($row['country']) . "',
			locationid='" . addslashes($locationid) . "'";
		mysql_query($sql) or die(mysql_error());
	}
}
?>
Jetzt hat man das optimierte Datenbank-Layout.


Dann braucht man ein paar Felder in der Tabelle vb_userfield, in der die PLZ etc eines jeden Benutzers gespeichert wird. Diese Felder könnte man über das AdminCP einrichten, wir haben uns jedoch dafür entschieden sie manuell einzurichten, da wir im Profil nach Angabe von Land+PLZ die Wahl eines exakten Ortes ermöglichen wollen und man dazu ein Dropdown-Menü dynamisch mit Inhalt füllen muss, was vBulletin von sich aus soweit wir wissen nicht kann:

Code:
ALTER TABLE `vb_userfield` ADD `cb_postcode` VARCHAR( 5 ) NOT NULL ,
ADD `cb_country` CHAR( 2 ) NOT NULL ,
ADD `cb_locationid` INT UNSIGNED NOT NULL ;
cb_postcode ist vom Typ Varchar anstatt Integer, um Probleme mit Postleitzahlen, deren erste Ziffer eine Null ist, zu umgehen. Das Prefix "cb_" dient nur der Markierung, damit wir irgendwann noch wissen, welche Felder von uns stammen und welche von vBulletin. =)


Damit man im Profil Angaben zum Wohnort machen kann haben wir die vBulletin-Datei profile.php editiert. Nach folgender Zeile
PHP:
fetch_profilefields(0);
haben wir folgendes eingefügt:
PHP:
	// ############################### Mitgliederkarte ###############################
	$cb_postcode = $vbulletin->userinfo['cb_postcode'];

	$cb_countries = array(
		'' => '',
		'DE' => 'Deutschland',
		'FL' => 'Liechtenstein',
		'AT' => 'Österreich',
		'CH' => 'Schweiz'
	);

	$cb_countrydropdown = '';
	foreach ($cb_countries as $cb_countryshort => $cb_country) {
		if ($cb_countryshort == $vbulletin->userinfo['cb_country']) {
			$cb_countrydropdown .= "<option value=\"$cb_countryshort\" selected=\"selected\">$cb_country</option>";
		} else {
			$cb_countrydropdown .= "<option value=\"$cb_countryshort\">$cb_country</option>";
		}
	}

	$sql = 'SELECT
		geo_locations.locationid,
		geo_locations.name
		FROM geo_postcodes
		LEFT JOIN geo_locations USING(locationid)
		WHERE geo_postcodes.postcode=' . intval($vbulletin->userinfo['cb_postcode']);
	$result = $db->query_read($sql);

	$cb_citydropdown = '';
	while ($row = $db->fetch_array($result)) {
		if ($row['locationid'] == $vbulletin->userinfo['cb_locationid']) {
			$cb_citydropdown .= "<option value=\"$row[locationid]\" selected=\"selected\">$row[name]</option>";
		} else {
			$cb_citydropdown .= "<option value=\"$row[locationid]\">$row[name]</option>";
		}
	}
	// ############################### Mitgliederkarte ###############################

Weiter unten in der profile.php, nach den folgenden Zeilen

PHP:
	($hook = vBulletinHook::fetch_hook('profile_updateprofile')) ? eval($hook) : false;

	// save the data
	$userdata->save();

muss noch der Code zum Speichern dieser Angaben hinzugefügt werden:
PHP:
	// ############################### Mitgliederkarte ###############################
	$vbulletin->input->clean_array_gpc('p', array(
		'cb_postcode' => TYPE_STR,
		'cb_country' => TYPE_STR,
		'cb_locationid' => TYPE_INT
	));

	if (preg_match('#^(\d){4,5}$#', $vbulletin->GPC['cb_postcode'])) {
		$cb_postcode = $vbulletin->GPC['cb_postcode'];
	} else {
		$cb_postcode = '';
	}

	if (in_array($vbulletin->GPC['cb_country'], array('DE', 'FL', 'AT', 'CH'))) {
		$cb_country = $vbulletin->GPC['cb_country'];
	} else {
		$cb_country = '';
	}

	if ($vbulletin->GPC['cb_locationid'] != false) {
		$cb_locationid = $vbulletin->GPC['cb_locationid'];
	} else {
		$cb_locationid = 0;
	}

	$db->query_write("UPDATE " . TABLE_PREFIX . "userfield SET
		cb_postcode = '$cb_postcode',
		cb_country = '$cb_country',
		cb_locationid = $cb_locationid
		WHERE userid = " . $vbulletin->userinfo['userid']
	);

	// PLZ oder Land hat sich geändert -> neuen Standardort setzen
	if ($cb_postcode != $vbulletin->userinfo['cb_postcode'] || $cb_country != $vbulletin->userinfo['cb_country']) {
		$sql = "SELECT locationid FROM geo_postcodes WHERE postcode='$cb_postcode' AND country='$cb_country'";
		if (($location = $db->query_first($sql)) !== false) {
			$locationid = $location['locationid'];
		} else {
			$locationid = 0;
		}
		$db->query_write("UPDATE " . TABLE_PREFIX . "userfield SET cb_locationid=$locationid
			WHERE userid=" . $vbulletin->userinfo['userid']);
	}
	// ############################### Mitgliederkarte ###############################

Zudem muss noch das vBulletin-Template "modifyprofile" angepasst werden. Folgenden Code haben wir vor dem letzten Table-Tag in diesem Template eingefügt:
Code:
<table class="tborder" cellpadding="$stylevar[cellpadding]" cellspacing="$stylevar[cellspacing]" border="0" width="100%" align="center">
<tr>
	<td class="thead">Mitgliederkarte - Optional zur Ausgabe deines Wohnortes auf der Mitgliederkarte</td>
</tr>
<tr>
	<td class="panelsurround" align="center">
	<div class="panel">
		<div style="width:$stylevar[formwidth_usercp]" align="$stylevar[left]">
			
			<fieldset class="fieldset">
				<legend>Land und Postleitzahl</legend>
				<table cellpadding="0" cellspacing="$stylevar[formspacer]" border="0" width="100%">
				<tr>
					<td>Land:</td>
				</tr>
				<tr>
					<td><select name="cb_country">$cb_countrydropdown</select></td>
				</tr>
				<tr>
					<td>Postleitzahl:</td>
				</tr>
				<tr>
					<td><input type="text" name="cb_postcode" size="5" value="$cb_postcode" /></td>
				</tr>
				</table>
			</fieldset>

			<fieldset class="fieldset">
				<legend>Genaue Ortsangabe</legend>
				<if condition="$cb_citydropdown != ''">
				<table cellpadding="0" cellspacing="$stylevar[formspacer]" border="0" width="100%">
				<tr>
					<td>Orte mit oben angegebener Postleitzahl:</td>
				</tr>
				<tr>
					<td><select name="cb_locationid">$cb_citydropdown</select></td>
				</tr>
				</table>
				<else />
				In der Datenbank konnten keine zu dieser Postleitzahl gehörenden Orte gefunden werden!
				</if>
			</fieldset>

		</div>
	</div>
	</td>
</tr>
</table>

<br />
Nun sollte man in der Lage sein, in seinem Profil Land+PLZ einzugeben. Zudem sollte man daraufhin alle in diesem Postleitzahlbereich liegenden Orte per Dropdown-Menü auswählen können.


Das Script, das die Membermap alle paar Minuten generiert (und dafür z.B. per Cronjob alle paar Minuten ausgeführt werden muss!), sieht wie folgt aus (am Anfang müssen ein paar Konfigurationsvariablen gesetzt werden!):
PHP:
#!/usr/bin/php
<?php
/*
// Konfiguration
*/
define('DSN', "mysql://__BENUTZER__:__PASSWORT__@__SERVER__/__DATENBANK__");
$radii = array(0 => 1, 2 => 2, 5 => 3, 10 => 4);

// Pfad zu Template- und Ausgabe-Grafiken
$maptemplate = '/var/www/localhost/templates/membermap.png';
$mapoutput = '/var/www/localhost/htdocs/membermap/map.png';


/*
// Geo-Klasse
*/
require_once('Geo/Geo.php');
$geodb = Geo::setupSource('DB', DSN);

$db = DB::connect(DSN, true);
if (DB::isError($db)) {
	die ($db->getMessage());
}

$map2 = Geo::setupMap($maptemplate);
$map2->setRange(5.68, 17.2, 45.8, 55.1);


/*
// Eingegebene Länder und Postleitzahlen auslesen
*/
$sql = "SELECT cb_locationid FROM vb_userfield WHERE cb_locationid!=0 GROUP BY cb_locationid";
$res = $db->query($sql);

$users = array();
while ($row = $res->fetchRow()) {
	list($locationid) = $row;

	/*
	// Stadtname und Koordinaten auslesen
	*/
	$sql = "SELECT name, latitude, longitude FROM geo_locations WHERE locationid=$locationid";
	$temp = $geodb->performQuery($sql);
	if (count($temp) == 0) {
		continue;
	}
	list($ort) = $temp;

	/*
	// Boardies aus dieser Stadt auslesen
	*/
	$sql = "SELECT
		vb_user.userid,
		vb_user.username,
		vb_userfield.cb_postcode,
		vb_userfield.cb_country
		FROM vb_userfield
		LEFT JOIN vb_user USING(userid)
		WHERE vb_userfield.cb_locationid=$locationid
		ORDER BY vb_user.username ASC";
	$res2 = $db->query($sql);

	while ($row2 = $res2->fetchRow()) {
		$i = $map2->addGeoObjectIncrease($ort, 'orange', $radii);
		$users[$i][] = "<a href=\"member.php?u=$row2[0]\">$row2[1]</a>";
	}
}

$map2->saveImage($mapoutput);
$code = $map2->getImageMap('map');

$userarray = array();
foreach ($users as $users2) {
	$userarray[] = "'" . str_replace('\'', '\\\'', implode(', ', $users2)) . "'";
}

$code .= "\n\n<script type=\"text/javascript\">\nvar mmap_users = new Array(\n";
$code .= implode(",\n", $userarray);
$code .= "\n);\n</script>\n";

// In der $code-Variable steht nun der JavaScript-Code zum Anzeigen der Popups
// Dieser muss irgendwo gespeichert werden. Zum Beispiel in einer Datenbank,
// aber eine normale Datei tut es auch. Zum Beispiel:
$mapdatafile = '/var/www/localhost/membermap/membermap.data';
file_put_contents($mapdatafile, $code);
?>


Die Datei membermap.php, die für die Ausgabe der Karte zuständig ist und die man im vBulletin-Verzeichnis bei den anderen PHP-Dateien ablegt, sieht wie folgt aus (Wert von $mapdatafile gegen Ende anpassen!):
PHP:
<?php
// ######################## SET PHP ENVIRONMENT ###########################
error_reporting(E_ALL & ~E_NOTICE);

// ##################### DEFINE IMPORTANT CONSTANTS #######################
define('THIS_SCRIPT', 'calendar');

// ################### PRE-CACHE TEMPLATES AND DATA ######################
// pre-cache templates used by all actions
$globaltemplates = array('membermaphome');

// ######################### REQUIRE BACK-END ############################
require_once('./global.php');

// #######################################################################
// ######################## START MAIN SCRIPT ############################
// #######################################################################

// get permissions to view forumhome
if (!($permissions['forumpermissions'] & $vbulletin->bf_ugp_forumpermissions['canview']))
{
	print_no_permission();
}

// draw nav bar
$navbits = array('' => 'Mitgliederkarte');
$navbits = construct_navbits($navbits);

$mapdatafile = "/var/www/localhost/membermap/membermap.data";
$html = file_get_contents($mapdatafile);

eval('$navbar = "' . fetch_template('navbar') . '";');
eval('print_output("' . fetch_template('membermaphome') . '");');
?>

Nun fehlt nur noch das Template "membermaphome", das man im AdminCP von vBulletin hinzufügt:
Code:
$stylevar[htmldoctype]
<html dir="$stylevar[textdirection]" lang="$stylevar[languagecode]">
<head>
$headinclude
<script type="text/javascript" src="/clientscripts/cb_membermap.js"></script>
<link rel="stylesheet" type="text/css" href="/stylesheets/cb_membermap.css" />
<title>$vboptions[bbtitle] - Mitgliederkarte</title>
</head>
<body onload="mmap_watch_areas()">
$header
$navbar

<table class="tborder" cellpadding="$stylevar[cellpadding]" cellspacing="$stylevar[cellspacing]" border="0" width="100%" align="center">
<tr>
	<td class="tcat">
		<strong>Mitgliederkarte</strong>
	</td>
</tr>
<tr>
	<td class="alt1" align="center">

$html
<div align="center">
	<img src="https://pics.computerbase.de/membermap/map.png" width="950" height="1243" usemap="#map" alt="" border="0" /><br />
	<small>Trage Postleitzahl und Land in deinem <a href="profile.php?do=editprofile">Profil</a> ein, nach spätestens vier Minuten erscheinst du dann auf der Karte!</small>
</div>

	</td>
</tr>
</table>

<div id="membermap_popup" style="display:none"></div>

$forumrules

$footer

</body>
</html>

Die darin erwähnten Dateien cb_membermap.css und cb_membermap.js (unsere Alternative zur overlib.js des Original MapHacks, funktioniert wahrscheinlich mit weniger Browsern, ist jedoch kleiner und hat einen netten Fade-In-Effekt) dürft ihr auf euren Server kopieren und modifizieren, vorausgesetzt dass der Hinweis am Anfang der Dateien erhalten bleibt!

Ich denke das war alles. Wie eingangs erwähnt ist dies keine eigenständige Anleitung, Grundvoraussetzung ist das Verständnis des verlinkten Original MapHack!
 
Zuletzt bearbeitet:
big thx, werde das so schnell wie möglich selber auf meinem board / server testen!
 
So, nach langem hin und her funzt die Mitgliederkarte bei mir...
Nur ein problem habe ich.
Ich wohne in einem Ort wo noch andere Orte die gleiche PLZ haben. Nun werden alle Orte auf der Karte angezeigt!!!
Wie habt ihr das hinbekommen, dass man im Profil in einem Dropdown Menü die Orte aufgelistet bekommt und sich dann den richtigen ort aussucht wo man wohnt?

Vielen Dank im vorraus!
 
Hallo Steffen, ich habe mich von dieser Mitgliederkarte inspirieren lassen und etwas ähnliches für Invisionboard 1.3 programmiert.

Das Datenbanklayout habe ich auch so ähnlich angelegt, allerdings frage ich mich nun, ob es überhaupt notwendig ist, die beiden geo-Tabellen zu trennen.

Es werden doch sowieso in den zeitkritischen Fällen nur "left join"-Abfragen durchgeführt, so dass wir eigentlich die beiden Tabellen zusammenführen können.

Code:
CREATE TABLE `geo_locations` (
  `locationid` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(255) NOT NULL default '',
  `postcode` mediumint(10) unsigned NOT NULL default '0',
  `country` enum('DE','AT','CH','FL') NOT NULL default 'DE',
  `latitude` float NOT NULL default '0',
  `longitude` float NOT NULL default '0',
  PRIMARY KEY  (`locationid`),
  KEY `postcode` (`postcode`,`country`)
) TYPE=MyISAM;

Ich habe das testweise mal abgeändert, kann aber mangels Eintragungen das Performanceproblem nicht untersuchen.

Spricht aus deiner Sicht etwas gegen die Änderung?

Das Zeitproblem, was ihr mit dem cronjob gelöst habt, erledige ich ebenfalls mit Caching, allerdings werden die Cachedaten nur dann neu angelegt, wenn sich etwas ändert. Also wenn ein neuer User sein Profilfeld ausfüllt bzw. ändert usw.

Dafür brauche ich also keinen cronjob.


Gruß Peter


PS:

Wen es interessiert:

Meine Testseite

Diese Seite ist nur ein Showroom für Mods und Skins für das IPB.

Auf unserer Seite ibforen.de ist etwas mehr Leben in der Mitgliederkarte zu sehen.
 
Ich sehe keinen Nachteil im Zusammenführen der beiden Tabellen abgesehen davon, dass man dann einige Daten redundant hat, aber das dürfte in diesem Fall praktisch egal sein, weil man ja nicht viel mit den Daten anstellen möchte.

Von der Performance her bringt das Zusammenführen denke ich kaum etwas, ein von einem Index Gebrauch machender JOIN ist glaube ich kaum langsamer als eine Query ohne JOINs.

Dass die Membermap nur wenn sich tatsächlich etwas geändert hat neu erstellt wird muss ich ebenfalls in absehbarer Zeit einbauen oder aber irgendwas anderes optimieren. Bei unseren ca. 3200 Einträgen auf der Mitgliederkarte dauert deren Generierung mittlerweile wieder knapp 10 Sekunden, was eindeutig zu viel ist. Ich habe die Vermutung, dass das an der Verwendung der addGeoObjectIncrease-Methode liegt, die einige Schleifen beinhaltet.

Wenn ich da eine Lösung habe werde ich das hier posten, ich weiß allerdings noch nicht wann ich das angehen werde.

Edit: Kurz getestet, mit addGeoObject anstatt addGeoObjectIncrease ist es fast doppelt so schnell, allerdings sind dann natürlich alle Punkte gleich groß. Und fünf Sekunden ist immer noch nicht sonderlich schnell. :)
 
Zuletzt bearbeitet:
Hallo zusammen,

ich habe da ein Verständnisproblem. Ich nutze noch die erste Version der Membermap, allerdings mit Erstellung per Cronjob. Nun wollte ich diese hier mal auf meinem Testserver ausprobieren, und hänge wohl an der letzten Stelle:

In der Map.php aus der GeoClass in Zeile 375 wird der Html-Code für die Imagemap erzeugt:

HTML:
$html .= '<area shape="circle" coords="'.round($koord['x']).','.round($koord['y']).','.$koord['r'].'" href="#" alt="'.$koord['name'].'">';
Dort wird keinerlei Ausgabe ala id="areaX" erzeugt.

Bei euch ist hinten noch ein
HTML:
id="area532"
dran. Auf Grund dessen ja aus dem Javascript der richtige Eintrag ausgewählt wird. Genau dieses id="areaX" wird bei mir aber nicht ausgegeben.

Habt Ihr die Map.php da entsprechend angepasst?

Danke

Thomas
 
Wie werden die Breiten- und Längengrade in Pixel für die Karte umgerechnet?
Kann mir mal jemand nur diesen Code ausschnitt schicken?
 
Danke für die Antwort. Habe das Problem jetzt gelöst.

Gibt es die Usermap ohne Punkte irgendwo zum runterladen?
 
Aufgrund von Performance-Problemen mit der bisherigen Implementierung wird nun folgendes Script zum periodischen Aktualisieren der Mitgliederkartengrafik eingesetzt.

Ich habe mir jetzt nicht die Mühe gemacht, das ganze so umzuprogrammieren, dass es Out-of-The-Box funktioniert. init.php stellt bei uns z.B. die Datenbankverbindung her und speichert in der Variablen $db einen Verweis auf das Datenbankobjekt. Die Datenbankklasse basiert auf PDO und ist um ein paar Methoden erweitert wie z.B. fetchPairs und update, deren Namen an entsprechende Methoden der Datenbank-Klasse des Zend Framework angelehnt sind.

Darüber hinaus definiert init.php z.B. eine Konstante TIMENOW mit dem Wert der time()-Funktion. $conf['image_dir'] zeigt auf das Verzeichnis der Grafikkdateien etc.

Das folgende Script wird also wie gesagt ohne kleinere Anpassungen nicht funktionieren. Vorausgesetzt man kennt sich mit PHP aus, sind die Änderungen aber nicht schwer. Ich habe nicht die Zeit es universell einsatzfähig zu machen, stelle es aber trotzdem hier bereit, damit es als Ausgangspunkt verwendet werden kann.

PHP:
#!/usr/bin/php
<?php
require('init.php');

require('Geo/Geo.php');
$map = Geo::setupMap($conf['image_dir'] . '/membermap/template.png');
$map->setRange(5.68, 17.2, 45.8, 55.1);

$users = array();
foreach ($db->fetchPairs('SELECT cb_locationid, COUNT(*) FROM vb_userfield
	WHERE cb_locationid!=0 GROUP BY cb_locationid') as $locationid => $citizens)
{
	$location = $db->query('SELECT name, latitude, longitude FROM geo_locations
		WHERE locationid=' . $locationid)->fetch(PDO::FETCH_OBJ);
	if ($location)
	{
		if ($citizens >= 10) $radius = 4;
		else if ($citizens >= 5) $radius = 3;
		else if ($citizens >= 2) $radius = 2;
		else $radius = 1;

		$map->addGeoObject($location, 'orange', $radius);
		$users[$locationid] = '';

		foreach ($db->fetchPairs("SELECT vb_user.userid, vb_user.username
			FROM vb_userfield LEFT JOIN vb_user USING(userid)
			WHERE vb_userfield.cb_locationid=$locationid
			ORDER BY vb_user.username ASC") as $userid => $username)
		{
			if (!empty($users[$locationid]))
			{
				$users[$locationid] .= ', ';
			}
			$username = str_replace('\'', '\\\'', $username);
			$users[$locationid] .= "<a href=\"member.php?u=$userid\">$username</a>";
		}
	}
}

$map->saveImage($conf['image_dir'] . '/membermap/map.png');
$code = $map->getImageMap('map');
$code .= "\n\n<script type=\"text/javascript\">\nvar mmap_users = ['"
	. implode("','", $users) . "'];\n</script>\n";

$db->update('cb_cache', array(
	'contents' => $code,
	'timestamp' => TIMENOW
), 'cacheid="forum_membermap"');
?>
 
Zurück
Oben