2

I looked at source code of Nominatim and it's look like that mostly of it's functionality is just preparing SQL query and formating it's result.

And possible other geo(de)coders works in same way. Can I use direct SQL queries to get data from DB?

Where I can find examples of them? I would like to get place name from coordinates.

Dmitry Bubnenkov
  • 9,415
  • 19
  • 85
  • 145

1 Answers1

3

Yes, it is possible.

I tried it a few years ago and came up with a simple, crude geocoder you can find on Github: https://github.com/plechi/mini-geocoder

You need a PostgreSQL-Server with installed PostGIS and HSTORE extensions.

Create a new Postgres Database.

Enable PostGIS and HSTORE extensions:

CREATE EXTENSION postgis;  
CREATE EXTENSION hstore;  

After that you need Osmosis and a dataset from Openstreetmap, look in the OSM-Wiki for details: http://wiki.openstreetmap.org/wiki/Downloading_data

Create the database schema from Osmosis (run commands in the terminal):

psql -d <database> -f <osmosis-folder>/script/pgsnapshot_schema_0.6.sql

Import the data (also execute in the terminal):

osmosis --read-xml file="<downloaded_datafile>.osm" --write-apidb host="<dbhost>" database="<dbname>" user="<dbuser>" password="<dbpassword>"

Theoretically, you could query the resulting database (examine the schema for details).

For my geocoder, I created a "optimization table" for easier queries:

CREATE TABLE geocode_optimized
AS SELECT
     w.tags -> 'addr:street'                            AS street,
     w.tags -> 'addr:housenumber'                       AS housenumber,
     w.tags -> 'addr:postcode'                          AS postcode,
     w.tags -> 'addr:city'                              AS city,
     w.tags -> 'addr:country'                           AS country,
     AVG(ST_X(n.geom))                                  AS longitude,
     AVG(ST_Y(n.geom))                                  AS latitude,
     to_tsvector(concat_ws(' ', w.tags -> 'addr:street',
                           w.tags -> 'addr:housenumber',
                           w.tags -> 'addr:postcode',
                           w.tags -> 'addr:city',
                           w.tags -> 'addr:country'
                 ))                                     AS full_text,
     st_makepoint(AVG(ST_X(n.geom)), AVG(ST_Y(n.geom))) AS point
   FROM ways w
     INNER JOIN way_nodes wn ON w.id = wn.way_id
     INNER JOIN nodes n ON n.id = wn.node_id
   WHERE exist(w.tags, 'addr:housenumber') AND exist(w.tags, 'addr:street')
   GROUP BY housenumber, street, postcode, city, country;

CREATE INDEX idx_geocode_full_text ON geocode_optimized USING GIN (full_text);

Geocode (address to coordinates):

SELECT 
   street, 
   housenumber, 
   postcode, 
   city, 
   country, 
   longitude, 
   latitude 
FROM geocode_optimized 
WHERE full_text @@ plainto_tsquery('YOUR ADDRESS') 

Reverse geocode (coordinates to address)

SELECT 
    street, 
    housenumber, 
    postcode, 
    city, 
    country, 
    longitude, 
    latitude, 
    CAST (st_distance_sphere(st_makepoint(longitude,latitude), st_makepoint('<longitude>','<latitude>')) AS FLOAT) as distance 
FROM geocode_optimized;

As mentioned, this is quite crude and it is probably not the most performant solution.

Plechi
  • 358
  • 2
  • 10