forked from TUM-Dev/NavigaTUM
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstyle.lua
More file actions
504 lines (487 loc) · 17.8 KB
/
Copy pathstyle.lua
File metadata and controls
504 lines (487 loc) · 17.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
-- See https://github.com/osm2pgsql-dev/osm2pgsql/tree/master/flex-config
-- for configuration examples
SantiseLevel = require(".map.osm2pgsql.levels")
-- For debugging
-- inspect = require('inspect')
-- print(inspect(object))
print("osm2pgsql version: " .. osm2pgsql.version)
local tables = {}
tables.doors =
osm2pgsql.define_node_table(
"doors",
{
{ column = "width_cm", type = "integer", not_null = true },
{ column = "level_min", type = "real", not_null = true },
{ column = "level_max", type = "real", not_null = true },
{ column = "geom", type = "point", not_null = true }
}
)
tables.indoor_ways =
osm2pgsql.define_way_table(
"indoor_ways",
{
{ column = "level_min", type = "real", not_null = true },
{ column = "level_max", type = "real", not_null = true },
{ column = "geom", type = "linestring", not_null = true }
}
)
tables.rooms =
osm2pgsql.define_area_table(
"rooms",
{
{ column = "indoor", type = "text", not_null = true },
{ column = "ref_tum", type = "text" },
{ column = "students_have_access", type = "boolean", not_null = true },
-- Mirror the WC attribute flags carried on `pois`, so room-fill styling can dim
-- non-matching toilet rooms the same way the icon filter hides their POIs.
{ column = "is_male_toilet", type = "boolean", not_null = true },
{ column = "is_female_toilet", type = "boolean", not_null = true },
{ column = "is_unisex_toilet", type = "boolean", not_null = true },
{ column = "is_wheelchair_toilet", type = "boolean", not_null = true },
{ column = "level_min", type = "real", not_null = true },
{ column = "level_max", type = "real", not_null = true },
-- The type of the `geom` column is `geometry`, because we need to store
-- polygons AND multipolygons
{ column = "geom", type = "geometry", not_null = true }
}
)
tables.pois =
osm2pgsql.define_table(
{
name = "pois",
-- `any` ids (osm_type + osm_id) so nodes can share this table: the `area` id type a
-- `define_area_table` gives rejects node inserts, but bare amenity=toilets points are nodes.
ids = { type = "any", id_column = "osm_id", type_column = "osm_type" },
columns = {
{ column = "indoor", type = "text", not_null = true },
{ column = "ref", type = "text" },
{ column = "name", type = "text" },
{ column = "students_have_access", type = "boolean", not_null = true },
{ column = "is_male_toilet", type = "boolean", not_null = true },
{ column = "is_female_toilet", type = "boolean", not_null = true },
{ column = "is_unisex_toilet", type = "boolean", not_null = true },
{ column = "is_wheelchair_toilet", type = "boolean", not_null = true },
{ column = "area", type = "real", not_null = true },
{ column = "level_min", type = "real", not_null = true },
{ column = "level_max", type = "real", not_null = true },
-- a point is all we render; the geometry is reduced to one before insertion.
{ column = "geom", type = "point", not_null = true }
}
}
)
-- Debug output: Show definition of tables
for name, _ in pairs(tables) do
print("\ntable '" .. name .. "'")
-- print(" name='" .. dtable:name() .. "'")
-- print(" columns=" .. inspect(dtable:columns()))
end
-- These tag keys are generally regarded as useless for most rendering. Most
-- of them are from imports or intended as internal information for mappers.
--
-- If a key ends in '*' it will match all keys with the specified prefix.
--
-- If you want some of these keys, perhaps for a debugging layer, just
-- delete the corresponding lines.
local delete_keys = {
-- "mapper" keys
"attribution",
"comment",
"created_by",
"fixme",
"note",
"note:*",
"odbl",
"odbl:note",
"source",
"source:*",
"source_ref",
-- "import" keys
-- Corine Land Cover (CLC) (Europe)
"CLC:*",
-- Geobase (CA)
"geobase:*",
-- CanVec (CA)
"canvec:*",
-- osak (DK)
"osak:*",
-- kms (DK)
"kms:*",
-- ngbe (ES)
-- See also note:es and source:file above
"ngbe:*",
-- Friuli Venezia Giulia (IT)
"it:fvg:*",
-- KSJ2 (JA)
-- See also note:ja and source_ref above
"KSJ2:*",
-- Yahoo/ALPS (JA)
"yh:*",
-- LINZ (NZ)
"LINZ2OSM:*",
"linz2osm:*",
"LINZ:*",
"ref:linz:*",
-- WroclawGIS (PL)
"WroclawGIS:*",
-- Naptan (UK)
"naptan:*",
-- TIGER (US)
"tiger:*",
-- GNIS (US)
"gnis:*",
-- National Hydrography Dataset (US)
"NHD:*",
"nhd:*",
-- mvdgis (Montevideo, UY)
"mvdgis:*",
-- EUROSHA (Various countries)
"project:eurosha_2012",
-- UrbIS (Brussels, BE)
"ref:UrbIS",
-- NHN (CA)
"accuracy:meters",
"sub_sea:type",
"waterway:type",
-- StatsCan (CA)
"statscan:rbuid",
-- RUIAN (CZ)
"ref:ruian:addr",
"ref:ruian",
"building:ruian:type",
-- DIBAVOD (CZ)
"dibavod:id",
-- UIR-ADR (CZ)
"uir_adr:ADRESA_KOD",
-- GST (DK)
"gst:feat_id",
-- Maa-amet (EE)
"maaamet:ETAK",
-- FANTOIR (FR)
"ref:FR:FANTOIR",
-- 3dshapes (NL)
"3dshapes:ggmodelk",
-- AND (NL)
"AND_nosr_r",
-- OPPDATERIN (NO)
"OPPDATERIN",
-- Various imports (PL)
"addr:city:simc",
"addr:street:sym_ul",
"building:usage:pl",
"building:use:pl",
-- TERYT (PL)
"teryt:simc",
-- RABA (SK)
"raba:id",
-- DCGIS (Washington DC, US)
"dcgis:gis_id",
-- Building Identification Number (New York, US)
"nycdoitt:bin",
-- Chicago Building Inport (US)
"chicago:building_id",
-- Louisville, Kentucky/Building Outlines Import (US)
"lojic:bgnum",
-- MassGIS (Massachusetts, US)
"massgis:way_id",
-- Los Angeles County building ID (US)
"lacounty:*",
-- Address import from Bundesamt für Eich- und Vermessungswesen (AT)
"at_bev:addr_date",
-- misc
"import",
"import_uuid",
"OBJTYPE",
"SK53_bulk:load",
"mml:class",
-- we are not doing 3D
"height"
}
local clean_useless_tags = osm2pgsql.make_clean_tags_func(delete_keys)
-- Helper function to remove some of the tags we usually are not interested in.
-- Returns true if there are no tags left.
local function clean_tags_indoor(tags)
if clean_useless_tags(tags) then
return true
end
-- clean up the indoor tags
-- not relevant for us
if (tags.indoor == nil and tags.level == nil) or tags.indoor == "no" or tags.indoor == "false" or tags.indoor == "level" then
return true
end
-- clean up levels and layer misconceptions
if tags.level == nil then
if tags.layer ~= nil then
-- usually, this is something which is wrongly tagged or if we use the layer, it has the same effect
tags.level = tags.layer
else
tags.level = "0"
end
end
-- infer the indoor tag
if tags.indoor == nil then
-- need to infer indoor tag
if tags.inside ~= nil then
tags.indoor = tags.inside
elseif tags.room ~= nil then
tags.indoor = "room"
elseif tags.area ~= nil then
tags.indoor = "area"
else
tags.indoor = "yes"
end
end
tags.inside = nil -- used to infer indoor, but nothing else
-- to simplify our data model, we smudge some room= tags into the indoor= space
if tags.indoor == "room" then
if tags.room == "toilet" or tags.room == "toilets" or tags.room == "shower" or tags.room == "bathroom" or tags.amenity == "toilet" or tags.amenity == "toilets" then
tags.indoor = "toilet"
elseif tags.amenity == "shower" or tags.amenity == "showers" or tags.room == "shower" or tags.room == "showers" or tags.indoor == "shower" or tags.indoor == "showers" then
tags.indoor = "shower"
elseif tags.room == "elevator" then
tags.indoor = "elevator"
elseif tags.room == "stairs" then
tags.indoor = "stairs"
elseif tags.room == "auditorium" or tags.room == "lecture_hall" then
tags.indoor = "auditorium"
end
end
-- we will never show more than a poi icon here
if tags.indoor == "elevator" or tags.indoor == "shower" or tags.indoor == "toilet" then
tags.ref = nil
tags.name = nil
end
-- why are there so many objects with just the layer set, nothing else
if tags.indoor == nil and tags.level ~= nil and #(tags) == 1 then
return true
end
-- why do people like mapping clocks so much??
-- they are not usefully for us (or likely anybody)
if tags.amenity == "clock" then
return true
end
return next(tags) == nil
end
-- Called for every node in the input. The `object` argument contains all the
-- attributes of the node like `id`, `version`, etc. as well as all tags as a
-- Lua table (`object.tags`).
function osm2pgsql.process_node(object)
-- Uncomment next line to look at the object data:
-- print(inspect(object))
if clean_tags_indoor(object.tags) then
return
end
-- Bare amenity=toilets/shower nodes lack a room= tag, so normalise them into the poi space
-- here (nodes only; ways/relations get smudged in clean_tags_indoor). The indoor-or-level
-- guard there keeps context-less outdoor amenities out.
if object.tags.indoor ~= "toilet" and object.tags.indoor ~= "shower" then
if object.tags.amenity == "toilet" or object.tags.amenity == "toilets" then
object.tags.indoor = "toilet"
elseif object.tags.amenity == "shower" or object.tags.amenity == "showers" then
object.tags.indoor = "shower"
end
end
-- Student-card validators are vending machines in OSM; lift them into their own poi category.
if object.tags.amenity == "vending_machine" and object.tags.vending == "student_card_validation" then
object.tags.indoor = "card_validator"
end
if object.tags.indoor == "door" then
-- pois should not need layers. Using them is likely a bug
object.tags.layer = nil
-- we want the width_cm, no width_m
-- invalid or unset widths get 86cm
if object.tags.width ~= nil then
object.tags.width = tonumber(object.tags.width)
end
if object.tags.width == nil then
object.tags.width = 86
else
object.tags.width = object.tags.width * 100
end
for _, level in ipairs(SantiseLevel(object.tags.level)) do
tables.doors:insert(
{
width_cm = object.tags.width,
level_min = level.min,
level_max = level.max,
geom = object:as_point()
}
)
end
elseif object.tags.indoor == "toilet" or object.tags.indoor == "shower" then
-- Point geometry has no area, so synthesize `area = 0` (the icon does not use it). No
-- name/ref: these render as icons only.
for _, level in ipairs(SantiseLevel(object.tags.level)) do
tables.pois:insert(
{
indoor = object.tags.indoor,
students_have_access = object.tags.access ~= "private" and object.tags.access ~= "no",
is_male_toilet = object.tags.indoor == "toilet" and object.tags.male == "yes",
is_female_toilet = object.tags.indoor == "toilet" and object.tags.female == "yes",
is_unisex_toilet = object.tags.indoor == "toilet" and object.tags.unisex == "yes",
is_wheelchair_toilet = object.tags.indoor == "toilet" and object.tags.wheelchair == "yes",
area = 0,
level_min = level.min,
level_max = level.max,
geom = object:as_point()
}
)
end
elseif object.tags.indoor == "card_validator" then
-- Unlike toilets, these render as named markers, so carry name + ref:tum into the poi.
for _, level in ipairs(SantiseLevel(object.tags.level)) do
tables.pois:insert(
{
indoor = object.tags.indoor,
name = object.tags.name,
ref = object.tags["ref:tum"],
students_have_access = true,
is_male_toilet = false,
is_female_toilet = false,
is_unisex_toilet = false,
is_wheelchair_toilet = false,
area = 0,
level_min = level.min,
level_max = level.max,
geom = object:as_point()
}
)
end
end
end
-- Called for every way in the input. The `object` argument contains the same
-- information as with nodes and additionally a boolean `is_closed` flag and
-- the list of node IDs referenced by the way (`object.nodes`).
function osm2pgsql.process_way(object)
-- Uncomment next line to look at the object data:
-- print(inspect(object))
if object.tags.building ~= nil then
object.tags.indoor = nil
object.tags.level = nil
object.tags.inside = nil
elseif clean_tags_indoor(object.tags) then
return
end
for _, level in ipairs(SantiseLevel(object.tags.level)) do
object.tags.level_min = level.min
object.tags.level_max = level.max
-- Very simple check to decide whether a way is a polygon or not, in a
-- real stylesheet we'd have to also look at the tags...
if object.is_closed then
local geom = object:as_polygon()
if object.tags.indoor ~= "stairs" then
tables.rooms:insert(
{
indoor = object.tags.indoor,
ref_tum = object.tags["ref:tum"],
students_have_access = object.tags.access ~= "private" and object.tags.access ~= "no",
is_male_toilet = object.tags.indoor == "toilet" and object.tags.male == "yes",
is_female_toilet = object.tags.indoor == "toilet" and object.tags.female == "yes",
is_unisex_toilet = object.tags.indoor == "toilet" and object.tags.unisex == "yes",
is_wheelchair_toilet = object.tags.indoor == "toilet" and object.tags.wheelchair == "yes",
level_min = level.min,
level_max = level.max,
geom = geom
}
)
end
if object.tags.indoor ~= "corridor" then
local geom_point = nil
if object.tags.indoor == "elevator" then
-- elevators get an icon -> needs no need to strech
-- looks slightly better if in the middle
geom_point = geom:centroid()
else
geom_point = geom:pole_of_inaccessibility({ stretch = 2 })
end
tables.pois:insert(
{
indoor = object.tags.indoor,
name = object.tags.name,
ref = object.tags.ref,
students_have_access = object.tags.access ~= "private" and object.tags.access ~= "no",
is_male_toilet = object.tags.indoor == "toilet" and object.tags.male == "yes",
is_female_toilet = object.tags.indoor == "toilet" and object.tags.female == "yes",
is_unisex_toilet = object.tags.indoor == "toilet" and object.tags.unisex == "yes",
is_wheelchair_toilet = object.tags.indoor == "toilet" and object.tags.wheelchair == "yes",
area = geom:spherical_area(),
level_min = level.min,
level_max = level.max,
geom = geom_point
}
)
end
else
tables.indoor_ways:insert(
{
level_min = level.min,
level_max = level.max,
indoor = object.tags.indoor,
geom = object:as_linestring()
}
)
end
end
end
-- Called for every relation in the input. The `object` argument contains the
-- same information as with nodes and additionally an array of members
-- (`object.members`).
function osm2pgsql.process_relation(object)
-- Uncomment next line to look at the object data:
-- print(inspect(object))
if clean_tags_indoor(object.tags) then
return
end
-- Store multipolygons and boundaries as polygons
if object.tags.type == "multipolygon" or
object.tags.type == "boundary" then
local geom = object:as_multipolygon()
for _, level in ipairs(SantiseLevel(object.tags.level)) do
if object.tags.indoor ~= "stairs" then
tables.rooms:insert(
{
indoor = object.tags.indoor,
ref_tum = object.tags["ref:tum"],
students_have_access = object.tags.access ~= "private" and object.tags.access ~= "no",
is_male_toilet = object.tags.indoor == "toilet" and object.tags.male == "yes",
is_female_toilet = object.tags.indoor == "toilet" and object.tags.female == "yes",
is_unisex_toilet = object.tags.indoor == "toilet" and object.tags.unisex == "yes",
is_wheelchair_toilet = object.tags.indoor == "toilet" and object.tags.wheelchair == "yes",
level_min = level.min,
level_max = level.max,
geom = geom
}
)
end
if object.tags.indoor ~= "corridor" then
-- The pole_of_inaccessibility() function only works for polygons,
-- not multipolygons. So we split up the multipolygons here and
-- calculate the pole for each part separately.
for g in geom:geometries() do
local geom_point = nil
if object.tags.indoor == "elevator" then
-- elevators get an icon -> needs no need to strech
-- looks slightly better if in the middle
geom_point = geom:centroid()
else
geom_point = geom:pole_of_inaccessibility({ stretch = 2 })
end
tables.pois:insert(
{
indoor = object.tags.indoor,
ref = object.tags.ref,
students_have_access = object.tags.access ~= "private" and object.tags.access ~= "no",
is_male_toilet = object.tags.indoor == "toilet" and object.tags.male,
is_female_toilet = object.tags.indoor == "toilet" and object.tags.female,
is_unisex_toilet = object.tags.indoor == "toilet" and object.tags.unisex,
is_wheelchair_toilet = object.tags.indoor == "toilet" and object.tags.wheelchair == "yes",
area = g:area(),
level_min = level.min,
level_max = level.max,
geom = geom_point
}
)
end
end
end
end
end