1 -- Core functions for Nominatim import flex style.
10 local ADDRESS_TAGS = nil
11 local SAVE_EXTRA_MAINS = false
12 local POSTCODE_FALLBACK = true
14 -- tables required for taginfo
15 module.TAGINFO_MAIN = {keys = {}, delete_tags = {}}
16 module.TAGINFO_NAME_KEYS = {}
17 module.TAGINFO_ADDRESS_KEYS = {}
20 -- The single place table.
21 local place_table = osm2pgsql.define_table{
23 ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
25 { column = 'class', type = 'text', not_null = true },
26 { column = 'type', type = 'text', not_null = true },
27 { column = 'admin_level', type = 'smallint' },
28 { column = 'name', type = 'hstore' },
29 { column = 'address', type = 'hstore' },
30 { column = 'extratags', type = 'hstore' },
31 { column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true },
36 ------------ Geometry functions for relations ---------------------
38 function module.relation_as_multipolygon(o)
39 return o:as_multipolygon()
42 function module.relation_as_multiline(o)
43 return o:as_multilinestring():line_merge()
47 module.RELATION_TYPES = {
48 multipolygon = module.relation_as_multipolygon,
49 boundary = module.relation_as_multipolygon,
50 waterway = module.relation_as_multiline
53 ------------- Place class ------------------------------------------
58 function Place.new(object, geom_func)
59 local self = setmetatable({}, Place)
61 self.geom_func = geom_func
63 self.admin_level = tonumber(self.object:grab_tag('admin_level'))
64 if self.admin_level == nil
65 or self.admin_level <= 0 or self.admin_level > 15
66 or math.floor(self.admin_level) ~= self.admin_level then
79 function Place:clean(data)
80 for k, v in pairs(self.object.tags) do
81 if data.delete ~= nil and data.delete(k, v) then
82 self.object.tags[k] = nil
83 elseif data.extra ~= nil and data.extra(k, v) then
85 self.object.tags[k] = nil
90 function Place:delete(data)
91 if data.match ~= nil then
92 for k, v in pairs(self.object.tags) do
93 if data.match(k, v) then
94 self.object.tags[k] = nil
100 function Place:grab_extratags(data)
103 if data.match ~= nil then
104 for k, v in pairs(self.object.tags) do
105 if data.match(k, v) then
106 self.object.tags[k] = nil
107 self.extratags[k] = v
116 local function strip_address_prefix(k)
117 if k:sub(1, 5) == 'addr:' then
121 if k:sub(1, 6) == 'is_in:' then
129 function Place:grab_address_parts(data)
132 if data.groups ~= nil then
133 for k, v in pairs(self.object.tags) do
134 local atype = data.groups(k, v)
137 if atype == 'main' then
139 self.address[strip_address_prefix(k)] = v
141 elseif atype == 'extra' then
142 self.address[strip_address_prefix(k)] = v
144 self.address[atype] = v
146 self.object.tags[k] = nil
155 function Place:grab_name_parts(data)
158 if data.groups ~= nil then
159 for k, v in pairs(self.object.tags) do
160 local atype = data.groups(k, v)
164 self.object.tags[k] = nil
165 if atype == 'main' then
167 elseif atype == 'house' then
169 fallback = {'place', 'house', 'always'}
179 function Place:write_place(k, v, mtype, save_extra_mains)
184 v = v or self.object.tags[k]
189 if type(mtype) == 'table' then
190 mtype = mtype[v] or mtype[1]
193 if mtype == 'always' or (self.has_name and mtype == 'named') then
194 return self:write_row(k, v, save_extra_mains)
197 if mtype == 'named_with_key' then
199 local prefix = k .. ':name'
200 for namek, namev in pairs(self.object.tags) do
201 if namek:sub(1, #prefix) == prefix
202 and (#namek == #prefix
203 or namek:sub(#prefix + 1, #prefix + 1) == ':') then
204 names[namek:sub(#k + 2)] = namev
208 if next(names) ~= nil then
209 local saved_names = self.names
212 local results = self:write_row(k, v, save_extra_mains)
214 self.names = saved_names
223 function Place:write_row(k, v, save_extra_mains)
224 if self.geometry == nil then
225 self.geometry = self.geom_func(self.object)
227 if self.geometry:is_null() then
231 if save_extra_mains ~= nil then
232 for extra_k, extra_v in pairs(self.object.tags) do
233 if extra_k ~= k and save_extra_mains(extra_k, extra_v) then
234 self.extratags[extra_k] = extra_v
242 admin_level = self.admin_level,
243 name = next(self.names) and self.names,
244 address = next(self.address) and self.address,
245 extratags = next(self.extratags) and self.extratags,
246 geometry = self.geometry
249 if save_extra_mains then
250 for k, v in pairs(self.object.tags) do
251 if save_extra_mains(k, v) then
252 self.extratags[k] = nil
257 self.num_entries = self.num_entries + 1
263 function module.tag_match(data)
264 if data == nil or next(data) == nil then
268 local fullmatches = {}
269 local key_prefixes = {}
270 local key_suffixes = {}
272 if data.keys ~= nil then
273 for _, key in pairs(data.keys) do
274 if key:sub(1, 1) == '*' then
276 if key_suffixes[#key - 1] == nil then
277 key_suffixes[#key - 1] = {}
279 key_suffixes[#key - 1][key:sub(2)] = true
281 elseif key:sub(#key, #key) == '*' then
282 if key_prefixes[#key - 1] == nil then
283 key_prefixes[#key - 1] = {}
285 key_prefixes[#key - 1][key:sub(1, #key - 1)] = true
287 fullmatches[key] = true
292 if data.tags ~= nil then
293 for k, vlist in pairs(data.tags) do
294 if fullmatches[k] == nil then
296 for _, v in pairs(vlist) do
297 fullmatches[k][v] = true
303 return function (k, v)
304 if fullmatches[k] ~= nil and (fullmatches[k] == true or fullmatches[k][v] ~= nil) then
308 for slen, slist in pairs(key_suffixes) do
309 if #k >= slen and slist[k:sub(-slen)] ~= nil then
314 for slen, slist in pairs(key_prefixes) do
315 if #k >= slen and slist[k:sub(1, slen)] ~= nil then
325 function module.tag_group(data)
326 if data == nil or next(data) == nil then
330 local fullmatches = {}
331 local key_prefixes = {}
332 local key_suffixes = {}
334 for group, tags in pairs(data) do
335 for _, key in pairs(tags) do
336 if key:sub(1, 1) == '*' then
338 if key_suffixes[#key - 1] == nil then
339 key_suffixes[#key - 1] = {}
341 key_suffixes[#key - 1][key:sub(2)] = group
343 elseif key:sub(#key, #key) == '*' then
344 if key_prefixes[#key - 1] == nil then
345 key_prefixes[#key - 1] = {}
347 key_prefixes[#key - 1][key:sub(1, #key - 1)] = group
349 fullmatches[key] = group
354 return function (k, v)
355 local val = fullmatches[k]
360 for slen, slist in pairs(key_suffixes) do
362 val = slist[k:sub(-slen)]
369 for slen, slist in pairs(key_prefixes) do
371 val = slist[k:sub(1, slen)]
380 -- Returns prefix part of the keys, and reject suffix matching keys
381 local function process_key(key)
382 if key:sub(1, 1) == '*' then
385 if key:sub(#key, #key) == '*' then
386 return key:sub(1, #key - 2)
391 -- Process functions for all data types
392 function module.process_node(object)
394 local function geom_func(o)
398 module.process_tags(Place.new(object, geom_func))
401 function module.process_way(object)
403 local function geom_func(o)
404 local geom = o:as_polygon()
406 if geom:is_null() then
407 geom = o:as_linestring()
413 module.process_tags(Place.new(object, geom_func))
416 function module.process_relation(object)
417 local geom_func = module.RELATION_TYPES[object.tags.type]
419 if geom_func ~= nil then
420 module.process_tags(Place.new(object, geom_func))
424 -- The process functions are used by default by osm2pgsql.
425 osm2pgsql.process_node = module.process_node
426 osm2pgsql.process_way = module.process_way
427 osm2pgsql.process_relation = module.process_relation
429 function module.process_tags(o)
430 o:clean{delete = PRE_DELETE, extra = PRE_EXTRAS}
432 -- Exception for boundary/place double tagging
433 if o.object.tags.boundary == 'administrative' then
434 o:grab_extratags{match = function (k, v)
435 return k == 'place' and v:sub(1,3) ~= 'isl'
440 local fallback = o:grab_name_parts{groups=NAMES}
443 if o:grab_address_parts{groups=ADDRESS_TAGS} > 0 and fallback == nil then
444 fallback = {'place', 'house', 'always'}
446 if o.address.country ~= nil and #o.address.country ~= 2 then
447 o.address['country'] = nil
449 if POSTCODE_FALLBACK and fallback == nil and o.address.postcode ~= nil then
450 fallback = {'place', 'postcode', 'always'}
453 if o.address.interpolation ~= nil then
454 o:write_place('place', 'houses', 'always', SAVE_EXTRA_MAINS)
458 o:clean{delete = POST_DELETE}
461 for k, v in pairs(o.object.tags) do
462 local ktype = MAIN_KEYS[k]
463 if ktype == 'fallback' then
465 fallback = {k, v, 'named'}
467 elseif ktype ~= nil then
468 o:write_place(k, v, MAIN_KEYS[k], SAVE_EXTRA_MAINS)
472 if fallback ~= nil and o.num_entries == 0 then
473 o:write_place(fallback[1], fallback[2], fallback[3], SAVE_EXTRA_MAINS)
477 --------- Convenience functions for simple style configuration -----------------
480 function module.set_prefilters(data)
481 PRE_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
482 PRE_EXTRAS = module.tag_match{keys = data.extra_keys,
483 tags = data.extra_tags}
484 module.TAGINFO_MAIN.delete_tags = data.delete_tags
487 function module.set_main_tags(data)
490 for k, _ in pairs(data) do
491 table.insert(keys, k)
493 module.TAGINFO_MAIN.keys = keys
496 function module.set_name_tags(data)
497 NAMES = module.tag_group(data)
499 for _, lst in pairs(data) do
500 for _, k in ipairs(lst) do
501 local key = process_key(k)
503 module.TAGINFO_NAME_KEYS[key] = true
509 function module.set_address_tags(data)
510 if data.postcode_fallback ~= nil then
511 POSTCODE_FALLBACK = data.postcode_fallback
512 data.postcode_fallback = nil
514 ADDRESS_TAGS = module.tag_group(data)
516 for _, lst in pairs(data) do
518 for _, k in ipairs(lst) do
519 local key = process_key(k)
521 module.TAGINFO_ADDRESS_KEYS[key] = true
528 function module.set_unused_handling(data)
529 if data.extra_keys == nil and data.extra_tags == nil then
530 POST_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
531 SAVE_EXTRA_MAINS = function() return true end
532 elseif data.delete_keys == nil and data.delete_tags == nil then
534 SAVE_EXTRA_MAINS = module.tag_match{keys = data.extra_keys, tags = data.extra_tags}
536 error("unused handler can have only 'extra_keys' or 'delete_keys' set.")
540 function set_relation_types(data)
541 module.RELATION_TYPES = {}
543 if v == 'multipolygon' then
544 module.RELATION_TYPES[k] = module.relation_as_multipolygon
545 elseif v == 'multiline' then
546 module.RELATION_TYPES[k] = module.relation_as_multiline