<?php
/**
 * OziExplorer Waypoint text file to Google KML converter
 * @version 0.2
 * @author Pavel Rubin http://pash7ka.livejournal.com/74838.html
 * @license http://www.gnu.org/licenses/gpl.html GNU GPL
 * @link http://www.hphoenix.net/oziwpt2kml/oziwpt2kml.php
 *
 * Format description may be found at
 * http://www.rus-roads.ru/gps/help_ozi/importing_waypoints.html
 *
 * This converter is currently supports only files exported
 * with WGS 84 Datum and D, DMX, DMSX string formats.
 *
 *
 * Version history:
 * -- 0.1 - 2010-07-13 --
 * Initial release
 * -- 0.2 - 2010-07-22 --
 * + Skip empty lines
 * ! Fixed DMSX format parsing
 * + D and DMSX formats parsing was checked and become "officialy" supported
 */

// ---- SETTINGS ----

// Charset of input file
define('INPUT_CHARSET''windows-1251');

// Uncomment and modify the line bellow if you would like to change default timezone
// which is used to parse waypoint date/time
//date_default_timezone_set('GMT'); 


// ---- Main ----
return_source_if_required();
$error '';
$inpFile get_input_filename();
if(
$inpFile == false){
    
print_welcome();
    die();
}
$waypoints read_file($inpFile);
if(
$waypoints == false){
    
print_welcome();
    die();
}
//var_dump($waypoints);
//print_kml($waypoints);
output_waypoints($waypoints);

// ---- Function to read input ---
function get_input_filename(){
    return 
is_web_mode()?get_web_input_filename():get_cmd_input_filename();
}
function 
get_web_input_filename(){
    global 
$error;
    if(!isset(
$_FILES['in']['tmp_name'])){
            
//File was not uploaded
            
if($_SERVER['REQUEST_METHOD'] == 'POST'){
                
//... but form was submited
                
$error 'No file submited';
            }
            return 
false;
    }
    
$file $_FILES['in']['tmp_name'];
    if(!
is_readable($file)){
        
$error 'Can\'t read submited file.';
        return 
false;
    }
    return 
$file;
}
function 
get_cmd_input_filename(){
    global 
$error$argv;
    if(!isset(
$argv[1])){
        
$error 'Input file not specified!';
        return 
false;
    }
    
$file $argv[1];
    if(!
is_readable($file)){
        
$error 'Can\'t read file: '.$file;
        return 
false;
    }
    return 
$file;
}
function 
read_file($file){
    global 
$error;
    
$fp fopen($file'r');
    if(
$fp === false){
        
$error 'Can\'t open input file';
        return 
false;
    }
    
$headerLine fgets($fp4096);
    if(
$headerLine === false){
        
$error 'Can\'t read header line';
        
fclose($fp);
        return 
false;
    }
    if(!
preg_match('/^Datum\s*,\s*WGS\s+84$/i'trim($headerLine))){
        
$error 'File format is not supported: wrong header line, should be "Datum,WGS 84"';
        
fclose($fp);
        return 
false;
    }
    
$waypoints = array();
    
$lineCount 2;
    while(!
feof($fp)){
        
$buffer fgets($fp4096);
        if(
trim($buffer) == '') continue;
        
$line read_line($buffer);
        if(
$line === false){
            
$error "Line ${lineCount} error: ".$error;
            
fclose($fp);
            return 
false;
        }
        
$waypoints[] = $line;
        
$lineCount++;
    }
    
fclose($fp);
    return 
$waypoints;
}
// ---- Parser functions ---
function read_line($line){
    global 
$error;
    
$parts explode(','$line);
    
    for(
$i=0$i count($parts); $i++) $parts[i] = trim($parts[i]);
    
    if(
$parts['0'] != 'WP'){
        
$error 'Line should start with "WP"';
        return;
    }
    
$wp = array();
    
$format $parts['1'];
    switch(
$format){
        case 
'D':
            
$coorFieldCount 2;
            if(!
is_parts_length_ok($parts$coorFieldCount)) return false;
            
$res parse_d_coors($wp$parts);
            if(
$res == false) return false;
            break;
        case 
'DMX':
            
$coorFieldCount 4;
            if(!
is_parts_length_ok($parts$coorFieldCount)) return false;
            
$res parse_dmx_coors($wp$parts);
            if(
$res == false){
                return 
false;
            }
            break;
        case 
'DMSX':
            
$coorFieldCount 6;
            if(!
is_parts_length_ok($parts$coorFieldCount)) return false;
            
$res parse_dmsx_coors($wp$parts);
            if(
$res == false) return false;
            break;
        default:
            
$error 'Unknown line data format';
            return 
false;
    }
    
$wp['name'] = iconv(INPUT_CHARSET'UTF-8'$parts[2]);
    
$dateStr $parts[$coorFieldCount 3].' '.$parts[$coorFieldCount 4];
    if(
trim($dateStr) == ''){
        
$date time();
    }else{
        
$date strtotime($dateStr);
    }
    if(
$date == false){
        
$error 'Can\'t parse date/time of waypoint';
        return 
false;
    }
    
$wp['date'] = date('c',$date); //gmdate('Y-m-d\TH:i:s\Z',$date);
    
$wp['description'] = iconv(INPUT_CHARSET'UTF-8'$parts[$coorFieldCount 5]);
    return 
$wp;
}
function 
is_parts_length_ok($parts$coorFieldCount){
    global 
$error;
    if(
count($parts) <= $coorFieldCount 5){
        
$error 'Not enougth fields in line: '.count($parts);
        return 
false;
    }else{
        return 
true;
    }
}
function 
parse_d_coors(&$wp$parts){
    global 
$error;
    
$wp['lat'] = parse_coors_deg($parts[3]);
    
$wp['lon'] = parse_coors_deg($parts[4]);
    return 
true;
}
function 
parse_dmx_coors(&$wp$parts){
    global 
$error;
    
$wp['lat'] = parse_coors_deg_min($parts[3], $parts[4]);
    
$wp['lon'] = parse_coors_deg_min($parts[5], $parts[6]);
    return 
true;
}
function 
parse_dmsx_coors(&$wp$parts){
    global 
$error;
    
$wp['lat'] = parse_coors_deg_min_sec($parts[3], $parts[4], $parts[5]);
    
$wp['lon'] = parse_coors_deg_min_sec($parts[6], $parts[7], $parts[8]);
    return 
true;
}
function 
parse_coors_deg($deg){
    return 
round(1000000*$deg)/1000000;
}
function 
parse_coors_deg_min($deg,$min){
    if(
$min 60) return 0//wrong value
    
return round(1000000*($deg $min/60))/1000000;
}
function 
parse_coors_deg_min_sec($deg,$min,$sec){
    if(
$sec 60) return 0//wrong value
    
if($min 60) return 0//wrong value
    
$min $min $sec/60;
    return 
round(1000000*($deg $min/60))/1000000;
}

// ---- Function to output result or help ---
function output_waypoints($waypoints){
    
is_web_mode()?output_waypoints_web($waypoints):output_waypoints_cmd($waypoints);
}
function 
output_waypoints_web($waypoints){
    
$outputName get_output_file_name();
    
header('Content-type: text/xml');
    if(
$outputName != null){
        
header('Content-Disposition: attachment; filename="'.$outputName.'"');
    }
    
print_kml($waypoints);
}
function 
output_waypoints_cmd($waypoints){
    
$outputName get_output_file_name();
    if(
$outputName == null){
        
print_kml($waypoints);
    }else{
        
ob_start();
        
print_kml($waypoints);
        
$kml ob_get_contents();
        
ob_end_clean();
        
$res file_put_contents($outputName$kml);
        if(
$res === false){
            die(
'Can not write output file: '.$outputName);
        }
    }
}

function 
print_welcome(){
    
is_web_mode()?print_web_welcome():print_cmd_welcome();
}

function 
print_web_welcome(){
    global 
$error;
    echo <<<EOT
<html>
    <head>
        <title>OziExplorer WPT to KML converter</title>
    </head>
    <body>
        <div><b>OziExplorer Waypoint text file</b> to Google <b>KML</b> converter</div>
        <div style="font-size: small;">Current version supports only WGS 84 Datum and D, DMX, DMSX string formats.</div>
        <div style="color:red;">
${error}</div>
        <form action="
${_SERVER['PHP_SELF']}" method="POST" enctype="multipart/form-data">
            Convert file: <input type="file" name="in" /><br />
            <input type="submit"/>
        </form>
        <div style="font-size: small;">This converter is licened under <a href="http://www.gnu.org/licenses/gpl.html">GNU GPL</a>
        and its source may be <a href="
${_SERVER['PHP_SELF']}?source=view">viewed</a>
        or <a href="
${_SERVER['PHP_SELF']}?source=download">downloaded</a>.<br />
        Any comments and suggestions are <a href="http://pash7ka.livejournal.com/74838.html?mode=reply">welcomed</a>! :)</div>
    </body>
</html>
EOT;
}
function 
print_cmd_welcome(){
    global 
$argv$error;
    echo <<<EOT
OziExplorer Waypoint text file to Google KML converter
Current version supports only WGS 84 Datum and D, DMX, DMSX string formats.

Usage: php 
${argv[0]} <input_file> [output_file]
EOT;
    if(!empty(
$error)){
        echo 
"\nError: ${error}\n";
    }
}

function 
print_kml($waypoints){
    
$docName get_output_file_name();
    if(
$docName == null){
        
$docName date('Y-m-d_H-i-s');
    }else{
        
$docName basename($docName'.kml');
    }
    echo <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
    xmlns:gx="http://www.google.com/kml/ext/2.2">
  <Document>
    <name>
${docName}</name>
    <Folder>
      <name>
${docName}</name>
XML;
    foreach(
$waypoints as $wp){
        
print_kml_wp($wp);
    }
    echo <<<XML

    </Folder>
  </Document>
</kml>
XML;
}
function 
print_kml_wp($wp){
    echo <<<XML

      <Placemark>
        <name>
${wp['name']}</name>
        <description>
${wp['description']}</description>
        <TimeStamp><when>
${wp['date']}</when></TimeStamp>
        <Point>
          <coordinates>
${wp['lon']},${wp['lat']},0.000000</coordinates>
        </Point>
      </Placemark>
XML;

// ---- Other function ---
function is_web_mode(){
    global 
$argv;
    return !isset(
$argv);
}
function 
get_output_file_name(){
    global 
$argv;
    if(
is_web_mode()){
        return 
get_basename_with_kml_ext($_FILES['in']['name']);
    }else{
        if(isset(
$argv[2])){
            return 
$argv[2];
        }else{
            
$name get_basename_with_kml_ext($argv[1]);
            if(!
file_exists($name)){
                return 
$name;
            }else{
                return 
null;
            }
        }
    }
}
function 
get_basename_with_kml_ext($orig_name){
    
$basename basename($orig_name'.txt');
    
$basename basename($basename'.wp');
    
$basename basename($basename'.wpt');
    if(empty(
$basename)) $basename date('Y-m-d_H-i-s');
    return 
$basename.'.kml';
}

function 
return_source_if_required(){
    
$title basename($_SERVER['PHP_SELF']);
    if(isset(
$_GET['source'])){
        if(
$_GET['source'] == 'view'){
            
header('Content-type: text/html');
            echo <<<EOT
<html>
<head>
    <title>
${title}</title>
</head>
<body>
EOT;
            
highlight_file(__FILE__);
            echo <<<EOT
</body>
</html>
EOT;
            die();
        }else{
            
header('Content-type: application/x-httpd-php-source');
            
header('Content-Disposition: attachment; filename="'.$title.'"');
            
readfile(__FILE__);
            die();
        }
    }
}

?>