Add --coalesce-fraction-as-needed and --coalesce-densest-as-needed

This commit is contained in:
Eric Fischer 2018-02-06 14:39:05 -08:00
parent 8fa5a5ff87
commit 2e5b597ca1
9 changed files with 12464 additions and 27 deletions

View File

@ -87,7 +87,7 @@ test: tippecanoe tippecanoe-decode $(addsuffix .check,$(TESTS)) raw-tiles-test p
# Work around Makefile and filename punctuation limits: _ for space, @ for :, % for /
%.json.check:
./tippecanoe -aD -f -o $@.mbtiles $(subst @,:,$(subst %,/,$(subst _, ,$(patsubst %.json.check,%,$(word 4,$(subst /, ,$@)))))) $(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.json) < /dev/null
./tippecanoe -a@ -f -o $@.mbtiles $(subst @,:,$(subst %,/,$(subst _, ,$(patsubst %.json.check,%,$(word 4,$(subst /, ,$@)))))) $(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.json) < /dev/null
./tippecanoe-decode $@.mbtiles > $@.out
cmp $@.out $(patsubst %.check,%,$@)
rm $@.out $@.mbtiles
@ -101,7 +101,7 @@ fewer-tests: tippecanoe tippecanoe-decode geobuf-test raw-tiles-test parallel-te
# XXX Use proper makefile rules instead of a for loop
%.json.checkbuf:
for i in $(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.json); do ./tippecanoe-json-tool -w $$i | ./node_modules/geobuf/bin/json2geobuf > $$i.geobuf; done
./tippecanoe -aD -f -o $@.mbtiles $(subst @,:,$(subst %,/,$(subst _, ,$(patsubst %.json.checkbuf,%,$(word 4,$(subst /, ,$@)))))) $(addsuffix .geobuf,$(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.json)) < /dev/null
./tippecanoe -a@ -f -o $@.mbtiles $(subst @,:,$(subst %,/,$(subst _, ,$(patsubst %.json.checkbuf,%,$(word 4,$(subst /, ,$@)))))) $(addsuffix .geobuf,$(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.json)) < /dev/null
./tippecanoe-decode $@.mbtiles | sed 's/checkbuf/check/g' > $@.out
cmp $@.out $(patsubst %.checkbuf,%,$@)
rm $@.out $@.mbtiles

View File

@ -212,6 +212,8 @@ tippecanoe -z5 -o filtered.mbtiles -j '{ "ne_10m_admin_0_countries": [ "all", [
* `-ad` or `--drop-fraction-as-needed`: Dynamically drop some fraction of features from each zoom level to keep large tiles under the 500K size limit. (This is like `-pd` but applies to the entire zoom level, not to each tile.)
* `-an` or `--drop-smallest-as-needed`: Dynamically drop the smallest features (physically smallest: the shortest lines or the smallest polygons) from each zoom level to keep large tiles under the 500K size limit. This option will not work for point features.
* `-aN` or `--coalesce-smallest-as-needed`: Dynamically combine the smallest features (physically smallest: the shortest lines or the smallest polygons) from each zoom level into other nearby features to keep large tiles under the 500K size limit. This option will not work for point features, and will probably not help very much with LineStrings. It is mostly intended for polygons, to maintain the full original area covered by polygons while still reducing the feature count somehow. The attributes of the small polygons are *not* preserved into the combined features, only their geometry.
* `-aD` or `--coalesce-densest-as-needed`: Dynamically combine the densest features from each zoom level into other nearby features to keep large tiles under the 500K size limit. (Again, mostly useful for polygons.)
* `-aS` or `--coalesce-fraction-as-needed`: Dynamically combine a fraction of features from each zoom level into other nearby features to keep large tiles under the 500K size limit. (Again, mostly useful for polygons.)
* `-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. (This is like `-ad` but applies to each tile individually, not to the entire zoom level.) You probably don't want to use this.
* `-aC` or `--cluster-densest-as-needed`: If a tile is too large, try to reduce its size by increasing the minimum spacing between features, and leaving one placeholder feature from each group. The remaining feature will be given a `"cluster": true` attribute to indicate that it represents a cluster and a `"point_count"` attribute to indicate the number of features that were clustered into it.

View File

@ -2305,10 +2305,12 @@ int main(int argc, char **argv) {
{"drop-polygons", no_argument, &additional[A_POLYGON_DROP], 1},
{"cluster-distance", required_argument, 0, 'K'},
{"Dropping a fraction of features to keep under tile size limits", 0, 0, 0},
{"Dropping or merging a fraction of features to keep under tile size limits", 0, 0, 0},
{"drop-densest-as-needed", no_argument, &additional[A_DROP_DENSEST_AS_NEEDED], 1},
{"drop-fraction-as-needed", no_argument, &additional[A_DROP_FRACTION_AS_NEEDED], 1},
{"drop-smallest-as-needed", no_argument, &additional[A_DROP_SMALLEST_AS_NEEDED], 1},
{"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},
{"coalesce-smallest-as-needed", no_argument, &additional[A_COALESCE_SMALLEST_AS_NEEDED], 1},
{"force-feature-limit", no_argument, &prevent[P_DYNAMIC_DROP], 1},
{"cluster-densest-as-needed", no_argument, &additional[A_CLUSTER_DENSEST_AS_NEEDED], 1},

View File

@ -247,6 +247,10 @@ compensate for the larger marker, or \fB\fC\-Bf\fR\fInumber\fP to allow at most
.IP \(bu 2
\fB\fC\-aN\fR or \fB\fC\-\-coalesce\-smallest\-as\-needed\fR: Dynamically combine the smallest features (physically smallest: the shortest lines or the smallest polygons) from each zoom level into other nearby features to keep large tiles under the 500K size limit. This option will not work for point features, and will probably not help very much with LineStrings. It is mostly intended for polygons, to maintain the full original area covered by polygons while still reducing the feature count somehow. The attributes of the small polygons are \fInot\fP preserved into the combined features, only their geometry.
.IP \(bu 2
\fB\fC\-aD\fR or \fB\fC\-\-coalesce\-densest\-as\-needed\fR: Dynamically combine the densest features from each zoom level into other nearby features to keep large tiles under the 500K size limit. (Again, mostly useful for polygons.)
.IP \(bu 2
\fB\fC\-aS\fR or \fB\fC\-\-coalesce\-fraction\-as\-needed\fR: Dynamically combine a fraction of features from each zoom level into other nearby features to keep large tiles under the 500K size limit. (Again, mostly useful for polygons.)
.IP \(bu 2
\fB\fC\-pd\fR or \fB\fC\-\-force\-feature\-limit\fR: 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. (This is like \fB\fC\-ad\fR but applies to each tile individually, not to the entire zoom level.) You probably don't want to use this.
.IP \(bu 2
\fB\fC\-aC\fR or \fB\fC\-\-cluster\-densest\-as\-needed\fR: If a tile is too large, try to reduce its size by increasing the minimum spacing between features, and leaving one placeholder feature from each group. The remaining feature will be given a \fB\fC"cluster": true\fR attribute to indicate that it represents a cluster and a \fB\fC"point_count"\fR attribute to indicate the number of features that were clustered into it.

View File

@ -5,7 +5,7 @@
#define A_REVERSE ((int) 'r')
#define A_REORDER ((int) 'o')
#define A_LINE_DROP ((int) 'l')
#define A_DEBUG_POLYGON ((int) 'D')
#define A_DEBUG_POLYGON ((int) '@')
#define A_POLYGON_DROP ((int) 'p')
#define A_DETECT_SHARED_BORDERS ((int) 'b')
#define A_PREFER_RADIX_SORT ((int) 'R')
@ -15,7 +15,9 @@
#define A_DROP_DENSEST_AS_NEEDED ((int) 's')
#define A_DROP_FRACTION_AS_NEEDED ((int) 'd')
#define A_DROP_SMALLEST_AS_NEEDED ((int) 'n')
#define A_COALESCE_DENSEST_AS_NEEDED ((int) 'S')
#define A_COALESCE_SMALLEST_AS_NEEDED ((int) 'N')
#define A_COALESCE_FRACTION_AS_NEEDED ((int) 'D')
#define A_GRID_LOW_ZOOMS ((int) 'L')
#define A_DETECT_WRAPAROUND ((int) 'w')
#define A_EXTEND_ZOOMS ((int) 'e')

View File

@ -491,7 +491,7 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf) {
long long midy = (sf.bbox[1] / 2 + sf.bbox[3] / 2) & ((1LL << 32) - 1);
bbox_index = encode(midx, midy);
if (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED] || additional[A_CALCULATE_FEATURE_DENSITY] || additional[A_INCREASE_GAMMA_AS_NEEDED] || sst->uses_gamma || cluster_distance != 0) {
if (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_COALESCE_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED] || additional[A_CALCULATE_FEATURE_DENSITY] || additional[A_INCREASE_GAMMA_AS_NEEDED] || sst->uses_gamma || cluster_distance != 0) {
sf.index = bbox_index;
} else {
sf.index = 0;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1601,14 +1601,18 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
if (sf.index - merge_previndex < mingap) {
continue;
}
}
if (additional[A_DROP_SMALLEST_AS_NEEDED]) {
} else if (additional[A_COALESCE_DENSEST_AS_NEEDED]) {
indices.push_back(sf.index);
if (sf.index - merge_previndex < mingap) {
coalesced_geometry.push_back(sf);
continue;
}
} else if (additional[A_DROP_SMALLEST_AS_NEEDED]) {
extents.push_back(sf.extent);
if (sf.extent + coalesced_area <= minextent && sf.t != VT_POINT) {
continue;
}
}
if (additional[A_COALESCE_SMALLEST_AS_NEEDED]) {
} else if (additional[A_COALESCE_SMALLEST_AS_NEEDED]) {
extents.push_back(sf.extent);
if (sf.extent + coalesced_area <= minextent) {
coalesced_geometry.push_back(sf);
@ -1616,17 +1620,6 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
}
}
if (coalesced_geometry.size() != 0) {
for (ssize_t i = coalesced_geometry.size() - 1; i >= 0; i--) {
if (coalesced_geometry[i].t == sf.t && coalesced_geometry[i].layer == sf.layer) {
for (size_t j = 0; j < coalesced_geometry[i].geometry.size(); j++) {
sf.geometry.push_back(coalesced_geometry[i].geometry[j]);
}
coalesced_geometry.erase(coalesced_geometry.begin() + i);
}
}
}
if (additional[A_CALCULATE_FEATURE_DENSITY]) {
// Gamma is always 1 for this calculation so there is a reasonable
// interpretation when no features are being dropped.
@ -1641,10 +1634,24 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
fraction_accum += fraction;
if (fraction_accum < 1) {
if (additional[A_COALESCE_FRACTION_AS_NEEDED]) {
coalesced_geometry.push_back(sf);
}
continue;
}
fraction_accum -= 1;
if (coalesced_geometry.size() != 0) {
for (ssize_t i = coalesced_geometry.size() - 1; i >= 0; i--) {
if (coalesced_geometry[i].t == sf.t && coalesced_geometry[i].layer == sf.layer) {
for (size_t j = 0; j < coalesced_geometry[i].geometry.size(); j++) {
sf.geometry.push_back(coalesced_geometry[i].geometry[j]);
}
coalesced_geometry.erase(coalesced_geometry.begin() + i);
}
}
}
bool reduced = false;
if (sf.t == VT_POLYGON) {
if (!prevent[P_TINY_POLYGON_REDUCTION] && !additional[A_GRID_LOW_ZOOMS]) {
@ -2020,7 +2027,7 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
}
line_detail++; // to keep it the same when the loop decrements it
continue;
} else if (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED]) {
} else if (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_COALESCE_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED]) {
mingap_fraction = mingap_fraction * max_tile_features / totalsize * 0.90;
unsigned long long mg = choose_mingap(indices, mingap_fraction);
if (mg <= mingap) {
@ -2051,12 +2058,12 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
line_detail++;
continue;
}
} else if (prevent[P_DYNAMIC_DROP] || additional[A_DROP_FRACTION_AS_NEEDED]) {
} else if (prevent[P_DYNAMIC_DROP] || additional[A_DROP_FRACTION_AS_NEEDED] || additional[A_COALESCE_FRACTION_AS_NEEDED]) {
fraction = fraction * max_tile_features / totalsize * 0.95;
if (!quiet) {
fprintf(stderr, "Going to try keeping %0.2f%% of the features to make it fit\n", fraction * 100);
}
if (additional[A_DROP_FRACTION_AS_NEEDED] && fraction < arg->fraction_out) {
if ((additional[A_DROP_FRACTION_AS_NEEDED] || additional[A_COALESCE_FRACTION_AS_NEEDED]) && fraction < arg->fraction_out) {
arg->fraction_out = fraction;
arg->still_dropping = true;
}
@ -2104,7 +2111,7 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
fprintf(stderr, "Going to try gamma of %0.3f to make it fit\n", gamma);
}
line_detail++; // to keep it the same when the loop decrements it
} else if (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED]) {
} else if (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_COALESCE_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED]) {
mingap_fraction = mingap_fraction * max_tile_size / compressed.size() * 0.90;
unsigned long long mg = choose_mingap(indices, mingap_fraction);
if (mg <= mingap) {
@ -2134,7 +2141,7 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
line_detail++;
continue;
}
} else if (prevent[P_DYNAMIC_DROP] || additional[A_DROP_FRACTION_AS_NEEDED]) {
} else if (prevent[P_DYNAMIC_DROP] || additional[A_DROP_FRACTION_AS_NEEDED] || additional[A_COALESCE_FRACTION_AS_NEEDED]) {
// The 95% is a guess to avoid too many retries
// and probably actually varies based on how much duplicated metadata there is
@ -2142,7 +2149,7 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
if (!quiet) {
fprintf(stderr, "Going to try keeping %0.2f%% of the features to make it fit\n", fraction * 100);
}
if (additional[A_DROP_FRACTION_AS_NEEDED] && fraction < arg->fraction_out) {
if ((additional[A_DROP_FRACTION_AS_NEEDED] || additional[A_COALESCE_FRACTION_AS_NEEDED]) && fraction < arg->fraction_out) {
arg->fraction_out = fraction;
arg->still_dropping = true;
}
@ -2418,7 +2425,7 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpo
int err = INT_MAX;
size_t start = 1;
if (additional[A_INCREASE_GAMMA_AS_NEEDED] || additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED] || additional[A_DROP_FRACTION_AS_NEEDED] || additional[A_DROP_SMALLEST_AS_NEEDED] || additional[A_COALESCE_SMALLEST_AS_NEEDED]) {
if (additional[A_INCREASE_GAMMA_AS_NEEDED] || additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_COALESCE_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED] || additional[A_DROP_FRACTION_AS_NEEDED] || additional[A_COALESCE_FRACTION_AS_NEEDED] || additional[A_DROP_SMALLEST_AS_NEEDED] || additional[A_COALESCE_SMALLEST_AS_NEEDED]) {
start = 0;
}