mirror of
https://github.com/systemed/tilemaker
synced 2025-02-21 21:54:08 +01:00
163 lines
5.2 KiB
C++
163 lines
5.2 KiB
C++
/*! \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
|