diff --git a/geometry.cpp b/geometry.cpp index c004f80..3ea97b0 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/plugin.cpp b/plugin.cpp index 0fb0837..66070b7 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -15,14 +15,15 @@ #include #include #include "mvt.hpp" -#include "plugin.hpp" #include "projection.hpp" #include "geometry.hpp" +#include "serial.hpp" extern "C" { #include "jsonpull/jsonpull.h" } +#include "plugin.hpp" #include "write_json.hpp" #include "read_json.hpp" @@ -203,9 +204,140 @@ mvt_layer parse_layer(int fd, unsigned z, unsigned x, unsigned y, mvt_layer cons } json_end(jp); + fclose(f); return ret; } +serial_feature parse_feature(json_pull *jp, unsigned z, unsigned x, unsigned y) { + serial_feature sf; + + while (1) { + json_object *j = json_read(jp); + if (j == NULL) { + if (jp->error != NULL) { + fprintf(stderr, "Filter output:%d: %s\n", jp->line, jp->error); + if (jp->root != NULL) { + json_context(jp->root); + } + exit(EXIT_FAILURE); + } + + json_free(jp->root); + sf.t = -1; + return sf; + } + + json_object *type = json_hash_get(j, "type"); + if (type == NULL || type->type != JSON_STRING) { + continue; + } + if (strcmp(type->string, "Feature") != 0) { + continue; + } + + json_object *geometry = json_hash_get(j, "geometry"); + if (geometry == NULL) { + fprintf(stderr, "Filter output:%d: filtered feature with no geometry\n", jp->line); + json_context(j); + json_free(j); + exit(EXIT_FAILURE); + } + + 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\n", jp->line); + json_context(j); + json_free(j); + exit(EXIT_FAILURE); + } + + json_object *geometry_type = json_hash_get(geometry, "type"); + if (geometry_type == NULL) { + fprintf(stderr, "Filter output:%d: null geometry (additional not reported)\n", jp->line); + json_context(j); + exit(EXIT_FAILURE); + } + + if (geometry_type->type != JSON_STRING) { + fprintf(stderr, "Filter output:%d: geometry type is not a string\n", jp->line); + json_context(j); + exit(EXIT_FAILURE); + } + + json_object *coordinates = json_hash_get(geometry, "coordinates"); + if (coordinates == NULL || coordinates->type != JSON_ARRAY) { + fprintf(stderr, "Filter output:%d: feature without coordinates array\n", jp->line); + json_context(j); + exit(EXIT_FAILURE); + } + + int t; + for (t = 0; t < GEOM_TYPES; t++) { + if (strcmp(geometry_type->string, geometry_names[t]) == 0) { + break; + } + } + if (t >= GEOM_TYPES) { + fprintf(stderr, "Filter output:%d: Can't handle geometry type %s\n", jp->line, geometry_type->string); + json_context(j); + exit(EXIT_FAILURE); + } + + drawvec dv; + parse_geometry(t, coordinates, dv, VT_MOVETO, "Filter output", jp->line, j); + if (mb_geometry[t] == VT_POLYGON) { + dv = fix_polygon(dv); + } + + // Scale and offset geometry from global to tile + for (size_t i = 0; i < dv.size(); i++) { + unsigned sx = 0, sy = 0; + if (z != 0) { + sx = x << (32 - z); + sy = y << (32 - z); + } + dv[i].x -= sx; + dv[i].y -= sy; + } + + if (dv.size() > 0) { + sf.t = mb_geometry[t]; + sf.geometry = dv; + sf.segment = 0; + sf.layer = 0; // XXX + sf.seq = 0; // XXX + sf.index = 0; // XXX + sf.bbox[0] = sf.bbox[1] = sf.bbox[2] = sf.bbox[3] = 0; // XXX + sf.extent = 0; // XXX + sf.m = 0; // XXX + sf.metapos = 0; // XXX + sf.has_id = false; + + json_object *id = json_hash_get(j, "id"); + if (id != NULL) { + sf.id = atoll(id->string); + sf.has_id = true; + } + + for (size_t i = 0; i < properties->length; i++) { + serial_val v; + v.type = -1; + + stringify_value(properties->values[i], v.type, v.s, "Filter output", jp->line, j); + + if (v.type >= 0) { + sf.kv.insert(std::pair(std::string(properties->keys[i]->string), v)); + } + } + + json_free(j); + return sf; + } + + json_free(j); + } +} + static pthread_mutex_t pipe_lock = PTHREAD_MUTEX_INITIALIZER; void setup_filter(const char *filter, int *write_to, int *read_from, pid_t *pid, unsigned z, unsigned x, unsigned y) { diff --git a/plugin.hpp b/plugin.hpp index de6e450..d0ff01e 100644 --- a/plugin.hpp +++ b/plugin.hpp @@ -1,2 +1,3 @@ mvt_layer filter_layer(const char *filter, mvt_layer &layer, unsigned z, unsigned x, unsigned y); void setup_filter(const char *filter, int *write_to, int *read_from, pid_t *pid, unsigned z, unsigned x, unsigned y); +serial_feature parse_feature(json_pull *jp, unsigned z, unsigned x, unsigned y); diff --git a/serial.hpp b/serial.hpp index 065ca0c..a554920 100644 --- a/serial.hpp +++ b/serial.hpp @@ -19,6 +19,11 @@ int deserialize_ulong_long_io(FILE *f, unsigned long long *n, long long *geompos int deserialize_uint_io(FILE *f, unsigned *n, long long *geompos); int deserialize_byte_io(FILE *f, signed char *n, long long *geompos); +struct serial_val { + int type; + std::string s; +}; + struct serial_feature { long long layer; int segment; @@ -47,6 +52,7 @@ struct serial_feature { // XXX This isn't serialized. Should it be here? long long bbox[4]; + std::map kv; }; void serialize_feature(FILE *geomfile, serial_feature *sf, long long *geompos, const char *fname, long long wx, long long wy, bool include_minzoom); diff --git a/tile.cpp b/tile.cpp index e213407..883b8b2 100644 --- a/tile.cpp +++ b/tile.cpp @@ -35,9 +35,14 @@ #include "serial.hpp" #include "options.hpp" #include "main.hpp" -#include "plugin.hpp" #include "write_json.hpp" +extern "C" { +#include "jsonpull/jsonpull.h" +} + +#include "plugin.hpp" + #define CMD_BITS 3 #define XSTRINGIFY(s) STRINGIFY(s) @@ -1450,6 +1455,8 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s FILE *prefilter_fp = NULL; pthread_t prefilter_writer; run_prefilter_args rpa; // here so it stays in scope until joined + FILE *prefilter_read_fp; + json_pull *prefilter_jp; if (prefilter != NULL) { setup_filter(prefilter, &prefilter_write, &prefilter_read, &prefilter_pid, z, tx, ty); @@ -1497,6 +1504,13 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s perror("pthread_create (prefilter writer)"); exit(EXIT_FAILURE); } + + prefilter_read_fp = fdopen(prefilter_read, "r"); + if (prefilter_read_fp == NULL) { + perror("fdopen prefilter output"); + exit(EXIT_FAILURE); + } + prefilter_jp = json_begin_file(prefilter_read_fp); } while (1) { @@ -1505,8 +1519,7 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s if (prefilter == NULL) { sf = next_feature(geoms, geompos_in, metabase, meta_off, z, tx, ty, initial_x, initial_y, &original_features, &unclipped_features, nextzoom, maxzoom, minzoom, max_zoom_increment, pass, passes, along, alongminus, buffer, within, &first_time, line_detail, geomfile, geompos, &oprogress, todo, fname, child_shards); } else { - // XXX parse prefilter - break; + sf = parse_feature(prefilter_jp, z, tx, ty); } if (sf.t < 0) { @@ -1586,7 +1599,8 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s } if (prefilter != NULL) { - if (close(prefilter_read) != 0) { + json_end(prefilter_jp); + if (fclose(prefilter_read_fp) != 0) { perror("close output from prefilter"); exit(EXIT_FAILURE); }