Exporting geo-nick information to GoogleEarth

Inspired by Marc Quinton I made an application for on-line generating KML files with the GPS coordinates of developers here. It can be used as a simple example of using some Gtk widget, PHP DOM extension, XMLRPC and for flying over our heads - enjoy :)

A KML file is a simple XML file which can be opened by GoogleEarth.

<?php
/*
*  KML file generator
*  (c) june 2007, Jan Fosfor Pospisil
*  jabber: fosfor@jabber.cz
*  icq: 96560956
*  email: fosfor.software@seznam.cz
*  web: http://www.vpp-net.com/php-gtk/
*  originaly posted at: http://www.php-gtk.eu/

*  This application is able to generate KML file (XML structure) from selected
*  user from the database of the developers registered at www.php-gtk.eu .

*  It show usage of widgets:
*     GtkAboutDialog, GtkMessageDialog
*     GtkFileSelection
*     GtkTreeView
*     menu
*     toolbar
*  and algorithm for:
*     DOM
*     XMLRPC

*  Original code of XMLRPC access to the server was adopted from Marc Quinton.

*  Thanks to all on www.php-gtk.eu and irc #php-gtk :)

*/

error_reporting(E_ALL);

require_once
'XML/RPC.php';

if (!
class_exists('gtk'))
    die(
'Please load the php-gtk2 module.'.PHP_EOL);

/*
*  handler for application end
*/
function hnd_quit() {
   
gtk::main_quit();
}

/*
*  handler for About menu item
*/
function hnd_about() {
   
$dlg = new GtkAboutDialog();
    
   
$dlg->set_name('KML generator');
   
$dlg->set_version('0.1');
    
   
$dlg->set_comments(
       
'Generate KML file (XML) whitch can by opened by GoogleEarth.'.PHP_EOL.
       
'The KML file contains GPS coordinations of PHP-GTK developers registered'.
       
' at www.php-gtk.eu.'
   
);
   
$dlg->set_copyright('Copyright (C) 2007 Jan Pospisil');
   
$dlg->set_license('Free for non-comercial use.');
   
$dlg->set_logo(
       
$dlg->render_icon(Gtk::STOCK_DIALOG_INFO, Gtk::ICON_SIZE_DIALOG)
    );
   
$dlg->set_website('http://www.vpp-net.com/php-gtk');

   
$dlg->run();
   
$dlg->destroy();
}

/*
*  function to reload data from the internet to the treeview
*/
function reload_data() {
    global
$model;

   
// Prepare the request data
   
$host    = 'www.php-gtk.eu';
   
$port    = 80;
   
$backend = '/xmlrpc.php';

   
// XML-RPC API description here : http://www.php-gtk.eu/site/geo-nick
   
$params = array ();
   
$msg = new XML_RPC_Message('gtkphpnet.geo_nick', $params);

   
$cli = new XML_RPC_Client($backend, $host);

   
$resp = $cli->send($msg);
    if (!
$resp)
        return
false// exception ?

   
if ($resp->faultCode())
        return
false// exception ?

   
$val = $resp->value();
   
$data = XML_RPC_decode($val);

   
$model->clear();
    foreach (
$data as $user => $coords) {
        if (
$coords[0] && $coords[1]) {
           
$tmp = array();
           
$tmp[0] = true;
           
$tmp[1] = $user;
           
$tmp[2] = $coords[1]*1;
           
$tmp[3] = $coords[0]*1;
           
$model->append($tmp);
        }
    }
}

/*
*  handler for select/deselect row
*/
function row_activated() {
    global
$tree, $model;
   
   
$selection = $tree->get_selection();
    list(
$model, $iter) = $selection->get_selected();
   
$value = $model->get_value($iter, 0);
   
$model->set($iter, 0, !$value);
}

/*
*  function to show Save dialog
*/
function save() {
   
$filePrompt = new GtkFileSelection('Save to...');
   
$filePrompt->cancel_button->connect_simple('clicked', array($filePrompt, 'destroy'));
   
$filePrompt->ok_button->connect('clicked', 'export');
   
$filePrompt->show();
}

function
dialog_box($text, $inform = true) {
    global
$wnd;

   
$dialog = new GtkMessageDialog(
       
$wnd,
       
Gtk::DIALOG_DESTROY_WITH_PARENT,
        ((
$inform)?Gtk::MESSAGE_INFO:Gtk::MESSAGE_QUESTION),
        ((
$inform)?Gtk::BUTTONS_OK:Gtk::BUTTONS_YES_NO),
       
$text
   
);

   
$answer = $dialog->run();
   
$dialog->destroy();

    if (
$inform)
        return
true;

    if (
$answer == Gtk::RESPONSE_YES)
        return
true;
    return
false;
}

/*
*  function to process one row into XML output
*/
function process_row($model, $path, $iter, $dom, $folder) {
   
$export = $model->get_value($iter, 0);
    if (!
$export)
        return;

   
$user = $model->get_value($iter, 1);
   
$long = $model->get_value($iter, 2);
   
$lat = $model->get_value($iter, 3);

   
$placemark = $dom->createElement('Placemark');
       
$pm_name = $dom->createElement('name', $user);
       
$pm_vis = $dom->createElement('visibility', '1');
       
$pm_style = $dom->createElement(
           
'styleUrl',
           
'#default+icon=http://maps.google.com/mapfiles/kml/pal3/icon63.png'
       
);
       
$pm_point = $dom->createElement('Point');
           
$coords = $dom->createElement(
               
'coordinates',
               
str_replace(',', '.', $long) . ',' . str_replace(',', '.', $lat) . ',0'
           
);
   
   
$folder->appendChild($placemark);
       
$placemark->appendChild($pm_name);
       
$placemark->appendChild($pm_vis);
       
$placemark->appendChild($pm_style);
       
$placemark->appendChild($pm_point);
           
$pm_point->appendChild($coords);
}

/*
*  function to process all selected rows into KML file (XML output)
*/
function export($okbutton) {
    global
$model;
   
   
$filePrompt = $okbutton->get_toplevel();
   
$fileName = $filePrompt->get_filename();

    if (
is_dir($fileName)) {
       
dialog_box('Please select file to write the data.');
        return;
    } else if (
file_exists($fileName))
        if (!
dialog_box('File exitst. Overwrite?', false))
            return;
   
   
$filePrompt->destroy();

   
$dom = new DOMDocument('1.0', 'utf-8');
   
   
$main_kml = $dom->createElement('kml');
      {
$main_kml->setAttribute('xmlns', 'http://earth.google.com/kml/2.1');}
       
$document = $dom->createElement('Document');
           
$name = $dom->createElement('name', 'PHP-GTK developers');
           
$folder = $dom->createElement('Folder');
               
$folder_name = $dom->createElement('name', 'php-gtk.eu');
               
$folder_vis = $dom->createElement('visibility', '1');
   
   
$dom->appendChild($main_kml);
       
$main_kml->appendChild($document);
           
$document->appendChild($name);
           
$document->appendChild($folder);
               
$folder->appendChild($folder_name);
               
$folder->appendChild($folder_vis);
   
   
$model->foreach('process_row', $dom, $folder);
   
    if ((
strpos($fileName, '.') === false) && (!file_exists($fileName)))
       
$fileName .= '.kml';
   
$fp = fopen($fileName, 'w');
   
fwrite($fp, $dom->saveXML());
   
fclose($fp);
}

/*
*  function to process row by row when (de)selecting all
*/
function select_row($model, $path, $iter, $select = true) {
   
$model->set($iter, 0, $select);
}

/*
*  main application - building GUI
*/

/*
*  model for storing data
*/
$model = new GtkListStore(
   
Gtk::TYPE_BOOLEAN,
   
Gtk::TYPE_STRING,
   
Gtk::TYPE_DOUBLE,
   
Gtk::TYPE_DOUBLE
);
$model->set_sort_column_id(1, Gtk::SORT_ASCENDING);

/*
*  building GUI element and setting properities
*/
$wnd = new GtkWindow();
  {
$wnd->connect_simple('destroy', 'hnd_quit');}
  {
$wnd->set_title('KML generator');}
  {
$wnd->set_default_size(600, 600);}
  {
$wnd->set_position(Gtk::WIN_POS_CENTER);}
   
$main = new GtkVBox();
       
$mn = new GtkMenuBar();
           
$mniFile = new GtkMenuItem('_File');
               
$mnuFile = new GtkMenu();
                   
$mniFileQuit = new GtkImageMenuItem(Gtk::STOCK_QUIT);
                      {
$mniFileQuit->connect_simple('activate', 'hnd_quit');}
           
$mniHelp = new GtkMenuItem('_Help');
               
$mnuHelp = new GtkMenu();
                   
$mniHelpAbout = new GtkImageMenuItem(Gtk::STOCK_ABOUT);
                      {
$mniHelpAbout->connect_simple('activate', 'hnd_about');}
       
$tb = new GtkToolbar();
          {
$tb->set_show_arrow(false);}
           
$tbReload = new GtkToolButton(
               
GtkImage::new_from_stock(Gtk::STOCK_REFRESH, Gtk::ICON_SIZE_SMALL_TOOLBAR),
               
'Reload data'
           
);
              {
$tbReload->connect_simple('clicked', 'reload_data');}
           
$tbSave = GtkToolButton::new_from_stock(Gtk::STOCK_SAVE);
              {
$tbSave->connect_simple('clicked', 'save');}
           
$tbAll = new GtkToolButton(
               
GtkImage::new_from_stock(
                   
Gtk::STOCK_APPLY,
                   
Gtk::ICON_SIZE_SMALL_TOOLBAR
               
),
               
'Select All'
           
);
              {
$tbAll->connect_simple('clicked', array($model,'foreach'), 'select_row');}
           
$tbNone = new GtkToolButton(
               
GtkImage::new_from_stock(
                   
Gtk::STOCK_CANCEL,
                   
Gtk::ICON_SIZE_SMALL_TOOLBAR
               
),
               
'Select None'
           
);
              {
$tbNone->connect_simple('clicked', array($model,'foreach'), 'select_row', false);}
       
$scrl = new GtkScrolledWindow();
          {
$scrl->set_property('hscrollbar-policy', Gtk::POLICY_AUTOMATIC);}
          {
$scrl->set_property('vscrollbar-policy', Gtk::POLICY_AUTOMATIC);}
           
$tree = new GtkTreeView($model);
              {
$tree->set_search_column(1);}
              {
$tree->connect_simple('row-activated', 'row_activated');}
               
$clmnExport = new GtkTreeViewColumn(
                   
'Export',
                    new
GtkCellRendererToggle(),
                   
'active',
                   
0
               
);
                  {
$clmnExport->set_sort_column_id(0);}
               
$clmnName = new GtkTreeViewColumn('Name', new GtkCellRendererText(), 'text', 1);
                  {
$clmnName->set_sort_column_id(1);}
               
$clmnX = new GtkTreeViewColumn('Longitude', new GtkCellRendererText(), 'text', 2);
                  {
$clmnX->set_sort_column_id(2);}
               
$clmnY = new GtkTreeViewColumn('Latitude', new GtkCellRendererText(), 'text', 3);
                  {
$clmnY->set_sort_column_id(3);}

/*
*  connecting GUI elements together
*/
$wnd->add($main);
   
$main->pack_start($mn, false);
       
$mn->append($mniFile);
           
$mniFile->set_submenu($mnuFile);
               
$mnuFile->append($mniFileQuit);
       
$mn->append($mniHelp);
           
$mniHelp->set_submenu($mnuHelp);
               
$mnuHelp->append($mniHelpAbout);
   
$main->pack_start($tb, false);
       
$tb->insert($tbReload, -1);
       
$tb->insert($tbSave, -1);
       
$tb->insert($tbAll, -1);
       
$tb->insert($tbNone, -1);
   
$main->pack_start($scrl);
       
$scrl->add($tree);
           
$tree->append_column($clmnExport);
           
$tree->append_column($clmnName);
           
$tree->append_column($clmnX);
           
$tree->append_column($clmnY);

/*
*  and run it:)
*/

$wnd->show_all();
gtk::main();
?>

PS: actual KML can be downloaded at http://www.vpp-net.com/php-gtk/

PPS: to this script, the PEAR implementation of XML-RPC can be used as well as the standard xml-rpc extension