]> git.openstreetmap.org Git - nominatim.git/blob - settings/flex-base.lua
add support for lookup of linked places
[nominatim.git] / settings / flex-base.lua
1 -- Core functions for Nominatim import flex style.
2 --
3
4
5 -- The single place table.
6 place_table = osm2pgsql.define_table{
7     name = "place",
8     ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
9     columns = {
10         { column = 'class', type = 'text', not_null = true },
11         { column = 'type', type = 'text', not_null = true },
12         { column = 'admin_level', type = 'smallint' },
13         { column = 'name', type = 'hstore' },
14         { column = 'address', type = 'hstore' },
15         { column = 'extratags', type = 'hstore' },
16         { column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true },
17     }
18 }
19
20 ------------- Place class ------------------------------------------
21
22 local Place = {}
23 Place.__index = Place
24
25 function Place.new(object, geom_func)
26     local self = setmetatable({}, Place)
27     self.object = object
28     self.geom_func = geom_func
29
30     self.admin_level = tonumber(self.object:grab_tag('admin_level'))
31     if self.admin_level == nil
32        or self.admin_level <= 0 or self.admin_level > 15
33        or math.floor(self.admin_level) ~= self.admin_level then
34         self.admin_level = 15
35     end
36
37     self.num_entries = 0
38     self.has_name = false
39     self.names = {}
40     self.address = {}
41     self.extratags = {}
42
43     return self
44 end
45
46 function Place:delete(data)
47     if data.match ~= nil then
48         for k, v in pairs(self.object.tags) do
49             if data.match(k, v) then
50                 self.object.tags[k] = nil
51             end
52         end
53     end
54 end
55
56 function Place:grab_extratags(data)
57     local count = 0
58
59     if data.match ~= nil then
60         for k, v in pairs(self.object.tags) do
61             if data.match(k, v) then
62                 self.object.tags[k] = nil
63                 self.extratags[k] = v
64                 count = count + 1
65             end
66         end
67     end
68
69     return count
70 end
71
72 function Place:grab_address(data)
73     local count = 0
74
75     if data.match ~= nil then
76         for k, v in pairs(self.object.tags) do
77             if data.match(k, v) then
78                 self.object.tags[k] = nil
79
80                 if data.include_on_name == true then
81                     self.has_name = true
82                 end
83
84                 if data.out_key ~= nil then
85                     self.address[data.out_key] = v
86                     return 1
87                 end
88
89                 if k:sub(1, 5) == 'addr:' then
90                     self.address[k:sub(6)] = v
91                 elseif k:sub(1, 6) == 'is_in:' then
92                     self.address[k:sub(7)] = v
93                 else
94                     self.address[k] = v
95                 end
96                 count = count + 1
97             end
98         end
99     end
100
101     return count
102 end
103
104 function Place:set_address(key, value)
105     self.address[key] = value
106 end
107
108 function Place:grab_name(data)
109     local count = 0
110
111     if data.match ~= nil then
112         for k, v in pairs(self.object.tags) do
113             if data.match(k, v) then
114                 self.object.tags[k] = nil
115                 self.names[k] = v
116                 if data.include_on_name ~= false then
117                     self.has_name = true
118                 end
119                 count = count + 1
120             end
121         end
122     end
123
124     return count
125 end
126
127 function Place:grab_tag(key)
128     return self.object:grab_tag(key)
129 end
130
131 function Place:tags()
132     return self.object.tags
133 end
134
135 function Place:write_place(k, v, mtype, save_extra_mains)
136     if mtype == nil then
137         return 0
138     end
139
140     v = v or self.object.tags[k]
141     if v == nil then
142         return 0
143     end
144
145     if type(mtype) == 'table' then
146         mtype = mtype[v] or mtype[1]
147     end
148
149     if mtype == 'always' or (self.has_name and mtype == 'named') then
150         return self:write_row(k, v, save_extra_mains)
151     end
152
153     if mtype == 'named_with_key' then
154         local names = {}
155         local prefix = k .. ':name'
156         for namek, namev in pairs(self.object.tags) do
157             if namek:sub(1, #prefix) == prefix
158                and (#namek == #prefix
159                     or namek:sub(#prefix + 1, #prefix + 1) == ':') then
160                 names[namek:sub(#k + 2)] = namev
161             end
162         end
163
164         if next(names) ~= nil then
165             local saved_names = self.names
166             self.names = names
167
168             local results = self:write_row(k, v, save_extra_mains)
169
170             self.names = saved_names
171
172             return results
173         end
174     end
175
176     return 0
177 end
178
179 function Place:write_row(k, v, save_extra_mains)
180     if self.geometry == nil then
181         self.geometry = self.geom_func(self.object)
182     end
183     if self.geometry:is_null() then
184         return 0
185     end
186
187     if save_extra_mains then
188         for extra_k, extra_v in pairs(self.object.tags) do
189             if extra_k ~= k then
190                 self.extratags[extra_k] = extra_v
191             end
192         end
193     end
194
195     place_table:insert{
196         class = k,
197         type = v,
198         admin_level = self.admin_level,
199         name = next(self.names) and self.names,
200         address = next(self.address) and self.address,
201         extratags = next(self.extratags) and self.extratags,
202         geometry = self.geometry
203     }
204
205     if save_extra_mains then
206         for k, v in pairs(self.object.tags) do
207             self.extratags[k] = nil
208         end
209     end
210
211     self.num_entries = self.num_entries + 1
212
213     return 1
214 end
215
216
217 function tag_match(data)
218     if data == nil or next(data) == nil then
219         return nil
220     end
221
222     local fullmatches = {}
223     local key_prefixes = {}
224     local key_suffixes = {}
225
226     if data.keys ~= nil then
227         for _, key in pairs(data.keys) do
228             if key:sub(1, 1) == '*' then
229                 if #key > 1 then
230                     if key_suffixes[#key - 1] == nil then
231                         key_suffixes[#key - 1] = {}
232                     end
233                     key_suffixes[#key - 1][key:sub(2)] = true
234                 end
235             elseif key:sub(#key, #key) == '*' then
236                 if key_prefixes[#key - 1] == nil then
237                     key_prefixes[#key - 1] = {}
238                 end
239                 key_prefixes[#key - 1][key:sub(1, #key - 1)] = true
240             else
241                 fullmatches[key] = true
242             end
243         end
244     end
245
246     if data.tags ~= nil then
247         for k, vlist in pairs(data.tags) do
248             if fullmatches[k] == nil then
249                 fullmatches[k] = {}
250                 for _, v in pairs(vlist) do
251                     fullmatches[k][v] = true
252                 end
253             end
254         end
255     end
256
257     return function (k, v)
258         if fullmatches[k] ~= nil and (fullmatches[k] == true or fullmatches[k][v] ~= nil) then
259             return true
260         end
261
262         for slen, slist in pairs(key_suffixes) do
263             if #k >= slen and slist[k:sub(-slen)] ~= nil then
264                 return true
265             end
266         end
267
268         for slen, slist in pairs(key_prefixes) do
269             if #k >= slen and slist[k:sub(1, slen)] ~= nil then
270                 return true
271             end
272         end
273
274         return false
275     end
276 end
277
278
279 -- Process functions for all data types
280 function osm2pgsql.process_node(object)
281
282     local function geom_func(o)
283         return o:as_point()
284     end
285
286     process_tags(Place.new(object, geom_func))
287 end
288
289 function osm2pgsql.process_way(object)
290
291     local function geom_func(o)
292         local geom = o:as_polygon()
293
294         if geom:is_null() then
295             geom = o:as_linestring()
296         end
297
298         return geom
299     end
300
301     process_tags(Place.new(object, geom_func))
302 end
303
304 function relation_as_multipolygon(o)
305     return o:as_multipolygon()
306 end
307
308 function relation_as_multiline(o)
309     return o:as_multilinestring():line_merge()
310 end
311
312 function osm2pgsql.process_relation(object)
313     local geom_func = RELATION_TYPES[object.tags.type]
314
315     if geom_func ~= nil then
316         process_tags(Place.new(object, geom_func))
317     end
318 end
319
320 function process_tags(o)
321     local fallback
322
323     o:delete{match = PRE_DELETE}
324     o:grab_extratags{match = PRE_EXTRAS}
325
326     -- Exception for boundary/place double tagging
327     if o.object.tags.boundary == 'administrative' then
328         o:grab_extratags{match = function (k, v)
329             return k == 'place' and v:sub(1,3) ~= 'isl'
330         end}
331     end
332
333     -- address keys
334     o:grab_address{match=COUNTRY_TAGS, out_key='country'}
335     if o.address.country ~= nil and #o.address.country ~= 2 then
336         o.address['country'] = nil
337     end
338     if o:grab_name{match=HOUSENAME_TAGS} > 0 then
339         fallback = {'place', 'house'}
340     end
341     if o:grab_address{match=HOUSENUMBER_TAGS, include_on_name = true} > 0 and fallback == nil then
342         fallback = {'place', 'house'}
343     end
344     if o:grab_address{match=POSTCODES, out_key='postcode'} > 0 and fallback == nil then
345         fallback = {'place', 'postcode'}
346     end
347
348     local is_interpolation = o:grab_address{match=INTERPOLATION_TAGS} > 0
349
350     if ADD_TIGER_COUNTY then
351         local v = o:grab_tag('tiger:county')
352         if v ~= nil then
353             v, num = v:gsub(',.*', ' county')
354             if num == 0 then
355                 v = v .. ' county'
356             end
357             o:set_address('tiger:county', v)
358         end
359     end
360     o:grab_address{match=ADDRESS_TAGS}
361
362     if is_interpolation then
363         o:write_place('place', 'houses', 'always', SAVE_EXTRA_MAINS)
364         return
365     end
366
367     -- name keys
368     o:grab_name{match = NAMES}
369     o:grab_name{match = REFS, include_on_name = false}
370
371     o:delete{match = POST_DELETE}
372     o:grab_extratags{match = POST_EXTRAS}
373
374     -- collect main keys
375     local num_mains = 0
376     for k, v in pairs(o:tags()) do
377         num_mains = num_mains + o:write_place(k, v, MAIN_KEYS[k], SAVE_EXTRA_MAINS)
378     end
379
380     if num_mains == 0 then
381         for tag, mtype in pairs(MAIN_FALLBACK_KEYS) do
382             if o:write_place(tag, nil, mtype, SAVE_EXTRA_MAINS) > 0 then
383                 return
384             end
385         end
386
387         if fallback ~= nil then
388             o:write_place(fallback[1], fallback[2], 'always', SAVE_EXTRA_MAINS)
389         end
390     end
391 end
392
393