コード例 #1
0
ファイル: qgscadutils.cpp プロジェクト: GeoCat/QGIS
// TODO: move to geometry utils (if not already there)
static bool lineCircleIntersection( const QgsPointXY &center, const double radius, const QgsPointXY &edgePt0, const QgsPointXY &edgePt1, QgsPointXY &intersection )
{
  // formula taken from http://mathworld.wolfram.com/Circle-LineIntersection.html

  const double x1 = edgePt0.x() - center.x();
  const double y1 = edgePt0.y() - center.y();
  const double x2 = edgePt1.x() - center.x();
  const double y2 = edgePt1.y() - center.y();
  const double dx = x2 - x1;
  const double dy = y2 - y1;

  const double dr = std::sqrt( std::pow( dx, 2 ) + std::pow( dy, 2 ) );
  const double d = x1 * y2 - x2 * y1;

  const double disc = std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 );

  if ( disc < 0 )
  {
    //no intersection or tangent
    return false;
  }
  else
  {
    // two solutions
    const int sgnDy = dy < 0 ? -1 : 1;

    const double ax = center.x() + ( d * dy + sgnDy * dx * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
    const double ay = center.y() + ( -d * dx + std::fabs( dy ) * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
    const QgsPointXY p1( ax, ay );

    const double bx = center.x() + ( d * dy - sgnDy * dx * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
    const double by = center.y() + ( -d * dx - std::fabs( dy ) * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
    const QgsPointXY p2( bx, by );

    // snap to nearest intersection

    if ( intersection.sqrDist( p1 ) < intersection.sqrDist( p2 ) )
    {
      intersection.set( p1.x(), p1.y() );
    }
    else
    {
      intersection.set( p2.x(), p2.y() );
    }
    return true;
  }
}
コード例 #2
0
void QgsAnnotation::updateBalloon()
{
  //first test if the point is in the frame. In that case we don't need a balloon.
  if ( !mHasFixedMapPosition ||
       ( mOffsetFromReferencePoint.x() < 0 && ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) > 0
         && mOffsetFromReferencePoint.y() < 0 && ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) > 0 ) )
  {
    mBalloonSegment = -1;
    return;
  }

  //edge list
  QList<QLineF> segmentList;
  segmentList << segment( 0 );
  segmentList << segment( 1 );
  segmentList << segment( 2 );
  segmentList << segment( 3 );

  //find  closest edge / closest edge point
  double minEdgeDist = std::numeric_limits<double>::max();
  int minEdgeIndex = -1;
  QLineF minEdge;
  QgsPointXY minEdgePoint;
  QgsPointXY origin( 0, 0 );

  for ( int i = 0; i < 4; ++i )
  {
    QLineF currentSegment = segmentList.at( i );
    QgsPointXY currentMinDistPoint;
    double currentMinDist = origin.sqrDistToSegment( currentSegment.x1(), currentSegment.y1(), currentSegment.x2(), currentSegment.y2(), currentMinDistPoint );
    if ( currentMinDist < minEdgeDist )
    {
      minEdgeIndex = i;
      minEdgePoint = currentMinDistPoint;
      minEdgeDist = currentMinDist;
      minEdge = currentSegment;
    }
  }

  if ( minEdgeIndex < 0 )
  {
    return;
  }

  //make that configurable for the item
  double segmentPointWidth = 10;

  mBalloonSegment = minEdgeIndex;
  QPointF minEdgeEnd = minEdge.p2();
  mBalloonSegmentPoint1 = QPointF( minEdgePoint.x(), minEdgePoint.y() );
  if ( std::sqrt( minEdgePoint.sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < segmentPointWidth )
  {
    mBalloonSegmentPoint1 = pointOnLineWithDistance( minEdge.p2(), minEdge.p1(), segmentPointWidth );
  }

  mBalloonSegmentPoint2 = pointOnLineWithDistance( mBalloonSegmentPoint1, minEdge.p2(), 10 );
}
コード例 #3
0
ファイル: qgstolerance.cpp プロジェクト: cz172638/QGIS
// return ratio [mu/lu] between map units and layer units
// this is of course only an approximation
double _ratioMU2LU( const QgsMapSettings &mapSettings, QgsMapLayer *layer )
{
  double distMU = mapSettings.mapUnitsPerPixel();
  QgsPointXY ptMapCenterMU = mapSettings.visibleExtent().center();
  QgsPointXY ptMapCenterRightMU( ptMapCenterMU.x() + distMU, ptMapCenterMU.y() );
  QgsPointXY ptMapCenterLU = mapSettings.mapToLayerCoordinates( layer, ptMapCenterMU );
  QgsPointXY ptMapCenterRightLU = mapSettings.mapToLayerCoordinates( layer, ptMapCenterRightMU );
  double distLU = std::sqrt( ptMapCenterLU.sqrDist( ptMapCenterRightLU ) );
  double ratio = distMU / distLU;
  return ratio;
}
コード例 #4
0
void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues( const QgsPointXY &point )
{
  bool previousPointExist, penulPointExist;
  QgsPointXY previousPt = previousPoint( &previousPointExist );
  QgsPointXY penultimatePt = penultimatePoint( &penulPointExist );

  // --- angle
  if ( !mAngleConstraint->isLocked() && previousPointExist )
  {
    double angle = 0.0;
    if ( penulPointExist && mAngleConstraint->relative() )
    {
      // previous angle
      angle = std::atan2( previousPt.y() - penultimatePt.y(),
                          previousPt.x() - penultimatePt.x() );
    }
    angle = ( std::atan2( point.y() - previousPt.y(),
                          point.x() - previousPt.x()
                        ) - angle ) * 180 / M_PI;
    // modulus
    angle = std::fmod( angle, 360.0 );
    mAngleConstraint->setValue( angle );
  }
  // --- distance
  if ( !mDistanceConstraint->isLocked() && previousPointExist )
  {
    mDistanceConstraint->setValue( std::sqrt( previousPt.sqrDist( point ) ) );
  }
  // --- X
  if ( !mXConstraint->isLocked() )
  {
    if ( previousPointExist && mXConstraint->relative() )
    {
      mXConstraint->setValue( point.x() - previousPt.x() );
    }
    else
    {
      mXConstraint->setValue( point.x() );
    }
  }
  // --- Y
  if ( !mYConstraint->isLocked() )
  {
    if ( previousPointExist && mYConstraint->relative() )
    {
      mYConstraint->setValue( point.y() - previousPt.y() );
    }
    else
    {
      mYConstraint->setValue( point.y() );
    }
  }
}
コード例 #5
0
bool ProjectorData::checkRows( const QgsCoordinateTransform &ct )
{
  if ( !ct.isValid() )
  {
    return false;
  }

  for ( int r = 0; r < mCPRows; r++ )
  {
    for ( int c = 1; c < mCPCols - 1; c += 2 )
    {
      double myDestX, myDestY;
      destPointOnCPMatrix( r, c, &myDestX, &myDestY );

      QgsPointXY myDestPoint( myDestX, myDestY );
      QgsPointXY mySrcPoint1 = mCPMatrix[r][c - 1];
      QgsPointXY mySrcPoint2 = mCPMatrix[r][c];
      QgsPointXY mySrcPoint3 = mCPMatrix[r][c + 1];

      QgsPointXY mySrcApprox( ( mySrcPoint1.x() + mySrcPoint3.x() ) / 2, ( mySrcPoint1.y() + mySrcPoint3.y() ) / 2 );
      if ( !mCPLegalMatrix[r][c - 1] || !mCPLegalMatrix[r][c] || !mCPLegalMatrix[r][c + 1] )
      {
        // There was an error earlier in transform, just abort
        return false;
      }
      try
      {
        QgsPointXY myDestApprox = ct.transform( mySrcApprox, QgsCoordinateTransform::ReverseTransform );
        double mySqrDist = myDestApprox.sqrDist( myDestPoint );
        if ( mySqrDist > mSqrTolerance )
        {
          return false;
        }
      }
      catch ( QgsCsException &e )
      {
        Q_UNUSED( e );
        // Caught an error in transform
        return false;
      }
    }
  }
  return true;
}
コード例 #6
0
void ProjectorData::calcSrcRowsCols()
{
  // Wee need to calculate minimum cell size in the source
  // TODO: Think it over better, what is the right source resolution?
  //       Taking distances between cell centers projected to source along source
  //       axis would result in very high resolution
  // TODO: different resolution for rows and cols ?

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

  if ( mApproximate )
  {
    // For now, we take cell sizes projected to source but not to source axes
    double myDestColsPerMatrixCell = static_cast< double >( mDestCols ) / mCPCols;
    double myDestRowsPerMatrixCell = static_cast< double >( mDestRows ) / mCPRows;
    QgsDebugMsgLevel( QStringLiteral( "myDestColsPerMatrixCell = %1 myDestRowsPerMatrixCell = %2" ).arg( myDestColsPerMatrixCell ).arg( myDestRowsPerMatrixCell ), 4 );
    for ( int i = 0; i < mCPRows - 1; i++ )
    {
      for ( int j = 0; j < mCPCols - 1; j++ )
      {
        QgsPointXY myPointA = mCPMatrix[i][j];
        QgsPointXY myPointB = mCPMatrix[i][j + 1];
        QgsPointXY myPointC = mCPMatrix[i + 1][j];
        if ( mCPLegalMatrix[i][j] && mCPLegalMatrix[i][j + 1] && mCPLegalMatrix[i + 1][j] )
        {
          double mySize = std::sqrt( myPointA.sqrDist( myPointB ) ) / myDestColsPerMatrixCell;
          if ( mySize < myMinSize )
            myMinSize = mySize;

          mySize = std::sqrt( myPointA.sqrDist( myPointC ) ) / myDestRowsPerMatrixCell;
          if ( mySize < myMinSize )
            myMinSize = mySize;
        }
      }
    }
  }
  else
  {
    // take highest from corners, points in in the middle of corners and center (3 x 3 )
    //double
    QgsRectangle srcExtent;
    int srcXSize, srcYSize;
    if ( QgsRasterProjector::extentSize( mInverseCt, mDestExtent, mDestCols, mDestRows, srcExtent, srcXSize, srcYSize ) )
    {
      double srcXRes = srcExtent.width() / srcXSize;
      double srcYRes = srcExtent.height() / srcYSize;
      myMinSize = std::min( srcXRes, srcYRes );
    }
    else
    {
      QgsDebugMsg( QStringLiteral( "Cannot get src extent/size" ) );
    }
  }

  // Make it a bit higher resolution
  // TODO: find the best coefficient, attention, increasing resolution for WMS
  // is changing WMS content
  myMinSize *= 0.75;

  QgsDebugMsgLevel( QStringLiteral( "mMaxSrcXRes = %1 mMaxSrcYRes = %2" ).arg( mMaxSrcXRes ).arg( mMaxSrcYRes ), 4 );
  // mMaxSrcXRes, mMaxSrcYRes may be 0 - no limit (WMS)
  double myMinXSize = mMaxSrcXRes > myMinSize ? mMaxSrcXRes : myMinSize;
  double myMinYSize = mMaxSrcYRes > myMinSize ? mMaxSrcYRes : myMinSize;
  QgsDebugMsgLevel( QStringLiteral( "myMinXSize = %1 myMinYSize = %2" ).arg( myMinXSize ).arg( myMinYSize ), 4 );
  QgsDebugMsgLevel( QStringLiteral( "mSrcExtent.width = %1 mSrcExtent.height = %2" ).arg( mSrcExtent.width() ).arg( mSrcExtent.height() ), 4 );

  // we have to round to keep alignment set in calcSrcExtent
  mSrcRows = static_cast< int >( std::round( mSrcExtent.height() / myMinYSize ) );
  mSrcCols = static_cast< int >( std::round( mSrcExtent.width() / myMinXSize ) );

  QgsDebugMsgLevel( QStringLiteral( "mSrcRows = %1 mSrcCols = %2" ).arg( mSrcRows ).arg( mSrcCols ), 4 );
}
コード例 #7
0
ファイル: qgscadutils.cpp プロジェクト: GeoCat/QGIS
QgsCadUtils::AlignMapPointOutput QgsCadUtils::alignMapPoint( const QgsPointXY &originalMapPoint, const QgsCadUtils::AlignMapPointContext &ctx )
{
  QgsCadUtils::AlignMapPointOutput res;
  res.valid = true;
  res.softLockCommonAngle = -1;

  // try to snap to anything
  QgsPointLocator::Match snapMatch = ctx.snappingUtils->snapToMap( originalMapPoint );
  QgsPointXY point = snapMatch.isValid() ? snapMatch.point() : originalMapPoint;

  // try to snap explicitly to a segment - useful for some constraints
  QgsPointXY edgePt0, edgePt1;
  EdgesOnlyFilter edgesOnlyFilter;
  QgsPointLocator::Match edgeMatch = ctx.snappingUtils->snapToMap( originalMapPoint, &edgesOnlyFilter );
  if ( edgeMatch.hasEdge() )
    edgeMatch.edgePoints( edgePt0, edgePt1 );

  res.edgeMatch = edgeMatch;

  QgsPointXY previousPt, penultimatePt;
  if ( ctx.cadPointList.count() >= 2 )
    previousPt = ctx.cadPointList.at( 1 );
  if ( ctx.cadPointList.count() >= 3 )
    penultimatePt = ctx.cadPointList.at( 2 );

  // *****************************
  // ---- X constraint
  if ( ctx.xConstraint.locked )
  {
    if ( !ctx.xConstraint.relative )
    {
      point.setX( ctx.xConstraint.value );
    }
    else if ( ctx.cadPointList.count() >= 2 )
    {
      point.setX( previousPt.x() + ctx.xConstraint.value );
    }
    if ( edgeMatch.hasEdge() && !ctx.yConstraint.locked )
    {
      // intersect with snapped segment line at X ccordinate
      const double dx = edgePt1.x() - edgePt0.x();
      if ( dx == 0 )
      {
        point.setY( edgePt0.y() );
      }
      else
      {
        const double dy = edgePt1.y() - edgePt0.y();
        point.setY( edgePt0.y() + ( dy * ( point.x() - edgePt0.x() ) ) / dx );
      }
    }
  }

  // *****************************
  // ---- Y constraint
  if ( ctx.yConstraint.locked )
  {
    if ( !ctx.yConstraint.relative )
    {
      point.setY( ctx.yConstraint.value );
    }
    else if ( ctx.cadPointList.count() >= 2 )
    {
      point.setY( previousPt.y() + ctx.yConstraint.value );
    }
    if ( edgeMatch.hasEdge() && !ctx.xConstraint.locked )
    {
      // intersect with snapped segment line at Y ccordinate
      const double dy = edgePt1.y() - edgePt0.y();
      if ( dy == 0 )
      {
        point.setX( edgePt0.x() );
      }
      else
      {
        const double dx = edgePt1.x() - edgePt0.x();
        point.setX( edgePt0.x() + ( dx * ( point.y() - edgePt0.y() ) ) / dy );
      }
    }
  }

  // *****************************
  // ---- Common Angle constraint
  if ( !ctx.angleConstraint.locked && ctx.cadPointList.count() >= 2 && ctx.commonAngleConstraint.locked && ctx.commonAngleConstraint.value != 0 )
  {
    double commonAngle = ctx.commonAngleConstraint.value * M_PI / 180;
    // see if soft common angle constraint should be performed
    // only if not in HardLock mode
    double softAngle = std::atan2( point.y() - previousPt.y(),
                                   point.x() - previousPt.x() );
    double deltaAngle = 0;
    if ( ctx.commonAngleConstraint.relative && ctx.cadPointList.count() >= 3 )
    {
      // compute the angle relative to the last segment (0° is aligned with last segment)
      deltaAngle = std::atan2( previousPt.y() - penultimatePt.y(),
                               previousPt.x() - penultimatePt.x() );
      softAngle -= deltaAngle;
    }
    int quo = std::round( softAngle / commonAngle );
    if ( std::fabs( softAngle - quo * commonAngle ) * 180.0 * M_1_PI <= SOFT_CONSTRAINT_TOLERANCE_DEGREES )
    {
      // also check the distance in pixel to the line, otherwise it's too sticky at long ranges
      softAngle = quo * commonAngle;
      // http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
      // use the direction vector (cos(a),sin(a)) from previous point. |x2-x1|=1 since sin2+cos2=1
      const double dist = std::fabs( std::cos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() )
                                     - std::sin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) );
      if ( dist / ctx.mapUnitsPerPixel < SOFT_CONSTRAINT_TOLERANCE_PIXEL )
      {
        res.softLockCommonAngle = 180.0 / M_PI * softAngle;
      }
    }
  }

  // angle can be locked in one of the two ways:
  // 1. "hard" lock defined by the user
  // 2. "soft" lock from common angle (e.g. 45 degrees)
  bool angleLocked = false, angleRelative = false;
  int angleValueDeg = 0;
  if ( ctx.angleConstraint.locked )
  {
    angleLocked = true;
    angleRelative = ctx.angleConstraint.relative;
    angleValueDeg = ctx.angleConstraint.value;
  }
  else if ( res.softLockCommonAngle != -1 )
  {
    angleLocked = true;
    angleRelative = ctx.commonAngleConstraint.relative;
    angleValueDeg = res.softLockCommonAngle;
  }

  // *****************************
  // ---- Angle constraint
  // input angles are in degrees
  if ( angleLocked )
  {
    double angleValue = angleValueDeg * M_PI / 180;
    if ( angleRelative && ctx.cadPointList.count() >= 3 )
    {
      // compute the angle relative to the last segment (0° is aligned with last segment)
      angleValue += std::atan2( previousPt.y() - penultimatePt.y(),
                                previousPt.x() - penultimatePt.x() );
    }

    double cosa = std::cos( angleValue );
    double sina = std::sin( angleValue );
    double v = ( point.x() - previousPt.x() ) * cosa + ( point.y() - previousPt.y() ) * sina;
    if ( ctx.xConstraint.locked && ctx.yConstraint.locked )
    {
      // do nothing if both X,Y are already locked
    }
    else if ( ctx.xConstraint.locked )
    {
      if ( qgsDoubleNear( cosa, 0.0 ) )
      {
        res.valid = false;
      }
      else
      {
        double x = ctx.xConstraint.value;
        if ( !ctx.xConstraint.relative )
        {
          x -= previousPt.x();
        }
        point.setY( previousPt.y() + x * sina / cosa );
      }
    }
    else if ( ctx.yConstraint.locked )
    {
      if ( qgsDoubleNear( sina, 0.0 ) )
      {
        res.valid = false;
      }
      else
      {
        double y = ctx.yConstraint.value;
        if ( !ctx.yConstraint.relative )
        {
          y -= previousPt.y();
        }
        point.setX( previousPt.x() + y * cosa / sina );
      }
    }
    else
    {
      point.setX( previousPt.x() + cosa * v );
      point.setY( previousPt.y() + sina * v );
    }

    if ( edgeMatch.hasEdge() && !ctx.distanceConstraint.locked )
    {
      // magnetize to the intersection of the snapped segment and the lockedAngle

      // line of previous point + locked angle
      const double x1 = previousPt.x();
      const double y1 = previousPt.y();
      const double x2 = previousPt.x() + cosa;
      const double y2 = previousPt.y() + sina;
      // line of snapped segment
      const double x3 = edgePt0.x();
      const double y3 = edgePt0.y();
      const double x4 = edgePt1.x();
      const double y4 = edgePt1.y();

      const double d = ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 );

      // do not compute intersection if lines are almost parallel
      // this threshold might be adapted
      if ( std::fabs( d ) > 0.01 )
      {
        point.setX( ( ( x3 - x4 ) * ( x1 * y2 - y1 * x2 ) - ( x1 - x2 ) * ( x3 * y4 - y3 * x4 ) ) / d );
        point.setY( ( ( y3 - y4 ) * ( x1 * y2 - y1 * x2 ) - ( y1 - y2 ) * ( x3 * y4 - y3 * x4 ) ) / d );
      }
    }
  }

  // *****************************
  // ---- Distance constraint
  if ( ctx.distanceConstraint.locked && ctx.cadPointList.count() >= 2 )
  {
    if ( ctx.xConstraint.locked || ctx.yConstraint.locked )
    {
      // perform both to detect errors in constraints
      if ( ctx.xConstraint.locked )
      {
        QgsPointXY verticalPt0( ctx.xConstraint.value, point.y() );
        QgsPointXY verticalPt1( ctx.xConstraint.value, point.y() + 1 );
        res.valid &= lineCircleIntersection( previousPt, ctx.distanceConstraint.value, verticalPt0, verticalPt1, point );
      }
      if ( ctx.yConstraint.locked )
      {
        QgsPointXY horizontalPt0( point.x(), ctx.yConstraint.value );
        QgsPointXY horizontalPt1( point.x() + 1, ctx.yConstraint.value );
        res.valid &= lineCircleIntersection( previousPt, ctx.distanceConstraint.value, horizontalPt0, horizontalPt1, point );
      }
    }
    else
    {
      const double dist = std::sqrt( point.sqrDist( previousPt ) );
      if ( dist == 0 )
      {
        // handle case where mouse is over origin and distance constraint is enabled
        // take arbitrary horizontal line
        point.set( previousPt.x() + ctx.distanceConstraint.value, previousPt.y() );
      }
      else
      {
        const double vP = ctx.distanceConstraint.value / dist;
        point.set( previousPt.x() + ( point.x() - previousPt.x() ) * vP,
                   previousPt.y() + ( point.y() - previousPt.y() ) * vP );
      }

      if ( edgeMatch.hasEdge() && !ctx.angleConstraint.locked )
      {
        // we will magnietize to the intersection of that segment and the lockedDistance !
        res.valid &= lineCircleIntersection( previousPt, ctx.distanceConstraint.value, edgePt0, edgePt1, point );
      }
    }
  }

  // *****************************
  // ---- calculate CAD values
  QgsDebugMsgLevel( QString( "point:             %1 %2" ).arg( point.x() ).arg( point.y() ), 4 );
  QgsDebugMsgLevel( QString( "previous point:    %1 %2" ).arg( previousPt.x() ).arg( previousPt.y() ), 4 );
  QgsDebugMsgLevel( QString( "penultimate point: %1 %2" ).arg( penultimatePt.x() ).arg( penultimatePt.y() ), 4 );
  //QgsDebugMsg( QString( "dx: %1 dy: %2" ).arg( point.x() - previousPt.x() ).arg( point.y() - previousPt.y() ) );
  //QgsDebugMsg( QString( "ddx: %1 ddy: %2" ).arg( previousPt.x() - penultimatePt.x() ).arg( previousPt.y() - penultimatePt.y() ) );

  res.finalMapPoint = point;

  return res;
}