osmtile.c (8359B)
1 #include <math.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <sys/wait.h> 6 #include <unistd.h> 7 8 #define fail(z, ...) ({fprintf(stderr, __VA_ARGS__); exit(z);}) 9 10 /* https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames */ 11 static int long2tilex(double lon, int z) { 12 return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, z))); 13 } 14 15 static int lat2tiley(double lat, int z) { 16 return (int)(floor((1.0 - log(tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z))); 17 } 18 19 static double tilex2long(int x, int z) { 20 return x / pow(2.0, z) * 360.0 - 180; 21 } 22 23 static double tiley2lat(int y, int z) { 24 double n = M_PI - 2.0 * M_PI * y / pow(2.0, z); 25 return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); 26 } 27 28 static int zoom_from_string(char *string) { 29 if(string) { 30 int z; 31 if(1 == sscanf(string, "%d", &z)) { 32 if(0 <= z && z <= 18) { 33 return z; 34 } 35 } 36 } 37 fail(1, "expected zoom 0 <= %s <= 18\n", string); 38 } 39 40 static int main_lat(int argc, char *argv[]) { 41 int z = zoom_from_string(argv[0]); 42 char *value = argv[1]; 43 if(value) { 44 int y; 45 if(1 == sscanf(value, "%d", &y)) { 46 printf("%lf\n", tiley2lat(y, z)); 47 return 0; 48 } 49 } 50 fail(1, "unexpected y %s\n", value); 51 } 52 53 static int main_lon(int argc, char *argv[]) { 54 int z = zoom_from_string(argv[0]); 55 char *value = argv[1]; 56 if(value) { 57 int x; 58 if(1 == sscanf(value, "%d", &x)) { 59 printf("%lf\n", tilex2long(x, z)); 60 return 0; 61 } 62 } 63 fail(1, "unexpected x %s\n", value); 64 } 65 66 static double lon_from_string(char *string) { 67 if(string) { 68 double z; 69 if(1 == sscanf(string, "%lf", &z)) { 70 return z; 71 } 72 } 73 fail(1, "unexpected longitude %s\n", string); 74 } 75 76 static int main_x(int argc, char *argv[]) { 77 int z = zoom_from_string(argv[0]); 78 double lon = lon_from_string(argv[1]); 79 printf("%d\n", long2tilex(lon, z)); 80 return 0; 81 } 82 83 static double lat_from_string(char *string) { 84 if(string) { 85 double z; 86 if(1 == sscanf(string, "%lf", &z)) { 87 return z; 88 } 89 } 90 fail(1, "unexpected latitude %s\n", string); 91 } 92 93 static int main_y(int argc, char *argv[]) { 94 int z = zoom_from_string(argv[0]); 95 double lat = lat_from_string(argv[1]); 96 printf("%d\n", lat2tiley(lat, z)); 97 return 0; 98 } 99 100 static int probe(char *path) { 101 FILE *f; 102 if((f = fopen(path, "r"))) { 103 fclose(f); 104 return 1; 105 } 106 return 0; 107 } 108 109 static int cached_file(int z, int x, int y, char *dir, char file[4096], int draw) { 110 int max = (1 << z) - 1; 111 if(x < 0) x = max; 112 if(y < 0) y = max; 113 if(max < x) x = 0; 114 if(max < y) y = 0; 115 char zxy[4096]; 116 int n = snprintf(zxy, sizeof(zxy), "%d/%d/%d", z, x, y); 117 if(n < 0 || sizeof(zxy) < n) fail(1, "zxy error\n"); 118 n = snprintf(file, 4096, "%s/%s.png", dir, zxy); 119 if(n < 0 || 4096 < n) fail(1, "file path error\n"); 120 if(probe(file)) return 1; 121 if(draw) return 0; 122 /* download tile in background */ 123 char *args[] = {"osmtile-download", zxy, dir, NULL}; 124 int pid = fork(); 125 if(pid < 0) fail(1, "fork failed\n"); 126 if(!pid) { /* child */ 127 execvp(*args, args); 128 fail(1, "exec %s failed\n", *args); 129 } 130 return 0; 131 } 132 133 static void screen_size(int *w, int *h) { 134 // TODO popen r stty size -> w h 135 FILE *f = fopen("/sys/class/graphics/fb0/virtual_size", "r"); 136 if(!f) fail(1, "unknown screen\n"); 137 if(2 != fscanf(f, "%d,%d", w, h)) fail(1, "unknown screen size\n"); 138 fclose(f); 139 } 140 141 #define min(x, y) (x < y ? x : y) 142 143 static unsigned char key(int timeout) { 144 unsigned char c = 0; // timeout key by default 145 if(timeout) { 146 fd_set fds; 147 FD_ZERO(&fds); 148 FD_SET(0, &fds); 149 struct timeval tv; 150 tv.tv_sec = 1; 151 tv.tv_usec = 0; 152 int z = select(1, &fds, NULL, NULL, &tv); 153 if(z < 0) { 154 perror("select()"); 155 exit(1); 156 } else if(z) { 157 /* FD_ISSET(0, &fds) will be true. */ 158 if(1 != fread(&c, 1, 1, stdin)) exit(0); 159 } 160 } else { 161 if(1 != fread(&c, 1, 1, stdin)) exit(0); 162 } 163 return c; 164 } 165 166 static int main_tty(int argc, char *argv[]) { 167 int width, height; 168 screen_size(&width, &height); 169 char *dir = argv[0]; 170 if(!dir) fail(1, "unexpected dir %s\n", dir); 171 int z0 = zoom_from_string(argv[1]); 172 double a0 = lat_from_string(argv[2]); 173 double o0 = lon_from_string(argv[3]); 174 int z = z0; 175 double a = a0, o = o0; 176 int x = long2tilex(o, z); 177 int y = lat2tiley(a, z); 178 signal(SIGCHLD, SIG_IGN); 179 for(;;) { 180 FILE *p = popen("w3mimgdisplay", "w"); 181 if(!p) fail(1, "popen w3mimgdisplay failed\n"); 182 for(int i = 0; i < 3; i++) { 183 for(int j = 0; j < 3; j++) { 184 char file[4096]; 185 if(cached_file(z, x + i - 1, y + j - 1, dir, file, 1)) { 186 int w = min(width, height) / 3; 187 int h = w; 188 fprintf(p, "0;1;%d;%d;%d;%d;;;;;%s\n", w * i, h * j, w, h, file); 189 } 190 } 191 } 192 pclose(p); 193 for(int i = 0; i < 3; i++) { 194 for(int j = 0; j < 3; j++) { 195 char file[4096]; 196 cached_file(z, x + i - 1, y + j - 1, dir, file, 0); 197 } 198 } 199 unsigned char c = key(1); 200 switch(c) { 201 case 27: /* esc */ 202 c = key(0); 203 switch(c) { 204 case '[': 205 c = key(0); 206 switch(c) { 207 case 'A': goto up; 208 case 'B': goto down; 209 case 'C': goto right; 210 case 'D': goto left; 211 } 212 break; 213 } 214 break; 215 case 'h': goto left; 216 case 'j': goto down; 217 case 'k': goto up; 218 case 'l': goto right; 219 case 'o': /* original position */ 220 a = a0; 221 o = o0; 222 x = long2tilex(o, z); 223 y = lat2tiley(a, z); 224 break; 225 case '+': /* zoom in */ 226 z++; 227 if(18 < z) z = 18; 228 x = long2tilex(o, z); 229 y = lat2tiley(a, z); 230 break; 231 case '-': /* zoom out */ 232 z--; 233 if(z < 0) z = 0; 234 x = long2tilex(o, z); 235 y = lat2tiley(a, z); 236 break; 237 case '0': /* original zoom */ 238 z = z0; 239 x = long2tilex(o, z); 240 y = lat2tiley(a, z); 241 break; 242 case 'p': /* TODO print */ 243 break; 244 case '?': /* TODO help */ 245 break; 246 case 'q': /* quit */ 247 return 0; 248 case '1': 249 z = 2; 250 x = long2tilex(o, z); 251 y = lat2tiley(a, z); 252 break; 253 case '2': 254 z = 4; 255 x = long2tilex(o, z); 256 y = lat2tiley(a, z); 257 break; 258 case '3': 259 z = 6; 260 x = long2tilex(o, z); 261 y = lat2tiley(a, z); 262 break; 263 case '4': 264 z = 8; 265 x = long2tilex(o, z); 266 y = lat2tiley(a, z); 267 break; 268 case '5': 269 z = 10; 270 x = long2tilex(o, z); 271 y = lat2tiley(a, z); 272 break; 273 case '6': 274 z = 12; 275 x = long2tilex(o, z); 276 y = lat2tiley(a, z); 277 break; 278 case '7': 279 z = 14; 280 x = long2tilex(o, z); 281 y = lat2tiley(a, z); 282 break; 283 case '8': 284 z = 16; 285 x = long2tilex(o, z); 286 y = lat2tiley(a, z); 287 break; 288 case '9': 289 z = 18; 290 x = long2tilex(o, z); 291 y = lat2tiley(a, z); 292 break; 293 } 294 continue; 295 up: 296 if(0 < y) y--; 297 // TODO recompute a 298 continue; 299 down: 300 if(y < (1 << z) - 1) y++; 301 // TODO recompute a 302 continue; 303 right: 304 x++; 305 while((1 << z) - 1 < x) x -= (1 << z); 306 // TODO recompute o 307 continue; 308 left: 309 x--; 310 while(x < 0) x += (1 << z); 311 // TODO recompute o 312 continue; 313 } 314 } 315 316 static int main_version(int argc, char *argv[]) { 317 printf("%s\n", 318 #include "VERSION" 319 ); 320 return 0; 321 } 322 323 static int main_help(int argc, char *argv[]) { 324 printf("usage: osmtile cmd args\n"); 325 printf(" lat zoom y\n"); 326 printf(" lon zoom x\n"); 327 printf(" x zoom longitude\n"); 328 printf(" y zoom latitude\n"); 329 printf(" tty dir zoom lat lon\n"); 330 printf(" --version\n"); 331 printf(" --help\n"); 332 return 0; 333 } 334 335 int main(int argc, char *argv[]) { 336 char *cmd = argv[1]; 337 if(cmd) { 338 static const struct { 339 char *cmd; 340 int (*fn)(int argc, char *argv[]); 341 } dispatch[] = { 342 {"lat", main_lat}, 343 {"lon", main_lon}, 344 {"x", main_x}, 345 {"y", main_y}, 346 {"tty", main_tty}, 347 {"--version", main_version}, 348 {"--help", main_help}, 349 {NULL} 350 }; 351 for(int i = 0; dispatch[i].cmd; i++) { 352 if(!strcmp(dispatch[i].cmd, cmd)) { 353 return dispatch[i].fn(argc - 2, &argv[2]); 354 } 355 } 356 } 357 fail(1, "unexpected command %s\n", cmd); 358 }