Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

min cluster feature option added #940

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ the same layer, enclose them in an `all` expression so they will all be evaluate
* `--tile-stats-attributes-limit=`*count*: Include `tilestats` information about at most *count* attributes instead of the default 1000.
* `--tile-stats-sample-values-limit=`*count*: Calculate `tilestats` attribute statistics based on *count* values instead of the default 1000.
* `--tile-stats-values-limit=`*count*: Report *count* unique attribute values in `tilestats` instead of the default 100.
* `-i` __features_ or `--minimum-cluster-features=`_features_: Clusters with less than specified number of _features_ will be backed out and the features restored.

### Temporary storage

Expand Down
6 changes: 6 additions & 0 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ int geometry_scale = 0;
double simplification = 1;
size_t max_tile_size = 500000;
size_t max_tile_features = 200000;
size_t min_cluster_features = 0;
int cluster_distance = 0;
long justx = -1, justy = -1;
std::string attribute_for_id = "";
Expand Down Expand Up @@ -2605,6 +2606,7 @@ int main(int argc, char **argv) {
{"maximum-tile-bytes", required_argument, 0, 'M'},
{"maximum-tile-features", required_argument, 0, 'O'},
{"no-feature-limit", no_argument, &prevent[P_FEATURE_LIMIT], 1},
{"minimum-cluster-features", required_argument, 0, 'i'},
{"no-tile-size-limit", no_argument, &prevent[P_KILOBYTE_LIMIT], 1},
{"no-tile-compression", no_argument, &prevent[P_TILE_COMPRESSION], 1},
{"no-tile-stats", no_argument, &prevent[P_TILE_STATS], 1},
Expand Down Expand Up @@ -2990,6 +2992,10 @@ int main(int argc, char **argv) {
max_tile_features = atoll_require(optarg, "Max tile features");
break;

case 'i':
min_cluster_features = atoll_require(optarg, "Min cluster features");
break;

case 'c':
postfilter = optarg;
break;
Expand Down
1 change: 1 addition & 0 deletions main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ extern size_t TEMP_FILES;

extern size_t max_tile_size;
extern size_t max_tile_features;
extern size_t min_cluster_features;
extern int cluster_distance;
extern std::string attribute_for_id;

Expand Down
95 changes: 73 additions & 22 deletions tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ struct partial {
long long clustered = 0;
std::set<std::string> need_tilestats;
std::map<std::string, accum_state> attribute_accum_state;
std::vector<partial> clustered_features = std::vector<partial>();
};

struct partial_arg {
Expand Down Expand Up @@ -1715,6 +1716,32 @@ static bool line_is_too_small(drawvec const &geometry, int z, int detail) {
return true;
}

partial create_partial(serial_feature sf, bool reduced, int z, int line_detail, int maxzoom, double spacing, double simplification) {
partial p;
p.geoms.push_back(sf.geometry);
p.layer = sf.layer;
p.t = sf.t;
p.segment = sf.segment;
p.original_seq = sf.seq;
p.reduced = reduced;
p.z = z;
p.line_detail = line_detail;
p.maxzoom = maxzoom;
p.keys = sf.keys;
p.values = sf.values;
p.full_keys = sf.full_keys;
p.full_values = sf.full_values;
p.spacing = spacing;
p.simplification = simplification;
p.id = sf.id;
p.has_id = sf.has_id;
p.index = sf.index;
p.renamed = -1;
p.extent = sf.extent;
p.clustered = 0;
return p;
}

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) {
int line_detail;
double merge_fraction = 1;
Expand Down Expand Up @@ -1890,6 +1917,24 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
partials[which_partial].geoms.size() == 1 &&
partials[which_partial].geoms[0].size() == 1 &&
sf.geometry.size() == 1) {
if (min_cluster_features > 0) {
if (partials[which_partial].clustered + 1 < (long long int)min_cluster_features) {
if (partials[which_partial].clustered_features.empty()) {
// First feature being added to the cluster, add a copy of the original partial geom location
partial copy;
copy.geoms.push_back(partials[which_partial].geoms[0]);
partials[which_partial].clustered_features.push_back(copy);
}
// Save the feature that was added to the cluster until it passes the minimum number of features
partial p = create_partial(sf, false, z, line_detail, maxzoom, spacing, simplification);
partials[which_partial].clustered_features.push_back(p);
} else {
// Clear out any saved features as the cluster will not be backed out
if (!partials[which_partial].clustered_features.empty()) {
partials[which_partial].clustered_features.clear();
}
}
}
double x = (double) partials[which_partial].geoms[0][0].x * partials[which_partial].clustered;
double y = (double) partials[which_partial].geoms[0][0].y * partials[which_partial].clustered;
x += sf.geometry[0].x;
Expand Down Expand Up @@ -1974,28 +2019,7 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
}
}

partial p;
p.geoms.push_back(sf.geometry);
p.layer = sf.layer;
p.t = sf.t;
p.segment = sf.segment;
p.original_seq = sf.seq;
p.reduced = reduced;
p.z = z;
p.line_detail = line_detail;
p.maxzoom = maxzoom;
p.keys = sf.keys;
p.values = sf.values;
p.full_keys = sf.full_keys;
p.full_values = sf.full_values;
p.spacing = spacing;
p.simplification = simplification;
p.id = sf.id;
p.has_id = sf.has_id;
p.index = sf.index;
p.renamed = -1;
p.extent = sf.extent;
p.clustered = 0;
partial p = create_partial(sf, reduced, z, line_detail, maxzoom, spacing, simplification);
partials.push_back(p);
}

Expand All @@ -2022,6 +2046,33 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
shared_nodes = just_shared_nodes;
}

{
std::vector<struct partial> backed_out_partials;
for (size_t i = 0; i < partials.size(); i++) {
partial &p = partials[i];

if (p.clustered > 0 && p.clustered+1 < (long long int)min_cluster_features) {
// Not enough features, back out the cluster
for (size_t j = 0; j < p.clustered_features.size(); j++) {
partial copy = p.clustered_features[j];
if (j == 0 &&
p.t == VT_POINT &&
p.geoms.size() == 1 &&
p.geoms[0].size() == 1) {
// The copy with the location for this feature, restore it's location
p.geoms.clear();
p.geoms.push_back(p.clustered_features.front().geoms.front());
p.clustered = 0;
} else {
// One of the features clustered into this feature, restore it
backed_out_partials.push_back(copy);
}
}
}
}
partials.insert( partials.end(), backed_out_partials.begin(), backed_out_partials.end());
}

for (size_t i = 0; i < partials.size(); i++) {
partial &p = partials[i];

Expand Down