Fix:script:Converted to new osm api
authormartin-s <martin-s@ffa7fe5e-494d-0410-b361-a75ebd5db220>
Thu, 27 Aug 2009 19:09:25 +0000 (19:09 +0000)
committermartin-s <martin-s@ffa7fe5e-494d-0410-b361-a75ebd5db220>
Thu, 27 Aug 2009 19:09:25 +0000 (19:09 +0000)
git-svn-id: https://navit.svn.sourceforge.net/svnroot/navit/trunk@2534 ffa7fe5e-494d-0410-b361-a75ebd5db220

20 files changed:
navit/navit/script/osm/Geo/OSM/APIClientV4.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/APIClientV5.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/APIClientV6.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/EntitiesV3.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/EntitiesV5.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/EntitiesV6.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/MapFeatures.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/OsmChangeReaderV3.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/OsmChangeReaderV5.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/OsmChangeReaderV6.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/OsmReaderV3.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/OsmReaderV5.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/OsmReaderV6.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/OsmXML.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/Planet.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/SegmentList.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/Tracks2OSM.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/Upload.pm [new file with mode: 0644]
navit/navit/script/osm/Geo/OSM/Write.pm [new file with mode: 0644]
navit/navit/script/osm/osmtool.pl

diff --git a/navit/navit/script/osm/Geo/OSM/APIClientV4.pm b/navit/navit/script/osm/Geo/OSM/APIClientV4.pm
new file mode 100644 (file)
index 0000000..26b8ec5
--- /dev/null
@@ -0,0 +1,226 @@
+##################################################################
+## APIClientV4.pm - General Perl client for the API             ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Currently only supports uploading. Note the package actually ##
+## creates a package named Geo::OSM::APIClient so upgrades to   ##
+## later versions will be easier.                               ##
+## Licence: LGPL                                                ##
+##################################################################
+
+use LWP::UserAgent;
+use strict;
+
+package Geo::OSM::APIClient;
+use Geo::OSM::OsmReaderV3;
+use MIME::Base64;
+use HTTP::Request;
+use Carp;
+use Encode;
+use POSIX qw(sigprocmask);
+
+sub new
+{
+  my( $class, %attr ) = @_;
+  
+  my $obj = bless {}, $class;
+
+  my $url = $attr{api};  
+  if( not defined $url )
+  {
+    croak "Did not specify aip url";
+  }
+
+  $url =~ s,/$,,;   # Strip trailing slash
+  $obj->{url} = $url;
+  $obj->{client} = new LWP::UserAgent(agent => 'Geo::OSM::APIClientV4', timeout => 1200);
+  
+  if( defined $attr{username} and defined $attr{password} )
+  {
+    if( $obj->{url} =~ m,http://([\w.]+)/, )
+    {
+      $obj->{client}->credentials( "$1:80", "Web Password",  $attr{username}, $attr{password} );
+    }
+    my $encoded = MIME::Base64::encode_base64("$attr{username}:$attr{password}","");
+    $obj->{client}->default_header( "Authorization", "Basic $encoded" );
+  }
+  
+  $obj->{reader} = init Geo::OSM::OsmReader( sub { _process($obj,@_) } );
+  return $obj;
+}
+
+# This is the callback from the parser. If checks if the buffer is defined.
+# If the buffer is an array, append the new object. If the buffer is a proc,
+# call it.
+sub _process
+{
+  my($obj,$ent) = @_;
+  if( not defined $obj->{buffer} )
+  { die "Internal error: Received object with buffer" }
+  if( ref $obj->{buffer} eq "ARRAY" )
+  { push @{$obj->{buffer}}, $ent; return }
+  if( ref $obj->{buffer} eq "CODE" )
+  { $obj->{buffer}->($ent); return }
+  die "Internal error: don't know what to do with buffer $obj->{buffer}";
+}  
+
+# Utility function to handle the temporary blocking of signals in a way that
+# works with exception handling.
+sub _with_blocked_sigs(&)
+{
+  my $ss = new POSIX::SigSet( &POSIX::SIGINT );
+  my $func = shift;
+  my $os = new POSIX::SigSet;
+  sigprocmask( &POSIX::SIG_BLOCK, $ss, $os );
+  my $ret = eval { &$func };
+  sigprocmask( &POSIX::SIG_SETMASK, $os );
+  die $@ if $@;
+  return $ret;
+}
+
+sub _request
+{
+  my $self = shift;
+  my $req = shift;
+  return _with_blocked_sigs { $self->{client}->request($req) };
+}
+
+sub last_error_code
+{
+  return shift->{last_error}->code;
+}
+
+sub last_error_message
+{
+  return shift->{last_error}->message;
+}
+
+sub create
+{
+  my( $self, $ent ) = @_;
+  my $oldid = $ent->id;
+  $ent->set_id(0);
+  my $content = encode("utf-8", $ent->full_xml);
+  $ent->set_id($oldid);
+  my $req = new HTTP::Request PUT => $self->{url}."/".$ent->type()."/create";
+  $req->content($content);
+  
+#  print $req->as_string;
+  
+  my $res = $self->_request($req);
+  
+#  print $res->as_string;
+
+  if( $res->code == 200 )
+  {
+    return $res->content
+  }
+    
+  $self->{last_error} = $res;
+  return undef;
+}
+
+sub modify
+{
+  my( $self, $ent ) = @_;
+  my $content = encode("utf-8", $ent->full_xml);
+  my $req = new HTTP::Request PUT => $self->{url}."/".$ent->type()."/".$ent->id();
+  $req->content($content);
+  
+#  print $req->as_string;
+  
+  my $res = $self->_request($req);
+  
+  return $ent->id() if $res->code == 200;
+  $self->{last_error} = $res;
+  return undef;
+}
+
+sub delete
+{
+  my( $self, $ent ) = @_;
+  my $content = encode("utf-8", $ent->full_xml);
+  my $req = new HTTP::Request DELETE => $self->{url}."/".$ent->type()."/".$ent->id();
+#  $req->content($content);
+  
+#  print $req->as_string;
+  
+  my $res = $self->_request($req);
+  
+  return $ent->id() if $res->code == 200;
+  $self->{last_error} = $res;
+  return undef;
+}
+
+sub get($$)
+{
+  my $self = shift;
+  my $type = shift;
+  my $id = shift;
+  
+  my $req = new HTTP::Request GET => $self->{url}."/$type/$id";
+  
+  my $res = $self->_request($req);
+
+  if( $res->code != 200 )
+  {
+    $self->{last_error} = $res;
+    return undef;
+  }
+  
+  my @res;
+  $self->{buffer} = \@res;
+  $self->{reader}->parse($res->content);
+  undef $self->{buffer};
+  if( scalar(@res) != 1 )
+  {
+    die "Unexpected response for get_$type [".$res->content()."]\n";
+  }
+  
+  return $res[0];
+}
+
+sub get_node($)
+{
+  my $self = shift;
+  return $self->get("node",shift);
+}
+
+sub get_way($)
+{
+  my $self = shift;
+  return $self->get("way",shift);
+}
+
+sub get_segment($)
+{
+  my $self = shift;
+  return $self->get("segment",shift);
+}
+
+
+sub map($$$$)
+{
+  my $self = shift;
+  my @bbox = @_;
+  
+  my $req = new HTTP::Request GET => $self->{url}."/map?bbox=$bbox[0],$bbox[1],$bbox[2],$bbox[3]";
+  
+  my $res = $self->_request($req);
+
+  if( $res->code != 200 )
+  {
+    $self->{last_error} = $res;
+    return undef;
+  }
+  
+  my @res;
+  $self->{buffer} = \@res;
+  $self->{reader}->parse($res->content);
+  undef $self->{buffer};
+  
+  return \@res;
+}
+
+
+1;
diff --git a/navit/navit/script/osm/Geo/OSM/APIClientV5.pm b/navit/navit/script/osm/Geo/OSM/APIClientV5.pm
new file mode 100644 (file)
index 0000000..5bee097
--- /dev/null
@@ -0,0 +1,327 @@
+##################################################################
+## APIClientV5.pm - General Perl client for the API             ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Currently only supports uploading. Note the package actually ##
+## creates a package named Geo::OSM::APIClient so upgrades to   ##
+## later versions will be easier.                               ##
+## Licence: LGPL                                                ##
+##################################################################
+
+use LWP::UserAgent;
+use strict;
+
+package Geo::OSM::APIClient;
+use Geo::OSM::OsmReaderV5;
+use MIME::Base64;
+use HTTP::Request;
+use Carp;
+use Encode;
+use POSIX qw(sigprocmask);
+use URI;
+use Socket qw(inet_ntoa);
+
+sub new
+{
+  my( $class, %attr ) = @_;
+  
+  my $obj = bless {}, $class;
+
+  my $url = $attr{api};  
+  if( not defined $url )
+  {
+    croak "Did not specify api url";
+  }
+
+  $url =~ s,/$,,;   # Strip trailing slash
+  $obj->{url} = URI->new($url);
+  $obj->{client} = new LWP::UserAgent(agent => 'Geo::OSM::APIClientV5', timeout => 1200);
+  
+  if( defined $attr{username} and defined $attr{password} )
+  {
+    my $encoded = MIME::Base64::encode_base64("$attr{username}:$attr{password}","");
+    $obj->{client}->default_header( "Authorization", "Basic $encoded" );
+  }
+  
+  # We had the problem of the client doing a DNS lookup each request. To
+  # solve this we do a gethostbyname now and store that in the URI.
+  {
+    my $addr;
+    (undef, undef, undef, undef, $addr) = gethostbyname($obj->{url}->host);
+    if( defined $addr )
+    {
+      $obj->{client}->default_header( "Host", $obj->{url}->host );
+      $obj->{url}->host( inet_ntoa($addr) );
+      print STDERR "Using address: ".$obj->{url}->as_string()."\n";
+    }
+  }
+  # Hack to avoid protocol lookups each time
+  @LWP::Protocol::http::EXTRA_SOCK_OPTS = ( 'Proto' => 6 );
+      
+  $obj->{reader} = init Geo::OSM::OsmReader( sub { _process($obj,@_) } );
+  return $obj;
+}
+
+# This is the callback from the parser. If checks if the buffer is defined.
+# If the buffer is an array, append the new object. If the buffer is a proc,
+# call it.
+sub _process
+{
+  my($obj,$ent) = @_;
+  if( not defined $obj->{buffer} )
+  { die "Internal error: Received object with buffer" }
+  if( ref $obj->{buffer} eq "ARRAY" )
+  { push @{$obj->{buffer}}, $ent; return }
+  if( ref $obj->{buffer} eq "CODE" )
+  { $obj->{buffer}->($ent); return }
+  die "Internal error: don't know what to do with buffer $obj->{buffer}";
+}  
+
+# Utility function to handle the temporary blocking of signals in a way that
+# works with exception handling.
+sub _with_blocked_sigs(&)
+{
+  my $ss = new POSIX::SigSet( &POSIX::SIGINT );
+  my $func = shift;
+  my $os = new POSIX::SigSet;
+  sigprocmask( &POSIX::SIG_BLOCK, $ss, $os );
+  my $ret = eval { &$func };
+  sigprocmask( &POSIX::SIG_SETMASK, $os );
+  die $@ if $@;
+  return $ret;
+}
+
+sub _request
+{
+  my $self = shift;
+  my $req = shift;
+  return _with_blocked_sigs { $self->{client}->request($req) };
+}
+
+sub last_error_code
+{
+  my $self=shift;
+  croak "No last error" unless defined $self->{last_error};
+  return $self->{last_error}->code;
+}
+
+sub last_error_message
+{
+  my $self=shift;
+  croak "No last error" unless defined $self->{last_error};
+  return $self->{last_error}->message;
+}
+
+sub create($)
+{
+  my( $self, $ent ) = @_;
+  my $oldid = $ent->id;
+  $ent->set_id(0);
+  my $content = encode("utf-8", $ent->full_xml);
+  $ent->set_id($oldid);
+  my $req = new HTTP::Request PUT => $self->{url}."/".$ent->type()."/create";
+  $req->content($content);
+  
+#  print $req->as_string;
+  
+  my $res = $self->_request($req);
+  
+#  print $res->as_string;
+
+  if( $res->code == 200 )
+  {
+    return $res->content
+  }
+    
+  $self->{last_error} = $res;
+  return undef;
+}
+
+sub modify($)
+{
+  my( $self, $ent ) = @_;
+  my $content = encode("utf-8", $ent->full_xml);
+  my $req = new HTTP::Request PUT => $self->{url}."/".$ent->type()."/".$ent->id();
+  $req->content($content);
+  
+#  print $req->as_string;
+  
+  my $res = $self->_request($req);
+  
+  return $ent->id() if $res->code == 200;
+  $self->{last_error} = $res;
+  return undef;
+}
+
+sub delete($)
+{
+  my( $self, $ent ) = @_;
+  my $content = encode("utf-8", $ent->full_xml);
+  my $req = new HTTP::Request DELETE => $self->{url}."/".$ent->type()."/".$ent->id();
+#  $req->content($content);
+  
+#  print $req->as_string;
+  
+  my $res = $self->_request($req);
+  
+  return $ent->id() if $res->code == 200;
+  $self->{last_error} = $res;
+  return undef;
+}
+
+sub get($$)
+{
+  my $self = shift;
+  my $type = shift;
+  my $id = shift;
+  my $extra = shift;
+  
+  $extra = "/".$extra if (defined $extra);
+  $extra = "" if not defined $extra;
+
+  my $req = new HTTP::Request GET => $self->{url}."/$type/$id$extra";
+  
+  my $res = $self->_request($req);
+
+  if( $res->code != 200 )
+  {
+    $self->{last_error} = $res;
+    return undef;
+  }
+  
+  my @res;
+  $self->{buffer} = \@res;
+  $self->{reader}->parse($res->content);
+  undef $self->{buffer};
+  if($extra =~ /history/)
+  {
+    return @res;
+  }
+  if(scalar(@res) != 1 )
+  {
+    die "Unexpected response for get_$type [".$res->content()."]\n";
+  }
+
+  return $res[0];
+}
+
+sub resurrect($$)
+{
+  my $self = shift;
+  my $type = shift;
+  my $id = shift;
+
+  my $ret = $self->get($type, $id);
+  if (defined $ret || !defined $self->{last_error} || ($self->{last_error}->code != 410)) {
+    return $ret;
+  }
+  
+  my @ents = $self->get($type, $id, 'history');
+  # we want the last _visible_ one
+  my $ent = $ents[-2];
+  if ($ent->type eq 'way') {
+       printf("resurrecting way, checking all member nodes...\n");
+       foreach my $node_id (@{$ent->nodes()}) {
+               printf("checking node: $node_id...");
+               my $node_ent = $self->get('node', $node_id);
+               if (defined $node_ent) {
+                       printf("good\n");
+                       next;
+               }
+               printf("attempting to resurrect node: $node_id...");
+               $node_ent = $self->resurrect('node', $node_id);
+               if (!defined $node_ent) {
+                       die "failed";
+               }
+               printf("success!\n");
+       }
+       printf("all way nodes are OK, ");
+  }
+  printf("attempting to resurrect %s...", $ent->type);
+  $ret = $self->modify($ent);
+  if ($ret == $ent->id) {
+       printf("ok\n");
+       return $ret;
+  }
+  die sprintf("unable to resurrect $type $id: %s\n", $self->last_error_message);
+}
+
+sub get_node($)
+{
+  my $self = shift;
+  return $self->get("node",shift);
+}
+
+sub get_way($)
+{
+  my $self = shift;
+  return $self->get("way",shift);
+}
+
+sub get_relation($)
+{
+  my $self = shift;
+  return $self->get("relation",shift);
+}
+
+sub get_subtype($$)
+{
+  my $self = shift;
+  my $type = shift;
+  my $id = shift;
+  my $subtype = shift;
+  
+  my $req = new HTTP::Request GET => $self->{url}."/$type/$id/$subtype";
+  
+  my $res = $self->_request($req);
+
+  if( $res->code != 200 )
+  {
+    $self->{last_error} = $res;
+    return undef;
+  }
+  
+  my @res;
+  $self->{buffer} = \@res;
+  $self->{reader}->parse($res->content);
+  undef $self->{buffer};
+  if( scalar(@res) < 1 )
+  {
+    die "Unexpected response for get_subtype($type,$id,$subtype) [".$res->content()."]\n";
+  }
+  
+  return \@res;
+}
+
+sub get_node_ways($)
+{
+  my $self = shift;
+  my $id = shift;
+  
+  return $self->get_subtype("node",$id,"ways");
+}
+
+sub map($$$$)
+{
+  my $self = shift;
+  my @bbox = @_;
+  
+  my $req = new HTTP::Request GET => $self->{url}."/map?bbox=$bbox[0],$bbox[1],$bbox[2],$bbox[3]";
+  
+  my $res = $self->_request($req);
+
+  if( $res->code != 200 )
+  {
+    $self->{last_error} = $res;
+    return undef;
+  }
+  
+  my @res;
+  $self->{buffer} = \@res;
+  $self->{reader}->parse($res->content);
+  undef $self->{buffer};
+  
+  return \@res;
+}
+
+1;
diff --git a/navit/navit/script/osm/Geo/OSM/APIClientV6.pm b/navit/navit/script/osm/Geo/OSM/APIClientV6.pm
new file mode 100644 (file)
index 0000000..d659ac1
--- /dev/null
@@ -0,0 +1,364 @@
+##################################################################
+## APIClientV6.pm - General Perl client for the API             ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Currently only supports uploading. Note the package actually ##
+## creates a package named Geo::OSM::APIClient so upgrades to   ##
+## later versions will be easier.                               ##
+## Licence: LGPL                                                ##
+##################################################################
+
+use LWP::UserAgent;
+use strict;
+
+package Geo::OSM::APIClient;
+use Geo::OSM::OsmReaderV6;
+use MIME::Base64;
+use HTTP::Request;
+use Carp;
+use Encode;
+use POSIX qw(sigprocmask);
+use URI;
+use Socket qw(inet_ntoa);
+
+sub new
+{
+  my( $class, %attr ) = @_;
+  
+  my $obj = bless {}, $class;
+
+  my $url = $attr{api};  
+  if( not defined $url )
+  {
+    croak "Did not specify api url";
+  }
+
+  $url =~ s,/$,,;   # Strip trailing slash
+  $obj->{url} = URI->new($url);
+  $obj->{client} = new LWP::UserAgent(agent => 'Geo::OSM::APIClientV6', timeout => 1200);
+  
+  if( defined $attr{username} and defined $attr{password} )
+  {
+    my $encoded = MIME::Base64::encode_base64("$attr{username}:$attr{password}","");
+    $obj->{client}->default_header( "Authorization", "Basic $encoded" );
+  }
+  
+  # We had the problem of the client doing a DNS lookup each request. To
+  # solve this we do a gethostbyname now and store that in the URI.
+  {
+    my $addr;
+    (undef, undef, undef, undef, $addr) = gethostbyname($obj->{url}->host);
+    if( defined $addr )
+    {
+      $obj->{client}->default_header( "Host", $obj->{url}->host );
+      $obj->{url}->host( inet_ntoa($addr) );
+      print STDERR "Using address: ".$obj->{url}->as_string()."\n";
+    }
+  }
+  # Hack to avoid protocol lookups each time
+  @LWP::Protocol::http::EXTRA_SOCK_OPTS = ( 'Proto' => 6 );
+      
+  $obj->{reader} = init Geo::OSM::OsmReader( sub { _process($obj,@_) } );
+  return $obj;
+}
+
+# This is the callback from the parser. If checks if the buffer is defined.
+# If the buffer is an array, append the new object. If the buffer is a proc,
+# call it.
+sub _process
+{
+  my($obj,$ent) = @_;
+  if( not defined $obj->{buffer} )
+  { die "Internal error: Received object with buffer" }
+  if( ref $obj->{buffer} eq "ARRAY" )
+  { push @{$obj->{buffer}}, $ent; return }
+  if( ref $obj->{buffer} eq "CODE" )
+  { $obj->{buffer}->($ent); return }
+  die "Internal error: don't know what to do with buffer $obj->{buffer}";
+}  
+
+# Utility function to handle the temporary blocking of signals in a way that
+# works with exception handling.
+sub _with_blocked_sigs(&)
+{
+  my $ss = new POSIX::SigSet( &POSIX::SIGINT );
+  my $func = shift;
+  my $os = new POSIX::SigSet;
+  sigprocmask( &POSIX::SIG_BLOCK, $ss, $os );
+  my $ret = eval { &$func };
+  sigprocmask( &POSIX::SIG_SETMASK, $os );
+  die $@ if $@;
+  return $ret;
+}
+
+sub _request
+{
+  my $self = shift;
+  my $req = shift;
+  return _with_blocked_sigs { $self->{client}->request($req) };
+}
+
+sub last_error_code
+{
+  my $self=shift;
+  croak "No last error" unless defined $self->{last_error};
+  return $self->{last_error}->code;
+}
+
+sub last_error_message
+{
+  my $self=shift;
+  croak "No last error" unless defined $self->{last_error};
+  return $self->{last_error}->message;
+}
+
+sub create_changeset
+{
+  my( $self ) = @_;
+  my $req = new HTTP::Request PUT => $self->{url}."/changeset/create";
+  my $content="<osm><changeset></changeset></osm>";
+  $req->content($content);
+  my $res = $self->_request($req);
+
+  if( $res->code == 200 )
+  {
+    $self->{changeset}=$res->content;
+    return 1;
+  }
+    
+  $self->{last_error} = $res;
+  $self->{changeset}=undef;
+  return 0;
+  
+}
+
+sub close_changeset
+{
+  my( $self ) = @_;
+  my $req = new HTTP::Request PUT => $self->{url}."/changeset/".$self->{changeset}."/close";
+  my $res = $self->_request($req);
+
+  $self->{changeset}=undef;
+  if( $res->code == 200 )
+  {
+    return 1;
+  }
+  return 0;
+  
+}
+
+sub create($)
+{
+  my( $self, $ent ) = @_;
+  my $oldid = $ent->id;
+  $ent->set_id(0);
+  my $content = encode("utf-8", $ent->full_xml);
+  $ent->set_id($oldid);
+  my $req = new HTTP::Request PUT => $self->{url}."/".$ent->type()."/create";
+  $req->content($content);
+  
+#  print $req->as_string;
+  
+  my $res = $self->_request($req);
+  
+#  print $res->as_string;
+
+  if( $res->code == 200 )
+  {
+    return $res->content
+  }
+    
+  $self->{last_error} = $res;
+  return undef;
+}
+
+sub modify($)
+{
+  my( $self, $ent ) = @_;
+  $ent->set_changeset($self->{changeset});
+  my $content = encode("utf-8", $ent->full_xml);
+  my $req = new HTTP::Request PUT => $self->{url}."/".$ent->type()."/".$ent->id();
+  $req->content($content);
+  
+#  print $req->as_string;
+  
+  my $res = $self->_request($req);
+  
+  return $ent->id() if $res->code == 200;
+  $self->{last_error} = $res;
+  return undef;
+}
+
+sub delete($)
+{
+  my( $self, $ent ) = @_;
+  $ent->set_changeset($self->{changeset});
+  my $content = encode("utf-8", $ent->full_xml);
+  my $req = new HTTP::Request DELETE => $self->{url}."/".$ent->type()."/".$ent->id();
+  $req->content($content);
+  
+#  print $req->as_string;
+  
+  my $res = $self->_request($req);
+  
+  return $ent->id() if $res->code == 200;
+  $self->{last_error} = $res;
+  return undef;
+}
+
+sub get($$)
+{
+  my $self = shift;
+  my $type = shift;
+  my $id = shift;
+  my $extra = shift;
+  
+  $extra = "/".$extra if (defined $extra);
+  $extra = "" if not defined $extra;
+
+  my $req = new HTTP::Request GET => $self->{url}."/$type/$id$extra";
+  
+  my $res = $self->_request($req);
+
+  if( $res->code != 200 )
+  {
+    $self->{last_error} = $res;
+    return undef;
+  }
+  
+  my @res;
+  $self->{buffer} = \@res;
+  $self->{reader}->parse($res->content);
+  undef $self->{buffer};
+  if($extra =~ /history/)
+  {
+    return @res;
+  }
+  if(scalar(@res) != 1 )
+  {
+    die "Unexpected response for get_$type [".$res->content()."]\n";
+  }
+
+  return $res[0];
+}
+
+sub resurrect($$)
+{
+  my $self = shift;
+  my $type = shift;
+  my $id = shift;
+
+  my $ret = $self->get($type, $id);
+  if (defined $ret || !defined $self->{last_error} || ($self->{last_error}->code != 410)) {
+    return $ret;
+  }
+  
+  my @ents = $self->get($type, $id, 'history');
+  # we want the last _visible_ one
+  my $ent = $ents[-2];
+  if ($ent->type eq 'way') {
+       printf("resurrecting way, checking all member nodes...\n");
+       foreach my $node_id (@{$ent->nodes()}) {
+               printf("checking node: $node_id...");
+               my $node_ent = $self->get('node', $node_id);
+               if (defined $node_ent) {
+                       printf("good\n");
+                       next;
+               }
+               printf("attempting to resurrect node: $node_id...");
+               $node_ent = $self->resurrect('node', $node_id);
+               if (!defined $node_ent) {
+                       die "failed";
+               }
+               printf("success!\n");
+       }
+       printf("all way nodes are OK, ");
+  }
+  printf("attempting to resurrect %s...", $ent->type);
+  $ret = $self->modify($ent);
+  if ($ret == $ent->id) {
+       printf("ok\n");
+       return $ret;
+  }
+  die sprintf("unable to resurrect $type $id: %s\n", $self->last_error_message);
+}
+
+sub get_node($)
+{
+  my $self = shift;
+  return $self->get("node",shift);
+}
+
+sub get_way($)
+{
+  my $self = shift;
+  return $self->get("way",shift);
+}
+
+sub get_relation($)
+{
+  my $self = shift;
+  return $self->get("relation",shift);
+}
+
+sub get_subtype($$)
+{
+  my $self = shift;
+  my $type = shift;
+  my $id = shift;
+  my $subtype = shift;
+  
+  my $req = new HTTP::Request GET => $self->{url}."/$type/$id/$subtype";
+  
+  my $res = $self->_request($req);
+
+  if( $res->code != 200 )
+  {
+    $self->{last_error} = $res;
+    return undef;
+  }
+  
+  my @res;
+  $self->{buffer} = \@res;
+  $self->{reader}->parse($res->content);
+  undef $self->{buffer};
+  if( scalar(@res) < 1 )
+  {
+    die "Unexpected response for get_subtype($type,$id,$subtype) [".$res->content()."]\n";
+  }
+  
+  return \@res;
+}
+
+sub get_node_ways($)
+{
+  my $self = shift;
+  my $id = shift;
+  
+  return $self->get_subtype("node",$id,"ways");
+}
+
+sub map($$$$)
+{
+  my $self = shift;
+  my @bbox = @_;
+  my $req = new HTTP::Request GET => $self->{url}."/map?bbox=$bbox[0],$bbox[1],$bbox[2],$bbox[3]";
+  
+  my $res = $self->_request($req);
+
+  if( $res->code != 200 )
+  {
+    $self->{last_error} = $res;
+    return undef;
+  }
+  
+  my @res;
+  $self->{buffer} = \@res;
+  $self->{reader}->parse($res->content);
+  undef $self->{buffer};
+  
+  return \@res;
+}
+
+1;
diff --git a/navit/navit/script/osm/Geo/OSM/EntitiesV3.pm b/navit/navit/script/osm/Geo/OSM/EntitiesV3.pm
new file mode 100644 (file)
index 0000000..2708463
--- /dev/null
@@ -0,0 +1,296 @@
+##################################################################
+## EntitiesV3.pm - Wraps entities used by OSM                   ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Package that wraps the entities used by OSM into Perl        ##
+## object, so they can be easily manipulated by various         ##
+## packages.                                                    ##
+##                                                              ##
+## Licence: LGPL                                                ##
+##################################################################
+
+use XML::Writer;
+use strict;
+
+############################################################################
+## Top level Entity type, parent of all types, includes stuff relating to ##
+## tags and IDs which are shared by all entity types                      ##
+############################################################################
+package Geo::OSM::Entity;
+use POSIX qw(strftime);
+
+use Carp;
+
+sub _new
+{
+  bless {}, shift;
+}
+
+sub _get_writer
+{
+  my($self,$res) = @_;
+  return new XML::Writer(OUTPUT => $res, NEWLINES => 0, ENCODING => 'utf-8');
+}
+
+sub add_tag
+{
+  my($self, $k,$v) = @_;
+  push @{$self->{tags}}, $k, $v;
+}
+
+sub add_tags
+{
+  my($self, @tags) = @_;
+  if( scalar(@tags)&1 )
+  { croak "add_tags requires an even number of arguments" }
+  push @{$self->{tags}}, @tags;
+}
+
+sub set_tags
+{
+  my($self,$tags) = @_;
+  if( ref($tags) eq "HASH" )
+  { $self->{tags} = [%$tags] }
+  elsif( ref($tags) eq "ARRAY" )
+  { $self->{tags} = [@$tags] }
+  else
+  { croak "set_tags must be HASH or ARRAY" }
+}
+
+sub tags
+{
+  my $self = shift;
+  return [@{$self->{tags}}];  # Return copy
+}
+
+sub tag_xml
+{
+  my ($self,$writer) = @_;
+  my @a = @{$self->{tags}};
+  
+  my $str = "";
+  
+  while( my($k,$v) = splice @a, 0, 2 )
+  {
+    $writer->emptyTag( "tag", 'k' => $k, 'v' => $v );
+  }
+}
+
+our $_ID = -1;
+
+sub set_id
+{
+  my($self,$id) = @_;
+
+  if( not defined $id )
+  { $id = $_ID-- }  
+  $self->{id} = $id;
+}  
+
+sub id
+{
+  my $self = shift;
+  return $self->{id};
+}
+
+sub set_timestamp
+{
+  my($self,$time) = @_;
+  if( defined $time )
+  { $self->{timestamp} = $time }
+  else
+  { $self->{timestamp} = strftime "%Y-%m-%dT%H:%M:%S+00:00", gmtime(time) }
+}
+
+sub timestamp
+{
+  my $self = shift;
+  return $self->{timestamp};
+}
+
+sub full_xml
+{
+  my $self = shift;
+  return qq(<?xml version="1.0"?>\n<osm version="0.4">\n).$self->xml()."</osm>\n";
+}
+
+package Geo::OSM::Way;
+our @ISA = qw(Geo::OSM::Entity);
+
+sub new
+{
+  my($class, $attr, $tags, $segs) = @_;
+  
+  my $obj = bless $class->SUPER::_new(), $class;
+  
+  $obj->set_tags($tags);
+  $obj->set_segs($segs);
+  $obj->set_id($attr->{id} );
+  $obj->set_timestamp( $attr->{timestamp} );
+  
+  return $obj;
+}
+
+sub type { return "way" }
+
+sub set_segs
+{
+  my($self,$segs) = @_;
+  $self->{segs} = $segs;
+}
+
+sub segs
+{
+  my $self = shift;
+  return [@{$self->{segs}}];  # Return a copy
+}
+
+sub xml
+{
+  my $self = shift;
+  my $str = "";
+  my $writer = $self->_get_writer(\$str);
+
+  $writer->startTag( "way", id => $self->id, timestamp => $self->timestamp );
+  $self->tag_xml( $writer );
+  for my $seg (@{$self->segs})
+  {
+    $writer->emptyTag( "seg", id => $seg );
+  }
+  $writer->endTag( "way" );
+  $writer->end;
+  return $str;
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my $incomplete = 0;
+  my ($new_id) = $mapper->map('way',$self->id);   # Determine mapped ID
+  # It is ok for the new_id to be incomplete; it may be a create request
+  
+  my @new_segs = map { [ $mapper->map('segment',$_) ] } @{$self->segs};
+  map { $incomplete |= $_->[1] } @new_segs;
+  # incomplete tracks if any of the segs are incomplete
+  
+  my $new_ent = new Geo::OSM::Way( {id=>$new_id, timestamp=>$self->timestamp}, $self->tags, [map {$_->[0]} @new_segs] );
+  return($new_ent,$incomplete);
+}
+
+package Geo::OSM::Segment;
+our @ISA = qw(Geo::OSM::Entity);
+
+sub new
+{
+  my($class, $attr, $tags) = @_;
+  
+  my $obj = bless $class->SUPER::_new(), $class;
+  
+  $obj->set_tags($tags);
+  $obj->set_id($attr->{id} );
+  $obj->set_timestamp( $attr->{timestamp} );
+  $obj->{from} = $attr->{from};
+  $obj->{to} = $attr->{to};
+  
+  return $obj;
+}
+
+sub type { return "segment" }
+
+sub set_fromto
+{
+  my($self,$from,$to) = @_;
+  $self->{from} = $from;
+  $self->{to} = $to;
+}
+
+sub from
+{
+  shift->{from};
+}
+sub to
+{
+  shift->{to};
+}
+
+sub xml
+{
+  my $self = shift;
+  my $str = "";
+  my $writer = $self->_get_writer(\$str);
+
+  $writer->startTag( "segment", id => $self->id, from => $self->from, to => $self->to, timestamp => $self->timestamp );
+  $self->tag_xml( $writer );
+  $writer->endTag( "segment" );
+  $writer->end;
+  return $str;
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my ($new_id) = $mapper->map('segment',$self->id);
+  my ($new_from,$i1) = $mapper->map('node',$self->from);
+  my ($new_to,$i2) = $mapper->map('node',$self->to);
+  my $new_ent = new Geo::OSM::Segment( {id=>$new_id, timestamp=>$self->timestamp, from=>$new_from, to=>$new_to}, $self->tags );
+  return($new_ent,$i1|$i2);
+}
+
+package Geo::OSM::Node;
+our @ISA = qw(Geo::OSM::Entity);
+
+sub new
+{
+  my($class, $attr, $tags) = @_;
+  
+  my $obj = bless $class->SUPER::_new(), $class;
+  
+  $obj->set_tags($tags);
+  $obj->set_id($attr->{id} );
+  $obj->set_timestamp( $attr->{timestamp} );
+  $obj->{lon} = $attr->{lon};
+  $obj->{lat} = $attr->{lat};
+  
+  return $obj;
+}
+
+sub type { return "node" }
+
+sub set_latlon
+{
+  my($self,$lat,$lon) = @_;
+  $self->{lat} = $lat;
+  $self->{lon} = $lon;
+}
+
+sub lat
+{
+  shift->{lat};
+}
+sub lon
+{
+  shift->{lon};
+}
+
+sub xml
+{
+  my $self = shift;
+  my $str = "";
+  my $writer = $self->_get_writer(\$str);
+  
+  $writer->startTag( "node", id => $self->id, lat => $self->lat, lon => $self->lon, timestamp => $self->timestamp );
+  $self->tag_xml( $writer );
+  $writer->endTag( "node" );
+  $writer->end;
+  return $str;
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my ($new_id) = $mapper->map('node',$self->id);
+  my $new_ent = new Geo::OSM::Node( {id=>$new_id, timestamp=>$self->timestamp, lat=>$self->lat, lon=>$self->lon}, $self->tags );
+  return($new_ent,0);
+}
+
+1;
diff --git a/navit/navit/script/osm/Geo/OSM/EntitiesV5.pm b/navit/navit/script/osm/Geo/OSM/EntitiesV5.pm
new file mode 100644 (file)
index 0000000..625ff6b
--- /dev/null
@@ -0,0 +1,375 @@
+##################################################################
+## EntitiesV3.pm - Wraps entities used by OSM                   ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Package that wraps the entities used by OSM into Perl        ##
+## object, so they can be easily manipulated by various         ##
+## packages.                                                    ##
+##                                                              ##
+## Licence: LGPL                                                ##
+##################################################################
+
+use XML::Writer;
+use strict;
+
+############################################################################
+## Top level Entity type, parent of all types, includes stuff relating to ##
+## tags and IDs which are shared by all entity types                      ##
+############################################################################
+package Geo::OSM::Entity;
+use POSIX qw(strftime);
+
+use Carp;
+
+sub _new
+{
+  bless {}, shift;
+}
+
+sub _get_writer
+{
+  my($self,$res) = @_;
+  return new XML::Writer(OUTPUT => $res, NEWLINES => 0, ENCODING => 'utf-8');
+}
+
+sub add_tag
+{
+  my($self, $k,$v) = @_;
+  push @{$self->{tags}}, $k, $v;
+}
+
+sub add_tags
+{
+  my($self, @tags) = @_;
+  if( scalar(@tags)&1 )
+  { croak "add_tags requires an even number of arguments" }
+  push @{$self->{tags}}, @tags;
+}
+
+sub set_tags
+{
+  my($self,$tags) = @_;
+  if( ref($tags) eq "HASH" )
+  { $self->{tags} = [%$tags] }
+  elsif( ref($tags) eq "ARRAY" )
+  { $self->{tags} = [@$tags] }
+  else
+  { croak "set_tags must be HASH or ARRAY" }
+}
+
+sub tags
+{
+  my $self = shift;
+  return [@{$self->{tags}}];  # Return copy
+}
+
+sub tag_xml
+{
+  my ($self,$writer) = @_;
+  my @a = @{$self->{tags}};
+  
+  my $str = "";
+  
+  while( my($k,$v) = splice @a, 0, 2 )
+  {
+    $writer->emptyTag( "tag", 'k' => $k, 'v' => $v );
+  }
+}
+
+our $_ID = -1;
+
+sub set_id
+{
+  my($self,$id) = @_;
+
+  if( not defined $id )
+  { $id = $_ID-- }  
+  $self->{id} = $id;
+}  
+
+sub id
+{
+  my $self = shift;
+  return $self->{id};
+}
+
+sub set_timestamp
+{
+  my($self,$time) = @_;
+  if( defined $time )
+  { $self->{timestamp} = $time }
+  else
+  { $self->{timestamp} = strftime "%Y-%m-%dT%H:%M:%S+00:00", gmtime(time) }
+}
+
+sub timestamp
+{
+  my $self = shift;
+  return $self->{timestamp};
+}
+
+sub full_xml
+{
+  my $self = shift;
+  return qq(<?xml version="1.0"?>\n<osm version="0.5">\n).$self->xml()."</osm>\n";
+}
+
+package Geo::OSM::Way;
+our @ISA = qw(Geo::OSM::Entity);
+use Carp;
+
+sub new
+{
+  my($class, $attr, $tags, $nodes) = @_;
+  
+  my $obj = bless $class->SUPER::_new(), $class;
+  
+  $obj->set_tags($tags);
+  $obj->set_nodes($nodes);
+  $obj->set_id($attr->{id} );
+  $obj->set_timestamp( $attr->{timestamp} );
+  
+  return $obj;
+}
+
+sub type { return "way" }
+
+sub set_nodes
+{
+  my($self,$nodes) = @_;
+  if( not defined $nodes )
+  { $nodes = [] }
+  if( ref($nodes) ne "ARRAY" )
+  { $nodes = [$nodes] }
+  if( scalar( grep { (ref($_) ne "")?$_->type ne "node":$_ !~ /^-?\d+/ } @$nodes ) )
+  { croak "Expected array of nodes" }
+  $self->{nodes} = [map { ref($_)?$_->id:$_ } @$nodes];
+}
+
+sub nodes
+{
+  my $self = shift;
+  return [@{$self->{nodes}}];  # Return a copy
+}
+
+sub xml
+{
+  my $self = shift;
+  my $str = "";
+  my $writer = $self->_get_writer(\$str);
+
+  $writer->startTag( "way", id => $self->id, timestamp => $self->timestamp );
+  $self->tag_xml( $writer );
+  for my $node (@{$self->nodes})
+  {
+    $writer->emptyTag( "nd", ref => $node );
+  }
+  $writer->endTag( "way" );
+  $writer->end;
+  return $str;
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my $incomplete = 0;
+  my ($new_id) = $mapper->map('way',$self->id);   # Determine mapped ID
+  # It is ok for the new_id to be incomplete; it may be a create request
+  
+  my @new_nodes = map { [ $mapper->map('node',$_) ] } @{$self->nodes};
+  map { $incomplete |= $_->[1] } @new_nodes;
+  # incomplete tracks if any of the segs are incomplete
+  
+  my $new_ent = new Geo::OSM::Way( {id=>$new_id, timestamp=>$self->timestamp}, $self->tags, [map {$_->[0]} @new_nodes] );
+  return($new_ent,$incomplete);
+}
+
+package Geo::OSM::Relation::Member;
+use Carp;
+# Relation reference can be specified in several ways:
+# { type => $type, ref => $id [ , role => $str ] }
+# { ref => $obj [ , role => $str ] }
+# [ $type, $id [,$role] ]
+# [ $obj, [,$role] ]
+sub new
+{
+  my $class = shift;
+  my $arg = shift;
+  return $arg if ref($arg) eq $class;   # Return if already object
+  if( ref($arg) eq "ARRAY" )
+  {
+    if( ref $arg->[0] )
+    { $arg = { ref => $arg->[0], role => $arg->[1] } }
+    else
+    { $arg = { type => $arg->[0], ref => $arg->[1], role => $arg->[2] } }
+  }
+  if( ref($arg) eq "HASH" )
+  { 
+    if( ref $arg->{ref} )
+    { $arg = [ $arg->{ref}->type, $arg->{ref}->id, $arg->{role} ] }
+    else
+    { $arg = [ $arg->{type}, $arg->{ref}, $arg->{role} ] }
+  }
+  else
+  { croak "Relation reference must be array or hash" }
+  croak "Bad type of member '$arg->[0]'"
+    unless $arg->[0] =~ /^(way|node|relation)$/;
+  croak "Bad member id '$arg->[1]'"
+    unless $arg->[1] =~ /^-?\d+$/;
+  $arg->[2] ||= "";
+    
+  return bless $arg, $class;
+}
+
+sub member_type { shift->[0] }
+sub ref { shift->[1] }
+sub role { shift->[2] }
+
+sub type { return "relation:member" }
+
+sub _xml
+{
+  my $self = shift;
+  my $writer = shift;
+
+  $writer->emptyTag( "member", type => $self->member_type, ref => $self->ref, role => $self->role );
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my ($new_ref,$incomplete) = $mapper->map($self->member_type,$self->ref);
+  my $new_member = new Geo::OSM::Relation::Member( { type => $self->member_type, ref => $new_ref, role => $self->role } );
+  return($new_member,$incomplete);
+}
+
+package Geo::OSM::Relation;
+our @ISA = qw(Geo::OSM::Entity);
+
+sub new
+{
+  my($class, $attr, $tags, $members) = @_;
+  
+  my $obj = bless $class->SUPER::_new(), $class;
+  
+  $obj->set_tags($tags);
+  $obj->set_members($members);
+  $obj->set_id($attr->{id} );
+  $obj->set_timestamp( $attr->{timestamp} );
+  
+  return $obj;
+}
+
+sub set_members
+{
+  my($self,$members) = @_;
+  if( not defined $members )
+  { $members = [] }
+  if( ref($members) ne "ARRAY" )
+  { $members = [$members] }
+  $self->{members} = [map { new Geo::OSM::Relation::Member($_) } @$members];
+}
+
+sub members
+{
+  my $self = shift;
+  return [@{$self->{members}}];
+}
+
+sub type { return "relation" }
+
+sub xml
+{
+  my $self = shift;
+  my $str = "";
+  my $writer = $self->_get_writer(\$str);
+
+  $writer->startTag( "relation", id => $self->id, timestamp => $self->timestamp );
+  $self->tag_xml( $writer );
+  # Write members
+  foreach my $member (@{$self->{members}})
+  { $member->_xml( $writer ) }
+  $writer->endTag( "relation" );
+  $writer->end;
+  return $str;
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my $incomplete = 0;
+  
+  my ($new_id) = $mapper->map('relation',$self->id);
+  my @new_members = map { [ $_->map($mapper) ] } @{$self->members};
+  map { $incomplete |= $_->[1] } @new_members;
+  # incomplete tracks if any of the members are incomplete
+  my $new_ent = new Geo::OSM::Relation( {id=>$new_id, timestamp=>$self->timestamp}, $self->tags, [map {$_->[0]} @new_members] );
+  return($new_ent,$incomplete);
+}
+
+package Geo::OSM::Node;
+use Carp;
+our @ISA = qw(Geo::OSM::Entity);
+
+sub new
+{
+  my($class, $attr, $tags) = @_;
+  
+  my $obj = bless $class->SUPER::_new(), $class;
+  
+  $obj->set_tags($tags);
+  $obj->set_id($attr->{id} );
+  $obj->set_timestamp( $attr->{timestamp} );
+  if( $attr->{lon} !~ /^[-+]?\d+(\.\d+)?([eE][+-]?\d+)?$/ or
+      $attr->{lat} !~ /^[-+]?\d+(\.\d+)?([eE][+-]?\d+)?$/ )
+  {
+    croak "Invalid lat,lon values ($attr->{lat},$attr->{lon})\n";
+  }
+  $obj->{lon} = $attr->{lon};
+  $obj->{lat} = $attr->{lat};
+  
+  return $obj;
+}
+
+sub type { return "node" }
+
+sub set_latlon
+{
+  my($self,$lat,$lon) = @_;
+  $self->{lat} = $lat;
+  $self->{lon} = $lon;
+}
+
+sub lat
+{
+  shift->{lat};
+}
+sub lon
+{
+  shift->{lon};
+}
+
+sub xml
+{
+  my $self = shift;
+  my $str = "";
+  my $writer = $self->_get_writer(\$str);
+  
+  $writer->startTag( "node", id => $self->id, lat => $self->lat, lon => $self->lon, timestamp => $self->timestamp );
+  $self->tag_xml( $writer );
+  $writer->endTag( "node" );
+  $writer->end;
+  return $str;
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my ($new_id) = $mapper->map('node',$self->id);
+  my $new_ent = new Geo::OSM::Node( {id=>$new_id, timestamp=>$self->timestamp, lat=>$self->lat, lon=>$self->lon}, $self->tags );
+  return($new_ent,0);
+}
+
+
+
+1;
diff --git a/navit/navit/script/osm/Geo/OSM/EntitiesV6.pm b/navit/navit/script/osm/Geo/OSM/EntitiesV6.pm
new file mode 100644 (file)
index 0000000..8747c17
--- /dev/null
@@ -0,0 +1,404 @@
+##################################################################
+## EntitiesV3.pm - Wraps entities used by OSM                   ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Package that wraps the entities used by OSM into Perl        ##
+## object, so they can be easily manipulated by various         ##
+## packages.                                                    ##
+##                                                              ##
+## Licence: LGPL                                                ##
+##################################################################
+
+use XML::Writer;
+use strict;
+
+############################################################################
+## Top level Entity type, parent of all types, includes stuff relating to ##
+## tags and IDs which are shared by all entity types                      ##
+############################################################################
+package Geo::OSM::Entity;
+use POSIX qw(strftime);
+
+use Carp;
+
+sub _new
+{
+  bless {}, shift;
+}
+
+sub _get_writer
+{
+  my($self,$res) = @_;
+  return new XML::Writer(OUTPUT => $res, NEWLINES => 0, ENCODING => 'utf-8');
+}
+
+sub add_tag
+{
+  my($self, $k,$v) = @_;
+  push @{$self->{tags}}, $k, $v;
+}
+
+sub add_tags
+{
+  my($self, @tags) = @_;
+  if( scalar(@tags)&1 )
+  { croak "add_tags requires an even number of arguments" }
+  push @{$self->{tags}}, @tags;
+}
+
+sub set_tags
+{
+  my($self,$tags) = @_;
+  if( ref($tags) eq "HASH" )
+  { $self->{tags} = [%$tags] }
+  elsif( ref($tags) eq "ARRAY" )
+  { $self->{tags} = [@$tags] }
+  else
+  { croak "set_tags must be HASH or ARRAY" }
+}
+
+sub tags
+{
+  my $self = shift;
+  return [@{$self->{tags}}];  # Return copy
+}
+
+sub tag_xml
+{
+  my ($self,$writer) = @_;
+  my @a = @{$self->{tags}};
+  
+  my $str = "";
+  
+  while( my($k,$v) = splice @a, 0, 2 )
+  {
+    $writer->emptyTag( "tag", 'k' => $k, 'v' => $v );
+  }
+}
+
+our $_ID = -1;
+
+sub set_id
+{
+  my($self,$id) = @_;
+
+  if( not defined $id )
+  { $id = $_ID-- }  
+  $self->{id} = $id;
+}  
+
+sub id
+{
+  my $self = shift;
+  return $self->{id};
+}
+
+sub set_timestamp
+{
+  my($self,$time) = @_;
+  if( defined $time )
+  { $self->{timestamp} = $time }
+  else
+  { $self->{timestamp} = strftime "%Y-%m-%dT%H:%M:%S+00:00", gmtime(time) }
+}
+
+sub timestamp
+{
+  my $self = shift;
+  return $self->{timestamp};
+}
+
+sub set_changeset
+{
+  my($self,$changeset) = @_;
+  $self->{changeset} = $changeset;
+}
+
+sub changeset
+{
+  shift->{changeset};
+}
+
+sub set_version
+{
+  my($self,$version) = @_;
+  $self->{version} = $version;
+}
+
+sub version
+{
+  shift->{version};
+}
+
+sub full_xml
+{
+  my $self = shift;
+  return qq(<?xml version="1.0"?>\n<osm version="0.6">\n).$self->xml()."</osm>\n";
+}
+
+package Geo::OSM::Way;
+our @ISA = qw(Geo::OSM::Entity);
+use Carp;
+
+sub new
+{
+  my($class, $attr, $tags, $nodes) = @_;
+  
+  my $obj = bless $class->SUPER::_new(), $class;
+  
+  $obj->set_tags($tags);
+  $obj->set_nodes($nodes);
+  $obj->set_id($attr->{id} );
+  $obj->set_timestamp( $attr->{timestamp} );
+  $obj->set_changeset( $attr->{changeset} );
+  $obj->set_version( $attr->{version} );
+  
+  return $obj;
+}
+
+sub type { return "way" }
+
+sub set_nodes
+{
+  my($self,$nodes) = @_;
+  if( not defined $nodes )
+  { $nodes = [] }
+  if( ref($nodes) ne "ARRAY" )
+  { $nodes = [$nodes] }
+  if( scalar( grep { (ref($_) ne "")?$_->type ne "node":$_ !~ /^-?\d+/ } @$nodes ) )
+  { croak "Expected array of nodes" }
+  $self->{nodes} = [map { ref($_)?$_->id:$_ } @$nodes];
+}
+
+sub nodes
+{
+  my $self = shift;
+  return [@{$self->{nodes}}];  # Return a copy
+}
+
+sub xml
+{
+  my $self = shift;
+  my $str = "";
+  my $writer = $self->_get_writer(\$str);
+
+  $writer->startTag( "way", id => $self->id, timestamp => $self->timestamp, changeset => $self->changeset, version => $self->version);
+  $self->tag_xml( $writer );
+  for my $node (@{$self->nodes})
+  {
+    $writer->emptyTag( "nd", ref => $node );
+  }
+  $writer->endTag( "way" );
+  $writer->end;
+  return $str;
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my $incomplete = 0;
+  my ($new_id) = $mapper->map('way',$self->id);   # Determine mapped ID
+  # It is ok for the new_id to be incomplete; it may be a create request
+  
+  my @new_nodes = map { [ $mapper->map('node',$_) ] } @{$self->nodes};
+  map { $incomplete |= $_->[1] } @new_nodes;
+  # incomplete tracks if any of the segs are incomplete
+  
+  my $new_ent = new Geo::OSM::Way( {id=>$new_id, timestamp=>$self->timestamp}, $self->tags, [map {$_->[0]} @new_nodes] );
+  return($new_ent,$incomplete);
+}
+
+package Geo::OSM::Relation::Member;
+use Carp;
+# Relation reference can be specified in several ways:
+# { type => $type, ref => $id [ , role => $str ] }
+# { ref => $obj [ , role => $str ] }
+# [ $type, $id [,$role] ]
+# [ $obj, [,$role] ]
+sub new
+{
+  my $class = shift;
+  my $arg = shift;
+  return $arg if ref($arg) eq $class;   # Return if already object
+  if( ref($arg) eq "ARRAY" )
+  {
+    if( ref $arg->[0] )
+    { $arg = { ref => $arg->[0], role => $arg->[1] } }
+    else
+    { $arg = { type => $arg->[0], ref => $arg->[1], role => $arg->[2] } }
+  }
+  if( ref($arg) eq "HASH" )
+  { 
+    if( ref $arg->{ref} )
+    { $arg = [ $arg->{ref}->type, $arg->{ref}->id, $arg->{role} ] }
+    else
+    { $arg = [ $arg->{type}, $arg->{ref}, $arg->{role} ] }
+  }
+  else
+  { croak "Relation reference must be array or hash" }
+  croak "Bad type of member '$arg->[0]'"
+    unless $arg->[0] =~ /^(way|node|relation)$/;
+  croak "Bad member id '$arg->[1]'"
+    unless $arg->[1] =~ /^-?\d+$/;
+  $arg->[2] ||= "";
+    
+  return bless $arg, $class;
+}
+
+sub member_type { shift->[0] }
+sub ref { shift->[1] }
+sub role { shift->[2] }
+
+sub type { return "relation:member" }
+
+sub _xml
+{
+  my $self = shift;
+  my $writer = shift;
+
+  $writer->emptyTag( "member", type => $self->member_type, ref => $self->ref, role => $self->role );
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my ($new_ref,$incomplete) = $mapper->map($self->member_type,$self->ref);
+  my $new_member = new Geo::OSM::Relation::Member( { type => $self->member_type, ref => $new_ref, role => $self->role } );
+  return($new_member,$incomplete);
+}
+
+package Geo::OSM::Relation;
+our @ISA = qw(Geo::OSM::Entity);
+
+sub new
+{
+  my($class, $attr, $tags, $members) = @_;
+  
+  my $obj = bless $class->SUPER::_new(), $class;
+  
+  $obj->set_tags($tags);
+  $obj->set_members($members);
+  $obj->set_id($attr->{id} );
+  $obj->set_timestamp( $attr->{timestamp} );
+  $obj->set_changeset( $attr->{changeset} );
+  $obj->set_version( $attr->{version} );
+  
+  return $obj;
+}
+
+sub set_members
+{
+  my($self,$members) = @_;
+  if( not defined $members )
+  { $members = [] }
+  if( ref($members) ne "ARRAY" )
+  { $members = [$members] }
+  $self->{members} = [map { new Geo::OSM::Relation::Member($_) } @$members];
+}
+
+sub members
+{
+  my $self = shift;
+  return [@{$self->{members}}];
+}
+
+sub type { return "relation" }
+
+sub xml
+{
+  my $self = shift;
+  my $str = "";
+  my $writer = $self->_get_writer(\$str);
+
+  $writer->startTag( "relation", id => $self->id, timestamp => $self->timestamp, changeset => $self->changeset, version => $self->version );
+  $self->tag_xml( $writer );
+  # Write members
+  foreach my $member (@{$self->{members}})
+  { $member->_xml( $writer ) }
+  $writer->endTag( "relation" );
+  $writer->end;
+  return $str;
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my $incomplete = 0;
+  
+  my ($new_id) = $mapper->map('relation',$self->id);
+  my @new_members = map { [ $_->map($mapper) ] } @{$self->members};
+  map { $incomplete |= $_->[1] } @new_members;
+  # incomplete tracks if any of the members are incomplete
+  my $new_ent = new Geo::OSM::Relation( {id=>$new_id, timestamp=>$self->timestamp}, $self->tags, [map {$_->[0]} @new_members] );
+  return($new_ent,$incomplete);
+}
+
+package Geo::OSM::Node;
+use Carp;
+use Data::Dumper;
+our @ISA = qw(Geo::OSM::Entity);
+
+sub new
+{
+  my($class, $attr, $tags) = @_;
+  
+  my $obj = bless $class->SUPER::_new(), $class;
+  
+  $obj->set_tags($tags);
+  $obj->set_id($attr->{id} );
+  $obj->set_timestamp( $attr->{timestamp} );
+  $obj->set_changeset( $attr->{changeset} );
+  $obj->set_version( $attr->{version} );
+  if( $attr->{lon} !~ /^[-+]?\d+(\.\d+)?([eE][+-]?\d+)?$/ or
+      $attr->{lat} !~ /^[-+]?\d+(\.\d+)?([eE][+-]?\d+)?$/ )
+  {
+    croak "Invalid lat,lon values ($attr->{lat},$attr->{lon})\n";
+  }
+  $obj->{lon} = $attr->{lon};
+  $obj->{lat} = $attr->{lat};
+  
+  return $obj;
+}
+
+sub type { return "node" }
+
+sub set_latlon
+{
+  my($self,$lat,$lon) = @_;
+  $self->{lat} = $lat;
+  $self->{lon} = $lon;
+}
+
+sub lat
+{
+  shift->{lat};
+}
+sub lon
+{
+  shift->{lon};
+}
+
+sub xml
+{
+  my $self = shift;
+  my $str = "";
+  my $writer = $self->_get_writer(\$str);
+  
+  $writer->startTag( "node", id => $self->id, lat => $self->lat, lon => $self->lon, timestamp => $self->timestamp, changeset => $self->changeset, version => $self->version);
+  $self->tag_xml( $writer );
+  $writer->endTag( "node" );
+  $writer->end;
+  return $str;
+}
+
+sub map
+{
+  my($self,$mapper) = @_;
+  my ($new_id) = $mapper->map('node',$self->id);
+  my $new_ent = new Geo::OSM::Node( {id=>$new_id, timestamp=>$self->timestamp, lat=>$self->lat, lon=>$self->lon}, $self->tags );
+  return($new_ent,0);
+}
+
+
+
+1;
diff --git a/navit/navit/script/osm/Geo/OSM/MapFeatures.pm b/navit/navit/script/osm/Geo/OSM/MapFeatures.pm
new file mode 100644 (file)
index 0000000..c4ae62f
--- /dev/null
@@ -0,0 +1,194 @@
+##################################################################
+package Geo::OSM::MapFeatures;
+##################################################################
+
+use Exporter;
+@ISA = qw( Exporter );
+use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
+@EXPORT = qw( );
+
+use strict;
+use warnings;
+
+use HTTP::Request;
+use File::Basename;
+use File::Copy;
+use File::Path;
+use Getopt::Long;
+use HTTP::Request;
+use Storable ();
+use Data::Dumper;
+
+use Utils::File;
+use Utils::Debug;
+use Utils::LWP::Utils;
+use XML::Parser;
+use XML::Simple;
+
+my $self;
+
+# ------------------------------------------------------------------
+sub style($){
+    my $self = shift;
+
+}
+
+# ------------------------------------------------------------------
+# load the complete MapFeatures Structure into memory
+sub load($;$){
+    my ($class, $filename) = @_;
+    #$filename ||= '../../freemap/freemap.xml';
+    $filename ||= './map-features.xml';
+    print("Loading Map Features from  $filename\n") if $VERBOSE || $DEBUG;
+    print "$filename:  ".(-s $filename)." Bytes\n" if $DEBUG;
+    print STDERR "Parsing file: $filename\n" if $DEBUG;
+
+    my $fh = data_open($filename);
+    if (not $fh) {
+       print STDERR "WARNING: Could not open osm data from $filename\n";
+       return;
+    }
+    my $self = XMLin($fh);
+    
+    if (not $self) {
+       print STDERR "WARNING: Could not parse osm data from $filename\n";
+       return;
+    }
+
+    #delete $self->{defs}->{symbol};
+    #warn Dumper(\$self->{defs});
+    #warn Dumper(\$self->{data});
+    #warn Dumper(\$self->{rule});
+    #warn Dumper(keys %{$self});
+    #warn Dumper(%{$self});
+    
+    bless($self,$class);
+    return $self;
+}
+
+# ------------------------------------------------------------------
+# Load icons into memory nad create a PDF Version out of it
+sub load_icons($$){
+    my $self = shift;
+    my $PDF  = shift;
+    die "No PDF Document defined\n" unless $PDF;
+
+    print STDERR "load_icons():\n" if $DEBUG;
+#    print STDERR Dumper(\$self);
+
+    for my $rule ( @{$self->{rule}} ) {
+       my $img = $rule->{style}->{image};
+       next unless defined $img;
+       $img =~s/^images\///;
+       my $img_filename;
+       for my $path ( qw( ../../freemap/images
+                          ../../map-icons/square.small
+                          ../../map-icons/square.big
+                          ../../map-icons/classic.big
+                          ../../map-icons/classic.small
+                          ../../map-icons/nick
+                          ../../map-icons/classic/other
+                          ) ) {
+           $img_filename = "$path/$img";
+           if ( -s $img_filename ) {
+               my $png = $PDF->image_png($img_filename);
+               $rule->{png}=$png;
+               #print STDERR "image: $img_filename\n";
+               last;
+           }
+       }
+       
+       if ( ! $rule->{png} ) {
+           warn "missing $img\n";
+       }
+       #print STDERR "rule: ".Dumper(\$rule);
+#      print STDERR "condition: ".Dumper(\$rule->{condition});
+       my $condition = $rule->{condition};
+#      print STDERR "image: $img_filename\t";
+       print STDERR "     #condition: $condition->{k}=$condition->{v}\t";
+       printf STDERR "scale: %d-%d",
+           ($rule->{style}->{scale_max}||0),
+           ($rule->{style}->{scale_min}||0);
+       print STDERR "get_icon() image: $img\n";
+
+    }
+}
+
+# ------------------------------------------------------------------
+sub get_icons($$$){
+    my $self = shift;
+    my $rule_line= shift;
+    my $scale = shift;
+    return undef if $rule_line =~ m/^[\s\,]*$/;
+#    return undef if $rule_line eq ",";
+
+    my ($dummy1,$dummy2,$attr) = split(",",$rule_line,3);
+    my %attr;
+    foreach my $kv ( split(",",$attr)){
+       my ($k,$v)=split("=",$kv);
+       $attr{$k}=$v;
+    }
+
+
+    print STDERR "get_icon($attr)\n";
+
+    my $png =undef;
+    for my $rule ( @{$self->{rule}} ) {
+       my $img = $rule->{style}->{image};
+       next unless $img;
+
+       my $condition = $rule->{condition};
+#      print STDERR "condition: $condition->{k}=$condition->{v}\n";
+       if ( defined ( $attr{scale_max}) && 
+            $scale > $attr{scale_max}) {
+           next;
+       }
+
+       if ( defined( $attr{ $condition->{k}}) &&
+            $attr{ $condition->{k}} eq $condition->{v} ) {
+           print STDERR "condition: $condition->{k}=$condition->{v}\n";
+           print STDERR "get_icon() image: $img\t";
+           $png = $rule->{png};
+       }
+       
+       return $png if $png;
+    }
+    return undef;
+}
+1;
+
+=head1 NAME
+
+Geo::OSM::MapFeature
+
+=head1 DESCRIPTION
+
+Load the MapFeatures.xml into memory
+
+=head1 COPYRIGHT
+
+Copyright 2006, Jörg Ostertag
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=head1 AUTHOR
+
+Jörg Ostertag (MapFeatures-for-openstreetmap@ostertag.name)
+
+=head1 SEE ALSO
+
+http://www.openstreetmap.org/
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/OsmChangeReaderV3.pm b/navit/navit/script/osm/Geo/OSM/OsmChangeReaderV3.pm
new file mode 100644 (file)
index 0000000..65103a3
--- /dev/null
@@ -0,0 +1,323 @@
+##################################################################
+## OsmChangeReader.pm - Library for reading OSM change files    ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Package that reads both osmChange and JOSM file format change##
+## files. The user creates the parse with a callback and the    ##
+## loader will call the callback for each detected change. Note ##
+## that for JOSM file entires that are not changes are ignored. ##
+##                                                              ##
+## Licence: LGPL                                                ##
+##################################################################
+package Geo::OSM::OsmChangeReader;
+
+use strict;
+use warnings;
+
+use Utils::File;
+use Utils::Math;
+use Utils::Debug;
+use XML::Parser;
+use Carp;
+
+use Geo::OSM::EntitiesV3;
+
+use constant STATE_INIT => 1;
+use constant STATE_EXPECT_COMMAND => 2;
+use constant STATE_EXPECT_ENTITY => 3;
+use constant STATE_EXPECT_BODY => 4;
+
+use constant FILETYPE_UNKNOWN   => 0;
+use constant FILETYPE_OSMCHANGE => 1;
+use constant FILETYPE_OSM       => 2;
+
+sub new
+{ 
+  my $obj = bless{}, shift;
+  my $proc = shift;
+  my $prog = shift;
+  if( ref $proc ne "CODE" )
+  { die "new Geo::OSM::OsmChangeReader requires a sub as argument\n" }
+  $obj->{oldproc}  = $proc;
+  if( defined $prog )
+  { $obj->{progress} = $prog }
+  return $obj;
+}
+
+# With this initialiser, your process will get called with instantiated objects rather than useless details
+sub init
+{ 
+  my $obj = bless{}, shift;
+  my $proc = shift;
+  my $prog = shift;
+  if( ref $proc ne "CODE" )
+  { die "init Geo::OSM::OsmChangeReader requires a sub as argument\n" }
+  $obj->{newproc}  = $proc;
+  if( defined $prog )
+  { $obj->{progress} = $prog }
+  return $obj;
+}
+
+sub _process
+{
+  my($self, $command, $entity, $attr, $tags, $segs) = @_;
+  
+  if( defined $self->{oldproc} )
+  {
+    return $self->{oldproc}->($command, $entity, $attr, $tags, $segs);
+  }
+  
+  my $ent;
+  if( $entity eq "node" )
+  {
+    $ent = new Geo::OSM::Node( $attr, $tags );
+  }
+  if( $entity eq "segment" )
+  {
+    $ent = new Geo::OSM::Segment( $attr, $tags );
+  }
+  if( $entity eq "way" )
+  {
+    $ent = new Geo::OSM::Way( $attr, $tags, $segs );
+  }
+  croak "Unknown entity '$entity'" if not defined $ent;
+
+  return $self->{newproc}->($command, $ent );
+}
+
+sub load{
+  my ($self, $file_name) = @_;
+
+  $self->{filetype} = FILETYPE_UNKNOWN;
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    my $fh = data_open($file_name);
+    die "Cannot open OSM File $file_name\n" unless $fh;
+    $self->{input_length} = -s $fh;
+    $self->{count}=0;
+    eval {
+       $P->parse($fh);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed $file_name in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n $file_name\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+
+}
+
+sub parse($)
+{
+  my ($self, $string) = @_;
+
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    $self->{input_length} = length($string);
+    $self->{count}=0;
+    eval {
+       $P->parse($string);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed string in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n [$string]\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+}
+
+# Function is called whenever an XML tag is started
+sub DoStart
+{
+#print @_,"\n";   
+  my ($self, $Expat, $Name, %Attr) = @_;
+
+  if( $self->{filetype} == FILETYPE_UNKNOWN )
+  {
+    if( $self->{state} == STATE_INIT )
+    {
+      if($Name eq "osmChange"){
+        $self->{state} = STATE_EXPECT_COMMAND;
+        $self->{filetype} = FILETYPE_OSMCHANGE;
+
+        if( $Attr{version} ne "0.3" and $Attr{version} ne "0.4" )
+        { die "OsmChangeReaderV3 can only read 0.3 and 0.4 files, found '$Attr{version}'\n" }
+      } elsif($Name eq "osm"){
+        $self->{state} = STATE_EXPECT_ENTITY;
+        $self->{filetype} = FILETYPE_OSM;
+        
+        if( $Attr{version} ne "0.3" and $Attr{version} ne "0.4" )
+        { die "OsmChangeReaderV3 can only read 0.3 and 0.4 files, found '$Attr{version}'\n" }
+      } else {
+        die "Expected 'osmChange' tag, got '$Name'\n";
+      }
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_COMMAND )
+  {
+    if($Name eq 'create' or $Name eq 'modify' or $Name eq 'delete'){
+      $self->{command} = $Name;
+      $self->{state} = STATE_EXPECT_ENTITY;
+    } else {
+      die "Expected command\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    # Pick up the origin attribute from the bound tag
+    if( $Name eq "bound" )
+    {
+      if( exists $Attr{origin} )
+      {
+        $self->{origin} = $Attr{origin};
+      }
+      return;
+    }
+    if($Name eq "node" or $Name eq "segment" or $Name eq "way"){
+      $self->{entity} = $Name;
+      $self->{attr} = {%Attr};
+      $self->{tags} = [];
+      $self->{segs} = ($Name eq "way") ? [] : undef;
+      $self->{state} = STATE_EXPECT_BODY;
+    } else {
+      die "Expected entity\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if($Name eq "tag"){
+      push @{$self->{tags}}, $Attr{"k"}, $Attr{"v"};
+    }
+    if($Name eq "seg"){
+      push @{$self->{segs}}, $Attr{"id"};
+    }
+  }
+}
+
+# Function is called whenever an XML tag is ended
+sub DoEnd
+{
+  my ($self, $Expat, $Name) = @_;
+  if( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if( $Name eq $self->{entity} )
+    {
+      if( $self->{filetype} == FILETYPE_OSMCHANGE )
+      {
+        $self->_process( $self->{command}, $self->{entity}, $self->{attr}, $self->{tags}, $self->{segs} );
+      }
+      else  # FILETYPE_OSM
+      {
+        # Only entities with a modify tag are interesting, or if they have a negative ID (that's create)
+        if( exists $self->{attr}->{action} )
+        {
+          $self->_process( $self->{attr}->{action}, $self->{entity}, $self->{attr}, $self->{tags}, $self->{segs} );
+        }
+        elsif( $self->{attr}{id} < 0 )
+        {
+          $self->_process( "create", $self->{entity}, $self->{attr}, $self->{tags}, $self->{segs} );
+        }
+      }
+      $self->{count}++;
+      if( $self->{progress} and ($self->{count}%11) == 1)
+      {
+        $self->{progress}->($self->{count}, $Expat->current_byte()/$self->{input_length} );
+      }
+      $self->{state} = STATE_EXPECT_ENTITY;
+    }
+    return;
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    return if $Name eq "bound";
+    if( $self->{filetype} == FILETYPE_OSMCHANGE )
+    {
+      if( $Name eq $self->{command} )
+      {
+        $self->{state} = STATE_EXPECT_COMMAND;
+      }else {die}
+    }
+    else  # FILETYPE_OSM
+    {
+      if( $Name eq "osm" )
+      {
+        $self->{state} = STATE_INIT;
+      } else {die}
+    }
+    return;
+  }
+  elsif( $self->{state} == STATE_EXPECT_COMMAND )
+  {
+    if( $Name eq "osmChange" )
+    {
+      $self->{state} = STATE_INIT;
+    }else {die}
+    return;
+  }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+OsmChangeReaderV3 - Module for reading OpenStreetMap V3 Change XML data files
+
+=head1 SYNOPSIS
+
+  my $OSM = new Geo::OSM::OsmChangeReader(\&process);
+  $OSM->load("Data/changes.osc");
+  
+  sub process
+  {
+    my($OSM, $command, $entity, $attr, $tags, $segs) = @_;
+    print "Doing a $command on a $entity $attr->{id}\n";
+    while( my($k,$v) = splice @{$tags}, 0, 2 )
+    { print "  $k: $v\n" }
+    if( defined $segs )
+    { print "  Segs: ", join(", ",@$segs),"\n"; }
+  }
+
+=head1 AUTHOR
+
+Martijn van Oosterhout <kleptog@svana.org>
+based on OsmXML.pm written by:
+Oliver White (oliver.white@blibbleblobble.co.uk)
+
+=head1 COPYRIGHT
+
+Copyright 2007, Martijn van Oosterhout
+Copyright 2006, Oliver White
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/OsmChangeReaderV5.pm b/navit/navit/script/osm/Geo/OSM/OsmChangeReaderV5.pm
new file mode 100644 (file)
index 0000000..fdc65e1
--- /dev/null
@@ -0,0 +1,316 @@
+##################################################################
+## OsmChangeReader.pm - Library for reading OSM change files    ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Package that reads both osmChange and JOSM file format change##
+## files. The user creates the parse with a callback and the    ##
+## loader will call the callback for each detected change. Note ##
+## that for JOSM file entires that are not changes are ignored. ##
+##                                                              ##
+## Licence: LGPL                                                ##
+##################################################################
+package Geo::OSM::OsmChangeReader;
+
+use strict;
+use warnings;
+
+use Utils::File;
+use Utils::Math;
+use Utils::Debug;
+use XML::Parser;
+use Carp;
+
+use Geo::OSM::EntitiesV5;
+
+use constant STATE_INIT => 1;
+use constant STATE_EXPECT_COMMAND => 2;
+use constant STATE_EXPECT_ENTITY => 3;
+use constant STATE_EXPECT_BODY => 4;
+
+use constant FILETYPE_UNKNOWN   => 0;
+use constant FILETYPE_OSMCHANGE => 1;
+use constant FILETYPE_OSM       => 2;
+
+# With this initialiser, your process will get called with instantiated objects
+sub init
+{ 
+  my $obj = bless{}, shift;
+  my $proc = shift;
+  my $prog = shift;
+  if( ref $proc ne "CODE" )
+  { die "init Geo::OSM::OsmChangeReader requires a sub as argument\n" }
+  $obj->{newproc}  = $proc;
+  if( defined $prog )
+  { $obj->{progress} = $prog }
+  return $obj;
+}
+
+sub _process
+{
+  my($self, $command, $entity, $attr, $tags, $members) = @_;
+  
+  if( defined $self->{oldproc} )
+  {
+    return $self->{oldproc}->($command, $entity, $attr, $tags, $members);
+  }
+  
+  my $ent;
+  if( $entity eq "node" )
+  {
+    $ent = new Geo::OSM::Node( $attr, $tags );
+  }
+  if( $entity eq "relation" )
+  {
+    $ent = new Geo::OSM::Relation( $attr, $tags, $members );
+  }
+  if( $entity eq "way" )
+  {
+    $ent = new Geo::OSM::Way( $attr, $tags, $members );
+  }
+  croak "Unknown entity '$entity'" if not defined $ent;
+
+  return $self->{newproc}->($command, $ent );
+}
+
+sub load{
+  my ($self, $file_name) = @_;
+
+  $self->{filetype} = FILETYPE_UNKNOWN;
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    my $fh = data_open($file_name);
+    die "Cannot open OSM File $file_name\n" unless $fh;
+    $self->{input_length} = -s $fh;
+    $self->{count}=0;
+    eval {
+       $P->parse($fh);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed $file_name in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n $file_name\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+
+}
+
+sub parse($)
+{
+  my ($self, $string) = @_;
+
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    $self->{input_length} = length($string);
+    $self->{count}=0;
+    eval {
+       $P->parse($string);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed string in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n [$string]\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+}
+
+# Function is called whenever an XML tag is started
+sub DoStart
+{
+#print @_,"\n";   
+  my ($self, $Expat, $Name, %Attr) = @_;
+
+  if( $self->{filetype} == FILETYPE_UNKNOWN )
+  {
+    if( $self->{state} == STATE_INIT )
+    {
+      if($Name eq "osmChange"){
+        $self->{state} = STATE_EXPECT_COMMAND;
+        $self->{filetype} = FILETYPE_OSMCHANGE;
+
+        if( $Attr{version} ne "0.5" )
+        { die "OsmChangeReaderV3 can only read 0.5 files, found '$Attr{version}'\n" }
+      } elsif($Name eq "osm"){
+        $self->{state} = STATE_EXPECT_ENTITY;
+        $self->{filetype} = FILETYPE_OSM;
+        
+        if( $Attr{version} ne "0.5" )
+        { die "OsmChangeReaderV3 can only read 0.5 files, found '$Attr{version}'\n" }
+      } else {
+        die "Expected 'osmChange' tag, got '$Name'\n";
+      }
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_COMMAND )
+  {
+    if($Name eq 'create' or $Name eq 'modify' or $Name eq 'delete'){
+      $self->{command} = $Name;
+      $self->{state} = STATE_EXPECT_ENTITY;
+    } else {
+      die "Expected command\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    # Pick up the origin attribute from the bound tag
+    if( $Name eq "bound" )
+    {
+      if( exists $Attr{origin} )
+      {
+        $self->{origin} = $Attr{origin};
+      }
+      return;
+    }
+    if($Name eq "node" or $Name eq "relation" or $Name eq "way"){
+      $self->{entity} = $Name;
+      $self->{attr} = {%Attr};
+      $self->{tags} = [];
+      $self->{members} = ($Name ne "node") ? [] : undef;
+      $self->{state} = STATE_EXPECT_BODY;
+    } else {
+      die "Expected entity\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if($Name eq "tag"){
+      push @{$self->{tags}}, $Attr{"k"}, $Attr{"v"};
+    }
+    if($Name eq "nd"){
+      push @{$self->{members}}, $Attr{"ref"};
+    }
+    if($Name eq "member"){
+      push @{$self->{members}}, new Geo::OSM::Relation::Member( \%Attr );
+    }
+  }
+}
+
+# Function is called whenever an XML tag is ended
+sub DoEnd
+{
+  my ($self, $Expat, $Name) = @_;
+  if( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if( $Name eq $self->{entity} )
+    {
+      if( $self->{filetype} == FILETYPE_OSMCHANGE )
+      {
+        $self->_process( $self->{command}, $self->{entity}, $self->{attr}, $self->{tags}, $self->{members} );
+      }
+      else  # FILETYPE_OSM
+      {
+        # Only entities with a modify tag are interesting, or if they have a negative ID (that's create)
+        if( exists $self->{attr}->{action} and $self->{attr}->{action} eq "modify" and $self->{attr}{id} < 0 )
+        { $self->{attr}->{action} = "create" }
+        if( exists $self->{attr}->{action} )
+        {
+          $self->_process( $self->{attr}->{action}, $self->{entity}, $self->{attr}, $self->{tags}, $self->{members} );
+        }
+        elsif( $self->{attr}{id} < 0 )
+        {
+          $self->_process( "create", $self->{entity}, $self->{attr}, $self->{tags}, $self->{members} );
+        }
+      }
+      $self->{count}++;
+      if( $self->{progress} and ($self->{count}%11) == 1)
+      {
+        $self->{progress}->($self->{count}, $Expat->current_byte()/$self->{input_length} );
+      }
+      $self->{state} = STATE_EXPECT_ENTITY;
+    }
+    return;
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    return if $Name eq "bound";
+    if( $self->{filetype} == FILETYPE_OSMCHANGE )
+    {
+      if( $Name eq $self->{command} )
+      {
+        $self->{state} = STATE_EXPECT_COMMAND;
+      }else {die}
+    }
+    else  # FILETYPE_OSM
+    {
+      if( $Name eq "osm" )
+      {
+        $self->{state} = STATE_INIT;
+      } else {die}
+    }
+    return;
+  }
+  elsif( $self->{state} == STATE_EXPECT_COMMAND )
+  {
+    if( $Name eq "osmChange" )
+    {
+      $self->{state} = STATE_INIT;
+    }else {die}
+    return;
+  }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+OsmChangeReaderV5 - Module for reading OpenStreetMap V5 Change XML data files
+
+=head1 SYNOPSIS
+
+  my $OSM = new Geo::OSM::OsmChangeReader(\&process);
+  $OSM->load("Data/changes.osc");
+  
+  sub process
+  {
+    my($OSM, $command, $entity) = @_;
+    print "Doing a $command on a $entity ".$entity->id."\n";
+    my $tags = $entity->tags;
+    while( my($k,$v) = splice @{$tags}, 0, 2 )
+    { print "  $k: $v\n" }
+    if( $entity->type eq "way" )
+    { print "  Nodes: ", join(", ",@{$entity->nodes}),"\n"; }
+  }
+
+=head1 AUTHOR
+
+Martijn van Oosterhout <kleptog@svana.org>
+based on OsmXML.pm written by:
+Oliver White (oliver.white@blibbleblobble.co.uk)
+
+=head1 COPYRIGHT
+
+Copyright 2007, Martijn van Oosterhout
+Copyright 2006, Oliver White
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/OsmChangeReaderV6.pm b/navit/navit/script/osm/Geo/OSM/OsmChangeReaderV6.pm
new file mode 100644 (file)
index 0000000..e673978
--- /dev/null
@@ -0,0 +1,316 @@
+##################################################################
+## OsmChangeReader.pm - Library for reading OSM change files    ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Package that reads both osmChange and JOSM file format change##
+## files. The user creates the parse with a callback and the    ##
+## loader will call the callback for each detected change. Note ##
+## that for JOSM file entires that are not changes are ignored. ##
+##                                                              ##
+## Licence: LGPL                                                ##
+##################################################################
+package Geo::OSM::OsmChangeReader;
+
+use strict;
+use warnings;
+
+use Utils::File;
+use Utils::Math;
+use Utils::Debug;
+use XML::Parser;
+use Carp;
+
+use Geo::OSM::EntitiesV6;
+
+use constant STATE_INIT => 1;
+use constant STATE_EXPECT_COMMAND => 2;
+use constant STATE_EXPECT_ENTITY => 3;
+use constant STATE_EXPECT_BODY => 4;
+
+use constant FILETYPE_UNKNOWN   => 0;
+use constant FILETYPE_OSMCHANGE => 1;
+use constant FILETYPE_OSM       => 2;
+
+# With this initialiser, your process will get called with instantiated objects
+sub init
+{ 
+  my $obj = bless{}, shift;
+  my $proc = shift;
+  my $prog = shift;
+  if( ref $proc ne "CODE" )
+  { die "init Geo::OSM::OsmChangeReader requires a sub as argument\n" }
+  $obj->{newproc}  = $proc;
+  if( defined $prog )
+  { $obj->{progress} = $prog }
+  return $obj;
+}
+
+sub _process
+{
+  my($self, $command, $entity, $attr, $tags, $members) = @_;
+  
+  if( defined $self->{oldproc} )
+  {
+    return $self->{oldproc}->($command, $entity, $attr, $tags, $members);
+  }
+  
+  my $ent;
+  if( $entity eq "node" )
+  {
+    $ent = new Geo::OSM::Node( $attr, $tags );
+  }
+  if( $entity eq "relation" )
+  {
+    $ent = new Geo::OSM::Relation( $attr, $tags, $members );
+  }
+  if( $entity eq "way" )
+  {
+    $ent = new Geo::OSM::Way( $attr, $tags, $members );
+  }
+  croak "Unknown entity '$entity'" if not defined $ent;
+
+  return $self->{newproc}->($command, $ent );
+}
+
+sub load{
+  my ($self, $file_name) = @_;
+
+  $self->{filetype} = FILETYPE_UNKNOWN;
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    my $fh = data_open($file_name);
+    die "Cannot open OSM File $file_name\n" unless $fh;
+    $self->{input_length} = -s $fh;
+    $self->{count}=0;
+    eval {
+       $P->parse($fh);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed $file_name in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n $file_name\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+
+}
+
+sub parse($)
+{
+  my ($self, $string) = @_;
+
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    $self->{input_length} = length($string);
+    $self->{count}=0;
+    eval {
+       $P->parse($string);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed string in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n [$string]\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+}
+
+# Function is called whenever an XML tag is started
+sub DoStart
+{
+#print @_,"\n";   
+  my ($self, $Expat, $Name, %Attr) = @_;
+
+  if( $self->{filetype} == FILETYPE_UNKNOWN )
+  {
+    if( $self->{state} == STATE_INIT )
+    {
+      if($Name eq "osmChange"){
+        $self->{state} = STATE_EXPECT_COMMAND;
+        $self->{filetype} = FILETYPE_OSMCHANGE;
+
+        if( $Attr{version} ne "0.6" )
+        { die "OsmChangeReaderV6 can only read 0.6 files, found '$Attr{version}'\n" }
+      } elsif($Name eq "osm"){
+        $self->{state} = STATE_EXPECT_ENTITY;
+        $self->{filetype} = FILETYPE_OSM;
+        
+        if( $Attr{version} ne "0.6" )
+        { die "OsmChangeReaderV6 can only read 0.6 files, found '$Attr{version}'\n" }
+      } else {
+        die "Expected 'osmChange' tag, got '$Name'\n";
+      }
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_COMMAND )
+  {
+    if($Name eq 'create' or $Name eq 'modify' or $Name eq 'delete'){
+      $self->{command} = $Name;
+      $self->{state} = STATE_EXPECT_ENTITY;
+    } else {
+      die "Expected command\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    # Pick up the origin attribute from the bound tag
+    if( $Name eq "bound" )
+    {
+      if( exists $Attr{origin} )
+      {
+        $self->{origin} = $Attr{origin};
+      }
+      return;
+    }
+    if($Name eq "node" or $Name eq "relation" or $Name eq "way"){
+      $self->{entity} = $Name;
+      $self->{attr} = {%Attr};
+      $self->{tags} = [];
+      $self->{members} = ($Name ne "node") ? [] : undef;
+      $self->{state} = STATE_EXPECT_BODY;
+    } else {
+      die "Expected entity\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if($Name eq "tag"){
+      push @{$self->{tags}}, $Attr{"k"}, $Attr{"v"};
+    }
+    if($Name eq "nd"){
+      push @{$self->{members}}, $Attr{"ref"};
+    }
+    if($Name eq "member"){
+      push @{$self->{members}}, new Geo::OSM::Relation::Member( \%Attr );
+    }
+  }
+}
+
+# Function is called whenever an XML tag is ended
+sub DoEnd
+{
+  my ($self, $Expat, $Name) = @_;
+  if( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if( $Name eq $self->{entity} )
+    {
+      if( $self->{filetype} == FILETYPE_OSMCHANGE )
+      {
+        $self->_process( $self->{command}, $self->{entity}, $self->{attr}, $self->{tags}, $self->{members} );
+      }
+      else  # FILETYPE_OSM
+      {
+        # Only entities with a modify tag are interesting, or if they have a negative ID (that's create)
+        if( exists $self->{attr}->{action} and $self->{attr}->{action} eq "modify" and $self->{attr}{id} < 0 )
+        { $self->{attr}->{action} = "create" }
+        if( exists $self->{attr}->{action} )
+        {
+          $self->_process( $self->{attr}->{action}, $self->{entity}, $self->{attr}, $self->{tags}, $self->{members} );
+        }
+        elsif( $self->{attr}{id} < 0 )
+        {
+          $self->_process( "create", $self->{entity}, $self->{attr}, $self->{tags}, $self->{members} );
+        }
+      }
+      $self->{count}++;
+      if( $self->{progress} and ($self->{count}%11) == 1)
+      {
+        $self->{progress}->($self->{count}, $Expat->current_byte()/$self->{input_length} );
+      }
+      $self->{state} = STATE_EXPECT_ENTITY;
+    }
+    return;
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    return if $Name eq "bound";
+    if( $self->{filetype} == FILETYPE_OSMCHANGE )
+    {
+      if( $Name eq $self->{command} )
+      {
+        $self->{state} = STATE_EXPECT_COMMAND;
+      }else {die}
+    }
+    else  # FILETYPE_OSM
+    {
+      if( $Name eq "osm" )
+      {
+        $self->{state} = STATE_INIT;
+      } else {die}
+    }
+    return;
+  }
+  elsif( $self->{state} == STATE_EXPECT_COMMAND )
+  {
+    if( $Name eq "osmChange" )
+    {
+      $self->{state} = STATE_INIT;
+    }else {die}
+    return;
+  }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+OsmChangeReaderV6 - Module for reading OpenStreetMap V6 Change XML data files
+
+=head1 SYNOPSIS
+
+  my $OSM = new Geo::OSM::OsmChangeReader(\&process);
+  $OSM->load("Data/changes.osc");
+  
+  sub process
+  {
+    my($OSM, $command, $entity) = @_;
+    print "Doing a $command on a $entity ".$entity->id."\n";
+    my $tags = $entity->tags;
+    while( my($k,$v) = splice @{$tags}, 0, 2 )
+    { print "  $k: $v\n" }
+    if( $entity->type eq "way" )
+    { print "  Nodes: ", join(", ",@{$entity->nodes}),"\n"; }
+  }
+
+=head1 AUTHOR
+
+Martijn van Oosterhout <kleptog@svana.org>
+based on OsmXML.pm written by:
+Oliver White (oliver.white@blibbleblobble.co.uk)
+
+=head1 COPYRIGHT
+
+Copyright 2007, Martijn van Oosterhout
+Copyright 2006, Oliver White
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/OsmReaderV3.pm b/navit/navit/script/osm/Geo/OSM/OsmReaderV3.pm
new file mode 100644 (file)
index 0000000..b019634
--- /dev/null
@@ -0,0 +1,246 @@
+##################################################################
+## OsmReader.pm - Library for reading OSM  files                ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Package that reads both OSM file format files.               ##
+## The user creates the parse with a callback and the           ##
+## loader will call the callback for each detected object.      ##
+## Licence: LGPL                                                ##
+##################################################################
+package Geo::OSM::OsmReader;
+
+use strict;
+use warnings;
+
+use Utils::File;
+use Utils::Math;
+use Utils::Debug;
+use XML::Parser;
+use Carp;
+
+use Geo::OSM::EntitiesV3;
+
+use constant STATE_INIT => 1;
+use constant STATE_EXPECT_ENTITY => 3;
+use constant STATE_EXPECT_BODY => 4;
+
+# With this initialiser, your process will get called with instantiated objects
+sub init
+{ 
+  my $obj = bless{}, shift;
+  my $proc = shift;
+  my $prog = shift;
+  if( ref $proc ne "CODE" )
+  { die "init Geo::OSM::OsmReader requires a sub as argument\n" }
+  $obj->{newproc}  = $proc;
+  if( defined $prog )
+  { $obj->{progress} = $prog }
+  return $obj;
+}
+
+sub _process
+{
+  my($self, $entity, $attr, $tags, $members) = @_;
+  
+  my $ent;
+  if( $entity eq "node" )
+  {
+    $ent = new Geo::OSM::Node( $attr, $tags );
+  }
+  if( $entity eq "segment" )
+  {
+    $ent = new Geo::OSM::Segment( $attr, $tags );
+  }
+  if( $entity eq "way" )
+  {
+    $ent = new Geo::OSM::Way( $attr, $tags, $members );
+  }
+  croak "Unknown entity '$entity'" if not defined $ent;
+
+  return $self->{newproc}->( $ent );
+}
+
+sub load($)
+{
+  my ($self, $file_name) = @_;
+
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    my $fh = data_open($file_name);
+    die "Cannot open OSM File $file_name\n" unless $fh;
+    $self->{input_length} = -s $fh;
+    $self->{count}=0;
+    eval {
+       $P->parse($fh);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed $file_name in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n $file_name\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+}
+
+sub parse($)
+{
+  my ($self, $string) = @_;
+
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    $self->{input_length} = length($string);
+    $self->{count}=0;
+    eval {
+       $P->parse($string);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed string in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n [$string]\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+}
+
+# Function is called whenever an XML tag is started
+sub DoStart
+{
+#print @_,"\n";   
+  my ($self, $Expat, $Name, %Attr) = @_;
+
+  if( $self->{state} == STATE_INIT )
+  {
+    if($Name eq "osm"){
+      $self->{state} = STATE_EXPECT_ENTITY;
+      
+      if( $Attr{version} ne "0.3" and $Attr{version} ne "0.4")
+      { die "OsmReaderV5 can only read 0.3 or 0.4 files, found '$Attr{version}'\n" }
+    } else {
+      die "Expected 'osm' tag, got '$Name'\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    # Pick up the origin attribute from the bound tag
+    if( $Name eq "bound" )
+    {
+      if( exists $Attr{origin} )
+      {
+        $self->{origin} = $Attr{origin};
+      }
+      return;
+    }
+    if($Name eq "node" or $Name eq "segment" or $Name eq "way"){
+      $self->{entity} = $Name;
+      $self->{attr} = {%Attr};
+      $self->{tags} = [];
+      $self->{members} = ($Name ne "node") ? [] : undef;
+      $self->{state} = STATE_EXPECT_BODY;
+    } else {
+      die "Expected entity\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if($Name eq "tag"){
+      push @{$self->{tags}}, $Attr{"k"}, $Attr{"v"};
+    }
+    if($Name eq "seg"){
+      push @{$self->{members}}, $Attr{"id"};
+    }
+  }
+}
+
+# Function is called whenever an XML tag is ended
+sub DoEnd
+{
+  my ($self, $Expat, $Name) = @_;
+  if( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if( $Name eq $self->{entity} )
+    {
+      $self->_process( $self->{entity}, $self->{attr}, $self->{tags}, $self->{members} );
+      $self->{count}++;
+      if( $self->{progress} and ($self->{count}%11) == 1)
+      {
+        $self->{progress}->($self->{count}, $self->{input_length} ? $Expat->current_byte()/$self->{input_length} : $Expat->current_byte());
+      }
+      $self->{state} = STATE_EXPECT_ENTITY;
+    }
+    return;
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    return if $Name eq "bound";
+    if( $Name eq "osm" )
+    {
+      $self->{state} = STATE_INIT;
+    } else {die}
+    return;
+  }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+OsmReaderV3 - Module for reading OpenStreetMap V3 XML data files
+
+=head1 SYNOPSIS
+
+  my $OSM = new Geo::OSM::OsmReader(\&process);
+  $OSM->load("Data/changes.osc");
+  
+  sub process
+  {
+    my($OSM, $entity) = @_;
+    print "Read $entity ".$entity->id."\n";
+    my $tags = $entity->tags;
+    while( my($k,$v) = splice @{$tags}, 0, 2 )
+    { print "  $k: $v\n" }
+    if( $entity->type eq "way" )
+    { print "  Segs: ", join(", ",@{$entity->segs}),"\n"; }
+  }
+
+=head1 AUTHOR
+
+Martijn van Oosterhout <kleptog@svana.org>
+based on OsmXML.pm written by:
+Oliver White (oliver.white@blibbleblobble.co.uk)
+
+=head1 COPYRIGHT
+
+Copyright 2007, Martijn van Oosterhout
+Copyright 2006, Oliver White
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/OsmReaderV5.pm b/navit/navit/script/osm/Geo/OSM/OsmReaderV5.pm
new file mode 100644 (file)
index 0000000..37a477b
--- /dev/null
@@ -0,0 +1,249 @@
+##################################################################
+## OsmReader.pm - Library for reading OSM  files                ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Package that reads both OSM file format files.               ##
+## The user creates the parse with a callback and the           ##
+## loader will call the callback for each detected object.      ##
+## Licence: LGPL                                                ##
+##################################################################
+package Geo::OSM::OsmReader;
+
+use strict;
+use warnings;
+
+use Utils::File;
+use Utils::Math;
+use Utils::Debug;
+use XML::Parser;
+use Carp;
+
+use Geo::OSM::EntitiesV5;
+
+use constant STATE_INIT => 1;
+use constant STATE_EXPECT_ENTITY => 3;
+use constant STATE_EXPECT_BODY => 4;
+
+# With this initialiser, your process will get called with instantiated objects
+sub init
+{ 
+  my $obj = bless{}, shift;
+  my $proc = shift;
+  my $prog = shift;
+  if( ref $proc ne "CODE" )
+  { die "init Geo::OSM::OsmReader requires a sub as argument\n" }
+  $obj->{newproc}  = $proc;
+  if( defined $prog )
+  { $obj->{progress} = $prog }
+  return $obj;
+}
+
+sub _process
+{
+  my($self, $entity, $attr, $tags, $members) = @_;
+  
+  my $ent;
+  if( $entity eq "node" )
+  {
+    $ent = new Geo::OSM::Node( $attr, $tags );
+  }
+  if( $entity eq "relation" )
+  {
+    $ent = new Geo::OSM::Relation( $attr, $tags, $members );
+  }
+  if( $entity eq "way" )
+  {
+    $ent = new Geo::OSM::Way( $attr, $tags, $members );
+  }
+  croak "Unknown entity '$entity'" if not defined $ent;
+
+  return $self->{newproc}->( $ent );
+}
+
+sub load($)
+{
+  my ($self, $file_name) = @_;
+
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    my $fh = data_open($file_name);
+    die "Cannot open OSM File $file_name\n" unless $fh;
+    $self->{input_length} = -s $fh;
+    $self->{count}=0;
+    eval {
+       $P->parse($fh);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed $file_name in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n $file_name\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+}
+
+sub parse($)
+{
+  my ($self, $string) = @_;
+
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    $self->{input_length} = length($string);
+    $self->{count}=0;
+    eval {
+       $P->parse($string);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed string in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n [$string]\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+}
+
+# Function is called whenever an XML tag is started
+sub DoStart
+{
+#print @_,"\n";   
+  my ($self, $Expat, $Name, %Attr) = @_;
+
+  if( $self->{state} == STATE_INIT )
+  {
+    if($Name eq "osm"){
+      $self->{state} = STATE_EXPECT_ENTITY;
+      
+      if( $Attr{version} ne "0.5" )
+      { die "OsmReaderV5 can only read 0.5 files, found '$Attr{version}'\n" }
+    } else {
+      die "Expected 'osm' tag, got '$Name'\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    # Pick up the origin attribute from the bound tag
+    if( $Name eq "bound" )
+    {
+      if( exists $Attr{origin} )
+      {
+        $self->{origin} = $Attr{origin};
+      }
+      return;
+    }
+    if($Name eq "node" or $Name eq "relation" or $Name eq "way"){
+      $self->{entity} = $Name;
+      $self->{attr} = {%Attr};
+      $self->{tags} = [];
+      $self->{members} = ($Name ne "node") ? [] : undef;
+      $self->{state} = STATE_EXPECT_BODY;
+    } else {
+      die "Expected entity\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if($Name eq "tag"){
+      push @{$self->{tags}}, $Attr{"k"}, $Attr{"v"};
+    }
+    if($Name eq "nd"){
+      push @{$self->{members}}, $Attr{"ref"};
+    }
+    if($Name eq "member"){
+      push @{$self->{members}}, new Geo::OSM::Relation::Member( \%Attr );
+    }
+  }
+}
+
+# Function is called whenever an XML tag is ended
+sub DoEnd
+{
+  my ($self, $Expat, $Name) = @_;
+  if( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if( $Name eq $self->{entity} )
+    {
+      $self->_process( $self->{entity}, $self->{attr}, $self->{tags}, $self->{members} );
+      $self->{count}++;
+      if( $self->{progress} and ($self->{count}%11) == 1)
+      {
+        $self->{progress}->($self->{count}, $Expat->current_byte()/$self->{input_length} );
+      }
+      $self->{state} = STATE_EXPECT_ENTITY;
+    }
+    return;
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    return if $Name eq "bound";
+    if( $Name eq "osm" )
+    {
+      $self->{state} = STATE_INIT;
+    } else {die}
+    return;
+  }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+OsmReaderV5 - Module for reading OpenStreetMap V5 XML data files
+
+=head1 SYNOPSIS
+
+  my $OSM = new Geo::OSM::OsmReader(\&process);
+  $OSM->load("Data/changes.osc");
+  
+  sub process
+  {
+    my($OSM, $entity) = @_;
+    print "Read $entity ".$entity->id."\n";
+    my $tags = $entity->tags;
+    while( my($k,$v) = splice @{$tags}, 0, 2 )
+    { print "  $k: $v\n" }
+    if( $entity->type eq "way" )
+    { print "  Nodes: ", join(", ",@{$entity->nodes}),"\n"; }
+  }
+
+=head1 AUTHOR
+
+Martijn van Oosterhout <kleptog@svana.org>
+based on OsmXML.pm written by:
+Oliver White (oliver.white@blibbleblobble.co.uk)
+
+=head1 COPYRIGHT
+
+Copyright 2007, Martijn van Oosterhout
+Copyright 2006, Oliver White
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/OsmReaderV6.pm b/navit/navit/script/osm/Geo/OSM/OsmReaderV6.pm
new file mode 100644 (file)
index 0000000..4f6a683
--- /dev/null
@@ -0,0 +1,249 @@
+##################################################################
+## OsmReader.pm - Library for reading OSM  files                ##
+## By Martijn van Oosterhout <kleptog@svana.org>                ##
+##                                                              ##
+## Package that reads both OSM file format files.               ##
+## The user creates the parse with a callback and the           ##
+## loader will call the callback for each detected object.      ##
+## Licence: LGPL                                                ##
+##################################################################
+package Geo::OSM::OsmReader;
+
+use strict;
+use warnings;
+
+use Utils::File;
+use Utils::Math;
+use Utils::Debug;
+use XML::Parser;
+use Carp;
+
+use Geo::OSM::EntitiesV6;
+
+use constant STATE_INIT => 1;
+use constant STATE_EXPECT_ENTITY => 3;
+use constant STATE_EXPECT_BODY => 4;
+
+# With this initialiser, your process will get called with instantiated objects
+sub init
+{ 
+  my $obj = bless{}, shift;
+  my $proc = shift;
+  my $prog = shift;
+  if( ref $proc ne "CODE" )
+  { die "init Geo::OSM::OsmReader requires a sub as argument\n" }
+  $obj->{newproc}  = $proc;
+  if( defined $prog )
+  { $obj->{progress} = $prog }
+  return $obj;
+}
+
+sub _process
+{
+  my($self, $entity, $attr, $tags, $members) = @_;
+  
+  my $ent;
+  if( $entity eq "node" )
+  {
+    $ent = new Geo::OSM::Node( $attr, $tags );
+  }
+  if( $entity eq "relation" )
+  {
+    $ent = new Geo::OSM::Relation( $attr, $tags, $members );
+  }
+  if( $entity eq "way" )
+  {
+    $ent = new Geo::OSM::Way( $attr, $tags, $members );
+  }
+  croak "Unknown entity '$entity'" if not defined $ent;
+
+  return $self->{newproc}->( $ent );
+}
+
+sub load($)
+{
+  my ($self, $file_name) = @_;
+
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    my $fh = data_open($file_name);
+    die "Cannot open OSM File $file_name\n" unless $fh;
+    $self->{input_length} = -s $fh;
+    $self->{count}=0;
+    eval {
+       $P->parse($fh);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed $file_name in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n $file_name\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+}
+
+sub parse($)
+{
+  my ($self, $string) = @_;
+
+  $self->{state} = STATE_INIT;
+  
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => sub{ DoStart( $self, @_ )}, End => sub { DoEnd( $self, @_ )}});
+    $self->{input_length} = length($string);
+    $self->{count}=0;
+    eval {
+       $P->parse($string);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed string in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n [$string]\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+}
+
+# Function is called whenever an XML tag is started
+sub DoStart
+{
+#print @_,"\n";   
+  my ($self, $Expat, $Name, %Attr) = @_;
+
+  if( $self->{state} == STATE_INIT )
+  {
+    if($Name eq "osm"){
+      $self->{state} = STATE_EXPECT_ENTITY;
+      
+      if( $Attr{version} ne "0.6" )
+      { die "OsmReaderV6 can only read 0.6 files, found '$Attr{version}'\n" }
+    } else {
+      die "Expected 'osm' tag, got '$Name'\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    # Pick up the origin attribute from the bound tag
+    if( $Name eq "bounds" )
+    {
+      if( exists $Attr{origin} )
+      {
+        $self->{origin} = $Attr{origin};
+      }
+      return;
+    }
+    if($Name eq "node" or $Name eq "relation" or $Name eq "way"){
+      $self->{entity} = $Name;
+      $self->{attr} = {%Attr};
+      $self->{tags} = [];
+      $self->{members} = ($Name ne "node") ? [] : undef;
+      $self->{state} = STATE_EXPECT_BODY;
+    } else {
+      die "Expected entity\n";
+    }
+  }
+  elsif( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if($Name eq "tag"){
+      push @{$self->{tags}}, $Attr{"k"}, $Attr{"v"};
+    }
+    if($Name eq "nd"){
+      push @{$self->{members}}, $Attr{"ref"};
+    }
+    if($Name eq "member"){
+      push @{$self->{members}}, new Geo::OSM::Relation::Member( \%Attr );
+    }
+  }
+}
+
+# Function is called whenever an XML tag is ended
+sub DoEnd
+{
+  my ($self, $Expat, $Name) = @_;
+  if( $self->{state} == STATE_EXPECT_BODY )
+  {
+    if( $Name eq $self->{entity} )
+    {
+      $self->_process( $self->{entity}, $self->{attr}, $self->{tags}, $self->{members} );
+      $self->{count}++;
+      if( $self->{progress} and ($self->{count}%11) == 1)
+      {
+        $self->{progress}->($self->{count}, $Expat->current_byte()/$self->{input_length} );
+      }
+      $self->{state} = STATE_EXPECT_ENTITY;
+    }
+    return;
+  }
+  elsif( $self->{state} == STATE_EXPECT_ENTITY )
+  {
+    return if $Name eq "bounds";
+    if( $Name eq "osm" )
+    {
+      $self->{state} = STATE_INIT;
+    } else {die}
+    return;
+  }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+OsmReaderV6 - Module for reading OpenStreetMap V6 XML data files
+
+=head1 SYNOPSIS
+
+  my $OSM = new Geo::OSM::OsmReader(\&process);
+  $OSM->load("Data/changes.osc");
+  
+  sub process
+  {
+    my($OSM, $entity) = @_;
+    print "Read $entity ".$entity->id."\n";
+    my $tags = $entity->tags;
+    while( my($k,$v) = splice @{$tags}, 0, 2 )
+    { print "  $k: $v\n" }
+    if( $entity->type eq "way" )
+    { print "  Nodes: ", join(", ",@{$entity->nodes}),"\n"; }
+  }
+
+=head1 AUTHOR
+
+Martijn van Oosterhout <kleptog@svana.org>
+based on OsmXML.pm written by:
+Oliver White (oliver.white@blibbleblobble.co.uk)
+
+=head1 COPYRIGHT
+
+Copyright 2007, Martijn van Oosterhout
+Copyright 2006, Oliver White
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/OsmXML.pm b/navit/navit/script/osm/Geo/OSM/OsmXML.pm
new file mode 100644 (file)
index 0000000..60f458d
--- /dev/null
@@ -0,0 +1,161 @@
+package Geo::OSM::OsmXML;
+
+use strict;
+use warnings;
+
+use Utils::File;
+use Utils::Math;
+use Utils::Debug;
+use XML::Parser;
+
+sub new(){ bless{} }
+
+sub load(){
+  my ($self, $file_name) = @_;
+
+  my $start_time = time();
+  my $P = new XML::Parser(Handlers => {Start => \&DoStart, End => \&DoEnd, Char => \&DoChar});
+    my $fh = data_open($file_name);
+    die "Cannot open OSM File $file_name\n" unless $fh;
+    eval {
+       $P->parse($fh);
+    };
+    print "\n" if $DEBUG || $VERBOSE;
+    if ( $VERBOSE) {
+       printf "Read and parsed $file_name in %.0f sec\n",time()-$start_time;
+    }
+    if ( $@ ) {
+       warn "$@Error while parsing\n $file_name\n";
+       return;
+    }
+    if (not $P) {
+       warn "WARNING: Could not parse osm data\n";
+       return;
+    }
+
+}
+sub name(){return($OsmXML::lastName)};
+
+sub bounds(){
+  my ($S,$W,$N,$E) = (1e+6,1e+6,-1e+6,-1e+6); # S,W,N,E
+  foreach my $Node(values %OsmXML::Nodes){
+    $S = $Node->{"lat"} if($Node->{"lat"} < $S);
+    $N = $Node->{"lat"} if($Node->{"lat"} > $N);
+    $W = $Node->{"lon"} if($Node->{"lon"} < $W);
+    $E = $Node->{"lon"} if($Node->{"lon"} > $E);
+  }
+  return($S,$W,$N,$E);
+}
+
+# Function is called whenever an XML tag is started
+sub DoStart()
+{
+  my ($Expat, $Name, %Attr) = @_;
+  
+  if($Name eq "node"){
+    undef %OsmXML::Tags;
+    %OsmXML::MainAttr = %Attr;
+  }
+  if($Name eq "segment"){
+    undef %OsmXML::Tags;
+    %OsmXML::MainAttr = %Attr;
+  }
+  if($Name eq "way"){
+    undef %OsmXML::Tags;
+    undef @OsmXML::WaySegments;
+    %OsmXML::MainAttr = %Attr;
+  }
+  if($Name eq "tag"){
+    $OsmXML::Tags{$Attr{"k"}} = $Attr{"v"};
+  }
+  if($Name eq "seg"){
+    push(@OsmXML::WaySegments, $Attr{"id"});
+  }
+}
+
+# Function is called whenever an XML tag is ended
+sub DoEnd(){
+  my ($Expat, $Element) = @_;
+  if($Element eq "node"){
+    my $ID = $OsmXML::MainAttr{"id"};
+    $OsmXML::Nodes{$ID}{"lat"} = $OsmXML::MainAttr{"lat"};
+    $OsmXML::Nodes{$ID}{"lon"} = $OsmXML::MainAttr{"lon"};
+    foreach(keys(%OsmXML::Tags)){
+      $OsmXML::Nodes{$ID}{$_} = $OsmXML::Tags{$_};
+    }
+  }
+  if($Element eq "segment"){
+    my $ID = $OsmXML::MainAttr{"id"};
+    $OsmXML::Segments{$ID}{"from"} = $OsmXML::MainAttr{"from"};
+    $OsmXML::Segments{$ID}{"to"} = $OsmXML::MainAttr{"to"};
+    foreach(keys(%OsmXML::Tags)){
+      $OsmXML::Segments{$ID}{$_} = $OsmXML::Tags{$_};
+    }
+  }
+  if($Element eq "way"){
+    my $ID = $OsmXML::MainAttr{"id"};
+    $OsmXML::Ways{$ID}{"segments"} = join(",",@OsmXML::WaySegments);
+    foreach(keys(%OsmXML::Tags)){
+      $OsmXML::Ways{$ID}{$_} = $OsmXML::Tags{$_};
+    }    
+  }
+}
+
+# Function is called whenever text is encountered in the XML file
+sub DoChar(){
+  my ($Expat, $String) = @_;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+OsmXML - Module for reading OpenStreetMap XML data files
+
+=head1 SYNOPSIS
+
+  $OSM = new OsmXML();
+  $OSM->load("Data/nottingham.osm");
+  
+  foreach $Way(%OsmXML::Ways){
+    @Segments = split(/,/, $Way->{"segments"});
+    
+    foreach $SegmentID(@Segments){
+      $Segment = $OsmXML::Segments{$SegmentID};
+    
+      $Node1 = $OsmXML::Nodes{$Segment->{"from"}};
+      $Node2 = $OsmXML::Nodes{$Segment->{"to"}};
+    
+      printf "Node at %f,%f, named %s, is a %s",
+        $Node2->{"lat"},
+        $Node2->{"lon"},
+        $Node2->{"name"},
+        $Node2->{"highway"};
+      }
+    }
+
+=head1 AUTHOR
+
+Oliver White (oliver.white@blibbleblobble.co.uk)
+
+=head1 COPYRIGHT
+
+Copyright 2006, Oliver White
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/Planet.pm b/navit/navit/script/osm/Geo/OSM/Planet.pm
new file mode 100644 (file)
index 0000000..bedfdff
--- /dev/null
@@ -0,0 +1,336 @@
+##################################################################
+package Geo::OSM::Planet;
+##################################################################
+
+use Exporter;
+@ISA = qw( Exporter );
+use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
+@EXPORT = qw( mirror_planet
+             osm_dir
+             planet_dir
+             UTF8sanitize
+             estimated_max_id
+             estimated_max_count
+             );
+
+use strict;
+use warnings;
+
+use HTTP::Request;
+use File::Basename;
+use File::Copy;
+use File::Path;
+use File::Slurp;
+use Getopt::Long;
+use HTTP::Request;
+use Storable ();
+use Data::Dumper;
+
+use Utils::File;
+use Utils::Debug;
+use Utils::LWP::Utils;
+
+
+# As of planet-061220
+my $estimations = {
+            'way' => {
+                       'count' => 3918624,
+                       'max_id' => 8013668
+                     },
+            'elem' => {
+                        'count' => 312752630,
+                        'max_id' => 312752630
+                      },
+            'seg' => {
+                       'count' => 40992844,
+                       'max_id' => 57261050
+                     },
+            'segment' => {
+                           'count' => 41325764,
+                           'max_id' => 57259818
+                         },
+            'tag' => {
+                       'count' => 186445779,
+                       'max_id' => 1
+                     },
+            'node' => {
+                        'count' => 40069619,
+                        'max_id' => 59943310
+                      },
+            'line' => {
+                        'count' => 388379350,
+                        'max_id' => 312752630
+                      }
+};
+
+# ------------------------------------------------------------------
+# This routine estimates the maximum id for way,elem,seg,... 
+# The values are taken from older planet.osm Files
+# So they mostly are a little bit to low
+# ARGS: 
+#   $type: line|way|tag|...
+# RETURNS:
+#   $result: number of estimated max_id
+sub estimated_max_id($){
+    my $type= shift;
+    unless ( defined ($estimations->{$type}->{max_id})) {
+       warn("\n estimated_max_id($type): unknown Tag-Type\n");
+       return 0;
+    };
+    return $estimations->{$type}->{max_id};
+}
+
+# ------------------------------------------------------------------
+# This routine estimates the maximim number of elements for way,elem,seg,... 
+# The values are taken from older planet.osm Files
+# So they mostly are a little bit to low
+# ARGS: 
+#   $type: line|way|tag|...
+# RETURNS:
+#   $result: number of estimated elements
+sub estimated_max_count($){
+    my $type= shift;
+    unless ( defined ($estimations->{$type}->{count})) {
+       warn("\n estimated_max_id($type): unknown Tag-Type\n");
+       return 0;
+    };
+    return $estimations->{$type}->{count};
+}
+
+# ------------------------------------------------------------------
+# returns the osm main directory for holding data
+sub osm_dir() {
+    # For later these are the defaults
+    # on where we can read/write
+    #  ~/osm
+    # /var/data/osm
+    my $dir;
+
+    my $home = $ENV{HOME};
+    unless ( $home ) {
+       $home = `whoami`;
+       chomp $home;
+       $home = "/home/$home";
+    }
+    
+    $dir = "$home/osm";
+    return $dir;
+}
+
+# ------------------------------------------------------------------
+# Returns (or sets) the directory where the planet.osm files will be found
+my $PLANET_DIR='';
+sub planet_dir(;$) {
+    my $new_dir=shift;
+
+    if ( $new_dir ) {
+       $PLANET_DIR  = $new_dir;
+    } elsif( ! $PLANET_DIR) {
+       my $dir = osm_dir();
+       $PLANET_DIR  = "$dir/planet";
+    }
+    return $PLANET_DIR;
+}
+
+
+sub sort_unique(@){
+    my @srt = sort @_;
+    my @erg;
+    my $last_val=undef;
+    for my $val ( @srt ){
+       next if $last_val &&  $val eq $last_val;
+       $last_val=$val;
+       push (@erg,$val);
+    }
+    return @erg
+}
+    
+
+# ------------------------------------------------------------------
+# mirror the newest planet.osm File to
+#  ~/osm/planet/planet.osm.bz2
+# and the resulting 
+# Filename is returned
+#
+# the file is -----NO LONGER--- Sanitized afterwards  
+sub mirror_planet(){
+    my $planet_server="http://planet.openstreetmap.org";
+    my $url = "$planet_server";
+
+    my $mirror_dir=planet_dir();
+    mkdir_if_needed( $mirror_dir );
+    
+    my $current_file;
+    if ( !$Utils::LWP::Utils::NO_MIRROR ) {
+       # Get Index.html of Planet.osm.org
+       my $apache_sort_hy_date="?C=M;O=D";
+       my $index_file="$mirror_dir/planet_index.html";
+       my $result = mirror_file("$url/$apache_sort_hy_date",$index_file);
+       if ( $result ) {
+           my $index_content = read_file( $index_file ) ;
+
+           # Get the current planet.osm File
+           my @all_files = ($index_content =~ m/(planet-\d\d\d\d\d\d.osm.bz2|planet-\d\d\d\d\d\d.osm.gz)/g);
+           my ( $current_file1,$current_file2 )
+               = grep { $_ !~ m/planet-061008/ } reverse sort_unique(@all_files);
+           print STDERR " TOP Files:   ( $current_file1,$current_file2 ) \n" if $DEBUG;
+           $current_file = $current_file1;
+           $current_file1 =~ s/\.bz2$/\.gz/;
+           if (  $current_file1 eq $current_file2 ) {
+               $current_file =  $current_file1
+           };
+           if ( $current_file ) {
+               $url .= "/$current_file";
+               $current_file = "$mirror_dir/$current_file";
+               print STDERR "Mirror OSM Data from $url\n" if $VERBOSE || $DEBUG;
+               $result = mirror_file($url,$current_file);
+               #return undef unless $result;
+           }
+       }
+    }
+
+    my @files= reverse sort_unique( grep { $_ !~ m/planet-061008/ } glob("$mirror_dir/planet-*.osm.{bz2,gz}"));
+    if ( $DEBUG) {
+       print STDERR "Existing Files: \n\t".join("\n\t",@files)."\n";
+    }
+    $current_file = $files[0];
+    
+    if ( $DEBUG) {
+       print STDERR "Choosen File: $current_file\n";
+    }
+    
+    return undef unless $current_file;
+
+#    $current_file = UTF8sanitize($current_file);
+#    if ( $DEBUG >2 || $VERBOSE>3) {
+#      print STDERR "Sanitized File: $current_file\n";
+#    }
+
+    my ($unpacked_file) = ($current_file=~ m/(.*\.osm)/);
+    $current_file = $unpacked_file
+       unless file_needs_re_generation($current_file,$unpacked_file);
+
+    print STDERR "Mirror done, using '$current_file'\n" if $VERBOSE>1 || $DEBUG>1;
+    return $current_file;
+}
+
+# ------------------------------------------------------------------
+# creates a second file with a sanitized Version of planet.osm
+# the resulting file can be found at
+#    ~/osm/planet/planet-07XXXX-a.osm.bz2
+# If a recent enought Version is found in ~/osm/planet/
+# nothing is done, but the filename of the file is returned
+# if the routine finds an uncompressed up to date Version 
+#   ~/osm/planet/planet-07XXXX-a.osm
+# this Filename is returned.
+sub UTF8sanitize($){
+    my $filename = shift;
+    if ( $DEBUG) {
+       print STDERR "UTF8sanitize($filename)\n";
+    }
+    my $start_time=time();
+
+    # the newer Files do not need to be sanitized
+    my ($file_date) = ($filename =~ m/planet-(\d+)/ );
+    return $filename
+       if ($file_date >= 061205) && ( $file_date < 061213);
+
+    my $filename_new= $filename;
+    $filename_new =~ s/\.osm/-a.osm/;
+    my $filename_new_check=newest_unpacked_filename($filename_new);
+
+    # check if planet-070101-a.osm[.bz2] is newer than  planet-070101.osm.bz2
+    return $filename_new_check
+       unless file_needs_re_generation($filename,$filename_new_check);
+
+    # We have to create a new one
+    print STDERR "UTF8 Sanitize $filename ... \n";
+    # Uggly Hack, but for now it works
+    my $UTF8sanitizer=`which UTF8sanitizer`;
+    chomp $UTF8sanitizer;
+    unless ( -x $UTF8sanitizer ) {
+       $UTF8sanitizer=find_file_in_perl_path('../planet.osm/C/UTF8sanitizer');
+    }
+    die "Sanitizer not found\n" unless -x $UTF8sanitizer;
+    print STDERR "Sanitizer found at '$UTF8sanitizer'\n" if $DEBUG;
+
+    print STDERR "     this may take some time ... \n";
+    my $cmd = "gzip -dc $filename | $UTF8sanitizer  | bzip2 >$filename_new.part";
+    print "Command: $cmd" if $DEBUG || $VERBOSE;
+    my $result = `$cmd`;
+    print $result if $DEBUG || $VERBOSE;
+  
+    print "Sanitized $filename " if $DEBUG || $VERBOSE;
+       print_time($start_time);
+
+    my $file_size     = -s "$filename";
+    my $file_size_new = -s "$filename_new.part";
+    if ( $file_size_new < ($file_size*0.9) ) {
+       die "File Sanitize seems not successfull.\n".
+           "Original Size $file_size\n".
+           "Sanitized Size $file_size_new\n";
+    }
+    rename "$filename_new.part","$filename_new";
+    if ( ! -s $filename_new ) {
+       die "Cannot sanitize $filename\n";
+    }
+    print "now we have a sanitized $filename_new\n" if $DEBUG || $VERBOSE;
+    return $filename_new;
+}
+
+# ------------------------------------------------------------------
+# find a file in the current Perl Search path. For now this was the 
+# easiest solution to find programms like UTF8Sanitize
+# ARGS: relative filename (relative to @INC-path
+# RETURNS: Absolute path to file
+sub find_file_in_perl_path($){
+    my $file = shift;
+
+    my $found_file = '';
+    for my $path ( @INC ) {
+       my $filename = "$path/$file";
+       print "find_file_in_perl_path: looking in '$filename'\n" if $DEBUG>2;
+       if ( -s $filename){
+           $found_file = $filename;
+           last;
+       };
+    }
+    
+    print "find_file_in_perl_path($file): --> $found_file\n" if $DEBUG;
+    return $found_file;
+}
+
+# ------------------------------------------------------------------
+1;
+
+=head1 NAME
+
+Geo::OSM::Planet
+
+=head1 COPYRIGHT
+
+Copyright 2006, Jörg Ostertag
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=head1 AUTHOR
+
+Jörg Ostertag (planet-count-for-openstreetmap@ostertag.name)
+
+=head1 SEE ALSO
+
+http://www.openstreetmap.org/
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/SegmentList.pm b/navit/navit/script/osm/Geo/OSM/SegmentList.pm
new file mode 100644 (file)
index 0000000..0de35eb
--- /dev/null
@@ -0,0 +1,520 @@
+##################################################################
+package Geo::OSM::SegmentList;
+##################################################################
+
+use Exporter;
+@ISA = qw( Exporter );
+use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
+@EXPORT = qw( LoadOSM_segment_csv
+             reduce_segments_list
+             read_osm_file
+             load_segment_list
+             );
+
+use strict;
+use warnings;
+
+use Math::Trig;
+
+use Data::Dumper;
+use Geo::Geometry;
+use Geo::OSM::Planet;
+use Utils::Debug;
+use Utils::File;
+use Utils::Math;
+use Geo::Filter::Area;
+
+sub load_segment_list($;$){
+    my $do_filter_against_osm = shift;
+    my $bounds  = shift;
+
+    my $osm_segments;
+    print STDERR "load_segment_list from: '$do_filter_against_osm'\n";
+    if (  $do_filter_against_osm =~ m/^postgis|mapnik$/ ) {
+       $osm_segments = Geo::OSM::SegmentList::LoadOSM_segment_postgis($bounds);
+    } elsif ( -s $do_filter_against_osm ) {
+       if (  $do_filter_against_osm =~ m/\.csv/ ) {
+           $osm_segments = Geo::OSM::SegmentList::LoadOSM_segment_csv($do_filter_against_osm, $bounds);
+       } elsif ( $do_filter_against_osm =~ m/\.osm/ ) {
+           $osm_segments = Geo::OSM::SegmentList::read_osm_file($do_filter_against_osm, $bounds);
+       } else {
+           die "Unknown Datatype for $do_filter_against_osm\n";
+       }
+       #print Dumper(\$osm_segments ) if $DEBUG;
+    } elsif (  $do_filter_against_osm !~ m/^\d*$/ ) {
+           die "Unknown Datatype for $do_filter_against_osm\n";
+    } else {
+
+       # later we search in:
+       #  ~/.osm/data/planet.osm.csv
+       # /var/data/osm/planet.osm.csv
+
+       my $home = $ENV{HOME}|| '~/';
+       my $path=       planet_dir();
+       my $osm_filename = "${path}/csv/osm-segments.csv";
+       $osm_filename =~ s,\~/,$home/,;
+       printf STDERR "check $osm_filename for loading\n" if $DEBUG;
+       
+       die "Cannot open $osm_filename\n" unless -s $osm_filename;
+       $osm_segments = Geo::OSM::SegmentList::LoadOSM_segment_csv($osm_filename, $bounds);
+    };
+    return $osm_segments
+}
+
+# ------------------------------------------------------------------
+# reduce osm Segments to only those inside the bounding box
+# This make comparison faster
+sub reduce_segments_list($$) {
+    my $all_osm_segments = shift;
+    my $bounds           = shift;
+
+    my $start_time=time();
+
+    #printf STDERR "reduce_osm_segments(".Dumper(\$bounds).")\n" if $DEBUG;
+
+    my $osm_segments = [];
+    my $count=0;
+    my $all_count=0;
+    for my $segment ( @{$all_osm_segments} ) {
+       $all_count++;
+       next unless $segment->[0] >= $bounds->{lat_min};
+       next unless $segment->[0] <= $bounds->{lat_max};
+       next unless $segment->[1] >= $bounds->{lon_min};
+       next unless $segment->[1] <= $bounds->{lon_max};
+       next unless $segment->[2] >= $bounds->{lat_min};
+       next unless $segment->[2] <= $bounds->{lat_max};
+       next unless $segment->[3] >= $bounds->{lon_min};
+       next unless $segment->[3] <= $bounds->{lon_max};
+       $count++;
+       push(@{$osm_segments},$segment);
+    }
+    if ( $VERBOSE > 3 || $DEBUG > 3 ) {
+       printf STDERR "         Reduced OSM Segment list to $count( $all_count) OSM-Segments ";
+       print_time($start_time);
+    }
+
+    return $osm_segments;
+}
+
+# -------------------------------------------------------
+# Load the csv Version of a segment list
+# Args: $filename, {lat_min=> .., lat_max => ..., lon_min => .., lon_max => .. }
+sub LoadOSM_segment_csv($;$){
+    my $filename = shift;
+    my $bounds  = shift;
+    printf STDERR "Reading OSM-Segment-csv File: $filename, ($bounds->{lat_min} ... $bounds->{lat_max} , $bounds->{lon_min} ... $bounds->{lon_max})\n"
+       if $DEBUG || $VERBOSE;
+    my $start_time=time();
+
+    my $segments;
+    my $check_bounds = 1 if defined $bounds;
+    $main::dont_write_osm_storable=1 ||  $check_bounds;
+    $main::dont_read_osm_storable=1;
+
+    if ( -s "$filename.storable" && 
+        ! file_needs_re_generation($filename,"$filename.storable")
+        && ! $main::dont_read_osm_storable ) {
+       # later we should compare if the file also is newer than the source
+       $filename .= ".storable";
+       printf STDERR "Reading OSM File as storable: $filename\n"
+           if $DEBUG || $VERBOSE;
+       $segments = Storable::retrieve($filename);
+       if ( $VERBOSE >1 || $DEBUG) {
+           printf STDERR "Reading $filename done";
+           print_time($start_time);
+       }
+    } else {
+       my $fh = data_open($filename);
+       my $count=0;
+       my $count_read=0;
+
+       die "Cannot open $filename in LoadOSM_segment_csv.\n".
+           "Please create it first to use the option --osm.\n".
+           "See --help for more info"  unless $fh;
+
+       while ( my $line = $fh ->getline() ) {
+           $count++;
+           chomp $line;
+           my @segment;
+           my $dummy;
+           ($segment[0],$segment[1],$segment[2],$segment[3],$segment[4]) = split(/,/,$line,5);
+           #print STDERR Dumper(\@segment);
+
+           if ( $check_bounds ) {
+               next unless $segment[0] >= $bounds->{lat_min};
+               next unless $segment[0] <= $bounds->{lat_max};
+               next unless $segment[1] >= $bounds->{lon_min};
+               next unless $segment[1] <= $bounds->{lon_max};
+               next unless $segment[2] >= $bounds->{lat_min};
+               next unless $segment[2] <= $bounds->{lat_max};
+               next unless $segment[3] >= $bounds->{lon_min};
+               next unless $segment[3] <= $bounds->{lon_max};
+           }
+
+           push (@{$segments},\@segment);
+           $count_read++;
+       }
+       $fh->close();
+       if ( $VERBOSE >1 || $DEBUG) {
+           printf STDERR "Read and parsed $count_read($count) Lines in $filename";
+           print_time($start_time);
+       }
+       if ( ! $main::dont_write_osm_storable ) {
+           $start_time=time();
+           Storable::store($segments   ,"$filename.storable");
+           if ( $VERBOSE >1 || $DEBUG) {
+               printf STDERR "Wrote Storable in to $filename.storable";
+               print_time($start_time);
+           }
+       };
+    }
+
+
+    return($segments);
+}
+
+
+
+# -------------------------------------------------------
+# Load the segment list from postgis
+# Args: {lat_min=> .., lat_max => ..., lon_min => .., lon_max => .. }
+# This is a test to see if we can handle all segments of the world 
+# with reasonable query times
+sub LoadOSM_segment_postgis($;$){
+    my $bounds  = shift;
+    printf STDERR "Reading OSM-Segments form PostGis($bounds->{lat_min} ... $bounds->{lat_max} , $bounds->{lon_min} ... $bounds->{lon_max})\n"
+       if $DEBUG || $VERBOSE;
+    my $start_time=time();
+
+    my $segments;
+    my $check_bounds = 1 if defined $bounds;
+    my @row;
+    my $sth;
+
+    use DBI;
+    
+    my $dbname="gis";
+    my $dbhost="/var/run/postgresql";
+    my $dbh = DBI->connect("dbi:Pg:dbname=$dbname;host=$dbhost", "", "", {AutoCommit => 0});
+
+    unless( $dbh) {
+       warn "\n\n!!!!!!!!!!!!!!!! WARNING: Cannot Open Database for reading OSM Segments\n\n";
+       return;
+    };
+
+    # WHERE  the_geom && setsrid('BOX(47.36 21.40,51.185 21.53)'::box2d, 42102)
+    # WHERE GeomFromText('POINT(25.3 25.40)', 42102) && the_geom \
+    #       AND distance(the_geom, GeomFromText('POINT(25.7 5.3)', 42102)) = 0
+    # WHERE distance(the_geom, setsrid('BOX(4.36 2.3,1.5 2.8)'::box2d, 42102)) = 0 
+    # WHERE  the_point && setsrid('BOX(295149 2315499, 465992 2163790)'::box2d, -1)
+
+    ##############################
+    # FOR DEBUG
+#    $bounds->{lat_min}=10;
+#    $bounds->{lon_min}=10;
+#    $bounds->{lat_max}=70;
+#    $bounds->{lon_max}=70;
+    #
+    ####################################
+
+    my $SRID_WGS84="4326";
+    my $SRID_UNKNOWN="3395";
+    my $SRID_OSM="900913";
+
+    my $polygon_text=sprintf("POLYGON((%.5f %.5f,%.5f %.5f,%.5f %.5f,%.5f %.5f,%.5f %.5f))",
+                            $bounds->{lat_min}-0.1 , $bounds->{lon_min}-0.1,
+                            $bounds->{lat_max}+0.1 , $bounds->{lon_min}-0.1 , 
+                            $bounds->{lat_max}+0.1 , $bounds->{lon_max}+0.1 , 
+                            $bounds->{lat_min}-0.1 , $bounds->{lon_max}+0.1 , 
+                            $bounds->{lat_min}-0.1 , $bounds->{lon_min}-0.1);
+    my $polygon="transform(GeomFromText('$polygon_text',${SRID_WGS84}),$SRID_OSM)";
+   
+    my $select = "select osm_id,highway,name from planet_osm_line where highway is not null and way && ".
+       " $polygon ".
+       "limit 5;";
+    print "\nprepare: $select\n";
+    $sth = $dbh->prepare($select, { pg_server_prepare => 1 });
+    $sth->execute();
+    while ( @row = $sth->fetchrow_array ) {
+       print "postgis ROW: @row\n";
+    }
+    printf STDERR "---------------------------------------------------------------\n" if $DEBUG || $VERBOSE;
+
+    
+#     $select = "SELECT name ,astext(way),$distance FROM planet_osm_roads WHERE  $distance < 500 order by  $distance;";
+    
+    #SELECT * FROM planet_osm_roads WHERE way in ?",
+    #$select = "SELECT name FROM planet_osm_roads WHERE distance(way,GeomFromText('POINT(856371.58 6683083.41)', 3395)) < 1000;";
+#    my $distance="distance(way,     GeomFromText('POINT(856371.58 6683083.41)', 3395))";
+    my $point_text=sprintf("POINT(%.5f %.5f)",     $bounds->{lat_min} , $bounds->{lon_min});
+    my $point="transform(GeomFromText('$point_text',${SRID_WGS84}),$SRID_OSM)";
+    my $distance="distance(way,$point)";
+
+    $select = "SELECT name FROM planet_osm_line WHERE ${distance} < 1000;";
+    print "\nprepare: $select\n";
+    $sth = $dbh->prepare($select, { pg_server_prepare => 1 });
+    $sth->execute();
+    while ( @row = $sth->fetchrow_array ) {
+       print "postgis ROW: @row\n";
+    }
+    printf STDERR "---------------------------------------------------------------\n" if $DEBUG || $VERBOSE;
+
+    $select = "SELECT name FROM planet_osm_roads WHERE ${distance} < 1000;";
+    print "\nprepare: $select\n";
+    $sth = $dbh->prepare($select, { pg_server_prepare => 1 });
+    $sth->execute();
+    while ( @row = $sth->fetchrow_array ) {
+       print "postgis ROW: @row\n";
+    }
+    printf STDERR "---------------------------------------------------------------\n" if $DEBUG || $VERBOSE;
+
+    $select = "SELECT osm_id,highway,name,asText(transform(way,${SRID_WGS84})) from planet_osm_line where highway is not null  limit 5;";
+    print "\nprepare: $select\n";
+    $sth = $dbh->prepare($select, { pg_server_prepare => 1 });
+    $sth->execute();
+    while ( @row = $sth->fetchrow_array ) {
+       print "postgis ROW: @row\n";
+    }
+
+    printf STDERR "---------------------------------------------------------------\n" if $DEBUG || $VERBOSE;
+        
+    my $count=0;
+    my $count_read=0;
+    while ( @row = $sth->fetchrow_array ) {
+       print "postgis ROW: @row\n";
+
+       $count++;
+       my $line="";
+       chomp $line;
+       my @segment;
+       my $dummy;
+       ($segment[0],$segment[1],$segment[2],$segment[3],$segment[4]) = split(/,/,$line,5);
+       #print STDERR Dumper(\@segment);
+
+       if ( $check_bounds ) {
+           next unless $segment[0] >= $bounds->{lat_min};
+           next unless $segment[0] <= $bounds->{lat_max};
+           next unless $segment[1] >= $bounds->{lon_min};
+           next unless $segment[1] <= $bounds->{lon_max};
+           next unless $segment[2] >= $bounds->{lat_min};
+           next unless $segment[2] <= $bounds->{lat_max};
+           next unless $segment[3] >= $bounds->{lon_min};
+           next unless $segment[3] <= $bounds->{lon_max};
+       }
+
+       push (@{$segments},\@segment);
+       $count_read++;
+    }
+
+    $dbh->disconnect;
+
+
+    return($segments);
+}
+
+
+# ----------------------
+sub Storable_save($$){
+    my $filename = shift;
+    my $segments = shift;
+    eval{
+       Storable::store($segments   ,"$filename.storable");
+       };
+    if ( $@ ) {
+       #warn Dumper(\$segments);
+       die "Storable_save(): $@\n";
+    }
+    printf STDERR "Stored OSM File: $filename as storable\n"
+       if $DEBUG || $VERBOSE;
+}
+
+# ----------------------
+sub Storable_load($){
+    my $filename = shift;
+    $filename .= ".storable";
+    my $segments  = Storable::retrieve($filename);
+    printf STDERR "Loaded OSM File: $filename as storable\n"
+       if $DEBUG || $VERBOSE;
+    return $segments;
+}
+
+##################################################################
+# read Segment list from osm File
+##################################################################
+
+#our $read_osm_nodes;
+our $read_osm_segments;
+#our $read_osm_obj;
+our (%MainAttr,$Type,%Tags, @WaySegments);
+# Stats
+our %AllTags;
+# Stored data
+our (%Nodes, %Segments, %Stats);
+our $AREA_FILTER;
+$AREA_FILTER = Geo::Filter::Area->new( area => "world" );
+my $from_node=0;
+
+# Function is called whenever an XML tag is started
+#----------------------------------------------
+sub DoStart()
+{
+    my ($Expat, $Name, %Attr) = @_;
+    
+    if($Name eq "node"){
+       undef %Tags;
+       %MainAttr = %Attr;
+       $Type = "n";
+    }
+    if($Name eq "segment"){
+       undef %Tags;
+       %MainAttr = %Attr;
+       $Type = "s";
+    }
+    if($Name eq "way"){
+       undef %Tags;
+       undef @WaySegments;
+       %MainAttr = %Attr;
+       $Type = "w";
+    }
+    if($Name eq "tag"){
+       # TODO: protect against id,from,to,lat,long,etc. being used as tags
+       $Tags{$Attr{"k"}} = $Attr{"v"};
+       $AllTags{$Attr{"k"}}++;
+       $Stats{"tags"}++;
+    }
+    if($Name eq "way"){
+       $from_node=0;
+    }
+    if($Name eq "nd" ) {
+       my $to_node   =  $Attr{"ref"};
+       if ( $from_node &&
+            defined($Nodes{$from_node}) &&
+            defined($Nodes{$to_node}) 
+            ) {
+           my ($lat1,$lon1)=split(",",$Nodes{$from_node});
+           my ($lat2,$lon2)=split(",",$Nodes{$to_node});
+           my $angle = angle_north_relative(
+                                      { lat => $lat1 , lon => $lon1 },
+                                      { lat => $lat2 , lon => $lon2 });
+           push (@{$read_osm_segments},[$lat1,$lon1,$lat2,$lon2,$angle]);
+    }
+       $from_node = $to_node;
+    }
+}
+
+# Function is called whenever an XML tag is ended
+#----------------------------------------------
+sub DoEnd(){
+    my ($Expat, $Element) = @_;
+    my $ID = $MainAttr{"id"};
+    
+    if($Element eq "node"){
+       my $node={};
+       $node->{"lat"} = $MainAttr{"lat"};
+       $node->{"lon"} = $MainAttr{"lon"};
+       
+       if ( $AREA_FILTER->inside($node) ) {
+           $Nodes{$ID} = sprintf("%f,%f",$MainAttr{lat}, $MainAttr{lon});
+           foreach(keys(%Tags)){
+               $node->{$_} = $Tags{$_};
+           }
+       }
+    }
+
+    if($Element eq "segment"){
+       my $from = $MainAttr{"from"};
+       my $to   = $MainAttr{"to"};
+       if ( defined($Nodes{$from}) && defined($Nodes{$to}) ) {
+           $Segments{$ID}{"from"} = $from;
+           $Segments{$ID}{"to"} = $to;
+       }
+    }
+
+    if($Element eq "way"){
+       if ( @WaySegments ) {
+           foreach my $seg_id( @WaySegments ){ # we only have the needed ones in here
+           }
+       }
+    }
+}
+
+# Function is called whenever text is encountered in the XML file
+#----------------------------------------------
+sub DoChar(){
+    my ($Expat, $String) = @_;
+}
+
+# --------------------------------------------
+sub read_osm_file($;$) { # Insert Segments from osm File
+    my $filename = shift;
+    my $bounds  = shift;
+
+    print("Reading OSM Segment from File $filename\n") if $VERBOSE || $DEBUG;
+    if ( file_needs_re_generation($filename,"$filename.storable")) {
+       print "$filename:       ".(-s $filename)." Bytes\n" if $DEBUG;
+       
+       print STDERR "Parsing file: $filename\n" if $DEBUG;
+       my $p = new XML::Parser( Handlers => {
+           Start => \&DoStart, 
+           End => \&DoEnd, 
+           Char => \&DoChar,
+                            },
+                                ErrorContext => 10,
+           );
+       
+       my $fh = data_open($filename);
+       if (not $fh) {
+           print STDERR "WARNING: Could not open osm data from $filename\n";
+           return;
+       }
+       my $content;
+       eval {
+           $content = $p->parse($fh);
+       };
+       if (not $p) {
+           print STDERR "WARNING: Could not parse osm data from $filename\n";
+           return;
+       }
+       #warn Dumper(\$read_osm_segments);
+       Storable_save($filename,$read_osm_segments);    
+    } else {
+       $read_osm_segments=Storable_load($filename);
+    }
+       return($read_osm_segments);
+}
+
+# -------------------------------------------------------
+
+1;
+
+=head1 NAME
+
+Geo::OSM::SegmentList
+
+=head1 COPYRIGHT
+
+Copyright 2006, Jörg Ostertag
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=head1 AUTHOR
+
+Jörg Ostertag (planet-count-for-openstreetmap@ostertag.name)
+
+=head1 SEE ALSO
+
+http://www.openstreetmap.org/
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/Tracks2OSM.pm b/navit/navit/script/osm/Geo/OSM/Tracks2OSM.pm
new file mode 100644 (file)
index 0000000..4016f8c
--- /dev/null
@@ -0,0 +1,145 @@
+##################################################################
+package Geo::OSM::Tracks2OSM;
+# Functions:
+# tracks2osm:
+#     converts a tracks Hash to an OSM Datastructure
+#
+##################################################################
+
+use Exporter;
+@ISA = qw( Exporter );
+use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
+@EXPORT = qw( tracks2osm );
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+
+use Geo::Geometry;
+use Geo::Tracks::Tools;
+use Utils::Debug;
+use Utils::File;
+use Utils::Math;
+
+my $first_id = -10000;
+
+my $lat_lon2node={};
+my $next_osm_node_number = $first_id;
+my $osm_nodes_duplicate   = {};
+# Check if a node at this position exists
+# if it exists we get the old id; otherwise we create a new one
+sub create_node($$) {
+    my $osm_nodes = shift;
+    my $elem      = shift;
+
+    printf STDERR "create_node(): lat or lon undefined : $elem->{lat},$elem->{lon} ".Dumper(\$elem)."\n" 
+       unless  defined($elem->{lat}) && defined($elem->{lon}) ;
+
+    my $id=0;
+    my $lat_lon = sprintf("%f_%f",$elem->{lat},$elem->{lon});
+    if ( defined( $osm_nodes_duplicate->{$lat_lon} ) ) {
+       $id = $osm_nodes_duplicate->{$lat_lon};
+       printf STDERR "Node already exists as $id       pos:$lat_lon\n"
+           if $DEBUG>2;
+       $osm_nodes->{$id}=$elem;
+       # TODO: We have to check that the tags of the old and new nodes don't differ
+       # or we have to merge them
+    } else {
+       $next_osm_node_number--;
+       $id = $next_osm_node_number;
+       $elem->{tag}->{converted_by} = "Track2osm" ;
+       $elem->{node_id} ||= $id;
+       $osm_nodes->{$id}=$elem;
+       $lat_lon2node->{$lat_lon}=$id;
+       $osm_nodes_duplicate->{$lat_lon}=$id;
+    };
+    if ( !$id  ) {
+       print STDERR "create_node(): Null node($id,$lat_lon) created\n".
+           Dumper($elem)
+           if $DEBUG;
+    }
+    return $id;
+}
+
+
+my $osm_way_number     = $first_id;
+# ------------------------------------------------------------------
+sub tracks2osm($){
+    my $tracks = shift;
+
+
+    my $osm_nodes = {};
+    my $osm_ways  = {};
+    my $reference = $tracks->{filename};
+
+    my $last_angle         = 999999999;
+    my $angle;
+    my $angle_to_last;
+
+
+    my $count_valid_points_for_ways=0;
+
+    enrich_tracks($tracks);
+
+    for my $track ( @{$tracks->{tracks}} ) {
+
+       # We need at least two elements in track
+       next unless scalar(@{$track})>1;
+
+       $osm_way_number--;
+       $osm_ways->{$osm_way_number}->{tag} =  {
+           "converted_by" => "Track2osm",
+           "highway"      => "FIXME",
+           "note"         => "FIXME",
+       };
+       for my $elem ( @{$track} ) {
+           my $node_id   = $elem->{node_id}      || create_node($osm_nodes,$elem);
+           
+           # -------------------------------------------- Add to Way
+           push(@{$osm_ways->{$osm_way_number}->{nd}},$node_id);
+           $count_valid_points_for_ways++;
+       }
+    }
+
+    my $bounds = GPS::get_bounding_box($tracks);
+    printf STDERR "Track $reference Bounds: ".Dumper(\$bounds) if $DEBUG>5;
+    return { nodes    => $osm_nodes,
+            ways     => $osm_ways,
+            bounds   => $bounds,
+        };
+}
+
+
+
+=head1 NAME
+
+Geo::OSM::Track2OSM
+
+=head1 COPYRIGHT
+
+Copyright 2006, Jörg Ostertag
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=head1 AUTHOR
+
+Jörg Ostertag (planet-count-for-openstreetmap@ostertag.name)
+
+=head1 SEE ALSO
+
+http://www.openstreetmap.org/
+
+=cut
diff --git a/navit/navit/script/osm/Geo/OSM/Upload.pm b/navit/navit/script/osm/Geo/OSM/Upload.pm
new file mode 100644 (file)
index 0000000..da18e97
--- /dev/null
@@ -0,0 +1,145 @@
+package osm;
+
+use strict;
+use warnings;
+
+
+use WWW::Curl::easy;
+
+sub new(){bless{}};
+
+sub setup(){
+  my $self = shift();
+  $self->{Username} = shift();
+  $self->{Password} = shift();
+  $self->{UserAgent} = shift();
+}
+
+sub tempfiles(){
+  my $self = shift();
+  $self->{file1} = shift();
+  $self->{file2} = shift();
+}
+
+sub uploadWay(){
+  my ($self, $Tags, @Segments) = @_;
+  $Tags .= sprintf("<tag k=\"created_by\" v=\"%s\"/>", $self->{UserAgent});
+  
+  my $Segments = "";
+  foreach $Segment(@Segments){
+    $Segments .= "<seg id=\"$Segment\"/>";
+  }
+  
+  my $Way = "<way id=\"0\">$Segments$Tags</way>";
+  my $OSM = "<osm version=\"0.3\">$Way</osm>";
+  my $data = "<?xml version=\"1.0\"?>\n$OSM";
+  my $path = "way/0";
+
+  my ($response, $http_code) = $self->upload($data, $path);
+  return($response);
+}
+
+sub uploadSegment(){
+  my ($self, $Node1,$Node2,$Tags) = @_;
+  $Tags .= sprintf("<tag k=\"created_by\" v=\"%s\"/>", $self->{UserAgent});
+  
+  my $Segment = sprintf("<segment id=\"0\" from=\"%d\" to=\"%d\">$Tags</segment>", $Node1,$Node2);
+  my $OSM = "<osm version=\"0.3\">$Segment</osm>";
+  my $data = "<?xml version=\"1.0\"?>\n$OSM";
+  my $path = "segment/0";
+
+  my ($response, $http_code) = $self->upload($data, $path);
+  
+  
+  return($response);
+}
+
+sub uploadNode(){
+  my ($self, $Lat, $Long, $Tags) = @_;
+  $Tags .= sprintf("<tag k=\"created_by\" v=\"%s\"/>", $self->{UserAgent});
+  
+  my $Node = sprintf("<node id=\"0\" lon=\"%f\" lat=\"%f\">$Tags</node>", $Long, $Lat);
+  my $OSM = "<osm version=\"0.3\">$Node</osm>";
+  my $data = "<?xml version=\"1.0\"?>\n$OSM";
+  my $path = "node/0";
+
+  my ($response, $http_code) = $self->upload($data, $path);
+  
+  return($response);
+}
+
+sub upload(){
+  my($self, $data, $path) = @_;
+  
+  my $curl = new WWW::Curl::easy;
+
+  my $login = sprintf("%s:%s", $self->{Username}, $self->{Password});
+  
+  open(my $FileToSend, ">", $self->{file1});
+  print $FileToSend $data;
+  close $FileToSend;
+  
+  my $url = "http://www.openstreetmap.org/api/0.3/$path";  
+  
+  open(my $TxFile, "<", $self->{file1});
+  open(my $RxFile, ">",$self->{file2});
+  $curl->setopt(CURLOPT_URL,$url);
+  $curl->setopt(CURLOPT_RETURNTRANSFER,-1);
+  $curl->setopt(CURLOPT_HEADER,0);
+  $curl->setopt(CURLOPT_USERPWD,$login);
+  $curl->setopt(CURLOPT_PUT,-1);
+  $curl->setopt(CURLOPT_INFILE,$TxFile);
+  $curl->setopt(CURLOPT_INFILESIZE, -s $self->{file1});
+  $curl->setopt(CURLOPT_FILE, $RxFile);
+  
+  $curl->perform();
+  my $http_code = $curl->getinfo(CURLINFO_HTTP_CODE);
+  my $err = $curl->errbuf;
+  $curl->close();
+  close $TxFile;
+  close $RxFile;
+  
+  open(my $ResponseFile, "<", $self->{file2});
+  my $response = int(<$ResponseFile>);
+  close $ResponseFile;
+  
+  print "Code $http_code\n" if($http_code != 200);
+  
+  return($response, $http_code);
+}
+
+1;
+
+
+=head1 NAME
+
+Geo::OSM::Upload
+
+=head1 COPYRIGHT
+
+Copyright 2006, Jörg Ostertag
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=head1 AUTHOR
+
+Jörg Ostertag (planet-count-for-openstreetmap@ostertag.name)
+
+=head1 SEE ALSO
+
+http://www.openstreetmap.org/
+
+=cut
+
diff --git a/navit/navit/script/osm/Geo/OSM/Write.pm b/navit/navit/script/osm/Geo/OSM/Write.pm
new file mode 100644 (file)
index 0000000..463c2d0
--- /dev/null
@@ -0,0 +1,170 @@
+##################################################################
+package Geo::OSM::Write;
+##################################################################
+
+use Exporter;
+@ISA = qw( Exporter );
+use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
+@EXPORT = qw( write_osm_file );
+
+use strict;
+use warnings;
+
+use Math::Trig;
+use Data::Dumper;
+
+use Geo::Geometry;
+use Utils::Debug;
+use Utils::File;
+use Utils::Math;
+
+
+# ------------------------------------------------------------------
+sub tags2osm($){
+    my $obj = shift;
+    
+    my $erg = "";
+    for my $k ( keys %{$obj->{tag}} ) {
+       my $v = $obj->{tag}{$k};
+       if ( ! defined $v ) {
+           warn "incomplete Object: ".Dumper($obj);
+       }
+       #next unless defined $v;
+
+       # character escaping as per http://www.w3.org/TR/REC-xml/
+       $v =~ s/&/&amp;/g;
+       $v =~ s/\'/&apos;/g;
+       $v =~ s/</&lt;/g;
+       $v =~ s/>/&gt;/g;
+       $v =~ s/\"/&quot;/g;
+
+       $erg .= "    <tag k=\'$k\' v=\'$v\' />\n";
+    }
+    return $erg;
+}
+
+sub write_osm_file($$) { # Write an osm File
+    my $filename = shift;
+    my $osm = shift;
+
+    my $osm_nodes    = $osm->{nodes};
+    my $osm_ways     = $osm->{ways};
+
+    $osm->{tool} ||= "OSM-Tool";
+    my $count_nodes    = 0;
+    my $count_ways     = 0;
+
+    my $generate_ways=$main::generate_ways;
+
+    my $start_time=time();
+
+    printf STDERR ("Writing OSM File $filename\n") if $VERBOSE >1 || $DEBUG>1;
+
+    my $fh;
+    if ( $filename eq "-" ) {
+       $fh = IO::File->new('>&STDOUT');
+       $fh or  die("cannot open STDOUT: $!");
+    } else {
+       $fh = IO::File->new(">$filename");
+    }
+    $fh->binmode(':utf8');
+
+    print $fh "<?xml version='1.0' encoding='UTF-8'?>\n";
+    print $fh "<osm version=\'0.5\' generator=\'".$osm->{tool}."\'>\n";
+    if ( defined ( $osm->{bounds} ) ) {
+       my $bounds = $osm->{bounds};
+       my $bounds_sting = "$bounds->{lat_min},$bounds->{lon_min},$bounds->{lat_max},$bounds->{lon_max}"; 
+       # -90,-180,90,180
+       print $fh "   <bound box=\"$bounds_sting\" origin=\"OSM-perl-writer\" />\n";
+       
+    }
+
+    # --- Nodes
+    for my $node_id (  sort keys %{$osm_nodes} ) {
+       next unless $node_id;
+       my $node = $osm_nodes->{$node_id};
+       my $lat = $node->{lat};
+       my $lon = $node->{lon};
+       unless ( defined($lat) && defined($lon)){
+           printf STDERR "Node '$node_id' not complete\n";
+           next;
+       }
+       print $fh "  <node id=\'$node_id\' ";
+       print $fh " timestamp=\'".$node->{timestamp}."\' " 
+           if defined $node->{timestamp};
+       print $fh " changeset=\'".$node->{changeset}."\' " 
+           if defined $node->{changeset};
+       print $fh " lat=\'$lat\' ";
+       print $fh " lon=\'$lon\' ";
+       print $fh ">\n";
+       print $fh tags2osm($node);
+       print $fh "  </node>\n";
+       $count_nodes++;
+    }
+
+    # --- Ways
+    for my $way_id ( sort keys %{$osm_ways} ) {
+       next unless $way_id;
+       my $way = $osm_ways->{$way_id};
+       next unless scalar( @{$way->{nd}} )>1;
+
+       print $fh "  <way id=\'$way_id\'";
+       print $fh " timestamp=\'".$way->{timestamp}."\'" 
+           if defined $way->{timestamp};
+       print $fh ">";
+       
+       for my $way_nd ( @{$way->{nd}} ) {
+           next unless $way_nd;
+           print $fh "    <nd ref=\'$way_nd\' />\n";
+       }
+       print $fh tags2osm($way);
+       print $fh "  </way>\n";
+       $count_ways++;
+       
+    }
+
+    print $fh "</osm>\n";
+    $fh->close();
+
+    if ( $VERBOSE || $DEBUG ) {
+       printf STDERR "%-35s:   ",$filename;
+       printf STDERR " Wrote OSM File ".
+           "($count_nodes Nodes, $count_ways Ways)";
+       print_time($start_time);
+    }
+
+}
+
+1;
+
+=head1 NAME
+
+Geo::OSM::Write
+
+=head1 COPYRIGHT
+
+Copyright 2006, Jörg Ostertag
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=head1 AUTHOR
+
+Jörg Ostertag (planet-count-for-openstreetmap@ostertag.name)
+
+=head1 SEE ALSO
+
+http://www.openstreetmap.org/
+
+=cut
index 30a443f..10f50a8 100755 (executable)
@@ -1,5 +1,5 @@
 #! /usr/bin/perl
-use Geo::OSM::APIClientV5;
+use Geo::OSM::APIClientV6;
 use Data::Dumper;
 
 sub print_error
@@ -42,10 +42,18 @@ sub cmd_delete
        my($type,$id)=@_;
        my($res);
        $res=$api->get($type,$id);
+       if (!$api->create_changeset()) {
+               print_error();
+               return 1;
+       }
        if (!$api->delete($res)) {
                print_error();
                return 1;
        }
+       if (!$api->close_changeset()) {
+               print_error();
+               return 1;
+       }
        return 0;
 }
 
@@ -88,10 +96,18 @@ sub cmd_reload
 {
        my($type,$id)=@_;
        $res=$api->get($type,$id);
+       if (!$api->create_changeset()) {
+               print_error();
+               return 1;
+       }
        if (!$api->modify($res)) {
                print_error();
                return 1;
        }
+       if (!$api->close_changeset()) {
+               print_error();
+               return 1;
+       }
        return 0;
 }
 
@@ -122,5 +138,5 @@ while (substr($ARGV[0],0,2) eq '--') {
        $attr{$key}=$value;
        shift;
 }
-$api=new Geo::OSM::APIClient(api=>'http://www.openstreetmap.org/api/0.5',%attr);
+$api=new Geo::OSM::APIClient(api=>'http://www.openstreetmap.org/api/0.6',%attr);
 command(@ARGV);