QGIS API Documentation  3.6.0-Noosa (5873452)
qgsline3dsymbol_p.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsline3dsymbol_p.cpp
3  --------------------------------------
4  Date : July 2017
5  Copyright : (C) 2017 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsline3dsymbol_p.h"
17 
18 #include "qgsline3dsymbol.h"
20 #include "qgs3dmapsettings.h"
21 //#include "qgsterraingenerator.h"
22 #include "qgs3dutils.h"
23 
24 #include "qgsvectorlayer.h"
25 #include "qgsmultilinestring.h"
26 #include "qgsmultipolygon.h"
27 #include "qgsgeos.h"
28 
29 #include <Qt3DExtras/QPhongMaterial>
30 #include <Qt3DRender/QAttribute>
31 #include <Qt3DRender/QBuffer>
32 #include <Qt3DRender/QGeometryRenderer>
33 
35 
36 
37 static Qt3DExtras::QPhongMaterial *_material( const QgsLine3DSymbol &symbol )
38 {
39  Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
40 
41  material->setAmbient( symbol.material().ambient() );
42  material->setDiffuse( symbol.material().diffuse() );
43  material->setSpecular( symbol.material().specular() );
44  material->setShininess( symbol.material().shininess() );
45 
46  return material;
47 }
48 
49 
50 // -----------
51 
52 
53 class QgsBufferedLine3DSymbolHandler : public QgsFeature3DHandler
54 {
55  public:
56  QgsBufferedLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
57  : mSymbol( symbol ), mSelectedIds( selectedIds ) {}
58 
59  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
60  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
61  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
62 
63  private:
64 
66  struct LineData
67  {
68  QList<QgsPolygon *> polygons;
69  QList<QgsFeatureId> fids;
70  };
71 
72  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected );
73 
74  // input specific for this class
75  const QgsLine3DSymbol &mSymbol;
76  // inputs - generic
77  QgsFeatureIds mSelectedIds;
78 
79  // outputs
80  LineData outNormal;
81  LineData outSelected;
82 };
83 
84 
85 
86 bool QgsBufferedLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
87 {
88  Q_UNUSED( context );
89  Q_UNUSED( attributeNames );
90  return true;
91 }
92 
93 void QgsBufferedLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
94 {
95  if ( f.geometry().isNull() )
96  return;
97 
98  LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
99 
100  QgsGeometry geom = f.geometry();
101 
102  // segmentize curved geometries if necessary
103  if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
104  geom = QgsGeometry( geom.constGet()->segmentize() );
105 
106  const QgsAbstractGeometry *g = geom.constGet();
107 
108  // TODO: configurable
109  const int nSegments = 4;
112  const double mitreLimit = 0;
113 
114  QgsGeos engine( g );
115  QgsAbstractGeometry *buffered = engine.buffer( mSymbol.width() / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory
116 
117  if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::Polygon )
118  {
119  QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered );
120  Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() );
121  out.polygons.append( polyBuffered );
122  out.fids.append( f.id() );
123  }
124  else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon )
125  {
126  QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered );
127  for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i )
128  {
129  QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i );
130  Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon );
131  QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts
132  Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() );
133  out.polygons.append( polyBuffered );
134  out.fids.append( f.id() );
135  }
136  delete buffered;
137  }
138 }
139 
140 
141 void QgsBufferedLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
142 {
143  // create entity for selected and not selected
144  makeEntity( parent, context, outNormal, false );
145  makeEntity( parent, context, outSelected, true );
146 }
147 
148 
149 void QgsBufferedLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected )
150 {
151  if ( out.polygons.isEmpty() )
152  return; // nothing to show - no need to create the entity
153 
154  Qt3DExtras::QPhongMaterial *mat = _material( mSymbol );
155  if ( selected )
156  {
157  // update the material with selection colors
158  mat->setDiffuse( context.map().selectionColor() );
159  mat->setAmbient( context.map().selectionColor().darker() );
160  }
161 
162  QgsPointXY origin( context.map().origin().x(), context.map().origin().y() );
164  geometry->setPolygons( out.polygons, out.fids, origin, mSymbol.extrusionHeight() );
165 
166  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
167  renderer->setGeometry( geometry );
168 
169  // make entity
170  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
171  entity->addComponent( renderer );
172  entity->addComponent( mat );
173  entity->setParent( parent );
174 
175  if ( !selected )
176  entity->findChild<Qt3DRender::QGeometryRenderer *>()->setObjectName( QStringLiteral( "main" ) ); // temporary measure to distinguish between "selected" and "main"
177 }
178 
179 
180 // --------------
181 
182 
183 class QgsSimpleLine3DSymbolHandler : public QgsFeature3DHandler
184 {
185  public:
186  QgsSimpleLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
187  : mSymbol( symbol ), mSelectedIds( selectedIds )
188  {
189  // the first index is invalid, we use it for primitive restart
190  outNormal.vertices << QVector3D();
191  outSelected.vertices << QVector3D();
192  }
193 
194  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
195  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
196  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
197 
198  private:
199 
201  struct LineData
202  {
203  QVector<QVector3D> vertices;
204  QVector<unsigned int> indexes;
205  };
206 
207  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected );
208  Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
209 
210  // input specific for this class
211  const QgsLine3DSymbol &mSymbol;
212  // inputs - generic
213  QgsFeatureIds mSelectedIds;
214 
215  // outputs
216  LineData outNormal;
217  LineData outSelected;
218 };
219 
220 
221 
222 bool QgsSimpleLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
223 {
224  Q_UNUSED( context );
225  Q_UNUSED( attributeNames );
226  return true;
227 }
228 
229 void QgsSimpleLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
230 {
231  if ( f.geometry().isNull() )
232  return;
233 
234  LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
235 
236  QgsPoint centroid;
237  if ( mSymbol.altitudeBinding() == Qgs3DTypes::AltBindCentroid )
238  centroid = QgsPoint( f.geometry().centroid().asPoint() );
239 
240  QgsGeometry geom = f.geometry();
241  const QgsAbstractGeometry *g = geom.constGet();
242  if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
243  {
244  for ( int i = 0; i < ls->vertexCount(); ++i )
245  {
246  QgsPoint p = ls->pointN( i );
247  float z = Qgs3DUtils::clampAltitude( p, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), centroid, context.map() );
248  out.vertices << QVector3D( p.x() - context.map().origin().x(), z, -( p.y() - context.map().origin().y() ) );
249  out.indexes << out.vertices.count() - 1;
250  }
251  }
252  else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
253  {
254  for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
255  {
256  const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
257  for ( int i = 0; i < ls->vertexCount(); ++i )
258  {
259  QgsPoint p = ls->pointN( i );
260  float z = Qgs3DUtils::clampAltitude( p, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), centroid, context.map() );
261  out.vertices << QVector3D( p.x() - context.map().origin().x(), z, -( p.y() - context.map().origin().y() ) );
262  out.indexes << out.vertices.count() - 1;
263  }
264  out.indexes << 0; // add primitive restart
265  }
266  }
267 
268  out.indexes << 0; // add primitive restart
269 }
270 
271 void QgsSimpleLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
272 {
273  // create entity for selected and not selected
274  makeEntity( parent, context, outNormal, false );
275  makeEntity( parent, context, outSelected, true );
276 }
277 
278 
279 void QgsSimpleLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected )
280 {
281  if ( out.indexes.isEmpty() )
282  return;
283 
284  // material (only ambient color is used for the color)
285 
286  Qt3DExtras::QPhongMaterial *mat = _material( mSymbol );
287  if ( selected )
288  {
289  // update the material with selection colors
290  mat->setAmbient( context.map().selectionColor() );
291  }
292 
293  // geometry renderer
294 
295  QByteArray vertexBufferData;
296  vertexBufferData.resize( out.vertices.size() * 3 * sizeof( float ) );
297  float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
298  int idx = 0;
299  for ( const auto &v : qgis::as_const( out.vertices ) )
300  {
301  rawVertexArray[idx++] = v.x();
302  rawVertexArray[idx++] = v.y();
303  rawVertexArray[idx++] = v.z();
304  }
305 
306  QByteArray indexBufferData;
307  indexBufferData.resize( out.indexes.size() * sizeof( int ) );
308  unsigned int *rawIndexArray = reinterpret_cast<unsigned int *>( indexBufferData.data() );
309  idx = 0;
310  for ( unsigned int indexVal : qgis::as_const( out.indexes ) )
311  {
312  rawIndexArray[idx++] = indexVal;
313  }
314 
315  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
316 
317  Qt3DRender::QBuffer *vertexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, entity );
318  vertexBuffer->setData( vertexBufferData );
319 
320  Qt3DRender::QBuffer *indexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::IndexBuffer, entity );
321  indexBuffer->setData( indexBufferData );
322 
323  Qt3DRender::QAttribute *positionAttribute = new Qt3DRender::QAttribute( entity );
324  positionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
325  positionAttribute->setBuffer( vertexBuffer );
326  positionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
327  positionAttribute->setVertexSize( 3 );
328  positionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );
329 
330  Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute( entity );
331  indexAttribute->setAttributeType( Qt3DRender::QAttribute::IndexAttribute );
332  indexAttribute->setBuffer( indexBuffer );
333  indexAttribute->setVertexBaseType( Qt3DRender::QAttribute::UnsignedInt );
334 
335  Qt3DRender::QGeometry *geom = new Qt3DRender::QGeometry;
336  geom->addAttribute( positionAttribute );
337  geom->addAttribute( indexAttribute );
338 
339  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
340  renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStrip );
341  renderer->setGeometry( geom );
342  renderer->setVertexCount( out.vertices.count() );
343  renderer->setPrimitiveRestartEnabled( true );
344  renderer->setRestartIndexValue( 0 );
345 
346  // make entity
347  entity->addComponent( renderer );
348  entity->addComponent( mat );
349  entity->setParent( parent );
350 }
351 
352 
353 // --------------
354 
355 
356 namespace Qgs3DSymbolImpl
357 {
358 
359  QgsFeature3DHandler *handlerForLine3DSymbol( QgsVectorLayer *layer, const QgsLine3DSymbol &symbol )
360  {
361  if ( symbol.renderAsSimpleLines() )
362  return new QgsSimpleLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
363  else
364  return new QgsBufferedLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
365  }
366 
367  Qt3DCore::QEntity *entityForLine3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsLine3DSymbol &symbol )
368  {
369  QgsFeature3DHandler *handler = handlerForLine3DSymbol( layer, symbol );
370  Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
371  delete handler;
372  return e;
373  }
374 }
375 
QgsFeatureId id
Definition: qgsfeature.h:64
float shininess() const
Returns shininess of the surface.
double y
Definition: qgspoint.h:42
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QColor specular() const
Returns specular color component.
A class to represent a 2D point.
Definition: qgspointxy.h:43
Multi line string geometry collection.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:106
QgsGeometry centroid() const
Returns the center of mass of a geometry.
void setPolygons(const QList< QgsPolygon *> &polygons, const QList< QgsFeatureId > &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList< float > &extrusionHeightPerPolygon=QList< float >())
Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
EndCapStyle
End cap styles for buffers.
Definition: qgsgeometry.h:1036
3 Definition of the world
const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
Definition: qgscurve.cpp:151
3 Class derived from Qt3DRender::QGeometry that represents polygons tessellated into 3D geometry...
QgsPhongMaterialSettings material() const
Returns material used for shading of the symbol.
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
static float clampAltitude(const QgsPoint &p, Qgs3DTypes::AltitudeClamping altClamp, Qgs3DTypes::AltitudeBinding altBind, float height, const QgsPoint &centroid, const Qgs3DMapSettings &map)
Clamps altitude of a vertex according to the settings, returns Z value.
Definition: qgs3dutils.cpp:159
3 3D symbol that draws linestring geometries as planar polygons (created from lines using a buffer wi...
Abstract base class for all geometries.
Does vector analysis using the geos library and handles import, export, exception handling*...
Definition: qgsgeos.h:103
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
int numGeometries() const
Returns the number of geometries within the collection.
Use rounded joins.
Definition: qgsgeometry.h:1047
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool renderAsSimpleLines() const
Returns whether the renderer will render data with simple lines (otherwise it uses buffer) ...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
Multi polygon geometry collection.
static bool isCurvedType(Type type)
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:608
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
QColor ambient() const
Returns ambient color component.
static void clampAltitudes(QgsLineString *lineString, Qgs3DTypes::AltitudeClamping altClamp, Qgs3DTypes::AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map)
Clamps altitude of vertices of a linestring according to the settings.
Definition: qgs3dutils.cpp:174
QColor diffuse() const
Returns diffuse color component.
QgsGeometry geometry
Definition: qgsfeature.h:67
Polygon geometry type.
Definition: qgspolygon.h:31
JoinStyle
Join styles for buffers.
Definition: qgsgeometry.h:1045
Represents a vector layer which manages a vector based data sets.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:429
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
Clamp just centroid of feature.
Definition: qgs3dtypes.h:45
double x
Definition: qgspoint.h:41