Simplify the filter format, and actually run the filter.

This commit is contained in:
Eric Fischer 2017-08-28 13:42:14 -07:00
parent 7be21f6046
commit 5878213516
7 changed files with 49 additions and 19 deletions

View File

@ -482,7 +482,14 @@ The options are:
* `-x` *key* or `--exclude=`*key*: Remove attributes of type *key* from the output. You can use this to remove the field you are matching against if you no longer need it after joining, or to remove any other attributes you don't want.
* `-i` or `--if-matched`: Only include features that matched the CSV.
* `-J` *filter-file* or `--gl-filter-file`=*filter-file*: Check features against a per-layer filter (as defined in the [Mapbox GL Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#types-filter)) and only include those that match.
* `-j` *filter* or `--gl-filter`=*filter*: Check features against a per-layer filter (as defined in the [Mapbox GL Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#types-filter)) and only include those that match. Any features in layers that have no filter specified will be passed through.
* `-J` *filter-file* or `--gl-filter-file`=*filter-file*: Like `-j`, but read the filter from a file.
Example: to find the Natural Earth countries with low `scalerank` but high `LABELRANK`:
```
tile-join -o filtered.mbtiles -j '{ "ne_10m_admin_0_countries": [ "all", [ "<", "scalerank", 3 ], [ ">", "LABELRANK", 5 ] ] }' ne_10m_admin_0_countries.mbtiles
```
### Setting or disabling tile size limits

View File

@ -174,19 +174,7 @@ bool eval(std::map<std::string, mvt_value> const &feature, json_object *f) {
}
bool evaluate(std::map<std::string, mvt_value> const &feature, std::string const &layer, json_object *filter) {
json_object *layers = json_hash_get(filter, "layers");
if (layers == NULL) {
fprintf(stderr, "Filter: no \"layers\" key at top level\n");
exit(EXIT_FAILURE);
}
if (layers->type != JSON_HASH) {
fprintf(stderr, "Filter: \"layers\" is not a hash\n");
exit(EXIT_FAILURE);
}
json_object *f = json_hash_get(layers, layer.c_str());
json_object *f = json_hash_get(filter, layer.c_str());
if (f != NULL) {
return eval(feature, f);

View File

@ -6,6 +6,6 @@
#include "jsonpull/jsonpull.h"
#include "mvt.hpp"
bool evaluate(std::map<std::string, mvt_value> const &feature, std::string const &layer, json_pull *filter);
bool evaluate(std::map<std::string, mvt_value> const &feature, std::string const &layer, json_object *filter);
#endif

View File

@ -69,7 +69,7 @@ json_pull *json_begin_file(FILE *f) {
}
static ssize_t read_string(json_pull *j, char *buffer, size_t n) {
char *cp = j->source;
const char *cp = j->source;
size_t out = 0;
while (out < n && cp[out] != '\0') {
@ -81,7 +81,7 @@ static ssize_t read_string(json_pull *j, char *buffer, size_t n) {
return out;
}
json_pull *json_begin_string(char *s) {
json_pull *json_begin_string(const char *s) {
return json_begin(read_string, s);
}

View File

@ -46,7 +46,7 @@ typedef struct json_pull {
int line;
ssize_t (*read)(struct json_pull *, char *buf, size_t n);
void *source;
const void *source;
char *buffer;
ssize_t buffer_tail;
ssize_t buffer_head;
@ -56,7 +56,7 @@ typedef struct json_pull {
} json_pull;
json_pull *json_begin_file(FILE *f);
json_pull *json_begin_string(char *s);
json_pull *json_begin_string(const char *s);
json_pull *json_begin(ssize_t (*read)(struct json_pull *, char *buffer, size_t n), void *source);
void json_end(json_pull *p);

View File

@ -566,6 +566,18 @@ The options are:
\fB\fC\-x\fR \fIkey\fP or \fB\fC\-\-exclude=\fR\fIkey\fP: Remove attributes of type \fIkey\fP from the output. You can use this to remove the field you are matching against if you no longer need it after joining, or to remove any other attributes you don't want.
.IP \(bu 2
\fB\fC\-i\fR or \fB\fC\-\-if\-matched\fR: Only include features that matched the CSV.
.IP \(bu 2
\fB\fC\-j\fR \fIfilter\fP or \fB\fC\-\-gl\-filter\fR=\fIfilter\fP: Check features against a per\-layer filter (as defined in the Mapbox GL Style Specification \[la]https://www.mapbox.com/mapbox-gl-js/style-spec/#types-filter\[ra]) and only include those that match. Any features in layers that have no filter specified will be passed through.
.IP \(bu 2
\fB\fC\-J\fR \fIfilter\-file\fP or \fB\fC\-\-gl\-filter\-file\fR=\fIfilter\-file\fP: Like \fB\fC\-j\fR, but read the filter from a file.
.RE
.PP
Example: to find the Natural Earth countries with low \fB\fCscalerank\fR but high \fB\fCLABELRANK\fR:
.PP
.RS
.nf
tile\-join \-o filtered.mbtiles \-j '{ "ne_10m_admin_0_countries": [ "all", [ "<", "scalerank", 3 ], [ ">", "LABELRANK", 5 ] ] }' ne_10m_admin_0_countries.mbtiles
.fi
.RE
.SS Setting or disabling tile size limits
.RS

View File

@ -22,6 +22,7 @@
#include "mbtiles.hpp"
#include "geometry.hpp"
#include "dirtiles.hpp"
#include "evaluator.hpp"
#include <fstream>
#include <sstream>
#include <algorithm>
@ -130,6 +131,10 @@ void handle(std::string message, int z, unsigned x, unsigned y, std::map<std::st
}
attributes.insert(std::pair<std::string, mvt_value>("$type", v));
if (!evaluate(attributes, layer.name, filter)) {
continue;
}
}
mvt_feature outfeature;
@ -1050,6 +1055,19 @@ json_object *read_filter(const char *fname) {
return filter;
}
json_object *parse_filter(const char *s) {
json_pull *jp = json_begin_string(s);
json_object *filter = json_read_tree(jp);
if (filter == NULL) {
fprintf(stderr, "Could not parse filter %s\n", s);
fprintf(stderr, "%s\n", jp->error);
exit(EXIT_FAILURE);
}
// XXX clone tree instead of leaving pull open
return filter;
}
int main(int argc, char **argv) {
char *out_mbtiles = NULL;
char *out_dir = NULL;
@ -1095,6 +1113,7 @@ int main(int argc, char **argv) {
{"maximum-zoom", required_argument, 0, 'z'},
{"minimum-zoom", required_argument, 0, 'Z'},
{"gl-filter-file", required_argument, 0, 'J'},
{"gl-filter", required_argument, 0, 'j'},
{"no-tile-size-limit", no_argument, &pk, 1},
{"no-tile-compression", no_argument, &pC, 1},
@ -1163,6 +1182,10 @@ int main(int argc, char **argv) {
filter = read_filter(optarg);
break;
case 'j':
filter = parse_filter(optarg);
break;
case 'p':
if (strcmp(optarg, "k") == 0) {
pk = true;