1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
/*! \file */
#ifndef _GEOJSON_WRITER_H
#define _GEOJSON_WRITER_H
/*
GeoJSON writer for boost::geometry objects, using RapidJSON.
This isn't core tilemaker functionality but helps with debugging.
As yet it only outputs (Multi)Polygons but can be extended for more types.
Example:
auto gj = GeoJSONWriter();
gj.addGeometry(myMultiPolygon);
gj.finalise();
std::cout << gj.toString() << std::endl;
Or use gj.toFile("output.geojson") to write to file.
Calling finalise(true) will 'unproject' Y values (i.e. latp2lat).
Todo: support more geometries, set/write properties.
*/
#include <iostream>
#include <math.h>
#include "geom.h"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/filewritestream.h"
typedef boost::variant<Point,Linestring,MultiLinestring,Polygon,MultiPolygon,Ring> AnyGeometry;
using namespace rapidjson;
struct GeoJSONWriter {
Document document;
std::vector<AnyGeometry> geometries;
GeoJSONWriter() {
document.SetObject();
document.AddMember("type", Value().SetString("FeatureCollection"), document.GetAllocator());
}
void addGeometry(AnyGeometry geom) {
geometries.emplace_back(geom);
}
struct SerialiseGeometry {
Value *obj;
Document::AllocatorType *alloc;
bool unproject;
SerialiseGeometry(Value *obj, Document::AllocatorType *alloc, bool unproject) :
obj(obj), alloc(alloc), unproject(unproject) {}
double deg2rad(double deg) { return (M_PI/180.0) * deg; }
double rad2deg(double rad) { return (180.0/M_PI) * rad; }
double latp2lat(double latp) { return rad2deg(atan(exp(deg2rad(latp)))*2.0)-90.0; }
Value serializePoint(const Point& point) {
Value pt(kArrayType);
pt.PushBack(point.x(), *alloc);
pt.PushBack(unproject ? latp2lat(point.y()) : point.y(), *alloc);
return pt;
}
Value ringToArray(const Ring &r) {
Value arr(kArrayType);
for (const auto &point : r) {
Value pt = serializePoint(point);
arr.PushBack(pt, *alloc);
}
return arr;
}
void operator()(Point &p) {
Value coordinates(kArrayType);
obj->AddMember("coordinates", serializePoint(p), *alloc);
obj->AddMember("type", Value().SetString("Point"), *alloc);
}
void operator()(Linestring &ls) {
Value coordinates(kArrayType);
for (const auto &point : ls) {
coordinates.PushBack(serializePoint(point), *alloc);
}
obj->AddMember("coordinates", coordinates, *alloc);
obj->AddMember("type", Value().SetString("LineString"), *alloc);
}
void operator()(MultiLinestring &mls) {
Value coordinates(kArrayType);
for (const auto& ls : mls) {
Value lsCoordinates(kArrayType);
for (const auto& point : ls) {
lsCoordinates.PushBack(serializePoint(point), *alloc);
}
coordinates.PushBack(lsCoordinates, *alloc);
}
obj->AddMember("coordinates", coordinates, *alloc);
obj->AddMember("type", Value().SetString("MultiLineString"), *alloc);
}
void operator()(Ring &r) {
Value coordinates(kArrayType);
coordinates.PushBack(ringToArray(r), *alloc);
obj->AddMember("coordinates", coordinates, *alloc);
obj->AddMember("type", Value().SetString("Polygon"), *alloc);
}
void operator()(Polygon &p) {
Value coordinates(kArrayType);
coordinates.PushBack(ringToArray(p.outer()), *alloc);
for (const auto &inner : p.inners()) {
coordinates.PushBack(ringToArray(inner), *alloc);
}
obj->AddMember("coordinates", coordinates, *alloc);
obj->AddMember("type", Value().SetString("Polygon"), *alloc);
}
void operator()(MultiPolygon &mp) {
Value coordinates(kArrayType);
for (const auto &polygon : mp) {
Value polyCoords(kArrayType);
polyCoords.PushBack(ringToArray(polygon.outer()), *alloc);
for (const auto &inner : polygon.inners()) {
polyCoords.PushBack(ringToArray(inner), *alloc);
}
coordinates.PushBack(polyCoords, *alloc);
}
obj->AddMember("coordinates", coordinates, *alloc);
obj->AddMember("type", Value().SetString("MultiPolygon"), *alloc);
}
};
void finalise(bool unproject = false) {
Value features(kArrayType);
for (AnyGeometry &g : geometries) {
// type
Value obj(kObjectType);
obj.AddMember("type", Value().SetString("Feature"), document.GetAllocator());
// properties (todo)
Value properties(kObjectType);
obj.AddMember("properties", properties, document.GetAllocator());
// geometry
Value geometry(kObjectType);
boost::apply_visitor(SerialiseGeometry(&geometry, &(document.GetAllocator()), unproject), g);
obj.AddMember("geometry", geometry, document.GetAllocator());
// add to list
features.PushBack(obj, document.GetAllocator());
}
document.AddMember("features", features, document.GetAllocator());
geometries.clear();
}
std::string toString() {
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
writer.SetMaxDecimalPlaces(5);
document.Accept(writer);
std::string json(buffer.GetString(), buffer.GetSize());
return json;
}
void toFile(std::string filename) {
auto fp = std::fopen(filename.c_str(), "w");
char writeBuffer[65536];
FileWriteStream os(fp, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os);
writer.SetMaxDecimalPlaces(5);
document.Accept(writer);
fclose(fp);
}
};
#endif //_GEOJSON_WRITER_H
|