Address PR Comments

This commit is contained in:
Alessandro Pasotti 2023-01-17 10:28:11 +01:00
parent ac3701ef00
commit 24703f7e6a
10 changed files with 95 additions and 17 deletions

View File

@ -198,7 +198,7 @@ E.g. if ``path`` is "/home/user/Pictures/test.png", the returned list will conta
%Docstring
Creates a unique file path name from a desired path by appending "_<n>" (where "<n>" is an integer number) before the file suffix.
E.g. if "/path/my_image.png" already exists "/path/my_image_1.png" (and "_2", "_3" etc.) will be checked until a file path that does not already exist is found.
E.g. if "/path/my_image.png" already exists "/path/my_image_2.png" (and "_3", "_4" etc.) will be checked until a file path that does not already exist is found.
:param path: the desired path.

View File

@ -1315,7 +1315,7 @@ The ``rings`` argument optionally specifies a list of polygon rings to render as
virtual QImage toTiledPatternImage( ) const;
%Docstring
Renders the symbol layer to an image that can be used as a seamless pattern fill
Renders the symbol layer as an image that can be used as a seamless pattern fill
for polygons, this method is used by SLD export to generate image tiles for
ExternalGraphic polygon fills.

View File

@ -999,12 +999,14 @@ Evaluates a map of properties using the given ``context`` and returns a variant
static QSize tileSize( int width, int height, double &angleRad /In,Out/ );
%Docstring
Calculate the minimum size in pixels of a symbol tile given the symbol ``width`` and ``height`` and the grid rotation ``angle`` in radians.
Calculate the minimum size in pixels of a symbol tile given the symbol ``width`` and ``height`` and the symbol layer rotation ``angleRad`` in radians (counter clockwise).
The method makes approximations and can modify ``angle`` in order to generate the smallest possible tile.
.. note::
:param width: marker width, including margins
:param height: marker height, including margins
:param angleRad: symbol layer rotation angle in radians (counter clockwise), it may be approximated by the method to minimize the tile size.
Angle must be >= 0 and < 2 * PI
:return: the size of the tile
.. versionadded:: 3.30
%End

View File

@ -540,7 +540,7 @@ QString QgsFileUtils::uniquePath( const QString &path )
QFileInfo info { path };
const QString suffix { info.completeSuffix() };
const QString pathPattern { QString( suffix.length() > 0 ? path.chopped( suffix.length() + 1 ) : path ).append( QStringLiteral( "_%1." ) ).append( suffix ) };
int i { 1 };
int i { 2 };
QString uniquePath { pathPattern.arg( i ) };
while ( QFileInfo::exists( uniquePath ) )
{

View File

@ -228,7 +228,7 @@ class CORE_EXPORT QgsFileUtils
/**
* Creates a unique file path name from a desired path by appending "_<n>" (where "<n>" is an integer number) before the file suffix.
*
* E.g. if "/path/my_image.png" already exists "/path/my_image_1.png" (and "_2", "_3" etc.) will be checked until a file path that does not already exist is found.
* E.g. if "/path/my_image.png" already exists "/path/my_image_2.png" (and "_3", "_4" etc.) will be checked until a file path that does not already exist is found.
*
* \param path the desired path.
* \return the unmodified path if path is already unique or the new path with "_<n>" (where "<n>" is an integer number) appended to the file name before the suffix.

View File

@ -1242,7 +1242,7 @@ class CORE_EXPORT QgsFillSymbolLayer : public QgsSymbolLayer
double angle() const { return mAngle; }
/**
* Renders the symbol layer to an image that can be used as a seamless pattern fill
* Renders the symbol layer as an image that can be used as a seamless pattern fill
* for polygons, this method is used by SLD export to generate image tiles for
* ExternalGraphic polygon fills.
*

View File

@ -5101,8 +5101,12 @@ QgsStringMap QgsSymbolLayerUtils::evaluatePropertiesMap( const QMap<QString, Qgs
QSize QgsSymbolLayerUtils::tileSize( int width, int height, double &angleRad )
{
// Precondition
Q_ASSERT( angleRad >= 0 && angleRad < M_PI * 2 );
angleRad = std::fmod( angleRad, M_PI * 2 );
if ( angleRad < 0 )
{
angleRad += M_PI * 2;
}
// tan with rational sin/cos
struct rationalTangent
@ -5257,13 +5261,13 @@ QSize QgsSymbolLayerUtils::tileSize( int width, int height, double &angleRad )
}
}
if ( qgsDoubleNear( angleRad, 0 ) )
if ( qgsDoubleNear( angleRad, 0, 10E-3 ) )
{
angleRad = 0;
tileSize.setWidth( width );
tileSize.setHeight( height );
}
else if ( qgsDoubleNear( angleRad, M_PI_2 ) )
else if ( qgsDoubleNear( angleRad, M_PI_2, 10E-3 ) )
{
angleRad = M_PI_2;
tileSize.setWidth( height );
@ -5277,9 +5281,8 @@ QSize QgsSymbolLayerUtils::tileSize( int width, int height, double &angleRad )
for ( int idx = 0; idx < rationalTangents.count(); ++idx )
{
const auto item = rationalTangents.at( idx );
if ( qgsDoubleNear( item.angle, angleRad ) || item.angle > angleRad )
if ( qgsDoubleNear( item.angle, angleRad, 10E-3 ) || item.angle > angleRad )
{
angleRad = item.angle;
rTanIdx = idx;
break;
}

View File

@ -898,9 +898,12 @@ class CORE_EXPORT QgsSymbolLayerUtils
static QgsStringMap evaluatePropertiesMap( const QMap<QString, QgsProperty> &propertiesMap, const QgsExpressionContext &context );
/**
* Calculate the minimum size in pixels of a symbol tile given the symbol \a width and \a height and the grid rotation \a angle in radians.
* Calculate the minimum size in pixels of a symbol tile given the symbol \a width and \a height and the symbol layer rotation \a angleRad in radians (counter clockwise).
* The method makes approximations and can modify \a angle in order to generate the smallest possible tile.
* \note Angle must be >= 0 and < 2 * PI
* \param width marker width, including margins
* \param height marker height, including margins
* \param angleRad symbol layer rotation angle in radians (counter clockwise), it may be approximated by the method to minimize the tile size.
* \return the size of the tile
* \since QGIS 3.30
*/
static QSize tileSize( int width, int height, double &angleRad SIP_INOUT );

View File

@ -20,6 +20,7 @@ from qgis.core import (
Qgis,
QgsFileUtils
)
from qgis.PyQt.QtCore import QTemporaryDir
from qgis.testing import unittest
from utilities import unitTestDataPath
@ -311,6 +312,24 @@ class TestQgsFileUtils(unittest.TestCase):
self.assertEqual(QgsFileUtils.splitPathToComponents(''), [])
self.assertEqual(QgsFileUtils.splitPathToComponents('c:/home/user'), ["c:", "home", "user"])
def testUniquePath(self):
temp_dir = QTemporaryDir()
temp_path = temp_dir.path()
with open(os.path.join(temp_path, 'test.txt'), 'w+') as f:
f.close()
self.assertEqual(QgsFileUtils.uniquePath(os.path.join(temp_path, 'my_test.txt')), os.path.join(temp_path, 'my_test.txt'))
self.assertEqual(QgsFileUtils.uniquePath(os.path.join(temp_path, 'test.txt')), os.path.join(temp_path, 'test_2.txt'))
with open(os.path.join(temp_path, 'test_2.txt'), 'w+') as f:
f.close()
self.assertEqual(QgsFileUtils.uniquePath(os.path.join(temp_path, 'test_2.txt')), os.path.join(temp_path, 'test_2_2.txt'))
self.assertEqual(QgsFileUtils.uniquePath(os.path.join(temp_path, 'test.txt')), os.path.join(temp_path, 'test_3.txt'))
self.assertEqual(QgsFileUtils.uniquePath(os.path.join(temp_path, 'test_1.txt')), os.path.join(temp_path, 'test_1.txt'))
if __name__ == '__main__':
unittest.main()

View File

@ -690,11 +690,62 @@ class PyQgsSymbolLayerUtils(unittest.TestCase):
[10, 20, math.pi + math.pi / 2 + math.pi / 6, 72, 36, math.pi + math.pi / 2 + 0.5880031703261417], # Angle approx
[10, 10, math.pi + math.pi / 2 + math.pi / 4, 10 * math.sqrt(2), 10 * math.sqrt(2), math.pi + math.pi / 2 + math.pi / 4],
[10, 20, math.pi + math.pi / 2 + math.pi / 4, 20 * math.sqrt(2), 20 * math.sqrt(2), math.pi + math.pi / 2 + math.pi / 4],
# Test out of range angles > 2 PI
# First quadrant
[10, 10, math.pi * 2 + math.pi / 4, 10 * math.sqrt(2), 10 * math.sqrt(2), math.pi / 4],
[10, 20, math.pi * 2 + math.pi / 2, 20, 10, math.pi / 2],
[10, 20, math.pi * 2 + math.pi / 4, 20 * math.sqrt(2), 20 * math.sqrt(2), math.pi / 4],
[10, 20, math.pi * 2 + math.pi / 6, 36, 72, 0.5880031703261417], # Angle approx
# Second quadrant
[10, 20, math.pi * 2 + math.pi / 2 + math.pi / 6, 72, 36, math.pi / 2 + 0.5880031703261417], # Angle approx
[10, 10, math.pi * 2 + math.pi / 2 + math.pi / 4, 10 * math.sqrt(2), 10 * math.sqrt(2), math.pi / 2 + math.pi / 4],
[10, 20, math.pi * 2 + math.pi / 2 + math.pi / 2, 10, 20, math.pi / 2 + math.pi / 2],
[10, 20, math.pi * 2 + math.pi / 2 + math.pi / 4, 20 * math.sqrt(2), 20 * math.sqrt(2), math.pi / 2 + math.pi / 4],
# Third quadrant
[10, 20, math.pi * 2 + math.pi + math.pi / 6, 36, 72, math.pi + 0.5880031703261417], # Angle approx
[10, 10, math.pi * 2 + math.pi + math.pi / 4, 10 * math.sqrt(2), 10 * math.sqrt(2), math.pi + math.pi / 4],
[10, 20, math.pi * 2 + math.pi + math.pi / 2, 20, 10, math.pi + math.pi / 2],
[10, 20, math.pi * 2 + math.pi + math.pi / 4, 20 * math.sqrt(2), 20 * math.sqrt(2), math.pi + math.pi / 4],
# Fourth quadrant
[10, 20, math.pi * 2 + math.pi + math.pi / 2 + math.pi / 6, 72, 36, math.pi + math.pi / 2 + 0.5880031703261417], # Angle approx
[10, 10, math.pi * 2 + math.pi + math.pi / 2 + math.pi / 4, 10 * math.sqrt(2), 10 * math.sqrt(2), math.pi + math.pi / 2 + math.pi / 4],
[10, 20, math.pi * 2 + math.pi + math.pi / 2 + math.pi / 4, 20 * math.sqrt(2), 20 * math.sqrt(2), math.pi + math.pi / 2 + math.pi / 4],
# Test out of range angles < 0
# First quadrant
[10, 10, - math.pi * 2 + math.pi / 4, 10 * math.sqrt(2), 10 * math.sqrt(2), math.pi / 4],
[10, 20, - math.pi * 2 + math.pi / 2, 20, 10, math.pi / 2],
[10, 20, - math.pi * 2 + math.pi / 4, 20 * math.sqrt(2), 20 * math.sqrt(2), math.pi / 4],
[10, 20, - math.pi * 2 + math.pi / 6, 36, 72, 0.5880031703261417], # Angle approx
# Second quadrant
[10, 20, - math.pi * 2 + math.pi / 2 + math.pi / 6, 72, 36, math.pi / 2 + 0.5880031703261417], # Angle approx
[10, 10, - math.pi * 2 + math.pi / 2 + math.pi / 4, 10 * math.sqrt(2), 10 * math.sqrt(2), math.pi / 2 + math.pi / 4],
[10, 20, - math.pi * 2 + math.pi / 2 + math.pi / 2, 10, 20, math.pi / 2 + math.pi / 2],
[10, 20, - math.pi * 2 + math.pi / 2 + math.pi / 4, 20 * math.sqrt(2), 20 * math.sqrt(2), math.pi / 2 + math.pi / 4],
# Third quadrant
[10, 20, - math.pi * 2 + math.pi + math.pi / 6, 36, 72, math.pi + 0.5880031703261417], # Angle approx
[10, 10, - math.pi * 2 + math.pi + math.pi / 4, 10 * math.sqrt(2), 10 * math.sqrt(2), math.pi + math.pi / 4],
[10, 20, - math.pi * 2 + math.pi + math.pi / 2, 20, 10, math.pi + math.pi / 2],
[10, 20, - math.pi * 2 + math.pi + math.pi / 4, 20 * math.sqrt(2), 20 * math.sqrt(2), math.pi + math.pi / 4],
# Fourth quadrant
[10, 20, - math.pi * 2 + math.pi + math.pi / 2 + math.pi / 6, 72, 36, math.pi + math.pi / 2 + 0.5880031703261417], # Angle approx
[10, 10, - math.pi * 2 + math.pi + math.pi / 2 + math.pi / 4, 10 * math.sqrt(2), 10 * math.sqrt(2), math.pi + math.pi / 2 + math.pi / 4],
[10, 20, - math.pi * 2 + math.pi + math.pi / 2 + math.pi / 4, 20 * math.sqrt(2), 20 * math.sqrt(2), math.pi + math.pi / 2 + math.pi / 4],
]
for width, height, angle, exp_width, exp_height, exp_angle in test_data:
(res_size, res_angle) = QgsSymbolLayerUtils.tileSize(width, height, angle)
self.assertEqual(res_size.height(), int(exp_height))
self.assertEqual(res_size.height(), int(exp_height), angle)
self.assertEqual(res_size.width(), int(exp_width))
self.assertAlmostEqual(res_angle, exp_angle)