From 0a4f6fe546d94b53ce57605c01c04b8b28136a84 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Thu, 21 Nov 2024 15:44:46 -0800 Subject: [PATCH] Factoring out geometry parsing from feature parsing --- geojson.cpp | 2 +- plugin.cpp | 2 +- read_json.cpp | 252 ++++++++++++++++++++++++++------------------------ read_json.hpp | 2 +- 4 files changed, 134 insertions(+), 124 deletions(-) diff --git a/geojson.cpp b/geojson.cpp index dae22ec3..3798d14b 100644 --- a/geojson.cpp +++ b/geojson.cpp @@ -199,7 +199,7 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom } drawvec dv; - parse_geometry(t, coordinates, dv, VT_MOVETO, sst->fname, sst->line, feature); + parse_coordinates(t, coordinates, dv, VT_MOVETO, sst->fname, sst->line, feature); serial_feature sf; sf.layer = layer; diff --git a/plugin.cpp b/plugin.cpp index 856bb14b..cfe06593 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -223,7 +223,7 @@ serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std:: } drawvec dv; - parse_geometry(t, coordinates, dv, VT_MOVETO, "Filter output", jp->line, j); + parse_coordinates(t, coordinates, dv, VT_MOVETO, "Filter output", jp->line, j); if (mb_geometry[t] == VT_POLYGON) { dv = fix_polygon(dv, false, false); } diff --git a/read_json.cpp b/read_json.cpp index c439f265..bf5a5e3f 100644 --- a/read_json.cpp +++ b/read_json.cpp @@ -53,7 +53,7 @@ void json_context(json_object *j) { free(s); // stringify } -void parse_geometry(int t, json_object *j, drawvec &out, int op, const char *fname, int line, json_object *feature) { +void parse_coordinates(int t, json_object *j, drawvec &out, int op, const char *fname, int line, json_object *feature) { if (j == NULL || j->type != JSON_ARRAY) { fprintf(stderr, "%s:%d: expected array for geometry type %d: ", fname, line, t); json_context(feature); @@ -72,7 +72,7 @@ void parse_geometry(int t, json_object *j, drawvec &out, int op, const char *fna } } - parse_geometry(within, j->value.array.array[i], out, op, fname, line, feature); + parse_coordinates(within, j->value.array.array[i], out, op, fname, line, feature); } } else { if (j->value.array.length >= 2 && j->value.array.array[0]->type == JSON_NUMBER && j->value.array.array[1]->type == JSON_NUMBER) { @@ -178,6 +178,126 @@ static std::vector to_feature(drawvec &geom) { return out; } +std::pair parse_geometry(json_object *geometry, json_pull *jp, json_object *j, + int z, int x, int y, int extent, bool fix_longitudes) { + json_object *geometry_type = json_hash_get(geometry, "type"); + if (geometry_type == NULL) { + fprintf(stderr, "Filter output:%d: null geometry (additional not reported): ", jp->line); + json_context(j); + exit(EXIT_JSON); + } + + 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); + } + + 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); + } + + int t; + for (t = 0; t < GEOM_TYPES; t++) { + if (strcmp(geometry_type->value.string.string, geometry_names[t]) == 0) { + break; + } + } + 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); + } + + drawvec dv; + parse_coordinates(t, coordinates, dv, VT_MOVETO, "Filter output", jp->line, j); + + // handle longitude wraparound + // + // this is supposed to be data for a single tile, + // so any jump from the left hand side edge of the world + // to the right edge, or vice versa, is unexpected, + // so move it to the other side. + + if (fix_longitudes && mb_geometry[t] == VT_POLYGON) { + const long long quarter_world = 1LL << 30; + const long long world = 1LL << 32; + + bool copy_to_left = false; + bool copy_to_right = false; + + for (size_t i = 0; i < dv.size(); i++) { + // is this vertex on a different side of the world + // than the first vertex? then shift this one to match + if (i > 0) { + if ((dv[0].x < quarter_world) && (dv[i].x > 3 * quarter_world)) { + dv[i].x -= world; + } + if ((dv[0].x > 3 * quarter_world) && (dv[i].x < quarter_world)) { + dv[i].x += world; + } + } + + // does it stick off the edge of the world? + // then we need another copy on the other side of the world + if (dv[i].x < 0) { + copy_to_right = true; + } + if (dv[i].x > world) { + copy_to_left = true; + } + } + + if (copy_to_left) { + size_t n = dv.size(); + for (size_t i = 0; i < n; i++) { + dv.emplace_back(dv[i].op, dv[i].x - world, (long long) dv[i].y); + } + } + if (copy_to_right) { + size_t n = dv.size(); + for (size_t i = 0; i < n; i++) { + dv.emplace_back(dv[i].op, dv[i].x + world, (long long) dv[i].y); + } + } + } + + if (mb_geometry[t] == VT_POLYGON) { + dv = fix_polygon(dv, false, false); + } + + // Offset and scale geometry from global to tile + for (size_t i = 0; i < dv.size(); i++) { + long long scale = 1LL << (32 - z); + + // offset to tile + dv[i].x -= scale * x; + dv[i].y -= scale * y; + + // scale to tile + dv[i].x = std::round(dv[i].x * extent / (double) scale); + dv[i].y = std::round(dv[i].y * extent / (double) scale); + } + + if (mb_geometry[t] == VT_POLYGON) { + // don't try scaling up because we may have coordinates + // on the other side of the world + dv = clean_or_clip_poly(dv, z, 256, true, false); + if (dv.size() < 3) { + dv.clear(); + } + } + dv = remove_noop(dv, mb_geometry[t], 0); + if (mb_geometry[t] == VT_POLYGON) { + dv = close_poly(dv); + } + + return std::pair(t, dv); +} + std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int extent, bool fix_longitudes) { std::map ret; std::shared_ptr tile_stringpool = std::make_shared(); @@ -208,14 +328,6 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int continue; } - json_object *geometry = json_hash_get(j, "geometry"); - if (geometry == NULL) { - fprintf(stderr, "Filter output:%d: filtered feature with no geometry: ", jp->line); - json_context(j); - json_free(j); - exit(EXIT_JSON); - } - json_object *properties = json_hash_get(j, "properties"); if (properties == NULL || (properties->type != JSON_HASH && properties->type != JSON_NULL)) { fprintf(stderr, "Filter output:%d: feature without properties hash: ", jp->line); @@ -224,38 +336,6 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int exit(EXIT_JSON); } - json_object *geometry_type = json_hash_get(geometry, "type"); - if (geometry_type == NULL) { - fprintf(stderr, "Filter output:%d: null geometry (additional not reported): ", jp->line); - json_context(j); - exit(EXIT_JSON); - } - - 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); - } - - json_object *coordinates = json_hash_get(geometry, "coordinates"); - if (coordinates == NULL || coordinates->type != JSON_ARRAY) { - fprintf(stderr, "Filter output:%d: feature without coordinates array: ", jp->line); - json_context(j); - exit(EXIT_JSON); - } - - int t; - for (t = 0; t < GEOM_TYPES; t++) { - if (strcmp(geometry_type->value.string.string, geometry_names[t]) == 0) { - break; - } - } - 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); - } - std::string layername = "unknown"; json_object *tippecanoe = json_hash_get(j, "tippecanoe"); json_object *layer = NULL; @@ -276,88 +356,18 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int } auto l = ret.find(layername); - drawvec dv; - parse_geometry(t, coordinates, dv, VT_MOVETO, "Filter output", jp->line, j); - - // handle longitude wraparound - // - // this is supposed to be data for a single tile, - // so any jump from the left hand side edge of the world - // to the right edge, or vice versa, is unexpected, - // so move it to the other side. - - if (fix_longitudes && mb_geometry[t] == VT_POLYGON) { - const long long quarter_world = 1LL << 30; - const long long world = 1LL << 32; - - bool copy_to_left = false; - bool copy_to_right = false; - - for (size_t i = 0; i < dv.size(); i++) { - // is this vertex on a different side of the world - // than the first vertex? then shift this one to match - if (i > 0) { - if ((dv[0].x < quarter_world) && (dv[i].x > 3 * quarter_world)) { - dv[i].x -= world; - } - if ((dv[0].x > 3 * quarter_world) && (dv[i].x < quarter_world)) { - dv[i].x += world; - } - } - - // does it stick off the edge of the world? - // then we need another copy on the other side of the world - if (dv[i].x < 0) { - copy_to_right = true; - } - if (dv[i].x > world) { - copy_to_left = true; - } - } - - if (copy_to_left) { - size_t n = dv.size(); - for (size_t i = 0; i < n; i++) { - dv.emplace_back(dv[i].op, dv[i].x - world, (long long) dv[i].y); - } - } - if (copy_to_right) { - size_t n = dv.size(); - for (size_t i = 0; i < n; i++) { - dv.emplace_back(dv[i].op, dv[i].x + world, (long long) dv[i].y); - } - } - } - - if (mb_geometry[t] == VT_POLYGON) { - dv = fix_polygon(dv, false, false); + json_object *geometry = json_hash_get(j, "geometry"); + if (geometry == NULL) { + fprintf(stderr, "Filter output:%d: filtered feature with no geometry: ", jp->line); + json_context(j); + json_free(j); + exit(EXIT_JSON); } - // Offset and scale geometry from global to tile - for (size_t i = 0; i < dv.size(); i++) { - long long scale = 1LL << (32 - z); - - // offset to tile - dv[i].x -= scale * x; - dv[i].y -= scale * y; + std::pair parsed_geometry = parse_geometry(geometry, jp, j, z, x, y, extent, fix_longitudes); - // scale to tile - dv[i].x = std::round(dv[i].x * extent / (double) scale); - dv[i].y = std::round(dv[i].y * extent / (double) scale); - } - - if (mb_geometry[t] == VT_POLYGON) { - // don't try scaling up because we may have coordinates - // on the other side of the world - dv = clean_or_clip_poly(dv, z, 256, true, false); - if (dv.size() < 3) { - dv.clear(); - } - } - dv = remove_noop(dv, mb_geometry[t], 0); - if (mb_geometry[t] == VT_POLYGON) { - dv = close_poly(dv); - } + int t = parsed_geometry.first; + drawvec &dv = parsed_geometry.second; if (dv.size() > 0) { mvt_feature feature; diff --git a/read_json.hpp b/read_json.hpp index 487329c6..84e0853b 100644 --- a/read_json.hpp +++ b/read_json.hpp @@ -11,7 +11,7 @@ extern int geometry_within[GEOM_TYPES]; extern int mb_geometry[GEOM_TYPES]; void json_context(json_object *j); -void parse_geometry(int t, json_object *j, drawvec &out, int op, const char *fname, int line, json_object *feature); +void parse_coordinates(int t, json_object *j, drawvec &out, int op, const char *fname, int line, json_object *feature); std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int extent, bool fix_longitudes); serial_val stringify_value(json_object *value, const char *reading, int line, json_object *feature);