Add an option to retain extra coordinate precision at maxzoom

This commit is contained in:
Erica Fischer
2022-08-17 16:16:20 -07:00
parent 4ea8a37611
commit 2559fb4fd7
7 changed files with 1060 additions and 13 deletions
+1 -1
View File
@@ -351,7 +351,7 @@ layer-json-test:
prep-test: $(TESTS)
tests/%.json: Makefile tippecanoe tippecanoe-decode
./tippecanoe -q -a@ -f -o $@.check.mbtiles $(subst @,:,$(subst %,/,$(subst _, ,$(patsubst %.json,%,$(word 4,$(subst /, ,$@)))))) $(foreach suffix,$(suffixes),$(sort $(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.$(suffix))))
./tippecanoe -aE -q -a@ -f -o $@.check.mbtiles $(subst @,:,$(subst %,/,$(subst _, ,$(patsubst %.json,%,$(word 4,$(subst /, ,$@)))))) $(foreach suffix,$(suffixes),$(sort $(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.$(suffix))))
./tippecanoe-decode -x generator $@.check.mbtiles > $@
cmp $(patsubst %.check,%,$@) $@
rm $@.check.mbtiles
+1
View File
@@ -383,6 +383,7 @@ zoom level | precision (ft) | precision (m) | map scale
* `-d` _detail_ or `--full-detail=`_detail_: Detail at max zoom level (default 12, for tile resolution of 2^12=4096)
* `-D` _detail_ or `--low-detail=`_detail_: Detail at lower zoom levels (default 12, for tile resolution of 2^12=4096)
* `-m` _detail_ or `--minimum-detail=`_detail_: Minimum detail that it will try if tiles are too big at regular detail (default 7)
* `-aE` or `--extra-detail`: Generate tiles with even more detail than the "full" detail at the max zoom level, to maximize location precision. These tiles may not work with some rendering software that internally limits detail to 12 or 13. The extra detail does not affect the choice of maxzoom guessing, the amount of simplification, or the "tiny polygon" threshold as `--full-detail` does. The tiles should look the same as they did without it, except that they will be more precise when overzoomed.
All internal math is done in terms of a 32-bit tile coordinate system, so 1/(2^32) of the size of Earth,
or about 1cm, is the smallest distinguishable distance. If _maxzoom_ + _detail_ > 32, no additional
+1
View File
@@ -2653,6 +2653,7 @@ int main(int argc, char **argv) {
{"full-detail", required_argument, 0, 'd'},
{"low-detail", required_argument, 0, 'D'},
{"minimum-detail", required_argument, 0, 'm'},
{"extra-detail", no_argument, &additional[A_EXTRA_DETAIL], 1},
{"Filtering feature attributes", 0, 0, 0},
{"exclude", required_argument, 0, 'x'},
+2
View File
@@ -475,6 +475,8 @@ zoom level precision (ft) precision (m) map scale
\fB\fC\-D\fR \fIdetail\fP or \fB\fC\-\-low\-detail=\fR\fIdetail\fP: Detail at lower zoom levels (default 12, for tile resolution of 2
.IP \(bu 2
\fB\fC\-m\fR \fIdetail\fP or \fB\fC\-\-minimum\-detail=\fR\fIdetail\fP: Minimum detail that it will try if tiles are too big at regular detail (default 7)
.IP \(bu 2
\fB\fC\-aE\fR or \fB\fC\-\-extra\-detail\fR: Generate tiles with even more detail than the "full" detail at the max zoom level, to maximize location precision. These tiles may not work with some rendering software that internally limits detail to 12 or 13. The extra detail does not affect the choice of maxzoom guessing, the amount of simplification, or the "tiny polygon" threshold as \fB\fC\-\-full\-detail\fR does. The tiles should look the same as they did without it, except that they will be more precise when overzoomed.
.RE
.PP
All internal math is done in terms of a 32\-bit tile coordinate system, so 1/(2 of the size of Earth,
+1
View File
@@ -25,6 +25,7 @@
#define A_GENERATE_IDS ((int) 'i')
#define A_CONVERT_NUMERIC_IDS ((int) 'I')
#define A_HILBERT ((int) 'h')
#define A_EXTRA_DETAIL ((int) 'E')
#define P_SIMPLIFY ((int) 's')
#define P_SIMPLIFY_LOW ((int) 'S')
File diff suppressed because one or more lines are too long
+22 -12
View File
@@ -374,6 +374,7 @@ struct partial {
bool reduced = 0;
int z = 0;
int line_detail = 0;
int extra_detail = 0;
int maxzoom = 0;
double spacing = 0;
double simplification = 0;
@@ -452,6 +453,7 @@ void *partial_feature_worker(void *v) {
signed char t = (*partials)[i].t;
int z = (*partials)[i].z;
int line_detail = (*partials)[i].line_detail;
int extra_detail = (*partials)[i].extra_detail;
int maxzoom = (*partials)[i].maxzoom;
if (additional[A_GRID_LOW_ZOOMS] && z < maxzoom) {
@@ -466,6 +468,7 @@ void *partial_feature_worker(void *v) {
if ((t == VT_LINE || t == VT_POLYGON) && !(prevent[P_SIMPLIFY] || (z == maxzoom && prevent[P_SIMPLIFY_LOW]) || (z < maxzoom && additional[A_GRID_LOW_ZOOMS]))) {
if (1 /* !reduced */) { // XXX why did this not simplify if reduced?
if (t == VT_LINE) {
// continues to deduplicate to line_detail even if we have extra detail
geom = remove_noop(geom, t, 32 - z - line_detail);
}
@@ -475,6 +478,7 @@ void *partial_feature_worker(void *v) {
}
if (!already_marked) {
// continues to simplify to line_detail even if we have extra detail
drawvec ngeom = simplify_lines(geom, z, line_detail, !(prevent[P_CLIPPING] || prevent[P_DUPLICATION]), (*partials)[i].simplification, t == VT_POLYGON ? 4 : 0, *(a->shared_nodes));
if (t != VT_POLYGON || ngeom.size() >= 3) {
@@ -484,17 +488,11 @@ void *partial_feature_worker(void *v) {
}
}
#if 0
if (t == VT_LINE && z != basezoom) {
geom = shrink_lines(geom, z, line_detail, basezoom, &along);
}
#endif
if (t == VT_LINE && additional[A_REVERSE]) {
geom = reorder_lines(geom);
}
to_tile_scale(geom, z, line_detail);
to_tile_scale(geom, z, extra_detail);
std::vector<drawvec> geoms;
geoms.push_back(geom);
@@ -511,7 +509,8 @@ void *partial_feature_worker(void *v) {
if (geoms[g].size() < 3) {
if (area > 0) {
geoms[g] = revive_polygon(before, area / geoms.size(), z, line_detail);
// area is in world coordinates, calculated before scaling down
geoms[g] = revive_polygon(before, area / geoms.size(), z, extra_detail);
} else {
geoms[g].clear();
}
@@ -1721,8 +1720,7 @@ static bool line_is_too_small(drawvec const &geometry, int z, int detail) {
return true;
}
long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *metabase, char *stringpool, int z, unsigned tx, unsigned ty, int detail, int min_detail, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, FILE **geomfile, int minzoom, int maxzoom, double todo, std::atomic<long long> *along, long long alongminus, double gamma, int child_shards, long long *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y, std::atomic<int> *running, double simplification, std::vector<std::map<std::string, layermap_entry>> *layermaps, std::vector<std::vector<std::string>> *layer_unmaps, size_t tiling_seg, size_t pass, size_t passes, unsigned long long mingap, long long minextent, double fraction, const char *prefilter, const char *postfilter, struct json_object *filter, write_tile_args *arg, atomic_strategy *strategy) {
int line_detail;
long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *metabase, char *stringpool, int z, unsigned tx, unsigned ty, const int detail, int min_detail, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, FILE **geomfile, int minzoom, int maxzoom, double todo, std::atomic<long long> *along, long long alongminus, double gamma, int child_shards, long long *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y, std::atomic<int> *running, double simplification, std::vector<std::map<std::string, layermap_entry>> *layermaps, std::vector<std::vector<std::string>> *layer_unmaps, size_t tiling_seg, size_t pass, size_t passes, unsigned long long mingap, long long minextent, double fraction, const char *prefilter, const char *postfilter, struct json_object *filter, write_tile_args *arg, atomic_strategy *strategy) {
double merge_fraction = 1;
double mingap_fraction = 1;
double minextent_fraction = 1;
@@ -1755,6 +1753,7 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
bool first_time = true;
// This only loops if the tile data didn't fit, in which case the detail
// goes down and the progress indicator goes backward for the next try.
int line_detail;
for (line_detail = detail; line_detail >= min_detail || line_detail == detail; line_detail--, oprogress = 0) {
long long count = 0;
double accum_area = 0;
@@ -1777,6 +1776,8 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
double coalesced_area = 0;
drawvec shared_nodes;
int tile_detail = line_detail;
int within[child_shards];
std::atomic<long long> geompos[child_shards];
for (size_t i = 0; i < (size_t) child_shards; i++) {
@@ -2007,6 +2008,7 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
p.reduced = reduced;
p.z = z;
p.line_detail = line_detail;
p.extra_detail = line_detail;
p.maxzoom = maxzoom;
p.keys = sf.keys;
p.values = sf.values;
@@ -2020,6 +2022,14 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
p.renamed = -1;
p.extent = sf.extent;
p.clustered = 0;
if (line_detail == detail && additional[A_EXTRA_DETAIL] && z == maxzoom) {
// maximum allowed coordinate delta in geometries is 2^31 - 1
// so we need to stay under that, including the buffer
p.extra_detail = 30 - z;
tile_detail = p.extra_detail;
}
partials.push_back(p);
}
@@ -2272,7 +2282,7 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
mvt_layer layer;
layer.name = layer_iterator->first;
layer.version = 2;
layer.extent = 1 << line_detail;
layer.extent = 1 << tile_detail;
for (size_t x = 0; x < layer_features.size(); x++) {
mvt_feature feature;
@@ -2330,7 +2340,7 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
}
if (postfilter != NULL) {
tile.layers = filter_layers(postfilter, tile.layers, z, tx, ty, layermaps, tiling_seg, layer_unmaps, 1 << line_detail);
tile.layers = filter_layers(postfilter, tile.layers, z, tx, ty, layermaps, tiling_seg, layer_unmaps, 1 << tile_detail);
}
if (z == 0 && unclipped_features < original_features / 2 && clipbboxes.size() == 0) {