QGIS API Documentation  2.14.0-Essen
qgslinevectorlayerdirector.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2010 by Sergey Yakushev *
3  * yakushevs <at> list.ru *
4  * *
5  * *
6  * This program is free software; you can redistribute it and/or modify *
7  * it under the terms of the GNU General Public License as published by *
8  * the Free Software Foundation; either version 2 of the License, or *
9  * (at your option) any later version. *
10  ***************************************************************************/
11 
18 #include "qgsgraphbuilderintr.h"
19 
20 // Qgis includes
21 #include <qgsvectorlayer.h>
22 #include <qgsvectordataprovider.h>
23 #include <qgspoint.h>
24 #include <qgsgeometry.h>
25 #include <qgsdistancearea.h>
26 
27 // QT includes
28 #include <QString>
29 #include <QtAlgorithms>
30 
32 {
33  public:
34  explicit QgsPointCompare( double tolerance )
35  : mTolerance( tolerance )
36  { }
37 
38  bool operator()( const QgsPoint& p1, const QgsPoint& p2 ) const
39  {
40  if ( mTolerance <= 0 )
41  return p1.x() == p2.x() ? p1.y() < p2.y() : p1.x() < p2.x();
42 
43  double tx1 = ceil( p1.x() / mTolerance );
44  double tx2 = ceil( p2.x() / mTolerance );
45  if ( tx1 == tx2 )
46  return ceil( p1.y() / mTolerance ) < ceil( p2.y() / mTolerance );
47  return tx1 < tx2;
48  }
49 
50  private:
51  double mTolerance;
52 };
53 
54 template <typename RandIter, typename Type, typename CompareOp > RandIter my_binary_search( RandIter begin, RandIter end, Type val, CompareOp comp )
55 {
56  // result if not found
57  RandIter not_found = end;
58 
59  while ( true )
60  {
61  RandIter avg = begin + ( end - begin ) / 2;
62  if ( begin == avg || end == avg )
63  {
64  if ( !comp( *begin, val ) && !comp( val, *begin ) )
65  return begin;
66  if ( !comp( *end, val ) && !comp( val, *end ) )
67  return end;
68 
69  return not_found;
70  }
71  if ( comp( val, *avg ) )
72  end = avg;
73  else if ( comp( *avg, val ) )
74  begin = avg;
75  else
76  return avg;
77  }
78 
79  return not_found;
80 }
81 
83 {
85  double mLength;
88 };
89 
90 bool TiePointInfoCompare( const TiePointInfo& a, const TiePointInfo& b )
91 {
92  if ( a.mFirstPoint == b.mFirstPoint )
93  return a.mLastPoint.x() == b.mLastPoint.x() ? a.mLastPoint.y() < b.mLastPoint.y() : a.mLastPoint.x() < b.mLastPoint.x();
94 
95  return a.mFirstPoint.x() == b.mFirstPoint.x() ? a.mFirstPoint.y() < b.mFirstPoint.y() : a.mFirstPoint.x() < b.mFirstPoint.x();
96 }
97 
99  int directionFieldId,
100  const QString& directDirectionValue,
101  const QString& reverseDirectionValue,
102  const QString& bothDirectionValue,
103  int defaultDirection
104  )
105 {
106  mVectorLayer = myLayer;
107  mDirectionFieldId = directionFieldId;
108  mDirectDirectionValue = directDirectionValue;
109  mReverseDirectionValue = reverseDirectionValue;
110  mDefaultDirection = defaultDirection;
111  mBothDirectionValue = bothDirectionValue;
112 }
113 
115 {
116 
117 }
118 
120 {
121  return QString( "Vector line" );
122 }
123 
125  QVector< QgsPoint >& tiedPoint ) const
126 {
127  QgsVectorLayer *vl = mVectorLayer;
128 
129  if ( !vl )
130  return;
131 
132  int featureCount = ( int ) vl->featureCount() * 2;
133  int step = 0;
134 
136  ct.setSourceCrs( vl->crs() );
137  if ( builder->coordinateTransformationEnabled() )
138  {
139  ct.setDestCRS( builder->destinationCrs() );
140  }
141  else
142  {
143  ct.setDestCRS( vl->crs() );
144  }
145 
146  tiedPoint = QVector< QgsPoint >( additionalPoints.size(), QgsPoint( 0.0, 0.0 ) );
147 
148  TiePointInfo tmpInfo;
149  tmpInfo.mLength = std::numeric_limits<double>::infinity();
150 
151  QVector< TiePointInfo > pointLengthMap( additionalPoints.size(), tmpInfo );
152  QVector< TiePointInfo >::iterator pointLengthIt;
153 
154  //Graph's points;
155  QVector< QgsPoint > points;
156 
157  QgsFeatureIterator fit = vl->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) );
158 
159  // begin: tie points to the graph
160  QgsAttributeList la;
161  QgsFeature feature;
162  while ( fit.nextFeature( feature ) )
163  {
164  QgsMultiPolyline mpl;
165  if ( feature.constGeometry()->wkbType() == QGis::WKBMultiLineString )
166  mpl = feature.constGeometry()->asMultiPolyline();
167  else if ( feature.constGeometry()->wkbType() == QGis::WKBLineString )
168  mpl.push_back( feature.constGeometry()->asPolyline() );
169 
171  for ( mplIt = mpl.begin(); mplIt != mpl.end(); ++mplIt )
172  {
173  QgsPoint pt1, pt2;
174  bool isFirstPoint = true;
175  QgsPolyline::iterator pointIt;
176  for ( pointIt = mplIt->begin(); pointIt != mplIt->end(); ++pointIt )
177  {
178  pt2 = ct.transform( *pointIt );
179  points.push_back( pt2 );
180 
181  if ( !isFirstPoint )
182  {
183  int i = 0;
184  for ( i = 0; i != additionalPoints.size(); ++i )
185  {
186  TiePointInfo info;
187  if ( pt1 == pt2 )
188  {
189  info.mLength = additionalPoints[ i ].sqrDist( pt1 );
190  info.mTiedPoint = pt1;
191  }
192  else
193  {
194  info.mLength = additionalPoints[ i ].sqrDistToSegment( pt1.x(), pt1.y(),
195  pt2.x(), pt2.y(), info.mTiedPoint );
196  }
197 
198  if ( pointLengthMap[ i ].mLength > info.mLength )
199  {
200  Q_UNUSED( info.mTiedPoint );
201  info.mFirstPoint = pt1;
202  info.mLastPoint = pt2;
203 
204  pointLengthMap[ i ] = info;
205  tiedPoint[ i ] = info.mTiedPoint;
206  }
207  }
208  }
209  pt1 = pt2;
210  isFirstPoint = false;
211  }
212  }
213  emit buildProgress( ++step, featureCount );
214  }
215  // end: tie points to graph
216 
217  // add tied point to graph
218  int i = 0;
219  for ( i = 0; i < tiedPoint.size(); ++i )
220  {
221  if ( tiedPoint[ i ] != QgsPoint( 0.0, 0.0 ) )
222  {
223  points.push_back( tiedPoint [ i ] );
224  }
225  }
226 
227  QgsPointCompare pointCompare( builder->topologyTolerance() );
228 
229  qSort( points.begin(), points.end(), pointCompare );
230  QVector< QgsPoint >::iterator tmp = std::unique( points.begin(), points.end() );
231  points.resize( tmp - points.begin() );
232 
233  for ( i = 0;i < points.size();++i )
234  builder->addVertex( i, points[ i ] );
235 
236  for ( i = 0; i < tiedPoint.size() ; ++i )
237  tiedPoint[ i ] = *( my_binary_search( points.begin(), points.end(), tiedPoint[ i ], pointCompare ) );
238 
239  qSort( pointLengthMap.begin(), pointLengthMap.end(), TiePointInfoCompare );
240 
241  {
242  // fill attribute list 'la'
243  QgsAttributeList tmpAttr;
244  if ( mDirectionFieldId != -1 )
245  {
246  tmpAttr.push_back( mDirectionFieldId );
247  }
248 
251 
252  for ( it = mProperterList.begin(); it != mProperterList.end(); ++it )
253  {
254  QgsAttributeList tmp = ( *it )->requiredAttributes();
255  for ( it2 = tmp.begin(); it2 != tmp.end(); ++it2 )
256  {
257  tmpAttr.push_back( *it2 );
258  }
259  }
260  qSort( tmpAttr.begin(), tmpAttr.end() );
261 
262  int lastAttrId = -1;
263  for ( it2 = tmpAttr.begin(); it2 != tmpAttr.end(); ++it2 )
264  {
265  if ( *it2 == lastAttrId )
266  {
267  continue;
268  }
269 
270  la.push_back( *it2 );
271 
272  lastAttrId = *it2;
273  }
274  } // end fill attribute list 'la'
275 
276  // begin graph construction
277  fit = vl->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( la ) );
278  while ( fit.nextFeature( feature ) )
279  {
280  int directionType = mDefaultDirection;
281 
282  // What direction have feature?
283  QString str = feature.attribute( mDirectionFieldId ).toString();
284  if ( str == mBothDirectionValue )
285  {
286  directionType = 3;
287  }
288  else if ( str == mDirectDirectionValue )
289  {
290  directionType = 1;
291  }
292  else if ( str == mReverseDirectionValue )
293  {
294  directionType = 2;
295  }
296 
297  // begin features segments and add arc to the Graph;
298  QgsMultiPolyline mpl;
299  if ( feature.constGeometry()->wkbType() == QGis::WKBMultiLineString )
300  mpl = feature.constGeometry()->asMultiPolyline();
301  else if ( feature.constGeometry()->wkbType() == QGis::WKBLineString )
302  mpl.push_back( feature.constGeometry()->asPolyline() );
303 
305  for ( mplIt = mpl.begin(); mplIt != mpl.end(); ++mplIt )
306  {
307  QgsPoint pt1, pt2;
308 
309  bool isFirstPoint = true;
310  QgsPolyline::iterator pointIt;
311  for ( pointIt = mplIt->begin(); pointIt != mplIt->end(); ++pointIt )
312  {
313  pt2 = ct.transform( *pointIt );
314 
315  if ( !isFirstPoint )
316  {
317  QMap< double, QgsPoint > pointsOnArc;
318  pointsOnArc[ 0.0 ] = pt1;
319  pointsOnArc[ pt1.sqrDist( pt2 )] = pt2;
320 
321  TiePointInfo t;
322  t.mFirstPoint = pt1;
323  t.mLastPoint = pt2;
324  t.mLength = 0.0;
325  pointLengthIt = my_binary_search( pointLengthMap.begin(), pointLengthMap.end(), t, TiePointInfoCompare );
326 
327  if ( pointLengthIt != pointLengthMap.end() )
328  {
330  for ( it = pointLengthIt; it - pointLengthMap.begin() >= 0; --it )
331  {
332  if ( it->mFirstPoint == pt1 && it->mLastPoint == pt2 )
333  {
334  pointsOnArc[ pt1.sqrDist( it->mTiedPoint )] = it->mTiedPoint;
335  }
336  }
337  for ( it = pointLengthIt + 1; it != pointLengthMap.end(); ++it )
338  {
339  if ( it->mFirstPoint == pt1 && it->mLastPoint == pt2 )
340  {
341  pointsOnArc[ pt1.sqrDist( it->mTiedPoint )] = it->mTiedPoint;
342  }
343  }
344  }
345 
347  QgsPoint pt1;
348  QgsPoint pt2;
349  int pt1idx = -1, pt2idx = -1;
350  bool isFirstPoint = true;
351  for ( pointsIt = pointsOnArc.begin(); pointsIt != pointsOnArc.end(); ++pointsIt )
352  {
353  pt2 = *pointsIt;
354  tmp = my_binary_search( points.begin(), points.end(), pt2, pointCompare );
355  pt2 = *tmp;
356  pt2idx = tmp - points.begin();
357 
358  if ( !isFirstPoint && pt1 != pt2 )
359  {
360  double distance = builder->distanceArea()->measureLine( pt1, pt2 );
361  QVector< QVariant > prop;
363  for ( it = mProperterList.begin(); it != mProperterList.end(); ++it )
364  {
365  prop.push_back(( *it )->property( distance, feature ) );
366  }
367 
368  if ( directionType == 1 ||
369  directionType == 3 )
370  {
371  builder->addArc( pt1idx, pt1, pt2idx, pt2, prop );
372  }
373  if ( directionType == 2 ||
374  directionType == 3 )
375  {
376  builder->addArc( pt2idx, pt2, pt1idx, pt1, prop );
377  }
378  }
379  pt1idx = pt2idx;
380  pt1 = pt2;
381  isFirstPoint = false;
382  }
383  } // if ( !isFirstPoint )
384  pt1 = pt2;
385  isFirstPoint = false;
386  } // for (it = pl.begin(); it != pl.end(); ++it)
387  }
388  emit buildProgress( ++step, featureCount );
389  } // while( vl->nextFeature(feature) )
390 } // makeGraph( QgsGraphBuilderInterface *builder, const QVector< QgsPoint >& additionalPoints, QVector< QgsPoint >& tiedPoint )
391 
Wrapper for iterator of features from vector data provider or vector layer.
virtual void addVertex(int id, const QgsPoint &pt)
add vertex
void makeGraph(QgsGraphBuilderInterface *builder, const QVector< QgsPoint > &additionalPoints, QVector< QgsPoint > &tiedPoints) const override
Make a graph using RgGraphBuilder.
bool operator()(const QgsPoint &p1, const QgsPoint &p2) const
iterator begin()
void push_back(const T &value)
typedef iterator
void setSourceCrs(const QgsCoordinateReferenceSystem &theCRS)
QgsMultiPolyline asMultiPolyline() const
Return contents of the geometry as a multi linestring if wkbType is WKBMultiLineString, otherwise an empty list.
bool coordinateTransformationEnabled()
get coordinate transformation enabled
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
Determine interface for creating a graph.
QgsPoint transform(const QgsPoint &p, TransformDirection direction=ForwardTransform) const
Transform the point from Source Coordinate System to Destination Coordinate System If the direction i...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
double sqrDist(double x, double y) const
Returns the squared distance between this point and x,y.
Definition: qgspoint.cpp:345
double x() const
Get the x value of the point.
Definition: qgspoint.h:128
QgsCoordinateReferenceSystem & destinationCrs()
get destinaltion Crs
long featureCount(QgsSymbolV2 *symbol)
Number of features rendered with specified symbol.
virtual void addArc(int pt1id, const QgsPoint &pt1, int pt2id, const QgsPoint &pt2, const QVector< QVariant > &properties)
add arc
void resize(int size)
QString name() const override
return Director name
This class wraps a request for features to a vector layer (or directly its vector data provider)...
double measureLine(const QList< QgsPoint > &points) const
Measures the length of a line with multiple segments.
bool TiePointInfoCompare(const TiePointInfo &a, const TiePointInfo &b)
QList< int > QgsAttributeList
QGis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
iterator end()
A class to represent a point.
Definition: qgspoint.h:65
iterator begin()
iterator end()
void setDestCRS(const QgsCoordinateReferenceSystem &theCRS)
QgsPolyline asPolyline() const
Return contents of the geometry as a polyline if wkbType is WKBLineString, otherwise an empty list...
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:271
QgsLineVectorLayerDirector(QgsVectorLayer *myLayer, int directionFieldId, const QString &directDirectionValue, const QString &reverseDirectionValue, const QString &bothDirectionValue, int defaultDirection)
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
Class for doing transforms between two map coordinate systems.
virtual ~QgsLineVectorLayerDirector()
Destructor.
void push_back(const T &value)
double y() const
Get the y value of the point.
Definition: qgspoint.h:136
double topologyTolerance()
get topology tolerance
const QgsCoordinateReferenceSystem & crs() const
Returns layer&#39;s spatial reference system.
bool nextFeature(QgsFeature &f)
QgsPointCompare(double tolerance)
int size() const
Represents a vector layer which manages a vector based data sets.
QString toString() const
iterator end()
QgsDistanceArea * distanceArea()
get measurement tool
iterator begin()
RandIter my_binary_search(RandIter begin, RandIter end, Type val, CompareOp comp)