OSM a Python

Protože mám doma Raspberry PI, přirostl mi k srdci programovací jazyk Python. Při psaní je nutné dodržovat odsazování vnořených bloků, čímž je na druhou stranu možné zbavit se nepraktického závorkování a středníků na konci řádku. Syntaxe má také blíž k běžnému jazyku, a v neposlední řadě jsme zbaveni nutného kompilování a následného spouštění ve speciálním editoru.
Napsat program v Pythonu tedy není nijak těžké, dnes zveřejním k libovolnému užití kód, který používám k vytváření mapy na základě OSM do svého Androida. Původně byl vytvořen v Qt, které mám také velmi rád, ale postupně se mi na tak malém projektu přestala líbit složitost úprav a neustálé kompilování. Tvorba zabrala několik dlouhých nocí, než byl program vyladěn a urychlen z původní hodiny a půl běhu na 17 minut. V Pythonu se díky využití jeho základních struktur čas zkrátil dokonce na deset minut, ale pak jsme přidával další kontroly a vylepšováky, což běh o nějakou dobu protáhne. Protože jde o moje dítko a piplání mi zabralo nějaký čas, nejprve jsem jej zveřejňovat nechtěl, ale pak jsem si řekl, že s Linuxem měl taky kdosi spoustu práce… Stejně si myslím (mám tady jeden), že existují mnohem sofistikovanější skripty, které procházejí přímo databázi. Tu já ale k dispozici nemám, přestože jsem na stránky umístil reklamu, za měsíc lidé naklikali asi 5 Kč a za to si server nepořídím. Skript funguje jednoduše, nechá si pomocí osmconvert převést PBF do XML formátu, ten přelouská, najde relace s trasami KČT a přesype vybrané tagy z relace na všechny cesty, které jsou jejími členy. výsledek opět vrátí do osmconvert, který tedy musí ležet vedle skriptu, a ten jej převede zpět do PBF tak, aby se maximálně šetřilo místo na mém zaplněném disku osobního počítače.
Vytvořené mapy už je jiná záležitost, která zahrnuje všehovšudy jeden příkaz do osmosis, ale o tom třeba jindy.

#!/usr/bin/python
# -*- coding: utf-8 -*-

__author__="Speirs"

import sys, re, subprocess

#nodes = subprocess.Popen(["./osmconvert", "--drop-relations", "--drop-ways", sys.argv[1]], stdout=subprocess.PIPE)
#ways = subprocess.Popen(["./osmconvert", "--drop-relations", "--drop-nodes", sys.argv[1]], stdout=subprocess.PIPE)
mapFile = subprocess.Popen(["./osmconvert", sys.argv[1]], stdout=subprocess.PIPE)
relations = subprocess.Popen(["./osmconvert", "--drop-nodes", "--drop-ways", sys.argv[1]], stdout=subprocess.PIPE)

nodeStart = re.compile('\s*<node id="(\d+)" lat="([0-9.]*)" lon="([0-9.]*)"')
nodeEnd = re.compile('\s*</node>')
wayStart = re.compile('\s*<way id="(\d+)"\s')
wayEnd = re.compile('\s*</way>')
relationStart = re.compile('\s*<relation id="(\d+)"')
relationEnd = re.compile('\s*</relation>')

nodeID = re.compile( '\s*<nd ref="(\d+)"')
hikeRoute = re.compile('\s*<tag k=\"route\" v=\"(hiking|foot)\"')
kctRoute = re.compile('\s*<tag k=\"kct_.+\" v=\"')
bikeRoute = re.compile('\s*<tag k=\"route\" v=\"(bicycle|mtb)\"')
tag = re.compile('\s*<tag k=\"(.+)\" v=\"(.+)\"')
member = re.compile('\s*<member type="(.+)" ref="(.+)"\s')

stack = []
wayTags = {}

def compare(a, b):
        return cmp(int(a), int(b))
        
def combine(i, l):
	for key, value in l.items():
		if (key in ('name', 'ref')) and (key in wayTags[i]):
			if value not in wayTags[i][key].split(','):
				wayTags[i][key] = wayTags[i][key] + ',' + value
		else:
			wayTags[i][key] = value
        
def printTag(key, value):
        print '\t\t<tag k="{}" v="{}"/>'.format(key, value)
        
def hikingRoute(l):
	wayIds = []
	tags = {}
	kctKeys = ('kct_red', 'kct_blue', 'kct_green', 'kct_yellow')
	kctValues = ('major', 'local', 'peak', 'learning', 'ruin', 'spring', 'horse', 'bicycle', 'ski', 'wheelchair', 'interesting_object')

	for line in l:
		t = tag.match(line)
		m = member.match(line)
		if m:
			if m.group(1) == 'way':
				wayIds.append(m.group(2))
    		elif t:
    			if t.group(1) in kctKeys:
    				kct = t.group(1).split('_')
    				typ = t.group(2).split(';')
    				tags[t.group(1)] = typ[0]
    				tags['osmc'] = 'yes'
    				tags['osmc_color'] = kct[1]
    				if typ[0] in ('major', 'local', 'peak', 'learning', 'ruin', 'spring', 'horse', 'wheelchair', 'interesting_object'):
    					tags['osmc_background'] = 'white'
    				elif typ[0] == 'bicycle':
    					tags['osmc_background'] = 'yellow'
    				elif typ[0] == 'ski':
    					tags['osmc_background'] = 'orange' 					
    					
    				if typ[0] in ('major', 'bicycle', 'ski'):
    					tags['osmc_foreground'] = kct[1] + '_bar'
    				elif typ[0]== 'peak':
    					tags['osmc_foreground'] = kct[1] + '_triangle'
    				elif typ[0] == 'local':
    					tags['osmc_foreground'] = kct[1] + '_corner'
    				elif typ[0] == 'interesting_object':
    					tags['osmc_foreground'] = kct[1] + '_turned_T'
    				elif typ[0] == 'ruin':
    					tags['osmc_foreground'] = kct[1] + '_L'    					
    				elif typ[0] == 'spring':
    					tags['osmc_foreground'] = kct[1] + '_bowl'
    				elif typ[0] == 'horse':
    					tags['osmc_foreground'] = kct[1] + '_dot'
    				elif typ[0] == 'wheelchair':
    					tags['osmc_foreground'] = kct[1] + '_wheelchair'
    				elif typ[0] == 'learning':
    					tags['osmc_foreground'] = kct[1] + '_backslash'
    				
    	for i in wayIds:
    		if i in wayTags:
    			combine(i, tags)
    			#wayTags[i].update(tags)
    		else:
    			wayTags[i] = {}
    			for key, value in tags.items():
    				wayTags[i][key] = value
    	
    	tags.clear()
    	del wayIds[:]
    			
def bikingRoute(l):
	wayIds = []
	tags = {}
	for line in l:		
		t = tag.match(line)
		m = member.match(line)
		if m:
			if m.group(1) == 'way':
				wayIds.append(m.group(2))
    		elif t:
    			if t.group(1) in ('network', 'ref', 'route'):
    				tags[t.group(1)] = t.group(2)
    	
    	for i in wayIds:
    		if i in wayTags:
    			combine(i, tags)
    			#wayTags[i].update(tags)
    		else:
    			wayTags[i] = {}
    			for key, value in tags.items():
    				wayTags[i][key] = value
    	tags.clear()
    	del wayIds[:]

flag = 0
for line in relations.stdout:
	relS = relationStart.match(line)		
    	relE = relationEnd.match(line)
    	hikeR = kctRoute.match(line)
    	bikeR = bikeRoute.match(line)
    	if relS:
    	   	flag = -1
    	elif relE:
    		if flag == 1:
    			hikingRoute(stack)
    		elif flag == 2:
    			bikingRoute(stack)    		
    		del stack[:]
    		flag = 0
    	elif hikeR:
    		flag = 1
    		stack.append(line)
    	elif bikeR:
    		flag = 2
    		stack.append(line)
    	elif flag != 0:    		
    		stack.append(line)
	
id = -1
h = 0
b = 0
for line in mapFile.stdout:
	s = wayStart.match(line)
	e = wayEnd.match(line)
	hi = kctRoute.match(line)
	bi = bikeRoute.match(line)
	if s:
		id = s.group(1)
	elif hi:
		h = 1
	elif bi:
		b = 1
	elif e:
		if id != -1:
			if id in wayTags:
				for key, value in wayTags[id].items():
					if h == 0 and (value in ('major', 'local', 'peak', 'learning', 'ruin', 'spring', 'horse', 'wheelchair', 'interesting_object')):
						printTag('route', 'hiking')
						h = 1
					elif key == 'route' and value == 'bicycle':
						b = 1
					elif b == 0 and value == 'bicycle':
						printTag('route', 'bicycle')
						b = 1
					printTag(key, value)
				del wayTags[id]
				h = 0
				b = 0
		id = -1
	print line.rstrip()
	
	
exit(0)

Přidám ještě druhou upravenou verzi, která umí vytvořit turistické značky pro Slovensko, ale hlavní změna spočívá v duplikaci cest. Jednoduše vezme všechny body z původní cesty a vytvoří novou cestu pouze s tagy turistických značek. Tím se zbavím několika problémů naráz. Interní vzhled Locusu používá takové značky, že najednou může cesta nést jen jednu barvu. Dále jediný Locus má opravenou nesmyslnou chybu, při které vyhledávač tag pro renderování odsouhlasí libovolnou dvojici klíč-hodnota, kterou najde. Takže měla-li cesta najednou kct_red=major i kct_green=learning, v c:geo se bez ostychu vykreslily ikonky, jako by šlo o kct_red=learning. Výsledný soubor nabyde na velikosti jen o pár MB. Při zpracování výsledného souboru je třeba v osmosis použít –sort, protože id u cest nejsou seřazena.

#!/usr/bin/python
# -*- coding: utf-8 -*-

__author__="Speirs"

import sys, re, subprocess
from datetime import datetime

#nodes = subprocess.Popen(["./osmconvert", "--drop-relations", "--drop-ways", sys.argv[1]], stdout=subprocess.PIPE)
#ways = subprocess.Popen(["./osmconvert", "--drop-relations", "--drop-nodes", sys.argv[1]], stdout=subprocess.PIPE)
mapFile = subprocess.Popen(["./osmconvert", sys.argv[1]], stdout=subprocess.PIPE)
relations = subprocess.Popen(["./osmconvert", "--drop-nodes", "--drop-ways", sys.argv[1]], stdout=subprocess.PIPE)

nodeStart = re.compile('\s*')
wayStart = re.compile('\s*')
relationStart = re.compile('\s*')

nodeID = re.compile( '\s*'.format(key, value)
        
def hikingRoute(l):
	wayIds = []
	tags = {}
	kctKeys = ('kct_red', 'kct_blue', 'kct_green', 'kct_yellow')
	kctValues = ('major', 'local', 'peak', 'learning', 'ruin', 'spring', 'horse', 'bicycle', 'ski', 'wheelchair', 'interesting_object')

	for line in l:
		t = tag.match(line)
		m = member.match(line)
		if m:
			if m.group(1) == 'way':
				wayIds.append(long(m.group(2)))
    		elif t:
    			if t.group(1) in kctKeys:
    				kct = t.group(1).split('_')
    				typ = t.group(2).split(';')
    				tags[t.group(1)] = typ[0]
    				tags['osmc'] = 'yes'
    				tags['osmc_color'] = kct[1]
    				if typ[0] in ('major', 'local', 'peak', 'learning', 'ruin', 'spring', 'horse', 'wheelchair', 'interesting_object'):
    					tags['osmc_background'] = 'white'
    				elif typ[0] == 'bicycle':
    					tags['osmc_background'] = 'yellow'
    				elif typ[0] == 'ski':
    					tags['osmc_background'] = 'orange' 					
    					
    				if typ[0] in ('major', 'bicycle', 'ski'):
    					tags['osmc_foreground'] = kct[1] + '_bar'
    				elif typ[0]== 'peak':
    					tags['osmc_foreground'] = kct[1] + '_triangle'
    				elif typ[0] == 'local':
    					tags['osmc_foreground'] = kct[1] + '_corner'
    				elif typ[0] == 'interesting_object':
    					tags['osmc_foreground'] = kct[1] + '_turned_T'
    				elif typ[0] == 'ruin':
    					tags['osmc_foreground'] = kct[1] + '_L'    					
    				elif typ[0] == 'spring':
    					tags['osmc_foreground'] = kct[1] + '_bowl'
    				elif typ[0] == 'horse':
    					tags['osmc_foreground'] = kct[1] + '_dot'
    				elif typ[0] == 'wheelchair':
    					tags['osmc_foreground'] = kct[1] + '_wheelchair'
    				elif typ[0] == 'learning':
    					tags['osmc_foreground'] = kct[1] + '_backslash'
    			elif t.group(1) in ('network', 'ref', 'route'):
    				tags[t.group(1)] = t.group(2)
    				
    	for i in wayIds:
    		if i in wayTags:
    			wayTags[i].append(tags.copy())
    		else:
    			wayTags[i] = []
    			wayTags[i].append(tags.copy())
    	tags.clear()
    	del wayIds[:]

def hikeRoute(l):
	wayIds = []
	tags = {}
	kstKeys = ('red', 'blue', 'green', 'yellow')
	kstValues = ('major', 'local', 'peak', 'education', 'ruin', 'spring', 'interesting_object')
	barva = ''
	symbol = ''

	for line in l:
		t = tag.match(line)
		m = member.match(line)
		
		if m:
			if m.group(1) == 'way':
				wayIds.append(long(m.group(2)))
    		elif t:
    			if t.group(1) in ('colour'):
    				tags['osmc'] = 'yes'
    				barva = t.group(2)
    				tags['osmc_color'] = t.group(2)
    			elif t.group(1) in ('network', 'ref', 'route'):
    				tags[t.group(1)] = t.group(2)    			
    			elif t.group(1) in ('symbol'):
    				if t.group(2) in kstValues:
    					tags['osmc_background'] = 'white'
    					symbol = t.group(2)
    					
    	if barva == '' or symbol == '':
    		tags.clear()
    		del wayIds[:]
    		return
    		
    	
    	if symbol == 'educational':
    		tags['kct_'+barva] = 'learning'
    	else:
    		tags['kct_'+barva] = symbol
    		
    	if symbol == 'major':
    		tags['osmc_foreground'] = barva + '_bar'
    	elif symbol == 'peak':
    		tags['osmc_foreground'] = barva + '_triangle'
    	elif symbol == 'local':
    		tags['osmc_foreground'] = barva + '_corner'
    	elif symbol == 'interesting_object':
    		tags['osmc_foreground'] = barva + '_turned_T'
    	elif symbol == 'ruin':
    		tags['osmc_foreground'] = barva + '_L'    					
    	elif symbol == 'spring':
    		tags['osmc_foreground'] = barva + '_bowl'
    	elif symbol == 'roundtrip':
    		tags['osmc_foreground'] = barva + '_dot'
    	elif symbol == 'education':
    		tags['osmc_foreground'] = barva + '_backslash'
    				
    	for i in wayIds:
    		if i in wayTags:
    			wayTags[i].append(tags.copy())
    		else:
    			wayTags[i] = []
    			wayTags[i].append(tags.copy())
    	tags.clear()
    	del wayIds[:]
    	
def bikingRoute(l):
	wayIds = []
	tags = {}
	for line in l:		
		t = tag.match(line)
		m = member.match(line)
		if m:
			if m.group(1) == 'way':
				wayIds.append(long(m.group(2)))
    		elif t:
    			if t.group(1) in ('network', 'ref', 'route'):
    				tags[t.group(1)] = t.group(2)
    	
    	for i in wayIds:
    		if i in wayTags:
    			wayTags[i].append(tags.copy())
    		else:
    			wayTags[i] = []
    			wayTags[i].append(tags.copy())
    	tags.clear()
    	del wayIds[:]

flag = 0
for line in relations.stdout:
	relS = relationStart.match(line)		
    	relE = relationEnd.match(line)
    	hikeR = kctRoute.match(line)
    	kstR = kstRoute.match(line)
    	bikeR = bikeRoute.match(line)
    	if relS:
    	   	flag = -1
    	elif relE:
    		if flag == 1:
    			hikingRoute(stack)
    		elif flag == 2:
    			bikingRoute(stack)
    		elif flag == 3:
    			hikeRoute(stack)
    		del stack[:]
    		flag = 0
    	elif hikeR:
    		flag = 1
    		stack.append(line)
    	elif bikeR:
    		flag = 2
    		stack.append(line)
    	elif kstR:
    		flag = 3
    		stack.append(line)
    	elif flag != 0:    		
    		stack.append(line)
	
id = -1
nid = 50000000000
del stack[:]
datum = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')

for line in mapFile.stdout:
	s = wayStart.match(line)
	e = wayEnd.match(line)
	n = nodeID.match(line)
	if s:
		id = long(s.group(1))
	elif n and id != -1:
		stack.append(line)
	elif e and id != -1:
		if id in wayTags:
			for i in wayTags[id]:
				print '\t'
				print '\t'.format(nid, datum)
				for li in stack:
					print li.rstrip()			
				for key, value in i.items():
					printTag(key, value)
				nid += 1
			del wayTags[id]
		del stack[:]
		id = -1
	print line.rstrip()
	
exit(0)

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *