]> git.openstreetmap.org Git - nominatim.git/blob - lib-lua/themes/nominatim/init.lua
convert flex-base.lua into a themepark theme
[nominatim.git] / lib-lua / themes / nominatim / init.lua
1 -- Nominatim themepark theme.
2 --
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.
7 --
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:
11 --
12 --      local nominatim = themepark:init_theme('nominatim')
13 --      nominatim.set_main_tags{boundary = 'always'}
14 --
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:
18 --
19 --      themepark:add_topic('nominatim/full')
20 --      local nominatim = themepark:init_theme('nominatim')
21 --      nominatim.ignore_tags{'amenity'}
22
23 local module = {}
24
25 local PRE_DELETE = nil
26 local PRE_EXTRAS = nil
27 local POST_DELETE = nil
28 local MAIN_KEYS = nil
29 local NAMES = nil
30 local ADDRESS_TAGS = nil
31 local SAVE_EXTRA_MAINS = false
32 local POSTCODE_FALLBACK = true
33
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.
38 local themepark = ...
39 if type(themepark) ~= 'table' then
40     themepark = nil
41 end
42
43 -- tables required for taginfo
44 module.TAGINFO_MAIN = {keys = {}, delete_tags = {}}
45 module.TAGINFO_NAME_KEYS = {}
46 module.TAGINFO_ADDRESS_KEYS = {}
47
48
49 -- The single place table.
50 local place_table_definition = {
51     name = "place",
52     ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
53     columns = {
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 },
61     },
62     data_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_DATA"),
63     index_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_INDEX"),
64     indexes = {}
65 }
66
67 local insert_row
68
69 if themepark then
70     themepark:add_table(place_table_definition)
71     insert_row = function(columns)
72         themepark:insert('place', columns, {}, {})
73     end
74 else
75     local place_table = osm2pgsql.define_table(place_table_definition)
76     insert_row = function(columns)
77         place_table:insert(columns)
78     end
79 end
80
81 ------------ Geometry functions for relations ---------------------
82
83 function module.relation_as_multipolygon(o)
84     return o:as_multipolygon()
85 end
86
87 function module.relation_as_multiline(o)
88     return o:as_multilinestring():line_merge()
89 end
90
91
92 module.RELATION_TYPES = {
93     multipolygon = module.relation_as_multipolygon,
94     boundary = module.relation_as_multipolygon,
95     waterway = module.relation_as_multiline
96 }
97
98 ------------- Place class ------------------------------------------
99
100 local Place = {}
101 Place.__index = Place
102
103 function Place.new(object, geom_func)
104     local self = setmetatable({}, Place)
105     self.object = object
106     self.geom_func = geom_func
107
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
113     end
114
115     self.num_entries = 0
116     self.has_name = false
117     self.names = {}
118     self.address = {}
119     self.extratags = {}
120
121     return self
122 end
123
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
131         end
132     end
133 end
134
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
140             end
141         end
142     end
143 end
144
145 function Place:grab_extratags(data)
146     local count = 0
147
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
153                 count = count + 1
154             end
155         end
156     end
157
158     return count
159 end
160
161 local function strip_address_prefix(k)
162     if k:sub(1, 5) == 'addr:' then
163         return k:sub(6)
164     end
165
166     if k:sub(1, 6) == 'is_in:' then
167         return k:sub(7)
168     end
169
170     return k
171 end
172
173
174 function Place:grab_address_parts(data)
175     local count = 0
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                 if atype == 'main' then
183                     self.has_name = true
184                     self.address[strip_address_prefix(k)] = v
185                     count = count + 1
186                 elseif atype == 'extra' then
187                     self.address[strip_address_prefix(k)] = v
188                 else
189                     self.address[atype] = v
190                 end
191                 self.object.tags[k] = nil
192             end
193         end
194     end
195
196     return count
197 end
198
199
200 function Place:grab_name_parts(data)
201     local fallback = nil
202
203     if data.groups ~= nil then
204         for k, v in pairs(self.object.tags) do
205             local atype = data.groups(k, v)
206
207             if atype ~= nil then
208                 self.names[k] = v
209                 self.object.tags[k] = nil
210                 if atype == 'main' then
211                     self.has_name = true
212                 elseif atype == 'house' then
213                     self.has_name = true
214                     fallback = {'place', 'house', 'always'}
215                 end
216             end
217         end
218     end
219
220     return fallback
221 end
222
223
224 function Place:write_place(k, v, mtype, save_extra_mains)
225     if mtype == nil then
226         return 0
227     end
228
229     v = v or self.object.tags[k]
230     if v == nil then
231         return 0
232     end
233
234     if type(mtype) == 'table' then
235         mtype = mtype[v] or mtype[1]
236     end
237
238     if mtype == 'always' or (self.has_name and mtype == 'named') then
239         return self:write_row(k, v, save_extra_mains)
240     end
241
242     if mtype == 'named_with_key' then
243         local names = {}
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
250             end
251         end
252
253         if next(names) ~= nil then
254             local saved_names = self.names
255             self.names = names
256
257             local results = self:write_row(k, v, save_extra_mains)
258
259             self.names = saved_names
260
261             return results
262         end
263     end
264
265     return 0
266 end
267
268 function Place:write_row(k, v, save_extra_mains)
269     if self.geometry == nil then
270         self.geometry = self.geom_func(self.object)
271     end
272     if self.geometry:is_null() then
273         return 0
274     end
275
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
280             end
281         end
282     end
283
284     insert_row{
285         class = k,
286         type = 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
292     }
293
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
298             end
299         end
300     end
301
302     self.num_entries = self.num_entries + 1
303
304     return 1
305 end
306
307
308 function module.tag_match(data)
309     if data == nil or next(data) == nil then
310         return nil
311     end
312
313     local fullmatches = {}
314     local key_prefixes = {}
315     local key_suffixes = {}
316
317     if data.keys ~= nil then
318         for _, key in pairs(data.keys) do
319             if key:sub(1, 1) == '*' then
320                 if #key > 1 then
321                     if key_suffixes[#key - 1] == nil then
322                         key_suffixes[#key - 1] = {}
323                     end
324                     key_suffixes[#key - 1][key:sub(2)] = true
325                 end
326             elseif key:sub(#key, #key) == '*' then
327                 if key_prefixes[#key - 1] == nil then
328                     key_prefixes[#key - 1] = {}
329                 end
330                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = true
331             else
332                 fullmatches[key] = true
333             end
334         end
335     end
336
337     if data.tags ~= nil then
338         for k, vlist in pairs(data.tags) do
339             if fullmatches[k] == nil then
340                 fullmatches[k] = {}
341                 for _, v in pairs(vlist) do
342                     fullmatches[k][v] = true
343                 end
344             end
345         end
346     end
347
348     return function (k, v)
349         if fullmatches[k] ~= nil and (fullmatches[k] == true or fullmatches[k][v] ~= nil) then
350             return true
351         end
352
353         for slen, slist in pairs(key_suffixes) do
354             if #k >= slen and slist[k:sub(-slen)] ~= nil then
355                 return true
356             end
357         end
358
359         for slen, slist in pairs(key_prefixes) do
360             if #k >= slen and slist[k:sub(1, slen)] ~= nil then
361                 return true
362             end
363         end
364
365         return false
366     end
367 end
368
369
370 function module.tag_group(data)
371     if data == nil or next(data) == nil then
372         return nil
373     end
374
375     local fullmatches = {}
376     local key_prefixes = {}
377     local key_suffixes = {}
378
379     for group, tags in pairs(data) do
380         for _, key in pairs(tags) do
381             if key:sub(1, 1) == '*' then
382                 if #key > 1 then
383                     if key_suffixes[#key - 1] == nil then
384                         key_suffixes[#key - 1] = {}
385                     end
386                     key_suffixes[#key - 1][key:sub(2)] = group
387                 end
388             elseif key:sub(#key, #key) == '*' then
389                 if key_prefixes[#key - 1] == nil then
390                     key_prefixes[#key - 1] = {}
391                 end
392                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = group
393             else
394                 fullmatches[key] = group
395             end
396         end
397     end
398
399     return function (k, v)
400         local val = fullmatches[k]
401         if val ~= nil then
402             return val
403         end
404
405         for slen, slist in pairs(key_suffixes) do
406             if #k >= slen then
407                 val = slist[k:sub(-slen)]
408                 if val ~= nil then
409                     return val
410                 end
411             end
412         end
413
414         for slen, slist in pairs(key_prefixes) do
415             if #k >= slen then
416                 val = slist[k:sub(1, slen)]
417                 if val ~= nil then
418                     return val
419                 end
420             end
421         end
422     end
423 end
424
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
428         return nil
429     end
430     if key:sub(#key, #key) == '*' then
431         return key:sub(1, #key - 2)
432     end
433     return key
434 end
435
436 -- Process functions for all data types
437 function module.process_node(object)
438
439     local function geom_func(o)
440         return o:as_point()
441     end
442
443     module.process_tags(Place.new(object, geom_func))
444 end
445
446 function module.process_way(object)
447
448     local function geom_func(o)
449         local geom = o:as_polygon()
450
451         if geom:is_null() then
452             geom = o:as_linestring()
453         end
454
455         return geom
456     end
457
458     module.process_tags(Place.new(object, geom_func))
459 end
460
461 function module.process_relation(object)
462     local geom_func = module.RELATION_TYPES[object.tags.type]
463
464     if geom_func ~= nil then
465         module.process_tags(Place.new(object, geom_func))
466     end
467 end
468
469 -- The process functions are used by default by osm2pgsql.
470 if themepark then
471     themepark:add_proc('node', module.process_node)
472     themepark:add_proc('way', module.process_way)
473     themepark:add_proc('relation', module.process_relation)
474 else
475     osm2pgsql.process_node = module.process_node
476     osm2pgsql.process_way = module.process_way
477     osm2pgsql.process_relation = module.process_relation
478 end
479
480 function module.process_tags(o)
481     o:clean{delete = PRE_DELETE, extra = PRE_EXTRAS}
482
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'
487         end}
488     end
489
490     -- name keys
491     local fallback = o:grab_name_parts{groups=NAMES}
492
493     -- address keys
494     if o:grab_address_parts{groups=ADDRESS_TAGS} > 0 and fallback == nil then
495         fallback = {'place', 'house', 'always'}
496     end
497     if o.address.country ~= nil and #o.address.country ~= 2 then
498         o.address['country'] = nil
499     end
500     if POSTCODE_FALLBACK and fallback == nil and o.address.postcode ~= nil then
501         fallback = {'place', 'postcode', 'always'}
502     end
503
504     if o.address.interpolation ~= nil then
505         o:write_place('place', 'houses', 'always', SAVE_EXTRA_MAINS)
506         return
507     end
508
509     o:clean{delete = POST_DELETE}
510
511     -- collect main keys
512     for k, v in pairs(o.object.tags) do
513         local ktype = MAIN_KEYS[k]
514         if ktype == 'fallback' then
515             if o.has_name then
516                 fallback = {k, v, 'named'}
517             end
518         elseif ktype ~= nil then
519             o:write_place(k, v, MAIN_KEYS[k], SAVE_EXTRA_MAINS)
520         end
521     end
522
523     if fallback ~= nil and o.num_entries == 0 then
524         o:write_place(fallback[1], fallback[2], fallback[3], SAVE_EXTRA_MAINS)
525     end
526 end
527
528 --------- Convenience functions for simple style configuration -----------------
529
530
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
536 end
537
538 function module.set_main_tags(data)
539     MAIN_KEYS = data
540     local keys = {}
541     for k, _ in pairs(data) do
542         table.insert(keys, k)
543     end
544     module.TAGINFO_MAIN.keys = keys
545 end
546
547 function module.set_name_tags(data)
548     NAMES = module.tag_group(data)
549
550     for _, lst in pairs(data) do
551         for _, k in ipairs(lst) do
552             local key = process_key(k)
553             if key ~= nil then
554                 module.TAGINFO_NAME_KEYS[key] = true
555             end
556         end
557     end
558 end
559
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
564     end
565     ADDRESS_TAGS = module.tag_group(data)
566
567     for _, lst in pairs(data) do
568         if lst ~= nil then
569             for _, k in ipairs(lst) do
570                 local key = process_key(k)
571                 if key ~= nil then
572                     module.TAGINFO_ADDRESS_KEYS[key] = true
573                 end
574             end
575         end
576     end
577 end
578
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
584         POST_DELETE = nil
585         SAVE_EXTRA_MAINS = module.tag_match{keys = data.extra_keys, tags = data.extra_tags}
586     else
587         error("unused handler can have only 'extra_keys' or 'delete_keys' set.")
588     end
589 end
590
591 function module.set_relation_types(data)
592     module.RELATION_TYPES = {}
593     for k, v in data do
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
598         end
599     end
600 end
601
602 return module