453 changes: 453 additions & 0 deletions src/providers/gpx/qgsgpxfeatureiterator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,453 @@
#include "qgsgpxfeatureiterator.h"

#include "qgsgpxprovider.h"

#include "qgsapplication.h"
#include "qgsgeometry.h"
#include "qgslogger.h"

#include <limits>
#include <cstring>


QgsGPXFeatureIterator::QgsGPXFeatureIterator( QgsGPXProvider* p, const QgsFeatureRequest& request )
: QgsAbstractFeatureIterator( request ), P( p )
{
rewind();
}

QgsGPXFeatureIterator::~QgsGPXFeatureIterator()
{
close();
}

bool QgsGPXFeatureIterator::rewind()
{
if ( mClosed )
return false;

if ( mRequest.filterType() == QgsFeatureRequest::FilterFid )
{
mFetchedFid = false;
}
else
{
if ( P->mFeatureType == QgsGPXProvider::WaypointType )
mWptIter = P->data->waypointsBegin();
else if ( P->mFeatureType == QgsGPXProvider::RouteType )
mRteIter = P->data->routesBegin();
else if ( P->mFeatureType == QgsGPXProvider::TrackType )
mTrkIter = P->data->tracksBegin();
}

return true;
}

bool QgsGPXFeatureIterator::close()
{
if ( mClosed )
return false;

// nothing to do

mClosed = true;
return true;
}





bool QgsGPXFeatureIterator::nextFeature( QgsFeature& feature )
{
feature.setValid( false );

if ( mRequest.filterType() == QgsFeatureRequest::FilterFid )
{
return readFid( feature );
}


if ( P->mFeatureType == QgsGPXProvider::WaypointType )
{
// go through the list of waypoints and return the first one that is in
// the bounds rectangle
for ( ; mWptIter != P->data->waypointsEnd(); ++mWptIter )
{
if ( readWaypoint( *mWptIter, feature ) )
{
++mWptIter;
return true;
}
}
}
else if ( P->mFeatureType == QgsGPXProvider::RouteType )
{
// go through the routes and return the first one that is in the bounds
// rectangle
for ( ; mRteIter != P->data->routesEnd(); ++mRteIter )
{
if ( readRoute( *mRteIter, feature ) )
{
++mRteIter;
return true;
}
}
}
else if ( P->mFeatureType == QgsGPXProvider::TrackType )
{
// go through the tracks and return the first one that is in the bounds
// rectangle
for ( ; mTrkIter != P->data->tracksEnd(); ++mTrkIter )
{
if ( readTrack( *mTrkIter, feature ) )
{
++mTrkIter;
return true;
}
}
}

return false;
}


bool QgsGPXFeatureIterator::readFid( QgsFeature& feature )
{
if ( mFetchedFid )
return false;

mFetchedFid = true;
QgsFeatureId fid = mRequest.filterFid();

if ( P->mFeatureType == QgsGPXProvider::WaypointType )
{
for ( QgsGPSData::WaypointIterator it = P->data->waypointsBegin() ; it != P->data->waypointsEnd(); ++it )
{
if ( it->id == fid )
{
readWaypoint( *it, feature );
return true;
}
}
}
else if ( P->mFeatureType == QgsGPXProvider::RouteType )
{
for ( QgsGPSData::RouteIterator it = P->data->routesBegin() ; it != P->data->routesEnd(); ++it )
{
if ( it->id == fid )
{
readRoute( *it, feature );
return true;
}
}
}
else if ( P->mFeatureType == QgsGPXProvider::TrackType )
{
for ( QgsGPSData::TrackIterator it = P->data->tracksBegin() ; it != P->data->tracksEnd(); ++it )
{
if ( it->id == fid )
{
readTrack( *it, feature );
return true;
}
}
}

return false;
}


bool QgsGPXFeatureIterator::readWaypoint(const QgsWaypoint& wpt, QgsFeature& feature)
{
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
{
const QgsRectangle& rect = mRequest.filterRect();
if ( ! rect.contains( QgsPoint( wpt.lon, wpt.lat ) ) )
return false;
}

// some wkb voodoo
if ( !(mRequest.flags() & QgsFeatureRequest::NoGeometry) )
{
feature.setGeometry( readWaypointGeometry(wpt) );
}
feature.setFeatureId( wpt.id );
feature.setValid( true );
feature.setFields( &P->attributeFields ); // allow name-based attribute lookups
feature.initAttributes( P->attributeFields.count() );

readAttributes( feature, wpt );

return true;
}


bool QgsGPXFeatureIterator::readRoute(const QgsRoute& rte, QgsFeature& feature)
{
if ( rte.points.size() == 0 )
return false;

QgsGeometry* theGeometry = readRouteGeometry(rte);

if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
{
const QgsRectangle& rect = mRequest.filterRect();
if (( rte.xMax < rect.xMinimum() ) || ( rte.xMin > rect.xMaximum() ) ||
( rte.yMax < rect.yMinimum() ) || ( rte.yMin > rect.yMaximum() ) )
return false;

if ( !theGeometry->intersects( rect ) ) //use geos for precise intersection test
{
delete theGeometry;
return false;
}
}

if ( !(mRequest.flags() & QgsFeatureRequest::NoGeometry) )
{
feature.setGeometry( theGeometry );
}
else
{
delete theGeometry;
}
feature.setFeatureId( rte.id );
feature.setValid( true );
feature.setFields( &P->attributeFields ); // allow name-based attribute lookups
feature.initAttributes( P->attributeFields.count() );

readAttributes( feature, rte );

return true;
}


bool QgsGPXFeatureIterator::readTrack(const QgsTrack& trk, QgsFeature& feature)
{
//QgsDebugMsg( QString( "GPX feature track segments: %1" ).arg( trk.segments.size() ) );

QgsGeometry* theGeometry = readTrackGeometry(trk);

if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
{
const QgsRectangle& rect = mRequest.filterRect();
if (( trk.xMax < rect.xMinimum() ) || ( trk.xMin > rect.xMaximum() ) ||
( trk.yMax < rect.yMinimum() ) || ( trk.yMin > rect.yMaximum() ) )
return false;

if ( !theGeometry->intersects( rect ) ) //use geos for precise intersection test
{
delete theGeometry;
return false;
}
}

if ( !(mRequest.flags() & QgsFeatureRequest::NoGeometry) )
{
feature.setGeometry( theGeometry );
}
else
{
delete theGeometry;
}
feature.setFeatureId( trk.id );
feature.setValid( true );
feature.setFields( &P->attributeFields ); // allow name-based attribute lookups
feature.initAttributes( P->attributeFields.count() );

readAttributes( feature, trk );

return true;
}


void QgsGPXFeatureIterator::readAttributes( QgsFeature& feature, const QgsWaypoint& wpt )
{
// add attributes if they are wanted
for ( int i = 0; i < P->attributeFields.count(); ++i )
{
switch ( P->indexToAttr[i] )
{
case QgsGPXProvider::NameAttr:
feature.setAttribute( i, QVariant( wpt.name ) );
break;
case QgsGPXProvider::EleAttr:
if ( wpt.ele != -std::numeric_limits<double>::max() )
feature.setAttribute( i, QVariant( wpt.ele ) );
break;
case QgsGPXProvider::SymAttr:
feature.setAttribute( i, QVariant( wpt.sym ) );
break;
case QgsGPXProvider::CmtAttr:
feature.setAttribute( i, QVariant( wpt.cmt ) );
break;
case QgsGPXProvider::DscAttr:
feature.setAttribute( i, QVariant( wpt.desc ) );
break;
case QgsGPXProvider::SrcAttr:
feature.setAttribute( i, QVariant( wpt.src ) );
break;
case QgsGPXProvider::URLAttr:
feature.setAttribute( i, QVariant( wpt.url ) );
break;
case QgsGPXProvider::URLNameAttr:
feature.setAttribute( i, QVariant( wpt.urlname ) );
break;
}
}
}

void QgsGPXFeatureIterator::readAttributes( QgsFeature& feature, const QgsRoute& rte )
{
// add attributes if they are wanted
for ( int i = 0; i < P->attributeFields.count(); ++i )
{
switch ( P->indexToAttr[i] )
{
case QgsGPXProvider::NameAttr:
feature.setAttribute( i, QVariant( rte.name ) );
break;
case QgsGPXProvider::NumAttr:
if ( rte.number != std::numeric_limits<int>::max() )
feature.setAttribute( i, QVariant( rte.number ) );
break;
case QgsGPXProvider::CmtAttr:
feature.setAttribute( i, QVariant( rte.cmt ) );
break;
case QgsGPXProvider::DscAttr:
feature.setAttribute( i, QVariant( rte.desc ) );
break;
case QgsGPXProvider::SrcAttr:
feature.setAttribute( i, QVariant( rte.src ) );
break;
case QgsGPXProvider::URLAttr:
feature.setAttribute( i, QVariant( rte.url ) );
break;
case QgsGPXProvider::URLNameAttr:
feature.setAttribute( i, QVariant( rte.urlname ) );
break;
}
}
}


void QgsGPXFeatureIterator::readAttributes( QgsFeature& feature, const QgsTrack& trk )
{
// add attributes if they are wanted
for ( int i = 0; i < P->attributeFields.count(); ++i )
{
switch ( P->indexToAttr[i] )
{
case QgsGPXProvider::NameAttr:
feature.setAttribute( i, QVariant( trk.name ) );
break;
case QgsGPXProvider::NumAttr:
if ( trk.number != std::numeric_limits<int>::max() )
feature.setAttribute( i, QVariant( trk.number ) );
break;
case QgsGPXProvider::CmtAttr:
feature.setAttribute( i, QVariant( trk.cmt ) );
break;
case QgsGPXProvider::DscAttr:
feature.setAttribute( i, QVariant( trk.desc ) );
break;
case QgsGPXProvider::SrcAttr:
feature.setAttribute( i, QVariant( trk.src ) );
break;
case QgsGPXProvider::URLAttr:
feature.setAttribute( i, QVariant( trk.url ) );
break;
case QgsGPXProvider::URLNameAttr:
feature.setAttribute( i, QVariant( trk.urlname ) );
break;
}
}

}



QgsGeometry* QgsGPXFeatureIterator::readWaypointGeometry(const QgsWaypoint& wpt)
{
char* geo = new char[21];
std::memset( geo, 0, 21 );
geo[0] = QgsApplication::endian();
geo[geo[0] == QgsApplication::NDR ? 1 : 4] = QGis::WKBPoint;
std::memcpy( geo + 5, &wpt.lon, sizeof( double ) );
std::memcpy( geo + 13, &wpt.lat, sizeof( double ) );
QgsGeometry *g = new QgsGeometry();
g->fromWkb( ( unsigned char * )geo, 21 );
return g;
}


QgsGeometry* QgsGPXFeatureIterator::readRouteGeometry(const QgsRoute& rte)
{
// some wkb voodoo
int nPoints = rte.points.size();
char* geo = new char[9 + 16 * nPoints];
std::memset( geo, 0, 9 + 16 * nPoints );
geo[0] = QgsApplication::endian();
geo[geo[0] == QgsApplication::NDR ? 1 : 4] = QGis::WKBLineString;
std::memcpy( geo + 5, &nPoints, 4 );
for ( uint i = 0; i < rte.points.size(); ++i )
{
std::memcpy( geo + 9 + 16 * i, &rte.points[i].lon, sizeof( double ) );
std::memcpy( geo + 9 + 16 * i + 8, &rte.points[i].lat, sizeof( double ) );
}

//create QgsGeometry and use it for intersection test
//if geometry is to be fetched, it is attached to the feature, otherwise we delete it
QgsGeometry* theGeometry = new QgsGeometry();
theGeometry->fromWkb(( unsigned char * )geo, 9 + 16 * nPoints );
return theGeometry;
}

QgsGeometry* QgsGPXFeatureIterator::readTrackGeometry(const QgsTrack& trk)
{
// TODO: support multi line string for segments

if ( trk.segments.size() == 0 )
return 0;

// A track consists of several segments. Add all those segments into one.
int totalPoints = 0;;
for ( std::vector<QgsTrackSegment>::size_type i = 0; i < trk.segments.size(); i ++ )
{
totalPoints += trk.segments[i].points.size();
}
if ( totalPoints == 0 )
return 0;
//QgsDebugMsg( "GPX feature track total points: " + QString::number( totalPoints ) );

// some wkb voodoo
char* geo = new char[9 + 16 * totalPoints];
if ( !geo )
{
QgsDebugMsg( "Too large track!!!" );
return 0;
}
std::memset( geo, 0, 9 + 16 * totalPoints );
geo[0] = QgsApplication::endian();
geo[geo[0] == QgsApplication::NDR ? 1 : 4] = QGis::WKBLineString;
std::memcpy( geo + 5, &totalPoints, 4 );

int thisPoint = 0;
for ( std::vector<QgsTrackSegment>::size_type k = 0; k < trk.segments.size(); k++ )
{
int nPoints = trk.segments[k].points.size();
for ( int i = 0; i < nPoints; ++i )
{
std::memcpy( geo + 9 + 16 * thisPoint, &trk.segments[k].points[i].lon, sizeof( double ) );
std::memcpy( geo + 9 + 16 * thisPoint + 8, &trk.segments[k].points[i].lat, sizeof( double ) );
thisPoint++;
}
}

//create QgsGeometry and use it for intersection test
//if geometry is to be fetched, it is attached to the feature, otherwise we delete it
QgsGeometry* theGeometry = new QgsGeometry();
theGeometry->fromWkb(( unsigned char * )geo, 9 + 16 * totalPoints );
return theGeometry;
}
56 changes: 56 additions & 0 deletions src/providers/gpx/qgsgpxfeatureiterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#ifndef QGSGPXFEATUREITERATOR_H
#define QGSGPXFEATUREITERATOR_H

#include "qgsfeatureiterator.h"

#include "gpsdata.h"

class QgsGPXProvider;

class QgsGPXFeatureIterator : public QgsAbstractFeatureIterator
{
public:
QgsGPXFeatureIterator( QgsGPXProvider* p, const QgsFeatureRequest& request );

~QgsGPXFeatureIterator();

//! fetch next feature, return true on success
virtual bool nextFeature( QgsFeature& feature );

//! reset the iterator to the starting position
virtual bool rewind();

//! end of iterating: free the resources / lock
virtual bool close();

protected:

bool readFid( QgsFeature& feature );

bool readWaypoint(const QgsWaypoint& wpt, QgsFeature& feature);
bool readRoute(const QgsRoute& rte, QgsFeature& feature);
bool readTrack(const QgsTrack& trk, QgsFeature& feature);

QgsGeometry* readWaypointGeometry(const QgsWaypoint& wpt);
QgsGeometry* readRouteGeometry(const QgsRoute& rte);
QgsGeometry* readTrackGeometry(const QgsTrack& trk);

void readAttributes( QgsFeature& feature, const QgsWaypoint& wpt );
void readAttributes( QgsFeature& feature, const QgsRoute& rte );
void readAttributes( QgsFeature& feature, const QgsTrack& trk );

protected:
QgsGPXProvider* P;

//! Current waypoint iterator
QgsGPSData::WaypointIterator mWptIter;
//! Current route iterator
QgsGPSData::RouteIterator mRteIter;
//! Current track iterator
QgsGPSData::TrackIterator mTrkIter;


bool mFetchedFid;
};

#endif // QGSGPXFEATUREITERATOR_H
497 changes: 78 additions & 419 deletions src/providers/gpx/qgsgpxprovider.cpp

Large diffs are not rendered by default.

67 changes: 24 additions & 43 deletions src/providers/gpx/qgsgpxprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,7 @@ class QgsGPXProvider : public QgsVectorDataProvider
*/
virtual QString storageType() const;

/** Select features based on a bounding rectangle. Features can be retrieved with calls to nextFeature.
* @param fetchAttributes list of attributes which should be fetched
* @param rect spatial filter
* @param fetchGeometry true if the feature geometry should be fetched
* @param useIntersect true if an accurate intersection test should be used,
* false if a test based on bounding box is sufficient
*/
virtual void select( QgsAttributeList fetchAttributes = QgsAttributeList(),
QgsRectangle rect = QgsRectangle(),
bool fetchGeometry = true,
bool useIntersect = false );

/**
* Get the next feature resulting from a select operation.
* @param feature feature which will receive data from the provider
* @return true when there was a feature to fetch, false when end was hit
*/
virtual bool nextFeature( QgsFeature& feature );
virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request );

/**
* Get feature type.
Expand All @@ -87,9 +70,6 @@ class QgsGPXProvider : public QgsVectorDataProvider
*/
virtual const QgsFields& fields() const;

/** Restart reading features from previous select operation */
virtual void rewind();

/**
* Adds a list of features
* @return true in case of success and false in case of failure
Expand Down Expand Up @@ -145,42 +125,42 @@ class QgsGPXProvider : public QgsVectorDataProvider
/** Adds one feature (used by addFeatures()) */
bool addFeature( QgsFeature& f );

/**
* Check to see if the point is withn the selection
* rectangle
* @param x X value of point
* @param y Y value of point
* @return True if point is within the rectangle
*/
bool boundsCheck( double x, double y );


private:

enum DataType {
WaypointType = 1,
RouteType = 2,
TrackType = 4,

TrkRteType = RouteType|TrackType,
AllType = WaypointType|RouteType|TrackType

};

enum Attribute { NameAttr = 0, EleAttr, SymAttr, NumAttr,
CmtAttr, DscAttr, SrcAttr, URLAttr, URLNameAttr
};

QgsGPSData* data;

//! Fields
QgsFields attributeFields;
//! map from field index to attribute
QVector<int> indexToAttr;

QString mFileName;

enum { WaypointType, RouteType, TrackType } mFeatureType;
enum Attribute { NameAttr = 0, EleAttr, SymAttr, NumAttr,
CmtAttr, DscAttr, SrcAttr, URLAttr, URLNameAttr
};
DataType mFeatureType;

static const char* attr[];
//! Current selection rectangle
QgsRectangle *mSelectionRectangle;
static QVariant::Type attrType[];
static DataType attrUsed[];
static const int attrCount;

bool mValid;
long mNumberFeatures;

//! Current waypoint iterator
QgsGPSData::WaypointIterator mWptIter;
//! Current route iterator
QgsGPSData::RouteIterator mRteIter;
//! Current track iterator
QgsGPSData::TrackIterator mTrkIter;

struct wkbPoint
{
char byteOrder;
Expand All @@ -190,4 +170,5 @@ class QgsGPXProvider : public QgsVectorDataProvider
};
wkbPoint mWKBpt;

friend class QgsGPXFeatureIterator;
};