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

Reply via email to