]>
Commit | Line | Data |
---|---|---|
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 | } |