]> git.openstreetmap.org Git - nominatim.git/blob - lib-lua/themes/nominatim/init.lua
55634227f4ba2185f427a682966d13968e2b1185
[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 POST_DELETE = nil
26 local MAIN_KEYS = {admin_level = {'delete'}}
27 local PRE_FILTER = {prefix = {}, suffix = {}}
28 local NAMES = nil
29 local ADDRESS_TAGS = nil
30 local SAVE_EXTRA_MAINS = false
31 local POSTCODE_FALLBACK = true
32
33 -- This file can also be directly require'd instead of running it under
34 -- the themepark framework. In that case the first parameter is usually
35 -- the module name. Lets check for that, so that further down we can call
36 -- the low-level osm2pgsql functions instead of themepark functions.
37 local themepark = ...
38 if type(themepark) ~= 'table' then
39     themepark = nil
40 end
41
42 -- tables required for taginfo
43 module.TAGINFO_MAIN = {keys = {}, delete_tags = {}}
44 module.TAGINFO_NAME_KEYS = {}
45 module.TAGINFO_ADDRESS_KEYS = {}
46
47
48 -- The single place table.
49 local place_table_definition = {
50     name = "place",
51     ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
52     columns = {
53         { column = 'class', type = 'text', not_null = true },
54         { column = 'type', type = 'text', not_null = true },
55         { column = 'admin_level', type = 'smallint' },
56         { column = 'name', type = 'hstore' },
57         { column = 'address', type = 'hstore' },
58         { column = 'extratags', type = 'hstore' },
59         { column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true },
60     },
61     data_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_DATA"),
62     index_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_INDEX"),
63     indexes = {}
64 }
65
66 local insert_row
67
68 if themepark then
69     themepark:add_table(place_table_definition)
70     insert_row = function(columns)
71         themepark:insert('place', columns, {}, {})
72     end
73 else
74     local place_table = osm2pgsql.define_table(place_table_definition)
75     insert_row = function(columns)
76         place_table:insert(columns)
77     end
78 end
79
80 ------------ Geometry functions for relations ---------------------
81
82 function module.relation_as_multipolygon(o)
83     return o:as_multipolygon()
84 end
85
86 function module.relation_as_multiline(o)
87     return o:as_multilinestring():line_merge()
88 end
89
90
91 module.RELATION_TYPES = {
92     multipolygon = module.relation_as_multipolygon,
93     boundary = module.relation_as_multipolygon,
94     waterway = module.relation_as_multiline
95 }
96
97 --------- Built-in place transformation functions --------------------------
98
99 local PlaceTransform = {}
100
101 -- Special transform meanings which are interpreted elsewhere
102 PlaceTransform.fallback = 'fallback'
103 PlaceTransform.delete = 'delete'
104 PlaceTransform.extra = 'extra'
105
106 -- always: unconditionally use that place
107 function PlaceTransform.always(place)
108     return place
109 end
110
111 -- never: unconditionally drop the place
112 function PlaceTransform.never()
113     return nil
114 end
115
116 -- named: use the place if it has a fully-qualified name
117 function PlaceTransform.named(place)
118     if place.has_name then
119         return place
120     end
121 end
122
123 -- named_with_key: use place if there is a name with the main key prefix
124 function PlaceTransform.named_with_key(place, k)
125     local names = {}
126     local prefix = k .. ':name'
127     for namek, namev in pairs(place.intags) do
128         if namek:sub(1, #prefix) == prefix
129            and (#namek == #prefix
130                 or namek:sub(#prefix + 1, #prefix + 1) == ':') then
131             names[namek:sub(#k + 2)] = namev
132         end
133     end
134
135     if next(names) ~= nil then
136         return place:clone{names=names}
137     end
138 end
139
140 ----------------- other helper functions -----------------------------
141
142 local function lookup_prefilter_classification(k, v)
143     -- full matches
144     local desc = MAIN_KEYS[k]
145     local fullmatch = desc and (desc[v] or desc[1])
146     if fullmatch ~= nil then
147         return fullmatch
148     end
149     -- suffixes
150     for slen, slist in pairs(PRE_FILTER.suffix) do
151         if #k >= slen then
152             local group = slist[k:sub(-slen)]
153             if group ~= nil then
154                 return group
155             end
156         end
157     end
158     -- prefixes
159     for slen, slist in pairs(PRE_FILTER.prefix) do
160         if #k >= slen then
161             local group = slist[k:sub(1, slen)]
162             if group ~= nil then
163                 return group
164             end
165         end
166     end
167 end
168
169
170 local function merge_filters_into_main(group, keys, tags)
171     if keys ~= nil then
172         for _, key in pairs(keys) do
173             -- ignore suffix and prefix matches
174             if key:sub(1, 1) ~= '*' and key:sub(#key, #key) ~= '*' then
175                 if MAIN_KEYS[key] == nil then
176                     MAIN_KEYS[key] = {}
177                 end
178                 MAIN_KEYS[key][1] = group
179             end
180         end
181     end
182
183     if tags ~= nil then
184         for key, values in pairs(tags) do
185             if MAIN_KEYS[key] == nil then
186                 MAIN_KEYS[key] = {}
187             end
188             for _, v in pairs(values) do
189                 MAIN_KEYS[key][v] = group
190             end
191         end
192     end
193 end
194
195
196 local function remove_group_from_main(group)
197     for key, values in pairs(MAIN_KEYS) do
198         for _, ttype in pairs(values) do
199             if ttype == group then
200                 values[ttype] = nil
201             end
202         end
203         if next(values) == nil then
204             MAIN_KEYS[key] = nil
205         end
206     end
207 end
208
209
210 local function add_pre_filter(data)
211     for group, keys in pairs(data) do
212         for _, key in pairs(keys) do
213             local klen = #key - 1
214             if key:sub(1, 1) == '*' then
215                 if klen > 0 then
216                     if PRE_FILTER.suffix[klen] == nil then
217                         PRE_FILTER.suffix[klen] = {}
218                     end
219                     PRE_FILTER.suffix[klen][key:sub(2)] = group
220                 end
221             elseif key:sub(#key, #key) == '*' then
222                 if PRE_FILTER.prefix[klen] == nil then
223                     PRE_FILTER.prefix[klen] = {}
224                 end
225                 PRE_FILTER.prefix[klen][key:sub(1, klen)] = group
226             end
227         end
228     end
229 end
230
231 ------------- Place class ------------------------------------------
232
233 local Place = {}
234 Place.__index = Place
235
236 function Place.new(object, geom_func)
237     local self = setmetatable({}, Place)
238     self.object = object
239     self.geom_func = geom_func
240
241     self.admin_level = tonumber(self.object.tags.admin_level or 15) or 15
242     if self.admin_level == nil
243        or self.admin_level <= 0 or self.admin_level > 15
244        or math.floor(self.admin_level) ~= self.admin_level then
245         self.admin_level = 15
246     end
247
248     self.num_entries = 0
249     self.has_name = false
250     self.names = {}
251     self.address = {}
252     self.extratags = {}
253
254     self.intags = {}
255
256     local has_main_tags = false
257     for k, v in pairs(self.object.tags) do
258         local group = lookup_prefilter_classification(k, v)
259         if group == 'extra' then
260             self.extratags[k] = v
261         elseif group ~= 'delete' then
262             self.intags[k] = v
263             if group ~= nil then
264                 has_main_tags = true
265             end
266         end
267     end
268
269     if not has_main_tags then
270         -- no interesting tags, don't bother processing
271         self.intags = {}
272     end
273
274     return self
275 end
276
277 function Place:clean(data)
278     for k, v in pairs(self.intags) do
279         if data.delete ~= nil and data.delete(k, v) then
280             self.intags[k] = nil
281         elseif data.extra ~= nil and data.extra(k, v) then
282             self.extratags[k] = v
283             self.intags[k] = nil
284         end
285     end
286 end
287
288 function Place:delete(data)
289     if data.match ~= nil then
290         for k, v in pairs(self.intags) do
291             if data.match(k, v) then
292                 self.intags[k] = nil
293             end
294         end
295     end
296 end
297
298 function Place:grab_extratags(data)
299     local count = 0
300
301     if data.match ~= nil then
302         for k, v in pairs(self.intags) do
303             if data.match(k, v) then
304                 self.intags[k] = nil
305                 self.extratags[k] = v
306                 count = count + 1
307             end
308         end
309     end
310
311     return count
312 end
313
314 local function strip_address_prefix(k)
315     if k:sub(1, 5) == 'addr:' then
316         return k:sub(6)
317     end
318
319     if k:sub(1, 6) == 'is_in:' then
320         return k:sub(7)
321     end
322
323     return k
324 end
325
326
327 function Place:grab_address_parts(data)
328     local count = 0
329
330     if data.groups ~= nil then
331         for k, v in pairs(self.intags) do
332             local atype = data.groups(k, v)
333
334             if atype ~= nil then
335                 if atype == 'main' then
336                     self.has_name = true
337                     self.address[strip_address_prefix(k)] = v
338                     count = count + 1
339                 elseif atype == 'extra' then
340                     self.address[strip_address_prefix(k)] = v
341                 else
342                     self.address[atype] = v
343                 end
344                 self.intags[k] = nil
345             end
346         end
347     end
348
349     return count
350 end
351
352
353 function Place:grab_name_parts(data)
354     local fallback = nil
355
356     if data.groups ~= nil then
357         for k, v in pairs(self.intags) do
358             local atype = data.groups(k, v)
359
360             if atype ~= nil then
361                 self.names[k] = v
362                 self.intags[k] = nil
363                 if atype == 'main' then
364                     self.has_name = true
365                 elseif atype == 'house' then
366                     self.has_name = true
367                     fallback = {'place', 'house', PlaceTransform.always}
368                 end
369             end
370         end
371     end
372
373     return fallback
374 end
375
376
377 function Place:write_place(k, v, mfunc, save_extra_mains)
378     v = v or self.intags[k]
379     if v == nil then
380         return 0
381     end
382
383     local place = mfunc(self, k, v)
384     if place then
385         local res = place:write_row(k, v, save_extra_mains)
386         self.num_entries = self.num_entries + res
387         return res
388     end
389
390     return 0
391 end
392
393 function Place:write_row(k, v, save_extra_mains)
394     if self.geometry == nil then
395         self.geometry = self.geom_func(self.object)
396     end
397     if self.geometry:is_null() then
398         return 0
399     end
400
401     if save_extra_mains ~= nil then
402         for extra_k, extra_v in pairs(self.intags) do
403             if extra_k ~= k and save_extra_mains(extra_k, extra_v) then
404                 self.extratags[extra_k] = extra_v
405             end
406         end
407     end
408
409     insert_row{
410         class = k,
411         type = v,
412         admin_level = self.admin_level,
413         name = next(self.names) and self.names,
414         address = next(self.address) and self.address,
415         extratags = next(self.extratags) and self.extratags,
416         geometry = self.geometry
417     }
418
419     if save_extra_mains then
420         for tk, tv in pairs(self.intags) do
421             if save_extra_mains(tk, tv) then
422                 self.extratags[tk] = nil
423             end
424         end
425     end
426
427     return 1
428 end
429
430
431 function Place:clone(data)
432     local cp = setmetatable({}, Place)
433     cp.object = self.object
434     cp.geometry = data.geometry or self.geometry
435     cp.geom_func = self.geom_func
436     cp.intags = data.intags or self.intags
437     cp.admin_level = data.admin_level or self.admin_level
438     cp.names = data.names or self.names
439     cp.address = data.address or self.address
440     cp.extratags = data.extratags or self.extratags
441
442     return cp
443 end
444
445
446 function module.tag_match(data)
447     if data == nil or next(data) == nil then
448         return nil
449     end
450
451     local fullmatches = {}
452     local key_prefixes = {}
453     local key_suffixes = {}
454
455     if data.keys ~= nil then
456         for _, key in pairs(data.keys) do
457             if key:sub(1, 1) == '*' then
458                 if #key > 1 then
459                     if key_suffixes[#key - 1] == nil then
460                         key_suffixes[#key - 1] = {}
461                     end
462                     key_suffixes[#key - 1][key:sub(2)] = true
463                 end
464             elseif key:sub(#key, #key) == '*' then
465                 if key_prefixes[#key - 1] == nil then
466                     key_prefixes[#key - 1] = {}
467                 end
468                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = true
469             else
470                 fullmatches[key] = true
471             end
472         end
473     end
474
475     if data.tags ~= nil then
476         for k, vlist in pairs(data.tags) do
477             if fullmatches[k] == nil then
478                 fullmatches[k] = {}
479                 for _, v in pairs(vlist) do
480                     fullmatches[k][v] = true
481                 end
482             end
483         end
484     end
485
486     return function (k, v)
487         if fullmatches[k] ~= nil and (fullmatches[k] == true or fullmatches[k][v] ~= nil) then
488             return true
489         end
490
491         for slen, slist in pairs(key_suffixes) do
492             if #k >= slen and slist[k:sub(-slen)] ~= nil then
493                 return true
494             end
495         end
496
497         for slen, slist in pairs(key_prefixes) do
498             if #k >= slen and slist[k:sub(1, slen)] ~= nil then
499                 return true
500             end
501         end
502
503         return false
504     end
505 end
506
507
508 function module.tag_group(data)
509     if data == nil or next(data) == nil then
510         return nil
511     end
512
513     local fullmatches = {}
514     local key_prefixes = {}
515     local key_suffixes = {}
516
517     for group, tags in pairs(data) do
518         for _, key in pairs(tags) do
519             if key:sub(1, 1) == '*' then
520                 if #key > 1 then
521                     if key_suffixes[#key - 1] == nil then
522                         key_suffixes[#key - 1] = {}
523                     end
524                     key_suffixes[#key - 1][key:sub(2)] = group
525                 end
526             elseif key:sub(#key, #key) == '*' then
527                 if key_prefixes[#key - 1] == nil then
528                     key_prefixes[#key - 1] = {}
529                 end
530                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = group
531             else
532                 fullmatches[key] = group
533             end
534         end
535     end
536
537     return function (k, v)
538         local val = fullmatches[k]
539         if val ~= nil then
540             return val
541         end
542
543         for slen, slist in pairs(key_suffixes) do
544             if #k >= slen then
545                 val = slist[k:sub(-slen)]
546                 if val ~= nil then
547                     return val
548                 end
549             end
550         end
551
552         for slen, slist in pairs(key_prefixes) do
553             if #k >= slen then
554                 val = slist[k:sub(1, slen)]
555                 if val ~= nil then
556                     return val
557                 end
558             end
559         end
560     end
561 end
562
563 -- Returns prefix part of the keys, and reject suffix matching keys
564 local function process_key(key)
565     if key:sub(1, 1) == '*' then
566         return nil
567     end
568     if key:sub(#key, #key) == '*' then
569         return key:sub(1, #key - 2)
570     end
571     return key
572 end
573
574 -- Process functions for all data types
575 function module.process_node(object)
576
577     local function geom_func(o)
578         return o:as_point()
579     end
580
581     module.process_tags(Place.new(object, geom_func))
582 end
583
584 function module.process_way(object)
585
586     local function geom_func(o)
587         local geom = o:as_polygon()
588
589         if geom:is_null() then
590             geom = o:as_linestring()
591         end
592
593         return geom
594     end
595
596     module.process_tags(Place.new(object, geom_func))
597 end
598
599 function module.process_relation(object)
600     local geom_func = module.RELATION_TYPES[object.tags.type]
601
602     if geom_func ~= nil then
603         module.process_tags(Place.new(object, geom_func))
604     end
605 end
606
607 -- The process functions are used by default by osm2pgsql.
608 if themepark then
609     themepark:add_proc('node', module.process_node)
610     themepark:add_proc('way', module.process_way)
611     themepark:add_proc('relation', module.process_relation)
612 else
613     osm2pgsql.process_node = module.process_node
614     osm2pgsql.process_way = module.process_way
615     osm2pgsql.process_relation = module.process_relation
616 end
617
618 function module.process_tags(o)
619     if next(o.intags) == nil then
620         return  -- shortcut when pre-filtering has removed all tags
621     end
622
623     -- Exception for boundary/place double tagging
624     if o.intags.boundary == 'administrative' then
625         o:grab_extratags{match = function (k, v)
626             return k == 'place' and v:sub(1,3) ~= 'isl'
627         end}
628     end
629
630     -- name keys
631     local fallback = o:grab_name_parts{groups=NAMES}
632
633     -- address keys
634     if o:grab_address_parts{groups=ADDRESS_TAGS} > 0 and fallback == nil then
635         fallback = {'place', 'house', PlaceTransform.always}
636     end
637     if o.address.country ~= nil and #o.address.country ~= 2 then
638         o.address['country'] = nil
639     end
640     if POSTCODE_FALLBACK and fallback == nil and o.address.postcode ~= nil then
641         fallback = {'place', 'postcode', PlaceTransform.always}
642     end
643
644     if o.address.interpolation ~= nil then
645         o:write_place('place', 'houses', PlaceTransform.always, SAVE_EXTRA_MAINS)
646         return
647     end
648
649     o:clean{delete = POST_DELETE}
650
651     -- collect main keys
652     for k, v in pairs(o.intags) do
653         local ktable = MAIN_KEYS[k]
654         if ktable then
655             local ktype = ktable[v] or ktable[1]
656             if type(ktype) == 'function' then
657                 o:write_place(k, v, ktype, SAVE_EXTRA_MAINS)
658             elseif ktype == 'fallback' and o.has_name then
659                 fallback = {k, v, PlaceTransform.named}
660             end
661         end
662     end
663
664     if fallback ~= nil and o.num_entries == 0 then
665         o:write_place(fallback[1], fallback[2], fallback[3], SAVE_EXTRA_MAINS)
666     end
667 end
668
669 --------- Convenience functions for simple style configuration -----------------
670
671 function module.set_prefilters(data)
672     remove_group_from_main('delete')
673     merge_filters_into_main('delete', data.delete_keys, data.delete_tags)
674
675     remove_group_from_main('extra')
676     merge_filters_into_main('extra', data.extra_keys, data.extra_tags)
677
678     PRE_FILTER = {prefix = {}, suffix = {}}
679     add_pre_filter{delete = data.delete_keys, extra = data.extra_keys}
680 end
681
682
683 function module.ignore_tags(data)
684     merge_filters_into_main('delete', data)
685     add_pre_filter{delete = data}
686 end
687
688
689 function module.add_for_extratags(data)
690     merge_filters_into_main('extra', data)
691     add_pre_filter{extra = data}
692 end
693
694
695 function module.set_main_tags(data)
696     for key, values in pairs(MAIN_KEYS) do
697         for _, ttype in pairs(values) do
698             if ttype == 'fallback' or type(ttype) == 'function' then
699                 values[ttype] = nil
700             end
701         end
702         if next(values) == nil then
703             MAIN_KEYS[key] = nil
704         end
705     end
706     module.add_main_tags(data)
707 end
708
709
710 function module.add_main_tags(data)
711     for k, v in pairs(data) do
712         if MAIN_KEYS[k] == nil then
713             MAIN_KEYS[k] = {}
714         end
715         if type(v) == 'function' then
716             MAIN_KEYS[k][1] = v
717         elseif type(v) == 'string' then
718             MAIN_KEYS[k][1] = PlaceTransform[v]
719         elseif type(v) == 'table' then
720             for subk, subv in pairs(v) do
721                 if type(subv) == 'function' then
722                     MAIN_KEYS[k][subk] = subv
723                 else
724                     MAIN_KEYS[k][subk] = PlaceTransform[subv]
725                 end
726             end
727         end
728     end
729 end
730
731
732 function module.set_name_tags(data)
733     NAMES = module.tag_group(data)
734
735     for _, lst in pairs(data) do
736         for _, k in ipairs(lst) do
737             local key = process_key(k)
738             if key ~= nil then
739                 module.TAGINFO_NAME_KEYS[key] = true
740             end
741         end
742     end
743     remove_group_from_main('fallback:name')
744     merge_filters_into_main('fallback:name', data.house)
745 end
746
747
748 function module.set_address_tags(data)
749     if data.postcode_fallback ~= nil then
750         POSTCODE_FALLBACK = data.postcode_fallback
751         data.postcode_fallback = nil
752     end
753     ADDRESS_TAGS = module.tag_group(data)
754
755     for _, lst in pairs(data) do
756         if lst ~= nil then
757             for _, k in ipairs(lst) do
758                 local key = process_key(k)
759                 if key ~= nil then
760                     module.TAGINFO_ADDRESS_KEYS[key] = true
761                 end
762             end
763         end
764     end
765
766     remove_group_from_main('fallback:address')
767     remove_group_from_main('fallback:postcode')
768     merge_filters_into_main('fallback:address', data.main)
769     if POSTCODE_FALLBACK then
770         merge_filters_into_main('fallback:postcode', data.postcode)
771     end
772     merge_filters_into_main('fallback:address', data.interpolation)
773 end
774
775
776 function module.set_unused_handling(data)
777     if data.extra_keys == nil and data.extra_tags == nil then
778         POST_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
779         SAVE_EXTRA_MAINS = function() return true end
780     elseif data.delete_keys == nil and data.delete_tags == nil then
781         POST_DELETE = nil
782         SAVE_EXTRA_MAINS = module.tag_match{keys = data.extra_keys, tags = data.extra_tags}
783     else
784         error("unused handler can have only 'extra_keys' or 'delete_keys' set.")
785     end
786 end
787
788 function module.set_relation_types(data)
789     module.RELATION_TYPES = {}
790     for k, v in data do
791         if v == 'multipolygon' then
792             module.RELATION_TYPES[k] = module.relation_as_multipolygon
793         elseif v == 'multiline' then
794             module.RELATION_TYPES[k] = module.relation_as_multiline
795         end
796     end
797 end
798
799 return module