2016-04-27 22:14:09 +00:00
# ifdef MTRACE
# include <mcheck.h>
# endif
2016-12-08 20:33:02 +00:00
# ifdef __APPLE__
# define _DARWIN_UNLIMITED_STREAMS
# endif
2016-04-27 22:14:09 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <math.h>
# include <string.h>
# include <unistd.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <sys/mman.h>
# include <string.h>
# include <fcntl.h>
2018-02-07 21:15:36 +00:00
# include <ctype.h>
2016-04-27 22:14:09 +00:00
# include <errno.h>
# include <limits.h>
# include <sqlite3.h>
# include <stdarg.h>
# include <sys/resource.h>
# include <pthread.h>
# include <getopt.h>
2016-12-21 18:10:22 +00:00
# include <signal.h>
2018-03-13 21:51:41 +00:00
# include <sys/time.h>
2018-05-11 22:51:53 +00:00
# include <zlib.h>
2017-04-07 16:03:30 +00:00
# include <algorithm>
2016-04-27 22:14:09 +00:00
# include <vector>
2016-04-28 19:14:19 +00:00
# include <string>
2016-04-28 19:57:03 +00:00
# include <set>
2016-08-23 22:33:53 +00:00
# include <map>
2016-10-11 00:15:33 +00:00
# include <cmath>
2016-04-27 22:14:09 +00:00
# ifdef __APPLE__
# include <sys/types.h>
# include <sys/sysctl.h>
# include <sys/param.h>
# include <sys/mount.h>
# else
# include <sys/statfs.h>
# endif
2016-04-27 22:33:30 +00:00
# include "jsonpull/jsonpull.h"
2016-08-30 00:42:46 +00:00
# include "mbtiles.hpp"
2016-04-27 22:14:09 +00:00
# include "tile.hpp"
# include "pool.hpp"
# include "projection.hpp"
# include "version.hpp"
# include "memfile.hpp"
# include "main.hpp"
# include "geojson.hpp"
2017-08-08 17:31:26 +00:00
# include "geobuf.hpp"
2017-12-06 01:18:19 +00:00
# include "geocsv.hpp"
2016-04-27 22:14:09 +00:00
# include "geometry.hpp"
2016-08-08 22:36:49 +00:00
# include "serial.hpp"
2016-04-27 22:14:09 +00:00
# include "options.hpp"
2017-03-16 22:06:26 +00:00
# include "mvt.hpp"
2017-07-20 21:17:09 +00:00
# include "dirtiles.hpp"
2017-09-01 18:51:12 +00:00
# include "evaluator.hpp"
2018-02-07 21:15:36 +00:00
# include "text.hpp"
2016-04-27 22:14:09 +00:00
static int low_detail = 12 ;
static int full_detail = - 1 ;
static int min_detail = 7 ;
int quiet = 0 ;
2017-11-17 01:08:03 +00:00
int quiet_progress = 0 ;
2018-03-13 21:51:41 +00:00
double progress_interval = 0 ;
2018-03-13 22:04:28 +00:00
std : : atomic < double > last_progress ( 0 ) ;
2016-04-27 22:14:09 +00:00
int geometry_scale = 0 ;
2016-07-12 23:51:56 +00:00
double simplification = 1 ;
2016-10-24 22:33:14 +00:00
size_t max_tile_size = 500000 ;
2017-12-14 22:30:08 +00:00
size_t max_tile_features = 200000 ;
2017-12-21 01:31:11 +00:00
int cluster_distance = 0 ;
2018-01-26 20:41:30 +00:00
long justx = - 1 , justy = - 1 ;
2018-11-02 22:21:52 +00:00
std : : string attribute_for_id = " " ;
2016-04-27 22:14:09 +00:00
2016-05-21 00:50:20 +00:00
int prevent [ 256 ] ;
int additional [ 256 ] ;
2016-04-27 22:14:09 +00:00
struct source {
2017-11-07 23:57:56 +00:00
std : : string layer = " " ;
std : : string file = " " ;
2018-05-24 17:27:43 +00:00
std : : string description = " " ;
2018-07-17 21:57:56 +00:00
std : : string format = " " ;
2016-04-27 22:14:09 +00:00
} ;
2016-10-15 00:11:57 +00:00
size_t CPUS ;
size_t TEMP_FILES ;
2016-04-27 22:14:09 +00:00
long long MAX_FILES ;
static long long diskfree ;
2018-02-27 01:35:39 +00:00
char * * av ;
2016-04-27 22:14:09 +00:00
2018-10-22 23:49:33 +00:00
std : : vector < clipbbox > clipbboxes ;
2017-11-07 23:20:17 +00:00
void checkdisk ( std : : vector < struct reader > * r ) {
2016-04-27 22:14:09 +00:00
long long used = 0 ;
2017-11-07 23:20:17 +00:00
for ( size_t i = 0 ; i < r - > size ( ) ; i + + ) {
2016-04-27 22:14:09 +00:00
// Meta, pool, and tree are used once.
// Geometry and index will be duplicated during sorting and tiling.
2017-11-07 23:20:17 +00:00
used + = ( * r ) [ i ] . metapos + 2 * ( * r ) [ i ] . geompos + 2 * ( * r ) [ i ] . indexpos + ( * r ) [ i ] . poolfile - > len + ( * r ) [ i ] . treefile - > len ;
2016-04-27 22:14:09 +00:00
}
static int warned = 0 ;
if ( used > diskfree * .9 & & ! warned ) {
fprintf ( stderr , " You will probably run out of disk space. \n %lld bytes used or committed, of %lld originally available \n " , used , diskfree ) ;
warned = 1 ;
}
} ;
2018-02-27 01:35:39 +00:00
int atoi_require ( const char * s , const char * what ) {
char * err = NULL ;
if ( * s = = ' \0 ' ) {
fprintf ( stderr , " %s: %s must be a number (got %s) \n " , * av , what , s ) ;
exit ( EXIT_FAILURE ) ;
}
int ret = strtol ( s , & err , 10 ) ;
if ( * err ! = ' \0 ' ) {
fprintf ( stderr , " %s: %s must be a number (got %s) \n " , * av , what , s ) ;
exit ( EXIT_FAILURE ) ;
}
return ret ;
}
double atof_require ( const char * s , const char * what ) {
char * err = NULL ;
if ( * s = = ' \0 ' ) {
fprintf ( stderr , " %s: %s must be a number (got %s) \n " , * av , what , s ) ;
exit ( EXIT_FAILURE ) ;
}
double ret = strtod ( s , & err ) ;
if ( * err ! = ' \0 ' ) {
fprintf ( stderr , " %s: %s must be a number (got %s) \n " , * av , what , s ) ;
exit ( EXIT_FAILURE ) ;
}
return ret ;
}
long long atoll_require ( const char * s , const char * what ) {
char * err = NULL ;
if ( * s = = ' \0 ' ) {
fprintf ( stderr , " %s: %s must be a number (got %s) \n " , * av , what , s ) ;
exit ( EXIT_FAILURE ) ;
}
long long ret = strtoll ( s , & err , 10 ) ;
if ( * err ! = ' \0 ' ) {
fprintf ( stderr , " %s: %s must be a number (got %s) \n " , * av , what , s ) ;
exit ( EXIT_FAILURE ) ;
}
return ret ;
}
2016-04-27 22:14:09 +00:00
void init_cpus ( ) {
2017-02-18 00:47:21 +00:00
const char * TIPPECANOE_MAX_THREADS = getenv ( " TIPPECANOE_MAX_THREADS " ) ;
if ( TIPPECANOE_MAX_THREADS ! = NULL ) {
2018-02-27 01:35:39 +00:00
CPUS = atoi_require ( TIPPECANOE_MAX_THREADS , " TIPPECANOE_MAX_THREADS " ) ;
2017-02-18 00:47:21 +00:00
} else {
CPUS = sysconf ( _SC_NPROCESSORS_ONLN ) ;
}
2016-04-27 22:14:09 +00:00
if ( CPUS < 1 ) {
CPUS = 1 ;
}
// Guard against short struct index.segment
if ( CPUS > 32767 ) {
CPUS = 32767 ;
}
// Round down to a power of 2
CPUS = 1 < < ( int ) ( log ( CPUS ) / log ( 2 ) ) ;
struct rlimit rl ;
if ( getrlimit ( RLIMIT_NOFILE , & rl ) ! = 0 ) {
perror ( " getrlimit " ) ;
exit ( EXIT_FAILURE ) ;
} else {
MAX_FILES = rl . rlim_cur ;
}
// Don't really want too many temporary files, because the file system
// will start to bog down eventually
if ( MAX_FILES > 2000 ) {
MAX_FILES = 2000 ;
}
// MacOS can run out of system file descriptors
// even if we stay under the rlimit, so try to
// find out the real limit.
long long fds [ MAX_FILES ] ;
long long i ;
for ( i = 0 ; i < MAX_FILES ; i + + ) {
2016-12-07 18:57:56 +00:00
fds [ i ] = open ( " /dev/null " , O_RDONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( fds [ i ] < 0 ) {
break ;
}
}
long long j ;
for ( j = 0 ; j < i ; j + + ) {
if ( close ( fds [ j ] ) < 0 ) {
perror ( " close " ) ;
exit ( EXIT_FAILURE ) ;
}
}
// Scale down because we really don't want to run the system out of files
MAX_FILES = i * 3 / 4 ;
if ( MAX_FILES < 32 ) {
fprintf ( stderr , " Can't open a useful number of files: %lld \n " , MAX_FILES ) ;
exit ( EXIT_FAILURE ) ;
}
TEMP_FILES = ( MAX_FILES - 10 ) / 2 ;
if ( TEMP_FILES > CPUS * 4 ) {
TEMP_FILES = CPUS * 4 ;
}
}
int indexcmp ( const void * v1 , const void * v2 ) {
const struct index * i1 = ( const struct index * ) v1 ;
const struct index * i2 = ( const struct index * ) v2 ;
2017-11-07 23:20:17 +00:00
if ( i1 - > ix < i2 - > ix ) {
2016-04-27 22:14:09 +00:00
return - 1 ;
2017-11-07 23:20:17 +00:00
} else if ( i1 - > ix > i2 - > ix ) {
2016-04-27 22:14:09 +00:00
return 1 ;
}
if ( i1 - > seq < i2 - > seq ) {
return - 1 ;
} else if ( i1 - > seq > i2 - > seq ) {
return 1 ;
}
return 0 ;
}
2016-05-03 23:39:26 +00:00
struct mergelist {
2016-04-27 22:14:09 +00:00
long long start ;
long long end ;
2016-05-03 23:39:26 +00:00
struct mergelist * next ;
2016-04-27 22:14:09 +00:00
} ;
2016-05-03 23:39:26 +00:00
static void insert ( struct mergelist * m , struct mergelist * * head , unsigned char * map ) {
2016-04-27 22:14:09 +00:00
while ( * head ! = NULL & & indexcmp ( map + m - > start , map + ( * head ) - > start ) > 0 ) {
head = & ( ( * head ) - > next ) ;
}
m - > next = * head ;
* head = m ;
}
2016-10-11 19:05:50 +00:00
struct drop_state {
double gap ;
unsigned long long previndex ;
double interval ;
double scale ;
double seq ;
long long included ;
2016-10-12 16:49:25 +00:00
unsigned x ;
unsigned y ;
2016-10-11 19:05:50 +00:00
} ;
2017-11-07 18:48:19 +00:00
int calc_feature_minzoom ( struct index * ix , struct drop_state * ds , int maxzoom , double gamma ) {
2016-10-11 19:42:20 +00:00
int feature_minzoom = 0 ;
2016-10-12 16:49:25 +00:00
unsigned xx , yy ;
2018-12-12 18:49:53 +00:00
decode_index ( ix - > ix , & xx , & yy ) ;
2016-10-11 19:42:20 +00:00
if ( gamma > = 0 & & ( ix - > t = = VT_POINT | |
( additional [ A_LINE_DROP ] & & ix - > t = = VT_LINE ) | |
( additional [ A_POLYGON_DROP ] & & ix - > t = = VT_POLYGON ) ) ) {
for ( ssize_t i = maxzoom ; i > = 0 ; i - - ) {
ds [ i ] . seq + + ;
}
for ( ssize_t i = maxzoom ; i > = 0 ; i - - ) {
if ( ds [ i ] . seq > = 0 ) {
ds [ i ] . seq - = ds [ i ] . interval ;
ds [ i ] . included + + ;
} else {
feature_minzoom = i + 1 ;
break ;
}
}
// XXX manage_gap
}
return feature_minzoom ;
}
2018-05-07 20:17:00 +00:00
static void merge ( struct mergelist * merges , size_t nmerges , unsigned char * map , FILE * indexfile , int bytes , char * geom_map , FILE * geom_out , std : : atomic < long long > * geompos , long long * progress , long long * progress_max , long long * progress_reported , int maxzoom , double gamma , struct drop_state * ds ) {
2016-05-03 23:39:26 +00:00
struct mergelist * head = NULL ;
2016-04-27 22:14:09 +00:00
2016-10-11 00:15:33 +00:00
for ( size_t i = 0 ; i < nmerges ; i + + ) {
2016-04-27 22:14:09 +00:00
if ( merges [ i ] . start < merges [ i ] . end ) {
insert ( & ( merges [ i ] ) , & head , map ) ;
}
}
2018-03-13 21:51:41 +00:00
last_progress = 0 ;
2016-04-27 22:14:09 +00:00
while ( head ! = NULL ) {
2016-10-11 22:13:27 +00:00
struct index ix = * ( ( struct index * ) ( map + head - > start ) ) ;
long long pos = * geompos ;
fwrite_check ( geom_map + ix . start , 1 , ix . end - ix . start , geom_out , " merge geometry " ) ;
* geompos + = ix . end - ix . start ;
2017-11-07 18:48:19 +00:00
int feature_minzoom = calc_feature_minzoom ( & ix , ds , maxzoom , gamma ) ;
2016-10-11 00:15:33 +00:00
serialize_byte ( geom_out , feature_minzoom , geompos , " merge geometry " ) ;
2016-04-27 22:14:09 +00:00
// Count this as an 75%-accomplishment, since we already 25%-counted it
2016-10-11 22:13:27 +00:00
* progress + = ( ix . end - ix . start ) * 3 / 4 ;
2018-03-13 21:51:41 +00:00
if ( ! quiet & & ! quiet_progress & & progress_time ( ) & & 100 * * progress / * progress_max ! = * progress_reported ) {
2016-04-27 22:14:09 +00:00
fprintf ( stderr , " Reordering geometry: %lld%% \r " , 100 * * progress / * progress_max ) ;
* progress_reported = 100 * * progress / * progress_max ;
}
2016-10-11 22:13:27 +00:00
ix . start = pos ;
ix . end = * geompos ;
fwrite_check ( & ix , bytes , 1 , indexfile , " merge temporary " ) ;
2016-04-27 22:14:09 +00:00
head - > start + = bytes ;
2016-05-03 23:39:26 +00:00
struct mergelist * m = head ;
2016-04-27 22:14:09 +00:00
head = m - > next ;
m - > next = NULL ;
if ( m - > start < m - > end ) {
insert ( m , & head , map ) ;
}
}
}
struct sort_arg {
int task ;
int cpus ;
long long indexpos ;
2016-05-03 23:39:26 +00:00
struct mergelist * merges ;
2016-04-27 22:14:09 +00:00
int indexfd ;
2016-10-15 00:11:57 +00:00
size_t nmerges ;
2016-04-27 22:14:09 +00:00
long long unit ;
int bytes ;
2017-11-10 21:59:57 +00:00
sort_arg ( int task1 , int cpus1 , long long indexpos1 , struct mergelist * merges1 , int indexfd1 , size_t nmerges1 , long long unit1 , int bytes1 )
: task ( task1 ) , cpus ( cpus1 ) , indexpos ( indexpos1 ) , merges ( merges1 ) , indexfd ( indexfd1 ) , nmerges ( nmerges1 ) , unit ( unit1 ) , bytes ( bytes1 ) {
}
2016-04-27 22:14:09 +00:00
} ;
void * run_sort ( void * v ) {
struct sort_arg * a = ( struct sort_arg * ) v ;
long long start ;
for ( start = a - > task * a - > unit ; start < a - > indexpos ; start + = a - > unit * a - > cpus ) {
long long end = start + a - > unit ;
if ( end > a - > indexpos ) {
end = a - > indexpos ;
}
a - > merges [ start / a - > unit ] . start = start ;
a - > merges [ start / a - > unit ] . end = end ;
a - > merges [ start / a - > unit ] . next = NULL ;
// MAP_PRIVATE to avoid disk writes if it fits in memory
void * map = mmap ( NULL , end - start , PROT_READ | PROT_WRITE , MAP_PRIVATE , a - > indexfd , start ) ;
if ( map = = MAP_FAILED ) {
perror ( " mmap in run_sort " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( map , end - start , MADV_RANDOM ) ;
madvise ( map , end - start , MADV_WILLNEED ) ;
qsort ( map , ( end - start ) / a - > bytes , a - > bytes , indexcmp ) ;
// Sorting and then copying avoids disk access to
// write out intermediate stages of the sort.
void * map2 = mmap ( NULL , end - start , PROT_READ | PROT_WRITE , MAP_SHARED , a - > indexfd , start ) ;
if ( map2 = = MAP_FAILED ) {
perror ( " mmap (write) " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( map2 , end - start , MADV_SEQUENTIAL ) ;
memcpy ( map2 , map , end - start ) ;
// No madvise, since caller will want the sorted data
munmap ( map , end - start ) ;
munmap ( map2 , end - start ) ;
}
return NULL ;
}
2019-01-22 22:26:06 +00:00
void do_read_parallel ( char * map , long long len , long long initial_offset , const char * reading , std : : vector < struct reader > * readers , std : : atomic < long long > * progress_seq , std : : set < std : : string > * exclude , std : : set < std : : string > * include , int exclude_all , int basezoom , int source , std : : vector < std : : map < std : : string , layermap_entry > > * layermaps , int * initialized , unsigned * initial_x , unsigned * initial_y , int maxzoom , std : : string layername , bool uses_gamma , std : : map < std : : string , int > const * attribute_types , int separator , double * dist_sum , size_t * dist_count , bool want_dist , bool filters ) {
2016-04-27 22:14:09 +00:00
long long segs [ CPUS + 1 ] ;
segs [ 0 ] = 0 ;
segs [ CPUS ] = len ;
2016-10-15 00:11:57 +00:00
for ( size_t i = 1 ; i < CPUS ; i + + ) {
2016-04-27 22:14:09 +00:00
segs [ i ] = len * i / CPUS ;
2017-04-28 17:41:20 +00:00
while ( segs [ i ] < len & & map [ segs [ i ] ] ! = separator ) {
2016-04-27 22:14:09 +00:00
segs [ i ] + + ;
}
}
2017-05-30 20:28:25 +00:00
double dist_sums [ CPUS ] ;
size_t dist_counts [ CPUS ] ;
2018-03-13 22:21:21 +00:00
std : : atomic < long long > layer_seq [ CPUS ] ;
2016-10-15 00:11:57 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
2016-04-27 22:14:09 +00:00
// To preserve feature ordering, unique id for each segment
// begins with that segment's offset into the input
layer_seq [ i ] = segs [ i ] + initial_offset ;
2017-05-30 20:28:25 +00:00
dist_sums [ i ] = dist_counts [ i ] = 0 ;
2016-04-27 22:14:09 +00:00
}
2017-11-07 23:20:17 +00:00
std : : vector < parse_json_args > pja ;
std : : vector < serialization_state > sst ;
sst . resize ( CPUS ) ;
2016-04-27 22:14:09 +00:00
pthread_t pthreads [ CPUS ] ;
2016-04-28 21:43:04 +00:00
std : : vector < std : : set < type_and_string > > file_subkeys ;
2016-10-15 00:11:57 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
2016-04-28 21:43:04 +00:00
file_subkeys . push_back ( std : : set < type_and_string > ( ) ) ;
}
2016-04-27 22:14:09 +00:00
2016-10-15 00:11:57 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
2017-08-23 18:43:48 +00:00
sst [ i ] . fname = reading ;
sst [ i ] . line = 0 ;
sst [ i ] . layer_seq = & layer_seq [ i ] ;
sst [ i ] . progress_seq = progress_seq ;
2017-08-31 20:49:19 +00:00
sst [ i ] . readers = readers ;
2017-08-23 18:43:48 +00:00
sst [ i ] . segment = i ;
sst [ i ] . initialized = & initialized [ i ] ;
sst [ i ] . initial_x = & initial_x [ i ] ;
sst [ i ] . initial_y = & initial_y [ i ] ;
sst [ i ] . dist_sum = & ( dist_sums [ i ] ) ;
sst [ i ] . dist_count = & ( dist_counts [ i ] ) ;
2017-08-24 22:46:35 +00:00
sst [ i ] . want_dist = want_dist ;
sst [ i ] . maxzoom = maxzoom ;
sst [ i ] . uses_gamma = uses_gamma ;
sst [ i ] . filters = filters ;
2017-08-24 23:30:01 +00:00
sst [ i ] . layermap = & ( * layermaps ) [ i ] ;
2017-08-25 00:10:15 +00:00
sst [ i ] . exclude = exclude ;
sst [ i ] . include = include ;
sst [ i ] . exclude_all = exclude_all ;
sst [ i ] . basezoom = basezoom ;
2017-08-25 00:27:30 +00:00
sst [ i ] . attribute_types = attribute_types ;
2017-08-23 18:43:48 +00:00
2017-11-10 21:06:47 +00:00
pja . push_back ( parse_json_args (
json_begin_map ( map + segs [ i ] , segs [ i + 1 ] - segs [ i ] ) ,
source ,
& layername ,
& sst [ i ] ) ) ;
}
2017-08-23 18:43:48 +00:00
2017-11-10 21:06:47 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
2016-04-27 22:14:09 +00:00
if ( pthread_create ( & pthreads [ i ] , NULL , run_parse_json , & pja [ i ] ) ! = 0 ) {
perror ( " pthread_create " ) ;
exit ( EXIT_FAILURE ) ;
}
}
2016-10-15 00:11:57 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
2016-04-27 22:14:09 +00:00
void * retval ;
if ( pthread_join ( pthreads [ i ] , & retval ) ! = 0 ) {
2016-07-13 20:00:38 +00:00
perror ( " pthread_join 370 " ) ;
2016-04-27 22:14:09 +00:00
}
2017-05-30 20:28:25 +00:00
* dist_sum + = dist_sums [ i ] ;
* dist_count + = dist_counts [ i ] ;
2016-08-30 21:02:51 +00:00
json_end_map ( pja [ i ] . jp ) ;
2016-04-27 22:14:09 +00:00
}
}
2018-05-11 21:58:29 +00:00
static ssize_t read_stream ( json_pull * j , char * buffer , size_t n ) ;
2018-05-11 21:36:46 +00:00
struct STREAM {
2018-05-14 19:17:49 +00:00
FILE * fp = NULL ;
gzFile gz = NULL ;
2018-05-11 22:51:53 +00:00
2018-05-11 21:36:46 +00:00
int fclose ( ) {
2018-05-14 19:17:49 +00:00
int ret ;
if ( gz ! = NULL ) {
ret = gzclose ( gz ) ;
} else {
ret = : : fclose ( fp ) ;
2018-05-11 22:51:53 +00:00
}
2018-05-11 21:36:46 +00:00
delete this ;
return ret ;
}
int peekc ( ) {
2018-05-14 19:17:49 +00:00
if ( gz ! = NULL ) {
int c = gzgetc ( gz ) ;
if ( c ! = EOF ) {
gzungetc ( c , gz ) ;
2018-05-11 23:02:53 +00:00
}
2018-05-14 19:17:49 +00:00
return c ;
2018-05-11 23:02:53 +00:00
} else {
int c = getc ( fp ) ;
if ( c ! = EOF ) {
ungetc ( c , fp ) ;
}
return c ;
2018-05-11 21:36:46 +00:00
}
}
2018-05-11 23:02:53 +00:00
size_t read ( char * out , size_t count ) {
2018-05-14 19:17:49 +00:00
if ( gz ! = NULL ) {
int ret = gzread ( gz , out , count ) ;
if ( ret < 0 ) {
fprintf ( stderr , " %s: Error reading compressed data \n " , * av ) ;
exit ( EXIT_FAILURE ) ;
2018-05-11 22:51:53 +00:00
}
2018-05-14 19:17:49 +00:00
return ret ;
2018-05-11 22:51:53 +00:00
} else {
2018-05-11 23:02:53 +00:00
return : : fread ( out , 1 , count , fp ) ;
2018-05-11 22:51:53 +00:00
}
2018-05-11 21:36:46 +00:00
}
json_pull * json_begin ( ) {
2018-05-11 21:58:29 +00:00
return : : json_begin ( read_stream , this ) ;
2018-05-11 21:36:46 +00:00
}
} ;
2018-05-11 21:58:29 +00:00
static ssize_t read_stream ( json_pull * j , char * buffer , size_t n ) {
2018-05-11 23:02:53 +00:00
return ( ( STREAM * ) j - > source ) - > read ( buffer , n ) ;
2018-05-11 21:58:29 +00:00
}
2018-05-11 21:36:46 +00:00
2018-05-11 21:58:29 +00:00
STREAM * streamfdopen ( int fd , const char * mode , std : : string const & fname ) {
2018-05-11 21:36:46 +00:00
STREAM * s = new STREAM ;
2018-05-14 19:17:49 +00:00
s - > fp = NULL ;
s - > gz = NULL ;
2018-05-11 21:58:29 +00:00
if ( fname . size ( ) > 3 & & fname . substr ( fname . size ( ) - 3 ) = = std : : string ( " .gz " ) ) {
2018-05-14 19:17:49 +00:00
s - > gz = gzdopen ( fd , mode ) ;
if ( s - > gz = = NULL ) {
fprintf ( stderr , " %s: %s: Decompression error \n " , * av , fname . c_str ( ) ) ;
exit ( EXIT_FAILURE ) ;
}
} else {
s - > fp = fdopen ( fd , mode ) ;
if ( s - > fp = = NULL ) {
perror ( fname . c_str ( ) ) ;
2018-05-11 22:51:53 +00:00
exit ( EXIT_FAILURE ) ;
}
2018-05-11 21:58:29 +00:00
}
2018-05-11 21:36:46 +00:00
return s ;
}
STREAM * streamfpopen ( FILE * fp ) {
STREAM * s = new STREAM ;
s - > fp = fp ;
2018-05-14 19:17:49 +00:00
s - > gz = NULL ;
2018-05-12 00:13:06 +00:00
2018-05-11 21:36:46 +00:00
return s ;
}
2016-04-27 22:14:09 +00:00
struct read_parallel_arg {
2017-11-07 23:57:56 +00:00
int fd = 0 ;
2018-05-11 21:36:46 +00:00
STREAM * fp = NULL ;
2017-11-07 23:57:56 +00:00
long long offset = 0 ;
long long len = 0 ;
2018-03-13 22:21:21 +00:00
std : : atomic < int > * is_parsing = NULL ;
2017-11-07 23:57:56 +00:00
int separator = 0 ;
const char * reading = NULL ;
std : : vector < struct reader > * readers = NULL ;
2018-03-13 22:21:21 +00:00
std : : atomic < long long > * progress_seq = NULL ;
2017-11-07 23:57:56 +00:00
std : : set < std : : string > * exclude = NULL ;
std : : set < std : : string > * include = NULL ;
int exclude_all = 0 ;
int maxzoom = 0 ;
int basezoom = 0 ;
int source = 0 ;
std : : vector < std : : map < std : : string , layermap_entry > > * layermaps = NULL ;
int * initialized = NULL ;
unsigned * initial_x = NULL ;
unsigned * initial_y = NULL ;
std : : string layername = " " ;
bool uses_gamma = false ;
std : : map < std : : string , int > const * attribute_types = NULL ;
double * dist_sum = NULL ;
size_t * dist_count = NULL ;
bool want_dist = false ;
bool filters = false ;
2016-04-27 22:14:09 +00:00
} ;
void * run_read_parallel ( void * v ) {
2016-08-30 21:02:51 +00:00
struct read_parallel_arg * rpa = ( struct read_parallel_arg * ) v ;
2016-04-27 22:14:09 +00:00
struct stat st ;
2016-08-30 21:02:51 +00:00
if ( fstat ( rpa - > fd , & st ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " stat read temp " ) ;
}
2016-08-30 21:02:51 +00:00
if ( rpa - > len ! = st . st_size ) {
fprintf ( stderr , " wrong number of bytes in temporary: %lld vs %lld \n " , rpa - > len , ( long long ) st . st_size ) ;
2016-04-27 22:14:09 +00:00
}
2016-08-30 21:02:51 +00:00
rpa - > len = st . st_size ;
2016-04-27 22:14:09 +00:00
2016-08-30 21:02:51 +00:00
char * map = ( char * ) mmap ( NULL , rpa - > len , PROT_READ , MAP_PRIVATE , rpa - > fd , 0 ) ;
2016-04-27 22:14:09 +00:00
if ( map = = NULL | | map = = MAP_FAILED ) {
perror ( " map intermediate input " ) ;
exit ( EXIT_FAILURE ) ;
}
2016-08-30 21:02:51 +00:00
madvise ( map , rpa - > len , MADV_RANDOM ) ; // sequential, but from several pointers at once
2016-04-27 22:14:09 +00:00
2019-01-22 22:26:06 +00:00
do_read_parallel ( map , rpa - > len , rpa - > offset , rpa - > reading , rpa - > readers , rpa - > progress_seq , rpa - > exclude , rpa - > include , rpa - > exclude_all , rpa - > basezoom , rpa - > source , rpa - > layermaps , rpa - > initialized , rpa - > initial_x , rpa - > initial_y , rpa - > maxzoom , rpa - > layername , rpa - > uses_gamma , rpa - > attribute_types , rpa - > separator , rpa - > dist_sum , rpa - > dist_count , rpa - > want_dist , rpa - > filters ) ;
2016-04-27 22:14:09 +00:00
2016-08-30 21:02:51 +00:00
madvise ( map , rpa - > len , MADV_DONTNEED ) ;
if ( munmap ( map , rpa - > len ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " munmap source file " ) ;
}
2018-05-11 21:36:46 +00:00
if ( rpa - > fp - > fclose ( ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " close source file " ) ;
exit ( EXIT_FAILURE ) ;
}
2016-08-30 21:02:51 +00:00
* ( rpa - > is_parsing ) = 0 ;
delete rpa ;
2016-04-27 22:14:09 +00:00
return NULL ;
}
2019-01-22 22:26:06 +00:00
void start_parsing ( int fd , STREAM * fp , long long offset , long long len , std : : atomic < int > * is_parsing , pthread_t * parallel_parser , bool & parser_created , const char * reading , std : : vector < struct reader > * readers , std : : atomic < long long > * progress_seq , std : : set < std : : string > * exclude , std : : set < std : : string > * include , int exclude_all , int basezoom , int source , std : : vector < std : : map < std : : string , layermap_entry > > & layermaps , int * initialized , unsigned * initial_x , unsigned * initial_y , int maxzoom , std : : string layername , bool uses_gamma , std : : map < std : : string , int > const * attribute_types , int separator , double * dist_sum , size_t * dist_count , bool want_dist , bool filters ) {
2016-04-27 22:14:09 +00:00
// This has to kick off an intermediate thread to start the parser threads,
// so the main thread can get back to reading the next input stage while
// the intermediate thread waits for the completion of the parser threads.
* is_parsing = 1 ;
2016-08-30 21:02:51 +00:00
struct read_parallel_arg * rpa = new struct read_parallel_arg ;
2016-04-27 22:14:09 +00:00
if ( rpa = = NULL ) {
perror ( " Out of memory " ) ;
exit ( EXIT_FAILURE ) ;
}
rpa - > fd = fd ;
rpa - > fp = fp ;
rpa - > offset = offset ;
rpa - > len = len ;
rpa - > is_parsing = is_parsing ;
2017-04-28 17:41:20 +00:00
rpa - > separator = separator ;
2016-04-27 22:14:09 +00:00
rpa - > reading = reading ;
2017-08-31 20:49:19 +00:00
rpa - > readers = readers ;
2016-04-27 22:14:09 +00:00
rpa - > progress_seq = progress_seq ;
rpa - > exclude = exclude ;
rpa - > include = include ;
rpa - > exclude_all = exclude_all ;
rpa - > basezoom = basezoom ;
rpa - > source = source ;
2016-08-23 22:33:53 +00:00
rpa - > layermaps = & layermaps ;
2016-04-27 22:14:09 +00:00
rpa - > initialized = initialized ;
rpa - > initial_x = initial_x ;
rpa - > initial_y = initial_y ;
2016-05-23 22:45:55 +00:00
rpa - > maxzoom = maxzoom ;
2016-08-29 23:38:57 +00:00
rpa - > layername = layername ;
2016-11-12 01:37:46 +00:00
rpa - > uses_gamma = uses_gamma ;
2017-04-17 21:42:50 +00:00
rpa - > attribute_types = attribute_types ;
2017-05-30 20:28:25 +00:00
rpa - > dist_sum = dist_sum ;
rpa - > dist_count = dist_count ;
2017-05-30 23:58:56 +00:00
rpa - > want_dist = want_dist ;
2017-08-08 20:38:18 +00:00
rpa - > filters = filters ;
2016-04-27 22:14:09 +00:00
if ( pthread_create ( parallel_parser , NULL , run_read_parallel , rpa ) ! = 0 ) {
perror ( " pthread_create " ) ;
exit ( EXIT_FAILURE ) ;
}
2016-07-13 20:00:38 +00:00
parser_created = true ;
2016-04-27 22:14:09 +00:00
}
2018-05-07 20:17:00 +00:00
void radix1 ( int * geomfds_in , int * indexfds_in , int inputs , int prefix , int splits , long long mem , const char * tmpdir , long long * availfiles , FILE * geomfile , FILE * indexfile , std : : atomic < long long > * geompos_out , long long * progress , long long * progress_max , long long * progress_reported , int maxzoom , int basezoom , double droprate , double gamma , struct drop_state * ds ) {
2016-04-27 22:14:09 +00:00
// Arranged as bits to facilitate subdividing again if a subdivided file is still huge
int splitbits = log ( splits ) / log ( 2 ) ;
splits = 1 < < splitbits ;
FILE * geomfiles [ splits ] ;
FILE * indexfiles [ splits ] ;
int geomfds [ splits ] ;
int indexfds [ splits ] ;
2018-05-07 20:17:00 +00:00
std : : atomic < long long > sub_geompos [ splits ] ;
2016-04-27 22:14:09 +00:00
int i ;
for ( i = 0 ; i < splits ; i + + ) {
sub_geompos [ i ] = 0 ;
char geomname [ strlen ( tmpdir ) + strlen ( " /geom.XXXXXXXX " ) + 1 ] ;
sprintf ( geomname , " %s%s " , tmpdir , " /geom.XXXXXXXX " ) ;
char indexname [ strlen ( tmpdir ) + strlen ( " /index.XXXXXXXX " ) + 1 ] ;
sprintf ( indexname , " %s%s " , tmpdir , " /index.XXXXXXXX " ) ;
2016-12-07 18:57:56 +00:00
geomfds [ i ] = mkstemp_cloexec ( geomname ) ;
2016-04-27 22:14:09 +00:00
if ( geomfds [ i ] < 0 ) {
perror ( geomname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
indexfds [ i ] = mkstemp_cloexec ( indexname ) ;
2016-04-27 22:14:09 +00:00
if ( indexfds [ i ] < 0 ) {
perror ( indexname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
geomfiles [ i ] = fopen_oflag ( geomname , " wb " , O_WRONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( geomfiles [ i ] = = NULL ) {
perror ( geomname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
indexfiles [ i ] = fopen_oflag ( indexname , " wb " , O_WRONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( indexfiles [ i ] = = NULL ) {
perror ( indexname ) ;
exit ( EXIT_FAILURE ) ;
}
* availfiles - = 4 ;
unlink ( geomname ) ;
unlink ( indexname ) ;
}
for ( i = 0 ; i < inputs ; i + + ) {
struct stat geomst , indexst ;
if ( fstat ( geomfds_in [ i ] , & geomst ) < 0 ) {
perror ( " stat geom " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( fstat ( indexfds_in [ i ] , & indexst ) < 0 ) {
perror ( " stat index " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( indexst . st_size ! = 0 ) {
struct index * indexmap = ( struct index * ) mmap ( NULL , indexst . st_size , PROT_READ , MAP_PRIVATE , indexfds_in [ i ] , 0 ) ;
if ( indexmap = = MAP_FAILED ) {
fprintf ( stderr , " fd %lld, len %lld \n " , ( long long ) indexfds_in [ i ] , ( long long ) indexst . st_size ) ;
perror ( " map index " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( indexmap , indexst . st_size , MADV_SEQUENTIAL ) ;
madvise ( indexmap , indexst . st_size , MADV_WILLNEED ) ;
char * geommap = ( char * ) mmap ( NULL , geomst . st_size , PROT_READ , MAP_PRIVATE , geomfds_in [ i ] , 0 ) ;
if ( geommap = = MAP_FAILED ) {
perror ( " map geom " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( geommap , geomst . st_size , MADV_SEQUENTIAL ) ;
madvise ( geommap , geomst . st_size , MADV_WILLNEED ) ;
2016-05-03 23:39:26 +00:00
for ( size_t a = 0 ; a < indexst . st_size / sizeof ( struct index ) ; a + + ) {
2016-04-27 22:14:09 +00:00
struct index ix = indexmap [ a ] ;
2017-11-07 23:20:17 +00:00
unsigned long long which = ( ix . ix < < prefix ) > > ( 64 - splitbits ) ;
2016-04-27 22:14:09 +00:00
long long pos = sub_geompos [ which ] ;
fwrite_check ( geommap + ix . start , ix . end - ix . start , 1 , geomfiles [ which ] , " geom " ) ;
sub_geompos [ which ] + = ix . end - ix . start ;
// Count this as a 25%-accomplishment, since we will copy again
* progress + = ( ix . end - ix . start ) / 4 ;
2018-03-13 21:51:41 +00:00
if ( ! quiet & & ! quiet_progress & & progress_time ( ) & & 100 * * progress / * progress_max ! = * progress_reported ) {
2016-04-27 22:14:09 +00:00
fprintf ( stderr , " Reordering geometry: %lld%% \r " , 100 * * progress / * progress_max ) ;
* progress_reported = 100 * * progress / * progress_max ;
}
ix . start = pos ;
ix . end = sub_geompos [ which ] ;
fwrite_check ( & ix , sizeof ( struct index ) , 1 , indexfiles [ which ] , " index " ) ;
}
madvise ( indexmap , indexst . st_size , MADV_DONTNEED ) ;
if ( munmap ( indexmap , indexst . st_size ) < 0 ) {
perror ( " unmap index " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( geommap , geomst . st_size , MADV_DONTNEED ) ;
if ( munmap ( geommap , geomst . st_size ) < 0 ) {
perror ( " unmap geom " ) ;
exit ( EXIT_FAILURE ) ;
}
}
if ( close ( geomfds_in [ i ] ) < 0 ) {
perror ( " close geom " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( close ( indexfds_in [ i ] ) < 0 ) {
perror ( " close index " ) ;
exit ( EXIT_FAILURE ) ;
}
* availfiles + = 2 ;
}
for ( i = 0 ; i < splits ; i + + ) {
if ( fclose ( geomfiles [ i ] ) ! = 0 ) {
perror ( " fclose geom " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( fclose ( indexfiles [ i ] ) ! = 0 ) {
perror ( " fclose index " ) ;
exit ( EXIT_FAILURE ) ;
}
* availfiles + = 2 ;
}
for ( i = 0 ; i < splits ; i + + ) {
int already_closed = 0 ;
struct stat geomst , indexst ;
if ( fstat ( geomfds [ i ] , & geomst ) < 0 ) {
perror ( " stat geom " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( fstat ( indexfds [ i ] , & indexst ) < 0 ) {
perror ( " stat index " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( indexst . st_size > 0 ) {
2016-05-03 23:39:26 +00:00
if ( indexst . st_size + geomst . st_size < mem ) {
2018-05-07 20:17:00 +00:00
std : : atomic < long long > indexpos ( indexst . st_size ) ;
2016-04-27 22:14:09 +00:00
int bytes = sizeof ( struct index ) ;
int page = sysconf ( _SC_PAGESIZE ) ;
// Don't try to sort more than 2GB at once,
// which used to crash Macs and may still
long long max_unit = 2LL * 1024 * 1024 * 1024 ;
long long unit = ( ( indexpos / CPUS + bytes - 1 ) / bytes ) * bytes ;
if ( unit > max_unit ) {
unit = max_unit ;
}
unit = ( ( unit + page - 1 ) / page ) * page ;
2017-02-17 22:14:55 +00:00
if ( unit < page ) {
unit = page ;
}
2016-04-27 22:14:09 +00:00
2016-10-15 00:11:57 +00:00
size_t nmerges = ( indexpos + unit - 1 ) / unit ;
2016-05-03 23:39:26 +00:00
struct mergelist merges [ nmerges ] ;
2016-04-27 22:14:09 +00:00
2016-10-15 00:11:57 +00:00
for ( size_t a = 0 ; a < nmerges ; a + + ) {
2016-04-27 22:14:09 +00:00
merges [ a ] . start = merges [ a ] . end = 0 ;
}
pthread_t pthreads [ CPUS ] ;
2017-11-10 21:59:57 +00:00
std : : vector < sort_arg > args ;
2016-04-27 22:14:09 +00:00
2016-10-15 00:11:57 +00:00
for ( size_t a = 0 ; a < CPUS ; a + + ) {
2017-11-10 21:59:57 +00:00
args . push_back ( sort_arg (
a ,
CPUS ,
indexpos ,
merges ,
indexfds [ i ] ,
nmerges ,
unit ,
bytes ) ) ;
}
2016-04-27 22:14:09 +00:00
2017-11-10 21:59:57 +00:00
for ( size_t a = 0 ; a < CPUS ; a + + ) {
2016-04-27 22:14:09 +00:00
if ( pthread_create ( & pthreads [ a ] , NULL , run_sort , & args [ a ] ) ! = 0 ) {
perror ( " pthread_create " ) ;
exit ( EXIT_FAILURE ) ;
}
}
2016-10-15 00:11:57 +00:00
for ( size_t a = 0 ; a < CPUS ; a + + ) {
2016-04-27 22:14:09 +00:00
void * retval ;
if ( pthread_join ( pthreads [ a ] , & retval ) ! = 0 ) {
2016-07-13 20:00:38 +00:00
perror ( " pthread_join 679 " ) ;
2016-04-27 22:14:09 +00:00
}
}
struct indexmap * indexmap = ( struct indexmap * ) mmap ( NULL , indexst . st_size , PROT_READ , MAP_PRIVATE , indexfds [ i ] , 0 ) ;
if ( indexmap = = MAP_FAILED ) {
fprintf ( stderr , " fd %lld, len %lld \n " , ( long long ) indexfds [ i ] , ( long long ) indexst . st_size ) ;
perror ( " map index " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( indexmap , indexst . st_size , MADV_RANDOM ) ; // sequential, but from several pointers at once
madvise ( indexmap , indexst . st_size , MADV_WILLNEED ) ;
char * geommap = ( char * ) mmap ( NULL , geomst . st_size , PROT_READ , MAP_PRIVATE , geomfds [ i ] , 0 ) ;
if ( geommap = = MAP_FAILED ) {
perror ( " map geom " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( geommap , geomst . st_size , MADV_RANDOM ) ;
madvise ( geommap , geomst . st_size , MADV_WILLNEED ) ;
2017-11-07 19:06:30 +00:00
merge ( merges , nmerges , ( unsigned char * ) indexmap , indexfile , bytes , geommap , geomfile , geompos_out , progress , progress_max , progress_reported , maxzoom , gamma , ds ) ;
2016-04-27 22:14:09 +00:00
madvise ( indexmap , indexst . st_size , MADV_DONTNEED ) ;
if ( munmap ( indexmap , indexst . st_size ) < 0 ) {
perror ( " unmap index " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( geommap , geomst . st_size , MADV_DONTNEED ) ;
if ( munmap ( geommap , geomst . st_size ) < 0 ) {
perror ( " unmap geom " ) ;
exit ( EXIT_FAILURE ) ;
}
} else if ( indexst . st_size = = sizeof ( struct index ) | | prefix + splitbits > = 64 ) {
struct index * indexmap = ( struct index * ) mmap ( NULL , indexst . st_size , PROT_READ , MAP_PRIVATE , indexfds [ i ] , 0 ) ;
if ( indexmap = = MAP_FAILED ) {
fprintf ( stderr , " fd %lld, len %lld \n " , ( long long ) indexfds [ i ] , ( long long ) indexst . st_size ) ;
perror ( " map index " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( indexmap , indexst . st_size , MADV_SEQUENTIAL ) ;
madvise ( indexmap , indexst . st_size , MADV_WILLNEED ) ;
char * geommap = ( char * ) mmap ( NULL , geomst . st_size , PROT_READ , MAP_PRIVATE , geomfds [ i ] , 0 ) ;
if ( geommap = = MAP_FAILED ) {
perror ( " map geom " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( geommap , geomst . st_size , MADV_RANDOM ) ;
madvise ( geommap , geomst . st_size , MADV_WILLNEED ) ;
2016-05-03 23:39:26 +00:00
for ( size_t a = 0 ; a < indexst . st_size / sizeof ( struct index ) ; a + + ) {
2016-04-27 22:14:09 +00:00
struct index ix = indexmap [ a ] ;
long long pos = * geompos_out ;
fwrite_check ( geommap + ix . start , ix . end - ix . start , 1 , geomfile , " geom " ) ;
* geompos_out + = ix . end - ix . start ;
2017-11-07 18:48:19 +00:00
int feature_minzoom = calc_feature_minzoom ( & ix , ds , maxzoom , gamma ) ;
2016-10-11 19:42:20 +00:00
serialize_byte ( geomfile , feature_minzoom , geompos_out , " merge geometry " ) ;
2016-04-27 22:14:09 +00:00
// Count this as an 75%-accomplishment, since we already 25%-counted it
* progress + = ( ix . end - ix . start ) * 3 / 4 ;
2018-03-13 21:51:41 +00:00
if ( ! quiet & & ! quiet_progress & & progress_time ( ) & & 100 * * progress / * progress_max ! = * progress_reported ) {
2016-04-27 22:14:09 +00:00
fprintf ( stderr , " Reordering geometry: %lld%% \r " , 100 * * progress / * progress_max ) ;
* progress_reported = 100 * * progress / * progress_max ;
}
ix . start = pos ;
ix . end = * geompos_out ;
fwrite_check ( & ix , sizeof ( struct index ) , 1 , indexfile , " index " ) ;
}
madvise ( indexmap , indexst . st_size , MADV_DONTNEED ) ;
if ( munmap ( indexmap , indexst . st_size ) < 0 ) {
perror ( " unmap index " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( geommap , geomst . st_size , MADV_DONTNEED ) ;
if ( munmap ( geommap , geomst . st_size ) < 0 ) {
perror ( " unmap geom " ) ;
exit ( EXIT_FAILURE ) ;
}
} else {
// We already reported the progress from splitting this radix out
// but we need to split it again, which will be credited with more
// progress. So increase the total amount of progress to report by
// the additional progress that will happpen, which may move the
// counter backward but will be an honest estimate of the work remaining.
* progress_max + = geomst . st_size / 4 ;
2016-10-11 19:05:50 +00:00
radix1 ( & geomfds [ i ] , & indexfds [ i ] , 1 , prefix + splitbits , * availfiles / 4 , mem , tmpdir , availfiles , geomfile , indexfile , geompos_out , progress , progress_max , progress_reported , maxzoom , basezoom , droprate , gamma , ds ) ;
2016-04-27 22:14:09 +00:00
already_closed = 1 ;
}
}
if ( ! already_closed ) {
if ( close ( geomfds [ i ] ) < 0 ) {
perror ( " close geom " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( close ( indexfds [ i ] ) < 0 ) {
perror ( " close index " ) ;
exit ( EXIT_FAILURE ) ;
}
* availfiles + = 2 ;
}
}
}
2016-10-12 00:24:22 +00:00
void prep_drop_states ( struct drop_state * ds , int maxzoom , int basezoom , double droprate ) {
// Needs to be signed for interval calculation
for ( ssize_t i = 0 ; i < = maxzoom ; i + + ) {
ds [ i ] . gap = 0 ;
ds [ i ] . previndex = 0 ;
ds [ i ] . interval = 0 ;
if ( i < basezoom ) {
ds [ i ] . interval = std : : exp ( std : : log ( droprate ) * ( basezoom - i ) ) ;
}
ds [ i ] . scale = ( double ) ( 1LL < < ( 64 - 2 * ( i + 8 ) ) ) ;
ds [ i ] . seq = 0 ;
ds [ i ] . included = 0 ;
2016-10-12 16:49:25 +00:00
ds [ i ] . x = 0 ;
ds [ i ] . y = 0 ;
2016-10-12 00:24:22 +00:00
}
}
2018-05-07 20:17:00 +00:00
void radix ( std : : vector < struct reader > & readers , int nreaders , FILE * geomfile , FILE * indexfile , const char * tmpdir , std : : atomic < long long > * geompos , int maxzoom , int basezoom , double droprate , double gamma ) {
2016-04-27 22:14:09 +00:00
// Run through the index and geometry for each reader,
// splitting the contents out by index into as many
// sub-files as we can write to simultaneously.
// Then sort each of those by index, recursively if it is
// too big to fit in memory.
// Then concatenate each of the sub-outputs into a final output.
long long mem ;
# ifdef __APPLE__
int64_t hw_memsize ;
size_t len = sizeof ( int64_t ) ;
if ( sysctlbyname ( " hw.memsize " , & hw_memsize , & len , NULL , 0 ) < 0 ) {
perror ( " sysctl hw.memsize " ) ;
exit ( EXIT_FAILURE ) ;
}
mem = hw_memsize ;
# else
long long pagesize = sysconf ( _SC_PAGESIZE ) ;
long long pages = sysconf ( _SC_PHYS_PAGES ) ;
if ( pages < 0 | | pagesize < 0 ) {
perror ( " sysconf _SC_PAGESIZE or _SC_PHYS_PAGES " ) ;
exit ( EXIT_FAILURE ) ;
}
mem = ( long long ) pages * pagesize ;
# endif
// Just for code coverage testing. Deeply recursive sorting is very slow
// compared to sorting in memory.
if ( additional [ A_PREFER_RADIX_SORT ] ) {
mem = 8192 ;
}
long long availfiles = MAX_FILES - 2 * nreaders // each reader has a geom and an index
- 4 // pool, meta, mbtiles, mbtiles journal
- 4 // top-level geom and index output, both FILE and fd
- 3 ; // stdin, stdout, stderr
// 4 because for each we have output and input FILE and fd for geom and index
int splits = availfiles / 4 ;
// Be somewhat conservative about memory availability because the whole point of this
// is to keep from thrashing by working on chunks that will fit in memory.
mem / = 2 ;
long long geom_total = 0 ;
int geomfds [ nreaders ] ;
int indexfds [ nreaders ] ;
2016-10-11 19:05:50 +00:00
for ( int i = 0 ; i < nreaders ; i + + ) {
2017-08-31 20:49:19 +00:00
geomfds [ i ] = readers [ i ] . geomfd ;
indexfds [ i ] = readers [ i ] . indexfd ;
2016-04-27 22:14:09 +00:00
struct stat geomst ;
2017-08-31 20:49:19 +00:00
if ( fstat ( readers [ i ] . geomfd , & geomst ) < 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " stat geom " ) ;
exit ( EXIT_FAILURE ) ;
}
geom_total + = geomst . st_size ;
}
2016-10-11 19:05:50 +00:00
struct drop_state ds [ maxzoom + 1 ] ;
2016-10-12 00:24:22 +00:00
prep_drop_states ( ds , maxzoom , basezoom , droprate ) ;
2016-10-11 19:05:50 +00:00
2016-04-27 22:14:09 +00:00
long long progress = 0 , progress_max = geom_total , progress_reported = - 1 ;
long long availfiles_before = availfiles ;
2016-10-11 19:05:50 +00:00
radix1 ( geomfds , indexfds , nreaders , 0 , splits , mem , tmpdir , & availfiles , geomfile , indexfile , geompos , & progress , & progress_max , & progress_reported , maxzoom , basezoom , droprate , gamma , ds ) ;
2016-04-27 22:14:09 +00:00
if ( availfiles - 2 * nreaders ! = availfiles_before ) {
fprintf ( stderr , " Internal error: miscounted available file descriptors: %lld vs %lld \n " , availfiles - 2 * nreaders , availfiles ) ;
exit ( EXIT_FAILURE ) ;
}
}
2017-11-07 23:20:17 +00:00
void choose_first_zoom ( long long * file_bbox , std : : vector < struct reader > & readers , unsigned * iz , unsigned * ix , unsigned * iy , int minzoom , int buffer ) {
2016-12-16 20:20:57 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
2017-08-31 20:49:19 +00:00
if ( readers [ i ] . file_bbox [ 0 ] < file_bbox [ 0 ] ) {
file_bbox [ 0 ] = readers [ i ] . file_bbox [ 0 ] ;
2016-12-16 20:20:57 +00:00
}
2017-08-31 20:49:19 +00:00
if ( readers [ i ] . file_bbox [ 1 ] < file_bbox [ 1 ] ) {
file_bbox [ 1 ] = readers [ i ] . file_bbox [ 1 ] ;
2016-12-16 20:20:57 +00:00
}
2017-08-31 20:49:19 +00:00
if ( readers [ i ] . file_bbox [ 2 ] > file_bbox [ 2 ] ) {
file_bbox [ 2 ] = readers [ i ] . file_bbox [ 2 ] ;
2016-12-16 20:20:57 +00:00
}
2017-08-31 20:49:19 +00:00
if ( readers [ i ] . file_bbox [ 3 ] > file_bbox [ 3 ] ) {
file_bbox [ 3 ] = readers [ i ] . file_bbox [ 3 ] ;
2016-12-16 20:20:57 +00:00
}
}
// If the bounding box extends off the plane on either side,
// a feature wrapped across the date line, so the width of the
// bounding box is the whole world.
if ( file_bbox [ 0 ] < 0 ) {
file_bbox [ 0 ] = 0 ;
file_bbox [ 2 ] = ( 1LL < < 32 ) - 1 ;
}
if ( file_bbox [ 2 ] > ( 1LL < < 32 ) - 1 ) {
file_bbox [ 0 ] = 0 ;
file_bbox [ 2 ] = ( 1LL < < 32 ) - 1 ;
}
if ( file_bbox [ 1 ] < 0 ) {
file_bbox [ 1 ] = 0 ;
}
if ( file_bbox [ 3 ] > ( 1LL < < 32 ) - 1 ) {
file_bbox [ 3 ] = ( 1LL < < 32 ) - 1 ;
}
for ( ssize_t z = minzoom ; z > = 0 ; z - - ) {
long long shift = 1LL < < ( 32 - z ) ;
long long left = ( file_bbox [ 0 ] - buffer * shift / 256 ) / shift ;
long long top = ( file_bbox [ 1 ] - buffer * shift / 256 ) / shift ;
long long right = ( file_bbox [ 2 ] + buffer * shift / 256 ) / shift ;
long long bottom = ( file_bbox [ 3 ] + buffer * shift / 256 ) / shift ;
if ( left = = right & & top = = bottom ) {
* iz = z ;
* ix = left ;
* iy = top ;
break ;
}
}
}
2019-04-05 00:01:02 +00:00
int read_input ( std : : vector < source > & sources , char * fname , int maxzoom , int minzoom , int basezoom , double basezoom_marker_width , sqlite3 * outdb , const char * outdir , std : : set < std : : string > * exclude , std : : set < std : : string > * include , int exclude_all , json_object * filter , double droprate , int buffer , const char * tmpdir , double gamma , int read_parallel , int forcetable , const char * attribution , bool uses_gamma , long long * file_bbox , const char * prefilter , const char * postfilter , const char * description , bool guess_maxzoom , std : : map < std : : string , int > const * attribute_types , const char * pgm , std : : map < std : : string , attribute_op > const * attribute_accum , std : : map < std : : string , std : : string > const & attribute_descriptions , std : : string const & commandline ) {
2016-04-27 22:14:09 +00:00
int ret = EXIT_SUCCESS ;
2017-11-07 23:20:17 +00:00
std : : vector < struct reader > readers ;
readers . resize ( CPUS ) ;
2016-10-15 00:11:57 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
2017-08-31 20:49:19 +00:00
struct reader * r = & readers [ i ] ;
2016-04-27 22:14:09 +00:00
char metaname [ strlen ( tmpdir ) + strlen ( " /meta.XXXXXXXX " ) + 1 ] ;
char poolname [ strlen ( tmpdir ) + strlen ( " /pool.XXXXXXXX " ) + 1 ] ;
char treename [ strlen ( tmpdir ) + strlen ( " /tree.XXXXXXXX " ) + 1 ] ;
char geomname [ strlen ( tmpdir ) + strlen ( " /geom.XXXXXXXX " ) + 1 ] ;
char indexname [ strlen ( tmpdir ) + strlen ( " /index.XXXXXXXX " ) + 1 ] ;
sprintf ( metaname , " %s%s " , tmpdir , " /meta.XXXXXXXX " ) ;
sprintf ( poolname , " %s%s " , tmpdir , " /pool.XXXXXXXX " ) ;
sprintf ( treename , " %s%s " , tmpdir , " /tree.XXXXXXXX " ) ;
sprintf ( geomname , " %s%s " , tmpdir , " /geom.XXXXXXXX " ) ;
sprintf ( indexname , " %s%s " , tmpdir , " /index.XXXXXXXX " ) ;
2016-12-07 18:57:56 +00:00
r - > metafd = mkstemp_cloexec ( metaname ) ;
2016-04-27 22:14:09 +00:00
if ( r - > metafd < 0 ) {
perror ( metaname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
r - > poolfd = mkstemp_cloexec ( poolname ) ;
2016-04-27 22:14:09 +00:00
if ( r - > poolfd < 0 ) {
perror ( poolname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
r - > treefd = mkstemp_cloexec ( treename ) ;
2016-04-27 22:14:09 +00:00
if ( r - > treefd < 0 ) {
perror ( treename ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
r - > geomfd = mkstemp_cloexec ( geomname ) ;
2016-04-27 22:14:09 +00:00
if ( r - > geomfd < 0 ) {
perror ( geomname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
r - > indexfd = mkstemp_cloexec ( indexname ) ;
2016-04-27 22:14:09 +00:00
if ( r - > indexfd < 0 ) {
perror ( indexname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
r - > metafile = fopen_oflag ( metaname , " wb " , O_WRONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( r - > metafile = = NULL ) {
perror ( metaname ) ;
exit ( EXIT_FAILURE ) ;
}
r - > poolfile = memfile_open ( r - > poolfd ) ;
if ( r - > poolfile = = NULL ) {
perror ( poolname ) ;
exit ( EXIT_FAILURE ) ;
}
r - > treefile = memfile_open ( r - > treefd ) ;
if ( r - > treefile = = NULL ) {
perror ( treename ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
r - > geomfile = fopen_oflag ( geomname , " wb " , O_WRONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( r - > geomfile = = NULL ) {
perror ( geomname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
r - > indexfile = fopen_oflag ( indexname , " wb " , O_WRONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( r - > indexfile = = NULL ) {
perror ( indexname ) ;
exit ( EXIT_FAILURE ) ;
}
r - > metapos = 0 ;
r - > geompos = 0 ;
r - > indexpos = 0 ;
unlink ( metaname ) ;
unlink ( poolname ) ;
unlink ( treename ) ;
unlink ( geomname ) ;
unlink ( indexname ) ;
// To distinguish a null value
{
struct stringpool p ;
memfile_write ( r - > treefile , & p , sizeof ( struct stringpool ) ) ;
}
// Keep metadata file from being completely empty if no attributes
serialize_int ( r - > metafile , 0 , & r - > metapos , " meta " ) ;
r - > file_bbox [ 0 ] = r - > file_bbox [ 1 ] = UINT_MAX ;
r - > file_bbox [ 2 ] = r - > file_bbox [ 3 ] = 0 ;
}
struct statfs fsstat ;
2017-08-31 20:49:19 +00:00
if ( fstatfs ( readers [ 0 ] . geomfd , & fsstat ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " fstatfs " ) ;
exit ( EXIT_FAILURE ) ;
}
diskfree = ( long long ) fsstat . f_bsize * fsstat . f_bavail ;
2018-03-13 22:21:21 +00:00
std : : atomic < long long > progress_seq ( 0 ) ;
2016-04-27 22:14:09 +00:00
2016-12-13 01:00:45 +00:00
// 2 * CPUS: One per reader thread, one per tiling thread
int initialized [ 2 * CPUS ] ;
unsigned initial_x [ 2 * CPUS ] , initial_y [ 2 * CPUS ] ;
for ( size_t i = 0 ; i < 2 * CPUS ; i + + ) {
2016-04-27 22:14:09 +00:00
initialized [ i ] = initial_x [ i ] = initial_y [ i ] = 0 ;
}
2017-02-21 00:16:06 +00:00
size_t nlayers = sources . size ( ) ;
2016-08-23 22:33:53 +00:00
for ( size_t l = 0 ; l < nlayers ; l + + ) {
2017-02-21 00:16:06 +00:00
if ( sources [ l ] . layer . size ( ) = = 0 ) {
2016-08-23 22:33:53 +00:00
const char * src ;
2017-02-21 00:16:06 +00:00
if ( sources [ l ] . file . size ( ) = = 0 ) {
2016-08-23 22:33:53 +00:00
src = fname ;
} else {
src = sources [ l ] . file . c_str ( ) ;
}
// Find the last component of the pathname
const char * ocp , * use = src ;
for ( ocp = src ; * ocp ; ocp + + ) {
if ( * ocp = = ' / ' & & ocp [ 1 ] ! = ' \0 ' ) {
use = ocp + 1 ;
}
}
std : : string trunc = std : : string ( use ) ;
2018-05-11 22:51:53 +00:00
std : : vector < std : : string > trim = {
" .json " ,
" .geojson " ,
" .geobuf " ,
" .mbtiles " ,
2018-08-01 23:16:50 +00:00
" .csv " ,
2018-05-11 22:51:53 +00:00
" .gz " ,
} ;
2016-08-23 22:33:53 +00:00
// Trim .json or .mbtiles from the name
2018-05-11 22:51:53 +00:00
bool again = true ;
while ( again ) {
again = false ;
for ( size_t i = 0 ; i < trim . size ( ) ; i + + ) {
if ( trunc . size ( ) > trim [ i ] . size ( ) & & trunc . substr ( trunc . size ( ) - trim [ i ] . size ( ) ) = = trim [ i ] ) {
trunc = trunc . substr ( 0 , trunc . size ( ) - trim [ i ] . size ( ) ) ;
again = true ;
}
2017-11-30 00:44:55 +00:00
}
2016-08-23 22:33:53 +00:00
}
// Trim out characters that can't be part of selector
2018-03-02 18:35:58 +00:00
std : : string out ;
2018-02-07 21:15:36 +00:00
for ( size_t p = 0 ; p < trunc . size ( ) ; p + + ) {
if ( isalpha ( trunc [ p ] ) | | isdigit ( trunc [ p ] ) | | trunc [ p ] = = ' _ ' | | ( trunc [ p ] & 0x80 ) ! = 0 ) {
out . append ( trunc , p , 1 ) ;
2016-08-23 22:33:53 +00:00
}
}
2018-02-07 01:22:26 +00:00
2018-02-07 21:15:36 +00:00
sources [ l ] . layer = out ;
if ( sources [ l ] . layer . size ( ) = = 0 | | check_utf8 ( out ) . size ( ) ! = 0 ) {
sources [ l ] . layer = " unknown " + std : : to_string ( l ) ;
2018-02-07 01:22:26 +00:00
}
2016-08-23 22:33:53 +00:00
if ( ! quiet ) {
2018-02-07 01:22:26 +00:00
fprintf ( stderr , " For layer %d, using name \" %s \" \n " , ( int ) l , sources [ l ] . layer . c_str ( ) ) ;
2016-08-23 22:33:53 +00:00
}
}
}
2016-08-29 21:59:28 +00:00
std : : map < std : : string , layermap_entry > layermap ;
2016-08-23 22:33:53 +00:00
for ( size_t l = 0 ; l < nlayers ; l + + ) {
2017-07-18 00:31:46 +00:00
layermap_entry e = layermap_entry ( l ) ;
2018-05-24 17:27:43 +00:00
e . description = sources [ l ] . description ;
2017-07-18 00:31:46 +00:00
layermap . insert ( std : : pair < std : : string , layermap_entry > ( sources [ l ] . layer , e ) ) ;
2016-08-23 22:33:53 +00:00
}
2017-07-18 00:31:46 +00:00
2016-08-29 21:59:28 +00:00
std : : vector < std : : map < std : : string , layermap_entry > > layermaps ;
2016-08-23 22:33:53 +00:00
for ( size_t l = 0 ; l < CPUS ; l + + ) {
layermaps . push_back ( layermap ) ;
}
2016-04-27 22:14:09 +00:00
long overall_offset = 0 ;
2017-05-30 20:28:25 +00:00
double dist_sum = 0 ;
size_t dist_count = 0 ;
2016-04-27 22:14:09 +00:00
2018-02-06 20:02:23 +00:00
int files_open_before_reading = open ( " /dev/null " , O_RDONLY | O_CLOEXEC ) ;
if ( files_open_before_reading < 0 ) {
perror ( " open /dev/null " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( close ( files_open_before_reading ) ! = 0 ) {
perror ( " close " ) ;
exit ( EXIT_FAILURE ) ;
}
2017-02-21 00:16:06 +00:00
size_t nsources = sources . size ( ) ;
2016-05-03 23:39:26 +00:00
for ( size_t source = 0 ; source < nsources ; source + + ) {
2016-04-28 19:14:19 +00:00
std : : string reading ;
2016-04-27 22:14:09 +00:00
int fd ;
2017-02-21 00:16:06 +00:00
if ( sources [ source ] . file . size ( ) = = 0 ) {
2016-04-27 22:14:09 +00:00
reading = " standard input " ;
fd = 0 ;
} else {
2016-04-28 19:14:19 +00:00
reading = sources [ source ] . file ;
2016-12-07 18:57:56 +00:00
fd = open ( sources [ source ] . file . c_str ( ) , O_RDONLY , O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( fd < 0 ) {
2016-04-28 19:14:19 +00:00
perror ( sources [ source ] . file . c_str ( ) ) ;
2016-04-27 22:14:09 +00:00
continue ;
}
}
2017-02-21 00:16:06 +00:00
auto a = layermap . find ( sources [ source ] . layer ) ;
if ( a = = layermap . end ( ) ) {
fprintf ( stderr , " Internal error: couldn't find layer %s " , sources [ source ] . layer . c_str ( ) ) ;
exit ( EXIT_FAILURE ) ;
}
size_t layer = a - > second . id ;
2018-07-17 21:57:56 +00:00
if ( sources [ source ] . format = = " geobuf " | | ( sources [ source ] . file . size ( ) > 7 & & sources [ source ] . file . substr ( sources [ source ] . file . size ( ) - 7 ) = = std : : string ( " .geobuf " ) ) ) {
2017-08-28 18:30:30 +00:00
struct stat st ;
if ( fstat ( fd , & st ) ! = 0 ) {
perror ( " fstat " ) ;
perror ( sources [ source ] . file . c_str ( ) ) ;
exit ( EXIT_FAILURE ) ;
2017-08-25 21:23:06 +00:00
}
2017-08-28 18:30:30 +00:00
char * map = ( char * ) mmap ( NULL , st . st_size , PROT_READ , MAP_PRIVATE , fd , 0 ) ;
if ( map = = MAP_FAILED ) {
2018-07-17 21:57:56 +00:00
fprintf ( stderr , " %s: mmap: %s: %s \n " , * av , reading . c_str ( ) , strerror ( errno ) ) ;
2017-08-25 21:23:06 +00:00
exit ( EXIT_FAILURE ) ;
}
2018-03-13 22:21:21 +00:00
std : : atomic < long long > layer_seq [ CPUS ] ;
2017-08-31 20:49:19 +00:00
double dist_sums [ CPUS ] ;
size_t dist_counts [ CPUS ] ;
2017-11-07 23:20:17 +00:00
std : : vector < struct serialization_state > sst ;
sst . resize ( CPUS ) ;
2017-08-31 20:49:19 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
layer_seq [ i ] = overall_offset ;
dist_sums [ i ] = 0 ;
dist_counts [ i ] = 0 ;
sst [ i ] . fname = reading . c_str ( ) ;
sst [ i ] . line = 0 ;
sst [ i ] . layer_seq = & layer_seq [ i ] ;
sst [ i ] . progress_seq = & progress_seq ;
2017-11-07 23:20:17 +00:00
sst [ i ] . readers = & readers ;
2017-08-31 20:49:19 +00:00
sst [ i ] . segment = i ;
sst [ i ] . initial_x = & initial_x [ i ] ;
sst [ i ] . initial_y = & initial_y [ i ] ;
sst [ i ] . initialized = & initialized [ i ] ;
sst [ i ] . dist_sum = & dist_sums [ i ] ;
sst [ i ] . dist_count = & dist_counts [ i ] ;
sst [ i ] . want_dist = guess_maxzoom ;
sst [ i ] . maxzoom = maxzoom ;
sst [ i ] . filters = prefilter ! = NULL | | postfilter ! = NULL ;
sst [ i ] . uses_gamma = uses_gamma ;
sst [ i ] . layermap = & layermaps [ i ] ;
sst [ i ] . exclude = exclude ;
sst [ i ] . include = include ;
sst [ i ] . exclude_all = exclude_all ;
sst [ i ] . basezoom = basezoom ;
sst [ i ] . attribute_types = attribute_types ;
}
2017-11-07 23:20:17 +00:00
parse_geobuf ( & sst , map , st . st_size , layer , sources [ layer ] . layer ) ;
2017-08-31 20:49:19 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
dist_sum + = dist_sums [ i ] ;
dist_count + = dist_counts [ i ] ;
}
2017-08-28 18:30:30 +00:00
if ( munmap ( map , st . st_size ) ! = 0 ) {
perror ( " munmap source file " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( close ( fd ) ! = 0 ) {
perror ( " close " ) ;
exit ( EXIT_FAILURE ) ;
}
2017-08-25 21:23:06 +00:00
2017-08-31 20:49:19 +00:00
overall_offset = layer_seq [ 0 ] ;
2017-11-07 23:20:17 +00:00
checkdisk ( & readers ) ;
2017-08-25 21:23:06 +00:00
continue ;
}
2018-07-17 21:57:56 +00:00
if ( sources [ source ] . format = = " csv " | | ( sources [ source ] . file . size ( ) > 4 & & sources [ source ] . file . substr ( sources [ source ] . file . size ( ) - 4 ) = = std : : string ( " .csv " ) ) ) {
2018-03-13 22:21:21 +00:00
std : : atomic < long long > layer_seq [ CPUS ] ;
2017-12-06 01:18:19 +00:00
double dist_sums [ CPUS ] ;
size_t dist_counts [ CPUS ] ;
std : : vector < struct serialization_state > sst ;
sst . resize ( CPUS ) ;
// XXX factor out this duplicated setup
for ( size_t i = 0 ; i < CPUS ; i + + ) {
layer_seq [ i ] = overall_offset ;
dist_sums [ i ] = 0 ;
dist_counts [ i ] = 0 ;
sst [ i ] . fname = reading . c_str ( ) ;
sst [ i ] . line = 0 ;
sst [ i ] . layer_seq = & layer_seq [ i ] ;
sst [ i ] . progress_seq = & progress_seq ;
sst [ i ] . readers = & readers ;
sst [ i ] . segment = i ;
sst [ i ] . initial_x = & initial_x [ i ] ;
sst [ i ] . initial_y = & initial_y [ i ] ;
sst [ i ] . initialized = & initialized [ i ] ;
sst [ i ] . dist_sum = & dist_sums [ i ] ;
sst [ i ] . dist_count = & dist_counts [ i ] ;
sst [ i ] . want_dist = guess_maxzoom ;
sst [ i ] . maxzoom = maxzoom ;
sst [ i ] . filters = prefilter ! = NULL | | postfilter ! = NULL ;
sst [ i ] . uses_gamma = uses_gamma ;
sst [ i ] . layermap = & layermaps [ i ] ;
sst [ i ] . exclude = exclude ;
sst [ i ] . include = include ;
sst [ i ] . exclude_all = exclude_all ;
sst [ i ] . basezoom = basezoom ;
sst [ i ] . attribute_types = attribute_types ;
}
parse_geocsv ( sst , sources [ source ] . file , layer , sources [ layer ] . layer ) ;
2018-02-07 00:23:44 +00:00
if ( close ( fd ) ! = 0 ) {
perror ( " close " ) ;
exit ( EXIT_FAILURE ) ;
}
2017-12-06 01:18:19 +00:00
overall_offset = layer_seq [ 0 ] ;
checkdisk ( & readers ) ;
continue ;
}
2016-04-27 22:14:09 +00:00
struct stat st ;
char * map = NULL ;
off_t off = 0 ;
2017-05-01 22:26:53 +00:00
int read_parallel_this = read_parallel ? ' \n ' : 0 ;
2017-04-28 17:41:20 +00:00
2018-05-11 21:36:46 +00:00
if ( ! ( sources [ source ] . file . size ( ) > 3 & & sources [ source ] . file . substr ( sources [ source ] . file . size ( ) - 3 ) = = std : : string ( " .gz " ) ) ) {
2016-04-27 22:14:09 +00:00
if ( fstat ( fd , & st ) = = 0 ) {
off = lseek ( fd , 0 , SEEK_CUR ) ;
if ( off > = 0 ) {
map = ( char * ) mmap ( NULL , st . st_size - off , PROT_READ , MAP_PRIVATE , fd , off ) ;
// No error if MAP_FAILED because check is below
if ( map ! = MAP_FAILED ) {
madvise ( map , st . st_size - off , MADV_RANDOM ) ; // sequential, but from several pointers at once
}
}
}
}
2017-04-28 17:41:20 +00:00
if ( map ! = NULL & & map ! = MAP_FAILED & & st . st_size - off > 0 ) {
if ( map [ 0 ] = = 0x1E ) {
read_parallel_this = 0x1E ;
}
if ( ! read_parallel_this ) {
// Not a GeoJSON text sequence, so unmap and read serially
if ( munmap ( map , st . st_size - off ) ! = 0 ) {
perror ( " munmap source file " ) ;
exit ( EXIT_FAILURE ) ;
}
map = NULL ;
}
}
2017-05-01 22:26:53 +00:00
if ( map ! = NULL & & map ! = MAP_FAILED & & read_parallel_this ) {
2019-01-22 22:26:06 +00:00
do_read_parallel ( map , st . st_size - off , overall_offset , reading . c_str ( ) , & readers , & progress_seq , exclude , include , exclude_all , basezoom , layer , & layermaps , initialized , initial_x , initial_y , maxzoom , sources [ layer ] . layer , uses_gamma , attribute_types , read_parallel_this , & dist_sum , & dist_count , guess_maxzoom , prefilter ! = NULL | | postfilter ! = NULL ) ;
2016-04-27 22:14:09 +00:00
overall_offset + = st . st_size - off ;
2017-11-07 23:20:17 +00:00
checkdisk ( & readers ) ;
2016-04-27 22:14:09 +00:00
if ( munmap ( map , st . st_size - off ) ! = 0 ) {
perror ( " munmap source file " ) ;
2017-04-28 17:41:20 +00:00
exit ( EXIT_FAILURE ) ;
2016-04-27 22:14:09 +00:00
}
2018-02-06 20:02:23 +00:00
if ( close ( fd ) ! = 0 ) {
perror ( " close input file " ) ;
exit ( EXIT_FAILURE ) ;
}
2016-04-27 22:14:09 +00:00
} else {
2018-05-11 21:58:29 +00:00
STREAM * fp = streamfdopen ( fd , " r " , sources [ layer ] . file ) ;
2016-04-27 22:14:09 +00:00
if ( fp = = NULL ) {
2017-02-21 00:16:06 +00:00
perror ( sources [ layer ] . file . c_str ( ) ) ;
2016-04-27 22:14:09 +00:00
if ( close ( fd ) ! = 0 ) {
perror ( " close source file " ) ;
exit ( EXIT_FAILURE ) ;
}
continue ;
}
2018-05-11 21:36:46 +00:00
int c = fp - > peekc ( ) ;
2017-04-28 17:41:20 +00:00
if ( c = = 0x1E ) {
read_parallel_this = 0x1E ;
}
if ( read_parallel_this ) {
2016-04-27 22:14:09 +00:00
// Serial reading of chunks that are then parsed in parallel
char readname [ strlen ( tmpdir ) + strlen ( " /read.XXXXXXXX " ) + 1 ] ;
sprintf ( readname , " %s%s " , tmpdir , " /read.XXXXXXXX " ) ;
2016-12-07 18:57:56 +00:00
int readfd = mkstemp_cloexec ( readname ) ;
2016-04-27 22:14:09 +00:00
if ( readfd < 0 ) {
perror ( readname ) ;
exit ( EXIT_FAILURE ) ;
}
FILE * readfp = fdopen ( readfd , " w " ) ;
if ( readfp = = NULL ) {
perror ( readname ) ;
exit ( EXIT_FAILURE ) ;
}
unlink ( readname ) ;
2018-03-13 22:21:21 +00:00
std : : atomic < int > is_parsing ( 0 ) ;
2016-04-27 22:14:09 +00:00
long long ahead = 0 ;
long long initial_offset = overall_offset ;
pthread_t parallel_parser ;
2016-07-13 20:00:38 +00:00
bool parser_created = false ;
2016-04-27 22:14:09 +00:00
# define READ_BUF 2000
# define PARSE_MIN 10000000
# define PARSE_MAX (1LL * 1024 * 1024 * 1024)
char buf [ READ_BUF ] ;
int n ;
2018-05-11 23:02:53 +00:00
while ( ( n = fp - > read ( buf , READ_BUF ) ) > 0 ) {
2016-04-28 19:14:19 +00:00
fwrite_check ( buf , sizeof ( char ) , n , readfp , reading . c_str ( ) ) ;
2016-04-27 22:14:09 +00:00
ahead + = n ;
2017-04-28 17:41:20 +00:00
if ( buf [ n - 1 ] = = read_parallel_this & & ahead > PARSE_MIN ) {
2016-04-27 22:14:09 +00:00
// Don't let the streaming reader get too far ahead of the parsers.
// If the buffered input gets huge, even if the parsers are still running,
// wait for the parser thread instead of continuing to stream input.
if ( is_parsing = = 0 | | ahead > = PARSE_MAX ) {
2016-07-13 20:00:38 +00:00
if ( parser_created ) {
2016-04-27 22:14:09 +00:00
if ( pthread_join ( parallel_parser , NULL ) ! = 0 ) {
2016-07-13 20:00:38 +00:00
perror ( " pthread_join 1088 " ) ;
2016-04-27 22:14:09 +00:00
exit ( EXIT_FAILURE ) ;
}
2016-07-13 20:00:38 +00:00
parser_created = false ;
2016-04-27 22:14:09 +00:00
}
fflush ( readfp ) ;
2019-01-22 22:26:06 +00:00
start_parsing ( readfd , streamfpopen ( readfp ) , initial_offset , ahead , & is_parsing , & parallel_parser , parser_created , reading . c_str ( ) , & readers , & progress_seq , exclude , include , exclude_all , basezoom , layer , layermaps , initialized , initial_x , initial_y , maxzoom , sources [ layer ] . layer , gamma ! = 0 , attribute_types , read_parallel_this , & dist_sum , & dist_count , guess_maxzoom , prefilter ! = NULL | | postfilter ! = NULL ) ;
2016-04-27 22:14:09 +00:00
initial_offset + = ahead ;
overall_offset + = ahead ;
2017-11-07 23:20:17 +00:00
checkdisk ( & readers ) ;
2016-04-27 22:14:09 +00:00
ahead = 0 ;
sprintf ( readname , " %s%s " , tmpdir , " /read.XXXXXXXX " ) ;
2016-12-07 18:57:56 +00:00
readfd = mkstemp_cloexec ( readname ) ;
2016-04-27 22:14:09 +00:00
if ( readfd < 0 ) {
perror ( readname ) ;
exit ( EXIT_FAILURE ) ;
}
readfp = fdopen ( readfd , " w " ) ;
if ( readfp = = NULL ) {
perror ( readname ) ;
exit ( EXIT_FAILURE ) ;
}
unlink ( readname ) ;
}
}
}
if ( n < 0 ) {
2016-04-28 19:14:19 +00:00
perror ( reading . c_str ( ) ) ;
2016-04-27 22:14:09 +00:00
}
2016-07-13 20:00:38 +00:00
if ( parser_created ) {
2016-04-27 22:14:09 +00:00
if ( pthread_join ( parallel_parser , NULL ) ! = 0 ) {
2016-07-13 20:00:38 +00:00
perror ( " pthread_join 1122 " ) ;
2016-04-27 22:14:09 +00:00
exit ( EXIT_FAILURE ) ;
}
2016-07-13 20:00:38 +00:00
parser_created = false ;
2016-04-27 22:14:09 +00:00
}
fflush ( readfp ) ;
if ( ahead > 0 ) {
2019-01-22 22:26:06 +00:00
start_parsing ( readfd , streamfpopen ( readfp ) , initial_offset , ahead , & is_parsing , & parallel_parser , parser_created , reading . c_str ( ) , & readers , & progress_seq , exclude , include , exclude_all , basezoom , layer , layermaps , initialized , initial_x , initial_y , maxzoom , sources [ layer ] . layer , gamma ! = 0 , attribute_types , read_parallel_this , & dist_sum , & dist_count , guess_maxzoom , prefilter ! = NULL | | postfilter ! = NULL ) ;
2016-04-27 22:14:09 +00:00
2016-07-13 20:00:38 +00:00
if ( parser_created ) {
if ( pthread_join ( parallel_parser , NULL ) ! = 0 ) {
perror ( " pthread_join 1133 " ) ;
}
parser_created = false ;
2016-04-27 22:14:09 +00:00
}
overall_offset + = ahead ;
2017-11-07 23:20:17 +00:00
checkdisk ( & readers ) ;
2016-04-27 22:14:09 +00:00
}
2017-08-23 18:43:48 +00:00
} else {
2016-04-27 22:14:09 +00:00
// Plain serial reading
2018-03-13 22:21:21 +00:00
std : : atomic < long long > layer_seq ( overall_offset ) ;
2018-05-11 21:36:46 +00:00
json_pull * jp = fp - > json_begin ( ) ;
2017-08-23 18:43:48 +00:00
struct serialization_state sst ;
sst . fname = reading . c_str ( ) ;
sst . line = 0 ;
sst . layer_seq = & layer_seq ;
sst . progress_seq = & progress_seq ;
2017-11-07 23:20:17 +00:00
sst . readers = & readers ;
2017-08-23 18:43:48 +00:00
sst . segment = 0 ;
sst . initial_x = & initial_x [ 0 ] ;
sst . initial_y = & initial_y [ 0 ] ;
sst . initialized = & initialized [ 0 ] ;
sst . dist_sum = & dist_sum ;
sst . dist_count = & dist_count ;
2017-08-24 22:46:35 +00:00
sst . want_dist = guess_maxzoom ;
sst . maxzoom = maxzoom ;
sst . filters = prefilter ! = NULL | | postfilter ! = NULL ;
sst . uses_gamma = uses_gamma ;
2017-08-24 23:30:01 +00:00
sst . layermap = & layermaps [ 0 ] ;
2017-08-25 00:10:15 +00:00
sst . exclude = exclude ;
sst . include = include ;
sst . exclude_all = exclude_all ;
sst . basezoom = basezoom ;
2017-08-25 00:27:30 +00:00
sst . attribute_types = attribute_types ;
2017-08-23 18:43:48 +00:00
2017-08-25 00:27:30 +00:00
parse_json ( & sst , jp , layer , sources [ layer ] . layer ) ;
2016-04-27 22:14:09 +00:00
json_end ( jp ) ;
overall_offset = layer_seq ;
2017-11-07 23:20:17 +00:00
checkdisk ( & readers ) ;
2016-04-27 22:14:09 +00:00
}
2018-05-11 21:36:46 +00:00
if ( fp - > fclose ( ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " fclose input " ) ;
exit ( EXIT_FAILURE ) ;
}
}
}
2018-02-06 20:02:23 +00:00
int files_open_after_reading = open ( " /dev/null " , O_RDONLY | O_CLOEXEC ) ;
if ( files_open_after_reading < 0 ) {
perror ( " open /dev/null " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( close ( files_open_after_reading ) ! = 0 ) {
perror ( " close " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( files_open_after_reading > files_open_before_reading ) {
fprintf ( stderr , " Internal error: Files left open after reading input. (%d vs %d) \n " ,
files_open_before_reading , files_open_after_reading ) ;
ret = EXIT_FAILURE ;
}
2016-04-27 22:14:09 +00:00
if ( ! quiet ) {
fprintf ( stderr , " \r " ) ;
// (stderr, "Read 10000.00 million features\r", *progress_seq / 1000000.0);
}
2016-10-15 00:11:57 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
2017-08-31 20:49:19 +00:00
if ( fclose ( readers [ i ] . metafile ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " fclose meta " ) ;
exit ( EXIT_FAILURE ) ;
}
2017-08-31 20:49:19 +00:00
if ( fclose ( readers [ i ] . geomfile ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " fclose geom " ) ;
exit ( EXIT_FAILURE ) ;
}
2017-08-31 20:49:19 +00:00
if ( fclose ( readers [ i ] . indexfile ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " fclose index " ) ;
exit ( EXIT_FAILURE ) ;
}
2017-08-31 20:49:19 +00:00
memfile_close ( readers [ i ] . treefile ) ;
2016-04-27 22:14:09 +00:00
2017-08-31 20:49:19 +00:00
if ( fstat ( readers [ i ] . geomfd , & readers [ i ] . geomst ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " stat geom \n " ) ;
exit ( EXIT_FAILURE ) ;
}
2017-08-31 20:49:19 +00:00
if ( fstat ( readers [ i ] . metafd , & readers [ i ] . metast ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " stat meta \n " ) ;
exit ( EXIT_FAILURE ) ;
}
}
// Create a combined string pool and a combined metadata file
// but keep track of the offsets into it since we still need
// segment+offset to find the data.
2016-12-13 01:00:45 +00:00
// 2 * CPUS: One per input thread, one per tiling thread
long long pool_off [ 2 * CPUS ] ;
long long meta_off [ 2 * CPUS ] ;
for ( size_t i = 0 ; i < 2 * CPUS ; i + + ) {
pool_off [ i ] = meta_off [ i ] = 0 ;
}
2016-04-27 22:14:09 +00:00
char poolname [ strlen ( tmpdir ) + strlen ( " /pool.XXXXXXXX " ) + 1 ] ;
sprintf ( poolname , " %s%s " , tmpdir , " /pool.XXXXXXXX " ) ;
2016-12-07 18:57:56 +00:00
int poolfd = mkstemp_cloexec ( poolname ) ;
2016-04-27 22:14:09 +00:00
if ( poolfd < 0 ) {
perror ( poolname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
FILE * poolfile = fopen_oflag ( poolname , " wb " , O_WRONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( poolfile = = NULL ) {
perror ( poolname ) ;
exit ( EXIT_FAILURE ) ;
}
unlink ( poolname ) ;
char metaname [ strlen ( tmpdir ) + strlen ( " /meta.XXXXXXXX " ) + 1 ] ;
sprintf ( metaname , " %s%s " , tmpdir , " /meta.XXXXXXXX " ) ;
2016-12-07 18:57:56 +00:00
int metafd = mkstemp_cloexec ( metaname ) ;
2016-04-27 22:14:09 +00:00
if ( metafd < 0 ) {
perror ( metaname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
FILE * metafile = fopen_oflag ( metaname , " wb " , O_WRONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( metafile = = NULL ) {
perror ( metaname ) ;
exit ( EXIT_FAILURE ) ;
}
unlink ( metaname ) ;
2018-05-07 20:17:00 +00:00
std : : atomic < long long > metapos ( 0 ) ;
std : : atomic < long long > poolpos ( 0 ) ;
2016-04-27 22:14:09 +00:00
2016-10-15 00:11:57 +00:00
for ( size_t i = 0 ; i < CPUS ; i + + ) {
2017-08-31 20:49:19 +00:00
if ( readers [ i ] . metapos > 0 ) {
void * map = mmap ( NULL , readers [ i ] . metapos , PROT_READ , MAP_PRIVATE , readers [ i ] . metafd , 0 ) ;
2016-04-27 22:14:09 +00:00
if ( map = = MAP_FAILED ) {
perror ( " mmap unmerged meta " ) ;
exit ( EXIT_FAILURE ) ;
}
2017-08-31 20:49:19 +00:00
madvise ( map , readers [ i ] . metapos , MADV_SEQUENTIAL ) ;
madvise ( map , readers [ i ] . metapos , MADV_WILLNEED ) ;
if ( fwrite ( map , readers [ i ] . metapos , 1 , metafile ) ! = 1 ) {
2016-04-27 22:14:09 +00:00
perror ( " Reunify meta " ) ;
exit ( EXIT_FAILURE ) ;
}
2017-08-31 20:49:19 +00:00
madvise ( map , readers [ i ] . metapos , MADV_DONTNEED ) ;
if ( munmap ( map , readers [ i ] . metapos ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " unmap unmerged meta " ) ;
}
}
meta_off [ i ] = metapos ;
2017-08-31 20:49:19 +00:00
metapos + = readers [ i ] . metapos ;
if ( close ( readers [ i ] . metafd ) ! = 0 ) {
2016-04-27 22:14:09 +00:00
perror ( " close unmerged meta " ) ;
}
2017-08-31 20:49:19 +00:00
if ( readers [ i ] . poolfile - > off > 0 ) {
if ( fwrite ( readers [ i ] . poolfile - > map , readers [ i ] . poolfile - > off , 1 , poolfile ) ! = 1 ) {
2016-04-27 22:14:09 +00:00
perror ( " Reunify string pool " ) ;
exit ( EXIT_FAILURE ) ;
}
}
pool_off [ i ] = poolpos ;
2017-08-31 20:49:19 +00:00
poolpos + = readers [ i ] . poolfile - > off ;
memfile_close ( readers [ i ] . poolfile ) ;
2016-04-27 22:14:09 +00:00
}
if ( fclose ( poolfile ) ! = 0 ) {
perror ( " fclose pool " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( fclose ( metafile ) ! = 0 ) {
perror ( " fclose meta " ) ;
exit ( EXIT_FAILURE ) ;
}
char * meta = ( char * ) mmap ( NULL , metapos , PROT_READ , MAP_PRIVATE , metafd , 0 ) ;
if ( meta = = MAP_FAILED ) {
perror ( " mmap meta " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( meta , metapos , MADV_RANDOM ) ;
char * stringpool = NULL ;
if ( poolpos > 0 ) { // Will be 0 if -X was specified
stringpool = ( char * ) mmap ( NULL , poolpos , PROT_READ , MAP_PRIVATE , poolfd , 0 ) ;
if ( stringpool = = MAP_FAILED ) {
perror ( " mmap string pool " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( stringpool , poolpos , MADV_RANDOM ) ;
}
char indexname [ strlen ( tmpdir ) + strlen ( " /index.XXXXXXXX " ) + 1 ] ;
sprintf ( indexname , " %s%s " , tmpdir , " /index.XXXXXXXX " ) ;
2016-12-07 18:57:56 +00:00
int indexfd = mkstemp_cloexec ( indexname ) ;
2016-04-27 22:14:09 +00:00
if ( indexfd < 0 ) {
perror ( indexname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
FILE * indexfile = fopen_oflag ( indexname , " wb " , O_WRONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( indexfile = = NULL ) {
perror ( indexname ) ;
exit ( EXIT_FAILURE ) ;
}
unlink ( indexname ) ;
char geomname [ strlen ( tmpdir ) + strlen ( " /geom.XXXXXXXX " ) + 1 ] ;
sprintf ( geomname , " %s%s " , tmpdir , " /geom.XXXXXXXX " ) ;
2016-12-07 18:57:56 +00:00
int geomfd = mkstemp_cloexec ( geomname ) ;
2016-04-27 22:14:09 +00:00
if ( geomfd < 0 ) {
perror ( geomname ) ;
exit ( EXIT_FAILURE ) ;
}
2016-12-07 18:57:56 +00:00
FILE * geomfile = fopen_oflag ( geomname , " wb " , O_WRONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
if ( geomfile = = NULL ) {
perror ( geomname ) ;
exit ( EXIT_FAILURE ) ;
}
unlink ( geomname ) ;
2016-12-16 20:20:57 +00:00
unsigned iz = 0 , ix = 0 , iy = 0 ;
2017-08-31 20:49:19 +00:00
choose_first_zoom ( file_bbox , readers , & iz , & ix , & iy , minzoom , buffer ) ;
2016-12-16 20:20:57 +00:00
2018-01-26 20:41:30 +00:00
if ( justx > = 0 ) {
iz = minzoom ;
ix = justx ;
iy = justy ;
}
2018-05-07 20:17:00 +00:00
std : : atomic < long long > geompos ( 0 ) ;
2016-04-27 22:14:09 +00:00
/* initial tile is 0/0/0 */
2016-12-16 20:20:57 +00:00
serialize_int ( geomfile , iz , & geompos , fname ) ;
serialize_uint ( geomfile , ix , & geompos , fname ) ;
serialize_uint ( geomfile , iy , & geompos , fname ) ;
2016-04-27 22:14:09 +00:00
2017-11-07 19:06:30 +00:00
radix ( readers , CPUS , geomfile , indexfile , tmpdir , & geompos , maxzoom , basezoom , droprate , gamma ) ;
2016-04-27 22:14:09 +00:00
/* end of tile */
serialize_byte ( geomfile , - 2 , & geompos , fname ) ;
if ( fclose ( geomfile ) ! = 0 ) {
perror ( " fclose geom " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( fclose ( indexfile ) ! = 0 ) {
perror ( " fclose index " ) ;
exit ( EXIT_FAILURE ) ;
}
struct stat indexst ;
if ( fstat ( indexfd , & indexst ) < 0 ) {
perror ( " stat index " ) ;
exit ( EXIT_FAILURE ) ;
}
2018-05-07 20:17:00 +00:00
std : : atomic < long long > indexpos ( indexst . st_size ) ;
2016-04-27 22:14:09 +00:00
progress_seq = indexpos / sizeof ( struct index ) ;
2018-03-13 21:51:41 +00:00
last_progress = 0 ;
2016-04-27 22:14:09 +00:00
if ( ! quiet ) {
2018-03-13 22:21:21 +00:00
long long s = progress_seq ;
2018-05-07 20:17:00 +00:00
long long geompos_print = geompos ;
long long metapos_print = metapos ;
long long poolpos_print = poolpos ;
fprintf ( stderr , " %lld features, %lld bytes of geometry, %lld bytes of separate metadata, %lld bytes of string pool \n " , s , geompos_print , metapos_print , poolpos_print ) ;
2016-04-27 22:14:09 +00:00
}
if ( indexpos = = 0 ) {
fprintf ( stderr , " Did not read any valid geometries \n " ) ;
2017-07-18 16:54:59 +00:00
if ( outdb ! = NULL ) {
mbtiles_close ( outdb , pgm ) ;
}
2016-04-27 22:14:09 +00:00
exit ( EXIT_FAILURE ) ;
}
2017-04-13 21:07:11 +00:00
struct index * map = ( struct index * ) mmap ( NULL , indexpos , PROT_READ , MAP_PRIVATE , indexfd , 0 ) ;
if ( map = = MAP_FAILED ) {
perror ( " mmap index for basezoom " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( map , indexpos , MADV_SEQUENTIAL ) ;
madvise ( map , indexpos , MADV_WILLNEED ) ;
long long indices = indexpos / sizeof ( struct index ) ;
bool fix_dropping = false ;
if ( guess_maxzoom ) {
double sum = 0 ;
size_t count = 0 ;
long long progress = - 1 ;
long long ip ;
2017-04-13 21:23:43 +00:00
for ( ip = 1 ; ip < indices ; ip + + ) {
2017-11-07 23:20:17 +00:00
if ( map [ ip ] . ix ! = map [ ip - 1 ] . ix ) {
2017-04-13 21:07:11 +00:00
count + + ;
2017-11-07 23:20:17 +00:00
sum + = log ( map [ ip ] . ix - map [ ip - 1 ] . ix ) ;
2017-04-13 21:07:11 +00:00
}
long long nprogress = 100 * ip / indices ;
if ( nprogress ! = progress ) {
progress = nprogress ;
2018-03-13 21:51:41 +00:00
if ( ! quiet & & ! quiet_progress & & progress_time ( ) ) {
2017-04-13 21:07:11 +00:00
fprintf ( stderr , " Maxzoom: %lld%% \r " , progress ) ;
}
}
}
2017-05-30 20:28:25 +00:00
if ( count = = 0 & & dist_count = = 0 ) {
fprintf ( stderr , " Can't guess maxzoom (-zg) without at least two distinct feature locations \n " ) ;
2017-07-18 16:54:59 +00:00
if ( outdb ! = NULL ) {
mbtiles_close ( outdb , pgm ) ;
}
2017-05-30 20:28:25 +00:00
exit ( EXIT_FAILURE ) ;
}
2017-04-13 21:07:11 +00:00
if ( count > 0 ) {
// Geometric mean is appropriate because distances between features
// are typically lognormally distributed
double avg = exp ( sum / count ) ;
// Convert approximately from tile units to feet
double dist_ft = sqrt ( avg ) / 33 ;
2017-05-30 20:28:25 +00:00
// Factor of 8 (3 zooms) beyond minimum required to distinguish features
double want = dist_ft / 8 ;
2017-04-13 21:07:11 +00:00
maxzoom = ceil ( log ( 360 / ( .00000274 * want ) ) / log ( 2 ) - full_detail ) ;
if ( maxzoom < 0 ) {
maxzoom = 0 ;
}
2018-07-25 18:10:56 +00:00
if ( maxzoom > 32 - full_detail ) {
maxzoom = 32 - full_detail ;
}
if ( maxzoom > 33 - low_detail ) { // that is, maxzoom - 1 > 32 - low_detail
maxzoom = 33 - low_detail ;
2017-04-13 21:07:11 +00:00
}
if ( ! quiet ) {
2018-02-06 20:15:24 +00:00
fprintf ( stderr , " Choosing a maxzoom of -z%d for features about %d feet (%d meters) apart \n " , maxzoom , ( int ) ceil ( dist_ft ) , ( int ) ceil ( dist_ft / 3.28084 ) ) ;
2017-04-13 21:07:11 +00:00
}
2018-08-01 20:14:14 +00:00
bool changed = false ;
while ( maxzoom < 32 - full_detail & & maxzoom < 33 - low_detail & & cluster_distance > 0 ) {
unsigned long long zoom_mingap = ( ( 1LL < < ( 32 - maxzoom ) ) / 256 * cluster_distance ) * ( ( 1LL < < ( 32 - maxzoom ) ) / 256 * cluster_distance ) ;
if ( avg > zoom_mingap ) {
break ;
}
maxzoom + + ;
changed = true ;
}
if ( changed ) {
printf ( " Choosing a maxzoom of -z%d to keep most features distinct with cluster distance %d \n " , maxzoom , cluster_distance ) ;
}
2017-05-30 20:28:25 +00:00
}
if ( dist_count ! = 0 ) {
double want2 = exp ( dist_sum / dist_count ) / 8 ;
int mz = ceil ( log ( 360 / ( .00000274 * want2 ) ) / log ( 2 ) - full_detail ) ;
if ( mz < 0 ) {
mz = 0 ;
}
2018-07-25 18:10:56 +00:00
if ( mz > 32 - full_detail ) {
mz = 32 - full_detail ;
}
if ( mz > 33 - low_detail ) { // that is, mz - 1 > 32 - low_detail
mz = 33 - low_detail ;
2017-05-30 20:28:25 +00:00
}
if ( mz > maxzoom | | count < = 0 ) {
if ( ! quiet ) {
2018-02-06 20:15:24 +00:00
fprintf ( stderr , " Choosing a maxzoom of -z%d for resolution of about %d feet (%d meters) within features \n " , mz , ( int ) exp ( dist_sum / dist_count ) , ( int ) ( exp ( dist_sum / dist_count ) / 3.28084 ) ) ;
2017-05-30 20:28:25 +00:00
}
maxzoom = mz ;
}
2016-04-27 22:14:09 +00:00
}
2017-04-13 21:07:11 +00:00
if ( maxzoom < minzoom ) {
fprintf ( stderr , " Can't use %d for maxzoom because minzoom is %d \n " , maxzoom , minzoom ) ;
maxzoom = minzoom ;
}
fix_dropping = true ;
if ( basezoom = = - 1 ) {
basezoom = maxzoom ;
}
}
if ( basezoom < 0 | | droprate < 0 ) {
2016-04-27 22:14:09 +00:00
struct tile {
unsigned x ;
unsigned y ;
long long count ;
long long fullcount ;
double gap ;
unsigned long long previndex ;
} tile [ MAX_ZOOM + 1 ] , max [ MAX_ZOOM + 1 ] ;
{
2016-05-03 22:48:42 +00:00
int z ;
for ( z = 0 ; z < = MAX_ZOOM ; z + + ) {
tile [ z ] . x = tile [ z ] . y = tile [ z ] . count = tile [ z ] . fullcount = tile [ z ] . gap = tile [ z ] . previndex = 0 ;
max [ z ] . x = max [ z ] . y = max [ z ] . count = max [ z ] . fullcount = 0 ;
2016-04-27 22:14:09 +00:00
}
}
long long progress = - 1 ;
2016-05-03 22:48:42 +00:00
long long ip ;
for ( ip = 0 ; ip < indices ; ip + + ) {
2016-04-27 22:14:09 +00:00
unsigned xx , yy ;
2018-12-12 18:49:53 +00:00
decode_index ( map [ ip ] . ix , & xx , & yy ) ;
2016-04-27 22:14:09 +00:00
2016-05-03 22:48:42 +00:00
long long nprogress = 100 * ip / indices ;
2016-04-27 22:14:09 +00:00
if ( nprogress ! = progress ) {
progress = nprogress ;
2018-03-13 21:51:41 +00:00
if ( ! quiet & & ! quiet_progress & & progress_time ( ) ) {
2016-04-27 22:14:09 +00:00
fprintf ( stderr , " Base zoom/drop rate: %lld%% \r " , progress ) ;
}
}
int z ;
for ( z = 0 ; z < = MAX_ZOOM ; z + + ) {
unsigned xxx = 0 , yyy = 0 ;
if ( z ! = 0 ) {
xxx = xx > > ( 32 - z ) ;
yyy = yy > > ( 32 - z ) ;
}
double scale = ( double ) ( 1LL < < ( 64 - 2 * ( z + 8 ) ) ) ;
if ( tile [ z ] . x ! = xxx | | tile [ z ] . y ! = yyy ) {
if ( tile [ z ] . count > max [ z ] . count ) {
max [ z ] = tile [ z ] ;
}
tile [ z ] . x = xxx ;
tile [ z ] . y = yyy ;
tile [ z ] . count = 0 ;
tile [ z ] . fullcount = 0 ;
tile [ z ] . gap = 0 ;
tile [ z ] . previndex = 0 ;
}
tile [ z ] . fullcount + + ;
2017-11-07 23:20:17 +00:00
if ( manage_gap ( map [ ip ] . ix , & tile [ z ] . previndex , scale , gamma , & tile [ z ] . gap ) ) {
2016-04-27 22:14:09 +00:00
continue ;
}
tile [ z ] . count + + ;
}
}
int z ;
for ( z = MAX_ZOOM ; z > = 0 ; z - - ) {
if ( tile [ z ] . count > max [ z ] . count ) {
max [ z ] = tile [ z ] ;
}
}
int max_features = 50000 / ( basezoom_marker_width * basezoom_marker_width ) ;
int obasezoom = basezoom ;
if ( basezoom < 0 ) {
basezoom = MAX_ZOOM ;
for ( z = MAX_ZOOM ; z > = 0 ; z - - ) {
if ( max [ z ] . count < max_features ) {
basezoom = z ;
}
// printf("%d/%u/%u %lld\n", z, max[z].x, max[z].y, max[z].count);
}
2018-06-07 20:27:49 +00:00
if ( ! quiet ) {
fprintf ( stderr , " Choosing a base zoom of -B%d to keep %lld features in tile %d/%u/%u. \n " , basezoom , max [ basezoom ] . count , basezoom , max [ basezoom ] . x , max [ basezoom ] . y ) ;
}
2016-04-27 22:14:09 +00:00
}
if ( obasezoom < 0 & & basezoom > maxzoom ) {
fprintf ( stderr , " Couldn't find a suitable base zoom. Working from the other direction. \n " ) ;
if ( gamma = = 0 ) {
fprintf ( stderr , " You might want to try -g1 to limit near-duplicates. \n " ) ;
}
if ( droprate < 0 ) {
if ( maxzoom = = 0 ) {
droprate = 2.5 ;
} else {
droprate = exp ( log ( ( long double ) max [ 0 ] . count / max [ maxzoom ] . count ) / ( maxzoom ) ) ;
2018-06-07 20:27:49 +00:00
if ( ! quiet ) {
fprintf ( stderr , " Choosing a drop rate of -r%f to get from %lld to %lld in %d zooms \n " , droprate , max [ maxzoom ] . count , max [ 0 ] . count , maxzoom ) ;
}
2016-04-27 22:14:09 +00:00
}
}
basezoom = 0 ;
for ( z = 0 ; z < = maxzoom ; z + + ) {
double zoomdiff = log ( ( long double ) max [ z ] . count / max_features ) / log ( droprate ) ;
if ( zoomdiff + z > basezoom ) {
basezoom = ceil ( zoomdiff + z ) ;
}
}
2018-06-07 20:27:49 +00:00
if ( ! quiet ) {
fprintf ( stderr , " Choosing a base zoom of -B%d to keep %f features in tile %d/%u/%u. \n " , basezoom , max [ maxzoom ] . count * exp ( log ( droprate ) * ( maxzoom - basezoom ) ) , maxzoom , max [ maxzoom ] . x , max [ maxzoom ] . y ) ;
}
2016-04-27 22:14:09 +00:00
} else if ( droprate < 0 ) {
droprate = 1 ;
for ( z = basezoom - 1 ; z > = 0 ; z - - ) {
double interval = exp ( log ( droprate ) * ( basezoom - z ) ) ;
if ( max [ z ] . count / interval > = max_features ) {
interval = ( long double ) max [ z ] . count / max_features ;
droprate = exp ( log ( interval ) / ( basezoom - z ) ) ;
interval = exp ( log ( droprate ) * ( basezoom - z ) ) ;
2018-06-07 20:27:49 +00:00
if ( ! quiet ) {
fprintf ( stderr , " Choosing a drop rate of -r%f to keep %f features in tile %d/%u/%u. \n " , droprate , max [ z ] . count / interval , z , max [ z ] . x , max [ z ] . y ) ;
}
2016-04-27 22:14:09 +00:00
}
}
}
if ( gamma > 0 ) {
int effective = 0 ;
for ( z = 0 ; z < maxzoom ; z + + ) {
if ( max [ z ] . count < max [ z ] . fullcount ) {
effective = z + 1 ;
}
}
if ( effective = = 0 ) {
2018-06-07 22:06:42 +00:00
if ( ! quiet ) {
fprintf ( stderr , " With gamma, effective base zoom is 0, so no effective drop rate \n " ) ;
}
2016-04-27 22:14:09 +00:00
} else {
double interval_0 = exp ( log ( droprate ) * ( basezoom - 0 ) ) ;
double interval_eff = exp ( log ( droprate ) * ( basezoom - effective ) ) ;
if ( effective > basezoom ) {
interval_eff = 1 ;
}
double scaled_0 = max [ 0 ] . count / interval_0 ;
double scaled_eff = max [ effective ] . count / interval_eff ;
double rate_at_0 = scaled_0 / max [ 0 ] . fullcount ;
double rate_at_eff = scaled_eff / max [ effective ] . fullcount ;
double eff_drop = exp ( log ( rate_at_eff / rate_at_0 ) / ( effective - 0 ) ) ;
2018-06-07 22:06:42 +00:00
if ( ! quiet ) {
fprintf ( stderr , " With gamma, effective base zoom of %d, effective drop rate of %f \n " , effective , eff_drop ) ;
}
2016-04-27 22:14:09 +00:00
}
}
2017-04-13 21:07:11 +00:00
fix_dropping = true ;
}
if ( fix_dropping ) {
2016-10-12 00:24:22 +00:00
// Fix up the minzooms for features, now that we really know the base zoom
// and drop rate.
struct stat geomst ;
if ( fstat ( geomfd , & geomst ) ! = 0 ) {
perror ( " stat sorted geom \n " ) ;
exit ( EXIT_FAILURE ) ;
}
char * geom = ( char * ) mmap ( NULL , geomst . st_size , PROT_READ | PROT_WRITE , MAP_SHARED , geomfd , 0 ) ;
if ( geom = = MAP_FAILED ) {
perror ( " mmap geom for fixup " ) ;
exit ( EXIT_FAILURE ) ;
}
madvise ( geom , indexpos , MADV_SEQUENTIAL ) ;
madvise ( geom , indexpos , MADV_WILLNEED ) ;
struct drop_state ds [ maxzoom + 1 ] ;
prep_drop_states ( ds , maxzoom , basezoom , droprate ) ;
2017-04-13 21:07:11 +00:00
for ( long long ip = 0 ; ip < indices ; ip + + ) {
2016-10-12 00:24:22 +00:00
if ( ip > 0 & & map [ ip ] . start ! = map [ ip - 1 ] . end ) {
fprintf ( stderr , " Mismatched index at %lld: %lld vs %lld \n " , ip , map [ ip ] . start , map [ ip ] . end ) ;
}
2017-11-07 18:48:19 +00:00
int feature_minzoom = calc_feature_minzoom ( & map [ ip ] , ds , maxzoom , gamma ) ;
2016-10-12 00:24:22 +00:00
geom [ map [ ip ] . end - 1 ] = feature_minzoom ;
}
munmap ( geom , geomst . st_size ) ;
2016-04-27 22:14:09 +00:00
}
2017-04-13 21:07:11 +00:00
madvise ( map , indexpos , MADV_DONTNEED ) ;
munmap ( map , indexpos ) ;
2016-04-27 22:14:09 +00:00
if ( close ( indexfd ) ! = 0 ) {
perror ( " close sorted index " ) ;
}
/* Traverse and split the geometries for each zoom level */
struct stat geomst ;
if ( fstat ( geomfd , & geomst ) ! = 0 ) {
perror ( " stat sorted geom \n " ) ;
exit ( EXIT_FAILURE ) ;
}
int fd [ TEMP_FILES ] ;
off_t size [ TEMP_FILES ] ;
fd [ 0 ] = geomfd ;
size [ 0 ] = geomst . st_size ;
2016-10-15 00:11:57 +00:00
for ( size_t j = 1 ; j < TEMP_FILES ; j + + ) {
2016-04-27 22:14:09 +00:00
fd [ j ] = - 1 ;
size [ j ] = 0 ;
}
2018-03-13 22:21:21 +00:00
std : : atomic < unsigned > midx ( 0 ) ;
std : : atomic < unsigned > midy ( 0 ) ;
2018-04-05 20:42:54 +00:00
int written = traverse_zooms ( fd , size , meta , stringpool , & midx , & midy , maxzoom , minzoom , outdb , outdir , buffer , fname , tmpdir , gamma , full_detail , low_detail , min_detail , meta_off , pool_off , initial_x , initial_y , simplification , layermaps , prefilter , postfilter , attribute_accum , filter ) ;
2016-04-27 22:14:09 +00:00
if ( maxzoom ! = written ) {
2018-04-18 17:48:13 +00:00
if ( written > minzoom ) {
fprintf ( stderr , " \n \n \n *** NOTE TILES ONLY COMPLETE THROUGH ZOOM %d *** \n \n \n " , written ) ;
maxzoom = written ;
ret = 100 ;
} else {
fprintf ( stderr , " %s: No zoom levels were successfully written \n " , * av ) ;
exit ( EXIT_FAILURE ) ;
}
2016-04-27 22:14:09 +00:00
}
madvise ( meta , metapos , MADV_DONTNEED ) ;
if ( munmap ( meta , metapos ) ! = 0 ) {
perror ( " munmap meta " ) ;
}
if ( close ( metafd ) < 0 ) {
perror ( " close meta " ) ;
}
if ( poolpos > 0 ) {
2016-04-28 21:43:04 +00:00
madvise ( ( void * ) stringpool , poolpos , MADV_DONTNEED ) ;
2016-04-27 22:14:09 +00:00
if ( munmap ( stringpool , poolpos ) ! = 0 ) {
perror ( " munmap stringpool " ) ;
}
}
if ( close ( poolfd ) < 0 ) {
perror ( " close pool " ) ;
}
double minlat = 0 , minlon = 0 , maxlat = 0 , maxlon = 0 , midlat = 0 , midlon = 0 ;
2016-06-02 00:09:20 +00:00
tile2lonlat ( midx , midy , maxzoom , & minlon , & maxlat ) ;
tile2lonlat ( midx + 1 , midy + 1 , maxzoom , & maxlon , & minlat ) ;
2016-04-27 22:14:09 +00:00
midlat = ( maxlat + minlat ) / 2 ;
midlon = ( maxlon + minlon ) / 2 ;
2016-06-01 22:49:41 +00:00
tile2lonlat ( file_bbox [ 0 ] , file_bbox [ 1 ] , 32 , & minlon , & maxlat ) ;
tile2lonlat ( file_bbox [ 2 ] , file_bbox [ 3 ] , 32 , & maxlon , & minlat ) ;
2016-04-27 22:14:09 +00:00
if ( midlat < minlat ) {
midlat = minlat ;
}
if ( midlat > maxlat ) {
midlat = maxlat ;
}
if ( midlon < minlon ) {
midlon = minlon ;
}
if ( midlon > maxlon ) {
midlon = maxlon ;
}
2016-08-29 23:38:57 +00:00
std : : map < std : : string , layermap_entry > merged_lm = merge_layermaps ( layermaps ) ;
2016-09-20 00:53:31 +00:00
for ( auto ai = merged_lm . begin ( ) ; ai ! = merged_lm . end ( ) ; + + ai ) {
ai - > second . minzoom = minzoom ;
ai - > second . maxzoom = maxzoom ;
}
2017-04-04 23:34:54 +00:00
2019-04-05 00:01:02 +00:00
mbtiles_write_metadata ( outdb , outdir , fname , minzoom , maxzoom , minlat , minlon , maxlat , maxlon , midlat , midlon , forcetable , attribution , merged_lm , true , description , ! prevent [ P_TILE_STATS ] , attribute_descriptions , " tippecanoe " , commandline ) ;
2016-04-27 22:14:09 +00:00
return ret ;
}
2016-05-03 23:34:19 +00:00
static bool has_name ( struct option * long_options , int * pl ) {
for ( size_t lo = 0 ; long_options [ lo ] . name ! = NULL ; lo + + ) {
if ( long_options [ lo ] . flag = = pl ) {
return true ;
2016-04-27 22:14:09 +00:00
}
}
2016-05-03 23:34:19 +00:00
return false ;
2016-04-27 22:14:09 +00:00
}
2017-04-17 21:42:50 +00:00
void set_attribute_type ( std : : map < std : : string , int > & attribute_types , const char * arg ) {
const char * s = strchr ( arg , ' : ' ) ;
if ( s = = NULL ) {
fprintf ( stderr , " -T%s option must be in the form -Tname:type \n " , arg ) ;
exit ( EXIT_FAILURE ) ;
}
std : : string name = std : : string ( arg , s - arg ) ;
std : : string type = std : : string ( s + 1 ) ;
int t = - 1 ;
if ( type = = " int " ) {
t = mvt_int ;
} else if ( type = = " float " ) {
t = mvt_float ;
} else if ( type = = " string " ) {
t = mvt_string ;
} else if ( type = = " bool " ) {
t = mvt_bool ;
} else {
fprintf ( stderr , " Attribute type (%s) must be int, float, string, or bool \n " , type . c_str ( ) ) ;
exit ( EXIT_FAILURE ) ;
}
attribute_types . insert ( std : : pair < std : : string , int > ( name , t ) ) ;
}
2018-02-27 00:47:15 +00:00
void set_attribute_accum ( std : : map < std : : string , attribute_op > & attribute_accum , const char * arg ) {
2018-02-21 00:19:49 +00:00
const char * s = strchr ( arg , ' : ' ) ;
if ( s = = NULL ) {
2018-02-27 00:47:15 +00:00
fprintf ( stderr , " -E%s option must be in the form -Ename:method \n " , arg ) ;
2018-02-21 00:19:49 +00:00
exit ( EXIT_FAILURE ) ;
}
std : : string name = std : : string ( arg , s - arg ) ;
std : : string type = std : : string ( s + 1 ) ;
2018-02-27 00:47:15 +00:00
attribute_op t ;
2018-02-21 00:19:49 +00:00
if ( type = = " sum " ) {
2018-02-27 00:47:15 +00:00
t = op_sum ;
2018-02-21 00:19:49 +00:00
} else if ( type = = " product " ) {
2018-02-27 00:47:15 +00:00
t = op_product ;
2018-02-21 00:19:49 +00:00
} else if ( type = = " mean " ) {
2018-02-27 00:47:15 +00:00
t = op_mean ;
2018-02-27 22:45:46 +00:00
} else if ( type = = " max " ) {
t = op_max ;
} else if ( type = = " min " ) {
t = op_min ;
2018-02-21 00:19:49 +00:00
} else if ( type = = " concat " ) {
2018-02-27 00:47:15 +00:00
t = op_concat ;
} else if ( type = = " comma " ) {
t = op_comma ;
2018-02-21 00:19:49 +00:00
} else {
2018-02-27 22:45:46 +00:00
fprintf ( stderr , " Attribute method (%s) must be sum, product, mean, max, min, concat, or comma \n " , type . c_str ( ) ) ;
2018-02-21 00:19:49 +00:00
exit ( EXIT_FAILURE ) ;
}
2018-02-27 00:47:15 +00:00
attribute_accum . insert ( std : : pair < std : : string , attribute_op > ( name , t ) ) ;
2018-02-21 00:19:49 +00:00
}
2018-05-24 18:30:42 +00:00
void parse_json_source ( const char * arg , struct source & src ) {
json_pull * jp = json_begin_string ( arg ) ;
json_object * o = json_read_tree ( jp ) ;
if ( o = = NULL ) {
fprintf ( stderr , " %s: -L%s: %s \n " , * av , arg , jp - > error ) ;
exit ( EXIT_FAILURE ) ;
}
if ( o - > type ! = JSON_HASH ) {
fprintf ( stderr , " %s: -L%s: not a JSON object \n " , * av , arg ) ;
exit ( EXIT_FAILURE ) ;
}
json_object * fname = json_hash_get ( o , " file " ) ;
if ( fname = = NULL | | fname - > type ! = JSON_STRING ) {
fprintf ( stderr , " %s: -L%s: requires \" file \" : filename \n " , * av , arg ) ;
exit ( EXIT_FAILURE ) ;
}
src . file = std : : string ( fname - > string ) ;
json_object * layer = json_hash_get ( o , " layer " ) ;
if ( layer ! = NULL & & layer - > type = = JSON_STRING ) {
src . layer = std : : string ( layer - > string ) ;
}
json_object * description = json_hash_get ( o , " description " ) ;
if ( description ! = NULL & & description - > type = = JSON_STRING ) {
src . description = std : : string ( description - > string ) ;
}
2018-07-17 21:57:56 +00:00
json_object * format = json_hash_get ( o , " format " ) ;
if ( format ! = NULL & & format - > type = = JSON_STRING ) {
src . format = std : : string ( format - > string ) ;
}
2018-05-24 18:30:42 +00:00
json_free ( o ) ;
json_end ( jp ) ;
}
2016-04-27 22:14:09 +00:00
int main ( int argc , char * * argv ) {
# ifdef MTRACE
mtrace ( ) ;
# endif
2018-02-27 01:35:39 +00:00
av = argv ;
2016-04-27 22:14:09 +00:00
init_cpus ( ) ;
extern int optind ;
extern char * optarg ;
int i ;
char * name = NULL ;
2017-03-21 18:07:15 +00:00
char * description = NULL ;
2017-02-21 00:16:06 +00:00
char * layername = NULL ;
2017-04-07 19:36:34 +00:00
char * out_mbtiles = NULL ;
2017-07-20 21:17:09 +00:00
char * out_dir = NULL ;
2017-04-04 23:34:54 +00:00
sqlite3 * outdb = NULL ;
2016-04-27 22:14:09 +00:00
int maxzoom = 14 ;
int minzoom = 0 ;
int basezoom = - 1 ;
double basezoom_marker_width = 1 ;
int force = 0 ;
int forcetable = 0 ;
double droprate = 2.5 ;
double gamma = 0 ;
int buffer = 5 ;
const char * tmpdir = " /tmp " ;
const char * attribution = NULL ;
2016-04-28 19:14:19 +00:00
std : : vector < source > sources ;
2016-12-08 23:13:38 +00:00
const char * prefilter = NULL ;
const char * postfilter = NULL ;
2017-04-13 21:07:11 +00:00
bool guess_maxzoom = false ;
2016-04-27 22:14:09 +00:00
2016-04-28 19:57:03 +00:00
std : : set < std : : string > exclude , include ;
2017-04-17 21:42:50 +00:00
std : : map < std : : string , int > attribute_types ;
2018-02-27 00:47:15 +00:00
std : : map < std : : string , attribute_op > attribute_accum ;
2018-05-24 21:17:26 +00:00
std : : map < std : : string , std : : string > attribute_descriptions ;
2016-04-27 22:14:09 +00:00
int exclude_all = 0 ;
int read_parallel = 0 ;
int files_open_at_start ;
2017-09-01 18:51:12 +00:00
json_object * filter = NULL ;
2016-04-27 22:14:09 +00:00
for ( i = 0 ; i < 256 ; i + + ) {
prevent [ i ] = 0 ;
additional [ i ] = 0 ;
}
2017-04-21 20:48:39 +00:00
static struct option long_options_orig [ ] = {
{ " Output tileset " , 0 , 0 , 0 } ,
2016-04-27 22:14:09 +00:00
{ " output " , required_argument , 0 , ' o ' } ,
2017-04-07 12:33:01 +00:00
{ " output-to-directory " , required_argument , 0 , ' e ' } ,
2017-04-21 20:48:39 +00:00
{ " force " , no_argument , 0 , ' f ' } ,
{ " allow-existing " , no_argument , 0 , ' F ' } ,
2016-04-27 22:14:09 +00:00
2017-04-21 20:48:39 +00:00
{ " Tileset description and attribution " , 0 , 0 , 0 } ,
2016-04-27 22:14:09 +00:00
{ " name " , required_argument , 0 , ' n ' } ,
2017-04-21 20:48:39 +00:00
{ " attribution " , required_argument , 0 , ' A ' } ,
2017-03-21 18:07:15 +00:00
{ " description " , required_argument , 0 , ' N ' } ,
2017-04-21 20:48:39 +00:00
{ " Input files and layer names " , 0 , 0 , 0 } ,
2016-04-27 22:14:09 +00:00
{ " layer " , required_argument , 0 , ' l ' } ,
{ " named-layer " , required_argument , 0 , ' L ' } ,
2017-04-21 20:48:39 +00:00
{ " Parallel processing of input " , 0 , 0 , 0 } ,
{ " read-parallel " , no_argument , 0 , ' P ' } ,
{ " Projection of input " , 0 , 0 , 0 } ,
{ " projection " , required_argument , 0 , ' s ' } ,
{ " Zoom levels " , 0 , 0 , 0 } ,
2016-04-27 22:14:09 +00:00
{ " maximum-zoom " , required_argument , 0 , ' z ' } ,
{ " minimum-zoom " , required_argument , 0 , ' Z ' } ,
2017-05-31 17:41:23 +00:00
{ " extend-zooms-if-still-dropping " , no_argument , & additional [ A_EXTEND_ZOOMS ] , 1 } ,
2018-01-26 20:41:30 +00:00
{ " one-tile " , required_argument , 0 , ' R ' } ,
2017-04-21 20:48:39 +00:00
{ " Tile resolution " , 0 , 0 , 0 } ,
2016-04-27 22:14:09 +00:00
{ " full-detail " , required_argument , 0 , ' d ' } ,
{ " low-detail " , required_argument , 0 , ' D ' } ,
{ " minimum-detail " , required_argument , 0 , ' m ' } ,
2017-04-21 20:48:39 +00:00
{ " Filtering feature attributes " , 0 , 0 , 0 } ,
2016-04-27 22:14:09 +00:00
{ " exclude " , required_argument , 0 , ' x ' } ,
{ " include " , required_argument , 0 , ' y ' } ,
{ " exclude-all " , no_argument , 0 , ' X ' } ,
2018-02-27 22:45:46 +00:00
{ " Modifying feature attributes " , 0 , 0 , 0 } ,
2017-04-21 20:48:39 +00:00
{ " attribute-type " , required_argument , 0 , ' T ' } ,
2018-05-24 21:17:26 +00:00
{ " attribute-description " , required_argument , 0 , ' Y ' } ,
2018-02-21 00:19:49 +00:00
{ " accumulate-attribute " , required_argument , 0 , ' E ' } ,
2018-07-19 21:18:57 +00:00
{ " empty-csv-columns-are-null " , no_argument , & prevent [ P_EMPTY_CSV_COLUMNS ] , 1 } ,
2018-11-02 00:07:33 +00:00
{ " convert-stringified-ids-to-numbers " , no_argument , & additional [ A_CONVERT_NUMERIC_IDS ] , 1 } ,
2018-11-02 22:21:52 +00:00
{ " use-attribute-for-id " , required_argument , 0 , ' ~ ' } ,
2018-02-27 22:45:46 +00:00
{ " Filtering features by attributes " , 0 , 0 , 0 } ,
2017-09-01 18:51:12 +00:00
{ " feature-filter-file " , required_argument , 0 , ' J ' } ,
{ " feature-filter " , required_argument , 0 , ' j ' } ,
2016-04-27 22:14:09 +00:00
2017-04-21 20:48:39 +00:00
{ " Dropping a fixed fraction of features by zoom level " , 0 , 0 , 0 } ,
{ " drop-rate " , required_argument , 0 , ' r ' } ,
{ " base-zoom " , required_argument , 0 , ' B ' } ,
2016-04-27 22:14:09 +00:00
{ " drop-lines " , no_argument , & additional [ A_LINE_DROP ] , 1 } ,
{ " drop-polygons " , no_argument , & additional [ A_POLYGON_DROP ] , 1 } ,
2017-12-21 01:31:11 +00:00
{ " cluster-distance " , required_argument , 0 , ' K ' } ,
2017-04-21 20:48:39 +00:00
2018-02-06 22:39:05 +00:00
{ " Dropping or merging a fraction of features to keep under tile size limits " , 0 , 0 , 0 } ,
2016-11-17 20:40:11 +00:00
{ " drop-densest-as-needed " , no_argument , & additional [ A_DROP_DENSEST_AS_NEEDED ] , 1 } ,
2016-11-04 19:26:13 +00:00
{ " drop-fraction-as-needed " , no_argument , & additional [ A_DROP_FRACTION_AS_NEEDED ] , 1 } ,
2016-11-10 00:32:40 +00:00
{ " drop-smallest-as-needed " , no_argument , & additional [ A_DROP_SMALLEST_AS_NEEDED ] , 1 } ,
2018-02-06 22:39:05 +00:00
{ " coalesce-densest-as-needed " , no_argument , & additional [ A_COALESCE_DENSEST_AS_NEEDED ] , 1 } ,
{ " coalesce-fraction-as-needed " , no_argument , & additional [ A_COALESCE_FRACTION_AS_NEEDED ] , 1 } ,
2017-10-07 01:01:08 +00:00
{ " coalesce-smallest-as-needed " , no_argument , & additional [ A_COALESCE_SMALLEST_AS_NEEDED ] , 1 } ,
2017-04-21 20:48:39 +00:00
{ " force-feature-limit " , no_argument , & prevent [ P_DYNAMIC_DROP ] , 1 } ,
2017-12-14 22:30:08 +00:00
{ " cluster-densest-as-needed " , no_argument , & additional [ A_CLUSTER_DENSEST_AS_NEEDED ] , 1 } ,
2017-04-21 20:48:39 +00:00
{ " Dropping tightly overlapping features " , 0 , 0 , 0 } ,
{ " gamma " , required_argument , 0 , ' g ' } ,
{ " increase-gamma-as-needed " , no_argument , & additional [ A_INCREASE_GAMMA_AS_NEEDED ] , 1 } ,
2016-04-27 22:14:09 +00:00
2017-04-21 20:48:39 +00:00
{ " Line and polygon simplification " , 0 , 0 , 0 } ,
{ " simplification " , required_argument , 0 , ' S ' } ,
2016-04-27 22:14:09 +00:00
{ " no-line-simplification " , no_argument , & prevent [ P_SIMPLIFY ] , 1 } ,
{ " simplify-only-low-zooms " , no_argument , & prevent [ P_SIMPLIFY_LOW ] , 1 } ,
2017-04-21 20:48:39 +00:00
{ " no-tiny-polygon-reduction " , no_argument , & prevent [ P_TINY_POLYGON_REDUCTION ] , 1 } ,
2019-05-14 00:39:07 +00:00
{ " no-simplification-of-shared-nodes " , no_argument , & prevent [ P_SIMPLIFY_SHARED_NODES ] , 1 } ,
2017-04-21 20:48:39 +00:00
{ " Attempts to improve shared polygon boundaries " , 0 , 0 , 0 } ,
{ " detect-shared-borders " , no_argument , & additional [ A_DETECT_SHARED_BORDERS ] , 1 } ,
{ " grid-low-zooms " , no_argument , & additional [ A_GRID_LOW_ZOOMS ] , 1 } ,
{ " Controlling clipping to tile boundaries " , 0 , 0 , 0 } ,
{ " buffer " , required_argument , 0 , ' b ' } ,
2016-04-27 22:14:09 +00:00
{ " no-clipping " , no_argument , & prevent [ P_CLIPPING ] , 1 } ,
{ " no-duplication " , no_argument , & prevent [ P_DUPLICATION ] , 1 } ,
2017-04-21 20:48:39 +00:00
{ " Reordering features within each tile " , 0 , 0 , 0 } ,
{ " preserve-input-order " , no_argument , & prevent [ P_INPUT_ORDER ] , 1 } ,
{ " reorder " , no_argument , & additional [ A_REORDER ] , 1 } ,
{ " coalesce " , no_argument , & additional [ A_COALESCE ] , 1 } ,
{ " reverse " , no_argument , & additional [ A_REVERSE ] , 1 } ,
2018-12-12 18:49:53 +00:00
{ " hilbert " , no_argument , & additional [ A_HILBERT ] , 1 } ,
2017-04-21 20:48:39 +00:00
{ " Adding calculated attributes " , 0 , 0 , 0 } ,
{ " calculate-feature-density " , no_argument , & additional [ A_CALCULATE_FEATURE_DENSITY ] , 1 } ,
2018-07-26 22:08:47 +00:00
{ " generate-ids " , no_argument , & additional [ A_GENERATE_IDS ] , 1 } ,
2017-04-21 20:48:39 +00:00
{ " Trying to correct bad source geometry " , 0 , 0 , 0 } ,
{ " detect-longitude-wraparound " , no_argument , & additional [ A_DETECT_WRAPAROUND ] , 1 } ,
2018-06-01 19:59:28 +00:00
{ " use-source-polygon-winding " , no_argument , & prevent [ P_USE_SOURCE_POLYGON_WINDING ] , 1 } ,
{ " reverse-source-polygon-winding " , no_argument , & prevent [ P_REVERSE_SOURCE_POLYGON_WINDING ] , 1 } ,
2018-10-22 23:49:33 +00:00
{ " clip-bounding-box " , required_argument , 0 , ' ~ ' } ,
2017-04-21 20:48:39 +00:00
2017-04-21 23:18:00 +00:00
{ " Filtering tile contents " , 0 , 0 , 0 } ,
{ " prefilter " , required_argument , 0 , ' C ' } ,
{ " postfilter " , required_argument , 0 , ' c ' } ,
2017-04-21 20:48:39 +00:00
{ " Setting or disabling tile size limits " , 0 , 0 , 0 } ,
{ " maximum-tile-bytes " , required_argument , 0 , ' M ' } ,
2017-12-14 22:30:08 +00:00
{ " maximum-tile-features " , required_argument , 0 , ' O ' } ,
2017-04-21 20:48:39 +00:00
{ " no-feature-limit " , no_argument , & prevent [ P_FEATURE_LIMIT ] , 1 } ,
{ " no-tile-size-limit " , no_argument , & prevent [ P_KILOBYTE_LIMIT ] , 1 } ,
2017-04-07 12:33:01 +00:00
{ " no-tile-compression " , no_argument , & prevent [ P_TILE_COMPRESSION ] , 1 } ,
2017-08-15 17:35:41 +00:00
{ " no-tile-stats " , no_argument , & prevent [ P_TILE_STATS ] , 1 } ,
2018-08-31 23:19:55 +00:00
{ " tile-stats-attributes-limit " , required_argument , 0 , ' ~ ' } ,
2018-08-31 22:11:05 +00:00
{ " tile-stats-sample-values-limit " , required_argument , 0 , ' ~ ' } ,
{ " tile-stats-values-limit " , required_argument , 0 , ' ~ ' } ,
2016-04-27 22:14:09 +00:00
2017-04-21 20:48:39 +00:00
{ " Temporary storage " , 0 , 0 , 0 } ,
{ " temporary-directory " , required_argument , 0 , ' t ' } ,
{ " Progress indicator " , 0 , 0 , 0 } ,
{ " quiet " , no_argument , 0 , ' q ' } ,
2017-11-17 01:08:03 +00:00
{ " no-progress-indicator " , no_argument , 0 , ' Q ' } ,
2018-03-13 21:51:41 +00:00
{ " progress-interval " , required_argument , 0 , ' U ' } ,
2017-04-21 20:48:39 +00:00
{ " version " , no_argument , 0 , ' v ' } ,
{ " " , 0 , 0 , 0 } ,
{ " prevent " , required_argument , 0 , ' p ' } ,
{ " additional " , required_argument , 0 , ' a ' } ,
{ " check-polygons " , no_argument , & additional [ A_DEBUG_POLYGON ] , 1 } ,
{ " no-polygon-splitting " , no_argument , & prevent [ P_POLYGON_SPLIT ] , 1 } ,
{ " prefer-radix-sort " , no_argument , & additional [ A_PREFER_RADIX_SORT ] , 1 } ,
2019-03-19 17:13:44 +00:00
{ " help " , no_argument , 0 , ' H ' } ,
2017-04-21 20:48:39 +00:00
2016-04-27 22:14:09 +00:00
{ 0 , 0 , 0 , 0 } ,
} ;
2017-04-21 20:48:39 +00:00
static struct option long_options [ sizeof ( long_options_orig ) / sizeof ( long_options_orig [ 0 ] ) ] ;
2017-04-21 20:56:02 +00:00
static char getopt_str [ sizeof ( long_options_orig ) / sizeof ( long_options_orig [ 0 ] ) * 2 + 1 ] ;
2017-04-21 20:48:39 +00:00
2016-05-03 23:34:19 +00:00
{
2017-04-21 20:48:39 +00:00
size_t out = 0 ;
2017-04-21 20:56:02 +00:00
size_t cout = 0 ;
2017-04-21 20:48:39 +00:00
for ( size_t lo = 0 ; long_options_orig [ lo ] . name ! = NULL ; lo + + ) {
if ( long_options_orig [ lo ] . val ! = 0 ) {
long_options [ out + + ] = long_options_orig [ lo ] ;
2017-04-21 20:56:02 +00:00
if ( long_options_orig [ lo ] . val > ' ' ) {
getopt_str [ cout + + ] = long_options_orig [ lo ] . val ;
if ( long_options_orig [ lo ] . has_arg = = required_argument ) {
getopt_str [ cout + + ] = ' : ' ;
}
}
2017-04-21 20:48:39 +00:00
}
}
2017-04-21 20:56:02 +00:00
long_options [ out ] = { 0 , 0 , 0 , 0 } ;
getopt_str [ cout ] = ' \0 ' ;
2017-04-21 20:48:39 +00:00
2016-05-03 23:34:19 +00:00
for ( size_t lo = 0 ; long_options [ lo ] . name ! = NULL ; lo + + ) {
if ( long_options [ lo ] . flag ! = NULL ) {
if ( * long_options [ lo ] . flag ! = 0 ) {
fprintf ( stderr , " Internal error: reused %s \n " , long_options [ lo ] . name ) ;
exit ( EXIT_FAILURE ) ;
}
* long_options [ lo ] . flag = 1 ;
}
}
for ( size_t lo = 0 ; long_options [ lo ] . name ! = NULL ; lo + + ) {
if ( long_options [ lo ] . flag ! = NULL ) {
* long_options [ lo ] . flag = 0 ;
}
}
}
2019-04-05 00:01:02 +00:00
std : : string commandline = format_commandline ( argc , argv ) ;
2018-08-31 22:11:05 +00:00
int option_index = 0 ;
while ( ( i = getopt_long ( argc , argv , getopt_str , long_options , & option_index ) ) ! = - 1 ) {
2016-04-27 22:14:09 +00:00
switch ( i ) {
case 0 :
break ;
2018-08-31 22:11:05 +00:00
case ' ~ ' : {
const char * opt = long_options [ option_index ] . name ;
2018-08-31 23:19:55 +00:00
if ( strcmp ( opt , " tile-stats-attributes-limit " ) = = 0 ) {
2018-08-31 22:11:05 +00:00
max_tilestats_attributes = atoi ( optarg ) ;
} else if ( strcmp ( opt , " tile-stats-sample-values-limit " ) = = 0 ) {
max_tilestats_sample_values = atoi ( optarg ) ;
} else if ( strcmp ( opt , " tile-stats-values-limit " ) = = 0 ) {
max_tilestats_values = atoi ( optarg ) ;
2018-10-22 23:49:33 +00:00
} else if ( strcmp ( opt , " clip-bounding-box " ) = = 0 ) {
clipbbox clip ;
if ( sscanf ( optarg , " %lf,%lf,%lf,%lf " , & clip . lon1 , & clip . lat1 , & clip . lon2 , & clip . lat2 ) = = 4 ) {
clipbboxes . push_back ( clip ) ;
} else {
fprintf ( stderr , " %s: Can't parse bounding box --%s=%s \n " , argv [ 0 ] , opt , optarg ) ;
exit ( EXIT_FAILURE ) ;
}
2018-11-02 22:21:52 +00:00
} else if ( strcmp ( opt , " use-attribute-for-id " ) = = 0 ) {
attribute_for_id = optarg ;
2018-10-22 23:49:33 +00:00
} else {
fprintf ( stderr , " %s: Unrecognized option --%s \n " , argv [ 0 ] , opt ) ;
exit ( EXIT_FAILURE ) ;
2018-08-31 22:11:05 +00:00
}
break ;
}
2016-04-27 22:14:09 +00:00
case ' n ' :
name = optarg ;
break ;
2017-03-21 18:07:15 +00:00
case ' N ' :
description = optarg ;
break ;
2016-04-27 22:14:09 +00:00
case ' l ' :
2017-02-21 00:16:06 +00:00
layername = optarg ;
2016-04-27 22:14:09 +00:00
break ;
case ' A ' :
attribution = optarg ;
break ;
case ' L ' : {
2016-04-28 19:14:19 +00:00
struct source src ;
2018-05-24 18:30:42 +00:00
if ( optarg [ 0 ] = = ' { ' ) {
parse_json_source ( optarg , src ) ;
} else {
char * cp = strchr ( optarg , ' : ' ) ;
if ( cp = = NULL | | cp = = optarg ) {
fprintf ( stderr , " %s: -L requires layername:file \n " , argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
src . layer = std : : string ( optarg ) . substr ( 0 , cp - optarg ) ;
src . file = std : : string ( cp + 1 ) ;
}
2016-04-28 19:14:19 +00:00
sources . push_back ( src ) ;
2018-03-02 18:35:58 +00:00
break ;
}
2016-04-27 22:14:09 +00:00
case ' z ' :
2017-04-13 21:07:11 +00:00
if ( strcmp ( optarg , " g " ) = = 0 ) {
maxzoom = MAX_ZOOM ;
guess_maxzoom = true ;
} else {
2018-02-27 01:35:39 +00:00
maxzoom = atoi_require ( optarg , " Maxzoom " ) ;
2017-04-13 21:07:11 +00:00
}
2016-04-27 22:14:09 +00:00
break ;
case ' Z ' :
2018-02-27 01:35:39 +00:00
minzoom = atoi_require ( optarg , " Minzoom " ) ;
2016-04-27 22:14:09 +00:00
break ;
2018-01-26 20:41:30 +00:00
case ' R ' : {
unsigned z , x , y ;
if ( sscanf ( optarg , " %u/%u/%u " , & z , & x , & y ) = = 3 ) {
minzoom = z ;
maxzoom = z ;
justx = x ;
justy = y ;
} else {
fprintf ( stderr , " --one-tile argument must be z/x/y \n " ) ;
exit ( EXIT_FAILURE ) ;
}
2018-02-27 01:35:39 +00:00
break ;
2018-01-26 20:41:30 +00:00
}
2016-04-27 22:14:09 +00:00
case ' B ' :
if ( strcmp ( optarg , " g " ) = = 0 ) {
basezoom = - 2 ;
} else if ( optarg [ 0 ] = = ' g ' | | optarg [ 0 ] = = ' f ' ) {
basezoom = - 2 ;
if ( optarg [ 0 ] = = ' g ' ) {
2018-02-27 01:35:39 +00:00
basezoom_marker_width = atof_require ( optarg + 1 , " Marker width " ) ;
2016-04-27 22:14:09 +00:00
} else {
2018-02-27 01:35:39 +00:00
basezoom_marker_width = sqrt ( 50000 / atof_require ( optarg + 1 , " Marker width " ) ) ;
2016-04-27 22:14:09 +00:00
}
2018-02-27 01:35:39 +00:00
if ( basezoom_marker_width = = 0 | | atof_require ( optarg + 1 , " Marker width " ) = = 0 ) {
2016-04-27 22:14:09 +00:00
fprintf ( stderr , " %s: Must specify value >0 with -B%c \n " , argv [ 0 ] , optarg [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
} else {
2018-02-27 01:35:39 +00:00
basezoom = atoi_require ( optarg , " Basezoom " ) ;
2016-04-27 22:14:09 +00:00
if ( basezoom = = 0 & & strcmp ( optarg , " 0 " ) ! = 0 ) {
fprintf ( stderr , " %s: Couldn't understand -B%s \n " , argv [ 0 ] , optarg ) ;
exit ( EXIT_FAILURE ) ;
}
}
break ;
2017-12-21 01:31:11 +00:00
case ' K ' :
2018-02-27 01:35:39 +00:00
cluster_distance = atoi_require ( optarg , " Cluster distance " ) ;
2017-12-21 01:31:11 +00:00
if ( cluster_distance > 255 ) {
fprintf ( stderr , " %s: --cluster-distance %d is too big; limit is 255 \n " , argv [ 0 ] , cluster_distance ) ;
exit ( EXIT_FAILURE ) ;
}
break ;
2016-04-27 22:14:09 +00:00
case ' d ' :
2018-02-27 01:35:39 +00:00
full_detail = atoi_require ( optarg , " Full detail " ) ;
2018-12-18 23:53:11 +00:00
if ( full_detail > 30 ) {
// So the maximum geometry delta of just under 2 tile extents
// is less than 2^31
fprintf ( stderr , " %s: --full-detail can be at most 30 \n " , argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
2016-04-27 22:14:09 +00:00
break ;
case ' D ' :
2018-02-27 01:35:39 +00:00
low_detail = atoi_require ( optarg , " Low detail " ) ;
2018-12-18 23:53:11 +00:00
if ( low_detail > 30 ) {
fprintf ( stderr , " %s: --low-detail can be at most 30 \n " , argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
2016-04-27 22:14:09 +00:00
break ;
case ' m ' :
2018-02-27 01:35:39 +00:00
min_detail = atoi_require ( optarg , " Min detail " ) ;
2016-04-27 22:14:09 +00:00
break ;
case ' o ' :
2017-07-18 21:11:48 +00:00
if ( out_mbtiles ! = NULL ) {
fprintf ( stderr , " %s: Can't specify both %s and %s as output \n " , argv [ 0 ] , out_mbtiles , optarg ) ;
exit ( EXIT_FAILURE ) ;
}
2017-07-20 21:17:09 +00:00
if ( out_dir ! = NULL ) {
fprintf ( stderr , " %s: Can't specify both %s and %s as output \n " , argv [ 0 ] , out_dir , optarg ) ;
2017-07-18 21:11:48 +00:00
exit ( EXIT_FAILURE ) ;
}
2017-04-07 19:36:34 +00:00
out_mbtiles = optarg ;
2016-04-27 22:14:09 +00:00
break ;
2017-04-07 12:33:01 +00:00
case ' e ' :
2017-07-18 21:11:48 +00:00
if ( out_mbtiles ! = NULL ) {
fprintf ( stderr , " %s: Can't specify both %s and %s as output \n " , argv [ 0 ] , out_mbtiles , optarg ) ;
exit ( EXIT_FAILURE ) ;
}
2017-07-20 21:17:09 +00:00
if ( out_dir ! = NULL ) {
fprintf ( stderr , " %s: Can't specify both %s and %s as output \n " , argv [ 0 ] , out_dir , optarg ) ;
2017-07-18 21:11:48 +00:00
exit ( EXIT_FAILURE ) ;
}
2017-07-20 21:17:09 +00:00
out_dir = optarg ;
2017-04-07 12:33:01 +00:00
break ;
2016-04-27 22:14:09 +00:00
case ' x ' :
2016-04-28 19:57:03 +00:00
exclude . insert ( std : : string ( optarg ) ) ;
2016-04-27 22:14:09 +00:00
break ;
case ' y ' :
exclude_all = 1 ;
2016-04-28 19:57:03 +00:00
include . insert ( std : : string ( optarg ) ) ;
2016-04-27 22:14:09 +00:00
break ;
case ' X ' :
exclude_all = 1 ;
break ;
2018-05-24 21:17:26 +00:00
case ' Y ' : {
char * cp = strchr ( optarg , ' : ' ) ;
if ( cp = = NULL | | cp = = optarg ) {
fprintf ( stderr , " %s: -Y requires attribute:description \n " , argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
std : : string attrib = std : : string ( optarg ) . substr ( 0 , cp - optarg ) ;
std : : string desc = std : : string ( cp + 1 ) ;
attribute_descriptions . insert ( std : : pair < std : : string , std : : string > ( attrib , desc ) ) ;
} break ;
2017-09-01 18:51:12 +00:00
case ' J ' :
filter = read_filter ( optarg ) ;
break ;
case ' j ' :
filter = parse_filter ( optarg ) ;
break ;
2016-04-27 22:14:09 +00:00
case ' r ' :
if ( strcmp ( optarg , " g " ) = = 0 ) {
droprate = - 2 ;
} else if ( optarg [ 0 ] = = ' g ' | | optarg [ 0 ] = = ' f ' ) {
droprate = - 2 ;
if ( optarg [ 0 ] = = ' g ' ) {
2018-02-27 01:35:39 +00:00
basezoom_marker_width = atof_require ( optarg + 1 , " Marker width " ) ;
2016-04-27 22:14:09 +00:00
} else {
2018-02-27 01:35:39 +00:00
basezoom_marker_width = sqrt ( 50000 / atof_require ( optarg + 1 , " Marker width " ) ) ;
2016-04-27 22:14:09 +00:00
}
2018-02-27 01:35:39 +00:00
if ( basezoom_marker_width = = 0 | | atof_require ( optarg + 1 , " Marker width " ) = = 0 ) {
2016-04-27 22:14:09 +00:00
fprintf ( stderr , " %s: Must specify value >0 with -r%c \n " , argv [ 0 ] , optarg [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
} else {
2018-02-27 01:35:39 +00:00
droprate = atof_require ( optarg , " Drop rate " ) ;
2016-04-27 22:14:09 +00:00
}
break ;
case ' b ' :
2018-02-27 01:35:39 +00:00
buffer = atoi_require ( optarg , " Buffer " ) ;
2018-12-18 23:53:11 +00:00
if ( buffer > 127 ) {
// So the maximum geometry delta is under 2 tile extents,
// from less than half a tile beyond one side to less than
// half a tile beyond the other.
fprintf ( stderr , " %s: --buffer can be at most 127 \n " , argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
2016-04-27 22:14:09 +00:00
break ;
case ' f ' :
force = 1 ;
break ;
case ' F ' :
forcetable = 1 ;
break ;
case ' t ' :
tmpdir = optarg ;
if ( tmpdir [ 0 ] ! = ' / ' ) {
fprintf ( stderr , " Warning: temp directory %s doesn't begin with / \n " , tmpdir ) ;
}
break ;
case ' g ' :
2018-02-27 01:35:39 +00:00
gamma = atof_require ( optarg , " Gamma " ) ;
2016-04-27 22:14:09 +00:00
break ;
case ' q ' :
quiet = 1 ;
break ;
2017-11-17 01:08:03 +00:00
case ' Q ' :
quiet_progress = 1 ;
break ;
2018-03-13 21:51:41 +00:00
case ' U ' :
progress_interval = atof_require ( optarg , " Progress interval " ) ;
break ;
2016-04-27 22:14:09 +00:00
case ' p ' : {
char * cp ;
for ( cp = optarg ; * cp ! = ' \0 ' ; cp + + ) {
2016-05-03 23:34:19 +00:00
if ( has_name ( long_options , & prevent [ * cp & 0xFF ] ) ) {
2016-04-27 22:14:09 +00:00
prevent [ * cp & 0xFF ] = 1 ;
} else {
fprintf ( stderr , " %s: Unknown option -p%c \n " , argv [ 0 ] , * cp ) ;
exit ( EXIT_FAILURE ) ;
}
}
2018-03-02 18:35:58 +00:00
break ;
}
2016-04-27 22:14:09 +00:00
case ' a ' : {
char * cp ;
for ( cp = optarg ; * cp ! = ' \0 ' ; cp + + ) {
2016-05-03 23:34:19 +00:00
if ( has_name ( long_options , & additional [ * cp & 0xFF ] ) ) {
2016-04-27 22:14:09 +00:00
additional [ * cp & 0xFF ] = 1 ;
} else {
fprintf ( stderr , " %s: Unknown option -a%c \n " , argv [ 0 ] , * cp ) ;
exit ( EXIT_FAILURE ) ;
}
}
2018-03-02 18:35:58 +00:00
break ;
}
2016-04-27 22:14:09 +00:00
case ' v ' :
2018-06-08 04:37:25 +00:00
fprintf ( stderr , " tippecanoe %s \n " , VERSION ) ;
2019-03-19 17:13:44 +00:00
exit ( EXIT_SUCCESS ) ;
2016-04-27 22:14:09 +00:00
case ' P ' :
read_parallel = 1 ;
break ;
2016-06-28 22:18:27 +00:00
case ' s ' :
set_projection_or_exit ( optarg ) ;
break ;
2016-06-01 22:49:41 +00:00
2016-07-12 23:51:56 +00:00
case ' S ' :
2018-02-27 01:35:39 +00:00
simplification = atof_require ( optarg , " Simplification " ) ;
2016-07-12 23:51:56 +00:00
if ( simplification < = 0 ) {
fprintf ( stderr , " %s: --simplification must be > 0 \n " , argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
break ;
2016-11-04 18:15:53 +00:00
case ' M ' :
2018-02-27 01:35:39 +00:00
max_tile_size = atoll_require ( optarg , " Max tile size " ) ;
2016-10-24 22:33:14 +00:00
break ;
2017-12-14 22:30:08 +00:00
case ' O ' :
2018-02-27 01:35:39 +00:00
max_tile_features = atoll_require ( optarg , " Max tile features " ) ;
2017-12-14 22:30:08 +00:00
break ;
2016-12-07 20:15:57 +00:00
case ' c ' :
2016-12-08 23:13:38 +00:00
postfilter = optarg ;
break ;
case ' C ' :
prefilter = optarg ;
2016-12-07 20:15:57 +00:00
break ;
2017-04-17 21:42:50 +00:00
case ' T ' :
set_attribute_type ( attribute_types , optarg ) ;
break ;
2018-02-21 00:19:49 +00:00
case ' E ' :
set_attribute_accum ( attribute_accum , optarg ) ;
break ;
2016-04-27 22:14:09 +00:00
default : {
2019-03-19 17:16:14 +00:00
if ( i ! = ' H ' & & i ! = ' ? ' ) {
2018-12-18 23:53:11 +00:00
fprintf ( stderr , " Unknown option -%c \n " , i ) ;
}
2016-04-27 22:14:09 +00:00
int width = 7 + strlen ( argv [ 0 ] ) ;
2017-04-21 20:48:39 +00:00
fprintf ( stderr , " Usage: %s [options] [file.json ...] " , argv [ 0 ] ) ;
for ( size_t lo = 0 ; long_options_orig [ lo ] . name ! = NULL & & strlen ( long_options_orig [ lo ] . name ) > 0 ; lo + + ) {
if ( long_options_orig [ lo ] . val = = 0 ) {
fprintf ( stderr , " \n %s \n " , long_options_orig [ lo ] . name ) ;
width = 8 ;
continue ;
}
if ( width + strlen ( long_options_orig [ lo ] . name ) + 9 > = 80 ) {
2016-04-27 22:14:09 +00:00
fprintf ( stderr , " \n " ) ;
width = 8 ;
}
2017-04-21 20:48:39 +00:00
width + = strlen ( long_options_orig [ lo ] . name ) + 9 ;
if ( strcmp ( long_options_orig [ lo ] . name , " output " ) = = 0 ) {
fprintf ( stderr , " --%s=output.mbtiles " , long_options_orig [ lo ] . name ) ;
2016-04-27 22:14:09 +00:00
width + = 9 ;
2017-04-21 20:48:39 +00:00
} else if ( long_options_orig [ lo ] . has_arg ) {
fprintf ( stderr , " [--%s=...] " , long_options_orig [ lo ] . name ) ;
2016-04-27 22:14:09 +00:00
} else {
2017-04-21 20:48:39 +00:00
fprintf ( stderr , " [--%s] " , long_options_orig [ lo ] . name ) ;
2016-04-27 22:14:09 +00:00
}
}
if ( width + 16 > = 80 ) {
fprintf ( stderr , " \n " ) ;
width = 8 ;
}
2017-04-21 20:48:39 +00:00
fprintf ( stderr , " \n " ) ;
2019-03-19 17:13:44 +00:00
if ( i = = ' H ' ) {
exit ( EXIT_SUCCESS ) ;
} else {
exit ( EXIT_FAILURE ) ;
}
2016-04-27 22:14:09 +00:00
}
2017-04-21 20:48:39 +00:00
}
2016-04-27 22:14:09 +00:00
}
2018-12-12 18:49:53 +00:00
if ( additional [ A_HILBERT ] ) {
encode_index = encode_hilbert ;
decode_index = decode_hilbert ;
} else {
encode_index = encode_quadkey ;
decode_index = decode_quadkey ;
}
2018-10-22 23:49:33 +00:00
// Wait until here to project the bounding box, so that the behavior is
// the same no matter what order the projection and bounding box are
// specified in
for ( auto & c : clipbboxes ) {
projection - > project ( c . lon1 , c . lat1 , 32 , & c . minx , & c . maxy ) ;
projection - > project ( c . lon2 , c . lat2 , 32 , & c . maxx , & c . miny ) ;
}
2018-08-31 22:11:05 +00:00
if ( max_tilestats_sample_values < max_tilestats_values ) {
max_tilestats_sample_values = max_tilestats_values ;
}
2016-12-21 18:10:22 +00:00
signal ( SIGPIPE , SIG_IGN ) ;
2016-12-07 18:57:56 +00:00
files_open_at_start = open ( " /dev/null " , O_RDONLY | O_CLOEXEC ) ;
2016-12-01 23:34:44 +00:00
if ( files_open_at_start < 0 ) {
perror ( " open /dev/null " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( close ( files_open_at_start ) ! = 0 ) {
perror ( " close " ) ;
exit ( EXIT_FAILURE ) ;
}
2016-04-27 22:14:09 +00:00
if ( full_detail < = 0 ) {
full_detail = 12 ;
}
2018-11-21 23:34:43 +00:00
if ( full_detail < min_detail ) {
min_detail = full_detail ;
fprintf ( stderr , " %s: Reducing minimum detail to match full detail %d \n " , argv [ 0 ] , min_detail ) ;
}
if ( low_detail < min_detail ) {
min_detail = low_detail ;
fprintf ( stderr , " %s: Reducing minimum detail to match low detail %d \n " , argv [ 0 ] , min_detail ) ;
2016-04-27 22:14:09 +00:00
}
// Need two checks: one for geometry representation, the other for
// index traversal when guessing base zoom and drop rate
2017-04-13 21:07:11 +00:00
if ( ! guess_maxzoom ) {
if ( maxzoom > 32 - full_detail ) {
maxzoom = 32 - full_detail ;
fprintf ( stderr , " Highest supported zoom with detail %d is %d \n " , full_detail , maxzoom ) ;
}
2018-07-25 18:10:56 +00:00
if ( maxzoom > 33 - low_detail ) { // that is, maxzoom - 1 > 32 - low_detail
maxzoom = 33 - low_detail ;
fprintf ( stderr , " Highest supported zoom with low detail %d is %d \n " , low_detail , maxzoom ) ;
}
2016-04-27 22:14:09 +00:00
}
if ( maxzoom > MAX_ZOOM ) {
maxzoom = MAX_ZOOM ;
fprintf ( stderr , " Highest supported zoom is %d \n " , maxzoom ) ;
}
if ( minzoom > maxzoom ) {
2018-09-07 17:08:02 +00:00
fprintf ( stderr , " %s: Minimum zoom -Z%d cannot be greater than maxzoom -z%d \n " , argv [ 0 ] , minzoom , maxzoom ) ;
2016-04-27 22:14:09 +00:00
exit ( EXIT_FAILURE ) ;
}
if ( basezoom = = - 1 ) {
2017-04-13 21:07:11 +00:00
if ( ! guess_maxzoom ) {
basezoom = maxzoom ;
}
2016-04-27 22:14:09 +00:00
}
geometry_scale = 32 - ( full_detail + maxzoom ) ;
if ( geometry_scale < 0 ) {
geometry_scale = 0 ;
2017-04-13 21:07:11 +00:00
if ( ! guess_maxzoom ) {
fprintf ( stderr , " Full detail + maxzoom > 32, so you are asking for more detail than is available. \n " ) ;
}
2016-04-27 22:14:09 +00:00
}
if ( ( basezoom < 0 | | droprate < 0 ) & & ( gamma < 0 ) ) {
// Can't use randomized (as opposed to evenly distributed) dot dropping
// if rate and base aren't known during feature reading.
gamma = 0 ;
fprintf ( stderr , " Forcing -g0 since -B or -r is not known \n " ) ;
}
2017-07-20 21:17:09 +00:00
if ( out_mbtiles = = NULL & & out_dir = = NULL ) {
2017-04-07 12:33:01 +00:00
fprintf ( stderr , " %s: must specify -o out.mbtiles or -e directory \n " , argv [ 0 ] ) ;
2016-04-27 22:14:09 +00:00
exit ( EXIT_FAILURE ) ;
}
2017-07-20 21:17:09 +00:00
if ( out_mbtiles ! = NULL & & out_dir ! = NULL ) {
2017-04-07 15:24:29 +00:00
fprintf ( stderr , " %s: Options -o and -e cannot be used together \n " , argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
2017-04-07 19:36:34 +00:00
if ( out_mbtiles ! = NULL ) {
if ( force ) {
unlink ( out_mbtiles ) ;
}
2016-04-27 22:14:09 +00:00
2017-04-07 19:36:34 +00:00
outdb = mbtiles_open ( out_mbtiles , argv , forcetable ) ;
2017-04-04 23:34:54 +00:00
}
2017-07-20 21:17:09 +00:00
if ( out_dir ! = NULL ) {
2018-12-13 19:20:06 +00:00
check_dir ( out_dir , argv , force , forcetable ) ;
2017-07-20 21:17:09 +00:00
}
2017-04-04 23:34:54 +00:00
2016-04-27 22:14:09 +00:00
int ret = EXIT_SUCCESS ;
for ( i = optind ; i < argc ; i + + ) {
2016-04-28 19:14:19 +00:00
struct source src ;
src . layer = " " ;
src . file = std : : string ( argv [ i ] ) ;
sources . push_back ( src ) ;
2016-04-27 22:14:09 +00:00
}
2017-02-21 00:16:06 +00:00
if ( sources . size ( ) = = 0 ) {
struct source src ;
src . layer = " " ;
src . file = " " ; // standard input
sources . push_back ( src ) ;
}
if ( layername ! = NULL ) {
for ( size_t a = 0 ; a < sources . size ( ) ; a + + ) {
sources [ a ] . layer = layername ;
}
}
2016-12-16 20:20:57 +00:00
long long file_bbox [ 4 ] = { UINT_MAX , UINT_MAX , 0 , 0 } ;
2019-04-05 00:01:02 +00:00
ret = read_input ( sources , name ? name : out_mbtiles ? out_mbtiles : out_dir , maxzoom , minzoom , basezoom , basezoom_marker_width , outdb , out_dir , & exclude , & include , exclude_all , filter , droprate , buffer , tmpdir , gamma , read_parallel , forcetable , attribution , gamma ! = 0 , file_bbox , prefilter , postfilter , description , guess_maxzoom , & attribute_types , argv [ 0 ] , & attribute_accum , attribute_descriptions , commandline ) ;
2016-04-27 22:14:09 +00:00
2017-04-07 19:36:34 +00:00
if ( outdb ! = NULL ) {
2017-07-18 16:54:59 +00:00
mbtiles_close ( outdb , argv [ 0 ] ) ;
2017-04-07 19:36:34 +00:00
}
2016-04-27 22:14:09 +00:00
# ifdef MTRACE
muntrace ( ) ;
# endif
2016-12-07 18:57:56 +00:00
i = open ( " /dev/null " , O_RDONLY | O_CLOEXEC ) ;
2016-04-27 22:14:09 +00:00
// i < files_open_at_start is not an error, because reading from a pipe closes stdin
if ( i > files_open_at_start ) {
fprintf ( stderr , " Internal error: did not close all files: %d \n " , i ) ;
exit ( EXIT_FAILURE ) ;
}
2017-09-01 23:41:01 +00:00
if ( filter ! = NULL ) {
json_free ( filter ) ;
}
2016-04-27 22:14:09 +00:00
return ret ;
}
2016-12-07 18:57:56 +00:00
int mkstemp_cloexec ( char * name ) {
int fd = mkstemp ( name ) ;
if ( fd > = 0 ) {
if ( fcntl ( fd , F_SETFD , FD_CLOEXEC ) < 0 ) {
perror ( " cloexec for temporary file " ) ;
exit ( EXIT_FAILURE ) ;
}
}
return fd ;
}
FILE * fopen_oflag ( const char * name , const char * mode , int oflag ) {
int fd = open ( name , oflag ) ;
if ( fd < 0 ) {
return NULL ;
}
return fdopen ( fd , mode ) ;
}
2018-03-13 21:51:41 +00:00
bool progress_time ( ) {
if ( progress_interval = = 0.0 ) {
return true ;
}
struct timeval tv ;
double now ;
if ( gettimeofday ( & tv , NULL ) ! = 0 ) {
fprintf ( stderr , " %s: Can't get the time of day: %s \n " , * av , strerror ( errno ) ) ;
now = 0 ;
} else {
now = tv . tv_sec + tv . tv_usec / 1000000.0 ;
}
if ( now - last_progress > = progress_interval ) {
last_progress = now ;
return true ;
} else {
return false ;
}
}