QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
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
21#include <nlohmann/json.hpp>
22
23#include "qgsjsonutils.h"
24#include "qgslogger.h"
25#include "qgsmatrix4x4.h"
26#include "qgsorientedbox3d.h"
27#include "qgssphere.h"
28
29#include <QIODevice>
30#include <QString>
31#include <QtCore/QBuffer>
32
33using namespace Qt::StringLiterals;
34
36{
37 try
38 {
39 // The latitude and longitude values are given in radians!
40 // 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?
41
42 const double west = region[0].get<double>() * 180 / M_PI;
43 const double south = region[1].get<double>() * 180 / M_PI;
44 const double east = region[2].get<double>() * 180 / M_PI;
45 const double north = region[3].get<double>() * 180 / M_PI;
46 double minHeight = region[4].get<double>();
47 double maxHeight = region[5].get<double>();
48
49 return QgsBox3D( west, south, minHeight, east, north, maxHeight );
50 }
51 catch ( nlohmann::json::exception & )
52 {
53 return QgsBox3D();
54 }
55}
56
57QgsBox3D QgsCesiumUtils::parseRegion( const QVariantList &region )
58{
59 if ( region.size() != 6 )
60 return QgsBox3D();
61
63}
64
66{
67 if ( box.size() != 12 )
68 return QgsOrientedBox3D();
69
70 try
71 {
73 for ( int i = 0; i < 3; ++i )
74 {
75 res.mCenter[i] = box[i].get<double>();
76 }
77 for ( int i = 0; i < 9; ++i )
78 {
79 res.mHalfAxes[i] = box[i + 3].get<double>();
80 }
81 return res;
82 }
83 catch ( nlohmann::json::exception & )
84 {
85 return QgsOrientedBox3D();
86 }
87}
88
90{
91 if ( box.size() != 12 )
92 return QgsOrientedBox3D();
93
95}
96
98{
99 if ( sphere.size() != 4 )
100 return QgsSphere();
101
102 try
103 {
104 const double centerX = sphere[0].get<double>();
105 const double centerY = sphere[1].get<double>();
106 const double centerZ = sphere[2].get<double>();
107 const double radius = sphere[3].get<double>();
108 return QgsSphere( centerX, centerY, centerZ, radius );
109 }
110 catch ( nlohmann::json::exception & )
111 {
112 return QgsSphere();
113 }
114}
115
116QgsSphere QgsCesiumUtils::parseSphere( const QVariantList &sphere )
117{
118 if ( sphere.size() != 4 )
119 return QgsSphere();
120
121 return parseSphere( QgsJsonUtils::jsonFromVariant( sphere ) );
122}
123
125{
126 if ( !transform.isIdentity() )
127 {
128 // center is transformed, radius is scaled by maximum scalar from transform
129 // see https://github.com/CesiumGS/cesium-native/blob/fd20f5e272850dde6b58c74059e6de767fe25df6/Cesium3DTilesSelection/src/BoundingVolume.cpp#L33
130 const QgsVector3D center = transform.map( sphere.centerVector() );
131 const double uniformScale = std::max(
132 std::max(
133 std::sqrt( transform.constData()[0] * transform.constData()[0] + transform.constData()[1] * transform.constData()[1] + transform.constData()[2] * transform.constData()[2] ),
134 std::sqrt( transform.constData()[4] * transform.constData()[4] + transform.constData()[5] * transform.constData()[5] + transform.constData()[6] * transform.constData()[6] )
135 ),
136 std::sqrt( transform.constData()[8] * transform.constData()[8] + transform.constData()[9] * transform.constData()[9] + transform.constData()[10] * transform.constData()[10] )
137 );
138
139 return QgsSphere( center.x(), center.y(), center.z(), sphere.radius() * uniformScale );
140 }
141 return sphere;
142}
143
145{
146 struct b3dmHeader
147 {
148 unsigned char magic[4];
149 quint32 version;
150 quint32 byteLength;
151 quint32 featureTableJsonByteLength;
152 quint32 featureTableBinaryByteLength;
153 quint32 batchTableJsonByteLength;
154 quint32 batchTableBinaryByteLength;
155 };
156
158 if ( tileContent.size() < static_cast<int>( sizeof( b3dmHeader ) ) )
159 return res;
160
161 b3dmHeader hdr;
162 memcpy( &hdr, tileContent.constData(), sizeof( b3dmHeader ) );
163
164 const QString featureTableJson( tileContent.mid( sizeof( b3dmHeader ), hdr.featureTableJsonByteLength ) );
165 if ( !featureTableJson.isEmpty() )
166 {
167 try
168 {
169 const json featureTable = json::parse( featureTableJson.toStdString() );
170 if ( featureTable.contains( "RTC_CENTER" ) )
171 {
172 const auto &rtcCenterJson = featureTable["RTC_CENTER"];
173 if ( rtcCenterJson.is_array() && rtcCenterJson.size() == 3 )
174 {
175 res.rtcCenter.setX( rtcCenterJson[0].get<double>() );
176 res.rtcCenter.setY( rtcCenterJson[1].get<double>() );
177 res.rtcCenter.setZ( rtcCenterJson[2].get<double>() );
178 }
179 else
180 {
181 QgsDebugError( u"Invalid RTC_CENTER value"_s );
182 }
183 }
184 }
185 catch ( json::parse_error &ex )
186 {
187 QgsDebugError( u"Error parsing feature table JSON: %1"_s.arg( ex.what() ) );
188 }
189 }
190
191 res.gltf = tileContent.mid( sizeof( b3dmHeader ) + hdr.featureTableJsonByteLength + hdr.featureTableBinaryByteLength + hdr.batchTableJsonByteLength + hdr.batchTableBinaryByteLength );
192 return res;
193}
194
196{
197 TileContents res;
198 if ( tileContent.startsWith( QByteArray( "b3dm" ) ) )
199 {
200 const B3DMContents b3dmContents = QgsCesiumUtils::extractGltfFromB3dm( tileContent );
201 res.gltf = b3dmContents.gltf;
202 res.rtcCenter = b3dmContents.rtcCenter;
203 return res;
204 }
205 else if ( tileContent.startsWith( QByteArray( "glTF" ) ) )
206 {
207 res.gltf = tileContent;
208 return res;
209 }
210 else
211 {
212 // unsupported tile content type
213 // TODO: we could extract "b3dm" data from a composite tile ("cmpt")
214 return res;
215 }
216}
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
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.
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,...
const double * constData() const
Returns pointer to the matrix data (stored in column-major order).
Represents a oriented (rotated) box in 3 dimensions.
A spherical geometry object.
Definition qgssphere.h:46
QgsVector3D centerVector() const
Returns the vector to the center of the sphere.
Definition qgssphere.cpp:47
double radius() const
Returns the radius of the sphere.
Definition qgssphere.h:144
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:60
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:62
void setZ(double z)
Sets Z coordinate.
Definition qgsvector3d.h:80
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58
void setX(double x)
Sets X coordinate.
Definition qgsvector3d.h:68
void setY(double y)
Sets Y coordinate.
Definition qgsvector3d.h:74
#define QgsDebugError(str)
Definition qgslogger.h:59
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
Center position of relative-to-center coordinates (when used).
QByteArray gltf
GLTF binary content.