QgsRegularPolygon::QgsRegularPolygon( const QgsPoint ¢er, const QgsPoint &pt1, const int numSides, const ConstructionOption circle ) : mCenter( center ) , mFirstVertex( QgsPoint() ) , mNumberSides( 0 ) , mRadius( 0.0 ) { if ( numSides >= 3 ) { mNumberSides = numSides; switch ( circle ) { case InscribedCircle: { mFirstVertex = pt1; mRadius = center.distance( pt1 ); break; } case CircumscribedCircle: { mRadius = apothemToRadius( center.distance( pt1 ), numSides ); double azimuth = center.azimuth( pt1 ); // TODO: inclination mFirstVertex = mCenter.project( mRadius, azimuth - centralAngle( numSides ) / 2 ); break; } default: break; } } }
QgsGeometry QgsInternalGeometryEngine::taperedBuffer( double start, double end, int segments ) const { start = std::fabs( start ); end = std::fabs( end ); auto interpolateWidths = [ start, end ]( const QgsLineString * line )->std::unique_ptr< double [] > { // ported from JTS VariableWidthBuffer, // https://github.com/topobyte/jts/blob/master/jts-lab/src/main/java/com/vividsolutions/jts/operation/buffer/VariableWidthBuffer.java std::unique_ptr< double [] > widths( new double[ line->nCoordinates() ] ); widths[0] = start; widths[line->nCoordinates() - 1] = end; double lineLength = line->length(); double currentLength = 0; QgsPoint prevPoint = line->pointN( 0 ); for ( int i = 1; i < line->nCoordinates() - 1; ++i ) { QgsPoint point = line->pointN( i ); double segmentLength = point.distance( prevPoint ); currentLength += segmentLength; double lengthFraction = lineLength > 0 ? currentLength / lineLength : 1; double delta = lengthFraction * ( end - start ); widths[i] = start + delta; prevPoint = point; } return widths; }; return variableWidthBuffer( segments, interpolateWidths ); }
QgsCircle QgsCircle::from2Points( const QgsPoint &pt1, const QgsPoint &pt2 ) { QgsPoint center = QgsGeometryUtils::midpoint( pt1, pt2 ); double azimuth = QgsGeometryUtils::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() ) * 180.0 / M_PI; double radius = pt1.distance( pt2 ); return QgsCircle( center, radius, azimuth ); }
QgsRegularPolygon::QgsRegularPolygon( const QgsPoint &pt1, const QgsPoint &pt2, const int numSides ) : mCenter( QgsPoint() ) , mFirstVertex( QgsPoint() ) , mNumberSides( 0 ) , mRadius( 0.0 ) { if ( numSides >= 3 ) { mNumberSides = numSides; double azimuth = pt1.azimuth( pt2 ); QgsPoint pm = QgsGeometryUtils::midpoint( pt1, pt2 ); double length = pt1.distance( pm ); double angle = ( 180 - ( 360 / numSides ) ) / 2.0; double hypothenuse = length / cos( angle * M_PI / 180 ); // TODO: inclination mCenter = pt1.project( hypothenuse, azimuth + angle ); mFirstVertex = pt1; mRadius = qAbs( hypothenuse ); } }
QgsCircle QgsCircle::from3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon ) { QgsPoint p1, p2, p3; if ( !isPerpendicular( pt1, pt2, pt3, epsilon ) ) { p1 = pt1; p2 = pt2; p3 = pt3; } else if ( !isPerpendicular( pt1, pt3, pt2, epsilon ) ) { p1 = pt1; p2 = pt3; p3 = pt2; } else if ( !isPerpendicular( pt2, pt1, pt3, epsilon ) ) { p1 = pt2; p2 = pt1; p3 = pt3; } else if ( !isPerpendicular( pt2, pt3, pt1, epsilon ) ) { p1 = pt2; p2 = pt3; p3 = pt1; } else if ( !isPerpendicular( pt3, pt2, pt1, epsilon ) ) { p1 = pt3; p2 = pt2; p3 = pt1; } else if ( !isPerpendicular( pt3, pt1, pt2, epsilon ) ) { p1 = pt3; p2 = pt1; p3 = pt2; } else { return QgsCircle(); } QgsPoint center = QgsPoint(); double radius = -0.0; // Paul Bourke's algorithm double yDelta_a = p2.y() - p1.y(); double xDelta_a = p2.x() - p1.x(); double yDelta_b = p3.y() - p2.y(); double xDelta_b = p3.x() - p2.x(); if ( qgsDoubleNear( xDelta_a, 0.0, epsilon ) || qgsDoubleNear( xDelta_b, 0.0, epsilon ) ) { return QgsCircle(); } double aSlope = yDelta_a / xDelta_a; double bSlope = yDelta_b / xDelta_b; if ( ( qAbs( xDelta_a ) <= epsilon ) && ( qAbs( yDelta_b ) <= epsilon ) ) { center.setX( 0.5 * ( p2.x() + p3.x() ) ); center.setY( 0.5 * ( p1.y() + p2.y() ) ); radius = center.distance( pt1 ); return QgsCircle( center, radius ); } if ( qAbs( aSlope - bSlope ) <= epsilon ) { return QgsCircle(); } center.setX( ( aSlope * bSlope * ( p1.y() - p3.y() ) + bSlope * ( p1.x() + p2.x() ) - aSlope * ( p2.x() + p3.x() ) ) / ( 2.0 * ( bSlope - aSlope ) ) ); center.setY( -1.0 * ( center.x() - ( p1.x() + p2.x() ) / 2.0 ) / aSlope + ( p1.y() + p2.y() ) / 2.0 ); radius = center.distance( p1 ); return QgsCircle( center, radius ); }
QgsCircle QgsCircle::fromCenterPoint( const QgsPoint ¢er, const QgsPoint &pt1 ) { double azimuth = QgsGeometryUtils::lineAngle( center.x(), center.y(), pt1.x(), pt1.y() ) * 180.0 / M_PI; return QgsCircle( center, center.distance( pt1 ), azimuth ); }
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; }