From bd3b9a51364ada9214894a5724cd2b48d1973c8e Mon Sep 17 00:00:00 2001 From: Eric Fischer <enf@pobox.com> Date: Tue, 29 Mar 2016 13:12:59 -0700 Subject: [PATCH] Add an option to give specific layer names to specific input files --- CHANGELOG.md | 4 +++ README.md | 5 ++-- geojson.c | 78 ++++++++++++++++++++++++++++++++++++++++++------ man/tippecanoe.1 | 69 ++++++++++++++++++++++-------------------- version.h | 2 +- 5 files changed, 113 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a686a0e..aad67e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.9.6 + +* Add an option to give specific layer names to specific input files + ## 1.9.5 * Remove temporary files that were accidentally left behind diff --git a/README.md b/README.md index 60c6029..f3701e8 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,9 @@ Options ### Naming * -l _name_ or --layer=_name_: Layer name (default "file" if source is file.json or output is file.mbtiles). If there are multiple input files - specified, the files are all merged into the single named layer. - * -n _name_ or --name=_name_: Human-readable name (default file.json) + specified, the files are all merged into the single named layer, even if they try to specify individual names with -L. + * -L _name_:_file.json_ or --named-layer=_name_:_file.json_: Specify layer names for individual files. If your shell supports it, you can use a subshell redirect like -L _name_:<(cat dir/*.json) to specify a layer name for the output of streamed input. + * -n _name_ or --name=_name_: Human-readable name for the tileset (default file.json) ### File control diff --git a/geojson.c b/geojson.c index ab6a0c5..111430a 100644 --- a/geojson.c +++ b/geojson.c @@ -64,6 +64,12 @@ static int mb_geometry[GEOM_TYPES] = { VT_POINT, VT_POINT, VT_LINE, VT_LINE, VT_POLYGON, VT_POLYGON, }; +struct source { + char *layer; + char *file; + struct source *next; +}; + int CPUS; int TEMP_FILES; @@ -1084,7 +1090,7 @@ void start_parsing(int fd, FILE *fp, long long offset, long long len, volatile i } } -int read_json(int argc, char **argv, char *fname, const char *layername, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, struct pool *exclude, struct pool *include, int exclude_all, double droprate, int buffer, const char *tmpdir, double gamma, int *prevent, int *additional, int read_parallel, int forcetable) { +int read_json(int argc, struct source **sourcelist, char *fname, const char *layername, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, struct pool *exclude, struct pool *include, int exclude_all, double droprate, int buffer, const char *tmpdir, double gamma, int *prevent, int *additional, int read_parallel, int forcetable) { int ret = EXIT_SUCCESS; struct reader reader[CPUS]; @@ -1219,10 +1225,10 @@ int read_json(int argc, char **argv, char *fname, const char *layername, int max reading = "standard input"; fd = 0; } else { - reading = argv[source]; - fd = open(argv[source], O_RDONLY); + reading = sourcelist[source]->file; + fd = open(sourcelist[source]->file, O_RDONLY); if (fd < 0) { - perror(argv[source]); + perror(sourcelist[source]->file); continue; } } @@ -1250,7 +1256,7 @@ int read_json(int argc, char **argv, char *fname, const char *layername, int max } else { FILE *fp = fdopen(fd, "r"); if (fp == NULL) { - perror(argv[source]); + perror(sourcelist[source]->file); close(fd); continue; } @@ -1388,9 +1394,13 @@ int read_json(int argc, char **argv, char *fname, const char *layername, int max exit(EXIT_FAILURE); } } else { - char *src = argv[i]; + char *src; if (argc < 1) { src = fname; + } else if (sourcelist[i]->layer != NULL) { + src = sourcelist[i]->layer; + } else { + src = sourcelist[i]->file; } char *trunc = layernames[i] = malloc(strlen(src) + 1); @@ -2113,6 +2123,9 @@ int main(int argc, char **argv) { int buffer = 5; const char *tmpdir = "/tmp"; + int nsources = 0; + struct source *sources = NULL; + struct pool exclude, include; pool_init(&exclude, 0); pool_init(&include, 0); @@ -2127,6 +2140,7 @@ int main(int argc, char **argv) { static struct option long_options[] = { {"name", required_argument, 0, 'n'}, {"layer", required_argument, 0, 'l'}, + {"named-layer", required_argument, 0, 'L'}, {"maximum-zoom", required_argument, 0, 'z'}, {"minimum-zoom", required_argument, 0, 'Z'}, {"base-zoom", required_argument, 0, 'B'}, @@ -2166,7 +2180,7 @@ int main(int argc, char **argv) { {0, 0, 0, 0}, }; - while ((i = getopt_long(argc, argv, "n:l:z:Z:B:d:D:m:o:x:y:r:b:t:g:p:a:XfFqvP", long_options, NULL)) != -1) { + while ((i = getopt_long(argc, argv, "n:l:z:Z:B:d:D:m:o:x:y:r:b:t:g:p:a:XfFqvPL:", long_options, NULL)) != -1) { switch (i) { case 0: break; @@ -2179,6 +2193,32 @@ int main(int argc, char **argv) { layer = optarg; break; + case 'L': + { + char *cp = strchr(optarg, ':'); + if (cp == NULL || cp == optarg) { + fprintf(stderr, "%s: -L requires layername:file\n", argv[0]); + exit(EXIT_FAILURE); + } + struct source *src = malloc(sizeof(struct source)); + if (src == NULL) { + perror("Out of memory"); + exit(EXIT_FAILURE); + } + + src->layer = strdup(optarg); + src->file = strdup(cp + 1); + if (src->layer == NULL || src->file == NULL) { + perror("Out of memory"); + exit(EXIT_FAILURE); + } + src->layer[cp - optarg] = '\0'; + src->next = sources; + sources = src; + nsources++; + } + break; + case 'z': maxzoom = atoi(optarg); break; @@ -2315,7 +2355,7 @@ int main(int argc, char **argv) { break; default: - fprintf(stderr, "Usage: %s -o out.mbtiles [-n name] [-l layername] [-z maxzoom] [-Z minzoom] [-B basezoom] [-d detail] [-D lower-detail] [-m min-detail] [-x excluded-field ...] [-y included-field ...] [-X] [-r droprate] [-b buffer] [-t tmpdir] [-a rco] [-p sfkld] [-q] [-P] [file.json ...]\n", argv[0]); + fprintf(stderr, "Usage: %s -o out.mbtiles [-n name] [-l source] [-z maxzoom] [-Z minzoom] [-B basezoom] [-d detail] [-D lower-detail] [-m min-detail] [-x excluded-field ...] [-y included-field ...] [-X] [-r droprate] [-b buffer] [-t tmpdir] [-a rco] [-p sfkld] [-q] [-P] [file.json ...]\n", argv[0]); exit(EXIT_FAILURE); } } @@ -2368,7 +2408,27 @@ int main(int argc, char **argv) { sqlite3 *outdb = mbtiles_open(outdir, argv, forcetable); int ret = EXIT_SUCCESS; - ret = read_json(argc - optind, argv + optind, name ? name : outdir, layer, maxzoom, minzoom, basezoom, basezoom_marker_width, outdb, &exclude, &include, exclude_all, droprate, buffer, tmpdir, gamma, prevent, additional, read_parallel, forcetable); + for (i = optind; i < argc; i++) { + struct source *src = malloc(sizeof(struct source)); + if (src == NULL) { + perror("Out of memory"); + exit(EXIT_FAILURE); + } + + src->layer = NULL; + src->file = argv[i]; + src->next = sources; + sources = src; + nsources++; + } + + struct source *sourcelist[nsources]; + i = nsources - 1; + for (; sources != NULL; sources = sources->next) { + sourcelist[i--] = sources; + } + + ret = read_json(nsources, sourcelist, name ? name : outdir, layer, maxzoom, minzoom, basezoom, basezoom_marker_width, outdb, &exclude, &include, exclude_all, droprate, buffer, tmpdir, gamma, prevent, additional, read_parallel, forcetable); mbtiles_close(outdb, argv); diff --git a/man/tippecanoe.1 b/man/tippecanoe.1 index 762d3a2..bd7d2c8 100644 --- a/man/tippecanoe.1 +++ b/man/tippecanoe.1 @@ -59,48 +59,51 @@ it encounters. .SS Naming .RS .IP \(bu 2 -\-l \fIname\fP: Layer name (default "file" if source is file.json or output is file.mbtiles). If there are multiple input files -specified, the files are all merged into the single named layer. +\-l \fIname\fP or \-\-layer=\fIname\fP: Layer name (default "file" if source is file.json or output is file.mbtiles). If there are multiple input files +specified, the files are all merged into the single named layer, even if they try to specify individual names with \-L. .IP \(bu 2 -\-n \fIname\fP: Human\-readable name (default file.json) +\-L \fIname\fP:\fIfile.json\fP or \-\-named\-layer=\fIname\fP:\fIfile.json\fP: Specify layer names for individual files. If your shell supports it, you can use a subshell redirect like \-L \fIname\fP:<(cat dir/*.json) to specify a layer name for the output of streamed input. +.IP \(bu 2 +\-n \fIname\fP or \-\-name=\fIname\fP: Human\-readable name for the tileset (default file.json) .RE .SS File control .RS .IP \(bu 2 -\-o \fIfile\fP\&.mbtiles: Name the output file. +\-o \fIfile\fP\&.mbtiles or \-\-output=\fIfile\fP\&.mbtiles: Name the output file. .IP \(bu 2 -\-f: Delete the mbtiles file if it already exists instead of giving an error +\-f or \-\-force: Delete the mbtiles file if it already exists instead of giving an error .IP \(bu 2 -\-F: Proceed (without deleting existing data) if the metadata or tiles table already exists +\-F or \-\-allow\-existing: Proceed (without deleting existing data) if the metadata or tiles table already exists or if metadata fields can't be set .IP \(bu 2 -\-t \fIdirectory\fP: Put the temporary files in \fIdirectory\fP\&. +\-t \fIdirectory\fP or \-\-temporary\-directory=\fIdirectory\fP: Put the temporary files in \fIdirectory\fP\&. .IP \(bu 2 -\-P: Use multiple threads to read different parts of each input file at once. +\-P or \-\-read\-parallel: Use multiple threads to read different parts of each input file at once. This will only work if the input is line\-delimited JSON with each Feature on its -own line, because it knows nothing of the top\-level structure around the Features. +own line, because it knows nothing of the top\-level structure around the Features. Spurious "EOF" error +messages may result otherwise. Performance will be better if the input is a named file that can be mapped into memory rather than a stream that can only be read sequentially. .RE .SS Zoom levels and resolution .RS .IP \(bu 2 -\-z \fIzoom\fP: Maxzoom: the highest zoom level for which tiles are generated (default 14) +\-z \fIzoom\fP or \-\-maximum\-zoom=\fIzoom\fP: Maxzoom: the highest zoom level for which tiles are generated (default 14) .IP \(bu 2 -\-Z \fIzoom\fP: Minzoom: the lowest zoom level for which tiles are generated (default 0) +\-Z \fIzoom\fP or \-\-minimum\-zoom=\fIzoom\fP: Minzoom: the lowest zoom level for which tiles are generated (default 0) .IP \(bu 2 -\-B \fIzoom\fP: Base zoom, the level at and above which all points are included in the tiles (default maxzoom). +\-B \fIzoom\fP or \-\-base\-zoom=\fIzoom\fP: Base zoom, the level at and above which all points are included in the tiles (default maxzoom). If you use \-Bg, it will guess a zoom level that will keep at most 50,000 features in the densest tile. You can also specify a marker\-width with \-Bg\fIwidth\fP to allow fewer features in the densest tile to compensate for the larger marker, or \-Bf\fInumber\fP to allow at most \fInumber\fP features in the densest tile. .IP \(bu 2 -\-d \fIdetail\fP: Detail at max zoom level (default 12, for tile resolution of 4096) +\-d \fIdetail\fP or \-\-full\-detail=\fIdetail\fP: Detail at max zoom level (default 12, for tile resolution of 4096) .IP \(bu 2 -\-D \fIdetail\fP: Detail at lower zoom levels (default 12, for tile resolution of 4096) +\-D \fIdetail\fP or \-\-low\-detail=\fIdetail\fP: Detail at lower zoom levels (default 12, for tile resolution of 4096) .IP \(bu 2 -\-m \fIdetail\fP: Minimum detail that it will try if tiles are too big at regular detail (default 7) +\-m \fIdetail\fP or \-\-minimum\-detail=\fIdetail\fP: Minimum detail that it will try if tiles are too big at regular detail (default 7) .IP \(bu 2 -\-b \fIpixels\fP: Buffer size where features are duplicated from adjacent tiles. Units are "screen pixels"\-\-1/256th of the tile width or height. (default 5) +\-b \fIpixels\fP or \-\-buffer=\fIpixels\fP: Buffer size where features are duplicated from adjacent tiles. Units are "screen pixels"\-\-1/256th of the tile width or height. (default 5) .RE .PP All internal math is done in terms of a 32\-bit tile coordinate system, so 1/(2 of the size of Earth, @@ -109,51 +112,51 @@ resolution is obtained than by using a smaller \fImaxzoom\fP or \fIdetail\fP\&. .SS Properties .RS .IP \(bu 2 -\-x \fIname\fP: Exclude the named properties from all features +\-x \fIname\fP or \-\-exclude=\fIname\fP: Exclude the named properties from all features .IP \(bu 2 -\-y \fIname\fP: Include the named properties in all features, excluding all those not explicitly named +\-y \fIname\fP or \-\-include=\fIname\fP: Include the named properties in all features, excluding all those not explicitly named .IP \(bu 2 -\-X: Exclude all properties and encode only geometries +\-X or \-\-exclude\-all: Exclude all properties and encode only geometries .RE .SS Point simplification .RS .IP \(bu 2 -\-r \fIrate\fP: Rate at which dots are dropped at zoom levels below basezoom (default 2.5). +\-r \fIrate\fP or \-\-drop\fIrate=\fPrate_: Rate at which dots are dropped at zoom levels below basezoom (default 2.5). If you use \-rg, it will guess a drop rate that will keep at most 50,000 features in the densest tile. You can also specify a marker\-width with \-rg\fIwidth\fP to allow fewer features in the densest tile to compensate for the larger marker, or \-rf\fInumber\fP to allow at most \fInumber\fP features in the densest tile. .IP \(bu 2 -\-g \fIgamma\fP: Rate at which especially dense dots are dropped (default 0, for no effect). A gamma of 2 reduces the number of dots less than a pixel apart to the square root of their original number. +\-g \fIgamma\fP or \-\-gamma=\fIgamma\fP: Rate at which especially dense dots are dropped (default 0, for no effect). A gamma of 2 reduces the number of dots less than a pixel apart to the square root of their original number. .RE .SS Doing more .RS .IP \(bu 2 -\-ac: Coalesce adjacent line and polygon features that have the same properties +\-ac or \-\-coalesce: Coalesce adjacent line and polygon features that have the same properties .IP \(bu 2 -\-ar: Try reversing the directions of lines to make them coalesce and compress better +\-ar or \-\-reverse: Try reversing the directions of lines to make them coalesce and compress better .IP \(bu 2 -\-ao: Reorder features to put ones with the same properties in sequence, to try to get them to coalesce +\-ao or \-\-reorder: Reorder features to put ones with the same properties in sequence, to try to get them to coalesce .IP \(bu 2 -\-al: Let "dot" dropping at lower zooms apply to lines too +\-al or \-\-drop\-lines: Let "dot" dropping at lower zooms apply to lines too .RE .SS Doing less .RS .IP \(bu 2 -\-ps: Don't simplify lines +\-ps or \-\-no\-line\-simplification: Don't simplify lines .IP \(bu 2 -\-pS: Don't simplify lines at maxzoom (but do simplify at lower zooms) +\-pS or \-\-simplify\-only\-low\-zooms: Don't simplify lines at maxzoom (but do simplify at lower zooms) .IP \(bu 2 -\-pf: Don't limit tiles to 200,000 features +\-pf or \-\-no\-feature\-limit: Don't limit tiles to 200,000 features .IP \(bu 2 -\-pk: Don't limit tiles to 500K bytes +\-pk or \-\-no\-tile\-size\-limit: Don't limit tiles to 500K bytes .IP \(bu 2 -\-pd: Dynamically drop some fraction of features from large tiles to keep them under the 500K size limit. It will probably look ugly at the tile boundaries. +\-pd or \-\-force\-feature\-limit: Dynamically drop some fraction of features from large tiles to keep them under the 500K size limit. It will probably look ugly at the tile boundaries. .IP \(bu 2 -\-pi: Preserve the original input order of features as the drawing order instead of ordering geographically. (This is implemented as a restoration of the original order at the end, so that dot\-dropping is still geographic, which means it also undoes \-ao). +\-pi or \-\-preserve\-input\-order: Preserve the original input order of features as the drawing order instead of ordering geographically. (This is implemented as a restoration of the original order at the end, so that dot\-dropping is still geographic, which means it also undoes \-ao). .IP \(bu 2 -\-pp: Don't split complex polygons (over 700 vertices after simplification) into multiple features. +\-pp or \-\-no\-polygon\-splitting: Don't split complex polygons (over 700 vertices after simplification) into multiple features. .IP \(bu 2 -\-q: Work quietly instead of reporting progress +\-q or \-\-quiet: Work quietly instead of reporting progress .RE .SH Example .PP diff --git a/version.h b/version.h index ddb3937..686ebe0 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION "tippecanoe v1.9.5\n" +#define VERSION "tippecanoe v1.9.6\n"