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