]> git.openstreetmap.org Git - nominatim.git/blob - lib-lua/themes/nominatim/init.lua
002a9e1543fba9c163cd9fe9c8c78a52199c939a
[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.tags.admin_level or 15) or 15
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     self.intags = {}
122     for k, v in pairs(self.object.tags) do
123         if PRE_DELETE ~= nil and PRE_DELETE(k, v) then
124             -- ignore
125         elseif PRE_EXTRAS ~= nil and PRE_EXTRAS(k, v) then
126             self.extratags[k] = v
127         elseif k ~= 'admin_level' then
128             self.intags[k] = v
129         end
130     end
131
132     return self
133 end
134
135 function Place:clean(data)
136     for k, v in pairs(self.intags) do
137         if data.delete ~= nil and data.delete(k, v) then
138             self.intags[k] = nil
139         elseif data.extra ~= nil and data.extra(k, v) then
140             self.extratags[k] = v
141             self.intags[k] = nil
142         end
143     end
144 end
145
146 function Place:delete(data)
147     if data.match ~= nil then
148         for k, v in pairs(self.intags) do
149             if data.match(k, v) then
150                 self.intags[k] = nil
151             end
152         end
153     end
154 end
155
156 function Place:grab_extratags(data)
157     local count = 0
158
159     if data.match ~= nil then
160         for k, v in pairs(self.intags) do
161             if data.match(k, v) then
162                 self.intags[k] = nil
163                 self.extratags[k] = v
164                 count = count + 1
165             end
166         end
167     end
168
169     return count
170 end
171
172 local function strip_address_prefix(k)
173     if k:sub(1, 5) == 'addr:' then
174         return k:sub(6)
175     end
176
177     if k:sub(1, 6) == 'is_in:' then
178         return k:sub(7)
179     end
180
181     return k
182 end
183
184
185 function Place:grab_address_parts(data)
186     local count = 0
187
188     if data.groups ~= nil then
189         for k, v in pairs(self.intags) do
190             local atype = data.groups(k, v)
191
192             if atype ~= nil then
193                 if atype == 'main' then
194                     self.has_name = true
195                     self.address[strip_address_prefix(k)] = v
196                     count = count + 1
197                 elseif atype == 'extra' then
198                     self.address[strip_address_prefix(k)] = v
199                 else
200                     self.address[atype] = v
201                 end
202                 self.intags[k] = nil
203             end
204         end
205     end
206
207     return count
208 end
209
210
211 function Place:grab_name_parts(data)
212     local fallback = nil
213
214     if data.groups ~= nil then
215         for k, v in pairs(self.intags) do
216             local atype = data.groups(k, v)
217
218             if atype ~= nil then
219                 self.names[k] = v
220                 self.intags[k] = nil
221                 if atype == 'main' then
222                     self.has_name = true
223                 elseif atype == 'house' then
224                     self.has_name = true
225                     fallback = {'place', 'house', 'always'}
226                 end
227             end
228         end
229     end
230
231     return fallback
232 end
233
234
235 function Place:write_place(k, v, mtype, save_extra_mains)
236     if mtype == nil then
237         return 0
238     end
239
240     v = v or self.intags[k]
241     if v == nil then
242         return 0
243     end
244
245     if type(mtype) == 'table' then
246         mtype = mtype[v] or mtype[1]
247     end
248
249     if mtype == 'always' or (self.has_name and mtype == 'named') then
250         return self:write_row(k, v, save_extra_mains)
251     end
252
253     if mtype == 'named_with_key' then
254         local names = {}
255         local prefix = k .. ':name'
256         for namek, namev in pairs(self.intags) do
257             if namek:sub(1, #prefix) == prefix
258                and (#namek == #prefix
259                     or namek:sub(#prefix + 1, #prefix + 1) == ':') then
260                 names[namek:sub(#k + 2)] = namev
261             end
262         end
263
264         if next(names) ~= nil then
265             local saved_names = self.names
266             self.names = names
267
268             local results = self:write_row(k, v, save_extra_mains)
269
270             self.names = saved_names
271
272             return results
273         end
274     end
275
276     return 0
277 end
278
279 function Place:write_row(k, v, save_extra_mains)
280     if self.geometry == nil then
281         self.geometry = self.geom_func(self.object)
282     end
283     if self.geometry:is_null() then
284         return 0
285     end
286
287     if save_extra_mains ~= nil then
288         for extra_k, extra_v in pairs(self.intags) do
289             if extra_k ~= k and save_extra_mains(extra_k, extra_v) then
290                 self.extratags[extra_k] = extra_v
291             end
292         end
293     end
294
295     insert_row{
296         class = k,
297         type = v,
298         admin_level = self.admin_level,
299         name = next(self.names) and self.names,
300         address = next(self.address) and self.address,
301         extratags = next(self.extratags) and self.extratags,
302         geometry = self.geometry
303     }
304
305     if save_extra_mains then
306         for tk, tv in pairs(self.intags) do
307             if save_extra_mains(tk, tv) then
308                 self.extratags[tk] = nil
309             end
310         end
311     end
312
313     self.num_entries = self.num_entries + 1
314
315     return 1
316 end
317
318
319 function module.tag_match(data)
320     if data == nil or next(data) == nil then
321         return nil
322     end
323
324     local fullmatches = {}
325     local key_prefixes = {}
326     local key_suffixes = {}
327
328     if data.keys ~= nil then
329         for _, key in pairs(data.keys) do
330             if key:sub(1, 1) == '*' then
331                 if #key > 1 then
332                     if key_suffixes[#key - 1] == nil then
333                         key_suffixes[#key - 1] = {}
334                     end
335                     key_suffixes[#key - 1][key:sub(2)] = true
336                 end
337             elseif key:sub(#key, #key) == '*' then
338                 if key_prefixes[#key - 1] == nil then
339                     key_prefixes[#key - 1] = {}
340                 end
341                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = true
342             else
343                 fullmatches[key] = true
344             end
345         end
346     end
347
348     if data.tags ~= nil then
349         for k, vlist in pairs(data.tags) do
350             if fullmatches[k] == nil then
351                 fullmatches[k] = {}
352                 for _, v in pairs(vlist) do
353                     fullmatches[k][v] = true
354                 end
355             end
356         end
357     end
358
359     return function (k, v)
360         if fullmatches[k] ~= nil and (fullmatches[k] == true or fullmatches[k][v] ~= nil) then
361             return true
362         end
363
364         for slen, slist in pairs(key_suffixes) do
365             if #k >= slen and slist[k:sub(-slen)] ~= nil then
366                 return true
367             end
368         end
369
370         for slen, slist in pairs(key_prefixes) do
371             if #k >= slen and slist[k:sub(1, slen)] ~= nil then
372                 return true
373             end
374         end
375
376         return false
377     end
378 end
379
380
381 function module.tag_group(data)
382     if data == nil or next(data) == nil then
383         return nil
384     end
385
386     local fullmatches = {}
387     local key_prefixes = {}
388     local key_suffixes = {}
389
390     for group, tags in pairs(data) do
391         for _, key in pairs(tags) do
392             if key:sub(1, 1) == '*' then
393                 if #key > 1 then
394                     if key_suffixes[#key - 1] == nil then
395                         key_suffixes[#key - 1] = {}
396                     end
397                     key_suffixes[#key - 1][key:sub(2)] = group
398                 end
399             elseif key:sub(#key, #key) == '*' then
400                 if key_prefixes[#key - 1] == nil then
401                     key_prefixes[#key - 1] = {}
402                 end
403                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = group
404             else
405                 fullmatches[key] = group
406             end
407         end
408     end
409
410     return function (k, v)
411         local val = fullmatches[k]
412         if val ~= nil then
413             return val
414         end
415
416         for slen, slist in pairs(key_suffixes) do
417             if #k >= slen then
418                 val = slist[k:sub(-slen)]
419                 if val ~= nil then
420                     return val
421                 end
422             end
423         end
424
425         for slen, slist in pairs(key_prefixes) do
426             if #k >= slen then
427                 val = slist[k:sub(1, slen)]
428                 if val ~= nil then
429                     return val
430                 end
431             end
432         end
433     end
434 end
435
436 -- Returns prefix part of the keys, and reject suffix matching keys
437 local function process_key(key)
438     if key:sub(1, 1) == '*' then
439         return nil
440     end
441     if key:sub(#key, #key) == '*' then
442         return key:sub(1, #key - 2)
443     end
444     return key
445 end
446
447 -- Process functions for all data types
448 function module.process_node(object)
449
450     local function geom_func(o)
451         return o:as_point()
452     end
453
454     module.process_tags(Place.new(object, geom_func))
455 end
456
457 function module.process_way(object)
458
459     local function geom_func(o)
460         local geom = o:as_polygon()
461
462         if geom:is_null() then
463             geom = o:as_linestring()
464         end
465
466         return geom
467     end
468
469     module.process_tags(Place.new(object, geom_func))
470 end
471
472 function module.process_relation(object)
473     local geom_func = module.RELATION_TYPES[object.tags.type]
474
475     if geom_func ~= nil then
476         module.process_tags(Place.new(object, geom_func))
477     end
478 end
479
480 -- The process functions are used by default by osm2pgsql.
481 if themepark then
482     themepark:add_proc('node', module.process_node)
483     themepark:add_proc('way', module.process_way)
484     themepark:add_proc('relation', module.process_relation)
485 else
486     osm2pgsql.process_node = module.process_node
487     osm2pgsql.process_way = module.process_way
488     osm2pgsql.process_relation = module.process_relation
489 end
490
491 function module.process_tags(o)
492     -- Exception for boundary/place double tagging
493     if o.intags.boundary == 'administrative' then
494         o:grab_extratags{match = function (k, v)
495             return k == 'place' and v:sub(1,3) ~= 'isl'
496         end}
497     end
498
499     -- name keys
500     local fallback = o:grab_name_parts{groups=NAMES}
501
502     -- address keys
503     if o:grab_address_parts{groups=ADDRESS_TAGS} > 0 and fallback == nil then
504         fallback = {'place', 'house', 'always'}
505     end
506     if o.address.country ~= nil and #o.address.country ~= 2 then
507         o.address['country'] = nil
508     end
509     if POSTCODE_FALLBACK and fallback == nil and o.address.postcode ~= nil then
510         fallback = {'place', 'postcode', 'always'}
511     end
512
513     if o.address.interpolation ~= nil then
514         o:write_place('place', 'houses', 'always', SAVE_EXTRA_MAINS)
515         return
516     end
517
518     o:clean{delete = POST_DELETE}
519
520     -- collect main keys
521     for k, v in pairs(o.intags) do
522         local ktype = MAIN_KEYS[k]
523         if ktype == 'fallback' then
524             if o.has_name then
525                 fallback = {k, v, 'named'}
526             end
527         elseif ktype ~= nil then
528             o:write_place(k, v, MAIN_KEYS[k], SAVE_EXTRA_MAINS)
529         end
530     end
531
532     if fallback ~= nil and o.num_entries == 0 then
533         o:write_place(fallback[1], fallback[2], fallback[3], SAVE_EXTRA_MAINS)
534     end
535 end
536
537 --------- Convenience functions for simple style configuration -----------------
538
539
540 function module.set_prefilters(data)
541     PRE_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
542     PRE_EXTRAS = module.tag_match{keys = data.extra_keys,
543                                   tags = data.extra_tags}
544     module.TAGINFO_MAIN.delete_tags = data.delete_tags
545 end
546
547 function module.set_main_tags(data)
548     MAIN_KEYS = data
549     local keys = {}
550     for k, _ in pairs(data) do
551         table.insert(keys, k)
552     end
553     module.TAGINFO_MAIN.keys = keys
554 end
555
556 function module.set_name_tags(data)
557     NAMES = module.tag_group(data)
558
559     for _, lst in pairs(data) do
560         for _, k in ipairs(lst) do
561             local key = process_key(k)
562             if key ~= nil then
563                 module.TAGINFO_NAME_KEYS[key] = true
564             end
565         end
566     end
567 end
568
569 function module.set_address_tags(data)
570     if data.postcode_fallback ~= nil then
571         POSTCODE_FALLBACK = data.postcode_fallback
572         data.postcode_fallback = nil
573     end
574     ADDRESS_TAGS = module.tag_group(data)
575
576     for _, lst in pairs(data) do
577         if lst ~= nil then
578             for _, k in ipairs(lst) do
579                 local key = process_key(k)
580                 if key ~= nil then
581                     module.TAGINFO_ADDRESS_KEYS[key] = true
582                 end
583             end
584         end
585     end
586 end
587
588 function module.set_unused_handling(data)
589     if data.extra_keys == nil and data.extra_tags == nil then
590         POST_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
591         SAVE_EXTRA_MAINS = function() return true end
592     elseif data.delete_keys == nil and data.delete_tags == nil then
593         POST_DELETE = nil
594         SAVE_EXTRA_MAINS = module.tag_match{keys = data.extra_keys, tags = data.extra_tags}
595     else
596         error("unused handler can have only 'extra_keys' or 'delete_keys' set.")
597     end
598 end
599
600 function module.set_relation_types(data)
601     module.RELATION_TYPES = {}
602     for k, v in data do
603         if v == 'multipolygon' then
604             module.RELATION_TYPES[k] = module.relation_as_multipolygon
605         elseif v == 'multiline' then
606             module.RELATION_TYPES[k] = module.relation_as_multiline
607         end
608     end
609 end
610
611 return module