Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Address PR Comments
  • Loading branch information
elpaso committed Jan 17, 2023
1 parent ac3701e commit 24703f7
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 17 deletions.
2 changes: 1 addition & 1 deletion python/core/auto_generated/qgsfileutils.sip.in
Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_generated/symbology/qgssymbollayer.sip.in
Expand Up @@ -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.

Expand Down
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsfileutils.cpp
Expand Up @@ -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 ) )
{
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsfileutils.h
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/core/symbology/qgssymbollayer.h
Expand Up @@ -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.
*
Expand Down
15 changes: 9 additions & 6 deletions src/core/symbology/qgssymbollayerutils.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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 );
Expand All @@ -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;
}
Expand Down
7 changes: 5 additions & 2 deletions src/core/symbology/qgssymbollayerutils.h
Expand Up @@ -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 );
Expand Down
19 changes: 19 additions & 0 deletions tests/src/python/test_qgsfileutils.py
Expand Up @@ -20,6 +20,7 @@
Qgis,
QgsFileUtils
)
from qgis.PyQt.QtCore import QTemporaryDir
from qgis.testing import unittest
from utilities import unitTestDataPath

Expand Down Expand Up @@ -311,6 +312,24 @@ def testSplitPathToComponents(self):
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()
53 changes: 52 additions & 1 deletion tests/src/python/test_qgssymbollayerutils.py
Expand Up @@ -690,11 +690,62 @@ def testTileSize(self):
[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)

Expand Down

0 comments on commit 24703f7

Please sign in to comment.