2025-05-29 01:19:39 +08:00
|
|
|
#include <CesiumGeometry/AxisAlignedBox.h>
|
2025-06-11 23:12:56 +08:00
|
|
|
#include <CesiumNativeTests/SimpleAssetAccessor.h>
|
|
|
|
|
#include <CesiumNativeTests/SimpleAssetRequest.h>
|
|
|
|
|
#include <CesiumNativeTests/SimpleAssetResponse.h>
|
|
|
|
|
#include <CesiumNativeTests/SimpleTaskProcessor.h>
|
2025-05-07 02:49:46 +08:00
|
|
|
#include <CesiumNativeTests/readFile.h>
|
2025-05-13 03:49:52 +08:00
|
|
|
#include <CesiumUtility/IntrusivePointer.h>
|
2025-05-07 02:49:46 +08:00
|
|
|
#include <CesiumUtility/Math.h>
|
2025-05-09 04:44:16 +08:00
|
|
|
#include <CesiumVectorData/GeoJsonDocument.h>
|
|
|
|
|
#include <CesiumVectorData/GeoJsonObject.h>
|
2025-05-29 05:29:44 +08:00
|
|
|
#include <CesiumVectorData/GeoJsonObjectTypes.h>
|
2025-04-03 05:59:09 +08:00
|
|
|
|
|
|
|
|
#include <doctest/doctest.h>
|
|
|
|
|
|
|
|
|
|
#include <cstddef>
|
2025-04-04 02:55:41 +08:00
|
|
|
#include <filesystem>
|
2025-04-03 05:59:09 +08:00
|
|
|
#include <span>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <variant>
|
|
|
|
|
|
|
|
|
|
using namespace CesiumVectorData;
|
|
|
|
|
using namespace CesiumUtility;
|
2025-05-29 01:19:39 +08:00
|
|
|
using namespace CesiumGeometry;
|
2025-06-11 23:10:31 +08:00
|
|
|
using namespace CesiumNativeTests;
|
2025-04-03 05:59:09 +08:00
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
}
|
2025-04-04 02:55:41 +08:00
|
|
|
|
2025-04-05 02:10:24 +08:00
|
|
|
void expectParserResult(
|
|
|
|
|
const std::string& json,
|
2025-05-29 01:19:39 +08:00
|
|
|
const std::function<void(const GeoJsonDocument&)>& checkFunc) {
|
|
|
|
|
Result<GeoJsonDocument> doc =
|
2025-05-09 04:44:16 +08:00
|
|
|
GeoJsonDocument::fromGeoJson(stringToBytes(json));
|
2025-04-04 02:55:41 +08:00
|
|
|
CHECK(!doc.errors.hasErrors());
|
2025-05-29 01:19:39 +08:00
|
|
|
REQUIRE(doc.value);
|
|
|
|
|
checkFunc(*doc.value);
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
2025-04-03 05:59:09 +08:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Parse Point primitives") {
|
2025-04-04 02:55:41 +08:00
|
|
|
SUBCASE("Valid points") {
|
|
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "Point",
|
|
|
|
|
"coordinates": [100.0, 0.0]
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonPoint* pPoint =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonPoint>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPoint);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pPoint->TYPE == GeoJsonObjectType::Point);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(pPoint->coordinates == glm::dvec3(100.0, 0.0, 0.0));
|
2025-05-09 04:44:16 +08:00
|
|
|
});
|
2025-04-03 05:59:09 +08:00
|
|
|
|
2025-04-04 02:55:41 +08:00
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "Point",
|
|
|
|
|
"coordinates": [-100.0, 20.0, 500.0]
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonPoint* pPoint =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonPoint>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPoint);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pPoint->TYPE == GeoJsonObjectType::Point);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(pPoint->coordinates == glm::dvec3(-100.0, 20.0, 500.0));
|
2025-05-09 04:44:16 +08:00
|
|
|
});
|
|
|
|
|
|
2025-04-04 02:55:41 +08:00
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "Point",
|
|
|
|
|
"coordinates": [-90, 180.0, -500.0],
|
2025-05-29 01:19:39 +08:00
|
|
|
"bbox": [30.0, 35.0, 50.0, 90, -90.0, -50]
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonPoint* pPoint =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonPoint>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPoint);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pPoint->TYPE == GeoJsonObjectType::Point);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(pPoint->coordinates == glm::dvec3(-90.0, 180.0, -500.0));
|
|
|
|
|
const std::optional<AxisAlignedBox>& bbox = pPoint->boundingBox;
|
|
|
|
|
REQUIRE(bbox);
|
|
|
|
|
CHECK(bbox->minimumX == 30.0);
|
|
|
|
|
CHECK(bbox->minimumY == -90.0);
|
|
|
|
|
CHECK(bbox->maximumX == 90.0);
|
|
|
|
|
CHECK(bbox->maximumY == 35.0);
|
|
|
|
|
CHECK(bbox->minimumZ == -50.0);
|
|
|
|
|
CHECK(bbox->maximumZ == 50.0);
|
2025-05-09 04:44:16 +08:00
|
|
|
});
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("'coordinates' must exist'") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(
|
|
|
|
|
stringToBytes(R"==({ "type": "Point" })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(doc.errors.errors[0] == "'coordinates' member required.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Position must be an array") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(
|
|
|
|
|
stringToBytes(R"==({ "type": "Point", "coordinates": 2 })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(doc.errors.errors[0] == "Position value must be an array.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Position value must be 2D or 3D") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(
|
|
|
|
|
stringToBytes(R"==({ "type": "Point", "coordinates": [2.0] })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"Position value must be an array with two or three members.");
|
|
|
|
|
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
2025-04-04 02:55:41 +08:00
|
|
|
R"==({ "type": "Point", "coordinates": [2.0, 1.0, 0.0, 3.0] })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"Position value must be an array with two or three members.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Position value must only contain numbers") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
|
|
|
|
R"==({ "type": "Point", "coordinates": [2.0, false] })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"Position value must be an array of only numbers.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Parse MultiPoint primitives") {
|
|
|
|
|
SUBCASE("Valid MultiPoint") {
|
|
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "MultiPoint",
|
|
|
|
|
"coordinates": [
|
|
|
|
|
[-75.1428517, 39.9644934, 400],
|
|
|
|
|
[129.6869721, 62.0256947, 100]
|
|
|
|
|
],
|
|
|
|
|
"bbox": [30.0, -30.0, 40.0, -40.0]
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonMultiPoint* pPoint =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonMultiPoint>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPoint);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pPoint->TYPE == GeoJsonObjectType::MultiPoint);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPoint->coordinates.size() == 2);
|
2025-05-09 04:44:16 +08:00
|
|
|
CHECK(
|
2025-05-13 03:49:52 +08:00
|
|
|
pPoint->coordinates[0] ==
|
2025-05-29 01:19:39 +08:00
|
|
|
glm::dvec3(-75.1428517, 39.9644934, 400.0));
|
2025-05-09 04:44:16 +08:00
|
|
|
CHECK(
|
2025-05-13 03:49:52 +08:00
|
|
|
pPoint->coordinates[1] ==
|
2025-05-29 01:19:39 +08:00
|
|
|
glm::dvec3(129.6869721, 62.0256947, 100.0));
|
|
|
|
|
const std::optional<AxisAlignedBox>& bbox = pPoint->boundingBox;
|
|
|
|
|
REQUIRE(bbox);
|
|
|
|
|
CHECK(bbox->minimumX == 30.0);
|
|
|
|
|
CHECK(bbox->minimumY == -40.0);
|
|
|
|
|
CHECK(bbox->maximumX == 40.0);
|
|
|
|
|
CHECK(bbox->maximumY == -30.0);
|
|
|
|
|
CHECK(bbox->minimumZ == 0.0);
|
|
|
|
|
CHECK(bbox->maximumZ == 0.0);
|
2025-05-09 04:44:16 +08:00
|
|
|
});
|
2025-04-04 02:55:41 +08:00
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "MultiPoint",
|
|
|
|
|
"coordinates": [
|
|
|
|
|
[-75.1428517, 39.9644934, 400],
|
|
|
|
|
[129.6869721, 62.0256947, 100]
|
|
|
|
|
],
|
|
|
|
|
"exampleA": 40,
|
|
|
|
|
"exampleB": "test"
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonMultiPoint* pPoint =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonMultiPoint>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPoint);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pPoint->TYPE == GeoJsonObjectType::MultiPoint);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPoint->coordinates.size() == 2);
|
2025-05-09 04:44:16 +08:00
|
|
|
CHECK(
|
2025-05-13 03:49:52 +08:00
|
|
|
pPoint->coordinates[0] ==
|
2025-05-29 01:19:39 +08:00
|
|
|
glm::dvec3(-75.1428517, 39.9644934, 400.0));
|
2025-05-09 04:44:16 +08:00
|
|
|
CHECK(
|
2025-05-13 03:49:52 +08:00
|
|
|
pPoint->coordinates[1] ==
|
2025-05-29 01:19:39 +08:00
|
|
|
glm::dvec3(129.6869721, 62.0256947, 100.0));
|
2025-05-13 03:49:52 +08:00
|
|
|
JsonValue::Object foreignMembers = pPoint->foreignMembers;
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(!foreignMembers.empty());
|
|
|
|
|
CHECK(foreignMembers["exampleA"] == JsonValue(40));
|
|
|
|
|
CHECK(foreignMembers["exampleB"] == JsonValue("test"));
|
|
|
|
|
});
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Coordinates must be an array") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(
|
|
|
|
|
stringToBytes(R"==({ "type": "MultiPoint", "coordinates": false })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"MultiPoint 'coordinates' member must be an array of positions.");
|
2025-04-03 05:59:09 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Parse LineString primitives") {
|
2025-04-04 02:55:41 +08:00
|
|
|
SUBCASE("Valid LineString") {
|
|
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "LineString",
|
|
|
|
|
"coordinates": [
|
|
|
|
|
[-75.1428517, 39.9644934, 400],
|
|
|
|
|
[129.6869721, 62.0256947, 100]
|
|
|
|
|
],
|
|
|
|
|
"bbox": [30.0, -30.0, 40.0, -40.0]
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonLineString* pLine =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonLineString>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pLine);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pLine->TYPE == GeoJsonObjectType::LineString);
|
2025-05-29 01:19:39 +08:00
|
|
|
const std::vector<glm::dvec3>& points = pLine->coordinates;
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(points.size() == 2);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(points[0] == glm::dvec3(-75.1428517, 39.9644934, 400.0));
|
|
|
|
|
CHECK(points[1] == glm::dvec3(129.6869721, 62.0256947, 100.0));
|
|
|
|
|
const std::optional<AxisAlignedBox>& bbox = pLine->boundingBox;
|
|
|
|
|
REQUIRE(bbox);
|
|
|
|
|
CHECK(bbox->minimumX == 30.0);
|
|
|
|
|
CHECK(bbox->minimumY == -40.0);
|
|
|
|
|
CHECK(bbox->maximumX == 40.0);
|
|
|
|
|
CHECK(bbox->maximumY == -30.0);
|
|
|
|
|
CHECK(bbox->minimumZ == 0.0);
|
|
|
|
|
CHECK(bbox->maximumZ == 0.0);
|
2025-05-09 04:44:16 +08:00
|
|
|
});
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
2025-04-03 05:59:09 +08:00
|
|
|
|
2025-04-04 02:55:41 +08:00
|
|
|
SUBCASE("Coordinates must be an array") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(
|
|
|
|
|
stringToBytes(R"==({ "type": "LineString", "coordinates": false })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"LineString 'coordinates' member must be an array of positions.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Coordinates must contain two or more positions") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
|
|
|
|
R"==({ "type": "LineString", "coordinates": [[0, 1, 2]] })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"LineString 'coordinates' member must contain two or more positions.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Parse MultiLineString primitives") {
|
|
|
|
|
SUBCASE("Valid MultiLineString") {
|
|
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "MultiLineString",
|
|
|
|
|
"coordinates": [
|
|
|
|
|
[
|
|
|
|
|
[-75.1428517, 39.9644934, 400],
|
|
|
|
|
[129.6869721, 62.0256947, 100]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonMultiLineString* pLine =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonMultiLineString>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pLine);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pLine->TYPE == GeoJsonObjectType::MultiLineString);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pLine->coordinates.size() == 1);
|
2025-05-29 01:19:39 +08:00
|
|
|
const std::vector<glm::dvec3>& points = pLine->coordinates[0];
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(points.size() == 2);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(points[0] == glm::dvec3(-75.1428517, 39.9644934, 400.0));
|
|
|
|
|
CHECK(points[1] == glm::dvec3(129.6869721, 62.0256947, 100.0));
|
2025-05-09 04:44:16 +08:00
|
|
|
});
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Coordinates must be an array of arrays") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
|
|
|
|
R"==({ "type": "MultiLineString", "coordinates": false })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] == "MultiLineString 'coordinates' member must be "
|
|
|
|
|
"an array of position arrays.");
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
2025-04-04 02:55:41 +08:00
|
|
|
R"==({ "type": "MultiLineString", "coordinates": [[1, 2, 3]] })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(doc.errors.errors[0] == "Position value must be an array.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Lines must contain two or more positions") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
|
|
|
|
R"==({ "type": "MultiLineString", "coordinates": [[[0, 1, 2]]] })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"MultiLineString 'coordinates' member must be an "
|
|
|
|
|
"array of arrays of 2 or more positions.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Parse Polygon primitives") {
|
|
|
|
|
SUBCASE("Valid Polygon") {
|
|
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "Polygon",
|
|
|
|
|
"coordinates": [
|
|
|
|
|
[
|
|
|
|
|
[-75.1428517, 39.9644934, 400],
|
|
|
|
|
[129.6869721, 62.0256947, 100],
|
|
|
|
|
[103.8245805, 1.3043744, 100],
|
2025-04-04 03:48:53 +08:00
|
|
|
[-80.1976364, 25.7708431, 400],
|
|
|
|
|
[-75.1428517, 39.9644934, 400]
|
2025-04-04 02:55:41 +08:00
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonPolygon* pPolygon =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonPolygon>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPolygon);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pPolygon->TYPE == GeoJsonObjectType::Polygon);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPolygon->coordinates.size() == 1);
|
2025-05-29 01:19:39 +08:00
|
|
|
const std::vector<glm::dvec3>& points = pPolygon->coordinates[0];
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(points.size() == 5);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(points[0].x == -75.1428517);
|
|
|
|
|
CHECK(points[0].y == 39.9644934);
|
|
|
|
|
CHECK(points[0].z == 400.0);
|
|
|
|
|
CHECK(points[1].x == 129.6869721);
|
|
|
|
|
CHECK(points[1].y == 62.0256947);
|
|
|
|
|
CHECK(points[1].z == 100.0);
|
|
|
|
|
CHECK(points[2].x == 103.8245805);
|
|
|
|
|
CHECK(points[2].y == 1.3043744);
|
|
|
|
|
CHECK(points[2].z == 100.0);
|
|
|
|
|
CHECK(points[3].x == -80.1976364);
|
|
|
|
|
CHECK(points[3].y == 25.7708431);
|
|
|
|
|
CHECK(points[3].z == 400.0);
|
|
|
|
|
CHECK(points[4].x == -75.1428517);
|
|
|
|
|
CHECK(points[4].y == 39.9644934);
|
|
|
|
|
CHECK(points[4].z == 400.0);
|
2025-05-09 04:44:16 +08:00
|
|
|
});
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
2025-04-03 05:59:09 +08:00
|
|
|
|
2025-04-04 02:55:41 +08:00
|
|
|
SUBCASE("Coordinates must be an array of arrays") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(
|
|
|
|
|
stringToBytes(R"==({ "type": "Polygon", "coordinates": false })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] == "Polygon 'coordinates' member must be "
|
|
|
|
|
"an array of position arrays.");
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
2025-04-04 02:55:41 +08:00
|
|
|
R"==({ "type": "Polygon", "coordinates": [[1, 2, 3]] })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(doc.errors.errors[0] == "Position value must be an array.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Lines must contain two or more positions") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
|
|
|
|
R"==({ "type": "Polygon", "coordinates": [[[0, 1, 2], [1, 2, 3], [4, 3, 5]]] })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] == "Polygon 'coordinates' member must be an "
|
|
|
|
|
"array of arrays of 4 or more positions.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Parse MultiPolygon primitives") {
|
|
|
|
|
SUBCASE("Valid MultiPolygon") {
|
|
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "MultiPolygon",
|
|
|
|
|
"coordinates": [
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
[-75.1428517, 39.9644934, 400],
|
|
|
|
|
[129.6869721, 62.0256947, 100],
|
|
|
|
|
[103.8245805, 1.3043744, 100],
|
2025-04-04 03:48:53 +08:00
|
|
|
[-80.1976364, 25.7708431, 400],
|
|
|
|
|
[-75.1428517, 39.9644934, 400]
|
2025-04-04 02:55:41 +08:00
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonMultiPolygon* pPolygon =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonMultiPolygon>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPolygon);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pPolygon->TYPE == GeoJsonObjectType::MultiPolygon);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPolygon->coordinates.size() == 1);
|
|
|
|
|
REQUIRE(pPolygon->coordinates[0].size() == 1);
|
2025-05-29 01:19:39 +08:00
|
|
|
const std::vector<glm::dvec3>& points = pPolygon->coordinates[0][0];
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(points.size() == 5);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(points[0].x == -75.1428517);
|
|
|
|
|
CHECK(points[0].y == 39.9644934);
|
|
|
|
|
CHECK(points[0].z == 400.0);
|
|
|
|
|
CHECK(points[1].x == 129.6869721);
|
|
|
|
|
CHECK(points[1].y == 62.0256947);
|
|
|
|
|
CHECK(points[1].z == 100.0);
|
|
|
|
|
CHECK(points[2].x == 103.8245805);
|
|
|
|
|
CHECK(points[2].y == 1.3043744);
|
|
|
|
|
CHECK(points[2].z == 100.0);
|
|
|
|
|
CHECK(points[3].x == -80.1976364);
|
|
|
|
|
CHECK(points[3].y == 25.7708431);
|
|
|
|
|
CHECK(points[3].z == 400.0);
|
|
|
|
|
CHECK(points[4].x == -75.1428517);
|
|
|
|
|
CHECK(points[4].y == 39.9644934);
|
|
|
|
|
CHECK(points[4].z == 400.0);
|
2025-05-09 04:44:16 +08:00
|
|
|
});
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Coordinates must be an array of arrays of arrays") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
|
|
|
|
R"==({ "type": "MultiPolygon", "coordinates": false })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] == "MultiPolygon 'coordinates' member must be an "
|
|
|
|
|
"array of arrays of position arrays.");
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
2025-04-04 02:55:41 +08:00
|
|
|
R"==({ "type": "MultiPolygon", "coordinates": [1, 2, 3] })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] == "MultiPolygon 'coordinates' member must be an "
|
|
|
|
|
"array of arrays of position arrays.");
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
2025-04-04 02:55:41 +08:00
|
|
|
R"==({ "type": "MultiPolygon", "coordinates": [[1, 2, 3]] })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] == "MultiPolygon 'coordinates' member must be an "
|
|
|
|
|
"array of position arrays.");
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
2025-04-04 02:55:41 +08:00
|
|
|
R"==({ "type": "MultiPolygon", "coordinates": [[[1, 2, 3]]] })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(doc.errors.errors[0] == "Position value must be an array.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Lines must contain two or more positions") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
|
|
|
|
R"==({ "type": "MultiPolygon", "coordinates": [[[[0, 1, 2], [1, 2, 3], [4, 3, 5]] ]] })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] == "MultiPolygon 'coordinates' member must be an "
|
|
|
|
|
"array of arrays of 4 or more positions.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Parsing GeometryCollection") {
|
|
|
|
|
SUBCASE("Valid GeometryCollection") {
|
|
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "GeometryCollection",
|
|
|
|
|
"geometries": [
|
|
|
|
|
{ "type": "Point", "coordinates": [1, 2], "bbox": [40.0, 40.0, -40.0, -40.0] },
|
|
|
|
|
{ "type": "LineString", "coordinates": [[1, 2], [3, 4]], "test": 104.0, "test2": false }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonGeometryCollection* pGeomCollection =
|
|
|
|
|
std::get_if<GeoJsonGeometryCollection>(
|
2025-05-29 01:19:39 +08:00
|
|
|
&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pGeomCollection);
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(
|
2025-05-20 03:39:25 +08:00
|
|
|
pGeomCollection->TYPE == GeoJsonObjectType::GeometryCollection);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pGeomCollection->geometries.size() == 2);
|
|
|
|
|
const GeoJsonPoint* pPoint =
|
2025-05-20 03:39:25 +08:00
|
|
|
std::get_if<GeoJsonPoint>(&pGeomCollection->geometries[0].value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPoint);
|
2025-05-20 03:39:25 +08:00
|
|
|
CHECK(pPoint->TYPE == GeoJsonObjectType::Point);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(pPoint->coordinates == glm::dvec3(1.0, 2.0, 0.0));
|
2025-05-20 03:39:25 +08:00
|
|
|
const GeoJsonLineString* pLineString = std::get_if<GeoJsonLineString>(
|
|
|
|
|
&pGeomCollection->geometries[1].value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pLineString);
|
2025-05-20 03:39:25 +08:00
|
|
|
CHECK(pLineString->TYPE == GeoJsonObjectType::LineString);
|
2025-05-29 01:19:39 +08:00
|
|
|
const std::vector<glm::dvec3>& linePoints = pLineString->coordinates;
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(linePoints.size() == 2);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(linePoints[0] == glm::dvec3(1.0, 2.0, 0.0));
|
|
|
|
|
CHECK(linePoints[1] == glm::dvec3(3.0, 4.0, 0.0));
|
2025-05-13 03:49:52 +08:00
|
|
|
JsonValue::Object foreignMembersObj = pLineString->foreignMembers;
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(!foreignMembersObj.empty());
|
|
|
|
|
CHECK(foreignMembersObj["test"] == JsonValue(104.0));
|
|
|
|
|
CHECK(foreignMembersObj["test2"] == JsonValue(false));
|
|
|
|
|
});
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Requires 'geometries'") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(
|
|
|
|
|
stringToBytes(R"==({ "type": "GeometryCollection" })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"GeometryCollection requires array 'geometries' member.");
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
2025-04-04 02:55:41 +08:00
|
|
|
R"==({ "type": "GeometryCollection", "geometries": {} })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"GeometryCollection requires array 'geometries' member.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("'geometries' must only include geometry primitives") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
|
|
|
|
R"==({ "type": "GeometryCollection", "geometries": [{"type": "Feature", "geometry": null, "properties": null}] })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
2025-04-05 02:10:24 +08:00
|
|
|
"GeoJSON GeometryCollection 'geometries' member may only contain "
|
|
|
|
|
"GeoJSON Geometry objects, found Feature.");
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
2025-04-04 02:55:41 +08:00
|
|
|
R"==({ "type": "GeometryCollection", "geometries": [1, 2, 3] })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] == "GeometryCollection 'geometries' member must "
|
|
|
|
|
"contain only GeoJSON objects.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Parsing Feature") {
|
|
|
|
|
SUBCASE("Valid Feature") {
|
|
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "Feature",
|
|
|
|
|
"id": 20,
|
|
|
|
|
"properties": {
|
|
|
|
|
"a": 1,
|
|
|
|
|
"b": false,
|
|
|
|
|
"c": "3"
|
|
|
|
|
},
|
|
|
|
|
"geometry": {
|
|
|
|
|
"type": "LineString",
|
|
|
|
|
"coordinates": [[1,2,3],[4,5,6]]
|
|
|
|
|
},
|
|
|
|
|
"test": "test"
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonFeature* pFeature =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonFeature>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pFeature);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pFeature->TYPE == GeoJsonObjectType::Feature);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pFeature->geometry);
|
|
|
|
|
const int64_t* pId = std::get_if<int64_t>(&pFeature->id);
|
|
|
|
|
REQUIRE(pId);
|
2025-05-09 04:44:16 +08:00
|
|
|
CHECK(*pId == 20);
|
|
|
|
|
CHECK(
|
2025-05-13 03:49:52 +08:00
|
|
|
pFeature->properties == JsonValue::Object{
|
|
|
|
|
{"a", JsonValue(1)},
|
|
|
|
|
{"b", JsonValue(false)},
|
|
|
|
|
{"c", JsonValue("3")}});
|
|
|
|
|
const GeoJsonLineString* pLineString =
|
2025-05-21 01:14:54 +08:00
|
|
|
std::get_if<GeoJsonLineString>(&pFeature->geometry->value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pLineString);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pLineString->TYPE == GeoJsonObjectType::LineString);
|
2025-05-29 01:19:39 +08:00
|
|
|
const std::vector<glm::dvec3>& points = pLineString->coordinates;
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(points.size() == 2);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(points[0] == glm::dvec3(1.0, 2.0, 3.0));
|
|
|
|
|
CHECK(points[1] == glm::dvec3(4.0, 5.0, 6.0));
|
2025-05-13 03:49:52 +08:00
|
|
|
JsonValue::Object foreignMembers = pFeature->foreignMembers;
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(!foreignMembers.empty());
|
|
|
|
|
CHECK(foreignMembers["test"] == JsonValue("test"));
|
|
|
|
|
});
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("Missing required members") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(
|
|
|
|
|
stringToBytes(R"==({ "type": "Feature" })=="));
|
|
|
|
|
REQUIRE(!doc.errors.warnings.empty());
|
|
|
|
|
CHECK(doc.errors.warnings.size() == 2);
|
|
|
|
|
CHECK(doc.errors.warnings[0] == "Feature must have a 'geometry' member.");
|
|
|
|
|
CHECK(doc.errors.warnings[1] == "Feature must have a 'properties' member.");
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("'id' must be string or number") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(
|
|
|
|
|
stringToBytes(R"==({ "type": "Feature", "id": null })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"Feature 'id' member must be either a string or a number.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Parsing FeatureCollection") {
|
|
|
|
|
SUBCASE("Valid FeatureCollection") {
|
|
|
|
|
expectParserResult(
|
|
|
|
|
R"==(
|
|
|
|
|
{
|
|
|
|
|
"type": "FeatureCollection",
|
|
|
|
|
"features": [
|
|
|
|
|
{
|
|
|
|
|
"type": "Feature",
|
|
|
|
|
"properties": null,
|
|
|
|
|
"geometry": {
|
|
|
|
|
"type": "Point",
|
|
|
|
|
"coordinates": [1, 2, 3]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
)==",
|
2025-05-29 01:19:39 +08:00
|
|
|
[](const GeoJsonDocument& document) {
|
2025-05-13 03:49:52 +08:00
|
|
|
const GeoJsonFeatureCollection* pFeatureCollection =
|
2025-05-29 01:19:39 +08:00
|
|
|
std::get_if<GeoJsonFeatureCollection>(&document.rootObject.value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pFeatureCollection);
|
2025-05-09 04:44:16 +08:00
|
|
|
REQUIRE(
|
2025-05-20 03:39:25 +08:00
|
|
|
pFeatureCollection->TYPE == GeoJsonObjectType::FeatureCollection);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pFeatureCollection->features.size() == 1);
|
2025-05-29 05:29:44 +08:00
|
|
|
CHECK(
|
|
|
|
|
pFeatureCollection->features[0]
|
|
|
|
|
.get<GeoJsonFeature>()
|
|
|
|
|
.properties == std::nullopt);
|
|
|
|
|
const GeoJsonPoint* pPoint =
|
|
|
|
|
std::get_if<GeoJsonPoint>(&pFeatureCollection->features[0]
|
|
|
|
|
.get<GeoJsonFeature>()
|
|
|
|
|
.geometry->value);
|
2025-05-13 03:49:52 +08:00
|
|
|
REQUIRE(pPoint);
|
2025-05-20 03:39:25 +08:00
|
|
|
REQUIRE(pPoint->TYPE == GeoJsonObjectType::Point);
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(pPoint->coordinates == glm::dvec3(1, 2, 3));
|
2025-05-09 04:44:16 +08:00
|
|
|
});
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SUBCASE("'features' member must be an array of features") {
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc = GeoJsonDocument::fromGeoJson(
|
|
|
|
|
stringToBytes(R"==({ "type": "FeatureCollection" })=="));
|
2025-04-04 02:55:41 +08:00
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"FeatureCollection must have 'features' member.");
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(
|
2025-04-04 02:55:41 +08:00
|
|
|
stringToBytes(R"==({ "type": "FeatureCollection", "features": 1 })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
|
|
|
|
"FeatureCollection 'features' member must be an array of features.");
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
2025-04-04 02:55:41 +08:00
|
|
|
R"==({ "type": "FeatureCollection", "features": [1] })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] == "FeatureCollection 'features' member must "
|
|
|
|
|
"contain only GeoJSON objects.");
|
2025-05-09 04:44:16 +08:00
|
|
|
doc = GeoJsonDocument::fromGeoJson(stringToBytes(
|
2025-04-04 02:55:41 +08:00
|
|
|
R"==({ "type": "FeatureCollection", "features": [{"type": "Point", "coordinates": [1,2,3]}] })=="));
|
|
|
|
|
REQUIRE(doc.errors.hasErrors());
|
|
|
|
|
CHECK(doc.errors.errors.size() == 1);
|
|
|
|
|
CHECK(
|
|
|
|
|
doc.errors.errors[0] ==
|
2025-04-05 02:10:24 +08:00
|
|
|
"GeoJSON FeatureCollection 'features' member may only contain Feature "
|
|
|
|
|
"objects, found Point.");
|
2025-04-04 02:55:41 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Load test GeoJSON without errors") {
|
|
|
|
|
std::filesystem::path dir(
|
|
|
|
|
std::filesystem::path(CesiumVectorData_TEST_DATA_DIR) / "geojson");
|
|
|
|
|
for (auto& file : std::filesystem::directory_iterator(dir)) {
|
|
|
|
|
if (!file.path().extension().string().ends_with("json")) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-05-29 01:19:39 +08:00
|
|
|
Result<GeoJsonDocument> doc =
|
2025-05-09 04:44:16 +08:00
|
|
|
GeoJsonDocument::fromGeoJson(readFile(file.path()));
|
2025-05-29 01:19:39 +08:00
|
|
|
CHECK(doc.value);
|
2025-04-03 05:59:09 +08:00
|
|
|
CHECK(!doc.errors.hasErrors());
|
2025-04-04 02:55:41 +08:00
|
|
|
CHECK(doc.errors.warnings.empty());
|
2025-04-03 05:59:09 +08:00
|
|
|
}
|
2025-06-11 23:10:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Load GeoJSON from URL") {
|
|
|
|
|
const std::string url = "http://example.com/point.geojson";
|
|
|
|
|
std::filesystem::path dir(
|
|
|
|
|
std::filesystem::path(CesiumVectorData_TEST_DATA_DIR) / "geojson");
|
|
|
|
|
std::map<std::string, std::shared_ptr<SimpleAssetRequest>>
|
|
|
|
|
mockCompletedRequests;
|
2025-06-11 23:12:56 +08:00
|
|
|
std::unique_ptr<SimpleAssetResponse> mockCompletedResponse =
|
|
|
|
|
std::make_unique<SimpleAssetResponse>(
|
|
|
|
|
static_cast<uint16_t>(200),
|
|
|
|
|
"doesn't matter",
|
|
|
|
|
CesiumAsync::HttpHeaders{},
|
|
|
|
|
readFile(dir / "point.geojson"));
|
|
|
|
|
mockCompletedRequests.insert(
|
|
|
|
|
{url,
|
|
|
|
|
std::make_shared<SimpleAssetRequest>(
|
|
|
|
|
"GET",
|
|
|
|
|
url,
|
|
|
|
|
CesiumAsync::HttpHeaders{},
|
|
|
|
|
std::move(mockCompletedResponse))});
|
2025-06-11 23:10:31 +08:00
|
|
|
|
|
|
|
|
std::shared_ptr<SimpleAssetAccessor> mockAssetAccessor =
|
|
|
|
|
std::make_shared<SimpleAssetAccessor>(std::move(mockCompletedRequests));
|
|
|
|
|
CesiumAsync::AsyncSystem asyncSystem(std::make_shared<SimpleTaskProcessor>());
|
|
|
|
|
|
2025-06-11 23:12:56 +08:00
|
|
|
CesiumUtility::Result<GeoJsonDocument> result =
|
|
|
|
|
GeoJsonDocument::fromUrl(asyncSystem, mockAssetAccessor, url)
|
|
|
|
|
.waitInMainThread();
|
2025-06-11 23:10:31 +08:00
|
|
|
|
|
|
|
|
CHECK(!result.errors.hasErrors());
|
|
|
|
|
REQUIRE(result.value);
|
|
|
|
|
REQUIRE(result.value->rootObject.isType<GeoJsonPoint>());
|
2025-06-11 23:12:56 +08:00
|
|
|
CHECK(
|
|
|
|
|
result.value->rootObject.get<GeoJsonPoint>().coordinates ==
|
|
|
|
|
glm::dvec3(42.3, 49.34, 11.3413));
|
2025-04-03 05:59:09 +08:00
|
|
|
}
|