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

Last change on this file since 29338 was 7049, checked in by nick, 12 years ago

increased node limit

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