Add an option to use a specified feature attribute as the feature ID

This commit is contained in:
Eric Fischer 2018-11-02 15:21:52 -07:00
parent 53de8a2014
commit bd2423a5ea
11 changed files with 155 additions and 4 deletions

View File

@ -1,6 +1,7 @@
## 1.32.3 ## 1.32.3
* Add an option to convert stringified number feature IDs to numbers * Add an option to convert stringified number feature IDs to numbers
* Add an option to use a specified feature attribute as the feature ID
## 1.32.2 ## 1.32.2

View File

@ -353,6 +353,7 @@ resolution is obtained than by using a smaller _maxzoom_ or _detail_.
to specify how the named _attribute_ is accumulated onto the attribute of the same name in a feature that does survive. to specify how the named _attribute_ is accumulated onto the attribute of the same name in a feature that does survive.
* `-pe` or `--empty-csv-columns-are-null`: Treat empty CSV columns as nulls rather than as empty strings. * `-pe` or `--empty-csv-columns-are-null`: Treat empty CSV columns as nulls rather than as empty strings.
* `-aI` or `--convert-stringified-ids-to-numbers`: If a feature ID is the string representation of a number, convert it to a plain number to use as the feature ID. * `-aI` or `--convert-stringified-ids-to-numbers`: If a feature ID is the string representation of a number, convert it to a plain number to use as the feature ID.
* `--use-attribute-for-id=`*name*: Use the attribute with the specified *name* as if it were specified as the feature ID. (If this attribute is a stringified number, you must also use `-aI` to convert it to a number.)
### Filtering features by attributes ### Filtering features by attributes

View File

@ -76,6 +76,7 @@ size_t max_tile_size = 500000;
size_t max_tile_features = 200000; size_t max_tile_features = 200000;
int cluster_distance = 0; int cluster_distance = 0;
long justx = -1, justy = -1; long justx = -1, justy = -1;
std::string attribute_for_id = "";
int prevent[256]; int prevent[256];
int additional[256]; int additional[256];
@ -2542,6 +2543,7 @@ int main(int argc, char **argv) {
{"accumulate-attribute", required_argument, 0, 'E'}, {"accumulate-attribute", required_argument, 0, 'E'},
{"empty-csv-columns-are-null", no_argument, &prevent[P_EMPTY_CSV_COLUMNS], 1}, {"empty-csv-columns-are-null", no_argument, &prevent[P_EMPTY_CSV_COLUMNS], 1},
{"convert-stringified-ids-to-numbers", no_argument, &additional[A_CONVERT_NUMERIC_IDS], 1}, {"convert-stringified-ids-to-numbers", no_argument, &additional[A_CONVERT_NUMERIC_IDS], 1},
{"use-attribute-for-id", required_argument, 0, '~'},
{"Filtering features by attributes", 0, 0, 0}, {"Filtering features by attributes", 0, 0, 0},
{"feature-filter-file", required_argument, 0, 'J'}, {"feature-filter-file", required_argument, 0, 'J'},
@ -2694,6 +2696,8 @@ int main(int argc, char **argv) {
fprintf(stderr, "%s: Can't parse bounding box --%s=%s\n", argv[0], opt, optarg); fprintf(stderr, "%s: Can't parse bounding box --%s=%s\n", argv[0], opt, optarg);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} else if (strcmp(opt, "use-attribute-for-id") == 0) {
attribute_for_id = optarg;
} else { } else {
fprintf(stderr, "%s: Unrecognized option --%s\n", argv[0], opt); fprintf(stderr, "%s: Unrecognized option --%s\n", argv[0], opt);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);

View File

@ -3,6 +3,7 @@
#include <stddef.h> #include <stddef.h>
#include <atomic> #include <atomic>
#include <string>
struct index { struct index {
long long start = 0; long long start = 0;
@ -46,6 +47,7 @@ extern size_t TEMP_FILES;
extern size_t max_tile_size; extern size_t max_tile_size;
extern size_t max_tile_features; extern size_t max_tile_features;
extern int cluster_distance; extern int cluster_distance;
extern std::string attribute_for_id;
int mkstemp_cloexec(char *name); int mkstemp_cloexec(char *name);
FILE *fopen_oflag(const char *name, const char *mode, int oflag); FILE *fopen_oflag(const char *name, const char *mode, int oflag);

View File

@ -426,6 +426,8 @@ to specify how the named \fIattribute\fP is accumulated onto the attribute of th
\fB\fC\-pe\fR or \fB\fC\-\-empty\-csv\-columns\-are\-null\fR: Treat empty CSV columns as nulls rather than as empty strings. \fB\fC\-pe\fR or \fB\fC\-\-empty\-csv\-columns\-are\-null\fR: Treat empty CSV columns as nulls rather than as empty strings.
.IP \(bu 2 .IP \(bu 2
\fB\fC\-aI\fR or \fB\fC\-\-convert\-stringified\-ids\-to\-numbers\fR: If a feature ID is the string representation of a number, convert it to a plain number to use as the feature ID. \fB\fC\-aI\fR or \fB\fC\-\-convert\-stringified\-ids\-to\-numbers\fR: If a feature ID is the string representation of a number, convert it to a plain number to use as the feature ID.
.IP \(bu 2
\fB\fC\-\-use\-attribute\-for\-id=\fR\fIname\fP: Use the attribute with the specified \fIname\fP as if it were specified as the feature ID. (If this attribute is a stringified number, you must also use \fB\fC\-aI\fR to convert it to a number.)
.RE .RE
.SS Filtering features by attributes .SS Filtering features by attributes
.RS .RS

View File

@ -182,6 +182,7 @@ static void write_geometry(drawvec const &dv, std::atomic<long long> *fpos, FILE
} }
} }
// called from generating the next zoom level
void serialize_feature(FILE *geomfile, serial_feature *sf, std::atomic<long long> *geompos, const char *fname, long long wx, long long wy, bool include_minzoom) { void serialize_feature(FILE *geomfile, serial_feature *sf, std::atomic<long long> *geompos, const char *fname, long long wx, long long wy, bool include_minzoom) {
serialize_byte(geomfile, sf->t, geompos, fname); serialize_byte(geomfile, sf->t, geompos, fname);
@ -372,6 +373,7 @@ static long long scale_geometry(struct serialization_state *sst, long long *bbox
return geom.size(); return geom.size();
} }
// called from frontends
int serialize_feature(struct serialization_state *sst, serial_feature &sf) { int serialize_feature(struct serialization_state *sst, serial_feature &sf) {
struct reader *r = &(*sst->readers)[sst->segment]; struct reader *r = &(*sst->readers)[sst->segment];
@ -573,6 +575,44 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf) {
} }
coerce_value(sf.full_keys[i], sf.full_values[i].type, sf.full_values[i].s, sst->attribute_types); coerce_value(sf.full_keys[i], sf.full_values[i].type, sf.full_values[i].s, sst->attribute_types);
if (sf.full_keys[i] == attribute_for_id) {
if (sf.full_values[i].type != mvt_double && !additional[A_CONVERT_NUMERIC_IDS]) {
static bool warned = false;
if (!warned) {
fprintf(stderr, "Warning: Attribute \"%s\"=\"%s\" as feature ID is not a number\n", sf.full_keys[i].c_str(), sf.full_values[i].s.c_str());
warned = true;
}
} else {
char *err;
long long id_value = strtoull(sf.full_values[i].s.c_str(), &err, 10);
if (err != NULL && *err != '\0') {
static bool warned_frac = false;
if (!warned_frac) {
fprintf(stderr, "Warning: Can't represent non-integer feature ID %s\n", sf.full_values[i].s.c_str());
warned_frac = true;
}
} else if (std::to_string(id_value) != sf.full_values[i].s) {
static bool warned = false;
if (!warned) {
fprintf(stderr, "Warning: Can't represent too-large feature ID %s\n", sf.full_values[i].s.c_str());
warned = true;
}
} else {
sf.id = id_value;
sf.has_id = true;
sf.full_keys.erase(sf.full_keys.begin() + i);
sf.full_values.erase(sf.full_values.begin() + i);
continue;
}
}
}
} }
if (!sst->filters) { if (!sst->filters) {

View File

@ -4,3 +4,8 @@
{ "type": "Feature", "id": "9837489273489273894728943728903480989080938597489274389", "properties": {}, "geometry": { "type": "Point", "coordinates": [ 4, 1 ] } } { "type": "Feature", "id": "9837489273489273894728943728903480989080938597489274389", "properties": {}, "geometry": { "type": "Point", "coordinates": [ 4, 1 ] } }
{ "type": "Feature", "id": 9837489273489273894728943728903480989080938597489274389, "properties": {}, "geometry": { "type": "Point", "coordinates": [ 5, 1 ] } } { "type": "Feature", "id": 9837489273489273894728943728903480989080938597489274389, "properties": {}, "geometry": { "type": "Point", "coordinates": [ 5, 1 ] } }
{ "type": "Feature", "id": "1e5", "properties": {}, "geometry": { "type": "Point", "coordinates": [ 6, 1 ] } } { "type": "Feature", "id": "1e5", "properties": {}, "geometry": { "type": "Point", "coordinates": [ 6, 1 ] } }
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 27, "something": false }, "geometry": { "type": "Point", "coordinates": [ 7, 1 ] } }
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": "27", "something": false }, "geometry": { "type": "Point", "coordinates": [ 8, 1 ] } }
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 27.5, "something": false }, "geometry": { "type": "Point", "coordinates": [ 9, 1 ] } }
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 98237489273489274389728497289472839478297489274892734892, "something": false }, "geometry": { "type": "Point", "coordinates": [ 10, 1 ] } }
{ "type": "Feature", "properties": { "yes": "no", "special": 27, "something": false }, "geometry": { "type": "Point", "coordinates": [ 11, 1 ] } }

View File

@ -1,9 +1,9 @@
{ "type": "FeatureCollection", "properties": { { "type": "FeatureCollection", "properties": {
"bounds": "1.000000,1.000000,6.000000,1.000000", "bounds": "1.000000,1.000000,11.000000,1.000000",
"center": "1.000000,1.000000,0", "center": "1.000000,1.000000,0",
"description": "tests/stringid/out/-z0.json.check.mbtiles", "description": "tests/stringid/out/-z0.json.check.mbtiles",
"format": "pbf", "format": "pbf",
"json": "{\"vector_layers\": [ { \"id\": \"in\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 0, \"fields\": {} } ],\"tilestats\": {\"layerCount\": 1,\"layers\": [{\"layer\": \"in\",\"count\": 6,\"geometry\": \"Point\",\"attributeCount\": 0,\"attributes\": []}]}}", "json": "{\"vector_layers\": [ { \"id\": \"in\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 0, \"fields\": {\"something\": \"Boolean\", \"special\": \"Mixed\", \"yes\": \"String\"} } ],\"tilestats\": {\"layerCount\": 1,\"layers\": [{\"layer\": \"in\",\"count\": 11,\"geometry\": \"Point\",\"attributeCount\": 3,\"attributes\": [{\"attribute\": \"something\",\"count\": 1,\"type\": \"boolean\",\"values\": [false]},{\"attribute\": \"special\",\"count\": 4,\"type\": \"mixed\",\"values\": [\"27\",27,27.5,9.823748927348929e+55],\"min\": 27,\"max\": 9.823748927348929e+55},{\"attribute\": \"yes\",\"count\": 1,\"type\": \"string\",\"values\": [\"no\"]}]}]}}",
"maxzoom": "0", "maxzoom": "0",
"minzoom": "0", "minzoom": "0",
"name": "tests/stringid/out/-z0.json.check.mbtiles", "name": "tests/stringid/out/-z0.json.check.mbtiles",
@ -23,6 +23,16 @@
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 4.921875, 1.054628 ] } } { "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 4.921875, 1.054628 ] } }
, ,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 5.976562, 1.054628 ] } } { "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 5.976562, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 27, "something": false }, "geometry": { "type": "Point", "coordinates": [ 6.943359, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": "27", "something": false }, "geometry": { "type": "Point", "coordinates": [ 7.998047, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 27.5, "something": false }, "geometry": { "type": "Point", "coordinates": [ 8.964844, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 9.823748927348929e+55, "something": false }, "geometry": { "type": "Point", "coordinates": [ 9.931641, 1.054628 ] } }
,
{ "type": "Feature", "properties": { "yes": "no", "special": 27, "something": false }, "geometry": { "type": "Point", "coordinates": [ 10.986328, 1.054628 ] } }
] } ] }
] } ] }
] } ] }

View File

@ -0,0 +1,38 @@
{ "type": "FeatureCollection", "properties": {
"bounds": "1.000000,1.000000,11.000000,1.000000",
"center": "1.000000,1.000000,0",
"description": "tests/stringid/out/-z0_--use-attribute-for-id_special.json.check.mbtiles",
"format": "pbf",
"json": "{\"vector_layers\": [ { \"id\": \"in\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 0, \"fields\": {\"something\": \"Boolean\", \"special\": \"Mixed\", \"yes\": \"String\"} } ],\"tilestats\": {\"layerCount\": 1,\"layers\": [{\"layer\": \"in\",\"count\": 11,\"geometry\": \"Point\",\"attributeCount\": 3,\"attributes\": [{\"attribute\": \"something\",\"count\": 1,\"type\": \"boolean\",\"values\": [false]},{\"attribute\": \"special\",\"count\": 3,\"type\": \"mixed\",\"values\": [\"27\",27.5,9.823748927348929e+55],\"min\": 27.5,\"max\": 9.823748927348929e+55},{\"attribute\": \"yes\",\"count\": 1,\"type\": \"string\",\"values\": [\"no\"]}]}]}}",
"maxzoom": "0",
"minzoom": "0",
"name": "tests/stringid/out/-z0_--use-attribute-for-id_special.json.check.mbtiles",
"type": "overlay",
"version": "2"
}, "features": [
{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "id": 12345, "properties": { }, "geometry": { "type": "Point", "coordinates": [ 0.966797, 1.054628 ] } }
,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 1.933594, 1.054628 ] } }
,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.054628 ] } }
,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 3.955078, 1.054628 ] } }
,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 4.921875, 1.054628 ] } }
,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 5.976562, 1.054628 ] } }
,
{ "type": "Feature", "id": 27, "properties": { "yes": "no", "something": false }, "geometry": { "type": "Point", "coordinates": [ 6.943359, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": "27", "something": false }, "geometry": { "type": "Point", "coordinates": [ 7.998047, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 27.5, "something": false }, "geometry": { "type": "Point", "coordinates": [ 8.964844, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 9.823748927348929e+55, "something": false }, "geometry": { "type": "Point", "coordinates": [ 9.931641, 1.054628 ] } }
,
{ "type": "Feature", "id": 27, "properties": { "yes": "no", "something": false }, "geometry": { "type": "Point", "coordinates": [ 10.986328, 1.054628 ] } }
] }
] }
] }

View File

@ -1,9 +1,9 @@
{ "type": "FeatureCollection", "properties": { { "type": "FeatureCollection", "properties": {
"bounds": "1.000000,1.000000,6.000000,1.000000", "bounds": "1.000000,1.000000,11.000000,1.000000",
"center": "1.000000,1.000000,0", "center": "1.000000,1.000000,0",
"description": "tests/stringid/out/-z0_-aI.json.check.mbtiles", "description": "tests/stringid/out/-z0_-aI.json.check.mbtiles",
"format": "pbf", "format": "pbf",
"json": "{\"vector_layers\": [ { \"id\": \"in\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 0, \"fields\": {} } ],\"tilestats\": {\"layerCount\": 1,\"layers\": [{\"layer\": \"in\",\"count\": 6,\"geometry\": \"Point\",\"attributeCount\": 0,\"attributes\": []}]}}", "json": "{\"vector_layers\": [ { \"id\": \"in\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 0, \"fields\": {\"something\": \"Boolean\", \"special\": \"Mixed\", \"yes\": \"String\"} } ],\"tilestats\": {\"layerCount\": 1,\"layers\": [{\"layer\": \"in\",\"count\": 11,\"geometry\": \"Point\",\"attributeCount\": 3,\"attributes\": [{\"attribute\": \"something\",\"count\": 1,\"type\": \"boolean\",\"values\": [false]},{\"attribute\": \"special\",\"count\": 4,\"type\": \"mixed\",\"values\": [\"27\",27,27.5,9.823748927348929e+55],\"min\": 27,\"max\": 9.823748927348929e+55},{\"attribute\": \"yes\",\"count\": 1,\"type\": \"string\",\"values\": [\"no\"]}]}]}}",
"maxzoom": "0", "maxzoom": "0",
"minzoom": "0", "minzoom": "0",
"name": "tests/stringid/out/-z0_-aI.json.check.mbtiles", "name": "tests/stringid/out/-z0_-aI.json.check.mbtiles",
@ -23,6 +23,16 @@
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 4.921875, 1.054628 ] } } { "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 4.921875, 1.054628 ] } }
, ,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 5.976562, 1.054628 ] } } { "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 5.976562, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 27, "something": false }, "geometry": { "type": "Point", "coordinates": [ 6.943359, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": "27", "something": false }, "geometry": { "type": "Point", "coordinates": [ 7.998047, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 27.5, "something": false }, "geometry": { "type": "Point", "coordinates": [ 8.964844, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 9.823748927348929e+55, "something": false }, "geometry": { "type": "Point", "coordinates": [ 9.931641, 1.054628 ] } }
,
{ "type": "Feature", "properties": { "yes": "no", "special": 27, "something": false }, "geometry": { "type": "Point", "coordinates": [ 10.986328, 1.054628 ] } }
] } ] }
] } ] }
] } ] }

View File

@ -0,0 +1,38 @@
{ "type": "FeatureCollection", "properties": {
"bounds": "1.000000,1.000000,11.000000,1.000000",
"center": "1.000000,1.000000,0",
"description": "tests/stringid/out/-z0_-aI_--use-attribute-for-id_special.json.check.mbtiles",
"format": "pbf",
"json": "{\"vector_layers\": [ { \"id\": \"in\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 0, \"fields\": {\"something\": \"Boolean\", \"special\": \"Number\", \"yes\": \"String\"} } ],\"tilestats\": {\"layerCount\": 1,\"layers\": [{\"layer\": \"in\",\"count\": 11,\"geometry\": \"Point\",\"attributeCount\": 3,\"attributes\": [{\"attribute\": \"something\",\"count\": 1,\"type\": \"boolean\",\"values\": [false]},{\"attribute\": \"special\",\"count\": 2,\"type\": \"number\",\"values\": [27.5,9.823748927348929e+55],\"min\": 27.5,\"max\": 9.823748927348929e+55},{\"attribute\": \"yes\",\"count\": 1,\"type\": \"string\",\"values\": [\"no\"]}]}]}}",
"maxzoom": "0",
"minzoom": "0",
"name": "tests/stringid/out/-z0_-aI_--use-attribute-for-id_special.json.check.mbtiles",
"type": "overlay",
"version": "2"
}, "features": [
{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "id": 12345, "properties": { }, "geometry": { "type": "Point", "coordinates": [ 0.966797, 1.054628 ] } }
,
{ "type": "Feature", "id": 12345, "properties": { }, "geometry": { "type": "Point", "coordinates": [ 1.933594, 1.054628 ] } }
,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.054628 ] } }
,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 3.955078, 1.054628 ] } }
,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 4.921875, 1.054628 ] } }
,
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 5.976562, 1.054628 ] } }
,
{ "type": "Feature", "id": 27, "properties": { "yes": "no", "something": false }, "geometry": { "type": "Point", "coordinates": [ 6.943359, 1.054628 ] } }
,
{ "type": "Feature", "id": 27, "properties": { "yes": "no", "something": false }, "geometry": { "type": "Point", "coordinates": [ 7.998047, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 27.5, "something": false }, "geometry": { "type": "Point", "coordinates": [ 8.964844, 1.054628 ] } }
,
{ "type": "Feature", "id": 1234, "properties": { "yes": "no", "special": 9.823748927348929e+55, "something": false }, "geometry": { "type": "Point", "coordinates": [ 9.931641, 1.054628 ] } }
,
{ "type": "Feature", "id": 27, "properties": { "yes": "no", "something": false }, "geometry": { "type": "Point", "coordinates": [ 10.986328, 1.054628 ] } }
] }
] }
] }