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