Python/Script for reducing the precision of nodes

From OpenStreetMap Wiki
Jump to navigation Jump to search

About

  • This script is to help the LINZ data import, where roads are downloaded in JOSM with 6 (lon) or 7 (lat) decimal place precision, but the LINZ data comes with 9 (lon) or 10 (lat) dpp. So we round the bridge layer to match the road layer downloaded with JOSM then snap duplicate nodes with the JOSM Validator plugin. After that (hopefully) the Validator notices the overlapping roadways, splits the road at the end-nodes of the bridge, merges all tags, and discards the smaller/newer segment.
But that's just in theory. In reality the JOSM Validator only gets us as far as snapping the nodes.
  • An alternative to all this is to just upload everything to the server and then download everything again using the same program.
Depending on your data source you'll have to adjust to suit
  • JOSM seems to save .osm with %.7f for lat and %.6f for lon (~1cm) at -44ˆ,-176ˆ.
Probably they are using %.9g for both?
  • Merkaartor 0.14 seems to save .osm with %.8f (~1mm) for both.
  • Xapi seems to save .osm with %.7f (~1cm) for both.
  • LINZ-2-OSM seems to export .osm with %.10f for lat and %.9f for lon at -44,-176. (0s stripped off)
Probably they are using %.12g for both?


  • TODO: starting with two .osm files, search for nodes within a certain threshold and snap/reuse those nodes in the new datafile (e.g. epsilon < 1cm).

It's not the most efficient thing in the world, but what the heck, it works for its purpose.

Usage

  • Extract bridge_cl from the LINZ-2-OSM web app.
  • Adjust the script for Xapi matching mode (%.7f,%.7f)
  • Run this ./osm_...py script
  • Download roads using Xapi:
BASE_URL="http://osmxapi.hypercube.telascience.org/api/0.6"
BBOX="-177,-44.5,-175.5,-43.5"

wget -O chatham_roads_dl.osm \
  "$BASE_URL/way[highway=*][LINZ:source_version=V16][bbox=$BBOX]"
  • Start JOSM, have the Validator plugin installed.
    • Open Xapi chathams_roads_dl.osm
    • Open bridge_cl_7f.osm
  • Merge bridge layer into road layer
  • Zoom out a tiny bit
  • Select all (ctrl-a)
  • Validate
  • Fix
  • Review
  • Upload (should be many new highways and not many new nodes)

The script

#!/usr/bin/env python
############################################################################
#
# MODULE:       osm_reduce_coord_fidelity.py
# AUTHOR:       Hamish Bowman, Dunedin, New Zealand
# PURPOSE:      Read in LINZ-2-OSM exported bridge_cl.osm and simplify coords
# COPYRIGHT:    (c) 2010 Hamish Bowman, and the OpenStreetMap Foundation
#               Various bits inspired by various GRASS GIS Python scripts (GPL2)
#
#               This program is free software under the GNU General Public
#               License (>=v2) and comes with ABSOLUTELY NO WARRANTY.
#               See http://www.gnu.org/licenses/gpl2.html for details.
#
#############################################################################

import sys
import os

def main():
    input = 'bridge_cl.osm'
    output = 'bridge_cl_76f.osm'
    #dp=7
    #output = 'bridge_cl_%df.osm' % dp

    #### set up input file
    if input == '-':
        infile = None
        inf = sys.stdin
    else:
        infile = input
        if not os.path.exists(infile):
            print("Unable to read input data")
            sys.exit(1)
        inf = file(infile)
        print("input file=[%s]" % infile)

    #### set up output file
    if not output:
        outfile = None
        outf = sys.stdout
    else:
        outfile = output
        outf = open(outfile, 'w')
        print("output file=[%s]" % outfile)

    lines = inf.readlines()

    for i in range(len(lines)):
        lines[i] = lines[i].rstrip('\n')

# lines[105]:
#'    <node id="-6698677" lat="-43.9585473707" lon="-176.550362475" />'

    for line in lines:
        if 'node id=' not in line:
            outf.write(str(line) + '\n')
        else:
            bits = line.split('"')
            outf.write('%s"%s"%s"%.7f"%s"%.6f"%s\n' % (bits[0],
                         bits[1], bits[2], float(bits[3]),
                         bits[4], float(bits[5]), bits[6] ))

    inf.close()
    if outfile is not None:
        outf.close()


if __name__ == "__main__":
    main()