source: subversion/applications/utils/osm-extract/planetosm-excerpt-area.pl @ 4779

Last change on this file since 4779 was 4779, checked in by frederik, 13 years ago

increase size of bit vectors, add 0.5 warning

  • Property svn:executable set to *
File size: 7.4 KB
Line 
1#e!lusr/bin/perl
2# Takes a planet.osm, and extracts just the bits that relate to one area
3#
4# Nick Burch
5#     v0.01   31/10/2006
6
7BEGIN {
8    my $dir = $0;
9    $dir =~s,[^/]+/[^/]+$,,;
10    unshift(@INC,"$dir/perl");
11
12    unshift(@INC,"./perl_lib");
13    unshift(@INC,"../../perl_lib");
14    unshift(@INC,"~/svn.openstreetmap.org/applications/utils/perl_lib");
15    unshift(@INC,"$ENV{HOME}/svn.openstreetmap.org/applications/utils/perl_lib");
16}
17
18use strict;
19use warnings;
20
21use Getopt::Long;
22
23use Geo::OSM::Planet;
24use Pod::Usage;
25
26# We need Bit::Vector, as perl hashes can't handle the sort of data we need
27use Bit::Vector;
28
29our $man=0;
30our $help=0;
31my $bbox_opts='';
32
33my $VERBOSE;
34
35print STDERR <<EOF;
36Note that this script is not (yet?) 0.5 compatible. For bounding box excerpts
37with 0.5 style planet files, use the -b option with the polygon extract
38script!
39
40EOF
41
42Getopt::Long::Configure('no_ignore_case');
43GetOptions ( 
44             'verbose+'         => \$VERBOSE,
45             'v+'               => \$VERBOSE,
46             'MAN'              => \$man, 
47             'man'              => \$man, 
48             'h|help|x'         => \$help, 
49
50             'bbox=s'           => \$bbox_opts,
51             ) or pod2usage(1);
52
53pod2usage(1) if $help;
54pod2usage(1) unless $bbox_opts;
55pod2usage(-verbose=>2) if $man;
56
57
58# Grab the filename
59my $xml = shift||'';
60if (!$xml)
61{
62    print STDERR "Input file name must be given on command line - reading \n";
63    print STDERR "from stdin not supported.\n";
64    pod2usage(1);
65}
66
67# Should we warn for things we skip due to the bbox?
68my $warn_bbox_skip = 0;
69
70# Exclude nodes not in this lat,long,lat,long bounding box
71my @bbox = ();
72warn "Only outputting things within $bbox_opts\n";
73@bbox = split(/,/, $bbox_opts);
74
75# Check that things are in the right order
76check_bbox_valid(@bbox);
77
78
79# Check we can load the file
80unless( -f $xml || $xml eq "-" ) {
81        die("Planet.osm file '$xml' could not be found\n");
82}
83
84if ( $xml ne "-" && ! -s $xml ) {
85    die " $xml has 0 size\n";
86}
87
88
89# Counts of the numbers handled
90my $node_count = 0;
91my $seg_count = 0;
92my $way_count = 0;
93my $line_count = 0;
94
95# We assume IDs to be up to 50 million
96my $nodes = Bit::Vector->new( 250 * 1000 * 1000 );
97my $segs = Bit::Vector->new( 250 * 1000 * 1000 );
98
99# Process
100open(XML, "<$xml") or die("$!");
101#open(XML, "<:utf8","$xml") or die("$!");
102
103# Hold the id and type of the last valid main tag
104my $last_id;
105my $last_type;
106
107# Hold the segment and tags list for a way
108# (We only add the way+segments+tags if has valid segments)
109my $way_line;
110my @way_tags;
111my @way_segs;
112
113# Loop over the data
114while(my $line = <XML>) {
115        $line_count++;
116
117        #&display_count("line",$line_count);
118
119        # Process the line of XML
120        if($line =~ /^\s*<node/) {
121                my ($id,$lat,$long) = ($line =~ /^\s*<node[^>]+id=['"](\d+)['"][^>]+lat=['"]?(\-?[\d\.]+)['"]?[^>]+lon=['"]?(\-?[\d\.]+e?\-?\d*)['"]?/);
122                $last_id = undef; # In case it has tags we need to exclude
123                $last_type = "node";
124
125                unless($id) { warn "Invalid line '$line'"; next; }
126
127                # Do we need to exclude this node?
128                if(@bbox) {
129                        if($lat > $bbox[0] && $lat < $bbox[2] &&
130                                $long > $bbox[1] && $long < $bbox[3]) {
131                                # This one's inside the bbox
132                        } else {
133                                if($warn_bbox_skip) {
134                                        warn("Skipping node at $lat $long as not in bbox\n");
135                                }
136                                next;
137                        }
138                }
139
140                # Output the node
141                print $line;
142
143                $nodes->Bit_On($id);
144                $last_id = $id;
145
146                $node_count++;
147                #&display_count("node", $node_count);
148        }
149        elsif($line =~ /^\s*<segment/) {
150                my ($id,$from,$to) = ($line =~ /^\s*<segment[^>]+id=['"](\d+)['"][^>]+from=['"](\d+)['"][^>]+to=['"](\d+)['"]/);
151                $last_id = undef; # In case it has tags we need to exclude
152                $last_type = "segment";
153
154                unless($id) { warn "Invalid line '$line'"; next; }
155
156                unless($nodes->contains($to)) { 
157                        if($warn_bbox_skip) {
158                                warn "No node $to for line '$line'"; 
159                        }
160                        next; 
161                }
162                unless($nodes->contains($from)) { 
163                        if($warn_bbox_skip) {
164                                warn "No node $from for line '$line'"; 
165                        }
166                        next; 
167                }
168
169                # Output
170                print $line;
171
172                $segs->Bit_On($id);
173                $last_id = $id;
174
175                $seg_count++;
176                #&display_count("segment", $seg_count);
177        }
178        elsif($line =~ /^\s*\<way/) {
179                my ($id) = ($line =~ /^\s*\<way id=[\'\"](\d+)[\'\"]/);
180                $last_id = undef; # In case it has tags we need to exclude
181                $last_type = "way";
182
183                unless($id) { warn "Invalid line '$line'"; next; }
184
185                # Save ID and line, will add later
186                $last_id = $id;
187                $way_line = $line;
188
189                $way_count++;
190                #&display_count("way", $way_count);
191
192                # Blank way children lists
193                @way_tags = ();
194                @way_segs = ();
195        }
196        elsif($line =~ /^\s*\<\/way/) {
197                my $way_id = $last_id;
198                $last_id = undef;
199
200                unless($way_id) { 
201                        # Invalid way, skip
202                        next; 
203                }
204
205                unless(@way_segs) {
206                        if($warn_bbox_skip) {
207                                warn("Skipping way with no valid segments with id '$way_id'");
208                        }
209                        next;
210                }
211
212                # Output way
213                print $way_line;
214
215                # Output way segments
216                my $way_seg_count = 0;
217                foreach my $ws (@way_segs) {
218                        print $ws->{line};
219                }
220                # Add way tags
221                foreach my $wt (@way_tags) {
222                        print $wt->{line};
223                }
224
225                # Finish way
226                print $line;
227        }
228        elsif($line =~ /^\s*\<seg /) {
229                my ($id) = ($line =~ /^\s*\<seg id=[\'\"](\d+)[\'\"]/);
230                unless($last_id) { next; }
231                unless($id) { warn "Invalid line '$line'"; next; }
232                unless($segs->contains($id)) { 
233                        if($warn_bbox_skip) {
234                                warn "Invalid segment for line '$line'"; 
235                        }
236                        next; 
237                }
238
239                # Save, only add later
240                my %ws; 
241                $ws{'line'} = $line;
242                $ws{'id'} = $id;
243
244                push (@way_segs,\%ws);
245        }
246        elsif($line =~ /^\s*\<tag/) {
247                my ($name,$value) = ($line =~ /^\s*\<tag k=[\'\"](.*?)[\'\"] v=[\'\"](.*?)[\'\"]/);
248                unless($name) { 
249                        unless($line =~ /k="" v=""/) {
250                                warn "Invalid line '$line'"; 
251                        }
252                        next; 
253                }
254                if($name =~ /^\s+$/) { warn "Skipping invalid tag line '$line'"; next; }
255
256                # Decode the XML elements in the name and value
257                $value =~ s/\&apos\;/\'/g;
258               
259                # If last_id isn't there, the tag we're attached to was invalid
260                unless($last_id) {
261                        if($warn_bbox_skip) {
262                                warn("Invalid previous $last_type, ignoring its tag '$line'");
263                        }
264                        next;
265                }
266
267                if($last_type eq "node") {
268                        print $line;
269                } elsif($last_type eq "segment") {
270                        print $line;
271                } elsif($last_type eq "way") {
272                        # Save, only add if way has segments
273                        my %wt; 
274                        $wt{'line'} = $line;
275                        $wt{'name'} = $name;
276                        $wt{'value'} = $value;
277
278                        push (@way_tags,\%wt);
279                }
280            }   
281        elsif($line =~ /^\s*\<\?xml/) {
282                print $line;
283        }
284        elsif($line =~ /^\s*\<osm / || $line =~ /^\s*\<\/osm\>/ ) {
285                print $line;
286        }
287        elsif($line =~ /^\s*\<\/node\>/) {
288                if($last_id) {
289                        print $line;
290                }
291        }
292        elsif($line =~ /^\s*\<\/segment\>/) {
293                if($last_id) {
294                        print $line;
295                }
296        }
297        else {
298            print STDERR "Unknown line $line\n";
299        };
300}
301
302
303########################################################################
304
305
306sub check_bbox_valid {
307        my @bbox = @_;
308        unless($bbox[0] < $bbox[2]) {
309                die("1st lat ($bbox[0]) must be smaller than second ($bbox[2])");
310        }
311        unless($bbox[1] < $bbox[3]) {
312                die("1st long ($bbox[1]) must be smaller than second ($bbox[3])");
313        }
314}
315
316
317##################################################################
318# Usage/manual
319
320__END__
321
322=head1 NAME
323
324B<planetosm-excerpt-area.pl>
325
326=head1 DESCRIPTION
327
328=head1 SYNOPSIS
329
330B<Common usages:>
331
332
333B<planetosm-excerpt-area.pl> -bbox 54,-1.5,55,-1.4 planet.osm.xml > excerpt.osm
334
335parse planet.osm file, and output the parts between the bbox
336
337=head1 OPTIONS
338
339=over 2
340
341=item B<--bbox>
342
343planetosm-excerpt-area.pl -bbox 10,-3.5,11,-3 planet.osm.xml
344        Only output things inside the bounding box
345     (min lat, min long, max lat, max long)
346
347=back
348
349=head1 COPYRIGHT
350
351=head1 AUTHOR
352
353=head1 SEE ALSO
354
355http://www.openstreetmap.org/
356
357=cut
Note: See TracBrowser for help on using the repository browser.