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

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

moved polygon stuff, added readme

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