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

Last change on this file since 4225 was 2744, checked in by nickburch, 12 years ago

Tweak getopt to support negative lat

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