Geokódování

Z FreeGIS portál
Přejít na: navigace, hledání

Tato stránka shrnuje poznámky k procesu geokódování, pomocí kterého se textovým adresním záznamům v tabulce/databázi přiřadí prostorové souřadnice X,Y s využitím uliční sítě.

Rozlišujeme:

  • dopředné geokódování (forward geocoding), kdy na základě adres míst určujeme prostorové souřadnice
  • zpětné geokódování (backward geocoding), kdy naopak na základě lokalizace geoprvků odvozujeme např. jejich adresy v uliční síti

Existuje několik geokódovacích služeb. Mezi nejrozšířenější patří Nominatim, který je založen na datech z projektu OpenStreetMap.

Příklad skriptu pro Nominatim

Níže uvedený skript na základě adres uložených v databázi odvodí souřadnice jednotlivých prvků v souřadnicovém systému WGS-84 (EPSG 4326) a uloží je do dané tabulky (sloupce lat, long). Kvalitu geokódování uloží do sloupce level jako hodnoty:

  • 0 - geokódováno na ulici, čp, část obce a obec
  • 1 - geokódováno na ulici část obce a obec
  • 2 - geokódováno na část obce a obec
  • 3 - geokódováno na obec
  • 4 - geokódováno na obec (první část před čárkou)

Příklad vstupních dat v DB:

 id1 |    ulice    | cp_co |  cast_obce  |      obec      |  psc  
-----+-------------+-------+-------------+----------------+-------
   1 | Rákosníkova | 162   | Neveklov    | Neveklov       | 25756
   2 | Lesní cesta | 236/5 | Klíše       | Ústí nad Labem | 40001
   3 | Svojsíkova  | 833   | Český Těšín | Český Těšín 1  | 73701
   4 | Vídeňská    | 9     | Štýřice     | Brno 39        | 63900
   5 | Hradská-KD  | 723/8 | Loštice     | Loštice        | 78983
...

Spuštění skriptu:

python chess.py
1 -> 49.7541028,14.5363398 (0)
2 -> 50.6730414,14.0141232 (0)
3 -> 49.74285935,18.6228764915793 (3)
4 -> 49.1817338,16.5951966 (1)
5 -> 49.7449432,16.9281353 (3)
...

Skript vypisuje na standardní výstup informaci o id -> lat,long (level). Výsledek je SQL dávka, kterou můžeme nahrát do databáze.

V našem případě může výsledná dávka vypadat následovně:

BEGIN;
update tabulka set lat=49.7541028,lon=14.5363398,level=0 where id1=1;
update tabulka set lat=50.6730414,lon=14.0141232,level=0 where id1=2;
update tabulka set lat=49.7428593,lon=18.6228764,level=3 where id1=3;
update tabulka set lat=49.1817338,lon=16.5951966,level=1 where id1=4;
update tabulka set lat=49.7449432,lon=16.9281353,level=3 where id1=5;
...
COMMIT;

Výsledek nahrajeme do databáze:

psql -d databaze -f davka.sql

Příklad výsledku geokódování:

 id1 |    ulice    | cp_co |  cast_obce  |      obec      |  psc  |     lat     |       lon        | level 
-----+-------------+-------+-------------+----------------+-------+-------------+------------------+-------
   1 | Rákosníkova | 162   | Neveklov    | Neveklov       | 25756 |  49.7541028 |       14.5363398 |     0
   2 | Lesní cesta | 236/5 | Klíše       | Ústí nad Labem | 40001 |  50.6730414 |       14.0141232 |     0
   3 | Svojsíkova  | 833   | Český Těšín | Český Těšín 1  | 73701 |  49.7428593 |       18.6228764 |     3
   4 | Vídeňská    | 9     | Štýřice     | Brno 39        | 63900 |  49.1817338 |       16.5951966 |     1
   5 | Hradská-KD  | 723/8 | Loštice     | Loštice        | 78983 |  49.7449432 |       16.9281353 |     3

Ukázka skriptu (Python)

import psycopg2
import urllib2
import json
import time
import sys
import os

# nazev databaze
DB='databaze'
# sloupce v tabulce obsahujici klic, ulici, c.p., cast obce a obec
COLUMNS = ['id1', 'ulice', 'cp_co', 'cast_obce', 'obec']
# nazev tabulky
TABLE='tabulka'
# cesta k vystupnimu souboru
OUTPUT='davka.sql'

def get_data(db, table, columns):
    conn = psycopg2.connect("dbname=%s" % db)
    cur = conn.cursor()

    cur.execute("SELECT %s FROM %s WHERE %s IS NOT NULL ORDER BY %s" % (','.join(columns), table,
                                                                        columns[-1], columns[0]))
    data = []
    for rec in cur.fetchall():
        row = {}
        for idx in range(len(columns)):
            row[columns[idx]] = rec[idx]
        data.append(row)
        
    cur.close()
    conn.close()
    
    return data

def geocode(data, key, output, ocols=('lat', 'lon', 'level')):
    fd = open(output, 'w')
    fd.write('BEGIN;%s' % os.linesep)
    for addr in data:
        level=0
        while True:
            try:
                data = json.loads(try_it(addr, level))[0]
            except:
                level += 1
            break

        lat = data['lat']
        lon = data['lon']
        
        sys.stdout.write("%s -> %s,%s (%s)%s" % (addr[key], lat, lon, level, os.linesep))
        fd.write('update chess_oddil2 set %s=%s,%s=%s,%s=%d where %s=%s;%s' % \
                 (ocols[0], lat, ocols[1], lon, ocols[2], level, key, addr[key], os.linesep))
        
    fd.write('COMMIT;%s' % os.linesep)
    fd.close()
        
def try_it(addr, level=0):
    qstring = ''
    if level < 2 and addr['ulice']:
        qstring += addr['ulice'].replace(' ', '+')
        if level == 0 and addr['cp_co']:
            if '/' in addr['cp_co']:
                qstring += '+' + addr['cp_co'].split('/')[0]
            else:
                qstring += '+' + addr['cp_co']
        qstring += '+' + addr['obec'].replace(' ', '+')
    else:
        if level < 3:
            qstring += addr['obec'].replace(' ', '+')
        else:
            if ',' in addr['obec']:
                qstring += addr['obec'].split(',')[0].replace(' ', '+')
            else:
                qstring += addr['obec'].split(' ', 1)[0]
    url = 'http://nominatim.openstreetmap.org/search?q=' + qstring + '&format=json&limit=1'
    
    response = urllib2.urlopen(url)
    jsondata = response.read()
    
    return jsondata
    
if __name__ == "__main__":
    geocode(data=get_data(DB, TABLE, COLUMNS), key=COLUMNS[0], output=OUTPUT)
    sys.exit(0)