QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgscesiumutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscesiumutils.cpp
3 --------------------
4 begin : July 2023
5 copyright : (C) 2023 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ******************************************************************
8 ***************************************************************************/
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19#include "qgscesiumutils.h"
20#include "nlohmann/json.hpp"
21#include "qgsjsonutils.h"
22#include "qgsmatrix4x4.h"
23#include "qgssphere.h"
24#include "qgsorientedbox3d.h"
25#include "qgslogger.h"
26
27#include <QtCore/QBuffer>
28#include <QIODevice>
29
31{
32 try
33 {
34 // The latitude and longitude values are given in radians!
35 // TODO -- is this ALWAYS the case? What if there's a region root bounding volume, but a transform object present? What if there's crs metadata specifying a different crs?
36
37 const double west = region[0].get<double>() * 180 / M_PI;
38 const double south = region[1].get<double>() * 180 / M_PI;
39 const double east = region[2].get<double>() * 180 / M_PI;
40 const double north = region[3].get<double>() * 180 / M_PI;
41 double minHeight = region[4].get<double>();
42 double maxHeight = region[5].get<double>();
43
44 return QgsBox3D( west, south, minHeight, east, north, maxHeight );
45 }
46 catch ( nlohmann::json::exception & )
47 {
48 return QgsBox3D();
49 }
50}
51
52QgsBox3D QgsCesiumUtils::parseRegion( const QVariantList &region )
53{
54 if ( region.size() != 6 )
55 return QgsBox3D();
56
58}
59
61{
62 if ( box.size() != 12 )
63 return QgsOrientedBox3D();
64
65 try
66 {
68 for ( int i = 0; i < 3; ++i )
69 {
70 res.mCenter[i] = box[i].get<double>();
71 }
72 for ( int i = 0; i < 9; ++i )
73 {
74 res.mHalfAxes[i] = box[i + 3].get<double>();
75 }
76 return res;
77 }
78 catch ( nlohmann::json::exception & )
79 {
80 return QgsOrientedBox3D();
81 }
82}
83
85{
86 if ( box.size() != 12 )
87 return QgsOrientedBox3D();
88
90}
91
93{
94 if ( sphere.size() != 4 )
95 return QgsSphere();
96
97 try
98 {
99 const double centerX = sphere[0].get<double>();
100 const double centerY = sphere[1].get<double>();
101 const double centerZ = sphere[2].get<double>();
102 const double radius = sphere[3].get<double>();
103 return QgsSphere( centerX, centerY, centerZ, radius );
104 }
105 catch ( nlohmann::json::exception & )
106 {
107 return QgsSphere();
108 }
109}
110
111QgsSphere QgsCesiumUtils::parseSphere( const QVariantList &sphere )
112{
113 if ( sphere.size() != 4 )
114 return QgsSphere();
115
116 return parseSphere( QgsJsonUtils::jsonFromVariant( sphere ) );
117}
118
120{
121 if ( !transform.isIdentity() )
122 {
123 // center is transformed, radius is scaled by maximum scalar from transform
124 // see https://github.com/CesiumGS/cesium-native/blob/fd20f5e272850dde6b58c74059e6de767fe25df6/Cesium3DTilesSelection/src/BoundingVolume.cpp#L33
125 const QgsVector3D center = transform.map( sphere.centerVector() );
126 const double uniformScale = std::max(
127 std::max(
128 std::sqrt(
129 transform.constData()[0] * transform.constData()[0] +
130 transform.constData()[1] * transform.constData()[1] +
131 transform.constData()[2] * transform.constData()[2] ),
132 std::sqrt(
133 transform.constData()[4] * transform.constData()[4] +
134 transform.constData()[5] * transform.constData()[5] +
135 transform.constData()[6] * transform.constData()[6] ) ),
136 std::sqrt(
137 transform.constData()[8] * transform.constData()[8] +
138 transform.constData()[9] * transform.constData()[9] +
139 transform.constData()[10] * transform.constData()[10] ) );
140
141 return QgsSphere( center.x(), center.y(), center.z(), sphere.radius() * uniformScale );
142 }
143 return sphere;
144}
145
147{
148 struct b3dmHeader
149 {
150 unsigned char magic[4];
151 quint32 version;
152 quint32 byteLength;
153 quint32 featureTableJsonByteLength;
154 quint32 featureTableBinaryByteLength;
155 quint32 batchTableJsonByteLength;
156 quint32 batchTableBinaryByteLength;
157 };
158
160 if ( tileContent.size() < static_cast<int>( sizeof( b3dmHeader ) ) )
161 return res;
162
163 b3dmHeader hdr;
164 memcpy( &hdr, tileContent.constData(), sizeof( b3dmHeader ) );
165
166 const QString featureTableJson( tileContent.mid( sizeof( b3dmHeader ), hdr.featureTableJsonByteLength ) );
167 if ( !featureTableJson.isEmpty() )
168 {
169 try
170 {
171 const json featureTable = json::parse( featureTableJson.toStdString() );
172 if ( featureTable.contains( "RTC_CENTER" ) )
173 {
174 const auto &rtcCenterJson = featureTable[ "RTC_CENTER" ];
175 if ( rtcCenterJson.is_array() && rtcCenterJson.size() == 3 )
176 {
177 res.rtcCenter.setX( rtcCenterJson[0].get<double>() );
178 res.rtcCenter.setY( rtcCenterJson[1].get<double>() );
179 res.rtcCenter.setZ( rtcCenterJson[2].get<double>() );
180 }
181 else
182 {
183 QgsDebugError( QStringLiteral( "Invalid RTC_CENTER value" ) );
184 }
185 }
186 }
187 catch ( json::parse_error &ex )
188 {
189 QgsDebugError( QStringLiteral( "Error parsing feature table JSON: %1" ).arg( ex.what() ) );
190 }
191 }
192
193 res.gltf = tileContent.mid( sizeof( b3dmHeader ) +
194 hdr.featureTableJsonByteLength + hdr.featureTableBinaryByteLength +
195 hdr.batchTableJsonByteLength + hdr.batchTableBinaryByteLength );
196 return res;
197}
198
200{
201 TileContents res;
202 if ( tileContent.startsWith( QByteArray( "b3dm" ) ) )
203 {
204 const B3DMContents b3dmContents = QgsCesiumUtils::extractGltfFromB3dm( tileContent );
205 res.gltf = b3dmContents.gltf;
206 res.rtcCenter = b3dmContents.rtcCenter;
207 return res;
208 }
209 else if ( tileContent.startsWith( QByteArray( "glTF" ) ) )
210 {
211 res.gltf = tileContent;
212 return res;
213 }
214 else
215 {
216 // unsupported tile content type
217 // TODO: we could extract "b3dm" data from a composite tile ("cmpt")
218 return res;
219 }
220}
A 3-dimensional box composed of x, y, z coordinates.
Definition: qgsbox3d.h:43
static QgsSphere parseSphere(const json &sphere)
Parses a sphere object from a Cesium JSON document.
static B3DMContents extractGltfFromB3dm(const QByteArray &tileContent)
Extracts GLTF binary data and other contents from the legacy b3dm (Batched 3D Model) tile format.
static QgsOrientedBox3D parseBox(const json &box)
Parses a box object from a Cesium JSON document to an oriented bounding box.
static QgsBox3D parseRegion(const json &region)
Parses a region object from a Cesium JSON object to a 3D box.
static QgsSphere transformSphere(const QgsSphere &sphere, const QgsMatrix4x4 &transform)
Applies a transform to a sphere.
static TileContents extractGltfFromTileContent(const QByteArray &tileContent)
Parses tile content.
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
A simple 4x4 matrix implementation useful for transformation in 3D space.
Definition: qgsmatrix4x4.h:40
bool isIdentity() const
Returns whether this matrix is an identity matrix.
QgsVector3D map(const QgsVector3D &vector) const
Matrix-vector multiplication (vector is converted to homogeneous coordinates [X,Y,...
Definition: qgsmatrix4x4.h:80
const double * constData() const
Returns pointer to the matrix data (stored in column-major order)
Definition: qgsmatrix4x4.h:68
Represents a oriented (rotated) box in 3 dimensions.
A spherical geometry object.
Definition: qgssphere.h:41
QgsVector3D centerVector() const
Returns the vector to the center of the sphere.
Definition: qgssphere.cpp:48
double radius() const
Returns the radius of the sphere.
Definition: qgssphere.h:139
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition: qgsvector3d.h:31
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:50
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:52
void setZ(double z)
Sets Z coordinate.
Definition: qgsvector3d.h:70
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:48
void setX(double x)
Sets X coordinate.
Definition: qgsvector3d.h:58
void setY(double y)
Sets Y coordinate.
Definition: qgsvector3d.h:64
#define QgsDebugError(str)
Definition: qgslogger.h:38
QgsSQLStatement::Node * parse(const QString &str, QString &parserErrorMsg, bool allowFragments)
Encapsulates the contents of a B3DM file.
QByteArray gltf
GLTF binary content.
QgsVector3D rtcCenter
Optional RTC center.
Encapsulates the contents of a 3D tile.
QgsVector3D rtcCenter
Optional RTC center.
QByteArray gltf
GLTF binary content.