2014-09-29 19:48:58 +00:00
|
|
|
// for vasprintf() on Linux
|
2016-04-27 22:12:03 +00:00
|
|
|
#ifndef _GNU_SOURCE
|
2014-09-29 19:48:58 +00:00
|
|
|
#define _GNU_SOURCE
|
2016-04-27 22:12:03 +00:00
|
|
|
#endif
|
2014-09-29 19:48:58 +00:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sqlite3.h>
|
2016-04-27 22:09:06 +00:00
|
|
|
#include <vector>
|
2016-04-28 18:56:30 +00:00
|
|
|
#include <string>
|
2016-04-28 21:43:04 +00:00
|
|
|
#include <set>
|
2016-08-29 23:38:57 +00:00
|
|
|
#include <map>
|
2017-11-30 00:24:48 +00:00
|
|
|
#include <sys/stat.h>
|
2017-03-16 22:06:26 +00:00
|
|
|
#include "mvt.hpp"
|
2016-04-27 21:00:14 +00:00
|
|
|
#include "mbtiles.hpp"
|
2017-07-21 19:28:14 +00:00
|
|
|
#include "text.hpp"
|
2017-08-29 21:44:34 +00:00
|
|
|
#include "milo/dtoa_milo.h"
|
2018-03-16 22:19:23 +00:00
|
|
|
#include "write_json.hpp"
|
2018-06-07 18:19:19 +00:00
|
|
|
#include "version.hpp"
|
2014-09-29 19:48:58 +00:00
|
|
|
|
2018-08-31 22:11:05 +00:00
|
|
|
size_t max_tilestats_attributes = 1000;
|
|
|
|
size_t max_tilestats_sample_values = 1000;
|
|
|
|
size_t max_tilestats_values = 100;
|
|
|
|
|
2016-01-28 22:18:31 +00:00
|
|
|
sqlite3 *mbtiles_open(char *dbname, char **argv, int forcetable) {
|
2014-09-29 19:48:58 +00:00
|
|
|
sqlite3 *outdb;
|
|
|
|
|
|
|
|
if (sqlite3_open(dbname, &outdb) != SQLITE_OK) {
|
2015-06-03 18:21:40 +00:00
|
|
|
fprintf(stderr, "%s: %s: %s\n", argv[0], dbname, sqlite3_errmsg(outdb));
|
2014-09-29 19:48:58 +00:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *err = NULL;
|
|
|
|
if (sqlite3_exec(outdb, "PRAGMA synchronous=0", NULL, NULL, &err) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "%s: async: %s\n", argv[0], err);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (sqlite3_exec(outdb, "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL, &err) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "%s: async: %s\n", argv[0], err);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (sqlite3_exec(outdb, "PRAGMA journal_mode=DELETE", NULL, NULL, &err) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "%s: async: %s\n", argv[0], err);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (sqlite3_exec(outdb, "CREATE TABLE metadata (name text, value text);", NULL, NULL, &err) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "%s: create metadata table: %s\n", argv[0], err);
|
2016-01-28 22:18:31 +00:00
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
if (sqlite3_exec(outdb, "CREATE TABLE tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob);", NULL, NULL, &err) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "%s: create tiles table: %s\n", argv[0], err);
|
2016-01-28 22:18:31 +00:00
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
if (sqlite3_exec(outdb, "create unique index name on metadata (name);", NULL, NULL, &err) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "%s: index metadata: %s\n", argv[0], err);
|
2016-01-28 22:18:31 +00:00
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
if (sqlite3_exec(outdb, "create unique index tile_index on tiles (zoom_level, tile_column, tile_row);", NULL, NULL, &err) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "%s: index tiles: %s\n", argv[0], err);
|
2016-01-28 22:18:31 +00:00
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return outdb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mbtiles_write_tile(sqlite3 *outdb, int z, int tx, int ty, const char *data, int size) {
|
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
const char *query = "insert into tiles (zoom_level, tile_column, tile_row, tile_data) values (?, ?, ?, ?)";
|
|
|
|
if (sqlite3_prepare_v2(outdb, query, -1, &stmt, NULL) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "sqlite3 insert prep failed\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
sqlite3_bind_int(stmt, 1, z);
|
|
|
|
sqlite3_bind_int(stmt, 2, tx);
|
|
|
|
sqlite3_bind_int(stmt, 3, (1 << z) - 1 - ty);
|
|
|
|
sqlite3_bind_blob(stmt, 4, data, size, NULL);
|
|
|
|
|
|
|
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
|
|
|
fprintf(stderr, "sqlite3 insert failed: %s\n", sqlite3_errmsg(outdb));
|
|
|
|
}
|
|
|
|
if (sqlite3_finalize(stmt) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "sqlite3 finalize failed: %s\n", sqlite3_errmsg(outdb));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-28 21:43:04 +00:00
|
|
|
bool type_and_string::operator<(const type_and_string &o) const {
|
|
|
|
if (string < o.string) {
|
|
|
|
return true;
|
2016-01-28 23:46:58 +00:00
|
|
|
}
|
2016-04-28 21:43:04 +00:00
|
|
|
if (string == o.string && type < o.type) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2016-01-28 23:46:58 +00:00
|
|
|
}
|
|
|
|
|
2017-07-19 23:40:06 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t elements, json_writer &state) {
|
2017-07-21 19:56:30 +00:00
|
|
|
// 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);
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.json_write_hash();
|
2017-07-18 20:27:37 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("layerCount");
|
|
|
|
state.json_write_unsigned(layermap.size());
|
2017-07-18 20:27:37 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("layers");
|
|
|
|
state.json_write_array();
|
2017-07-18 20:27:37 +00:00
|
|
|
|
|
|
|
bool first = true;
|
|
|
|
for (auto layer : layermap) {
|
|
|
|
first = false;
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_hash();
|
2017-07-18 20:27:37 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("layer");
|
|
|
|
state.json_write_string(layer.first);
|
2017-07-18 20:27:37 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("count");
|
|
|
|
state.json_write_unsigned(layer.second.points + layer.second.lines + layer.second.polygons);
|
2017-07-18 20:27:37 +00:00
|
|
|
|
|
|
|
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";
|
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("geometry");
|
|
|
|
state.json_write_string(geomtype);
|
2017-07-18 20:27:37 +00:00
|
|
|
|
|
|
|
size_t attrib_count = layer.second.file_keys.size();
|
2018-08-31 22:11:05 +00:00
|
|
|
if (attrib_count > max_tilestats_sample_values) {
|
|
|
|
attrib_count = max_tilestats_sample_values;
|
2017-07-18 20:27:37 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("attributeCount");
|
|
|
|
state.json_write_unsigned(attrib_count);
|
2017-07-18 20:27:37 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("attributes");
|
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_array();
|
2017-07-18 20:27:37 +00:00
|
|
|
|
|
|
|
size_t attrs = 0;
|
|
|
|
for (auto attribute : layer.second.file_keys) {
|
2017-08-15 18:23:03 +00:00
|
|
|
if (attrs == elements) {
|
2017-07-18 21:20:33 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-07-18 20:27:37 +00:00
|
|
|
attrs++;
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_hash();
|
2017-07-18 20:27:37 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("attribute");
|
|
|
|
state.json_write_string(attribute.first);
|
2017-07-18 20:27:37 +00:00
|
|
|
|
|
|
|
size_t val_count = attribute.second.sample_values.size();
|
2018-08-31 22:11:05 +00:00
|
|
|
if (val_count > max_tilestats_sample_values) {
|
|
|
|
val_count = max_tilestats_sample_values;
|
2017-07-18 20:27:37 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("count");
|
|
|
|
state.json_write_unsigned(val_count);
|
2017-07-18 20:27:37 +00:00
|
|
|
|
2017-07-18 21:20:33 +00:00
|
|
|
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";
|
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("type");
|
|
|
|
state.json_write_string(type_str);
|
2017-07-18 21:20:33 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("values");
|
|
|
|
state.json_write_array();
|
2017-07-18 21:20:33 +00:00
|
|
|
|
|
|
|
size_t vals = 0;
|
|
|
|
for (auto value : attribute.second.sample_values) {
|
2017-08-15 18:23:03 +00:00
|
|
|
if (vals == elements) {
|
2017-07-18 21:20:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
|
2017-07-18 21:20:33 +00:00
|
|
|
if (value.type == mvt_double || value.type == mvt_bool) {
|
2017-08-15 18:23:03 +00:00
|
|
|
vals++;
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.json_write_stringified(value.string);
|
2017-07-18 21:20:33 +00:00
|
|
|
} else {
|
2017-07-21 19:28:14 +00:00
|
|
|
std::string trunc = truncate16(value.string, 256);
|
|
|
|
|
|
|
|
if (trunc.size() == value.string.size()) {
|
2017-08-15 18:23:03 +00:00
|
|
|
vals++;
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.json_write_string(value.string);
|
2017-07-21 19:28:14 +00:00
|
|
|
}
|
2017-07-18 21:20:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_end_array();
|
2017-07-18 21:20:33 +00:00
|
|
|
|
|
|
|
if ((type & (1 << mvt_double)) != 0) {
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("min");
|
|
|
|
state.json_write_number(attribute.second.min);
|
2017-07-18 21:20:33 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("max");
|
|
|
|
state.json_write_number(attribute.second.max);
|
2017-07-18 21:20:33 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_end_hash();
|
2017-07-18 20:27:37 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_end_array();
|
|
|
|
state.nospace = true;
|
|
|
|
state.json_end_hash();
|
2017-07-19 20:09:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_end_array();
|
|
|
|
state.nospace = true;
|
|
|
|
state.json_end_hash();
|
2017-07-18 20:27:37 +00:00
|
|
|
}
|
|
|
|
|
2018-06-08 04:37:25 +00:00
|
|
|
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, std::map<std::string, std::string> const &attribute_descriptions, std::string const &program) {
|
2014-09-29 19:48:58 +00:00
|
|
|
char *sql, *err;
|
|
|
|
|
2017-04-07 20:21:38 +00:00
|
|
|
sqlite3 *db = outdb;
|
|
|
|
if (outdb == NULL) {
|
|
|
|
if (sqlite3_open("", &db) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "Temporary db: %s\n", sqlite3_errmsg(db));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (sqlite3_exec(db, "CREATE TABLE metadata (name text, value text);", NULL, NULL, &err) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "Create metadata table: %s\n", err);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-29 19:48:58 +00:00
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('name', %Q);", fname);
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2014-09-29 19:48:58 +00:00
|
|
|
fprintf(stderr, "set name in metadata: %s\n", err);
|
2016-01-28 22:18:31 +00:00
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
|
2017-03-21 18:07:15 +00:00
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('description', %Q);", description != NULL ? description : fname);
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2014-09-29 19:48:58 +00:00
|
|
|
fprintf(stderr, "set description in metadata: %s\n", err);
|
2016-01-28 22:18:31 +00:00
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
|
2016-02-11 19:09:05 +00:00
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('version', %d);", 2);
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2016-01-28 22:18:31 +00:00
|
|
|
fprintf(stderr, "set version : %s\n", err);
|
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
|
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('minzoom', %d);", minzoom);
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2016-01-28 22:18:31 +00:00
|
|
|
fprintf(stderr, "set minzoom: %s\n", err);
|
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
|
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('maxzoom', %d);", maxzoom);
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2016-01-28 22:18:31 +00:00
|
|
|
fprintf(stderr, "set maxzoom: %s\n", err);
|
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
|
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('center', '%f,%f,%d');", midlon, midlat, maxzoom);
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2016-01-28 22:18:31 +00:00
|
|
|
fprintf(stderr, "set center: %s\n", err);
|
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
|
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('bounds', '%f,%f,%f,%f');", minlon, minlat, maxlon, maxlat);
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2016-01-28 22:18:31 +00:00
|
|
|
fprintf(stderr, "set bounds: %s\n", err);
|
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
|
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('type', %Q);", "overlay");
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2016-01-28 22:18:31 +00:00
|
|
|
fprintf(stderr, "set type: %s\n", err);
|
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
|
2016-04-13 19:49:41 +00:00
|
|
|
if (attribution != NULL) {
|
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('attribution', %Q);", attribution);
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2016-04-13 19:49:41 +00:00
|
|
|
fprintf(stderr, "set type: %s\n", err);
|
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
}
|
|
|
|
|
2017-03-16 22:06:26 +00:00
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('format', %Q);", vector ? "pbf" : "png");
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2016-01-28 22:18:31 +00:00
|
|
|
fprintf(stderr, "set format: %s\n", err);
|
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
|
2018-06-08 04:37:25 +00:00
|
|
|
std::string version = program + " " + VERSION;
|
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('generator', %Q);", version.c_str());
|
2018-06-07 18:19:19 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "set type: %s\n", err);
|
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sqlite3_free(sql);
|
|
|
|
|
2017-03-16 22:06:26 +00:00
|
|
|
if (vector) {
|
2018-08-31 22:11:05 +00:00
|
|
|
size_t elements = max_tilestats_values;
|
2017-08-15 18:23:03 +00:00
|
|
|
std::string buf;
|
2014-09-29 19:48:58 +00:00
|
|
|
|
2017-08-16 19:53:10 +00:00
|
|
|
{
|
2018-03-16 22:19:23 +00:00
|
|
|
json_writer state(&buf);
|
|
|
|
|
|
|
|
state.json_write_hash();
|
|
|
|
state.nospace = true;
|
|
|
|
|
|
|
|
state.json_write_string("vector_layers");
|
|
|
|
state.json_write_array();
|
2014-09-29 19:48:58 +00:00
|
|
|
|
2017-08-15 18:23:03 +00:00
|
|
|
std::vector<std::string> lnames;
|
|
|
|
for (auto ai = layermap.begin(); ai != layermap.end(); ++ai) {
|
|
|
|
lnames.push_back(ai->first);
|
2016-04-28 21:43:04 +00:00
|
|
|
}
|
2016-01-28 23:46:58 +00:00
|
|
|
|
2017-08-15 18:23:03 +00:00
|
|
|
for (size_t i = 0; i < lnames.size(); i++) {
|
|
|
|
auto fk = layermap.find(lnames[i]);
|
2018-03-16 22:19:23 +00:00
|
|
|
state.json_write_hash();
|
|
|
|
|
|
|
|
state.json_write_string("id");
|
|
|
|
state.json_write_string(lnames[i]);
|
|
|
|
|
|
|
|
state.json_write_string("description");
|
2018-05-24 17:27:43 +00:00
|
|
|
state.json_write_string(fk->second.description);
|
2018-03-16 22:19:23 +00:00
|
|
|
|
|
|
|
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;
|
2017-08-15 18:23:03 +00:00
|
|
|
|
|
|
|
bool first = true;
|
|
|
|
for (auto j = fk->second.file_keys.begin(); j != fk->second.file_keys.end(); ++j) {
|
|
|
|
if (first) {
|
|
|
|
first = false;
|
|
|
|
}
|
2017-03-16 22:06:26 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.json_write_string(j->first);
|
2017-07-18 00:31:46 +00:00
|
|
|
|
2018-05-24 21:17:26 +00:00
|
|
|
auto f = attribute_descriptions.find(j->first);
|
|
|
|
if (f == attribute_descriptions.end()) {
|
|
|
|
int type = 0;
|
|
|
|
for (auto s : j->second.sample_values) {
|
|
|
|
type |= (1 << s.type);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == (1 << mvt_double)) {
|
|
|
|
state.json_write_string("Number");
|
|
|
|
} else if (type == (1 << mvt_bool)) {
|
|
|
|
state.json_write_string("Boolean");
|
|
|
|
} else if (type == (1 << mvt_string)) {
|
|
|
|
state.json_write_string("String");
|
|
|
|
} else {
|
|
|
|
state.json_write_string("Mixed");
|
|
|
|
}
|
2017-08-15 18:23:03 +00:00
|
|
|
} else {
|
2018-05-24 21:17:26 +00:00
|
|
|
state.json_write_string(f->second);
|
2017-08-15 18:23:03 +00:00
|
|
|
}
|
2017-03-16 22:06:26 +00:00
|
|
|
}
|
2017-08-15 18:23:03 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_end_hash();
|
|
|
|
state.json_end_hash();
|
2015-03-23 23:12:12 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.json_end_array();
|
2017-08-15 18:23:03 +00:00
|
|
|
|
|
|
|
if (do_tilestats && elements > 0) {
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_write_string("tilestats");
|
|
|
|
tilestats(layermap, elements, state);
|
2017-08-15 18:23:03 +00:00
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
|
2018-03-16 22:19:23 +00:00
|
|
|
state.nospace = true;
|
|
|
|
state.json_end_hash();
|
2017-08-15 17:35:41 +00:00
|
|
|
}
|
|
|
|
|
2017-03-16 22:06:26 +00:00
|
|
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('json', %Q);", buf.c_str());
|
2017-04-07 20:21:38 +00:00
|
|
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
2017-03-16 22:06:26 +00:00
|
|
|
fprintf(stderr, "set json: %s\n", err);
|
|
|
|
if (!forcetable) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2016-01-28 22:18:31 +00:00
|
|
|
}
|
2017-03-16 22:06:26 +00:00
|
|
|
sqlite3_free(sql);
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
2017-04-07 20:21:38 +00:00
|
|
|
|
|
|
|
if (outdir != NULL) {
|
|
|
|
std::string metadata = std::string(outdir) + "/metadata.json";
|
|
|
|
|
2017-11-30 00:24:48 +00:00
|
|
|
struct stat st;
|
|
|
|
if (stat(metadata.c_str(), &st) == 0) {
|
|
|
|
// Leave existing metadata in place with --allow-existing
|
|
|
|
} else {
|
|
|
|
FILE *fp = fopen(metadata.c_str(), "w");
|
|
|
|
if (fp == NULL) {
|
|
|
|
perror(metadata.c_str());
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2017-04-07 20:21:38 +00:00
|
|
|
|
2018-03-16 22:35:26 +00:00
|
|
|
json_writer state(fp);
|
|
|
|
|
|
|
|
state.json_write_hash();
|
|
|
|
state.json_write_newline();
|
2017-04-07 20:21:38 +00:00
|
|
|
|
2017-11-30 00:24:48 +00:00
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
bool first = true;
|
|
|
|
if (sqlite3_prepare_v2(db, "SELECT name, value from metadata;", -1, &stmt, NULL) == SQLITE_OK) {
|
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
|
|
std::string key, value;
|
2017-10-28 00:38:07 +00:00
|
|
|
|
2017-11-30 00:24:48 +00:00
|
|
|
const char *k = (const char *) sqlite3_column_text(stmt, 0);
|
|
|
|
const char *v = (const char *) sqlite3_column_text(stmt, 1);
|
|
|
|
if (k == NULL || v == NULL) {
|
|
|
|
fprintf(stderr, "Corrupt mbtiles file: null metadata\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2018-03-16 22:35:26 +00:00
|
|
|
state.json_comma_newline();
|
|
|
|
state.json_write_string(k);
|
|
|
|
state.json_write_string(v);
|
2017-11-30 00:24:48 +00:00
|
|
|
first = false;
|
2017-04-07 20:21:38 +00:00
|
|
|
}
|
2017-11-30 00:24:48 +00:00
|
|
|
sqlite3_finalize(stmt);
|
2017-04-07 20:21:38 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 22:35:26 +00:00
|
|
|
state.json_write_newline();
|
|
|
|
state.json_end_hash();
|
|
|
|
state.json_write_newline();
|
2017-11-30 00:24:48 +00:00
|
|
|
fclose(fp);
|
|
|
|
}
|
2017-04-07 20:21:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (outdb == NULL) {
|
|
|
|
if (sqlite3_close(db) != SQLITE_OK) {
|
|
|
|
fprintf(stderr, "Could not close temp database: %s\n", sqlite3_errmsg(db));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
2014-09-29 19:48:58 +00:00
|
|
|
}
|
|
|
|
|
2017-07-18 16:54:59 +00:00
|
|
|
void mbtiles_close(sqlite3 *outdb, const char *pgm) {
|
2014-09-29 19:48:58 +00:00
|
|
|
char *err;
|
|
|
|
|
|
|
|
if (sqlite3_exec(outdb, "ANALYZE;", NULL, NULL, &err) != SQLITE_OK) {
|
2017-07-18 16:54:59 +00:00
|
|
|
fprintf(stderr, "%s: ANALYZE failed: %s\n", pgm, err);
|
2014-09-29 19:48:58 +00:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (sqlite3_close(outdb) != SQLITE_OK) {
|
2017-07-18 16:54:59 +00:00
|
|
|
fprintf(stderr, "%s: could not close database: %s\n", pgm, sqlite3_errmsg(outdb));
|
2014-09-29 19:48:58 +00:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
2016-09-20 18:04:24 +00:00
|
|
|
|
2017-07-21 19:56:30 +00:00
|
|
|
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) {
|
2016-09-20 18:04:24 +00:00
|
|
|
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) {
|
2017-12-14 22:30:08 +00:00
|
|
|
if (map->second.points + map->second.lines + map->second.polygons + map->second.retain == 0) {
|
2017-08-08 23:32:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-07-21 19:56:30 +00:00
|
|
|
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);
|
2016-09-20 18:04:24 +00:00
|
|
|
out_entry->second.minzoom = map->second.minzoom;
|
|
|
|
out_entry->second.maxzoom = map->second.maxzoom;
|
2018-05-24 17:27:43 +00:00
|
|
|
out_entry->second.description = map->second.description;
|
2016-09-20 18:04:24 +00:00
|
|
|
}
|
|
|
|
|
2017-07-21 19:56:30 +00:00
|
|
|
auto out_entry = out.find(layername);
|
2016-09-20 18:04:24 +00:00
|
|
|
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) {
|
2017-07-21 19:56:30 +00:00
|
|
|
std::string attribname = fk->first;
|
|
|
|
if (trunc) {
|
|
|
|
attribname = truncate16(attribname, 256);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto fk2 = out_entry->second.file_keys.find(attribname);
|
2017-07-18 00:31:46 +00:00
|
|
|
|
|
|
|
if (fk2 == out_entry->second.file_keys.end()) {
|
2017-07-21 19:56:30 +00:00
|
|
|
out_entry->second.file_keys.insert(std::pair<std::string, type_and_string_stats>(attribname, fk->second));
|
2017-07-18 00:31:46 +00:00
|
|
|
} else {
|
2017-07-19 23:40:06 +00:00
|
|
|
for (auto val : fk->second.sample_values) {
|
|
|
|
auto pt = std::lower_bound(fk2->second.sample_values.begin(), fk2->second.sample_values.end(), val);
|
2017-07-19 23:53:53 +00:00
|
|
|
if (pt == fk2->second.sample_values.end() || *pt != val) { // not found
|
2017-07-19 23:40:06 +00:00
|
|
|
fk2->second.sample_values.insert(pt, val);
|
|
|
|
|
2018-08-31 22:11:05 +00:00
|
|
|
if (fk2->second.sample_values.size() > max_tilestats_sample_values) {
|
2017-07-19 23:40:06 +00:00
|
|
|
fk2->second.sample_values.pop_back();
|
|
|
|
}
|
|
|
|
}
|
2017-07-18 00:31:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-09-20 18:04:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (map->second.minzoom < out_entry->second.minzoom) {
|
|
|
|
out_entry->second.minzoom = map->second.minzoom;
|
|
|
|
}
|
|
|
|
if (map->second.maxzoom > out_entry->second.maxzoom) {
|
|
|
|
out_entry->second.maxzoom = map->second.maxzoom;
|
|
|
|
}
|
2017-07-15 00:23:41 +00:00
|
|
|
|
|
|
|
out_entry->second.points += map->second.points;
|
|
|
|
out_entry->second.lines += map->second.lines;
|
|
|
|
out_entry->second.polygons += map->second.polygons;
|
2016-09-20 18:04:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
2017-07-19 21:45:15 +00:00
|
|
|
|
|
|
|
void add_to_file_keys(std::map<std::string, type_and_string_stats> &file_keys, std::string const &attrib, type_and_string const &val) {
|
2018-04-05 20:42:54 +00:00
|
|
|
if (val.type == mvt_null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-19 21:45:15 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-19 23:40:06 +00:00
|
|
|
auto pt = std::lower_bound(fka->second.sample_values.begin(), fka->second.sample_values.end(), val);
|
2017-07-19 23:53:53 +00:00
|
|
|
if (pt == fka->second.sample_values.end() || *pt != val) { // not found
|
2017-07-19 23:40:06 +00:00
|
|
|
fka->second.sample_values.insert(pt, val);
|
|
|
|
|
2018-08-31 22:11:05 +00:00
|
|
|
if (fka->second.sample_values.size() > max_tilestats_sample_values) {
|
2017-07-19 23:40:06 +00:00
|
|
|
fka->second.sample_values.pop_back();
|
2017-07-19 21:45:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fka->second.type |= (1 << val.type);
|
|
|
|
}
|