KML creator class


Update: This is now available on github!!! Much better! Check it.


For my latest project I needed a script to create a KML file from a set of data so it could be loaded into Google Earth. So here is the class if anyone else wants to do the same thing.

Here’s an example of how to use it:

<?php
// Include the class
include 'create.kml.php';

// Create a new KML object
$kml = new KML;

$styles = array(
  array('myStyle','iconId','http://example.com/icon.gif'),
  array('myStyle2','iconId2','http://example.com/icon2.gif')
);

foreach($styles as $style) {
  $s = new KMLStyle;
  $s->id = $style[0]; // Id of the style for use in placemarks
  $s->iconid = $style[1]; // Icon ID
  $s->iconhref = $style[2]; // Link to the icon

  // Add the style to the KML
  $kml->addItem($s);
}

// Get the data from the database
$q = mysql_query('SELECT id,name,desc,link,city,state,country,coords FROM table;');

while(list($id,$name,$desc,$folder,$coords,$style) = mysql_fetch_row($q)) {
  // Create a new place object
  $p = new KMLPlace;

  // Assign bits of the array
  $p->id = $id;
  $p->name = $name;
  $p->desc = $desc;
  $p->folder = $folder;
  $p->style = $style;
  $p->coords = $coords;

  // If you have HTML in both name and desc call this
  //$p->setCDATA();

  // If you have HTML in just desc then call this
  //$p->setCDATA('desc');

  // Add the item to the kml
  $kml->addItem($p);
}

// Create the KML
$kml->printHeader('xml'); // Allows the KML to be viewed in the browser
//$kml->printHeader(); // Forces the user to download the KML file

// Print the KML to the browser
echo $kml->create();

// Save the file as well if we want
$kml->save('output.kml');
?>

This is what the output would look like (I changed the extension to XML so you can view it in your browser).

Here’s a breakdown of each function:

KMLPlace::setCDATA()

This function encloses fields in a KMLPlace object (eg. $p) with <![CDATA[ and ]]> which allows Google Earth to process it as HTML. Its an XML standard thing. Called without an argument it will enclose ‘name’ and ‘desc’ in CDATA tags however you can put a comma separated string of all the fields you want enclosed as a function argument (eg. $p->setData(’name,style,desc’) or $p->setData(’desc’)).

KML::addItem($item)

This function adds an item to your KML object that you created (eg $kml). This must be an either a KMLPlace object (eg. $p) or a KMLStyle object (eg. $s). If the object given is not either of those it will return false.

KML::save(’output.kml’)

This function saves the KML data loaded into the KML object (eg. $kml) to the given filename. Without the filename it returns false.

KML::printHeader()

This function prints the content type header to the page so the browser knows what kind of content is coming after it. With no arguments it will output the content type for XML which is “application/vnd.google-earth.kml+xml”. However the browser usually thinks it can’t display this so automatically wants you to download it. If you give an argument that is boolean true to the function (eg. ‘xml’, 1 or true) it will send a much more browser friendly “text/xml” header and you will be able to view the KML in the browser.

KML::create()

This is the function that creates all the KML. It doesn’t need any arguments and will return to you a string. What you do with this string is up to you. I usually just echo it to the page. Be aware there are \t’s and \n’s in the output.

KML::clear()

This function will clear the KML data out of the KML object (eg. $kml) to save on memory. Only really need to do this if you are creating lots of KML files in the same script.

Yeah, thats the class, hope it helps some people. I bet there is already a class for this, but I couldn’t find one.

EDIT: Y’know what, here’s the whole class here:

<?php

    /**
    * Create a KML file
    *
    * Class for creating a KML file from a data source
    * and outputing it to either a file or string
    *  
    * PHP version 5
    *
    * @category     XML
    * @package      Create_KML
    * @author       Robert McLeod <hamstar[[@]]telescum.co.nz>
    * @copyright    2009 Robert McLeod
    * @license      http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
    * @version      SVN: 1.0
    *
    */

   
    /**
    * Class to define a style to be added to the KML class
    *
    */

    class KMLStyle
    {
        public $type = 'style';
        public $id, $iconid, $iconhref;
    }
   
    /**
    * Class to define a place to be added to the KML class
    *
    */

    class KMLPlace
    {
        public $type = 'place';
        public $folder = '**[root]**';
        public $id, $name, $desc, $style, $coords;
       
        /**
        * Sets the folder name or empty argument sets the folder to root
        *
        * @param string
        * @access public
        */

        public function setFolder($folder = '') {
            if ($folder == '') {
                $this->folder = '**[root]**';
            } else {
                $this->folder = $folder;
            }
        }
       
        /**
        * Sets a CDATA surround on the given comma separated fields
        * Called without params it will CDATA the name and desc fields
        *
        * @param string
        * @access public
        */

        public function setCDATA($fields = '') {
            if ($fields = '') {
                $this->desc = '<![CDATA['.$this->desc.']]>';
                $this->name = '<![CDATA['.$this->name.']]>';
            } else {
                if(strstr($fields,',')) {
                    $fields = explode(',',$fields);
                    foreach($fields as $f) {
                        $this->$f = '<![CDATA['.$this->$f.']]>';
                    }
                } else {
                    $this->$fields = '<![CDATA['.$this->$fields.']]>';
                }
            }
    }
   
    /**
    * Main class to add items to and create the KML
    *
    */

    class KML
    {
        /**
        * The array of styles
        *
        * @var array
        * @access protected
        */

        protected $styles = array();
       
        /**
        * The array of places in their folders
        *
        * @var array
        * @access protected
        */

        protected $folders = array();
       
        /**
        * Print the content header for a KML file or optionally an XML file
        * (so it can be viewed in a browser)
        *
        * @param bool
        * @access public
        */

        public function printHeader($xml = false)
        {
            if ($xml == false {
                header('Content-type: application/vnd.google-earth.kml+xml');
            } else {
                header('Content-type: text/xml');
            }
        }
       
        /**
        * Adds an item to the KML data
        *
        * @param object
        * @access public
        * @return bool
        */

        public function addItem($item)
        {
            if (!$item
                || gettype($item) != 'object'
                || $item->type != 'style'
                && $item->type != 'place'
            ) {
                return false;
            }
       
            // Switch which array to put the given item in
            switch ($item->type) {
                case 'place':
                    $this->folders[$item->folder][] = $item;
                    break;
                   
                case 'style':
                    $this->styles[] = $item;
                    break;
                   
                default:
                    return false;
                    break;
            }
            return true;
        }
   
        /**
        * Creates the file under the filename given with the KML code
        *
        * @param string     filename
        * @access public
        * @return bool
        */

        public function save($filename)
        {
            if (!$filename) {
                return false;
            }
           
            return file_put_contents($filename, $this->create());
        }
       
        /**
        * Creates the KML code from data given
        *
        * @access public
        * @return string
        */

        public function create()
        {
       
            // Set some tabs
            function pad($num, $str) {
                return str_pad($str,$num,"\t",STR_PAD_LEFT);
            }
       
            // Set the xml version and open the kml and document
            $kml[] = '<?xml version="1.0" encoding="UTF-8"?>';
            $kml[] = '<kml xmlns = "http://earth.google.com/kml/2.1">';
            $kml[] = pad(1, '<Document>');
           
            // Set all the styles for the document
            foreach ($this->styles as $s) {
                $kml[] = pad(2, '<Style id="'.$s->id.'">');
                $kml[] = pad(3, '<IconStyle id="'.$s->iconid.'">');
                $kml[] = pad(3, '<Icon>');
                $kml[] = pad(4, '<href>'.$s->iconhref.'</href>');
                $kml[] = pad(3, '</Icon>');
                $kml[] = pad(3, '</IconStyle>');
                $kml[] = pad(2, '</Style>');
            }
           
            // Set all the folders and places in each folder
            foreach ($this->folders as $f => $places) {
                if ($f != '**[root]**') {
                    // Set the folder and folder name
                    $kml[] = pad(2, '<Folder>');
                    $kml[] = pad(3, "<name>$f</name>");
                    $kml[] = pad(3, '<open>0</open>');
                   
                    // Set all the placemarks in this folder
                    foreach ($places as $p) {
                        $kml[] = pad(3, '<Placemark id="'.$p->id.'">');
                        $kml[] = pad(4, '<name>'.$p->name.'</name>');
                        $kml[] = pad(4, '<description>'.$p->desc.'</description>');
                        $kml[] = pad(4, '<styleUrl>'.$p->style.'</styleUrl>');
                        $kml[] = pad(4, '<Point>');
                        $kml[] = pad(5, '<coordinates>'.$p->coords.'</coordinates>');
                        $kml[] = pad(4, '</Point>');
                        $kml[] = pad(3, '</Placemark>');
                    }
                   
                    // Close the folder
                    $kml[] = pad(2, '</Folder>');
                }
            }
           
            // Set all the root placemarks so they are not in a folder
            foreach($this->folders['**[root]**'] as $p) {
                // Set all the placemarks in the root folder
                foreach ($places as $p) {
                    $kml[] = pad(2, '<Placemark id="'.$p->id.'">');
                    $kml[] = pad(3, '<name>'.$p->name.'</name>');
                    $kml[] = pad(3, '<description>'.$p->desc.'</description>');
                    $kml[] = pad(3, '<styleUrl>'.$p->style.'</styleUrl>');
                    $kml[] = pad(3, '<Point>');
                    $kml[] = pad(4, '<coordinates>'.$p->coords.'</coordinates>');
                    $kml[] = pad(3, '</Point>');
                    $kml[] = pad(2, '</Placemark>');
                }
            }
           
            // End the document and kml
            $kml[] = pad(1, '</Document>');
            $kml[] = '</kml>';
       
            // Return the string of KML data
            return join("\n", $kml);
        }
   
        /**
        * Clears all the data to free up memory
        *
        * @access public
        */

        public function clear()
        {
            $this->folders = null;
            $this->styles = null;
        }
    }
?>
Tags: , , , , , ,

Leave a Reply