Skip to content

Commit cec2320

Browse files
authored
Merge pull request #14 from pka/bbox-config-gen
Add bbox plugin
2 parents 6aabbf5 + e072ca8 commit cec2320

File tree

5 files changed

+343
-2
lines changed

5 files changed

+343
-2
lines changed

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ how to create one yourself.
3030
The framework has support for plugins adding some functionality. They are in
3131
the `lua/themepark/plugins` directory.
3232

33-
Available plugins are `taginfo`, `tilekiln`, and `t-rex`.
33+
Available plugins are `taginfo`, `tilekiln`, `t-rex` and `bbox`.
3434

3535
### Plugin `taginfo`
3636

@@ -80,6 +80,27 @@ with options. Available options are:
8080
* `extra_layers`: Extra layers that should be added to the config file. Use
8181
the same structure as the T-Rex config file would use.
8282

83+
### Plugin `bbox`
84+
85+
This plugin can be used to create a config file for the
86+
[BBOX](https://www.bbox.earth/) tile server.
87+
88+
Call like this from your config file to create a file called
89+
`bbox-config.toml`:
90+
91+
```
92+
themepark:plugin('bbox'):write_config('bbox-config.toml')
93+
```
94+
95+
A second argument on the `write_config()` function can contain a Lua table
96+
with options. Available options are:
97+
98+
* `tileset`: Name of the tileset, defaults to `osm`.
99+
* `attribution`: Set attribution string, defaults to the setting from the
100+
themepark config file.
101+
* `extra_layers`: Extra layers that should be added to the config file. Use
102+
the same structure as the BBOX config file would use.
103+
83104
## Themes
84105

85106
Themes provide building blocks for map data transformations. They usually

config/shortbread.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
-- If you are creating a tilekiln config you must also create
1111
-- the 'shortbread_config' directory.
1212
local TREX = false
13+
local BBOX = false
1314
local TILEKILN = false
1415
local TAGINFO = false
1516

@@ -70,6 +71,10 @@ if osm2pgsql.mode == 'create' then
7071
themepark:plugin('t-rex'):write_config('t-rex-config.toml', {})
7172
end
7273

74+
if BBOX then
75+
themepark:plugin('bbox'):write_config('bbox-config.toml', {})
76+
end
77+
7378
if TILEKILN then
7479
themepark:plugin('tilekiln'):write_config('shortbread_config', {
7580
tileset = 'shortbread_v1',

config/shortbread_gen.lua

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
-- If you are creating a tilekiln config you must also create
1111
-- the 'shortbread_config' directory.
1212
local TREX = false
13+
local BBOX = false
1314
local TILEKILN = false
1415

1516
local themepark = require('themepark')
@@ -100,6 +101,37 @@ SELECT "name","name_de","name_en","kind","layer","ref","ref_rows","ref_cols","z_
100101
}
101102
})
102103
end
104+
if BBOX then
105+
themepark:plugin('bbox'):write_config('bbox-config.toml', {
106+
tileset = 'osm',
107+
extra_layers = {
108+
{
109+
buffer_size = 10,
110+
name = 'street_labels',
111+
geometry_type = 'LINESTRING',
112+
query = {
113+
{
114+
minzoom = 14,
115+
sql = [[
116+
SELECT "name","name_de","name_en","kind","layer","ref","ref_rows","ref_cols","z_order","geom"
117+
FROM "streets"
118+
WHERE !zoom! >= "minzoom"
119+
ORDER BY "z_order" asc]]
120+
},
121+
{
122+
minzoom = 11,
123+
maxzoom = 13,
124+
sql = [[
125+
SELECT "name","name_de","name_en","kind","layer","ref","ref_rows","ref_cols","z_order","geom"
126+
FROM "streets_med"
127+
WHERE !zoom! >= "minzoom"
128+
ORDER BY "z_order" asc]]
129+
},
130+
}
131+
}
132+
}
133+
})
134+
end
103135
if TILEKILN then
104136
themepark:plugin('tilekiln'):write_config('shortbread_config', {
105137
tileset = 'shortbread_v1',

lua/themepark/plugins/bbox.lua

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
-- ---------------------------------------------------------------------------
2+
--
3+
-- Osm2pgsql Themepark
4+
--
5+
-- A framework for pluggable osm2pgsql config files.
6+
--
7+
-- ---------------------------------------------------------------------------
8+
--
9+
-- lib/themepark/plugins/bbox.lua
10+
--
11+
-- ---------------------------------------------------------------------------
12+
--
13+
-- Copyright 2024 Jochen Topf <[email protected]>
14+
--
15+
-- Licensed under the Apache License, Version 2.0 (the "License");
16+
-- you may not use this file except in compliance with the License.
17+
-- You may obtain a copy of the License at
18+
--
19+
-- https://www.apache.org/licenses/LICENSE-2.0
20+
--
21+
-- Unless required by applicable law or agreed to in writing, software
22+
-- distributed under the License is distributed on an "AS IS" BASIS,
23+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24+
-- See the License for the specific language governing permissions and
25+
-- limitations under the License.
26+
--
27+
-- ---------------------------------------------------------------------------
28+
29+
local plugin = {}
30+
31+
local utils = require 'themepark/utils'
32+
33+
local min_max_zoom_columns = function(columns)
34+
local column_names = {}
35+
local minzoom
36+
local maxzoom
37+
38+
for _, column in ipairs(columns) do
39+
if column.tiles == 'minzoom' then
40+
minzoom = column.column
41+
elseif column.tiles == 'maxzoom' then
42+
maxzoom = column.column
43+
elseif column.tiles ~= false and column.column ~= 'all_tags' then
44+
table.insert(column_names, '"' .. column.column .. '"')
45+
end
46+
end
47+
48+
return minzoom, maxzoom, column_names
49+
end
50+
51+
local function assemble_conditions(minzoom, maxzoom, xycondition)
52+
local conditions = {}
53+
54+
if minzoom then
55+
table.insert(conditions, '!zoom! >= "' .. minzoom .. '"')
56+
end
57+
if maxzoom then
58+
table.insert(conditions, '!zoom! <= "' .. maxzoom .. '"')
59+
end
60+
61+
if xycondition then
62+
table.insert(conditions, '!x! = x AND !y! = y')
63+
end
64+
65+
return conditions
66+
end
67+
68+
local function build_layer_config(info)
69+
local schema_prefix = ""
70+
if plugin.themepark.options.schema then
71+
schema_prefix = plugin.themepark.options.schema .. "."
72+
end
73+
local data = {
74+
name = info.name,
75+
geometry_field = info.geom_column,
76+
geometry_type = string.upper(info.geom_type),
77+
srid = plugin.themepark.options.srid,
78+
buffer_size = 10,
79+
}
80+
81+
local tiles = info.tiles or {}
82+
if tiles.group then
83+
return
84+
end
85+
86+
if tiles.simplify ~= nil then
87+
data.simplify = tiles.simplify
88+
end
89+
90+
if tiles.make_valid ~= nil then
91+
data.make_valid = tiles.make_valid
92+
end
93+
94+
if tiles.buffer_size ~= nil then
95+
data.buffer_size = tiles.buffer_size
96+
end
97+
98+
if not plugin.themepark.layer_groups[info.name] then
99+
if tiles.minzoom then
100+
data.minzoom = tiles.minzoom
101+
end
102+
if tiles.maxzoom then
103+
data.maxzoom = tiles.maxzoom
104+
end
105+
end
106+
107+
if tiles.sql then
108+
data.query = {{
109+
sql = info.sql
110+
}}
111+
else
112+
local minzoom, maxzoom, columns = min_max_zoom_columns(info.columns)
113+
local from = schema_prefix .. info.name
114+
if plugin.themepark.layer_groups[info.name] then
115+
local conditions = assemble_conditions(minzoom, maxzoom, tiles.xycondition)
116+
local sql = utils.build_select_query(columns, from, conditions, tiles.order_by, tiles.order_dir)
117+
118+
data.query = {{
119+
sql = sql,
120+
}}
121+
if tiles.minzoom then
122+
data.query[1].minzoom = tiles.minzoom
123+
end
124+
if tiles.maxzoom then
125+
data.query[1].maxzoom = tiles.maxzoom
126+
end
127+
else
128+
local conditions = assemble_conditions(minzoom, maxzoom, tiles.xycondition)
129+
local sql = utils.build_select_query(columns, from, conditions, tiles.order_by, tiles.order_dir)
130+
data.query = {{
131+
sql = sql,
132+
}}
133+
end
134+
135+
if plugin.themepark.layer_groups[info.name] then
136+
for _, group_table in ipairs(plugin.themepark.layer_groups[info.name]) do
137+
local sublayer = utils.find_name_in_array(plugin.themepark.layers, group_table.name)
138+
minzoom, maxzoom, columns = min_max_zoom_columns(sublayer.columns)
139+
140+
local conditions = assemble_conditions(minzoom, maxzoom, sublayer.tiles.xycondition)
141+
local subfrom = schema_prefix .. group_table.name
142+
local sql = utils.build_select_query(columns, subfrom,
143+
conditions, sublayer.tiles.order_by,
144+
sublayer.tiles.order_dir)
145+
table.insert(data.query, {
146+
sql = sql,
147+
minzoom = group_table.minzoom,
148+
maxzoom = group_table.maxzoom,
149+
})
150+
end
151+
end
152+
end
153+
154+
return data
155+
end
156+
157+
-- ---------------------------------------------------------------------------
158+
-- ---------------------------------------------------------------------------
159+
local function build_tileset_config(extra_layers)
160+
local layers = {}
161+
162+
for _, layer in ipairs(plugin.themepark.layers) do
163+
if layer.tiles ~= false then
164+
local layer_config = build_layer_config(layer)
165+
if layer_config then
166+
table.insert(layers, layer_config)
167+
end
168+
end
169+
end
170+
for _, data in ipairs(extra_layers) do
171+
if not data.geometry_field then
172+
data.geometry_field = 'geom'
173+
end
174+
if not data.srid then
175+
data.srid = plugin.themepark.options.srid
176+
end
177+
table.insert(layers, data)
178+
end
179+
return layers
180+
end
181+
182+
local function ordered_keys(o)
183+
local keys1 = {}
184+
local keys2 = {}
185+
for k, v in pairs(o) do
186+
if type(v) == 'table' then
187+
table.insert(keys2, k)
188+
else
189+
table.insert(keys1, k)
190+
end
191+
end
192+
table.sort(keys1)
193+
table.sort(keys2)
194+
for _, v in ipairs(keys2) do
195+
table.insert(keys1, v)
196+
end
197+
return keys1
198+
end
199+
200+
local function dump_toml(o, indent_size, parents)
201+
indent_size = indent_size or 2
202+
parents = parents or {}
203+
if type(o) == 'table' then
204+
local s = ''
205+
local indent = string.rep(" ", #parents * indent_size)
206+
-- sort keys for stable output order
207+
for _, k in ipairs(ordered_keys(o)) do
208+
local v = o[k]
209+
if type(v) == 'table' then
210+
local array_val = v[1] ~= nil
211+
if array_val then
212+
table.insert(parents, k)
213+
s = s .. dump_toml(v, indent_size, parents)
214+
table.remove(parents)
215+
elseif type(k) == 'number' then
216+
local tag = table.concat(parents, ".")
217+
s = s .. '\n\n' .. indent .. '[[' .. tag .. ']]' .. dump_toml(v, indent_size, parents)
218+
else
219+
table.insert(parents, k)
220+
indent = string.rep(" ", #parents * indent_size)
221+
local tag = table.concat(parents, ".")
222+
s = s .. '\n\n' .. indent .. '[' .. tag .. ']' .. dump_toml(v, indent_size, parents)
223+
table.remove(parents)
224+
end
225+
else
226+
if type(v) == 'string' then
227+
if string.find(v, '"') then
228+
v = '"""' ..v.. '"""'
229+
else
230+
v = '"' ..v.. '"'
231+
end
232+
end
233+
s = s .. '\n' .. indent .. k .. ' = ' .. dump_toml(v, indent_size, parents)
234+
end
235+
end
236+
return s
237+
else
238+
return tostring(o)
239+
end
240+
end
241+
242+
-- ---------------------------------------------------------------------------
243+
-- ---------------------------------------------------------------------------
244+
function plugin:write_config(filename, options)
245+
if not options then
246+
options = {}
247+
end
248+
249+
local config = {
250+
webserver = { server_addr = '127.0.0.1:8080' },
251+
datasource = {
252+
{
253+
name = 'db',
254+
postgis = {
255+
url = "postgres:///db" -- overwrite with env vars
256+
}
257+
}
258+
},
259+
tileset = {
260+
{
261+
name = options.tileset or 'osm',
262+
tms = 'WebMercatorQuad',
263+
postgis = {
264+
datasource = 'db',
265+
attribution = options.attribution or plugin.themepark.options.attribution,
266+
layer = build_tileset_config(options.extra_layers or {})
267+
}
268+
}
269+
}
270+
}
271+
272+
if plugin.themepark.options.extent then
273+
config.tileset[1].postgis.extent = plugin.themepark.options.extent
274+
end
275+
276+
utils.write_to_file(filename, dump_toml(config, 0) .. "\n")
277+
end
278+
279+
return plugin
280+
281+
-- ---------------------------------------------------------------------------

lua/themepark/utils.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ function utils.write_to_file(filename, content)
3838
end
3939

4040
function utils.build_select_query(columns, from, conditions, order_by, order_dir)
41+
-- Handle from with schema prefix
42+
local quoted_from = '"' .. string.gsub(from, '%.', '"."') .. '"'
4143
local sql = 'SELECT ' .. table.concat(columns, ',')
42-
.. ' FROM "' .. from .. '"'
44+
.. ' FROM ' .. quoted_from
4345

4446
if conditions and #conditions > 0 then
4547
sql = sql .. ' WHERE ' .. table.concat(conditions, ' AND ')

0 commit comments

Comments
 (0)