source: subversion/applications/utils/mod_tile/gen_tile.cpp @ 15800

Last change on this file since 15800 was 15800, checked in by jochen, 10 years ago

renderd uses syslog now instead of logging messages to stderr
renderd has -c, --config option to set config file at runtime

File size: 10.5 KB
Line 
1#include <mapnik/map.hpp>
2#include <mapnik/datasource_cache.hpp>
3#include <mapnik/agg_renderer.hpp>
4#include <mapnik/filter_factory.hpp>
5#include <mapnik/color_factory.hpp>
6#include <mapnik/image_util.hpp>
7#include <mapnik/load_map.hpp>
8#include <mapnik/image_util.hpp>
9
10#include <iostream>
11#include <fstream>
12#include <sys/stat.h>
13#include <sys/types.h>
14#include <dirent.h>
15#include <syslog.h>
16#include <unistd.h>
17#include <pthread.h>
18
19#include "gen_tile.h"
20#include "daemon.h"
21#include "protocol.h"
22#include "render_config.h"
23#include "dir_utils.h"
24#include "store.h"
25
26
27using namespace mapnik;
28#define DEG_TO_RAD (M_PI/180)
29#define RAD_TO_DEG (180/M_PI)
30
31#ifdef METATILE
32#define RENDER_SIZE (256 * (METATILE + 1))
33#else
34#define RENDER_SIZE (512)
35#endif
36
37static const int minZoom = 0;
38static const int maxZoom = 18;
39
40typedef struct {
41    char xmlname[XMLCONFIG_MAX];
42    char xmlfile[PATH_MAX];
43    Map map;
44    projection prj;
45} xmlmapconfig;
46
47
48class SphericalProjection
49{
50    double *Ac, *Bc, *Cc, *zc;
51
52    public:
53        SphericalProjection(int levels=18) {
54            Ac = new double[levels];
55            Bc = new double[levels];
56            Cc = new double[levels];
57            zc = new double[levels];
58            int d, c = 256;
59            for (d=0; d<levels; d++) {
60                int e = c/2;
61                Bc[d] = c/360.0;
62                Cc[d] = c/(2 * M_PI);
63                zc[d] = e;
64                Ac[d] = c;
65                c *=2;
66            }
67        }
68
69        void fromLLtoPixel(double &x, double &y, int zoom) {
70            double d = zc[zoom];
71            double f = minmax(sin(DEG_TO_RAD * y),-0.9999,0.9999);
72            x = round(d + x * Bc[zoom]);
73            y = round(d + 0.5*log((1+f)/(1-f))*-Cc[zoom]);
74        }
75        void fromPixelToLL(double &x, double &y, int zoom) {
76            double e = zc[zoom];
77            double g = (y - e)/-Cc[zoom];
78            x = (x - e)/Bc[zoom];
79            y = RAD_TO_DEG * ( 2 * atan(exp(g)) - 0.5 * M_PI);
80        }
81
82    private:
83        double minmax(double a, double b, double c)
84        {
85            #define MIN(x,y) ((x)<(y)?(x):(y))
86            #define MAX(x,y) ((x)>(y)?(x):(y))
87            a = MAX(a,b);
88            a = MIN(a,c);
89            return a;
90        }
91};
92
93static SphericalProjection tiling(maxZoom+1);
94
95static void load_fonts(const char *font_dir, int recurse)
96{
97    DIR *fonts = opendir(font_dir);
98    struct dirent *entry;
99    char path[PATH_MAX]; // FIXME: Eats lots of stack space when recursive
100
101    if (!fonts) {
102        syslog(LOG_CRIT, "Unable to open font directory: %s", font_dir);
103        return;
104    }
105
106    while ((entry = readdir(fonts))) {
107        struct stat b;
108        char *p;
109
110        if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
111            continue;
112        snprintf(path, sizeof(path), "%s/%s", font_dir, entry->d_name);
113        if (stat(path, &b))
114            continue;
115        if (S_ISDIR(b.st_mode)) {
116            if (recurse)
117                load_fonts(path, recurse);
118            continue;
119        }
120        p = strrchr(path, '.');
121        if (p && !strcmp(p, ".ttf")) {
122            syslog(LOG_DEBUG, "DEBUG: Loading font: %s", path);
123            freetype_engine::register_font(path);
124        }
125    }
126    closedir(fonts);
127}
128
129
130#ifdef METATILE
131
132class metaTile {
133    public:
134        metaTile(const std::string &xmlconfig, int x, int y, int z):
135            x_(x), y_(y), z_(z), xmlconfig_(xmlconfig)
136        {
137            clear();
138        }
139
140        void clear()
141        {
142            for (int x = 0; x < METATILE; x++)
143                for (int y = 0; y < METATILE; y++)
144                    tile[x][y] = "";
145        }
146
147        void set(int x, int y, const std::string &data)
148        {
149            tile[x][y] = data;
150        }
151
152        const std::string get(int x, int y)
153        {
154            return tile[x][y];
155        }
156
157        // Returns the offset within the meta-tile index table
158        int xyz_to_meta_offset(int x, int y, int z)
159        {
160            unsigned char mask = METATILE - 1;
161            return (x & mask) * METATILE + (y & mask);
162        }
163
164        void save()
165        {
166            int ox, oy, limit;
167            size_t offset;
168            struct meta_layout m;
169            char meta_path[PATH_MAX];
170            struct entry offsets[METATILE * METATILE];
171
172            memset(&m, 0, sizeof(m));
173            memset(&offsets, 0, sizeof(offsets));
174
175            xyz_to_meta(meta_path, sizeof(meta_path), xmlconfig_.c_str(), x_, y_, z_);
176            std::stringstream ss;
177            ss << std::string(meta_path) << "." << pthread_self();
178            std::string tmp(ss.str());
179
180            mkdirp(tmp.c_str());
181
182            std::ofstream file(tmp.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
183
184            // Create and write header
185            m.count = METATILE * METATILE;
186            memcpy(m.magic, META_MAGIC, strlen(META_MAGIC));
187            m.x = x_;
188            m.y = y_;
189            m.z = z_;
190            file.write((const char *)&m, sizeof(m));
191
192            offset = header_size;
193            limit = (1 << z_);
194            limit = MIN(limit, METATILE);
195
196            // Generate offset table
197            for (ox=0; ox < limit; ox++) {
198                for (oy=0; oy < limit; oy++) {
199                    int mt = xyz_to_meta_offset(x_ + ox, y_ + oy, z_);
200                    offsets[mt].offset = offset;
201                    offsets[mt].size   = tile[ox][oy].size();
202                    offset += offsets[mt].size;
203                }
204            }
205            file.write((const char *)&offsets, sizeof(offsets));
206
207            // Write tiles
208            for (ox=0; ox < limit; ox++) {
209                for (oy=0; oy < limit; oy++) {
210                    file.write((const char *)tile[ox][oy].data(), tile[ox][oy].size());
211                }
212            }
213
214            file.close();
215
216            rename(tmp.c_str(), meta_path);
217            //printf("Produced .meta: %s\n", meta_path);
218        }
219
220        int x_, y_, z_;
221        std::string xmlconfig_;
222        std::string tile[METATILE][METATILE];
223        static const int header_size = sizeof(struct meta_layout) + (sizeof(struct entry) * (METATILE * METATILE));
224};
225
226
227static enum protoCmd render(Map &m, char *xmlname, projection &prj, int x, int y, int z, unsigned int size, metaTile &tiles)
228{
229    int render_size = 256 * size;
230    double p0x = x * 256;
231    double p0y = (y + size) * 256;
232    double p1x = (x + size) * 256;
233    double p1y = y * 256;
234
235    //std::cout << "META TILE " << z << " " << x << "-" << x+size-1 << " " << y << "-" << y+size-1 << "\n";
236
237    tiling.fromPixelToLL(p0x, p0y, z);
238    tiling.fromPixelToLL(p1x, p1y, z);
239
240    prj.forward(p0x, p0y);
241    prj.forward(p1x, p1y);
242
243    Envelope<double> bbox(p0x, p0y, p1x,p1y);
244    m.resize(render_size, render_size);
245    m.zoomToBox(bbox);
246    m.set_buffer_size(128);
247    //m.zoom(size+1);
248
249    Image32 buf(render_size, render_size);
250    agg_renderer<Image32> ren(m,buf);
251    ren.apply();
252
253    // Split the meta tile into an NxN grid of tiles
254    unsigned int xx, yy;
255    for (yy = 0; yy < size; yy++) {
256        for (xx = 0; xx < size; xx++) {
257            image_view<ImageData32> vw(xx * 256, yy * 256, 256, 256, buf.data());
258            tiles.set(xx, yy, save_to_string(vw, "png256"));
259        }
260    }
261//    std::cout << "DONE TILE " << xmlname << " " << z << " " << x << "-" << x+size-1 << " " << y << "-" << y+size-1 << "\n";
262    syslog(LOG_DEBUG, "DEBUG: DONE TILE %s %d %d-%d %d-%d", xmlname, z, x, x+size-1, y, y+size-1);
263    return cmdDone; // OK
264}
265#else
266static enum protoCmd render(Map &m, char *xmlname, projection &prj, int x, int y, int z)
267{
268    char filename[PATH_MAX];
269    char tmp[PATH_MAX];
270    double p0x = x * 256.0;
271    double p0y = (y + 1) * 256.0;
272    double p1x = (x + 1) * 256.0;
273    double p1y = y * 256.0;
274
275    tiling.fromPixelToLL(p0x, p0y, z);
276    tiling.fromPixelToLL(p1x, p1y, z);
277
278    prj.forward(p0x, p0y);
279    prj.forward(p1x, p1y);
280
281    Envelope<double> bbox(p0x, p0y, p1x,p1y);
282    bbox.width(bbox.width() * 2);
283    bbox.height(bbox.height() * 2);
284    m.zoomToBox(bbox);
285
286    Image32 buf(RENDER_SIZE, RENDER_SIZE);
287    agg_renderer<Image32> ren(m,buf);
288    ren.apply();
289
290    xyz_to_path(filename, sizeof(filename), xmlname, x, y, z);
291    if (mkdirp(filename))
292        return cmdNotDone;
293    snprintf(tmp, sizeof(tmp), "%s.tmp", filename);
294
295    image_view<ImageData32> vw(128,128,256,256, buf.data());
296    //std::cout << "Render " << z << " " << x << " " << y << " " << filename << "\n";
297    save_to_file(vw, tmp,"png256");
298    if (rename(tmp, filename)) {
299        perror(tmp);
300        return cmdNotDone;
301    }
302
303    return cmdDone; // OK
304}
305#endif
306
307
308void render_init(const char *plugins_dir, const char* font_dir, int font_dir_recurse)
309{
310    datasource_cache::instance()->register_datasources(plugins_dir);
311    load_fonts(font_dir, font_dir_recurse);
312}
313
314void *render_thread(void * arg)
315{
316    xmlconfigitem * parentxmlconfig = (xmlconfigitem *)arg;
317    xmlmapconfig maps[XMLCONFIGS_MAX];
318    int i,iMaxConfigs;
319
320    for (iMaxConfigs = 0; iMaxConfigs < XMLCONFIGS_MAX; ++iMaxConfigs) {
321        if (parentxmlconfig[iMaxConfigs].xmlname[0] == 0 || parentxmlconfig[iMaxConfigs].xmlfile[0] == 0) break;
322        strcpy(maps[iMaxConfigs].xmlname, parentxmlconfig[iMaxConfigs].xmlname);
323        strcpy(maps[iMaxConfigs].xmlfile, parentxmlconfig[iMaxConfigs].xmlfile);
324        maps[iMaxConfigs].map = Map(RENDER_SIZE, RENDER_SIZE);
325        load_map(maps[iMaxConfigs].map, maps[iMaxConfigs].xmlfile);
326        maps[iMaxConfigs].prj = projection(maps[iMaxConfigs].map.srs());
327    }
328
329    while (1) {
330        enum protoCmd ret;
331        struct item *item = fetch_request();
332        if (item) {
333            struct protocol *req = &item->req;
334#ifdef METATILE
335            // At very low zoom the whole world may be smaller than METATILE
336            unsigned int size = MIN(METATILE, 1 << req->z);
337            for (i = 0; i < iMaxConfigs; ++i) {
338                if (!strcmp(maps[i].xmlname, req->xmlname)) {
339                    metaTile tiles(req->xmlname, item->mx, item->my, req->z);
340
341                    ret = render(maps[i].map, req->xmlname, maps[i].prj, item->mx, item->my, req->z, size, tiles);
342                    if (ret == cmdDone)
343                        tiles.save();
344#else
345                    ret = render(maps[i].map, req->xmlname, maps[i].prj, req->x, req->y, req->z);
346#endif
347                    send_response(item, ret);
348                    break;
349               }
350            }
351            if (i == iMaxConfigs){
352                syslog(LOG_ERR, "No map for: %s", req->xmlname);
353            }
354        } else {
355            sleep(1); // TODO: Use an event to indicate there are new requests
356        }
357    }
358    return NULL;
359}
Note: See TracBrowser for help on using the repository browser.