Hello,
the attached script joins up the ways in multipolygons that satisfy
the following:
all members are non-closed ways with role=""
all members share the same tags (apart from created_by)
by reversing some of the ways, they form one long way
The member ways are merged into one long way and the relation is
deleted.
I've run it on relations with id below 1000. It fixed about 300.
Cheers
Robert
import osm
import sys
import codecs
def delete(e):
e.action = "delete"
def modify(e):
try:
if e.action == "delete":
raise Exception("try to modify deleted object: %s" % e)
except AttributeError:
pass
e.action = "modify"
e.tags["created_by"] = "connectmulti.py"
def closed(w):
return w.nodes[0] == w.nodes[-1]
def reverse(w):
w.nodes.reverse()
modify(w)
def join(ws):
# merge a list of ways into the first
w = ws[0]
t = w.tags.copy()
try:
del t["created_by"]
except:
pass
for i in range(1, len(ws)):
assert ws[i-1].nodes[-1] == ws[i].nodes[0]
t2 = ws[i].tags.copy()
try:
del t2["created_by"]
except:
pass
assert t == t2
for i in range(1, len(ws)):
w.nodes.extend(ws[i].nodes[1:])
delete(ws[i])
modify(w)
def main():
(nodes,ways,relations) = osm.read(sys.stdin)
for r in relations.values():
if [ m for m in r.members if m.type != "way"]:
continue
if [ m for m in r.members if m.role != "" ]:
continue
if [ m for m in r.members if closed(ways[m.ref]) ]:
continue
ws = [ ways[m.ref] for m in r.members ]
ends = {}
for i in range(len(ws)):
def add(ends, k, v):
ends[k] = ends.get(k, []) + [v]
add(ends, ws[i].nodes[0], (i,0))
add(ends, ws[i].nodes[-1], (i,-1))
once = [ n for n in ends.keys() if len(ends[n]) == 1 ]
twice = [ n for n in ends.keys() if len(ends[n]) == 2 ]
more = [ n for n in ends.keys() if len(ends[n]) > 2 ]
if more:
continue
if len(once) not in [0, 2]:
continue
if once:
(first, last) = once
else:
first = twice[0]
last = twice[0]
newws = []
while ends[first]:
(w,o) = ends[first].pop()
newws.append((w,o))
first = ws[w].nodes[-1-o]
ends[first].remove((w,-1-o))
if len(newws) < len(ws):
# didn't get all ways
continue
for w in [ w for (w,o) in newws if o == -1 ]:
reverse(ws[w])
try:
join([ ws[w] for (w,o) in newws ])
except AssertionError:
sys.stderr.write("rel %d: different tags\n" % r.id)
continue
delete(r)
# work around brokenness
sys.stdout = codecs.getwriter("utf-8")(sys.__stdout__)
osm.write((nodes,ways,relations), sys.stdout)
if __name__ == "__main__":
main()
#!/opt/local/bin/python
import sys
import xml.parsers.expat
class Node:
def __init__(self):
self.tags = {}
class Way:
def __init__(self):
self.tags = {}
self.nodes = []
class Relation:
def __init__(self):
self.tags = {}
self.members = set([])
class Member:
def __hash__(self):
return hash((self.type, self.ref, self.role))
class OSMReader:
def __init__(self):
self.current = None
self.nodes = {}
self.ways = {}
self.relations = {}
def ignore(self, name, attrs):
if name != "osm":
sys.stderr.write("ignoring %s\n" % name)
def start_element(self, name, attrs):
if self.current is None:
self.start_new(name, attrs)
else:
self.start_other(name, attrs)
def start_new(self, name, attrs):
if name == "node":
self.current = Node()
elif name == "way":
self.current = Way()
elif name == "relation":
self.current = Relation()
else:
self.ignore(name, attrs)
return
# for a in ["id", "timestamp", "visible", "lat", "lon"]:
# if attrs.has_key(a):
# # the values happen to be in YAML format
# attrs[a] = yaml.load(attrs[a])
attrs["id"] = int(attrs["id"])
self.current.__dict__.update(attrs)
def start_other(self, name, attrs):
if name == "tag":
self.current.tags[attrs["k"]] = attrs["v"]
elif name == "nd":
self.current.nodes.append(int(attrs["ref"]))
elif name == "member":
m = Member()
m.__dict__.update(attrs)
m.ref = int(m.ref)
self.current.members.add(m)
else:
self.ignore(name, attrs)
def end_element(self, name):
if name in ["node", "way", "relation"]:
self.handle_element(self.current)
self.current = None
def handle_element(self, elem):
if isinstance(elem, Node):
self.nodes[elem.id] = elem
elif isinstance(elem, Way):
self.ways[elem.id] = elem
elif isinstance(elem, Relation):
self.relations[elem.id] = elem
def read(f):
p = xml.parsers.expat.ParserCreate()
r = OSMReader()
p.StartElementHandler = r.start_element
p.EndElementHandler = r.end_element
p.ParseFile(f)
return (r.nodes,r.ways,r.relations)
def write((nodes,ways,relations), f):
f.write("<?xml version='1.0' encoding='UTF-8'?>\n")
f.write("<osm version='0.5' generator='osm.py'>\n")
for n in nodes.values():
f.write("<node")
write_attrs(n, f)
f.write(">\n")
write_tags(n, f)
f.write("</node>\n")
for w in ways.values():
f.write("<way")
write_attrs(w, f)
f.write(">\n")
write_tags(w, f)
for n in w.nodes:
f.write(" <nd ref='%d'/>\n" % n)
f.write("</way>\n")
for r in relations.values():
f.write("<relation")
write_attrs(r, f)
f.write(">\n")
for m in r.members:
f.write(" <member type='%s' ref='%d' role='%s'/>\n"
% (m.type,m.ref,m.role))
f.write("</relation>\n")
f.write('</osm>\n')
def write_attrs(e, f):
for a in ["id", "visible", "timestamp", "user", "lat", "lon", "action"]:
if hasattr(e, a):
f.write(" %s='%s'" % (a,getattr(e,a)))
def write_tags(e, f):
for (k,v) in e.tags.items():
f.write(" <tag k='%s' v='%s'/>\n" % (k,v))
_______________________________________________
talk mailing list
[email protected]
http://lists.openstreetmap.org/cgi-bin/mailman/listinfo/talk