#!/usr/bin/perl -w
# NAME: osm2ac3dd.pl
# AIM: Search for a 'way' id in an osm file, and generate an AC3D .ac file of the building model
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use Data::Dumper;
use Math::Trig qw(great_circle_distance great_circle_destination great_circle_bearing deg2rad rad2deg);
use LWP::Simple;
use Cwd;
my $os = $^O;
my $perl_dir = '/home/geoff/bin';
my $PATH_SEP = '/';
my $temp_dir = '/tmp';
if ($os =~ /win/i) {
    $perl_dir = 'C:\GTools\perl';
    $temp_dir = $perl_dir;
    $PATH_SEP = "\\";
}
unshift(@INC, $perl_dir);
require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n";
# log file stuff
our ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt";
open_log($outfile);

# user variables
my $VERS = "0.0.1 2013-05-14";
my $load_log = 0;
my $in_file = '';
my $verbosity = 0;
my $out_file = '';
my $start_line = 0;
my $end_line   = 0;
my $max_lines  = 0;

my $node_id = '';
my $way_id = '';
my $latitude  = '';
my $longitude = '';
my $user_lat = 0;
my $user_lon = 0;
my $closedway = 1;
my $nodecount = 0;
my $wayxml = '';
my $height    = 100; # 20;
my $modelname = "Building";
my $roofshape = 2;
my @way = ();

my $out_xml = $temp_dir.$PATH_SEP."temp.building.osm";

# constants
my $pi = 3.14159265358979323846;

# ### DEBUG ###
my $debug_on = 1;
my $def_out = $temp_dir.$PATH_SEP."temp.building.ac";
my $def_file = 'C:\GTools\perl\piccatwr.osm';
##my $def_file = 'D:\SAVES\OSM\australia.osm';
#my $def_nid = 14988821;
my $def_wid = 101917220; # Piccadilly Tower, Sydney - 32 storey, this est abt 100m height
# osm xml
# <way id="101917220" ...>
# <nd ref="1176558347"/>
# plus 8 more
# <tag v="Piccadilly Tower"/>
# <tag...
# </way>
# a 32-story building
### program variables
my @warnings = ();
my $cwd = cwd();

sub VERB1() { return $verbosity >= 1; }
sub VERB2() { return $verbosity >= 2; }
sub VERB5() { return $verbosity >= 5; }
sub VERB9() { return $verbosity >= 9; }

sub show_warnings($) {
    my ($val) = @_;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } else {
        prt( "\nNo warnings issued.\n\n" ) if (VERB9());
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        $msg .= "\n" if (!($msg =~ /\n$/));
        prt($msg);
    }
    show_warnings($val);
    close_log($outfile,$load_log);
    exit($val);
}


sub prtw($) {
   my ($tx) = shift;
   $tx =~ s/\n$//;
   prt("$tx\n");
   push(@warnings,$tx);
}

sub rad2m {
    my ($rad) = @_;
    return $rad * 6377997;
}

sub m2rad {
  my ($m) = @_;
  return $m / 6377997;
}

my $infile;

sub getline() {
    my $line = <$infile>;
    return if ( !$line );
    return $line;
}

sub get_nd_ids($) {
    my $ra = shift;
    my $inf = $in_file;
    if (! open($infile, "<$inf") ) {
        pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); 
    }
    my ($line,$id,$lat,$lon,$lnn,$fnd,$blen);
    $blen = scalar @{$ra};
    my %h = ();
    # hmmmm, seems can have DUPLICATED node ids
    foreach $id (@{$ra}) {
        $h{$id} = 1;
    }
    my $cnt = scalar keys(%h);
    prt("Processing [$inf] for $cnt nd ids... ($blen)\n");
    $blen = 0;
    $lnn = 0;
    $| = 1; # turn OFF stdout buffering
    while ($line = getline()) {
        $lnn++;
        if (($lnn % 100000) == 0) {
            prt(".");
            $blen++;
            if ($blen >= 100) {
                prt("\n");
                $blen = 0;
            }
        }
        if ($line =~ /<node id=[\'\"](\d+)[\'\"].*?\/>/) {
            $id = $1;
            if (defined $h{$id}) {
                ###my ($lat,$lon) = $line =~ /lat=[\'\"]([0-9\-\.]+)[\'\"] lon=[\'\"]([0-9\-\.]+)[\'\"]/gis;
                $fnd = 0;
                if ($line =~ /lat=[\'\"]([0-9\-\.]+)[\'\"]/gis) {
                    $lat = $1;
                    $fnd += 1;
                }
                if ($line =~ /lon=[\'\"]([0-9\-\.]+)[\'\"]/gis) {
                    $lon = $1;
                    $fnd += 2;
                }
                $way[$nodecount]->{'node'} = $id;
                $way[$nodecount]->{'lat'}  = $lat;
                $way[$nodecount]->{'lon'}  = $lon;
                $way[$nodecount]->{'x'}    = 0;
                $way[$nodecount]->{'y'}    = 0;
                $nodecount++;
                prt("\n") if ($blen);
                prt("$nodecount of $cnt: $id $lat $lon \n"); #if $verbose;
                $blen = 0;
                $h{$id} = $fnd;
                if ($nodecount >= $cnt) {
                    prt("Found all nodes, at line $. in file\n");
                    last;
                }
            }
        }
    }

    close($infile);
    prt("\n") if ($blen);
    foreach $id (keys %h) {
        $blen = $h{$id};
        if ($blen != 3) {
            prt("Seems missed node id [$id]? ($blen)\n");
        }
    }
    prt("Done get nds...\n");
    return \@way;
}

sub get_way_nd_ids($) {
    my $ra = shift;     # \@waylines
    $wayxml = join("\n",@{$ra});
    prt("\nWay lines\n");
    prt("$wayxml\n");
    my %ndids = ();
    foreach ($wayxml=~/nd ref=[\'\"]([0-9]+)[\'\"]/gis) {
        $ndids{$_ } = 1;
    }
    return \%ndids;
}

sub process_way_lines($) {
    my $ra = shift;     # \@waylines
    my $wayxml = join("\n",@{$ra});
    prt("\nWay lines\n");
    prt("$wayxml\n");
    my @ndids = ();
    foreach ($wayxml=~/nd ref=[\'\"]([0-9]+)[\'\"]/gis) {
        push( @ndids,$_ );
    }
    if (! @ndids) {
        prt("Failed to get and nd ids!\n");
        return;
    }

    get_nd_ids(\@ndids);

    ##prt(Dumper(@way));
    prt("\n");
    my ($i,$max,$reflat,$reflon,$lat,$lon);
    if ($user_lat) {
        $reflat = $latitude;
    } else {
        $reflat = $way[0]->{'lat'};
    }
    if ($user_lon) {
        $reflon = $longitude;
    } else {
        $reflon = $way[0]->{'lon'};
    }

    $max = scalar @way;
    prt("Reference lat [$reflat], lon [$reflon], applied to $max waypoints...\n");

    # Process data structure
    #if ($way[0]->{'node'}!=$way[$max]->{'node'}) {
    # Work around!! Consider all ways "closed", even if they are not!!
        $closedway = 1;
    # $closedway=0;
    # carp "This is not a closed way";
    # exit 1;  
    #} else {
    #  $closedway=1;
    #};

    for ($i = 0; $i < $max; $i++) {
        $lon = $way[$i]->{'lon'};
        $lat = $way[$i]->{'lat'};
        if (($lon == $reflon) && ($lat == $reflat)) {
            $way[$i]->{'x'} = 0;
            $way[$i]->{'y'} = 0;
        } else {
            my $distancerad = great_circle_distance(deg2rad($reflon),deg2rad(90-$reflat)
                                ,deg2rad($lon),deg2rad(90-$lat));
            my $bearingrad = great_circle_bearing(deg2rad($reflon),deg2rad(90-$reflat)
                                ,deg2rad($lon),deg2rad(90-$lat));
            my $distancem = &rad2m($distancerad);
            $way[$i]->{'x'} = $distancem * cos($bearingrad);
            $way[$i]->{'y'} = $distancem * sin($bearingrad);
        }
        prt("Way ".($i+1).": x=".$way[$i]->{'x'}.", y=".$way[$i]->{'y'}."\n"); # if $verbose;
    }
}

# Get osm building data from way
sub getosmdata {
    my ($wayheight) = $wayxml =~ /tag k=\"height\" v=[\'\"]([0-9]+)[\'\"]/gis;
    my ($waylevels) = $wayxml =~ /tag k=\"building:levels\" v=[\'\"]([0-9]+)[\'\"]/gis;
    my ($wayroofshape) = $wayxml =~ /tag k=\"building:roof:shape\" v=[\'\"]([0-9]+)[\'\"]/gis;
  
    # No height given, number of floors maybe?
    #if ($wayheight == "") {
    if ((!defined $wayheight || (length($wayheight) == 0))&&
        (defined $waylevels && length($waylevels))) {
        # NO way height, but have waylevels, then
        $wayheight = $waylevels * 3; # One floor is roughly 3m

    }
    # Use given/estimated height
    #if ($wayheight != "") {
    if (defined $wayheight && length($wayheight)) {
        $height = $wayheight;
    } else { 
        # Fall back to default height, with randomly an extra level
        $height = $height + int(rand(2))*3;
    }
  
    #if ($wayroofshape != "") {
    if (defined $wayroofshape && length($wayroofshape)) {
        $roofshape = $wayroofshape;
    } elsif ($height < 25) {
        # Estimate roof shape
        $roofshape = 2;
    }
    if (!defined $waylevels || (length($waylevels) == 0)) {
        $waylevels = int($height / 3);
    }
  
    return ($height,$waylevels,$roofshape);
}

sub get_ac3d_info() {
    my ($i,$max,$reflat,$reflon,$lat,$lon);
    my ($levels,$walltexture,$rooftexture,$node);
    my @side = ();
    my ($tc,$bl,$br,$tl,$tr);
    my @roofvert = ();
    my ($roofheight,$roofvert);
    my ($surfaces,$sidenum,$wall);
    my ($texturex,$texturey);
    my ($texturescalex,$texturescaley);

    if ($user_lat) {
        $reflat = $latitude;
    } else {
        $reflat = $way[0]->{'lat'};
    }
    if ($user_lon) {
        $reflon = $longitude;
    } else {
        $reflon = $way[0]->{'lon'};
    }

    $max = scalar @way;
    prt("Reference lat [$reflat], lon [$reflon], applied to $max waypoints...\n");

    # Process data structure
    #if ($way[0]->{'node'}!=$way[$max]->{'node'}) {
    # Work around!! Consider all ways "closed", even if they are not!!
        $closedway = 1;
    # $closedway=0;
    # carp "This is not a closed way";
    # exit 1;  
    #} else {
    #  $closedway=1;
    #};

    for ($i = 0; $i < $max; $i++) {
        $lon = $way[$i]->{'lon'};
        $lat = $way[$i]->{'lat'};
        if (($lon == $reflon) && ($lat == $reflat)) {
            $way[$i]->{'x'} = 0;
            $way[$i]->{'y'} = 0;
        } else {
            my $distancerad = great_circle_distance(deg2rad($reflon),deg2rad(90-$reflat)
                                ,deg2rad($lon),deg2rad(90-$lat));
            my $bearingrad = great_circle_bearing(deg2rad($reflon),deg2rad(90-$reflat)
                                ,deg2rad($lon),deg2rad(90-$lat));
            my $distancem = &rad2m($distancerad);
            $way[$i]->{'x'} = $distancem * cos($bearingrad);
            $way[$i]->{'y'} = $distancem * sin($bearingrad);
        }
        prt("Way ".($i+1).": x=".$way[$i]->{'x'}.", y=".$way[$i]->{'y'}."\n"); # if $verbose;
    }
    # got all x,y points relative points
    ($height,$levels,$roofshape) = getosmdata();
    # Number of vertices
    my $totalverts;
    my $layerverts;
    if ($closedway == 1) {
        $totalverts = (@way -1) * 2;
        $layerverts = @way - 1;
    } else {
        $totalverts = (@way) * 2;
        $layerverts = (@way);
    }

    # Disable gable roof for models with more than 4 corners.
    if (($roofshape == 2) and ($totalverts != 8 )) {
        $roofshape = 0;
    }

    # Textures
    if ($roofshape == 2) {
        $walltexture = "wall.png"; 
    } else {
        $walltexture = "flat".int(rand(3)).".png"; 
    }
    $rooftexture = "roof.png";

    my $ac3d = '';
    $ac3d .= "AC3Db\n";
    $ac3d .= "MATERIAL \"DefaultWhite\" rgb 1 1 1  amb 1 1 1  emis 0 0 0  spec 0.5 0.5 0.5  shi 64  trans 0\n";
    $ac3d .= "MATERIAL \"Roof\" rgb 1 1 1 amb 1 1 1 emis 0 0 0  spec 0.5 0.5 0.5  shi 64  trans 0\n";
    $ac3d .= "OBJECT world\n";
    $ac3d .= "kids 1\n";
    $ac3d .= "OBJECT group\n";
    $ac3d .= "name \"".$modelname."\"\n";
    if ($roofshape == 2) {
        $ac3d .= "kids 2\n";
    } else {
        $ac3d .= "kids 1\n";
    }
    $ac3d .= "OBJECT poly\n";
    $ac3d .= "name \"Walls\"\n";
    $ac3d .= "texture \"".$walltexture."\"\n";
    $ac3d .= "texrep 1 1\n";
    if ($roofshape == 0) {
        $ac3d .= "numvert ".$totalverts."\n";
    } else {
        $ac3d .= "numvert ".($totalverts+2)."\n";
    }

    for ( $node = 0; $node < $layerverts; $node++ ) {
        $ac3d .= $way[$node]->{'x'}." $height ".$way[$node]->{'y'}."\n";
    }
    for ( $node = 0; $node < $layerverts; $node++ ) {
        $ac3d .= $way[$node]->{'x'}." -5 ".$way[$node]->{'y'}."\n";
    }
    $side[0] = sqrt((abs($way[1]->{'x'}-$way[0]->{'x'})) ** 2+(abs($way[1]->{'y'}-$way[0]->{'y'})) ** 2);
    $side[1] = sqrt((abs($way[1]->{'x'}-$way[2]->{'x'})) ** 2+(abs($way[1]->{'y'}-$way[2]->{'y'})) ** 2);
    if ($roofshape == 2) {
        # Calculate length of two sides, to propose a roof orientation
        #$side[0] = sqrt((abs($way[1]->{'x'}-$way[0]->{'x'})) ** 2+(abs($way[1]->{'y'}-$way[0]->{'y'})) ** 2);
        #$side[1] = sqrt((abs($way[1]->{'x'}-$way[2]->{'x'})) ** 2+(abs($way[1]->{'y'}-$way[2]->{'y'})) ** 2);
        # Two vertices for the roof, location depends on roof orientation
        $roofheight = (2/3)*$side[$side[0] > $side[1]];
        if ($side[0] < $side[1]) { 
            $roofvert[0] = (($way[0]->{'x'}+$way[1]->{'x'})/2)." ".($height+$roofheight)." ".(($way[0]->{'y'}+$way[1]->{'y'})/2)."\n";
            $roofvert[1] = (($way[2]->{'x'}+$way[3]->{'x'})/2)." ".($height+$roofheight)." ".(($way[2]->{'y'}+$way[3]->{'y'})/2)."\n";
        } else {
            $roofvert[0] = (($way[1]->{'x'}+$way[2]->{'x'})/2)." ".($height+$roofheight)." ".(($way[1]->{'y'}+$way[2]->{'y'})/2)."\n";
            $roofvert[1] = (($way[3]->{'x'}+$way[0]->{'x'})/2)." ".($height+$roofheight)." ".(($way[3]->{'y'}+$way[0]->{'y'})/2)."\n";
        }
        $ac3d .= $roofvert[0];
        $ac3d .= $roofvert[1];
        $surfaces = $layerverts + 2;
        $ac3d .= "numsurf ".$surfaces."\n";
    } else {
        # Generate roof poly
        if ($closedway == 1) {
            $surfaces = $layerverts + 1;
            $ac3d .= "numsurf ".$surfaces."\n";
            $ac3d .= "SURF 0x20\n";
            $ac3d .= "mat 1\n";
            $ac3d .= "refs ".($layerverts-1)."\n";
            for ($node = $max-1; $node > 0; $node--) {
                $ac3d .= $node ." 0 0\n";
            }
        } else {
            $surfaces = $layerverts - 1;
            $ac3d .= "numsurf ".$surfaces."\n";
        }
    }

    # Generate walls
    $sidenum = 0;
    for ( $wall = 0; $wall < $layerverts; $wall++ ) {
        $ac3d .= "SURF 0x20\n";
        $ac3d .= "mat 0\n";
        $ac3d .= "refs 4\n";
        # Special case for final wall
        if (($wall == ($layerverts-1)) && ($closedway == 1)) {
            $tl = 0;
            $tr = $max - 1;
            $bl = $max;
            $br = (2*$max)-1;
        } else {
            $tr = $wall;
            $tl = $wall + 1;
            $br = $wall + $layerverts;
            $bl = $wall + $layerverts+1;
        }
        # calculate texture scales (how often should the texture repeat?)
        # one texture is 3m high and wide
        $texturex = 6;
        $texturey = 6;
  
        #if ($levels == "") {
        if (!defined $levels || (length($levels) == 0)) {
            if ($roofshape == 2) {
                $texturescaley = ($height + 5) / $texturey;
            } else {
                $texturescaley = ($height + 5) / 3 / 2 / 10;
            }
        } else { 
            if ($roofshape == 2) {
                $texturescaley = $levels / 2 / 2;
            } else {
                $texturescaley = $levels / 2 / 10;
            }
        }
        $texturescalex = $side[$sidenum] / 2 / $texturex;
        if ($sidenum == 1) {
            $sidenum = 0;
        } else {
            $sidenum++;
        }
  
        $ac3d .= $tr." -".$texturescalex." 0\n";
        $ac3d .= $tl." ".$texturescalex." 0\n";
        $ac3d .= $bl." ".$texturescalex." -".$texturescaley."\n";
        $ac3d .= $br." -".$texturescalex." -".$texturescaley."\n";
    }

    # Vertical end faces of the triangle roof
    if ($roofshape == 2) {
        $texturescaley = $roofheight / $texturey;
        $texturescalex = $side[($side[0] < $side[1])] / 2 / $texturex;
        for ($node = 0; $node < 2; $node++) {
            $bl = 1 + (2*$node) + ($side[0] > $side[1]);
            $br = 2 * $node + ($side[0] > $side[1]);
            $tc = ($totalverts) + $node;
            if ($bl > 3) { $bl = 0; }
            if ($br > 3) { $br = 0; }
            $ac3d .= "SURF 0x20\n";
            $ac3d .= "mat 0\n";
            $ac3d .= "refs 3\n";
            $ac3d .= $bl." ".$texturescalex." 0\n";
            $ac3d .= $br." ".$texturescalex." 0\n";
            $ac3d .= $tc." 0 ".$texturescaley."\n";
        }
        $ac3d .= "kids 0\n";
        $ac3d .= "OBJECT poly\n";
        $ac3d .= "name \"Roof\"\n";
        $ac3d .= "texture \"roof.png\"\n";
        $ac3d .= "texrep 1 1\n";
        $ac3d .= "numvert 6\n";
        for ( $node = 0; $node < $layerverts; $node++ ) {
            $ac3d .= $way[$node]->{'x'}." $height ".$way[$node]->{'y'}."\n";
        }
        # Two vertices for the roof
        $ac3d .= $roofvert[0];
        $ac3d .= $roofvert[1];
        $ac3d .= "numsurf 2\n";
        $texturescaley = $roofheight / $texturey;
        $texturescalex = $side[($side[0] < $side[1])] / 4 / $texturex;
  
        # Actual roof, exists of two faces
        for ($node = 0; $node < 2; $node++) {
            $bl = (2*$node) + ($side[0] > $side[1]);
            $br = $bl - 1;
            $tl = 5 - $node;
            $tr = 5 - 1 + $node;
            if ($bl > 3) { $bl = $bl-3; }
            if ($br > 3) { $br = $br-3; }
            if ($bl < 0) { $bl = $bl+4; }
            if ($br < 0) { $br = $br+4; }
            $ac3d .= "SURF 0x20\n";
            $ac3d .= "mat 1\n";
            $ac3d .= "refs 4\n";
            $ac3d .= $bl." ".$texturescalex." 0\n";
            $ac3d .= $br." -".$texturescalex." 0\n";
            $ac3d .= $tl." -".$texturescalex." 1\n";
            $ac3d .= $tr." ".$texturescalex." 1\n";
        }
        $ac3d .= "kids 0\n";
    } else {
        $ac3d .= "kids 0\n";
    }

    write2file($ac3d,$out_file);
    prt("Written AC3D to [$out_file]\n");
}

sub dump_osm_xml($$) {
    my ($rwa,$rna) = @_; # \@waylines,\@nodelines
    my $txt = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
    $txt .= "<osm version=\"0.6\" generator=\"$pgmname\">\n";
    $txt .= join("\n",@{$rna})."\n";
    $txt .= join("\n",@{$rwa})."\n";
    $txt .= "</osm>\n";
    write2file($txt,$out_xml);
    prt("Written osm xml to [$out_xml]\n");
}


sub process_in_file($) {
    my ($inf) = @_;
    prt("Openning [$inf]...\n");
    if (! open($infile, "<$inf") ) {
        pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); 
    }
    prt("Processing [$inf]...\n");
    my ($line,$inc,$lnn,$lncnt,$id,$inway);
    $lnn = 0;
    $lncnt = 0;
    $inway = 0;
    my $blen = 0;
    my @waylines = ();
    my @nodelines = ();
    my $got_first = 0;
    my $first_node_pos = 0;
    my $first_node_line = 0;
    $| = 1; # turn OFF stdout buffering
    while ($line = getline()) {
        chomp $line;
        $lnn++;
        if (($lnn % 100000) == 0) {
            prt(".");
            $blen++;
            if ($blen >= 100) {
                prt("\n");
                $blen = 0;
            }
        }
        next if (($start_line > 0)&&($lnn < $start_line));
        last if (($end_line > 0)&&($lnn > $end_line));
        last if (($max_lines > 0)&& ($lncnt > $max_lines));
        $lncnt++;
        if ($inway) {
            push(@waylines,$line);
            if ($line =~ /<\/way>/) {
                last;
            }
        } elsif ($line =~ /<way id=[\'\"](\d+)[\'\"].*?>/) {
            $id = $1;
            if ($id == $way_id) {
                push(@waylines,$line);
                $inway = 1;
                prt("\n") if ($blen);
                prt("$lnn: Found [$line]\n");
                $blen = 0;
            }
        } elsif (!$got_first && ($line =~ /<node id=[\'\"](\d+)[\'\"].*?\/>/)) {
            $id = $1;
            $got_first = 1;
            $first_node_pos = tell($infile);
            if ($first_node_pos > (length($line) + 1)) {
                $first_node_pos -= (length($line) + 1);
            }
            $first_node_line = $lnn - 1;
            prt("\n") if ($blen);
            $blen = 0;
            prt("Found first node $id at line $lnn, offset $first_node_pos\n");
        }
    }
    prt("$lnn: [$line]\n") if ($line);
    prt("Processed $lnn lines... ");
    if ($start_line > 0) {
        prt("starting at $start_line... ");
    }
    if (($end_line > 0)&&($lnn > $end_line)) {
        prt("ending due end line ");
    } elsif (($max_lines > 0)&& ($lncnt > $max_lines)) {
        prt("ending due max lines $lncnt...");
    }
    prt("\n");
    if (@waylines) {
        $end_line = $lnn;
        ###process_way_lines(\@waylines);
        my ($cnt,$fnd,$lat,$lon);
        my $rwids = get_way_nd_ids(\@waylines);
        my @arr = sort keys(%{$rwids});
        $cnt = scalar @arr;
        prt("Seeking $cnt node ids [".join(",",@arr)."]\n from getline $., line $lnn onwards...\n");
        $blen = 0;
        while ($cnt && ($line = getline())) {
            chomp $line;
            $lnn++;
            if (($lnn % 100000) == 0) {
                prt(".");
                $blen++;
                if ($blen >= 100) {
                    prt("\n");
                    $blen = 0;
                }
            }
            if ($line =~ /<node id=[\'\"](\d+)[\'\"].*?\/>/) {
                $id = $1;
                if (defined ${$rwids}{$id}) {
                    push(@nodelines,$line);
                    $fnd = 0;
                    if ($line =~ /lat=[\'\"]([0-9\-\.]+)[\'\"]/gis) {
                        $lat = $1;
                        $fnd += 1;
                    }
                    if ($line =~ /lon=[\'\"]([0-9\-\.]+)[\'\"]/gis) {
                        $lon = $1;
                        $fnd += 2;
                    }
                    $way[$nodecount]->{'node'} = $id;
                    $way[$nodecount]->{'lat'}  = $lat;
                    $way[$nodecount]->{'lon'}  = $lon;
                    $way[$nodecount]->{'x'}    = 0;
                    $way[$nodecount]->{'y'}    = 0;
                    $nodecount++;
                    prt("\n") if ($blen);
                    prt("$nodecount of $cnt: $id $lat $lon \n"); #if $verbose;
                    ${$rwids}{$id} = $fnd;
                    $blen = 0;
                    if ($nodecount >= $cnt) {
                        prt("Found all nodes, by getline $., line $lnn in file\n");
                        last;
                    }
                }
            }
        }
        if ($nodecount < $cnt) {
            $lnn = $first_node_line;
            prt("\n") if ($blen);
            prt("Seek to first node pos $first_node_pos, line $lnn...\n");
            seek($infile,$first_node_pos,0);    # put file back to pos of first node
            $blen = 0;
            while ($cnt && ($line = getline())) {
                chomp $line;
                $lnn++;
                if (($lnn % 100000) == 0) {
                    prt(".");
                    $blen++;
                    if ($blen >= 100) {
                        prt("\n");
                        $blen = 0;
                    }
                }
                if ($line =~ /<node id=[\'\"](\d+)[\'\"].*?\/>/) {
                    $id = $1;
                    if (defined ${$rwids}{$id}) {
                        push(@nodelines,$line);
                        $fnd = 0;
                        if ($line =~ /lat=[\'\"]([0-9\-\.]+)[\'\"]/gis) {
                            $lat = $1;
                            $fnd += 1;
                        }
                        if ($line =~ /lon=[\'\"]([0-9\-\.]+)[\'\"]/gis) {
                            $lon = $1;
                            $fnd += 2;
                        }
                        $way[$nodecount]->{'node'} = $id;
                        $way[$nodecount]->{'lat'}  = $lat;
                        $way[$nodecount]->{'lon'}  = $lon;
                        $way[$nodecount]->{'x'}    = 0;
                        $way[$nodecount]->{'y'}    = 0;
                        $nodecount++;
                        prt("\n") if ($blen);
                        prt("$nodecount of $cnt: $id $lat $lon \n"); #if $verbose;
                        ${$rwids}{$id} = $fnd;
                        $blen = 0;
                        if ($nodecount >= $cnt) {
                            prt("Found all nodes, by getline $. (line $lnn) in file\n");
                            last;
                        }
                    }
                }
                if ($lnn > $end_line) {
                    prtw("WARNING: Reached end line $end_line before finding all! $nodecount of $cnt\n");
                    last;
                }
            }
        }
        close($infile);
        get_ac3d_info() if ($nodecount);
        dump_osm_xml(\@waylines,\@nodelines);
    } else {
        close($infile);

    }
}

sub getwayfromapi($) {
    my $wayid = shift;
    my $wayxml = get("http://api.openstreetmap.org/api/0.6/way/$wayid");
    return $wayxml;
}

sub getnodefromapi($) {
    my $nodeid = shift;
    my $nodexml = get("http://api.openstreetmap.org/api/0.6/node/$nodeid");
    return $nodexml;
}

sub use_osm_api() {
    my $wayxml = getwayfromapi($way_id);
    if (!defined $wayxml || (length($wayxml) == 0)) {
        prt("Failed to get way id [$way_id]!\n");
        return;
    }
    my @waylines = split("\n",$wayxml);
    my $rwids = get_way_nd_ids(\@waylines);
    my @arr = sort keys(%{$rwids});
    my $cnt = scalar @arr;
    prt("Seeking $cnt node ids [".join(",",@arr)."] from api...\n");
    my ($id,$nodexml,$fnd,$line,$lat,$lon);
    my @nodelines = ();
    foreach $id (@arr) {
        $nodexml = getnodefromapi($id);
        if (!defined $nodexml || (length($nodexml) == 0)) {
            prt("Failed to get node id [$id]!\n");
        } else {
            push(@nodelines,$nodexml);
            $line = $nodexml;
            $fnd = 0;
            $lat = 0;
            $lon = 0;
            if ($line =~ /lat=[\'\"]([0-9\-\.]+)[\'\"]/gis) {
                $lat = $1;
                $fnd += 1;
            }
            if ($line =~ /lon=[\'\"]([0-9\-\.]+)[\'\"]/gis) {
                $lon = $1;
                $fnd += 2;
            }
            $way[$nodecount]->{'node'} = $id;
            $way[$nodecount]->{'lat'}  = $lat;
            $way[$nodecount]->{'lon'}  = $lon;
            $way[$nodecount]->{'x'}    = 0;
            $way[$nodecount]->{'y'}    = 0;
            $nodecount++;
            prt("$nodecount of $cnt: $id $lat $lon \n"); #if $verbose;
            ${$rwids}{$id} = $fnd;
        }
    }
    get_ac3d_info() if ($nodecount);
    dump_osm_xml(\@waylines,\@nodelines);
}

#########################################
### MAIN ###
parse_args(@ARGV);
if (length($in_file)) {
    process_in_file($in_file);
} else {
    use_osm_api();
}
pgm_exit(0,"");
########################################

sub need_arg {
    my ($arg,@av) = @_;
    pgm_exit(1,"ERROR: [$arg] must have a following argument!\n") if (!@av);
}

sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg);
    while (@av) {
        $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
                give_help();
                pgm_exit(0,"Help exit(0)");
            } elsif ($sarg =~ /^v/) {
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                prt("Verbosity = $verbosity\n") if (VERB1());
            } elsif ($sarg =~ /^l/) {
                if ($sarg =~ /^ll/) {
                    $load_log = 2;
                } else {
                    $load_log = 1;
                }
                prt("Set to load log at end. ($load_log)\n") if (VERB1());
            } elsif ($sarg =~ /^o/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $out_file = $sarg;
                prt("Set out file to [$out_file].\n") if (VERB1());
            } elsif ($sarg =~ /^s/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if ($sarg =~ /^\d+$/) {
                    $start_line = $sarg;
                    prt("Set start line to [$start_line].\n") if (VERB1());
                } else {
                    pgm_exit(1,"ERROR: -s must be followed by a NUMBER! Not [$sarg]\n");
                }
            } elsif ($sarg =~ /^e/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if ($sarg =~ /^\d+$/) {
                    $end_line = $sarg;
                    prt("Set end line to [$end_line].\n") if (VERB1());
                } else {
                    pgm_exit(1,"ERROR: -e must be followed by a NUMBER! Not [$sarg]\n");
                }
            } elsif ($sarg =~ /^m/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if ($sarg =~ /^\d+$/) {
                    $max_lines = $sarg;
                    prt("Set maximum lines to [$max_lines].\n") if (VERB1());
                } else {
                    pgm_exit(1,"ERROR: -m must be followed by a NUMBER! Not [$sarg]\n");
                }
            } elsif ($sarg =~ /^n/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if ($sarg =~ /^\d+$/) {
                    $node_id = $sarg;
                    prt("Set node id to [$node_id].\n") if (VERB1());
                } else {
                    pgm_exit(1,"ERROR: -n must be followed by a NUMBER! Not [$sarg]\n");
                }
            } elsif ($sarg =~ /^w/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if ($sarg =~ /^\d+$/) {
                    $way_id = $sarg;
                    prt("Set way id to [$way_id].\n") if (VERB1());
                } else {
                    pgm_exit(1,"ERROR: -w must be followed by a NUMBER! Not [$sarg]\n");
                }

            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $in_file = $arg;
            prt("Set input to [$in_file]\n") if (VERB1());
        }
        shift @av;
    }

    if ($debug_on) {
        prtw("WARNING: DEBUG is ON!\n");
        if (length($in_file) ==  0) {
            $in_file = $def_file;
            prt("Set DEFAULT input to [$in_file]\n");
        }
        #if (length($node_id) == 0) {
        #    $node_id = $def_nid;
        #    prt("Set DEFAULT node id to [$node_id]\n");
        #}
        if (length($way_id) == 0) {
            $way_id = $def_wid;
            prt("Set DEFAULT way id to [$way_id]\n");
        }
    }
    if (length($in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if (! -f $in_file) {
        pgm_exit(1,"ERROR: Unable to find in file [$in_file]! Check name, location...\n");
    }
    if ((length($node_id) == 0)&&(length($way_id) == 0)) {
        pgm_exit(1,"ERROR: No way or node id given in command!\n");
    }
    if (length($out_file) == 0) {
        $out_file = $def_out;
        prt("Set DEFAULT out file to [$out_file]\n");
    }

}

sub give_help {
    prt("$pgmname: version $VERS\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt("Options:\n");
    prt(" --help  (-h or -?) = This help, and exit 0.\n");
    prt(" --verb[n]     (-v) = Bump [or set] verbosity. def=$verbosity\n");
    prt(" --load        (-l) = Load LOG at end. ($outfile)\n");
    prt(" --out <file>  (-o) = Write output to this file.\n");
}

# eof - template.pl
