2014-10-23 22:40:27 +00:00
# include <stdio.h>
# include <stdlib.h>
2016-04-26 21:01:59 +00:00
# include <string.h>
2014-10-23 22:40:27 +00:00
# include <unistd.h>
# include <sqlite3.h>
2017-07-07 19:37:08 +00:00
# include <getopt.h>
2014-10-23 22:40:27 +00:00
# include <string>
2016-04-22 20:27:03 +00:00
# include <vector>
2016-04-25 21:19:38 +00:00
# include <map>
2017-02-27 18:10:28 +00:00
# include <set>
2014-10-23 22:40:27 +00:00
# include <zlib.h>
# include <math.h>
2016-03-22 23:50:01 +00:00
# include <fcntl.h>
# include <sys/stat.h>
# include <sys/mman.h>
2016-05-17 22:43:42 +00:00
# include <protozero/pbf_reader.hpp>
2016-04-27 19:22:47 +00:00
# include "mvt.hpp"
2016-04-27 21:00:14 +00:00
# include "projection.hpp"
2016-04-27 22:09:06 +00:00
# include "geometry.hpp"
2016-12-05 22:12:39 +00:00
# include "write_json.hpp"
2015-10-09 19:41:28 +00:00
2017-01-24 22:14:10 +00:00
int minzoom = 0 ;
int maxzoom = 32 ;
2017-05-05 17:56:50 +00:00
bool force = false ;
2017-01-24 22:14:10 +00:00
2017-03-28 23:25:40 +00:00
void handle ( std : : string message , int z , unsigned x , unsigned y , int describe , std : : set < std : : string > const & to_decode , bool pipeline ) {
2016-04-22 22:10:16 +00:00
mvt_tile tile ;
2017-05-11 19:08:47 +00:00
bool was_compressed ;
2014-10-23 22:40:27 +00:00
2016-05-17 22:43:42 +00:00
try {
2017-05-11 19:08:47 +00:00
if ( ! tile . decode ( message , was_compressed ) ) {
2016-05-17 22:43:42 +00:00
fprintf ( stderr , " Couldn't parse tile %d/%u/%u \n " , z , x , y ) ;
exit ( EXIT_FAILURE ) ;
}
} catch ( protozero : : unknown_pbf_wire_type_exception e ) {
fprintf ( stderr , " PBF decoding error in tile %d/%u/%u \n " , z , x , y ) ;
2014-10-23 22:40:27 +00:00
exit ( EXIT_FAILURE ) ;
}
2017-03-28 23:25:40 +00:00
if ( ! pipeline ) {
printf ( " { \" type \" : \" FeatureCollection \" " ) ;
2015-10-19 18:12:11 +00:00
2017-03-28 23:25:40 +00:00
if ( describe ) {
2017-05-13 00:20:30 +00:00
printf ( " , \" properties \" : { \" zoom \" : %d, \" x \" : %d, \" y \" : %d " , z , x , y ) ;
if ( ! was_compressed ) {
printf ( " , \" compressed \" : false " ) ;
}
printf ( " } " ) ;
2016-06-28 22:27:19 +00:00
2017-03-28 23:25:40 +00:00
if ( projection ! = projections ) {
printf ( " , \" crs \" : { \" type \" : \" name \" , \" properties \" : { \" name \" : " ) ;
fprintq ( stdout , projection - > alias ) ;
printf ( " } } " ) ;
}
2016-06-28 22:27:19 +00:00
}
2015-10-19 18:12:11 +00:00
2017-03-28 23:25:40 +00:00
printf ( " , \" features \" : [ \n " ) ;
}
2015-10-09 19:41:28 +00:00
2017-02-27 18:10:28 +00:00
bool first_layer = true ;
2016-04-23 06:32:02 +00:00
for ( size_t l = 0 ; l < tile . layers . size ( ) ; l + + ) {
2016-04-22 22:10:16 +00:00
mvt_layer & layer = tile . layers [ l ] ;
2014-10-23 22:40:27 +00:00
2017-02-27 18:10:28 +00:00
if ( to_decode . size ( ) ! = 0 & & ! to_decode . count ( layer . name ) ) {
continue ;
}
2017-03-28 23:25:40 +00:00
if ( ! pipeline ) {
if ( describe ) {
if ( ! first_layer ) {
printf ( " , \n " ) ;
}
2015-10-31 00:30:18 +00:00
2017-03-28 23:25:40 +00:00
printf ( " { \" type \" : \" FeatureCollection \" " ) ;
printf ( " , \" properties \" : { \" layer \" : " ) ;
fprintq ( stdout , layer . name . c_str ( ) ) ;
printf ( " , \" version \" : %d, \" extent \" : %lld " , layer . version , layer . extent ) ;
printf ( " } " ) ;
printf ( " , \" features \" : [ \n " ) ;
2015-10-31 00:30:18 +00:00
2017-03-28 23:25:40 +00:00
first_layer = false ;
}
2015-10-31 00:30:18 +00:00
}
2017-05-13 00:20:30 +00:00
layer_to_geojson ( stdout , layer , z , x , y , ! pipeline , pipeline , pipeline , 0 , 0 , 0 , ! force ) ;
2015-10-31 00:30:18 +00:00
2017-03-28 23:25:40 +00:00
if ( ! pipeline ) {
if ( describe ) {
printf ( " ] } \n " ) ;
}
2015-10-31 00:30:18 +00:00
}
2014-10-23 22:40:27 +00:00
}
2015-10-09 19:41:28 +00:00
2017-03-28 23:25:40 +00:00
if ( ! pipeline ) {
printf ( " ] } \n " ) ;
}
2014-10-23 22:40:27 +00:00
}
2017-03-28 23:25:40 +00:00
void decode ( char * fname , int z , unsigned x , unsigned y , std : : set < std : : string > const & to_decode , bool pipeline ) {
2014-10-23 22:40:27 +00:00
sqlite3 * db ;
2014-10-27 23:00:16 +00:00
int oz = z ;
unsigned ox = x , oy = y ;
2014-10-23 22:40:27 +00:00
2016-12-07 18:57:56 +00:00
int fd = open ( fname , O_RDONLY | O_CLOEXEC ) ;
2016-03-22 23:50:01 +00:00
if ( fd > = 0 ) {
struct stat st ;
if ( fstat ( fd , & st ) = = 0 ) {
if ( st . st_size < 50 * 1024 * 1024 ) {
char * map = ( char * ) mmap ( NULL , st . st_size , PROT_READ , MAP_PRIVATE , fd , 0 ) ;
if ( map ! = NULL & & map ! = MAP_FAILED ) {
if ( strcmp ( map , " SQLite format 3 " ) ! = 0 ) {
2016-03-23 00:12:09 +00:00
if ( z > = 0 ) {
std : : string s = std : : string ( map , st . st_size ) ;
2017-03-28 23:25:40 +00:00
handle ( s , z , x , y , 1 , to_decode , pipeline ) ;
2016-03-23 00:12:09 +00:00
munmap ( map , st . st_size ) ;
return ;
} else {
fprintf ( stderr , " Must specify zoom/x/y to decode a single pbf file \n " ) ;
exit ( EXIT_FAILURE ) ;
}
2016-03-22 23:50:01 +00:00
}
}
munmap ( map , st . st_size ) ;
}
} else {
perror ( " fstat " ) ;
}
2016-12-01 23:34:44 +00:00
if ( close ( fd ) ! = 0 ) {
perror ( " close " ) ;
exit ( EXIT_FAILURE ) ;
}
2016-03-22 23:50:01 +00:00
} else {
perror ( fname ) ;
}
2014-10-23 22:40:27 +00:00
if ( sqlite3_open ( fname , & db ) ! = SQLITE_OK ) {
2015-06-03 18:21:40 +00:00
fprintf ( stderr , " %s: %s \n " , fname , sqlite3_errmsg ( db ) ) ;
2014-10-23 22:40:27 +00:00
exit ( EXIT_FAILURE ) ;
}
2015-10-19 18:12:11 +00:00
if ( z < 0 ) {
2017-03-28 23:25:40 +00:00
int within = 0 ;
2016-01-28 22:06:51 +00:00
2017-03-28 23:25:40 +00:00
if ( ! pipeline ) {
printf ( " { \" type \" : \" FeatureCollection \" , \" properties \" : { \n " ) ;
2016-01-28 22:06:51 +00:00
2017-03-28 23:25:40 +00:00
const char * sql2 = " SELECT name, value from metadata order by name; " ;
sqlite3_stmt * stmt2 ;
if ( sqlite3_prepare_v2 ( db , sql2 , - 1 , & stmt2 , NULL ) ! = SQLITE_OK ) {
fprintf ( stderr , " %s: select failed: %s \n " , fname , sqlite3_errmsg ( db ) ) ;
exit ( EXIT_FAILURE ) ;
2016-01-28 22:06:51 +00:00
}
2017-03-28 23:25:40 +00:00
while ( sqlite3_step ( stmt2 ) = = SQLITE_ROW ) {
if ( within ) {
printf ( " , \n " ) ;
}
within = 1 ;
2016-01-28 22:06:51 +00:00
2017-03-28 23:25:40 +00:00
const unsigned char * name = sqlite3_column_text ( stmt2 , 0 ) ;
const unsigned char * value = sqlite3_column_text ( stmt2 , 1 ) ;
fprintq ( stdout , ( char * ) name ) ;
printf ( " : " ) ;
fprintq ( stdout , ( char * ) value ) ;
}
2016-01-28 22:06:51 +00:00
2017-03-28 23:25:40 +00:00
sqlite3_finalize ( stmt2 ) ;
}
2016-01-28 22:06:51 +00:00
2017-01-24 22:14:10 +00:00
const char * sql = " SELECT tile_data, zoom_level, tile_column, tile_row from tiles where zoom_level between ? and ? order by zoom_level, tile_column, tile_row; " ;
2014-10-27 23:00:16 +00:00
sqlite3_stmt * stmt ;
if ( sqlite3_prepare_v2 ( db , sql , - 1 , & stmt , NULL ) ! = SQLITE_OK ) {
fprintf ( stderr , " %s: select failed: %s \n " , fname , sqlite3_errmsg ( db ) ) ;
exit ( EXIT_FAILURE ) ;
}
2014-10-23 22:40:27 +00:00
2017-01-24 22:14:10 +00:00
sqlite3_bind_int ( stmt , 1 , minzoom ) ;
sqlite3_bind_int ( stmt , 2 , maxzoom ) ;
2017-03-28 23:25:40 +00:00
if ( ! pipeline ) {
printf ( " \n }, \" features \" : [ \n " ) ;
}
2014-10-23 22:40:27 +00:00
2016-01-28 22:06:51 +00:00
within = 0 ;
2014-10-27 23:00:16 +00:00
while ( sqlite3_step ( stmt ) = = SQLITE_ROW ) {
2017-03-28 23:25:40 +00:00
if ( ! pipeline ) {
if ( within ) {
printf ( " , \n " ) ;
}
within = 1 ;
2015-10-19 18:12:11 +00:00
}
2014-10-27 23:00:16 +00:00
int len = sqlite3_column_bytes ( stmt , 0 ) ;
2016-05-03 22:48:42 +00:00
int tz = sqlite3_column_int ( stmt , 1 ) ;
int tx = sqlite3_column_int ( stmt , 2 ) ;
int ty = sqlite3_column_int ( stmt , 3 ) ;
ty = ( 1LL < < tz ) - 1 - ty ;
2014-10-27 23:00:16 +00:00
const char * s = ( const char * ) sqlite3_column_blob ( stmt , 0 ) ;
2014-10-23 22:40:27 +00:00
2017-03-28 23:25:40 +00:00
handle ( std : : string ( s , len ) , tz , tx , ty , 1 , to_decode , pipeline ) ;
2014-10-27 23:00:16 +00:00
}
2014-10-23 22:40:27 +00:00
2017-03-28 23:25:40 +00:00
if ( ! pipeline ) {
printf ( " ] } \n " ) ;
}
2015-10-19 18:12:11 +00:00
2014-10-27 23:00:16 +00:00
sqlite3_finalize ( stmt ) ;
2015-10-19 18:12:11 +00:00
} else {
int handled = 0 ;
while ( z > = 0 & & ! handled ) {
const char * sql = " SELECT tile_data from tiles where zoom_level = ? and tile_column = ? and tile_row = ?; " ;
sqlite3_stmt * stmt ;
if ( sqlite3_prepare_v2 ( db , sql , - 1 , & stmt , NULL ) ! = SQLITE_OK ) {
fprintf ( stderr , " %s: select failed: %s \n " , fname , sqlite3_errmsg ( db ) ) ;
exit ( EXIT_FAILURE ) ;
}
sqlite3_bind_int ( stmt , 1 , z ) ;
sqlite3_bind_int ( stmt , 2 , x ) ;
sqlite3_bind_int ( stmt , 3 , ( 1LL < < z ) - 1 - y ) ;
while ( sqlite3_step ( stmt ) = = SQLITE_ROW ) {
int len = sqlite3_column_bytes ( stmt , 0 ) ;
const char * s = ( const char * ) sqlite3_column_blob ( stmt , 0 ) ;
if ( z ! = oz ) {
fprintf ( stderr , " %s: Warning: using tile %d/%u/%u instead of %d/%u/%u \n " , fname , z , x , y , oz , ox , oy ) ;
}
2017-03-28 23:25:40 +00:00
handle ( std : : string ( s , len ) , z , x , y , 0 , to_decode , pipeline ) ;
2015-10-19 18:12:11 +00:00
handled = 1 ;
}
2014-10-27 23:00:16 +00:00
2015-10-19 18:12:11 +00:00
sqlite3_finalize ( stmt ) ;
z - - ;
x / = 2 ;
y / = 2 ;
}
2014-10-27 23:00:16 +00:00
}
2014-10-23 22:40:27 +00:00
2015-06-03 18:21:40 +00:00
if ( sqlite3_close ( db ) ! = SQLITE_OK ) {
fprintf ( stderr , " %s: could not close database: %s \n " , fname , sqlite3_errmsg ( db ) ) ;
exit ( EXIT_FAILURE ) ;
}
2014-10-23 22:40:27 +00:00
}
void usage ( char * * argv ) {
2017-08-08 18:08:10 +00:00
fprintf ( stderr , " Usage: %s [-s projection] [-Z minzoom] [-z maxzoom] [-l layer ...] file.mbtiles [zoom x y] \n " , argv [ 0 ] ) ;
2014-10-23 22:40:27 +00:00
exit ( EXIT_FAILURE ) ;
}
int main ( int argc , char * * argv ) {
extern int optind ;
2016-06-28 22:18:27 +00:00
extern char * optarg ;
2014-10-23 22:40:27 +00:00
int i ;
2017-02-27 18:10:28 +00:00
std : : set < std : : string > to_decode ;
2017-03-28 23:25:40 +00:00
bool pipeline = false ;
2014-10-23 22:40:27 +00:00
2017-07-07 19:37:08 +00:00
struct option long_options [ ] = {
{ " projection " , required_argument , 0 , ' s ' } ,
{ " maximum-zoom " , required_argument , 0 , ' z ' } ,
{ " minimum-zoom " , required_argument , 0 , ' Z ' } ,
{ " layer " , required_argument , 0 , ' l ' } ,
2017-08-08 18:08:10 +00:00
{ " tag-layer-and-zoom " , no_argument , 0 , ' c ' } ,
2017-07-07 19:37:08 +00:00
{ " force " , no_argument , 0 , ' f ' } ,
{ 0 , 0 , 0 , 0 } ,
} ;
std : : string getopt_str ;
for ( size_t lo = 0 ; long_options [ lo ] . name ! = NULL ; lo + + ) {
if ( long_options [ lo ] . val > ' ' ) {
getopt_str . push_back ( long_options [ lo ] . val ) ;
if ( long_options [ lo ] . has_arg = = required_argument ) {
getopt_str . push_back ( ' : ' ) ;
}
}
}
while ( ( i = getopt_long ( argc , argv , getopt_str . c_str ( ) , long_options , NULL ) ) ! = - 1 ) {
2016-06-28 22:18:27 +00:00
switch ( i ) {
2017-07-07 19:37:08 +00:00
case 0 :
break ;
case ' s ' :
2016-06-28 22:18:27 +00:00
set_projection_or_exit ( optarg ) ;
break ;
2017-01-24 22:14:10 +00:00
case ' z ' :
maxzoom = atoi ( optarg ) ;
break ;
case ' Z ' :
minzoom = atoi ( optarg ) ;
break ;
2017-02-27 18:10:28 +00:00
case ' l ' :
to_decode . insert ( optarg ) ;
break ;
2017-03-28 23:25:40 +00:00
case ' c ' :
pipeline = true ;
break ;
2017-05-05 17:56:50 +00:00
case ' f ' :
force = true ;
break ;
2016-06-28 22:18:27 +00:00
default :
usage ( argv ) ;
}
2014-10-23 22:40:27 +00:00
}
2015-10-19 18:12:11 +00:00
if ( argc = = optind + 4 ) {
2017-03-28 23:25:40 +00:00
decode ( argv [ optind ] , atoi ( argv [ optind + 1 ] ) , atoi ( argv [ optind + 2 ] ) , atoi ( argv [ optind + 3 ] ) , to_decode , pipeline ) ;
2015-10-19 18:12:11 +00:00
} else if ( argc = = optind + 1 ) {
2017-03-28 23:25:40 +00:00
decode ( argv [ optind ] , - 1 , - 1 , - 1 , to_decode , pipeline ) ;
2015-10-19 18:12:11 +00:00
} else {
2014-10-23 22:40:27 +00:00
usage ( argv ) ;
}
return 0 ;
}