tippecanoe/geobuf.cpp

345 lines
7.6 KiB
C++
Raw Normal View History

2017-08-25 14:23:06 -07:00
#include <stdio.h>
#include <string>
2017-08-25 15:46:32 -07:00
#include "mvt.hpp"
2017-08-25 14:23:06 -07:00
#include "serial.hpp"
#include "geobuf.hpp"
2017-08-25 15:46:32 -07:00
#include "geojson.hpp"
#include "projection.hpp"
2017-08-25 14:23:06 -07:00
#include "protozero/varint.hpp"
#include "protozero/pbf_reader.hpp"
#include "protozero/pbf_writer.hpp"
#define POINT 0
#define MULTIPOINT 1
#define LINESTRING 2
#define MULTILINESTRING 3
#define POLYGON 4
#define MULTIPOLYGON 5
serial_val readValue(protozero::pbf_reader &pbf, std::vector<std::string> &keys) {
2017-08-25 15:46:32 -07:00
serial_val sv;
sv.type = mvt_null;
sv.s = "null";
while (pbf.next()) {
switch (pbf.tag()) {
case 1:
sv.type = mvt_string;
sv.s = pbf.get_string();
break;
case 2:
sv.type = mvt_double;
sv.s = std::to_string(pbf.get_double());
break;
case 3:
sv.type = mvt_double;
sv.s = std::to_string(pbf.get_uint64());
break;
case 4:
sv.type = mvt_double;
sv.s = std::to_string(-pbf.get_uint64());
break;
case 5:
sv.type = mvt_bool;
if (pbf.get_bool()) {
sv.s = "true";
} else {
sv.s = "false";
}
break;
case 6:
sv.type = mvt_string; // stringified JSON
sv.s = pbf.get_string();
break;
default:
pbf.skip();
}
}
return sv;
2017-08-25 14:23:06 -07:00
}
drawvec readPoint(std::vector<long long> &coords, std::vector<int> &lengths, size_t dim, double e) {
long long x, y;
projection->project(coords[0] / e, coords[1] / e, 32, &x, &y);
drawvec dv;
dv.push_back(draw(VT_MOVETO, x, y));
return dv;
}
drawvec readLinePart(std::vector<long long> &coords, std::vector<int> &lengths, size_t dim, double e, size_t start, size_t end, bool closed) {
drawvec dv;
std::vector<long long> prev;
std::vector<double> p;
prev.resize(dim);
p.resize(dim);
for (size_t i = start; i + dim - 1 < end; i += dim) {
if (i + dim - 1 >= coords.size()) {
fprintf(stderr, "Internal error: line segment %zu vs %zu\n", i + dim - 1, coords.size());
exit(EXIT_FAILURE);
}
for (size_t d = 0; d < dim; d++) {
prev[d] += coords[i + d];
p[d] = prev[d] / e;
}
long long x, y;
projection->project(p[0], p[1], 32, &x, &y);
if (i == 0) {
dv.push_back(draw(VT_MOVETO, x, y));
} else {
dv.push_back(draw(VT_LINETO, x, y));
}
}
if (closed && dv.size() > 0) {
dv.push_back(draw(VT_LINETO, dv[0].x, dv[0].y));
}
return dv;
}
drawvec readLine(std::vector<long long> &coords, std::vector<int> &lengths, size_t dim, double e, bool closed) {
return readLinePart(coords, lengths, dim, e, 0, coords.size(), closed);
}
drawvec readMultiLine(std::vector<long long> &coords, std::vector<int> &lengths, size_t dim, double e, bool closed) {
if (lengths.size() == 0) {
return readLinePart(coords, lengths, dim, e, 0, coords.size(), closed);
}
drawvec dv;
size_t here = 0;
for (size_t i = 0; i < lengths.size(); i++) {
drawvec dv2 = readLinePart(coords, lengths, dim, e, here, here + lengths[i] * dim, closed);
here += lengths[i] * dim;
for (size_t j = 0; i < dv2.size(); i++) {
dv.push_back(dv2[j]);
}
}
return dv;
}
drawvec readMultiPolygon(std::vector<long long> &coords, std::vector<int> &lengths, size_t dim, double e) {
return drawvec();
}
drawvec readGeometry(protozero::pbf_reader &pbf, size_t dim, double e, std::vector<std::string> &keys, int &type) {
2017-08-25 14:23:06 -07:00
std::vector<long long> coords;
std::vector<int> lengths;
2017-08-25 14:23:06 -07:00
while (pbf.next()) {
switch (pbf.tag()) {
case 1:
type = pbf.get_enum();
break;
case 2: {
auto pi = pbf.get_packed_sint32();
for (auto it = pi.first; it != pi.second; ++it) {
lengths.push_back(*it);
}
break;
}
case 3: {
auto pi = pbf.get_packed_sint64();
for (auto it = pi.first; it != pi.second; ++it) {
coords.push_back(*it);
}
break;
}
case 4: {
int type2;
protozero::pbf_reader geometry_reader(pbf.get_message());
drawvec dv2 = readGeometry(geometry_reader, dim, e, keys, type2);
2017-08-25 14:23:06 -07:00
break;
}
default:
pbf.skip();
}
}
2017-08-25 15:46:32 -07:00
drawvec dv;
if (type == POINT) {
dv = readPoint(coords, lengths, dim, e);
} else if (type == MULTIPOINT) {
dv = readLine(coords, lengths, dim, e, true);
} else if (type == LINESTRING) {
dv = readLine(coords, lengths, dim, e, false);
} else if (type == POLYGON) {
dv = readMultiLine(coords, lengths, dim, e, true);
} else if (type == MULTIPOLYGON) {
dv = readMultiPolygon(coords, lengths, dim, e);
}
#if 0
for (size_t i = 0; i < dv.size(); i++) {
printf("%d %lld %lld\n", dv[i].op, dv[i].x, dv[i].y);
}
printf("\n");
#endif
type /= 2;
2017-08-25 15:46:32 -07:00
return dv;
2017-08-25 14:23:06 -07:00
}
void readFeature(protozero::pbf_reader &pbf, size_t dim, double e, std::vector<std::string> &keys, struct serialization_state *sst, int layer, std::string layername) {
2017-08-25 14:23:06 -07:00
drawvec dv;
2017-08-25 15:46:32 -07:00
long long id = 0;
bool has_id = false;
2017-08-25 14:23:06 -07:00
std::vector<serial_val> values;
2017-08-25 15:46:32 -07:00
std::vector<size_t> properties;
2017-08-25 14:23:06 -07:00
while (pbf.next()) {
switch (pbf.tag()) {
case 1: {
int type;
protozero::pbf_reader geometry_reader(pbf.get_message());
dv = readGeometry(geometry_reader, dim, e, keys, type);
2017-08-25 14:23:06 -07:00
break;
}
case 11:
id = pbf.get_int64();
2017-08-25 15:46:32 -07:00
has_id = true;
2017-08-25 14:23:06 -07:00
break;
2017-08-25 15:46:32 -07:00
case 12: {
static bool warned = false;
if (!warned) {
fprintf(stderr, "Non-numeric feature IDs not supported");
warned = true;
}
2017-08-25 14:23:06 -07:00
break;
2017-08-25 15:46:32 -07:00
}
2017-08-25 14:23:06 -07:00
case 13: {
protozero::pbf_reader value_reader(pbf.get_message());
values.push_back(readValue(value_reader, keys));
2017-08-25 14:23:06 -07:00
break;
}
case 14: {
auto pi = pbf.get_packed_uint32();
for (auto it = pi.first; it != pi.second; ++it) {
properties.push_back(*it);
}
break;
}
default:
pbf.skip();
}
}
2017-08-25 15:46:32 -07:00
serial_feature sf;
sf.layer = layer;
sf.layername = layername;
sf.segment = 0; // single thread
sf.has_id = has_id;
sf.id = id;
sf.has_tippecanoe_minzoom = false;
sf.has_tippecanoe_maxzoom = false;
sf.feature_minzoom = false;
sf.seq = (*sst->layer_seq);
sf.geometry = dv;
for (size_t i = 0; i + 1 < properties.size(); i += 2) {
if (properties[i] >= keys.size()) {
fprintf(stderr, "Out of bounds key: %zu in %zu\n", properties[i], keys.size());
exit(EXIT_FAILURE);
}
if (properties[i + 1] >= values.size()) {
fprintf(stderr, "Out of bounds value: %zu in %zu\n", properties[i + 1], values.size());
exit(EXIT_FAILURE);
}
sf.full_keys.push_back(keys[properties[i]]);
sf.full_values.push_back(values[properties[i + 1]]);
}
sf.m = sf.full_values.size();
serialize_feature(sst, sf);
2017-08-25 14:23:06 -07:00
}
void readFeatureCollection(protozero::pbf_reader &pbf, size_t dim, double e, std::vector<std::string> &keys, struct serialization_state *sst, int layer, std::string layername) {
2017-08-25 14:23:06 -07:00
while (pbf.next()) {
switch (pbf.tag()) {
case 1: {
protozero::pbf_reader feature_reader(pbf.get_message());
readFeature(feature_reader, dim, e, keys, sst, layer, layername);
2017-08-25 14:23:06 -07:00
break;
}
default:
pbf.skip();
}
}
}
void parse_geobuf(struct serialization_state *sst, std::string const &src, int layer, std::string layername) {
protozero::pbf_reader pbf(src);
size_t dim = 2;
double e = 1e6;
2017-08-25 14:23:06 -07:00
std::vector<std::string> keys;
while (pbf.next()) {
switch (pbf.tag()) {
case 1:
keys.push_back(pbf.get_string());
break;
case 2:
dim = pbf.get_int64();
if (dim < 2) {
fprintf(stderr, "Geometry has fewer than 2 dimensions: %zu\n", dim);
exit(EXIT_FAILURE);
}
2017-08-25 14:23:06 -07:00
break;
case 3:
e = pow(10, pbf.get_int64());
break;
case 4: {
protozero::pbf_reader feature_collection_reader(pbf.get_message());
readFeatureCollection(feature_collection_reader, dim, e, keys, sst, layer, layername);
2017-08-25 14:23:06 -07:00
break;
}
case 5: {
protozero::pbf_reader feature_reader(pbf.get_message());
readFeature(feature_reader, dim, e, keys, sst, layer, layername);
2017-08-25 14:23:06 -07:00
break;
}
case 6: {
int type;
protozero::pbf_reader geometry_reader(pbf.get_message());
drawvec dv = readGeometry(geometry_reader, dim, e, keys, type);
2017-08-25 14:23:06 -07:00
break;
}
default:
pbf.skip();
}
}
}