void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( const QPointF& centerPoint, int nPosition, double radius,
    double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts ) const
{
  symbolPositions.clear();
  labelShifts.clear();

  if ( nPosition < 1 )
  {
    return;
  }
  else if ( nPosition == 1 ) //If there is only one feature, draw it exactly at the center position
  {
    symbolPositions.append( centerPoint );
    labelShifts.append( QPointF( symbolDiagonal / 2.0, -symbolDiagonal / 2.0 ) );
    return;
  }

  double fullPerimeter = 2 * M_PI;
  double angleStep = fullPerimeter / nPosition;
  double currentAngle;

  for ( currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep )
  {
    double sinusCurrentAngle = sin( currentAngle );
    double cosinusCurrentAngle = cos( currentAngle );
    QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
    QPointF labelShift(( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle );
    symbolPositions.append( centerPoint + positionShift );
    labelShifts.append( labelShift );
  }
}
void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( const QPointF& centerPoint, int nPosition, double radius, \
    double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts ) const
{
  symbolPositions.clear();
  labelShifts.clear();

  double fullPerimeter = 2 * M_PI;
  double angleStep = fullPerimeter / nPosition;
  double currentAngle;

  for ( currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep )
  {
    double sinusCurrentAngle = sin( currentAngle );
    double cosinusCurrentAngle = cos( currentAngle );
    QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
    QPointF labelShift(( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle );
    symbolPositions.append( centerPoint + positionShift );
    labelShifts.append( labelShift );
  }
}
void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolV2RenderContext& symbolContext, const QPointF& centerPoint, int nPosition,
    double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts, double& circleRadius ) const
{
  symbolPositions.clear();
  labelShifts.clear();

  if ( nPosition < 1 )
  {
    return;
  }
  else if ( nPosition == 1 ) //If there is only one feature, draw it exactly at the center position
  {
    symbolPositions.append( centerPoint );
    labelShifts.append( QPointF( symbolDiagonal / 2.0, -symbolDiagonal / 2.0 ) );
    return;
  }

  double circleAdditionPainterUnits = symbolContext.outputLineWidth( mCircleRadiusAddition );

  switch ( mPlacement )
  {
    case Ring:
    {
      double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
      double radius = qMax( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;

      double fullPerimeter = 2 * M_PI;
      double angleStep = fullPerimeter / nPosition;
      for ( double currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep )
      {
        double sinusCurrentAngle = sin( currentAngle );
        double cosinusCurrentAngle = cos( currentAngle );
        QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
        QPointF labelShift(( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle );
        symbolPositions.append( centerPoint + positionShift );
        labelShifts.append( labelShift );
      }

      circleRadius = radius;
      break;
    }
    case ConcentricRings:
    {
      double centerDiagonal = QgsSymbolLayerV2Utils::convertToPainterUnits( symbolContext.renderContext(),
                              M_SQRT2 * mCenterSymbol->size(),
                              mCenterSymbol->outputUnit(), mCenterSymbol->mapUnitScale() );

      int pointsRemaining = nPosition;
      int ringNumber = 1;
      double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
      while ( pointsRemaining > 0 )
      {
        double radiusCurrentRing = qMax( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
        int maxPointsCurrentRing = qMax( floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
        int actualPointsCurrentRing = qMin( maxPointsCurrentRing, pointsRemaining );

        double angleStep = 2 * M_PI / actualPointsCurrentRing;
        double currentAngle = 0.0;
        for ( int i = 0; i < actualPointsCurrentRing; ++i )
        {
          double sinusCurrentAngle = sin( currentAngle );
          double cosinusCurrentAngle = cos( currentAngle );
          QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
          QPointF labelShift(( radiusCurrentRing + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radiusCurrentRing + symbolDiagonal / 2 ) * cosinusCurrentAngle );
          symbolPositions.append( centerPoint + positionShift );
          labelShifts.append( labelShift );
          currentAngle += angleStep;
        }

        pointsRemaining -= actualPointsCurrentRing;
        ringNumber++;
        circleRadius = radiusCurrentRing;
      }
      break;
    }
  }
}
void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolRenderContext &symbolContext, QPointF centerPoint, int nPosition,
    double symbolDiagonal, QList<QPointF> &symbolPositions, QList<QPointF> &labelShifts, double &circleRadius, double &gridRadius,
    int &gridSize ) const
{
  symbolPositions.clear();
  labelShifts.clear();

  if ( nPosition < 1 )
  {
    return;
  }
  else if ( nPosition == 1 ) //If there is only one feature, draw it exactly at the center position
  {
    symbolPositions.append( centerPoint );
    labelShifts.append( QPointF( symbolDiagonal / 2.0, -symbolDiagonal / 2.0 ) );
    return;
  }

  double circleAdditionPainterUnits = symbolContext.outputLineWidth( mCircleRadiusAddition );

  switch ( mPlacement )
  {
    case Ring:
    {
      double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
      double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;

      double fullPerimeter = 2 * M_PI;
      double angleStep = fullPerimeter / nPosition;
      for ( double currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep )
      {
        double sinusCurrentAngle = std::sin( currentAngle );
        double cosinusCurrentAngle = std::cos( currentAngle );
        QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
        QPointF labelShift( ( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle );
        symbolPositions.append( centerPoint + positionShift );
        labelShifts.append( labelShift );
      }

      circleRadius = radius;
      break;
    }
    case ConcentricRings:
    {
      double centerDiagonal = mCenterSymbol->size( symbolContext.renderContext() ) * M_SQRT2;

      int pointsRemaining = nPosition;
      int ringNumber = 1;
      double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
      while ( pointsRemaining > 0 )
      {
        double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
        int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
        int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining );

        double angleStep = 2 * M_PI / actualPointsCurrentRing;
        double currentAngle = 0.0;
        for ( int i = 0; i < actualPointsCurrentRing; ++i )
        {
          double sinusCurrentAngle = std::sin( currentAngle );
          double cosinusCurrentAngle = std::cos( currentAngle );
          QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
          QPointF labelShift( ( radiusCurrentRing + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radiusCurrentRing + symbolDiagonal / 2 ) * cosinusCurrentAngle );
          symbolPositions.append( centerPoint + positionShift );
          labelShifts.append( labelShift );
          currentAngle += angleStep;
        }

        pointsRemaining -= actualPointsCurrentRing;
        ringNumber++;
        circleRadius = radiusCurrentRing;
      }
      break;
    }
    case Grid:
    {
      double centerDiagonal = mCenterSymbol->size( symbolContext.renderContext() ) * M_SQRT2;
      int pointsRemaining = nPosition;
      gridSize = std::ceil( std::sqrt( pointsRemaining ) );
      if ( pointsRemaining - std::pow( gridSize - 1, 2 ) < gridSize )
        gridSize -= 1;
      double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
      double userPointRadius =  originalPointRadius + circleAdditionPainterUnits;

      int yIndex = 0;
      while ( pointsRemaining > 0 )
      {
        for ( int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
        {
          QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
          QPointF labelShift( ( userPointRadius + symbolDiagonal / 2 ) * xIndex, ( userPointRadius + symbolDiagonal / 2 ) * yIndex );
          symbolPositions.append( centerPoint + positionShift );
          labelShifts.append( labelShift );
          pointsRemaining--;
        }
        yIndex++;
      }

      centralizeGrid( symbolPositions, userPointRadius, gridSize );
      centralizeGrid( labelShifts, userPointRadius, gridSize );
      gridRadius = userPointRadius;
      break;
    }
  }
}