Last active
July 22, 2024 05:15
-
-
Save balrog-kun/4241509 to your computer and use it in GitHub Desktop.
Merge two .osm files, one with building footprints, one with address nodes. Write output.osm.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/python2 | |
# vim: fileencoding=utf-8 encoding=utf-8 et sw=4 | |
import sys | |
import os | |
import xml.etree.cElementTree as ElementTree | |
import string | |
outroot = ElementTree.Element("osm", { "version": "0.6" }) | |
bldgroot = ElementTree.parse(sys.argv[1]).getroot() | |
addrroot = ElementTree.parse(sys.argv[2]).getroot() | |
waynodes = {} | |
bldgs = [] | |
addrs = [] | |
# Read the building outlines | |
for elem in bldgroot: | |
if 'id' not in elem.attrib: | |
continue | |
id = int(elem.attrib['id']) | |
if elem.tag == 'node': | |
lat = float(elem.attrib['lat']) | |
lon = float(elem.attrib['lon']) | |
waynodes[id] = ( lat, lon ) | |
outroot.append(elem) | |
if elem.tag != 'way': | |
outroot.append(elem) | |
continue | |
tags = {} | |
for sub in elem: | |
if sub.tag != 'tag': | |
continue | |
v = sub.attrib['v'].strip() | |
if v: | |
tags[sub.attrib['k']] = v | |
# Tag transformations can happen here | |
# Parse the geometry, store in a convenient format, | |
# also find some point in the middle of the outline to be used to | |
# speed up distance calculation | |
way = [] | |
refs = [] | |
j = 0 | |
lat = 0.0 | |
lon = 0.0 | |
for sub in elem: | |
if sub.tag != 'nd': | |
continue | |
ref = int(sub.attrib['ref']) | |
if ref not in waynodes: | |
continue | |
way.append(waynodes[ref]) | |
refs.append(ref) | |
j += 1 | |
lat += waynodes[ref][0] | |
lon += waynodes[ref][1] | |
lat /= j | |
lon /= j | |
if refs[0] != refs[-1]: | |
outroot.append(elem) | |
continue | |
if 'version' in elem.attrib: | |
v = int(elem.attrib['version']) | |
else: | |
v = 1 | |
bldgs.append(( lat, lon, way, refs, tags, id, v, [] )) | |
bldgroot = None # Make python release the XML structure | |
def contains(poly, pos): | |
cont = 0 | |
prev = poly[0] | |
for node in poly[1:]: | |
if (node[1] > pos[1]) != (prev[1] > pos[1]) and pos[0] < node[0] + \ | |
(prev[0] - node[0]) * (pos[1] - node[1]) / (prev[1] - node[1]): | |
cont = not cont | |
prev = node | |
return cont | |
# Read the address nodes data | |
for elem in addrroot: | |
if 'id' not in elem.attrib: | |
continue | |
tags = {} | |
for sub in elem: | |
if sub.tag != 'tag': | |
continue | |
v = sub.attrib['v'].strip() | |
if v: | |
tags[sub.attrib['k']] = v | |
if elem.tag != 'node': | |
continue | |
lat = float(elem.attrib['lat']) | |
lon = float(elem.attrib['lon']) | |
id = int(elem.attrib['id']) | |
if 'version' in elem.attrib: | |
v = int(elem.attrib['version']) | |
else: | |
v = 1 | |
addr = ( lat, lon, tags, id, v, [] ) | |
addrs.append(addr) | |
# Find what if any building shapes contain this lat/lon | |
for elat, elon, way, refs, btags, id, v, newaddrs in bldgs: | |
if 'addr:housenumber' in btags: | |
continue | |
if abs(elat - lat) + abs(elon - lon) > 0.006: | |
continue | |
if not contains(way, ( lat, lon )): | |
continue | |
newaddrs.append(addr) | |
break | |
addrroot = None | |
for lat, lon, way, refs, tags, id, v, newaddrs in bldgs: | |
attrs = { "version": str(v), "id": str(id) } | |
# If this building contains only a single address node, copy its tags | |
# to the building way and mark the node as no longer needed. | |
if len(newaddrs) == 1: | |
newaddrs[0][5].append(1) | |
if 'source' in newaddrs[0][2]: | |
newaddrs[0][2]['source:addr'] = newaddrs[0][2]['source'] | |
del newaddrs[0][2]['source'] | |
tags.update(newaddrs[0][2]) | |
attrs['action'] = 'modify' | |
elem = ElementTree.SubElement(outroot, "way", attrs) | |
for k in tags: | |
ElementTree.SubElement(elem, 'tag', { 'k': k, 'v': tags[k] }) | |
for ref in refs: | |
ElementTree.SubElement(elem, 'nd', { 'ref': str(ref) }) | |
# Add remaining addresses as nodes | |
for lat, lon, tags, id, v, bbs in addrs: | |
if bbs: | |
continue | |
i = id | |
if i < 0: | |
i -= 2000000 | |
elem = ElementTree.SubElement(outroot, "node", { | |
'lat': str(lat), | |
'lon': str(lon), | |
"version": str(v), | |
"id": str(i) }) | |
for k in tags: | |
ElementTree.SubElement(elem, 'tag', { 'k': k, 'v': tags[k] }) | |
sys.stdout.write("Writing .osm's\n") | |
ElementTree.ElementTree(outroot).write("output.osm", "utf-8") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment