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