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
|
# 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
|
* Add `--drop-by-attribute-as-needed=`*attribute* to drop the features with
|
||||||
the lowest values of a numeric attribute from oversized tiles, and
|
the lowest values of a numeric attribute from oversized tiles, and
|
||||||
`--drop-by-attribute-order=desc` to drop the highest values instead.
|
`--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
|
INCLUDES = -I/usr/local/include -I. -Iclipper2/include
|
||||||
LIBS = -L/usr/local/lib
|
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
|
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
|
||||||
|
|
||||||
tippecanoe-enumerate: enumerate.o usage.o
|
tippecanoe-enumerate: enumerate.o usage.o
|
||||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lsqlite3
|
$(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
|
$(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
|
$(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
|
$(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
|
$(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
|
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
|
||||||
|
|
||||||
-include $(wildcard *.d)
|
-include $(wildcard *.d)
|
||||||
@@ -128,7 +128,7 @@ clean:
|
|||||||
rm -f ./tippecanoe ./tippecanoe-* ./tile-join ./unit *.o *.d */*.o */*.d tests/**/*.mbtiles tests/**/*.check
|
rm -f ./tippecanoe ./tippecanoe-* ./tile-join ./unit *.o *.d */*.o */*.d tests/**/*.mbtiles tests/**/*.check
|
||||||
|
|
||||||
indent:
|
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)
|
TESTS = $(wildcard tests/*/out/*.json)
|
||||||
SPACE = $(NULL) $(NULL)
|
SPACE = $(NULL) $(NULL)
|
||||||
|
|||||||
+10
-17
@@ -5,6 +5,7 @@
|
|||||||
#include "serial.hpp"
|
#include "serial.hpp"
|
||||||
#include "jsonpull/jsonpull.h"
|
#include "jsonpull/jsonpull.h"
|
||||||
#include "milo/dtoa_milo.h"
|
#include "milo/dtoa_milo.h"
|
||||||
|
#include "raii.hpp"
|
||||||
|
|
||||||
std::map<std::string, attribute_op> numeric_operations = {
|
std::map<std::string, attribute_op> numeric_operations = {
|
||||||
{"sum", op_sum},
|
{"sum", op_sum},
|
||||||
@@ -33,8 +34,7 @@ void set_attribute_accum(std::unordered_map<std::string, attribute_op> &attribut
|
|||||||
} else if (type == "count") {
|
} else if (type == "count") {
|
||||||
t = op_count;
|
t = op_count;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Attribute method (%s) must be sum, product, mean, max, min, concat, comma, or count\n", type.c_str());
|
throw_tippecanoe_error(EXIT_ARGS, "Attribute method (%s) must be sum, product, mean, max, min, concat, comma, or count", type.c_str());
|
||||||
exit(EXIT_ARGS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attribute_accum.insert(std::pair<std::string, attribute_op>(name, t));
|
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) {
|
void set_attribute_accum(std::unordered_map<std::string, attribute_op> &attribute_accum, const char *arg, char **argv) {
|
||||||
if (*arg == '{') {
|
if (*arg == '{') {
|
||||||
json_pull *jp = json_begin_string(arg);
|
unique_json_pull jp(json_begin_string(arg));
|
||||||
json_object *o = json_read_tree(jp);
|
json_object *o = json_read_tree(jp.get());
|
||||||
|
|
||||||
if (o == NULL) {
|
if (o == NULL) {
|
||||||
fprintf(stderr, "%s: -E%s: %s\n", *argv, arg, jp->error);
|
throw_tippecanoe_error(EXIT_JSON, "%s: -E%s: %s", *argv, arg, jp->error);
|
||||||
exit(EXIT_JSON);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (o->type != JSON_HASH) {
|
if (o->type != JSON_HASH) {
|
||||||
fprintf(stderr, "%s: -E%s: not a JSON object\n", *argv, arg);
|
throw_tippecanoe_error(EXIT_JSON, "%s: -E%s: not a JSON object", *argv, arg);
|
||||||
exit(EXIT_JSON);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < o->value.object.length; i++) {
|
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];
|
json_object *v = o->value.object.values[i];
|
||||||
|
|
||||||
if (k->type != JSON_STRING) {
|
if (k->type != JSON_STRING) {
|
||||||
fprintf(stderr, "%s: -E%s: key %zu not a string\n", *argv, arg, i);
|
throw_tippecanoe_error(EXIT_JSON, "%s: -E%s: key %zu not a string", *argv, arg, i);
|
||||||
exit(EXIT_JSON);
|
|
||||||
}
|
}
|
||||||
if (v->type != JSON_STRING) {
|
if (v->type != JSON_STRING) {
|
||||||
fprintf(stderr, "%s: -E%s: value %zu not a string\n", *argv, arg, i);
|
throw_tippecanoe_error(EXIT_JSON, "%s: -E%s: value %zu not a string", *argv, arg, i);
|
||||||
exit(EXIT_JSON);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set_attribute_accum(attribute_accum, k->value.string.string, v->value.string.string);
|
set_attribute_accum(attribute_accum, k->value.string.string, v->value.string.string);
|
||||||
}
|
}
|
||||||
|
|
||||||
json_free(o);
|
json_free(o);
|
||||||
json_end(jp);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *s = strchr(arg, ':');
|
const char *s = strchr(arg, ':');
|
||||||
if (s == NULL) {
|
if (s == NULL) {
|
||||||
fprintf(stderr, "-E%s option must be in the form -Ename:method\n", arg);
|
throw_tippecanoe_error(EXIT_ARGS, "-E%s option must be in the form -Ename:method", arg);
|
||||||
exit(EXIT_ARGS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string name = std::string(arg, s - 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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "can't happen: operation that isn't used by --accumulate-numeric-attributes\n");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "can't happen: operation that isn't used by --accumulate-numeric-attributes");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
full_keys.push_back(key_pool.pool(key));
|
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);
|
tmp = clip_poly1(tmp, minx, miny, maxx, maxy, ax, ay, bx, by, edge_nodes, prevent_simplify_shared_nodes);
|
||||||
if (tmp.size() > 0) {
|
if (tmp.size() > 0) {
|
||||||
if (tmp[0].first != tmp[tmp.size() - 1].first || tmp[0].second != tmp[tmp.size() - 1].second) {
|
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");
|
throw_tippecanoe_error(EXIT_FAILURE, "Internal error: Polygon ring not closed");
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t k = 0; k < tmp.size(); k++) {
|
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;
|
i = j - 1;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Unexpected operation in polygon %d\n", (int) geom[i].op);
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Unexpected operation in polygon %d", (int) geom[i].op);
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,8 +244,7 @@ static void decode_clipped(mapbox::geometry::multi_polygon<long long> &t, drawve
|
|||||||
double area = get_area(ring, 0, ring.size());
|
double area = get_area(ring, 0, ring.size());
|
||||||
|
|
||||||
if ((j == 0 && area < 0) || (j != 0 && area > 0)) {
|
if ((j == 0 && area < 0) || (j != 0 && area > 0)) {
|
||||||
fprintf(stderr, "Ring area has wrong sign: %f for %zu\n", area, j);
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Ring area has wrong sign: %f for %zu", area, j);
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t k = 0; k < ring.size(); k++) {
|
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");
|
fprintf(f, "\n\n\n\n\n");
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
fprintf(stderr, "Internal error: Polygon cleaning failed. Log in /tmp/wagyu.log\n");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: Polygon cleaning failed. Log in /tmp/wagyu.log");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scale != 1) {
|
if (scale != 1) {
|
||||||
@@ -439,8 +435,7 @@ drawvec clip_poly_poly(drawvec const &geom, drawvec const &bounds) {
|
|||||||
result.clear();
|
result.clear();
|
||||||
wagyu.execute(mapbox::geometry::wagyu::clip_type_intersection, result, mapbox::geometry::wagyu::fill_type_positive, mapbox::geometry::wagyu::fill_type_positive);
|
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) {
|
} catch (std::runtime_error &e) {
|
||||||
fprintf(stderr, "Internal error: Polygon clipping failed\n");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: Polygon clipping failed");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "get_area_scaled: can't happen");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double get_area(const drawvec &geom, size_t i, size_t j) {
|
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;
|
return d.first > minx;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "internal error inside\n");
|
throw_tippecanoe_error(EXIT_FAILURE, "internal error inside");
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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)));
|
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");
|
throw_tippecanoe_error(EXIT_FAILURE, "internal error intersecting");
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
|
// 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;
|
std::stack<int> recursion_stack;
|
||||||
|
|
||||||
if (!geom[start + 0].necessary || !geom[start + n - 1].necessary) {
|
if (!geom[start + 0].necessary || !geom[start + n - 1].necessary) {
|
||||||
fprintf(stderr, "endpoints not marked necessary\n");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "endpoints not marked necessary");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int prev = 0;
|
int prev = 0;
|
||||||
@@ -1237,12 +1228,10 @@ std::string overzoom(std::vector<input_tile> const &tiles, int nz, int nx, int n
|
|||||||
try {
|
try {
|
||||||
bool was_compressed;
|
bool was_compressed;
|
||||||
if (!tile.decode(t.tile, 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);
|
throw_tippecanoe_error(EXIT_MVT, "Couldn't parse tile %d/%u/%u", t.z, t.x, t.y);
|
||||||
exit(EXIT_MVT);
|
|
||||||
}
|
}
|
||||||
} catch (std::exception const &e) {
|
} catch (std::exception const &e) {
|
||||||
fprintf(stderr, "PBF decoding error in tile %d/%u/%u\n", t.z, t.x, t.y);
|
throw_tippecanoe_error(EXIT_PROTOBUF, "PBF decoding error in tile %d/%u/%u", t.z, t.x, t.y);
|
||||||
exit(EXIT_PROTOBUF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
source_tile out;
|
source_tile out;
|
||||||
@@ -1892,8 +1881,7 @@ drawvec fix_polygon(const drawvec &geom, bool use_winding, bool reverse_winding)
|
|||||||
i = j - 1;
|
i = j - 1;
|
||||||
outer = 0;
|
outer = 0;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Internal error: polygon ring begins with %d, not moveto\n", geom[i].op);
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: polygon ring begins with %d, not moveto", geom[i].op);
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-1
@@ -589,7 +589,7 @@ void usage(char **argv) {
|
|||||||
exit(EXIT_ARGS);
|
exit(EXIT_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int inner_main(int argc, char **argv) {
|
||||||
extern int optind;
|
extern int optind;
|
||||||
extern char *optarg;
|
extern char *optarg;
|
||||||
int i;
|
int i;
|
||||||
@@ -666,3 +666,15 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
return 0;
|
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_INCOMPLETE 100
|
||||||
#define EXIT_ARGS 101
|
#define EXIT_ARGS 101
|
||||||
#define EXIT_CLOSE 102
|
#define EXIT_CLOSE 102
|
||||||
@@ -21,3 +24,36 @@
|
|||||||
#define EXIT_WRITE 120
|
#define EXIT_WRITE 120
|
||||||
|
|
||||||
// avoid 124, 125, 126, 127, 137, which are used by GNU timeout
|
// 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 "mvt.hpp"
|
||||||
#include "evaluator.hpp"
|
#include "evaluator.hpp"
|
||||||
#include "errors.hpp"
|
#include "errors.hpp"
|
||||||
|
#include "raii.hpp"
|
||||||
#include "milo/dtoa_milo.h"
|
#include "milo/dtoa_milo.h"
|
||||||
#include "text.hpp"
|
#include "text.hpp"
|
||||||
|
|
||||||
@@ -48,8 +49,7 @@ int compare(mvt_value const &one, json_object *two, bool &fail) {
|
|||||||
break;
|
break;
|
||||||
case mvt_no_such_key:
|
case mvt_no_such_key:
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Internal error: bad mvt type %d\n", one.type);
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: bad mvt type %d", one.type);
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v < two->value.number.number) {
|
if (v < two->value.number.number) {
|
||||||
@@ -84,8 +84,7 @@ int compare(mvt_value const &one, json_object *two, bool &fail) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Internal error: bad mvt type %d\n", one.type);
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: bad mvt type %d", one.type);
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0: false
|
// 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) {
|
if (f == NULL || f->type != JSON_ARRAY) {
|
||||||
fprintf(stderr, "Filter is not an array: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "Filter is not an array: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f->value.array.length < 1) {
|
if (f->value.array.length < 1) {
|
||||||
fprintf(stderr, "Array too small in filter: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "Array too small in filter: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f->value.array.array[0]->type != JSON_STRING) {
|
if (f->value.array.array[0]->type != JSON_STRING) {
|
||||||
fprintf(stderr, "Filter operation is not a string: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "Filter operation is not a string: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(f->value.array.array[0]->value.string.string, "has") == 0 ||
|
if (strcmp(f->value.array.array[0]->value.string.string, "has") == 0 ||
|
||||||
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) {
|
if (f->value.array.length != 2) {
|
||||||
fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "Wrong number of array elements in filter: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(f->value.array.array[0]->value.string.string, "has") == 0) {
|
if (strcmp(f->value.array.array[0]->value.string.string, "has") == 0) {
|
||||||
if (f->value.array.array[1]->type != JSON_STRING) {
|
if (f->value.array.array[1]->type != JSON_STRING) {
|
||||||
fprintf(stderr, "\"has\" key is not a string: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "\"has\" key is not a string: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
return feature(std::string(f->value.array.array[1]->value.string.string)).type != mvt_no_such_key;
|
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 (strcmp(f->value.array.array[0]->value.string.string, "!has") == 0) {
|
||||||
if (f->value.array.array[1]->type != JSON_STRING) {
|
if (f->value.array.array[1]->type != JSON_STRING) {
|
||||||
fprintf(stderr, "\"!has\" key is not a string: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "\"!has\" key is not a string: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
return feature(std::string(f->value.array.array[1]->value.string.string)).type == mvt_no_such_key;
|
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 ||
|
||||||
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) {
|
if (f->value.array.length != 3) {
|
||||||
fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "Wrong number of array elements in filter: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
if (f->value.array.array[1]->type != JSON_STRING) {
|
if (f->value.array.array[1]->type != JSON_STRING) {
|
||||||
fprintf(stderr, "comparison key is not a string: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "comparison key is not a string: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mvt_value ff = feature(std::string(f->value.array.array[1]->value.string.string));
|
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;
|
return cmp <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Internal error: can't happen: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: can't happen: %s", json_stringify(f));
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(f->value.array.array[0]->value.string.string, "all") == 0 ||
|
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 ||
|
if (strcmp(f->value.array.array[0]->value.string.string, "in") == 0 ||
|
||||||
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) {
|
if (f->value.array.length < 2) {
|
||||||
fprintf(stderr, "Array too small in filter: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "Array too small in filter: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f->value.array.array[1]->type != JSON_STRING) {
|
if (f->value.array.array[1]->type != JSON_STRING) {
|
||||||
fprintf(stderr, "\"!in\" key is not a string: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "\"!in\" key is not a string: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mvt_value ff = feature(std::string(f->value.array.array[1]->value.string.string));
|
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 (strcmp(f->value.array.array[0]->value.string.string, "attribute-filter") == 0) {
|
||||||
if (f->value.array.length != 3) {
|
if (f->value.array.length != 3) {
|
||||||
fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "Wrong number of array elements in filter: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f->value.array.array[1]->type != JSON_STRING) {
|
if (f->value.array.array[1]->type != JSON_STRING) {
|
||||||
fprintf(stderr, "\"attribute-filter\" key is not a string: %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "\"attribute-filter\" key is not a string: %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = eval(feature, f->value.array.array[2], exclude_attributes, unidecode_data) > 0;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Unknown filter %s\n", json_stringify(f));
|
throw_tippecanoe_error(EXIT_FILTER, "Unknown filter %s", json_stringify(f));
|
||||||
exit(EXIT_FILTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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) {
|
if (filter == NULL || filter->type != JSON_HASH) {
|
||||||
fprintf(stderr, "Error: filter is not a hash: %s\n", json_stringify(filter));
|
throw_tippecanoe_error(EXIT_JSON, "Error: filter is not a hash: %s", json_stringify(filter));
|
||||||
exit(EXIT_JSON);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = true;
|
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) {
|
json_object *read_filter(const char *fname) {
|
||||||
FILE *fp = fopen(fname, "r");
|
unique_file fp(fopen(fname, "r"));
|
||||||
if (fp == NULL) {
|
if (fp.get() == NULL) {
|
||||||
perror(fname);
|
throw_perror(EXIT_OPEN, fname);
|
||||||
exit(EXIT_OPEN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
json_pull *jp = json_begin_file(fp);
|
unique_json_pull jp(json_begin_file(fp.get()));
|
||||||
json_object *filter = json_read_tree(jp);
|
json_object *filter = json_read_tree(jp.get());
|
||||||
if (filter == NULL) {
|
if (filter == NULL) {
|
||||||
fprintf(stderr, "%s: %s\n", fname, jp->error);
|
throw_tippecanoe_error(EXIT_JSON, "%s: %s", fname, jp.get()->error);
|
||||||
exit(EXIT_JSON);
|
|
||||||
}
|
}
|
||||||
json_disconnect(filter);
|
json_disconnect(filter);
|
||||||
json_end(jp);
|
|
||||||
fclose(fp);
|
|
||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object *parse_filter(const char *s) {
|
json_object *parse_filter(const char *s) {
|
||||||
json_pull *jp = json_begin_string(s);
|
unique_json_pull jp(json_begin_string(s));
|
||||||
json_object *filter = json_read_tree(jp);
|
json_object *filter = json_read_tree(jp.get());
|
||||||
if (filter == NULL) {
|
if (filter == NULL) {
|
||||||
fprintf(stderr, "Could not parse filter %s\n", s);
|
throw_tippecanoe_error(EXIT_JSON, "Could not parse filter %s\n%s", s, jp.get()->error);
|
||||||
fprintf(stderr, "%s\n", jp->error);
|
|
||||||
exit(EXIT_JSON);
|
|
||||||
}
|
}
|
||||||
json_disconnect(filter);
|
json_disconnect(filter);
|
||||||
json_end(jp);
|
|
||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -508,8 +508,7 @@ drawvec stairstep(drawvec &geom, int z, int detail) {
|
|||||||
|
|
||||||
// out.push_back(draw(VT_LINETO, xx, yy));
|
// out.push_back(draw(VT_LINETO, xx, yy));
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Can't happen: stairstepping lineto with no moveto\n");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Can't happen: stairstepping lineto with no moveto");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-2
@@ -365,7 +365,7 @@ void join_csv(json_object *j) {
|
|||||||
vo->value.number.large_unsigned = 0;
|
vo->value.number.large_unsigned = 0;
|
||||||
vo->value.number.large_signed = 0;
|
vo->value.number.large_signed = 0;
|
||||||
} else {
|
} else {
|
||||||
abort();
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "unexpected JSON attribute type %d", attr_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
properties->value.object.keys[properties->value.object.length] = ko;
|
properties->value.object.keys[properties->value.object.length] = ko;
|
||||||
@@ -437,7 +437,7 @@ void usage(char **argv) {
|
|||||||
exit(EXIT_ARGS);
|
exit(EXIT_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int inner_main(int argc, char **argv) {
|
||||||
const char *csv = NULL;
|
const char *csv = NULL;
|
||||||
|
|
||||||
strip_usage_headings(long_options, real_long_options);
|
strip_usage_headings(long_options, real_long_options);
|
||||||
@@ -520,3 +520,15 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
return fail;
|
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);
|
exit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int inner_main(int argc, char **argv) {
|
||||||
#ifdef MTRACE
|
#ifdef MTRACE
|
||||||
mtrace();
|
mtrace();
|
||||||
#endif
|
#endif
|
||||||
@@ -3870,6 +3870,18 @@ int main(int argc, char **argv) {
|
|||||||
return ret;
|
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 mkstemp_cloexec(char *name) {
|
||||||
int fd = mkstemp(name);
|
int fd = mkstemp(name);
|
||||||
if (fd >= 0) {
|
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);
|
deflate_s.next_out = (Bytef *) (output.data() + length);
|
||||||
int ret = deflate(&deflate_s, Z_FINISH);
|
int ret = deflate(&deflate_s, Z_FINISH);
|
||||||
if (ret != Z_STREAM_END && ret != Z_OK && ret != Z_BUF_ERROR) {
|
if (ret != Z_STREAM_END && ret != Z_OK && ret != Z_BUF_ERROR) {
|
||||||
|
deflateEnd(&deflate_s);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
length += (increase - deflate_s.avail_out);
|
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)) {
|
if (is_compressed(message)) {
|
||||||
std::string uncompressed;
|
std::string uncompressed;
|
||||||
if (decompress(message, uncompressed) == 0) {
|
if (decompress(message, uncompressed) == 0) {
|
||||||
exit(EXIT_MVT);
|
throw_tippecanoe_error(EXIT_MVT, "Tile decompression failed");
|
||||||
}
|
}
|
||||||
src = uncompressed;
|
src = uncompressed;
|
||||||
was_compressed = true;
|
was_compressed = true;
|
||||||
@@ -368,11 +369,9 @@ std::string mvt_tile::encode() {
|
|||||||
value_writer.add_bool(7, pbv.numeric_value.bool_value);
|
value_writer.add_bool(7, pbv.numeric_value.bool_value);
|
||||||
break;
|
break;
|
||||||
case mvt_null:
|
case mvt_null:
|
||||||
fprintf(stderr, "Internal error: trying to write null attribute to tile\n");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: trying to write null attribute to tile");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Internal error: trying to write undefined attribute type to tile\n");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: trying to write undefined attribute type to tile");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<sorted_value> sv = std::make_shared<sorted_value>();
|
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;
|
long long dy = wwy - py;
|
||||||
|
|
||||||
if (dx < INT_MIN || dx > INT_MAX || dy < INT_MIN || dy > INT_MAX) {
|
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);
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: Geometry delta is too big: %lld,%lld", dx, dy);
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
geometry.push_back(protozero::encode_zigzag32(dx));
|
geometry.push_back(protozero::encode_zigzag32(dx));
|
||||||
@@ -464,8 +462,7 @@ std::string mvt_tile::encode() {
|
|||||||
} else if (op == mvt_closepath) {
|
} else if (op == mvt_closepath) {
|
||||||
length++;
|
length++;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "\nInternal error: corrupted geometry\n");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "Internal error: corrupted geometry");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,8 +511,7 @@ bool mvt_value::operator<(const mvt_value &o) const {
|
|||||||
return numeric_value.null_value < o.numeric_value.null_value;
|
return numeric_value.null_value < o.numeric_value.null_value;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "mvt_value::operator<<: can't happen\n");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "mvt_value::operator<<: can't happen");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,8 +546,7 @@ bool mvt_value::operator==(const mvt_value &o) const {
|
|||||||
return numeric_value.null_value == o.numeric_value.null_value;
|
return numeric_value.null_value == o.numeric_value.null_value;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "mvt_value::operator==: can't happen\n");
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "mvt_value::operator==: can't happen");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,8 +808,7 @@ serial_val mvt_value_to_serial_val(mvt_value const &v) {
|
|||||||
sv.s = "null";
|
sv.s = "null";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "unhandled mvt_type %d\n", v.type);
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "unhandled mvt_type %d", v.type);
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sv;
|
return sv;
|
||||||
@@ -840,8 +834,7 @@ long long mvt_value_to_long_long(mvt_value const &v) {
|
|||||||
case mvt_null:
|
case mvt_null:
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "unhandled mvt_type %d\n", v.type);
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "unhandled mvt_type %d", v.type);
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -865,8 +858,7 @@ double mvt_value_to_double(mvt_value const &v) {
|
|||||||
case mvt_null:
|
case mvt_null:
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "unhandled mvt_type %d\n", v.type);
|
throw_tippecanoe_error(EXIT_IMPOSSIBLE, "unhandled mvt_type %d", v.type);
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -204,8 +204,7 @@ struct std::hash<mvt_value> {
|
|||||||
return fnv1a(sizeof(int), (void *) &k.numeric_value.null_value);
|
return fnv1a(sizeof(int), (void *) &k.numeric_value.null_value);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "mvt_value hash can't happen\n");
|
throw tippecanoe_error(EXIT_IMPOSSIBLE, "mvt_value hash: unexpected type");
|
||||||
exit(EXIT_IMPOSSIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+26
-20
@@ -12,6 +12,7 @@
|
|||||||
#include "read_json.hpp"
|
#include "read_json.hpp"
|
||||||
#include "projection.hpp"
|
#include "projection.hpp"
|
||||||
#include "usage.hpp"
|
#include "usage.hpp"
|
||||||
|
#include "raii.hpp"
|
||||||
|
|
||||||
extern char *optarg;
|
extern char *optarg;
|
||||||
extern int optind;
|
extern int optind;
|
||||||
@@ -91,24 +92,21 @@ void usage(char **argv) {
|
|||||||
std::string read_json_file(const char *fname) {
|
std::string read_json_file(const char *fname) {
|
||||||
std::string out;
|
std::string out;
|
||||||
|
|
||||||
FILE *f = fopen(fname, "r");
|
unique_file f(fopen(fname, "r"));
|
||||||
if (f == NULL) {
|
if (!f) {
|
||||||
perror(optarg);
|
throw_perror(EXIT_OPEN, fname);
|
||||||
exit(EXIT_OPEN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char buf[2000];
|
char buf[2000];
|
||||||
size_t nread;
|
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);
|
out += std::string(buf, nread);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int inner_main(int argc, char **argv) {
|
||||||
int i;
|
int i;
|
||||||
const char *outtile = NULL;
|
const char *outtile = NULL;
|
||||||
const char *outfile = NULL;
|
const char *outfile = NULL;
|
||||||
@@ -273,16 +271,14 @@ int main(int argc, char **argv) {
|
|||||||
char buf[1000];
|
char buf[1000];
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
FILE *f = fopen(s.tile.c_str(), "rb");
|
unique_file f(fopen(s.tile.c_str(), "rb"));
|
||||||
if (f == NULL) {
|
if (!f) {
|
||||||
perror(s.tile.c_str());
|
throw_perror(EXIT_OPEN, s.tile.c_str());
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
tile.append(std::string(buf, len));
|
||||||
}
|
}
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
input_tile t = s;
|
input_tile t = s;
|
||||||
t.tile = std::move(tile);
|
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);
|
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");
|
unique_file f(fopen(outfile, "wb"));
|
||||||
if (f == NULL) {
|
if (!f) {
|
||||||
perror(outfile);
|
throw_perror(EXIT_OPEN, outfile);
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite(out.c_str(), sizeof(char), out.size(), f);
|
fwrite(out.c_str(), sizeof(char), out.size(), f.get());
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
return 0;
|
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 "milo/dtoa_milo.h"
|
||||||
#include "errors.hpp"
|
#include "errors.hpp"
|
||||||
#include "serial.hpp"
|
#include "serial.hpp"
|
||||||
|
#include "raii.hpp"
|
||||||
|
|
||||||
const char *geometry_names[GEOM_TYPES] = {
|
const char *geometry_names[GEOM_TYPES] = {
|
||||||
"Point",
|
"Point",
|
||||||
@@ -99,7 +100,7 @@ void parse_coordinates(int t, json_object *j, drawvec &out, int op, const char *
|
|||||||
json_context(j);
|
json_context(j);
|
||||||
fprintf(stderr, "%s:%d: malformed point: ", fname, line);
|
fprintf(stderr, "%s:%d: malformed point: ", fname, line);
|
||||||
json_context(feature);
|
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) {
|
if (err.size() > 0) {
|
||||||
fprintf(stderr, "%s:%d: %s: ", reading, line, err.c_str());
|
fprintf(stderr, "%s:%d: %s: ", reading, line, err.c_str());
|
||||||
json_context(feature);
|
json_context(feature);
|
||||||
exit(EXIT_UTF8);
|
throw_tippecanoe_error(EXIT_UTF8, "%s:%d: %s", reading, line, err.c_str());
|
||||||
}
|
}
|
||||||
} else if (vt == JSON_NUMBER) {
|
} else if (vt == JSON_NUMBER) {
|
||||||
sv.type = mvt_double;
|
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) {
|
if (geometry_type == NULL) {
|
||||||
fprintf(stderr, "Filter output:%d: null geometry (additional not reported): ", jp->line);
|
fprintf(stderr, "Filter output:%d: null geometry (additional not reported): ", jp->line);
|
||||||
json_context(j);
|
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) {
|
if (geometry_type->type != JSON_STRING) {
|
||||||
fprintf(stderr, "Filter output:%d: geometry type is not a string: ", jp->line);
|
fprintf(stderr, "Filter output:%d: geometry type is not a string: ", jp->line);
|
||||||
json_context(j);
|
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");
|
json_object *coordinates = json_hash_get(geometry, "coordinates");
|
||||||
if (coordinates == NULL || coordinates->type != JSON_ARRAY) {
|
if (coordinates == NULL || coordinates->type != JSON_ARRAY) {
|
||||||
fprintf(stderr, "Filter output:%d: geometry without coordinates array: ", jp->line);
|
fprintf(stderr, "Filter output:%d: geometry without coordinates array: ", jp->line);
|
||||||
json_context(j);
|
json_context(j);
|
||||||
exit(EXIT_JSON);
|
throw_tippecanoe_error(EXIT_JSON, "Filter output:%d: geometry without coordinates array", jp->line);
|
||||||
}
|
}
|
||||||
|
|
||||||
int t;
|
int t;
|
||||||
@@ -209,7 +210,7 @@ std::pair<int, drawvec> parse_geometry(json_object *geometry, json_pull *jp, jso
|
|||||||
if (t >= GEOM_TYPES) {
|
if (t >= GEOM_TYPES) {
|
||||||
fprintf(stderr, "Filter output:%d: Can't handle geometry type %s: ", jp->line, geometry_type->value.string.string);
|
fprintf(stderr, "Filter output:%d: Can't handle geometry type %s: ", jp->line, geometry_type->value.string.string);
|
||||||
json_context(j);
|
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;
|
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::map<std::string, mvt_layer> ret;
|
||||||
std::shared_ptr<std::string> tile_stringpool = std::make_shared<std::string>();
|
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) {
|
while (1) {
|
||||||
json_object *j = json_read(jp);
|
json_object *j = json_read(jp.get());
|
||||||
if (j == NULL) {
|
if (j == NULL) {
|
||||||
if (jp->error != NULL) {
|
if (jp->error != NULL) {
|
||||||
fprintf(stderr, "Filter output:%d: %s: ", jp->line, jp->error);
|
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 {
|
} else {
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
exit(EXIT_JSON);
|
throw_tippecanoe_error(EXIT_JSON, "Filter output:%d: %s", jp->line, jp->error);
|
||||||
}
|
}
|
||||||
|
|
||||||
json_free(jp->root);
|
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);
|
fprintf(stderr, "Filter output:%d: feature without properties hash: ", jp->line);
|
||||||
json_context(j);
|
json_context(j);
|
||||||
json_free(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";
|
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);
|
fprintf(stderr, "Filter output:%d: filtered feature with no geometry: ", jp->line);
|
||||||
json_context(j);
|
json_context(j);
|
||||||
json_free(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;
|
int t = parsed_geometry.first;
|
||||||
drawvec &dv = parsed_geometry.second;
|
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_free(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
json_end(jp);
|
|
||||||
|
|
||||||
std::vector<mvt_layer> final;
|
std::vector<mvt_layer> final;
|
||||||
for (auto a : ret) {
|
for (auto a : ret) {
|
||||||
final.push_back(a.second);
|
final.push_back(a.second);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "milo/dtoa_milo.h"
|
#include "milo/dtoa_milo.h"
|
||||||
#include "milo/milo.h"
|
#include "milo/milo.h"
|
||||||
#include "errors.hpp"
|
#include "errors.hpp"
|
||||||
|
#include "raii.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an empty string if `s` is valid utf8;
|
* 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());
|
double d = atof(text.c_str());
|
||||||
if (!std::isfinite(d) || d != floor(d) || d < 0 || d > 32) {
|
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());
|
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;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -181,8 +182,7 @@ char *dtoa_milo(double val) {
|
|||||||
std::string s = milo::dtoa_milo(val);
|
std::string s = milo::dtoa_milo(val);
|
||||||
char *dup = strdup(s.c_str());
|
char *dup = strdup(s.c_str());
|
||||||
if (dup == NULL) {
|
if (dup == NULL) {
|
||||||
perror("strdup");
|
throw_perror(EXIT_MEMORY, "strdup");
|
||||||
exit(EXIT_MEMORY);
|
|
||||||
}
|
}
|
||||||
return dup;
|
return dup;
|
||||||
}
|
}
|
||||||
@@ -191,25 +191,22 @@ char *dtoa_milo(double val) {
|
|||||||
std::vector<std::string> read_unidecode(const char *fname) {
|
std::vector<std::string> read_unidecode(const char *fname) {
|
||||||
std::string data;
|
std::string data;
|
||||||
|
|
||||||
FILE *f = fopen(fname, "rb");
|
unique_file f(fopen(fname, "rb"));
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
perror(fname);
|
throw_perror(EXIT_OPEN, fname);
|
||||||
exit(EXIT_OPEN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string buf;
|
std::string buf;
|
||||||
buf.resize(2000);
|
buf.resize(2000);
|
||||||
|
|
||||||
while (true) {
|
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) {
|
if (nread == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
data.append(buf.c_str(), nread);
|
data.append(buf.c_str(), nread);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
std::vector<std::string> out;
|
std::vector<std::string> out;
|
||||||
out.emplace_back(); // because the data file is 1-indexed
|
out.emplace_back(); // because the data file is 1-indexed
|
||||||
out.emplace_back(); // ascii 001
|
out.emplace_back(); // ascii 001
|
||||||
|
|||||||
+13
-1
@@ -1332,7 +1332,7 @@ void usage(char **argv) {
|
|||||||
exit(EXIT_ARGS);
|
exit(EXIT_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int inner_main(int argc, char **argv) {
|
||||||
char *out_mbtiles = NULL;
|
char *out_mbtiles = NULL;
|
||||||
char *out_dir = NULL;
|
char *out_dir = NULL;
|
||||||
sqlite3 *outdb = NULL;
|
sqlite3 *outdb = NULL;
|
||||||
@@ -1683,3 +1683,15 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
return 0;
|
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() {
|
~json_writer() {
|
||||||
if (state.size() > 0) {
|
if (state.size() > 0) {
|
||||||
if (state.size() != 1 || state[0] != JSON_WRITE_TOP) {
|
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");
|
fprintf(stderr, "JSON not closed at end\n");
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user