maps/backend/scripts/pois.lua
2026-03-30 10:27:36 +02:00

147 lines
4.9 KiB
Lua

-- scripts/poi_flex.lua
-- osm2pgsql flex output for POI extraction
local pois = osm2pgsql.define_table({
name = 'pois',
ids = { type = 'any', type_column = 'osm_type', id_column = 'osm_id' },
columns = {
{ column = 'name', type = 'text', not_null = true },
{ column = 'category', type = 'text', not_null = true },
{ column = 'geometry', type = 'point', projection = 4326, not_null = true },
{ column = 'address', type = 'jsonb' },
{ column = 'tags', type = 'jsonb' },
{ column = 'opening_hours', type = 'text' },
{ column = 'phone', type = 'text' },
{ column = 'website', type = 'text' },
{ column = 'wheelchair', type = 'text' },
},
})
-- Maps OSM amenity/shop/tourism/leisure tags to normalized categories
local category_map = {
-- amenity
restaurant = 'restaurant',
fast_food = 'restaurant',
cafe = 'cafe',
pharmacy = 'pharmacy',
hospital = 'hospital',
clinic = 'hospital',
fuel = 'fuel',
parking = 'parking',
atm = 'atm',
bank = 'atm',
bus_station = 'public_transport',
hotel = 'hotel',
-- shop
supermarket = 'supermarket',
convenience = 'shop',
clothes = 'shop',
hairdresser = 'shop',
bakery = 'shop',
-- tourism
attraction = 'tourist_attraction',
museum = 'tourist_attraction',
viewpoint = 'tourist_attraction',
-- leisure
park = 'park',
garden = 'park',
playground = 'park',
}
local function get_category(tags)
for _, key in ipairs({'amenity', 'shop', 'tourism', 'leisure'}) do
local val = tags[key]
if val and category_map[val] then
return category_map[val]
end
end
return nil
end
local function build_address(tags)
local addr = {}
if tags['addr:street'] then addr.street = tags['addr:street'] end
if tags['addr:housenumber'] then addr.housenumber = tags['addr:housenumber'] end
if tags['addr:postcode'] then addr.postcode = tags['addr:postcode'] end
if tags['addr:city'] then addr.city = tags['addr:city'] end
if next(addr) then return addr end
return nil
end
local function build_extra_tags(tags)
local extra = {}
local dominated = {
'name', 'amenity', 'shop', 'tourism', 'leisure',
'addr:street', 'addr:housenumber', 'addr:postcode', 'addr:city',
'opening_hours', 'phone', 'contact:phone',
'website', 'contact:website', 'wheelchair',
}
local skip = {}
for _, k in ipairs(dominated) do skip[k] = true end
for k, v in pairs(tags) do
if not skip[k] and not k:match('^addr:') then
extra[k] = v
end
end
if next(extra) then return extra end
return nil
end
function osm2pgsql.process_node(object)
local tags = object.tags
if not tags.name then return end
local category = get_category(tags)
if not category then return end
pois:insert({
name = tags.name,
category = category,
geometry = object:as_point(),
address = build_address(tags),
tags = build_extra_tags(tags),
opening_hours = tags.opening_hours,
phone = tags.phone or tags['contact:phone'],
website = tags.website or tags['contact:website'],
wheelchair = tags.wheelchair,
})
end
function osm2pgsql.process_way(object)
local tags = object.tags
if not tags.name then return end
local category = get_category(tags)
if not category then return end
if not object.is_closed then return end
pois:insert({
name = tags.name,
category = category,
geometry = object:as_polygon():centroid(),
address = build_address(tags),
tags = build_extra_tags(tags),
opening_hours = tags.opening_hours,
phone = tags.phone or tags['contact:phone'],
website = tags.website or tags['contact:website'],
wheelchair = tags.wheelchair,
})
end
function osm2pgsql.process_relation(object)
local tags = object.tags
if not tags.name then return end
local category = get_category(tags)
if not category then return end
if tags.type ~= 'multipolygon' then return end
pois:insert({
name = tags.name,
category = category,
geometry = object:as_multipolygon():centroid(),
address = build_address(tags),
tags = build_extra_tags(tags),
opening_hours = tags.opening_hours,
phone = tags.phone or tags['contact:phone'],
website = tags.website or tags['contact:website'],
wheelchair = tags.wheelchair,
})
end