From 925ce67a5d6805118c212069559c56ac588409d3 Mon Sep 17 00:00:00 2001 From: systemed Date: Sat, 9 Dec 2023 14:23:27 +0000 Subject: [PATCH 1/2] GeoJSON writer for debugging --- include/geojson.h | 121 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 include/geojson.h diff --git a/include/geojson.h b/include/geojson.h new file mode 100644 index 00000000..0ce2b45b --- /dev/null +++ b/include/geojson.h @@ -0,0 +1,121 @@ +/*! \file */ +#ifndef _GEOJSON_H +#define _GEOJSON_H + +/* + GeoJSON writer for boost::geometry objects, using RapidJSON. + This isn't core tilemaker functionality but helps with debugging. + As yet it only outputs MultiPolygons but can be extended for more types. + + Example: + auto gj = GeoJSON() + 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 +#include +#include "geom.h" + +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/filewritestream.h" + +using namespace rapidjson; + +struct GeoJSON { + Document document; + std::vector geometries; + + GeoJSON() { + document.SetObject(); + document.AddMember("type", Value().SetString("FeatureCollection"), document.GetAllocator()); + } + void addGeometry(Geometry 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 ringToArray(Ring &r) { + Value arr(kArrayType); + for (auto &point : r) { + Value pt(kArrayType); + pt.PushBack(point.x(), *alloc); + pt.PushBack(unproject ? latp2lat(point.y()) : point.y(), *alloc); + arr.PushBack(pt, *alloc); + } + return arr; + } + void operator()(Point &p) {} // todo + void operator()(Linestring &ls) {} // todo + void operator()(MultiLinestring &mls) {} // todo + void operator()(Polygon &p) {} // todo + void operator()(MultiPolygon &mp) { + Value coordinates(kArrayType); + for (auto &polygon : mp) { + Value polyCoords(kArrayType); + polyCoords.PushBack(ringToArray(polygon.outer()), *alloc); + for (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 (Geometry &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 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 writer(os); + writer.SetMaxDecimalPlaces(5); + document.Accept(writer); + fclose(fp); + } +}; + +#endif //_GEOJSON_H From 79213ce66713216f3959e652a73b439ab69ec52a Mon Sep 17 00:00:00 2001 From: systemed Date: Sat, 9 Dec 2023 16:45:55 +0000 Subject: [PATCH 2/2] Support Polygon and Ring --- include/geojson.h | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/include/geojson.h b/include/geojson.h index 0ce2b45b..ef6aee44 100644 --- a/include/geojson.h +++ b/include/geojson.h @@ -5,7 +5,7 @@ /* GeoJSON writer for boost::geometry objects, using RapidJSON. This isn't core tilemaker functionality but helps with debugging. - As yet it only outputs MultiPolygons but can be extended for more types. + As yet it only outputs (Multi)Polygons but can be extended for more types. Example: auto gj = GeoJSON() @@ -28,17 +28,19 @@ #include "rapidjson/stringbuffer.h" #include "rapidjson/filewritestream.h" +typedef boost::variant AnyGeometry; + using namespace rapidjson; struct GeoJSON { Document document; - std::vector geometries; + std::vector geometries; GeoJSON() { document.SetObject(); document.AddMember("type", Value().SetString("FeatureCollection"), document.GetAllocator()); } - void addGeometry(Geometry geom) { + void addGeometry(AnyGeometry geom) { geometries.emplace_back(geom); } struct SerialiseGeometry { @@ -65,7 +67,21 @@ struct GeoJSON { void operator()(Point &p) {} // todo void operator()(Linestring &ls) {} // todo void operator()(MultiLinestring &mls) {} // todo - void operator()(Polygon &p) {} // todo + 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 (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 (auto &polygon : mp) { @@ -82,7 +98,7 @@ struct GeoJSON { }; void finalise(bool unproject = false) { Value features(kArrayType); - for (Geometry &g : geometries) { + for (AnyGeometry &g : geometries) { // type Value obj(kObjectType); obj.AddMember("type", Value().SetString("Feature"), document.GetAllocator());