]> git.openstreetmap.org Git - nominatim.git/blob - lib-lua/themes/nominatim/init.lua
Merge remote-tracking branch 'upstream/master'
[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 MAIN_KEYS = {admin_level = {'delete'}}
26 local PRE_FILTER = {prefix = {}, suffix = {}}
27 local NAMES = {}
28 local NAME_FILTER = nil
29 local ADDRESS_TAGS = {}
30 local ADDRESS_FILTER = nil
31 local EXTRATAGS_FILTER
32 local REQUIRED_EXTRATAGS_FILTER
33 local POSTCODE_FALLBACK = true
34 local ENTRANCE_FUNCTION = nil
35
36 -- This file can also be directly require'd instead of running it under
37 -- the themepark framework. In that case the first parameter is usually
38 -- the module name. Lets check for that, so that further down we can call
39 -- the low-level osm2pgsql functions instead of themepark functions.
40 local themepark = ...
41 if type(themepark) ~= 'table' then
42     themepark = nil
43 end
44
45 -- The place tables carry the raw OSM information.
46 local table_definitions = {
47     place = {
48         ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
49         columns = {
50             { column = 'class', type = 'text', not_null = true },
51             { column = 'type', type = 'text', not_null = true },
52             { column = 'admin_level', type = 'smallint' },
53             { column = 'name', type = 'hstore' },
54             { column = 'address', type = 'hstore' },
55             { column = 'extratags', type = 'hstore' },
56             { column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true },
57         },
58         indexes = {}
59     },
60     place_entrance = {
61         ids = { type = 'node', id_column = 'osm_id' },
62         columns = {
63             { column = 'type', type = 'text', not_null = true },
64             { column = 'extratags', type = 'hstore' },
65             { column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true }
66         },
67         indexes = {}
68     }
69 }
70
71 local insert_row = {}
72 local script_path = debug.getinfo(1, "S").source:match("@?(.*/)")
73 local PRESETS = loadfile(script_path .. 'presets.lua')()
74
75 for table_name, table_definition in pairs(table_definitions) do
76     table_definition.name = table_name
77     table_definition.data_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_DATA")
78     table_definition.index_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_INDEX")
79
80     if themepark then
81         themepark:add_table(table_definition)
82         insert_row[table_name] = function(columns)
83             themepark:insert(table_name, columns, {}, {})
84         end
85     else
86         local place_table = osm2pgsql.define_table(table_definition)
87         insert_row[table_name] = function(columns)
88             place_table:insert(columns)
89         end
90     end
91 end
92
93 ------------ Geometry functions for relations ---------------------
94
95 function module.relation_as_multipolygon(o)
96     return o:as_multipolygon()
97 end
98
99 function module.relation_as_multiline(o)
100     return o:as_multilinestring():line_merge()
101 end
102
103
104 module.RELATION_TYPES = {
105     multipolygon = module.relation_as_multipolygon,
106     boundary = module.relation_as_multipolygon,
107     waterway = module.relation_as_multiline
108 }
109
110 --------- Built-in place transformation functions --------------------------
111
112 local PlaceTransform = {}
113
114 -- Special transform meanings which are interpreted elsewhere
115 PlaceTransform.fallback = 'fallback'
116 PlaceTransform.delete = 'delete'
117 PlaceTransform.extra = 'extra'
118
119 -- always: unconditionally use that place
120 function PlaceTransform.always(place)
121     return place
122 end
123
124 -- never: unconditionally drop the place
125 function PlaceTransform.never()
126     return nil
127 end
128
129 -- named: use the place if it has a fully-qualified name
130 function PlaceTransform.named(place)
131     if place.has_name then
132         return place
133     end
134 end
135
136 -- named_with_key: use place if there is a name with the main key prefix
137 function PlaceTransform.named_with_key(place, k)
138     local names = {}
139     local prefix = k .. ':name'
140     for namek, namev in pairs(place.intags) do
141         if namek:sub(1, #prefix) == prefix
142            and (#namek == #prefix
143                 or namek:sub(#prefix + 1, #prefix + 1) == ':') then
144             names[namek:sub(#k + 2)] = namev
145         end
146     end
147
148     if next(names) ~= nil then
149         return place:clone{names=names}
150     end
151 end
152
153 -- Special transform used with address fallbacks: ignore all names
154 -- except for those marked as being part of the address.
155 local function address_fallback(place)
156     if next(place.names) == nil or NAMES.house == nil then
157         return place
158     end
159
160     local names = {}
161     for k, v in pairs(place.names) do
162         if NAME_FILTER(k, v) == 'house' then
163             names[k] = v
164         end
165     end
166     return place:clone{names=names}
167 end
168
169 ----------------- other helper functions -----------------------------
170
171 local function lookup_prefilter_classification(k, v)
172     -- full matches
173     local desc = MAIN_KEYS[k]
174     local fullmatch = desc and (desc[v] or desc[1])
175     if fullmatch ~= nil then
176         return fullmatch
177     end
178     -- suffixes
179     for slen, slist in pairs(PRE_FILTER.suffix) do
180         if #k >= slen then
181             local group = slist[k:sub(-slen)]
182             if group ~= nil then
183                 return group
184             end
185         end
186     end
187     -- prefixes
188     for slen, slist in pairs(PRE_FILTER.prefix) do
189         if #k >= slen then
190             local group = slist[k:sub(1, slen)]
191             if group ~= nil then
192                 return group
193             end
194         end
195     end
196 end
197
198
199 local function merge_filters_into_main(group, keys, tags)
200     if keys ~= nil then
201         for _, key in pairs(keys) do
202             -- ignore suffix and prefix matches
203             if key:sub(1, 1) ~= '*' and key:sub(#key, #key) ~= '*' then
204                 if MAIN_KEYS[key] == nil then
205                     MAIN_KEYS[key] = {}
206                 end
207                 MAIN_KEYS[key][1] = group
208             end
209         end
210     end
211
212     if tags ~= nil then
213         for key, values in pairs(tags) do
214             if MAIN_KEYS[key] == nil then
215                 MAIN_KEYS[key] = {}
216             end
217             for _, v in pairs(values) do
218                 MAIN_KEYS[key][v] = group
219             end
220         end
221     end
222 end
223
224
225 local function remove_group_from_main(group)
226     for key, values in pairs(MAIN_KEYS) do
227         for _, ttype in pairs(values) do
228             if ttype == group then
229                 values[ttype] = nil
230             end
231         end
232         if next(values) == nil then
233             MAIN_KEYS[key] = nil
234         end
235     end
236 end
237
238
239 local function add_pre_filter(data)
240     for group, keys in pairs(data) do
241         for _, key in pairs(keys) do
242             local klen = #key - 1
243             if key:sub(1, 1) == '*' then
244                 if klen > 0 then
245                     if PRE_FILTER.suffix[klen] == nil then
246                         PRE_FILTER.suffix[klen] = {}
247                     end
248                     PRE_FILTER.suffix[klen][key:sub(2)] = group
249                 end
250             elseif key:sub(#key, #key) == '*' then
251                 if PRE_FILTER.prefix[klen] == nil then
252                     PRE_FILTER.prefix[klen] = {}
253                 end
254                 PRE_FILTER.prefix[klen][key:sub(1, klen)] = group
255             end
256         end
257     end
258 end
259
260 ------------- Place class ------------------------------------------
261
262 local Place = {}
263 Place.__index = Place
264
265 function Place.new(object, geom_func)
266     local self = setmetatable({}, Place)
267     self.object = object
268     self.geom_func = geom_func
269
270     self.admin_level = tonumber(self.object.tags.admin_level or 15) or 15
271     if self.admin_level == nil
272        or self.admin_level <= 0 or self.admin_level > 15
273        or math.floor(self.admin_level) ~= self.admin_level then
274         self.admin_level = 15
275     end
276
277     self.num_entries = 0
278     self.has_name = false
279     self.names = {}
280     self.address = {}
281     self.extratags = {}
282
283     self.intags = {}
284
285     local has_main_tags = false
286     for k, v in pairs(self.object.tags) do
287         local group = lookup_prefilter_classification(k, v)
288         if group == 'extra' then
289             self.extratags[k] = v
290         elseif group ~= 'delete' then
291             self.intags[k] = v
292             if group ~= nil then
293                 has_main_tags = true
294             end
295         end
296     end
297
298     if not has_main_tags then
299         -- no interesting tags, don't bother processing
300         self.intags = {}
301     end
302
303     return self
304 end
305
306 function Place:clean(data)
307     for k, v in pairs(self.intags) do
308         if data.delete ~= nil and data.delete(k, v) then
309             self.intags[k] = nil
310         elseif data.extra ~= nil and data.extra(k, v) then
311             self.extratags[k] = v
312             self.intags[k] = nil
313         end
314     end
315 end
316
317 function Place:delete(data)
318     if data.match ~= nil then
319         for k, v in pairs(self.intags) do
320             if data.match(k, v) then
321                 self.intags[k] = nil
322             end
323         end
324     end
325 end
326
327 function Place:grab_extratags(data)
328     local count = 0
329
330     if data.match ~= nil then
331         for k, v in pairs(self.intags) do
332             if data.match(k, v) then
333                 self.intags[k] = nil
334                 self.extratags[k] = v
335                 count = count + 1
336             end
337         end
338     end
339
340     return count
341 end
342
343 local function strip_address_prefix(k)
344     if k:sub(1, 5) == 'addr:' then
345         return k:sub(6)
346     end
347
348     if k:sub(1, 6) == 'is_in:' then
349         return k:sub(7)
350     end
351
352     return k
353 end
354
355
356 function Place:grab_address_parts(data)
357     local count = 0
358
359     if data.groups ~= nil then
360         for k, v in pairs(self.intags) do
361             local atype = data.groups(k, v)
362
363             if atype ~= nil then
364                 if atype == 'main' then
365                     self.has_name = true
366                     self.address[strip_address_prefix(k)] = v
367                     count = count + 1
368                 elseif atype == 'extra' then
369                     self.address[strip_address_prefix(k)] = v
370                 else
371                     self.address[atype] = v
372                 end
373                 self.intags[k] = nil
374             end
375         end
376     end
377
378     return count
379 end
380
381
382 function Place:grab_name_parts(data)
383     local fallback = nil
384
385     if data.groups ~= nil then
386         for k, v in pairs(self.intags) do
387             local atype = data.groups(k, v)
388
389             if atype ~= nil then
390                 self.names[k] = v
391                 self.intags[k] = nil
392                 if atype == 'main' then
393                     self.has_name = true
394                 elseif atype == 'house' then
395                     self.has_name = true
396                     fallback = {'place', 'house', address_fallback}
397                 end
398             end
399         end
400     end
401
402     return fallback
403 end
404
405
406 function Place:write_place(k, v, mfunc)
407     v = v or self.intags[k]
408     if v == nil then
409         return 0
410     end
411
412     local place = mfunc(self, k, v)
413     if place then
414         local res = place:write_row(k, v)
415         self.num_entries = self.num_entries + res
416         return res
417     end
418
419     return 0
420 end
421
422 function Place:write_row(k, v)
423     if self.geometry == nil then
424         self.geometry = self.geom_func(self.object)
425     end
426     if self.geometry == nil or self.geometry:is_null() then
427         return 0
428     end
429
430      local extra = EXTRATAGS_FILTER(self, k, v) or {}
431
432      for tk, tv in pairs(self.object.tags) do
433          if REQUIRED_EXTRATAGS_FILTER(tk, tv) and extra[tk] == nil then
434              extra[tk] = tv
435          end
436      end
437
438      if extra and next(extra) == nil then
439          extra = nil
440      end
441
442     insert_row.place{
443         class = k,
444         type = v,
445         admin_level = self.admin_level,
446         name = next(self.names) and self.names,
447         address = next(self.address) and self.address,
448         extratags = extra,
449         geometry = self.geometry
450     }
451
452     return 1
453 end
454
455
456 function Place:clone(data)
457     local cp = setmetatable({}, Place)
458     cp.object = self.object
459     cp.geometry = data.geometry or self.geometry
460     cp.geom_func = self.geom_func
461     cp.intags = data.intags or self.intags
462     cp.admin_level = data.admin_level or self.admin_level
463     cp.names = data.names or self.names
464     cp.address = data.address or self.address
465     cp.extratags = data.extratags or self.extratags
466
467     return cp
468 end
469
470
471 function module.tag_match(data)
472     if data == nil or next(data) == nil then
473         return nil
474     end
475
476     local fullmatches = {}
477     local key_prefixes = {}
478     local key_suffixes = {}
479
480     if data.keys ~= nil then
481         for _, key in pairs(data.keys) do
482             if key:sub(1, 1) == '*' then
483                 if #key > 1 then
484                     if key_suffixes[#key - 1] == nil then
485                         key_suffixes[#key - 1] = {}
486                     end
487                     key_suffixes[#key - 1][key:sub(2)] = true
488                 end
489             elseif key:sub(#key, #key) == '*' then
490                 if key_prefixes[#key - 1] == nil then
491                     key_prefixes[#key - 1] = {}
492                 end
493                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = true
494             else
495                 fullmatches[key] = true
496             end
497         end
498     end
499
500     if data.tags ~= nil then
501         for k, vlist in pairs(data.tags) do
502             if fullmatches[k] == nil then
503                 fullmatches[k] = {}
504                 for _, v in pairs(vlist) do
505                     fullmatches[k][v] = true
506                 end
507             end
508         end
509     end
510
511     return function (k, v)
512         if fullmatches[k] ~= nil and (fullmatches[k] == true or fullmatches[k][v] ~= nil) then
513             return true
514         end
515
516         for slen, slist in pairs(key_suffixes) do
517             if #k >= slen and slist[k:sub(-slen)] ~= nil then
518                 return true
519             end
520         end
521
522         for slen, slist in pairs(key_prefixes) do
523             if #k >= slen and slist[k:sub(1, slen)] ~= nil then
524                 return true
525             end
526         end
527
528         return false
529     end
530 end
531
532
533 function module.tag_group(data)
534     if data == nil or next(data) == nil then
535         return nil
536     end
537
538     local fullmatches = {}
539     local key_prefixes = {}
540     local key_suffixes = {}
541
542     for group, tags in pairs(data) do
543         for _, key in pairs(tags) do
544             if key:sub(1, 1) == '*' then
545                 if #key > 1 then
546                     if key_suffixes[#key - 1] == nil then
547                         key_suffixes[#key - 1] = {}
548                     end
549                     key_suffixes[#key - 1][key:sub(2)] = group
550                 end
551             elseif key:sub(#key, #key) == '*' then
552                 if key_prefixes[#key - 1] == nil then
553                     key_prefixes[#key - 1] = {}
554                 end
555                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = group
556             else
557                 fullmatches[key] = group
558             end
559         end
560     end
561
562     return function (k)
563         local val = fullmatches[k]
564         if val ~= nil then
565             return val
566         end
567
568         for slen, slist in pairs(key_suffixes) do
569             if #k >= slen then
570                 val = slist[k:sub(-slen)]
571                 if val ~= nil then
572                     return val
573                 end
574             end
575         end
576
577         for slen, slist in pairs(key_prefixes) do
578             if #k >= slen then
579                 val = slist[k:sub(1, slen)]
580                 if val ~= nil then
581                     return val
582                 end
583             end
584         end
585     end
586 end
587
588 -- Returns prefix part of the keys, and reject suffix matching keys
589 local function process_key(key)
590     if key:sub(1, 1) == '*' then
591         return nil
592     end
593     if key:sub(#key, #key) == '*' then
594         return key:sub(1, #key - 2)
595     end
596     return key
597 end
598
599 -- Process functions for all data types
600 function module.process_node(object)
601     if ENTRANCE_FUNCTION ~= nil then
602         local entrance_info = ENTRANCE_FUNCTION(object)
603         if entrance_info ~= nil then
604             insert_row.place_entrance{
605                 type = entrance_info.entrance,
606                 extratags = entrance_info.extratags,
607                 geometry = object:as_point()
608             }
609         end
610     end
611
612     local function geom_func(o)
613         return o:as_point()
614     end
615
616     module.process_tags(Place.new(object, geom_func))
617 end
618
619 function module.process_way(object)
620
621     local function geom_func(o)
622         local geom = o:as_polygon()
623
624         if geom:is_null() then
625             geom = o:as_linestring()
626             if geom:is_null() or geom:length() > 30 then
627                 return nil
628             end
629         end
630
631         return geom
632     end
633
634     module.process_tags(Place.new(object, geom_func))
635 end
636
637 function module.process_relation(object)
638     local geom_func = module.RELATION_TYPES[object.tags.type]
639
640     if geom_func ~= nil then
641         module.process_tags(Place.new(object, geom_func))
642     end
643 end
644
645 -- The process functions are used by default by osm2pgsql.
646 if themepark then
647     themepark:add_proc('node', module.process_node)
648     themepark:add_proc('way', module.process_way)
649     themepark:add_proc('relation', module.process_relation)
650 else
651     osm2pgsql.process_node = module.process_node
652     osm2pgsql.process_way = module.process_way
653     osm2pgsql.process_relation = module.process_relation
654 end
655
656 function module.process_tags(o)
657     if next(o.intags) == nil then
658         return  -- shortcut when pre-filtering has removed all tags
659     end
660
661     -- Exception for boundary/place double tagging
662     if o.intags.boundary == 'administrative' then
663         o:grab_extratags{match = function (k, v)
664             return k == 'place' and v:sub(1,3) ~= 'isl'
665         end}
666     end
667
668     -- name keys
669     local fallback = o:grab_name_parts{groups=NAME_FILTER}
670
671     -- address keys
672     if o:grab_address_parts{groups=ADDRESS_FILTER} > 0 and fallback == nil then
673         fallback = {'place', 'house', address_fallback}
674     end
675     if o.address.country ~= nil and #o.address.country ~= 2 then
676         o.address['country'] = nil
677     end
678     if POSTCODE_FALLBACK and fallback == nil and o.address.postcode ~= nil then
679         fallback = {'place', 'postcode', PlaceTransform.always}
680     end
681
682     if o.address.interpolation ~= nil then
683         o:write_place('place', 'houses', PlaceTransform.always)
684         return
685     end
686
687     -- collect main keys
688     for k, v in pairs(o.intags) do
689         local ktable = MAIN_KEYS[k]
690         if ktable then
691             local ktype = ktable[v] or ktable[1]
692             if type(ktype) == 'function' then
693                 o:write_place(k, v, ktype)
694             elseif ktype == 'fallback' and o.has_name then
695                 fallback = {k, v, PlaceTransform.named}
696             end
697         end
698     end
699
700     if fallback ~= nil and o.num_entries == 0 then
701         o:write_place(fallback[1], fallback[2], fallback[3])
702     end
703 end
704
705 --------- Extratags post-processing functions ---------------
706
707 local function default_extratags_filter(p, k)
708     return p.extratags
709 end
710
711 EXTRATAGS_FILTER = default_extratags_filter
712 REQUIRED_EXTRATAGS_FILTER = module.tag_match(PRESETS.EXTRATAGS)
713
714 --------- Convenience functions for simple style configuration -----------------
715
716 function module.set_prefilters(data)
717     remove_group_from_main('delete')
718     merge_filters_into_main('delete', data.delete_keys, data.delete_tags)
719
720     remove_group_from_main('extra')
721     merge_filters_into_main('extra', data.extra_keys, data.extra_tags)
722
723     PRE_FILTER = {prefix = {}, suffix = {}}
724     add_pre_filter{delete = data.delete_keys, extra = data.extra_keys}
725 end
726
727
728 function module.ignore_keys(data)
729     if type(data) == 'string' then
730         local preset = data
731         data = PRESETS.IGNORE_KEYS[data]
732         if data == nil then
733             error('Unknown preset for ignored keys: ' .. preset)
734         end
735     end
736     merge_filters_into_main('delete', data)
737     add_pre_filter{delete = data}
738 end
739
740
741 function module.add_for_extratags(data)
742     if type(data) == 'string' then
743         local preset = data
744         data = PRESETS.IGNORE_KEYS[data]
745         if data == nil then
746             error('Unknown preset for extratags: ' .. preset)
747         end
748     end
749     merge_filters_into_main('extra', data)
750     add_pre_filter{extra = data}
751 end
752
753
754 function module.set_main_tags(data)
755     for key, values in pairs(MAIN_KEYS) do
756         for _, ttype in pairs(values) do
757             if ttype == 'fallback' or type(ttype) == 'function' then
758                 values[ttype] = nil
759             end
760         end
761         if next(values) == nil then
762             MAIN_KEYS[key] = nil
763         end
764     end
765     module.modify_main_tags(data)
766 end
767
768
769 function module.modify_main_tags(data)
770     if type(data) == 'string' then
771         local preset = data
772         if data:sub(1, 7) == 'street/' then
773             data = PRESETS.MAIN_TAGS_STREETS[data:sub(8)]
774         elseif data:sub(1, 4) == 'poi/' then
775             data = PRESETS.MAIN_TAGS_POIS(data:sub(5))
776         else
777             data = PRESETS.MAIN_TAGS[data]
778         end
779         if data == nil then
780             error('Unknown preset for main tags: ' .. preset)
781         end
782     end
783
784     for k, v in pairs(data) do
785         if MAIN_KEYS[k] == nil then
786             MAIN_KEYS[k] = {}
787         end
788         if type(v) == 'function' then
789             MAIN_KEYS[k][1] = v
790         elseif type(v) == 'string' then
791             MAIN_KEYS[k][1] = PlaceTransform[v]
792         elseif type(v) == 'table' then
793             for subk, subv in pairs(v) do
794                 if type(subv) == 'function' then
795                     MAIN_KEYS[k][subk] = subv
796                 else
797                     MAIN_KEYS[k][subk] = PlaceTransform[subv]
798                 end
799             end
800         end
801     end
802 end
803
804
805 function module.modify_name_tags(data)
806     if type(data) == 'string' then
807         local preset = data
808         data = PRESETS.NAME_TAGS[data]
809         if data == nil then
810             error('Unknown preset for name keys: ' .. preset)
811         end
812     end
813
814     for k,v in pairs(data) do
815         if next(v) then
816             NAMES[k] = v
817         else
818             NAMES[k] = nil
819         end
820     end
821     NAME_FILTER = module.tag_group(NAMES)
822     remove_group_from_main('fallback:name')
823     if data.house ~= nil then
824         merge_filters_into_main('fallback:name', data.house)
825     end
826 end
827
828
829 function module.set_name_tags(data)
830     NAMES = {}
831     module.modify_name_tags(data)
832 end
833
834
835 function module.set_address_tags(data)
836     ADDRESS_TAGS = {}
837     module.modify_address_tags(data)
838 end
839
840
841 function module.modify_address_tags(data)
842     if type(data) == 'string' then
843         local preset = data
844         data = PRESETS.ADDRESS_TAGS[data]
845         if data == nil then
846             error('Unknown preset for address keys: ' .. preset)
847         end
848     end
849
850     for k, v in pairs(data) do
851         if k == 'postcode_fallback' then
852             POSTCODE_FALLBACK = v
853         elseif next(v) == nil then
854             ADDRESS_TAGS[k] = nil
855         else
856             ADDRESS_TAGS[k] = v
857         end
858     end
859
860     ADDRESS_FILTER = module.tag_group(ADDRESS_TAGS)
861
862     remove_group_from_main('fallback:address')
863     merge_filters_into_main('fallback:address', data.main)
864     merge_filters_into_main('fallback:address', data.interpolation)
865     remove_group_from_main('fallback:postcode')
866     if POSTCODE_FALLBACK then
867         merge_filters_into_main('fallback:postcode', data.postcode)
868     end
869 end
870
871
872 function module.set_address_tags(data)
873     ADDRESS_TAGS_SOURCE = {}
874     module.modify_address_tags(data)
875 end
876
877
878 function module.set_postcode_fallback(enable)
879     if POSTCODE_FALLBACK ~= enable then
880         remove_group_from_main('fallback:postcode')
881         if enable then
882             merge_filters_into_main('fallback:postcode', ADDRESS_TAGS.postcode)
883         end
884     end
885     POSTCODE_FALLBACK = enable
886 end
887
888
889 function module.set_unused_handling(data)
890     if type(data) == 'function' then
891         EXTRATAGS_FILTER = data
892     elseif data == nil then
893         EXTRATAGS_FILTER = default_extratags_filter
894     elseif data.extra_keys == nil and data.extra_tags == nil then
895         local delfilter = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
896         EXTRATAGS_FILTER = function (p, k)
897             local extra = {}
898             for kin, vin in pairs(p.intags) do
899                 if kin ~= k and not delfilter(kin, vin) then
900                     extra[kin] = vin
901                 end
902             end
903             if next(extra) == nil then
904                 return p.extratags
905             end
906             for kextra, vextra in pairs(p.extratags) do
907                 extra[kextra] = vextra
908             end
909             return extra
910         end
911     elseif data.delete_keys == nil and data.delete_tags == nil then
912         local incfilter = module.tag_match{keys = data.extra_keys, tags = data.extra_tags}
913         EXTRATAGS_FILTER = function (p, k)
914             local extra = {}
915             for kin, vin in pairs(p.intags) do
916                 if kin ~= k and incfilter(kin, vin) then
917                     extra[kin] = vin
918                 end
919             end
920             if next(extra) == nil then
921                 return p.extratags
922             end
923             for kextra, vextra in pairs(p.extratags) do
924                 extra[kextra] = vextra
925             end
926             return extra
927         end
928     else
929         error("unused handler can have only 'extra_keys' or 'delete_keys' set.")
930     end
931 end
932
933 function module.set_relation_types(data)
934     module.RELATION_TYPES = {}
935     for k, v in data do
936         if v == 'multipolygon' then
937             module.RELATION_TYPES[k] = module.relation_as_multipolygon
938         elseif v == 'multiline' then
939             module.RELATION_TYPES[k] = module.relation_as_multiline
940         end
941     end
942 end
943
944 function module.set_entrance_filter(data)
945     if data == nil or type(data) == 'function' then
946         ENTRANCE_FUNCTION = data
947         return nil
948     end
949
950     if type(data) == 'string' then
951         local preset = data
952         data = PRESETS.ENTRANCE_TABLE[data]
953         if data == nil then
954             error('Unknown preset for entrance table: ' .. preset)
955         end
956     end
957
958     ENTRANCE_FUNCTION = nil
959
960     if data.main_tags ~= nil and next(data.main_tags) ~= nil then
961         if data.extra_include ~= nil and next(data.extra_include) == nil then
962             -- shortcut: no extra tags requested
963             ENTRANCE_FUNCTION = function(o)
964                 for _, v in ipairs(data.main_tags) do
965                     if o.tags[v] ~= nil then
966                         return {entrance = o.tags[v]}
967                     end
968                 end
969                 return nil
970             end
971         else
972             if data.extra_include ~= nil then
973                 local tags = {}
974                 for _, v in pairs(data.extra_include) do
975                     tags[v] = true
976                 end
977                 if data.extra_exclude ~= nil then
978                     for _, v in pairs(data.extra_exclude) do
979                         tags[v] = nil
980                     end
981                 end
982                 for _, v in pairs(data.main_tags) do
983                     tags[v] = nil
984                 end
985
986                 ENTRANCE_FUNCTION = function(o)
987                     for _, v in ipairs(data.main_tags) do
988                         if o.tags[v] ~= nil then
989                             local entrance = o.tags[v]
990                             local extra = {}
991                             for k, v in pairs(tags) do
992                                 extra[k] = o.tags[k]
993                             end
994                             if next(extra) == nil then
995                                 extra = nil
996                             end
997                             return {entrance = entrance, extratags = extra}
998                         end
999                     end
1000
1001                     return nil
1002                 end
1003             else
1004                 local notags = {}
1005                 if data.extra_exclude ~= nil then
1006                     for _, v in pairs(data.extra_exclude) do
1007                         notags[v] = 1
1008                     end
1009                 end
1010                 for _, v in pairs(data.main_tags) do
1011                     notags[v] = 1
1012                 end
1013
1014                 ENTRANCE_FUNCTION = function(o)
1015                     for _, v in ipairs(data.main_tags) do
1016                         if o.tags[v] ~= nil then
1017                             local entrance = o.tags[v]
1018                             local extra = {}
1019                             for k, v in pairs(o.tags) do
1020                                 if notags[k] ~= 1 then
1021                                     extra[k] = v
1022                                 end
1023                             end
1024                             if next(extra) == nil then
1025                                 extra = nil
1026                             end
1027                             return {entrance = entrance, extratags = extra}
1028                         end
1029                     end
1030
1031                     return nil
1032                 end
1033             end
1034         end
1035     end
1036 end
1037
1038 function module.get_taginfo()
1039     return {main = MAIN_KEYS, name = NAMES, address = ADDRESS_TAGS}
1040 end
1041
1042 return module