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