Merge pull request #127 from mapbox/maxzoom

Separate the concept of basezoom for dot-dropping from maxzoom for tiling
This commit is contained in:
Eric Fischer
2015-12-18 11:29:00 -08:00
7 changed files with 298 additions and 52 deletions
+6
View File
@@ -1,3 +1,9 @@
## 1.5.0
* Base zoom for dot-dropping can be specified independently of
maxzoom for tiling.
* Tippecanoe can calculate a base zoom and drop rate for you.
## 1.4.3 ## 1.4.3
* Encode numeric attributes as integers instead of floating point if possible * Encode numeric attributes as integers instead of floating point if possible
+13 -6
View File
@@ -66,9 +66,13 @@ Options
### Zoom levels and resolution ### Zoom levels and resolution
* -z _zoom_: Base (maxzoom) zoom level (default 14) * -z _zoom_: Maxzoom: the highest zoom level for which tiles are generated (default 14)
* -Z _zoom_: Lowest (minzoom) zoom level (default 0) * -Z _zoom_: Minzoom: the lowest zoom level for which tiles are generated (default 0)
* -d _detail_: Detail at base zoom level (default 12, for tile resolution of 4096) * -B _zoom_: 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*width* to allow fewer features in the densest tile to
compensate for the larger marker.
* -d _detail_: Detail at max zoom level (default 12, for tile resolution of 4096)
* -D _detail_: Detail at lower zoom levels (default 12, for tile resolution of 4096) * -D _detail_: Detail at lower zoom levels (default 12, for tile resolution of 4096)
* -m _detail_: Minimum detail that it will try if tiles are too big at regular detail (default 7) * -m _detail_: Minimum detail that it will try if tiles are too big at regular detail (default 7)
* -b _pixels_: Buffer size where features are duplicated from adjacent tiles. Units are "screen pixels"--1/256th of the tile width or height. (default 5) * -b _pixels_: Buffer size where features are duplicated from adjacent tiles. Units are "screen pixels"--1/256th of the tile width or height. (default 5)
@@ -81,7 +85,8 @@ Options
### Point simplification ### Point simplification
* -r _rate_: Rate at which dots are dropped at lower zoom levels (default 2.5) * -r _rate_: 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.
* -g _gamma_: 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 _gamma_: 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.
### Doing more ### Doing more
@@ -142,7 +147,7 @@ coordinated with the base zoom level and dot-dropping rate. You can use this she
calculate the appropriate marker-width at high zoom levels to match the fraction of dots calculate the appropriate marker-width at high zoom levels to match the fraction of dots
that were dropped at low zoom levels. that were dropped at low zoom levels.
If you used `-z` to change the base zoom level or `-r` to change the If you used `-B` or `-z` to change the base zoom level or `-r` to change the
dot-dropping rate, replace them in the `basezoom` and `rate` below. dot-dropping rate, replace them in the `basezoom` and `rate` below.
awk 'BEGIN { awk 'BEGIN {
@@ -167,7 +172,9 @@ Geometric simplifications
At every zoom level, line and polygon features are subjected to Douglas-Peucker At every zoom level, line and polygon features are subjected to Douglas-Peucker
simplification to the resolution of the tile. simplification to the resolution of the tile.
For point features, it drops 1/2.5 of the dots for each zoom level above the base. For point features, it drops 1/2.5 of the dots for each zoom level above the
point base zoom (which is normally the same as the `-z` max zoom, but can be
a different zoom specified with `-B` if you have precise but sparse data).
I don't know why 2.5 is the appropriate number, but the densities of many different I don't know why 2.5 is the appropriate number, but the densities of many different
data sets fall off at about this same rate. You can use -r to specify a different rate. data sets fall off at about this same rate. You can use -r to specify a different rate.
+251 -22
View File
@@ -437,7 +437,7 @@ long long addpool(struct memfile *poolfile, struct memfile *treefile, char *s, c
return off; return off;
} }
int serialize_geometry(json_object *geometry, json_object *properties, const char *reading, json_pull *jp, long long *seq, long long *metapos, long long *geompos, long long *indexpos, struct pool *exclude, struct pool *include, int exclude_all, FILE *metafile, FILE *geomfile, FILE *indexfile, struct memfile *poolfile, struct memfile *treefile, const char *fname, int maxzoom, int layer, double droprate, long long *file_bbox, json_object *tippecanoe) { int serialize_geometry(json_object *geometry, json_object *properties, const char *reading, json_pull *jp, long long *seq, long long *metapos, long long *geompos, long long *indexpos, struct pool *exclude, struct pool *include, int exclude_all, FILE *metafile, FILE *geomfile, FILE *indexfile, struct memfile *poolfile, struct memfile *treefile, const char *fname, int maxzoom, int basezoom, int layer, double droprate, long long *file_bbox, json_object *tippecanoe) {
json_object *geometry_type = json_hash_get(geometry, "type"); json_object *geometry_type = json_hash_get(geometry, "type");
if (geometry_type == NULL) { if (geometry_type == NULL) {
static int warned = 0; static int warned = 0;
@@ -564,19 +564,19 @@ int serialize_geometry(json_object *geometry, json_object *properties, const cha
serialize_byte(geomfile, VT_END, geompos, fname); serialize_byte(geomfile, VT_END, geompos, fname);
/* /*
* Note that minzoom for lines is the dimension * Note that feature_minzoom for lines is the dimension
* of the geometry in world coordinates, but * of the geometry in world coordinates, but
* for points is the lowest zoom level (in tiles, * for points is the lowest zoom level (in tiles,
* not in pixels) at which it should be drawn. * not in pixels) at which it should be drawn.
* *
* So a line that is too small for, say, z8 * So a line that is too small for, say, z8
* will have minzoom of 18 (if tile detail is 10), * will have feature_minzoom of 18 (if tile detail is 10),
* not 8. * not 8.
*/ */
int minzoom = 0; int feature_minzoom = 0;
if (mb_geometry[t] == VT_LINE) { if (mb_geometry[t] == VT_LINE) {
for (minzoom = 0; minzoom < 31; minzoom++) { for (feature_minzoom = 0; feature_minzoom < 31; feature_minzoom++) {
unsigned mask = 1 << (32 - (minzoom + 1)); unsigned mask = 1 << (32 - (feature_minzoom + 1));
if (((bbox[0] & mask) != (bbox[2] & mask)) || ((bbox[1] & mask) != (bbox[3] & mask))) { if (((bbox[0] & mask) != (bbox[2] & mask)) || ((bbox[1] & mask) != (bbox[3] & mask))) {
break; break;
@@ -587,10 +587,10 @@ int serialize_geometry(json_object *geometry, json_object *properties, const cha
if (r == 0) { if (r == 0) {
r = .00000001; r = .00000001;
} }
minzoom = maxzoom - floor(log(r) / -log(droprate)); feature_minzoom = basezoom - floor(log(r) / -log(droprate));
} }
serialize_byte(geomfile, minzoom, geompos, fname); serialize_byte(geomfile, feature_minzoom, geompos, fname);
struct index index; struct index index;
index.start = geomstart; index.start = geomstart;
@@ -626,7 +626,7 @@ int serialize_geometry(json_object *geometry, json_object *properties, const cha
return 1; return 1;
} }
void parse_json(json_pull *jp, const char *reading, long long *seq, long long *metapos, long long *geompos, long long *indexpos, struct pool *exclude, struct pool *include, int exclude_all, FILE *metafile, FILE *geomfile, FILE *indexfile, struct memfile *poolfile, struct memfile *treefile, char *fname, int maxzoom, int layer, double droprate, long long *file_bbox) { void parse_json(json_pull *jp, const char *reading, long long *seq, long long *metapos, long long *geompos, long long *indexpos, struct pool *exclude, struct pool *include, int exclude_all, FILE *metafile, FILE *geomfile, FILE *indexfile, struct memfile *poolfile, struct memfile *treefile, char *fname, int maxzoom, int basezoom, int layer, double droprate, long long *file_bbox) {
long long found_hashes = 0; long long found_hashes = 0;
long long found_features = 0; long long found_features = 0;
long long found_geometries = 0; long long found_geometries = 0;
@@ -691,7 +691,7 @@ void parse_json(json_pull *jp, const char *reading, long long *seq, long long *m
} }
found_geometries++; found_geometries++;
serialize_geometry(j, NULL, reading, jp, seq, metapos, geompos, indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, maxzoom, layer, droprate, file_bbox, NULL); serialize_geometry(j, NULL, reading, jp, seq, metapos, geompos, indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, maxzoom, basezoom, layer, droprate, file_bbox, NULL);
json_free(j); json_free(j);
continue; continue;
} }
@@ -726,19 +726,24 @@ void parse_json(json_pull *jp, const char *reading, long long *seq, long long *m
if (geometries != NULL) { if (geometries != NULL) {
int g; int g;
for (g = 0; g < geometries->length; g++) { for (g = 0; g < geometries->length; g++) {
serialize_geometry(geometries->array[g], properties, reading, jp, seq, metapos, geompos, indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, maxzoom, layer, droprate, file_bbox, tippecanoe); serialize_geometry(geometries->array[g], properties, reading, jp, seq, metapos, geompos, indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, maxzoom, basezoom, layer, droprate, file_bbox, tippecanoe);
} }
} else { } else {
serialize_geometry(geometry, properties, reading, jp, seq, metapos, geompos, indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, maxzoom, layer, droprate, file_bbox, tippecanoe); serialize_geometry(geometry, properties, reading, jp, seq, metapos, geompos, indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, maxzoom, basezoom, layer, droprate, file_bbox, tippecanoe);
} }
json_free(j); json_free(j);
/* XXX check for any non-features in the outer object */ /* XXX check for any non-features in the outer object */
} }
if (!quiet) {
fprintf(stderr, " \r");
// (stderr, "Read 10000.00 million features\r", *seq / 1000000.0);
}
} }
int read_json(int argc, char **argv, char *fname, const char *layername, int maxzoom, int minzoom, sqlite3 *outdb, struct pool *exclude, struct pool *include, int exclude_all, double droprate, int buffer, const char *tmpdir, double gamma, char *prevent, char *additional) { 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, char *prevent, char *additional) {
int ret = EXIT_SUCCESS; int ret = EXIT_SUCCESS;
char metaname[strlen(tmpdir) + strlen("/meta.XXXXXXXX") + 1]; char metaname[strlen(tmpdir) + strlen("/meta.XXXXXXXX") + 1];
@@ -866,7 +871,7 @@ int read_json(int argc, char **argv, char *fname, const char *layername, int max
layer = source; layer = source;
} }
parse_json(jp, reading, &seq, &metapos, &geompos, &indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, maxzoom, layer, droprate, file_bbox); parse_json(jp, reading, &seq, &metapos, &geompos, &indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, maxzoom, basezoom, layer, droprate, file_bbox);
json_end(jp); json_end(jp);
fclose(fp); fclose(fp);
@@ -1035,13 +1040,200 @@ int read_json(int argc, char **argv, char *fname, const char *layername, int max
close(indexfd); close(indexfd);
} }
/* Copy geometries to a new file in index order */
indexfd = open(indexname, O_RDONLY); indexfd = open(indexname, O_RDONLY);
if (indexfd < 0) { if (indexfd < 0) {
perror("reopen sorted index"); perror("reopen sorted index");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (basezoom < 0 || droprate < 0) {
struct index *map = mmap(NULL, indexpos, PROT_READ, MAP_PRIVATE, indexfd, 0);
if (map == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
#define MAX_ZOOM 30
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];
{
int i;
for (i = 0; i <= MAX_ZOOM; i++) {
tile[i].x = tile[i].y = tile[i].count = tile[i].fullcount = tile[i].gap = tile[i].previndex = 0;
max[i].x = max[i].y = max[i].count = max[i].fullcount = 0;
}
}
long long indices = indexpos / sizeof(struct index);
long long i;
for (i = 0; i < indices; i++) {
unsigned xx, yy;
decode(map[i].index, &xx, &yy);
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++;
// Keep in sync with write_tile()
if (gamma > 0) {
if (tile[z].gap > 0) {
if (map[i].index == tile[z].previndex) {
continue; // Exact duplicate: can't fulfil the gap requirement
}
if (exp(log((map[i].index - tile[z].previndex) / scale) * gamma) >= tile[z].gap) {
// Dot is further from the previous than the nth root of the gap,
// so produce it, and choose a new gap at the next point.
tile[z].gap = 0;
} else {
continue;
}
} else {
tile[z].gap = (map[i].index - tile[z].previndex) / scale;
if (tile[z].gap == 0) {
continue; // Exact duplicate: skip
} else if (tile[z].gap < 1) {
continue; // Narrow dot spacing: need to stretch out
} else {
tile[z].gap = 0; // Wider spacing than minimum: so pass through unchanged
}
}
tile[z].previndex = map[i].index;
}
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);
}
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);
}
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));
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);
}
}
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);
}
}
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);
} 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));
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);
}
}
}
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) {
fprintf(stderr, "With gamma, effective base zoom is 0, so no effective drop rate\n");
} 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));
fprintf(stderr, "With gamma, effective base zoom of %d, effective drop rate of %f\n", effective, eff_drop);
}
}
munmap(map, indexpos);
}
/* Copy geometries to a new file in index order */
struct index *index_map = mmap(NULL, indexpos, PROT_READ, MAP_PRIVATE, indexfd, 0); struct index *index_map = mmap(NULL, indexpos, PROT_READ, MAP_PRIVATE, indexfd, 0);
if (index_map == MAP_FAILED) { if (index_map == MAP_FAILED) {
perror("mmap index"); perror("mmap index");
@@ -1138,7 +1330,7 @@ int read_json(int argc, char **argv, char *fname, const char *layername, int max
fprintf(stderr, "%lld features, %lld bytes of geometry, %lld bytes of metadata, %lld bytes of string pool\n", seq, (long long) geomst.st_size, (long long) metast.st_size, poolfile->off); fprintf(stderr, "%lld features, %lld bytes of geometry, %lld bytes of metadata, %lld bytes of string pool\n", seq, (long long) geomst.st_size, (long long) metast.st_size, poolfile->off);
} }
int written = traverse_zooms(fd, size, meta, stringpool, file_keys, &midx, &midy, layernames, maxzoom, minzoom, outdb, droprate, buffer, fname, tmpdir, gamma, nlayers, prevent, additional, full_detail, low_detail, min_detail); int written = traverse_zooms(fd, size, meta, stringpool, file_keys, &midx, &midy, layernames, maxzoom, minzoom, basezoom, outdb, droprate, buffer, fname, tmpdir, gamma, nlayers, prevent, additional, full_detail, low_detail, min_detail);
if (maxzoom != written) { if (maxzoom != written) {
fprintf(stderr, "\n\n\n*** NOTE TILES ONLY COMPLETE THROUGH ZOOM %d ***\n\n\n", written); fprintf(stderr, "\n\n\n*** NOTE TILES ONLY COMPLETE THROUGH ZOOM %d ***\n\n\n", written);
@@ -1179,7 +1371,7 @@ int read_json(int argc, char **argv, char *fname, const char *layername, int max
if (file_bbox[1] < 0) { if (file_bbox[1] < 0) {
file_bbox[1] = 0; file_bbox[1] = 0;
} }
if (file_bbox[3] < (1LL << 32) - 1) { if (file_bbox[3] > (1LL << 32) - 1) {
file_bbox[3] = (1LL << 32) - 1; file_bbox[3] = (1LL << 32) - 1;
} }
@@ -1224,6 +1416,8 @@ int main(int argc, char **argv) {
char *outdir = NULL; char *outdir = NULL;
int maxzoom = 14; int maxzoom = 14;
int minzoom = 0; int minzoom = 0;
int basezoom = -1;
double basezoom_marker_width = 1;
int force = 0; int force = 0;
double droprate = 2.5; double droprate = 2.5;
double gamma = 0; double gamma = 0;
@@ -1242,7 +1436,7 @@ int main(int argc, char **argv) {
additional[i] = 0; additional[i] = 0;
} }
while ((i = getopt(argc, argv, "l:n:z:Z:d:D:m:o:x:y:r:b:fXt:g:p:vqa:")) != -1) { while ((i = getopt(argc, argv, "l:n:z:Z:d:D:m:o:x:y:r:b:fXt:g:p:vqa:B:")) != -1) {
switch (i) { switch (i) {
case 'n': case 'n':
name = optarg; name = optarg;
@@ -1260,6 +1454,26 @@ int main(int argc, char **argv) {
minzoom = atoi(optarg); minzoom = atoi(optarg);
break; break;
case 'B':
if (strcmp(optarg, "g") == 0) {
basezoom = -2;
basezoom_marker_width = 1;
} else if (optarg[0] == 'g') {
basezoom = -2;
basezoom_marker_width = atof(optarg + 1);
if (basezoom_marker_width == 0) {
fprintf(stderr, "%s: Must specify marker width >0 with -Bg\n", argv[0]);
exit(EXIT_FAILURE);
}
} else {
basezoom = atoi(optarg);
if (basezoom == 0 && strcmp(optarg, "0") != 0) {
fprintf(stderr, "%s: Couldn't understand -B%s\n", argv[0], optarg);
exit(EXIT_FAILURE);
}
}
break;
case 'd': case 'd':
full_detail = atoi(optarg); full_detail = atoi(optarg);
break; break;
@@ -1290,7 +1504,11 @@ int main(int argc, char **argv) {
break; break;
case 'r': case 'r':
droprate = atof(optarg); if (strcmp(optarg, "g") == 0) {
droprate = -2;
} else {
droprate = atof(optarg);
}
break; break;
case 'b': case 'b':
@@ -1332,7 +1550,7 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
default: default:
fprintf(stderr, "Usage: %s -o out.mbtiles [-n name] [-l layername] [-z maxzoom] [-Z minzoom] [-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] [file.json ...]\n", argv[0]); 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] [file.json ...]\n", argv[0]);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
@@ -1342,6 +1560,10 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (basezoom == -1) {
basezoom = maxzoom;
}
if (full_detail <= 0) { if (full_detail <= 0) {
full_detail = 12; full_detail = 12;
} }
@@ -1353,6 +1575,13 @@ int main(int argc, char **argv) {
geometry_scale = 32 - (full_detail + maxzoom); geometry_scale = 32 - (full_detail + maxzoom);
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");
}
if (outdir == NULL) { if (outdir == NULL) {
fprintf(stderr, "%s: must specify -o out.mbtiles\n", argv[0]); fprintf(stderr, "%s: must specify -o out.mbtiles\n", argv[0]);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@@ -1365,7 +1594,7 @@ int main(int argc, char **argv) {
sqlite3 *outdb = mbtiles_open(outdir, argv); sqlite3 *outdb = mbtiles_open(outdir, argv);
int ret = EXIT_SUCCESS; int ret = EXIT_SUCCESS;
ret = read_json(argc - optind, argv + optind, name ? name : outdir, layer, maxzoom, minzoom, outdb, &exclude, &include, exclude_all, droprate, buffer, tmpdir, gamma, prevent, additional); 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);
mbtiles_close(outdb, argv); mbtiles_close(outdb, argv);
+14 -6
View File
@@ -71,11 +71,16 @@ specified, the files are all merged into the single named layer.
.SS Zoom levels and resolution .SS Zoom levels and resolution
.RS .RS
.IP \(bu 2 .IP \(bu 2
\-z \fIzoom\fP: Base (maxzoom) zoom level (default 14) \-z \fIzoom\fP: Maxzoom: the highest zoom level for which tiles are generated (default 14)
.IP \(bu 2 .IP \(bu 2
\-Z \fIzoom\fP: Lowest (minzoom) zoom level (default 0) \-Z \fIzoom\fP: Minzoom: the lowest zoom level for which tiles are generated (default 0)
.IP \(bu 2 .IP \(bu 2
\-d \fIdetail\fP: Detail at base zoom level (default 12, for tile resolution of 4096) \-B \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.
.IP \(bu 2
\-d \fIdetail\fP: Detail at max zoom level (default 12, for tile resolution of 4096)
.IP \(bu 2 .IP \(bu 2
\-D \fIdetail\fP: Detail at lower zoom levels (default 12, for tile resolution of 4096) \-D \fIdetail\fP: Detail at lower zoom levels (default 12, for tile resolution of 4096)
.IP \(bu 2 .IP \(bu 2
@@ -95,7 +100,8 @@ specified, the files are all merged into the single named layer.
.SS Point simplification .SS Point simplification
.RS .RS
.IP \(bu 2 .IP \(bu 2
\-r \fIrate\fP: Rate at which dots are dropped at lower zoom levels (default 2.5) \-r \fIrate\fP: 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.
.IP \(bu 2 .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: 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 .RE
@@ -168,7 +174,7 @@ coordinated with the base zoom level and dot\-dropping rate. You can use this sh
calculate the appropriate marker\-width at high zoom levels to match the fraction of dots calculate the appropriate marker\-width at high zoom levels to match the fraction of dots
that were dropped at low zoom levels. that were dropped at low zoom levels.
.PP .PP
If you used \fB\fC\-z\fR to change the base zoom level or \fB\fC\-r\fR to change the If you used \fB\fC\-B\fR or \fB\fC\-z\fR to change the base zoom level or \fB\fC\-r\fR to change the
dot\-dropping rate, replace them in the \fB\fCbasezoom\fR and \fB\fCrate\fR below. dot\-dropping rate, replace them in the \fB\fCbasezoom\fR and \fB\fCrate\fR below.
.PP .PP
.RS .RS
@@ -193,7 +199,9 @@ awk 'BEGIN {
At every zoom level, line and polygon features are subjected to Douglas\-Peucker At every zoom level, line and polygon features are subjected to Douglas\-Peucker
simplification to the resolution of the tile. simplification to the resolution of the tile.
.PP .PP
For point features, it drops 1/2.5 of the dots for each zoom level above the base. For point features, it drops 1/2.5 of the dots for each zoom level above the
point base zoom (which is normally the same as the \fB\fC\-z\fR max zoom, but can be
a different zoom specified with \fB\fC\-B\fR if you have precise but sparse data).
I don't know why 2.5 is the appropriate number, but the densities of many different I don't know why 2.5 is the appropriate number, but the densities of many different
data sets fall off at about this same rate. You can use \-r to specify a different rate. data sets fall off at about this same rate. You can use \-r to specify a different rate.
.PP .PP
+12 -16
View File
@@ -362,8 +362,8 @@ struct sll {
} }
}; };
void rewrite(drawvec &geom, int z, int nextzoom, int file_maxzoom, long long *bbox, unsigned tx, unsigned ty, int buffer, int line_detail, int *within, long long *geompos, FILE **geomfile, const char *fname, signed char t, int layer, long long metastart, signed char feature_minzoom, int child_shards, int max_zoom_increment, long long seq, int tippecanoe_minzoom, int tippecanoe_maxzoom) { void rewrite(drawvec &geom, int z, int nextzoom, int maxzoom, long long *bbox, unsigned tx, unsigned ty, int buffer, int line_detail, int *within, long long *geompos, FILE **geomfile, const char *fname, signed char t, int layer, long long metastart, signed char feature_minzoom, int child_shards, int max_zoom_increment, long long seq, int tippecanoe_minzoom, int tippecanoe_maxzoom) {
if (geom.size() > 0 && nextzoom <= file_maxzoom) { if (geom.size() > 0 && nextzoom <= maxzoom) {
int xo, yo; int xo, yo;
int span = 1 << (nextzoom - z); int span = 1 << (nextzoom - z);
@@ -463,7 +463,7 @@ void rewrite(drawvec &geom, int z, int nextzoom, int file_maxzoom, long long *bb
} }
} }
long long write_tile(char **geoms, char *metabase, char *stringpool, int z, unsigned tx, unsigned ty, int detail, int min_detail, int basezoom, struct pool **file_keys, char **layernames, sqlite3 *outdb, double droprate, int buffer, const char *fname, FILE **geomfile, int file_minzoom, int file_maxzoom, double todo, char *geomstart, volatile long long *along, double gamma, int nlayers, char *prevent, char *additional, int child_shards) { long long write_tile(char **geoms, char *metabase, char *stringpool, int z, unsigned tx, unsigned ty, int detail, int min_detail, int basezoom, struct pool **file_keys, char **layernames, sqlite3 *outdb, double droprate, int buffer, const char *fname, FILE **geomfile, int minzoom, int maxzoom, double todo, char *geomstart, volatile long long *along, double gamma, int nlayers, char *prevent, char *additional, int child_shards) {
int line_detail; int line_detail;
double fraction = 1; double fraction = 1;
@@ -481,9 +481,9 @@ long long write_tile(char **geoms, char *metabase, char *stringpool, int z, unsi
} }
int nextzoom = z + 1; int nextzoom = z + 1;
if (nextzoom < file_minzoom) { if (nextzoom < minzoom) {
if (z + max_zoom_increment > file_minzoom) { if (z + max_zoom_increment > minzoom) {
nextzoom = file_minzoom; nextzoom = minzoom;
} else { } else {
nextzoom = z + max_zoom_increment; nextzoom = z + max_zoom_increment;
} }
@@ -566,7 +566,7 @@ long long write_tile(char **geoms, char *metabase, char *stringpool, int z, unsi
signed char feature_minzoom; signed char feature_minzoom;
deserialize_byte(geoms, &feature_minzoom); deserialize_byte(geoms, &feature_minzoom);
double progress = floor((((*geoms - geomstart + *along) / (double) todo) + z) / (file_maxzoom + 1) * 1000) / 10; double progress = floor((((*geoms - geomstart + *along) / (double) todo) + z) / (maxzoom + 1) * 1000) / 10;
if (progress >= oprogress + 0.1) { if (progress >= oprogress + 0.1) {
if (!quiet) { if (!quiet) {
fprintf(stderr, " %3.1f%% %d/%u/%u \r", progress, z, tx, ty); fprintf(stderr, " %3.1f%% %d/%u/%u \r", progress, z, tx, ty);
@@ -630,10 +630,10 @@ long long write_tile(char **geoms, char *metabase, char *stringpool, int z, unsi
} }
if (line_detail == detail && fraction == 1) { /* only write out the next zoom once, even if we retry */ if (line_detail == detail && fraction == 1) { /* only write out the next zoom once, even if we retry */
rewrite(geom, z, nextzoom, file_maxzoom, bbox, tx, ty, buffer, line_detail, within, geompos, geomfile, fname, t, layer, metastart, feature_minzoom, child_shards, max_zoom_increment, original_seq, tippecanoe_minzoom, tippecanoe_maxzoom); rewrite(geom, z, nextzoom, maxzoom, bbox, tx, ty, buffer, line_detail, within, geompos, geomfile, fname, t, layer, metastart, feature_minzoom, child_shards, max_zoom_increment, original_seq, tippecanoe_minzoom, tippecanoe_maxzoom);
} }
if (z < file_minzoom) { if (z < minzoom) {
continue; continue;
} }
@@ -906,8 +906,6 @@ struct write_tile_args {
int buffer; int buffer;
const char *fname; const char *fname;
FILE **geomfile; FILE **geomfile;
int file_minzoom;
int file_maxzoom;
double todo; double todo;
volatile long long *along; volatile long long *along;
double gamma; double gamma;
@@ -963,7 +961,7 @@ void *run_thread(void *vargs) {
// fprintf(stderr, "%d/%u/%u\n", z, x, y); // fprintf(stderr, "%d/%u/%u\n", z, x, y);
long long len = write_tile(&geom, arg->metabase, arg->stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->maxzoom, arg->file_keys, arg->layernames, arg->outdb, arg->droprate, arg->buffer, arg->fname, arg->geomfile, arg->minzoom, arg->maxzoom, arg->todo, geomstart, arg->along, arg->gamma, arg->nlayers, arg->prevent, arg->additional, arg->child_shards); long long len = write_tile(&geom, arg->metabase, arg->stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->basezoom, arg->file_keys, arg->layernames, arg->outdb, arg->droprate, arg->buffer, arg->fname, arg->geomfile, arg->minzoom, arg->maxzoom, arg->todo, geomstart, arg->along, arg->gamma, arg->nlayers, arg->prevent, arg->additional, arg->child_shards);
if (len < 0) { if (len < 0) {
int *err = (int *) malloc(sizeof(int)); int *err = (int *) malloc(sizeof(int));
@@ -999,7 +997,7 @@ void *run_thread(void *vargs) {
return NULL; return NULL;
} }
int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpool, struct pool **file_keys, unsigned *midx, unsigned *midy, char **layernames, int maxzoom, int minzoom, sqlite3 *outdb, double droprate, int buffer, const char *fname, const char *tmpdir, double gamma, int nlayers, char *prevent, char *additional, int full_detail, int low_detail, int min_detail) { int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpool, struct pool **file_keys, unsigned *midx, unsigned *midy, char **layernames, int maxzoom, int minzoom, int basezoom, sqlite3 *outdb, double droprate, int buffer, const char *fname, const char *tmpdir, double gamma, int nlayers, char *prevent, char *additional, int full_detail, int low_detail, int min_detail) {
int i; int i;
for (i = 0; i <= maxzoom; i++) { for (i = 0; i <= maxzoom; i++) {
long long most = 0; long long most = 0;
@@ -1098,7 +1096,7 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpo
args[thread].metabase = metabase; args[thread].metabase = metabase;
args[thread].stringpool = stringpool; args[thread].stringpool = stringpool;
args[thread].min_detail = min_detail; args[thread].min_detail = min_detail;
args[thread].basezoom = maxzoom; // XXX rename? args[thread].basezoom = basezoom;
args[thread].file_keys = file_keys; // locked with var_lock args[thread].file_keys = file_keys; // locked with var_lock
args[thread].layernames = layernames; args[thread].layernames = layernames;
args[thread].outdb = outdb; // locked with db_lock args[thread].outdb = outdb; // locked with db_lock
@@ -1106,8 +1104,6 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpo
args[thread].buffer = buffer; args[thread].buffer = buffer;
args[thread].fname = fname; args[thread].fname = fname;
args[thread].geomfile = sub + thread * (TEMP_FILES / threads); args[thread].geomfile = sub + thread * (TEMP_FILES / threads);
args[thread].file_minzoom = minzoom;
args[thread].file_maxzoom = maxzoom;
args[thread].todo = todo; args[thread].todo = todo;
args[thread].along = &along; // locked with var_lock args[thread].along = &along; // locked with var_lock
args[thread].gamma = gamma; args[thread].gamma = gamma;
+1 -1
View File
@@ -27,7 +27,7 @@ struct pool_val *deserialize_string(char **f, struct pool *p, int type);
long long write_tile(char **geom, char *metabase, char *stringpool, unsigned *file_bbox, int z, unsigned x, unsigned y, int detail, int min_detail, int basezoom, struct pool **file_keys, char **layernames, sqlite3 *outdb, double droprate, int buffer, const char *fname, FILE **geomfile, int file_minzoom, int file_maxzoom, double todo, char *geomstart, long long along, double gamma, int nlayers, char *prevent, char *additional); long long write_tile(char **geom, char *metabase, char *stringpool, unsigned *file_bbox, int z, unsigned x, unsigned y, int detail, int min_detail, int basezoom, struct pool **file_keys, char **layernames, sqlite3 *outdb, double droprate, int buffer, const char *fname, FILE **geomfile, int file_minzoom, int file_maxzoom, double todo, char *geomstart, long long along, double gamma, int nlayers, char *prevent, char *additional);
int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpool, struct pool **file_keys, unsigned *midx, unsigned *midy, char **layernames, int maxzoom, int minzoom, sqlite3 *outdb, double droprate, int buffer, const char *fname, const char *tmpdir, double gamma, int nlayers, char *prevent, char *additional, int full_detail, int low_detail, int min_detail); int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpool, struct pool **file_keys, unsigned *midx, unsigned *midy, char **layernames, int maxzoom, int minzoom, int basezoom, sqlite3 *outdb, double droprate, int buffer, const char *fname, const char *tmpdir, double gamma, int nlayers, char *prevent, char *additional, int full_detail, int low_detail, int min_detail);
extern unsigned initial_x, initial_y; extern unsigned initial_x, initial_y;
extern int geometry_scale; extern int geometry_scale;
+1 -1
View File
@@ -1 +1 @@
#define VERSION "tippecanoe v1.4.3\n" #define VERSION "tippecanoe v1.5.0\n"