Example #1
0
bool QgsAfsProvider::getFeature( const QgsFeatureId &id, QgsFeature &f, bool fetchGeometry, const QList<int>& /*fetchAttributes*/, const QgsRectangle filterRect )
{
  // If cached, return cached feature
  QMap<QgsFeatureId, QgsFeature>::const_iterator it = mCache.find( id );
  if ( it != mCache.end() )
  {
    f = it.value();
    return filterRect.isNull() || f.geometry().intersects( filterRect );
  }

  // Determine attributes to fetch
  /*QStringList fetchAttribNames;
  foreach ( int idx, fetchAttributes )
    fetchAttribNames.append( mFields.at( idx ).name() );
  */

  // When fetching from server, fetch all attributes and geometry by default so that we can cache them
  QStringList fetchAttribNames;
  QList<int> fetchAttribIdx;
  for ( int idx = 0, n = mFields.size(); idx < n; ++idx )
  {
    fetchAttribNames.append( mFields.at( idx ).name() );
    fetchAttribIdx.append( idx );
  }
  fetchGeometry = true;

  // Fetch 100 features at the time
  int startId = ( id / 100 ) * 100;
  int stopId = qMin( startId + 100, mObjectIds.length() );
  QList<quint32> objectIds;
  for ( int i = startId; i < stopId; ++i )
  {
    objectIds.append( mObjectIds[i] );
  }


  // Query
  QString errorTitle, errorMessage;
  QVariantMap queryData = QgsArcGisRestUtils::getObjects(
                            mDataSource.param( "url" ), objectIds, mDataSource.param( "crs" ), fetchGeometry,
                            fetchAttribNames, QgsWKBTypes::hasM( mGeometryType ), QgsWKBTypes::hasZ( mGeometryType ),
                            filterRect, errorTitle, errorMessage );
  if ( queryData.isEmpty() )
  {
    const_cast<QgsAfsProvider*>( this )->pushError( errorTitle + ": " + errorMessage );
    QgsDebugMsg( "Query returned empty result" );
    return false;
  }

  QVariantList featuresData = queryData["features"].toList();
  if ( featuresData.isEmpty() )
  {
    QgsDebugMsg( "Query returned no features" );
    return false;
  }
  for ( int i = 0, n = featuresData.size(); i < n; ++i )
  {
    QVariantMap featureData = featuresData[i].toMap();
    QgsFeature feature;

    // Set FID
    feature.setFeatureId( startId + i );

    // Set attributes
    if ( !fetchAttribIdx.isEmpty() )
    {
      QVariantMap attributesData = featureData["attributes"].toMap();
      feature.setFields( mFields );
      QgsAttributes attributes( mFields.size() );
      foreach ( int idx, fetchAttribIdx )
      {
        attributes[idx] = attributesData[mFields.at( idx ).name()];
      }
      feature.setAttributes( attributes );
    }

    // Set geometry
    if ( fetchGeometry )
    {
      QVariantMap geometryData = featureData["geometry"].toMap();
      QgsAbstractGeometryV2* geometry = QgsArcGisRestUtils::parseEsriGeoJSON( geometryData, queryData["geometryType"].toString(),
                                        QgsWKBTypes::hasM( mGeometryType ), QgsWKBTypes::hasZ( mGeometryType ) );
      // Above might return 0, which is ok since in theory empty geometries are allowed
      feature.setGeometry( QgsGeometry( geometry ) );
    }
    feature.setValid( true );
    mCache.insert( feature.id(), feature );
  }
bool QgsSpatiaLiteFeatureIterator::getFeature( sqlite3_stmt *stmt, QgsFeature &feature )
{
  bool subsetAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;

  int ret = sqlite3_step( stmt );
  if ( ret == SQLITE_DONE )
  {
    // there are no more rows to fetch
    return false;
  }
  if ( ret != SQLITE_ROW )
  {
    // some unexpected error occurred
    QgsMessageLog::logMessage( QObject::tr( "SQLite error getting feature: %1" ).arg( QString::fromUtf8( sqlite3_errmsg( mHandle->handle() ) ) ), QObject::tr( "SpatiaLite" ) );
    return false;
  }

  // one valid row has been fetched from the result set
  if ( !mFetchGeometry )
  {
    // no geometry was required
    feature.setGeometryAndOwnership( 0, 0 );
  }

  feature.initAttributes( mSource->mFields.count() );
  feature.setFields( mSource->mFields ); // allow name-based attribute lookups

  int ic;
  int n_columns = sqlite3_column_count( stmt );
  for ( ic = 0; ic < n_columns; ic++ )
  {
    if ( ic == 0 )
    {
      if ( mHasPrimaryKey )
      {
        // first column always contains the ROWID (or the primary key)
        QgsFeatureId fid = sqlite3_column_int64( stmt, ic );
        QgsDebugMsgLevel( QString( "fid=%1" ).arg( fid ), 3 );
        feature.setFeatureId( fid );
      }
      else
      {
        // autoincrement a row number
        mRowNumber++;
        feature.setFeatureId( mRowNumber );
      }
    }
    else if ( mFetchGeometry && ic == mGeomColIdx )
    {
      getFeatureGeometry( stmt, ic, feature );
    }
    else
    {
      if ( subsetAttributes )
      {
        if ( ic <= mRequest.subsetOfAttributes().size() )
        {
          int attrIndex = mRequest.subsetOfAttributes()[ic-1];
          feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, mSource->mFields.at( attrIndex ).type() ) );
        }
      }
      else
      {
        int attrIndex = ic - 1;
        feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, mSource->mFields.at( attrIndex ).type() ) );
      }
    }
  }

  return true;
}
bool QgsGPXProvider::nextFeature( QgsFeature& feature )
{
  feature.setValid( false );
  bool result = false;

  QgsAttributeList::const_iterator iter;

  if ( mFeatureType == WaypointType )
  {
    // go through the list of waypoints and return the first one that is in
    // the bounds rectangle
    for ( ; mWptIter != data->waypointsEnd(); ++mWptIter )
    {
      const QgsWaypoint* wpt;
      wpt = &( *mWptIter );
      if ( boundsCheck( wpt->lon, wpt->lat ) )
      {
        feature.setFeatureId( wpt->id );
        result = true;

        // some wkb voodoo
        if ( mFetchGeom )
        {
          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 ) );
          feature.setGeometryAndOwnership(( unsigned char * )geo, sizeof( wkbPoint ) );
        }
        feature.setValid( true );

        // add attributes if they are wanted
        for ( iter = mAttributesToFetch.begin(); iter != mAttributesToFetch.end(); ++iter )
        {
          switch ( *iter )
          {
            case NameAttr:
              feature.addAttribute( NameAttr, QVariant( wpt->name ) );
              break;
            case EleAttr:
              if ( wpt->ele != -std::numeric_limits<double>::max() )
                feature.addAttribute( EleAttr, QVariant( wpt->ele ) );
              break;
            case SymAttr:
              feature.addAttribute( SymAttr, QVariant( wpt->sym ) );
              break;
            case CmtAttr:
              feature.addAttribute( CmtAttr, QVariant( wpt->cmt ) );
              break;
            case DscAttr:
              feature.addAttribute( DscAttr, QVariant( wpt->desc ) );
              break;
            case SrcAttr:
              feature.addAttribute( SrcAttr, QVariant( wpt->src ) );
              break;
            case URLAttr:
              feature.addAttribute( URLAttr, QVariant( wpt->url ) );
              break;
            case URLNameAttr:
              feature.addAttribute( URLNameAttr, QVariant( wpt->urlname ) );
              break;
          }
        }

        ++mWptIter;
        break;
      }
    }
  }

  else if ( mFeatureType == RouteType )
  {
    // go through the routes and return the first one that is in the bounds
    // rectangle
    for ( ; mRteIter != data->routesEnd(); ++mRteIter )
    {
      const QgsRoute* rte;
      rte = &( *mRteIter );

      if ( rte->points.size() == 0 )
        continue;
      const QgsRectangle& b( *mSelectionRectangle );
      if (( rte->xMax >= b.xMinimum() ) && ( rte->xMin <= b.xMaximum() ) &&
          ( rte->yMax >= b.yMinimum() ) && ( rte->yMin <= b.yMaximum() ) )
      {
        // 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 );
        bool intersection = theGeometry->intersects( b );//use geos for precise intersection test

        if ( !intersection )
        {
          delete theGeometry;
        }
        else
        {
          if ( mFetchGeom )
          {
            feature.setGeometry( theGeometry );
          }
          else
          {
            delete theGeometry;
          }
          feature.setFeatureId( rte->id );
          result = true;
          feature.setValid( true );

          // add attributes if they are wanted
          for ( iter = mAttributesToFetch.begin(); iter != mAttributesToFetch.end(); ++iter )
          {
            switch ( *iter )
            {
              case NameAttr:
                feature.addAttribute( NameAttr, QVariant( rte->name ) );
                break;
              case NumAttr:
                if ( rte->number != std::numeric_limits<int>::max() )
                  feature.addAttribute( NumAttr, QVariant( rte->number ) );
                break;
              case CmtAttr:
                feature.addAttribute( CmtAttr, QVariant( rte->cmt ) );
                break;
              case DscAttr:
                feature.addAttribute( DscAttr, QVariant( rte->desc ) );
                break;
              case SrcAttr:
                feature.addAttribute( SrcAttr, QVariant( rte->src ) );
                break;
              case URLAttr:
                feature.addAttribute( URLAttr, QVariant( rte->url ) );
                break;
              case URLNameAttr:
                feature.addAttribute( URLNameAttr, QVariant( rte->urlname ) );
                break;
            }
          }

          ++mRteIter;
          break;

        }

        //++mRteIter;
        //xbreak;
      }
    }
  }

  else if ( mFeatureType == TrackType )
  {
    // go through the tracks and return the first one that is in the bounds
    // rectangle
    for ( ; mTrkIter != data->tracksEnd(); ++mTrkIter )
    {
      const QgsTrack* trk;
      trk = &( *mTrkIter );

      QgsDebugMsg( QString( "GPX feature track segments: %1" ).arg( trk->segments.size() ) );
      if ( trk->segments.size() == 0 )
        continue;

      // 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 )
        continue;
      QgsDebugMsg( "GPX feature track total points: " + QString::number( totalPoints ) );
      const QgsRectangle& b( *mSelectionRectangle );
      if (( trk->xMax >= b.xMinimum() ) && ( trk->xMin <= b.xMaximum() ) &&
          ( trk->yMax >= b.yMinimum() ) && ( trk->yMin <= b.yMaximum() ) )
      {
        // some wkb voodoo
        char* geo = new char[9 + 16 * totalPoints];
        if ( !geo )
        {
          QgsDebugMsg( "Too large track!!!" );
          return false;
        }
        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 );
        bool intersection = theGeometry->intersects( b );//use geos for precise intersection test

        if ( !intersection ) //no intersection, delete geometry and move on
        {
          delete theGeometry;
        }
        else //intersection
        {
          if ( mFetchGeom )
          {
            feature.setGeometry( theGeometry );
          }
          else
          {
            delete theGeometry;
          }
          feature.setFeatureId( trk->id );
          result = true;

          feature.setValid( true );

          // add attributes if they are wanted
          for ( iter = mAttributesToFetch.begin(); iter != mAttributesToFetch.end(); ++iter )
          {
            switch ( *iter )
            {
              case NameAttr:
                feature.addAttribute( NameAttr, QVariant( trk->name ) );
                break;
              case NumAttr:
                if ( trk->number != std::numeric_limits<int>::max() )
                  feature.addAttribute( NumAttr, QVariant( trk->number ) );
                break;
              case CmtAttr:
                feature.addAttribute( CmtAttr, QVariant( trk->cmt ) );
                break;
              case DscAttr:
                feature.addAttribute( DscAttr, QVariant( trk->desc ) );
                break;
              case SrcAttr:
                feature.addAttribute( SrcAttr, QVariant( trk->src ) );
                break;
              case URLAttr:
                feature.addAttribute( URLAttr, QVariant( trk->url ) );
                break;
              case URLNameAttr:
                feature.addAttribute( URLNameAttr, QVariant( trk->urlname ) );
                break;
            }
          }

          ++mTrkIter;
          break;
        }
      }

    }
  }
  if ( result )
  {
    feature.setValid( true );
  }
  return result;
}
void QgsDelimitedTextProvider::scanFile( bool buildIndexes )
{
  QStringList messages;

  // assume the layer is invalid until proven otherwise

  mLayerValid = false;
  mValid = false;
  mRescanRequired = false;

  clearInvalidLines();

  // Initiallize indexes

  resetIndexes();
  bool buildSpatialIndex = buildIndexes && mSpatialIndex != 0;

  // No point building a subset index if there is no geometry, as all
  // records will be included.

  bool buildSubsetIndex = buildIndexes && mBuildSubsetIndex && mGeomRep != GeomNone;

  if ( ! mFile->isValid() )
  {
    // uri is invalid so the layer must be too...

    messages.append( tr( "File cannot be opened or delimiter parameters are not valid" ) );
    reportErrors( messages );
    QgsDebugMsg( "Delimited text source invalid - filename or delimiter parameters" );
    return;
  }

  // Open the file and get number of rows, etc. We assume that the
  // file has a header row and process accordingly. Caller should make
  // sure that the delimited file is properly formed.

  if ( mGeomRep == GeomAsWkt )
  {
    mWktFieldIndex = mFile->fieldIndex( mWktFieldName );
    if ( mWktFieldIndex < 0 )
    {
      messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( "Wkt" ).arg( mWktFieldName ) );
    }
  }
  else if ( mGeomRep == GeomAsXy )
  {
    mXFieldIndex = mFile->fieldIndex( mXFieldName );
    mYFieldIndex = mFile->fieldIndex( mYFieldName );
    if ( mXFieldIndex < 0 )
    {
      messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( "X" ).arg( mWktFieldName ) );
    }
    if ( mYFieldIndex < 0 )
    {
      messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( "Y" ).arg( mWktFieldName ) );
    }
  }
  if ( messages.size() > 0 )
  {
    reportErrors( messages );
    QgsDebugMsg( "Delimited text source invalid - missing geometry fields" );
    return;
  }

  // Scan the entire file to determine
  // 1) the number of fields (this is handled by QgsDelimitedTextFile mFile
  // 2) the number of valid features.  Note that the selection of valid features
  //    should match the code in QgsDelimitedTextFeatureIterator
  // 3) the geometric extents of the layer
  // 4) the type of each field
  //
  // Also build subset and spatial indexes.

  QStringList parts;
  long nEmptyRecords = 0;
  long nBadFormatRecords = 0;
  long nIncompatibleGeometry = 0;
  long nInvalidGeometry = 0;
  long nEmptyGeometry = 0;
  mNumberFeatures = 0;
  mExtent = QgsRectangle();

  QList<bool> isEmpty;
  QList<bool> couldBeInt;
  QList<bool> couldBeLongLong;
  QList<bool> couldBeDouble;

  while ( true )
  {
    QgsDelimitedTextFile::Status status = mFile->nextRecord( parts );
    if ( status == QgsDelimitedTextFile::RecordEOF ) break;
    if ( status != QgsDelimitedTextFile::RecordOk )
    {
      nBadFormatRecords++;
      recordInvalidLine( tr( "Invalid record format at line %1" ) );
      continue;
    }
    // Skip over empty records
    if ( recordIsEmpty( parts ) )
    {
      nEmptyRecords++;
      continue;
    }

    // Check geometries are valid
    bool geomValid = true;

    if ( mGeomRep == GeomAsWkt )
    {
      if ( mWktFieldIndex >= parts.size() || parts[mWktFieldIndex].isEmpty() )
      {
        nEmptyGeometry++;
        geomValid = false;
      }
      else
      {
        // Get the wkt - confirm it is valid, get the type, and
        // if compatible with the rest of file, add to the extents

        QString sWkt = parts[mWktFieldIndex];
        QgsGeometry *geom = 0;
        if ( !mWktHasPrefix && sWkt.indexOf( WktPrefixRegexp ) >= 0 )
          mWktHasPrefix = true;
        if ( !mWktHasZM && sWkt.indexOf( WktZMRegexp ) >= 0 )
          mWktHasZM = true;
        geom = geomFromWkt( sWkt, mWktHasPrefix, mWktHasZM );

        if ( geom )
        {
          QGis::WkbType type = geom->wkbType();
          if ( type != QGis::WKBNoGeometry )
          {
            if ( mGeometryType == QGis::UnknownGeometry || geom->type() == mGeometryType )
            {
              mGeometryType = geom->type();
              if ( mNumberFeatures == 0 )
              {
                mNumberFeatures++;
                mWkbType = type;
                mExtent = geom->boundingBox();
              }
              else
              {
                mNumberFeatures++;
                if ( geom->isMultipart() ) mWkbType = type;
                QgsRectangle bbox( geom->boundingBox() );
                mExtent.combineExtentWith( &bbox );
              }
              if ( buildSpatialIndex )
              {
                QgsFeature f;
                f.setFeatureId( mFile->recordId() );
                f.setGeometry( geom );
                mSpatialIndex->insertFeature( f );
                // Feature now has ownership of geometry, so set to null
                // here to avoid deleting twice.
                geom = 0;
              }
            }
            else
            {
              nIncompatibleGeometry++;
              geomValid = false;
            }
          }
          if ( geom ) delete geom;
        }
        else
        {
          geomValid = false;
          nInvalidGeometry++;
          recordInvalidLine( tr( "Invalid WKT at line %1" ) );
        }
      }
    }
    else if ( mGeomRep == GeomAsXy )
    {
      // Get the x and y values, first checking to make sure they
      // aren't null.

      QString sX = mXFieldIndex < parts.size() ? parts[mXFieldIndex] : "";
      QString sY = mYFieldIndex < parts.size() ? parts[mYFieldIndex] : "";
      if ( sX.isEmpty() && sY.isEmpty() )
      {
        geomValid = false;
        nEmptyGeometry++;
      }
      else
      {
        QgsPoint pt;
        bool ok = pointFromXY( sX, sY, pt, mDecimalPoint, mXyDms );

        if ( ok )
        {
          if ( mNumberFeatures > 0 )
          {
            mExtent.combineExtentWith( pt.x(), pt.y() );
          }
          else
          {
            // Extent for the first point is just the first point
            mExtent.set( pt.x(), pt.y(), pt.x(), pt.y() );
            mWkbType = QGis::WKBPoint;
            mGeometryType = QGis::Point;
          }
          mNumberFeatures++;
          if ( buildSpatialIndex && qIsFinite( pt.x() ) && qIsFinite( pt.y() ) )
          {
            QgsFeature f;
            f.setFeatureId( mFile->recordId() );
            f.setGeometry( QgsGeometry::fromPoint( pt ) );
            mSpatialIndex->insertFeature( f );
          }
        }
        else
        {
          geomValid = false;
          nInvalidGeometry++;
          recordInvalidLine( tr( "Invalid X or Y fields at line %1" ) );
        }
      }
    }
    else
    {
      mWkbType = QGis::WKBNoGeometry;
      mNumberFeatures++;
    }

    if ( ! geomValid ) continue;

    if ( buildSubsetIndex ) mSubsetIndex.append( mFile->recordId() );


    // If we are going to use this record, then assess the potential types of each colum

    for ( int i = 0; i < parts.size(); i++ )
    {

      QString &value = parts[i];
      // Ignore empty fields - spreadsheet generated CSV files often
      // have random empty fields at the end of a row
      if ( value.isEmpty() )
        continue;

      // Expand the columns to include this non empty field if necessary

      while ( couldBeInt.size() <= i )
      {
        isEmpty.append( true );
        couldBeInt.append( false );
        couldBeLongLong.append( false );
        couldBeDouble.append( false );
      }

      // If this column has been empty so far then initiallize it
      // for possible types

      if ( isEmpty[i] )
      {
        isEmpty[i] = false;
        couldBeInt[i] = true;
        couldBeLongLong[i] = true;
        couldBeDouble[i] = true;
      }

      // Now test for still valid possible types for the field
      // Types are possible until first record which cannot be parsed

      if ( couldBeInt[i] )
      {
        value.toInt( &couldBeInt[i] );
      }

      if ( couldBeLongLong[i] && ! couldBeInt[i] )
      {
        value.toLongLong( &couldBeLongLong[i] );
      }

      if ( couldBeDouble[i] && ! couldBeLongLong[i] )
      {
        if ( ! mDecimalPoint.isEmpty() )
        {
          value.replace( mDecimalPoint, "." );
        }
        value.toDouble( &couldBeDouble[i] );
      }
    }
  }

  // Now create the attribute fields.  Field types are integer by preference,
  // failing that double, failing that text.

  QStringList fieldNames = mFile->fieldNames();
  mFieldCount = fieldNames.size();
  attributeColumns.clear();
  attributeFields.clear();

  QString csvtMessage;
  QStringList csvtTypes = readCsvtFieldTypes( mFile->fileName(), &csvtMessage );

  for ( int i = 0; i < fieldNames.size(); i++ )
  {
    // Skip over WKT field ... don't want to display in attribute table
    if ( i == mWktFieldIndex ) continue;

    // Add the field index lookup for the column
    attributeColumns.append( i );
    QVariant::Type fieldType = QVariant::String;
    QString typeName = "text";
    if ( i < csvtTypes.size() )
    {
      if ( csvtTypes[i] == "integer" )
      {
        fieldType = QVariant::Int;
        typeName = "integer";
      }
      else if ( csvtTypes[i] == "long" || csvtTypes[i] == "longlong" || csvtTypes[i] == "int8" )
      {
        fieldType = QVariant::LongLong; //QVariant doesn't support long
        typeName = "longlong";
      }
      else if ( csvtTypes[i] == "real" || csvtTypes[i] == "double" )
      {
        fieldType = QVariant::Double;
        typeName = "double";
      }
    }
    else if ( i < couldBeInt.size() )
    {
      if ( couldBeInt[i] )
      {
        fieldType = QVariant::Int;
        typeName = "integer";
      }
      else if ( couldBeLongLong[i] )
      {
        fieldType = QVariant::LongLong;
        typeName = "longlong";
      }
      else if ( couldBeDouble[i] )
      {
        fieldType = QVariant::Double;
        typeName = "double";
      }
    }
    attributeFields.append( QgsField( fieldNames[i], fieldType, typeName ) );
  }


  QgsDebugMsg( "Field count for the delimited text file is " + QString::number( attributeFields.size() ) );
  QgsDebugMsg( "geometry type is: " + QString::number( mWkbType ) );
  QgsDebugMsg( "feature count is: " + QString::number( mNumberFeatures ) );

  QStringList warnings;
  if ( ! csvtMessage.isEmpty() ) warnings.append( csvtMessage );
  if ( nBadFormatRecords > 0 )
    warnings.append( tr( "%1 records discarded due to invalid format" ).arg( nBadFormatRecords ) );
  if ( nEmptyGeometry > 0 )
    warnings.append( tr( "%1 records discarded due to missing geometry definitions" ).arg( nEmptyGeometry ) );
  if ( nInvalidGeometry > 0 )
    warnings.append( tr( "%1 records discarded due to invalid geometry definitions" ).arg( nInvalidGeometry ) );
  if ( nIncompatibleGeometry > 0 )
    warnings.append( tr( "%1 records discarded due to incompatible geometry types" ).arg( nIncompatibleGeometry ) );

  reportErrors( warnings );

  // Decide whether to use subset ids to index records rather than simple iteration through all
  // If more than 10% of records are being skipped, then use index.  (Not based on any experimentation,
  // could do with some analysis?)

  if ( buildSubsetIndex )
  {
    long recordCount = mFile->recordCount();
    recordCount -= recordCount / SUBSET_ID_THRESHOLD_FACTOR;
    mUseSubsetIndex = mSubsetIndex.size() < recordCount;
    if ( ! mUseSubsetIndex ) mSubsetIndex = QList<quintptr>();
  }

  mUseSpatialIndex = buildSpatialIndex;

  mValid = mGeometryType != QGis::UnknownGeometry;
  mLayerValid = mValid;

  // If it is valid, then watch for changes to the file
  connect( mFile, SIGNAL( fileUpdated() ), this, SLOT( onFileUpdated() ) );


}
bool QgsDelimitedTextProvider::nextFeature( QgsFeature& feature )
{
  // before we do anything else, assume that there's something wrong with
  // the feature
  feature.setValid( false );
  while ( ! mStream->atEnd() )
  {
    double x = 0.0;
    double y = 0.0;
    QString line = mStream->readLine(); // Default local 8 bit encoding

    // lex the tokens from the current data line
    QStringList tokens;
    if ( mDelimiterType == "regexp" )
      tokens = line.split( mDelimiterRegexp );
    else
      tokens = line.split( mDelimiter );

    bool xOk = false;
    bool yOk = false;

    // Skip indexing malformed lines.
    if ( attributeFields.size() == tokens.size() )
    {
      x = tokens[mXFieldIndex].toDouble( &xOk );
      y = tokens[mYFieldIndex].toDouble( &yOk );
    }

    if ( !( xOk && yOk ) )
    {
      // Accumulate any lines that weren't ok, to report on them
      // later, and look at the next line in the file, but only if
      // we need to.
      QgsDebugMsg( "Malformed line : " + line );
      if ( mShowInvalidLines )
        mInvalidLines << line;

      continue;
    }

    // Give every valid line in the file an id, even if it's not
    // in the current extent or bounds.
    ++mFid;             // increment to next feature ID

    // skip the feature if it's out of current bounds
    if ( ! boundsCheck( x, y ) )
      continue;

    // at this point, one way or another, the current feature values
    // are valid
    feature.setValid( true );

    feature.setFeatureId( mFid );

    QByteArray  buffer;
    QDataStream s( &buffer, static_cast<QIODevice::OpenMode>( QIODevice::WriteOnly ) ); // open on buffers's data

    switch ( QgsApplication::endian() )
    {
      case QgsApplication::NDR :
        // we're on a little-endian platform, so tell the data
        // stream to use that
        s.setByteOrder( QDataStream::LittleEndian );
        s << ( quint8 )1; // 1 is for little-endian
        break;
      case QgsApplication::XDR :
        // don't change byte order since QDataStream is big endian by default
        s << ( quint8 )0; // 0 is for big-endian
        break;
      default :
        qDebug( "%s:%d unknown endian", __FILE__, __LINE__ );
        //delete [] geometry;
        return false;
    }

    s << ( quint32 )QGis::WKBPoint;
    s << x;
    s << y;

    unsigned char* geometry = new unsigned char[buffer.size()];
    memcpy( geometry, buffer.data(), buffer.size() );

    feature.setGeometryAndOwnership( geometry, sizeof( wkbPoint ) );

    for ( QgsAttributeList::const_iterator i = mAttributesToFetch.begin();
          i != mAttributesToFetch.end();
          ++i )
    {
      QVariant val;
      switch ( attributeFields[*i].type() )
      {
        case QVariant::Int:
          val = QVariant( tokens[*i].toInt() );
          break;
        case QVariant::Double:
          val = QVariant( tokens[*i].toDouble() );
          break;
        default:
          val = QVariant( tokens[*i] );
          break;
      }
      feature.addAttribute( *i, val );
    }

    // We have a good line, so return
    return true;

  } // ! textStream EOF

  // End of the file. If there are any lines that couldn't be
  // loaded, display them now.

  if ( mShowInvalidLines && !mInvalidLines.isEmpty() )
  {
    mShowInvalidLines = false;
    QgsMessageOutput* output = QgsMessageOutput::createMessageOutput();
    output->setTitle( tr( "Error" ) );
    output->setMessage( tr( "Note: the following lines were not loaded because Qgis was "
                            "unable to determine values for the x and y coordinates:\n" ),
                        QgsMessageOutput::MessageText );

    output->appendMessage( "Start of invalid lines." );
    for ( int i = 0; i < mInvalidLines.size(); ++i )
      output->appendMessage( mInvalidLines.at( i ) );
    output->appendMessage( "End of invalid lines." );

    output->showMessage();

    // We no longer need these lines.
    mInvalidLines.empty();
  }

  return false;

} // nextFeature
bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature )
{
  feature.initAttributes( mSource->mFields.count() );

  int col = 0;

  if ( mFetchGeometry )
  {
    int returnedLength = ::PQgetlength( queryResult.result(), row, col );
    if ( returnedLength > 0 )
    {
      unsigned char *featureGeom = new unsigned char[returnedLength + 1];
      memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength );
      memset( featureGeom + returnedLength, 0, 1 );

      // modify 2.5D WKB types to make them compliant with OGR
      unsigned int wkbType;
      memcpy( &wkbType, featureGeom + 1, sizeof( wkbType ) );
      wkbType = QgsPostgresConn::wkbTypeFromOgcWkbType( wkbType );
      memcpy( featureGeom + 1, &wkbType, sizeof( wkbType ) );

      // change wkb type of inner geometries
      if ( wkbType == QGis::WKBMultiPoint25D ||
           wkbType == QGis::WKBMultiLineString25D ||
           wkbType == QGis::WKBMultiPolygon25D )
      {
        unsigned int numGeoms;
        memcpy( &numGeoms, featureGeom + 5, sizeof( unsigned int ) );
        unsigned char *wkb = featureGeom + 9;
        for ( unsigned int i = 0; i < numGeoms; ++i )
        {
          unsigned int localType;
          memcpy( &localType, wkb + 1, sizeof( localType ) );
          localType = QgsPostgresConn::wkbTypeFromOgcWkbType( localType );
          memcpy( wkb + 1, &localType, sizeof( localType ) );

          // skip endian and type info
          wkb += sizeof( unsigned int ) + 1;

          // skip coordinates
          switch ( wkbType )
          {
            case QGis::WKBMultiPoint25D:
              wkb += sizeof( double ) * 3;
              break;
            case QGis::WKBMultiLineString25D:
            {
              unsigned int nPoints;
              memcpy( &nPoints, wkb, sizeof( int ) );
              wkb += sizeof( int ) + sizeof( double ) * 3 * nPoints;
            }
            break;
            default:
            case QGis::WKBMultiPolygon25D:
            {
              unsigned int nRings;
              memcpy( &nRings, wkb, sizeof( int ) );
              wkb += sizeof( int );
              for ( unsigned int j = 0; j < nRings; ++j )
              {
                unsigned int nPoints;
                memcpy( &nPoints, wkb, sizeof( int ) );
                wkb += sizeof( nPoints ) + sizeof( double ) * 3 * nPoints;
              }
            }
            break;
          }
        }
      }

      feature.setGeometryAndOwnership( featureGeom, returnedLength + 1 );
    }
    else
    {
      feature.setGeometryAndOwnership( 0, 0 );
    }

    col++;
  }

  QgsFeatureId fid = 0;

  bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;
  const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes();

  switch ( mSource->mPrimaryKeyType )
  {
    case pktOid:
    case pktTid:
    case pktInt:
      fid = mConn->getBinaryInt( queryResult, row, col++ );
      if ( mSource->mPrimaryKeyType == pktInt &&
           ( !subsetOfAttributes || fetchAttributes.contains( mSource->mPrimaryKeyAttrs[0] ) ) )
        feature.setAttribute( mSource->mPrimaryKeyAttrs[0], fid );
      break;

    case pktFidMap:
    {
      QList<QVariant> primaryKeyVals;

      Q_FOREACH ( int idx, mSource->mPrimaryKeyAttrs )
      {
        const QgsField &fld = mSource->mFields[idx];

        QVariant v = QgsPostgresProvider::convertValue( fld.type(), queryResult.PQgetvalue( row, col ) );
        primaryKeyVals << v;

        if ( !subsetOfAttributes || fetchAttributes.contains( idx ) )
          feature.setAttribute( idx, v );

        col++;
      }

      fid = mSource->mShared->lookupFid( QVariant( primaryKeyVals ) );

    }
    break;

    case pktUnknown:
      Q_ASSERT( !"FAILURE: cannot get feature with unknown primary key" );
      return false;
  }

  feature.setFeatureId( fid );
  QgsDebugMsgLevel( QString( "fid=%1" ).arg( fid ), 4 );

  // iterate attributes
  if ( subsetOfAttributes )
  {
    Q_FOREACH ( int idx, fetchAttributes )
      getFeatureAttribute( idx, queryResult, row, col, feature );
  }
  else
  {
    for ( int idx = 0; idx < mSource->mFields.count(); ++idx )
bool QgsGrassFeatureIterator::nextFeature( QgsFeature& feature )
{
  if ( mClosed )
    return false;

  feature.setValid( false );
  int cat = -1, type = -1, id = -1;
  QgsFeatureId featureId = -1;

  QgsDebugMsgLevel( "entered.", 3 );

  if ( P->isEdited() || P->isFrozen() || !P->mValid )
  {
    close();
    return false;
  }

  if ( P->mCidxFieldIndex < 0 || mNextCidx >= P->mCidxFieldNumCats )
  {
    close();
    return false; // No features, no features in this layer
  }

  bool filterById = mRequest.filterType() == QgsFeatureRequest::FilterFid;

  // Get next line/area id
  int found = 0;
  while ( mNextCidx < P->mCidxFieldNumCats )
  {
    Vect_cidx_get_cat_by_index( P->mMap, P->mCidxFieldIndex, mNextCidx++, &cat, &type, &id );
    // Warning: selection array is only of type line/area of current layer -> check type first
    if ( !( type & P->mGrassType ) )
      continue;

    // The 'id' is a unique id of a GRASS geometry object (point, line, area)
    // but it cannot be used as QgsFeatureId because one geometry object may
    // represent more features because it may have more categories.
    featureId = makeFeatureId( id, cat );

    if ( filterById && featureId != mRequest.filterFid() )
      continue;

    // it is correct to use id with mSelection because mSelection is only used
    // for geometry selection
    if ( !mSelection[id] )
      continue;

    found = 1;
    break;
  }
  if ( !found )
  {
    close();
    return false; // No more features
  }
  QgsDebugMsgLevel( QString( "cat = %1 type = %2 id = %3 fatureId = %4" ).arg( cat ).arg( type ).arg( id ).arg( featureId ), 3 );

  feature.setFeatureId( featureId );
  feature.initAttributes( P->fields().count() );
  feature.setFields( &P->fields() ); // allow name-based attribute lookups

  if ( mRequest.flags() & QgsFeatureRequest::NoGeometry )
    feature.setGeometry( 0 );
  else
    setFeatureGeometry( feature, id, type );

  if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
    P->setFeatureAttributes( P->mLayerId, cat, &feature, mRequest.subsetOfAttributes() );
  else
    P->setFeatureAttributes( P->mLayerId, cat, &feature );

  feature.setValid( true );

  return true;
}
Example #8
0
bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature )
{
  feature.setFeatureId( OGR_F_GetFID( fet ) );
  feature.initAttributes( mSource->mFields.count() );
  feature.setFields( &mSource->mFields ); // allow name-based attribute lookups

  bool useIntersect = mRequest.flags() & QgsFeatureRequest::ExactIntersect;
  bool geometryTypeFilter = mSource->mOgrGeometryTypeFilter != wkbUnknown;
  if ( mFetchGeometry || useIntersect || geometryTypeFilter )
  {
    OGRGeometryH geom = OGR_F_GetGeometryRef( fet );

    if ( geom )
    {
      if ( mGeometrySimplifier )
        mGeometrySimplifier->simplifyGeometry( geom );

      // get the wkb representation
      int memorySize = OGR_G_WkbSize( geom );
      unsigned char *wkb = new unsigned char[memorySize];
      OGR_G_ExportToWkb( geom, ( OGRwkbByteOrder ) QgsApplication::endian(), wkb );

      QgsGeometry* geometry = feature.geometry();
      if ( !geometry ) feature.setGeometryAndOwnership( wkb, memorySize ); else geometry->fromWkb( wkb, memorySize );
    }
    else
      feature.setGeometry( 0 );

    if (( useIntersect && ( !feature.geometry() || !feature.geometry()->intersects( mRequest.filterRect() ) ) )
        || ( geometryTypeFilter && ( !feature.geometry() || QgsOgrProvider::ogrWkbSingleFlatten(( OGRwkbGeometryType )feature.geometry()->wkbType() ) != mSource->mOgrGeometryTypeFilter ) ) )
    {
      OGR_F_Destroy( fet );
      return false;
    }
  }

  if ( !mFetchGeometry )
  {
    feature.setGeometry( 0 );
  }

  // fetch attributes
  if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
  {
    const QgsAttributeList& attrs = mRequest.subsetOfAttributes();
    for ( QgsAttributeList::const_iterator it = attrs.begin(); it != attrs.end(); ++it )
    {
      getFeatureAttribute( fet, feature, *it );
    }
  }
  else
  {
    // all attributes
    for ( int idx = 0; idx < mSource->mFields.count(); ++idx )
    {
      getFeatureAttribute( fet, feature, idx );
    }
  }

  return true;
}
bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature )
{
  feature.setValid( false );

  if ( mClosed )
    return false;

  if ( mFeatureQueue.empty() )
  {
    QString fetch = QString( "FETCH FORWARD %1 FROM %2" ).arg( mFeatureQueueSize ).arg( mCursorName );
    QgsDebugMsgLevel( QString( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 );
    if ( mConn->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously
    {
      QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
    }

    QgsPostgresResult queryResult;
    for ( ;; )
    {
      queryResult = mConn->PQgetResult();
      if ( !queryResult.result() )
        break;

      if ( queryResult.PQresultStatus() != PGRES_TUPLES_OK )
      {
        QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
        break;
      }

      int rows = queryResult.PQntuples();
      if ( rows == 0 )
        continue;

      for ( int row = 0; row < rows; row++ )
      {
        mFeatureQueue.enqueue( QgsFeature() );
        getFeature( queryResult, row, mFeatureQueue.back() );
      } // for each row in queue
    }
  }

  if ( mFeatureQueue.empty() )
  {
    QgsDebugMsg( QString( "Finished after %1 features" ).arg( mFetched ) );
    close();

    mSource->mShared->ensureFeaturesCountedAtLeast( mFetched );

    return false;
  }

  // Now return the next feature from the queue
  if ( !mFetchGeometry )
  {
    feature.setGeometryAndOwnership( 0, 0 );
  }
  else
  {
    QgsGeometry* featureGeom = mFeatureQueue.front().geometryAndOwnership();
    feature.setGeometry( featureGeom );
  }
  feature.setFeatureId( mFeatureQueue.front().id() );
  feature.setAttributes( mFeatureQueue.front().attributes() );

  mFeatureQueue.dequeue();
  mFetched++;

  feature.setValid( true );
  feature.setFields( &mSource->mFields ); // allow name-based attribute lookups

  return true;
}