mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-02 08:25:40 +02:00
Be more careful to retry when the feature count is exceeded (#257)
* Clip before dealing with multiplier or filters in overzoom * Be more careful to retry when the feature count is exceeded * Adjust the estimated total feature count for the multiplier too * Fix the feature count estimates, I think * Pass build info into the version string * Report the actual max zoom of any tiles as the metadata maxzoom * Revert unneeded renaming to make the diff more readable * Clean up the adjustments to tile sizes and feature counts * Update version and changelog * Dropping a feature into a multiplier cluster still effectively drops it * Update changelog * Rethink the changelog description * Don't try to truncate zooms if we are still tiling at z18
This commit is contained in:
@@ -1,3 +1,11 @@
|
|||||||
|
# 2.60.0
|
||||||
|
|
||||||
|
* Fix bad interaction between --retain-points-multiplier and stopping early when the tile feature limit is reached
|
||||||
|
* Fix another bad interaction between --retain-points-multiplier, dropping-as-needed, and variable depth tile pyramids
|
||||||
|
* Add optional BUILD_INFO string to version
|
||||||
|
* Reorder overzoom logic to clip before dealing with multiplier and filters
|
||||||
|
* When --generate-variable-depth-tile-pyramid is in use, report the actual highest zoom generated as tileset maxzoom
|
||||||
|
|
||||||
# 2.59.0
|
# 2.59.0
|
||||||
|
|
||||||
* Correct `antimeridian_adjusted_bounds` latitude calculation when vertices extend beyond the edge of the Mercator plane
|
* Correct `antimeridian_adjusted_bounds` latitude calculation when vertices extend beyond the edge of the Mercator plane
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
MANDIR ?= $(PREFIX)/share/man/man1/
|
MANDIR ?= $(PREFIX)/share/man/man1/
|
||||||
BUILDTYPE ?= Release
|
BUILDTYPE ?= Release
|
||||||
|
BUILD_INFO ?=
|
||||||
SHELL = /bin/sh
|
SHELL = /bin/sh
|
||||||
|
|
||||||
|
|
||||||
# inherit from env if set
|
# inherit from env if set
|
||||||
CC := $(CC)
|
CC := $(CC)
|
||||||
CXX := $(CXX)
|
CXX := $(CXX)
|
||||||
CFLAGS := $(CFLAGS) -fPIE
|
CFLAGS := $(CFLAGS) -fPIE -DBUILD_INFO=$(BUILD_INFO)
|
||||||
CXXFLAGS := $(CXXFLAGS) -std=c++17 -fPIE
|
CXXFLAGS := $(CXXFLAGS) -std=c++17 -fPIE -DBUILD_INFO=$(BUILD_INFO)
|
||||||
LDFLAGS := $(LDFLAGS)
|
LDFLAGS := $(LDFLAGS)
|
||||||
WARNING_FLAGS := -Wall -Wshadow -Wsign-compare -Wextra -Wunreachable-code -Wuninitialized -Wshadow
|
WARNING_FLAGS := -Wall -Wshadow -Wsign-compare -Wextra -Wunreachable-code -Wuninitialized -Wshadow
|
||||||
RELEASE_FLAGS := -O3 -DNDEBUG
|
RELEASE_FLAGS := -O3 -DNDEBUG
|
||||||
|
|||||||
@@ -1188,37 +1188,6 @@ std::string overzoom(std::vector<source_tile> const &tiles, int nz, int nx, int
|
|||||||
static const std::string retain_points_multiplier_sequence = "tippecanoe:retain_points_multiplier_sequence";
|
static const std::string retain_points_multiplier_sequence = "tippecanoe:retain_points_multiplier_sequence";
|
||||||
|
|
||||||
for (auto feature : layer.features) {
|
for (auto feature : layer.features) {
|
||||||
bool flush_multiplier_cluster = false;
|
|
||||||
if (demultiply) {
|
|
||||||
for (ssize_t i = feature.tags.size() - 2; i >= 0; i -= 2) {
|
|
||||||
if (layer.keys[feature.tags[i]] == retain_points_multiplier_first) {
|
|
||||||
mvt_value v = layer.values[feature.tags[i + 1]];
|
|
||||||
if (v.type == mvt_bool && v.numeric_value.bool_value) {
|
|
||||||
flush_multiplier_cluster = true;
|
|
||||||
feature.tags.erase(feature.tags.begin() + i, feature.tags.begin() + i + 2);
|
|
||||||
}
|
|
||||||
} else if (i < (ssize_t) feature.tags.size() && layer.keys[feature.tags[i]] == retain_points_multiplier_sequence) {
|
|
||||||
mvt_value v = layer.values[feature.tags[i + 1]];
|
|
||||||
feature.seq = mvt_value_to_long_long(v);
|
|
||||||
feature.tags.erase(feature.tags.begin() + i, feature.tags.begin() + i + 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
flush_multiplier_cluster = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flush_multiplier_cluster) {
|
|
||||||
if (pending_tile_features.size() > 0) {
|
|
||||||
feature_out(pending_tile_features, *outlayer, keep, attribute_accum, tile_stringpool);
|
|
||||||
pending_tile_features.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<std::string> exclude_attributes;
|
|
||||||
if (filter != NULL && !evaluate(feature, layer, filter, exclude_attributes, nz, unidecode_data)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
drawvec geom;
|
drawvec geom;
|
||||||
int t = feature.type;
|
int t = feature.type;
|
||||||
|
|
||||||
@@ -1268,6 +1237,7 @@ std::string overzoom(std::vector<source_tile> const &tiles, int nz, int nx, int
|
|||||||
|
|
||||||
long long b = outtilesize * buffer / 256;
|
long long b = outtilesize * buffer / 256;
|
||||||
if (xmax < -b || ymax < -b || xmin > outtilesize + b || ymin > outtilesize + b) {
|
if (xmax < -b || ymax < -b || xmin > outtilesize + b || ymin > outtilesize + b) {
|
||||||
|
// quick exclusion by bounding box
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1281,6 +1251,42 @@ std::string overzoom(std::vector<source_tile> const &tiles, int nz, int nx, int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (geom.size() == 0) {
|
||||||
|
// clipped away
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool flush_multiplier_cluster = false;
|
||||||
|
if (demultiply) {
|
||||||
|
for (ssize_t i = feature.tags.size() - 2; i >= 0; i -= 2) {
|
||||||
|
if (layer.keys[feature.tags[i]] == retain_points_multiplier_first) {
|
||||||
|
mvt_value v = layer.values[feature.tags[i + 1]];
|
||||||
|
if (v.type == mvt_bool && v.numeric_value.bool_value) {
|
||||||
|
flush_multiplier_cluster = true;
|
||||||
|
feature.tags.erase(feature.tags.begin() + i, feature.tags.begin() + i + 2);
|
||||||
|
}
|
||||||
|
} else if (i < (ssize_t) feature.tags.size() && layer.keys[feature.tags[i]] == retain_points_multiplier_sequence) {
|
||||||
|
mvt_value v = layer.values[feature.tags[i + 1]];
|
||||||
|
feature.seq = mvt_value_to_long_long(v);
|
||||||
|
feature.tags.erase(feature.tags.begin() + i, feature.tags.begin() + i + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
flush_multiplier_cluster = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flush_multiplier_cluster) {
|
||||||
|
if (pending_tile_features.size() > 0) {
|
||||||
|
feature_out(pending_tile_features, *outlayer, keep, attribute_accum, tile_stringpool);
|
||||||
|
pending_tile_features.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string> exclude_attributes;
|
||||||
|
if (filter != NULL && !evaluate(feature, layer, filter, exclude_attributes, nz, unidecode_data)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool still_need_simplification_after_reduction = false;
|
bool still_need_simplification_after_reduction = false;
|
||||||
if (t == VT_POLYGON && tiny_polygon_size > 0) {
|
if (t == VT_POLYGON && tiny_polygon_size > 0) {
|
||||||
bool simplified_away_by_reduction = false;
|
bool simplified_away_by_reduction = false;
|
||||||
|
|||||||
@@ -49,7 +49,6 @@
|
|||||||
#include "tile.hpp"
|
#include "tile.hpp"
|
||||||
#include "pool.hpp"
|
#include "pool.hpp"
|
||||||
#include "projection.hpp"
|
#include "projection.hpp"
|
||||||
#include "version.hpp"
|
|
||||||
#include "memfile.hpp"
|
#include "memfile.hpp"
|
||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
#include "geojson.hpp"
|
#include "geojson.hpp"
|
||||||
@@ -3608,7 +3607,7 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
fprintf(stderr, "tippecanoe %s\n", VERSION);
|
fprintf(stderr, "tippecanoe %s\n", version_str().c_str());
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
|
|||||||
+12
-1
@@ -654,6 +654,17 @@ static double sixdig(double val) {
|
|||||||
return std::round(val * 1e6) / 1e6;
|
return std::round(val * 1e6) / 1e6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define str(x) #x
|
||||||
|
#define xstr(x) str(x)
|
||||||
|
std::string version_str() {
|
||||||
|
std::string s = VERSION;
|
||||||
|
std::string build_info = xstr(BUILD_INFO);
|
||||||
|
if (build_info.size() > 0) {
|
||||||
|
s += " " + build_info;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
metadata make_metadata(const char *fname, int minzoom, int maxzoom, double minlat, double minlon, double maxlat, double maxlon, double minlat2, double minlon2, double maxlat2, double maxlon2, double midlat, double midlon, const char *attribution, std::map<std::string, layermap_entry> const &layermap, bool vector, const char *description, bool do_tilestats, std::map<std::string, std::string> const &attribute_descriptions, std::string const &program, std::string const &commandline, std::vector<strategy> const &strategies, int basezoom, double droprate, int retain_points_multiplier) {
|
metadata make_metadata(const char *fname, int minzoom, int maxzoom, double minlat, double minlon, double maxlat, double maxlon, double minlat2, double minlon2, double maxlat2, double maxlon2, double midlat, double midlon, const char *attribution, std::map<std::string, layermap_entry> const &layermap, bool vector, const char *description, bool do_tilestats, std::map<std::string, std::string> const &attribute_descriptions, std::string const &program, std::string const &commandline, std::vector<strategy> const &strategies, int basezoom, double droprate, int retain_points_multiplier) {
|
||||||
metadata m;
|
metadata m;
|
||||||
|
|
||||||
@@ -684,7 +695,7 @@ metadata make_metadata(const char *fname, int minzoom, int maxzoom, double minla
|
|||||||
m.attribution = attribution;
|
m.attribution = attribution;
|
||||||
}
|
}
|
||||||
|
|
||||||
m.generator = program + " " + VERSION;
|
m.generator = program + " " + version_str();
|
||||||
m.generator_options = commandline;
|
m.generator_options = commandline;
|
||||||
|
|
||||||
m.strategies_json = stringify_strategies(strategies);
|
m.strategies_json = stringify_strategies(strategies);
|
||||||
|
|||||||
@@ -77,5 +77,6 @@ std::map<std::string, layermap_entry> merge_layermaps(std::vector<std::map<std::
|
|||||||
std::map<std::string, layermap_entry> merge_layermaps(std::vector<std::map<std::string, layermap_entry> > const &maps, bool trunc);
|
std::map<std::string, layermap_entry> merge_layermaps(std::vector<std::map<std::string, layermap_entry> > const &maps, bool trunc);
|
||||||
|
|
||||||
void add_to_tilestats(std::map<std::string, tilestat> &tilestats, std::string const &layername, serial_val const &val);
|
void add_to_tilestats(std::map<std::string, tilestat> &tilestats, std::string const &layername, serial_val const &val);
|
||||||
|
std::string version_str();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"maxzoom": "0",
|
"maxzoom": "0",
|
||||||
"minzoom": "0",
|
"minzoom": "0",
|
||||||
"name": "tests/loop/out/-z0_-O200_--cluster-densest-as-needed.json.check.mbtiles",
|
"name": "tests/loop/out/-z0_-O200_--cluster-densest-as-needed.json.check.mbtiles",
|
||||||
"strategies": "[{\"coalesced_as_needed\":999,\"feature_count_desired\":201}]",
|
"strategies": "[{\"coalesced_as_needed\":999,\"feature_count_desired\":1000}]",
|
||||||
"type": "overlay",
|
"type": "overlay",
|
||||||
"version": "2"
|
"version": "2"
|
||||||
}, "features": [
|
}, "features": [
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"maxzoom": "0",
|
"maxzoom": "0",
|
||||||
"minzoom": "0",
|
"minzoom": "0",
|
||||||
"name": "tests/loop/out/-z0_-O200_--drop-densest-as-needed.json.check.mbtiles",
|
"name": "tests/loop/out/-z0_-O200_--drop-densest-as-needed.json.check.mbtiles",
|
||||||
"strategies": "[{\"dropped_as_needed\":999,\"feature_count_desired\":201}]",
|
"strategies": "[{\"dropped_as_needed\":999,\"feature_count_desired\":1000}]",
|
||||||
"type": "overlay",
|
"type": "overlay",
|
||||||
"version": "2"
|
"version": "2"
|
||||||
}, "features": [
|
}, "features": [
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"maxzoom": "0",
|
"maxzoom": "0",
|
||||||
"minzoom": "0",
|
"minzoom": "0",
|
||||||
"name": "tests/loop/out/-z0_-O200_--drop-fraction-as-needed.json.check.mbtiles",
|
"name": "tests/loop/out/-z0_-O200_--drop-fraction-as-needed.json.check.mbtiles",
|
||||||
"strategies": "[{\"dropped_as_needed\":999,\"feature_count_desired\":201}]",
|
"strategies": "[{\"dropped_as_needed\":999,\"feature_count_desired\":1000}]",
|
||||||
"type": "overlay",
|
"type": "overlay",
|
||||||
"version": "2"
|
"version": "2"
|
||||||
}, "features": [
|
}, "features": [
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+8958
-7768
File diff suppressed because it is too large
Load Diff
+3
-3
File diff suppressed because one or more lines are too long
@@ -1,14 +1,15 @@
|
|||||||
{ "type": "FeatureCollection", "properties": {
|
{ "type": "FeatureCollection", "properties": {
|
||||||
"antimeridian_adjusted_bounds": "0.000000,85.051129,0.000000,85.051129",
|
"antimeridian_adjusted_bounds": "0.000000,85.051129,0.000000,85.051129",
|
||||||
"bounds": "-180.000000,85.051129,180.000000,85.051129",
|
"bounds": "-180.000000,85.051129,180.000000,85.051129",
|
||||||
"center": "-179.989014,85.051129,14",
|
"center": "0.000000,85.051129,0",
|
||||||
"description": "tests/raw-tiles/nothing",
|
"description": "tests/raw-tiles/nothing",
|
||||||
"format": "pbf",
|
"format": "pbf",
|
||||||
"generator_options": "./tippecanoe -q -f -e tests/raw-tiles/nothing tests/raw-tiles/nothing.geojson",
|
"generator_options": "./tippecanoe -q -f -e tests/raw-tiles/nothing tests/raw-tiles/nothing.geojson",
|
||||||
"json": "{\"vector_layers\":[{\"id\":\"nothing\",\"description\":\"\",\"minzoom\":0,\"maxzoom\":14,\"fields\":{}}],\"tilestats\":{\"layerCount\":1,\"layers\":[{\"layer\":\"nothing\",\"count\":1,\"geometry\":\"Point\",\"attributeCount\":0,\"attributes\":[]}]}}",
|
"json": "{\"vector_layers\":[{\"id\":\"nothing\",\"description\":\"\",\"minzoom\":0,\"maxzoom\":0,\"fields\":{}}],\"tilestats\":{\"layerCount\":1,\"layers\":[{\"layer\":\"nothing\",\"count\":1,\"geometry\":\"Point\",\"attributeCount\":0,\"attributes\":[]}]}}",
|
||||||
"maxzoom": "14",
|
"maxzoom": "0",
|
||||||
"minzoom": "0",
|
"minzoom": "0",
|
||||||
"name": "tests/raw-tiles/nothing",
|
"name": "tests/raw-tiles/nothing",
|
||||||
|
"tippecanoe_decisions": "{\"basezoom\":14,\"droprate\":2.5,\"retain_points_multiplier\":1}",
|
||||||
"type": "overlay",
|
"type": "overlay",
|
||||||
"version": "2"
|
"version": "2"
|
||||||
}, "features": [
|
}, "features": [
|
||||||
|
|||||||
+4
-3
File diff suppressed because one or more lines are too long
@@ -1525,6 +1525,7 @@ bool drop_feature_unless_it_can_be_added_to_a_multiplier_cluster(layer_features
|
|||||||
ssize_t which_serial_feature;
|
ssize_t which_serial_feature;
|
||||||
|
|
||||||
if (find_feature_to_accumulate_onto(layer.features, sf, which_serial_feature, layer_unmaps, LLONG_MAX, multiplier_seq)) {
|
if (find_feature_to_accumulate_onto(layer.features, sf, which_serial_feature, layer_unmaps, LLONG_MAX, multiplier_seq)) {
|
||||||
|
strategy.dropped_as_needed++;
|
||||||
if (layer.multiplier_cluster_size < (size_t) retain_points_multiplier) {
|
if (layer.multiplier_cluster_size < (size_t) retain_points_multiplier) {
|
||||||
// we have capacity to keep this feature as part of an existing multiplier cluster that isn't full yet
|
// we have capacity to keep this feature as part of an existing multiplier cluster that isn't full yet
|
||||||
// so do that instead of dropping it
|
// so do that instead of dropping it
|
||||||
@@ -1532,7 +1533,6 @@ bool drop_feature_unless_it_can_be_added_to_a_multiplier_cluster(layer_features
|
|||||||
return false; // converted rather than dropped
|
return false; // converted rather than dropped
|
||||||
} else {
|
} else {
|
||||||
preserve_attributes(attribute_accum, sf, layer.features[which_serial_feature]);
|
preserve_attributes(attribute_accum, sf, layer.features[which_serial_feature]);
|
||||||
strategy.dropped_as_needed++;
|
|
||||||
drop_rest = true;
|
drop_rest = true;
|
||||||
return true; // dropped
|
return true; // dropped
|
||||||
}
|
}
|
||||||
@@ -1589,8 +1589,7 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
// empirical estimate from ne_10m_admin_0_countries, CPAD units, Cal fires.
|
// empirical estimate from ne_10m_admin_0_countries, CPAD units, Cal fires.
|
||||||
// only try to make an overzoomable final tile if it seems like it might work
|
// only try to make an overzoomable final tile if it seems like it might work
|
||||||
long long estimated_output_tile_size = 0.6693 * estimated_complexity - 3.36e+04;
|
long long estimated_output_tile_size = 0.6693 * estimated_complexity - 3.36e+04;
|
||||||
|
if (estimated_output_tile_size < (long long) (0.9 * max_tile_size) && 30 - z > detail) {
|
||||||
if (estimated_output_tile_size < (long long) (0.9 * max_tile_size)) {
|
|
||||||
first_detail = 30 - z;
|
first_detail = 30 - z;
|
||||||
second_detail = detail;
|
second_detail = detail;
|
||||||
trying_to_stop_early = true;
|
trying_to_stop_early = true;
|
||||||
@@ -1640,6 +1639,9 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
size_t lead_features_count = 0; // of the tile so far
|
size_t lead_features_count = 0; // of the tile so far
|
||||||
size_t other_multiplier_cluster_features_count = 0; // of the tile so far
|
size_t other_multiplier_cluster_features_count = 0; // of the tile so far
|
||||||
|
|
||||||
|
bool too_many_features = false;
|
||||||
|
bool too_many_bytes = false;
|
||||||
|
|
||||||
std::atomic<bool> within[child_shards];
|
std::atomic<bool> within[child_shards];
|
||||||
long long start_geompos[child_shards];
|
long long start_geompos[child_shards];
|
||||||
for (size_t i = 0; i < (size_t) child_shards; i++) {
|
for (size_t i = 0; i < (size_t) child_shards; i++) {
|
||||||
@@ -1867,8 +1869,8 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
} else if (additional[A_DROP_DENSEST_AS_NEEDED]) {
|
} else if (additional[A_DROP_DENSEST_AS_NEEDED]) {
|
||||||
add_sample_to(gaps, sf.gap, gaps_increment, seq);
|
add_sample_to(gaps, sf.gap, gaps_increment, seq);
|
||||||
if (sf.gap < mingap) {
|
if (sf.gap < mingap) {
|
||||||
|
can_stop_early = false;
|
||||||
if (drop_feature_unless_it_can_be_added_to_a_multiplier_cluster(layer, sf, layer_unmaps, multiplier_seq, strategy, drop_rest, arg->attribute_accum)) {
|
if (drop_feature_unless_it_can_be_added_to_a_multiplier_cluster(layer, sf, layer_unmaps, multiplier_seq, strategy, drop_rest, arg->attribute_accum)) {
|
||||||
can_stop_early = false;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1912,8 +1914,8 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
// search here is for LLONG_MAX, not minextent, because we are dropping features, not coalescing them,
|
// search here is for LLONG_MAX, not minextent, because we are dropping features, not coalescing them,
|
||||||
// so we shouldn't expect to find anything small that we can related this feature to.
|
// so we shouldn't expect to find anything small that we can related this feature to.
|
||||||
if (minextent != 0 && sf.extent + coalesced_area <= minextent) {
|
if (minextent != 0 && sf.extent + coalesced_area <= minextent) {
|
||||||
|
can_stop_early = false;
|
||||||
if (drop_feature_unless_it_can_be_added_to_a_multiplier_cluster(layer, sf, layer_unmaps, multiplier_seq, strategy, drop_rest, arg->attribute_accum)) {
|
if (drop_feature_unless_it_can_be_added_to_a_multiplier_cluster(layer, sf, layer_unmaps, multiplier_seq, strategy, drop_rest, arg->attribute_accum)) {
|
||||||
can_stop_early = false;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1931,11 +1933,9 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
}
|
}
|
||||||
} else if (additional[A_DROP_FRACTION_AS_NEEDED] || prevent[P_DYNAMIC_DROP]) {
|
} else if (additional[A_DROP_FRACTION_AS_NEEDED] || prevent[P_DYNAMIC_DROP]) {
|
||||||
add_sample_to(drop_sequences, drop_sequence, drop_sequences_increment, seq);
|
add_sample_to(drop_sequences, drop_sequence, drop_sequences_increment, seq);
|
||||||
// search here is for LLONG_MAX, not minextent, because we are dropping features, not coalescing them,
|
|
||||||
// so we shouldn't expect to find anything small that we can related this feature to.
|
|
||||||
if (mindrop_sequence != 0 && drop_sequence <= mindrop_sequence) {
|
if (mindrop_sequence != 0 && drop_sequence <= mindrop_sequence) {
|
||||||
|
can_stop_early = false;
|
||||||
if (drop_feature_unless_it_can_be_added_to_a_multiplier_cluster(layer, sf, layer_unmaps, multiplier_seq, strategy, drop_rest, arg->attribute_accum)) {
|
if (drop_feature_unless_it_can_be_added_to_a_multiplier_cluster(layer, sf, layer_unmaps, multiplier_seq, strategy, drop_rest, arg->attribute_accum)) {
|
||||||
can_stop_early = false;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1995,10 +1995,42 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
unsigned long long sfindex = sf.index;
|
unsigned long long sfindex = sf.index;
|
||||||
|
|
||||||
if (sf.geometry.size() > 0) {
|
if (sf.geometry.size() > 0) {
|
||||||
if (lead_features_count > max_tile_size || (lead_features_count + other_multiplier_cluster_features_count > max_tile_features && !prevent[P_FEATURE_LIMIT])) {
|
// There are two adjustments that we need to make
|
||||||
|
// when determining if we have hit the feature count
|
||||||
|
// or byte size limit:
|
||||||
|
//
|
||||||
|
// The max_tile_size and max_tile_features are inflated
|
||||||
|
// to account for the number of multiplier clusters features
|
||||||
|
// we are carrying around in addition to their lead features.
|
||||||
|
|
||||||
|
size_t adjusted_max_tile_size = max_tile_size;
|
||||||
|
if (lead_features_count > 0) {
|
||||||
|
adjusted_max_tile_size = adjusted_max_tile_size * (lead_features_count + other_multiplier_cluster_features_count) / lead_features_count;
|
||||||
|
}
|
||||||
|
size_t adjusted_max_tile_features = max_tile_features;
|
||||||
|
if (lead_features_count > 0) {
|
||||||
|
adjusted_max_tile_features = adjusted_max_tile_features * (lead_features_count + other_multiplier_cluster_features_count) / lead_features_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of features in the tile, meanwhile, is inflated
|
||||||
|
// to account for the number of features that we have skipped over
|
||||||
|
// because we were already over the limit, in addition to those
|
||||||
|
// that we have actually added to the layer (as either lead or
|
||||||
|
// multiplier features).
|
||||||
|
|
||||||
|
size_t adjusted_feature_count = lead_features_count + other_multiplier_cluster_features_count;
|
||||||
|
if (kept > 0) {
|
||||||
|
adjusted_feature_count = adjusted_feature_count * (skipped + kept) / kept;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (too_many_bytes || adjusted_feature_count > adjusted_max_tile_size) {
|
||||||
// Even being maximally conservative, each feature is still going to be
|
// Even being maximally conservative, each feature is still going to be
|
||||||
// at least one byte in the output tile, so this can't possibly work.
|
// at least one byte in the output tile, so this can't possibly work.
|
||||||
skipped++;
|
skipped++;
|
||||||
|
too_many_bytes = true;
|
||||||
|
} else if (too_many_features || ((adjusted_feature_count > adjusted_max_tile_features) && !prevent[P_FEATURE_LIMIT])) {
|
||||||
|
skipped++;
|
||||||
|
too_many_features = true;
|
||||||
} else {
|
} else {
|
||||||
kept++;
|
kept++;
|
||||||
|
|
||||||
@@ -2134,13 +2166,13 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
first_time = false;
|
first_time = false;
|
||||||
|
|
||||||
// Adjust tile size limit based on the ratio of multiplier cluster features to lead features
|
// Adjust tile size limit based on the ratio of multiplier cluster features to lead features
|
||||||
size_t scaled_max_tile_size = max_tile_size;
|
size_t adjusted_max_tile_size = max_tile_size;
|
||||||
if (lead_features_count > 0) {
|
if (lead_features_count > 0) {
|
||||||
scaled_max_tile_size *= (lead_features_count + other_multiplier_cluster_features_count) / lead_features_count;
|
adjusted_max_tile_size = adjusted_max_tile_size * (lead_features_count + other_multiplier_cluster_features_count) / lead_features_count;
|
||||||
}
|
}
|
||||||
size_t scaled_max_tile_features = max_tile_features;
|
size_t adjusted_max_tile_features = max_tile_features;
|
||||||
if (lead_features_count > 0) {
|
if (lead_features_count > 0) {
|
||||||
scaled_max_tile_features *= (lead_features_count + other_multiplier_cluster_features_count) / lead_features_count;
|
adjusted_max_tile_features = adjusted_max_tile_features * (lead_features_count + other_multiplier_cluster_features_count) / lead_features_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operations on the features within each layer:
|
// Operations on the features within each layer:
|
||||||
@@ -2387,11 +2419,11 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
mvt_tile tile;
|
mvt_tile tile;
|
||||||
size_t totalsize = 0;
|
size_t feature_count = 0;
|
||||||
|
|
||||||
for (auto layer_iterator = layers.begin(); layer_iterator != layers.end(); ++layer_iterator) {
|
for (auto layer_iterator = layers.begin(); layer_iterator != layers.end(); ++layer_iterator) {
|
||||||
std::vector<serial_feature> &layer_features = layer_iterator->second.features;
|
std::vector<serial_feature> &layer_features = layer_iterator->second.features;
|
||||||
totalsize += layer_features.size();
|
feature_count += layer_features.size();
|
||||||
|
|
||||||
mvt_layer layer;
|
mvt_layer layer;
|
||||||
layer.name = layer_iterator->first;
|
layer.name = layer_iterator->first;
|
||||||
@@ -2482,14 +2514,23 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalsize > 0 && tile.layers.size() > 0) {
|
// Again, adjust the retabulated feature count to estimate
|
||||||
if (totalsize > scaled_max_tile_features && !prevent[P_FEATURE_LIMIT]) {
|
// how many total features there would have been if we hadn't
|
||||||
if (totalsize > arg->feature_count_out) {
|
// hit the limit and started dropping early.
|
||||||
arg->feature_count_out = totalsize;
|
|
||||||
|
size_t adjusted_feature_count = feature_count;
|
||||||
|
if (kept > 0) {
|
||||||
|
adjusted_feature_count = adjusted_feature_count * (skipped + kept) / kept;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adjusted_feature_count > 0 && tile.layers.size() > 0) {
|
||||||
|
if (too_many_features || (adjusted_feature_count > adjusted_max_tile_features && !prevent[P_FEATURE_LIMIT])) {
|
||||||
|
if (adjusted_feature_count > arg->feature_count_out) {
|
||||||
|
arg->feature_count_out = adjusted_feature_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
fprintf(stderr, "tile %d/%u/%u has %zu features, >%zu \n", z, tx, ty, totalsize, scaled_max_tile_features);
|
fprintf(stderr, "tile %d/%u/%u has %zu (estimated %zu) features, >%zu \n", z, tx, ty, feature_count, adjusted_feature_count, adjusted_max_tile_features);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trying_to_stop_early && line_detail == first_detail) {
|
if (trying_to_stop_early && line_detail == first_detail) {
|
||||||
@@ -2515,7 +2556,7 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
line_detail++; // to keep it the same when the loop decrements it
|
line_detail++; // to keep it the same when the loop decrements it
|
||||||
continue;
|
continue;
|
||||||
} else if (mingap < ULONG_MAX && (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_COALESCE_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED])) {
|
} else if (mingap < ULONG_MAX && (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_COALESCE_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED])) {
|
||||||
mingap_fraction = mingap_fraction * scaled_max_tile_features / totalsize * 0.80;
|
mingap_fraction = mingap_fraction * adjusted_max_tile_features / adjusted_feature_count * 0.80;
|
||||||
unsigned long long m = choose_mingap(gaps, mingap_fraction, mingap);
|
unsigned long long m = choose_mingap(gaps, mingap_fraction, mingap);
|
||||||
if (m != mingap) {
|
if (m != mingap) {
|
||||||
mingap = m;
|
mingap = m;
|
||||||
@@ -2530,7 +2571,7 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (additional[A_DROP_SMALLEST_AS_NEEDED] || additional[A_COALESCE_SMALLEST_AS_NEEDED]) {
|
} else if (additional[A_DROP_SMALLEST_AS_NEEDED] || additional[A_COALESCE_SMALLEST_AS_NEEDED]) {
|
||||||
minextent_fraction = minextent_fraction * scaled_max_tile_features / totalsize * 0.75;
|
minextent_fraction = minextent_fraction * adjusted_max_tile_features / adjusted_feature_count * 0.75;
|
||||||
long long m = choose_minextent(extents, minextent_fraction, minextent);
|
long long m = choose_minextent(extents, minextent_fraction, minextent);
|
||||||
if (m != minextent) {
|
if (m != minextent) {
|
||||||
minextent = m;
|
minextent = m;
|
||||||
@@ -2544,11 +2585,11 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
line_detail++;
|
line_detail++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (totalsize > layers.size() && (additional[A_DROP_FRACTION_AS_NEEDED] || additional[A_COALESCE_FRACTION_AS_NEEDED] || prevent[P_DYNAMIC_DROP])) {
|
} else if (feature_count > layers.size() && (additional[A_DROP_FRACTION_AS_NEEDED] || additional[A_COALESCE_FRACTION_AS_NEEDED] || prevent[P_DYNAMIC_DROP])) {
|
||||||
// The 95% is a guess to avoid too many retries
|
// The 95% is a guess to avoid too many retries
|
||||||
// and probably actually varies based on how much duplicated metadata there is
|
// and probably actually varies based on how much duplicated metadata there is
|
||||||
|
|
||||||
mindrop_sequence_fraction = mindrop_sequence_fraction * scaled_max_tile_features / totalsize * 0.95;
|
mindrop_sequence_fraction = mindrop_sequence_fraction * adjusted_max_tile_features / adjusted_feature_count * 0.95;
|
||||||
unsigned long long m = choose_mindrop_sequence(drop_sequences, mindrop_sequence_fraction, mindrop_sequence);
|
unsigned long long m = choose_mindrop_sequence(drop_sequences, mindrop_sequence_fraction, mindrop_sequence);
|
||||||
if (m != mindrop_sequence) {
|
if (m != mindrop_sequence) {
|
||||||
mindrop_sequence = m;
|
mindrop_sequence = m;
|
||||||
@@ -2581,26 +2622,24 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
compressed = pbf;
|
compressed = pbf;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trying_to_stop_early && line_detail == first_detail) {
|
// And similarly, adjust the compressed byte size to estimate
|
||||||
// printf("%lld %zu\n", estimated_complexity, compressed.size());
|
// what it would have been if we hadn't stopped dropping features early
|
||||||
|
|
||||||
|
size_t adjusted_tile_size = compressed.size();
|
||||||
|
if (kept > 0) {
|
||||||
|
adjusted_tile_size = adjusted_tile_size * (kept + skipped) / kept;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compressed.size() > scaled_max_tile_size && !prevent[P_KILOBYTE_LIMIT]) {
|
if (too_many_bytes || (adjusted_tile_size > adjusted_max_tile_size && !prevent[P_KILOBYTE_LIMIT])) {
|
||||||
// Estimate how big it really should have been compressed
|
if (adjusted_tile_size > arg->tile_size_out) {
|
||||||
// from how many features were kept vs skipped for already being
|
arg->tile_size_out = adjusted_tile_size;
|
||||||
// over the threshold
|
|
||||||
|
|
||||||
double kept_adjust = (skipped + kept) / (double) kept;
|
|
||||||
|
|
||||||
if (compressed.size() > arg->tile_size_out) {
|
|
||||||
arg->tile_size_out = compressed.size() * kept_adjust;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
if (skipped > 0) {
|
if (adjusted_tile_size == compressed.size()) {
|
||||||
fprintf(stderr, "tile %d/%u/%u size is %lld (probably really %lld) with detail %d, >%zu \n", z, tx, ty, (long long) compressed.size(), (long long) (compressed.size() * kept_adjust), line_detail, scaled_max_tile_size);
|
fprintf(stderr, "tile %d/%u/%u size is %lld (probably really %zu) with detail %d, >%zu \n", z, tx, ty, (long long) compressed.size(), adjusted_tile_size, line_detail, adjusted_max_tile_size);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "tile %d/%u/%u size is %lld with detail %d, >%zu \n", z, tx, ty, (long long) compressed.size(), line_detail, scaled_max_tile_size);
|
fprintf(stderr, "tile %d/%u/%u size is %lld with detail %d, >%zu \n", z, tx, ty, (long long) compressed.size(), line_detail, adjusted_max_tile_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2627,7 +2666,7 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
}
|
}
|
||||||
line_detail++; // to keep it the same when the loop decrements it
|
line_detail++; // to keep it the same when the loop decrements it
|
||||||
} else if (mingap < ULONG_MAX && (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_COALESCE_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED])) {
|
} else if (mingap < ULONG_MAX && (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_COALESCE_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED])) {
|
||||||
mingap_fraction = mingap_fraction * scaled_max_tile_size / (kept_adjust * compressed.size()) * 0.80;
|
mingap_fraction = mingap_fraction * adjusted_max_tile_size / adjusted_tile_size * 0.80;
|
||||||
unsigned long long m = choose_mingap(gaps, mingap_fraction, mingap);
|
unsigned long long m = choose_mingap(gaps, mingap_fraction, mingap);
|
||||||
if (m != mingap) {
|
if (m != mingap) {
|
||||||
mingap = m;
|
mingap = m;
|
||||||
@@ -2642,7 +2681,7 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (additional[A_DROP_SMALLEST_AS_NEEDED] || additional[A_COALESCE_SMALLEST_AS_NEEDED]) {
|
} else if (additional[A_DROP_SMALLEST_AS_NEEDED] || additional[A_COALESCE_SMALLEST_AS_NEEDED]) {
|
||||||
minextent_fraction = minextent_fraction * scaled_max_tile_size / (kept_adjust * compressed.size()) * 0.75;
|
minextent_fraction = minextent_fraction * adjusted_max_tile_size / adjusted_tile_size * 0.75;
|
||||||
long long m = choose_minextent(extents, minextent_fraction, minextent);
|
long long m = choose_minextent(extents, minextent_fraction, minextent);
|
||||||
if (m != minextent) {
|
if (m != minextent) {
|
||||||
minextent = m;
|
minextent = m;
|
||||||
@@ -2656,8 +2695,8 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
line_detail++;
|
line_detail++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (totalsize > layers.size() && (additional[A_DROP_FRACTION_AS_NEEDED] || additional[A_COALESCE_FRACTION_AS_NEEDED] || prevent[P_DYNAMIC_DROP])) {
|
} else if (feature_count > layers.size() && (additional[A_DROP_FRACTION_AS_NEEDED] || additional[A_COALESCE_FRACTION_AS_NEEDED] || prevent[P_DYNAMIC_DROP])) {
|
||||||
mindrop_sequence_fraction = mindrop_sequence_fraction * scaled_max_tile_size / (kept_adjust * compressed.size()) * 0.75;
|
mindrop_sequence_fraction = mindrop_sequence_fraction * adjusted_max_tile_size / adjusted_tile_size * 0.75;
|
||||||
unsigned long long m = choose_mindrop_sequence(drop_sequences, mindrop_sequence_fraction, mindrop_sequence);
|
unsigned long long m = choose_mindrop_sequence(drop_sequences, mindrop_sequence_fraction, mindrop_sequence);
|
||||||
if (m != mindrop_sequence) {
|
if (m != mindrop_sequence) {
|
||||||
mindrop_sequence = m;
|
mindrop_sequence = m;
|
||||||
@@ -2682,6 +2721,11 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
|||||||
exit(EXIT_PTHREAD);
|
exit(EXIT_PTHREAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (skipped > 0 || too_many_bytes || too_many_features) {
|
||||||
|
fprintf(stderr, "Can't happen: writing tile even though we skipped\n");
|
||||||
|
exit(EXIT_IMPOSSIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
if (outdb != NULL) {
|
if (outdb != NULL) {
|
||||||
mbtiles_write_tile(outdb, z, tx, ty, compressed.data(), compressed.size());
|
mbtiles_write_tile(outdb, z, tx, ty, compressed.data(), compressed.size());
|
||||||
} else if (outdir != NULL) {
|
} else if (outdir != NULL) {
|
||||||
@@ -2802,7 +2846,6 @@ exit(EXIT_IMPOSSIBLE);
|
|||||||
dc.begin();
|
dc.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
arg->wrote_zoom = z;
|
|
||||||
long long len;
|
long long len;
|
||||||
|
|
||||||
struct zxy parent(z - 1, x / 2, y / 2);
|
struct zxy parent(z - 1, x / 2, y / 2);
|
||||||
@@ -2810,6 +2853,7 @@ exit(EXIT_IMPOSSIBLE);
|
|||||||
skip_tile(&dc, &geompos, arg->compressed);
|
skip_tile(&dc, &geompos, arg->compressed);
|
||||||
len = 1;
|
len = 1;
|
||||||
} else {
|
} else {
|
||||||
|
arg->wrote_zoom = z;
|
||||||
len = write_tile(&dc, &geompos, arg->global_stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->outdb, arg->outdir, arg->buffer, arg->fname, arg->geomfile, arg->geompos, arg->minzoom, arg->maxzoom, arg->todo, arg->along, geompos, arg->gamma, arg->child_shards, arg->pool_off, arg->initial_x, arg->initial_y, arg->running, arg->simplification, arg->layermaps, arg->layer_unmaps, arg->tiling_seg, arg->pass, arg->mingap, arg->minextent, arg->mindrop_sequence, arg->prefilter, arg->postfilter, arg->filter, arg, arg->strategy, arg->compressed, arg->shared_nodes_map, arg->nodepos, *(arg->shared_nodes_bloom), (*arg->unidecode_data), estimated_complexity, arg->skip_children_out);
|
len = write_tile(&dc, &geompos, arg->global_stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->outdb, arg->outdir, arg->buffer, arg->fname, arg->geomfile, arg->geompos, arg->minzoom, arg->maxzoom, arg->todo, arg->along, geompos, arg->gamma, arg->child_shards, arg->pool_off, arg->initial_x, arg->initial_y, arg->running, arg->simplification, arg->layermaps, arg->layer_unmaps, arg->tiling_seg, arg->pass, arg->mingap, arg->minextent, arg->mindrop_sequence, arg->prefilter, arg->postfilter, arg->filter, arg, arg->strategy, arg->compressed, arg->shared_nodes_map, arg->nodepos, *(arg->shared_nodes_bloom), (*arg->unidecode_data), estimated_complexity, arg->skip_children_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2903,6 +2947,8 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std::
|
|||||||
std::set<zxy> skip_children;
|
std::set<zxy> skip_children;
|
||||||
|
|
||||||
int z;
|
int z;
|
||||||
|
int largest_written = -1;
|
||||||
|
|
||||||
for (z = iz; z <= maxzoom; z++) {
|
for (z = iz; z <= maxzoom; z++) {
|
||||||
std::atomic<long long> most(0);
|
std::atomic<long long> most(0);
|
||||||
|
|
||||||
@@ -3122,6 +3168,9 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std::
|
|||||||
if (args[thread].wrote_zoom > z) {
|
if (args[thread].wrote_zoom > z) {
|
||||||
z = args[thread].wrote_zoom;
|
z = args[thread].wrote_zoom;
|
||||||
}
|
}
|
||||||
|
if (args[thread].wrote_zoom > largest_written) {
|
||||||
|
largest_written = args[thread].wrote_zoom;
|
||||||
|
}
|
||||||
|
|
||||||
if (args[thread].still_dropping) {
|
if (args[thread].still_dropping) {
|
||||||
extend_zooms = true;
|
extend_zooms = true;
|
||||||
@@ -3184,6 +3233,10 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (largest_written >= 0 && maxzoom > largest_written) {
|
||||||
|
maxzoom = largest_written;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t j = 0; j < TEMP_FILES; j++) {
|
for (size_t j = 0; j < TEMP_FILES; j++) {
|
||||||
// Can be < 0 if there is only one source file, at z0
|
// Can be < 0 if there is only one source file, at z0
|
||||||
if (geomfd[j] >= 0) {
|
if (geomfd[j] >= 0) {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#ifndef VERSION_HPP
|
#ifndef VERSION_HPP
|
||||||
#define VERSION_HPP
|
#define VERSION_HPP
|
||||||
|
|
||||||
#define VERSION "v2.59.0"
|
#define VERSION "v2.60.0"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user