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