]> git.openstreetmap.org Git - nominatim.git/blob - lib-lua/themes/nominatim/init.lua
prepare release 5.3.2-post1
[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             -- Streets can only be ways or relations, not nodes
692             if member.role == 'street' and (member.type == 'w' or member.type == 'r') then
693                 has_street = true
694                 break
695             end
696         end
697         if has_street then
698             for _, member in ipairs(object.members) do
699                 -- Only insert streets that are ways or relations
700                 if member.role == 'street' and (member.type == 'w' or member.type == 'r') then
701                     insert_row.place_associated_street{
702                         member_type = member.type:upper(),
703                         member_id = member.ref,
704                         member_role = member.role
705                     }
706                 elseif member.role == 'house' then
707                     insert_row.place_associated_street{
708                         member_type = member.type:upper(),
709                         member_id = member.ref,
710                         member_role = member.role
711                     }
712                 end
713             end
714         end
715     end
716
717     local geom_func = module.RELATION_TYPES[object.tags.type]
718
719     if geom_func ~= nil then
720         module.process_tags(Place.new(object, geom_func))
721     end
722 end
723
724 -- The process functions are used by default by osm2pgsql.
725 if themepark then
726     themepark:add_proc('node', module.process_node)
727     themepark:add_proc('way', module.process_way)
728     themepark:add_proc('relation', module.process_relation)
729 else
730     osm2pgsql.process_node = module.process_node
731     osm2pgsql.process_way = module.process_way
732     osm2pgsql.process_relation = module.process_relation
733 end
734
735 function module.process_tags(o)
736     if next(o.intags) == nil then
737         return  -- shortcut when pre-filtering has removed all tags
738     end
739
740     -- Exception for boundary/place double tagging
741     if o.intags.boundary == 'administrative' then
742         o:grab_extratags{match = function (k, v)
743             return k == 'place' and v:sub(1,3) ~= 'isl'
744         end}
745     end
746
747     -- name keys
748     local fallback = o:grab_name_parts{groups=NAME_FILTER}
749
750     -- address keys
751     if o:grab_address_parts{groups=ADDRESS_FILTER} > 0 and fallback == nil then
752         fallback = {'place', 'house', address_fallback}
753     end
754     if o.address.country ~= nil and #o.address.country ~= 2 then
755         o.address['country'] = nil
756     end
757
758     if o.address.interpolation ~= nil and o.address.housenumber == nil
759             and o.object.type == 'way' and o.object.nodes ~= nil then
760         local extra_addr = nil
761         for k, v in pairs(o.address) do
762             if k ~= 'interpolation' then
763                 if extra_addr == nil then
764                     extra_addr = {}
765                 end
766                 extra_addr[k] = v
767             end
768         end
769
770         insert_row.place_interpolation{
771             type = o.address.interpolation,
772             address = extra_addr,
773             nodes = '{' .. table.concat(o.object.nodes, ',') .. '}',
774             geometry = o.object:as_linestring()
775         }
776     end
777
778     -- collect main keys
779     local postcode_collect = false
780     for k, v in pairs(o.intags) do
781         local ktable = MAIN_KEYS[k]
782         if ktable then
783             local ktype = ktable[v] or ktable[1]
784             if type(ktype) == 'function' then
785                 o:write_place(k, v, ktype)
786             elseif ktype == 'postcode_area' then
787                 postcode_collect = true
788                 if o.object.type == 'relation'
789                         and o.address.postcode ~= nil
790                         and o:geometry_is_valid() then
791                     insert_row.place_postcode{
792                         postcode = o.address.postcode,
793                         centroid = o.geometry:centroid(),
794                         geometry = o.geometry
795                     }
796                 end
797             elseif ktype == 'fallback' and o.has_name then
798                 fallback = {k, v, PlaceTransform.always}
799             end
800         end
801     end
802
803     if o.num_entries == 0 then
804         if fallback ~= nil then
805             o:write_place(fallback[1], fallback[2], fallback[3])
806         elseif POSTCODE_FALLBACK and not postcode_collect
807                 and o.address.postcode ~= nil
808                 and o:geometry_is_valid() then
809             insert_row.place_postcode{
810                 postcode = o.address.postcode,
811                 centroid = o.geometry:centroid()
812             }
813         end
814     end
815 end
816
817 --------- Extratags post-processing functions ---------------
818
819 local function default_extratags_filter(p, k)
820     return p.extratags
821 end
822
823 EXTRATAGS_FILTER = default_extratags_filter
824 REQUIRED_EXTRATAGS_FILTER = module.tag_match(PRESETS.EXTRATAGS)
825
826 --------- Convenience functions for simple style configuration -----------------
827
828 function module.set_prefilters(data)
829     remove_group_from_main('delete')
830     merge_filters_into_main('delete', data.delete_keys, data.delete_tags)
831
832     remove_group_from_main('extra')
833     merge_filters_into_main('extra', data.extra_keys, data.extra_tags)
834
835     PRE_FILTER = {prefix = {}, suffix = {}}
836     add_pre_filter{delete = data.delete_keys, extra = data.extra_keys}
837 end
838
839
840 function module.ignore_keys(data)
841     if type(data) == 'string' then
842         local preset = data
843         data = PRESETS.IGNORE_KEYS[data]
844         if data == nil then
845             error('Unknown preset for ignored keys: ' .. preset)
846         end
847     end
848     merge_filters_into_main('delete', data)
849     add_pre_filter{delete = data}
850 end
851
852
853 function module.add_for_extratags(data)
854     if type(data) == 'string' then
855         local preset = data
856         data = PRESETS.IGNORE_KEYS[data]
857         if data == nil then
858             error('Unknown preset for extratags: ' .. preset)
859         end
860     end
861     merge_filters_into_main('extra', data)
862     add_pre_filter{extra = data}
863 end
864
865
866 function module.set_main_tags(data)
867     for key, values in pairs(MAIN_KEYS) do
868         for _, ttype in pairs(values) do
869             if ttype == 'fallback' or type(ttype) == 'function' then
870                 values[ttype] = nil
871             end
872         end
873         if next(values) == nil then
874             MAIN_KEYS[key] = nil
875         end
876     end
877     module.modify_main_tags(data)
878 end
879
880
881 function module.modify_main_tags(data)
882     if type(data) == 'string' then
883         local preset = data
884         if data:sub(1, 7) == 'street/' then
885             data = PRESETS.MAIN_TAGS_STREETS[data:sub(8)]
886         elseif data:sub(1, 4) == 'poi/' then
887             data = PRESETS.MAIN_TAGS_POIS(data:sub(5))
888         else
889             data = PRESETS.MAIN_TAGS[data]
890         end
891         if data == nil then
892             error('Unknown preset for main tags: ' .. preset)
893         end
894     end
895
896     for k, v in pairs(data) do
897         if MAIN_KEYS[k] == nil then
898             MAIN_KEYS[k] = {}
899         end
900         if type(v) == 'function' then
901             MAIN_KEYS[k][1] = v
902         elseif type(v) == 'string' then
903             MAIN_KEYS[k][1] = PlaceTransform[v]
904         elseif type(v) == 'table' then
905             for subk, subv in pairs(v) do
906                 if type(subv) == 'function' then
907                     MAIN_KEYS[k][subk] = subv
908                 else
909                     MAIN_KEYS[k][subk] = PlaceTransform[subv]
910                 end
911             end
912         end
913     end
914 end
915
916
917 function module.modify_name_tags(data)
918     if type(data) == 'string' then
919         local preset = data
920         data = PRESETS.NAME_TAGS[data]
921         if data == nil then
922             error('Unknown preset for name keys: ' .. preset)
923         end
924     end
925
926     for k,v in pairs(data) do
927         if next(v) then
928             NAMES[k] = v
929         else
930             NAMES[k] = nil
931         end
932     end
933     NAME_FILTER = module.tag_group(NAMES)
934     remove_group_from_main('fallback:name')
935     if data.house ~= nil then
936         merge_filters_into_main('fallback:name', data.house)
937     end
938 end
939
940
941 function module.set_name_tags(data)
942     NAMES = {}
943     module.modify_name_tags(data)
944 end
945
946
947 function module.set_address_tags(data)
948     ADDRESS_TAGS = {}
949     module.modify_address_tags(data)
950 end
951
952
953 function module.modify_address_tags(data)
954     if type(data) == 'string' then
955         local preset = data
956         data = PRESETS.ADDRESS_TAGS[data]
957         if data == nil then
958             error('Unknown preset for address keys: ' .. preset)
959         end
960     end
961
962     for k, v in pairs(data) do
963         if k == 'postcode_fallback' then
964             POSTCODE_FALLBACK = v
965         elseif next(v) == nil then
966             ADDRESS_TAGS[k] = nil
967         else
968             ADDRESS_TAGS[k] = v
969         end
970     end
971
972     ADDRESS_FILTER = module.tag_group(ADDRESS_TAGS)
973
974     remove_group_from_main('fallback:address')
975     merge_filters_into_main('fallback:address', data.main)
976     merge_filters_into_main('fallback:address', data.interpolation)
977     remove_group_from_main('fallback:postcode')
978     if POSTCODE_FALLBACK then
979         merge_filters_into_main('fallback:postcode', data.postcode)
980     end
981 end
982
983
984 function module.set_address_tags(data)
985     ADDRESS_TAGS_SOURCE = {}
986     module.modify_address_tags(data)
987 end
988
989
990 function module.set_postcode_fallback(enable)
991     if POSTCODE_FALLBACK ~= enable then
992         remove_group_from_main('fallback:postcode')
993         if enable then
994             merge_filters_into_main('fallback:postcode', ADDRESS_TAGS.postcode)
995         end
996     end
997     POSTCODE_FALLBACK = enable
998 end
999
1000
1001 function module.set_unused_handling(data)
1002     if type(data) == 'function' then
1003         EXTRATAGS_FILTER = data
1004     elseif data == nil then
1005         EXTRATAGS_FILTER = default_extratags_filter
1006     elseif data.extra_keys == nil and data.extra_tags == nil then
1007         local delfilter = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
1008         EXTRATAGS_FILTER = function (p, k)
1009             local extra = {}
1010             for kin, vin in pairs(p.intags) do
1011                 if kin ~= k and not delfilter(kin, vin) then
1012                     extra[kin] = vin
1013                 end
1014             end
1015             if next(extra) == nil then
1016                 return p.extratags
1017             end
1018             for kextra, vextra in pairs(p.extratags) do
1019                 extra[kextra] = vextra
1020             end
1021             return extra
1022         end
1023     elseif data.delete_keys == nil and data.delete_tags == nil then
1024         local incfilter = module.tag_match{keys = data.extra_keys, tags = data.extra_tags}
1025         EXTRATAGS_FILTER = function (p, k)
1026             local extra = {}
1027             for kin, vin in pairs(p.intags) do
1028                 if kin ~= k and incfilter(kin, vin) then
1029                     extra[kin] = vin
1030                 end
1031             end
1032             if next(extra) == nil then
1033                 return p.extratags
1034             end
1035             for kextra, vextra in pairs(p.extratags) do
1036                 extra[kextra] = vextra
1037             end
1038             return extra
1039         end
1040     else
1041         error("unused handler can have only 'extra_keys' or 'delete_keys' set.")
1042     end
1043 end
1044
1045 function module.set_relation_types(data)
1046     module.RELATION_TYPES = {}
1047     for k, v in data do
1048         if v == 'multipolygon' then
1049             module.RELATION_TYPES[k] = module.relation_as_multipolygon
1050         elseif v == 'multiline' then
1051             module.RELATION_TYPES[k] = module.relation_as_multiline
1052         end
1053     end
1054 end
1055
1056 function module.set_entrance_filter(data)
1057     if data == nil or type(data) == 'function' then
1058         ENTRANCE_FUNCTION = data
1059         return nil
1060     end
1061
1062     if type(data) == 'string' then
1063         local preset = data
1064         data = PRESETS.ENTRANCE_TABLE[data]
1065         if data == nil then
1066             error('Unknown preset for entrance table: ' .. preset)
1067         end
1068     end
1069
1070     ENTRANCE_FUNCTION = nil
1071
1072     if data.main_tags ~= nil and next(data.main_tags) ~= nil then
1073         if data.extra_include ~= nil and next(data.extra_include) == nil then
1074             -- shortcut: no extra tags requested
1075             ENTRANCE_FUNCTION = function(o)
1076                 for _, v in ipairs(data.main_tags) do
1077                     if o.tags[v] ~= nil then
1078                         return {entrance = o.tags[v]}
1079                     end
1080                 end
1081                 return nil
1082             end
1083         else
1084             if data.extra_include ~= nil then
1085                 local tags = {}
1086                 for _, v in pairs(data.extra_include) do
1087                     tags[v] = true
1088                 end
1089                 if data.extra_exclude ~= nil then
1090                     for _, v in pairs(data.extra_exclude) do
1091                         tags[v] = nil
1092                     end
1093                 end
1094                 for _, v in pairs(data.main_tags) do
1095                     tags[v] = nil
1096                 end
1097
1098                 ENTRANCE_FUNCTION = function(o)
1099                     for _, v in ipairs(data.main_tags) do
1100                         if o.tags[v] ~= nil then
1101                             local entrance = o.tags[v]
1102                             local extra = {}
1103                             for k, v in pairs(tags) do
1104                                 extra[k] = o.tags[k]
1105                             end
1106                             if next(extra) == nil then
1107                                 extra = nil
1108                             end
1109                             return {entrance = entrance, extratags = extra}
1110                         end
1111                     end
1112
1113                     return nil
1114                 end
1115             else
1116                 local notags = {}
1117                 if data.extra_exclude ~= nil then
1118                     for _, v in pairs(data.extra_exclude) do
1119                         notags[v] = 1
1120                     end
1121                 end
1122                 for _, v in pairs(data.main_tags) do
1123                     notags[v] = 1
1124                 end
1125
1126                 ENTRANCE_FUNCTION = function(o)
1127                     for _, v in ipairs(data.main_tags) do
1128                         if o.tags[v] ~= nil then
1129                             local entrance = o.tags[v]
1130                             local extra = {}
1131                             for k, v in pairs(o.tags) do
1132                                 if notags[k] ~= 1 then
1133                                     extra[k] = v
1134                                 end
1135                             end
1136                             if next(extra) == nil then
1137                                 extra = nil
1138                             end
1139                             return {entrance = entrance, extratags = extra}
1140                         end
1141                     end
1142
1143                     return nil
1144                 end
1145             end
1146         end
1147     end
1148 end
1149
1150 function module.get_taginfo()
1151     return {main = MAIN_KEYS, name = NAMES, address = ADDRESS_TAGS}
1152 end
1153
1154 return module