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

Last change on this file since 4894 was 4894, checked in by nickburch, 13 years ago

Finish converting to support the 0.5 API

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