1 -- Nominatim themepark theme.
 
   3 -- The Nominatim theme creates a fixed set of import tables for use with
 
   4 -- Nominatim. Creation and object processing are directly controlled by
 
   5 -- the theme. Topics provide preset configurations. You should add exactly
 
   6 -- one topic to your project.
 
   8 -- The theme also exports a number of functions that can be used to configure
 
   9 -- its behaviour. These may be directly called in the style file after
 
  10 -- importing the theme:
 
  12 --      local nominatim = themepark:init_theme('nominatim')
 
  13 --      nominatim.set_main_tags{boundary = 'always'}
 
  15 -- This allows to write your own configuration from scratch. You can also
 
  16 -- use it to customize topics. In that case, first add the topic, then
 
  17 -- change the configuration:
 
  19 --      themepark:add_topic('nominatim/full')
 
  20 --      local nominatim = themepark:init_theme('nominatim')
 
  21 --      nominatim.ignore_tags{'amenity'}
 
  25 local PRE_DELETE = nil
 
  26 local PRE_EXTRAS = nil
 
  27 local POST_DELETE = nil
 
  30 local ADDRESS_TAGS = nil
 
  31 local SAVE_EXTRA_MAINS = false
 
  32 local POSTCODE_FALLBACK = true
 
  34 -- This file can also be directly require'd instead of running it under
 
  35 -- the themepark framework. In that case the first parameter is usually
 
  36 -- the module name. Lets check for that, so that further down we can call
 
  37 -- the low-level osm2pgsql functions instead of themepark functions.
 
  39 if type(themepark) ~= 'table' then
 
  43 -- tables required for taginfo
 
  44 module.TAGINFO_MAIN = {keys = {}, delete_tags = {}}
 
  45 module.TAGINFO_NAME_KEYS = {}
 
  46 module.TAGINFO_ADDRESS_KEYS = {}
 
  49 -- The single place table.
 
  50 local place_table_definition = {
 
  52     ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
 
  54         { column = 'class', type = 'text', not_null = true },
 
  55         { column = 'type', type = 'text', not_null = true },
 
  56         { column = 'admin_level', type = 'smallint' },
 
  57         { column = 'name', type = 'hstore' },
 
  58         { column = 'address', type = 'hstore' },
 
  59         { column = 'extratags', type = 'hstore' },
 
  60         { column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true },
 
  62     data_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_DATA"),
 
  63     index_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_INDEX"),
 
  70     themepark:add_table(place_table_definition)
 
  71     insert_row = function(columns)
 
  72         themepark:insert('place', columns, {}, {})
 
  75     local place_table = osm2pgsql.define_table(place_table_definition)
 
  76     insert_row = function(columns)
 
  77         place_table:insert(columns)
 
  81 ------------ Geometry functions for relations ---------------------
 
  83 function module.relation_as_multipolygon(o)
 
  84     return o:as_multipolygon()
 
  87 function module.relation_as_multiline(o)
 
  88     return o:as_multilinestring():line_merge()
 
  92 module.RELATION_TYPES = {
 
  93     multipolygon = module.relation_as_multipolygon,
 
  94     boundary = module.relation_as_multipolygon,
 
  95     waterway = module.relation_as_multiline
 
  98 ------------- Place class ------------------------------------------
 
 101 Place.__index = Place
 
 103 function Place.new(object, geom_func)
 
 104     local self = setmetatable({}, Place)
 
 106     self.geom_func = geom_func
 
 108     self.admin_level = tonumber(self.object:grab_tag('admin_level'))
 
 109     if self.admin_level == nil
 
 110        or self.admin_level <= 0 or self.admin_level > 15
 
 111        or math.floor(self.admin_level) ~= self.admin_level then
 
 112         self.admin_level = 15
 
 116     self.has_name = false
 
 124 function Place:clean(data)
 
 125     for k, v in pairs(self.object.tags) do
 
 126         if data.delete ~= nil and data.delete(k, v) then
 
 127             self.object.tags[k] = nil
 
 128         elseif data.extra ~= nil and data.extra(k, v) then
 
 129             self.extratags[k] = v
 
 130             self.object.tags[k] = nil
 
 135 function Place:delete(data)
 
 136     if data.match ~= nil then
 
 137         for k, v in pairs(self.object.tags) do
 
 138             if data.match(k, v) then
 
 139                 self.object.tags[k] = nil
 
 145 function Place:grab_extratags(data)
 
 148     if data.match ~= nil then
 
 149         for k, v in pairs(self.object.tags) do
 
 150             if data.match(k, v) then
 
 151                 self.object.tags[k] = nil
 
 152                 self.extratags[k] = v
 
 161 local function strip_address_prefix(k)
 
 162     if k:sub(1, 5) == 'addr:' then
 
 166     if k:sub(1, 6) == 'is_in:' then
 
 174 function Place:grab_address_parts(data)
 
 177     if data.groups ~= nil then
 
 178         for k, v in pairs(self.object.tags) do
 
 179             local atype = data.groups(k, v)
 
 182                 if atype == 'main' then
 
 184                     self.address[strip_address_prefix(k)] = v
 
 186                 elseif atype == 'extra' then
 
 187                     self.address[strip_address_prefix(k)] = v
 
 189                     self.address[atype] = v
 
 191                 self.object.tags[k] = nil
 
 200 function Place:grab_name_parts(data)
 
 203     if data.groups ~= nil then
 
 204         for k, v in pairs(self.object.tags) do
 
 205             local atype = data.groups(k, v)
 
 209                 self.object.tags[k] = nil
 
 210                 if atype == 'main' then
 
 212                 elseif atype == 'house' then
 
 214                     fallback = {'place', 'house', 'always'}
 
 224 function Place:write_place(k, v, mtype, save_extra_mains)
 
 229     v = v or self.object.tags[k]
 
 234     if type(mtype) == 'table' then
 
 235         mtype = mtype[v] or mtype[1]
 
 238     if mtype == 'always' or (self.has_name and mtype == 'named') then
 
 239         return self:write_row(k, v, save_extra_mains)
 
 242     if mtype == 'named_with_key' then
 
 244         local prefix = k .. ':name'
 
 245         for namek, namev in pairs(self.object.tags) do
 
 246             if namek:sub(1, #prefix) == prefix
 
 247                and (#namek == #prefix
 
 248                     or namek:sub(#prefix + 1, #prefix + 1) == ':') then
 
 249                 names[namek:sub(#k + 2)] = namev
 
 253         if next(names) ~= nil then
 
 254             local saved_names = self.names
 
 257             local results = self:write_row(k, v, save_extra_mains)
 
 259             self.names = saved_names
 
 268 function Place:write_row(k, v, save_extra_mains)
 
 269     if self.geometry == nil then
 
 270         self.geometry = self.geom_func(self.object)
 
 272     if self.geometry:is_null() then
 
 276     if save_extra_mains ~= nil then
 
 277         for extra_k, extra_v in pairs(self.object.tags) do
 
 278             if extra_k ~= k and save_extra_mains(extra_k, extra_v) then
 
 279                 self.extratags[extra_k] = extra_v
 
 287         admin_level = self.admin_level,
 
 288         name = next(self.names) and self.names,
 
 289         address = next(self.address) and self.address,
 
 290         extratags = next(self.extratags) and self.extratags,
 
 291         geometry = self.geometry
 
 294     if save_extra_mains then
 
 295         for tk, tv in pairs(self.object.tags) do
 
 296             if save_extra_mains(tk, tv) then
 
 297                 self.extratags[tk] = nil
 
 302     self.num_entries = self.num_entries + 1
 
 308 function module.tag_match(data)
 
 309     if data == nil or next(data) == nil then
 
 313     local fullmatches = {}
 
 314     local key_prefixes = {}
 
 315     local key_suffixes = {}
 
 317     if data.keys ~= nil then
 
 318         for _, key in pairs(data.keys) do
 
 319             if key:sub(1, 1) == '*' then
 
 321                     if key_suffixes[#key - 1] == nil then
 
 322                         key_suffixes[#key - 1] = {}
 
 324                     key_suffixes[#key - 1][key:sub(2)] = true
 
 326             elseif key:sub(#key, #key) == '*' then
 
 327                 if key_prefixes[#key - 1] == nil then
 
 328                     key_prefixes[#key - 1] = {}
 
 330                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = true
 
 332                 fullmatches[key] = true
 
 337     if data.tags ~= nil then
 
 338         for k, vlist in pairs(data.tags) do
 
 339             if fullmatches[k] == nil then
 
 341                 for _, v in pairs(vlist) do
 
 342                     fullmatches[k][v] = true
 
 348     return function (k, v)
 
 349         if fullmatches[k] ~= nil and (fullmatches[k] == true or fullmatches[k][v] ~= nil) then
 
 353         for slen, slist in pairs(key_suffixes) do
 
 354             if #k >= slen and slist[k:sub(-slen)] ~= nil then
 
 359         for slen, slist in pairs(key_prefixes) do
 
 360             if #k >= slen and slist[k:sub(1, slen)] ~= nil then
 
 370 function module.tag_group(data)
 
 371     if data == nil or next(data) == nil then
 
 375     local fullmatches = {}
 
 376     local key_prefixes = {}
 
 377     local key_suffixes = {}
 
 379     for group, tags in pairs(data) do
 
 380         for _, key in pairs(tags) do
 
 381             if key:sub(1, 1) == '*' then
 
 383                     if key_suffixes[#key - 1] == nil then
 
 384                         key_suffixes[#key - 1] = {}
 
 386                     key_suffixes[#key - 1][key:sub(2)] = group
 
 388             elseif key:sub(#key, #key) == '*' then
 
 389                 if key_prefixes[#key - 1] == nil then
 
 390                     key_prefixes[#key - 1] = {}
 
 392                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = group
 
 394                 fullmatches[key] = group
 
 399     return function (k, v)
 
 400         local val = fullmatches[k]
 
 405         for slen, slist in pairs(key_suffixes) do
 
 407                 val = slist[k:sub(-slen)]
 
 414         for slen, slist in pairs(key_prefixes) do
 
 416                 val = slist[k:sub(1, slen)]
 
 425 -- Returns prefix part of the keys, and reject suffix matching keys
 
 426 local function process_key(key)
 
 427     if key:sub(1, 1) == '*' then
 
 430     if key:sub(#key, #key) == '*' then
 
 431         return key:sub(1, #key - 2)
 
 436 -- Process functions for all data types
 
 437 function module.process_node(object)
 
 439     local function geom_func(o)
 
 443     module.process_tags(Place.new(object, geom_func))
 
 446 function module.process_way(object)
 
 448     local function geom_func(o)
 
 449         local geom = o:as_polygon()
 
 451         if geom:is_null() then
 
 452             geom = o:as_linestring()
 
 458     module.process_tags(Place.new(object, geom_func))
 
 461 function module.process_relation(object)
 
 462     local geom_func = module.RELATION_TYPES[object.tags.type]
 
 464     if geom_func ~= nil then
 
 465         module.process_tags(Place.new(object, geom_func))
 
 469 -- The process functions are used by default by osm2pgsql.
 
 471     themepark:add_proc('node', module.process_node)
 
 472     themepark:add_proc('way', module.process_way)
 
 473     themepark:add_proc('relation', module.process_relation)
 
 475     osm2pgsql.process_node = module.process_node
 
 476     osm2pgsql.process_way = module.process_way
 
 477     osm2pgsql.process_relation = module.process_relation
 
 480 function module.process_tags(o)
 
 481     o:clean{delete = PRE_DELETE, extra = PRE_EXTRAS}
 
 483     -- Exception for boundary/place double tagging
 
 484     if o.object.tags.boundary == 'administrative' then
 
 485         o:grab_extratags{match = function (k, v)
 
 486             return k == 'place' and v:sub(1,3) ~= 'isl'
 
 491     local fallback = o:grab_name_parts{groups=NAMES}
 
 494     if o:grab_address_parts{groups=ADDRESS_TAGS} > 0 and fallback == nil then
 
 495         fallback = {'place', 'house', 'always'}
 
 497     if o.address.country ~= nil and #o.address.country ~= 2 then
 
 498         o.address['country'] = nil
 
 500     if POSTCODE_FALLBACK and fallback == nil and o.address.postcode ~= nil then
 
 501         fallback = {'place', 'postcode', 'always'}
 
 504     if o.address.interpolation ~= nil then
 
 505         o:write_place('place', 'houses', 'always', SAVE_EXTRA_MAINS)
 
 509     o:clean{delete = POST_DELETE}
 
 512     for k, v in pairs(o.object.tags) do
 
 513         local ktype = MAIN_KEYS[k]
 
 514         if ktype == 'fallback' then
 
 516                 fallback = {k, v, 'named'}
 
 518         elseif ktype ~= nil then
 
 519             o:write_place(k, v, MAIN_KEYS[k], SAVE_EXTRA_MAINS)
 
 523     if fallback ~= nil and o.num_entries == 0 then
 
 524         o:write_place(fallback[1], fallback[2], fallback[3], SAVE_EXTRA_MAINS)
 
 528 --------- Convenience functions for simple style configuration -----------------
 
 531 function module.set_prefilters(data)
 
 532     PRE_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
 
 533     PRE_EXTRAS = module.tag_match{keys = data.extra_keys,
 
 534                                   tags = data.extra_tags}
 
 535     module.TAGINFO_MAIN.delete_tags = data.delete_tags
 
 538 function module.set_main_tags(data)
 
 541     for k, _ in pairs(data) do
 
 542         table.insert(keys, k)
 
 544     module.TAGINFO_MAIN.keys = keys
 
 547 function module.set_name_tags(data)
 
 548     NAMES = module.tag_group(data)
 
 550     for _, lst in pairs(data) do
 
 551         for _, k in ipairs(lst) do
 
 552             local key = process_key(k)
 
 554                 module.TAGINFO_NAME_KEYS[key] = true
 
 560 function module.set_address_tags(data)
 
 561     if data.postcode_fallback ~= nil then
 
 562         POSTCODE_FALLBACK = data.postcode_fallback
 
 563         data.postcode_fallback = nil
 
 565     ADDRESS_TAGS = module.tag_group(data)
 
 567     for _, lst in pairs(data) do
 
 569             for _, k in ipairs(lst) do
 
 570                 local key = process_key(k)
 
 572                     module.TAGINFO_ADDRESS_KEYS[key] = true
 
 579 function module.set_unused_handling(data)
 
 580     if data.extra_keys == nil and data.extra_tags == nil then
 
 581         POST_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
 
 582         SAVE_EXTRA_MAINS = function() return true end
 
 583     elseif data.delete_keys == nil and data.delete_tags == nil then
 
 585         SAVE_EXTRA_MAINS = module.tag_match{keys = data.extra_keys, tags = data.extra_tags}
 
 587         error("unused handler can have only 'extra_keys' or 'delete_keys' set.")
 
 591 function module.set_relation_types(data)
 
 592     module.RELATION_TYPES = {}
 
 594         if v == 'multipolygon' then
 
 595             module.RELATION_TYPES[k] = module.relation_as_multipolygon
 
 596         elseif v == 'multiline' then
 
 597             module.RELATION_TYPES[k] = module.relation_as_multiline