]> git.street.me.uk Git - andy/viking.git/blob - src/metatile.c
Shift GTK+ compatibility definitions into vik_compat.h
[andy/viking.git] / src / metatile.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4  *
5  * Copyright (C) 2014, Rob Norris <rw_norris@hotmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 /*
23  * Mostly imported from https://github.com/openstreetmap/mod_tile/
24  *  Release 0.4
25  */
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <limits.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <errno.h>
34 #include <fcntl.h>
35
36 #include "metatile.h"
37 /**
38  * metatile.h
39  */
40 #define META_MAGIC "META"
41 #define META_MAGIC_COMPRESSED "METZ"
42
43 struct entry {
44     int offset;
45     int size;
46 };
47
48 struct meta_layout {
49     char magic[4]; // META_MAGIC or META_MAGIC_COMPRESSED
50     int count; // METATILE ^ 2
51     int x, y, z; // lowest x,y of this metatile, plus z
52     struct entry index[]; // count entries
53     // Followed by the tile data
54     // The index offsets are measured from the start of the file
55 };
56
57 // Use this to enable meta-tiles which will render NxN tiles at once
58 // Note: This should be a power of 2 (2, 4, 8, 16 ...)
59 #define METATILE (8)
60
61 /**
62  * xyz_to_meta:
63  * Based on function from mod_tile/src/store_file_utils.c
64  *
65  * Returns the path to the meta-tile and the offset within the meta-tile
66  */
67 int xyz_to_meta(char *path, size_t len, const char *dir, int x, int y, int z)
68 {
69     unsigned char i, hash[5], offset, mask;
70
71     // Each meta tile winds up in its own file, with several in each leaf directory
72     // the .meta tile name is based on the sub-tile at (0,0)
73     mask = METATILE - 1;
74     offset = (x & mask) * METATILE + (y & mask);
75     x &= ~mask;
76     y &= ~mask;
77
78     for (i=0; i<5; i++) {
79         hash[i] = ((x & 0x0f) << 4) | (y & 0x0f);
80         x >>= 4;
81         y >>= 4;
82     }
83
84     snprintf(path, len, "%s/%d/%u/%u/%u/%u/%u.meta", dir, z, hash[4], hash[3], hash[2], hash[1], hash[0]);
85     return offset;
86 }
87
88 /**
89  * metatile_read:
90  * From function in mod_tile/src/store_file.c
91  * Slightly reworked to use simplified xyz_to_meta() above
92  *
93  * Reads into buf upto size specified by sz
94  *
95  * Returns whether the file is in a compressed format (possibly only gzip)
96  *
97  * Error messages returned in log_msg
98  */
99 int metatile_read(const char *dir, int x, int y, int z, char *buf, size_t sz, int * compressed, char * log_msg)
100 {
101     char path[PATH_MAX];
102     int meta_offset, fd;
103     unsigned int pos;
104     unsigned int header_len = sizeof(struct meta_layout) + METATILE*METATILE*sizeof(struct entry);
105     struct meta_layout *meta = (struct meta_layout *)malloc(header_len);
106     size_t file_offset, tile_size;
107
108     meta_offset = xyz_to_meta(path, sizeof(path), dir, x, y, z);
109
110     fd = open(path, O_RDONLY);
111     if (fd < 0) {
112         snprintf(log_msg,PATH_MAX - 1, "Could not open metatile %s. Reason: %s\n", path, strerror(errno));
113         free(meta);
114         return -1;
115     }
116
117     pos = 0;
118     while (pos < header_len) {
119         size_t len = header_len - pos;
120         int got = read(fd, ((unsigned char *) meta) + pos, len);
121         if (got < 0) {
122             snprintf(log_msg,PATH_MAX - 1, "Failed to read complete header for metatile %s Reason: %s\n", path, strerror(errno));
123             close(fd);
124             free(meta);
125             return -2;
126         } else if (got > 0) {
127             pos += got;
128         } else {
129             break;
130         }
131     }
132     if (pos < header_len) {
133         snprintf(log_msg,PATH_MAX - 1, "Meta file %s too small to contain header\n", path);
134         close(fd);
135         free(meta);
136         return -3;
137     }
138     if (memcmp(meta->magic, META_MAGIC, strlen(META_MAGIC))) {
139         if (memcmp(meta->magic, META_MAGIC_COMPRESSED, strlen(META_MAGIC_COMPRESSED))) {
140             snprintf(log_msg,PATH_MAX - 1, "Meta file %s header magic mismatch\n", path);
141             close(fd);
142             free(meta);
143             return -4;
144         } else {
145             *compressed = 1;
146         }
147     } else *compressed = 0;
148
149     // Currently this code only works with fixed metatile sizes (due to xyz_to_meta above)
150     if (meta->count != (METATILE * METATILE)) {
151         snprintf(log_msg, PATH_MAX - 1, "Meta file %s header bad count %d != %d\n", path, meta->count, METATILE * METATILE);
152         free(meta);
153         close(fd);
154         return -5;
155     }
156
157     file_offset = meta->index[meta_offset].offset;
158     tile_size   = meta->index[meta_offset].size;
159
160     free(meta);
161
162     if (tile_size > sz) {
163         snprintf(log_msg, PATH_MAX - 1, "Truncating tile %zd to fit buffer of %zd\n", tile_size, sz);
164         tile_size = sz;
165         close(fd);
166         return -6;
167     }
168
169     if (lseek(fd, file_offset, SEEK_SET) < 0) {
170         snprintf(log_msg, PATH_MAX - 1, "Meta file %s seek error: %s\n", path, strerror(errno));
171         close(fd);
172         return -7;
173     }
174
175     pos = 0;
176     while (pos < tile_size) {
177         size_t len = tile_size - pos;
178         int got = read(fd, buf + pos, len);
179         if (got < 0) {
180             snprintf(log_msg, PATH_MAX - 1, "Failed to read data from file %s. Reason: %s\n", path, strerror(errno));
181             close(fd);
182             return -8;
183         } else if (got > 0) {
184             pos += got;
185         } else {
186             break;
187         }
188     }
189     close(fd);
190     return pos;
191 }