/*************************************************************************** qgsgpxprovider.cpp - Data provider for GPS eXchange files ------------------- begin : 2004-04-14 copyright : (C) 2004 by Lars Luthman email : larsl@users.sourceforge.net Partly based on qgsdelimitedtextprovider.cpp, (C) 2004 Gary E. Sherman ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "../../src/qgis.h" #include "../../src/qgsdataprovider.h" #include "../../src/qgsfeature.h" #include "../../src/qgsfield.h" #include "../../src/qgsrect.h" #include "qgsgpxprovider.h" #include "gpsdata.h" #ifdef WIN32 #define QGISEXTERN extern "C" __declspec( dllexport ) #else #define QGISEXTERN extern "C" #endif QgsGPXProvider::QgsGPXProvider(QString uri) : mDataSourceUri(uri), mMinMaxCacheDirty(true) { // assume that it won't work mValid = false; // get the filename and the type parameter from the URI int fileNameEnd = uri.find('?'); if (fileNameEnd == -1 || uri.mid(fileNameEnd + 1, 5) != "type=") { std::cerr<<"Bad URI - you need to specify the feature type"<getNumberOfWaypoints(); ++mFid) { const Waypoint& wpt(data->getWaypoint(mFid)); if (boundsCheck(wpt.lon, wpt.lat)) { result = new QgsFeature(mFid); // some wkb voodoo char* geo = new char[21]; std::memset(geo, 0, 21); geo[0] = endian(); geo[1] = 1; std::memcpy(geo+5, &wpt.lon, sizeof(double)); std::memcpy(geo+13, &wpt.lat, sizeof(double)); result->setGeometry((unsigned char *)geo, sizeof(wkbPoint)); result->setValid(true); // add attributes if they are wanted if (fetchAttributes) { result->addAttribute("name", wpt.name); result->addAttribute("lat", QString("%1").arg(wpt.lat)); result->addAttribute("lon", QString("%1").arg(wpt.lon)); if (wpt.ele == -std::numeric_limits::max()) result->addAttribute("ele", ""); else result->addAttribute("ele", QString("%1").arg(wpt.ele)); result->addAttribute("sym", wpt.sym); result->addAttribute("cmt", wpt.cmt); result->addAttribute("desc", wpt.desc); result->addAttribute("src", wpt.src); result->addAttribute("url", wpt.url); result->addAttribute("urlname", wpt.urlname); } ++mFid; break; } } } else if (mFeatureType == "route") { // go through the routes and return the first one that is in the bounds // rectangle for (; mFid < data->getNumberOfRoutes(); ++mFid) { const Route& rte(data->getRoute(mFid)); if (rte.points.size() == 0) continue; const Routepoint& rtept(rte.points[0]); const QgsRect& b(*mSelectionRectangle); if ((rte.xMax >= b.xMin()) && (rte.xMin <= b.xMax()) && (rte.yMax >= b.yMin()) && (rte.yMin <= b.yMax())) { result = new QgsFeature(mFid); // some wkb voodoo int nPoints = rte.points.size(); char* geo = new char[9 + 16 * nPoints]; std::memset(geo, 0, 9 + 16 * nPoints); geo[0] = endian(); geo[1] = 2; std::memcpy(geo + 5, &nPoints, 4); for (int 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)); } result->setGeometry((unsigned char *)geo, 9 + 16 * nPoints); result->setValid(true); // add attributes if they are wanted if (fetchAttributes) { result->addAttribute("name", rte.name); if (rte.number == std::numeric_limits::max()) result->addAttribute("number", ""); else result->addAttribute("number", QString("%1").arg(rte.number)); result->addAttribute("cmt", rte.cmt); result->addAttribute("desc", rte.desc); result->addAttribute("src", rte.src); result->addAttribute("url", rte.url); result->addAttribute("urlname", rte.urlname); } ++mFid; break; } } } else if (mFeatureType == "track") { // go through the tracks and return the first one that is in the bounds // rectangle for (; mFid < data->getNumberOfTracks(); ++mFid) { const Track& trk(data->getTrack(mFid)); if (trk.segments.size() == 0) continue; if (trk.segments[0].points.size() == 0) continue; const Trackpoint& trkpt(trk.segments[0].points[0]); const QgsRect& b(*mSelectionRectangle); if ((trk.xMax >= b.xMin()) && (trk.xMin <= b.xMax()) && (trk.yMax >= b.yMin()) && (trk.yMin <= b.yMax())) { result = new QgsFeature(mFid); // some wkb voodoo int nPoints = trk.segments[0].points.size(); char* geo = new char[9 + 16 * nPoints]; std::memset(geo, 0, 9 + 16 * nPoints); geo[0] = endian(); geo[1] = 2; std::memcpy(geo + 5, &nPoints, 4); for (int i = 0; i < nPoints; ++i) { std::memcpy(geo + 9 + 16 * i, &trk.segments[0].points[i].lon, sizeof(double)); std::memcpy(geo + 9 + 16 * i + 8, &trk.segments[0].points[i].lat, sizeof(double)); } result->setGeometry((unsigned char *)geo, 9 + 16 * nPoints); result->setValid(true); // add attributes if they are wanted if (fetchAttributes) { result->addAttribute("name", trk.name); if (trk.number == std::numeric_limits::max()) result->addAttribute("number", ""); else result->addAttribute("number", QString("%1").arg(trk.number)); result->addAttribute("cmt", trk.cmt); result->addAttribute("desc", trk.desc); result->addAttribute("src", trk.src); result->addAttribute("url", trk.url); result->addAttribute("urlname", trk.urlname); } ++mFid; break; } } } return result; } QgsFeature * QgsGPXProvider::getNextFeature(std::list& attlist) { QgsFeature* result = 0; std::list::const_iterator iter; if (mFeatureType == "waypoint") { // go through the list of waypoints and return the first one that is in // the bounds rectangle for (; mFid < data->getNumberOfWaypoints(); ++mFid) { const Waypoint& wpt(data->getWaypoint(mFid)); if (boundsCheck(wpt.lon, wpt.lat)) { result = new QgsFeature(mFid); // some wkb voodoo char* geo = new char[21]; std::memset(geo, 0, 21); geo[0] = endian(); geo[1] = 1; std::memcpy(geo+5, &wpt.lon, sizeof(double)); std::memcpy(geo+13, &wpt.lat, sizeof(double)); result->setGeometry((unsigned char *)geo, sizeof(wkbPoint)); result->setValid(true); // add attributes if they are wanted for (iter = attlist.begin(); iter != attlist.end(); ++iter) { switch (*iter) { case 0: result->addAttribute("name", wpt.name); break; case 1: result->addAttribute("lat", QString("%1").arg(wpt.lat)); break; case 2: result->addAttribute("lon", QString("%1").arg(wpt.lon)); break; case 3: if (wpt.ele == -std::numeric_limits::max()) result->addAttribute("ele", ""); else result->addAttribute("ele", QString("%1").arg(wpt.ele)); break; case 4: result->addAttribute("sym", wpt.sym); break; case 5: result->addAttribute("cmt", wpt.cmt); break; case 6: result->addAttribute("desc", wpt.desc); break; case 7: result->addAttribute("src", wpt.src); break; case 8: result->addAttribute("url", wpt.url); break; case 9: result->addAttribute("urlname", wpt.urlname); break; } } ++mFid; break; } } } else if (mFeatureType == "route") { // go through the routes and return the first one that is in the bounds // rectangle for (; mFid < data->getNumberOfRoutes(); ++mFid) { const Route& rte(data->getRoute(mFid)); if (rte.points.size() == 0) continue; const Routepoint& rtept(rte.points[0]); const QgsRect& b(*mSelectionRectangle); if ((rte.xMax >= b.xMin()) && (rte.xMin <= b.xMax()) && (rte.yMax >= b.yMin()) && (rte.yMin <= b.yMax())) { result = new QgsFeature(mFid); // some wkb voodoo int nPoints = rte.points.size(); char* geo = new char[9 + 16 * nPoints]; std::memset(geo, 0, 9 + 16 * nPoints); geo[0] = endian(); geo[1] = 2; std::memcpy(geo + 5, &nPoints, 4); for (int 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)); } result->setGeometry((unsigned char *)geo, 9 + 16 * nPoints); result->setValid(true); // add attributes if they are wanted for (iter = attlist.begin(); iter != attlist.end(); ++iter) { if (*iter == 0) result->addAttribute("name", rte.name); else if (*iter == 1) { if (rte.number == std::numeric_limits::max()) result->addAttribute("number", ""); else result->addAttribute("number", QString("%1").arg(rte.number)); } else if (*iter == 2) result->addAttribute("cmt", rte.url); else if (*iter == 3) result->addAttribute("desc", rte.url); else if (*iter == 4) result->addAttribute("src", rte.url); else if (*iter == 5) result->addAttribute("url", rte.url); else if (*iter == 6) result->addAttribute("urlname", rte.url); } ++mFid; break; } } } else if (mFeatureType == "track") { // go through the tracks and return the first one that is in the bounds // rectangle for (; mFid < data->getNumberOfTracks(); ++mFid) { const Track& trk(data->getTrack(mFid)); if (trk.segments.size() == 0) continue; if (trk.segments[0].points.size() == 0) continue; const Trackpoint& trkpt(trk.segments[0].points[0]); const QgsRect& b(*mSelectionRectangle); if ((trk.xMax >= b.xMin()) && (trk.xMin <= b.xMax()) && (trk.yMax >= b.yMin()) && (trk.yMin <= b.yMax())) { result = new QgsFeature(mFid); // some wkb voodoo int nPoints = trk.segments[0].points.size(); char* geo = new char[9 + 16 * nPoints]; std::memset(geo, 0, 9 + 16 * nPoints); geo[0] = endian(); geo[1] = 2; std::memcpy(geo + 5, &nPoints, 4); for (int i = 0; i < nPoints; ++i) { std::memcpy(geo + 9 + 16 * i, &trk.segments[0].points[i].lon, sizeof(double)); std::memcpy(geo + 9 + 16 * i + 8, &trk.segments[0].points[i].lat, sizeof(double)); } result->setGeometry((unsigned char *)geo, 9 + 16 * nPoints); result->setValid(true); // add attributes if they are wanted for (iter = attlist.begin(); iter != attlist.end(); ++iter) { if (*iter == 0) result->addAttribute("name", trk.name); else if (*iter == 1) { if (trk.number == std::numeric_limits::max()) result->addAttribute("number", ""); else result->addAttribute("number", QString("%1").arg(trk.number)); } else if (*iter == 2) result->addAttribute("cmt", trk.url); else if (*iter == 3) result->addAttribute("desc", trk.url); else if (*iter == 4) result->addAttribute("src", trk.url); else if (*iter == 5) result->addAttribute("url", trk.url); else if (*iter == 6) result->addAttribute("urlname", trk.url); } ++mFid; break; } } } return result; } /** * Select features based on a bounding rectangle. Features can be retrieved * with calls to getFirstFeature and getNextFeature. * @param mbr QgsRect containing the extent to use in selecting features */ void QgsGPXProvider::select(QgsRect *rect, bool useIntersect) { // Setting a spatial filter doesn't make much sense since we have to // compare each point against the rectangle. // We store the rect and use it in getNextFeature to determine if the // feature falls in the selection area mSelectionRectangle = new QgsRect(*rect); // Select implies an upcoming feature read so we reset the data source reset(); // Reset the feature id to 0 mFid = 0; } /** * Identify features within the search radius specified by rect * @param rect Bounding rectangle of search radius * @return std::vector containing QgsFeature objects that intersect rect */ std::vector& QgsGPXProvider::identify(QgsRect * rect) { // reset the data source since we need to be able to read through // all features reset(); std::cerr<<"Attempting to identify features falling within " <stringRep()< features; return features; } /* unsigned char * QgsGPXProvider::getGeometryPointer(OGRFeature *fet){ unsigned char *gPtr=0; // get the wkb representation //geom->exportToWkb((OGRwkbByteOrder) endian(), gPtr); return gPtr; } */ int QgsGPXProvider::endian() { char *chkEndian = new char[4]; memset(chkEndian, '\0', 4); chkEndian[0] = 0xE8; int *ce = (int *) chkEndian; int retVal; if (232 == *ce) retVal = NDR; else retVal = XDR; delete[]chkEndian; return retVal; } // Return the extent of the layer QgsRect *QgsGPXProvider::extent() { return data->getExtent(); } /** * Return the feature type */ int QgsGPXProvider::geometryType() { return mGeomType; } /** * Return the feature type */ long QgsGPXProvider::featureCount() { if (mFeatureType == "waypoint") return data->getNumberOfWaypoints(); if (mFeatureType == "route") return data->getNumberOfRoutes(); if (mFeatureType == "track") return data->getNumberOfTracks(); return 0; } /** * Return the number of fields */ int QgsGPXProvider::fieldCount() { return attributeFields.size(); } std::vector& QgsGPXProvider::fields(){ return attributeFields; } void QgsGPXProvider::reset() { // Reset feature id to 0 mFid = 0; } QString QgsGPXProvider::minValue(int position) { if (position >= fieldCount()) { std::cerr<<"Warning: access requested to invalid position " <<"in QgsGPXProvider::minValue(..)"<= fieldCount()) { std::cerr<<"Warning: access requested to invalid position " <<"in QgsGPXProvider::maxValue(..)"<mMinMaxCache[i][1]) { mMinMaxCache[i][1]=value; } } } while(getNextFeature(f, true)); mMinMaxCacheDirty=false; } void QgsGPXProvider::setDataSourceUri(QString uri) { mDataSourceUri = uri; } QString QgsGPXProvider::getDataSourceUri() { return mDataSourceUri; } bool QgsGPXProvider::isValid(){ return mValid; } bool QgsGPXProvider::addFeature(QgsFeature* f) { unsigned char* geo = f->getGeometry(); int id; QGis::WKBTYPE ftype; memcpy(&ftype, (geo + 1), sizeof(int)); /* Do different things for different geometry types: WKBPoint -> add waypoint if this is a waypoint layer WKBLineString -> add track if this is a track layer, route if this is a route layer WKBPolygon and the WKBMulti types are not supported because they don't make sense for GPX files. */ switch(ftype) { // the user is trying to add a point feature case QGis::WKBPoint: { if (mFeatureType != "waypoint") { std::cerr<<"Tried to write point feature to non-point layer!"<addWaypoint(*((double*)(geo + 5 + sizeof(double))), *((double*)(geo + 5))); Waypoint& wpt(data->getWaypoint(id)); std::vector::const_iterator iter; for (iter = f->attributeMap().begin(); iter != f->attributeMap().end(); ++iter) { if (iter->fieldName() == "name") wpt.name = iter->fieldValue(); else if (iter->fieldName() == "ele") wpt.ele = iter->fieldValue().toDouble(); else if (iter->fieldName() == "sym") wpt.sym = iter->fieldValue(); else if (iter->fieldName() == "cmt") wpt.cmt = iter->fieldValue(); else if (iter->fieldName() == "desc") wpt.desc = iter->fieldValue(); else if (iter->fieldName() == "src") wpt.src = iter->fieldValue(); else if (iter->fieldName() == "url") wpt.url = iter->fieldValue(); else if (iter->fieldName() == "urlname") wpt.urlname = iter->fieldValue(); } break; } // the user is trying to add a line feature case QGis::WKBLineString: { if (mFeatureType == "waypoint") { std::cerr<<"Tried to write line feature to point layer!"<getGeometry()+1+sizeof(int),sizeof(int)); #ifdef QGISDEBUG qWarning("length: "+QString::number(length)); #endif // add route if (mFeatureType == "route") { id = data->addRoute(); Route& rte(data->getRoute(id)); for (int i = 0; i < length; ++i) { Routepoint rpt; std::memcpy(&rpt.lon, geo + 9 + 16 * i, sizeof(double)); std::memcpy(&rpt.lat, geo + 9 + 16 * i + 8, sizeof(double)); // update the route bounds rte.xMin = (rte.xMin < rpt.lon ? rte.xMin : rpt.lon); rte.xMax = (rte.xMax > rpt.lon ? rte.xMax : rpt.lon); rte.yMin = (rte.yMin < rpt.lat ? rte.yMin : rpt.lat); rte.yMax = (rte.yMax > rpt.lat ? rte.yMax : rpt.lat); rte.points.push_back(rpt); } } // add track else if (mFeatureType == "track") { id = data->addTrack(); Track& trk(data->getTrack(id)); TrackSegment trkSeg; for (int i = 0; i < length; ++i) { Trackpoint tpt; std::memcpy(&tpt.lon, geo + 9 + 16 * i, sizeof(double)); std::memcpy(&tpt.lat, geo + 9 + 16 * i + 8, sizeof(double)); // update the track bounds trk.xMin = (trk.xMin < tpt.lon ? trk.xMin : tpt.lon); trk.xMax = (trk.xMax > tpt.lon ? trk.xMax : tpt.lon); trk.yMin = (trk.yMin < tpt.lat ? trk.yMin : tpt.lat); trk.yMax = (trk.yMax > tpt.lat ? trk.yMax : tpt.lat); trkSeg.points.push_back(tpt); } trk.segments.push_back(trkSeg); } break; } // unsupported geometry - something's wrong default: return false; } // write back to file QDomDocument qdd; data->fillDom(qdd); QFile file(mFileName); if (file.open(IO_WriteOnly)) { QTextStream stream(&file); stream<xMax()) && (x > mSelectionRectangle->xMin())) && ((y < mSelectionRectangle->yMax()) && (y > mSelectionRectangle->yMin()))); QString hit = inBounds?"true":"false"; return inBounds; } /** * Class factory to return a pointer to a newly created * QgsGPXProvider object */ QGISEXTERN QgsGPXProvider * classFactory(const char *uri) { return new QgsGPXProvider(uri); } /** Required key function (used to map the plugin to a data store type) */ QGISEXTERN QString providerKey(){ return QString("gpx"); } /** * Required description function */ QGISEXTERN QString description(){ return QString("GPS eXchange format and LOC provider"); } /** * Required isProvider function. Used to determine if this shared library * is a data provider plugin */ QGISEXTERN bool isProvider(){ return true; }