diff options
author | martin-s <martin-s@ffa7fe5e-494d-0410-b361-a75ebd5db220> | 2009-08-27 19:09:25 +0000 |
---|---|---|
committer | martin-s <martin-s@ffa7fe5e-494d-0410-b361-a75ebd5db220> | 2009-08-27 19:09:25 +0000 |
commit | c27854879123aadbed446247d891896f3c1d1246 (patch) | |
tree | 9fe4fc65de5c9cfccf391891f0b5ac5677d0a45e /navit/script | |
parent | a795f1486e1203da44c15b93017587af62738e48 (diff) | |
download | navit-c27854879123aadbed446247d891896f3c1d1246.tar.gz |
Fix:script:Converted to new osm api
git-svn-id: http://svn.code.sf.net/p/navit/code/trunk/navit@2534 ffa7fe5e-494d-0410-b361-a75ebd5db220
Diffstat (limited to 'navit/script')
20 files changed, 5380 insertions, 2 deletions
diff --git a/navit/script/osm/Geo/OSM/APIClientV4.pm b/navit/script/osm/Geo/OSM/APIClientV4.pm new file mode 100644 index 000000000..26b8ec5fc --- /dev/null +++ b/navit/script/osm/Geo/OSM/APIClientV4.pm @@ -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/script/osm/Geo/OSM/APIClientV5.pm b/navit/script/osm/Geo/OSM/APIClientV5.pm new file mode 100644 index 000000000..5bee097de --- /dev/null +++ b/navit/script/osm/Geo/OSM/APIClientV5.pm @@ -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/script/osm/Geo/OSM/APIClientV6.pm b/navit/script/osm/Geo/OSM/APIClientV6.pm new file mode 100644 index 000000000..d659ac14c --- /dev/null +++ b/navit/script/osm/Geo/OSM/APIClientV6.pm @@ -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/script/osm/Geo/OSM/EntitiesV3.pm b/navit/script/osm/Geo/OSM/EntitiesV3.pm new file mode 100644 index 000000000..2708463c7 --- /dev/null +++ b/navit/script/osm/Geo/OSM/EntitiesV3.pm @@ -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/script/osm/Geo/OSM/EntitiesV5.pm b/navit/script/osm/Geo/OSM/EntitiesV5.pm new file mode 100644 index 000000000..625ff6b03 --- /dev/null +++ b/navit/script/osm/Geo/OSM/EntitiesV5.pm @@ -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/script/osm/Geo/OSM/EntitiesV6.pm b/navit/script/osm/Geo/OSM/EntitiesV6.pm new file mode 100644 index 000000000..8747c17f0 --- /dev/null +++ b/navit/script/osm/Geo/OSM/EntitiesV6.pm @@ -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/script/osm/Geo/OSM/MapFeatures.pm b/navit/script/osm/Geo/OSM/MapFeatures.pm new file mode 100644 index 000000000..c4ae62f89 --- /dev/null +++ b/navit/script/osm/Geo/OSM/MapFeatures.pm @@ -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/script/osm/Geo/OSM/OsmChangeReaderV3.pm b/navit/script/osm/Geo/OSM/OsmChangeReaderV3.pm new file mode 100644 index 000000000..65103a36e --- /dev/null +++ b/navit/script/osm/Geo/OSM/OsmChangeReaderV3.pm @@ -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/script/osm/Geo/OSM/OsmChangeReaderV5.pm b/navit/script/osm/Geo/OSM/OsmChangeReaderV5.pm new file mode 100644 index 000000000..fdc65e1a9 --- /dev/null +++ b/navit/script/osm/Geo/OSM/OsmChangeReaderV5.pm @@ -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/script/osm/Geo/OSM/OsmChangeReaderV6.pm b/navit/script/osm/Geo/OSM/OsmChangeReaderV6.pm new file mode 100644 index 000000000..e67397829 --- /dev/null +++ b/navit/script/osm/Geo/OSM/OsmChangeReaderV6.pm @@ -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/script/osm/Geo/OSM/OsmReaderV3.pm b/navit/script/osm/Geo/OSM/OsmReaderV3.pm new file mode 100644 index 000000000..b01963470 --- /dev/null +++ b/navit/script/osm/Geo/OSM/OsmReaderV3.pm @@ -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/script/osm/Geo/OSM/OsmReaderV5.pm b/navit/script/osm/Geo/OSM/OsmReaderV5.pm new file mode 100644 index 000000000..37a477b1a --- /dev/null +++ b/navit/script/osm/Geo/OSM/OsmReaderV5.pm @@ -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/script/osm/Geo/OSM/OsmReaderV6.pm b/navit/script/osm/Geo/OSM/OsmReaderV6.pm new file mode 100644 index 000000000..4f6a683d2 --- /dev/null +++ b/navit/script/osm/Geo/OSM/OsmReaderV6.pm @@ -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/script/osm/Geo/OSM/OsmXML.pm b/navit/script/osm/Geo/OSM/OsmXML.pm new file mode 100644 index 000000000..60f458db7 --- /dev/null +++ b/navit/script/osm/Geo/OSM/OsmXML.pm @@ -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/script/osm/Geo/OSM/Planet.pm b/navit/script/osm/Geo/OSM/Planet.pm new file mode 100644 index 000000000..bedfdffc8 --- /dev/null +++ b/navit/script/osm/Geo/OSM/Planet.pm @@ -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/script/osm/Geo/OSM/SegmentList.pm b/navit/script/osm/Geo/OSM/SegmentList.pm new file mode 100644 index 000000000..0de35ebf5 --- /dev/null +++ b/navit/script/osm/Geo/OSM/SegmentList.pm @@ -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/script/osm/Geo/OSM/Tracks2OSM.pm b/navit/script/osm/Geo/OSM/Tracks2OSM.pm new file mode 100644 index 000000000..4016f8c02 --- /dev/null +++ b/navit/script/osm/Geo/OSM/Tracks2OSM.pm @@ -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/script/osm/Geo/OSM/Upload.pm b/navit/script/osm/Geo/OSM/Upload.pm new file mode 100644 index 000000000..da18e97ec --- /dev/null +++ b/navit/script/osm/Geo/OSM/Upload.pm @@ -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/script/osm/Geo/OSM/Write.pm b/navit/script/osm/Geo/OSM/Write.pm new file mode 100644 index 000000000..463c2d0cd --- /dev/null +++ b/navit/script/osm/Geo/OSM/Write.pm @@ -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/&/&/g; + $v =~ s/\'/'/g; + $v =~ s/</</g; + $v =~ s/>/>/g; + $v =~ s/\"/"/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 diff --git a/navit/script/osm/osmtool.pl b/navit/script/osm/osmtool.pl index 30a443fb6..10f50a8ed 100755 --- a/navit/script/osm/osmtool.pl +++ b/navit/script/osm/osmtool.pl @@ -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); |