1 /***************************************************************************
2                         qgsmultipoint.cpp
3   -------------------------------------------------------------------
4 Date                 : 29 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 "qgsmultipoint.h"
17 #include "qgsapplication.h"
18 #include "qgsgeometryutils.h"
19 #include "qgspoint.h"
20 #include "qgswkbptr.h"
21 
22 #include <QJsonArray>
23 #include <QJsonObject>
24 #include <QRegularExpression>
25 #include <nlohmann/json.hpp>
26 
QgsMultiPoint()27 QgsMultiPoint::QgsMultiPoint()
28 {
29   mWkbType = QgsWkbTypes::MultiPoint;
30 }
31 
pointN(int index)32 QgsPoint *QgsMultiPoint::pointN( int index )
33 {
34   return qgsgeometry_cast< QgsPoint * >( geometryN( index ) );
35 }
36 
pointN(int index) const37 const QgsPoint *QgsMultiPoint::pointN( int index ) const
38 {
39   return qgsgeometry_cast< const QgsPoint * >( geometryN( index ) );
40 }
41 
geometryType() const42 QString QgsMultiPoint::geometryType() const
43 {
44   return QStringLiteral( "MultiPoint" );
45 }
46 
createEmptyWithSameType() const47 QgsMultiPoint *QgsMultiPoint::createEmptyWithSameType() const
48 {
49   auto result = std::make_unique< QgsMultiPoint >();
50   result->mWkbType = mWkbType;
51   return result.release();
52 }
53 
clone() const54 QgsMultiPoint *QgsMultiPoint::clone() const
55 {
56   return new QgsMultiPoint( *this );
57 }
58 
toCurveType() const59 QgsMultiPoint *QgsMultiPoint::toCurveType() const
60 {
61   return clone();
62 }
63 
fromWkt(const QString & wkt)64 bool QgsMultiPoint::fromWkt( const QString &wkt )
65 {
66   QString collectionWkt( wkt );
67   //test for non-standard MultiPoint(x1 y1, x2 y2) format
68   const thread_local QRegularExpression regex( QStringLiteral( "^\\s*MultiPoint\\s*[ZM]*\\s*\\(\\s*[-\\d]" ), QRegularExpression::CaseInsensitiveOption );
69   const QRegularExpressionMatch match = regex.match( collectionWkt );
70   if ( match.hasMatch() )
71   {
72     //alternate style without extra brackets, upgrade to standard
73     collectionWkt.replace( '(', QLatin1String( "((" ) ).replace( ')', QLatin1String( "))" ) ).replace( ',', QLatin1String( "),(" ) );
74   }
75 
76   return fromCollectionWkt( collectionWkt, QVector<QgsAbstractGeometry *>() << new QgsPoint, QStringLiteral( "Point" ) );
77 }
78 
clear()79 void QgsMultiPoint::clear()
80 {
81   QgsGeometryCollection::clear();
82   mWkbType = QgsWkbTypes::MultiPoint;
83 }
84 
asGml2(QDomDocument & doc,int precision,const QString & ns,const AxisOrder axisOrder) const85 QDomElement QgsMultiPoint::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
86 {
87   QDomElement elemMultiPoint = doc.createElementNS( ns, QStringLiteral( "MultiPoint" ) );
88 
89   if ( isEmpty() )
90     return elemMultiPoint;
91 
92   for ( const QgsAbstractGeometry *geom : mGeometries )
93   {
94     if ( qgsgeometry_cast<const QgsPoint *>( geom ) )
95     {
96       QDomElement elemPointMember = doc.createElementNS( ns, QStringLiteral( "pointMember" ) );
97       elemPointMember.appendChild( geom->asGml2( doc, precision, ns, axisOrder ) );
98       elemMultiPoint.appendChild( elemPointMember );
99     }
100   }
101 
102   return elemMultiPoint;
103 }
104 
asGml3(QDomDocument & doc,int precision,const QString & ns,const QgsAbstractGeometry::AxisOrder axisOrder) const105 QDomElement QgsMultiPoint::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
106 {
107   QDomElement elemMultiPoint = doc.createElementNS( ns, QStringLiteral( "MultiPoint" ) );
108 
109   if ( isEmpty() )
110     return elemMultiPoint;
111 
112   for ( const QgsAbstractGeometry *geom : mGeometries )
113   {
114     if ( qgsgeometry_cast<const QgsPoint *>( geom ) )
115     {
116       QDomElement elemPointMember = doc.createElementNS( ns, QStringLiteral( "pointMember" ) );
117       elemPointMember.appendChild( geom->asGml3( doc, precision, ns, axisOrder ) );
118       elemMultiPoint.appendChild( elemPointMember );
119     }
120   }
121 
122   return elemMultiPoint;
123 }
124 
asJsonObject(int precision) const125 json QgsMultiPoint::asJsonObject( int precision ) const
126 {
127   json j
128   {
129     { "type", "MultiPoint" },
130     { "coordinates", json::array() },
131   };
132   for ( const QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
133   {
134     const QgsPoint *point = static_cast<const QgsPoint *>( geom );
135     if ( point->is3D() )
136       j[ "coordinates" ].push_back( { qgsRound( point->x(), precision ), qgsRound( point->y(), precision ), qgsRound( point->z(), precision ) } );
137     else
138       j[ "coordinates" ].push_back( { qgsRound( point->x(), precision ), qgsRound( point->y(), precision ) } );
139   }
140   return j;
141 }
142 
143 
nCoordinates() const144 int QgsMultiPoint::nCoordinates() const
145 {
146   return mGeometries.size();
147 }
148 
addGeometry(QgsAbstractGeometry * g)149 bool QgsMultiPoint::addGeometry( QgsAbstractGeometry *g )
150 {
151   if ( !qgsgeometry_cast<QgsPoint *>( g ) )
152   {
153     delete g;
154     return false;
155   }
156   if ( mGeometries.empty() )
157   {
158     setZMTypeFromSubGeometry( g, QgsWkbTypes::MultiPoint );
159   }
160   if ( is3D() && !g->is3D() )
161     g->addZValue();
162   else if ( !is3D() && g->is3D() )
163     g->dropZValue();
164   if ( isMeasure() && !g->isMeasure() )
165     g->addMValue();
166   else if ( !isMeasure() && g->isMeasure() )
167     g->dropMValue();
168 
169   return QgsGeometryCollection::addGeometry( g );
170 }
171 
insertGeometry(QgsAbstractGeometry * g,int index)172 bool QgsMultiPoint::insertGeometry( QgsAbstractGeometry *g, int index )
173 {
174   if ( !g || QgsWkbTypes::flatType( g->wkbType() ) != QgsWkbTypes::Point )
175   {
176     delete g;
177     return false;
178   }
179 
180   return QgsGeometryCollection::insertGeometry( g, index );
181 }
182 
boundary() const183 QgsAbstractGeometry *QgsMultiPoint::boundary() const
184 {
185   return nullptr;
186 }
187 
vertexNumberFromVertexId(QgsVertexId id) const188 int QgsMultiPoint::vertexNumberFromVertexId( QgsVertexId id ) const
189 {
190   if ( id.part < 0 || id.part >= mGeometries.count() || id.vertex != 0 || id.ring != 0 )
191     return -1;
192 
193   return id.part; // can shortcut the calculation, since each part will have 1 vertex
194 }
195 
segmentLength(QgsVertexId) const196 double QgsMultiPoint::segmentLength( QgsVertexId ) const
197 {
198   return 0.0;
199 }
200 
isValid(QString &,Qgis::GeometryValidityFlags) const201 bool QgsMultiPoint::isValid( QString &, Qgis::GeometryValidityFlags ) const
202 {
203   return true;
204 }
205 
filterVertices(const std::function<bool (const QgsPoint &)> & filter)206 void QgsMultiPoint::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
207 {
208   mGeometries.erase( std::remove_if( mGeometries.begin(), mGeometries.end(), // clazy:exclude=detaching-member
209                                      [&filter]( const QgsAbstractGeometry * part )
210   {
211     if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( part ) )
212     {
213       if ( !filter( *point ) )
214       {
215         delete point;
216         return true;
217       }
218       else
219       {
220         return false;
221       }
222     }
223     else
224     {
225       delete part;
226       return true;
227     }
228   } ), mGeometries.end() ); // clazy:exclude=detaching-member
229 }
230 
wktOmitChildType() const231 bool QgsMultiPoint::wktOmitChildType() const
232 {
233   return true;
234 }
235