void QgsZonalStatistics::statisticsFromPreciseIntersection( void* band, const QgsGeometry& poly, int pixelOffsetX,
    int pixelOffsetY, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle& rasterBBox, FeatureStats &stats )
{
  stats.reset();

  double currentY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2;
  float* pixelData = ( float * ) CPLMalloc( sizeof( float ) );
  QgsGeometry pixelRectGeometry;

  double hCellSizeX = cellSizeX / 2.0;
  double hCellSizeY = cellSizeY / 2.0;
  double pixelArea = cellSizeX * cellSizeY;
  double weight = 0;

  for ( int row = 0; row < nCellsY; ++row )
  {
    double currentX = rasterBBox.xMinimum() + cellSizeX / 2.0 + pixelOffsetX * cellSizeX;
    for ( int col = 0; col < nCellsX; ++col )
    {
      if ( GDALRasterIO( band, GF_Read, pixelOffsetX + col, pixelOffsetY + row, nCellsX, 1, pixelData, 1, 1, GDT_Float32, 0, 0 ) != CE_None )
      {
        QgsDebugMsg( "Raster IO Error" );
      }

      if ( !validPixel( *pixelData ) )
        continue;

      pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) );
      if ( !pixelRectGeometry.isEmpty() )
      {
        //intersection
        QgsGeometry intersectGeometry = pixelRectGeometry.intersection( poly );
        if ( !intersectGeometry.isEmpty() )
        {
          double intersectionArea = intersectGeometry.area();
          if ( intersectionArea >= 0.0 )
          {
            weight = intersectionArea / pixelArea;
            stats.addValue( *pixelData, weight );
          }
        }
        pixelRectGeometry = QgsGeometry();
      }
      currentX += cellSizeX;
    }
    currentY -= cellSizeY;
  }
  CPLFree( pixelData );
}
Example #2
0
void QgsMapToolLabel::createRubberBands()
{
  delete mLabelRubberBand;
  delete mFeatureRubberBand;

  //label rubber band
  QgsRectangle rect = mCurrentLabel.pos.labelRect;
  mLabelRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::LineGeometry );
  mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) );
  mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMaximum() ) );
  mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMaximum() ) );
  mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMinimum() ) );
  mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) );
  mLabelRubberBand->setColor( QColor( 0, 255, 0, 65 ) );
  mLabelRubberBand->setWidth( 3 );
  mLabelRubberBand->show();

  //feature rubber band
  QgsVectorLayer* vlayer = mCurrentLabel.layer;
  if ( vlayer )
  {
    QgsFeature f;
    if ( currentFeature( f, true ) )
    {
      QgsGeometry geom = f.geometry();
      if ( !geom.isEmpty() )
      {
        QSettings settings;
        int r = settings.value( QStringLiteral( "/qgis/digitizing/line_color_red" ), 255 ).toInt();
        int g = settings.value( QStringLiteral( "/qgis/digitizing/line_color_green" ), 0 ).toInt();
        int b = settings.value( QStringLiteral( "/qgis/digitizing/line_color_blue" ), 0 ).toInt();
        int a = settings.value( QStringLiteral( "/qgis/digitizing/line_color_alpha" ), 200 ).toInt();
        mFeatureRubberBand = new QgsRubberBand( mCanvas, geom.type() );
        mFeatureRubberBand->setColor( QColor( r, g, b, a ) );
        mFeatureRubberBand->setToGeometry( geom, vlayer );
        mFeatureRubberBand->show();
      }
    }

    //fixpoint rubber band
    QgsPoint fixPoint;
    if ( currentLabelRotationPoint( fixPoint, false, false ) )
    {
      if ( mCanvas )
      {
        const QgsMapSettings& s = mCanvas->mapSettings();
        if ( s.hasCrsTransformEnabled() )
        {
          fixPoint = s.mapToLayerCoordinates( vlayer, fixPoint );
        }
      }

      QgsGeometry pointGeom = QgsGeometry::fromPoint( fixPoint );
      mFixPointRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::LineGeometry );
      mFixPointRubberBand->setColor( QColor( 0, 0, 255, 65 ) );
      mFixPointRubberBand->setToGeometry( pointGeom, vlayer );
      mFixPointRubberBand->show();
    }
  }
}
int QgsInterpolator::addVerticesToCache( const QgsGeometry& geom, bool zCoord, double attributeValue )
{
  if ( geom.isEmpty() )
    return 1;

  bool hasZValue = false;
  QByteArray wkb( geom.exportToWkb() );
  QgsConstWkbPtr currentWkbPtr( wkb );
  currentWkbPtr.readHeader();
  vertexData theVertex; //the current vertex

  QgsWkbTypes::Type wkbType = geom.wkbType();
  switch ( wkbType )
  {
    case QgsWkbTypes::Point25D:
      hasZValue = true;
      //intentional fall-through
      FALLTHROUGH;
    case QgsWkbTypes::Point:
    {
      currentWkbPtr >> theVertex.x >> theVertex.y;
      if ( zCoord && hasZValue )
      {
        currentWkbPtr >> theVertex.z;
      }
      else
      {
        theVertex.z = attributeValue;
      }
      mCachedBaseData.push_back( theVertex );
      break;
    }
Example #4
0
bool QgsOverlayUtils::sanitizeIntersectionResult( QgsGeometry &geom, QgsWkbTypes::GeometryType geometryType )
{
  if ( geom.isNull() )
  {
    // TODO: not sure if this ever happens - if it does, that means GEOS failed badly - would be good to have a test for such situation
    throw QgsProcessingException( QStringLiteral( "%1\n\n%2" ).arg( QObject::tr( "GEOS geoprocessing error: intersection failed." ), geom.lastError() ) );
  }

  // Intersection of geometries may give use also geometries we do not want in our results.
  // For example, two square polygons touching at the corner have a point as the intersection, but no area.
  // In other cases we may get a mixture of geometries in the output - we want to keep only the expected types.
  if ( QgsWkbTypes::flatType( geom.wkbType() ) == QgsWkbTypes::GeometryCollection )
  {
    // try to filter out irrelevant parts with different geometry type than what we want
    geom.convertGeometryCollectionToSubclass( geometryType );
    if ( geom.isEmpty() )
      return false;
  }

  if ( QgsWkbTypes::geometryType( geom.wkbType() ) != geometryType )
  {
    // we can't make use of this resulting geometry
    return false;
  }

  // some data providers are picky about the geometries we pass to them: we can't add single-part geometries
  // when we promised multi-part geometries, so ensure we have the right type
  geom.convertToMultiType();

  return true;
}
QgsGeometry QgsGeometryAnalyzer::locateBetweenMeasures( double fromMeasure, double toMeasure, const QgsGeometry& lineGeom )
{
  if ( lineGeom.isEmpty() )
  {
    return QgsGeometry();
  }

  QgsMultiPolyline resultGeom;

  //need to go with WKB and z coordinate until QgsGeometry supports M values
  QByteArray wkb( lineGeom.exportToWkb() );
  QgsConstWkbPtr wkbPtr( wkb );
  wkbPtr.readHeader();

  QgsWkbTypes::Type wkbType = lineGeom.wkbType();
  if ( wkbType != QgsWkbTypes::LineString25D && wkbType != QgsWkbTypes::MultiLineString25D )
  {
    return QgsGeometry();
  }

  if ( wkbType == QgsWkbTypes::LineString25D )
  {
    locateBetweenWkbString( wkbPtr, resultGeom, fromMeasure, toMeasure );
  }
  else if ( wkbType == QgsWkbTypes::MultiLineString25D )
  {
    int nLines;
    wkbPtr >> nLines;
    for ( int i = 0; i < nLines; ++i )
    {
      wkbPtr.readHeader();
      wkbPtr = locateBetweenWkbString( wkbPtr, resultGeom, fromMeasure, toMeasure );
    }
  }
Example #6
0
QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertexV2( QgsFeatureId featureId, int vertex )
{
    if ( !L->hasGeometryType() )
        return QgsVectorLayer::InvalidLayer;

    QgsGeometry geometry;
    if ( !cache()->geometry( featureId, geometry ) )
    {
        // it's not in cache: let's fetch it from layer
        QgsFeature f;
        if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
            return QgsVectorLayer::FetchFeatureFailed; // geometry not found

        geometry = f.geometry();
    }

    if ( !geometry.deleteVertex( vertex ) )
        return QgsVectorLayer::EditFailed;

    if ( geometry.geometry() && geometry.geometry()->nCoordinates() == 0 )
    {
        //last vertex deleted, set geometry to null
        geometry.setGeometry( nullptr );
    }

    L->editBuffer()->changeGeometry( featureId, geometry );
    return !geometry.isEmpty() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry;
}
Example #7
0
ErrorList topolTest::checkMultipart( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent )
{
  Q_UNUSED( tolerance );
  Q_UNUSED( layer2 );
  Q_UNUSED( layer1 );
  Q_UNUSED( isExtent );

  int i = 0;
  ErrorList errorList;
  QList<FeatureLayer>::Iterator it;
  for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( ++i );
    if ( testCancelled() )
      break;
    QgsGeometry g = it->feature.geometry();
    if ( g.isEmpty() )
    {
      QgsMessageLog::logMessage( tr( "Missing geometry in multipart check." ), tr( "Topology plugin" ) );
      continue;
    }
    if ( !_canExportToGeos( g ) )
      continue;
    if ( g.isMultipart() )
    {
      QgsRectangle r = g.boundingBox();
      QList<FeatureLayer> fls;
      fls << *it << *it;
      TopolErroMultiPart* err = new TopolErroMultiPart( r, g, fls );
      errorList << err;
    }
  }
  return errorList;
}
Example #8
0
double QgsDistanceArea::measureLength( const QgsGeometry& geometry ) const
{
  if ( geometry.isEmpty() )
    return 0.0;

  const QgsAbstractGeometry* geomV2 = geometry.geometry();
  return measure( geomV2, Length );
}
void QgsGeometryAnalyzer::addEventLayerFeature( QgsFeature& feature, const QgsGeometry& geom, const QgsGeometry& lineGeom, QgsVectorFileWriter* fileWriter, QgsFeatureList& memoryFeatures,
    int offsetField, double offsetScale, bool forceSingleType )
{
  if ( geom.isEmpty() )
  {
    return;
  }

  QList<QgsGeometry> geomList;
  if ( forceSingleType )
  {
    geomList = geom.asGeometryCollection();
  }
  else
  {
    geomList.push_back( geom );
  }

  QList<QgsGeometry>::iterator geomIt = geomList.begin();
  for ( ; geomIt != geomList.end(); ++geomIt )
  {
    //consider offset
    QgsGeometry newGeom = *geomIt;
    if ( offsetField >= 0 )
    {
      double offsetVal = feature.attribute( offsetField ).toDouble();
      offsetVal *= offsetScale;
      newGeom = createOffsetGeometry( *geomIt, lineGeom, offsetVal );
      if ( newGeom.isEmpty() )
      {
        continue;
      }
    }

    feature.setGeometry( newGeom );
    if ( fileWriter )
    {
      fileWriter->addFeature( feature );
    }
    else
    {
      memoryFeatures << feature;
    }
  }
}
Example #10
0
double QgsDistanceArea::measurePerimeter( const QgsGeometry& geometry ) const
{
  if ( geometry.isEmpty() )
    return 0.0;

  const QgsAbstractGeometry* geomV2 = geometry.geometry();
  if ( !geomV2 || geomV2->dimension() < 2 )
  {
    return 0.0;
  }

  if ( !mEllipsoidalMode || mEllipsoid == GEO_NONE )
  {
    return geomV2->perimeter();
  }

  //create list with (single) surfaces
  QList< const QgsSurface* > surfaces;
  const QgsSurface* surf = dynamic_cast<const QgsSurface*>( geomV2 );
  if ( surf )
  {
    surfaces.append( surf );
  }
  const QgsMultiSurface* multiSurf = dynamic_cast<const QgsMultiSurface*>( geomV2 );
  if ( multiSurf )
  {
    surfaces.reserve(( surf ? 1 : 0 ) + multiSurf->numGeometries() );
    for ( int i = 0; i  < multiSurf->numGeometries(); ++i )
    {
      surfaces.append( static_cast<const QgsSurface*>( multiSurf->geometryN( i ) ) );
    }
  }

  double length = 0;
  QList<const QgsSurface*>::const_iterator surfaceIt = surfaces.constBegin();
  for ( ; surfaceIt != surfaces.constEnd(); ++surfaceIt )
  {
    if ( !*surfaceIt )
    {
      continue;
    }

    QgsPolygonV2* poly = ( *surfaceIt )->surfaceToPolygon();
    const QgsCurve* outerRing = poly->exteriorRing();
    if ( outerRing )
    {
      length += measure( outerRing );
    }
    int nInnerRings = poly->numInteriorRings();
    for ( int i = 0; i < nInnerRings; ++i )
    {
      length += measure( poly->interiorRing( i ) );
    }
    delete poly;
  }
  return length;
}
Example #11
0
void QgsRubberBand::setToGeometry( const QgsGeometry& geom, QgsVectorLayer* layer )
{
  if ( geom.isEmpty() )
  {
    reset( mGeometryType );
    return;
  }

  reset( geom.type() );
  addGeometry( geom, layer );
}
Example #12
0
bool QgsSpatialQuery::hasValidGeometry( QgsFeature &feature )
{
  if ( !feature.isValid() )
    return false;

  QgsGeometry geom = feature.geometry();

  if ( geom.isNull() || geom.isEmpty() )
    return false;

  return true;

} // bool QgsSpatialQuery::hasValidGeometry(QgsFeature &feature)
Example #13
0
QgsGeometry QgsMapToolDeleteRing::ringUnderPoint( const QgsPoint& p, QgsFeatureId& fid, int& partNum, int& ringNum )
{
  //There is no clean way to find if we are inside the ring of a feature,
  //so we iterate over all the features visible in the canvas
  //If several rings are found at this position, the smallest one is chosen,
  //in order to be able to delete a ring inside another ring
  QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( toLayerCoordinates( vlayer, mCanvas->extent() ) ) );
  QgsFeature f;
  QgsGeometry g;
  QgsGeometry ringGeom;
  QgsMultiPolygon pol;
  QgsPolygon tempPol;
  QgsGeometry tempGeom;
  double area = std::numeric_limits<double>::max();
  while ( fit.nextFeature( f ) )
  {
    g = f.geometry();
    if ( g.isEmpty() )
      continue;
    if ( g.wkbType() == QgsWkbTypes::Polygon ||  g.wkbType()  == QgsWkbTypes::Polygon25D )
    {
      pol = QgsMultiPolygon() << g.asPolygon();
    }
    else
    {
      pol = g.asMultiPolygon();
    }

    for ( int i = 0; i < pol.size() ; ++i )
    {//for each part
      if ( pol[i].size() > 1 )
      {
        for ( int j = 1; j < pol[i].size();++j )
        {
          tempPol = QgsPolygon() << pol[i][j];
          tempGeom = QgsGeometry::fromPolygon( tempPol );
          if ( tempGeom.area() < area && tempGeom.contains( &p ) )
          {
            fid = f.id();
            partNum = i;
            ringNum = j;
            area = tempGeom.area();
            ringGeom = tempGeom;
          }
        }
      }
    }
  }
  return ringGeom;
}
Example #14
0
bool QgsTransectSample::otherTransectWithinDistance( const QgsGeometry& geom, double minDistLayerUnit, double minDistance, QgsSpatialIndex& sIndex,
    const QMap< QgsFeatureId, QgsGeometry >& lineFeatureMap, QgsDistanceArea& da )
{
  if ( geom.isEmpty() )
  {
    return false;
  }

  QgsGeometry buffer = geom.buffer( minDistLayerUnit, 8 );
  if ( buffer.isEmpty() )
  {
    return false;
  }
  QgsRectangle rect = buffer.boundingBox();
  QList<QgsFeatureId> lineIdList = sIndex.intersects( rect );

  QList<QgsFeatureId>::const_iterator lineIdIt = lineIdList.constBegin();
  for ( ; lineIdIt != lineIdList.constEnd(); ++lineIdIt )
  {
    const QMap< QgsFeatureId, QgsGeometry >::const_iterator idMapIt = lineFeatureMap.find( *lineIdIt );
    if ( idMapIt != lineFeatureMap.constEnd() )
    {
      double dist = 0;
      QgsPoint pt1, pt2;
      closestSegmentPoints( geom, idMapIt.value(), dist, pt1, pt2 );
      dist = da.measureLine( pt1, pt2 ); //convert degrees to meters if necessary

      if ( dist < minDistance )
      {
        return true;
      }
    }
  }

  return false;
}
Example #15
0
ErrorList topolTest::checkValid( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent )
{
  Q_UNUSED( tolerance );
  Q_UNUSED( layer1 );
  Q_UNUSED( layer2 );
  Q_UNUSED( isExtent );

  int i = 0;
  ErrorList errorList;
  QgsFeature f;

  QList<FeatureLayer>::Iterator it;

  for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it )
  {
    if ( !( ++i % 100 ) )
      emit progress( ++i );
    if ( testCancelled() )
      break;

    QgsGeometry g = it->feature.geometry();
    if ( g.isEmpty() )
    {
      QgsMessageLog::logMessage( tr( "Invalid geometry in validity test." ), tr( "Topology plugin" ) );
      continue;
    }

    GEOSGeometry* gGeos = g.exportToGeos();
    if ( !gGeos )
      continue;

    if ( !GEOSisValid_r( QgsGeometry::getGEOSHandler(), gGeos ) )
    {
      QgsRectangle r = g.boundingBox();
      QList<FeatureLayer> fls;
      fls << *it << *it;

      TopolErrorValid* err = new TopolErrorValid( r, g, fls );
      errorList << err;
    }
    GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), gGeos );
  }

  return errorList;
}
QgsGeometry QgsGeometryAnalyzer::dissolveFeature( const QgsFeature& f, const QgsGeometry& dissolveInto )
{
  if ( !f.hasGeometry() )
  {
    return dissolveInto;
  }

  QgsGeometry featureGeometry = f.geometry();

  if ( dissolveInto.isEmpty() )
  {
    return featureGeometry;
  }
  else
  {
    return dissolveInto.combine( featureGeometry );
  }
}
Example #17
0
//! Makes sure that what came out from difference of two geometries is good to be used in the output
static bool sanitizeDifferenceResult( QgsGeometry &geom )
{
  if ( geom.isNull() )
  {
    // TODO: not sure if this ever happens - if it does, that means GEOS failed badly - would be good to have a test for such situation
    throw QgsProcessingException( QStringLiteral( "%1\n\n%2" ).arg( QObject::tr( "GEOS geoprocessing error: difference failed." ), geom.lastError() ) );
  }

  // if geomB covers the whole source geometry, we get an empty geometry collection
  if ( geom.isEmpty() )
    return false;

  // some data providers are picky about the geometries we pass to them: we can't add single-part geometries
  // when we promised multi-part geometries, so ensure we have the right type
  geom.convertToMultiType();

  return true;
}
Example #18
0
//! Returns a simplified version the specified geometry (Removing duplicated points) when is applied the specified map2pixel context
QgsGeometry QgsMapToPixelSimplifier::simplify( const QgsGeometry& geometry ) const
{
  if ( geometry.isEmpty() )
  {
    return QgsGeometry();
  }
  if ( mSimplifyFlags == QgsMapToPixelSimplifier::NoFlags )
  {
    return geometry;
  }

  // Check whether the geometry can be simplified using the map2pixel context
  const QgsWkbTypes::Type singleType = QgsWkbTypes::singleType( geometry.wkbType() );
  const QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( singleType );
  if ( flatType == QgsWkbTypes::Point )
  {
    return geometry;
  }

  const bool isaLinearRing = flatType == QgsWkbTypes::Polygon;
  const int numPoints = geometry.geometry()->nCoordinates();

  if ( numPoints <= ( isaLinearRing ? 6 : 3 ) )
  {
    // No simplify simple geometries
    return geometry;
  }

  const QgsRectangle envelope = geometry.boundingBox();
  if ( qMax( envelope.width(), envelope.height() ) / numPoints > mTolerance * 2.0 )
  {
    //points are in average too far apart to lead to any significant simplification
    return geometry;
  }

  return simplifyGeometry( mSimplifyFlags, mSimplifyAlgorithm, geometry.wkbType(), *geometry.geometry(), envelope, mTolerance, false );
}
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 && nullptr != mSpatialIndex;

  // 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( QStringLiteral( "Wkt" ), 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( QStringLiteral( "X" ), mWktFieldName ) );
    }
    if ( mYFieldIndex < 0 )
    {
      messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( QStringLiteral( "Y" ), mWktFieldName ) );
    }
  }
  if ( !messages.isEmpty() )
  {
    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;
  bool foundFirstGeometry = false;

  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++;
        mNumberFeatures++;
      }
      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;
        if ( !mWktHasPrefix && sWkt.indexOf( WktPrefixRegexp ) >= 0 )
          mWktHasPrefix = true;
        geom = geomFromWkt( sWkt, mWktHasPrefix );

        if ( !geom.isEmpty() )
        {
          QgsWkbTypes::Type type = geom.wkbType();
          if ( type != QgsWkbTypes::NoGeometry )
          {
            if ( mGeometryType == QgsWkbTypes::UnknownGeometry || geom.type() == mGeometryType )
            {
              mGeometryType = geom.type();
              if ( !foundFirstGeometry )
              {
                mNumberFeatures++;
                mWkbType = type;
                mExtent = geom.boundingBox();
                foundFirstGeometry = true;
              }
              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 );
              }
            }
            else
            {
              nIncompatibleGeometry++;
              geomValid = false;
            }
          }
        }
        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();
      QString sY = mYFieldIndex < parts.size() ? parts[mYFieldIndex] : QString();
      if ( sX.isEmpty() && sY.isEmpty() )
      {
        nEmptyGeometry++;
        mNumberFeatures++;
      }
      else
      {
        QgsPoint pt;
        bool ok = pointFromXY( sX, sY, pt, mDecimalPoint, mXyDms );

        if ( ok )
        {
          if ( foundFirstGeometry )
          {
            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 = QgsWkbTypes::Point;
            mGeometryType = QgsWkbTypes::PointGeometry;
            foundFirstGeometry = true;
          }
          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 = QgsWkbTypes::NoGeometry;
      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, QLatin1String( "." ) );
        }
        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 = QStringLiteral( "text" );
    if ( i < csvtTypes.size() )
    {
      if ( csvtTypes[i] == QLatin1String( "integer" ) )
      {
        fieldType = QVariant::Int;
        typeName = QStringLiteral( "integer" );
      }
      else if ( csvtTypes[i] == QLatin1String( "long" ) || csvtTypes[i] == QLatin1String( "longlong" ) || csvtTypes[i] == QLatin1String( "int8" ) )
      {
        fieldType = QVariant::LongLong; //QVariant doesn't support long
        typeName = QStringLiteral( "longlong" );
      }
      else if ( csvtTypes[i] == QLatin1String( "real" ) || csvtTypes[i] == QLatin1String( "double" ) )
      {
        fieldType = QVariant::Double;
        typeName = QStringLiteral( "double" );
      }
    }
    else if ( i < couldBeInt.size() )
    {
      if ( couldBeInt[i] )
      {
        fieldType = QVariant::Int;
        typeName = QStringLiteral( "integer" );
      }
      else if ( couldBeLongLong[i] )
      {
        fieldType = QVariant::LongLong;
        typeName = QStringLiteral( "longlong" );
      }
      else if ( couldBeDouble[i] )
      {
        fieldType = QVariant::Double;
        typeName = QStringLiteral( "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 have 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 != QgsWkbTypes::UnknownGeometry;
  mLayerValid = mValid;

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


}
bool QgsGeometryAnalyzer::buffer( QgsVectorLayer* layer, const QString& shapefileName, double bufferDistance,
                                  bool onlySelectedFeatures, bool dissolve, int bufferDistanceField, QProgressDialog* p )
{
  if ( !layer )
  {
    return false;
  }

  QgsVectorDataProvider* dp = layer->dataProvider();
  if ( !dp )
  {
    return false;
  }

  QgsWkbTypes::Type outputType = QgsWkbTypes::Polygon;
  if ( dissolve )
  {
    outputType = QgsWkbTypes::MultiPolygon;
  }
  QgsCoordinateReferenceSystem crs = layer->crs();

  QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, crs );
  QgsFeature currentFeature;
  QgsGeometry dissolveGeometry; //dissolve geometry (if dissolve enabled)

  //take only selection
  if ( onlySelectedFeatures )
  {
    //use QgsVectorLayer::featureAtId
    const QgsFeatureIds selection = layer->selectedFeaturesIds();
    if ( p )
    {
      p->setMaximum( selection.size() );
    }

    int processedFeatures = 0;
    QgsFeatureIds::const_iterator it = selection.constBegin();
    for ( ; it != selection.constEnd(); ++it )
    {
      if ( p )
      {
        p->setValue( processedFeatures );
      }

      if ( p && p->wasCanceled() )
      {
        break;
      }
      if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) )
      {
        continue;
      }
      bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, dissolveGeometry, bufferDistance, bufferDistanceField );
      ++processedFeatures;
    }

    if ( p )
    {
      p->setValue( selection.size() );
    }
  }
  //take all features
  else
  {
    QgsFeatureIterator fit = layer->getFeatures();

    int featureCount = layer->featureCount();
    if ( p )
    {
      p->setMaximum( featureCount );
    }
    int processedFeatures = 0;

    while ( fit.nextFeature( currentFeature ) )
    {
      if ( p )
      {
        p->setValue( processedFeatures );
      }
      if ( p && p->wasCanceled() )
      {
        break;
      }
      bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, dissolveGeometry, bufferDistance, bufferDistanceField );
      ++processedFeatures;
    }
    if ( p )
    {
      p->setValue( featureCount );
    }
  }

  if ( dissolve )
  {
    QgsFeature dissolveFeature;
    if ( dissolveGeometry.isEmpty() )
    {
      QgsDebugMsg( "no dissolved geometry - should not happen" );
      return false;
    }
    dissolveFeature.setGeometry( dissolveGeometry );
    vWriter.addFeature( dissolveFeature );
  }
  return true;
}
bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer* eventLayer, int lineField, int eventField, QgsFeatureIds &unlocatedFeatureIds, const QString& outputLayer,
                                      const QString& outputFormat, int locationField1, int locationField2, int offsetField, double offsetScale,
                                      bool forceSingleGeometry, QgsVectorDataProvider* memoryProvider, QProgressDialog* p )
{
  if ( !lineLayer || !eventLayer || !lineLayer->isValid() || !eventLayer->isValid() )
  {
    return false;
  }

  //create line field / id map for line layer
  QMultiHash< QString, QgsFeature > lineLayerIdMap; //1:n possible (e.g. several linear reference geometries for one feature in the event layer)
  QgsFeatureIterator fit = lineLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() << lineField ) );
  QgsFeature fet;
  while ( fit.nextFeature( fet ) )
  {
    lineLayerIdMap.insert( fet.attribute( lineField ).toString(), fet );
  }

  //create output datasource or attributes in memory provider
  QgsVectorFileWriter* fileWriter = nullptr;
  QgsFeatureList memoryProviderFeatures;
  if ( !memoryProvider )
  {
    QgsWkbTypes::Type memoryProviderType = QgsWkbTypes::MultiLineString;
    if ( locationField2 == -1 )
    {
      memoryProviderType = forceSingleGeometry ? QgsWkbTypes::Point : QgsWkbTypes::MultiPoint;
    }
    else
    {
      memoryProviderType = forceSingleGeometry ? QgsWkbTypes::LineString : QgsWkbTypes::MultiLineString;
    }
    fileWriter = new QgsVectorFileWriter( outputLayer,
                                          eventLayer->dataProvider()->encoding(),
                                          eventLayer->fields(),
                                          memoryProviderType,
                                          lineLayer->crs(),
                                          outputFormat );
  }
  else
  {
    memoryProvider->addAttributes( eventLayer->fields().toList() );
  }

  //iterate over eventLayer and write new features to output file or layer
  fit = eventLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) );
  QgsGeometry lrsGeom;
  double measure1, measure2 = 0.0;

  int nEventFeatures = eventLayer->featureCount();
  int featureCounter = 0;
  int nOutputFeatures = 0; //number of output features for the current event feature
  if ( p )
  {
    p->setWindowModality( Qt::WindowModal );
    p->setMinimum( 0 );
    p->setMaximum( nEventFeatures );
    p->show();
  }

  while ( fit.nextFeature( fet ) )
  {
    nOutputFeatures = 0;

    //update progress dialog
    if ( p )
    {
      if ( p->wasCanceled() )
      {
        break;
      }
      p->setValue( featureCounter );
      ++featureCounter;
    }

    measure1 = fet.attribute( locationField1 ).toDouble();
    if ( locationField2 != -1 )
    {
      measure2 = fet.attribute( locationField2 ).toDouble();
      if ( qgsDoubleNear(( measure2 - measure1 ), 0.0 ) )
      {
        continue;
      }
    }

    QList<QgsFeature> featureIdList = lineLayerIdMap.values( fet.attribute( eventField ).toString() );
    QList<QgsFeature>::iterator featureIdIt = featureIdList.begin();
    for ( ; featureIdIt != featureIdList.end(); ++featureIdIt )
    {
      if ( locationField2 == -1 )
      {
        lrsGeom = locateAlongMeasure( measure1, featureIdIt->geometry() );
      }
      else
      {
        lrsGeom = locateBetweenMeasures( measure1, measure2, featureIdIt->geometry() );
      }

      if ( !lrsGeom.isEmpty() )
      {
        ++nOutputFeatures;
        addEventLayerFeature( fet, lrsGeom, featureIdIt->geometry(), fileWriter, memoryProviderFeatures, offsetField, offsetScale, forceSingleGeometry );
      }
    }
    if ( nOutputFeatures < 1 )
    {
      unlocatedFeatureIds.insert( fet.id() );
    }
  }

  if ( p )
  {
    p->setValue( nEventFeatures );
  }

  if ( memoryProvider )
  {
    memoryProvider->addFeatures( memoryProviderFeatures );
  }
  delete fileWriter;
  return true;
}
bool QgsGeometryAnalyzer::convexHull( QgsVectorLayer* layer, const QString& shapefileName,
                                      bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p )
{
  if ( !layer )
  {
    return false;
  }
  QgsVectorDataProvider* dp = layer->dataProvider();
  if ( !dp )
  {
    return false;
  }
  bool useField = false;
  if ( uniqueIdField == -1 )
  {
    uniqueIdField = 0;
  }
  else
  {
    useField = true;
  }
  QgsFields fields;
  fields.append( QgsField( QStringLiteral( "UID" ), QVariant::String ) );
  fields.append( QgsField( QStringLiteral( "AREA" ), QVariant::Double ) );
  fields.append( QgsField( QStringLiteral( "PERIM" ), QVariant::Double ) );

  QgsWkbTypes::Type outputType = QgsWkbTypes::Polygon;
  QgsCoordinateReferenceSystem crs = layer->crs();

  QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, crs );
  QgsFeature currentFeature;
  QgsGeometry dissolveGeometry; //dissolve geometry
  QMultiMap<QString, QgsFeatureId> map;

  if ( onlySelectedFeatures )
  {
    //use QgsVectorLayer::featureAtId
    const QgsFeatureIds selection = layer->selectedFeaturesIds();
    QgsFeatureIds::const_iterator it = selection.constBegin();
    for ( ; it != selection.constEnd(); ++it )
    {
#if 0
      if ( p )
      {
        p->setValue( processedFeatures );
      }
      if ( p && p->wasCanceled() )
      {
        // break; // it may be better to do something else here?
        return false;
      }
#endif
      if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) )
      {
        continue;
      }
      map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() );
    }
  }
  else
  {
    QgsFeatureIterator fit = layer->getFeatures();
    while ( fit.nextFeature( currentFeature ) )
    {
#if 0
      if ( p )
      {
        p->setValue( processedFeatures );
      }
      if ( p && p->wasCanceled() )
      {
        // break; // it may be better to do something else here?
        return false;
      }
#endif
      map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() );
    }
  }

  QMultiMap<QString, QgsFeatureId>::const_iterator jt = map.constBegin();
  while ( jt != map.constEnd() )
  {
    QString currentKey = jt.key();
    int processedFeatures = 0;
    //take only selection
    if ( onlySelectedFeatures )
    {
      //use QgsVectorLayer::featureAtId
      const QgsFeatureIds selection = layer->selectedFeaturesIds();
      if ( p )
      {
        p->setMaximum( selection.size() );
      }
      processedFeatures = 0;
      while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
      {
        if ( p && p->wasCanceled() )
        {
          break;
        }
        if ( selection.contains( jt.value() ) )
        {
          if ( p )
          {
            p->setValue( processedFeatures );
          }
          if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) )
          {
            continue;
          }
          convexFeature( currentFeature, processedFeatures, dissolveGeometry );
          ++processedFeatures;
        }
        ++jt;
      }
      QList<double> values;
      if ( dissolveGeometry.isEmpty() )
      {
        QgsDebugMsg( "no dissolved geometry - should not happen" );
        return false;
      }
      dissolveGeometry = dissolveGeometry.convexHull();
      values = simpleMeasure( dissolveGeometry );
      QgsAttributes attributes( 3 );
      attributes[0] = QVariant( currentKey );
      attributes[1] = values.at( 0 );
      attributes[2] = values.at( 1 );
      QgsFeature dissolveFeature;
      dissolveFeature.setAttributes( attributes );
      dissolveFeature.setGeometry( dissolveGeometry );
      vWriter.addFeature( dissolveFeature );
    }
    //take all features
    else
    {
      int featureCount = layer->featureCount();
      if ( p )
      {
        p->setMaximum( featureCount );
      }
      processedFeatures = 0;
      while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
      {
        if ( p )
        {
          p->setValue( processedFeatures );
        }

        if ( p && p->wasCanceled() )
        {
          break;
        }
        if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) )
        {
          continue;
        }
        convexFeature( currentFeature, processedFeatures, dissolveGeometry );
        ++processedFeatures;
        ++jt;
      }
      QList<double> values;
      if ( dissolveGeometry.isEmpty() )
      {
        QgsDebugMsg( "no dissolved geometry - should not happen" );
        return false;
      }
      dissolveGeometry = dissolveGeometry.convexHull();
      // values = simpleMeasure( tmpGeometry );
      values = simpleMeasure( dissolveGeometry );
      QgsAttributes attributes;
      attributes[0] = QVariant( currentKey );
      attributes[1] = QVariant( values[ 0 ] );
      attributes[2] = QVariant( values[ 1 ] );
      QgsFeature dissolveFeature;
      dissolveFeature.setAttributes( attributes );
      dissolveFeature.setGeometry( dissolveGeometry );
      vWriter.addFeature( dissolveFeature );
    }
  }
  return true;
}
Example #23
0
int QgsTransectSample::createSample( QProgressDialog* pd )
{
  Q_UNUSED( pd );

  if ( !mStrataLayer || !mStrataLayer->isValid() )
  {
    return 1;
  }

  if ( !mBaselineLayer || !mBaselineLayer->isValid() )
  {
    return 2;
  }

  //stratum id is not necessarily an integer
  QVariant::Type stratumIdType = QVariant::Int;
  if ( !mStrataIdAttribute.isEmpty() )
  {
    stratumIdType = mStrataLayer->fields().field( mStrataIdAttribute ).type();
  }

  //create vector file writers for output
  QgsFields outputPointFields;
  outputPointFields.append( QgsField( "id", stratumIdType ) );
  outputPointFields.append( QgsField( "station_id", QVariant::Int ) );
  outputPointFields.append( QgsField( "stratum_id", stratumIdType ) );
  outputPointFields.append( QgsField( "station_code", QVariant::String ) );
  outputPointFields.append( QgsField( "start_lat", QVariant::Double ) );
  outputPointFields.append( QgsField( "start_long", QVariant::Double ) );

  QgsVectorFileWriter outputPointWriter( mOutputPointLayer, "utf-8", outputPointFields, QgsWkbTypes::Point,
                                         mStrataLayer->crs() );
  if ( outputPointWriter.hasError() != QgsVectorFileWriter::NoError )
  {
    return 3;
  }

  outputPointFields.append( QgsField( "bearing", QVariant::Double ) ); //add bearing attribute for lines
  QgsVectorFileWriter outputLineWriter( mOutputLineLayer, "utf-8", outputPointFields, QgsWkbTypes::LineString,
                                        mStrataLayer->crs() );
  if ( outputLineWriter.hasError() != QgsVectorFileWriter::NoError )
  {
    return 4;
  }

  QgsFields usedBaselineFields;
  usedBaselineFields.append( QgsField( "stratum_id", stratumIdType ) );
  usedBaselineFields.append( QgsField( "ok", QVariant::String ) );
  QgsVectorFileWriter usedBaselineWriter( mUsedBaselineLayer, "utf-8", usedBaselineFields, QgsWkbTypes::LineString,
                                          mStrataLayer->crs() );
  if ( usedBaselineWriter.hasError() != QgsVectorFileWriter::NoError )
  {
    return 5;
  }

  //debug: write clipped buffer bounds with stratum id to same directory as out_point
  QFileInfo outputPointInfo( mOutputPointLayer );
  QString bufferClipLineOutput = outputPointInfo.absolutePath() + "/out_buffer_clip_line.shp";
  QgsFields bufferClipLineFields;
  bufferClipLineFields.append( QgsField( "id", stratumIdType ) );
  QgsVectorFileWriter bufferClipLineWriter( bufferClipLineOutput, "utf-8", bufferClipLineFields, QgsWkbTypes::LineString, mStrataLayer->crs() );

  //configure distanceArea depending on minDistance units and output CRS
  QgsDistanceArea distanceArea;
  distanceArea.setSourceCrs( mStrataLayer->crs().srsid() );
  if ( mMinDistanceUnits == Meters )
  {
    distanceArea.setEllipsoidalMode( true );
  }
  else
  {
    distanceArea.setEllipsoidalMode( false );
  }

  //possibility to transform output points to lat/long
  QgsCoordinateTransform toLatLongTransform( mStrataLayer->crs(), QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ) );

  //init random number generator
  mt_srand( QTime::currentTime().msec() );

  QgsFeatureRequest fr;
  fr.setSubsetOfAttributes( QStringList() << mStrataIdAttribute << mMinDistanceAttribute << mNPointsAttribute, mStrataLayer->fields() );
  QgsFeatureIterator strataIt = mStrataLayer->getFeatures( fr );

  QgsFeature fet;
  int nTotalTransects = 0;
  int nFeatures = 0;

  if ( pd )
  {
    pd->setMaximum( mStrataLayer->featureCount() );
  }

  while ( strataIt.nextFeature( fet ) )
  {
    if ( pd )
    {
      pd->setValue( nFeatures );
    }
    if ( pd && pd->wasCanceled() )
    {
      break;
    }

    if ( !fet.hasGeometry() )
    {
      continue;
    }
    QgsGeometry strataGeom = fet.geometry();

    //find baseline for strata
    QVariant strataId = fet.attribute( mStrataIdAttribute );
    QgsGeometry baselineGeom = findBaselineGeometry( strataId.isValid() ? strataId : -1 );
    if ( baselineGeom.isEmpty() )
    {
      continue;
    }

    double minDistance = fet.attribute( mMinDistanceAttribute ).toDouble();
    double minDistanceLayerUnits = minDistance;
    //if minDistance is in meters and the data in degrees, we need to apply a rough conversion for the buffer distance
    double bufferDist = bufferDistance( minDistance );
    if ( mMinDistanceUnits == Meters && mStrataLayer->crs().mapUnits() == QgsUnitTypes::DistanceDegrees )
    {
      minDistanceLayerUnits = minDistance / 111319.9;
    }

    QgsGeometry clippedBaseline = strataGeom.intersection( baselineGeom );
    if ( !clippedBaseline || clippedBaseline.wkbType() == QgsWkbTypes::Unknown )
    {
      continue;
    }
    QgsGeometry* bufferLineClipped = clipBufferLine( strataGeom, &clippedBaseline, bufferDist );
    if ( !bufferLineClipped )
    {
      continue;
    }

    //save clipped baseline to file
    QgsFeature blFeature( usedBaselineFields );
    blFeature.setGeometry( clippedBaseline );
    blFeature.setAttribute( "stratum_id", strataId );
    blFeature.setAttribute( "ok", "f" );
    usedBaselineWriter.addFeature( blFeature );

    //start loop to create random points along the baseline
    int nTransects = fet.attribute( mNPointsAttribute ).toInt();
    int nCreatedTransects = 0;
    int nIterations = 0;
    int nMaxIterations = nTransects * 50;

    QgsSpatialIndex sIndex; //to check minimum distance
    QMap< QgsFeatureId, QgsGeometry > lineFeatureMap;

    while ( nCreatedTransects < nTransects && nIterations < nMaxIterations )
    {
      double randomPosition = (( double )mt_rand() / MD_RAND_MAX ) * clippedBaseline.length();
      QgsGeometry samplePoint = clippedBaseline.interpolate( randomPosition );
      ++nIterations;
      if ( samplePoint.isEmpty() )
      {
        continue;
      }
      QgsPoint sampleQgsPoint = samplePoint.asPoint();
      QgsPoint latLongSamplePoint = toLatLongTransform.transform( sampleQgsPoint );

      QgsFeature samplePointFeature( outputPointFields );
      samplePointFeature.setGeometry( samplePoint );
      samplePointFeature.setAttribute( "id", nTotalTransects + 1 );
      samplePointFeature.setAttribute( "station_id", nCreatedTransects + 1 );
      samplePointFeature.setAttribute( "stratum_id", strataId );
      samplePointFeature.setAttribute( "station_code", strataId.toString() + '_' + QString::number( nCreatedTransects + 1 ) );
      samplePointFeature.setAttribute( "start_lat", latLongSamplePoint.y() );
      samplePointFeature.setAttribute( "start_long", latLongSamplePoint.x() );

      //find closest point on clipped buffer line
      QgsPoint minDistPoint;

      int afterVertex;
      if ( bufferLineClipped->closestSegmentWithContext( sampleQgsPoint, minDistPoint, afterVertex ) < 0 )
      {
        continue;
      }

      //bearing between sample point and min dist point (transect direction)
      double bearing = distanceArea.bearing( sampleQgsPoint, minDistPoint ) / M_PI * 180.0;

      QgsPoint ptFarAway( sampleQgsPoint.x() + ( minDistPoint.x() - sampleQgsPoint.x() ) * 1000000,
                          sampleQgsPoint.y() + ( minDistPoint.y() - sampleQgsPoint.y() ) * 1000000 );
      QgsPolyline lineFarAway;
      lineFarAway << sampleQgsPoint << ptFarAway;
      QgsGeometry lineFarAwayGeom = QgsGeometry::fromPolyline( lineFarAway );
      QgsGeometry lineClipStratum = lineFarAwayGeom.intersection( strataGeom );
      if ( lineClipStratum.isEmpty() )
      {
        continue;
      }

      //cancel if distance between sample point and line is too large (line does not start at point
      if ( lineClipStratum.distance( samplePoint ) > 0.000001 )
      {
        continue;
      }

      //if lineClipStratum is a multiline, take the part line closest to sampleQgsPoint
      if ( lineClipStratum.wkbType() == QgsWkbTypes::MultiLineString
           || lineClipStratum.wkbType() == QgsWkbTypes::MultiLineString25D )
      {
        QgsGeometry singleLine = closestMultilineElement( sampleQgsPoint, lineClipStratum );
        if ( !singleLine.isEmpty() )
        {
          lineClipStratum = singleLine;
        }
      }

      //cancel if length of lineClipStratum is too small
      double transectLength = distanceArea.measureLength( lineClipStratum );
      if ( transectLength < mMinTransectLength )
      {
        continue;
      }

      //search closest existing profile. Cancel if dist < minDist
      if ( otherTransectWithinDistance( lineClipStratum, minDistanceLayerUnits, minDistance, sIndex, lineFeatureMap, distanceArea ) )
      {
        continue;
      }

      QgsFeatureId fid( nCreatedTransects );
      QgsFeature sampleLineFeature( outputPointFields, fid );
      sampleLineFeature.setGeometry( lineClipStratum );
      sampleLineFeature.setAttribute( "id", nTotalTransects + 1 );
      sampleLineFeature.setAttribute( "station_id", nCreatedTransects + 1 );
      sampleLineFeature.setAttribute( "stratum_id", strataId );
      sampleLineFeature.setAttribute( "station_code", strataId.toString() + '_' + QString::number( nCreatedTransects + 1 ) );
      sampleLineFeature.setAttribute( "start_lat", latLongSamplePoint.y() );
      sampleLineFeature.setAttribute( "start_long", latLongSamplePoint.x() );
      sampleLineFeature.setAttribute( "bearing", bearing );
      outputLineWriter.addFeature( sampleLineFeature );

      //add point to file writer here.
      //It can only be written if the corresponding transect has been as well
      outputPointWriter.addFeature( samplePointFeature );

      sIndex.insertFeature( sampleLineFeature );
      lineFeatureMap.insert( fid, lineClipStratum );

      ++nTotalTransects;
      ++nCreatedTransects;
    }

    QgsFeature bufferClipFeature;
    bufferClipFeature.setGeometry( *bufferLineClipped );
    delete bufferLineClipped;
    bufferClipFeature.setAttribute( "id", strataId );
    bufferClipLineWriter.addFeature( bufferClipFeature );
    //delete bufferLineClipped;

    ++nFeatures;
  }

  if ( pd )
  {
    pd->setValue( mStrataLayer->featureCount() );
  }

  return 0;
}
QgsGeometry QgsGeometryAnalyzer::createOffsetGeometry( const QgsGeometry& geom, const QgsGeometry& lineGeom, double offset )
{
  if ( !geom || lineGeom.isEmpty() )
  {
    return QgsGeometry();
  }

  QList<QgsGeometry> inputGeomList;

  if ( geom.isMultipart() )
  {
    inputGeomList = geom.asGeometryCollection();
  }
  else
  {
    inputGeomList.push_back( geom );
  }

  QList<GEOSGeometry*> outputGeomList;
  QList<QgsGeometry>::const_iterator inputGeomIt = inputGeomList.constBegin();
  GEOSContextHandle_t geosctxt = QgsGeometry::getGEOSHandler();
  for ( ; inputGeomIt != inputGeomList.constEnd(); ++inputGeomIt )
  {
    if ( geom.type() == QgsWkbTypes::LineGeometry )
    {
      GEOSGeometry* inputGeomItGeos = inputGeomIt->exportToGeos();
      GEOSGeometry* offsetGeom = GEOSOffsetCurve_r( geosctxt, inputGeomItGeos, -offset, 8 /*quadSegments*/, 0 /*joinStyle*/, 5.0 /*mitreLimit*/ );
      GEOSGeom_destroy_r( geosctxt, inputGeomItGeos );
      if ( !offsetGeom || !GEOSisValid_r( geosctxt, offsetGeom ) )
      {
        return QgsGeometry();
      }
      if ( !GEOSisValid_r( geosctxt, offsetGeom ) || GEOSGeomTypeId_r( geosctxt, offsetGeom ) != GEOS_LINESTRING || GEOSGeomGetNumPoints_r( geosctxt, offsetGeom ) < 1 )
      {
        GEOSGeom_destroy_r( geosctxt, offsetGeom );
        return QgsGeometry();
      }
      outputGeomList.push_back( offsetGeom );
    }
    else if ( geom.type() == QgsWkbTypes::PointGeometry )
    {
      QgsPoint p = ( *inputGeomIt ).asPoint();
      p = createPointOffset( p.x(), p.y(), offset, lineGeom );
      GEOSCoordSequence* ptSeq = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
      GEOSCoordSeq_setX_r( geosctxt, ptSeq, 0, p.x() );
      GEOSCoordSeq_setY_r( geosctxt, ptSeq, 0, p.y() );
      GEOSGeometry* geosPt = GEOSGeom_createPoint_r( geosctxt, ptSeq );
      outputGeomList.push_back( geosPt );
    }
  }

  QgsGeometry outGeometry;
  if ( !geom.isMultipart() )
  {
    GEOSGeometry* outputGeom = outputGeomList.at( 0 );
    if ( outputGeom )
    {
      outGeometry.fromGeos( outputGeom );
    }
  }
  else
  {
    GEOSGeometry** geomArray = new GEOSGeometry*[outputGeomList.size()];
    for ( int i = 0; i < outputGeomList.size(); ++i )
    {
      geomArray[i] = outputGeomList.at( i );
    }
    GEOSGeometry* collection = nullptr;
    if ( geom.type() == QgsWkbTypes::PointGeometry )
    {
      collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOINT, geomArray, outputGeomList.size() );
    }
    else if ( geom.type() == QgsWkbTypes::LineGeometry )
    {
      collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTILINESTRING, geomArray, outputGeomList.size() );
    }
    outGeometry.fromGeos( collection );
    delete[] geomArray;
  }
  return outGeometry;
}
Example #25
0
void QgsMapToolReshape::reshape( QgsVectorLayer *vlayer )
{
  QgsPointXY firstPoint = points().at( 0 );
  QgsRectangle bbox( firstPoint.x(), firstPoint.y(), firstPoint.x(), firstPoint.y() );
  for ( int i = 1; i < size(); ++i )
  {
    bbox.combineExtentWith( points().at( i ).x(), points().at( i ).y() );
  }

  QgsLineString reshapeLineString( points() );
  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) )
    reshapeLineString.addZValue( defaultZValue() );

  //query all the features that intersect bounding box of capture line
  QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() ) );
  QgsFeature f;
  int reshapeReturn;
  bool reshapeDone = false;
  bool isBinding = isBindingLine( vlayer, bbox );

  vlayer->beginEditCommand( tr( "Reshape" ) );
  while ( fit.nextFeature( f ) )
  {
    //query geometry
    //call geometry->reshape(mCaptureList)
    //register changed geometry in vector layer
    QgsGeometry geom = f.geometry();
    if ( !geom.isNull() )
    {
      // in case of a binding line, we just want to update the line from
      // the starting point and not both side
      if ( isBinding && !geom.asPolyline().contains( points().first() ) )
        continue;

      reshapeReturn = geom.reshapeGeometry( reshapeLineString );
      if ( reshapeReturn == 0 )
      {
        //avoid intersections on polygon layers
        if ( vlayer->geometryType() == QgsWkbTypes::PolygonGeometry )
        {
          //ignore all current layer features as they should be reshaped too
          QHash<QgsVectorLayer *, QSet<QgsFeatureId> > ignoreFeatures;
          ignoreFeatures.insert( vlayer, vlayer->allFeatureIds() );

          if ( geom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers(), ignoreFeatures ) != 0 )
          {
            emit messageEmitted( tr( "An error was reported during intersection removal" ), Qgis::Critical );
            vlayer->destroyEditCommand();
            stopCapturing();
            return;
          }

          if ( geom.isEmpty() ) //intersection removal might have removed the whole geometry
          {
            emit messageEmitted( tr( "The feature cannot be reshaped because the resulting geometry is empty" ), Qgis::Critical );
            vlayer->destroyEditCommand();
            return;
          }
        }

        vlayer->changeGeometry( f.id(), geom );
        reshapeDone = true;
      }
    }
  }

  if ( reshapeDone )
  {
    vlayer->endEditCommand();
  }
  else
  {
    vlayer->destroyEditCommand();
  }
}
Example #26
0
QVariantMap QgsClipAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
  std::unique_ptr< QgsFeatureSource > featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
  if ( !featureSource )
    throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );

  std::unique_ptr< QgsFeatureSource > maskSource( parameterAsSource( parameters, QStringLiteral( "OVERLAY" ), context ) );
  if ( !maskSource )
    throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "OVERLAY" ) ) );

  QString dest;
  QgsWkbTypes::GeometryType sinkType = QgsWkbTypes::geometryType( featureSource->wkbType() );
  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, featureSource->fields(), QgsWkbTypes::multiType( featureSource->wkbType() ), featureSource->sourceCrs() ) );

  if ( !sink )
    throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );

  // first build up a list of clip geometries
  QVector< QgsGeometry > clipGeoms;
  QgsFeatureIterator it = maskSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QList< int >() ).setDestinationCrs( featureSource->sourceCrs(), context.transformContext() ) );
  QgsFeature f;
  while ( it.nextFeature( f ) )
  {
    if ( f.hasGeometry() )
      clipGeoms << f.geometry();
  }

  QVariantMap outputs;
  outputs.insert( QStringLiteral( "OUTPUT" ), dest );

  if ( clipGeoms.isEmpty() )
    return outputs;

  // are we clipping against a single feature? if so, we can show finer progress reports
  bool singleClipFeature = false;
  QgsGeometry combinedClipGeom;
  if ( clipGeoms.length() > 1 )
  {
    combinedClipGeom = QgsGeometry::unaryUnion( clipGeoms );
    if ( combinedClipGeom.isEmpty() )
    {
      throw QgsProcessingException( QObject::tr( "Could not create the combined clip geometry: %1" ).arg( combinedClipGeom.lastError() ) );
    }
    singleClipFeature = false;
  }
  else
  {
    combinedClipGeom = clipGeoms.at( 0 );
    singleClipFeature = true;
  }

  // use prepared geometries for faster intersection tests
  std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( combinedClipGeom.constGet() ) );
  engine->prepareGeometry();

  QgsFeatureIds testedFeatureIds;

  int i = -1;
  Q_FOREACH ( const QgsGeometry &clipGeom, clipGeoms )
  {
    i++;
    if ( feedback->isCanceled() )
    {
      break;
    }
    QgsFeatureIterator inputIt = featureSource->getFeatures( QgsFeatureRequest().setFilterRect( clipGeom.boundingBox() ) );
    QgsFeatureList inputFeatures;
    QgsFeature f;
    while ( inputIt.nextFeature( f ) )
      inputFeatures << f;

    if ( inputFeatures.isEmpty() )
      continue;

    double step = 0;
    if ( singleClipFeature )
      step = 100.0 / inputFeatures.length();

    int current = 0;
    Q_FOREACH ( const QgsFeature &inputFeature, inputFeatures )
    {
      if ( feedback->isCanceled() )
      {
        break;
      }

      if ( !inputFeature.hasGeometry() )
        continue;

      if ( testedFeatureIds.contains( inputFeature.id() ) )
      {
        // don't retest a feature we have already checked
        continue;
      }
      testedFeatureIds.insert( inputFeature.id() );

      if ( !engine->intersects( inputFeature.geometry().constGet() ) )
        continue;

      QgsGeometry newGeometry;
      if ( !engine->contains( inputFeature.geometry().constGet() ) )
      {
        QgsGeometry currentGeometry = inputFeature.geometry();
        newGeometry = combinedClipGeom.intersection( currentGeometry );
        if ( newGeometry.wkbType() == QgsWkbTypes::Unknown || QgsWkbTypes::flatType( newGeometry.wkbType() ) == QgsWkbTypes::GeometryCollection )
        {
          QgsGeometry intCom = inputFeature.geometry().combine( newGeometry );
          QgsGeometry intSym = inputFeature.geometry().symDifference( newGeometry );
          newGeometry = intCom.difference( intSym );
        }
      }
      else
      {
        // clip geometry totally contains feature geometry, so no need to perform intersection
        newGeometry = inputFeature.geometry();
      }

      if ( !QgsOverlayUtils::sanitizeIntersectionResult( newGeometry, sinkType ) )
        continue;

      QgsFeature outputFeature;
      outputFeature.setGeometry( newGeometry );
      outputFeature.setAttributes( inputFeature.attributes() );
      sink->addFeature( outputFeature, QgsFeatureSink::FastInsert );


      if ( singleClipFeature )
        feedback->setProgress( current * step );
    }

    if ( !singleClipFeature )
    {
      // coarse progress report for multiple clip geometries
      feedback->setProgress( 100.0 * static_cast< double >( i ) / clipGeoms.length() );
    }
  }
Example #27
0
void QgsOSMDatabase::exportSpatiaLiteWays( bool closed, const QString& tableName,
    const QStringList& tagKeys,
    const QStringList& notNullTagKeys )
{
  Q_UNUSED( tagKeys );

  QString sqlInsertLine = QString( "INSERT INTO %1 VALUES (?" ).arg( quotedIdentifier( tableName ) );
  for ( int i = 0; i < tagKeys.count(); ++i )
    sqlInsertLine += QString( ",?" );
  sqlInsertLine += ", GeomFromWKB(?, 4326))";
  sqlite3_stmt* stmtInsert;
  if ( sqlite3_prepare_v2( mDatabase, sqlInsertLine.toUtf8().constData(), -1, &stmtInsert, nullptr ) != SQLITE_OK )
  {
    mError = "Prepare SELECT FROM ways failed.";
    return;
  }

  QgsOSMWayIterator ways = listWays();
  QgsOSMWay w;
  while (( w = ways.next() ).isValid() )
  {
    QgsOSMTags t = tags( true, w.id() );

    QgsPolyline polyline = wayPoints( w.id() );

    if ( polyline.count() < 2 )
      continue; // invalid way

    bool isArea = ( polyline.first() == polyline.last() ); // closed way?
    // filter out closed way that are not areas through tags
    if ( isArea && ( t.contains( "highway" ) || t.contains( "barrier" ) ) )
    {
      // make sure tags that indicate areas are taken into consideration when deciding on a closed way is or isn't an area
      // and allow for a closed way to be exported both as a polygon and a line in case both area and non-area tags are present
      if (( t.value( "area" ) != "yes" && !t.contains( "amenity" ) && !t.contains( "landuse" ) && !t.contains( "building" ) && !t.contains( "natural" ) && !t.contains( "leisure" ) && !t.contains( "aeroway" ) ) || !closed )
        isArea = false;
    }

    if ( closed != isArea )
      continue; // skip if it's not what we're looking for

    //check not null tags
    bool skipNull = false;
    for ( int i = 0; i < notNullTagKeys.count() && !skipNull; ++i )
      if ( !t.contains( notNullTagKeys[i] ) )
        skipNull = true;

    if ( skipNull )
      continue;

    QgsGeometry geom = closed ? QgsGeometry::fromPolygon( QgsPolygon() << polyline ) : QgsGeometry::fromPolyline( polyline );
    int col = 0;
    sqlite3_bind_int64( stmtInsert, ++col, w.id() );

    // tags
    for ( int i = 0; i < tagKeys.count(); ++i )
    {
      if ( t.contains( tagKeys[i] ) )
        sqlite3_bind_text( stmtInsert, ++col, t.value( tagKeys[i] ).toUtf8().constData(), -1, SQLITE_TRANSIENT );
      else
        sqlite3_bind_null( stmtInsert, ++col );
    }

    if ( !geom.isEmpty() )
      sqlite3_bind_blob( stmtInsert, ++col, geom.asWkb(), ( int ) geom.wkbSize(), SQLITE_STATIC );
    else
      sqlite3_bind_null( stmtInsert, ++col );

    int insertRes = sqlite3_step( stmtInsert );
    if ( insertRes != SQLITE_DONE )
    {
      mError = QString( "Error inserting way %1 [%2]" ).arg( w.id() ).arg( insertRes );
      break;
    }

    sqlite3_reset( stmtInsert );
    sqlite3_clear_bindings( stmtInsert );
  }

  sqlite3_finalize( stmtInsert );
}
Example #28
0
void QgsRubberBand::addGeometry( const QgsGeometry& geom, QgsVectorLayer* layer )
{
  if ( geom.isEmpty() )
  {
    return;
  }

  //maprender object of canvas
  const QgsMapSettings& ms = mMapCanvas->mapSettings();

  int idx = mPoints.size();

  switch ( geom.wkbType() )
  {

    case QgsWkbTypes::Point:
    case QgsWkbTypes::Point25D:
    {
      QgsPoint pt;
      if ( layer )
      {
        pt = ms.layerToMapCoordinates( layer, geom.asPoint() );
      }
      else
      {
        pt = geom.asPoint();
      }
      addPoint( pt, false, idx );
      removeLastPoint( idx, false );
    }
    break;

    case QgsWkbTypes::MultiPoint:
    case QgsWkbTypes::MultiPoint25D:
    {
      QgsMultiPoint mpt = geom.asMultiPoint();
      for ( int i = 0; i < mpt.size(); ++i, ++idx )
      {
        QgsPoint pt = mpt[i];
        if ( layer )
        {
          addPoint( ms.layerToMapCoordinates( layer, pt ), false, idx );
          removeLastPoint( idx, false );
        }
        else
        {
          addPoint( pt, false, idx );
          removeLastPoint( idx, false );
        }
      }
    }
    break;

    case QgsWkbTypes::LineString:
    case QgsWkbTypes::LineString25D:
    {
      QgsPolyline line = geom.asPolyline();
      for ( int i = 0; i < line.count(); i++ )
      {
        if ( layer )
        {
          addPoint( ms.layerToMapCoordinates( layer, line[i] ), false, idx );
        }
        else
        {
          addPoint( line[i], false, idx );
        }
      }
    }
    break;

    case QgsWkbTypes::MultiLineString:
    case QgsWkbTypes::MultiLineString25D:
    {

      QgsMultiPolyline mline = geom.asMultiPolyline();
      for ( int i = 0; i < mline.size(); ++i, ++idx )
      {
        QgsPolyline line = mline[i];

        if ( line.isEmpty() )
        {
          --idx;
        }

        for ( int j = 0; j < line.size(); ++j )
        {
          if ( layer )
          {
            addPoint( ms.layerToMapCoordinates( layer, line[j] ), false, idx );
          }
          else
          {
            addPoint( line[j], false, idx );
          }
        }
      }
    }
    break;

    case QgsWkbTypes::Polygon:
    case QgsWkbTypes::Polygon25D:
    {
      QgsPolygon poly = geom.asPolygon();
      QgsPolyline line = poly[0];
      for ( int i = 0; i < line.count(); i++ )
      {
        if ( layer )
        {
          addPoint( ms.layerToMapCoordinates( layer, line[i] ), false, idx );
        }
        else
        {
          addPoint( line[i], false, idx );
        }
      }
    }
    break;

    case QgsWkbTypes::MultiPolygon:
    case QgsWkbTypes::MultiPolygon25D:
    {

      QgsMultiPolygon multipoly = geom.asMultiPolygon();
      for ( int i = 0; i < multipoly.size(); ++i, ++idx )
      {
        QgsPolygon poly = multipoly[i];
        QgsPolyline line = poly[0];
        for ( int j = 0; j < line.count(); ++j )
        {
          if ( layer )
          {
            addPoint( ms.layerToMapCoordinates( layer, line[j] ), false, idx );
          }
          else
          {
            addPoint( line[j], false, idx );
          }
        }
      }
    }
    break;

    case QgsWkbTypes::Unknown:
    default:
      return;
  }

  setVisible( true );
  updateRect();
  update();
}
Example #29
0
void QgsMapToolRotateFeature::canvasReleaseEvent( QgsMapMouseEvent* e )
{
  deleteRotationWidget();

  if ( !mCanvas )
  {
    return;
  }

  QgsVectorLayer* vlayer = currentVectorLayer();
  if ( !vlayer )
  {
    deleteRubberband();
    notifyNotVectorLayer();
    return;
  }

  if ( e->button() == Qt::RightButton )
  {
    deleteRubberband();
    mRotationActive = false;
    return;
  }

  // place anchor point on CTRL + click
  if ( e->modifiers() & Qt::ControlModifier )
  {
    if ( !mAnchorPoint )
    {
      return;
    }
    mAnchorPoint->setCenter( toMapCoordinates( e->pos() ) );
    mStartPointMapCoords = toMapCoordinates( e->pos() );
    mStPoint = e->pos();
    return;
  }

  // Initialize rotation if not yet active
  if ( !mRotationActive )
  {
    mRotation = 0;
    mRotationOffset = 0;

    deleteRubberband();

    mInitialPos = e->pos();

    if ( !vlayer->isEditable() )
    {
      notifyNotEditableLayer();
      return;
    }

    QgsPoint layerCoords = toLayerCoordinates( vlayer, e->pos() );
    double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() );
    QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius,
                             layerCoords.x() + searchRadius, layerCoords.y() + searchRadius );

    if ( vlayer->selectedFeatureCount() == 0 )
    {
      QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setSubsetOfAttributes( QgsAttributeList() ) );

      //find the closest feature
      QgsGeometry pointGeometry = QgsGeometry::fromPoint( layerCoords );
      if ( pointGeometry.isEmpty() )
      {
        return;
      }

      double minDistance = std::numeric_limits<double>::max();

      QgsFeature cf;
      QgsFeature f;
      while ( fit.nextFeature( f ) )
      {
        if ( f.hasGeometry() )
        {
          double currentDistance = pointGeometry.distance( f.geometry() );
          if ( currentDistance < minDistance )
          {
            minDistance = currentDistance;
            cf = f;
          }
        }
      }

      if ( minDistance == std::numeric_limits<double>::max() )
      {
        emit messageEmitted( tr( "Could not find a nearby feature in the current layer." ) );
        return;
      }

      QgsRectangle bound = cf.geometry().boundingBox();
      mStartPointMapCoords = toMapCoordinates( vlayer, bound.center() );

      if ( !mAnchorPoint )
      {
        mAnchorPoint = new QgsVertexMarker( mCanvas );
      }
      mAnchorPoint->setIconType( QgsVertexMarker::ICON_CROSS );
      mAnchorPoint->setCenter( mStartPointMapCoords );

      mStPoint = toCanvasCoordinates( mStartPointMapCoords );

      mRotatedFeatures.clear();
      mRotatedFeatures << cf.id(); //todo: take the closest feature, not the first one...

      mRubberBand = createRubberBand( vlayer->geometryType() );
      mRubberBand->setToGeometry( cf.geometry(), vlayer );
    }
    else
    {
      mRotatedFeatures = vlayer->selectedFeaturesIds();

      mRubberBand = createRubberBand( vlayer->geometryType() );

      QgsFeature feat;
      QgsFeatureIterator it = vlayer->selectedFeaturesIterator();
      while ( it.nextFeature( feat ) )
      {
        mRubberBand->addGeometry( feat.geometry(), vlayer );
      }
    }

    mRubberBand->setColor( QColor( 255, 0, 0, 65 ) );
    mRubberBand->setWidth( 2 );
    mRubberBand->show();

    double XDistance = mInitialPos.x() - mAnchorPoint->x();
    double YDistance = mInitialPos.y() - mAnchorPoint->y() ;
    mRotationOffset = atan2( YDistance, XDistance ) * ( 180 / PI );

    createRotationWidget();
    if ( e->modifiers() & Qt::ShiftModifier )
    {
      if ( mRotationWidget )
      {
        mRotationWidget->setMagnet( 45 );
      }
    }

    mRotationActive = true;

    return;
  }

  applyRotation( mRotation );
}
QgsLabelFeature* QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature& feat, QgsRenderContext &context, QgsGeometry* obstacleGeometry )
{
  const QgsMapSettings& mapSettings = mEngine->mapSettings();

  const QgsDiagramRenderer* dr = mSettings.getRenderer();
  if ( dr )
  {
    QList<QgsDiagramSettings> settingList = dr->diagramSettings();
    if ( !settingList.isEmpty() && settingList.at( 0 ).scaleBasedVisibility )
    {
      double minScale = settingList.at( 0 ).minScaleDenominator;
      if ( minScale > 0 && context.rendererScale() < minScale )
      {
        return nullptr;
      }

      double maxScale = settingList.at( 0 ).maxScaleDenominator;
      if ( maxScale > 0 && context.rendererScale() > maxScale )
      {
        return nullptr;
      }
    }
  }

  //convert geom to geos
  QgsGeometry geom = feat.geometry();
  QgsGeometry extentGeom = QgsGeometry::fromRect( mapSettings.visibleExtent() );
  if ( !qgsDoubleNear( mapSettings.rotation(), 0.0 ) )
  {
    //PAL features are prerotated, so extent also needs to be unrotated
    extentGeom.rotate( -mapSettings.rotation(), mapSettings.visibleExtent().center() );
  }

  const GEOSGeometry* geos_geom = nullptr;
  QScopedPointer<QgsGeometry> scopedPreparedGeom;
  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, mSettings.coordinateTransform(), &extentGeom ) )
  {
    scopedPreparedGeom.reset( new QgsGeometry( QgsPalLabeling::prepareGeometry( geom, context, mSettings.coordinateTransform(), &extentGeom ) ) );
    QgsGeometry* preparedGeom = scopedPreparedGeom.data();
    if ( preparedGeom->isEmpty() )
      return nullptr;
    geos_geom = preparedGeom->asGeos();
  }
  else
  {
    geos_geom = geom.asGeos();
  }

  if ( !geos_geom )
    return nullptr; // invalid geometry

  GEOSGeometry* geomCopy = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );

  const GEOSGeometry* geosObstacleGeom = nullptr;
  QScopedPointer<QgsGeometry> scopedObstacleGeom;
  if ( mSettings.isObstacle() && obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( *obstacleGeometry, context, mSettings.coordinateTransform(), &extentGeom ) )
  {
    QgsGeometry preparedObstacleGeom = QgsPalLabeling::prepareGeometry( *obstacleGeometry, context, mSettings.coordinateTransform(), &extentGeom );
    geosObstacleGeom = preparedObstacleGeom.asGeos();
  }
  else if ( mSettings.isObstacle() && obstacleGeometry )
  {
    geosObstacleGeom = obstacleGeometry->asGeos();
  }
  GEOSGeometry* geosObstacleGeomClone = nullptr;
  if ( geosObstacleGeom )
  {
    geosObstacleGeomClone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geosObstacleGeom );
  }


  double diagramWidth = 0;
  double diagramHeight = 0;
  if ( dr )
  {
    QSizeF diagSize = dr->sizeMapUnits( feat, context );
    if ( diagSize.isValid() )
    {
      diagramWidth = diagSize.width();
      diagramHeight = diagSize.height();
    }
  }

  //  feature to the layer
  bool alwaysShow = mSettings.showAllDiagrams();
  int ddColX = mSettings.xPosColumn;
  int ddColY = mSettings.yPosColumn;
  double ddPosX = 0.0;
  double ddPosY = 0.0;
  bool ddPos = ( ddColX >= 0 && ddColY >= 0 );
  if ( ddPos && ! feat.attribute( ddColX ).isNull() && ! feat.attribute( ddColY ).isNull() )
  {
    bool posXOk, posYOk;
    ddPosX = feat.attribute( ddColX ).toDouble( &posXOk );
    ddPosY = feat.attribute( ddColY ).toDouble( &posYOk );
    if ( !posXOk || !posYOk )
    {
      ddPos = false;
    }
    else
    {
      QgsCoordinateTransform ct = mSettings.coordinateTransform();
      if ( ct.isValid() && !ct.isShortCircuited() )
      {
        double z = 0;
        ct.transformInPlace( ddPosX, ddPosY, z );
      }
      //data defined diagram position is always centered
      ddPosX -= diagramWidth / 2.0;
      ddPosY -= diagramHeight / 2.0;
    }
  }
  else
    ddPos = false;

  int ddColShow = mSettings.showColumn;
  if ( ddColShow >= 0 && ! feat.attribute( ddColShow ).isNull() )
  {
    bool showOk;
    bool ddShow = feat.attribute( ddColShow ).toDouble( &showOk );

    if ( showOk && ! ddShow )
      return nullptr;
  }

  QgsDiagramLabelFeature* lf = new QgsDiagramLabelFeature( feat.id(), geomCopy, QSizeF( diagramWidth, diagramHeight ) );
  lf->setHasFixedPosition( ddPos );
  lf->setFixedPosition( QgsPoint( ddPosX, ddPosY ) );
  lf->setHasFixedAngle( true );
  lf->setFixedAngle( 0 );
  lf->setAlwaysShow( alwaysShow );
  lf->setIsObstacle( mSettings.isObstacle() );
  lf->setZIndex( mSettings.getZIndex() );
  if ( geosObstacleGeomClone )
  {
    lf->setObstacleGeometry( geosObstacleGeomClone );
  }

  if ( dr )
  {
    //append the diagram attributes to lbl
    lf->setAttributes( feat.attributes() );
  }

  QgsPoint ptZero = mapSettings.mapToPixel().toMapCoordinates( 0, 0 );
  QgsPoint ptOne = mapSettings.mapToPixel().toMapCoordinates( 1, 0 );
  lf->setDistLabel( ptOne.distance( ptZero ) * mSettings.distance() );
  return lf;
}