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