cesium-native/CesiumVectorData/test/TestGeoJsonObject.cpp

561 lines
15 KiB
C++

#include "CesiumVectorData/GeoJsonObjectTypes.h"
#include <CesiumGeometry/AxisAlignedBox.h>
#include <CesiumNativeTests/readFile.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/Math.h>
#include <CesiumVectorData/GeoJsonDocument.h>
#include <CesiumVectorData/GeoJsonObject.h>
#include <doctest/doctest.h>
#include <cstddef>
#include <span>
#include <string>
using namespace CesiumVectorData;
using namespace CesiumUtility;
using namespace CesiumGeometry;
namespace {
static std::span<const std::byte> stringToBytes(const std::string& str) {
return std::span<const std::byte>(
reinterpret_cast<const std::byte*>(str.data()),
str.size());
}
GeoJsonObject parseGeoJsonObject(const std::string& json) {
Result<GeoJsonDocument> doc =
GeoJsonDocument::fromGeoJson(stringToBytes(json));
CHECK(!doc.errors.hasErrors());
REQUIRE(doc.value);
return doc.value->rootObject;
}
template <typename T>
bool operator==(const std::vector<T>& vectorA, const std::vector<T>& vectorB) {
if (vectorA.size() != vectorB.size()) {
return false;
}
for (size_t i = 0; i < vectorA.size(); i++) {
if (vectorA[i] != vectorB[i]) {
return false;
}
}
return true;
}
} // namespace
TEST_CASE("GeoJsonObject::points") {
SUBCASE("Point object") {
GeoJsonObject pointsObj = parseGeoJsonObject(R"==(
{
"type": "Point",
"coordinates": [1, 2, 3]
})==");
std::vector<glm::dvec3> points(
pointsObj.points().begin(),
pointsObj.points().end());
CHECK(points.size() == 1);
CHECK(points[0] == glm::dvec3(1.0, 2.0, 3.0));
}
SUBCASE("MultiPoint object") {
GeoJsonObject pointsObj = parseGeoJsonObject(R"==(
{
"type": "MultiPoint",
"coordinates": [
[ 1, 2, 3 ],
[ 2, 3, 4 ],
[ 3, 4, 5 ],
[ 4, 5, 6 ],
[ 5, 6, 7 ]
]
})==");
std::vector<glm::dvec3> points(
pointsObj.points().begin(),
pointsObj.points().end());
CHECK(points.size() == 5);
CHECK(points[0] == glm::dvec3(1.0, 2.0, 3.0));
CHECK(points[1] == glm::dvec3(2.0, 3.0, 4.0));
CHECK(points[2] == glm::dvec3(3.0, 4.0, 5.0));
CHECK(points[3] == glm::dvec3(4.0, 5.0, 6.0));
CHECK(points[4] == glm::dvec3(5.0, 6.0, 7.0));
}
SUBCASE("GeometryCollection") {
GeoJsonObject pointsObj = parseGeoJsonObject(R"==(
{
"type": "GeometryCollection",
"geometries": [
{
"type": "Point",
"coordinates": [ 1, 2, 3 ]
},
{
"type": "GeometryCollection",
"geometries": [
{
"type": "Point",
"coordinates": [ 2, 3, 4 ]
},
{
"type": "MultiPoint",
"coordinates": [
[ 3, 4, 5 ],
[ 4, 5, 6 ]
]
}
]
},
{
"type": "MultiPoint",
"coordinates": [ ]
},
{
"type": "Point",
"coordinates": [ 5, 6, 7 ]
}
]
})==");
std::vector<glm::dvec3> points(
pointsObj.points().begin(),
pointsObj.points().end());
CHECK(points.size() == 5);
CHECK(points[0] == glm::dvec3(1.0, 2.0, 3.0));
CHECK(points[1] == glm::dvec3(2.0, 3.0, 4.0));
CHECK(points[2] == glm::dvec3(3.0, 4.0, 5.0));
CHECK(points[3] == glm::dvec3(4.0, 5.0, 6.0));
CHECK(points[4] == glm::dvec3(5.0, 6.0, 7.0));
}
}
TEST_CASE("GeoJsonObject::lines") {
SUBCASE("LineString object") {
GeoJsonObject linesObj = parseGeoJsonObject(R"==(
{
"type": "LineString",
"coordinates": [
[1, 2, 3],
[4, 5, 6]
]
})==");
std::vector<std::vector<glm::dvec3>> lines(
linesObj.lines().begin(),
linesObj.lines().end());
const std::vector<std::vector<glm::dvec3>> linesExpected{
{glm::dvec3(1, 2, 3), glm::dvec3(4, 5, 6)}};
CHECK(lines == linesExpected);
}
SUBCASE("MultiLineString object") {
GeoJsonObject linesObj = parseGeoJsonObject(R"==(
{
"type": "MultiLineString",
"coordinates": [
[
[ 1, 2, 3 ],
[ 2, 3, 4 ]
],
[
[ 3, 4, 5 ],
[ 4, 5, 6 ]
],
[
[ 5, 6, 7 ],
[ 6, 7, 8 ],
[ 7, 8, 9 ]
]
]
})==");
std::vector<std::vector<glm::dvec3>> lines(
linesObj.lines().begin(),
linesObj.lines().end());
const std::vector<std::vector<glm::dvec3>> linesExpected{
{glm::dvec3(1, 2, 3), glm::dvec3(2, 3, 4)},
{glm::dvec3(3, 4, 5), glm::dvec3(4, 5, 6)},
{glm::dvec3(5, 6, 7), glm::dvec3(6, 7, 8), glm::dvec3(7, 8, 9)}};
CHECK(lines == linesExpected);
}
SUBCASE("Complex") {
GeoJsonObject linesObj = parseGeoJsonObject(R"==(
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": null,
"geometry": {
"type": "GeometryCollection",
"geometries": [
{
"type": "LineString",
"coordinates": [
[ 1, 2, 3 ],
[ 4, 5, 6 ]
]
},
{
"type": "Point",
"coordinates": [ 0, 1, 2 ]
},
{
"type": "GeometryCollection",
"geometries": [
{
"type": "LineString",
"coordinates": [
[ 2, 3, 4 ],
[ 3, 4, 5 ],
[ 4, 5, 6 ]
]
},
{
"type": "MultiLineString",
"coordinates": [
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ],
[ 10, 11, 12 ]
],
[
[ 0, 1, 2 ],
[ 1, 2, 3 ],
[ 2, 3, 4 ]
]
]
}
]
},
{
"type": "MultiLineString",
"coordinates": []
},
{
"type": "LineString",
"coordinates": [
[ 1, 2, 3 ],
[ 4, 5, 6 ]
]
}
]
}
}
]
})==");
std::vector<std::vector<glm::dvec3>> lines(
linesObj.lines().begin(),
linesObj.lines().end());
const std::vector<std::vector<glm::dvec3>> linesExpected{
{glm::dvec3(1, 2, 3), glm::dvec3(4, 5, 6)},
{glm::dvec3(2, 3, 4), glm::dvec3(3, 4, 5), glm::dvec3(4, 5, 6)},
{glm::dvec3(1, 2, 3),
glm::dvec3(4, 5, 6),
glm::dvec3(7, 8, 9),
glm::dvec3(10, 11, 12)},
{glm::dvec3(0, 1, 2), glm::dvec3(1, 2, 3), glm::dvec3(2, 3, 4)},
{glm::dvec3(1, 2, 3), glm::dvec3(4, 5, 6)}};
CHECK(lines == linesExpected);
}
}
TEST_CASE("GeoJsonObject::polygons") {
SUBCASE("Polygon object") {
GeoJsonObject polyObj = parseGeoJsonObject(R"==(
{
"type": "Polygon",
"coordinates": [
[
[1, 2, 3],
[4, 5, 6],
[5, 6, 7],
[1, 2, 3]
]
]
})==");
std::vector<std::vector<std::vector<glm::dvec3>>> polygons(
polyObj.polygons().begin(),
polyObj.polygons().end());
const std::vector<std::vector<std::vector<glm::dvec3>>> polygonsExpected{
{{glm::dvec3(1, 2, 3),
glm::dvec3(4, 5, 6),
glm::dvec3(5, 6, 7),
glm::dvec3(1, 2, 3)}}};
CHECK(polygons == polygonsExpected);
}
SUBCASE("MultiPolygon object") {
GeoJsonObject polyObj = parseGeoJsonObject(R"==(
{
"type": "MultiPolygon",
"coordinates": [
[
[
[ 1, 2, 3 ],
[ 2, 3, 4 ],
[ 4, 5, 6 ],
[ 1, 2, 3 ]
],
[
[ 3, 4, 5 ],
[ 4, 5, 6 ],
[ 5, 6, 7 ],
[ 3, 4, 5 ]
]
],
[
[
[ 5, 6, 7 ],
[ 6, 7, 8 ],
[ 7, 8, 9 ],
[ 5, 6, 7 ]
]
]
]
})==");
std::vector<std::vector<std::vector<glm::dvec3>>> polygons(
polyObj.polygons().begin(),
polyObj.polygons().end());
const std::vector<std::vector<std::vector<glm::dvec3>>> polygonsExpected{
{{glm::dvec3(1, 2, 3),
glm::dvec3(2, 3, 4),
glm::dvec3(4, 5, 6),
glm::dvec3(1, 2, 3)},
{glm::dvec3(3, 4, 5),
glm::dvec3(4, 5, 6),
glm::dvec3(5, 6, 7),
glm::dvec3(3, 4, 5)}},
{{glm::dvec3(5, 6, 7),
glm::dvec3(6, 7, 8),
glm::dvec3(7, 8, 9),
glm::dvec3(5, 6, 7)}}};
CHECK(polygons == polygonsExpected);
}
SUBCASE("Complex") {
GeoJsonObject polyObj = parseGeoJsonObject(R"==(
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": null,
"geometry": {
"type": "GeometryCollection",
"geometries": [
{
"type": "Polygon",
"coordinates": [
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 5, 6, 7 ],
[ 1, 2, 3 ]
]
]
},
{
"type": "Point",
"coordinates": [ 0, 1, 2 ]
},
{
"type": "GeometryCollection",
"geometries": [
{
"type": "Polygon",
"coordinates": [
[
[ 2, 3, 4 ],
[ 3, 4, 5 ],
[ 4, 5, 6 ],
[ 2, 3, 4 ]
],
[
[ 1, 2, 3 ],
[ 2, 3, 4 ],
[ 3, 4, 5 ],
[ 1, 2, 3 ]
]
]
},
{
"type": "MultiPolygon",
"coordinates": [
[
[
[ 2, 3, 4 ],
[ 3, 4, 5 ],
[ 4, 5, 6 ],
[ 2, 3, 4 ]
],
[
[ 1, 2, 3 ],
[ 2, 3, 4 ],
[ 3, 4, 5 ],
[ 1, 2, 3 ]
]
],
[
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 5, 6, 7 ],
[ 1, 2, 3 ]
]
]
]
}
]
},
{
"type": "MultiPolygon",
"coordinates": []
},
{
"type": "Polygon",
"coordinates": [
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ],
[ 1, 2, 3 ]
]
]
}
]
}
}
]
})==");
std::vector<std::vector<std::vector<glm::dvec3>>> polygons(
polyObj.polygons().begin(),
polyObj.polygons().end());
const std::vector<std::vector<std::vector<glm::dvec3>>> polygonsExpected{
{{glm::dvec3(1, 2, 3),
glm::dvec3(4, 5, 6),
glm::dvec3(5, 6, 7),
glm::dvec3(1, 2, 3)}},
{{glm::dvec3(2, 3, 4),
glm::dvec3(3, 4, 5),
glm::dvec3(4, 5, 6),
glm::dvec3(2, 3, 4)},
{glm::dvec3(1, 2, 3),
glm::dvec3(2, 3, 4),
glm::dvec3(3, 4, 5),
glm::dvec3(1, 2, 3)}},
{{glm::dvec3(2, 3, 4),
glm::dvec3(3, 4, 5),
glm::dvec3(4, 5, 6),
glm::dvec3(2, 3, 4)},
{glm::dvec3(1, 2, 3),
glm::dvec3(2, 3, 4),
glm::dvec3(3, 4, 5),
glm::dvec3(1, 2, 3)}},
{{glm::dvec3(1, 2, 3),
glm::dvec3(4, 5, 6),
glm::dvec3(5, 6, 7),
glm::dvec3(1, 2, 3)}},
{{glm::dvec3(1, 2, 3),
glm::dvec3(4, 5, 6),
glm::dvec3(7, 8, 9),
glm::dvec3(1, 2, 3)}}};
CHECK(polygons == polygonsExpected);
}
}
TEST_CASE("GeoJsonObject:allOfType") {
SUBCASE("Feature") {
GeoJsonObject featuresObj = parseGeoJsonObject(R"==(
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": null, "id": 0 },
{ "type": "Feature", "properties": null, "id": 1 },
{ "type": "Feature", "properties": null, "id": 2 },
{ "type": "Feature", "properties": null, "id": 3 },
{ "type": "Feature", "properties": null, "id": 4 }
]
}
)==");
const std::vector<int64_t> expectedIds = {0, 1, 2, 3, 4};
ConstGeoJsonObjectTypeIterator<GeoJsonFeature> featureIt =
featuresObj.allOfType<GeoJsonFeature>().begin();
for (size_t i = 0; i < expectedIds.size(); i++) {
const int64_t* pId = std::get_if<int64_t>(&featureIt->id);
REQUIRE(pId);
CHECK(*pId == expectedIds[i]);
++featureIt;
}
}
SUBCASE("Point") {
GeoJsonObject pointsObj = parseGeoJsonObject(R"==(
{
"type": "GeometryCollection",
"geometries": [
{ "type": "Point", "coordinates": [ 1, 2, 3 ] },
{ "type": "Point", "coordinates": [ 2, 3, 4 ] },
{ "type": "Point", "coordinates": [ 3, 4, 5 ] },
{ "type": "Point", "coordinates": [ 4, 5, 6 ] },
{ "type": "Point", "coordinates": [ 5, 6, 7 ] }
]
}
)==");
const std::vector<glm::dvec3> expectedCoordinates{
glm::dvec3(1, 2, 3),
glm::dvec3(2, 3, 4),
glm::dvec3(3, 4, 5),
glm::dvec3(4, 5, 6),
glm::dvec3(5, 6, 7)};
ConstGeoJsonObjectTypeIterator<GeoJsonPoint> pointsIt =
pointsObj.allOfType<GeoJsonPoint>().begin();
for (size_t i = 0; i < expectedCoordinates.size(); i++) {
CHECK(pointsIt->coordinates == expectedCoordinates[i]);
++pointsIt;
}
}
}
TEST_CASE("GeoJsonObject:visit") {
GeoJsonObject pointsObj = parseGeoJsonObject(R"==(
{
"type": "Point",
"coordinates": [1, 2, 3]
})==");
SUBCASE("visitor returns void") {
bool visited = false;
pointsObj.visit([&visited](const auto&) { visited = true; });
CHECK(visited);
}
SUBCASE("visitor returns value") {
CHECK(pointsObj.visit([](const auto&) {
return std::string("test");
}) == "test");
}
SUBCASE("visitor returns reference") {
std::string s = "test";
const std::string& result =
pointsObj.visit([&s](const auto&) -> const std::string& { return s; });
CHECK(&s == &result);
}
}