Relations/Relations to GPX
Jump to navigation
Jump to search
What it does
This little Perl script takes a bunch of route relations, and writes them out as a single GPX file.
The idea is that you use it in conjunction with a GPX visualiser. Just for example, if you were a ranger for your local cycle route, and wanted to embed a map of the route on your website, you could use this.
Possible future development
- a web interface
- the ability to load a list of ways, or the results of a XAPI query
- investigate using GML rather than GPX, which with OpenLayers' BBOX strategy should work better for big datasets
Code
Some consideration
- Due to http calls code may be slow.
- Program uses STDIN & STDOUT.
- I do not calculate bbox, because gpsbabel and gpsvisualizer do not need it.
- Was checked on single relation 5583485.
- Was checked on many relations 9327, 9333, 34610, 47904.
#!/usr/bin/perl -w use strict; use warnings; use LWP::Simple qw(get); use XML::Mini::Document; sub read_relations() { sub check_if_natural_number($) { my ($checked_string) = @_; unless ( $checked_string =~ /^\d+$/ ) { print "\nI got a $checked_string, which doesn't look like a valid relation id!\n"; exit 1; } } my @relations = <STDIN>; chomp @relations; map { check_if_natural_number($_) } @relations; return @relations; } sub call_osm_api($$) { my ( $member_type, $member_id ) = @_; sub osm_api_url($$) { my ( $member_type, $member_id ) = @_; my $common_part = "http://www.openstreetmap.org/api/0.6/"; if ( $member_type eq 'relation' ) { return "$common_part/relation/$member_id"; } if ( $member_type eq 'way' ) { return "$common_part/way/$member_id/full"; } } my $url = osm_api_url( $member_type, $member_id ); my $osm_data = get($url); unless ( defined($osm_data) ) { print "\nOSM knows nothing about the $member_type $member_id.\n"; exit 1; } my $parser = XML::Mini::Document->new(); $parser->fromString($osm_data); return $parser->toHash(); } sub read_ways_from_relation(@) { my @relations = @_; sub filter_ways($) { my ($all_memberes) = @_; return grep( { $_->{type} eq 'way' } @$all_memberes ); } sub extract_ids(@) { return map( { $_->{ref} } @_ ); } my @ways = (); foreach my $relation_id (@relations) { my $relation = call_osm_api( "relation", $relation_id ); push @ways, extract_ids( filter_ways( $relation->{osm}->{relation}->{member} ) ); } return \@ways; } sub ways_as_nodes($) { my ($way_ids) = @_; sub node_lat_lon($$) { my ( $node_id, $nodes_details ) = @_; my @node = grep { $_->{id} eq $node_id } @$nodes_details; my $node_data = $node[0]; return { lat => $node_data->{lat}, lon => $node_data->{lon}, }; } sub collect_nodes_data($) { my ($way_data) = @_; my $nodes_in_way = $way_data->{osm}->{way}->{nd}; my $nodes_details = $way_data->{osm}->{node}; my @nodes_ids = map( { $_->{ref} } @$nodes_in_way ); my @nodes = map( { node_lat_lon( $_, $nodes_details ) } @nodes_ids ); return \@nodes; } my @ways = (); foreach my $way_id (@$way_ids) { my $way_data = call_osm_api( 'way', $way_id ); push @ways, collect_nodes_data($way_data); } return \@ways; } sub dump_gpx_to_stdout($) { my ($ways) = @_; my $xml = XML::Mini::Document->new(); my $xml_root = $xml->getRoot(); my $xml_header = $xml_root->header('xml'); $xml_header->attribute( 'version', '1.0' ); $xml_header->attribute( 'encoding', 'UTF-8' ); my $gpx = $xml_root->createChild('gpx'); $gpx->attribute( 'xmlns', 'http://www.topografix.com/GPX/1/0' ); $gpx->attribute( 'version', '1.0' ); $gpx->attribute( 'creator', 'https://wiki.openstreetmap.org/wiki/Relations/Relations_to_GPX' ); my $trk = $gpx->createChild('trk'); foreach my $way (@$ways) { my $trkseg = $trk->createChild('trkseg'); foreach my $node (@$way) { my $trkpt = $trkseg->createChild('trkpt'); $trkpt->attribute( 'lat', $node->{lat} ); $trkpt->attribute( 'lon', $node->{lon} ); } } print $xml->toString(); } dump_gpx_to_stdout( ways_as_nodes( read_ways_from_relation( read_relations() ) ) );