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

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

increase size of bit vectors, add 0.5 warning

  • Property svn:executable set to *
File size: 8.8 KB
Line 
1#!/usr/bin/perl
2# Takes a planet.osm, and extracts just the bits that have certain tags
3#
4# Requires several passes over the file, so can't work with a stream from
5#  STDIN. Normally run on an area excerpt, or data downloaded from the API
6#
7# For now, all configuration is done in the code. In future, we'll want to
8#  split this out into a rules file
9#
10# Nick Burch
11#     v0.01   01/11/2006
12
13use strict;
14use warnings;
15
16print STDERR <<EOF;
17Note that this script is not (yet?) 0.5 compatible. For bounding box excerpts
18with 0.5 style planet files, use the -b option with the polygon extract
19script!
20
21EOF
22
23###########################################################################
24#                BEGIN USER CONFIGURATION BLOCK                           #
25###########################################################################
26
27# With these, give a tag name, and optionally a tag value
28# If you only want to match on name, not value, put in undef for the value
29
30# We will get all Nodes required by Segments (and Ways)
31# We can optionally also get other Nodes, based on their tags
32my @node_sel_tags = (
33        ['place','town'], 
34    ['place','city'],
35        ['railway',undef],
36);
37
38# We will get all Segments required by Ways
39# We can optionally also get other Segments, based on their tags
40my @seg_sel_tags = ();
41
42# Specify which ways to get, based on their tags
43my @way_sel_tags = (
44        ['railway',undef],
45        ['highway','motorway'],
46#       ['waterway','river'],
47#       ['natural','coastline'], # Gives really huge .osm files
48);
49
50###########################################################################
51#               END OF USER CONFIGURATION BLOCK                           #
52###########################################################################
53
54
55
56BEGIN {
57    my $dir = $0;
58    $dir =~s,[^/]+/[^/]+$,,;
59    unshift(@INC,"$dir/perl_lib");
60
61    unshift(@INC,"./perl_lib");
62    unshift(@INC,"../perl_lib");
63    unshift(@INC,"~/svn.openstreetmap.org/utils/perl_lib");
64    unshift(@INC,"$ENV{HOME}/svn.openstreetmap.org/utils/perl_lib");
65}
66
67use Getopt::Long;
68
69use Geo::OSM::Planet;
70use Pod::Usage;
71
72# We need Bit::Vector, as perl hashes can't handle the sort of data we need
73use Bit::Vector;
74
75our $man=0;
76our $help=0;
77my $bbox_opts='';
78
79my $VERBOSE;
80
81Getopt::Long::Configure('no_ignore_case');
82GetOptions ( 
83             'verbose+'         => \$VERBOSE,
84             'v+'               => \$VERBOSE,
85             'MAN'              => \$man, 
86             'man'              => \$man, 
87             'h|help|x'         => \$help, 
88             ) or pod2usage(1);
89
90pod2usage(1) if $help;
91pod2usage(-verbose=>2) if $man;
92
93
94# Grab the filename
95my $xml = shift||'';
96pod2usage(1) unless $xml;
97
98# Check we can load the file
99if($xml eq "-") {
100        die("Sorry, reading from stdin is not supported, as we have to make several passes\n");
101}
102unless( -f $xml) {
103        die("Planet.osm file '$xml' could not be found\n");
104}
105
106unless( -s $xml ) {
107    die " $xml has 0 size\n";
108}
109
110
111# We assume IDs to be up to 50 million
112my $wanted_nodes = Bit::Vector->new( 250 * 1000 * 1000 );
113my $wanted_segs = Bit::Vector->new( 250 * 1000 * 1000 );
114
115
116# Sub to open xml
117sub openXML {
118        open(XML, "<$xml") or die("$!");
119        #open(XML, "<:utf8","$xml") or die("$!");
120}
121# Sub to close xml
122sub closeXML {
123        close XML;
124}
125
126# Sub to build sub to do tag matching
127sub buildTagMatcher {
128        my @rules = @_;
129        return sub {
130                my @tagsToTest = @_;
131                foreach my $tagToTest (@tagsToTest) {
132                        my ($name,$value) = @$tagToTest;
133                        foreach my $r (@rules) {
134                                my ($rname,$rvalue) = @$r;
135                                if($rvalue) {
136                                        # Check the rule name+value with the supplied name+value
137                                        if($rname eq $name && $rvalue eq $value) { return 1; }
138                                } else {
139                                        # Check the rule name with the supplied name
140                                        if($rname eq $name) { return 1; }
141                                }
142                        }
143                }
144                # No match on any of the tags
145                return 0;
146        };
147}
148
149# To print out a series of tags as xml
150sub printTags {
151        my @tags = @_;
152        foreach my $tagSet (@tags) {
153                print "    <tag k=\"$tagSet->[0]\" v=\"$tagSet->[1]\" />\n";
154        }
155}
156
157
158# Sub to process the file, against a bunch of helper subroutines
159my $pass = 0;
160sub processXML {
161        my ($nodeH, $segH, $wayH) = @_;
162        openXML();
163        $pass++;
164
165        # Process the file, giving tags to the helpers that like them
166
167        # Hold the main line, tags and segs of the tag
168        my $main_line;
169        my $main_type;
170        my $wanted;
171        my @tags;
172        my @segs;
173
174        my $startNewTag = sub{
175                $wanted = 0;
176                @tags = ();
177                @segs = ();
178        };
179
180        while(my $line = <XML>) {
181                if($line =~ /^\s*<node/) {
182                        $main_line = $line;
183                        $main_type = "node";
184                        &$startNewTag();
185                        unless($line =~ /\/>\s*$/) { next; }
186                }
187                elsif($line =~ /^\s*<segment/) {
188                        $main_line = $line;
189                        $main_type = "segment";
190                        &$startNewTag();
191                        unless($line =~ /\/>\s*$/) { next; }
192                }
193                elsif($line =~ /^\s*\<way/) {
194                        $main_line = $line;
195                        $main_type = "way";
196                        &$startNewTag();
197                        unless($line =~ /\/>\s*$/) { next; }
198                }
199
200                if($line =~ /^\s*\<tag/) {
201                        my ($name,$value) = ($line =~ /^\s*\<tag k=[\'\"](.*?)[\'\"] v=[\'\"](.*?)[\'\"]/);
202                        unless($name) { 
203                                unless($line =~ /k="\s*" v="\s*"/) {
204                                        warn "Invalid line '$line'"; 
205                                }
206                                next; 
207                        }
208                        my @tag = ($name,$value);
209                        push @tags, \@tag;
210                }
211                elsif($line =~ /^\s*\<seg /) {
212                        my ($id) = ($line =~ /^\s*\<seg id=[\'\"](\d+)[\'\"]/);
213                        unless($main_type eq "way") { warn "Got seg when in $main_type\n"; next; }
214                        unless($id) { warn "Invalid line '$line'"; next; }
215                        push @segs, $id;
216                }
217
218                # Do the decisions when closing tags - can be self closing
219                elsif($line =~ /^\s*<\/?node/) {
220                        my ($id,$lat,$long) = ($main_line =~ /^\s*<node id=['"](\d+)['"] lat=['"]?(\-?[\d\.]+)['"]? lon=['"]?(\-?[\d\.]+e?\-?\d*)['"]?/);
221
222                        unless($id) { warn "Invalid node line '$main_line'"; next; }
223                        unless($main_type eq "node") { warn "$main_type ended with $line"; next; }
224                        if($nodeH) {
225                                &$nodeH($id,$lat,$long,\@tags,$main_line,$line);
226                        }
227                }
228                elsif($line =~ /^\s*<\/?segment/) {
229                        my ($id,$from,$to) = ($main_line =~ /^\s*<segment id=['"](\d+)['"] from=['"](\d+)['"] to=['"](\d+)['"]/);
230
231                        unless($id) { warn "Invalid segment line '$main_line'"; next; }
232                        unless($main_type eq "segment") { warn "$main_type ended with $line"; next; }
233                        if($segH) {
234                                &$segH($id,$from,$to,\@tags,$main_line,$line);
235                        }
236                }
237                elsif($line =~ /^\s*\<\/?way/) {
238                        my ($id) = ($main_line =~ /^\s*\<way id=[\'\"](\d+)[\'\"]/);
239
240                        unless($id) { warn "Invalid way line '$main_line'"; next; }
241                        unless($main_type eq "way") { warn "$main_type ended with $line"; next; }
242                        if($wayH) {
243                                &$wayH($id,\@tags,\@segs,$main_line,$line);
244                        }
245                }
246                elsif($line =~ /^\s*\<\?xml/) {
247                        if($pass == 1) {
248                                print $line;
249                        }
250                }
251                elsif($line =~ /^\s*\<osm /) {
252                        if($pass == 1) {
253                                print $line;
254                        }
255                }
256                elsif($line =~ /^\s*\<\/osm\>/ ) {
257                        if($pass == 3) {
258                                print $line;
259                        }
260                }
261                else {
262                        print STDERR "Unknown line $line\n";
263                };
264        }
265
266        # All done
267        closeXML();
268}
269
270
271# First up, call for ways
272my $wayTagHelper = &buildTagMatcher(@way_sel_tags);
273processXML(undef,undef, sub {
274        my ($id,$tagsRef,$segsRef,$main_line,$line) = @_;
275
276        # Test the tags, to see if we want this
277        if(&$wayTagHelper(@$tagsRef)) {
278                # Bingo, matched
279                # Record the segments we want to get
280                foreach my $seg (@$segsRef) {
281                        $wanted_segs->Bit_On($seg);
282                }
283
284                # Output
285                print $main_line;
286                foreach my $seg (@$segsRef) {
287                        print "    <seg id=\"$seg\" />\n";
288                }
289                &printTags(@$tagsRef);
290                print $line;
291        } else {
292                # Not wanted, skip
293        }
294});
295
296# Now for segments
297my $segTagHelper = &buildTagMatcher(@seg_sel_tags);
298processXML(undef, sub {
299        my ($id,$from,$to,$tagsRef,$main_line,$line) = @_;
300        my $wanted = 0;
301
302        # Test the tags, to see if we want this
303        if(&$segTagHelper(@$tagsRef)) {
304                # Bingo, matched
305                $wanted = 1;
306        } else {
307                # Does a way want it?
308                if($wanted_segs->contains($id)) {
309                        # A way wants it
310                        $wanted = 1;
311                }
312        }
313
314        if($wanted) {
315                # Record the nodes we want to get
316                $wanted_nodes->Bit_On($from);
317                $wanted_nodes->Bit_On($to);
318
319                # Output
320                print $main_line;
321                unless($main_line =~ /\/>/) {
322                        &printTags(@$tagsRef);
323                        print $line;
324                }
325        } else {
326                # Not wanted, skip
327        }
328}, undef);
329
330# Now for nodes
331my $nodeTagHelper = &buildTagMatcher(@node_sel_tags);
332processXML(sub {
333        my ($id,$lat,$long,$tagsRef,$main_line,$line) = @_;
334        my $wanted = 0;
335
336        # Test the tags, to see if we want this
337        if(&$nodeTagHelper(@$tagsRef)) {
338                # Bingo, matched
339                $wanted = 1;
340        } else {
341                # Does a segment want it?
342                if($wanted_nodes->contains($id)) {
343                        # A segment wants it
344                        $wanted = 1;
345                }
346        }
347
348        if($wanted) {
349                # Output
350                print $main_line;
351                unless($main_line =~ /\/>/) {
352                        &printTags(@$tagsRef);
353                        print $line;
354                }
355        } else {
356                # Not wanted, skip
357        }
358}, undef, undef);
359
360
361# All done
362
363##################################################################
364# Usage/manual
365
366__END__
367
368=head1 NAME
369
370B<planetosm-excerpt-tags.pl>
371
372=head1 DESCRIPTION
373
374=head1 SYNOPSIS
375
376B<Common usages:>
377
378
379B<planertosm-excerpt-tags.pl> <planet.osm.xml> > excerpt.osm
380
381parse an excerpted planet.osm file, and output the parts that match certain
382tags.
383
384=head1 AUTHOR
385
386=head1 COPYRIGHT
387
388
389=head1 SEE ALSO
390
391http://www.openstreetmap.org/
392
393=cut
Note: See TracBrowser for help on using the repository browser.