Use the standard JSON writer for tilestats

With a lot of weird special cases to keep the formatting
exactly like it accidentally was before
This commit is contained in:
Eric Fischer 2018-03-16 15:19:23 -07:00
parent 08fc2f281e
commit ebb0334ef0
3 changed files with 118 additions and 106 deletions

View File

@ -56,7 +56,7 @@ tippecanoe-enumerate: enumerate.o
tippecanoe-decode: decode.o projection.o mvt.o write_json.o text.o jsonpull/jsonpull.o dirtiles.o
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3
tile-join: tile-join.o projection.o pool.o mbtiles.o mvt.o memfile.o dirtiles.o jsonpull/jsonpull.o text.o evaluator.o csv.o
tile-join: tile-join.o projection.o pool.o mbtiles.o mvt.o memfile.o dirtiles.o jsonpull/jsonpull.o text.o evaluator.o csv.o write_json.o
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
tippecanoe-json-tool: jsontool.o jsonpull/jsonpull.o csv.o text.o
@ -165,7 +165,7 @@ enumerate-test:
cmp tests/ne_110m_admin_0_countries/out/enum tests/ne_110m_admin_0_countries/out/enum.check
rm tests/ne_110m_admin_0_countries/out/enum.mbtiles tests/ne_110m_admin_0_countries/out/enum.check
join-test:
join-test: tile-join
./tippecanoe -f -z12 -o tests/join-population/tabblock_06001420.mbtiles tests/join-population/tabblock_06001420.json
./tippecanoe -f -Z5 -z10 -o tests/join-population/macarthur.mbtiles -l macarthur tests/join-population/macarthur.json
./tile-join -f -Z6 -z9 -o tests/join-population/macarthur-6-9.mbtiles tests/join-population/macarthur.mbtiles

View File

@ -16,6 +16,7 @@
#include "mbtiles.hpp"
#include "text.hpp"
#include "milo/dtoa_milo.h"
#include "write_json.hpp"
sqlite3 *mbtiles_open(char *dbname, char **argv, int forcetable) {
sqlite3 *outdb;
@ -139,36 +140,36 @@ bool type_and_string::operator!=(const type_and_string &o) const {
return false;
}
std::string tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t elements) {
void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t elements, json_writer &state) {
// Consolidate layers/attributes whose names are truncated
std::vector<std::map<std::string, layermap_entry>> lmv;
lmv.push_back(layermap1);
std::map<std::string, layermap_entry> layermap = merge_layermaps(lmv, true);
std::string out = "{\n";
state.json_write_hash();
out.append("\t\"layerCount\": ");
out.append(std::to_string(layermap.size()));
out.append(",\n");
state.nospace = true;
state.json_write_string("layerCount");
state.json_write_unsigned(layermap.size());
out.append("\t\"layers\": [\n");
state.nospace = true;
state.json_write_string("layers");
state.json_write_array();
bool first = true;
for (auto layer : layermap) {
if (!first) {
out.append(",\n");
}
first = false;
out.append("\t\t{\n");
state.nospace = true;
state.json_write_hash();
out.append("\t\t\t\"layer\": \"");
quote(out, layer.first.c_str());
out.append("\",\n");
state.nospace = true;
state.json_write_string("layer");
state.json_write_string(layer.first);
out.append("\t\t\t\"count\": ");
out.append(std::to_string(layer.second.points + layer.second.lines + layer.second.polygons));
out.append(",\n");
state.nospace = true;
state.json_write_string("count");
state.json_write_unsigned(layer.second.points + layer.second.lines + layer.second.polygons);
std::string geomtype = "Polygon";
if (layer.second.points >= layer.second.lines && layer.second.points >= layer.second.polygons) {
@ -177,45 +178,46 @@ std::string tilestats(std::map<std::string, layermap_entry> const &layermap1, si
geomtype = "LineString";
}
out.append("\t\t\t\"geometry\": \"");
quote(out, geomtype.c_str());
out.append("\",\n");
state.nospace = true;
state.json_write_string("geometry");
state.json_write_string(geomtype);
size_t attrib_count = layer.second.file_keys.size();
if (attrib_count > 1000) {
attrib_count = 1000;
}
out.append("\t\t\t\"attributeCount\": ");
out.append(std::to_string(attrib_count));
out.append(",\n");
state.nospace = true;
state.json_write_string("attributeCount");
state.json_write_unsigned(attrib_count);
out.append("\t\t\t\"attributes\": [\n");
state.nospace = true;
state.json_write_string("attributes");
state.nospace = true;
state.json_write_array();
size_t attrs = 0;
for (auto attribute : layer.second.file_keys) {
if (attrs == elements) {
break;
}
if (attrs != 0) {
out.append(",\n");
}
attrs++;
out.append("\t\t\t\t{\n");
state.nospace = true;
state.json_write_hash();
out.append("\t\t\t\t\t\"attribute\": \"");
quote(out, attribute.first.c_str());
out.append("\",\n");
state.nospace = true;
state.json_write_string("attribute");
state.json_write_string(attribute.first);
size_t val_count = attribute.second.sample_values.size();
if (val_count > 1000) {
val_count = 1000;
}
out.append("\t\t\t\t\t\"count\": ");
out.append(std::to_string(val_count));
out.append(",\n");
state.nospace = true;
state.json_write_string("count");
state.json_write_unsigned(val_count);
int type = 0;
for (auto s : attribute.second.sample_values) {
@ -234,11 +236,13 @@ std::string tilestats(std::map<std::string, layermap_entry> const &layermap1, si
type_str = "mixed";
}
out.append("\t\t\t\t\t\"type\": \"");
quote(out, type_str.c_str());
out.append("\",\n");
state.nospace = true;
state.json_write_string("type");
state.json_write_string(type_str);
out.append("\t\t\t\t\t\"values\": [\n");
state.nospace = true;
state.json_write_string("values");
state.json_write_array();
size_t vals = 0;
for (auto value : attribute.second.sample_values) {
@ -246,65 +250,50 @@ std::string tilestats(std::map<std::string, layermap_entry> const &layermap1, si
break;
}
state.nospace = true;
if (value.type == mvt_double || value.type == mvt_bool) {
if (vals != 0) {
out.append(",\n");
}
vals++;
out.append("\t\t\t\t\t\t");
out.append(value.string);
state.json_write_stringified(value.string);
} else {
std::string trunc = truncate16(value.string, 256);
if (trunc.size() == value.string.size()) {
if (vals != 0) {
out.append(",\n");
}
vals++;
out.append("\t\t\t\t\t\t\"");
quote(out, value.string.c_str());
out.append("\"");
state.json_write_string(value.string);
}
}
}
out.append("\n");
out.append("\t\t\t\t\t]");
state.nospace = true;
state.json_end_array();
if ((type & (1 << mvt_double)) != 0) {
out.append(",\n");
state.nospace = true;
state.json_write_string("min");
state.json_write_number(attribute.second.min);
out.append("\t\t\t\t\t\"min\": ");
out.append(milo::dtoa_milo(attribute.second.min));
out.append(",\n");
out.append("\t\t\t\t\t\"max\": ");
out.append(milo::dtoa_milo(attribute.second.max));
state.nospace = true;
state.json_write_string("max");
state.json_write_number(attribute.second.max);
}
out.append("\n");
out.append("\t\t\t\t}");
state.nospace = true;
state.json_end_hash();
}
out.append("\n\t\t\t]\n");
out.append("\t\t}");
state.nospace = true;
state.json_end_array();
state.nospace = true;
state.json_end_hash();
}
out.append("\n");
out.append("\t]\n");
out.append("}");
std::string out2;
for (size_t i = 0; i < out.size(); i++) {
if (out[i] != '\t' && out[i] != '\n') {
out2.push_back(out[i]);
}
}
return out2;
state.nospace = true;
state.json_end_array();
state.nospace = true;
state.json_end_hash();
}
void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fname, int minzoom, int maxzoom, double minlat, double minlon, double maxlat, double maxlon, double midlat, double midlon, int forcetable, const char *attribution, std::map<std::string, layermap_entry> const &layermap, bool vector, const char *description, bool do_tilestats) {
@ -419,8 +408,13 @@ void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fnam
std::string buf;
{
buf = "{";
aprintf(&buf, "\"vector_layers\": [ ");
json_writer state(&buf);
state.json_write_hash();
state.nospace = true;
state.json_write_string("vector_layers");
state.json_write_array();
std::vector<std::string> lnames;
for (auto ai = layermap.begin(); ai != layermap.end(); ++ai) {
@ -428,25 +422,32 @@ void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fnam
}
for (size_t i = 0; i < lnames.size(); i++) {
if (i != 0) {
aprintf(&buf, ", ");
}
auto fk = layermap.find(lnames[i]);
aprintf(&buf, "{ \"id\": \"");
quote(buf, lnames[i]);
aprintf(&buf, "\", \"description\": \"\", \"minzoom\": %d, \"maxzoom\": %d, \"fields\": {", fk->second.minzoom, fk->second.maxzoom);
state.json_write_hash();
state.json_write_string("id");
state.json_write_string(lnames[i]);
state.json_write_string("description");
state.json_write_string("");
state.json_write_string("minzoom");
state.json_write_signed(fk->second.minzoom);
state.json_write_string("maxzoom");
state.json_write_signed(fk->second.maxzoom);
state.json_write_string("fields");
state.json_write_hash();
state.nospace = true;
bool first = true;
for (auto j = fk->second.file_keys.begin(); j != fk->second.file_keys.end(); ++j) {
if (first) {
first = false;
} else {
aprintf(&buf, ", ");
}
aprintf(&buf, "\"");
quote(buf, j->first.c_str());
state.json_write_string(j->first);
int type = 0;
for (auto s : j->second.sample_values) {
@ -454,26 +455,31 @@ void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fnam
}
if (type == (1 << mvt_double)) {
aprintf(&buf, "\": \"Number\"");
state.json_write_string("Number");
} else if (type == (1 << mvt_bool)) {
aprintf(&buf, "\": \"Boolean\"");
state.json_write_string("Boolean");
} else if (type == (1 << mvt_string)) {
aprintf(&buf, "\": \"String\"");
state.json_write_string("String");
} else {
aprintf(&buf, "\": \"Mixed\"");
state.json_write_string("Mixed");
}
}
aprintf(&buf, "} }");
state.nospace = true;
state.json_end_hash();
state.json_end_hash();
}
aprintf(&buf, " ]");
state.json_end_array();
if (do_tilestats && elements > 0) {
aprintf(&buf, ",\"tilestats\": %s", tilestats(layermap, elements).c_str());
state.nospace = true;
state.json_write_string("tilestats");
tilestats(layermap, elements, state);
}
aprintf(&buf, "}");
state.nospace = true;
state.json_end_hash();
}
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('json', %Q);", buf.c_str());

View File

@ -28,6 +28,9 @@ void json_writer::json_adjust() {
if (wantnl) {
adds(",\n");
nospace = false;
} else if (nospace) {
addc(',');
nospace = false;
} else {
adds(", ");
}
@ -43,6 +46,9 @@ void json_writer::json_adjust() {
if (wantnl) {
adds(",\n");
nospace = false;
} else if (nospace) {
addc(',');
nospace = false;
} else {
adds(", ");
}
@ -116,17 +122,17 @@ void json_writer::json_end_hash() {
}
}
void json_writer::json_write_string(std::string const &s) {
void json_writer::json_write_string(std::string const &str) {
json_adjust();
addc('"');
for (size_t i = 0; i < s.size(); i++) {
if (s[i] == '\\' || s[i] == '"') {
aprintf("\\%c", s[i]);
} else if ((unsigned char) s[i] < ' ') {
aprintf("\\u%04x", s[i]);
for (size_t i = 0; i < str.size(); i++) {
if (str[i] == '\\' || str[i] == '"') {
aprintf("\\%c", str[i]);
} else if ((unsigned char) str[i] < ' ') {
aprintf("\\u%04x", str[i]);
} else {
addc(s[i]);
addc(str[i]);
}
}
addc('"');
@ -157,10 +163,10 @@ void json_writer::json_write_signed(long long v) {
aprintf("%lld", v);
}
void json_writer::json_write_stringified(std::string const &s) {
void json_writer::json_write_stringified(std::string const &str) {
json_adjust();
adds(s);
adds(str);
}
void json_writer::json_write_bool(bool b) {