Add method to retrieve axis order for a QgsCoordinateReferenceSystem

This commit is contained in:
Nyall Dawson 2022-04-05 17:13:22 +10:00
parent 4f796e2be4
commit 28bb05b0d5
10 changed files with 359 additions and 2 deletions

View File

@ -785,6 +785,50 @@ Qgis.DataProviderFlag.__doc__ = 'Generic data provider flags.\n\n.. versionadded
# --
Qgis.DataProviderFlag.baseClass = Qgis
# monkey patching scoped based enum
Qgis.CrsAxisDirection.North.__doc__ = "North"
Qgis.CrsAxisDirection.NorthNorthEast.__doc__ = "North North East"
Qgis.CrsAxisDirection.NorthEast.__doc__ = "North East"
Qgis.CrsAxisDirection.EastNorthEast.__doc__ = "East North East"
Qgis.CrsAxisDirection.East.__doc__ = "East"
Qgis.CrsAxisDirection.EastSouthEast.__doc__ = "East South East"
Qgis.CrsAxisDirection.SouthEast.__doc__ = "South East"
Qgis.CrsAxisDirection.SouthSouthEast.__doc__ = "South South East"
Qgis.CrsAxisDirection.South.__doc__ = "South"
Qgis.CrsAxisDirection.SouthSouthWest.__doc__ = "South South West"
Qgis.CrsAxisDirection.SouthWest.__doc__ = "South West"
Qgis.CrsAxisDirection.WestSouthWest.__doc__ = "West South West"
Qgis.CrsAxisDirection.West.__doc__ = "West"
Qgis.CrsAxisDirection.WestNorthWest.__doc__ = "West North West"
Qgis.CrsAxisDirection.NorthWest.__doc__ = "North West"
Qgis.CrsAxisDirection.NorthNorthWest.__doc__ = "North North West"
Qgis.CrsAxisDirection.GeocentricX.__doc__ = "Geocentric (X)"
Qgis.CrsAxisDirection.GeocentricY.__doc__ = "Geocentric (Y)"
Qgis.CrsAxisDirection.GeocentricZ.__doc__ = "Geocentric (Z)"
Qgis.CrsAxisDirection.Up.__doc__ = "Up"
Qgis.CrsAxisDirection.Down.__doc__ = "Down"
Qgis.CrsAxisDirection.Forward.__doc__ = "Forward"
Qgis.CrsAxisDirection.Aft.__doc__ = "Aft"
Qgis.CrsAxisDirection.Port.__doc__ = "Port"
Qgis.CrsAxisDirection.Starboard.__doc__ = "Starboard"
Qgis.CrsAxisDirection.Clockwise.__doc__ = "Clockwise"
Qgis.CrsAxisDirection.CounterClockwise.__doc__ = "Counter clockwise"
Qgis.CrsAxisDirection.ColumnPositive.__doc__ = "Column positive"
Qgis.CrsAxisDirection.ColumnNegative.__doc__ = "Column negative"
Qgis.CrsAxisDirection.RowPositive.__doc__ = "Row positive"
Qgis.CrsAxisDirection.RowNegative.__doc__ = "Row negative"
Qgis.CrsAxisDirection.DisplayRight.__doc__ = "Display right"
Qgis.CrsAxisDirection.DisplayLeft.__doc__ = "Display left"
Qgis.CrsAxisDirection.DisplayUp.__doc__ = "Display up"
Qgis.CrsAxisDirection.DisplayDown.__doc__ = "Display down"
Qgis.CrsAxisDirection.Future.__doc__ = "Future"
Qgis.CrsAxisDirection.Past.__doc__ = "Past"
Qgis.CrsAxisDirection.Towards.__doc__ = "Towards"
Qgis.CrsAxisDirection.AwayFrom.__doc__ = "Away from"
Qgis.CrsAxisDirection.Unspecified.__doc__ = "Unspecified"
Qgis.CrsAxisDirection.__doc__ = 'Coordinate reference system axis directions.\n\nFrom "Geographic information — Well-known text representation of coordinate reference systems", section 7.5.1.\n\n.. versionadded:: 3.26\n\n' + '* ``North``: ' + Qgis.CrsAxisDirection.North.__doc__ + '\n' + '* ``NorthNorthEast``: ' + Qgis.CrsAxisDirection.NorthNorthEast.__doc__ + '\n' + '* ``NorthEast``: ' + Qgis.CrsAxisDirection.NorthEast.__doc__ + '\n' + '* ``EastNorthEast``: ' + Qgis.CrsAxisDirection.EastNorthEast.__doc__ + '\n' + '* ``East``: ' + Qgis.CrsAxisDirection.East.__doc__ + '\n' + '* ``EastSouthEast``: ' + Qgis.CrsAxisDirection.EastSouthEast.__doc__ + '\n' + '* ``SouthEast``: ' + Qgis.CrsAxisDirection.SouthEast.__doc__ + '\n' + '* ``SouthSouthEast``: ' + Qgis.CrsAxisDirection.SouthSouthEast.__doc__ + '\n' + '* ``South``: ' + Qgis.CrsAxisDirection.South.__doc__ + '\n' + '* ``SouthSouthWest``: ' + Qgis.CrsAxisDirection.SouthSouthWest.__doc__ + '\n' + '* ``SouthWest``: ' + Qgis.CrsAxisDirection.SouthWest.__doc__ + '\n' + '* ``WestSouthWest``: ' + Qgis.CrsAxisDirection.WestSouthWest.__doc__ + '\n' + '* ``West``: ' + Qgis.CrsAxisDirection.West.__doc__ + '\n' + '* ``WestNorthWest``: ' + Qgis.CrsAxisDirection.WestNorthWest.__doc__ + '\n' + '* ``NorthWest``: ' + Qgis.CrsAxisDirection.NorthWest.__doc__ + '\n' + '* ``NorthNorthWest``: ' + Qgis.CrsAxisDirection.NorthNorthWest.__doc__ + '\n' + '* ``GeocentricX``: ' + Qgis.CrsAxisDirection.GeocentricX.__doc__ + '\n' + '* ``GeocentricY``: ' + Qgis.CrsAxisDirection.GeocentricY.__doc__ + '\n' + '* ``GeocentricZ``: ' + Qgis.CrsAxisDirection.GeocentricZ.__doc__ + '\n' + '* ``Up``: ' + Qgis.CrsAxisDirection.Up.__doc__ + '\n' + '* ``Down``: ' + Qgis.CrsAxisDirection.Down.__doc__ + '\n' + '* ``Forward``: ' + Qgis.CrsAxisDirection.Forward.__doc__ + '\n' + '* ``Aft``: ' + Qgis.CrsAxisDirection.Aft.__doc__ + '\n' + '* ``Port``: ' + Qgis.CrsAxisDirection.Port.__doc__ + '\n' + '* ``Starboard``: ' + Qgis.CrsAxisDirection.Starboard.__doc__ + '\n' + '* ``Clockwise``: ' + Qgis.CrsAxisDirection.Clockwise.__doc__ + '\n' + '* ``CounterClockwise``: ' + Qgis.CrsAxisDirection.CounterClockwise.__doc__ + '\n' + '* ``ColumnPositive``: ' + Qgis.CrsAxisDirection.ColumnPositive.__doc__ + '\n' + '* ``ColumnNegative``: ' + Qgis.CrsAxisDirection.ColumnNegative.__doc__ + '\n' + '* ``RowPositive``: ' + Qgis.CrsAxisDirection.RowPositive.__doc__ + '\n' + '* ``RowNegative``: ' + Qgis.CrsAxisDirection.RowNegative.__doc__ + '\n' + '* ``DisplayRight``: ' + Qgis.CrsAxisDirection.DisplayRight.__doc__ + '\n' + '* ``DisplayLeft``: ' + Qgis.CrsAxisDirection.DisplayLeft.__doc__ + '\n' + '* ``DisplayUp``: ' + Qgis.CrsAxisDirection.DisplayUp.__doc__ + '\n' + '* ``DisplayDown``: ' + Qgis.CrsAxisDirection.DisplayDown.__doc__ + '\n' + '* ``Future``: ' + Qgis.CrsAxisDirection.Future.__doc__ + '\n' + '* ``Past``: ' + Qgis.CrsAxisDirection.Past.__doc__ + '\n' + '* ``Towards``: ' + Qgis.CrsAxisDirection.Towards.__doc__ + '\n' + '* ``AwayFrom``: ' + Qgis.CrsAxisDirection.AwayFrom.__doc__ + '\n' + '* ``Unspecified``: ' + Qgis.CrsAxisDirection.Unspecified.__doc__
# --
Qgis.CrsAxisDirection.baseClass = Qgis
# monkey patching scoped based enum
Qgis.AnnotationItemFlag.ScaleDependentBoundingBox.__doc__ = "Item's bounding box will vary depending on map scale"
Qgis.AnnotationItemFlag.__doc__ = 'Flags for annotation items.\n\n.. versionadded:: 3.22\n\n' + '* ``ScaleDependentBoundingBox``: ' + Qgis.AnnotationItemFlag.ScaleDependentBoundingBox.__doc__
# --

View File

@ -920,6 +920,47 @@ Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
:return: ``True`` if CRS axis is inverted
%End
SIP_PYOBJECT axisOrdering() const /TypeHint="List[Qgis.CrsAxisDirection]"/;
%Docstring
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
.. versionadded:: 3.26
%End
%MethodCode
// adapted from the qpymultimedia_qlist.sip file from the PyQt6 sources
const QList< Qgis::CrsAxisDirection > cppRes = sipCpp->axisOrdering();
PyObject *l = PyList_New( cppRes.size() );
if ( !l )
sipIsErr = 1;
else
{
for ( int i = 0; i < cppRes.size(); ++i )
{
PyObject *eobj = sipConvertFromEnum( static_cast<int>( cppRes.at( i ) ),
sipType_Qgis_CrsAxisDirection );
if ( !eobj )
{
sipIsErr = 1;
}
PyList_SetItem( l, i, eobj );
}
if ( !sipIsErr )
{
sipRes = l;
}
else
{
Py_DECREF( l );
}
}
%End
QgsUnitTypes::DistanceUnit mapUnits() const;
%Docstring
Returns the units for the projection used by the CRS.

View File

@ -560,6 +560,50 @@ The development version
typedef QFlags<Qgis::DataProviderFlag> DataProviderFlags;
enum class CrsAxisDirection
{
North,
NorthNorthEast,
NorthEast,
EastNorthEast,
East,
EastSouthEast,
SouthEast,
SouthSouthEast,
South,
SouthSouthWest,
SouthWest,
WestSouthWest,
West,
WestNorthWest,
NorthWest,
NorthNorthWest,
GeocentricX,
GeocentricY,
GeocentricZ,
Up,
Down,
Forward,
Aft,
Port,
Starboard,
Clockwise,
CounterClockwise,
ColumnPositive,
ColumnNegative,
RowPositive,
RowNegative,
DisplayRight,
DisplayLeft,
DisplayUp,
DisplayDown,
Future,
Past,
Towards,
AwayFrom,
Unspecified,
};
enum class AnnotationItemFlag
{
ScaleDependentBoundingBox,

View File

@ -298,7 +298,6 @@ template <TYPE>
%End
};
%MappedType QList<long>
{
%TypeHeaderCode

View File

@ -511,7 +511,7 @@ sub fix_annotations {
$line =~ s/\bSIP_GETWRAPPER\b/\/GetWrapper\//;
$line =~ s/SIP_PYNAME\(\s*(\w+)\s*\)/\/PyName=$1\//;
$line =~ s/SIP_TYPEHINT\(\s*([\w\s,\[\]]+?)\s*\)/\/TypeHint="$1"\//g;
$line =~ s/SIP_TYPEHINT\(\s*([\w\.\s,\[\]]+?)\s*\)/\/TypeHint="$1"\//g;
$line =~ s/SIP_VIRTUALERRORHANDLER\(\s*(\w+)\s*\)/\/VirtualErrorHandler=$1\//;
$line =~ s/SIP_THROW\(\s*(\w+)\s*\)/throw\( $1 \)/;

View File

@ -793,6 +793,101 @@ bool QgsCoordinateReferenceSystem::hasAxisInverted() const
return d->mAxisInverted;
}
QList<Qgis::CrsAxisDirection> QgsCoordinateReferenceSystem::axisOrdering() const
{
const PJ *projObject = d->threadLocalProjObject();
if ( !projObject )
return {};
PJ_CONTEXT *context = QgsProjContext::get();
QgsProjUtils::proj_pj_unique_ptr pjCs( proj_crs_get_coordinate_system( context, projObject ) );
if ( !pjCs )
return {};
const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
{
{ Qgis::CrsAxisDirection::North, QStringLiteral( "north" ) },
{ Qgis::CrsAxisDirection::NorthNorthEast, QStringLiteral( "northNorthEast" ) },
{ Qgis::CrsAxisDirection::NorthEast, QStringLiteral( "northEast" ) },
{ Qgis::CrsAxisDirection::EastNorthEast, QStringLiteral( "eastNorthEast" ) },
{ Qgis::CrsAxisDirection::East, QStringLiteral( "east" ) },
{ Qgis::CrsAxisDirection::EastSouthEast, QStringLiteral( "eastSouthEast" ) },
{ Qgis::CrsAxisDirection::SouthEast, QStringLiteral( "southEast" ) },
{ Qgis::CrsAxisDirection::SouthSouthEast, QStringLiteral( "southSouthEast" ) },
{ Qgis::CrsAxisDirection::South, QStringLiteral( "south" ) },
{ Qgis::CrsAxisDirection::SouthSouthWest, QStringLiteral( "southSouthWest" ) },
{ Qgis::CrsAxisDirection::SouthWest, QStringLiteral( "southWest" ) },
{ Qgis::CrsAxisDirection::WestSouthWest, QStringLiteral( "westSouthWest" ) },
{ Qgis::CrsAxisDirection::West, QStringLiteral( "west" ) },
{ Qgis::CrsAxisDirection::WestNorthWest, QStringLiteral( "westNorthWest" ) },
{ Qgis::CrsAxisDirection::NorthWest, QStringLiteral( "northWest" ) },
{ Qgis::CrsAxisDirection::NorthNorthWest, QStringLiteral( "northNorthWest" ) },
{ Qgis::CrsAxisDirection::GeocentricX, QStringLiteral( "geocentricX" ) },
{ Qgis::CrsAxisDirection::GeocentricY, QStringLiteral( "geocentricY" ) },
{ Qgis::CrsAxisDirection::GeocentricZ, QStringLiteral( "geocentricZ" ) },
{ Qgis::CrsAxisDirection::Up, QStringLiteral( "up" ) },
{ Qgis::CrsAxisDirection::Down, QStringLiteral( "down" ) },
{ Qgis::CrsAxisDirection::Forward, QStringLiteral( "forward" ) },
{ Qgis::CrsAxisDirection::Aft, QStringLiteral( "aft" ) },
{ Qgis::CrsAxisDirection::Port, QStringLiteral( "port" ) },
{ Qgis::CrsAxisDirection::Starboard, QStringLiteral( "starboard" ) },
{ Qgis::CrsAxisDirection::Clockwise, QStringLiteral( "clockwise" ) },
{ Qgis::CrsAxisDirection::CounterClockwise, QStringLiteral( "counterClockwise" ) },
{ Qgis::CrsAxisDirection::ColumnPositive, QStringLiteral( "columnPositive" ) },
{ Qgis::CrsAxisDirection::ColumnNegative, QStringLiteral( "columnNegative" ) },
{ Qgis::CrsAxisDirection::RowPositive, QStringLiteral( "rowPositive" ) },
{ Qgis::CrsAxisDirection::RowNegative, QStringLiteral( "rowNegative" ) },
{ Qgis::CrsAxisDirection::DisplayRight, QStringLiteral( "displayRight" ) },
{ Qgis::CrsAxisDirection::DisplayLeft, QStringLiteral( "displayLeft" ) },
{ Qgis::CrsAxisDirection::DisplayUp, QStringLiteral( "displayUp" ) },
{ Qgis::CrsAxisDirection::DisplayDown, QStringLiteral( "displayDown" ) },
{ Qgis::CrsAxisDirection::Future, QStringLiteral( "future" ) },
{ Qgis::CrsAxisDirection::Past, QStringLiteral( "past" ) },
{ Qgis::CrsAxisDirection::Towards, QStringLiteral( "towards" ) },
{ Qgis::CrsAxisDirection::AwayFrom, QStringLiteral( "awayFrom" ) },
};
QList< Qgis::CrsAxisDirection > res;
const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
if ( axisCount > 0 )
{
res.reserve( axisCount );
for ( int i = 0; i < axisCount; ++i )
{
const char *outDirection = nullptr;
proj_cs_get_axis_info( context, pjCs.get(), i,
nullptr,
nullptr,
&outDirection,
nullptr,
nullptr,
nullptr,
nullptr
);
// get first word of direction only
const thread_local QRegularExpression rx( QStringLiteral( "([^\\s]+).*" ) );
const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
if ( !match.hasMatch() )
continue;
const QString direction = match.captured( 1 );
Qgis::CrsAxisDirection dir = Qgis::CrsAxisDirection::Unspecified;
for ( auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
{
if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
{
dir = it.key();
break;
}
}
res.append( dir );
}
}
return res;
}
bool QgsCoordinateReferenceSystem::createFromWkt( const QString &wkt )
{
return createFromWktInternal( wkt, QString() );

View File

@ -841,6 +841,51 @@ class CORE_EXPORT QgsCoordinateReferenceSystem
*/
bool hasAxisInverted() const;
/**
* Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
*
* \since QGIS 3.26
*/
#ifndef SIP_RUN
QList< Qgis::CrsAxisDirection > axisOrdering() const;
#else
SIP_PYOBJECT axisOrdering() const SIP_TYPEHINT( List[Qgis.CrsAxisDirection] );
% MethodCode
// adapted from the qpymultimedia_qlist.sip file from the PyQt6 sources
const QList< Qgis::CrsAxisDirection > cppRes = sipCpp->axisOrdering();
PyObject *l = PyList_New( cppRes.size() );
if ( !l )
sipIsErr = 1;
else
{
for ( int i = 0; i < cppRes.size(); ++i )
{
PyObject *eobj = sipConvertFromEnum( static_cast<int>( cppRes.at( i ) ),
sipType_Qgis_CrsAxisDirection );
if ( !eobj )
{
sipIsErr = 1;
}
PyList_SetItem( l, i, eobj );
}
if ( !sipIsErr )
{
sipRes = l;
}
else
{
Py_DECREF( l );
}
}
% End
#endif
/**
* Returns the units for the projection used by the CRS.
*/

View File

@ -900,6 +900,58 @@ class CORE_EXPORT Qgis
Q_DECLARE_FLAGS( DataProviderFlags, DataProviderFlag )
Q_ENUM( DataProviderFlag )
/**
* Coordinate reference system axis directions.
*
* From "Geographic information — Well-known text representation of coordinate reference systems", section 7.5.1.
*
* \since QGIS 3.26
*/
enum class CrsAxisDirection : int
{
North, //!< North
NorthNorthEast, //!< North North East
NorthEast, //!< North East
EastNorthEast, //!< East North East
East, //!< East
EastSouthEast, //!< East South East
SouthEast, //!< South East
SouthSouthEast, //!< South South East
South, //!< South
SouthSouthWest, //!< South South West
SouthWest, //!< South West
WestSouthWest, //!< West South West
West, //!< West
WestNorthWest, //!< West North West
NorthWest, //!< North West
NorthNorthWest, //!< North North West
GeocentricX, //!< Geocentric (X)
GeocentricY, //!< Geocentric (Y)
GeocentricZ, //!< Geocentric (Z)
Up, //!< Up
Down, //!< Down
Forward, //!< Forward
Aft, //!< Aft
Port, //!< Port
Starboard, //!< Starboard
Clockwise, //!< Clockwise
CounterClockwise, //!< Counter clockwise
ColumnPositive, //!< Column positive
ColumnNegative, //!< Column negative
RowPositive, //!< Row positive
RowNegative, //!< Row negative
DisplayRight, //!< Display right
DisplayLeft, //!< Display left
DisplayUp, //!< Display up
DisplayDown, //!< Display down
Future, //!< Future
Past, //!< Past
Towards, //!< Towards
AwayFrom, //!< Away from
Unspecified, //!< Unspecified
};
Q_ENUM( CrsAxisDirection )
/**
* Flags for annotation items.
*

View File

@ -59,6 +59,7 @@ ADD_PYTHON_TEST(PyQgsColorSchemeRegistry test_qgscolorschemeregistry.py)
ADD_PYTHON_TEST(PyQgsCoordinateFormatter test_qgscoordinateformatter.py)
ADD_PYTHON_TEST(PyQgsCoordinateOperationWidget test_qgscoordinateoperationwidget.py)
ADD_PYTHON_TEST(PyQgsConditionalFormatWidgets test_qgsconditionalformatwidgets.py)
ADD_PYTHON_TEST(PyQgsCoordinateReferenceSystem test_qgscoordinatereferencesystem.py)
ADD_PYTHON_TEST(PyQgsConditionalStyle test_qgsconditionalstyle.py)
ADD_PYTHON_TEST(PyQgsConnectionRegistry test_qgsconnectionregistry.py)
ADD_PYTHON_TEST(PyQgsCoordinateTransformContext test_qgscoordinatetransformcontext.py)

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsCoordinateReferenceSystem.
Note that most of the tests for this class are in the c++ test file!
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = '(C) 2022 by Nyall Dawson'
__date__ = '06/04/2022'
__copyright__ = 'Copyright 2022, The QGIS Project'
import qgis # NOQA
from qgis.core import (QgsCoordinateReferenceSystem,
Qgis)
from qgis.testing import start_app, unittest
start_app()
class TestQgsCoordinateReferenceSystem(unittest.TestCase):
def test_axis_order(self):
"""
Test QgsCoordinateReferenceSystem.axisOrdering() (including the Python MethodCode associated with this)
"""
self.assertEqual(QgsCoordinateReferenceSystem().axisOrdering(), [])
self.assertEqual(QgsCoordinateReferenceSystem('EPSG:4326').axisOrdering(), [Qgis.CrsAxisDirection.North, Qgis.CrsAxisDirection.East])
self.assertEqual(QgsCoordinateReferenceSystem('EPSG:3111').axisOrdering(), [Qgis.CrsAxisDirection.East, Qgis.CrsAxisDirection.North])
if __name__ == '__main__':
unittest.main()