Reprojecting OSM data with Perl
(Redirected from Projected version of an OSM extract)
Jump to navigation
Jump to search
The following code can be used to reproject OSM data to (OSGB, aka EPSG:27700) by adding 'x' and 'y' (XML) attributes:
#!/usr/bin/perl
use warnings;
use strict;
use Geo::Proj4;
use XML::LibXSLT;
use XML::LibXML;
use XML::DOM;
my $osgb36 = Geo::Proj4->new(init => "epsg:27700") or die Geo::Proj4->error;
project_xsl();
sub project_dom {
my $parser = new XML::DOM::Parser;
my $doc = $parser->parse(\*STDIN);
my $nodes = $doc->getElementsByTagName ("node");
my $n = $nodes->getLength;
for (my $i = 0; $i < $n; $i++) {
my $node = $nodes->item($i);
my $lat = $node->getAttributeNode("lat")->getValue();
my $lon = $node->getAttributeNode("lon")->getValue();
my ($x, $y) = $osgb36->forward($lat, $lon) or die;
$node->setAttribute("x", $x);
$node->setAttribute("y", $y);
}
print $doc->toString();
}
sub project_xsl {
XML::LibXSLT->register_function("urn:proj", "toosgb",
sub {
# args: lat, lon
my ($x, $y) = $osgb36->forward(@_) or die;
my $result = XML::LibXML::NodeList->new;
$result->push(XML::LibXML::Attr->new("x", $x));
$result->push(XML::LibXML::Attr->new("y", $y));
return $result;
});
XML::LibXSLT->register_function("urn:proj", "bounds",
sub {
# args: bbox string
my @b = split ',', shift;
my $result = XML::LibXML::NodeList->new;
if ($#b == 3) {
$result->push(XML::LibXML::Attr->new("minlat", $b[0]));
$result->push(XML::LibXML::Attr->new("minlon", $b[1]));
$result->push(XML::LibXML::Attr->new("maxlat", $b[2]));
$result->push(XML::LibXML::Attr->new("maxlon", $b[3]));
}
return $result;
});
my $xslt = XML::LibXSLT->new();
my $doc = get_stylesheet();
my $stylesheet = $xslt->parse_stylesheet($doc);
my $osm = XML::LibXML->load_xml(IO=>\*STDIN, no_cdata=>1);
my $output = $stylesheet->transform($osm) or die;
print $stylesheet->output_as_chars($output);
}
sub get_stylesheet
{
return XML::LibXML->load_xml(string=><<'EOF', no_cdata=>1);
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:proj="urn:proj"
version="1.0">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="bound[@box]">
<xsl:copy-of select="."/>
<bounds>
<xsl:copy-of select="proj:bounds(@box)"/>
</bounds>
</xsl:template>
<xsl:template match="way|relation">
<xsl:copy>
<xsl:copy-of select="@id"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="node">
<xsl:copy>
<xsl:copy-of select="@id|@lat|@lon"/>
<xsl:copy-of select="proj:toosgb(@lat,@lon)"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
EOF
}
I hope this is useful to others wanting to use a projected version of an OSM extract.
The above code includes two working methods - the stylesheet version, though longer, may be useful if you're already processing OSM with XSLT.