2015-07-29 23:46:56 +00:00
|
|
|
#ifdef MTRACE
|
2015-08-21 21:27:11 +00:00
|
|
|
#include <mcheck.h>
|
2015-07-29 23:45:41 +00:00
|
|
|
#endif
|
|
|
|
|
2014-09-15 22:02:33 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <ctype.h>
|
2014-09-15 22:27:35 +00:00
|
|
|
#include <errno.h>
|
2014-09-16 18:16:51 +00:00
|
|
|
#include <limits.h>
|
2014-09-25 22:20:17 +00:00
|
|
|
#include <sqlite3.h>
|
2014-09-25 23:00:11 +00:00
|
|
|
#include <stdarg.h>
|
2015-11-12 00:10:39 +00:00
|
|
|
#include <sys/resource.h>
|
2015-12-22 22:02:31 +00:00
|
|
|
#include <pthread.h>
|
2016-04-27 22:09:06 +00:00
|
|
|
#include <vector>
|
2016-04-28 19:57:03 +00:00
|
|
|
#include <set>
|
|
|
|
#include <string>
|
2016-03-31 22:29:43 +00:00
|
|
|
|
2016-04-27 20:54:00 +00:00
|
|
|
extern "C" {
|
2016-04-27 22:33:30 +00:00
|
|
|
#include "jsonpull/jsonpull.h"
|
2016-04-27 20:54:00 +00:00
|
|
|
}
|
2014-09-15 22:18:08 +00:00
|
|
|
|
2016-04-27 21:00:14 +00:00
|
|
|
#include "pool.hpp"
|
|
|
|
#include "projection.hpp"
|
|
|
|
#include "version.hpp"
|
|
|
|
#include "memfile.hpp"
|
2016-04-27 21:19:10 +00:00
|
|
|
#include "serial.hpp"
|
2016-04-27 21:59:20 +00:00
|
|
|
#include "main.hpp"
|
2016-04-28 21:43:04 +00:00
|
|
|
#include "mbtiles.hpp"
|
2016-04-27 21:59:20 +00:00
|
|
|
#include "geojson.hpp"
|
2016-04-27 22:09:06 +00:00
|
|
|
#include "geometry.hpp"
|
2016-05-21 00:50:20 +00:00
|
|
|
#include "options.hpp"
|
2016-03-28 22:03:28 +00:00
|
|
|
|
2015-06-03 18:21:40 +00:00
|
|
|
#define GEOM_POINT 0 /* array of positions */
|
|
|
|
#define GEOM_MULTIPOINT 1 /* array of arrays of positions */
|
|
|
|
#define GEOM_LINESTRING 2 /* array of arrays of positions */
|
|
|
|
#define GEOM_MULTILINESTRING 3 /* array of arrays of arrays of positions */
|
|
|
|
#define GEOM_POLYGON 4 /* array of arrays of arrays of positions */
|
|
|
|
#define GEOM_MULTIPOLYGON 5 /* array of arrays of arrays of arrays of positions */
|
2014-09-15 22:27:35 +00:00
|
|
|
#define GEOM_TYPES 6
|
|
|
|
|
2016-03-25 20:03:57 +00:00
|
|
|
static const char *geometry_names[GEOM_TYPES] = {
|
2015-06-03 18:21:40 +00:00
|
|
|
"Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon",
|
2014-09-15 22:02:33 +00:00
|
|
|
};
|
|
|
|
|
2016-03-25 20:03:57 +00:00
|
|
|
static int geometry_within[GEOM_TYPES] = {
|
2015-06-03 18:21:40 +00:00
|
|
|
-1, /* point */
|
|
|
|
GEOM_POINT, /* multipoint */
|
|
|
|
GEOM_POINT, /* linestring */
|
|
|
|
GEOM_LINESTRING, /* multilinestring */
|
|
|
|
GEOM_LINESTRING, /* polygon */
|
|
|
|
GEOM_POLYGON, /* multipolygon */
|
2014-09-15 22:02:33 +00:00
|
|
|
};
|
|
|
|
|
2016-03-25 20:03:57 +00:00
|
|
|
static int mb_geometry[GEOM_TYPES] = {
|
2015-06-03 18:21:40 +00:00
|
|
|
VT_POINT, VT_POINT, VT_LINE, VT_LINE, VT_POLYGON, VT_POLYGON,
|
2014-09-16 00:33:54 +00:00
|
|
|
};
|
|
|
|
|
2016-05-21 00:50:20 +00:00
|
|
|
long long parse_geometry(int t, json_object *j, long long *bbox, long long *fpos, FILE *out, int op, const char *fname, int line, long long *wx, long long *wy, int *initialized, unsigned *initial_x, unsigned *initial_y) {
|
|
|
|
long long g = 0;
|
|
|
|
|
2014-09-15 23:32:06 +00:00
|
|
|
if (j == NULL || j->type != JSON_ARRAY) {
|
2015-12-18 23:12:27 +00:00
|
|
|
fprintf(stderr, "%s:%d: expected array for type %d\n", fname, line, t);
|
2016-05-21 00:50:20 +00:00
|
|
|
return g;
|
2014-09-15 23:32:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int within = geometry_within[t];
|
|
|
|
if (within >= 0) {
|
2016-03-25 20:03:57 +00:00
|
|
|
size_t i;
|
2014-09-15 23:32:06 +00:00
|
|
|
for (i = 0; i < j->length; i++) {
|
2014-09-16 00:33:54 +00:00
|
|
|
if (within == GEOM_POINT) {
|
2014-09-23 21:02:18 +00:00
|
|
|
if (i == 0 || mb_geometry[t] == GEOM_MULTIPOINT) {
|
2014-09-16 21:11:50 +00:00
|
|
|
op = VT_MOVETO;
|
2014-09-16 00:33:54 +00:00
|
|
|
} else {
|
2014-09-16 21:11:50 +00:00
|
|
|
op = VT_LINETO;
|
2014-09-16 20:51:22 +00:00
|
|
|
}
|
2014-09-16 00:33:54 +00:00
|
|
|
}
|
|
|
|
|
2016-05-21 00:50:20 +00:00
|
|
|
g += parse_geometry(within, j->array[i], bbox, fpos, out, op, fname, line, wx, wy, initialized, initial_x, initial_y);
|
2014-09-15 23:32:06 +00:00
|
|
|
}
|
|
|
|
} else {
|
2014-12-09 23:23:22 +00:00
|
|
|
if (j->length >= 2 && j->array[0]->type == JSON_NUMBER && j->array[1]->type == JSON_NUMBER) {
|
2015-12-03 21:12:34 +00:00
|
|
|
long long x, y;
|
2014-09-16 23:39:56 +00:00
|
|
|
double lon = j->array[0]->number;
|
|
|
|
double lat = j->array[1]->number;
|
2016-06-01 23:55:52 +00:00
|
|
|
projection->project(lon, lat, 32, &x, &y);
|
2014-09-16 23:39:56 +00:00
|
|
|
|
2014-12-09 23:23:22 +00:00
|
|
|
if (j->length > 2) {
|
|
|
|
static int warned = 0;
|
|
|
|
|
|
|
|
if (!warned) {
|
2015-12-18 23:12:27 +00:00
|
|
|
fprintf(stderr, "%s:%d: ignoring dimensions beyond two\n", fname, line);
|
2014-12-09 23:23:22 +00:00
|
|
|
warned = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-21 00:50:20 +00:00
|
|
|
if (x < bbox[0]) {
|
|
|
|
bbox[0] = x;
|
|
|
|
}
|
|
|
|
if (y < bbox[1]) {
|
|
|
|
bbox[1] = y;
|
|
|
|
}
|
|
|
|
if (x > bbox[2]) {
|
|
|
|
bbox[2] = x;
|
|
|
|
}
|
|
|
|
if (y > bbox[3]) {
|
|
|
|
bbox[3] = y;
|
2014-09-16 18:16:51 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 17:50:57 +00:00
|
|
|
if (!*initialized) {
|
2016-02-02 21:33:49 +00:00
|
|
|
if (x < 0 || x >= (1LL << 32) || y < 0 || y >= (1LL < 32)) {
|
|
|
|
*initial_x = 1LL << 31;
|
|
|
|
*initial_y = 1LL << 31;
|
|
|
|
*wx = 1LL << 31;
|
|
|
|
*wy = 1LL << 31;
|
|
|
|
} else {
|
|
|
|
*initial_x = (x >> geometry_scale) << geometry_scale;
|
|
|
|
*initial_y = (y >> geometry_scale) << geometry_scale;
|
|
|
|
*wx = x;
|
|
|
|
*wy = y;
|
|
|
|
}
|
|
|
|
|
2015-06-18 17:50:57 +00:00
|
|
|
*initialized = 1;
|
|
|
|
}
|
|
|
|
|
2015-03-24 00:44:23 +00:00
|
|
|
serialize_byte(out, op, fpos, fname);
|
2015-06-18 21:16:16 +00:00
|
|
|
serialize_long_long(out, (x >> geometry_scale) - (*wx >> geometry_scale), fpos, fname);
|
|
|
|
serialize_long_long(out, (y >> geometry_scale) - (*wy >> geometry_scale), fpos, fname);
|
2015-06-18 00:48:29 +00:00
|
|
|
*wx = x;
|
|
|
|
*wy = y;
|
2016-05-21 00:50:20 +00:00
|
|
|
g++;
|
2014-09-15 23:32:06 +00:00
|
|
|
} else {
|
2015-12-18 23:12:27 +00:00
|
|
|
fprintf(stderr, "%s:%d: malformed point\n", fname, line);
|
2014-09-15 23:32:06 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-16 00:33:54 +00:00
|
|
|
|
2014-10-15 23:30:33 +00:00
|
|
|
if (t == GEOM_POLYGON) {
|
2015-10-07 23:52:52 +00:00
|
|
|
// Note that this is not using the correct meaning of closepath.
|
|
|
|
//
|
|
|
|
// We are using it here to close an entire Polygon, to distinguish
|
|
|
|
// the Polygons within a MultiPolygon from each other.
|
|
|
|
//
|
|
|
|
// This will be undone in fix_polygon(), which needs to know which
|
|
|
|
// rings come from which Polygons so that it can make the winding order
|
|
|
|
// of the outer ring be the opposite of the order of the inner rings.
|
|
|
|
|
|
|
|
serialize_byte(out, VT_CLOSEPATH, fpos, fname);
|
2014-09-16 00:33:54 +00:00
|
|
|
}
|
2016-05-21 00:50:20 +00:00
|
|
|
|
|
|
|
return g;
|
2014-09-15 23:32:06 +00:00
|
|
|
}
|
2014-09-15 22:02:33 +00:00
|
|
|
|
2016-05-10 22:30:49 +00:00
|
|
|
int serialize_geometry(json_object *geometry, json_object *properties, const char *reading, int line, volatile long long *layer_seq, volatile long long *progress_seq, long long *metapos, long long *geompos, long long *indexpos, std::set<std::string> *exclude, std::set<std::string> *include, int exclude_all, FILE *metafile, FILE *geomfile, FILE *indexfile, struct memfile *poolfile, struct memfile *treefile, const char *fname, int basezoom, int layer, double droprate, long long *file_bbox, json_object *tippecanoe, int segment, int *initialized, unsigned *initial_x, unsigned *initial_y, struct reader *readers, std::set<type_and_string> *file_keys, int maxzoom) {
|
2015-08-11 23:46:20 +00:00
|
|
|
json_object *geometry_type = json_hash_get(geometry, "type");
|
|
|
|
if (geometry_type == NULL) {
|
|
|
|
static int warned = 0;
|
|
|
|
if (!warned) {
|
2015-12-18 23:12:27 +00:00
|
|
|
fprintf(stderr, "%s:%d: null geometry (additional not reported)\n", reading, line);
|
2015-08-11 23:46:20 +00:00
|
|
|
warned = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (geometry_type->type != JSON_STRING) {
|
2015-12-18 23:12:27 +00:00
|
|
|
fprintf(stderr, "%s:%d: geometry without type\n", reading, line);
|
2015-08-11 23:46:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
json_object *coordinates = json_hash_get(geometry, "coordinates");
|
|
|
|
if (coordinates == NULL || coordinates->type != JSON_ARRAY) {
|
2015-12-18 23:12:27 +00:00
|
|
|
fprintf(stderr, "%s:%d: feature without coordinates array\n", reading, line);
|
2015-08-11 23:46:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int t;
|
|
|
|
for (t = 0; t < GEOM_TYPES; t++) {
|
|
|
|
if (strcmp(geometry_type->string, geometry_names[t]) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (t >= GEOM_TYPES) {
|
2015-12-18 23:12:27 +00:00
|
|
|
fprintf(stderr, "%s:%d: Can't handle geometry type %s\n", reading, line, geometry_type->string);
|
2015-08-11 23:46:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-06 23:51:23 +00:00
|
|
|
int tippecanoe_minzoom = -1;
|
|
|
|
int tippecanoe_maxzoom = -1;
|
|
|
|
|
|
|
|
if (tippecanoe != NULL) {
|
|
|
|
json_object *min = json_hash_get(tippecanoe, "minzoom");
|
|
|
|
if (min != NULL && min->type == JSON_NUMBER) {
|
|
|
|
tippecanoe_minzoom = min->number;
|
|
|
|
}
|
2015-10-07 20:54:31 +00:00
|
|
|
if (min != NULL && min->type == JSON_STRING) {
|
|
|
|
tippecanoe_minzoom = atoi(min->string);
|
|
|
|
}
|
2015-10-06 23:51:23 +00:00
|
|
|
|
|
|
|
json_object *max = json_hash_get(tippecanoe, "maxzoom");
|
|
|
|
if (max != NULL && max->type == JSON_NUMBER) {
|
|
|
|
tippecanoe_maxzoom = max->number;
|
|
|
|
}
|
2015-10-07 20:54:31 +00:00
|
|
|
if (max != NULL && max->type == JSON_STRING) {
|
|
|
|
tippecanoe_maxzoom = atoi(max->string);
|
|
|
|
}
|
2015-10-06 23:51:23 +00:00
|
|
|
}
|
|
|
|
|
2016-05-10 22:30:49 +00:00
|
|
|
long long bbox[] = {LLONG_MAX, LLONG_MAX, LLONG_MIN, LLONG_MIN};
|
2015-08-11 23:46:20 +00:00
|
|
|
|
|
|
|
int nprop = 0;
|
2015-09-26 00:28:15 +00:00
|
|
|
if (properties != NULL && properties->type == JSON_HASH) {
|
2015-08-11 23:46:20 +00:00
|
|
|
nprop = properties->length;
|
|
|
|
}
|
|
|
|
|
|
|
|
long long metastart = *metapos;
|
|
|
|
char *metakey[nprop];
|
2016-04-27 20:54:00 +00:00
|
|
|
const char *metaval[nprop];
|
2015-08-11 23:46:20 +00:00
|
|
|
int metatype[nprop];
|
2016-01-27 22:29:34 +00:00
|
|
|
int mustfree[nprop];
|
2015-08-11 23:46:20 +00:00
|
|
|
int m = 0;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < nprop; i++) {
|
|
|
|
if (properties->keys[i]->type == JSON_STRING) {
|
2016-04-28 21:43:04 +00:00
|
|
|
std::string s(properties->keys[i]->string);
|
|
|
|
|
2015-08-11 23:46:20 +00:00
|
|
|
if (exclude_all) {
|
2016-04-28 21:43:04 +00:00
|
|
|
if (include->count(s) == 0) {
|
2015-08-11 23:46:20 +00:00
|
|
|
continue;
|
|
|
|
}
|
2016-04-28 21:43:04 +00:00
|
|
|
} else if (exclude->count(s) != 0) {
|
2015-08-11 23:46:20 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-04-28 21:43:04 +00:00
|
|
|
type_and_string tas;
|
|
|
|
tas.string = s;
|
|
|
|
tas.type = -1;
|
|
|
|
|
2015-08-11 23:46:20 +00:00
|
|
|
metakey[m] = properties->keys[i]->string;
|
2016-01-27 22:29:34 +00:00
|
|
|
mustfree[m] = 0;
|
2015-08-11 23:46:20 +00:00
|
|
|
|
|
|
|
if (properties->values[i] != NULL && properties->values[i]->type == JSON_STRING) {
|
2016-04-28 21:43:04 +00:00
|
|
|
tas.type = metatype[m] = VT_STRING;
|
2015-08-11 23:46:20 +00:00
|
|
|
metaval[m] = properties->values[i]->string;
|
|
|
|
m++;
|
|
|
|
} else if (properties->values[i] != NULL && properties->values[i]->type == JSON_NUMBER) {
|
2016-04-28 21:43:04 +00:00
|
|
|
tas.type = metatype[m] = VT_NUMBER;
|
2015-08-11 23:46:20 +00:00
|
|
|
metaval[m] = properties->values[i]->string;
|
|
|
|
m++;
|
|
|
|
} else if (properties->values[i] != NULL && (properties->values[i]->type == JSON_TRUE || properties->values[i]->type == JSON_FALSE)) {
|
2016-04-28 21:43:04 +00:00
|
|
|
tas.type = metatype[m] = VT_BOOLEAN;
|
2015-08-11 23:46:20 +00:00
|
|
|
metaval[m] = properties->values[i]->type == JSON_TRUE ? "true" : "false";
|
|
|
|
m++;
|
|
|
|
} else if (properties->values[i] != NULL && (properties->values[i]->type == JSON_NULL)) {
|
|
|
|
;
|
|
|
|
} else {
|
2016-04-28 21:43:04 +00:00
|
|
|
tas.type = metatype[m] = VT_STRING;
|
2016-01-27 22:29:34 +00:00
|
|
|
metaval[m] = json_stringify(properties->values[i]);
|
|
|
|
mustfree[m] = 1;
|
|
|
|
m++;
|
2015-08-11 23:46:20 +00:00
|
|
|
}
|
2016-04-28 21:43:04 +00:00
|
|
|
|
|
|
|
if (tas.type >= 0) {
|
|
|
|
file_keys->insert(tas);
|
|
|
|
}
|
2015-08-11 23:46:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
long long geomstart = *geompos;
|
|
|
|
|
|
|
|
serialize_byte(geomfile, mb_geometry[t], geompos, fname);
|
2015-12-23 00:29:45 +00:00
|
|
|
serialize_long_long(geomfile, *layer_seq, geompos, fname);
|
2015-10-06 23:51:23 +00:00
|
|
|
|
|
|
|
serialize_long_long(geomfile, (layer << 2) | ((tippecanoe_minzoom != -1) << 1) | (tippecanoe_maxzoom != -1), geompos, fname);
|
|
|
|
if (tippecanoe_minzoom != -1) {
|
|
|
|
serialize_int(geomfile, tippecanoe_minzoom, geompos, fname);
|
|
|
|
}
|
|
|
|
if (tippecanoe_maxzoom != -1) {
|
|
|
|
serialize_int(geomfile, tippecanoe_maxzoom, geompos, fname);
|
|
|
|
}
|
|
|
|
|
2015-12-22 01:21:18 +00:00
|
|
|
serialize_int(geomfile, segment, geompos, fname);
|
2015-12-23 00:58:27 +00:00
|
|
|
long long wx = *initial_x, wy = *initial_y;
|
2016-05-21 00:50:20 +00:00
|
|
|
long long g = parse_geometry(t, coordinates, bbox, geompos, geomfile, VT_MOVETO, fname, line, &wx, &wy, initialized, initial_x, initial_y);
|
2015-08-11 23:46:20 +00:00
|
|
|
serialize_byte(geomfile, VT_END, geompos, fname);
|
|
|
|
|
2016-05-10 22:30:49 +00:00
|
|
|
bool inline_meta = true;
|
|
|
|
// Don't inline metadata for features that will span several tiles at maxzoom
|
2016-05-21 00:50:20 +00:00
|
|
|
if (g > 0 && (bbox[2] < bbox[0] || bbox[3] < bbox[1])) {
|
2016-05-24 00:44:41 +00:00
|
|
|
fprintf(stderr, "Internal error: impossible feature bounding box %llx,%llx,%llx,%llx\n", bbox[0], bbox[1], bbox[2], bbox[3]);
|
2016-05-21 00:50:20 +00:00
|
|
|
}
|
2016-05-10 22:30:49 +00:00
|
|
|
if (bbox[2] - bbox[0] > (2LL << (32 - maxzoom)) || bbox[3] - bbox[1] > (2LL << (32 - maxzoom))) {
|
|
|
|
inline_meta = false;
|
2016-05-21 00:50:20 +00:00
|
|
|
|
|
|
|
if (prevent[P_CLIPPING]) {
|
|
|
|
static volatile long long warned = 0;
|
2016-05-25 18:08:04 +00:00
|
|
|
long long extent = ((bbox[2] - bbox[0]) / ((1LL << (32 - maxzoom)) + 1)) * ((bbox[3] - bbox[1]) / ((1LL << (32 - maxzoom)) + 1));
|
2016-05-21 00:50:20 +00:00
|
|
|
if (extent > warned) {
|
2016-05-25 18:08:04 +00:00
|
|
|
fprintf(stderr, "Warning: %s:%d: Large unclipped (-pc) feature may be duplicated across %lld tiles\n", fname, line, extent);
|
2016-05-21 00:50:20 +00:00
|
|
|
warned = extent;
|
|
|
|
|
|
|
|
if (extent > 10000) {
|
|
|
|
fprintf(stderr, "Exiting because this can't be right.\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-05-10 22:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
serialize_int(geomfile, m, geompos, fname);
|
|
|
|
if (inline_meta) {
|
|
|
|
serialize_long_long(geomfile, -1, geompos, fname);
|
|
|
|
|
|
|
|
for (i = 0; i < m; i++) {
|
|
|
|
serialize_long_long(geomfile, addpool(poolfile, treefile, metakey[i], VT_STRING), geompos, fname);
|
|
|
|
serialize_long_long(geomfile, addpool(poolfile, treefile, metaval[i], metatype[i]), geompos, fname);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
serialize_long_long(geomfile, metastart, geompos, fname);
|
|
|
|
|
|
|
|
for (i = 0; i < m; i++) {
|
|
|
|
serialize_long_long(metafile, addpool(poolfile, treefile, metakey[i], VT_STRING), metapos, fname);
|
|
|
|
serialize_long_long(metafile, addpool(poolfile, treefile, metaval[i], metatype[i]), metapos, fname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < m; i++) {
|
|
|
|
if (mustfree[i]) {
|
|
|
|
free((void *) metaval[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-11 23:46:20 +00:00
|
|
|
/*
|
2015-12-15 20:00:05 +00:00
|
|
|
* Note that feature_minzoom for lines is the dimension
|
2015-08-11 23:46:20 +00:00
|
|
|
* of the geometry in world coordinates, but
|
|
|
|
* for points is the lowest zoom level (in tiles,
|
|
|
|
* not in pixels) at which it should be drawn.
|
|
|
|
*
|
|
|
|
* So a line that is too small for, say, z8
|
2015-12-15 20:00:05 +00:00
|
|
|
* will have feature_minzoom of 18 (if tile detail is 10),
|
2015-08-11 23:46:20 +00:00
|
|
|
* not 8.
|
|
|
|
*/
|
2015-12-15 20:00:05 +00:00
|
|
|
int feature_minzoom = 0;
|
2015-08-11 23:46:20 +00:00
|
|
|
if (mb_geometry[t] == VT_LINE) {
|
2016-04-27 18:36:56 +00:00
|
|
|
// Skip z0 check because everything is always in the one z0 tile
|
|
|
|
for (feature_minzoom = 1; feature_minzoom < 31; feature_minzoom++) {
|
2015-12-15 20:00:05 +00:00
|
|
|
unsigned mask = 1 << (32 - (feature_minzoom + 1));
|
2015-08-11 23:46:20 +00:00
|
|
|
|
|
|
|
if (((bbox[0] & mask) != (bbox[2] & mask)) || ((bbox[1] & mask) != (bbox[3] & mask))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (mb_geometry[t] == VT_POINT) {
|
|
|
|
double r = ((double) rand()) / RAND_MAX;
|
|
|
|
if (r == 0) {
|
|
|
|
r = .00000001;
|
|
|
|
}
|
2015-12-15 20:00:05 +00:00
|
|
|
feature_minzoom = basezoom - floor(log(r) / -log(droprate));
|
2015-08-11 23:46:20 +00:00
|
|
|
}
|
|
|
|
|
2015-12-15 20:00:05 +00:00
|
|
|
serialize_byte(geomfile, feature_minzoom, geompos, fname);
|
2015-08-11 23:46:20 +00:00
|
|
|
|
|
|
|
struct index index;
|
|
|
|
index.start = geomstart;
|
|
|
|
index.end = *geompos;
|
2015-12-19 00:47:48 +00:00
|
|
|
index.segment = segment;
|
2016-01-12 22:12:56 +00:00
|
|
|
index.seq = *layer_seq;
|
2015-12-03 22:42:59 +00:00
|
|
|
|
|
|
|
// Calculate the center even if off the edge of the plane,
|
|
|
|
// and then mask to bring it back into the addressable area
|
|
|
|
long long midx = (bbox[0] / 2 + bbox[2] / 2) & ((1LL << 32) - 1);
|
|
|
|
long long midy = (bbox[1] / 2 + bbox[3] / 2) & ((1LL << 32) - 1);
|
|
|
|
index.index = encode(midx, midy);
|
|
|
|
|
2015-08-11 23:46:20 +00:00
|
|
|
fwrite_check(&index, sizeof(struct index), 1, indexfile, fname);
|
|
|
|
*indexpos += sizeof(struct index);
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
if (bbox[i] < file_bbox[i]) {
|
|
|
|
file_bbox[i] = bbox[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 2; i < 4; i++) {
|
|
|
|
if (bbox[i] > file_bbox[i]) {
|
|
|
|
file_bbox[i] = bbox[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-23 00:29:45 +00:00
|
|
|
if (*progress_seq % 10000 == 0) {
|
2016-04-07 17:35:36 +00:00
|
|
|
checkdisk(readers, CPUS);
|
2015-08-11 23:46:20 +00:00
|
|
|
if (!quiet) {
|
2015-12-23 00:29:45 +00:00
|
|
|
fprintf(stderr, "Read %.2f million features\r", *progress_seq / 1000000.0);
|
2015-08-11 23:46:20 +00:00
|
|
|
}
|
|
|
|
}
|
2015-12-23 00:29:45 +00:00
|
|
|
(*progress_seq)++;
|
|
|
|
(*layer_seq)++;
|
2015-08-11 23:46:20 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-06-01 23:55:52 +00:00
|
|
|
void check_crs(json_object *j, const char *reading) {
|
|
|
|
json_object *crs = json_hash_get(j, "crs");
|
|
|
|
if (crs != NULL) {
|
|
|
|
json_object *properties = json_hash_get(crs, "properties");
|
|
|
|
if (properties != NULL) {
|
|
|
|
json_object *name = json_hash_get(properties, "name");
|
|
|
|
if (name->type == JSON_STRING) {
|
|
|
|
if (strcmp(name->string, projection->alias) != 0) {
|
|
|
|
fprintf(stderr, "%s: Warning: GeoJSON specified projection \"%s\", not \"%s\".\n", reading, name->string, projection->alias);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-10 22:30:49 +00:00
|
|
|
void parse_json(json_pull *jp, const char *reading, volatile long long *layer_seq, volatile long long *progress_seq, long long *metapos, long long *geompos, long long *indexpos, std::set<std::string> *exclude, std::set<std::string> *include, int exclude_all, FILE *metafile, FILE *geomfile, FILE *indexfile, struct memfile *poolfile, struct memfile *treefile, char *fname, int basezoom, int layer, double droprate, long long *file_bbox, int segment, int *initialized, unsigned *initial_x, unsigned *initial_y, struct reader *readers, std::set<type_and_string> *file_keys, int maxzoom) {
|
2015-10-23 18:59:57 +00:00
|
|
|
long long found_hashes = 0;
|
|
|
|
long long found_features = 0;
|
|
|
|
long long found_geometries = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
json_object *j = json_read(jp);
|
|
|
|
if (j == NULL) {
|
|
|
|
if (jp->error != NULL) {
|
|
|
|
fprintf(stderr, "%s:%d: %s\n", reading, jp->line, jp->error);
|
|
|
|
}
|
|
|
|
|
|
|
|
json_free(jp->root);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j->type == JSON_HASH) {
|
|
|
|
found_hashes++;
|
|
|
|
|
|
|
|
if (found_hashes == 50 && found_features == 0 && found_geometries == 0) {
|
|
|
|
fprintf(stderr, "%s:%d: Warning: not finding any GeoJSON features or geometries in input yet after 50 objects.\n", reading, jp->line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
json_object *type = json_hash_get(j, "type");
|
|
|
|
if (type == NULL || type->type != JSON_STRING) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found_features == 0) {
|
|
|
|
int i;
|
|
|
|
int is_geometry = 0;
|
|
|
|
for (i = 0; i < GEOM_TYPES; i++) {
|
|
|
|
if (strcmp(type->string, geometry_names[i]) == 0) {
|
|
|
|
is_geometry = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_geometry) {
|
|
|
|
if (j->parent != NULL) {
|
|
|
|
if (j->parent->type == JSON_ARRAY) {
|
|
|
|
if (j->parent->parent->type == JSON_HASH) {
|
|
|
|
json_object *geometries = json_hash_get(j->parent->parent, "geometries");
|
|
|
|
if (geometries != NULL) {
|
|
|
|
// Parent of Parent must be a GeometryCollection
|
|
|
|
is_geometry = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (j->parent->type == JSON_HASH) {
|
|
|
|
json_object *geometry = json_hash_get(j->parent, "geometry");
|
|
|
|
if (geometry != NULL) {
|
|
|
|
// Parent must be a Feature
|
|
|
|
is_geometry = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_geometry) {
|
|
|
|
if (found_features != 0 && found_geometries == 0) {
|
|
|
|
fprintf(stderr, "%s:%d: Warning: found a mixture of features and bare geometries\n", reading, jp->line);
|
|
|
|
}
|
|
|
|
found_geometries++;
|
|
|
|
|
2016-05-10 22:30:49 +00:00
|
|
|
serialize_geometry(j, NULL, reading, jp->line, layer_seq, progress_seq, metapos, geompos, indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, basezoom, layer, droprate, file_bbox, NULL, segment, initialized, initial_x, initial_y, readers, file_keys, maxzoom);
|
2015-10-23 18:59:57 +00:00
|
|
|
json_free(j);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(type->string, "Feature") != 0) {
|
2016-06-02 00:09:20 +00:00
|
|
|
if (strcmp(type->string, "FeatureCollection") == 0) {
|
|
|
|
check_crs(j, reading);
|
|
|
|
}
|
|
|
|
|
2015-10-23 18:59:57 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found_features == 0 && found_geometries != 0) {
|
|
|
|
fprintf(stderr, "%s:%d: Warning: found a mixture of features and bare geometries\n", reading, jp->line);
|
|
|
|
}
|
|
|
|
found_features++;
|
|
|
|
|
|
|
|
json_object *geometry = json_hash_get(j, "geometry");
|
|
|
|
if (geometry == NULL) {
|
|
|
|
fprintf(stderr, "%s:%d: feature with no geometry\n", reading, jp->line);
|
|
|
|
json_free(j);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
json_object *properties = json_hash_get(j, "properties");
|
|
|
|
if (properties == NULL || (properties->type != JSON_HASH && properties->type != JSON_NULL)) {
|
|
|
|
fprintf(stderr, "%s:%d: feature without properties hash\n", reading, jp->line);
|
|
|
|
json_free(j);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
json_object *tippecanoe = json_hash_get(j, "tippecanoe");
|
|
|
|
|
|
|
|
json_object *geometries = json_hash_get(geometry, "geometries");
|
|
|
|
if (geometries != NULL) {
|
2016-03-25 20:45:28 +00:00
|
|
|
size_t g;
|
2015-10-23 18:59:57 +00:00
|
|
|
for (g = 0; g < geometries->length; g++) {
|
2016-05-10 22:30:49 +00:00
|
|
|
serialize_geometry(geometries->array[g], properties, reading, jp->line, layer_seq, progress_seq, metapos, geompos, indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, basezoom, layer, droprate, file_bbox, tippecanoe, segment, initialized, initial_x, initial_y, readers, file_keys, maxzoom);
|
2015-10-23 18:59:57 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-05-10 22:30:49 +00:00
|
|
|
serialize_geometry(geometry, properties, reading, jp->line, layer_seq, progress_seq, metapos, geompos, indexpos, exclude, include, exclude_all, metafile, geomfile, indexfile, poolfile, treefile, fname, basezoom, layer, droprate, file_bbox, tippecanoe, segment, initialized, initial_x, initial_y, readers, file_keys, maxzoom);
|
2015-10-23 18:59:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
json_free(j);
|
|
|
|
|
|
|
|
/* XXX check for any non-features in the outer object */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-22 22:02:31 +00:00
|
|
|
void *run_parse_json(void *v) {
|
2016-04-27 20:54:00 +00:00
|
|
|
struct parse_json_args *pja = (struct parse_json_args *) v;
|
2015-12-22 22:02:31 +00:00
|
|
|
|
2016-05-10 22:30:49 +00:00
|
|
|
parse_json(pja->jp, pja->reading, pja->layer_seq, pja->progress_seq, pja->metapos, pja->geompos, pja->indexpos, pja->exclude, pja->include, pja->exclude_all, pja->metafile, pja->geomfile, pja->indexfile, pja->poolfile, pja->treefile, pja->fname, pja->basezoom, pja->layer, pja->droprate, pja->file_bbox, pja->segment, pja->initialized, pja->initial_x, pja->initial_y, pja->readers, pja->file_keys, pja->maxzoom);
|
2015-12-22 22:02:31 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-10-26 23:05:01 +00:00
|
|
|
struct jsonmap {
|
|
|
|
char *map;
|
2016-03-25 20:45:28 +00:00
|
|
|
unsigned long long off;
|
|
|
|
unsigned long long end;
|
2015-10-26 23:05:01 +00:00
|
|
|
};
|
|
|
|
|
2016-03-25 18:20:56 +00:00
|
|
|
ssize_t json_map_read(struct json_pull *jp, char *buffer, size_t n) {
|
2016-04-27 20:54:00 +00:00
|
|
|
struct jsonmap *jm = (struct jsonmap *) jp->source;
|
2015-10-26 23:05:01 +00:00
|
|
|
|
|
|
|
if (jm->off + n >= jm->end) {
|
|
|
|
n = jm->end - jm->off;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(buffer, jm->map + jm->off, n);
|
|
|
|
jm->off += n;
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct json_pull *json_begin_map(char *map, long long len) {
|
2016-04-27 20:54:00 +00:00
|
|
|
struct jsonmap *jm = (struct jsonmap *) malloc(sizeof(struct jsonmap));
|
2016-03-25 18:57:33 +00:00
|
|
|
if (jm == NULL) {
|
|
|
|
perror("Out of memory");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2015-10-26 23:05:01 +00:00
|
|
|
|
|
|
|
jm->map = map;
|
|
|
|
jm->off = 0;
|
|
|
|
jm->end = len;
|
|
|
|
|
|
|
|
return json_begin(json_map_read, jm);
|
|
|
|
}
|