source: subversion/applications/utils/osm-extract/osm-subset.pl @ 4225

Last change on this file since 4225 was 2721, checked in by hakan, 12 years ago

utils moved to applications/utils

  • Property svn:executable set to *
File size: 10.3 KB
Line 
1#!/usr/bin/perl
2# Licence GPL
3
4BEGIN {
5    my $dir = $0;
6    $dir =~s,[^/]+/[^/]+$,,;
7    unshift(@INC,"$dir/perl_lib");
8
9    unshift(@INC,"../../perl_lib");
10    unshift(@INC,"~/svn.openstreetmap.org/applications/utils/perl_lib");
11    unshift(@INC,"$ENV{HOME}/svn.openstreetmap.org/applications/utils/perl_lib");
12}
13
14
15use strict;
16use warnings;
17
18use XML::Parser;
19use Getopt::Long;
20use IO::File;
21use Pod::Usage;
22use Data::Dumper;
23use Carp;
24
25use Geo::Filter::Area;
26use Geo::OSM::Planet;
27use Utils::Debug;
28use Utils::File;
29use Utils::Math;
30
31sub parse_planet($$$); # {}
32
33our $man=0;
34our $help=0;
35my $areas_todo;
36my $do_list_areas=0;
37my $do_update_only=0;
38
39my $use_max_mem=0;
40
41Getopt::Long::Configure('no_ignore_case');
42GetOptions ( 
43             'debug+'              => \$DEBUG,     
44             'd+'                  => \$DEBUG,     
45             'verbose+'            => \$VERBOSE,
46             'MAN'                 => \$man, 
47             'man'                 => \$man, 
48             'h|help|x'            => \$help, 
49
50             'no-mirror'           => \$Utils::LWP::Utils::NO_MIRROR,
51             'proxy=s'             => \$Utils::LWP::Utils::PROXY,
52
53             'area=s'              => \$areas_todo,
54             'list-areas'          => \$do_list_areas,
55             )
56    or pod2usage(1);
57
58pod2usage(1) if $help;
59pod2usage(-verbose=>2) if $man;
60
61
62# TODO:
63# if the input filename is not planet*osm* we have to change the output filename too.
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;
75our $WRITE_FH=undef;
76
77$areas_todo ||= 'europe';
78$areas_todo=lc($areas_todo);
79if ( $do_list_areas ) {
80    print Geo::Filter::Area->list_areas()."\n";
81    exit;
82}
83
84print STDERR "Using OSM-subset: $Filename\n" if $VERBOSE || $DEBUG;
85
86
87our (%MainAttr,$Type,%Tags, @WaySegments);
88# Stats
89# Stored data
90our @NODES;
91our @SEGMENTS;
92
93our %Stats;
94our $AREA_FILTER;
95our $PARSING_START_TIME=0;
96our $PARSING_DISPLAY_TIME=0;
97
98our $vsz0 = mem_usage('vsz');
99
100for my $area_name ( split(",",$areas_todo) ) {
101    # -----------------------------------------------------------------------------
102    # Temporary data
103
104    (%MainAttr,%Tags)=((),());
105    $Type='';
106    @WaySegments = ();
107    (%Stats)=('elem'=>0,'node'=>0,'segment'=>0,'way'=>0);
108    # Currently active Area Filter
109    $PARSING_START_TIME=0;
110
111    #----------------------------------------------
112    # Processing stage
113    #----------------------------------------------
114
115    my $file_out = $Filename;
116    $file_out =~ s/\.osm(\.gz|\.bz2)?$/-$area_name.osm/;
117    parse_planet($Filename,$file_out,$area_name);
118
119    printf STDERR "$area_name Done\n";
120}
121exit;
122
123#----------------------------------------------
124# Parsing planet.osm File
125#----------------------------------------------
126sub parse_planet($$$){
127    my $Filename_in = shift;
128    my $Filename_out = shift;
129    my $area_name = shift;
130
131    print STDERR "Reading and Parsing XML from $Filename for $area_name\n" if $DEBUG;
132
133    $AREA_FILTER = Geo::Filter::Area->new( area => $area_name );
134
135    $PARSING_START_TIME=time();
136    $READ_FH = data_open($Filename_in);
137    $WRITE_FH = IO::File->new(">$Filename_out");
138    $WRITE_FH->binmode(':utf8');
139    print $WRITE_FH '<?xml version="1.0" encoding="UTF-8"?>'."\n";
140    print $WRITE_FH '<osm version="0.3" generator="OpenStreetMap planet.rb">'."\n";
141
142    if ( $VERBOSE || $DEBUG )  {
143        print STDERR "\n";
144        print STDERR "osm-subset.pl: Parsing $Filename_in for area ".$AREA_FILTER->name()."\n";
145    }
146    my $P = new XML::Parser( Handlers => {
147        Start => \&DoStart, 
148        End => \&DoEnd, 
149        Char => \&DoChar});
150    eval {
151        $P->parse($READ_FH);
152        $READ_FH->close();
153        print $WRITE_FH '</osm>'."\n";
154        $WRITE_FH->close();
155    };
156    if ( $VERBOSE || $DEBUG )  {
157        print STDERR "\n";
158    }
159
160    if ($@) {
161        print STDERR "WARNING: Could not parse osm data $Filename_in\n";
162        print STDERR "ERROR: $@\n";
163        return;
164    }
165    if (not $P) {
166        print STDERR "WARNING: Could not parse osm data $Filename_in\n";
167        return;
168    }
169    $Stats{"time parsing"} = time()-$PARSING_START_TIME;
170    printf("osm-subset.pl: Parsing Osm-Data in %.0f sec\n",time()-$PARSING_START_TIME )
171        if $DEBUG || $VERBOSE;
172
173}
174
175sub display_status($){
176    my $mode = shift;
177    return unless $VERBOSE || $DEBUG ;
178    return unless time()-$PARSING_DISPLAY_TIME >2;
179
180    $PARSING_DISPLAY_TIME= time();
181    print STDERR "\r";
182    #print STDERR "$mode(".$AREA_FILTER->name()."): ";
183
184    print STDERR time_estimate($PARSING_START_TIME,
185                               $Stats{"elem read"},estimated_max_count("elem"));
186
187    my $pos = $READ_FH->tell();
188    printf STDERR " pos:%.2fGB ",$pos/1024/1024/1024;
189    for my $k ( sort keys %Stats ) {
190        next if $k =~ m/( read)$/;
191        next if $k =~ m/(tag|mem)$/;
192        next if $k =~ m/named/ && $VERBOSE <=1;
193        next if $k !~ m/elem/ && $VERBOSE <=2;
194        print STDERR "$k:".$Stats{$k};
195        my $estim=estimated_max_count($k);
196        if ( defined($Stats{"$k read"}) ) {
197            printf STDERR "=%.0f%%",(100*$Stats{"$k read"}/$estim) 
198                if $estim;
199        }
200        print STDERR "($estim) ";
201    }
202
203    my $vsz = mem_usage('vsz');
204
205    if ( ! $use_max_mem and (mem_info("MemFree")<10) ){
206        die "\nToo much memory ($vsz MB) used; MemFree: ".mem_info("MemFree")."MB\n";
207    }
208    if ( $use_max_mem >0 and ( $vsz > $use_max_mem ) ) {
209        die "\nToo much memory($vsz MB) used; max allowed: $use_max_mem MB ".mem_info()."\n";
210    }
211   
212    print STDERR mem_usage();
213    print STDERR "\r";
214   
215    #print STDERR "\n";
216    #store_mem_arrays();
217
218}
219
220
221# Function is called whenever an XML tag is started
222#----------------------------------------------
223sub DoStart()
224{
225    my ($Expat, $Name, %Attr) = @_;
226   
227    if($Name eq "node"){
228        undef %Tags;
229        %MainAttr = %Attr;
230        $Type = "n";
231    }
232    if($Name eq "segment"){
233        undef %Tags;
234        %MainAttr = %Attr;
235        $Type = "s";
236    }
237    if($Name eq "way"){
238        undef %Tags;
239        undef @WaySegments;
240        %MainAttr = %Attr;
241        $Type = "w";
242    }
243    if($Name eq "tag"){
244        # TODO: protect against id,from,to,lat,long,etc. being used as tags
245        $Tags{$Attr{"k"}} = $Attr{"v"};
246        $Stats{"tag"}++;
247    }
248    if($Name eq "seg"){
249        my $id = $Attr{"id"};
250        if ( $SEGMENTS[$id] ) {
251            push(@WaySegments, $id);
252        }
253    }
254    $Stats{"elem"}++;
255}
256
257# ------------------------------------------------------------------
258sub tags2osm($){
259    my $tags = shift;
260   
261    my $erg = "";
262    for my $k ( keys %{$tags} ) {
263        my $v = $tags->{$k};
264        if ( ! defined $v ) {
265            warn "incomplete Object: ".Dumper(\$tags);
266        }
267        #next unless defined $v;
268
269        # character escaping as per http://www.w3.org/TR/REC-xml/
270        $v =~ s/&/&amp;/g;
271        $v =~ s/\'/&apos;/g;
272        $v =~ s/</&lt;/g;
273        $v =~ s/>/&gt;/g;
274        $v =~ s/\"/&quot;/g;
275
276        $erg .= "    <tag k=\"$k\" v=\"$v\" />\n";
277    }
278    return $erg;
279}
280
281# Function is called whenever an XML tag is ended
282#----------------------------------------------
283sub DoEnd(){
284    my ($Expat, $Element) = @_;
285    my $id = $MainAttr{"id"};
286    $Stats{"elem read"}++;
287    display_status("Read: ");
288
289    if($Element eq "node"){
290        $Stats{"node read"}++;
291        if ( $AREA_FILTER->inside(\%MainAttr) ) {
292            $NODES[$id]=1;
293
294            print $WRITE_FH "  <node id=\"$id\"";
295            print $WRITE_FH " lat=\"$MainAttr{lat}\"";
296            print $WRITE_FH " lon=\"$MainAttr{lon}\"";
297            print $WRITE_FH " timestamp=\"".$MainAttr{timestamp}."\"" 
298                if defined $MainAttr{timestamp};
299            if ( keys %Tags ){
300                print $WRITE_FH ">\n";
301                print $WRITE_FH tags2osm(\%Tags);
302                print $WRITE_FH "  </node>\n";
303            } else {
304                print $WRITE_FH "/>\n";
305            }
306            $Stats{"node"}++;
307        }
308    }
309
310    if($Element eq "segment"){
311        print STDERR "\n" unless $Stats{"segment read"}++ && $VERBOSE;
312        my $from = $MainAttr{"from"};
313        my $to   = $MainAttr{"to"};
314        if ( $NODES[$from] and $NODES[$to] ) {
315            print $WRITE_FH "  <segment id=\"$id\"";
316            print $WRITE_FH " from=\"$MainAttr{from}\"";
317            print $WRITE_FH " to=\"$MainAttr{to}\" ";
318            print $WRITE_FH " timestamp=\"".$MainAttr{timestamp}."\"" 
319                if defined $MainAttr{timestamp};
320            if ( keys %Tags ){
321                print $WRITE_FH ">\n";
322                print $WRITE_FH tags2osm(\%Tags);
323                print $WRITE_FH "  </segment>\n";
324            } else {
325                print $WRITE_FH "/>\n";
326            }
327            $Stats{"segment"}++;
328            $SEGMENTS[$id]=1;
329        }
330    }
331
332    if($Element eq "way"){
333        print STDERR "\n" unless $Stats{"way read"}++ && $VERBOSE;
334        if ( @WaySegments ) {
335        print $WRITE_FH "  <way id=\'$id\'";
336        print $WRITE_FH " timestamp=\'".$MainAttr{timestamp}."\'" 
337            if defined $MainAttr{timestamp};
338        print $WRITE_FH ">";
339        print $WRITE_FH tags2osm(\%Tags);
340        for my $seg_id ( @WaySegments) {
341            next unless $seg_id;
342            print $WRITE_FH "    <seg id=\'$seg_id\'/>\n";
343        }
344        print $WRITE_FH "  </way>\n";
345        $Stats{"way"}++;
346        }
347    }
348
349}
350
351# Function is called whenever text is encountered in the XML file
352#----------------------------------------------
353sub DoChar(){
354    my ($Expat, $String) = @_;
355}
356
357##################################################################
358# Usage/manual
359
360__END__
361
362=head1 NAME
363
364B<osm-subset.pl.pl> Version 0.02
365
366=head1 DESCRIPTION
367
368B<osm-subset.pl.pl> is a program to extract an area from an osm File
369
370=head1 SYNOPSIS
371
372B<Common usages:>
373
374osm-subset.pl.pl [-d] [-v] [-h] [--no-mirror] [--proxy=<proxy:port>] [--list-areas] <planet_filename.osm>
375
376=head1 OPTIONS
377
378=over 2
379
380=item B<--man> Complete documentation
381
382Complete documentation
383
384=item B<--proxy=<proxy:port>>
385
386Use proxy Server to get the newest planet.osm File
387
388=item B<--no-mirror>
389
390do not try to get the newest planet.osm first
391
392=item B<--debug> B<-d>
393
394write out some debug info too
395
396=item B<--verbose> B<-v>
397
398write out more info while processing
399
400=item B<--area=germany> Area Filter
401
402Select Area for processing
403
404=item B<--list-areas>
405
406print all areas possible
407
408=item B<planet_filename.osm>
409
410the file to read from
411
412=back
413
414=head1 COPYRIGHT
415
416Copyright 2006, Jörg Ostertag
417
418This program is free software; you can redistribute it and/or
419modify it under the terms of the GNU General Public License
420as published by the Free Software Foundation; either version 2
421of the License, or (at your option) any later version.
422
423This program is distributed in the hope that it will be useful,
424but WITHOUT ANY WARRANTY; without even the implied warranty of
425MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
426GNU General Public License for more details.
427
428You should have received a copy of the GNU General Public License
429along with this program; if not, write to the Free Software
430Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
431
432=head1 AUTHOR
433
434Jörg Ostertag (planet-count-for-openstreetmap@ostertag.name)
435
436=head1 SEE ALSO
437
438http://www.openstreetmap.org/
439
440=cut
Note: See TracBrowser for help on using the repository browser.