-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserve_tile.lua
129 lines (104 loc) · 3.42 KB
/
serve_tile.lua
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
local S = require "syscall"
local mapnik = require "mapnik"
local fs = require "fs"
local min = math.min
local max = math.max
local log = math.log
local pow = math.pow
-- Modify this to validate the urls requested. This function (including parameters) can be
-- modified to suit your specific needs.
-- It should ensure that an invalid url returns 404
local function validate_url(source, layertype, pathrow, date)
-- define valid sources here
local valid_sources = {
l8 = true,
s2a = true,
uav = true,
mgrs = true
}
if valid_sources[source] then
return ngx.var.xmlroot .. ngx.var.xmlpath
end
ngx.status = 404
ngx.log(ngx.NOTICE, "Invalid tile request url")
ngx.exit(ngx.OK)
end
--- GEOMETRY ---
-- new coords with zoom applied
local function zoomTo(zoom_factor, z, x, y)
return x * pow(2, zoom_factor - z), y * pow(2, zoom_factor - z), zoom_factor
end
-- convert from Slippy Map coordinate to Web Mercator point
-- see https://en.wikipedia.org/wiki/Web_Mercator
local function coordinateProj(z, x, y)
-- zoom for meters on the ground
local diameter = 2 * math.pi * 6378137
local zoom_factor = log(diameter) / log(2)
local x, y, z = zoomTo(zoom_factor, z, x, y)
-- global offsets
x = x - diameter/2
y = diameter/2 - y
return x, y
end
-- projected rendering envelope (xmin, ymin, xmax, ymax) for Slippy map coord
local function envelope(z, x, y)
-- get upper left coords
local ul_x, ul_y = coordinateProj(z, x, y)
-- lower right can be determined from upper left of diagonally adjacent tile
local lr_x, lr_y = coordinateProj(z, x+1, y+1)
return min(ul_x, lr_x), min(ul_y, lr_y), max(ul_x, lr_x), max(ul_y, lr_y)
end
---- MAIN ----
-- get request path variables
local source, pathrow, type, date, x, y, z =
ngx.var.source, ngx.var.pathrow, ngx.var.type, ngx.var.date, ngx.var.x, ngx.var.y, ngx.var.z
-- validate the url according to custom logic
-- 404 redirect on invalid url
local xmlpath = validate_url(source, type, pathrow, date)
local result = mapnik:register_datasources(ngx.var.mapnik_datasource)
if result ~= 0 then
ngx.log(ngx.ERR, "failed to register datasource")
end
local map = mapnik:map(256,256)
---- load xml and get map
if xmlpath == nil then
xmlpath = ngx.var.xmlroot .. ngx.var.xmlpath
end
result = mapnik:map_load(map, xmlpath)
if result ~= 0 then
ngx.log(ngx.ERR, "failed to load " .. xmlpath)
local errstr = mapnik:last_error(map)
ngx.log(ngx.ERR, errstr)
ngx.exit(0)
end
-- derive web mercator bounds for slippy map tile
xmin, ymin, xmax, ymax = envelope(z, x, y)
local box = mapnik:bbox(xmin, ymin, xmax, ymax)
if result ~= 0 then
ngx.log(ngx.ERR, "failed to create bounding box")
local errstr = mapnik:last_error(map)
ngx.log(ngx.ERR, errstr)
ngx.exit(0)
end
mapnik:map_zoom_to_box(map, box)
-- match to trailing slash then remove slash
local dirpath = ngx.var.cachepath:match(".*/"):sub(1,-2)
if not S.stat(ngx.var.cacheroot .. dirpath) then
fs:mkdir(dirpath)
end
-- render image
local file_cache_path = ngx.var.cacheroot .. ngx.var.cachepath
result = mapnik:map_render_to_file(map, file_cache_path)
-- log where tile is being written to
ngx.log(ngx.NOTICE, "Writing to " .. file_cache_path)
if result ~= 0 then
ngx.log(ngx.ERR, "failed to render image")
local errstr = mapnik:last_error(map)
ngx.log(ngx.ERR, errstr)
ngx.exit(0)
end
-- free up resources
mapnik:bbox_free(box)
mapnik:map_free(map)
-- trigger new internal request
ngx.exec(ngx.var.request_uri)