Skip to content

Commit

Permalink
Merge pull request #59847 from qgis/backport-59839-to-release-3_40
Browse files Browse the repository at this point in the history
[Backport release-3_40] Improve CRS searching by string
  • Loading branch information
alexbruy authored Dec 12, 2024
2 parents 9897001 + 25bfca2 commit f585024
Show file tree
Hide file tree
Showing 15 changed files with 263 additions and 1 deletion.
1 change: 1 addition & 0 deletions python/PyQt6/core/auto_additions/qgsstringutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
QgsStringUtils.htmlToMarkdown = staticmethod(QgsStringUtils.htmlToMarkdown)
QgsStringUtils.qRegExpEscape = staticmethod(QgsStringUtils.qRegExpEscape)
QgsStringUtils.truncateMiddleOfString = staticmethod(QgsStringUtils.truncateMiddleOfString)
QgsStringUtils.containsByWord = staticmethod(QgsStringUtils.containsByWord)
except (NameError, AttributeError):
pass
13 changes: 13 additions & 0 deletions python/PyQt6/core/auto_generated/qgsstringutils.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,19 @@ will be truncated by removing characters from the middle of the string
and replacing them with a horizontal ellipsis character.

.. versionadded:: 3.22
%End

static bool containsByWord( const QString &candidate, const QString &words, Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive );
%Docstring
Given a ``candidate`` string, returns ``True`` if the ``candidate`` contains
all the individual words from another string, regardless of their order.

.. note::

The search does NOT need to match whole words in the ``candidate`` string,
so eg a candidate string of "Worldmap_Winkel_II" will return ``True`` for ``words`` "winkle world"

.. versionadded:: 3.42
%End

};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
QgsCoordinateReferenceSystemModel.Roles.RoleProj = QgsCoordinateReferenceSystemModel.CustomRole.Proj
QgsCoordinateReferenceSystemModel.RoleProj.is_monkey_patched = True
QgsCoordinateReferenceSystemModel.RoleProj.__doc__ = "The coordinate reference system's PROJ representation. This is only used for non-standard CRS (i.e. those not present in the database)."
QgsCoordinateReferenceSystemModel.Group = QgsCoordinateReferenceSystemModel.CustomRole.Group
QgsCoordinateReferenceSystemModel.Group.is_monkey_patched = True
QgsCoordinateReferenceSystemModel.Group.__doc__ = "Group name. \n.. versionadded:: 3.42"
QgsCoordinateReferenceSystemModel.Projection = QgsCoordinateReferenceSystemModel.CustomRole.Projection
QgsCoordinateReferenceSystemModel.Projection.is_monkey_patched = True
QgsCoordinateReferenceSystemModel.Projection.__doc__ = "Projection name. \n.. versionadded:: 3.42"
QgsCoordinateReferenceSystemModel.CustomRole.__doc__ = """Custom model roles.
.. note::
Expand Down Expand Up @@ -73,6 +79,14 @@
Available as ``QgsCoordinateReferenceSystemModel.RoleProj`` in older QGIS releases.
* ``Group``: Group name.
.. versionadded:: 3.42
* ``Projection``: Projection name.
.. versionadded:: 3.42
"""
# --
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ A tree model for display of known coordinate reference systems.
GroupId,
Wkt,
Proj,
Group,
Projection,
};

QgsCoordinateReferenceSystemModel( QObject *parent /TransferThis/ = 0 );
Expand Down
1 change: 1 addition & 0 deletions python/core/auto_additions/qgsstringutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
QgsStringUtils.htmlToMarkdown = staticmethod(QgsStringUtils.htmlToMarkdown)
QgsStringUtils.qRegExpEscape = staticmethod(QgsStringUtils.qRegExpEscape)
QgsStringUtils.truncateMiddleOfString = staticmethod(QgsStringUtils.truncateMiddleOfString)
QgsStringUtils.containsByWord = staticmethod(QgsStringUtils.containsByWord)
except (NameError, AttributeError):
pass
13 changes: 13 additions & 0 deletions python/core/auto_generated/qgsstringutils.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,19 @@ will be truncated by removing characters from the middle of the string
and replacing them with a horizontal ellipsis character.

.. versionadded:: 3.22
%End

static bool containsByWord( const QString &candidate, const QString &words, Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive );
%Docstring
Given a ``candidate`` string, returns ``True`` if the ``candidate`` contains
all the individual words from another string, regardless of their order.

.. note::

The search does NOT need to match whole words in the ``candidate`` string,
so eg a candidate string of "Worldmap_Winkel_II" will return ``True`` for ``words`` "winkle world"

.. versionadded:: 3.42
%End

};
Expand Down
14 changes: 14 additions & 0 deletions python/gui/auto_additions/qgscoordinatereferencesystemmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
QgsCoordinateReferenceSystemModel.Roles.RoleProj = QgsCoordinateReferenceSystemModel.CustomRole.Proj
QgsCoordinateReferenceSystemModel.RoleProj.is_monkey_patched = True
QgsCoordinateReferenceSystemModel.RoleProj.__doc__ = "The coordinate reference system's PROJ representation. This is only used for non-standard CRS (i.e. those not present in the database)."
QgsCoordinateReferenceSystemModel.Group = QgsCoordinateReferenceSystemModel.CustomRole.Group
QgsCoordinateReferenceSystemModel.Group.is_monkey_patched = True
QgsCoordinateReferenceSystemModel.Group.__doc__ = "Group name. \n.. versionadded:: 3.42"
QgsCoordinateReferenceSystemModel.Projection = QgsCoordinateReferenceSystemModel.CustomRole.Projection
QgsCoordinateReferenceSystemModel.Projection.is_monkey_patched = True
QgsCoordinateReferenceSystemModel.Projection.__doc__ = "Projection name. \n.. versionadded:: 3.42"
QgsCoordinateReferenceSystemModel.CustomRole.__doc__ = """Custom model roles.
.. note::
Expand Down Expand Up @@ -73,6 +79,14 @@
Available as ``QgsCoordinateReferenceSystemModel.RoleProj`` in older QGIS releases.
* ``Group``: Group name.
.. versionadded:: 3.42
* ``Projection``: Projection name.
.. versionadded:: 3.42
"""
# --
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ A tree model for display of known coordinate reference systems.
GroupId,
Wkt,
Proj,
Group,
Projection,
};

QgsCoordinateReferenceSystemModel( QObject *parent /TransferThis/ = 0 );
Expand Down
17 changes: 17 additions & 0 deletions src/core/qgsstringutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,23 @@ QString QgsStringUtils::truncateMiddleOfString( const QString &string, int maxLe
#endif
}

bool QgsStringUtils::containsByWord( const QString &candidate, const QString &words, Qt::CaseSensitivity sensitivity )
{
if ( candidate.trimmed().isEmpty() )
return false;

const thread_local QRegularExpression rxWhitespace( QStringLiteral( "\\s+" ) );
const QStringList parts = words.split( rxWhitespace, Qt::SkipEmptyParts );
if ( parts.empty() )
return false;
for ( const QString &word : parts )
{
if ( !candidate.contains( word, sensitivity ) )
return false;
}
return true;
}

QgsStringReplacement::QgsStringReplacement( const QString &match, const QString &replacement, bool caseSensitive, bool wholeWordOnly )
: mMatch( match )
, mReplacement( replacement )
Expand Down
11 changes: 11 additions & 0 deletions src/core/qgsstringutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,17 @@ class CORE_EXPORT QgsStringUtils
*/
static QString truncateMiddleOfString( const QString &string, int maxLength );

/**
* Given a \a candidate string, returns TRUE if the \a candidate contains
* all the individual words from another string, regardless of their order.
*
* \note The search does NOT need to match whole words in the \a candidate string,
* so eg a candidate string of "Worldmap_Winkel_II" will return TRUE for \a words "winkle world"
*
* \since QGIS 3.42
*/
static bool containsByWord( const QString &candidate, const QString &words, Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive );

};

#endif //QGSSTRINGUTILS_H
20 changes: 19 additions & 1 deletion src/gui/proj/qgscoordinatereferencesystemmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "moc_qgscoordinatereferencesystemmodel.cpp"
#include "qgscoordinatereferencesystemutils.h"
#include "qgsapplication.h"
#include "qgsstringutils.h"

#include <QFont>

Expand Down Expand Up @@ -157,6 +158,12 @@ QVariant QgsCoordinateReferenceSystemModel::data( const QModelIndex &index, int
case static_cast<int>( CustomRole::Proj ):
return crsNode->proj();

case static_cast<int>( CustomRole::Group ):
return crsNode->group();

case static_cast<int>( CustomRole::Projection ):
return crsNode->projection();

default:
break;
}
Expand Down Expand Up @@ -423,6 +430,7 @@ QgsCoordinateReferenceSystemModelCrsNode *QgsCoordinateReferenceSystemModel::add
break;
}
}
crsNode->setGroup( groupName );

if ( QgsCoordinateReferenceSystemModelGroupNode *group = parentNode->getChildGroupNode( groupId ) )
{
Expand All @@ -440,6 +448,8 @@ QgsCoordinateReferenceSystemModelCrsNode *QgsCoordinateReferenceSystemModel::add
QString projectionName = QgsCoordinateReferenceSystemUtils::translateProjection( record.projectionAcronym );
if ( projectionName.isEmpty() )
projectionName = tr( "Other" );
else
crsNode->setProjection( projectionName );

if ( QgsCoordinateReferenceSystemModelGroupNode *group = parentNode->getChildGroupNode( record.projectionAcronym ) )
{
Expand Down Expand Up @@ -706,7 +716,15 @@ bool QgsCoordinateReferenceSystemProxyModel::filterAcceptsRow( int sourceRow, co
if ( !mFilterString.trimmed().isEmpty() )
{
const QString name = sourceModel()->data( sourceIndex, static_cast<int>( QgsCoordinateReferenceSystemModel::CustomRole::Name ) ).toString();
if ( !( name.contains( mFilterString, Qt::CaseInsensitive )
QString candidate = name;
const QString groupName = sourceModel()->data( sourceIndex, static_cast<int>( QgsCoordinateReferenceSystemModel::CustomRole::Group ) ).toString();
if ( !groupName.isEmpty() )
candidate += ' ' + groupName;
const QString projectionName = sourceModel()->data( sourceIndex, static_cast<int>( QgsCoordinateReferenceSystemModel::CustomRole::Projection ) ).toString();
if ( !projectionName.isEmpty() )
candidate += ' ' + projectionName;

if ( !( QgsStringUtils::containsByWord( candidate, mFilterString )
|| authid.contains( mFilterString, Qt::CaseInsensitive ) ) )
return false;
}
Expand Down
36 changes: 36 additions & 0 deletions src/gui/proj/qgscoordinatereferencesystemmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,44 @@ class GUI_EXPORT QgsCoordinateReferenceSystemModelCrsNode : public QgsCoordinate
*/
QString proj() const { return mProj; }

/**
* Sets the CRS's group name.
*
* \see group()
* \since QGIS 3.42
*/
void setGroup( const QString &group ) { mGroup = group; }

/**
* Returns the CRS's group name.
*
* \see setGroup()
* \since QGIS 3.42
*/
QString group() const { return mGroup; }

/**
* Sets the CRS's projection name.
*
* \see projection()
* \since QGIS 3.42
*/
void setProjection( const QString &projection ) { mProjection = projection; }

/**
* Returns the CRS's projection name.
*
* \see setProjection()
* \since QGIS 3.42
*/
QString projection() const { return mProjection; }

private:
const QgsCrsDbRecord mRecord;
QString mWkt;
QString mProj;
QString mGroup;
QString mProjection;
};

#endif
Expand Down Expand Up @@ -232,6 +266,8 @@ class GUI_EXPORT QgsCoordinateReferenceSystemModel : public QAbstractItemModel
GroupId SIP_MONKEYPATCH_COMPAT_NAME( RoleGroupId ) = Qt::UserRole + 5, //!< The node ID (for group nodes)
Wkt SIP_MONKEYPATCH_COMPAT_NAME( RoleWkt ) = Qt::UserRole + 6, //!< The coordinate reference system's WKT representation. This is only used for non-standard CRS (i.e. those not present in the database).
Proj SIP_MONKEYPATCH_COMPAT_NAME( RoleProj ) = Qt::UserRole + 7, //!< The coordinate reference system's PROJ representation. This is only used for non-standard CRS (i.e. those not present in the database).
Group = Qt::UserRole + 8, //!< Group name. \since QGIS 3.42
Projection = Qt::UserRole + 9, //!< Projection name. \since QGIS 3.42
};
Q_ENUM( CustomRole )
// *INDENT-ON*
Expand Down
2 changes: 2 additions & 0 deletions src/gui/proj/qgsprojectionselectiontreewidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ QgsProjectionSelectionTreeWidget::QgsProjectionSelectionTreeWidget( QWidget *par
connect( leSearch, &QgsFilterLineEdit::textChanged, this, [=]( const QString &filter ) {
mCrsModel->setFilterString( filter );
mRecentCrsModel->setFilterString( filter );
if ( filter.length() >= 3 )
lstCoordinateSystems->expandAll();
} );

mAreaCanvas->setVisible( mShowMap );
Expand Down
39 changes: 39 additions & 0 deletions tests/src/python/test_qgscoordinatereferencesystemmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,16 @@ def test_model(self):
epsg_3577_index, QgsCoordinateReferenceSystemModel.Roles.RoleProj
)
)
self.assertEqual(
model.data(epsg_3577_index, QgsCoordinateReferenceSystemModel.Roles.Group),
"Projected",
)
self.assertEqual(
model.data(
epsg_3577_index, QgsCoordinateReferenceSystemModel.Roles.Projection
),
"Albers Equal Area",
)

# check that same result is returned by authIdToIndex
self.assertEqual(model.authIdToIndex("EPSG:3577"), epsg_3577_index)
Expand Down Expand Up @@ -765,6 +775,35 @@ def test_proxy_model(self):
][0]
self.assertTrue(epsg_4347_index.isValid())

model.setFilterString("equal gda2020 Area")
projected_index = [
model.index(row, 0, QModelIndex())
for row in range(model.rowCount(QModelIndex()))
if model.data(
model.index(row, 0, QModelIndex()),
QgsCoordinateReferenceSystemModel.Roles.RoleGroupId,
)
== "Projected"
][0]
aae_index = [
model.index(row, 0, projected_index)
for row in range(model.rowCount(projected_index))
if model.data(
model.index(row, 0, projected_index),
Qt.ItemDataRole.DisplayRole,
)
== "Albers Equal Area"
][0]
epsg_9473_index = [
model.index(row, 0, aae_index)
for row in range(model.rowCount(aae_index))
if model.data(
model.index(row, 0, aae_index),
QgsCoordinateReferenceSystemModel.Roles.RoleAuthId,
)
== "EPSG:9473"
][0]

model.setFilterString("")

# set filtered list of crs to show
Expand Down
Loading

0 comments on commit f585024

Please sign in to comment.