Convert from Google KML to GPS Exchange Format GPX with PHP

Here is a quick post on converting from KML files used in Google Earth/Maps to GPX Exchange Format (GPX) with PHP.

Code is quite explanatory, change $u with the location of the KML file, code will output GPX XML.

Alternatively, download the code here.

<?php
	//enter location of KML file here
	$u = "http://code.google.com/apis/kml/documentation/KML_Samples.kml";
	
	
	function utcdate() {
		return gmdate("Y-m-d\Th:i:s\Z");
	}

	
	$u_parts = pathinfo($u); //array of url parts
	$u_ext = strtoupper($u_parts['extension']);
	if ($u_ext== "KML") {

		$dom_kml = new DOMDocument();
		$dom_kml->load($u);

		$dom_gpx = new DOMDocument('1.0', 'UTF-8');
		$dom_gpx->formatOutput = true;
		
		//root node
		$gpx = $dom_gpx->createElement('gpx');
		$gpx = $dom_gpx->appendChild($gpx);
		
		$gpx_version = $dom_gpx->createAttribute('version');
		$gpx->appendChild($gpx_version);
		$gpx_version_text = $dom_gpx->createTextNode('1.0');
		$gpx_version->appendChild($gpx_version_text);
		
		$gpx_creator = $dom_gpx->createAttribute('creator');
		$gpx->appendChild($gpx_creator);
		$gpx_creator_text = $dom_gpx->createTextNode('http://thydzik.com');
		$gpx_creator->appendChild($gpx_creator_text);
		
		$gpx_xmlns_xsi = $dom_gpx->createAttribute('xmlns:xsi');
		$gpx->appendChild($gpx_xmlns_xsi);
		$gpx_xmlns_xsi_text = $dom_gpx->createTextNode('http://www.w3.org/2001/XMLSchema-instance');
		$gpx_xmlns_xsi->appendChild($gpx_xmlns_xsi_text);
		
		$gpx_xmlns = $dom_gpx->createAttribute('xmlns');
		$gpx->appendChild($gpx_xmlns);
		$gpx_xmlns_text = $dom_gpx->createTextNode('http://www.topografix.com/GPX/1/0');
		$gpx_xmlns->appendChild($gpx_xmlns_text);
		
		$gpx_xsi_schemaLocation = $dom_gpx->createAttribute('xsi:schemaLocation');
		$gpx->appendChild($gpx_xsi_schemaLocation);
		$gpx_xsi_schemaLocation_text = $dom_gpx->createTextNode('http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd');
		$gpx_xsi_schemaLocation->appendChild($gpx_xsi_schemaLocation_text);
		
		$gpx_url = $dom_gpx->createElement('url');
		$gpx_url = $gpx->appendChild($gpx_url);
		$gpx_url_text = $dom_gpx->createTextNode($u_parts['dirname']);
		$gpx_url->appendChild($gpx_url_text);
		
		$gpx_time = $dom_gpx->createElement('time');
		$gpx_time = $gpx->appendChild($gpx_time);
		$gpx_time_text = $dom_gpx->createTextNode(utcdate());
		$gpx_time->appendChild($gpx_time_text);
		
		// placemarks
		$names = array();
		foreach ($dom_kml->getElementsByTagName('Placemark') as $placemark) {
			//name
			foreach ($placemark->getElementsByTagName('name') as $name) {
				$name  = $name->nodeValue;
				//check if the key exists
				if (array_key_exists($name, $names)) {
					//increment the value
					++$names[$name];
					$name = $name." ({$names[$name]})";
				} else {
					$names[$name] = 0;
				}
			}
			//description
			foreach ($placemark->getElementsByTagName('description') as $description) {
				$description  = $description->nodeValue;
			}
			foreach ($placemark->getElementsByTagName('Point') as $point) {
				foreach ($point->getElementsByTagName('coordinates') as $coordinates) {
					//add the marker
					$coordinate = $coordinates->nodeValue;
					$coordinate = str_replace(" ", "", $coordinate);//trim white space
					$latlng = explode(",", $coordinate);
					
					if (($lat = $latlng[1]) && ($lng = $latlng[0])) {
						$gpx_wpt = $dom_gpx->createElement('wpt');
						$gpx_wpt = $gpx->appendChild($gpx_wpt);

						$gpx_wpt_lat = $dom_gpx->createAttribute('lat');
						$gpx_wpt->appendChild($gpx_wpt_lat);
						$gpx_wpt_lat_text = $dom_gpx->createTextNode($lat);
						$gpx_wpt_lat->appendChild($gpx_wpt_lat_text);
						
						$gpx_wpt_lon = $dom_gpx->createAttribute('lon');
						$gpx_wpt->appendChild($gpx_wpt_lon);
						$gpx_wpt_lon_text = $dom_gpx->createTextNode($lng);
						$gpx_wpt_lon->appendChild($gpx_wpt_lon_text);
						
						$gpx_time = $dom_gpx->createElement('time');
						$gpx_time = $gpx_wpt->appendChild($gpx_time);
						$gpx_time_text = $dom_gpx->createTextNode(utcdate());
						$gpx_time->appendChild($gpx_time_text);
						
						$gpx_name = $dom_gpx->createElement('name');
						$gpx_name = $gpx_wpt->appendChild($gpx_name);
						$gpx_name_text = $dom_gpx->createTextNode($name);
						$gpx_name->appendChild($gpx_name_text);
						
						$gpx_desc = $dom_gpx->createElement('desc');
						$gpx_desc = $gpx_wpt->appendChild($gpx_desc);
						$gpx_desc_text = $dom_gpx->createTextNode($description);
						$gpx_desc->appendChild($gpx_desc_text);
						
						//$gpx_url = $dom_gpx->createElement('url');
						//$gpx_url = $gpx_wpt->appendChild($gpx_url);
						//$gpx_url_text = $dom_gpx->createTextNode($ref);
						//$gpx_url->appendChild($gpx_url_text);
						
						$gpx_sym = $dom_gpx->createElement('sym');
						$gpx_sym = $gpx_wpt->appendChild($gpx_sym);
						$gpx_sym_text = $dom_gpx->createTextNode('Waypoint');
						$gpx_sym->appendChild($gpx_sym_text);
					}
				}
			}
			foreach ($placemark->getElementsByTagName('LineString') as $lineString) {
				foreach ($lineString->getElementsByTagName('coordinates') as $coordinates) {
					//add the new track
					$gpx_trk = $dom_gpx->createElement('trk');
					$gpx_trk = $gpx->appendChild($gpx_trk);
					
					$gpx_name = $dom_gpx->createElement('name');
					$gpx_name = $gpx_trk->appendChild($gpx_name);
					$gpx_name_text = $dom_gpx->createTextNode($name);
					$gpx_name->appendChild($gpx_name_text);
					
					$gpx_trkseg = $dom_gpx->createElement('trkseg');
					$gpx_trkseg = $gpx_trk->appendChild($gpx_trkseg);
				
					$coordinates = $coordinates->nodeValue;
					$coordinates = preg_split("/[\s\r\n]+/", $coordinates); //split the coords by new line
					foreach ($coordinates as $coordinate) {
						$latlng = explode(",", $coordinate);
						
						if (($lat = $latlng[1]) && ($lng = $latlng[0])) {
							$gpx_trkpt = $dom_gpx->createElement('trkpt');
							$gpx_trkpt = $gpx_trkseg->appendChild($gpx_trkpt);

							$gpx_trkpt_lat = $dom_gpx->createAttribute('lat');
							$gpx_trkpt->appendChild($gpx_trkpt_lat);
							$gpx_trkpt_lat_text = $dom_gpx->createTextNode($lat);
							$gpx_trkpt_lat->appendChild($gpx_trkpt_lat_text);
							
							$gpx_trkpt_lon = $dom_gpx->createAttribute('lon');
							$gpx_trkpt->appendChild($gpx_trkpt_lon);
							$gpx_trkpt_lon_text = $dom_gpx->createTextNode($lng);
							$gpx_trkpt_lon->appendChild($gpx_trkpt_lon_text);
							
							$gpx_time = $dom_gpx->createElement('time');
							$gpx_time = $gpx_trkpt->appendChild($gpx_time);
							$gpx_time_text = $dom_gpx->createTextNode(utcdate());
							$gpx_time->appendChild($gpx_time_text);
						}
					}
				}
			}
		}
		header("Content-Type: text/xml");
		echo $dom_gpx->saveXML();
	}
?>

Let me know if you find any issues.

  • Jez

    Brilliant script mate!

    I encountered only one error when processing with the script, which is:

    Undefined offset: 1 in kml-to-gpx.php on line 147

    which is line line
    $latlng = explode(“,”, $coordinate);
    if (($lat = $latlng[1]) && ($lng = $latlng[0])) {

    which I think is caused by blank spaces before the first coordinate, and after the last coordinate, in any set in the source kml file. This blank space is added as latitude and longitude figure despite having no numerical value.

    Top work anyway!