1 /***************************************************************************
2                         qgsmulticurve.cpp
3   -------------------------------------------------------------------
4 Date                 : 28 Oct 2014
5 Copyright            : (C) 2014 by Marco Hugentobler
6 email                : marco.hugentobler at sourcepole 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 "qgsmulticurve.h"
17 #include "qgsapplication.h"
18 #include "qgscurve.h"
19 #include "qgscircularstring.h"
20 #include "qgscompoundcurve.h"
21 #include "qgsgeometryutils.h"
22 #include "qgslinestring.h"
23 #include "qgsmultipoint.h"
24 
25 #include <QJsonObject>
26 #include <memory>
27 #include <nlohmann/json.hpp>
28 
QgsMultiCurve()29 QgsMultiCurve::QgsMultiCurve()
30 {
31   mWkbType = QgsWkbTypes::MultiCurve;
32 }
33 
curveN(int index)34 QgsCurve *QgsMultiCurve::curveN( int index )
35 {
36   return qgsgeometry_cast< QgsCurve * >( geometryN( index ) );
37 }
38 
curveN(int index) const39 const QgsCurve *QgsMultiCurve::curveN( int index ) const
40 {
41   return qgsgeometry_cast< const QgsCurve * >( geometryN( index ) );
42 }
43 
geometryType() const44 QString QgsMultiCurve::geometryType() const
45 {
46   return QStringLiteral( "MultiCurve" );
47 }
48 
createEmptyWithSameType() const49 QgsMultiCurve *QgsMultiCurve::createEmptyWithSameType() const
50 {
51   auto result = std::make_unique< QgsMultiCurve >();
52   result->mWkbType = mWkbType;
53   return result.release();
54 }
55 
clone() const56 QgsMultiCurve *QgsMultiCurve::clone() const
57 {
58   return new QgsMultiCurve( *this );
59 }
60 
clear()61 void QgsMultiCurve::clear()
62 {
63   QgsGeometryCollection::clear();
64   mWkbType = QgsWkbTypes::MultiCurve;
65 }
66 
toCurveType() const67 QgsMultiCurve *QgsMultiCurve::toCurveType() const
68 {
69   return clone();
70 }
71 
fromWkt(const QString & wkt)72 bool QgsMultiCurve::fromWkt( const QString &wkt )
73 {
74   return fromCollectionWkt( wkt,
75                             QVector<QgsAbstractGeometry *>() << new QgsLineString << new QgsCircularString << new QgsCompoundCurve,
76                             QStringLiteral( "LineString" ) );
77 }
78 
asGml2(QDomDocument & doc,int precision,const QString & ns,const AxisOrder axisOrder) const79 QDomElement QgsMultiCurve::asGml2( QDomDocument &doc, int precision, const QString &ns, const  AxisOrder axisOrder ) const
80 {
81   // GML2 does not support curves
82   QDomElement elemMultiLineString = doc.createElementNS( ns, QStringLiteral( "MultiLineString" ) );
83 
84   if ( isEmpty() )
85     return elemMultiLineString;
86 
87   for ( const QgsAbstractGeometry *geom : mGeometries )
88   {
89     if ( qgsgeometry_cast<const QgsCurve *>( geom ) )
90     {
91       std::unique_ptr< QgsLineString > lineString( static_cast<const QgsCurve *>( geom )->curveToLine() );
92 
93       QDomElement elemLineStringMember = doc.createElementNS( ns, QStringLiteral( "lineStringMember" ) );
94       elemLineStringMember.appendChild( lineString->asGml2( doc, precision, ns, axisOrder ) );
95       elemMultiLineString.appendChild( elemLineStringMember );
96     }
97   }
98 
99   return elemMultiLineString;
100 }
101 
asGml3(QDomDocument & doc,int precision,const QString & ns,const AxisOrder axisOrder) const102 QDomElement QgsMultiCurve::asGml3( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
103 {
104   QDomElement elemMultiCurve = doc.createElementNS( ns, QStringLiteral( "MultiCurve" ) );
105 
106   if ( isEmpty() )
107     return elemMultiCurve;
108 
109   for ( const QgsAbstractGeometry *geom : mGeometries )
110   {
111     if ( qgsgeometry_cast<const QgsCurve *>( geom ) )
112     {
113       const QgsCurve *curve = static_cast<const QgsCurve *>( geom );
114 
115       QDomElement elemCurveMember = doc.createElementNS( ns, QStringLiteral( "curveMember" ) );
116       elemCurveMember.appendChild( curve->asGml3( doc, precision, ns, axisOrder ) );
117       elemMultiCurve.appendChild( elemCurveMember );
118     }
119   }
120 
121   return elemMultiCurve;
122 }
123 
asJsonObject(int precision) const124 json QgsMultiCurve::asJsonObject( int precision ) const
125 {
126   json coordinates( json::array( ) );
127   for ( const QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
128   {
129     if ( qgsgeometry_cast<const QgsCurve *>( geom ) )
130     {
131       std::unique_ptr< QgsLineString > lineString( static_cast<const QgsCurve *>( geom )->curveToLine() );
132       QgsPointSequence pts;
133       lineString->points( pts );
134       coordinates.push_back( QgsGeometryUtils::pointsToJson( pts, precision ) );
135     }
136   }
137   return
138   {
139     {  "type",  "MultiLineString"  },
140     {  "coordinates", coordinates }
141   };
142 }
143 
addGeometry(QgsAbstractGeometry * g)144 bool QgsMultiCurve::addGeometry( QgsAbstractGeometry *g )
145 {
146   if ( !qgsgeometry_cast<QgsCurve *>( g ) )
147   {
148     delete g;
149     return false;
150   }
151 
152   if ( mGeometries.empty() )
153   {
154     setZMTypeFromSubGeometry( g, QgsWkbTypes::MultiCurve );
155   }
156   if ( is3D() && !g->is3D() )
157     g->addZValue();
158   else if ( !is3D() && g->is3D() )
159     g->dropZValue();
160   if ( isMeasure() && !g->isMeasure() )
161     g->addMValue();
162   else if ( !isMeasure() && g->isMeasure() )
163     g->dropMValue();
164 
165   return QgsGeometryCollection::addGeometry( g );
166 }
167 
insertGeometry(QgsAbstractGeometry * g,int index)168 bool QgsMultiCurve::insertGeometry( QgsAbstractGeometry *g, int index )
169 {
170   if ( !g || !qgsgeometry_cast<QgsCurve *>( g ) )
171   {
172     delete g;
173     return false;
174   }
175 
176   return QgsGeometryCollection::insertGeometry( g, index );
177 }
178 
reversed() const179 QgsMultiCurve *QgsMultiCurve::reversed() const
180 {
181   QgsMultiCurve *reversedMultiCurve = new QgsMultiCurve();
182   reversedMultiCurve->reserve( mGeometries.size() );
183   for ( const QgsAbstractGeometry *geom : mGeometries )
184   {
185     if ( qgsgeometry_cast<const QgsCurve *>( geom ) )
186     {
187       reversedMultiCurve->addGeometry( static_cast<const QgsCurve *>( geom )->reversed() );
188     }
189   }
190   return reversedMultiCurve;
191 }
192 
boundary() const193 QgsAbstractGeometry *QgsMultiCurve::boundary() const
194 {
195   std::unique_ptr< QgsMultiPoint > multiPoint( new QgsMultiPoint() );
196   multiPoint->reserve( mGeometries.size() * 2 );
197   for ( int i = 0; i < mGeometries.size(); ++i )
198   {
199     if ( QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( mGeometries.at( i ) ) )
200     {
201       if ( !curve->isClosed() )
202       {
203         multiPoint->addGeometry( new QgsPoint( curve->startPoint() ) );
204         multiPoint->addGeometry( new QgsPoint( curve->endPoint() ) );
205       }
206     }
207   }
208   if ( multiPoint->numGeometries() == 0 )
209   {
210     return nullptr;
211   }
212   return multiPoint.release();
213 }
214