diff --git a/Makefile b/Makefile index f83ccb1..c743ecc 100644 --- a/Makefile +++ b/Makefile @@ -52,10 +52,10 @@ tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o pro tippecanoe-enumerate: enumerate.o $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) -lsqlite3 -tippecanoe-decode: decode.o projection.o mvt.o write_json.o +tippecanoe-decode: decode.o projection.o mvt.o write_json.o text.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 +tile-join: tile-join.o projection.o pool.o mbtiles.o mvt.o memfile.o dirtiles.o jsonpull/jsonpull.o text.o write_json.o $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread unit: unit.o text.o @@ -78,7 +78,7 @@ indent: TESTS = $(wildcard tests/*/out/*.json) SPACE = $(NULL) $(NULL) -test: tippecanoe tippecanoe-decode $(addsuffix .check,$(TESTS)) raw-tiles-test parallel-test pbf-test join-test enumerate-test decode-test unit +test: tippecanoe tippecanoe-decode $(addsuffix .check,$(TESTS)) raw-tiles-test parallel-test pbf-test join-test enumerate-test decode-test unit join-test-object ./unit # Work around Makefile and filename punctuation limits: _ for space, @ for :, % for / @@ -199,6 +199,14 @@ join-test: rm tests/join-population/tabblock_06001420.mbtiles tests/join-population/joined.mbtiles tests/join-population/joined-i.mbtiles tests/join-population/joined.mbtiles.json.check tests/join-population/joined-i.mbtiles.json.check tests/join-population/macarthur.mbtiles tests/join-population/merged.mbtiles tests/join-population/merged.mbtiles.json.check tests/join-population/merged-folder.mbtiles tests/join-population/macarthur2.mbtiles tests/join-population/windows.mbtiles tests/join-population/windows-merged.mbtiles tests/join-population/windows-merged2.mbtiles tests/join-population/windows.mbtiles.json.check tests/join-population/just-macarthur.mbtiles tests/join-population/no-macarthur.mbtiles tests/join-population/just-macarthur.mbtiles.json.check tests/join-population/no-macarthur.mbtiles.json.check tests/join-population/merged-folder.mbtiles.json.check tests/join-population/windows-merged.mbtiles.json.check tests/join-population/windows-merged2.mbtiles.json.check tests/join-population/macarthur-and-macarthur2-merged.mbtiles tests/join-population/macarthur-and-macarthur2-merged2.mbtiles tests/join-population/macarthur-and-macarthur2-merged.mbtiles.json.check tests/join-population/macarthur-and-macarthur2-merged2.mbtiles.json.check rm -rf tests/join-population/raw-merged-folder tests/join-population/tabblock_06001420-folder tests/join-population/macarthur-folder tests/join-population/macarthur2-folder tests/join-population/merged-mbtiles-to-folder tests/join-population/merged-folders-to-folder tests/join-population/windows-merged-folder tests/join-population/macarthur-and-macarthur2-folder +join-test-object: + ./tippecanoe -z0 -f -o tests/object/out/before.mbtiles tests/object/in.json + ./tile-join -f -o tests/object/out/after.mbtiles tests/object/out/before.mbtiles + ./tippecanoe-decode tests/object/out/before.mbtiles | grep -v '"bounds"' > tests/object/out/before.mbtiles.json + ./tippecanoe-decode tests/object/out/after.mbtiles | grep -v '"bounds"' > tests/object/out/after.mbtiles.json + cmp tests/object/out/before.mbtiles.json tests/object/out/after.mbtiles.json + rm -f tests/object/out/before.mbtiles.json tests/object/out/after.mbtiles.json tests/object/out/before.mbtiles tests/object/out/after.mbtiles + # Use this target to regenerate the standards that the tests are compared against # after making a change that legitimately changes their output diff --git a/mbtiles.cpp b/mbtiles.cpp index 8b1a83a..1f0588c 100644 --- a/mbtiles.cpp +++ b/mbtiles.cpp @@ -1,8 +1,3 @@ -// for vasprintf() on Linux -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - #include #include #include @@ -102,21 +97,6 @@ static void quote(std::string &buf, std::string const &s) { } } -void aprintf(std::string *buf, const char *format, ...) { - va_list ap; - char *tmp; - - va_start(ap, format); - if (vasprintf(&tmp, format, ap) < 0) { - fprintf(stderr, "memory allocation failure\n"); - exit(EXIT_FAILURE); - } - va_end(ap); - - buf->append(tmp, strlen(tmp)); - free(tmp); -} - bool type_and_string::operator<(const type_and_string &o) const { if (string < o.string) { return true; diff --git a/mbtiles.hpp b/mbtiles.hpp index 2e25262..7727c67 100644 --- a/mbtiles.hpp +++ b/mbtiles.hpp @@ -43,8 +43,6 @@ void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fnam void mbtiles_close(sqlite3 *outdb, const char *pgm); -void aprintf(std::string *buf, const char *format, ...); - std::map merge_layermaps(std::vector > const &maps); std::map merge_layermaps(std::vector > const &maps, bool trunc); diff --git a/text.cpp b/text.cpp index dc27d21..aee4713 100644 --- a/text.cpp +++ b/text.cpp @@ -1,5 +1,11 @@ +// for vasprintf() on Linux +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include "text.hpp" #include +#include /** * Returns an empty string if `s` is valid utf8; @@ -122,3 +128,18 @@ std::string truncate16(std::string const &s, size_t runes) { return std::string(s, 0, lastgood - start); } + +void aprintf(std::string *buf, const char *format, ...) { + va_list ap; + char *tmp; + + va_start(ap, format); + if (vasprintf(&tmp, format, ap) < 0) { + fprintf(stderr, "memory allocation failure\n"); + exit(EXIT_FAILURE); + } + va_end(ap); + + buf->append(tmp, strlen(tmp)); + free(tmp); +} diff --git a/text.hpp b/text.hpp index 65274a3..25a8a62 100644 --- a/text.hpp +++ b/text.hpp @@ -6,5 +6,6 @@ std::string check_utf8(std::string text); const char *utf8_next(const char *s, long *c); std::string truncate16(std::string const &s, size_t runes); +void aprintf(std::string *buf, const char *format, ...); #endif diff --git a/tile-join.cpp b/tile-join.cpp index 2f0414b..c04bb92 100644 --- a/tile-join.cpp +++ b/tile-join.cpp @@ -22,6 +22,8 @@ #include "mbtiles.hpp" #include "geometry.hpp" #include "dirtiles.hpp" +#include "write_json.hpp" +#include "text.hpp" #include #include #include @@ -171,6 +173,7 @@ void handle(std::string message, int z, unsigned x, unsigned y, std::mapsecond.first); + if (fa->second.first.type == mvt_hash) { + copy_nested(layer, feat, k, fa->second.first, outlayer, outfeature); + } else { + outlayer.tag(outfeature, k, fa->second.first); + } + add_to_file_keys(file_keys->second.file_keys, k, fa->second.second); attributes.erase(fa); } diff --git a/write_json.cpp b/write_json.cpp index b652e3f..7dc46a9 100644 --- a/write_json.cpp +++ b/write_json.cpp @@ -7,6 +7,7 @@ #include "geometry.hpp" #include "mvt.hpp" #include "write_json.hpp" +#include "text.hpp" struct lonlat { int op; @@ -25,49 +26,74 @@ struct lonlat { }; void print_val(FILE *fp, mvt_feature const &feature, mvt_layer const &layer, mvt_value const &val, size_t vo) { + std::string s; + stringify_val(s, feature, layer, val, vo); + fprintf(fp, "%s", s.c_str()); +} + +static void quote(std::string &buf, std::string const &s) { + buf.push_back('"'); + for (size_t i = 0; i < s.size(); i++) { + unsigned char ch = s[i]; + + if (ch == '\\' || ch == '\"') { + buf.push_back('\\'); + buf.push_back(ch); + } else if (ch < ' ') { + char tmp[7]; + sprintf(tmp, "\\u%04x", ch); + buf.append(std::string(tmp)); + } else { + buf.push_back(ch); + } + } + buf.push_back('"'); +} + +void stringify_val(std::string &out, mvt_feature const &feature, mvt_layer const &layer, mvt_value const &val, size_t vo) { if (val.type == mvt_string) { - fprintq(fp, val.string_value.c_str()); + quote(out, val.string_value); } else if (val.type == mvt_int) { - fprintf(fp, "%lld", (long long) val.numeric_value.int_value); + out.append(std::to_string((long long) val.numeric_value.int_value)); } else if (val.type == mvt_double) { double v = val.numeric_value.double_value; if (v == (long long) v) { - fprintf(fp, "%lld", (long long) v); + out.append(std::to_string((long long) v)); } else { - fprintf(fp, "%g", v); + aprintf(&out, "%g", v); } } else if (val.type == mvt_float) { double v = val.numeric_value.float_value; if (v == (long long) v) { - fprintf(fp, "%lld", (long long) v); + out.append(std::to_string((long long) v)); } else { - fprintf(fp, "%g", v); + aprintf(&out, "%g", v); } } else if (val.type == mvt_sint) { - fprintf(fp, "%lld", (long long) val.numeric_value.sint_value); + out.append(std::to_string((long long) val.numeric_value.sint_value)); } else if (val.type == mvt_uint) { - fprintf(fp, "%lld", (long long) val.numeric_value.uint_value); + out.append(std::to_string((long long) val.numeric_value.uint_value)); } else if (val.type == mvt_bool) { - fprintf(fp, "%s", val.numeric_value.bool_value ? "true" : "false"); + out.append(val.numeric_value.bool_value ? "true" : "false"); } else if (val.type == mvt_list) { - fprintf(fp, "["); + out.push_back('['); for (size_t i = 0; i < val.list_value.size(); i++) { if (i != 0) { - fprintf(fp, ","); + out.push_back(','); } if (val.list_value[i] >= vo || val.list_value[i] >= layer.values.size()) { fprintf(stderr, "Invalid value reference in list (%lu from %lu within %lu)\n", val.list_value[i], vo, layer.values.size()); exit(EXIT_FAILURE); } - print_val(fp, feature, layer, layer.values[val.list_value[i]], val.list_value[i]); + stringify_val(out, feature, layer, layer.values[val.list_value[i]], val.list_value[i]); } - fprintf(fp, "]"); + out.push_back(']'); } else if (val.type == mvt_hash) { - fprintf(fp, "{"); + out.push_back('{'); for (size_t i = 0; i + 1 < val.list_value.size(); i += 2) { if (i != 0) { - fprintf(fp, ","); + out.push_back(','); } if (val.list_value[i] >= layer.keys.size()) { fprintf(stderr, "Invalid key reference in hash (%lu from %lu within %lu)\n", val.list_value[i], vo, layer.keys.size()); @@ -78,13 +104,13 @@ void print_val(FILE *fp, mvt_feature const &feature, mvt_layer const &layer, mvt vo, layer.values.size()); exit(EXIT_FAILURE); } - fprintq(fp, layer.keys[val.list_value[i]].c_str()); - fprintf(fp, ":"); - print_val(fp, feature, layer, layer.values[val.list_value[i + 1]], val.list_value[i + 1]); + quote(out, layer.keys[val.list_value[i]]); + out.push_back(':'); + stringify_val(out, feature, layer, layer.values[val.list_value[i + 1]], val.list_value[i + 1]); } - fprintf(fp, "}"); + out.push_back('}'); } else if (val.type == mvt_null) { - fprintf(fp, "null"); + out.append("null"); } } diff --git a/write_json.hpp b/write_json.hpp index 38a611d..da4e4bb 100644 --- a/write_json.hpp +++ b/write_json.hpp @@ -1,2 +1,3 @@ void layer_to_geojson(FILE *fp, mvt_layer const &layer, unsigned z, unsigned x, unsigned y, bool comma, bool name, bool zoom, unsigned long long index, long long sequence, long long extent, bool complain); void fprintq(FILE *f, const char *s); +void stringify_val(std::string &out, mvt_feature const &feature, mvt_layer const &layer, mvt_value const &val, size_t vo);