]> git.openstreetmap.org Git - nominatim.git/blob - settings/flex-base.lua
Merge pull request #3260 from lonvia/improve-catgeory-search
[nominatim.git] / settings / flex-base.lua
1 -- Core functions for Nominatim import flex style.
2 --
3
4 local module = {}
5
6 local PRE_DELETE = nil
7 local PRE_EXTRAS = nil
8 local MAIN_KEYS = nil
9 local NAMES = nil
10 local ADDRESS_TAGS = nil
11 local SAVE_EXTRA_MAINS = false
12 local POSTCODE_FALLBACK = true
13
14 -- tables required for taginfo
15 module.TAGINFO_MAIN = {keys = {}, delete_tags = {}}
16 module.TAGINFO_NAME_KEYS = {}
17 module.TAGINFO_ADDRESS_KEYS = {}
18
19
20 -- The single place table.
21 local place_table = osm2pgsql.define_table{
22     name = "place",
23     ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
24     columns = {
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 },
32     },
33     data_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_DATA"),
34     index_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_INDEX"),
35     indexes = {}
36 }
37
38 ------------ Geometry functions for relations ---------------------
39
40 function module.relation_as_multipolygon(o)
41     return o:as_multipolygon()
42 end
43
44 function module.relation_as_multiline(o)
45     return o:as_multilinestring():line_merge()
46 end
47
48
49 module.RELATION_TYPES = {
50     multipolygon = module.relation_as_multipolygon,
51     boundary = module.relation_as_multipolygon,
52     waterway = module.relation_as_multiline
53 }
54
55 ------------- Place class ------------------------------------------
56
57 local Place = {}
58 Place.__index = Place
59
60 function Place.new(object, geom_func)
61     local self = setmetatable({}, Place)
62     self.object = object
63     self.geom_func = geom_func
64
65     self.admin_level = tonumber(self.object:grab_tag('admin_level'))
66     if self.admin_level == nil
67        or self.admin_level <= 0 or self.admin_level > 15
68        or math.floor(self.admin_level) ~= self.admin_level then
69         self.admin_level = 15
70     end
71
72     self.num_entries = 0
73     self.has_name = false
74     self.names = {}
75     self.address = {}
76     self.extratags = {}
77
78     return self
79 end
80
81 function Place:clean(data)
82     for k, v in pairs(self.object.tags) do
83         if data.delete ~= nil and data.delete(k, v) then
84             self.object.tags[k] = nil
85         elseif data.extra ~= nil and data.extra(k, v) then
86             self.extratags[k] = v
87             self.object.tags[k] = nil
88         end
89     end
90 end
91
92 function Place:delete(data)
93     if data.match ~= nil then
94         for k, v in pairs(self.object.tags) do
95             if data.match(k, v) then
96                 self.object.tags[k] = nil
97             end
98         end
99     end
100 end
101
102 function Place:grab_extratags(data)
103     local count = 0
104
105     if data.match ~= nil then
106         for k, v in pairs(self.object.tags) do
107             if data.match(k, v) then
108                 self.object.tags[k] = nil
109                 self.extratags[k] = v
110                 count = count + 1
111             end
112         end
113     end
114
115     return count
116 end
117
118 local function strip_address_prefix(k)
119     if k:sub(1, 5) == 'addr:' then
120         return k:sub(6)
121     end
122
123     if k:sub(1, 6) == 'is_in:' then
124         return k:sub(7)
125     end
126
127     return k
128 end
129
130
131 function Place:grab_address_parts(data)
132     local count = 0
133
134     if data.groups ~= nil then
135         for k, v in pairs(self.object.tags) do
136             local atype = data.groups(k, v)
137
138             if atype ~= nil then
139                 if atype == 'main' then
140                     self.has_name = true
141                     self.address[strip_address_prefix(k)] = v
142                     count = count + 1
143                 elseif atype == 'extra' then
144                     self.address[strip_address_prefix(k)] = v
145                 else
146                     self.address[atype] = v
147                 end
148                 self.object.tags[k] = nil
149             end
150         end
151     end
152
153     return count
154 end
155
156
157 function Place:grab_name_parts(data)
158     local fallback = nil
159
160     if data.groups ~= nil then
161         for k, v in pairs(self.object.tags) do
162             local atype = data.groups(k, v)
163
164             if atype ~= nil then
165                 self.names[k] = v
166                 self.object.tags[k] = nil
167                 if atype == 'main' then
168                     self.has_name = true
169                 elseif atype == 'house' then
170                     self.has_name = true
171                     fallback = {'place', 'house', 'always'}
172                 end
173             end
174         end
175     end
176
177     return fallback
178 end
179
180
181 function Place:write_place(k, v, mtype, save_extra_mains)
182     if mtype == nil then
183         return 0
184     end
185
186     v = v or self.object.tags[k]
187     if v == nil then
188         return 0
189     end
190
191     if type(mtype) == 'table' then
192         mtype = mtype[v] or mtype[1]
193     end
194
195     if mtype == 'always' or (self.has_name and mtype == 'named') then
196         return self:write_row(k, v, save_extra_mains)
197     end
198
199     if mtype == 'named_with_key' then
200         local names = {}
201         local prefix = k .. ':name'
202         for namek, namev in pairs(self.object.tags) do
203             if namek:sub(1, #prefix) == prefix
204                and (#namek == #prefix
205                     or namek:sub(#prefix + 1, #prefix + 1) == ':') then
206                 names[namek:sub(#k + 2)] = namev
207             end
208         end
209
210         if next(names) ~= nil then
211             local saved_names = self.names
212             self.names = names
213
214             local results = self:write_row(k, v, save_extra_mains)
215
216             self.names = saved_names
217
218             return results
219         end
220     end
221
222     return 0
223 end
224
225 function Place:write_row(k, v, save_extra_mains)
226     if self.geometry == nil then
227         self.geometry = self.geom_func(self.object)
228     end
229     if self.geometry:is_null() then
230         return 0
231     end
232
233     if save_extra_mains ~= nil then
234         for extra_k, extra_v in pairs(self.object.tags) do
235             if extra_k ~= k and save_extra_mains(extra_k, extra_v) then
236                 self.extratags[extra_k] = extra_v
237             end
238         end
239     end
240
241     place_table:insert{
242         class = k,
243         type = v,
244         admin_level = self.admin_level,
245         name = next(self.names) and self.names,
246         address = next(self.address) and self.address,
247         extratags = next(self.extratags) and self.extratags,
248         geometry = self.geometry
249     }
250
251     if save_extra_mains then
252         for k, v in pairs(self.object.tags) do
253             if save_extra_mains(k, v) then
254                 self.extratags[k] = nil
255             end
256         end
257     end
258
259     self.num_entries = self.num_entries + 1
260
261     return 1
262 end
263
264
265 function module.tag_match(data)
266     if data == nil or next(data) == nil then
267         return nil
268     end
269
270     local fullmatches = {}
271     local key_prefixes = {}
272     local key_suffixes = {}
273
274     if data.keys ~= nil then
275         for _, key in pairs(data.keys) do
276             if key:sub(1, 1) == '*' then
277                 if #key > 1 then
278                     if key_suffixes[#key - 1] == nil then
279                         key_suffixes[#key - 1] = {}
280                     end
281                     key_suffixes[#key - 1][key:sub(2)] = true
282                 end
283             elseif key:sub(#key, #key) == '*' then
284                 if key_prefixes[#key - 1] == nil then
285                     key_prefixes[#key - 1] = {}
286                 end
287                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = true
288             else
289                 fullmatches[key] = true
290             end
291         end
292     end
293
294     if data.tags ~= nil then
295         for k, vlist in pairs(data.tags) do
296             if fullmatches[k] == nil then
297                 fullmatches[k] = {}
298                 for _, v in pairs(vlist) do
299                     fullmatches[k][v] = true
300                 end
301             end
302         end
303     end
304
305     return function (k, v)
306         if fullmatches[k] ~= nil and (fullmatches[k] == true or fullmatches[k][v] ~= nil) then
307             return true
308         end
309
310         for slen, slist in pairs(key_suffixes) do
311             if #k >= slen and slist[k:sub(-slen)] ~= nil then
312                 return true
313             end
314         end
315
316         for slen, slist in pairs(key_prefixes) do
317             if #k >= slen and slist[k:sub(1, slen)] ~= nil then
318                 return true
319             end
320         end
321
322         return false
323     end
324 end
325
326
327 function module.tag_group(data)
328     if data == nil or next(data) == nil then
329         return nil
330     end
331
332     local fullmatches = {}
333     local key_prefixes = {}
334     local key_suffixes = {}
335
336     for group, tags in pairs(data) do
337         for _, key in pairs(tags) do
338             if key:sub(1, 1) == '*' then
339                 if #key > 1 then
340                     if key_suffixes[#key - 1] == nil then
341                         key_suffixes[#key - 1] = {}
342                     end
343                     key_suffixes[#key - 1][key:sub(2)] = group
344                 end
345             elseif key:sub(#key, #key) == '*' then
346                 if key_prefixes[#key - 1] == nil then
347                     key_prefixes[#key - 1] = {}
348                 end
349                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = group
350             else
351                 fullmatches[key] = group
352             end
353         end
354     end
355
356     return function (k, v)
357         local val = fullmatches[k]
358         if val ~= nil then
359             return val
360         end
361
362         for slen, slist in pairs(key_suffixes) do
363             if #k >= slen then
364                 val = slist[k:sub(-slen)]
365                 if val ~= nil then
366                     return val
367                 end
368             end
369         end
370
371         for slen, slist in pairs(key_prefixes) do
372             if #k >= slen then
373                 val = slist[k:sub(1, slen)]
374                 if val ~= nil then
375                     return val
376                 end
377             end
378         end
379     end
380 end
381
382 -- Returns prefix part of the keys, and reject suffix matching keys
383 local function process_key(key)
384     if key:sub(1, 1) == '*' then
385         return nil
386     end
387     if key:sub(#key, #key) == '*' then
388         return key:sub(1, #key - 2)
389     end
390     return key
391 end
392
393 -- Process functions for all data types
394 function module.process_node(object)
395
396     local function geom_func(o)
397         return o:as_point()
398     end
399
400     module.process_tags(Place.new(object, geom_func))
401 end
402
403 function module.process_way(object)
404
405     local function geom_func(o)
406         local geom = o:as_polygon()
407
408         if geom:is_null() then
409             geom = o:as_linestring()
410         end
411
412         return geom
413     end
414
415     module.process_tags(Place.new(object, geom_func))
416 end
417
418 function module.process_relation(object)
419     local geom_func = module.RELATION_TYPES[object.tags.type]
420
421     if geom_func ~= nil then
422         module.process_tags(Place.new(object, geom_func))
423     end
424 end
425
426 -- The process functions are used by default by osm2pgsql.
427 osm2pgsql.process_node = module.process_node
428 osm2pgsql.process_way = module.process_way
429 osm2pgsql.process_relation = module.process_relation
430
431 function module.process_tags(o)
432     o:clean{delete = PRE_DELETE, extra = PRE_EXTRAS}
433
434     -- Exception for boundary/place double tagging
435     if o.object.tags.boundary == 'administrative' then
436         o:grab_extratags{match = function (k, v)
437             return k == 'place' and v:sub(1,3) ~= 'isl'
438         end}
439     end
440
441     -- name keys
442     local fallback = o:grab_name_parts{groups=NAMES}
443
444     -- address keys
445     if o:grab_address_parts{groups=ADDRESS_TAGS} > 0 and fallback == nil then
446         fallback = {'place', 'house', 'always'}
447     end
448     if o.address.country ~= nil and #o.address.country ~= 2 then
449         o.address['country'] = nil
450     end
451     if POSTCODE_FALLBACK and fallback == nil and o.address.postcode ~= nil then
452         fallback = {'place', 'postcode', 'always'}
453     end
454
455     if o.address.interpolation ~= nil then
456         o:write_place('place', 'houses', 'always', SAVE_EXTRA_MAINS)
457         return
458     end
459
460     o:clean{delete = POST_DELETE}
461
462     -- collect main keys
463     for k, v in pairs(o.object.tags) do
464         local ktype = MAIN_KEYS[k]
465         if ktype == 'fallback' then
466             if o.has_name then
467                 fallback = {k, v, 'named'}
468             end
469         elseif ktype ~= nil then
470             o:write_place(k, v, MAIN_KEYS[k], SAVE_EXTRA_MAINS)
471         end
472     end
473
474     if fallback ~= nil and o.num_entries == 0 then
475         o:write_place(fallback[1], fallback[2], fallback[3], SAVE_EXTRA_MAINS)
476     end
477 end
478
479 --------- Convenience functions for simple style configuration -----------------
480
481
482 function module.set_prefilters(data)
483     PRE_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
484     PRE_EXTRAS = module.tag_match{keys = data.extra_keys,
485                                   tags = data.extra_tags}
486     module.TAGINFO_MAIN.delete_tags = data.delete_tags
487 end
488
489 function module.set_main_tags(data)
490     MAIN_KEYS = data
491     local keys = {}
492     for k, _ in pairs(data) do
493         table.insert(keys, k)
494     end
495     module.TAGINFO_MAIN.keys = keys
496 end
497
498 function module.set_name_tags(data)
499     NAMES = module.tag_group(data)
500
501     for _, lst in pairs(data) do
502         for _, k in ipairs(lst) do
503             local key = process_key(k)
504             if key ~= nil then
505                 module.TAGINFO_NAME_KEYS[key] = true
506             end
507         end
508     end
509 end
510
511 function module.set_address_tags(data)
512     if data.postcode_fallback ~= nil then
513         POSTCODE_FALLBACK = data.postcode_fallback
514         data.postcode_fallback = nil
515     end
516     ADDRESS_TAGS = module.tag_group(data)
517
518     for _, lst in pairs(data) do
519         if lst ~= nil then
520             for _, k in ipairs(lst) do
521                 local key = process_key(k)
522                 if key ~= nil then
523                     module.TAGINFO_ADDRESS_KEYS[key] = true
524                 end
525             end
526         end
527     end
528 end
529
530 function module.set_unused_handling(data)
531     if data.extra_keys == nil and data.extra_tags == nil then
532         POST_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
533         SAVE_EXTRA_MAINS = function() return true end
534     elseif data.delete_keys == nil and data.delete_tags == nil then
535         POST_DELETE = nil
536         SAVE_EXTRA_MAINS = module.tag_match{keys = data.extra_keys, tags = data.extra_tags}
537     else
538         error("unused handler can have only 'extra_keys' or 'delete_keys' set.")
539     end
540 end
541
542 function set_relation_types(data)
543     module.RELATION_TYPES = {}
544     for k, v in data do
545         if v == 'multipolygon' then
546             module.RELATION_TYPES[k] = module.relation_as_multipolygon
547         elseif v == 'multiline' then
548             module.RELATION_TYPES[k] = module.relation_as_multiline
549         end
550     end
551 end
552
553 return module