mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-02 08:25:40 +02:00
Throw instead of exiting from the tile clipping and overzooming code
The code reachable from overzooming a tile no longer calls exit(). It reports errors by throwing tippecanoe_error, and owns its FILE * and json_pull * handles through RAII wrappers so that nothing leaks when an error unwinds the stack. New files: * errors.hpp/errors.cpp: the tippecanoe_error exception class, plus throw_tippecanoe_error() and throw_perror() helpers. * raii.hpp: unique_file and unique_json_pull, both std::unique_ptr aliases, for FILE * and json_pull *. exit() becomes throw in clip.cpp, mvt.cpp, mvt.hpp, read_json.cpp, evaluator.cpp, text.cpp, geometry.cpp, attribute.cpp, and write_json.hpp. Each tool's main() is now a try/catch around an inner_main(), so the command-line tools exit with the status they always did. Along the way this fixes a z_stream leaked when compress() failed, an exit() that could run from json_writer's destructor during unwinding, and an exit() in mvt_value's hash function. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuCSVGssWNUCpVEE8Yp5HT
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
# 2.81.0
|
||||
|
||||
* Report errors from the tile clipping and overzooming code by throwing a
|
||||
`tippecanoe_error` rather than by calling `exit()`, and give that code
|
||||
RAII ownership of its `FILE *` and `json_pull *` handles so that nothing
|
||||
leaks when an error unwinds the stack. Each command-line tool catches the
|
||||
exception in `main()` and exits with the same status it would have
|
||||
before. Along the way this fixes a leaked `z_stream` when `compress()`
|
||||
failed and two `exit()` calls that could run from a destructor or from
|
||||
`mvt_value`'s hash function.
|
||||
* Add `--drop-by-attribute-as-needed=`*attribute* to drop the features with
|
||||
the lowest values of a numeric attribute from oversized tiles, and
|
||||
`--drop-by-attribute-order=desc` to drop the highest values instead.
|
||||
|
||||
@@ -95,25 +95,25 @@ C = $(wildcard *.c) $(wildcard *.cpp)
|
||||
INCLUDES = -I/usr/local/include -I. -Iclipper2/include
|
||||
LIBS = -L/usr/local/lib
|
||||
|
||||
tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o serial.o main.o platform.o text.o dirtiles.o pmtiles_file.o plugin.o read_json.o write_json.o geobuf.o flatgeobuf.o evaluator.o geocsv.o csv.o geojson-loop.o json_logger.o visvalingam.o compression.o clip.o sort.o attribute.o thread.o shared_borders.o usage.o clipper2/src/clipper.engine.o
|
||||
tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o serial.o main.o platform.o text.o dirtiles.o pmtiles_file.o plugin.o read_json.o write_json.o geobuf.o flatgeobuf.o evaluator.o geocsv.o csv.o geojson-loop.o json_logger.o visvalingam.o compression.o clip.o sort.o attribute.o thread.o shared_borders.o usage.o clipper2/src/clipper.engine.o errors.o
|
||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
|
||||
|
||||
tippecanoe-enumerate: enumerate.o usage.o
|
||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lsqlite3
|
||||
|
||||
tippecanoe-decode: decode.o projection.o mvt.o write_json.o text.o jsonpull/jsonpull.o dirtiles.o pmtiles_file.o usage.o
|
||||
tippecanoe-decode: decode.o projection.o mvt.o write_json.o text.o jsonpull/jsonpull.o dirtiles.o pmtiles_file.o usage.o errors.o
|
||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3
|
||||
|
||||
tile-join: tile-join.o platform.o projection.o mbtiles.o mvt.o memfile.o dirtiles.o jsonpull/jsonpull.o text.o evaluator.o csv.o write_json.o pmtiles_file.o clip.o attribute.o thread.o read_json.o usage.o clipper2/src/clipper.engine.o
|
||||
tile-join: tile-join.o platform.o projection.o mbtiles.o mvt.o memfile.o dirtiles.o jsonpull/jsonpull.o text.o evaluator.o csv.o write_json.o pmtiles_file.o clip.o attribute.o thread.o read_json.o usage.o clipper2/src/clipper.engine.o errors.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 geojson-loop.o usage.o
|
||||
tippecanoe-json-tool: jsontool.o jsonpull/jsonpull.o csv.o text.o geojson-loop.o usage.o errors.o
|
||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
|
||||
|
||||
unit: unit.o text.o sort.o mvt.o projection.o clip.o attribute.o jsonpull/jsonpull.o evaluator.o read_json.o clipper2/src/clipper.engine.o
|
||||
unit: unit.o text.o sort.o mvt.o projection.o clip.o attribute.o jsonpull/jsonpull.o evaluator.o read_json.o clipper2/src/clipper.engine.o errors.o
|
||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
|
||||
|
||||
tippecanoe-overzoom: overzoom.o mvt.o clip.o evaluator.o jsonpull/jsonpull.o text.o attribute.o read_json.o projection.o read_json.o usage.o clipper2/src/clipper.engine.o
|
||||
tippecanoe-overzoom: overzoom.o mvt.o clip.o evaluator.o jsonpull/jsonpull.o text.o attribute.o read_json.o projection.o read_json.o usage.o clipper2/src/clipper.engine.o errors.o
|
||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
|
||||
|
||||
-include $(wildcard *.d)
|
||||
@@ -128,7 +128,7 @@ clean:
|
||||
rm -f ./tippecanoe ./tippecanoe-* ./tile-join ./unit *.o *.d */*.o */*.d tests/**/*.mbtiles tests/**/*.check
|
||||
|
||||
indent:
|
||||
clang-format -i -style="{BasedOnStyle: Google, IndentWidth: 8, UseTab: Always, AllowShortIfStatementsOnASingleLine: false, ColumnLimit: 0, ContinuationIndentWidth: 8, SpaceAfterCStyleCast: true, IndentCaseLabels: false, AllowShortBlocksOnASingleLine: false, AllowShortFunctionsOnASingleLine: false, SortIncludes: false}" $(filter-out flatgeobuf.cpp,$(C)) $(H) jsonpull/*.[ch]
|
||||
clang-format -i -style="{BasedOnStyle: Google, IndentWidth: 8, UseTab: Always, AllowShortIfStatementsOnASingleLine: false, ColumnLimit: 0, ContinuationIndentWidth: 8, SpaceAfterCStyleCast: true, IndentCaseLabels: false, AllowShortBlocksOnASingleLine: false, AllowShortFunctionsOnASingleLine: false, SortIncludes: false, PointerAlignment: Right, DerivePointerAlignment: false}" $(filter-out flatgeobuf.cpp,$(C)) $(H) jsonpull/*.[ch]
|
||||
|
||||
TESTS = $(wildcard tests/*/out/*.json)
|
||||
SPACE = $(NULL) $(NULL)
|
||||
|
||||
+10
-17
@@ -5,6 +5,7 @@
|
||||
#include "serial.hpp"
|
||||
#include "jsonpull/jsonpull.h"
|
||||
#include "milo/dtoa_milo.h"
|
||||
#include "raii.hpp"
|
||||
|
||||
std::map<std::string, attribute_op> numeric_operations = {
|
||||
{"sum", op_sum},
|
||||
@@ -33,8 +34,7 @@ void set_attribute_accum(std::unordered_map<std::string, attribute_op> &attribut
|
||||
} else if (type == "count") {
|
||||
t = op_count;
|
||||
} else {
|
||||
fprintf(stderr, "Attribute method (%s) must be sum, product, mean, max, min, concat, comma, or count\n", type.c_str());
|
||||
exit(EXIT_ARGS);
|
||||
throw_tippecanoe_error(EXIT_ARGS, "Attribute method (%s) must be sum, product, mean, max, min, concat, comma, or count", type.c_str());
|
||||
}
|
||||
|
||||
attribute_accum.insert(std::pair<std::string, attribute_op>(name, t));
|
||||
@@ -42,17 +42,15 @@ void set_attribute_accum(std::unordered_map<std::string, attribute_op> &attribut
|
||||
|
||||
void set_attribute_accum(std::unordered_map<std::string, attribute_op> &attribute_accum, const char *arg, char **argv) {
|
||||
if (*arg == '{') {
|
||||
json_pull *jp = json_begin_string(arg);
|
||||
json_object *o = json_read_tree(jp);
|
||||
unique_json_pull jp(json_begin_string(arg));
|
||||
json_object *o = json_read_tree(jp.get());
|
||||
|
||||
if (o == NULL) {
|
||||
fprintf(stderr, "%s: -E%s: %s\n", *argv, arg, jp->error);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "%s: -E%s: %s", *argv, arg, jp->error);
|
||||
}
|
||||
|
||||
if (o->type != JSON_HASH) {
|
||||
fprintf(stderr, "%s: -E%s: not a JSON object\n", *argv, arg);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "%s: -E%s: not a JSON object", *argv, arg);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < o->value.object.length; i++) {
|
||||
@@ -60,26 +58,22 @@ void set_attribute_accum(std::unordered_map<std::string, attribute_op> &attribut
|
||||
json_object *v = o->value.object.values[i];
|
||||
|
||||
if (k->type != JSON_STRING) {
|
||||
fprintf(stderr, "%s: -E%s: key %zu not a string\n", *argv, arg, i);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "%s: -E%s: key %zu not a string", *argv, arg, i);
|
||||
}
|
||||
if (v->type != JSON_STRING) {
|
||||
fprintf(stderr, "%s: -E%s: value %zu not a string\n", *argv, arg, i);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "%s: -E%s: value %zu not a string", *argv, arg, i);
|
||||
}
|
||||
|
||||
set_attribute_accum(attribute_accum, k->value.string.string, v->value.string.string);
|
||||
}
|
||||
|
||||
json_free(o);
|
||||
json_end(jp);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *s = strchr(arg, ':');
|
||||
if (s == NULL) {
|
||||
fprintf(stderr, "-E%s option must be in the form -Ename:method\n", arg);
|
||||
exit(EXIT_ARGS);
|
||||
throw_tippecanoe_error(EXIT_ARGS, "-E%s option must be in the form -Ename:method", arg);
|
||||
}
|
||||
|
||||
std::string name = std::string(arg, s - arg);
|
||||
@@ -171,8 +165,7 @@ static void preserve_attribute1(attribute_op const &op, std::string const &key,
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "can't happen: operation that isn't used by --accumulate-numeric-attributes\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "can't happen: operation that isn't used by --accumulate-numeric-attributes");
|
||||
}
|
||||
|
||||
full_keys.push_back(key_pool.pool(key));
|
||||
|
||||
@@ -46,8 +46,7 @@ drawvec simple_clip_poly(drawvec &geom, long long minx, long long miny, long lon
|
||||
tmp = clip_poly1(tmp, minx, miny, maxx, maxy, ax, ay, bx, by, edge_nodes, prevent_simplify_shared_nodes);
|
||||
if (tmp.size() > 0) {
|
||||
if (tmp[0].first != tmp[tmp.size() - 1].first || tmp[0].second != tmp[tmp.size() - 1].second) {
|
||||
fprintf(stderr, "Internal error: Polygon ring not closed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
throw_tippecanoe_error(EXIT_FAILURE, "Internal error: Polygon ring not closed");
|
||||
}
|
||||
}
|
||||
for (size_t k = 0; k < tmp.size(); k++) {
|
||||
@@ -60,8 +59,7 @@ drawvec simple_clip_poly(drawvec &geom, long long minx, long long miny, long lon
|
||||
|
||||
i = j - 1;
|
||||
} else {
|
||||
fprintf(stderr, "Unexpected operation in polygon %d\n", (int) geom[i].op);
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Unexpected operation in polygon %d", (int) geom[i].op);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,8 +244,7 @@ static void decode_clipped(mapbox::geometry::multi_polygon<long long> &t, drawve
|
||||
double area = get_area(ring, 0, ring.size());
|
||||
|
||||
if ((j == 0 && area < 0) || (j != 0 && area > 0)) {
|
||||
fprintf(stderr, "Ring area has wrong sign: %f for %zu\n", area, j);
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Ring area has wrong sign: %f for %zu", area, j);
|
||||
}
|
||||
|
||||
for (size_t k = 0; k < ring.size(); k++) {
|
||||
@@ -362,8 +359,7 @@ drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip, bool try
|
||||
fprintf(f, "\n\n\n\n\n");
|
||||
|
||||
fclose(f);
|
||||
fprintf(stderr, "Internal error: Polygon cleaning failed. Log in /tmp/wagyu.log\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: Polygon cleaning failed. Log in /tmp/wagyu.log");
|
||||
}
|
||||
|
||||
if (scale != 1) {
|
||||
@@ -439,8 +435,7 @@ drawvec clip_poly_poly(drawvec const &geom, drawvec const &bounds) {
|
||||
result.clear();
|
||||
wagyu.execute(mapbox::geometry::wagyu::clip_type_intersection, result, mapbox::geometry::wagyu::fill_type_positive, mapbox::geometry::wagyu::fill_type_positive);
|
||||
} catch (std::runtime_error &e) {
|
||||
fprintf(stderr, "Internal error: Polygon clipping failed\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: Polygon clipping failed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,8 +630,7 @@ double get_area_scaled(const drawvec &geom, size_t i, size_t j) {
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "get_area_scaled: can't happen\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "get_area_scaled: can't happen");
|
||||
}
|
||||
|
||||
double get_area(const drawvec &geom, size_t i, size_t j) {
|
||||
@@ -754,8 +748,7 @@ static bool inside(std::pair<double, double> d, int edge, long long minx, long l
|
||||
return d.first > minx;
|
||||
}
|
||||
|
||||
fprintf(stderr, "internal error inside\n");
|
||||
exit(EXIT_FAILURE);
|
||||
throw_tippecanoe_error(EXIT_FAILURE, "internal error inside");
|
||||
}
|
||||
|
||||
static std::pair<double, double> intersect(std::pair<double, double> a, std::pair<double, double> b, int edge, long long minx, long long miny, long long maxx, long long maxy) {
|
||||
@@ -773,8 +766,7 @@ static std::pair<double, double> intersect(std::pair<double, double> a, std::pai
|
||||
return std::pair<double, double>(minx, (a.second + (double) (b.second - a.second) * (minx - a.first) / (b.first - a.first)));
|
||||
}
|
||||
|
||||
fprintf(stderr, "internal error intersecting\n");
|
||||
exit(EXIT_FAILURE);
|
||||
throw_tippecanoe_error(EXIT_FAILURE, "internal error intersecting");
|
||||
}
|
||||
|
||||
// http://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
|
||||
@@ -910,8 +902,7 @@ void douglas_peucker(drawvec &geom, int start, int n, double e, size_t kept, siz
|
||||
std::stack<int> recursion_stack;
|
||||
|
||||
if (!geom[start + 0].necessary || !geom[start + n - 1].necessary) {
|
||||
fprintf(stderr, "endpoints not marked necessary\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "endpoints not marked necessary");
|
||||
}
|
||||
|
||||
int prev = 0;
|
||||
@@ -1237,12 +1228,10 @@ std::string overzoom(std::vector<input_tile> const &tiles, int nz, int nx, int n
|
||||
try {
|
||||
bool was_compressed;
|
||||
if (!tile.decode(t.tile, was_compressed)) {
|
||||
fprintf(stderr, "Couldn't parse tile %d/%u/%u\n", t.z, t.x, t.y);
|
||||
exit(EXIT_MVT);
|
||||
throw_tippecanoe_error(EXIT_MVT, "Couldn't parse tile %d/%u/%u", t.z, t.x, t.y);
|
||||
}
|
||||
} catch (std::exception const &e) {
|
||||
fprintf(stderr, "PBF decoding error in tile %d/%u/%u\n", t.z, t.x, t.y);
|
||||
exit(EXIT_PROTOBUF);
|
||||
throw_tippecanoe_error(EXIT_PROTOBUF, "PBF decoding error in tile %d/%u/%u", t.z, t.x, t.y);
|
||||
}
|
||||
|
||||
source_tile out;
|
||||
@@ -1892,8 +1881,7 @@ drawvec fix_polygon(const drawvec &geom, bool use_winding, bool reverse_winding)
|
||||
i = j - 1;
|
||||
outer = 0;
|
||||
} else {
|
||||
fprintf(stderr, "Internal error: polygon ring begins with %d, not moveto\n", geom[i].op);
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: polygon ring begins with %d, not moveto", geom[i].op);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-1
@@ -589,7 +589,7 @@ void usage(char **argv) {
|
||||
exit(EXIT_ARGS);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int inner_main(int argc, char **argv) {
|
||||
extern int optind;
|
||||
extern char *optarg;
|
||||
int i;
|
||||
@@ -666,3 +666,15 @@ int main(int argc, char **argv) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
return inner_main(argc, argv);
|
||||
} catch (tippecanoe_error &e) {
|
||||
fprintf(stderr, "%s\n", e.what());
|
||||
return e.exit_code;
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "Error: %s\n", e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "errors.hpp"
|
||||
|
||||
[[noreturn]] void throw_tippecanoe_error(int code, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
char *msg = NULL;
|
||||
if (vasprintf(&msg, fmt, ap) < 0) {
|
||||
va_end(ap);
|
||||
throw tippecanoe_error(code, "error (could not format message)");
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
std::string s(msg);
|
||||
free(msg);
|
||||
|
||||
// Remove trailing newline if present (stderr messages often have one)
|
||||
while (s.size() > 0 && s.back() == '\n') {
|
||||
s.pop_back();
|
||||
}
|
||||
|
||||
throw tippecanoe_error(code, s);
|
||||
}
|
||||
|
||||
[[noreturn]] void throw_perror(int code, const char *msg) {
|
||||
throw tippecanoe_error(code, std::string(msg) + ": " + strerror(errno));
|
||||
}
|
||||
+36
@@ -1,3 +1,6 @@
|
||||
#ifndef ERRORS_HPP
|
||||
#define ERRORS_HPP
|
||||
|
||||
#define EXIT_INCOMPLETE 100
|
||||
#define EXIT_ARGS 101
|
||||
#define EXIT_CLOSE 102
|
||||
@@ -21,3 +24,36 @@
|
||||
#define EXIT_WRITE 120
|
||||
|
||||
// avoid 124, 125, 126, 127, 137, which are used by GNU timeout
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
class tippecanoe_error : public std::runtime_error {
|
||||
public:
|
||||
int exit_code;
|
||||
tippecanoe_error(int code, const std::string &message)
|
||||
: std::runtime_error(message), exit_code(code) {
|
||||
}
|
||||
};
|
||||
|
||||
// Format and throw a tippecanoe_error. Use in place of fprintf(stderr,...)+exit().
|
||||
[[noreturn]] void throw_tippecanoe_error(int code, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
|
||||
// Throw from errno-based errors (replaces perror()+exit() pattern)
|
||||
[[noreturn]] void throw_perror(int code, const char *msg);
|
||||
|
||||
// Check the void* return value from pthread_join. If a thread caught an
|
||||
// exception, the return value is a heap-allocated std::exception_ptr.
|
||||
// This re-throws it on the joining thread, or does nothing if retval is NULL.
|
||||
inline void rethrow_if_thread_failed(void *retval) {
|
||||
if (retval != NULL) {
|
||||
std::exception_ptr *ep = (std::exception_ptr *) retval;
|
||||
std::exception_ptr copy = *ep;
|
||||
delete ep;
|
||||
std::rethrow_exception(copy);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+27
-50
@@ -6,6 +6,7 @@
|
||||
#include "mvt.hpp"
|
||||
#include "evaluator.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "raii.hpp"
|
||||
#include "milo/dtoa_milo.h"
|
||||
#include "text.hpp"
|
||||
|
||||
@@ -48,8 +49,7 @@ int compare(mvt_value const &one, json_object *two, bool &fail) {
|
||||
break;
|
||||
case mvt_no_such_key:
|
||||
default:
|
||||
fprintf(stderr, "Internal error: bad mvt type %d\n", one.type);
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: bad mvt type %d", one.type);
|
||||
}
|
||||
|
||||
if (v < two->value.number.number) {
|
||||
@@ -84,8 +84,7 @@ int compare(mvt_value const &one, json_object *two, bool &fail) {
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Internal error: bad mvt type %d\n", one.type);
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: bad mvt type %d", one.type);
|
||||
}
|
||||
|
||||
// 0: false
|
||||
@@ -119,39 +118,33 @@ static int eval(std::function<mvt_value(std::string const &)> feature, json_obje
|
||||
}
|
||||
|
||||
if (f == NULL || f->type != JSON_ARRAY) {
|
||||
fprintf(stderr, "Filter is not an array: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "Filter is not an array: %s", json_stringify(f));
|
||||
}
|
||||
|
||||
if (f->value.array.length < 1) {
|
||||
fprintf(stderr, "Array too small in filter: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "Array too small in filter: %s", json_stringify(f));
|
||||
}
|
||||
|
||||
if (f->value.array.array[0]->type != JSON_STRING) {
|
||||
fprintf(stderr, "Filter operation is not a string: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "Filter operation is not a string: %s", json_stringify(f));
|
||||
}
|
||||
|
||||
if (strcmp(f->value.array.array[0]->value.string.string, "has") == 0 ||
|
||||
strcmp(f->value.array.array[0]->value.string.string, "!has") == 0) {
|
||||
if (f->value.array.length != 2) {
|
||||
fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "Wrong number of array elements in filter: %s", json_stringify(f));
|
||||
}
|
||||
|
||||
if (strcmp(f->value.array.array[0]->value.string.string, "has") == 0) {
|
||||
if (f->value.array.array[1]->type != JSON_STRING) {
|
||||
fprintf(stderr, "\"has\" key is not a string: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "\"has\" key is not a string: %s", json_stringify(f));
|
||||
}
|
||||
return feature(std::string(f->value.array.array[1]->value.string.string)).type != mvt_no_such_key;
|
||||
}
|
||||
|
||||
if (strcmp(f->value.array.array[0]->value.string.string, "!has") == 0) {
|
||||
if (f->value.array.array[1]->type != JSON_STRING) {
|
||||
fprintf(stderr, "\"!has\" key is not a string: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "\"!has\" key is not a string: %s", json_stringify(f));
|
||||
}
|
||||
return feature(std::string(f->value.array.array[1]->value.string.string)).type == mvt_no_such_key;
|
||||
}
|
||||
@@ -164,12 +157,10 @@ static int eval(std::function<mvt_value(std::string const &)> feature, json_obje
|
||||
strcmp(f->value.array.array[0]->value.string.string, "<") == 0 ||
|
||||
strcmp(f->value.array.array[0]->value.string.string, "<=") == 0) {
|
||||
if (f->value.array.length != 3) {
|
||||
fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "Wrong number of array elements in filter: %s", json_stringify(f));
|
||||
}
|
||||
if (f->value.array.array[1]->type != JSON_STRING) {
|
||||
fprintf(stderr, "comparison key is not a string: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "comparison key is not a string: %s", json_stringify(f));
|
||||
}
|
||||
|
||||
mvt_value ff = feature(std::string(f->value.array.array[1]->value.string.string));
|
||||
@@ -223,8 +214,7 @@ static int eval(std::function<mvt_value(std::string const &)> feature, json_obje
|
||||
return cmp <= 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Internal error: can't happen: %s\n", json_stringify(f));
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: can't happen: %s", json_stringify(f));
|
||||
}
|
||||
|
||||
if (strcmp(f->value.array.array[0]->value.string.string, "all") == 0 ||
|
||||
@@ -266,13 +256,11 @@ static int eval(std::function<mvt_value(std::string const &)> feature, json_obje
|
||||
if (strcmp(f->value.array.array[0]->value.string.string, "in") == 0 ||
|
||||
strcmp(f->value.array.array[0]->value.string.string, "!in") == 0) {
|
||||
if (f->value.array.length < 2) {
|
||||
fprintf(stderr, "Array too small in filter: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "Array too small in filter: %s", json_stringify(f));
|
||||
}
|
||||
|
||||
if (f->value.array.array[1]->type != JSON_STRING) {
|
||||
fprintf(stderr, "\"!in\" key is not a string: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "\"!in\" key is not a string: %s", json_stringify(f));
|
||||
}
|
||||
|
||||
mvt_value ff = feature(std::string(f->value.array.array[1]->value.string.string));
|
||||
@@ -321,13 +309,11 @@ static int eval(std::function<mvt_value(std::string const &)> feature, json_obje
|
||||
|
||||
if (strcmp(f->value.array.array[0]->value.string.string, "attribute-filter") == 0) {
|
||||
if (f->value.array.length != 3) {
|
||||
fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "Wrong number of array elements in filter: %s", json_stringify(f));
|
||||
}
|
||||
|
||||
if (f->value.array.array[1]->type != JSON_STRING) {
|
||||
fprintf(stderr, "\"attribute-filter\" key is not a string: %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "\"attribute-filter\" key is not a string: %s", json_stringify(f));
|
||||
}
|
||||
|
||||
bool ok = eval(feature, f->value.array.array[2], exclude_attributes, unidecode_data) > 0;
|
||||
@@ -338,14 +324,12 @@ static int eval(std::function<mvt_value(std::string const &)> feature, json_obje
|
||||
return true;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unknown filter %s\n", json_stringify(f));
|
||||
exit(EXIT_FILTER);
|
||||
throw_tippecanoe_error(EXIT_FILTER, "Unknown filter %s", json_stringify(f));
|
||||
}
|
||||
|
||||
bool evaluate(std::function<mvt_value(std::string const &)> feature, std::string const &layer, json_object *filter, std::set<std::string> &exclude_attributes, std::vector<std::string> const &unidecode_data) {
|
||||
if (filter == NULL || filter->type != JSON_HASH) {
|
||||
fprintf(stderr, "Error: filter is not a hash: %s\n", json_stringify(filter));
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "Error: filter is not a hash: %s", json_stringify(filter));
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
@@ -365,34 +349,27 @@ bool evaluate(std::function<mvt_value(std::string const &)> feature, std::string
|
||||
}
|
||||
|
||||
json_object *read_filter(const char *fname) {
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (fp == NULL) {
|
||||
perror(fname);
|
||||
exit(EXIT_OPEN);
|
||||
unique_file fp(fopen(fname, "r"));
|
||||
if (fp.get() == NULL) {
|
||||
throw_perror(EXIT_OPEN, fname);
|
||||
}
|
||||
|
||||
json_pull *jp = json_begin_file(fp);
|
||||
json_object *filter = json_read_tree(jp);
|
||||
unique_json_pull jp(json_begin_file(fp.get()));
|
||||
json_object *filter = json_read_tree(jp.get());
|
||||
if (filter == NULL) {
|
||||
fprintf(stderr, "%s: %s\n", fname, jp->error);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "%s: %s", fname, jp.get()->error);
|
||||
}
|
||||
json_disconnect(filter);
|
||||
json_end(jp);
|
||||
fclose(fp);
|
||||
return filter;
|
||||
}
|
||||
|
||||
json_object *parse_filter(const char *s) {
|
||||
json_pull *jp = json_begin_string(s);
|
||||
json_object *filter = json_read_tree(jp);
|
||||
unique_json_pull jp(json_begin_string(s));
|
||||
json_object *filter = json_read_tree(jp.get());
|
||||
if (filter == NULL) {
|
||||
fprintf(stderr, "Could not parse filter %s\n", s);
|
||||
fprintf(stderr, "%s\n", jp->error);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "Could not parse filter %s\n%s", s, jp.get()->error);
|
||||
}
|
||||
json_disconnect(filter);
|
||||
json_end(jp);
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -508,8 +508,7 @@ drawvec stairstep(drawvec &geom, int z, int detail) {
|
||||
|
||||
// out.push_back(draw(VT_LINETO, xx, yy));
|
||||
} else {
|
||||
fprintf(stderr, "Can't happen: stairstepping lineto with no moveto\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Can't happen: stairstepping lineto with no moveto");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-2
@@ -365,7 +365,7 @@ void join_csv(json_object *j) {
|
||||
vo->value.number.large_unsigned = 0;
|
||||
vo->value.number.large_signed = 0;
|
||||
} else {
|
||||
abort();
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "unexpected JSON attribute type %d", attr_type);
|
||||
}
|
||||
|
||||
properties->value.object.keys[properties->value.object.length] = ko;
|
||||
@@ -437,7 +437,7 @@ void usage(char **argv) {
|
||||
exit(EXIT_ARGS);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int inner_main(int argc, char **argv) {
|
||||
const char *csv = NULL;
|
||||
|
||||
strip_usage_headings(long_options, real_long_options);
|
||||
@@ -520,3 +520,15 @@ int main(int argc, char **argv) {
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
return inner_main(argc, argv);
|
||||
} catch (tippecanoe_error &e) {
|
||||
fprintf(stderr, "%s\n", e.what());
|
||||
return e.exit_code;
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "Error: %s\n", e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3165,7 +3165,7 @@ void usage(char **argv, int status) {
|
||||
exit(status);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int inner_main(int argc, char **argv) {
|
||||
#ifdef MTRACE
|
||||
mtrace();
|
||||
#endif
|
||||
@@ -3870,6 +3870,18 @@ int main(int argc, char **argv) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
return inner_main(argc, argv);
|
||||
} catch (tippecanoe_error &e) {
|
||||
fprintf(stderr, "%s\n", e.what());
|
||||
return e.exit_code;
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "Error: %s\n", e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
int mkstemp_cloexec(char *name) {
|
||||
int fd = mkstemp(name);
|
||||
if (fd >= 0) {
|
||||
|
||||
@@ -102,6 +102,7 @@ int compress(std::string const &input, std::string &output, bool gz) {
|
||||
deflate_s.next_out = (Bytef *) (output.data() + length);
|
||||
int ret = deflate(&deflate_s, Z_FINISH);
|
||||
if (ret != Z_STREAM_END && ret != Z_OK && ret != Z_BUF_ERROR) {
|
||||
deflateEnd(&deflate_s);
|
||||
return -1;
|
||||
}
|
||||
length += (increase - deflate_s.avail_out);
|
||||
@@ -118,7 +119,7 @@ bool mvt_tile::decode(const std::string &message, bool &was_compressed) {
|
||||
if (is_compressed(message)) {
|
||||
std::string uncompressed;
|
||||
if (decompress(message, uncompressed) == 0) {
|
||||
exit(EXIT_MVT);
|
||||
throw_tippecanoe_error(EXIT_MVT, "Tile decompression failed");
|
||||
}
|
||||
src = uncompressed;
|
||||
was_compressed = true;
|
||||
@@ -368,11 +369,9 @@ std::string mvt_tile::encode() {
|
||||
value_writer.add_bool(7, pbv.numeric_value.bool_value);
|
||||
break;
|
||||
case mvt_null:
|
||||
fprintf(stderr, "Internal error: trying to write null attribute to tile\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: trying to write null attribute to tile");
|
||||
default:
|
||||
fprintf(stderr, "Internal error: trying to write undefined attribute type to tile\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: trying to write undefined attribute type to tile");
|
||||
}
|
||||
|
||||
std::shared_ptr<sorted_value> sv = std::make_shared<sorted_value>();
|
||||
@@ -451,8 +450,7 @@ std::string mvt_tile::encode() {
|
||||
long long dy = wwy - py;
|
||||
|
||||
if (dx < INT_MIN || dx > INT_MAX || dy < INT_MIN || dy > INT_MAX) {
|
||||
fprintf(stderr, "Internal error: Geometry delta is too big: %lld,%lld\n", dx, dy);
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: Geometry delta is too big: %lld,%lld", dx, dy);
|
||||
}
|
||||
|
||||
geometry.push_back(protozero::encode_zigzag32(dx));
|
||||
@@ -464,8 +462,7 @@ std::string mvt_tile::encode() {
|
||||
} else if (op == mvt_closepath) {
|
||||
length++;
|
||||
} else {
|
||||
fprintf(stderr, "\nInternal error: corrupted geometry\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: corrupted geometry");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,8 +511,7 @@ bool mvt_value::operator<(const mvt_value &o) const {
|
||||
return numeric_value.null_value < o.numeric_value.null_value;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "mvt_value::operator<<: can't happen\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "mvt_value::operator<<: can't happen");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,8 +546,7 @@ bool mvt_value::operator==(const mvt_value &o) const {
|
||||
return numeric_value.null_value == o.numeric_value.null_value;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "mvt_value::operator==: can't happen\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "mvt_value::operator==: can't happen");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,8 +808,7 @@ serial_val mvt_value_to_serial_val(mvt_value const &v) {
|
||||
sv.s = "null";
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unhandled mvt_type %d\n", v.type);
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "unhandled mvt_type %d", v.type);
|
||||
}
|
||||
|
||||
return sv;
|
||||
@@ -840,8 +834,7 @@ long long mvt_value_to_long_long(mvt_value const &v) {
|
||||
case mvt_null:
|
||||
return 0;
|
||||
default:
|
||||
fprintf(stderr, "unhandled mvt_type %d\n", v.type);
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "unhandled mvt_type %d", v.type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -865,8 +858,7 @@ double mvt_value_to_double(mvt_value const &v) {
|
||||
case mvt_null:
|
||||
return 0;
|
||||
default:
|
||||
fprintf(stderr, "unhandled mvt_type %d\n", v.type);
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "unhandled mvt_type %d", v.type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -204,8 +204,7 @@ struct std::hash<mvt_value> {
|
||||
return fnv1a(sizeof(int), (void *) &k.numeric_value.null_value);
|
||||
|
||||
default:
|
||||
fprintf(stderr, "mvt_value hash can't happen\n");
|
||||
exit(EXIT_IMPOSSIBLE);
|
||||
throw tippecanoe_error(EXIT_IMPOSSIBLE, "mvt_value hash: unexpected type");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+26
-20
@@ -12,6 +12,7 @@
|
||||
#include "read_json.hpp"
|
||||
#include "projection.hpp"
|
||||
#include "usage.hpp"
|
||||
#include "raii.hpp"
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
@@ -91,24 +92,21 @@ void usage(char **argv) {
|
||||
std::string read_json_file(const char *fname) {
|
||||
std::string out;
|
||||
|
||||
FILE *f = fopen(fname, "r");
|
||||
if (f == NULL) {
|
||||
perror(optarg);
|
||||
exit(EXIT_OPEN);
|
||||
unique_file f(fopen(fname, "r"));
|
||||
if (!f) {
|
||||
throw_perror(EXIT_OPEN, fname);
|
||||
}
|
||||
|
||||
char buf[2000];
|
||||
size_t nread;
|
||||
while ((nread = fread(buf, sizeof(char), 2000, f)) != 0) {
|
||||
while ((nread = fread(buf, sizeof(char), 2000, f.get())) != 0) {
|
||||
out += std::string(buf, nread);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int inner_main(int argc, char **argv) {
|
||||
int i;
|
||||
const char *outtile = NULL;
|
||||
const char *outfile = NULL;
|
||||
@@ -273,16 +271,14 @@ int main(int argc, char **argv) {
|
||||
char buf[1000];
|
||||
int len;
|
||||
|
||||
FILE *f = fopen(s.tile.c_str(), "rb");
|
||||
if (f == NULL) {
|
||||
perror(s.tile.c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
unique_file f(fopen(s.tile.c_str(), "rb"));
|
||||
if (!f) {
|
||||
throw_perror(EXIT_OPEN, s.tile.c_str());
|
||||
}
|
||||
|
||||
while ((len = fread(buf, sizeof(char), 1000, f)) > 0) {
|
||||
while ((len = fread(buf, sizeof(char), 1000, f.get())) > 0) {
|
||||
tile.append(std::string(buf, len));
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
input_tile t = s;
|
||||
t.tile = std::move(tile);
|
||||
@@ -292,14 +288,24 @@ int main(int argc, char **argv) {
|
||||
out = overzoom(its, nz, nx, ny, detail, buffer, keep, exclude, exclude_prefix, do_compress, NULL, demultiply, json_filter, preserve_input_order, attribute_accum, unidecode_data, simplification, tiny_polygon_size, std::vector<mvt_layer>(), "", "", SIZE_MAX, std::vector<clipbbox>(), deduplicate_by_id);
|
||||
}
|
||||
|
||||
FILE *f = fopen(outfile, "wb");
|
||||
if (f == NULL) {
|
||||
perror(outfile);
|
||||
exit(EXIT_FAILURE);
|
||||
unique_file f(fopen(outfile, "wb"));
|
||||
if (!f) {
|
||||
throw_perror(EXIT_OPEN, outfile);
|
||||
}
|
||||
|
||||
fwrite(out.c_str(), sizeof(char), out.size(), f);
|
||||
fclose(f);
|
||||
fwrite(out.c_str(), sizeof(char), out.size(), f.get());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
return inner_main(argc, argv);
|
||||
} catch (tippecanoe_error &e) {
|
||||
fprintf(stderr, "%s\n", e.what());
|
||||
return e.exit_code;
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "Error: %s\n", e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef RAII_HPP
|
||||
#define RAII_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <stdio.h>
|
||||
#include "jsonpull/jsonpull.h"
|
||||
|
||||
// FILE* → unique_file
|
||||
struct file_closer {
|
||||
void operator()(FILE *f) {
|
||||
if (f) {
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
};
|
||||
using unique_file = std::unique_ptr<FILE, file_closer>;
|
||||
|
||||
// json_pull* → unique_json_pull
|
||||
struct json_pull_closer {
|
||||
void operator()(json_pull *jp) {
|
||||
if (jp) {
|
||||
json_end(jp);
|
||||
}
|
||||
}
|
||||
};
|
||||
using unique_json_pull = std::unique_ptr<json_pull, json_pull_closer>;
|
||||
|
||||
#endif
|
||||
+13
-14
@@ -14,6 +14,7 @@
|
||||
#include "milo/dtoa_milo.h"
|
||||
#include "errors.hpp"
|
||||
#include "serial.hpp"
|
||||
#include "raii.hpp"
|
||||
|
||||
const char *geometry_names[GEOM_TYPES] = {
|
||||
"Point",
|
||||
@@ -99,7 +100,7 @@ void parse_coordinates(int t, json_object *j, drawvec &out, int op, const char *
|
||||
json_context(j);
|
||||
fprintf(stderr, "%s:%d: malformed point: ", fname, line);
|
||||
json_context(feature);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "%s:%d: malformed point", fname, line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +136,7 @@ serial_val stringify_value(json_object *value, const char *reading, int line, js
|
||||
if (err.size() > 0) {
|
||||
fprintf(stderr, "%s:%d: %s: ", reading, line, err.c_str());
|
||||
json_context(feature);
|
||||
exit(EXIT_UTF8);
|
||||
throw_tippecanoe_error(EXIT_UTF8, "%s:%d: %s", reading, line, err.c_str());
|
||||
}
|
||||
} else if (vt == JSON_NUMBER) {
|
||||
sv.type = mvt_double;
|
||||
@@ -184,20 +185,20 @@ std::pair<int, drawvec> parse_geometry(json_object *geometry, json_pull *jp, jso
|
||||
if (geometry_type == NULL) {
|
||||
fprintf(stderr, "Filter output:%d: null geometry (additional not reported): ", jp->line);
|
||||
json_context(j);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "Filter output:%d: null geometry (additional not reported)", jp->line);
|
||||
}
|
||||
|
||||
if (geometry_type->type != JSON_STRING) {
|
||||
fprintf(stderr, "Filter output:%d: geometry type is not a string: ", jp->line);
|
||||
json_context(j);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "Filter output:%d: geometry type is not a string", jp->line);
|
||||
}
|
||||
|
||||
json_object *coordinates = json_hash_get(geometry, "coordinates");
|
||||
if (coordinates == NULL || coordinates->type != JSON_ARRAY) {
|
||||
fprintf(stderr, "Filter output:%d: geometry without coordinates array: ", jp->line);
|
||||
json_context(j);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "Filter output:%d: geometry without coordinates array", jp->line);
|
||||
}
|
||||
|
||||
int t;
|
||||
@@ -209,7 +210,7 @@ std::pair<int, drawvec> parse_geometry(json_object *geometry, json_pull *jp, jso
|
||||
if (t >= GEOM_TYPES) {
|
||||
fprintf(stderr, "Filter output:%d: Can't handle geometry type %s: ", jp->line, geometry_type->value.string.string);
|
||||
json_context(j);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "Filter output:%d: Can't handle geometry type %s", jp->line, geometry_type->value.string.string);
|
||||
}
|
||||
|
||||
drawvec dv;
|
||||
@@ -305,9 +306,9 @@ std::vector<mvt_layer> parse_layers(FILE *fp, int z, unsigned x, unsigned y, int
|
||||
std::map<std::string, mvt_layer> ret;
|
||||
std::shared_ptr<std::string> tile_stringpool = std::make_shared<std::string>();
|
||||
|
||||
json_pull *jp = json_begin_file(fp);
|
||||
unique_json_pull jp(json_begin_file(fp));
|
||||
while (1) {
|
||||
json_object *j = json_read(jp);
|
||||
json_object *j = json_read(jp.get());
|
||||
if (j == NULL) {
|
||||
if (jp->error != NULL) {
|
||||
fprintf(stderr, "Filter output:%d: %s: ", jp->line, jp->error);
|
||||
@@ -316,7 +317,7 @@ std::vector<mvt_layer> parse_layers(FILE *fp, int z, unsigned x, unsigned y, int
|
||||
} else {
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "Filter output:%d: %s", jp->line, jp->error);
|
||||
}
|
||||
|
||||
json_free(jp->root);
|
||||
@@ -336,7 +337,7 @@ std::vector<mvt_layer> parse_layers(FILE *fp, int z, unsigned x, unsigned y, int
|
||||
fprintf(stderr, "Filter output:%d: feature without properties hash: ", jp->line);
|
||||
json_context(j);
|
||||
json_free(j);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "Filter output:%d: feature without properties hash", jp->line);
|
||||
}
|
||||
|
||||
std::string layername = "unknown";
|
||||
@@ -364,10 +365,10 @@ std::vector<mvt_layer> parse_layers(FILE *fp, int z, unsigned x, unsigned y, int
|
||||
fprintf(stderr, "Filter output:%d: filtered feature with no geometry: ", jp->line);
|
||||
json_context(j);
|
||||
json_free(j);
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "Filter output:%d: filtered feature with no geometry", jp->line);
|
||||
}
|
||||
|
||||
std::pair<int, drawvec> parsed_geometry = parse_geometry(geometry, jp, j, z, x, y, extent, fix_longitudes, true);
|
||||
std::pair<int, drawvec> parsed_geometry = parse_geometry(geometry, jp.get(), j, z, x, y, extent, fix_longitudes, true);
|
||||
|
||||
int t = parsed_geometry.first;
|
||||
drawvec &dv = parsed_geometry.second;
|
||||
@@ -404,8 +405,6 @@ std::vector<mvt_layer> parse_layers(FILE *fp, int z, unsigned x, unsigned y, int
|
||||
json_free(j);
|
||||
}
|
||||
|
||||
json_end(jp);
|
||||
|
||||
std::vector<mvt_layer> final;
|
||||
for (auto a : ret) {
|
||||
final.push_back(a.second);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "milo/dtoa_milo.h"
|
||||
#include "milo/milo.h"
|
||||
#include "errors.hpp"
|
||||
#include "raii.hpp"
|
||||
|
||||
/**
|
||||
* Returns an empty string if `s` is valid utf8;
|
||||
@@ -135,7 +136,7 @@ int integer_zoom(std::string where, std::string text) {
|
||||
double d = atof(text.c_str());
|
||||
if (!std::isfinite(d) || d != floor(d) || d < 0 || d > 32) {
|
||||
fprintf(stderr, "%s: Expected integer zoom level in \"tippecanoe\" GeoJSON extension, not %s\n", where.c_str(), text.c_str());
|
||||
exit(EXIT_JSON);
|
||||
throw_tippecanoe_error(EXIT_JSON, "%s: Expected integer zoom level in \"tippecanoe\" GeoJSON extension, not %s", where.c_str(), text.c_str());
|
||||
}
|
||||
return d;
|
||||
}
|
||||
@@ -181,8 +182,7 @@ char *dtoa_milo(double val) {
|
||||
std::string s = milo::dtoa_milo(val);
|
||||
char *dup = strdup(s.c_str());
|
||||
if (dup == NULL) {
|
||||
perror("strdup");
|
||||
exit(EXIT_MEMORY);
|
||||
throw_perror(EXIT_MEMORY, "strdup");
|
||||
}
|
||||
return dup;
|
||||
}
|
||||
@@ -191,25 +191,22 @@ char *dtoa_milo(double val) {
|
||||
std::vector<std::string> read_unidecode(const char *fname) {
|
||||
std::string data;
|
||||
|
||||
FILE *f = fopen(fname, "rb");
|
||||
unique_file f(fopen(fname, "rb"));
|
||||
if (f == NULL) {
|
||||
perror(fname);
|
||||
exit(EXIT_OPEN);
|
||||
throw_perror(EXIT_OPEN, fname);
|
||||
}
|
||||
|
||||
std::string buf;
|
||||
buf.resize(2000);
|
||||
|
||||
while (true) {
|
||||
size_t nread = fread((void *) buf.c_str(), sizeof(char), buf.size(), f);
|
||||
size_t nread = fread((void *) buf.c_str(), sizeof(char), buf.size(), f.get());
|
||||
if (nread == 0) {
|
||||
break;
|
||||
}
|
||||
data.append(buf.c_str(), nread);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
std::vector<std::string> out;
|
||||
out.emplace_back(); // because the data file is 1-indexed
|
||||
out.emplace_back(); // ascii 001
|
||||
|
||||
+13
-1
@@ -1332,7 +1332,7 @@ void usage(char **argv) {
|
||||
exit(EXIT_ARGS);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int inner_main(int argc, char **argv) {
|
||||
char *out_mbtiles = NULL;
|
||||
char *out_dir = NULL;
|
||||
sqlite3 *outdb = NULL;
|
||||
@@ -1683,3 +1683,15 @@ int main(int argc, char **argv) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
return inner_main(argc, argv);
|
||||
} catch (tippecanoe_error &e) {
|
||||
fprintf(stderr, "%s\n", e.what());
|
||||
return e.exit_code;
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "Error: %s\n", e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -25,8 +25,9 @@ struct json_writer {
|
||||
~json_writer() {
|
||||
if (state.size() > 0) {
|
||||
if (state.size() != 1 || state[0] != JSON_WRITE_TOP) {
|
||||
// Log but don't throw — throwing from a destructor
|
||||
// during stack unwinding calls std::terminate().
|
||||
fprintf(stderr, "JSON not closed at end\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user