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

Last change on this file since 19529 was 19529, checked in by twain, 10 years ago

check mapnik version, compatibility with 0.8

File size: 15.9 KB
Line 
1#include <mapnik/version.hpp>
2#include <mapnik/map.hpp>
3#include <mapnik/datasource_cache.hpp>
4#include <mapnik/agg_renderer.hpp>
5#include <mapnik/filter_factory.hpp>
6#include <mapnik/color_factory.hpp>
7#include <mapnik/image_util.hpp>
8#include <mapnik/load_map.hpp>
9#include <mapnik/image_util.hpp>
10
11#include <iostream>
12#include <fstream>
13#include <sys/stat.h>
14#include <sys/types.h>
15#include <dirent.h>
16#include <syslog.h>
17#include <unistd.h>
18#include <pthread.h>
19
20#include "gen_tile.h"
21#include "daemon.h"
22#include "protocol.h"
23#include "render_config.h"
24#include "dir_utils.h"
25#include "store.h"
26
27#ifdef HTCP_EXPIRE_CACHE
28#include <sys/socket.h>
29#include <netdb.h>
30#endif
31
32#if MAPNIK_VERSION >= 800
33#define Image32 image_32
34#define ImageData32 image_data_32
35#define Envelope box2d
36#endif
37
38using namespace mapnik;
39#define DEG_TO_RAD (M_PI/180)
40#define RAD_TO_DEG (180/M_PI)
41
42#ifdef METATILE
43#define RENDER_SIZE (256 * (METATILE + 1))
44#else
45#define RENDER_SIZE (512)
46#endif
47
48static const int minZoom = 0;
49static const int maxZoom = 18;
50
51typedef struct {
52    char xmlname[XMLCONFIG_MAX];
53    char xmlfile[PATH_MAX];
54    char tile_dir[PATH_MAX];
55    Map map;
56    projection prj;
57    char xmluri[PATH_MAX];
58    char host[PATH_MAX];
59    char htcphost[PATH_MAX];
60    int htcpsock;
61} xmlmapconfig;
62
63
64class SphericalProjection
65{
66    double *Ac, *Bc, *Cc, *zc;
67
68    public:
69        SphericalProjection(int levels=18) {
70            Ac = new double[levels];
71            Bc = new double[levels];
72            Cc = new double[levels];
73            zc = new double[levels];
74            int d, c = 256;
75            for (d=0; d<levels; d++) {
76                int e = c/2;
77                Bc[d] = c/360.0;
78                Cc[d] = c/(2 * M_PI);
79                zc[d] = e;
80                Ac[d] = c;
81                c *=2;
82            }
83        }
84
85        void fromLLtoPixel(double &x, double &y, int zoom) {
86            double d = zc[zoom];
87            double f = minmax(sin(DEG_TO_RAD * y),-0.9999,0.9999);
88            x = round(d + x * Bc[zoom]);
89            y = round(d + 0.5*log((1+f)/(1-f))*-Cc[zoom]);
90        }
91        void fromPixelToLL(double &x, double &y, int zoom) {
92            double e = zc[zoom];
93            double g = (y - e)/-Cc[zoom];
94            x = (x - e)/Bc[zoom];
95            y = RAD_TO_DEG * ( 2 * atan(exp(g)) - 0.5 * M_PI);
96        }
97
98    private:
99        double minmax(double a, double b, double c)
100        {
101            #define MIN(x,y) ((x)<(y)?(x):(y))
102            #define MAX(x,y) ((x)>(y)?(x):(y))
103            a = MAX(a,b);
104            a = MIN(a,c);
105            return a;
106        }
107};
108
109static SphericalProjection tiling(maxZoom+1);
110
111static void load_fonts(const char *font_dir, int recurse)
112{
113    DIR *fonts = opendir(font_dir);
114    struct dirent *entry;
115    char path[PATH_MAX]; // FIXME: Eats lots of stack space when recursive
116
117    if (!fonts) {
118        syslog(LOG_CRIT, "Unable to open font directory: %s", font_dir);
119        return;
120    }
121
122    while ((entry = readdir(fonts))) {
123        struct stat b;
124        char *p;
125
126        if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
127            continue;
128        snprintf(path, sizeof(path), "%s/%s", font_dir, entry->d_name);
129        if (stat(path, &b))
130            continue;
131        if (S_ISDIR(b.st_mode)) {
132            if (recurse)
133                load_fonts(path, recurse);
134            continue;
135        }
136        p = strrchr(path, '.');
137        if (p && !strcmp(p, ".ttf")) {
138            syslog(LOG_DEBUG, "DEBUG: Loading font: %s", path);
139            freetype_engine::register_font(path);
140        }
141    }
142    closedir(fonts);
143}
144
145#ifdef HTCP_EXPIRE_CACHE
146
147/**
148 * This function sends a HTCP cache clr request for a given
149 * URL.
150 * RFC for HTCP can be found at http://www.htcp.org/
151 */
152void cache_expire_url(int sock, char * url) {
153    char * buf;
154
155    if (sock < 0) {
156            return;
157    }
158
159    int idx = 0;
160    int url_len;
161
162    url_len = strlen(url);
163    buf = (char *) malloc(12 + 22 + url_len);
164    if (!buf) {
165        return;
166    }
167
168    idx = 0;
169
170    //16 bit: Overall length of the datagram packet, including this header
171    *((uint16_t *) (&buf[idx])) = htons(12 + 22 + url_len);
172    idx += 2;
173
174    //HTCP version. Currently at 0.0
175    buf[idx++] = 0; //Major version
176    buf[idx++] = 0; //Minor version
177
178    //Length of HTCP data, including this field
179    *((uint16_t *) (&buf[idx])) = htons(8 + 22 + url_len);
180    idx += 2;
181
182    //HTCP opcode CLR=4
183    buf[idx++] = 4;
184    //Reserved
185    buf[idx++] = 0;
186
187    //32 bit transaction id;
188    *((uint32_t *) (&buf[idx])) = htonl(255);
189    idx += 4;
190
191    buf[idx++] = 0;
192    buf[idx++] = 0; //HTCP reason
193
194    //Length of the Method string
195    *((uint16_t *) (&buf[idx])) = htons(4);
196    idx += 2;
197
198    ///Method string
199    memcpy(&buf[idx], "HEAD", 4);
200    idx += 4;
201
202    //Length of the url string
203    *((uint16_t *) (&buf[idx])) = htons(url_len);
204    idx += 2;
205
206    //Url string
207    memcpy(&buf[idx], url, url_len);
208    idx += url_len;
209
210    //Length of version string
211    *((uint16_t *) (&buf[idx])) = htons(8);
212    idx += 2;
213
214    //version string
215    memcpy(&buf[idx], "HTTP/1.1", 8);
216    idx += 8;
217
218    //Length of request headers. Currently 0 as we don't have any headers to send
219    *((uint16_t *) (&buf[idx])) = htons(0);
220
221    if (send(sock, (void *) buf, (12 + 22 + url_len), 0) < (12 + 22 + url_len)) {
222        syslog(LOG_ERR, "Failed to send HTCP purge for %s\n", url);
223    };
224
225    free(buf);
226}
227
228void cache_expire(int sock, char * host, char * uri, int x, int y, int z) {
229
230    if (sock < 0) {
231        return;
232    }
233    char * url = (char *)malloc(1024);
234    sprintf(url,"http://%s%s%i/%i/%i.png", host, uri, z,x,y);
235    cache_expire_url(sock, url);
236    free(url);
237}
238
239int init_cache_expire(char * htcphost) {
240    struct addrinfo hints;
241    struct addrinfo *result, *rp;
242    int sfd, s;
243
244    /* Obtain address(es) matching host/port */
245
246    memset(&hints, 0, sizeof(struct addrinfo));
247    hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
248    hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
249    hints.ai_flags = 0;
250    hints.ai_protocol = 0; /* Any protocol */
251
252    s = getaddrinfo(htcphost, HTCP_EXPIRE_CACHE_PORT, &hints, &result);
253    if (s != 0) {
254        syslog(LOG_ERR, "Failed to lookup HTCP cache host: %s", gai_strerror(s));
255        return -1;
256    }
257
258    /* getaddrinfo() returns a list of address structures.
259     Try each address until we successfully connect(2).
260     If socket(2) (or connect(2)) fails, we (close the socket
261     and) try the next address. */
262
263    for (rp = result; rp != NULL; rp = rp->ai_next) {
264        sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
265        if (sfd == -1)
266            continue;
267
268        if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
269            break; /* Success */
270
271        close(sfd);
272    }
273
274    if (rp == NULL) { /* No address succeeded */
275        syslog(LOG_ERR, "Failed to create HTCP cache socket");
276        return -1;
277    }
278
279    freeaddrinfo(result); /* No longer needed */
280
281    return sfd;
282
283}
284#endif
285
286
287
288#ifdef METATILE
289
290class metaTile {
291    public:
292        metaTile(const std::string &xmlconfig, int x, int y, int z):
293            x_(x), y_(y), z_(z), xmlconfig_(xmlconfig)
294        {
295            clear();
296        }
297
298        void clear()
299        {
300            for (int x = 0; x < METATILE; x++)
301                for (int y = 0; y < METATILE; y++)
302                    tile[x][y] = "";
303        }
304
305        void set(int x, int y, const std::string &data)
306        {
307            tile[x][y] = data;
308        }
309
310        const std::string get(int x, int y)
311        {
312            return tile[x][y];
313        }
314
315        // Returns the offset within the meta-tile index table
316        int xyz_to_meta_offset(int x, int y, int z)
317        {
318            unsigned char mask = METATILE - 1;
319            return (x & mask) * METATILE + (y & mask);
320        }
321
322        void save(const char *tile_dir)
323        {
324            int ox, oy, limit;
325            size_t offset;
326            struct meta_layout m;
327            char meta_path[PATH_MAX];
328            struct entry offsets[METATILE * METATILE];
329
330            memset(&m, 0, sizeof(m));
331            memset(&offsets, 0, sizeof(offsets));
332
333            xyz_to_meta(meta_path, sizeof(meta_path), tile_dir, xmlconfig_.c_str(), x_, y_, z_);
334            std::stringstream ss;
335            ss << std::string(meta_path) << "." << pthread_self();
336            std::string tmp(ss.str());
337
338            mkdirp(tmp.c_str());
339
340            std::ofstream file(tmp.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
341
342            // Create and write header
343            m.count = METATILE * METATILE;
344            memcpy(m.magic, META_MAGIC, strlen(META_MAGIC));
345            m.x = x_;
346            m.y = y_;
347            m.z = z_;
348            file.write((const char *)&m, sizeof(m));
349
350            offset = header_size;
351            limit = (1 << z_);
352            limit = MIN(limit, METATILE);
353
354            // Generate offset table
355            for (ox=0; ox < limit; ox++) {
356                for (oy=0; oy < limit; oy++) {
357                    int mt = xyz_to_meta_offset(x_ + ox, y_ + oy, z_);
358                    offsets[mt].offset = offset;
359                    offsets[mt].size   = tile[ox][oy].size();
360                    offset += offsets[mt].size;
361                }
362            }
363            file.write((const char *)&offsets, sizeof(offsets));
364
365            // Write tiles
366            for (ox=0; ox < limit; ox++) {
367                for (oy=0; oy < limit; oy++) {
368                    file.write((const char *)tile[ox][oy].data(), tile[ox][oy].size());
369                }
370            }
371
372            file.close();
373
374            rename(tmp.c_str(), meta_path);
375            //printf("Produced .meta: %s\n", meta_path);
376        }
377
378#ifdef HTCP_EXPIRE_CACHE
379        void expire_tiles(int sock, char * host, char * uri) {
380            if (sock < 0) {
381                return;
382            }
383            syslog(LOG_INFO, "Purging metatile via HTCP cache expiry");
384            int ox, oy;
385            int limit = (1 << z_);
386            limit = MIN(limit, METATILE);
387
388            // Generate offset table
389            for (ox=0; ox < limit; ox++) {
390                for (oy=0; oy < limit; oy++) {
391                    cache_expire(sock, host, uri, (x_ + ox), (y_ + oy), z_);
392                }
393            }
394        }
395#endif
396        int x_, y_, z_;
397        std::string xmlconfig_;
398        std::string tile[METATILE][METATILE];
399        static const int header_size = sizeof(struct meta_layout) + (sizeof(struct entry) * (METATILE * METATILE));
400};
401
402
403static enum protoCmd render(Map &m, char *xmlname, projection &prj, int x, int y, int z, unsigned int size, metaTile &tiles)
404{
405    int render_size = 256 * size;
406    double p0x = x * 256;
407    double p0y = (y + size) * 256;
408    double p1x = (x + size) * 256;
409    double p1y = y * 256;
410
411    //std::cout << "META TILE " << z << " " << x << "-" << x+size-1 << " " << y << "-" << y+size-1 << "\n";
412
413    tiling.fromPixelToLL(p0x, p0y, z);
414    tiling.fromPixelToLL(p1x, p1y, z);
415
416    prj.forward(p0x, p0y);
417    prj.forward(p1x, p1y);
418
419    Envelope<double> bbox(p0x, p0y, p1x,p1y);
420    m.resize(render_size, render_size);
421    m.zoomToBox(bbox);
422    m.set_buffer_size(128);
423    //m.zoom(size+1);
424
425    Image32 buf(render_size, render_size);
426    agg_renderer<Image32> ren(m,buf);
427    ren.apply();
428
429    // Split the meta tile into an NxN grid of tiles
430    unsigned int xx, yy;
431    for (yy = 0; yy < size; yy++) {
432        for (xx = 0; xx < size; xx++) {
433            image_view<ImageData32> vw(xx * 256, yy * 256, 256, 256, buf.data());
434            tiles.set(xx, yy, save_to_string(vw, "png256"));
435        }
436    }
437//    std::cout << "DONE TILE " << xmlname << " " << z << " " << x << "-" << x+size-1 << " " << y << "-" << y+size-1 << "\n";
438    syslog(LOG_DEBUG, "DEBUG: DONE TILE %s %d %d-%d %d-%d", xmlname, z, x, x+size-1, y, y+size-1);
439    return cmdDone; // OK
440}
441#else
442static enum protoCmd render(Map &m, const char *tile_dir, char *xmlname, projection &prj, int x, int y, int z)
443{
444    char filename[PATH_MAX];
445    char tmp[PATH_MAX];
446    double p0x = x * 256.0;
447    double p0y = (y + 1) * 256.0;
448    double p1x = (x + 1) * 256.0;
449    double p1y = y * 256.0;
450
451    tiling.fromPixelToLL(p0x, p0y, z);
452    tiling.fromPixelToLL(p1x, p1y, z);
453
454    prj.forward(p0x, p0y);
455    prj.forward(p1x, p1y);
456
457    Envelope<double> bbox(p0x, p0y, p1x,p1y);
458    bbox.width(bbox.width() * 2);
459    bbox.height(bbox.height() * 2);
460    m.zoomToBox(bbox);
461
462    Image32 buf(RENDER_SIZE, RENDER_SIZE);
463    agg_renderer<Image32> ren(m,buf);
464    ren.apply();
465
466    xyz_to_path(filename, sizeof(filename), tile_dir, xmlname, x, y, z);
467    if (mkdirp(filename))
468        return cmdNotDone;
469    snprintf(tmp, sizeof(tmp), "%s.tmp", filename);
470
471    image_view<ImageData32> vw(128, 128, 256, 256, buf.data());
472    //std::cout << "Render " << z << " " << x << " " << y << " " << filename << "\n";
473    save_to_file(vw, tmp, "png256");
474    if (rename(tmp, filename)) {
475        perror(tmp);
476        return cmdNotDone;
477    }
478
479    return cmdDone; // OK
480}
481#endif
482
483
484void render_init(const char *plugins_dir, const char* font_dir, int font_dir_recurse)
485{
486    datasource_cache::instance()->register_datasources(plugins_dir);
487    load_fonts(font_dir, font_dir_recurse);
488}
489
490void *render_thread(void * arg)
491{
492    xmlconfigitem * parentxmlconfig = (xmlconfigitem *)arg;
493    xmlmapconfig maps[XMLCONFIGS_MAX];
494    int i,iMaxConfigs;
495
496    for (iMaxConfigs = 0; iMaxConfigs < XMLCONFIGS_MAX; ++iMaxConfigs) {
497        if (parentxmlconfig[iMaxConfigs].xmlname[0] == 0 || parentxmlconfig[iMaxConfigs].xmlfile[0] == 0) break;
498        strcpy(maps[iMaxConfigs].xmlname, parentxmlconfig[iMaxConfigs].xmlname);
499        strcpy(maps[iMaxConfigs].xmlfile, parentxmlconfig[iMaxConfigs].xmlfile);
500        strcpy(maps[iMaxConfigs].tile_dir, parentxmlconfig[iMaxConfigs].tile_dir);
501        maps[iMaxConfigs].map = Map(RENDER_SIZE, RENDER_SIZE);
502        load_map(maps[iMaxConfigs].map, maps[iMaxConfigs].xmlfile);
503        maps[iMaxConfigs].prj = projection(maps[iMaxConfigs].map.srs());
504#ifdef HTCP_EXPIRE_CACHE
505        strcpy(maps[iMaxConfigs].xmluri, parentxmlconfig[iMaxConfigs].xmluri);
506        strcpy(maps[iMaxConfigs].host, parentxmlconfig[iMaxConfigs].host);
507        strcpy(maps[iMaxConfigs].htcphost, parentxmlconfig[iMaxConfigs].htcpip);
508        if (strlen(maps[iMaxConfigs].htcphost) > 0) {
509            maps[iMaxConfigs].htcpsock = init_cache_expire(
510                    maps[iMaxConfigs].htcphost);
511            if (maps[iMaxConfigs].htcpsock > 0) {
512                syslog(LOG_INFO, "Successfully opened socket for HTCP cache expiry");
513            } else {
514                syslog(LOG_ERR, "Failed to opened socket for HTCP cache expiry");
515            }
516        } else {
517            maps[iMaxConfigs].htcpsock = -1;
518        }
519#endif
520    }
521
522    while (1) {
523        enum protoCmd ret;
524        struct item *item = fetch_request();
525        if (item) {
526            struct protocol *req = &item->req;
527#ifdef METATILE
528            // At very low zoom the whole world may be smaller than METATILE
529            unsigned int size = MIN(METATILE, 1 << req->z);
530            for (i = 0; i < iMaxConfigs; ++i) {
531                if (!strcmp(maps[i].xmlname, req->xmlname)) {
532                    metaTile tiles(req->xmlname, item->mx, item->my, req->z);
533
534                    ret = render(maps[i].map, req->xmlname, maps[i].prj, item->mx, item->my, req->z, size, tiles);
535                    if (ret == cmdDone) {
536                        tiles.save(maps[i].tile_dir);
537#ifdef HTCP_EXPIRE_CACHE
538                        tiles.expire_tiles(maps[i].htcpsock,maps[i].host,maps[i].xmluri);
539#endif
540                    }
541#else
542                    ret = render(maps[i].map, maps[i].tile_dir, req->xmlname, maps[i].prj, req->x, req->y, req->z);
543#ifdef HTCP_EXPIRE_CACHE
544                    cache_expire(maps[i].htcpsock,maps[i].host, maps[i].xmluri, req->x,req->y,req->z);
545#endif
546#endif
547                    send_response(item, ret);
548                    break;
549               }
550            }
551            if (i == iMaxConfigs){
552                syslog(LOG_ERR, "No map for: %s", req->xmlname);
553            }
554        } else {
555            sleep(1); // TODO: Use an event to indicate there are new requests
556        }
557    }
558    return NULL;
559}
Note: See TracBrowser for help on using the repository browser.