Omdat ik behoorlijk wat data verzamel als waypoints in GPX files, duurt het steeds een hele poos vooraleer ik alle data heb verwerkt. Dit weekend heb ik wat tijd besteed aan het schrijven van een Python script om de GPX data om te zetten in OSM-formaat. Het onderstaande script is het resultaat. Het kan nog lang niet alles omzetten, maar is toch al een start.
Deze week ga ik het wat uittesten in de praktijk.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import xml.sax
import sys
import getopt
import io
import re
import time;
nodeId = -1
'''
This Python 2.7 compliant script will turn WayPoints coming from a Garmin device
into an OSM-compliant file that can be imported with JOSM.
It relies on the syntax used in the name of the waypoints
The following stuff is recognized:
- VB for amenity: waste_basket (Vuilbak)
- BK for amenity: bench (Bank or Zitbank)
- FIREH for emergency: fire_hydrant
- M50/M70/Z70/Z30/Z50 for maxspeeds (Z for zones)
- PIKNIK for tourism: picnic_site
- WSS [name] for historic: wayside_shrine
- CDC for power: cable_distribution_cabinet
- ESS for power: sub_station
- STOP/GW for highway: stop / give_way
- LLI/RLI for highway: street_lamp
- PAAL, KGATE, SWINGGATE, SWGATE, GATE, FBAR for
barrier: bollard / kissing_gate, swing_gate, swing_gate, gate, cycle_barrier
- house number notations, e.g. R10, L10-12, L10+-+20, L10+12---13+15
Execution:
parseWayPoints.py -f
waypoint file has to end on '.gpx'
The output is a file name the same as the waypoint file, but ending on '.osm'
'''
class Node():
def __init__(self, lon, lat):
global nodeId
self.lon = lon
self.lat = lat
self.id = nodeId
nodeId = nodeId - 1
def moveABit(self):
return Node(self.lon + 0.00001, self.lat + 0.00001)
def moveABitMore(self, move):
return Node(self.lon + move, self.lat + move)
class NodePlacer():
def __init__(self, file):
self.file = file
self.updatedText = ''
self.nodeCount = 0
self.node = None
def placeNode(self, node, tags):
self.file.write('')
for kv in tags:
self.file.write('')
self.file.write('\n')
self.node = node
def placeMovedNode(self, node, tags):
node = node.moveABit()
self.placeNode(node, tags)
class AmenityNode(NodePlacer):
def __init__(self, file):
NodePlacer.__init__(self, file)
def match(self, node, text):
if text.find("BK VB") > -1 or text.find("VB BK") > -1 :
self.placeNode(node, [('amenity', 'waste_basket')] )
self.placeMovedNode(node, [('amenity', 'bench')] )
self.updatedText = text.replace("BK VB", "").replace('VB BK', '')
self.nodeCount = 2
return True
if text.find("VB") > -1:
self.placeNode(node, [('amenity', 'waste_basket')] )
self.updatedText = text.replace('VB', '')
self.nodeCount = 1
return True
if text.find("BK") > -1:
self.placeNode(node, [('amenity', 'bench')] )
self.updatedText = text.replace('BK', '')
self.nodeCount = 1
return True
if text.find("FIREH") > -1:
self.placeNode(node, [('emergency', 'fire_hydrant')] )
self.updatedText = text.replace('FIREH', '')
self.nodeCount = 1
return True
return False
class PicnicNode(NodePlacer):
def __init__(self, file):
NodePlacer.__init__(self, file)
def match(self, node, text):
if text.find("PIKNIK") > -1:
self.placeNode(node, [('tourism', 'picnic_site')] )
self.updatedText.replace('PIKNIK', '')
self.nodeCount = 1
return True
class MaxSpeedNode(NodePlacer):
def __init__(self, file):
NodePlacer.__init__(self, file)
def match(self, node, text):
if text.find("M50") > -1:
self.placeNode(node, [('maxspeed', '50'), ('zone:traffic', 'BE:rural')])
self.nodeCount = 1
self.updatedText = text.replace('M50', '')
if text.find('M70') > -1:
self.placeNode(node, [('maxspeed', '70'), ('zone:traffic', 'BE:rural')])
self.nodeCount = 1
self.updatedText = text.replace('M70', '')
if text.find('Z70') > -1:
self.placeNode(node, [('maxspeed', '70'), ('zone:traffic', 'BE:rural'), ('source:maxspeed', 'zone70')])
self.nodeCount = 1
self.updatedText = text.replace('Z70', '')
if text.find('Z50') > -1:
self.placeNode(node, [('maxspeed', '50'), ('zone:traffic', 'BE:rural'), ("source:maxspeed", "zone50")])
self.nodeCount = 1
self.updatedText = text.replace('Z50', '')
if text.find('Z30') > -1:
self.placeNode(node, [('maxspeed', '30'), ('zone:traffic', 'BE:urban'), ("source:maxspeed", "zone30")])
self.nodeCount = 1
self.updatedText = text.replace('Z30', '')
class WaySideShrineNode(NodePlacer):
def __init__(self, file):
NodePlacer.__init__(self, file)
self.wssPattern = re.compile(r'\A[LR]+[ ]WSS([ A-Za-z0-9]*)')
def match(self, node, text):
wssMatch = self.wssPattern.match(text)
if wssMatch:
if (wssMatch.group(1) == ""):
self.placeNode(node, [('historic', 'wayside_shrine'), ('religion', 'christian'), ('denomination', 'roman_catholic')])
else:
self.placeNode(node, [('historic', 'wayside_shrine'), ('religion', 'christian'), ('denomination', 'roman_catholic'), ('name', wssMatch.group(1).strip())])
self.updatedText = ''
self.nodeCount = 1
return True
return False
class BarrierNode(NodePlacer):
def __init__(self, file):
NodePlacer.__init__(self, file)
def match(self, node, text):
if text.find("PAAL") > -1:
self.placeNode(node, [('barrier', 'bollard'), ('foot','yes'), ('bicycle', 'yes')] )
self.updatedText = text.replace('PAAL', '')
self.nodeCount = 1
return True
if text.find("SWINGGATE") > -1 or text.find("SWGATE") > -1:
self.placeNode(node, [('barrier', 'swing_gate'), ('foot','yes'), ('bicycle', 'yes')] )
self.updatedText = text.replace('SWINGGATE', '').replace('SWGATE', '')
self.nodeCount = 1
return True
if text.find("KGATE") > -1 or text.find("KISSGATE") > -1:
self.placeNode(node, [('barrier', 'kiss_gate'), ('foot','yes'), ('bicycle', 'no')] )
self.updatedText = ''
self.nodeCount = 1
return True
if text.find("GATE") > -1:
self.placeNode(node, [('barrier', 'gate'), ('foot','yes'), ('bicycle', 'yes')] )
self.updatedText = text.replace('GATE', '')
self.nodeCount = 1
return True
if text.find("FBAR") > -1 :
self.placeNode(node, [('barrier', 'cycle_barrier'), ('foot','yes'), ('bicycle', 'yes')] )
self.updatedText = text.replace('FBAR', '')
self.nodeCount = 1
return True
class PowerNode(NodePlacer):
def __init__(self, file):
NodePlacer.__init__(self, file)
def match(self, node, text):
if text == "L ESS" or text == "R ESS" or text == "ESS":
self.placeNode(node, [('power', 'sub_station')] )
self.updatedText = ''
self.nodeCount = 1
return True
if text == "L CDC" or text == "R CDC" or text == "CDC":
self.placeNode(node, [('power', 'cable_distribution_cabinet')] )
self.updatedText = ''
self.nodeCount = 1
return True
class HighwayNode(NodePlacer):
def __init__(self, file):
NodePlacer.__init__(self, file)
def match(self, node, text):
if text == "RLI" or text == "LLI" :
self.placeNode(node, [('highway', 'street_lamp')] )
self.updatedText = ''
self.nodeCount = 1
return True
if text == "STOP" :
self.placeNode(node, [('highway', 'stop')] )
self.updatedText = ''
self.nodeCount = 1
return True
if text.find("GW") == 0:
self.placeNode(node, [('highway', 'give_way')] )
self.updatedText = ''
self.nodeCount = 1
return True
class HouseNumberNode(NodePlacer):
def __init__(self, file):
NodePlacer.__init__(self, file)
self.numberPattern = re.compile('[0-9]')
self.houseNumberPattern = re.compile(r'\A[+-]*([0-9]+[a-zA-Z]*)')
self.terracePattern = re.compile(r'[+-]*([0-9]+[a-zA-Z]*)\+\-\+([0-9]+[a-zA-Z]*)')
def match(self, node, text):
if not ( text.startswith("L") or text.startswith("R")):
return False
numberpart = text.strip('LR ')
terraceMatch = self.terracePattern.match(numberpart)
if terraceMatch:
group1 = int(terraceMatch.group(1))
group2 = int(terraceMatch.group(2))
startNr = min(group1, group2)
endNr = max(group1, group2)
terraceNrs = range(startNr, endNr + 1, 2)
self.nodeCount = 0
for nr in terraceNrs:
self.placeNode(node, [('building', 'house'), ('addr:housenumber', str(nr))])
node = node.moveABitMore(0.00005)
self.nodeCount = self.nodeCount + 1
self.updatedText = 'L' + self.terracePattern.sub('', numberpart, 1)
return True
houseNumberMatch = self.houseNumberPattern.match(numberpart)
if houseNumberMatch:
self.placeNode(node, [('building', 'house'), ('addr:housenumber', houseNumberMatch.group(1).strip())])
self.updatedText = 'L' + self.houseNumberPattern.sub('', numberpart, 1)
self.nodeCount = 1
return True
class WaypointContentHandler(xml.sax.ContentHandler):
def __init__(self, filename):
xml.sax.ContentHandler.__init__(self)
self.nodeCount = 0
self.text = ""
self.f = open(filename, 'w')
self.f.write('\n')
self.f.write('\n')
self.f.write('\n')
self._matchers = [ AmenityNode(self.f), PicnicNode(self.f), HighwayNode(self.f),
BarrierNode(self.f), PowerNode(self.f), HouseNumberNode(self.f), WaySideShrineNode(self.f),
MaxSpeedNode(self.f) ]
def startElement(self, tagname, attrs):
if tagname == "wpt":
self.node = Node(float(attrs.getValue("lon")), float(attrs.getValue("lat")))
if tagname == "name":
self.text = ''
def endElement(self, tagname):
if tagname == "name":
self.tagNode(self.node, self.text.strip())
def endDocument(self):
self.f.write('\n')
self.f.close()
def characters(self, content):
self.text = self.text + content
def tagNode(self, node, text):
for matcher in self._matchers:
theText = text
matcher.node = None
while matcher.match(node, theText):
theText = matcher.updatedText.strip()
self.nodeCount = self.nodeCount + matcher.nodeCount
if matcher.node is None:
node = node.moveABitMore(0.00005)
else:
node = matcher.node.moveABitMore(0.00005)
class FileNameGenerator():
def generateFileName(self, waypointFile):
outputfile = waypointFile.replace('gpx', 'osm')
return outputfile
def main(argv):
file = ''
try:
opts, args = getopt.getopt(argv,"hf:",["file="])
except getopt.GetoptError:
print 'parseWayPoints.py -f '
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
print 'Usage: parseWayPoints.py -f '
sys.exit(0)
elif opt in ("-f", "--file"):
file = arg
if file == '':
print 'No file specified'
sys.exit(2)
print 'Parsing WayPoint file: ', file
outputfile = FileNameGenerator().generateFileName(file)
print outputfile
waypointHandler = WaypointContentHandler(outputfile)
fileHandle = io.open(file, "r")
xml.sax.parse(fileHandle, waypointHandler)
print 'Wrote ' + outputfile + ' containing ' + str(waypointHandler.nodeCount) + ' nodes.'
if __name__ == "__main__":
main(sys.argv[1:])