Almost there, need to make changes in geojson-support first

This commit is contained in:
Ashley Rogers 2025-05-15 12:27:07 -04:00
parent f25be89952
commit 9067a7da19
5 changed files with 255 additions and 114 deletions

View File

@ -1,7 +1,7 @@
#pragma once
#include "CesiumVectorData/VectorNode.h"
#include <CesiumVectorData/GeoJsonDocument.h>
#include <CesiumVectorData/GeoJsonObject.h>
#include <CesiumAsync/AsyncSystem.h>
#include <CesiumGeospatial/CartographicPolygon.h>
#include <CesiumGeospatial/Ellipsoid.h>
@ -11,7 +11,6 @@
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumVectorData/Color.h>
#include <CesiumVectorData/VectorDocument.h>
#include <CesiumVectorData/VectorStyle.h>
#include <spdlog/fwd.h>
@ -28,8 +27,8 @@ namespace CesiumRasterOverlays {
*/
using VectorDocumentRasterOverlayStyleCallback = std::function<std::optional<
CesiumVectorData::VectorStyle>(
const CesiumUtility::IntrusivePointer<CesiumVectorData::VectorDocument>&,
const CesiumVectorData::VectorNode*)>;
const CesiumUtility::IntrusivePointer<CesiumVectorData::GeoJsonDocument>&,
const CesiumVectorData::GeoJsonObject*)>;
/**
* @brief A set of options for configuring a VectorDocumentRasterOverlay.
@ -79,7 +78,7 @@ struct IonVectorDocumentRasterOverlaySource {
* @brief Possible sources for a VectorDocumentRasterOverlay's vector data.
*/
using VectorDocumentRasterOverlaySource = std::variant<
CesiumUtility::IntrusivePointer<CesiumVectorData::VectorDocument>,
CesiumUtility::IntrusivePointer<CesiumVectorData::GeoJsonDocument>,
IonVectorDocumentRasterOverlaySource>;
/**

View File

@ -5,7 +5,6 @@
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumGeospatial/BoundingRegionBuilder.h>
#include <CesiumGeospatial/Cartographic.h>
#include <CesiumGeospatial/CompositeCartographicPolygon.h>
#include <CesiumGeospatial/GeographicProjection.h>
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/Projection.h>
@ -18,8 +17,8 @@
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumVectorData/Color.h>
#include <CesiumVectorData/VectorDocument.h>
#include <CesiumVectorData/VectorNode.h>
#include <CesiumVectorData/GeoJsonDocument.h>
#include <CesiumVectorData/GeoJsonObject.h>
#include <CesiumVectorData/VectorRasterizer.h>
#include <glm/common.hpp>
@ -46,8 +45,7 @@ namespace CesiumRasterOverlays {
namespace {
struct QuadtreePrimitiveData {
const VectorPrimitive* pPrimitive;
const VectorNode* pNode;
const GeoJsonObject* pObject;
const VectorStyle* pStyle;
GlobeRectangle rectangle;
};
@ -72,55 +70,129 @@ struct Quadtree {
std::vector<uint32_t> dataNodeIndicesBegin;
};
struct GlobeRectangleFromPrimitiveVisitor {
GlobeRectangle
operator()(const CesiumGeospatial::Cartographic& cartographic) {
return GlobeRectangle(
cartographic.longitude,
cartographic.latitude,
cartographic.longitude,
cartographic.latitude);
struct GlobeRectangleFromObjectVisitor {
BoundingRegionBuilder& builder;
void operator()(GeoJsonPoint& point) {
builder.expandToIncludePosition(point.coordinates);
}
GlobeRectangle
operator()(const std::vector<CesiumGeospatial::Cartographic>& points) {
BoundingRegionBuilder builder;
for (const Cartographic& point : points) {
void operator()(GeoJsonMultiPoint& points) {
for (const Cartographic& point : points.coordinates) {
builder.expandToIncludePosition(point);
}
return builder.toGlobeRectangle();
}
void operator()(GeoJsonLineString& line) {
for (const Cartographic& point : line.coordinates) {
builder.expandToIncludePosition(point);
}
}
void operator()(GeoJsonMultiLineString& lines) {
for (const std::vector<Cartographic>& line : lines.coordinates) {
for (const Cartographic& point : line) {
builder.expandToIncludePosition(point);
}
}
}
void operator()(GeoJsonPolygon& polygon) {
for (const std::vector<Cartographic>& ring : polygon.coordinates) {
for (const Cartographic& point : ring) {
builder.expandToIncludePosition(point);
}
}
}
void operator()(GeoJsonMultiPolygon& polygons) {
for (const std::vector<std::vector<Cartographic>>& polygon :
polygons.coordinates) {
for (const std::vector<Cartographic>& ring : polygon) {
for (const Cartographic& point : ring) {
builder.expandToIncludePosition(point);
}
}
}
}
void operator()(auto& /*catchAll*/) {}
};
GlobeRectangle
operator()(const CesiumGeospatial::CompositeCartographicPolygon& polygon) {
return polygon.getBoundingRectangle();
struct GeoJsonObjectStyleVisitor {
const std::optional<VectorStyle>& operator()(const auto& object) {
return object.style;
}
};
void addPrimitivesToData(
const VectorNode& vectorNode,
const GeoJsonObject& geoJsonObject,
std::vector<QuadtreePrimitiveData>& data,
BoundingRegionBuilder& documentRegionBuilder,
const VectorStyle& style);
struct GeoJsonChildVisitor {
std::vector<QuadtreePrimitiveData>& data;
BoundingRegionBuilder& documentRegionBuilder;
const VectorStyle& style;
void operator()(const GeoJsonFeature& feature) {
if (feature.geometry) {
const std::optional<VectorStyle>& geometryStyle =
std::visit(GeoJsonObjectStyleVisitor{}, *feature.geometry);
const std::optional<VectorStyle>& featureStyle =
geometryStyle ? geometryStyle : feature.style;
addPrimitivesToData(
geoJsonGeometryObjectToObject(*feature.geometry),
data,
documentRegionBuilder,
featureStyle ? *featureStyle : style);
}
}
void operator()(const GeoJsonFeatureCollection& collection) {
for (const GeoJsonFeature& feature : collection.features) {
if (feature.geometry) {
const std::optional<VectorStyle>& geometryStyle =
std::visit(GeoJsonObjectStyleVisitor{}, *feature.geometry);
const std::optional<VectorStyle>& featureStyle =
geometryStyle ? geometryStyle : feature.style;
const std::optional<VectorStyle>& collectionStyle =
featureStyle ? featureStyle : collection.style;
addPrimitivesToData(
geoJsonGeometryObjectToObject(*feature.geometry),
data,
documentRegionBuilder,
collectionStyle ? *collectionStyle : style);
}
}
}
void operator()(const GeoJsonGeometryCollection& collection) {
for (const GeoJsonGeometryObject& geometry : collection.geometries) {
const std::optional<VectorStyle>& childStyle =
std::visit(GeoJsonObjectStyleVisitor{}, geometry);
const std::optional<VectorStyle>& useStyle =
childStyle ? childStyle : collection.style;
addPrimitivesToData(
geoJsonGeometryObjectToObject(geometry),
data,
documentRegionBuilder,
useStyle ? *useStyle : style);
}
}
void operator()(const auto& /*catchAll*/) {}
};
void addPrimitivesToData(
const GeoJsonObject& geoJsonObject,
std::vector<QuadtreePrimitiveData>& data,
BoundingRegionBuilder& documentRegionBuilder,
const VectorStyle& style) {
for (const VectorPrimitive& primitive : vectorNode.primitives) {
GlobeRectangle rect =
std::visit(GlobeRectangleFromPrimitiveVisitor{}, primitive);
documentRegionBuilder.expandToIncludePosition(rect.getSouthwest());
documentRegionBuilder.expandToIncludePosition(rect.getNortheast());
data.emplace_back(QuadtreePrimitiveData{
&primitive,
&vectorNode,
&style,
std::move(rect)});
}
BoundingRegionBuilder thisBuilder;
std::visit(GlobeRectangleFromObjectVisitor{thisBuilder}, geoJsonObject);
GlobeRectangle rect = thisBuilder.toGlobeRectangle();
data.emplace_back(&geoJsonObject, &style, std::move(rect));
documentRegionBuilder.expandToIncludePosition(rect.getSouthwest());
documentRegionBuilder.expandToIncludePosition(rect.getNortheast());
for (const VectorNode& child : vectorNode.children) {
addPrimitivesToData(
child,
data,
documentRegionBuilder,
child.style ? *child.style : style);
}
std::visit(
GeoJsonChildVisitor{data, documentRegionBuilder, style},
geoJsonObject);
}
const uint32_t DEPTH_LIMIT = 8;
@ -229,11 +301,17 @@ uint32_t buildQuadtreeNode(
}
Quadtree buildQuadtree(
const IntrusivePointer<VectorDocument>& document,
const IntrusivePointer<GeoJsonDocument>& document,
const VectorStyle& defaultStyle) {
BoundingRegionBuilder builder;
std::vector<QuadtreePrimitiveData> data;
addPrimitivesToData(document->getRootNode(), data, builder, defaultStyle);
const std::optional<VectorStyle>& rootObjectStyle =
std::visit(GeoJsonObjectStyleVisitor{}, document->getRootObject());
addPrimitivesToData(
document->getRootObject(),
data,
builder,
rootObjectStyle ? *rootObjectStyle : defaultStyle);
Quadtree tree{
builder.toGlobeRectangle(),
@ -282,7 +360,7 @@ void rasterizeQuadtreeNode(
}
primitivesRendered[dataIdx] = true;
const QuadtreePrimitiveData& data = tree.data[dataIdx];
rasterizer.drawPrimitive(*data.pPrimitive, *data.pStyle);
rasterizer.drawGeoJsonObject(*data.pObject, *data.pStyle);
}
} else {
for (size_t i = 0; i < 2; i++) {
@ -332,7 +410,7 @@ class CESIUMRASTEROVERLAYS_API VectorDocumentRasterOverlayTileProvider final
: public RasterOverlayTileProvider {
private:
IntrusivePointer<VectorDocument> _document;
IntrusivePointer<GeoJsonDocument> _document;
VectorStyle _defaultStyle;
Quadtree _tree;
Ellipsoid _ellipsoid;
@ -348,7 +426,7 @@ public:
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const VectorDocumentRasterOverlayOptions& options,
const CesiumUtility::IntrusivePointer<CesiumVectorData::VectorDocument>&
const CesiumUtility::IntrusivePointer<CesiumVectorData::GeoJsonDocument>&
document)
: RasterOverlayTileProvider(
pOwner,
@ -457,13 +535,35 @@ public:
return;
}
this->recomputeStyles(this->_document->getRootNode());
this->recomputeStyles(this->_document->getRootObject());
}
private:
void recomputeStyles(VectorNode& node) {
node.style = (*this->_styleCallback)(this->_document, &node);
for (VectorNode& child : node.children) {
void recomputeStyles(GeoJsonObject& object) {
struct SetStyleVisitor {
const std::optional<VectorStyle>& style;
void operator()(GeoJsonPoint& o) { o.style = style; }
void operator()(GeoJsonMultiPoint& o) { o.style = style; }
void operator()(GeoJsonLineString& o) { o.style = style; }
void operator()(GeoJsonMultiLineString& o) { o.style = style; }
void operator()(GeoJsonPolygon& o) { o.style = style; }
void operator()(GeoJsonMultiPolygon& o) { o.style = style; }
void operator()(GeoJsonFeature& o) { o.style = style; }
void operator()(GeoJsonFeatureCollection& o) { o.style = style; }
void operator()(GeoJsonGeometryCollection& o) { o.style = style; }
};
const std::optional<VectorStyle>& style = (*this->_styleCallback)(this->_document, &object);
std::visit(SetStyleVisitor{style}, object);
struct RecomputeChildStylesVisitor {
VectorDocumentRasterOverlayTileProvider* pThis;
void operator()(GeoJsonFeature& f) {
if(f.geometry) {
pThis->recomputeStyles();
}
}
};
for (GeoJsonObject& child : node.children) {
this->recomputeStyles(child);
}
}
@ -495,15 +595,15 @@ VectorDocumentRasterOverlay::createTileProvider(
struct DocumentSourceVisitor {
CesiumAsync::AsyncSystem asyncSystem;
std::shared_ptr<CesiumAsync::IAssetAccessor> pAssetAccessor;
CesiumAsync::Future<Result<IntrusivePointer<VectorDocument>>>
operator()(const IntrusivePointer<VectorDocument>& document) {
CesiumAsync::Future<Result<IntrusivePointer<GeoJsonDocument>>>
operator()(const IntrusivePointer<GeoJsonDocument>& document) {
return asyncSystem
.createResolvedFuture<Result<IntrusivePointer<VectorDocument>>>(
.createResolvedFuture<Result<IntrusivePointer<GeoJsonDocument>>>(
Result(document));
}
CesiumAsync::Future<Result<IntrusivePointer<VectorDocument>>>
CesiumAsync::Future<Result<IntrusivePointer<GeoJsonDocument>>>
operator()(const IonVectorDocumentRasterOverlaySource& ion) {
return VectorDocument::fromCesiumIonAsset(
return GeoJsonDocument::fromCesiumIonAsset(
asyncSystem,
pAssetAccessor,
ion.ionAssetID,
@ -522,7 +622,7 @@ VectorDocumentRasterOverlay::createTileProvider(
pPrepareRendererResources,
pLogger,
options = this->_options](
Result<IntrusivePointer<VectorDocument>>&& result)
Result<IntrusivePointer<GeoJsonDocument>>&& result)
-> CreateTileProviderResult {
if (!result.pValue) {
return nonstd::make_unexpected(RasterOverlayLoadFailureDetails{

View File

@ -1,5 +1,6 @@
#pragma once
#include "CesiumVectorData/VectorStyle.h"
#include <CesiumGeospatial/BoundingRegion.h>
#include <CesiumUtility/JsonValue.h>
#include <CesiumVectorData/Library.h>
@ -59,6 +60,11 @@ struct GeoJsonPoint {
*/
CesiumUtility::JsonValue::Object foreignMembers =
CesiumUtility::JsonValue::Object();
/**
* @brief The style for this specific GeoJSON object, if any.
*/
std::optional<VectorStyle> style = std::nullopt;
};
/**
@ -89,6 +95,11 @@ struct GeoJsonMultiPoint {
*/
CesiumUtility::JsonValue::Object foreignMembers =
CesiumUtility::JsonValue::Object();
/**
* @brief The style for this specific GeoJSON object, if any.
*/
std::optional<VectorStyle> style = std::nullopt;
};
/**
@ -120,6 +131,11 @@ struct GeoJsonLineString {
*/
CesiumUtility::JsonValue::Object foreignMembers =
CesiumUtility::JsonValue::Object();
/**
* @brief The style for this specific GeoJSON object, if any.
*/
std::optional<VectorStyle> style = std::nullopt;
};
/**
@ -152,6 +168,11 @@ struct GeoJsonMultiLineString {
*/
CesiumUtility::JsonValue::Object foreignMembers =
CesiumUtility::JsonValue::Object();
/**
* @brief The style for this specific GeoJSON object, if any.
*/
std::optional<VectorStyle> style = std::nullopt;
};
/**
@ -191,6 +212,11 @@ struct GeoJsonPolygon {
*/
CesiumUtility::JsonValue::Object foreignMembers =
CesiumUtility::JsonValue::Object();
/**
* @brief The style for this specific GeoJSON object, if any.
*/
std::optional<VectorStyle> style = std::nullopt;
};
/**
@ -224,6 +250,11 @@ struct GeoJsonMultiPolygon {
*/
CesiumUtility::JsonValue::Object foreignMembers =
CesiumUtility::JsonValue::Object();
/**
* @brief The style for this specific GeoJSON object, if any.
*/
std::optional<VectorStyle> style = std::nullopt;
};
struct GeoJsonGeometryCollection;
@ -270,6 +301,11 @@ struct GeoJsonGeometryCollection {
*/
CesiumUtility::JsonValue::Object foreignMembers =
CesiumUtility::JsonValue::Object();
/**
* @brief The style for this specific GeoJSON object, if any.
*/
std::optional<VectorStyle> style = std::nullopt;
};
/**
@ -312,6 +348,11 @@ struct GeoJsonFeature {
*/
CesiumUtility::JsonValue::Object foreignMembers =
CesiumUtility::JsonValue::Object();
/**
* @brief The style for this specific GeoJSON object, if any.
*/
std::optional<VectorStyle> style = std::nullopt;
};
/**
@ -343,6 +384,11 @@ struct GeoJsonFeatureCollection {
*/
CesiumUtility::JsonValue::Object foreignMembers =
CesiumUtility::JsonValue::Object();
/**
* @brief The style for this specific GeoJSON object, if any.
*/
std::optional<VectorStyle> style = std::nullopt;
};
/**

View File

@ -1,5 +1,6 @@
#pragma once
#include "CesiumGeospatial/Cartographic.h"
#include "Color.h"
#include "VectorStyle.h"
@ -9,6 +10,7 @@
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGltf/ImageAsset.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/ReferenceCounted.h>
#include <CesiumVectorData/GeoJsonObject.h>
#include <blend2d.h>
@ -45,13 +47,15 @@ public:
CesiumGeospatial::Ellipsoid::WGS84);
/**
* @brief Draws a \ref CesiumGeospatial::CartographicPolygon to the canvas.
* @brief Draws a \ref GeoJsonPolygon to the canvas.
*
* @param polygon The polygon to draw.
* @param polygon The polygon to draw. It is assumed to have right-hand
* winding order (exterior rings are counterclockwise, holes are clockwise) as
* is the case in GeoJSON.
* @param style The \ref VectorStyle to use when drawing the polygon.
*/
void drawPolygon(
const CesiumGeospatial::CartographicPolygon& polygon,
const std::vector<std::vector<CesiumGeospatial::Cartographic>>& polygon,
const VectorStyle& style);
/**
@ -65,16 +69,17 @@ public:
const VectorStyle& style);
/**
* @brief Rasterizes the provided `VectorPrimitive` to the canvas.
* @brief Rasterizes the provided `GeoJsonObject` to the canvas.
*
* Polygons are equivalent to calls to `drawPolygon`. Polylines are equivalent
* to calls to `drawPolyline`. Points are currently not drawn.
*
* @param primitive The primitive to draw.
* @param style The \ref VectorStyle to use when drawing the primitive.
* @param geoJsonObject The GeoJSON object to draw.
* @param style The \ref VectorStyle to use when drawing the object.
*/
void
drawGeoJsonObject(const GeoJsonObject& primitive, const VectorStyle& style);
void drawGeoJsonObject(
const GeoJsonObject& geoJsonObject,
const VectorStyle& style);
/**
* @brief Fills the entire canvas with the given color.

View File

@ -1,3 +1,5 @@
#include "CesiumVectorData/GeoJsonObject.h"
#include <CesiumGeospatial/Cartographic.h>
#include <CesiumGeospatial/CartographicPolygon.h>
#include <CesiumGeospatial/CompositeCartographicPolygon.h>
@ -99,49 +101,24 @@ VectorRasterizer::VectorRasterizer(
}
void VectorRasterizer::drawPolygon(
const CartographicPolygon& polygon,
const VectorStyle& style) {
if (_finalized) {
return;
}
std::vector<BLPoint> vertices;
vertices.reserve(polygon.getVertices().size());
for (const glm::dvec2& vertex : polygon.getVertices()) {
vertices.emplace_back(
radiansToPoint(vertex.x, vertex.y, this->_bounds, this->_context));
}
if (style.polygon.fill) {
this->_context.fillPolygon(
vertices.data(),
vertices.size(),
BLRgba32(style.polygon.getColor().toRgba32()));
}
if (style.polygon.outline) {
setStrokeWidth(this->_context, style.line, this->_ellipsoid, this->_bounds);
this->_context.strokePolygon(
vertices.data(),
vertices.size(),
BLRgba32(style.line.getColor().toRgba32()));
}
}
void VectorRasterizer::drawPolygon(
const CompositeCartographicPolygon& polygon,
const std::vector<std::vector<CesiumGeospatial::Cartographic>>& polygon,
const VectorStyle& style) {
if (_finalized || (!style.polygon.fill && !style.polygon.outline)) {
return;
}
std::vector<BLPoint> vertices;
vertices.reserve(polygon.getWoundVertices().size());
vertices.reserve(polygon.size());
for (const glm::dvec2& vertex : polygon.getWoundVertices()) {
vertices.emplace_back(
radiansToPoint(vertex.x, vertex.y, this->_bounds, this->_context));
for (const std::vector<Cartographic>& ring : polygon) {
// GeoJSON polygons have the reverse winding order from blend2D
for (auto it = ring.rbegin(); it != ring.rend(); ++it) {
vertices.emplace_back(radiansToPoint(
it->longitude,
it->latitude,
this->_bounds,
this->_context));
}
}
if (style.polygon.fill) {
@ -198,22 +175,36 @@ void VectorRasterizer::drawPolyline(
BLRgba32(style.line.getColor().toRgba32()));
}
void VectorRasterizer::drawPrimitive(
const VectorPrimitive& primitive,
void VectorRasterizer::drawGeoJsonObject(
const GeoJsonObject& geoJsonObject,
const VectorStyle& style) {
struct PrimitiveDrawVisitor {
VectorRasterizer& rasterizer;
const VectorStyle& style;
void operator()(const Cartographic& /*point*/) {}
void operator()(const std::vector<Cartographic>& points) {
rasterizer.drawPolyline(points, style);
void operator()(const GeoJsonLineString& line) {
rasterizer.drawPolyline(line.coordinates, style);
}
void operator()(const CompositeCartographicPolygon& polygon) {
rasterizer.drawPolygon(polygon, style);
void operator()(const GeoJsonMultiLineString& lines) {
for (const std::vector<Cartographic>& line : lines.coordinates) {
rasterizer.drawPolyline(line, style);
}
}
void operator()(const GeoJsonPolygon& polygon) {
rasterizer.drawPolygon(polygon.coordinates, style);
}
void operator()(const GeoJsonMultiPolygon& polygons) {
for(const std::vector<std::vector<Cartographic>>& polygon : polygons.coordinates) {
rasterizer.drawPolygon(polygon, style);
}
}
void operator()(const GeoJsonPoint& /*catchAll*/) {}
void operator()(const GeoJsonMultiPoint& /*catchAll*/) {}
void operator()(const GeoJsonFeature& /*catchAll*/) {}
void operator()(const GeoJsonFeatureCollection& /*catchAll*/) {}
void operator()(const GeoJsonGeometryCollection& /*catchAll*/) {}
};
std::visit(PrimitiveDrawVisitor{*this, style}, primitive);
std::visit(PrimitiveDrawVisitor{*this, style}, geoJsonObject);
}
void VectorRasterizer::clear(const Color& clearColor) {