Skip to content

Commit a2030d5

Browse files
committed
Fix 2.5D renderer problem when order of walls matters
E.g. when the walls have different styles
1 parent ffc80b1 commit a2030d5

File tree

4 files changed

+108
-12
lines changed

4 files changed

+108
-12
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "order_parts",
3+
"type": "function",
4+
"description": "Orders the parts of a MultiGeometry by a given criteria",
5+
"arguments": [
6+
{"arg":"geom","description":"a multi-type geometry"},
7+
{"arg":"orderby","description":"an expression string defining the order criteria"},
8+
{"arg":"ascending","description":"boolean, True for ascending, False for descending"}
9+
],
10+
"examples": [
11+
{
12+
"expression":"order_parts(geom_from_wkt('MultiPolygon (((1 1, 5 1, 5 5, 1 5, 1 1)),((1 1, 9 1, 9 9, 1 9, 1 1)))'), 'area($geometry)', False)",
13+
"returns":"MultiPolygon (((1 1, 9 1, 9 9, 1 9, 1 1)),((1 1, 5 1, 5 5, 1 5, 1 1)))"
14+
},
15+
{
16+
"expression":"order_parts(geom_from_wkt('LineString(1 2, 3 2, 4 3)'), '1', True)",
17+
"returns":"LineString(1 2, 3 2, 4 3)"
18+
}
19+
]
20+
}

src/core/qgsexpression.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "qgsmultilinestringv2.h"
4747
#include "qgscurvepolygonv2.h"
4848
#include "qgsexpressionprivate.h"
49+
#include "qgsexpressionsorter.h"
4950

5051
#if QT_VERSION < 0x050000
5152
#include <qtextdocument.h>
@@ -2168,6 +2169,70 @@ static QVariant fcnExtrude( const QVariantList& values, const QgsExpressionConte
21682169
return result;
21692170
}
21702171

2172+
static QVariant fcnOrderParts( const QVariantList& values, const QgsExpressionContext* ctx, QgsExpression* parent )
2173+
{
2174+
if ( values.length() < 2 )
2175+
return QVariant();
2176+
2177+
QgsGeometry fGeom = getGeometry( values.at( 0 ), parent );
2178+
2179+
if ( !fGeom.isMultipart() )
2180+
return values.at( 0 );
2181+
2182+
QString expString = getStringValue( values.at( 1 ), parent );
2183+
bool asc = values.value( 2 ).toBool();
2184+
2185+
QgsExpressionContext* unconstedContext;
2186+
QgsFeature f;
2187+
if ( ctx )
2188+
{
2189+
// ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
2190+
// so no reason to worry
2191+
unconstedContext = const_cast<QgsExpressionContext*>( ctx );
2192+
f = ctx->feature();
2193+
}
2194+
else
2195+
{
2196+
// If there's no context provided, create a fake one
2197+
unconstedContext = new QgsExpressionContext();
2198+
}
2199+
2200+
QgsGeometryCollectionV2* collection = dynamic_cast<QgsGeometryCollectionV2*>( fGeom.geometry() );
2201+
Q_ASSERT( collection ); // Should have failed the multipart check above
2202+
2203+
QgsFeatureRequest::OrderBy orderBy;
2204+
orderBy.append( QgsFeatureRequest::OrderByClause( expString, asc ) );
2205+
QgsExpressionSorter sorter( orderBy );
2206+
2207+
QList<QgsFeature> partFeatures;
2208+
for ( int i = 0; i < collection->partCount(); ++i )
2209+
{
2210+
f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
2211+
partFeatures << f;
2212+
}
2213+
2214+
sorter.sortFeatures( partFeatures, unconstedContext );
2215+
2216+
QgsGeometryCollectionV2* orderedGeom = dynamic_cast<QgsGeometryCollectionV2*>( fGeom.geometry()->clone() );
2217+
2218+
Q_ASSERT( orderedGeom );
2219+
2220+
while ( orderedGeom->partCount() )
2221+
orderedGeom->removeGeometry( 0 );
2222+
2223+
Q_FOREACH ( const QgsFeature& feature, partFeatures )
2224+
{
2225+
orderedGeom->addGeometry( feature.constGeometry()->geometry()->clone() );
2226+
}
2227+
2228+
QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
2229+
2230+
if ( !ctx )
2231+
delete unconstedContext;
2232+
2233+
return result;
2234+
}
2235+
21712236
static QVariant fcnClosestPoint( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
21722237
{
21732238
QgsGeometry fromGeom = getGeometry( values.at( 0 ), parent );
@@ -2954,6 +3019,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
29543019
<< new StaticFunction( "geometry", 1, fcnGetGeometry, "GeometryGroup", QString(), true )
29553020
<< new StaticFunction( "transform", 3, fcnTransformGeometry, "GeometryGroup" )
29563021
<< new StaticFunction( "extrude", 3, fcnExtrude, "GeometryGroup", QString() )
3022+
<< new StaticFunction( "order_parts", 3, fcnOrderParts, "GeometryGroup", QString() )
29573023
<< new StaticFunction( "closest_point", 2, fcnClosestPoint, "GeometryGroup" )
29583024
<< new StaticFunction( "shortest_line", 2, fcnShortestLine, "GeometryGroup" )
29593025
<< new StaticFunction( "$rownum", 0, fcnRowNumber, "deprecated" )

src/core/symbology-ng/qgs25drenderer.cpp

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,24 @@
2828
")"
2929

3030
#define WALL_EXPRESSION \
31-
"extrude(" \
32-
" segments_to_lines( exterior_ring( $geometry ) )," \
33-
" cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )," \
34-
" sin( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )" \
31+
"order_parts( "\
32+
" extrude(" \
33+
" segments_to_lines( exterior_ring( $geometry ) )," \
34+
" cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )," \
35+
" sin( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )" \
36+
" )," \
37+
" 'distance( $geometry, translate( @map_extent_center, 1000 * @map_extent_width * cos( radians( @qgis_25d_angle + 180 ) ), 1000 * @map_extent_width * sin( radians( @qgis_25d_angle + 180 ) ) ))'," \
38+
" False" \
39+
")"
40+
41+
#define ORDER_BY_EXPRESSION \
42+
"distance(" \
43+
" $geometry," \
44+
" translate(" \
45+
" @map_extent_center," \
46+
" 1000 * @map_extent_width * cos( radians( @qgis_25d_angle + 180 ) )," \
47+
" 1000 * @map_extent_width * sin( radians( @qgis_25d_angle + 180 ) )" \
48+
" )" \
3549
")"
3650

3751
Qgs25DRenderer::Qgs25DRenderer()
@@ -75,14 +89,7 @@ Qgs25DRenderer::Qgs25DRenderer()
7589

7690
QgsFeatureRequest::OrderBy orderBy;
7791
orderBy << QgsFeatureRequest::OrderByClause(
78-
"distance("
79-
" $geometry,"
80-
" translate("
81-
" @map_extent_center,"
82-
" 1000 * @map_extent_width * cos( radians( @qgis_25d_angle + 180 ) ),"
83-
" 1000 * @map_extent_width * sin( radians( @qgis_25d_angle + 180 ) )"
84-
" )"
85-
")",
92+
ORDER_BY_EXPRESSION,
8693
false );
8794

8895
setOrderBy( orderBy );

tests/src/core/testqgsexpression.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,9 @@ class TestQgsExpression: public QObject
586586
QTest::newRow( "extrude geom" ) << "geom_to_wkt(extrude( geom_from_wkt('LineString( 1 2, 3 2, 4 3)'),1,2))" << false << QVariant( "Polygon ((1 2, 3 2, 4 3, 5 5, 4 4, 2 4, 1 2))" );
587587
QTest::newRow( "extrude not geom" ) << "extrude('g',5,6)" << true << QVariant();
588588
QTest::newRow( "extrude null" ) << "extrude(NULL,5,6)" << false << QVariant();
589+
QTest::newRow( "order parts" ) << "geom_to_wkt(order_parts(geom_from_wkt('MultiPolygon (((1 1, 5 1, 5 5, 1 5, 1 1)),((1 1, 9 1, 9 9, 1 9, 1 1)))'), 'area($geometry)', False ) )" << false << QVariant( "MultiPolygon (((1 1, 9 1, 9 9, 1 9, 1 1)),((1 1, 5 1, 5 5, 1 5, 1 1)))" );
590+
QTest::newRow( "order parts not geom" ) << "order_parts('g', 'area($geometry)', False )" << true << QVariant();
591+
QTest::newRow( "order parts single geom" ) << "geom_to_wkt(order_parts(geom_from_wkt('POLYGON((2 0,2 2, 3 2, 3 0, 2 0))'), 'area($geometry)', False))" << false << QVariant( "Polygon ((2 0, 2 2, 3 2, 3 0, 2 0))" );
589592
QTest::newRow( "closest_point geom" ) << "geom_to_wkt(closest_point( geom_from_wkt('LineString( 1 1, 5 1, 5 5 )'),geom_from_wkt('Point( 6 3 )')))" << false << QVariant( "Point (5 3)" );
590593
QTest::newRow( "closest_point not geom" ) << "closest_point('g','b')" << true << QVariant();
591594
QTest::newRow( "closest_point null" ) << "closest_point(NULL,NULL)" << false << QVariant();

0 commit comments

Comments
 (0)