mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-02 08:25:40 +02:00
Merge branch 'master' into plugins
This commit is contained in:
+274
-20
@@ -13,6 +13,7 @@
|
||||
#include <map>
|
||||
#include "mvt.hpp"
|
||||
#include "mbtiles.hpp"
|
||||
#include "text.hpp"
|
||||
|
||||
sqlite3 *mbtiles_open(char *dbname, char **argv, int forcetable) {
|
||||
sqlite3 *outdb;
|
||||
@@ -126,6 +127,178 @@ bool type_and_string::operator<(const type_and_string &o) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool type_and_string::operator!=(const type_and_string &o) const {
|
||||
if (type != o.type) {
|
||||
return true;
|
||||
}
|
||||
if (string != o.string) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string tilestats(std::map<std::string, layermap_entry> const &layermap1) {
|
||||
// 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";
|
||||
|
||||
out.append("\t\"layerCount\": ");
|
||||
out.append(std::to_string(layermap.size()));
|
||||
out.append(",\n");
|
||||
|
||||
out.append("\t\"layers\": [\n");
|
||||
|
||||
bool first = true;
|
||||
for (auto layer : layermap) {
|
||||
if (!first) {
|
||||
out.append(",\n");
|
||||
}
|
||||
first = false;
|
||||
|
||||
out.append("\t\t{\n");
|
||||
|
||||
out.append("\t\t\t\"layer\": \"");
|
||||
quote(out, layer.first.c_str());
|
||||
out.append("\",\n");
|
||||
|
||||
out.append("\t\t\t\"count\": ");
|
||||
out.append(std::to_string(layer.second.points + layer.second.lines + layer.second.polygons));
|
||||
out.append(",\n");
|
||||
|
||||
std::string geomtype = "Polygon";
|
||||
if (layer.second.points >= layer.second.lines && layer.second.points >= layer.second.polygons) {
|
||||
geomtype = "Point";
|
||||
} else if (layer.second.lines >= layer.second.polygons && layer.second.lines >= layer.second.points) {
|
||||
geomtype = "LineString";
|
||||
}
|
||||
|
||||
out.append("\t\t\t\"geometry\": \"");
|
||||
quote(out, geomtype.c_str());
|
||||
out.append("\",\n");
|
||||
|
||||
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");
|
||||
|
||||
out.append("\t\t\t\"attributes\": [\n");
|
||||
|
||||
size_t attrs = 0;
|
||||
for (auto attribute : layer.second.file_keys) {
|
||||
if (attrs == 100) {
|
||||
break;
|
||||
}
|
||||
if (attrs != 0) {
|
||||
out.append(",\n");
|
||||
}
|
||||
attrs++;
|
||||
|
||||
out.append("\t\t\t\t{\n");
|
||||
|
||||
out.append("\t\t\t\t\t\"attribute\": \"");
|
||||
quote(out, attribute.first.c_str());
|
||||
out.append("\",\n");
|
||||
|
||||
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");
|
||||
|
||||
int type = 0;
|
||||
for (auto s : attribute.second.sample_values) {
|
||||
type |= (1 << s.type);
|
||||
}
|
||||
|
||||
std::string type_str;
|
||||
// No "null" because null attributes are dropped
|
||||
if (type == (1 << mvt_double)) {
|
||||
type_str = "number";
|
||||
} else if (type == (1 << mvt_bool)) {
|
||||
type_str = "boolean";
|
||||
} else if (type == (1 << mvt_string)) {
|
||||
type_str = "string";
|
||||
} else {
|
||||
type_str = "mixed";
|
||||
}
|
||||
|
||||
out.append("\t\t\t\t\t\"type\": \"");
|
||||
quote(out, type_str.c_str());
|
||||
out.append("\",\n");
|
||||
|
||||
out.append("\t\t\t\t\t\"values\": [\n");
|
||||
|
||||
size_t vals = 0;
|
||||
for (auto value : attribute.second.sample_values) {
|
||||
if (vals == 100) {
|
||||
break;
|
||||
}
|
||||
if (vals != 0) {
|
||||
out.append(",\n");
|
||||
}
|
||||
vals++;
|
||||
|
||||
if (value.type == mvt_double || value.type == mvt_bool) {
|
||||
out.append("\t\t\t\t\t\t");
|
||||
out.append(value.string);
|
||||
} else {
|
||||
std::string trunc = truncate16(value.string, 256);
|
||||
|
||||
if (trunc.size() == value.string.size()) {
|
||||
out.append("\t\t\t\t\t\t\"");
|
||||
quote(out, value.string.c_str());
|
||||
out.append("\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.append("\n");
|
||||
out.append("\t\t\t\t\t]");
|
||||
|
||||
if ((type & (1 << mvt_double)) != 0) {
|
||||
out.append(",\n");
|
||||
|
||||
out.append("\t\t\t\t\t\"min\": ");
|
||||
out.append(std::to_string(attribute.second.min));
|
||||
out.append(",\n");
|
||||
|
||||
out.append("\t\t\t\t\t\"max\": ");
|
||||
out.append(std::to_string(attribute.second.max));
|
||||
}
|
||||
|
||||
out.append("\n");
|
||||
out.append("\t\t\t\t}");
|
||||
}
|
||||
|
||||
out.append("\n\t\t\t]\n");
|
||||
out.append("\t\t}");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
char *sql, *err;
|
||||
|
||||
@@ -252,9 +425,8 @@ void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fnam
|
||||
quote(buf, lnames[i]);
|
||||
aprintf(&buf, "\", \"description\": \"\", \"minzoom\": %d, \"maxzoom\": %d, \"fields\": {", fk->second.minzoom, fk->second.maxzoom);
|
||||
|
||||
std::set<type_and_string>::iterator j;
|
||||
bool first = true;
|
||||
for (j = fk->second.file_keys.begin(); j != fk->second.file_keys.end(); ++j) {
|
||||
for (auto j = fk->second.file_keys.begin(); j != fk->second.file_keys.end(); ++j) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
@@ -262,25 +434,30 @@ void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fnam
|
||||
}
|
||||
|
||||
aprintf(&buf, "\"");
|
||||
quote(buf, j->string);
|
||||
quote(buf, j->first.c_str());
|
||||
|
||||
if (j->type == mvt_double ||
|
||||
j->type == mvt_float ||
|
||||
j->type == mvt_double ||
|
||||
j->type == mvt_uint ||
|
||||
j->type == mvt_sint) {
|
||||
int type = 0;
|
||||
for (auto s : j->second.sample_values) {
|
||||
type |= (1 << s.type);
|
||||
}
|
||||
|
||||
if (type == (1 << mvt_double)) {
|
||||
aprintf(&buf, "\": \"Number\"");
|
||||
} else if (j->type == mvt_bool) {
|
||||
} else if (type == (1 << mvt_bool)) {
|
||||
aprintf(&buf, "\": \"Boolean\"");
|
||||
} else {
|
||||
} else if (type == (1 << mvt_string)) {
|
||||
aprintf(&buf, "\": \"String\"");
|
||||
} else {
|
||||
aprintf(&buf, "\": \"Mixed\"");
|
||||
}
|
||||
}
|
||||
|
||||
aprintf(&buf, "} }");
|
||||
}
|
||||
|
||||
aprintf(&buf, " ] }");
|
||||
aprintf(&buf, " ],");
|
||||
aprintf(&buf, "\"tilestats\": %s", tilestats(layermap).c_str());
|
||||
aprintf(&buf, "}");
|
||||
|
||||
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('json', %Q);", buf.c_str());
|
||||
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
||||
@@ -332,39 +509,77 @@ void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fnam
|
||||
}
|
||||
}
|
||||
|
||||
void mbtiles_close(sqlite3 *outdb, char **argv) {
|
||||
void mbtiles_close(sqlite3 *outdb, const char *pgm) {
|
||||
char *err;
|
||||
|
||||
if (sqlite3_exec(outdb, "ANALYZE;", NULL, NULL, &err) != SQLITE_OK) {
|
||||
fprintf(stderr, "%s: ANALYZE failed: %s\n", argv[0], err);
|
||||
fprintf(stderr, "%s: ANALYZE failed: %s\n", pgm, err);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (sqlite3_close(outdb) != SQLITE_OK) {
|
||||
fprintf(stderr, "%s: could not close database: %s\n", argv[0], sqlite3_errmsg(outdb));
|
||||
fprintf(stderr, "%s: could not close database: %s\n", pgm, sqlite3_errmsg(outdb));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, layermap_entry> merge_layermaps(std::vector<std::map<std::string, layermap_entry> > const &maps) {
|
||||
std::map<std::string, layermap_entry> merge_layermaps(std::vector<std::map<std::string, layermap_entry>> const &maps) {
|
||||
return merge_layermaps(maps, false);
|
||||
}
|
||||
|
||||
std::map<std::string, layermap_entry> merge_layermaps(std::vector<std::map<std::string, layermap_entry>> const &maps, bool trunc) {
|
||||
std::map<std::string, layermap_entry> out;
|
||||
|
||||
for (size_t i = 0; i < maps.size(); i++) {
|
||||
for (auto map = maps[i].begin(); map != maps[i].end(); ++map) {
|
||||
if (out.count(map->first) == 0) {
|
||||
out.insert(std::pair<std::string, layermap_entry>(map->first, layermap_entry(out.size())));
|
||||
auto out_entry = out.find(map->first);
|
||||
std::string layername = map->first;
|
||||
if (trunc) {
|
||||
layername = truncate16(layername, 256);
|
||||
}
|
||||
|
||||
if (out.count(layername) == 0) {
|
||||
out.insert(std::pair<std::string, layermap_entry>(layername, layermap_entry(out.size())));
|
||||
auto out_entry = out.find(layername);
|
||||
out_entry->second.minzoom = map->second.minzoom;
|
||||
out_entry->second.maxzoom = map->second.maxzoom;
|
||||
}
|
||||
|
||||
auto out_entry = out.find(map->first);
|
||||
auto out_entry = out.find(layername);
|
||||
if (out_entry == out.end()) {
|
||||
fprintf(stderr, "Internal error merging layers\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (auto fk = map->second.file_keys.begin(); fk != map->second.file_keys.end(); ++fk) {
|
||||
out_entry->second.file_keys.insert(*fk);
|
||||
std::string attribname = fk->first;
|
||||
if (trunc) {
|
||||
attribname = truncate16(attribname, 256);
|
||||
}
|
||||
|
||||
auto fk2 = out_entry->second.file_keys.find(attribname);
|
||||
|
||||
if (fk2 == out_entry->second.file_keys.end()) {
|
||||
out_entry->second.file_keys.insert(std::pair<std::string, type_and_string_stats>(attribname, fk->second));
|
||||
} else {
|
||||
for (auto val : fk->second.sample_values) {
|
||||
auto pt = std::lower_bound(fk2->second.sample_values.begin(), fk2->second.sample_values.end(), val);
|
||||
if (pt == fk2->second.sample_values.end() || *pt != val) { // not found
|
||||
fk2->second.sample_values.insert(pt, val);
|
||||
|
||||
if (fk2->second.sample_values.size() > 1000) {
|
||||
fk2->second.sample_values.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fk2->second.type |= fk->second.type;
|
||||
|
||||
if (fk->second.min < fk2->second.min) {
|
||||
fk2->second.min = fk->second.min;
|
||||
}
|
||||
if (fk->second.max > fk2->second.max) {
|
||||
fk2->second.max = fk->second.max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (map->second.minzoom < out_entry->second.minzoom) {
|
||||
@@ -373,8 +588,47 @@ std::map<std::string, layermap_entry> merge_layermaps(std::vector<std::map<std::
|
||||
if (map->second.maxzoom > out_entry->second.maxzoom) {
|
||||
out_entry->second.maxzoom = map->second.maxzoom;
|
||||
}
|
||||
|
||||
out_entry->second.points += map->second.points;
|
||||
out_entry->second.lines += map->second.lines;
|
||||
out_entry->second.polygons += map->second.polygons;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void add_to_file_keys(std::map<std::string, type_and_string_stats> &file_keys, std::string const &attrib, type_and_string const &val) {
|
||||
auto fka = file_keys.find(attrib);
|
||||
if (fka == file_keys.end()) {
|
||||
file_keys.insert(std::pair<std::string, type_and_string_stats>(attrib, type_and_string_stats()));
|
||||
fka = file_keys.find(attrib);
|
||||
}
|
||||
|
||||
if (fka == file_keys.end()) {
|
||||
fprintf(stderr, "Can't happen (tilestats)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (val.type == mvt_double) {
|
||||
double d = atof(val.string.c_str());
|
||||
|
||||
if (d < fka->second.min) {
|
||||
fka->second.min = d;
|
||||
}
|
||||
if (d > fka->second.max) {
|
||||
fka->second.max = d;
|
||||
}
|
||||
}
|
||||
|
||||
auto pt = std::lower_bound(fka->second.sample_values.begin(), fka->second.sample_values.end(), val);
|
||||
if (pt == fka->second.sample_values.end() || *pt != val) { // not found
|
||||
fka->second.sample_values.insert(pt, val);
|
||||
|
||||
if (fka->second.sample_values.size() > 1000) {
|
||||
fka->second.sample_values.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
fka->second.type |= (1 << val.type);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user