1 /***************************************************************************
2 qgscircle.cpp
3 --------------
4 begin : March 2017
5 copyright : (C) 2017 by Loîc Bartoletti
6 email : lbartoletti at tuxfamily dot org
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "qgscircle.h"
19 #include "qgslinestring.h"
20 #include "qgsgeometryutils.h"
21 #include "qgstriangle.h"
22
23 #include <memory>
24 #include <utility>
25
QgsCircle()26 QgsCircle::QgsCircle() :
27 QgsEllipse( QgsPoint(), 0.0, 0.0, 0.0 )
28 {
29
30 }
31
QgsCircle(const QgsPoint & center,double radius,double azimuth)32 QgsCircle::QgsCircle( const QgsPoint ¢er, double radius, double azimuth ) :
33 QgsEllipse( center, radius, radius, azimuth )
34 {
35
36 }
37
from2Points(const QgsPoint & pt1,const QgsPoint & pt2)38 QgsCircle QgsCircle::from2Points( const QgsPoint &pt1, const QgsPoint &pt2 )
39 {
40 QgsPoint center = QgsGeometryUtils::midpoint( pt1, pt2 );
41 const double azimuth = QgsGeometryUtils::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() ) * 180.0 / M_PI;
42 const double radius = pt1.distance( pt2 ) / 2.0;
43
44 QgsGeometryUtils::transferFirstZOrMValueToPoint( QgsPointSequence() << pt1 << pt2, center );
45
46 return QgsCircle( center, radius, azimuth );
47 }
48
isPerpendicular(const QgsPoint & pt1,const QgsPoint & pt2,const QgsPoint & pt3,double epsilon)49 static bool isPerpendicular( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon )
50 {
51 // check the given point are perpendicular to x or y axis
52
53 const double yDelta_a = pt2.y() - pt1.y();
54 const double xDelta_a = pt2.x() - pt1.x();
55 const double yDelta_b = pt3.y() - pt2.y();
56 const double xDelta_b = pt3.x() - pt2.x();
57
58 if ( ( std::fabs( xDelta_a ) <= epsilon ) && ( std::fabs( yDelta_b ) <= epsilon ) )
59 {
60 return false;
61 }
62
63 if ( std::fabs( yDelta_a ) <= epsilon )
64 {
65 return true;
66 }
67 else if ( std::fabs( yDelta_b ) <= epsilon )
68 {
69 return true;
70 }
71 else if ( std::fabs( xDelta_a ) <= epsilon )
72 {
73 return true;
74 }
75 else if ( std::fabs( xDelta_b ) <= epsilon )
76 {
77 return true;
78 }
79
80 return false;
81
82 }
83
from3Points(const QgsPoint & pt1,const QgsPoint & pt2,const QgsPoint & pt3,double epsilon)84 QgsCircle QgsCircle::from3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon )
85 {
86 QgsPoint p1, p2, p3;
87
88 if ( !isPerpendicular( pt1, pt2, pt3, epsilon ) )
89 {
90 p1 = pt1;
91 p2 = pt2;
92 p3 = pt3;
93 }
94 else if ( !isPerpendicular( pt1, pt3, pt2, epsilon ) )
95 {
96 p1 = pt1;
97 p2 = pt3;
98 p3 = pt2;
99 }
100 else if ( !isPerpendicular( pt2, pt1, pt3, epsilon ) )
101 {
102 p1 = pt2;
103 p2 = pt1;
104 p3 = pt3;
105 }
106 else if ( !isPerpendicular( pt2, pt3, pt1, epsilon ) )
107 {
108 p1 = pt2;
109 p2 = pt3;
110 p3 = pt1;
111 }
112 else if ( !isPerpendicular( pt3, pt2, pt1, epsilon ) )
113 {
114 p1 = pt3;
115 p2 = pt2;
116 p3 = pt1;
117 }
118 else if ( !isPerpendicular( pt3, pt1, pt2, epsilon ) )
119 {
120 p1 = pt3;
121 p2 = pt1;
122 p3 = pt2;
123 }
124 else
125 {
126 return QgsCircle();
127 }
128 QgsPoint center = QgsPoint();
129 double radius = -0.0;
130 // Paul Bourke's algorithm
131 const double yDelta_a = p2.y() - p1.y();
132 const double xDelta_a = p2.x() - p1.x();
133 const double yDelta_b = p3.y() - p2.y();
134 const double xDelta_b = p3.x() - p2.x();
135
136 if ( qgsDoubleNear( xDelta_a, 0.0, epsilon ) || qgsDoubleNear( xDelta_b, 0.0, epsilon ) )
137 {
138 return QgsCircle();
139 }
140
141 const double aSlope = yDelta_a / xDelta_a;
142 const double bSlope = yDelta_b / xDelta_b;
143
144 // set z and m coordinate for center
145 QgsGeometryUtils::transferFirstZOrMValueToPoint( QgsPointSequence() << p1 << p2 << p3, center );
146
147 if ( ( std::fabs( xDelta_a ) <= epsilon ) && ( std::fabs( yDelta_b ) <= epsilon ) )
148 {
149 center.setX( 0.5 * ( p2.x() + p3.x() ) );
150 center.setY( 0.5 * ( p1.y() + p2.y() ) );
151 radius = center.distance( pt1 );
152
153 return QgsCircle( center, radius );
154 }
155
156 if ( std::fabs( aSlope - bSlope ) <= epsilon )
157 {
158 return QgsCircle();
159 }
160
161 center.setX(
162 ( aSlope * bSlope * ( p1.y() - p3.y() ) +
163 bSlope * ( p1.x() + p2.x() ) -
164 aSlope * ( p2.x() + p3.x() ) ) /
165 ( 2.0 * ( bSlope - aSlope ) )
166 );
167 center.setY(
168 -1.0 * ( center.x() - ( p1.x() + p2.x() ) / 2.0 ) /
169 aSlope + ( p1.y() + p2.y() ) / 2.0
170 );
171
172 radius = center.distance( p1 );
173
174 return QgsCircle( center, radius );
175 }
176
fromCenterDiameter(const QgsPoint & center,double diameter,double azimuth)177 QgsCircle QgsCircle::fromCenterDiameter( const QgsPoint ¢er, double diameter, double azimuth )
178 {
179 return QgsCircle( center, diameter / 2.0, azimuth );
180 }
181
fromCenterPoint(const QgsPoint & center,const QgsPoint & pt1)182 QgsCircle QgsCircle::fromCenterPoint( const QgsPoint ¢er, const QgsPoint &pt1 )
183 {
184 const double azimuth = QgsGeometryUtils::lineAngle( center.x(), center.y(), pt1.x(), pt1.y() ) * 180.0 / M_PI;
185
186 QgsPoint centerPt( center );
187 QgsGeometryUtils::transferFirstZOrMValueToPoint( QgsPointSequence() << center << pt1, centerPt );
188
189 return QgsCircle( centerPt, centerPt.distance( pt1 ), azimuth );
190 }
191
from2ParallelsLine(const QgsPoint & pt1_par1,const QgsPoint & pt2_par1,const QgsPoint & pt1_par2,const QgsPoint & pt2_par2,const QgsPoint & pt1_line1,const QgsPoint & pt2_line1,const QgsPoint & pos,double epsilon)192 static QVector<QgsCircle> from2ParallelsLine( const QgsPoint &pt1_par1, const QgsPoint &pt2_par1, const QgsPoint &pt1_par2, const QgsPoint &pt2_par2, const QgsPoint &pt1_line1, const QgsPoint &pt2_line1, const QgsPoint &pos, double epsilon )
193 {
194 const double radius = QgsGeometryUtils::perpendicularSegment( pt1_par2, pt1_par1, pt2_par1 ).length() / 2.0;
195
196 bool isInter;
197 const QgsPoint ptInter;
198 QVector<QgsCircle> circles;
199
200 QgsPoint ptInter_par1line1, ptInter_par2line1;
201 double angle1, angle2;
202 double x, y;
203 QgsGeometryUtils::angleBisector( pt1_par1.x(), pt1_par1.y(), pt2_par1.x(), pt2_par1.y(), pt1_line1.x(), pt1_line1.y(), pt2_line1.x(), pt2_line1.y(), x, y, angle1 );
204 ptInter_par1line1.setX( x );
205 ptInter_par1line1.setY( y );
206
207 QgsGeometryUtils::angleBisector( pt1_par2.x(), pt1_par2.y(), pt2_par2.x(), pt2_par2.y(), pt1_line1.x(), pt1_line1.y(), pt2_line1.x(), pt2_line1.y(), x, y, angle2 );
208 ptInter_par2line1.setX( x );
209 ptInter_par2line1.setY( y );
210
211 QgsPoint center;
212 QgsGeometryUtils::segmentIntersection( ptInter_par1line1, ptInter_par1line1.project( 1.0, angle1 ), ptInter_par2line1, ptInter_par2line1.project( 1.0, angle2 ), center, isInter, epsilon, true );
213 if ( isInter )
214 {
215 if ( !pos.isEmpty() )
216 {
217 if ( QgsGeometryUtils::leftOfLine( center, pt1_line1, pt2_line1 ) == QgsGeometryUtils::leftOfLine( pos, pt1_line1, pt2_line1 ) )
218 {
219 circles.append( QgsCircle( center, radius ) );
220 }
221 }
222 else
223 {
224 circles.append( QgsCircle( center, radius ) );
225 }
226 }
227
228 QgsGeometryUtils::segmentIntersection( ptInter_par1line1, ptInter_par1line1.project( 1.0, angle1 ), ptInter_par2line1, ptInter_par2line1.project( 1.0, angle2 + 90.0 ), center, isInter, epsilon, true );
229 if ( isInter )
230 {
231 if ( !pos.isEmpty() )
232 {
233 if ( QgsGeometryUtils::leftOfLine( center, pt1_line1, pt2_line1 ) == QgsGeometryUtils::leftOfLine( pos, pt1_line1, pt2_line1 ) )
234 {
235 circles.append( QgsCircle( center, radius ) );
236 }
237 }
238 else
239 {
240 circles.append( QgsCircle( center, radius ) );
241 }
242 }
243
244 QgsGeometryUtils::segmentIntersection( ptInter_par1line1, ptInter_par1line1.project( 1.0, angle1 + 90.0 ), ptInter_par2line1, ptInter_par2line1.project( 1.0, angle2 ), center, isInter, epsilon, true );
245 if ( isInter && !circles.contains( QgsCircle( center, radius ) ) )
246 {
247 if ( !pos.isEmpty() )
248 {
249 if ( QgsGeometryUtils::leftOfLine( center, pt1_line1, pt2_line1 ) == QgsGeometryUtils::leftOfLine( pos, pt1_line1, pt2_line1 ) )
250 {
251 circles.append( QgsCircle( center, radius ) );
252 }
253 }
254 else
255 {
256 circles.append( QgsCircle( center, radius ) );
257 }
258 }
259 QgsGeometryUtils::segmentIntersection( ptInter_par1line1, ptInter_par1line1.project( 1.0, angle1 + 90.0 ), ptInter_par2line1, ptInter_par2line1.project( 1.0, angle2 + 90.0 ), center, isInter, epsilon, true );
260 if ( isInter && !circles.contains( QgsCircle( center, radius ) ) )
261 {
262 if ( !pos.isEmpty() )
263 {
264 if ( QgsGeometryUtils::leftOfLine( center, pt1_line1, pt2_line1 ) == QgsGeometryUtils::leftOfLine( pos, pt1_line1, pt2_line1 ) )
265 {
266 circles.append( QgsCircle( center, radius ) );
267 }
268 }
269 else
270 {
271 circles.append( QgsCircle( center, radius ) );
272 }
273 }
274
275 return circles;
276 }
277
from3TangentsMulti(const QgsPoint & pt1_tg1,const QgsPoint & pt2_tg1,const QgsPoint & pt1_tg2,const QgsPoint & pt2_tg2,const QgsPoint & pt1_tg3,const QgsPoint & pt2_tg3,double epsilon,const QgsPoint & pos)278 QVector<QgsCircle> QgsCircle::from3TangentsMulti( const QgsPoint &pt1_tg1, const QgsPoint &pt2_tg1, const QgsPoint &pt1_tg2, const QgsPoint &pt2_tg2, const QgsPoint &pt1_tg3, const QgsPoint &pt2_tg3, double epsilon, const QgsPoint &pos )
279 {
280 QgsPoint p1, p2, p3;
281 bool isIntersect_tg1tg2 = false;
282 bool isIntersect_tg1tg3 = false;
283 bool isIntersect_tg2tg3 = false;
284 QgsGeometryUtils::segmentIntersection( pt1_tg1, pt2_tg1, pt1_tg2, pt2_tg2, p1, isIntersect_tg1tg2, epsilon );
285 QgsGeometryUtils::segmentIntersection( pt1_tg1, pt2_tg1, pt1_tg3, pt2_tg3, p2, isIntersect_tg1tg3, epsilon );
286 QgsGeometryUtils::segmentIntersection( pt1_tg2, pt2_tg2, pt1_tg3, pt2_tg3, p3, isIntersect_tg2tg3, epsilon );
287
288 QVector<QgsCircle> circles;
289 if ( !isIntersect_tg1tg2 && !isIntersect_tg2tg3 ) // three lines are parallels
290 return circles;
291
292 if ( !isIntersect_tg1tg2 )
293 return from2ParallelsLine( pt1_tg1, pt2_tg1, pt1_tg2, pt2_tg2, pt1_tg3, pt2_tg3, pos, epsilon );
294 else if ( !isIntersect_tg1tg3 )
295 return from2ParallelsLine( pt1_tg1, pt2_tg1, pt1_tg3, pt2_tg3, pt1_tg2, pt2_tg2, pos, epsilon );
296 else if ( !isIntersect_tg2tg3 )
297 return from2ParallelsLine( pt1_tg2, pt2_tg2, pt1_tg3, pt2_tg3, pt1_tg1, pt1_tg1, pos, epsilon );
298
299 if ( p1.is3D() )
300 {
301 p1.convertTo( QgsWkbTypes::dropZ( p1.wkbType() ) );
302 }
303
304 if ( p2.is3D() )
305 {
306 p2.convertTo( QgsWkbTypes::dropZ( p2.wkbType() ) );
307 }
308
309 if ( p3.is3D() )
310 {
311 p3.convertTo( QgsWkbTypes::dropZ( p3.wkbType() ) );
312 }
313
314 circles.append( QgsTriangle( p1, p2, p3 ).inscribedCircle() );
315 return circles;
316 }
317
from3Tangents(const QgsPoint & pt1_tg1,const QgsPoint & pt2_tg1,const QgsPoint & pt1_tg2,const QgsPoint & pt2_tg2,const QgsPoint & pt1_tg3,const QgsPoint & pt2_tg3,double epsilon,const QgsPoint & pos)318 QgsCircle QgsCircle::from3Tangents( const QgsPoint &pt1_tg1, const QgsPoint &pt2_tg1, const QgsPoint &pt1_tg2, const QgsPoint &pt2_tg2, const QgsPoint &pt1_tg3, const QgsPoint &pt2_tg3, double epsilon, const QgsPoint &pos )
319 {
320 const QVector<QgsCircle> circles = from3TangentsMulti( pt1_tg1, pt2_tg1, pt1_tg2, pt2_tg2, pt1_tg3, pt2_tg3, epsilon, pos );
321 if ( circles.length() != 1 )
322 return QgsCircle();
323 return circles.at( 0 );
324 }
325
minimalCircleFrom3Points(const QgsPoint & pt1,const QgsPoint & pt2,const QgsPoint & pt3,double epsilon)326 QgsCircle QgsCircle::minimalCircleFrom3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon )
327 {
328 const double l1 = pt2.distance( pt3 );
329 const double l2 = pt3.distance( pt1 );
330 const double l3 = pt1.distance( pt2 );
331
332 if ( ( l1 * l1 ) - ( l2 * l2 + l3 * l3 ) >= epsilon )
333 return QgsCircle().from2Points( pt2, pt3 );
334 else if ( ( l2 * l2 ) - ( l1 * l1 + l3 * l3 ) >= epsilon )
335 return QgsCircle().from2Points( pt3, pt1 );
336 else if ( ( l3 * l3 ) - ( l1 * l1 + l2 * l2 ) >= epsilon )
337 return QgsCircle().from2Points( pt1, pt2 );
338 else
339 return QgsCircle().from3Points( pt1, pt2, pt3, epsilon );
340 }
341
intersections(const QgsCircle & other,QgsPoint & intersection1,QgsPoint & intersection2,bool useZ) const342 int QgsCircle::intersections( const QgsCircle &other, QgsPoint &intersection1, QgsPoint &intersection2, bool useZ ) const
343 {
344 if ( useZ && mCenter.is3D() && other.center().is3D() && !qgsDoubleNear( mCenter.z(), other.center().z() ) )
345 return 0;
346
347 QgsPointXY int1, int2;
348
349 const int res = QgsGeometryUtils::circleCircleIntersections( QgsPointXY( mCenter ), radius(),
350 QgsPointXY( other.center() ), other.radius(),
351 int1, int2 );
352 if ( res == 0 )
353 return 0;
354
355 intersection1 = QgsPoint( int1 );
356 intersection2 = QgsPoint( int2 );
357 if ( useZ && mCenter.is3D() )
358 {
359 intersection1.addZValue( mCenter.z() );
360 intersection2.addZValue( mCenter.z() );
361 }
362 return res;
363 }
364
tangentToPoint(const QgsPointXY & p,QgsPointXY & pt1,QgsPointXY & pt2) const365 bool QgsCircle::tangentToPoint( const QgsPointXY &p, QgsPointXY &pt1, QgsPointXY &pt2 ) const
366 {
367 return QgsGeometryUtils::tangentPointAndCircle( QgsPointXY( mCenter ), radius(), p, pt1, pt2 );
368 }
369
outerTangents(const QgsCircle & other,QgsPointXY & line1P1,QgsPointXY & line1P2,QgsPointXY & line2P1,QgsPointXY & line2P2) const370 int QgsCircle::outerTangents( const QgsCircle &other, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2 ) const
371 {
372 return QgsGeometryUtils::circleCircleOuterTangents( QgsPointXY( mCenter ), radius(),
373 QgsPointXY( other.center() ), other.radius(), line1P1, line1P2, line2P1, line2P2 );
374 }
375
innerTangents(const QgsCircle & other,QgsPointXY & line1P1,QgsPointXY & line1P2,QgsPointXY & line2P1,QgsPointXY & line2P2) const376 int QgsCircle::innerTangents( const QgsCircle &other, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2 ) const
377 {
378 return QgsGeometryUtils::circleCircleInnerTangents( QgsPointXY( mCenter ), radius(),
379 QgsPointXY( other.center() ), other.radius(), line1P1, line1P2, line2P1, line2P2 );
380 }
381
fromExtent(const QgsPoint & pt1,const QgsPoint & pt2)382 QgsCircle QgsCircle::fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 )
383 {
384 const double delta_x = std::fabs( pt1.x() - pt2.x() );
385 const double delta_y = std::fabs( pt1.x() - pt2.y() );
386 if ( !qgsDoubleNear( delta_x, delta_y ) )
387 {
388 return QgsCircle();
389 }
390
391 QgsPoint center = QgsGeometryUtils::midpoint( pt1, pt2 );
392 QgsGeometryUtils::transferFirstZOrMValueToPoint( QgsPointSequence() << pt1 << pt2, center );
393
394 return QgsCircle( center, delta_x / 2.0, 0 );
395 }
396
area() const397 double QgsCircle::area() const
398 {
399 return M_PI * mSemiMajorAxis * mSemiMajorAxis;
400 }
401
perimeter() const402 double QgsCircle::perimeter() const
403 {
404 return 2.0 * M_PI * mSemiMajorAxis;
405 }
406
setSemiMajorAxis(const double semiMajorAxis)407 void QgsCircle::setSemiMajorAxis( const double semiMajorAxis )
408 {
409 mSemiMajorAxis = std::fabs( semiMajorAxis );
410 mSemiMinorAxis = mSemiMajorAxis;
411 }
412
setSemiMinorAxis(const double semiMinorAxis)413 void QgsCircle::setSemiMinorAxis( const double semiMinorAxis )
414 {
415 mSemiMajorAxis = std::fabs( semiMinorAxis );
416 mSemiMinorAxis = mSemiMajorAxis;
417 }
418
northQuadrant() const419 QVector<QgsPoint> QgsCircle::northQuadrant() const
420 {
421 QVector<QgsPoint> quad;
422 quad.append( QgsPoint( mCenter.x(), mCenter.y() + mSemiMajorAxis, mCenter.z(), mCenter.m() ) );
423 quad.append( QgsPoint( mCenter.x() + mSemiMajorAxis, mCenter.y(), mCenter.z(), mCenter.m() ) );
424 quad.append( QgsPoint( mCenter.x(), mCenter.y() - mSemiMajorAxis, mCenter.z(), mCenter.m() ) );
425 quad.append( QgsPoint( mCenter.x() - mSemiMajorAxis, mCenter.y(), mCenter.z(), mCenter.m() ) );
426
427 return quad;
428 }
429
toCircularString(bool oriented) const430 QgsCircularString *QgsCircle::toCircularString( bool oriented ) const
431 {
432 std::unique_ptr<QgsCircularString> circString( new QgsCircularString() );
433 QgsPointSequence points;
434 QVector<QgsPoint> quad;
435 if ( oriented )
436 {
437 quad = quadrant();
438 }
439 else
440 {
441 quad = northQuadrant();
442 }
443 quad.append( quad.at( 0 ) );
444 for ( QVector<QgsPoint>::const_iterator it = quad.constBegin(); it != quad.constEnd(); ++it )
445 {
446 points.append( *it );
447 }
448 circString->setPoints( points );
449
450 return circString.release();
451 }
452
contains(const QgsPoint & point,double epsilon) const453 bool QgsCircle::contains( const QgsPoint &point, double epsilon ) const
454 {
455 return ( mCenter.distance( point ) <= mSemiMajorAxis + epsilon );
456 }
457
boundingBox() const458 QgsRectangle QgsCircle::boundingBox() const
459 {
460 return QgsRectangle( mCenter.x() - mSemiMajorAxis, mCenter.y() - mSemiMajorAxis, mCenter.x() + mSemiMajorAxis, mCenter.y() + mSemiMajorAxis );
461 }
462
toString(int pointPrecision,int radiusPrecision,int azimuthPrecision) const463 QString QgsCircle::toString( int pointPrecision, int radiusPrecision, int azimuthPrecision ) const
464 {
465 QString rep;
466 if ( isEmpty() )
467 rep = QStringLiteral( "Empty" );
468 else
469 rep = QStringLiteral( "Circle (Center: %1, Radius: %2, Azimuth: %3)" )
470 .arg( mCenter.asWkt( pointPrecision ), 0, 's' )
471 .arg( qgsDoubleToString( mSemiMajorAxis, radiusPrecision ), 0, 'f' )
472 .arg( qgsDoubleToString( mAzimuth, azimuthPrecision ), 0, 'f' );
473
474 return rep;
475
476 }
477
asGml2(QDomDocument & doc,int precision,const QString & ns,const QgsAbstractGeometry::AxisOrder axisOrder) const478 QDomElement QgsCircle::asGml2( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
479 {
480 // Gml2 does not support curve. It will be converted to a linestring via CircularString
481 std::unique_ptr< QgsCircularString > circularString( toCircularString() );
482 const QDomElement gml = circularString->asGml2( doc, precision, ns, axisOrder );
483 return gml;
484 }
485
asGml3(QDomDocument & doc,int precision,const QString & ns,const QgsAbstractGeometry::AxisOrder axisOrder) const486 QDomElement QgsCircle::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
487 {
488 QgsPointSequence pts;
489 pts << northQuadrant().at( 0 ) << northQuadrant().at( 1 ) << northQuadrant().at( 2 );
490
491 QDomElement elemCircle = doc.createElementNS( ns, QStringLiteral( "Circle" ) );
492
493 if ( isEmpty() )
494 return elemCircle;
495
496 elemCircle.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, mCenter.is3D(), axisOrder ) );
497 return elemCircle;
498 }
499