Untested support for string feature IDs

This commit is contained in:
Eric Fischer 2018-09-07 15:37:46 -07:00
parent b4cb2c6f5e
commit bd186664ac
9 changed files with 157 additions and 9 deletions

View File

@ -105,6 +105,8 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom
bool has_id = false;
unsigned long long id_value = 0;
bool has_string_id = false;
std::string string_id_value;
if (id != NULL) {
if (id->type == JSON_NUMBER) {
if (id->number >= 0) {
@ -129,6 +131,9 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom
warned_neg = true;
}
}
} else if (id->type == JSON_STRING) {
has_string_id = true;
string_id_value = id->string;
} else {
static bool warned_nan = false;
@ -194,6 +199,8 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom
sf.t = mb_geometry[t];
sf.has_id = has_id;
sf.id = id_value;
sf.has_string_id = has_string_id;
sf.string_id = string_id_value;
sf.has_tippecanoe_minzoom = (tippecanoe_minzoom != -1);
sf.tippecanoe_minzoom = tippecanoe_minzoom;
sf.has_tippecanoe_maxzoom = (tippecanoe_maxzoom != -1);

10
mvt.cpp
View File

@ -303,6 +303,7 @@ bool mvt_tile::decode(std::string &message, bool &was_compressed) {
std::vector<uint32_t> geoms;
size_t dimensions = 0;
std::vector<double> elevations;
ssize_t string_id = -1;
while (feature_reader.next()) {
switch (feature_reader.tag()) {
@ -366,6 +367,12 @@ bool mvt_tile::decode(std::string &message, bool &was_compressed) {
break;
}
case 10: /* string id */
{
string_id = feature_reader.get_uint32();
break;
}
default:
feature_reader.skip();
break;
@ -551,6 +558,9 @@ std::string mvt_tile::encode() {
if (layers[i].features[f].has_id) {
feature_writer.add_uint64(1, layers[i].features[f].id);
}
if (layers[i].features[f].string_id >= 0) {
feature_writer.add_uint32(10, layers[i].features[f].string_id);
}
std::vector<uint32_t> geometry;

View File

@ -63,6 +63,7 @@ struct mvt_feature {
int /* mvt_geometry_type */ type = 0;
unsigned long long id = 0;
bool has_id = false;
ssize_t string_id = -1;
bool dropped = false;
// For use during decoding

View File

@ -214,8 +214,22 @@ std::vector<mvt_layer> parse_layers(int fd, int z, unsigned x, unsigned y, std::
json_object *id = json_hash_get(j, "id");
if (id != NULL) {
feature.id = atoll(id->string);
feature.has_id = true;
if (id->type == JSON_NUMBER) {
feature.id = atoll(id->string);
feature.has_id = true;
} else if (id->type == JSON_STRING) {
mvt_value val;
val.type = mvt_string;
val.string_value = id->string;
std::vector<unsigned long> onto;
l->second.tag_v3_value(val, onto);
if (onto.size() != 1) {
fprintf(stderr, "Internal error: tagging a string value didn't have a length of 1\n");
exit(EXIT_FAILURE);
}
feature.string_id = onto[0];
}
}
std::map<std::string, layermap_entry> &layermap = (*layermaps)[tiling_seg];
@ -411,6 +425,7 @@ serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std::
sf.extent = 0;
sf.metapos = 0;
sf.has_id = false;
sf.has_string_id = false;
std::string layername = "unknown";
json_object *tippecanoe = json_hash_get(j, "tippecanoe");
@ -460,8 +475,13 @@ serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std::
json_object *id = json_hash_get(j, "id");
if (id != NULL) {
sf.id = atoll(id->string);
sf.has_id = true;
if (id->type == JSON_NUMBER) {
sf.id = atoll(id->string);
sf.has_id = true;
} else if (id->type == JSON_STRING) {
sf.string_id = id->string;
sf.has_string_id = true;
}
}
std::map<std::string, layermap_entry> &layermap = (*layermaps)[tiling_seg];

View File

@ -239,7 +239,8 @@ void serialize_feature(FILE *geomfile, serial_feature *sf, std::atomic<long long
serialize_byte(geomfile, sf->t, geompos, fname);
long long layer = 0;
layer |= sf->layer << 6;
layer |= sf->layer << 7;
layer |= (sf->has_string_id << 6);
layer |= (sf->seq != 0) << 5;
layer |= (sf->index != 0) << 4;
layer |= (sf->extent != 0) << 3;
@ -260,6 +261,9 @@ void serialize_feature(FILE *geomfile, serial_feature *sf, std::atomic<long long
if (sf->has_id) {
serialize_ulong_long(geomfile, sf->id, geompos, fname);
}
if (sf->has_string_id) {
serialize_string(geomfile, sf->string_id, geompos, fname);
}
serialize_int(geomfile, sf->segment, geompos, fname);
@ -307,6 +311,7 @@ serial_feature deserialize_feature(FILE *geoms, std::atomic<long long> *geompos_
sf.tippecanoe_maxzoom = -1;
sf.id = 0;
sf.has_id = false;
sf.has_string_id = false;
if (sf.layer & (1 << 1)) {
deserialize_int_io(geoms, &sf.tippecanoe_minzoom, geompos_in);
}
@ -317,6 +322,10 @@ serial_feature deserialize_feature(FILE *geoms, std::atomic<long long> *geompos_
sf.has_id = true;
deserialize_ulong_long_io(geoms, &sf.id, geompos_in);
}
if (sf.layer & (1 << 6)) {
sf.has_string_id = true;
deserialize_string_io(geoms, sf.string_id, geompos_in);
}
deserialize_int_io(geoms, &sf.segment, geompos_in);
@ -331,7 +340,7 @@ serial_feature deserialize_feature(FILE *geoms, std::atomic<long long> *geompos_
deserialize_long_long_io(geoms, &sf.extent, geompos_in);
}
sf.layer >>= 6;
sf.layer >>= 7;
sf.metapos = 0;
deserialize_long_long_io(geoms, &sf.metapos, geompos_in);
@ -440,7 +449,7 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf) {
sf.geometry = fix_polygon(sf.geometry);
}
if (!sf.has_id) {
if (!sf.has_id && !sf.has_string_id) {
if (additional[A_GENERATE_IDS]) {
sf.has_id = true;
sf.id = sf.seq + 1;

View File

@ -51,6 +51,8 @@ struct serial_feature {
bool has_id = false;
unsigned long long id = 0;
bool has_string_id = false;
std::string string_id = "";
bool has_tippecanoe_minzoom = false;
int tippecanoe_minzoom = 0;

View File

@ -180,6 +180,17 @@ void handle(std::string message, int z, unsigned x, unsigned y, std::map<std::st
v.type = mvt_uint;
v.numeric_value.uint_value = feat.id;
attributes.insert(std::pair<std::string, mvt_value>("$id", v));
} else if (feat.string_id >= 0) {
if (feat.string_id >= (ssize_t) layer.string_values.string_values.size()) {
fprintf(stderr, "Internal error: Out of bounds string ID reference\n");
exit(EXIT_FAILURE);
}
mvt_value v;
v.type = mvt_string;
v.string_value = layer.string_values.string_values[feat.string_id];
attributes.insert(std::pair<std::string, mvt_value>("$id", v));
}
@ -214,6 +225,26 @@ void handle(std::string message, int z, unsigned x, unsigned y, std::map<std::st
outfeature.has_id = true;
outfeature.id = feat.id;
}
if (feat.string_id >= 0) {
if (feat.string_id >= (ssize_t) layer.string_values.string_values.size()) {
fprintf(stderr, "Internal error: Out of bounds string ID reference\n");
exit(EXIT_FAILURE);
}
mvt_value v;
v.type = mvt_string;
v.string_value = layer.string_values.string_values[feat.string_id];
std::vector<unsigned long> onto;
outlayer.tag_v3_value(v, onto);
if (onto.size() != 1) {
fprintf(stderr, "Internal error: tagging string value didn't have size of 1\n");
exit(EXIT_FAILURE);
}
outfeature.string_id = onto[0];
}
std::vector<std::pair<std::string, mvt_value>> todo;

View File

@ -85,6 +85,8 @@ struct coalesce {
double spacing = 0;
bool has_id = false;
unsigned long long id = 0;
bool has_string_id = false;
std::string string_id;
bool operator<(const coalesce &o) const {
int cmp = coalindexcmp(this, &o);
@ -114,6 +116,9 @@ int coalcmp(const void *v1, const void *v2) {
if (c1->has_id != c2->has_id) {
return (int) c1->has_id - (int) c2->has_id;
}
if (c1->has_string_id != c2->has_string_id) {
return (int) c1->has_string_id - (int) c2->has_string_id;
}
if (c1->has_id && c2->has_id) {
if (c1->id < c2->id) {
@ -123,6 +128,14 @@ int coalcmp(const void *v1, const void *v2) {
return 1;
}
}
if (c1->has_string_id && c2->has_string_id) {
if (c1->string_id < c2->string_id) {
return -1;
}
if (c1->string_id > c2->string_id) {
return 1;
}
}
cmp = metacmp(c1->keys, c1->values, c1->stringpool, c2->keys, c2->values, c2->stringpool);
if (cmp != 0) {
@ -244,7 +257,7 @@ static int metacmp(const std::vector<long long> &keys1, const std::vector<long l
}
}
void rewrite(drawvec &geom, int z, int nextzoom, int maxzoom, long long *bbox, unsigned tx, unsigned ty, int buffer, int *within, std::atomic<long long> *geompos, FILE **geomfile, const char *fname, signed char t, int layer, long long metastart, signed char feature_minzoom, int child_shards, int max_zoom_increment, long long seq, int tippecanoe_minzoom, int tippecanoe_maxzoom, int segment, unsigned *initial_x, unsigned *initial_y, std::vector<long long> &metakeys, std::vector<long long> &metavals, bool has_id, unsigned long long id, unsigned long long index, long long extent) {
void rewrite(drawvec &geom, int z, int nextzoom, int maxzoom, long long *bbox, unsigned tx, unsigned ty, int buffer, int *within, std::atomic<long long> *geompos, FILE **geomfile, const char *fname, signed char t, int layer, long long metastart, signed char feature_minzoom, int child_shards, int max_zoom_increment, long long seq, int tippecanoe_minzoom, int tippecanoe_maxzoom, int segment, unsigned *initial_x, unsigned *initial_y, std::vector<long long> &metakeys, std::vector<long long> &metavals, bool has_id, unsigned long long id, bool has_string_id, std::string string_id, unsigned long long index, long long extent) {
if (geom.size() > 0 && (nextzoom <= maxzoom || additional[A_EXTEND_ZOOMS])) {
int xo, yo;
int span = 1 << (nextzoom - z);
@ -330,6 +343,8 @@ void rewrite(drawvec &geom, int z, int nextzoom, int maxzoom, long long *bbox, u
sf.t = t;
sf.has_id = has_id;
sf.id = id;
sf.has_string_id = has_string_id;
sf.string_id = string_id;
sf.has_tippecanoe_minzoom = tippecanoe_minzoom != -1;
sf.tippecanoe_minzoom = tippecanoe_minzoom;
sf.has_tippecanoe_maxzoom = tippecanoe_maxzoom != -1;
@ -373,7 +388,9 @@ struct partial {
double simplification = 0;
signed char t = 0;
unsigned long long id = 0;
std::string string_id;
bool has_id = 0;
bool has_string_id = 0;
ssize_t renamed = 0;
long long extent = 0;
long long clustered = 0;
@ -1340,7 +1357,7 @@ serial_feature next_feature(FILE *geoms, std::atomic<long long> *geompos_in, cha
if (*first_time && pass == 1) { /* only write out the next zoom once, even if we retry */
if (sf.tippecanoe_maxzoom == -1 || sf.tippecanoe_maxzoom >= nextzoom) {
rewrite(sf.geometry, z, nextzoom, maxzoom, sf.bbox, tx, ty, buffer, within, geompos, geomfile, fname, sf.t, sf.layer, sf.metapos, sf.feature_minzoom, child_shards, max_zoom_increment, sf.seq, sf.tippecanoe_minzoom, sf.tippecanoe_maxzoom, sf.segment, initial_x, initial_y, sf.keys, sf.values, sf.has_id, sf.id, sf.index, sf.extent);
rewrite(sf.geometry, z, nextzoom, maxzoom, sf.bbox, tx, ty, buffer, within, geompos, geomfile, fname, sf.t, sf.layer, sf.metapos, sf.feature_minzoom, child_shards, max_zoom_increment, sf.seq, sf.tippecanoe_minzoom, sf.tippecanoe_maxzoom, sf.segment, initial_x, initial_y, sf.keys, sf.values, sf.has_id, sf.id, sf.has_string_id, sf.string_id, sf.index, sf.extent);
}
}
@ -1385,6 +1402,13 @@ serial_feature next_feature(FILE *geoms, std::atomic<long long> *geompos_in, cha
attributes.insert(std::pair<std::string, mvt_value>("$id", v));
}
if (sf.has_string_id) {
mvt_value v;
v.type = mvt_string;
v.string_value = sf.string_id;
attributes.insert(std::pair<std::string, mvt_value>("$id", v));
}
mvt_value v;
v.type = mvt_string;
@ -1501,6 +1525,22 @@ void *run_prefilter(void *v) {
tmp_feature.has_id = sf.has_id;
tmp_feature.dropped = sf.dropped;
if (sf.has_string_id) {
mvt_value sv;
sv.type = mvt_string;
sv.string_value = sf.string_id;
std::vector<unsigned long> onto;
tmp_layer.tag_v3_value(sv, onto);
if (onto.size() != 1) {
fprintf(stderr, "Internal error: Expected 1 element from tagging string\n");
exit(EXIT_FAILURE);
}
tmp_feature.string_id = onto[0];
}
// Offset from tile coordinates back to world coordinates
unsigned sx = 0, sy = 0;
if (rpa->z != 0) {
@ -1969,6 +2009,8 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
p.simplification = simplification;
p.id = sf.id;
p.has_id = sf.has_id;
p.string_id = sf.string_id;
p.has_string_id = sf.has_string_id;
p.index = sf.index;
p.renamed = -1;
p.extent = sf.extent;
@ -2108,6 +2150,8 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
c.spacing = partials[i].spacing;
c.id = partials[i].id;
c.has_id = partials[i].has_id;
c.string_id = partials[i].string_id;
c.has_string_id = partials[i].has_string_id;
// printf("segment %d layer %lld is %s\n", partials[i].segment, partials[i].layer, (*layer_unmaps)[partials[i].segment][partials[i].layer].c_str());
@ -2226,6 +2270,22 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
feature.id = layer_features[x].id;
feature.has_id = layer_features[x].has_id;
if (layer_features[x].has_string_id) {
mvt_value sv;
sv.type = mvt_string;
sv.string_value = layer_features[x].string_id;
std::vector<unsigned long> onto;
layer.tag_v3_value(sv, onto);
if (onto.size() != 1) {
fprintf(stderr, "Internal error: Expected 1 element from tagging string\n");
exit(EXIT_FAILURE);
}
feature.string_id = onto[0];
}
decode_meta(layer_features[x].keys, layer_features[x].values, layer_features[x].stringpool, layer, feature, true);
for (size_t a = 0; a < layer_features[x].full_keys.size(); a++) {

View File

@ -413,6 +413,14 @@ void layer_to_geojson(mvt_layer const &layer, unsigned z, unsigned x, unsigned y
state.json_write_string("id");
state.json_write_unsigned(feat.id);
}
if (feat.string_id >= 0) {
state.json_write_string("id");
if (feat.string_id >= (ssize_t) layer.string_values.string_values.size()) {
fprintf(stderr, "Internal error: out of bounds string ID reference\n");
exit(EXIT_FAILURE);
}
state.json_write_string(layer.string_values.string_values[feat.string_id]);
}
if (name || zoom || index != 0 || sequence != 0 || extent != 0) {
state.json_write_string("tippecanoe");