Compare commits

..

38 Commits

Author SHA1 Message Date
00062c65bc Decode polygons, although probably not correctly 2015-03-13 16:14:00 -07:00
4daa2970f3 Pass through LineStrings 2015-02-20 17:00:38 -08:00
5906a2d82c Can at least round-trip a single point feature now 2015-02-20 15:58:24 -08:00
acbdb7d39e Make standalone parser runnable for testing 2015-02-20 15:07:25 -08:00
150aa5d607 Pull feature parsing out of the outer loop 2015-02-17 17:33:44 -08:00
86fed8f4ff First stab at factoring out GeoJSON parsing 2015-02-17 17:24:23 -08:00
b2eff13667 Impose a limit on the maximum number of features in a tile. 2014-12-19 14:33:39 -08:00
290e39f80c Improve function name 2014-12-19 11:56:40 -08:00
0b84f13159 Merge pull request #18 from mapbox/topdown
Work from the top down instead of from feature indices
2014-12-18 11:28:40 -08:00
5a2003cb2c Fix failure to release temporary memory 2014-12-17 17:41:57 -08:00
32010fc893 Oops. Do a lot less progress indicator I/O. 2014-12-17 17:08:04 -08:00
48b5db6ae5 Keep the progress indicator progressing while working through big tiles 2014-12-17 16:01:33 -08:00
7f3551070e Remove unneeded debug output 2014-12-17 14:01:20 -08:00
92bbf27f72 Merge branch 'master' into topdown
Conflicts:
	geojson.c
2014-12-17 11:18:47 -08:00
cba1b8ae7f Remove unneeded old indexing code 2014-12-17 11:16:43 -08:00
0d0a546b1e No need to split features into child tiles when processing the final zoom. 2014-12-17 11:10:46 -08:00
ad17f1f282 Make -Z0 work again for file minimum zoom 2014-12-17 11:05:14 -08:00
3b9f4691c1 Merge pull request #16 from mapbox/nullprop
Allow features to have null properties
2014-12-16 22:49:23 -08:00
a40192bcde Allow features to have null properties 2014-12-16 22:46:00 -08:00
c90ba8511f Fix compiler warning 2014-12-12 14:52:12 -08:00
34a6422c42 Merge branch 'master' into topdown 2014-12-12 11:36:20 -08:00
3f2818a814 More useful progress indicator 2014-12-11 17:59:22 -08:00
c177b8bed2 No need to remove no-ops here unless we clipped 2014-12-11 16:08:53 -08:00
d69431e16b Partition each tile into children instead of repeatedly clipping the parent. 2014-12-11 15:46:54 -08:00
105dfa73d7 Use bounding box for quick accept/reject before detailed clipping. 2014-12-11 13:34:50 -08:00
a867646dfd Don't introduce bogus geometries in clipped-away features. 2014-12-10 12:07:54 -08:00
b068635acf Elevation in geometry is a warning, not an error.
Only complain about the first null geometry, not ever one.
2014-12-09 15:23:22 -08:00
40ecfc0668 Add missing newline to error message. 2014-12-09 15:17:20 -08:00
38a41f4df8 Installation instructions 2014-12-09 11:03:44 -05:00
380550ce85 Remember to remove the closepath after clipping away a polygon 2014-12-04 14:54:11 -08:00
028fef470e Fix the major bug: forgetting to offset back to world coordinates 2014-12-04 14:08:36 -08:00
b7b476b36c Don't introduce bogus geometries in clipped-away features. 2014-12-03 16:30:35 -08:00
08ff40e42f Trying to follow Vlad's suggestion and work from the top down.
Doesn't quite work yet, but seems like the right thing to do.
From the bottom up, indexing is a mess because of the buffering,
and includes unneeded areas in the bounding boxes of big features.
2014-12-03 16:18:43 -08:00
eaeb55bf71 Distinguish between signed and unsigned in the temporary file 2014-12-02 18:03:07 -08:00
a1e7426956 Sprinkle consts and casts to make it valid C++ as well as C 2014-12-02 14:17:49 -08:00
77c4ce4171 Make the bin directory on install if it doesn't already exist 2014-12-02 11:38:48 -08:00
cc58588724 More sensible error message when there are no valid geometries 2014-11-24 22:50:12 -08:00
2bd0cb9f43 Don't override PREFIX if it is set in the environment 2014-11-24 10:35:06 -08:00
12 changed files with 772 additions and 378 deletions

View File

@ -1,8 +1,9 @@
PREFIX=/usr/local
PREFIX ?= /usr/local
all: tippecanoe enumerate decode
install: tippecanoe
mkdir -p $(PREFIX)/bin
cp tippecanoe $(PREFIX)/bin/tippecanoe
vector_tile.pb.cc vector_tile.pb.h: vector_tile.proto

View File

@ -3,6 +3,13 @@ tippecanoe
Build vector tilesets from large collections of GeoJSON features.
Installation
------------
The easiest way to install tippecanoe on OSX is with [Homebrew](http://brew.sh/):
brew install tippecanoe
Usage
-----

View File

@ -24,7 +24,7 @@ void enumerate(char *fname) {
long long x = sqlite3_column_int(stmt, 1);
long long y = sqlite3_column_int(stmt, 2);
y = (1LL << zoom) - y;
y = (1LL << zoom) - 1 - y;
printf("%s %lld %lld %lld\n", fname, zoom, x, y);
}

838
geojson.c

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@
#include <unistd.h>
#include <math.h>
#include <sqlite3.h>
#include <limits.h>
#include "geometry.hh"
extern "C" {
@ -16,9 +17,14 @@ extern "C" {
#include "projection.h"
}
drawvec decode_geometry(char **meta, int z, unsigned tx, unsigned ty, int detail) {
drawvec decode_geometry(char **meta, int z, unsigned tx, unsigned ty, int detail, long long *bbox) {
drawvec out;
bbox[0] = LONG_LONG_MAX;
bbox[1] = LONG_LONG_MAX;
bbox[2] = LONG_LONG_MIN;
bbox[3] = LONG_LONG_MIN;
while (1) {
draw d;
@ -28,9 +34,9 @@ drawvec decode_geometry(char **meta, int z, unsigned tx, unsigned ty, int detail
}
if (d.op == VT_MOVETO || d.op == VT_LINETO) {
int wx, wy;
deserialize_int(meta, &wx);
deserialize_int(meta, &wy);
unsigned wx, wy;
deserialize_uint(meta, &wx);
deserialize_uint(meta, &wy);
long long wwx = (unsigned) wx;
long long wwy = (unsigned) wy;
@ -40,6 +46,19 @@ drawvec decode_geometry(char **meta, int z, unsigned tx, unsigned ty, int detail
wwy -= ty << (32 - z);
}
if (wwx < bbox[0]) {
bbox[0] = wwx;
}
if (wwy < bbox[1]) {
bbox[1] = wwy;
}
if (wwx > bbox[2]) {
bbox[2] = wwx;
}
if (wwy > bbox[3]) {
bbox[3] = wwy;
}
d.x = wwx;
d.y = wwy;
}
@ -308,7 +327,9 @@ drawvec clip_poly(drawvec &geom, int z, int detail, int buffer) {
}
if (j >= geom.size() || geom[j].op == VT_CLOSEPATH) {
out.push_back(draw(VT_CLOSEPATH, 0, 0));
if (out.size() > 0 && out[out.size() - 1].op != VT_CLOSEPATH) {
out.push_back(draw(VT_CLOSEPATH, 0, 0));
}
i = j;
} else {
i = j - 1;
@ -374,7 +395,13 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double
i = j;
} else {
fprintf(stderr, "how did we get here with %d?\n", geom[i].op);
fprintf(stderr, "how did we get here with %d in %d?\n", geom[i].op, (int) geom.size());
for (unsigned n = 0; n < geom.size(); n++) {
fprintf(stderr, "%d/%lld/%lld ", geom[n].op, geom[n].x, geom[n].y);
}
fprintf(stderr, "\n");
out.push_back(geom[i]);
}
}
@ -382,11 +409,68 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double
return out;
}
drawvec clip_point(drawvec &geom, int z, int detail, long long buffer) {
drawvec out;
unsigned i;
long long min = 0;
long long area = 0xFFFFFFFF;
if (z != 0) {
area = 1LL << (32 - z);
min -= buffer * area / 256;
area += buffer * area / 256;
}
for (i = 0; i < geom.size(); i++) {
if (geom[i].x >= min && geom[i].y >= min && geom[i].x <= area && geom[i].y <= area) {
out.push_back(geom[i]);
}
}
return out;
}
int quick_check(long long *bbox, int z, int detail, long long buffer) {
long long min = 0;
long long area = 0xFFFFFFFF;
if (z != 0) {
area = 1LL << (32 - z);
min -= buffer * area / 256;
area += buffer * area / 256;
}
// bbox entirely outside the tile
if (bbox[0] > area || bbox[1] > area) {
return 0;
}
if (bbox[2] < min || bbox[3] < min) {
return 0;
}
// bbox entirely within the tile
if (bbox[0] > min && bbox[1] > min && bbox[2] < area && bbox[3] < area) {
return 1;
}
// some overlap of edge
return 2;
}
drawvec clip_lines(drawvec &geom, int z, int detail, long long buffer) {
drawvec out;
unsigned i;
long long min = 0;
long long area = 0xFFFFFFFF;
if (z != 0) {
area = 1LL << (32 - z);
min -= buffer * area / 256;
area += buffer * area / 256;
}
for (i = 0; i < geom.size(); i++) {
if (i > 0 && (geom[i - 1].op == VT_MOVETO || geom[i - 1].op == VT_LINETO) && geom[i].op == VT_LINETO) {
double x1 = geom[i - 1].x;
@ -395,15 +479,6 @@ drawvec clip_lines(drawvec &geom, int z, int detail, long long buffer) {
double x2 = geom[i - 0].x;
double y2 = geom[i - 0].y;
long long min = 0;
long long area = 0xFFFFFFFF;
if (z != 0) {
area = 1LL << (32 - z);
min -= buffer * area / 256;
area += buffer * area / 256;
}
int c = clip(&x1, &y1, &x2, &y2, min, min, area, area);
if (c > 1) { // clipped
@ -548,6 +623,10 @@ drawvec simplify_lines(drawvec &geom, int z, int detail) {
drawvec reorder_lines(drawvec &geom) {
// Only reorder simple linestrings with a single moveto
if (geom.size() == 0) {
return geom;
}
unsigned i;
for (i = 0; i < geom.size(); i++) {
if (geom[i].op == VT_MOVETO) {

View File

@ -15,11 +15,13 @@ struct draw {
typedef std::vector<draw> drawvec;
drawvec decode_geometry(char **meta, int z, unsigned tx, unsigned ty, int detail);
drawvec decode_geometry(char **meta, int z, unsigned tx, unsigned ty, int detail, long long *bbox);
void to_tile_scale(drawvec &geom, int z, int detail);
drawvec remove_noop(drawvec geom, int type);
drawvec clip_point(drawvec &geom, int z, int detail, long long buffer);
drawvec clip_poly(drawvec &geom, int z, int detail, int buffer);
drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double *accum_area);
drawvec clip_lines(drawvec &geom, int z, int detail, long long buffer);
int quick_check(long long *bbox, int z, int detail, long long buffer);
drawvec simplify_lines(drawvec &geom, int z, int detail);
drawvec reorder_lines(drawvec &geom);

View File

@ -161,7 +161,7 @@ static json_object *add_object(json_pull *j, json_type type) {
return o;
}
json_object *json_hash_get(json_object *o, char *s) {
json_object *json_hash_get(json_object *o, const char *s) {
if (o == NULL || o->type != JSON_HASH) {
return NULL;
}

View File

@ -54,4 +54,4 @@ json_object *json_read(json_pull *j);
json_object *json_read_separators(json_pull *j, json_separator_callback cb, void *state);
void json_free(json_object *j);
json_object *json_hash_get(json_object *o, char *s);
json_object *json_hash_get(json_object *o, const char *s);

View File

@ -70,7 +70,7 @@ void mbtiles_write_tile(sqlite3 *outdb, int z, int tx, int ty, const char *data,
}
}
static void quote(char **buf, char *s) {
static void quote(char **buf, const char *s) {
char tmp[strlen(s) * 8 + 1];
char *out = tmp;
@ -107,7 +107,7 @@ static void aprintf(char **buf, const char *format, ...) {
free(tmp);
}
void mbtiles_write_metadata(sqlite3 *outdb, char *fname, char *layername, int minzoom, int maxzoom, double minlat, double minlon, double maxlat, double maxlon, double midlat, double midlon, struct pool *fields) {
void mbtiles_write_metadata(sqlite3 *outdb, const char *fname, const char *layername, int minzoom, int maxzoom, double minlat, double minlon, double maxlat, double maxlon, double midlat, double midlon, struct pool *fields) {
char *sql, *err;
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('name', %Q);", fname);

View File

@ -2,6 +2,6 @@ sqlite3 *mbtiles_open(char *dbname, char **argv);
void mbtiles_write_tile(sqlite3 *outdb, int z, int tx, int ty, const char *data, int size);
void mbtiles_write_metadata(sqlite3 *outdb, char *fname, char *layername, int minzoom, int maxzoom, double minlat, double minlon, double maxlat, double maxlon, double midlat, double midlon, struct pool *fields);
void mbtiles_write_metadata(sqlite3 *outdb, const char *fname, const char *layername, int minzoom, int maxzoom, double minlat, double minlon, double maxlat, double maxlon, double midlat, double midlon, struct pool *fields);
void mbtiles_close(sqlite3 *outdb, char **argv);

156
tile.cc
View File

@ -214,7 +214,7 @@ void decode_meta(char **meta, struct pool *keys, struct pool *values, struct poo
}
}
mapnik::vector::tile create_tile(char *layername, int line_detail, std::vector<coalesce> &features, long long *count, struct pool *keys, struct pool *values) {
mapnik::vector::tile create_tile(const char *layername, int line_detail, std::vector<coalesce> &features, long long *count, struct pool *keys, struct pool *values) {
mapnik::vector::tile tile;
mapnik::vector::tile_layer *layer = tile.add_layers();
@ -284,7 +284,7 @@ struct sll {
}
};
void evaluate(std::vector<coalesce> &features, char *metabase, struct pool *file_keys, char *layername, int line_detail, long long orig) {
void evaluate(std::vector<coalesce> &features, char *metabase, struct pool *file_keys, const char *layername, int line_detail, long long orig) {
std::vector<sll> options;
struct pool_val *pv;
@ -342,9 +342,12 @@ void evaluate(std::vector<coalesce> &features, char *metabase, struct pool *file
pool_free(&keys);
}
long long write_tile(struct index *start, struct index *end, char *metabase, unsigned *file_bbox, int z, unsigned tx, unsigned ty, int detail, int basezoom, struct pool *file_keys, char *layername, sqlite3 *outdb, double droprate, int buffer) {
long long write_tile(char **geoms, char *metabase, unsigned *file_bbox, int z, unsigned tx, unsigned ty, int detail, int basezoom, struct pool *file_keys, const char *layername, sqlite3 *outdb, double droprate, int buffer, const char *fname, json_pull *jp, FILE *geomfile[4], int file_minzoom, int file_maxzoom, double todo, char *geomstart, long long along) {
int line_detail;
static bool evaluated = false;
double oprogress = 0;
char *og = *geoms;
for (line_detail = detail; line_detail >= MIN_DETAIL || line_detail == detail; line_detail--) {
GOOGLE_PROTOBUF_VERIFY_VERSION;
@ -360,41 +363,128 @@ long long write_tile(struct index *start, struct index *end, char *metabase, uns
std::vector<coalesce> features;
struct index *i;
for (i = start; i < end; i++) {
int t = i->type;
int within[4] = { 0 };
long long geompos[4] = { 0 };
if (z > i->maxzoom) {
continue;
*geoms = og;
while (1) {
int t;
deserialize_int(geoms, &t);
if (t < 0) {
break;
}
if ((t == VT_LINE && z + line_detail <= i->minzoom) ||
(t == VT_POINT && z < i->minzoom)) {
long long metastart;
deserialize_long_long(geoms, &metastart);
char *meta = metabase + metastart;
long long bbox[4];
drawvec geom = decode_geometry(geoms, z, tx, ty, line_detail, bbox);
signed char feature_minzoom;
deserialize_byte(geoms, &feature_minzoom);
double progress = floor((((*geoms - geomstart + along) / (double) todo) + z) / (file_maxzoom + 1) * 1000) / 10;
if (progress != oprogress) {
fprintf(stderr, " %3.1f%% %d/%u/%u \r", progress, z, tx, ty);
oprogress = progress;
}
int quick = quick_check(bbox, z, line_detail, buffer);
if (quick == 0) {
continue;
}
if (i->candup) {
if (dup.count(i->fpos) != 0) {
continue;
if (quick != 1) {
if (t == VT_LINE) {
geom = clip_lines(geom, z, line_detail, buffer);
}
dup.insert(i->fpos);
if (t == VT_POLYGON) {
geom = clip_poly(geom, z, line_detail, buffer);
}
if (t == VT_POINT) {
geom = clip_point(geom, z, line_detail, buffer);
}
geom = remove_noop(geom, t);
}
char *meta = metabase + i->fpos;
drawvec geom = decode_geometry(&meta, z, tx, ty, line_detail);
if (line_detail == detail) { /* only write out the next zoom once, even if we retry */
if (geom.size() > 0 && z + 1 <= file_maxzoom) {
int j;
for (j = 0; j < 4; j++) {
int xo = j & 1;
int yo = (j >> 1) & 1;
long long bbox2[4];
int k;
for (k = 0; k < 4; k++) {
bbox2[k] = bbox[k];
}
if (z != 0) {
// Offset back to world-relative
bbox2[0] += tx << (32 - z);
bbox2[1] += ty << (32 - z);
bbox2[2] += tx << (32 - z);
bbox2[3] += ty << (32 - z);
}
// Offset to child tile-relative
bbox2[0] -= (tx * 2 + xo) << (32 - (z + 1));
bbox2[1] -= (ty * 2 + yo) << (32 - (z + 1));
bbox2[2] -= (tx * 2 + xo) << (32 - (z + 1));
bbox2[3] -= (ty * 2 + yo) << (32 - (z + 1));
int quick2 = quick_check(bbox2, z + 1, line_detail, buffer);
if (quick2 != 0) {
if (!within[j]) {
serialize_int(geomfile[j], z + 1, &geompos[j], fname, jp);
serialize_uint(geomfile[j], tx * 2 + xo, &geompos[j], fname, jp);
serialize_uint(geomfile[j], ty * 2 + yo, &geompos[j], fname, jp);
within[j] = 1;
}
// Offset from tile coordinates back to world coordinates
unsigned sx = 0, sy = 0;
if (z != 0) {
sx = tx << (32 - z);
sy = ty << (32 - z);
}
//printf("type %d, meta %lld\n", t, metastart);
serialize_int(geomfile[j], t, &geompos[j], fname, jp);
serialize_long_long(geomfile[j], metastart, &geompos[j], fname, jp);
for (unsigned u = 0; u < geom.size(); u++) {
serialize_byte(geomfile[j], geom[u].op, &geompos[j], fname, jp);
if (geom[u].op != VT_CLOSEPATH) {
serialize_uint(geomfile[j], geom[u].x + sx, &geompos[j], fname, jp);
serialize_uint(geomfile[j], geom[u].y + sy, &geompos[j], fname, jp);
}
}
serialize_byte(geomfile[j], VT_END, &geompos[j], fname, jp);
serialize_byte(geomfile[j], feature_minzoom, &geompos[j], fname, jp);
}
}
}
}
if (z < file_minzoom) {
continue;
}
if ((t == VT_LINE && z + line_detail <= feature_minzoom) ||
(t == VT_POINT && z < feature_minzoom)) {
continue;
}
bool reduced = false;
if (t == VT_POLYGON) {
geom = reduce_tiny_poly(geom, z, line_detail, &reduced, &accum_area);
}
if (t == VT_LINE) {
geom = clip_lines(geom, z, line_detail, buffer);
}
if (t == VT_POLYGON) {
geom = clip_poly(geom, z, line_detail, buffer);
}
if (t == VT_LINE || t == VT_POLYGON) {
if (!reduced) {
geom = simplify_lines(geom, z, line_detail);
@ -428,8 +518,8 @@ long long write_tile(struct index *start, struct index *end, char *metabase, uns
c.index2 = ~0LL;
}
} else {
c.index = i->index;
c.index2 = i->index;
c.index = 0;
c.index2 = 0;
}
c.geom = geom;
c.metasrc = meta;
@ -440,6 +530,14 @@ long long write_tile(struct index *start, struct index *end, char *metabase, uns
}
}
int j;
for (j = 0; j < 4; j++) {
if (within[j]) {
serialize_int(geomfile[j], -2, &geompos[j], fname, jp);
within[j] = 0;
}
}
std::sort(features.begin(), features.end());
std::vector<coalesce> out;
@ -471,6 +569,12 @@ long long write_tile(struct index *start, struct index *end, char *metabase, uns
}
if (features.size() > 0) {
if (features.size() > 200000) {
fprintf(stderr, "tile %d/%u/%u has %lld features, >200000 \n", z, tx, ty, (long long) features.size());
fprintf(stderr, "Try using -z to set a higher base zoom level.\n");
exit(EXIT_FAILURE);
}
mapnik::vector::tile tile = create_tile(layername, line_detail, features, &count, &keys, &values);
pool_free(&keys);

21
tile.h
View File

@ -12,19 +12,18 @@
#define VT_BOOLEAN 7
struct pool;
struct json_pull;
void serialize_int(FILE *out, int n, long long *fpos, const char *fname, struct json_pull *source);
void serialize_long_long(FILE *out, long long n, long long *fpos, const char *fname, struct json_pull *source);
void serialize_byte(FILE *out, signed char n, long long *fpos, const char *fname, struct json_pull *source);
void serialize_uint(FILE *out, unsigned n, long long *fpos, const char *fname, struct json_pull *source);
void serialize_string(FILE *out, const char *s, long long *fpos, const char *fname, struct json_pull *source);
void deserialize_int(char **f, int *n);
void deserialize_long_long(char **f, long long *n);
void deserialize_uint(char **f, unsigned *n);
void deserialize_byte(char **f, signed char *n);
struct pool_val *deserialize_string(char **f, struct pool *p, int type);
struct index {
unsigned long long index;
long long fpos : 44;
int maxzoom : 6;
int minzoom : 6;
int type : 7;
int candup : 1;
};
long long write_tile(struct index *start, struct index *end, char *metabase, unsigned *file_bbox, int z, unsigned x, unsigned y, int detail, int basezoom, struct pool *file_keys, char *layername, sqlite3 *outdb, double droprate, int buffer);
long long write_tile(char **geom, char *metabase, unsigned *file_bbox, int z, unsigned x, unsigned y, int detail, int basezoom, struct pool *file_keys, const char *layername, sqlite3 *outdb, double droprate, int buffer, const char *fname, struct json_pull *jp, FILE *geomfile[4], int file_minzoom, int file_maxzoom, double todo, char *geomstart, long long along);