source: subversion/utils/osm2csv/osm2csv.pl @ 1380

Last change on this file since 1380 was 1376, checked in by joerg, 14 years ago

new option update-only in osm2csv
add all tags of a segment to the end of the .csv line
automagically get the maximum extent for a pdf atlas. So the area covered by the atlas has no longer to be defined
add more function definitions to osm-pdf-atlas to minimize call errors
split Germany definition into Germany and German-cities
use osm_dir() and planet_dir() to point to the two different directories
improve Module Search path

  • Property svn:executable set to *
File size: 12.0 KB
Line 
1#!/usr/bin/perl
2
3BEGIN {
4    my $dir = $0;
5    $dir =~s,[^/]+/[^/]+$,,;
6    unshift(@INC,"$dir/perl");
7
8    unshift(@INC,"../perl");
9    unshift(@INC,"~/svn.openstreetmap.org/utils/perl");
10    unshift(@INC,"$ENV{HOME}/svn.openstreetmap.org/utils/perl");
11}
12
13
14use strict;
15use warnings;
16
17use XML::Parser;
18use Getopt::Long;
19use Storable ();
20use IO::File;
21use Pod::Usage;
22use Data::Dumper;
23
24use Geo::OSM::Planet;
25use Utils::Debug;
26use Utils::LWP::Utils;
27use Utils::File;
28use Geo::Filter::Area;
29
30sub combine_way_into_segments($); # {}
31sub output_osm($); # {}
32sub output_named_points($); # {}
33sub output_statistic($); # {}
34sub parse_planet($$); # {}
35
36our $man=0;
37our $help=0;
38my $areas_todo;
39my $do_list_areas=0;
40my $do_update_only=0;
41
42Getopt::Long::Configure('no_ignore_case');
43GetOptions ( 
44             'debug+'              => \$DEBUG,     
45             'd+'                  => \$DEBUG,     
46             'verbose+'            => \$VERBOSE,
47             'MAN'                 => \$man, 
48             'man'                 => \$man, 
49             'h|help|x'            => \$help, 
50
51             'no-mirror'           => \$Utils::LWP::Utils::NO_MIRROR,
52             'proxy=s'             => \$Utils::LWP::Utils::PROXY,
53
54             'area=s'              => \$areas_todo,
55             'list-areas'          => \$do_list_areas,
56             'update-only'         => \$do_update_only,
57             )
58    or pod2usage(1);
59
60pod2usage(1) if $help;
61pod2usage(-verbose=>2) if $man;
62
63
64my $Filename = shift();
65unless ( $Filename && -s $Filename ) {
66    $Filename = mirror_planet();
67};
68if ( ! -s $Filename ) {
69    die "Cannot read $Filename\n";
70}
71
72pod2usage(1) unless $Filename;
73
74our $READ_FH=undef;
75
76
77$areas_todo ||= 'germany';
78$areas_todo=lc($areas_todo);
79if ( $do_list_areas ) {
80    print Geo::Filter::Area->list_areas()."\n";
81    exit;
82}
83
84our (%MainAttr,$Type,%Tags, @WaySegments);
85# Stats
86our %AllTags;
87# Stored data
88our (%Nodes, %Segments, %Ways, %Stats);
89our $AREA_FILTER;
90our $PARSING_START_TIME=0;
91
92
93my $data_dir=planet_dir()."/csv";
94mkdir_if_needed( $data_dir );
95
96for my $area_name ( split(",",$areas_todo) ) {
97    if ( $do_update_only ) {
98        my $needs_update=0;
99        $needs_update ||= file_needs_re_generation($Filename,"$data_dir/ways-$area_name.csv");
100        $needs_update ||= file_needs_re_generation($Filename,"$data_dir/osm-$area_name.csv");
101        $needs_update ||= file_needs_re_generation($Filename,"$data_dir/points-$area_name.csv");
102        $needs_update ||= file_needs_re_generation($Filename,"$data_dir/stats-$area_name.txt");
103        next unless $needs_update;
104        print STDERR "Update needed. One of the files is old or non existent\n" if $VERBOSE;
105    }
106    # -----------------------------------------------------------------------------
107    # Temporary data
108
109    (%MainAttr,%Tags)=((),());
110    $Type='';
111    @WaySegments = ();
112    (%AllTags,%Nodes, %Segments, %Ways, %Stats)=((),(),(),(),());
113
114    # Currently active Area Filter
115    $PARSING_START_TIME=0;
116    # Estimated Number of elements to show readin progress in percent
117    # Currently taken from planet-060818
118    $Stats{"nodes estim"}    = 14135968;
119    $Stats{"segments estim"} = 10697464;
120    $Stats{"ways estim"}     = 2758781;
121    $Stats{"tags estim"}     = 51450000;
122
123
124    #----------------------------------------------
125    # Processing stage
126    #----------------------------------------------
127
128    print STDERR "creating $data_dir/osm-$area_name.csv\n" if $VERBOSE;
129
130    parse_planet($Filename,$area_name);
131
132    printf STDERR "Creating output files\n";
133    die "No Area Name defined\n"
134        unless $area_name;
135    combine_way_into_segments("$data_dir/ways-$area_name.csv");
136    output_osm("$data_dir/osm-$area_name.csv");
137    output_named_points("$data_dir/points-$area_name.csv");
138    output_statistic("$data_dir/stats-$area_name.txt");
139    printf STDERR "$area_name Done\n";
140}
141exit;
142
143#----------------------------------------------
144# Parsing planet.osm File
145#----------------------------------------------
146sub parse_planet($$){
147    my $Filename = shift;
148    my $area_name = shift;
149
150    print STDERR "Reading and Parsing XML from $Filename for $area_name\n" if $DEBUG;
151
152    $AREA_FILTER = Geo::Filter::Area->new( area => $area_name );
153
154    $PARSING_START_TIME=time();
155    $READ_FH = data_open($Filename);
156    if ( $VERBOSE || $DEBUG )  {
157        print STDERR "\n";
158        print STDERR "osm2csv: Parsing $Filename for area ".$AREA_FILTER->name()."\n";
159    }
160    my $P = new XML::Parser( Handlers => {
161        Start => \&DoStart, 
162        End => \&DoEnd, 
163        Char => \&DoChar});
164    eval {
165        $P->parse($READ_FH);
166        $READ_FH->close();
167    };
168    if ( $VERBOSE || $DEBUG )  {
169        print STDERR "\n";
170    }
171
172    if ($@) {
173        print STDERR "WARNING: Could not parse osm data $Filename\n";
174        print STDERR "ERROR: $@\n";
175        return;
176    }
177    if (not $P) {
178        print STDERR "WARNING: Could not parse osm data $Filename\n";
179        return;
180    }
181    printf("osm2csv: Parsing Osm-Data in %.0f sec\n",time()-$PARSING_START_TIME )
182        if $DEBUG || $VERBOSE;
183
184}
185
186#----------------------------------------------
187# Combine way data into segments
188#----------------------------------------------
189sub combine_way_into_segments($) {
190    my $filename = shift;
191    print STDERR "Combine way data into segments --> $filename\n" if $DEBUG;
192    if(! open(WAYS,">$filename")) {
193        warn "combine_way_into_segments: Cannot write to $filename\n";
194        return;
195    }
196    foreach my $id ( keys %Ways){
197        my $Way = $Ways{$id};
198        next unless defined $Way;
199        my $segments=$Way->{"segments"};
200        my @SubSegments = split(/,/, $segments);
201        unless ( scalar(@SubSegments) ) {
202            $Stats{"empty ways"}++; 
203            if ( $DEBUG ) {
204                printf WAYS "No Segments for Way: Name:%s\n",($Way->{"name"}||'');
205            }
206            next;
207        }
208        if ( $DEBUG ) {
209            printf WAYS "Way: %s,%s\n", $Way->{"segments"}, ($Way->{"name"}||'');
210        }
211        $Stats{"untagged ways"}++ 
212            unless scalar( keys (%$Way)); 
213       
214        if ( $DEBUG) {
215            printf WAYS "Copying keys: %s to segments %s\n",
216            join(",",keys(%$Way)),
217            join(",",@SubSegments);
218        }
219       
220        # Each segment in a way inherits the way's attributes
221        foreach my $Segment(@SubSegments){
222            foreach my $Key(keys(%$Way)){
223                $Segments{$Segment}{$Key} = $Way->{$Key}
224            }
225        }
226    }
227    close WAYS;
228
229}
230
231#----------------------------------------------
232# Main output (segments)
233#----------------------------------------------
234sub output_osm($){
235    my $filename = shift;
236    print STDERR "Writing Segments to $filename\n" if $DEBUG;
237    if(! open(OSM,">$filename")) {
238        warn "output_osm: Cannot write to $filename\n";
239        return;
240    }
241    foreach my $id (keys %Segments){
242        my $Segment = $Segments{$id};
243        next unless defined $Segment;
244        my $From = $Segment->{"from"};
245        my $To = $Segment->{"to"};
246        unless ( $From && $To ) {
247            $Stats{"segments without endpoints"}++;
248            next;
249        }
250        unless ( defined($Nodes{$From}) && defined($Nodes{$To}) ) {
251            $Stats{"segments without endpoint nodes defined"}++;
252            next;
253        }
254        printf OSM "%f,%f,%f,%f,%s,%s,%s",
255        $Nodes{$From}{"lat"},
256        $Nodes{$From}{"lon"},
257        $Nodes{$To}{"lat"},
258        $Nodes{$To}{"lon"},
259        ($Segment->{"class"}||''),
260        ($Segment->{"name"}||''),
261        ($Segment->{"highway"}||'');
262        foreach my $k ( keys %{$Segment} ){
263            next if $k =~ m/^(class|name|highway)$/;
264            my $v = $Segment->{$k};
265            printf OSM ",%s=%s",$k,$v
266        }
267        printf OSM "\n",
268    }
269    close OSM;
270}
271
272#----------------------------------------------
273# Secondary output (named points)
274#----------------------------------------------
275sub output_named_points($){
276    my $filename = shift;
277    print STDERR "Writing Points to $filename\n" if $DEBUG;
278    if(! open(POINTS,">$filename")) {
279        warn "output_osm: Cannot write to $filename\n";
280        return;
281    }
282    foreach my $id ( keys %Nodes ){
283        my $Node = $Nodes{$id};
284        next unless defined $Node;
285        $Stats{"Nodes with zero lat/long"}++ 
286            if($Node->{"lat"} == 0 and $Node->{"lon"} == 0);
287       
288        if($Node->{"name"} || $Node->{"amenity"} || $Node->{"class"}){
289            printf POINTS "%f,%f,%s,%s,%s\n",
290            $Node->{"lat"},
291            $Node->{"lon"},
292            ($Node->{"name"}||''),
293            ($Node->{"amenity"}||''),
294            ($Node->{"class"}||'');
295        }
296    }
297    close POINTS;
298   
299}
300
301#----------------------------------------------
302# Statistics output
303#----------------------------------------------
304sub output_statistic($){
305    my $filename = shift;
306    print STDERR "Statistics output $filename\n" if $DEBUG;
307    if(! open(STATS,">$filename")) {
308        warn "output_osm: Cannot write to $filename\n";
309        return;
310    }
311    foreach(sort {$AllTags{$b} <=> $AllTags{$a}} keys(%AllTags)){
312        printf STATS "* %d %s\n", $AllTags{$_}, $_;
313    }
314    printf STATS "\n\nStats:\n";
315    foreach(keys(%Stats)){
316        printf STATS "* %d %s\n", $Stats{$_}, $_;
317    }
318}
319
320# Function is called whenever an XML tag is started
321#----------------------------------------------
322sub DoStart()
323{
324    my ($Expat, $Name, %Attr) = @_;
325   
326    if($Name eq "node"){
327        undef %Tags;
328        %MainAttr = %Attr;
329        $Type = "n";
330    }
331    if($Name eq "segment"){
332        undef %Tags;
333        %MainAttr = %Attr;
334        $Type = "s";
335    }
336    if($Name eq "way"){
337        undef %Tags;
338        undef @WaySegments;
339        %MainAttr = %Attr;
340        $Type = "w";
341    }
342    if($Name eq "tag"){
343        # TODO: protect against id,from,to,lat,long,etc. being used as tags
344        $Tags{$Attr{"k"}} = $Attr{"v"};
345        $AllTags{$Attr{"k"}}++;
346        $Stats{"tags"}++;
347    }
348    if($Name eq "seg"){
349        my $id = $Attr{"id"};
350        if ( defined ( $Segments{$id} ) ) {
351            push(@WaySegments, $id);
352        }
353    }
354}
355
356# Function is called whenever an XML tag is ended
357#----------------------------------------------
358sub DoEnd(){
359    my ($Expat, $Element) = @_;
360    if($Element eq "node"){
361        my $ID = $MainAttr{"id"};
362        my $osm_obj={};
363        $osm_obj->{"lat"} = $MainAttr{"lat"};
364        $osm_obj->{"lon"} = $MainAttr{"lon"};
365       
366        if ( $AREA_FILTER->inside($osm_obj) ) {
367            $Nodes{$ID}{"lat"} = $MainAttr{"lat"};
368            $Nodes{$ID}{"lon"} = $MainAttr{"lon"};
369            foreach(keys(%Tags)){
370                $Nodes{$ID}{$_} = $Tags{$_};
371            }
372            $Stats{"nodes named"}++ if($Nodes{$ID}{"name"});
373            $Stats{"nodes tagged "}++ if($MainAttr{"tags"});
374            $Stats{"nodes"}++;
375        }
376        $Stats{"nodes read"}++;
377        #print "Node:".join(",",keys(%Tags))."\n" if(scalar(keys(%Tags))>0);
378    }
379
380    if($Element eq "segment"){
381        my $ID = $MainAttr{"id"};
382
383        my $from = $MainAttr{"from"};
384        my $to   = $MainAttr{"to"};
385        if ( defined($Nodes{$from}) && defined($Nodes{$to}) ) {
386            $Segments{$ID}{"from"} = $from;
387            $Segments{$ID}{"to"} = $to;
388            foreach(keys(%Tags)){
389                $Segments{$ID}{$_} = $Tags{$_};
390            }
391            $Stats{"segments tagged"}++ if($MainAttr{"tags"});
392            $Stats{"segments"}++;
393        }
394        $Stats{"segments read"}++;
395    }
396
397    if($Element eq "way"){
398        my $ID = $MainAttr{"id"};
399        if ( @WaySegments ) {
400            $Ways{$ID}{"segments"} = join(",",@WaySegments);
401            foreach(keys(%Tags)){
402                $Ways{$ID}{$_} = $Tags{$_};
403            }   
404            $Stats{"ways"}++;
405        }
406        $Stats{"ways read"}++;
407    }
408
409    $Stats{"tags read"}++;
410    if ( ( $VERBOSE || $DEBUG ) && ! ( $Stats{"tags read"} % 10000 ))  {
411        print STDERR "\r";
412        print STDERR "Read(".$AREA_FILTER->name()."): ";
413        for my $k ( sort keys %Stats ) {
414            next if $k =~ m/( estim| read)$/;
415            print STDERR " $k:".$Stats{$k};
416            if ( defined($Stats{"$k read"}) ) {
417                printf STDERR " %.0f%%",(100*$Stats{"$k read"}/$Stats{"$k estim"}) 
418                    if defined($Stats{"$k estim"});
419                print STDERR "(".$Stats{"$k read"}.") ";
420            }
421            print STDERR " ";
422        }
423
424        print STDERR mem_usage();
425        print STDERR time_estimate($PARSING_START_TIME,$Stats{"tags estim"},$Stats{"tags read"});
426        print STDERR "\r";
427    }
428}
429
430# Function is called whenever text is encountered in the XML file
431#----------------------------------------------
432sub DoChar(){
433    my ($Expat, $String) = @_;
434}
435
436##################################################################
437# Usage/manual
438
439__END__
440
441=head1 NAME
442
443B<osm2csv.pl> Version 0.02
444
445=head1 DESCRIPTION
446
447B<osm2csv.pl> is a program to convert osm-data from xml format to
448a plain text file in cvs form.
449
450=head1 SYNOPSIS
451
452B<Common usages:>
453
454planet_osm2txt.pl [-d] [-v] [-h] [--no-mirror] [--proxy=<proxy:port>] [--list-areas] <planet_filename.osm>
455
456=head1 OPTIONS
457
458=over 2
459
460=item B<--man> Complete documentation
461
462Complete documentation
463
464=item B<--proxy=<proxy:port>>
465
466Use proxy Server to get the newest planet.osm File
467
468=item B<--no-mirror>
469
470do not try to get the newest planet.osm first
471
472=item B<--area=germany> Area Filter
473
474Only read area for processing
475
476=item B<--list-areas>
477
478print all areas possible
479
480=item B<planet_filename.osm>
481
482the file to read from
483
484=back
Note: See TracBrowser for help on using the repository browser.