mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-15 00:07:25 -05:00
Add a numeric format for geographic coordinates
This commit is contained in:
parent
799679cfe4
commit
ee5cbe93a9
8
python/core/auto_additions/qgscoordinatenumericformat.py
Normal file
8
python/core/auto_additions/qgscoordinatenumericformat.py
Normal file
@ -0,0 +1,8 @@
|
||||
# The following has been generated automatically from src/core/numericformats/qgscoordinatenumericformat.h
|
||||
# monkey patching scoped based enum
|
||||
QgsGeographicCoordinateNumericFormat.AngleFormat.DegreesMinutesSeconds.__doc__ = "Degrees, minutes and seconds, eg 30 degrees 45'30"
|
||||
QgsGeographicCoordinateNumericFormat.AngleFormat.DegreesMinutes.__doc__ = "Degrees and decimal minutes, eg 30 degrees 45.55'"
|
||||
QgsGeographicCoordinateNumericFormat.AngleFormat.DecimalDegrees.__doc__ = "Decimal degrees, eg 30.7555 degrees"
|
||||
QgsGeographicCoordinateNumericFormat.AngleFormat.__doc__ = 'Angle format options.\n\n' + '* ``DegreesMinutesSeconds``: ' + QgsGeographicCoordinateNumericFormat.AngleFormat.DegreesMinutesSeconds.__doc__ + '\n' + '* ``DegreesMinutes``: ' + QgsGeographicCoordinateNumericFormat.AngleFormat.DegreesMinutes.__doc__ + '\n' + '* ``DecimalDegrees``: ' + QgsGeographicCoordinateNumericFormat.AngleFormat.DecimalDegrees.__doc__
|
||||
# --
|
||||
QgsGeographicCoordinateNumericFormat.AngleFormat.baseClass = QgsGeographicCoordinateNumericFormat
|
||||
8
python/core/auto_additions/qgsnumericformat.py
Normal file
8
python/core/auto_additions/qgsnumericformat.py
Normal file
@ -0,0 +1,8 @@
|
||||
# The following has been generated automatically from src/core/numericformats/qgsnumericformat.h
|
||||
# monkey patching scoped based enum
|
||||
QgsNumericFormatContext.Interpretation.Generic.__doc__ = "Generic"
|
||||
QgsNumericFormatContext.Interpretation.Latitude.__doc__ = "Latitude values"
|
||||
QgsNumericFormatContext.Interpretation.Longitude.__doc__ = "Longitude values"
|
||||
QgsNumericFormatContext.Interpretation.__doc__ = 'Interpretation of numeric values.\n\n.. versionadded:: 3.26\n\n' + '* ``Generic``: ' + QgsNumericFormatContext.Interpretation.Generic.__doc__ + '\n' + '* ``Latitude``: ' + QgsNumericFormatContext.Interpretation.Latitude.__doc__ + '\n' + '* ``Longitude``: ' + QgsNumericFormatContext.Interpretation.Longitude.__doc__
|
||||
# --
|
||||
QgsNumericFormatContext.Interpretation.baseClass = QgsNumericFormatContext
|
||||
@ -0,0 +1,124 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/numericformats/qgscoordinatenumericformat.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
class QgsGeographicCoordinateNumericFormat : QgsBasicNumericFormat
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A numeric formatter which returns a text representation of a geographic coordinate (latitude or longitude).
|
||||
|
||||
.. versionadded:: 3.26
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgscoordinatenumericformat.h"
|
||||
%End
|
||||
public:
|
||||
static const QMetaObject staticMetaObject;
|
||||
|
||||
public:
|
||||
|
||||
enum class AngleFormat
|
||||
{
|
||||
DegreesMinutesSeconds,
|
||||
DegreesMinutes,
|
||||
DecimalDegrees,
|
||||
};
|
||||
|
||||
QgsGeographicCoordinateNumericFormat();
|
||||
%Docstring
|
||||
Default constructor
|
||||
%End
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
virtual QString visibleName() const;
|
||||
|
||||
virtual int sortKey();
|
||||
|
||||
virtual double suggestSampleValue() const;
|
||||
|
||||
virtual QString formatDouble( double value, const QgsNumericFormatContext &context ) const;
|
||||
|
||||
virtual QgsGeographicCoordinateNumericFormat *clone() const /Factory/;
|
||||
|
||||
virtual QgsNumericFormat *create( const QVariantMap &configuration, const QgsReadWriteContext &context ) const /Factory/;
|
||||
|
||||
virtual QVariantMap configuration( const QgsReadWriteContext &context ) const;
|
||||
|
||||
|
||||
AngleFormat angleFormat() const;
|
||||
%Docstring
|
||||
Returns the angle format, which controls how bearing the angles are formatted
|
||||
described in the returned strings.
|
||||
|
||||
.. seealso:: :py:func:`setAngleFormat`
|
||||
%End
|
||||
|
||||
void setAngleFormat( AngleFormat format );
|
||||
%Docstring
|
||||
Sets the directional formatting option, which controls how bearing the angles are formatted
|
||||
described in the returned strings.
|
||||
|
||||
.. seealso:: :py:func:`angleFormat`
|
||||
%End
|
||||
|
||||
bool showLeadingZeros() const;
|
||||
%Docstring
|
||||
Returns ``True`` if leading zeros in the minutes or seconds values should be shown.
|
||||
|
||||
.. seealso:: :py:func:`setShowLeadingZeros`
|
||||
%End
|
||||
|
||||
void setShowLeadingZeros( bool show );
|
||||
%Docstring
|
||||
Sets whether leading zeros in the minutes or seconds values should be shown.
|
||||
|
||||
.. seealso:: :py:func:`showLeadingZeros`
|
||||
%End
|
||||
|
||||
bool showDegreeLeadingZeros() const;
|
||||
%Docstring
|
||||
Returns ``True`` if leading zeros for the degree values should be shown.
|
||||
|
||||
.. seealso:: :py:func:`setShowDegreeLeadingZeros`
|
||||
%End
|
||||
|
||||
void setShowDegreeLeadingZeros( bool show );
|
||||
%Docstring
|
||||
Sets whether leading zeros for the degree values should be shown.
|
||||
|
||||
.. seealso:: :py:func:`showDegreeLeadingZeros`
|
||||
%End
|
||||
|
||||
bool showDirectionalSuffix() const;
|
||||
%Docstring
|
||||
Returns ``True`` if directional suffixes (e.g. "N") should be included.
|
||||
|
||||
.. seealso:: :py:func:`setShowDirectionalSuffix`
|
||||
%End
|
||||
|
||||
void setShowDirectionalSuffix( bool show );
|
||||
%Docstring
|
||||
Sets whether directional suffixes (e.g. "N") should be included.
|
||||
|
||||
.. seealso:: :py:func:`showDirectionalSuffix`
|
||||
%End
|
||||
|
||||
virtual void setConfiguration( const QVariantMap &configuration, const QgsReadWriteContext &context );
|
||||
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/numericformats/qgscoordinatenumericformat.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
@ -21,6 +21,9 @@ A context for numeric formats
|
||||
%TypeHeaderCode
|
||||
#include "qgsnumericformat.h"
|
||||
%End
|
||||
public:
|
||||
static const QMetaObject staticMetaObject;
|
||||
|
||||
public:
|
||||
|
||||
QgsNumericFormatContext();
|
||||
@ -129,6 +132,32 @@ Sets the exponential ``character``.
|
||||
.. seealso:: :py:func:`exponential`
|
||||
%End
|
||||
|
||||
enum class Interpretation
|
||||
{
|
||||
Generic,
|
||||
Latitude,
|
||||
Longitude,
|
||||
};
|
||||
|
||||
Interpretation interpretation() const;
|
||||
%Docstring
|
||||
Returns the interpretation of the numbers being converted.
|
||||
|
||||
.. seealso:: :py:func:`setInterpretation`
|
||||
|
||||
.. versionadded:: 3.26
|
||||
%End
|
||||
|
||||
void setInterpretation( Interpretation interpretation );
|
||||
%Docstring
|
||||
Sets the ``interpretation`` of the numbers being converted.
|
||||
|
||||
.. seealso:: :py:func:`interpretation`
|
||||
|
||||
.. versionadded:: 3.26
|
||||
%End
|
||||
|
||||
|
||||
};
|
||||
|
||||
%ModuleHeaderCode
|
||||
@ -138,6 +167,7 @@ Sets the exponential ``character``.
|
||||
#include <qgsfallbacknumericformat.h>
|
||||
#include <qgspercentagenumericformat.h>
|
||||
#include <qgsscientificnumericformat.h>
|
||||
#include <qgscoordinatenumericformat.h>
|
||||
%End
|
||||
|
||||
class QgsNumericFormat
|
||||
@ -158,6 +188,8 @@ This is an abstract base class and will always need to be subclassed.
|
||||
%ConvertToSubClassCode
|
||||
if ( dynamic_cast< QgsBearingNumericFormat * >( sipCpp ) )
|
||||
sipType = sipType_QgsBearingNumericFormat;
|
||||
else if ( dynamic_cast< QgsGeographicCoordinateNumericFormat * >( sipCpp ) )
|
||||
sipType = sipType_QgsGeographicCoordinateNumericFormat;
|
||||
else if ( dynamic_cast< QgsFallbackNumericFormat * >( sipCpp ) )
|
||||
sipType = sipType_QgsFallbackNumericFormat;
|
||||
else if ( dynamic_cast< QgsPercentageNumericFormat * >( sipCpp ) )
|
||||
|
||||
@ -499,6 +499,7 @@
|
||||
%Include auto_generated/network/qgshttpheaders.sip
|
||||
%Include auto_generated/numericformats/qgsbasicnumericformat.sip
|
||||
%Include auto_generated/numericformats/qgsbearingnumericformat.sip
|
||||
%Include auto_generated/numericformats/qgscoordinatenumericformat.sip
|
||||
%Include auto_generated/numericformats/qgscurrencynumericformat.sip
|
||||
%Include auto_generated/numericformats/qgsfallbacknumericformat.sip
|
||||
%Include auto_generated/numericformats/qgsfractionnumericformat.sip
|
||||
|
||||
@ -141,6 +141,63 @@ Ownership of the returned object is transferred to the caller
|
||||
|
||||
|
||||
|
||||
class QgsGeographicCoordinateNumericFormatWidget : QgsNumericFormatWidget
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A widget which allow control over the properties of a :py:class:`QgsGeographicCoordinateNumericFormat`.
|
||||
|
||||
.. versionadded:: 3.26
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsnumericformatwidget.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsGeographicCoordinateNumericFormatWidget( const QgsNumericFormat *format, QWidget *parent /TransferThis/ = 0 );
|
||||
%Docstring
|
||||
Constructor for QgsGeographicCoordinateNumericFormatWidget, initially showing the specified ``format``.
|
||||
%End
|
||||
~QgsGeographicCoordinateNumericFormatWidget();
|
||||
|
||||
virtual void setFormat( QgsNumericFormat *format );
|
||||
|
||||
|
||||
virtual QgsNumericFormat *format() /Factory/;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
class QgsGeographicCoordinateNumericFormatDialog : QDialog
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A dialog which allow control over the properties of a :py:class:`QgsGeographicCoordinateNumericFormat`.
|
||||
|
||||
.. versionadded:: 3.26
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsnumericformatwidget.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsGeographicCoordinateNumericFormatDialog( const QgsNumericFormat *format, QWidget *parent /TransferThis/ = 0 );
|
||||
%Docstring
|
||||
Constructor for QgsGeographicCoordinateNumericFormatDialog, initially showing the specified ``format``.
|
||||
%End
|
||||
|
||||
QgsGeographicCoordinateNumericFormat *format() /Factory/;
|
||||
%Docstring
|
||||
Returns the format defined by the current settings in the dialog.
|
||||
|
||||
Ownership of the returned object is transferred to the caller
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsCurrencyNumericFormatWidget : QgsNumericFormatWidget
|
||||
|
||||
@ -166,6 +166,7 @@ set(QGIS_CORE_SRCS
|
||||
|
||||
numericformats/qgsbasicnumericformat.cpp
|
||||
numericformats/qgsbearingnumericformat.cpp
|
||||
numericformats/qgscoordinatenumericformat.cpp
|
||||
numericformats/qgscurrencynumericformat.cpp
|
||||
numericformats/qgsfallbacknumericformat.cpp
|
||||
numericformats/qgsfractionnumericformat.cpp
|
||||
@ -1576,6 +1577,7 @@ set(QGIS_CORE_HDRS
|
||||
|
||||
numericformats/qgsbasicnumericformat.h
|
||||
numericformats/qgsbearingnumericformat.h
|
||||
numericformats/qgscoordinatenumericformat.h
|
||||
numericformats/qgscurrencynumericformat.h
|
||||
numericformats/qgsfallbacknumericformat.h
|
||||
numericformats/qgsfractionnumericformat.h
|
||||
|
||||
653
src/core/numericformats/qgscoordinatenumericformat.cpp
Normal file
653
src/core/numericformats/qgscoordinatenumericformat.cpp
Normal file
@ -0,0 +1,653 @@
|
||||
/***************************************************************************
|
||||
qgscoordinatenumericformat.cpp
|
||||
--------------------------
|
||||
begin : April 2022
|
||||
copyright : (C) 2022 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgscoordinatenumericformat.h"
|
||||
#include "qgis.h"
|
||||
#include "qgscoordinateformatter.h"
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <iomanip>
|
||||
|
||||
struct formatter : std::numpunct<wchar_t>
|
||||
{
|
||||
formatter( QChar thousands, bool showThousands, QChar decimal )
|
||||
: mThousands( thousands.unicode() )
|
||||
, mDecimal( decimal.unicode() )
|
||||
, mShowThousands( showThousands )
|
||||
{}
|
||||
wchar_t do_decimal_point() const override { return mDecimal; }
|
||||
wchar_t do_thousands_sep() const override { return mThousands; }
|
||||
std::string do_grouping() const override { return mShowThousands ? "\3" : "\0"; }
|
||||
|
||||
wchar_t mThousands;
|
||||
wchar_t mDecimal;
|
||||
bool mShowThousands = true;
|
||||
};
|
||||
|
||||
|
||||
QgsGeographicCoordinateNumericFormat::QgsGeographicCoordinateNumericFormat()
|
||||
{
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::id() const
|
||||
{
|
||||
return QStringLiteral( "geographiccoordinate" );
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::visibleName() const
|
||||
{
|
||||
return QObject::tr( "Geographic Coordinate" );
|
||||
}
|
||||
|
||||
int QgsGeographicCoordinateNumericFormat::sortKey()
|
||||
{
|
||||
return QgsNumericFormat::sortKey();
|
||||
}
|
||||
|
||||
double QgsGeographicCoordinateNumericFormat::suggestSampleValue() const
|
||||
{
|
||||
return 3.7555;
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::formatDouble( double value, const QgsNumericFormatContext &context ) const
|
||||
{
|
||||
const QChar decimal = decimalSeparator().isNull() ? context.decimalSeparator() : decimalSeparator();
|
||||
std::basic_stringstream<wchar_t> os;
|
||||
os.imbue( std::locale( os.getloc(), new formatter( thousandsSeparator().isNull() ? context.thousandsSeparator() : thousandsSeparator(),
|
||||
false,
|
||||
decimal ) ) );
|
||||
|
||||
switch ( context.interpretation() )
|
||||
{
|
||||
case QgsNumericFormatContext::Interpretation::Latitude:
|
||||
return formatLatitude( value, os, context );
|
||||
|
||||
case QgsNumericFormatContext::Interpretation::Generic:
|
||||
case QgsNumericFormatContext::Interpretation::Longitude:
|
||||
return formatLongitude( value, os, context );
|
||||
}
|
||||
BUILTIN_UNREACHABLE
|
||||
}
|
||||
|
||||
QgsGeographicCoordinateNumericFormat *QgsGeographicCoordinateNumericFormat::clone() const
|
||||
{
|
||||
return new QgsGeographicCoordinateNumericFormat( *this );
|
||||
}
|
||||
|
||||
QgsNumericFormat *QgsGeographicCoordinateNumericFormat::create( const QVariantMap &configuration, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
std::unique_ptr< QgsGeographicCoordinateNumericFormat > res = std::make_unique< QgsGeographicCoordinateNumericFormat >();
|
||||
res->setConfiguration( configuration, context );
|
||||
res->mAngleFormat = qgsEnumKeyToValue( configuration.value( QStringLiteral( "angle_format" ) ).toString(), AngleFormat::DecimalDegrees );
|
||||
res->mShowLeadingZeros = configuration.value( QStringLiteral( "show_leading_zeros" ), false ).toBool();
|
||||
res->mShowLeadingDegreeZeros = configuration.value( QStringLiteral( "show_leading_degree_zeros" ), false ).toBool();
|
||||
res->mUseSuffix = configuration.value( QStringLiteral( "show_suffix" ), false ).toBool();
|
||||
return res.release();
|
||||
}
|
||||
|
||||
QVariantMap QgsGeographicCoordinateNumericFormat::configuration( const QgsReadWriteContext &context ) const
|
||||
{
|
||||
QVariantMap res = QgsBasicNumericFormat::configuration( context );
|
||||
res.insert( QStringLiteral( "angle_format" ), qgsEnumValueToKey( mAngleFormat ) );
|
||||
res.insert( QStringLiteral( "show_leading_zeros" ), mShowLeadingZeros );
|
||||
res.insert( QStringLiteral( "show_leading_degree_zeros" ), mShowLeadingDegreeZeros );
|
||||
res.insert( QStringLiteral( "show_suffix" ), mUseSuffix );
|
||||
return res;
|
||||
}
|
||||
|
||||
QgsGeographicCoordinateNumericFormat::AngleFormat QgsGeographicCoordinateNumericFormat::angleFormat() const
|
||||
{
|
||||
return mAngleFormat;
|
||||
}
|
||||
|
||||
void QgsGeographicCoordinateNumericFormat::setAngleFormat( QgsGeographicCoordinateNumericFormat::AngleFormat format )
|
||||
{
|
||||
mAngleFormat = format;
|
||||
}
|
||||
|
||||
void QgsGeographicCoordinateNumericFormat::setConfiguration( const QVariantMap &configuration, const QgsReadWriteContext &context )
|
||||
{
|
||||
QgsBasicNumericFormat::setConfiguration( configuration, context );
|
||||
mAngleFormat = qgsEnumKeyToValue( configuration.value( QStringLiteral( "angle_format" ) ).toString(), AngleFormat::DecimalDegrees );
|
||||
mShowLeadingZeros = configuration.value( QStringLiteral( "show_leading_zeros" ), false ).toBool();
|
||||
mShowLeadingDegreeZeros = configuration.value( QStringLiteral( "show_leading_degree_zeros" ), false ).toBool();
|
||||
mUseSuffix = configuration.value( QStringLiteral( "show_suffix" ), false ).toBool();
|
||||
}
|
||||
|
||||
bool QgsGeographicCoordinateNumericFormat::showLeadingZeros() const
|
||||
{
|
||||
return mShowLeadingZeros;
|
||||
}
|
||||
|
||||
void QgsGeographicCoordinateNumericFormat::setShowLeadingZeros( bool newShowLeadingZeros )
|
||||
{
|
||||
mShowLeadingZeros = newShowLeadingZeros;
|
||||
}
|
||||
|
||||
bool QgsGeographicCoordinateNumericFormat::showDegreeLeadingZeros() const
|
||||
{
|
||||
return mShowLeadingDegreeZeros;
|
||||
}
|
||||
|
||||
void QgsGeographicCoordinateNumericFormat::setShowDegreeLeadingZeros( bool show )
|
||||
{
|
||||
mShowLeadingDegreeZeros = show;
|
||||
}
|
||||
|
||||
bool QgsGeographicCoordinateNumericFormat::showDirectionalSuffix() const
|
||||
{
|
||||
return mUseSuffix;
|
||||
}
|
||||
|
||||
void QgsGeographicCoordinateNumericFormat::setShowDirectionalSuffix( bool show )
|
||||
{
|
||||
mUseSuffix = show;
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::formatLongitude( double value, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const
|
||||
{
|
||||
switch ( mAngleFormat )
|
||||
{
|
||||
case QgsGeographicCoordinateNumericFormat::AngleFormat::DegreesMinutesSeconds:
|
||||
return formatLongitudeAsDegreesMinutesSeconds( value, ss, context );
|
||||
case QgsGeographicCoordinateNumericFormat::AngleFormat::DegreesMinutes:
|
||||
return formatLongitudeAsDegreesMinutes( value, ss, context );
|
||||
case QgsGeographicCoordinateNumericFormat::AngleFormat::DecimalDegrees:
|
||||
return formatLongitudeAsDegrees( value, ss, context );
|
||||
}
|
||||
BUILTIN_UNREACHABLE
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::formatLatitude( double value, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const
|
||||
{
|
||||
switch ( mAngleFormat )
|
||||
{
|
||||
case QgsGeographicCoordinateNumericFormat::AngleFormat::DegreesMinutesSeconds:
|
||||
return formatLatitudeAsDegreesMinutesSeconds( value, ss, context );
|
||||
case QgsGeographicCoordinateNumericFormat::AngleFormat::DegreesMinutes:
|
||||
return formatLatitudeAsDegreesMinutes( value, ss, context );
|
||||
case QgsGeographicCoordinateNumericFormat::AngleFormat::DecimalDegrees:
|
||||
return formatLatitudeAsDegrees( value, ss, context );
|
||||
}
|
||||
BUILTIN_UNREACHABLE
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::formatLatitudeAsDegreesMinutesSeconds( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const
|
||||
{
|
||||
//first, limit latitude to -180 to 180 degree range
|
||||
double wrappedY = std::fmod( val, 180.0 );
|
||||
//next, wrap around latitudes > 90 or < -90 degrees, so that eg "110S" -> "70N"
|
||||
if ( wrappedY > 90.0 )
|
||||
{
|
||||
wrappedY = wrappedY - 180.0;
|
||||
}
|
||||
else if ( wrappedY < -90.0 )
|
||||
{
|
||||
wrappedY = wrappedY + 180.0;
|
||||
}
|
||||
|
||||
const int precisionMultiplier = std::pow( 10.0, numberDecimalPlaces() );
|
||||
|
||||
int degreesY = int( std::fabs( wrappedY ) );
|
||||
const double floatMinutesY = ( std::fabs( wrappedY ) - degreesY ) * 60.0;
|
||||
int intMinutesY = int( floatMinutesY );
|
||||
double secondsY = ( floatMinutesY - intMinutesY ) * 60.0;
|
||||
|
||||
//make sure rounding to specified precision doesn't create seconds >= 60
|
||||
if ( std::round( secondsY * precisionMultiplier ) >= 60 * precisionMultiplier )
|
||||
{
|
||||
secondsY = std::max( secondsY - 60, 0.0 );
|
||||
intMinutesY++;
|
||||
if ( intMinutesY >= 60 )
|
||||
{
|
||||
intMinutesY -= 60;
|
||||
degreesY++;
|
||||
}
|
||||
}
|
||||
|
||||
QString hemisphere;
|
||||
QString sign;
|
||||
if ( mUseSuffix )
|
||||
{
|
||||
hemisphere = wrappedY < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( wrappedY < 0 )
|
||||
{
|
||||
sign = context.negativeSign();
|
||||
}
|
||||
}
|
||||
//check if coordinate is all zeros for the specified precision, and if so,
|
||||
//remove the sign and hemisphere strings
|
||||
if ( degreesY == 0 && intMinutesY == 0 && std::round( secondsY * precisionMultiplier ) == 0 )
|
||||
{
|
||||
sign = QString();
|
||||
hemisphere.clear();
|
||||
}
|
||||
|
||||
QString strMinutesY;
|
||||
QString strSecondsY;
|
||||
|
||||
ss << std::fixed << std::setprecision( 0 );
|
||||
ss << intMinutesY;
|
||||
|
||||
strMinutesY = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
ss << std::fixed << std::setprecision( numberDecimalPlaces() );
|
||||
ss << secondsY;
|
||||
strSecondsY = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
trimTrailingZeros( strSecondsY, context );
|
||||
|
||||
//pad with leading digits if required
|
||||
if ( mShowLeadingZeros && intMinutesY < 10 )
|
||||
strMinutesY = '0' + strMinutesY;
|
||||
|
||||
if ( mShowLeadingZeros && secondsY < 10 )
|
||||
strSecondsY = '0' + strSecondsY;
|
||||
|
||||
ss << std::fixed << std::setprecision( 0 );
|
||||
ss << degreesY;
|
||||
QString degreesYStr = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
if ( mShowLeadingDegreeZeros )
|
||||
degreesYStr = QString( QStringLiteral( "00" ) + degreesYStr ).right( 2 );
|
||||
|
||||
return sign + degreesYStr + QChar( 176 ) +
|
||||
strMinutesY + QChar( 0x2032 ) +
|
||||
strSecondsY + QChar( 0x2033 ) +
|
||||
hemisphere;
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::formatLongitudeAsDegreesMinutesSeconds( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const
|
||||
{
|
||||
//first, limit longitude to -360 to 360 degree range
|
||||
double wrappedX = std::fmod( val, 360.0 );
|
||||
//next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W"
|
||||
if ( wrappedX > 180.0 )
|
||||
{
|
||||
wrappedX = wrappedX - 360.0;
|
||||
}
|
||||
else if ( wrappedX < -180.0 )
|
||||
{
|
||||
wrappedX = wrappedX + 360.0;
|
||||
}
|
||||
|
||||
const int precisionMultiplier = std::pow( 10.0, numberDecimalPlaces() );
|
||||
|
||||
int degreesX = int( std::fabs( wrappedX ) );
|
||||
const double floatMinutesX = ( std::fabs( wrappedX ) - degreesX ) * 60.0;
|
||||
int intMinutesX = int( floatMinutesX );
|
||||
double secondsX = ( floatMinutesX - intMinutesX ) * 60.0;
|
||||
|
||||
//make sure rounding to specified precision doesn't create seconds >= 60
|
||||
if ( std::round( secondsX * precisionMultiplier ) >= 60 * precisionMultiplier )
|
||||
{
|
||||
secondsX = std::max( secondsX - 60, 0.0 );
|
||||
intMinutesX++;
|
||||
if ( intMinutesX >= 60 )
|
||||
{
|
||||
intMinutesX -= 60;
|
||||
degreesX++;
|
||||
}
|
||||
}
|
||||
|
||||
QString hemisphere;
|
||||
QString sign;
|
||||
if ( mUseSuffix )
|
||||
{
|
||||
hemisphere = wrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( wrappedX < 0 )
|
||||
{
|
||||
sign = context.negativeSign();
|
||||
}
|
||||
}
|
||||
//check if coordinate is all zeros for the specified precision, and if so,
|
||||
//remove the sign and hemisphere strings
|
||||
if ( degreesX == 0 && intMinutesX == 0 && std::round( secondsX * precisionMultiplier ) == 0 )
|
||||
{
|
||||
sign.clear();
|
||||
hemisphere.clear();
|
||||
}
|
||||
|
||||
//also remove directional prefix from 180 degree longitudes
|
||||
if ( degreesX == 180 && intMinutesX == 0 && std::round( secondsX * precisionMultiplier ) == 0 )
|
||||
{
|
||||
hemisphere.clear();
|
||||
}
|
||||
|
||||
QString minutesX;
|
||||
QString strSecondsX;
|
||||
|
||||
ss << std::fixed << std::setprecision( 0 );
|
||||
ss << intMinutesX;
|
||||
|
||||
minutesX = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
ss << std::fixed << std::setprecision( numberDecimalPlaces() );
|
||||
ss << secondsX;
|
||||
strSecondsX = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
trimTrailingZeros( strSecondsX, context );
|
||||
|
||||
//pad with leading digits if required
|
||||
if ( mShowLeadingZeros && intMinutesX < 10 )
|
||||
minutesX = '0' + minutesX;
|
||||
|
||||
if ( mShowLeadingZeros && secondsX < 10 )
|
||||
strSecondsX = '0' + strSecondsX;
|
||||
|
||||
ss << std::fixed << std::setprecision( 0 );
|
||||
ss << degreesX;
|
||||
QString degreesXStr = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
if ( mShowLeadingDegreeZeros )
|
||||
degreesXStr = QString( QStringLiteral( "000" ) + degreesXStr ).right( 3 );
|
||||
|
||||
return sign + degreesXStr + QChar( 176 ) +
|
||||
minutesX + QChar( 0x2032 ) +
|
||||
strSecondsX + QChar( 0x2033 ) +
|
||||
hemisphere;
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::formatLatitudeAsDegreesMinutes( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const
|
||||
{
|
||||
//first, limit latitude to -180 to 180 degree range
|
||||
double wrappedY = std::fmod( val, 180.0 );
|
||||
//next, wrap around latitudes > 90 or < -90 degrees, so that eg "110S" -> "70N"
|
||||
if ( wrappedY > 90.0 )
|
||||
{
|
||||
wrappedY = wrappedY - 180.0;
|
||||
}
|
||||
else if ( wrappedY < -90.0 )
|
||||
{
|
||||
wrappedY = wrappedY + 180.0;
|
||||
}
|
||||
|
||||
int degreesY = int( std::fabs( wrappedY ) );
|
||||
double floatMinutesY = ( std::fabs( wrappedY ) - degreesY ) * 60.0;
|
||||
|
||||
const int precisionMultiplier = std::pow( 10.0, numberDecimalPlaces() );
|
||||
|
||||
//make sure rounding to specified precision doesn't create minutes >= 60
|
||||
if ( std::round( floatMinutesY * precisionMultiplier ) >= 60 * precisionMultiplier )
|
||||
{
|
||||
floatMinutesY = std::max( floatMinutesY - 60, 0.0 );
|
||||
degreesY++;
|
||||
}
|
||||
|
||||
QString hemisphere;
|
||||
QString sign;
|
||||
if ( mUseSuffix )
|
||||
{
|
||||
hemisphere = wrappedY < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( wrappedY < 0 )
|
||||
{
|
||||
sign = context.negativeSign();
|
||||
}
|
||||
}
|
||||
//check if coordinate is all zeros for the specified precision, and if so,
|
||||
//remove the sign and hemisphere strings
|
||||
if ( degreesY == 0 && std::round( floatMinutesY * precisionMultiplier ) == 0 )
|
||||
{
|
||||
sign.clear();
|
||||
hemisphere.clear();
|
||||
}
|
||||
|
||||
ss << std::fixed << std::setprecision( numberDecimalPlaces() );
|
||||
ss << floatMinutesY;
|
||||
QString strMinutesY = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
trimTrailingZeros( strMinutesY, context );
|
||||
|
||||
//pad with leading digits if required
|
||||
if ( mShowLeadingZeros && floatMinutesY < 10 )
|
||||
strMinutesY = '0' + strMinutesY;
|
||||
|
||||
ss << std::fixed << std::setprecision( 0 );
|
||||
ss << degreesY;
|
||||
QString degreesYStr = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
if ( mShowLeadingDegreeZeros )
|
||||
degreesYStr = QString( QStringLiteral( "00" ) + degreesYStr ).right( 2 );
|
||||
|
||||
return sign + degreesYStr + QChar( 176 ) +
|
||||
strMinutesY + QChar( 0x2032 ) +
|
||||
hemisphere;
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::formatLongitudeAsDegreesMinutes( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const
|
||||
{
|
||||
//first, limit longitude to -360 to 360 degree range
|
||||
double wrappedX = std::fmod( val, 360.0 );
|
||||
//next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W"
|
||||
if ( wrappedX > 180.0 )
|
||||
{
|
||||
wrappedX = wrappedX - 360.0;
|
||||
}
|
||||
else if ( wrappedX < -180.0 )
|
||||
{
|
||||
wrappedX = wrappedX + 360.0;
|
||||
}
|
||||
|
||||
int degreesX = int( std::fabs( wrappedX ) );
|
||||
double floatMinutesX = ( std::fabs( wrappedX ) - degreesX ) * 60.0;
|
||||
|
||||
const int precisionMultiplier = std::pow( 10.0, numberDecimalPlaces() );
|
||||
|
||||
//make sure rounding to specified precision doesn't create minutes >= 60
|
||||
if ( std::round( floatMinutesX * precisionMultiplier ) >= 60 * precisionMultiplier )
|
||||
{
|
||||
floatMinutesX = std::max( floatMinutesX - 60, 0.0 );
|
||||
degreesX++;
|
||||
}
|
||||
|
||||
QString hemisphere;
|
||||
QString sign;
|
||||
if ( mUseSuffix )
|
||||
{
|
||||
hemisphere = wrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( wrappedX < 0 )
|
||||
{
|
||||
sign = context.negativeSign();
|
||||
}
|
||||
}
|
||||
//check if coordinate is all zeros for the specified precision, and if so,
|
||||
//remove the sign and hemisphere strings
|
||||
if ( degreesX == 0 && std::round( floatMinutesX * precisionMultiplier ) == 0 )
|
||||
{
|
||||
sign.clear();
|
||||
hemisphere.clear();
|
||||
}
|
||||
|
||||
//also remove directional prefix from 180 degree longitudes
|
||||
if ( degreesX == 180 && std::round( floatMinutesX * precisionMultiplier ) == 0 )
|
||||
{
|
||||
hemisphere.clear();
|
||||
}
|
||||
|
||||
ss << std::fixed << std::setprecision( numberDecimalPlaces() );
|
||||
ss << floatMinutesX;
|
||||
QString strMinutesX = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
trimTrailingZeros( strMinutesX, context );
|
||||
|
||||
//pad with leading digits if required
|
||||
if ( mShowLeadingZeros && floatMinutesX < 10 )
|
||||
strMinutesX = '0' + strMinutesX;
|
||||
|
||||
ss << std::fixed << std::setprecision( 0 );
|
||||
ss << degreesX;
|
||||
QString degreesXStr = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
if ( mShowLeadingDegreeZeros )
|
||||
degreesXStr = QString( QStringLiteral( "000" ) + degreesXStr ).right( 3 );
|
||||
|
||||
return sign + degreesXStr + QChar( 176 ) +
|
||||
strMinutesX + QChar( 0x2032 ) +
|
||||
hemisphere;
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::formatLatitudeAsDegrees( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const
|
||||
{
|
||||
//first, limit latitude to -180 to 180 degree range
|
||||
double wrappedY = std::fmod( val, 180.0 );
|
||||
//next, wrap around latitudes > 90 or < -90 degrees, so that eg "110S" -> "70N"
|
||||
if ( wrappedY > 90.0 )
|
||||
{
|
||||
wrappedY = wrappedY - 180.0;
|
||||
}
|
||||
else if ( wrappedY < -90.0 )
|
||||
{
|
||||
wrappedY = wrappedY + 180.0;
|
||||
}
|
||||
|
||||
const double absY = std::fabs( wrappedY );
|
||||
|
||||
const int precisionMultiplier = std::pow( 10.0, numberDecimalPlaces() );
|
||||
|
||||
QString hemisphere;
|
||||
QString sign;
|
||||
if ( mUseSuffix )
|
||||
{
|
||||
hemisphere = wrappedY < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( wrappedY < 0 )
|
||||
{
|
||||
sign = context.negativeSign();
|
||||
}
|
||||
}
|
||||
//check if coordinate is all zeros for the specified precision, and if so,
|
||||
//remove the sign and hemisphere strings
|
||||
if ( std::round( absY * precisionMultiplier ) == 0 )
|
||||
{
|
||||
sign.clear();
|
||||
hemisphere.clear();
|
||||
}
|
||||
|
||||
ss << std::fixed << std::setprecision( numberDecimalPlaces() );
|
||||
ss << absY;
|
||||
QString strDegreesY = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
trimTrailingZeros( strDegreesY, context );
|
||||
|
||||
if ( mShowLeadingDegreeZeros && absY < 10 )
|
||||
strDegreesY = '0' + strDegreesY;
|
||||
|
||||
return sign + strDegreesY + QChar( 176 ) + hemisphere;
|
||||
}
|
||||
|
||||
QString QgsGeographicCoordinateNumericFormat::formatLongitudeAsDegrees( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const
|
||||
{
|
||||
//first, limit longitude to -360 to 360 degree range
|
||||
double wrappedX = std::fmod( val, 360.0 );
|
||||
//next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W"
|
||||
if ( wrappedX > 180.0 )
|
||||
{
|
||||
wrappedX = wrappedX - 360.0;
|
||||
}
|
||||
else if ( wrappedX < -180.0 )
|
||||
{
|
||||
wrappedX = wrappedX + 360.0;
|
||||
}
|
||||
|
||||
const double absX = std::fabs( wrappedX );
|
||||
|
||||
const int precisionMultiplier = std::pow( 10.0, numberDecimalPlaces() );
|
||||
|
||||
QString hemisphere;
|
||||
QString sign;
|
||||
if ( mUseSuffix )
|
||||
{
|
||||
hemisphere = wrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( wrappedX < 0 )
|
||||
{
|
||||
sign = context.negativeSign();
|
||||
}
|
||||
}
|
||||
//check if coordinate is all zeros for the specified precision, and if so,
|
||||
//remove the sign and hemisphere strings
|
||||
if ( std::round( absX * precisionMultiplier ) == 0 )
|
||||
{
|
||||
sign.clear();
|
||||
hemisphere.clear();
|
||||
}
|
||||
|
||||
//also remove directional prefix from 180 degree longitudes
|
||||
if ( std::round( absX * precisionMultiplier ) == 180 * precisionMultiplier )
|
||||
{
|
||||
sign.clear();
|
||||
hemisphere.clear();
|
||||
}
|
||||
|
||||
ss << std::fixed << std::setprecision( numberDecimalPlaces() );
|
||||
ss << absX;
|
||||
QString strDegreesX = QString::fromStdWString( ss.str() );
|
||||
ss.str( std::wstring() );
|
||||
|
||||
trimTrailingZeros( strDegreesX, context );
|
||||
|
||||
if ( mShowLeadingDegreeZeros && absX < 100 )
|
||||
strDegreesX = '0' + strDegreesX;
|
||||
if ( mShowLeadingDegreeZeros && absX < 10 )
|
||||
strDegreesX = '0' + strDegreesX;
|
||||
|
||||
return sign + strDegreesX + QChar( 176 ) + hemisphere;
|
||||
}
|
||||
|
||||
void QgsGeographicCoordinateNumericFormat::trimTrailingZeros( QString &input, const QgsNumericFormatContext &context ) const
|
||||
{
|
||||
const QChar decimal = decimalSeparator().isNull() ? context.decimalSeparator() : decimalSeparator();
|
||||
if ( !showTrailingZeros() && input.contains( decimal ) )
|
||||
{
|
||||
int trimPoint = input.length() - 1;
|
||||
|
||||
while ( input.at( trimPoint ) == context.zeroDigit() )
|
||||
trimPoint--;
|
||||
|
||||
if ( input.at( trimPoint ) == decimal )
|
||||
trimPoint--;
|
||||
|
||||
input.truncate( trimPoint + 1 );
|
||||
}
|
||||
}
|
||||
142
src/core/numericformats/qgscoordinatenumericformat.h
Normal file
142
src/core/numericformats/qgscoordinatenumericformat.h
Normal file
@ -0,0 +1,142 @@
|
||||
/***************************************************************************
|
||||
qgscoordinatenumericformat.h
|
||||
--------------------------
|
||||
begin : April 2022
|
||||
copyright : (C) 2022 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSCOORDINATENUMERICFORMAT_H
|
||||
#define QGSCOORDINATENUMERICFORMAT_H
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgis_sip.h"
|
||||
#include "qgsbasicnumericformat.h"
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \brief A numeric formatter which returns a text representation of a geographic coordinate (latitude or longitude).
|
||||
*
|
||||
* \since QGIS 3.26
|
||||
*/
|
||||
class CORE_EXPORT QgsGeographicCoordinateNumericFormat : public QgsBasicNumericFormat
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Angle format options.
|
||||
*/
|
||||
enum class AngleFormat
|
||||
{
|
||||
DegreesMinutesSeconds, //!< Degrees, minutes and seconds, eg 30 degrees 45'30
|
||||
DegreesMinutes, //!< Degrees and decimal minutes, eg 30 degrees 45.55'
|
||||
DecimalDegrees, //!< Decimal degrees, eg 30.7555 degrees
|
||||
};
|
||||
Q_ENUM( AngleFormat )
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
QgsGeographicCoordinateNumericFormat();
|
||||
|
||||
QString id() const override;
|
||||
QString visibleName() const override;
|
||||
int sortKey() override;
|
||||
double suggestSampleValue() const override;
|
||||
QString formatDouble( double value, const QgsNumericFormatContext &context ) const override;
|
||||
QgsGeographicCoordinateNumericFormat *clone() const override SIP_FACTORY;
|
||||
QgsNumericFormat *create( const QVariantMap &configuration, const QgsReadWriteContext &context ) const override SIP_FACTORY;
|
||||
QVariantMap configuration( const QgsReadWriteContext &context ) const override;
|
||||
|
||||
/**
|
||||
* Returns the angle format, which controls how bearing the angles are formatted
|
||||
* described in the returned strings.
|
||||
*
|
||||
* \see setAngleFormat()
|
||||
*/
|
||||
AngleFormat angleFormat() const;
|
||||
|
||||
/**
|
||||
* Sets the directional formatting option, which controls how bearing the angles are formatted
|
||||
* described in the returned strings.
|
||||
*
|
||||
* \see angleFormat()
|
||||
*/
|
||||
void setAngleFormat( AngleFormat format );
|
||||
|
||||
/**
|
||||
* Returns TRUE if leading zeros in the minutes or seconds values should be shown.
|
||||
*
|
||||
* \see setShowLeadingZeros()
|
||||
*/
|
||||
bool showLeadingZeros() const;
|
||||
|
||||
/**
|
||||
* Sets whether leading zeros in the minutes or seconds values should be shown.
|
||||
*
|
||||
* \see showLeadingZeros()
|
||||
*/
|
||||
void setShowLeadingZeros( bool show );
|
||||
|
||||
/**
|
||||
* Returns TRUE if leading zeros for the degree values should be shown.
|
||||
*
|
||||
* \see setShowDegreeLeadingZeros()
|
||||
*/
|
||||
bool showDegreeLeadingZeros() const;
|
||||
|
||||
/**
|
||||
* Sets whether leading zeros for the degree values should be shown.
|
||||
*
|
||||
* \see showDegreeLeadingZeros()
|
||||
*/
|
||||
void setShowDegreeLeadingZeros( bool show );
|
||||
|
||||
/**
|
||||
* Returns TRUE if directional suffixes (e.g. "N") should be included.
|
||||
*
|
||||
* \see setShowDirectionalSuffix()
|
||||
*/
|
||||
bool showDirectionalSuffix() const;
|
||||
|
||||
/**
|
||||
* Sets whether directional suffixes (e.g. "N") should be included.
|
||||
*
|
||||
* \see showDirectionalSuffix()
|
||||
*/
|
||||
void setShowDirectionalSuffix( bool show );
|
||||
|
||||
void setConfiguration( const QVariantMap &configuration, const QgsReadWriteContext &context ) override;
|
||||
|
||||
private:
|
||||
|
||||
AngleFormat mAngleFormat = AngleFormat::DecimalDegrees;
|
||||
bool mShowLeadingZeros = false;
|
||||
bool mShowLeadingDegreeZeros = false;
|
||||
bool mUseSuffix = true;
|
||||
|
||||
QString formatLongitude( double value, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const;
|
||||
QString formatLatitude( double value, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const;
|
||||
|
||||
QString formatLatitudeAsDegreesMinutesSeconds( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const;
|
||||
QString formatLongitudeAsDegreesMinutesSeconds( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const;
|
||||
|
||||
QString formatLatitudeAsDegreesMinutes( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const;
|
||||
QString formatLongitudeAsDegreesMinutes( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const;
|
||||
|
||||
QString formatLatitudeAsDegrees( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const;
|
||||
QString formatLongitudeAsDegrees( double val, std::basic_stringstream<wchar_t> &ss, const QgsNumericFormatContext &context ) const;
|
||||
|
||||
void trimTrailingZeros( QString &input, const QgsNumericFormatContext &context ) const;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSCOORDINATENUMERICFORMAT_H
|
||||
@ -33,6 +33,8 @@ class QgsReadWriteContext;
|
||||
*/
|
||||
class CORE_EXPORT QgsNumericFormatContext
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
@ -183,6 +185,44 @@ class CORE_EXPORT QgsNumericFormatContext
|
||||
mExponential = character;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpretation of numeric values.
|
||||
*
|
||||
* \since QGIS 3.26
|
||||
*/
|
||||
enum class Interpretation
|
||||
{
|
||||
Generic, //!< Generic
|
||||
Latitude, //!< Latitude values
|
||||
Longitude, //!< Longitude values
|
||||
};
|
||||
Q_ENUM( Interpretation )
|
||||
|
||||
/**
|
||||
* Returns the interpretation of the numbers being converted.
|
||||
*
|
||||
* \see setInterpretation()
|
||||
*
|
||||
* \since QGIS 3.26
|
||||
*/
|
||||
Interpretation interpretation() const
|
||||
{
|
||||
return mInterpretation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the \a interpretation of the numbers being converted.
|
||||
*
|
||||
* \see interpretation()
|
||||
*
|
||||
* \since QGIS 3.26
|
||||
*/
|
||||
void setInterpretation( Interpretation interpretation )
|
||||
{
|
||||
mInterpretation = interpretation;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
QChar mThousandsSep;
|
||||
QChar mDecimalSep;
|
||||
@ -191,6 +231,8 @@ class CORE_EXPORT QgsNumericFormatContext
|
||||
QChar mNegativeSign;
|
||||
QChar mPositiveSign;
|
||||
QChar mExponential;
|
||||
|
||||
Interpretation mInterpretation = Interpretation::Generic;
|
||||
};
|
||||
|
||||
#ifdef SIP_RUN
|
||||
@ -201,6 +243,7 @@ class CORE_EXPORT QgsNumericFormatContext
|
||||
#include <qgsfallbacknumericformat.h>
|
||||
#include <qgspercentagenumericformat.h>
|
||||
#include <qgsscientificnumericformat.h>
|
||||
#include <qgscoordinatenumericformat.h>
|
||||
% End
|
||||
#endif
|
||||
|
||||
@ -221,6 +264,8 @@ class CORE_EXPORT QgsNumericFormat
|
||||
SIP_CONVERT_TO_SUBCLASS_CODE
|
||||
if ( dynamic_cast< QgsBearingNumericFormat * >( sipCpp ) )
|
||||
sipType = sipType_QgsBearingNumericFormat;
|
||||
else if ( dynamic_cast< QgsGeographicCoordinateNumericFormat * >( sipCpp ) )
|
||||
sipType = sipType_QgsGeographicCoordinateNumericFormat;
|
||||
else if ( dynamic_cast< QgsFallbackNumericFormat * >( sipCpp ) )
|
||||
sipType = sipType_QgsFallbackNumericFormat;
|
||||
else if ( dynamic_cast< QgsPercentageNumericFormat * >( sipCpp ) )
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include "qgspercentagenumericformat.h"
|
||||
#include "qgsscientificnumericformat.h"
|
||||
#include "qgsfractionnumericformat.h"
|
||||
#include "qgscoordinatenumericformat.h"
|
||||
#include "qgsxmlutils.h"
|
||||
|
||||
QgsNumericFormatRegistry::QgsNumericFormatRegistry()
|
||||
@ -29,6 +30,7 @@ QgsNumericFormatRegistry::QgsNumericFormatRegistry()
|
||||
addFormat( new QgsFallbackNumericFormat() );
|
||||
addFormat( new QgsBasicNumericFormat() );
|
||||
addFormat( new QgsBearingNumericFormat() );
|
||||
addFormat( new QgsGeographicCoordinateNumericFormat() );
|
||||
addFormat( new QgsCurrencyNumericFormat() );
|
||||
addFormat( new QgsPercentageNumericFormat() );
|
||||
addFormat( new QgsScientificNumericFormat() );
|
||||
|
||||
@ -40,6 +40,16 @@ class QgsBearingNumericFormatConfigurationWidgetFactory : public QgsNumericForma
|
||||
}
|
||||
};
|
||||
|
||||
class QgsGeographicCoordinateNumericFormatConfigurationWidgetFactory : public QgsNumericFormatConfigurationWidgetFactory
|
||||
{
|
||||
public:
|
||||
|
||||
QgsNumericFormatWidget *create( const QgsNumericFormat *format ) const
|
||||
{
|
||||
return new QgsGeographicCoordinateNumericFormatWidget( format );
|
||||
}
|
||||
};
|
||||
|
||||
class QgsCurrencyNumericFormatConfigurationWidgetFactory : public QgsNumericFormatConfigurationWidgetFactory
|
||||
{
|
||||
public:
|
||||
@ -89,6 +99,7 @@ QgsNumericFormatGuiRegistry::QgsNumericFormatGuiRegistry()
|
||||
addFormatConfigurationWidgetFactory( QStringLiteral( "percentage" ), new QgsPercentageNumericFormatConfigurationWidgetFactory() );
|
||||
addFormatConfigurationWidgetFactory( QStringLiteral( "scientific" ), new QgsScientificNumericFormatConfigurationWidgetFactory() );
|
||||
addFormatConfigurationWidgetFactory( QStringLiteral( "fraction" ), new QgsFractionNumericFormatConfigurationWidgetFactory() );
|
||||
addFormatConfigurationWidgetFactory( QStringLiteral( "geographiccoordinate" ), new QgsGeographicCoordinateNumericFormatConfigurationWidgetFactory() );
|
||||
}
|
||||
|
||||
QgsNumericFormatGuiRegistry::~QgsNumericFormatGuiRegistry()
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include "qgsbearingnumericformat.h"
|
||||
#include "qgsscientificnumericformat.h"
|
||||
#include "qgsfractionnumericformat.h"
|
||||
#include "qgscoordinatenumericformat.h"
|
||||
#include "qgsgui.h"
|
||||
#include "qgis.h"
|
||||
#include <QDialogButtonBox>
|
||||
@ -218,6 +219,119 @@ QgsBearingNumericFormat *QgsBearingNumericFormatDialog::format()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// QgsGeographicCoordinateNumericFormatWidget
|
||||
//
|
||||
|
||||
QgsGeographicCoordinateNumericFormatWidget::QgsGeographicCoordinateNumericFormatWidget( const QgsNumericFormat *format, QWidget *parent )
|
||||
: QgsNumericFormatWidget( parent )
|
||||
{
|
||||
setupUi( this );
|
||||
|
||||
mDecimalsSpinBox->setClearValue( 6 );
|
||||
mFormatComboBox->addItem( QObject::tr( "Decimal Degrees" ), static_cast< int >( QgsGeographicCoordinateNumericFormat::AngleFormat::DecimalDegrees ) );
|
||||
mFormatComboBox->addItem( QObject::tr( "Degrees, Minutes" ), static_cast< int >( QgsGeographicCoordinateNumericFormat::AngleFormat::DegreesMinutes ) );
|
||||
mFormatComboBox->addItem( QObject::tr( "Degrees, Minutes, Seconds" ), static_cast< int >( QgsGeographicCoordinateNumericFormat::AngleFormat::DegreesMinutesSeconds ) );
|
||||
|
||||
setFormat( format->clone() );
|
||||
|
||||
connect( mShowTrailingZerosCheckBox, &QCheckBox::toggled, this, [ = ]( bool checked )
|
||||
{
|
||||
mFormat->setShowTrailingZeros( checked );
|
||||
if ( !mBlockSignals )
|
||||
emit changed();
|
||||
} );
|
||||
|
||||
connect( mShowDirectionalSuffixCheckBox, &QCheckBox::toggled, this, [ = ]( bool checked )
|
||||
{
|
||||
mFormat->setShowDirectionalSuffix( checked );
|
||||
if ( !mBlockSignals )
|
||||
emit changed();
|
||||
} );
|
||||
|
||||
connect( mShowLeadingZerosCheckBox, &QCheckBox::toggled, this, [ = ]( bool checked )
|
||||
{
|
||||
mFormat->setShowLeadingZeros( checked );
|
||||
if ( !mBlockSignals )
|
||||
emit changed();
|
||||
} );
|
||||
|
||||
connect( mShowLeadingZerosForDegreesCheckBox, &QCheckBox::toggled, this, [ = ]( bool checked )
|
||||
{
|
||||
mFormat->setShowDegreeLeadingZeros( checked );
|
||||
if ( !mBlockSignals )
|
||||
emit changed();
|
||||
} );
|
||||
|
||||
connect( mDecimalsSpinBox, qOverload<int>( &QSpinBox::valueChanged ), this, [ = ]( int value )
|
||||
{
|
||||
mFormat->setNumberDecimalPlaces( value );
|
||||
if ( !mBlockSignals )
|
||||
emit changed();
|
||||
} );
|
||||
|
||||
connect( mFormatComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [ = ]( int )
|
||||
{
|
||||
mFormat->setAngleFormat( static_cast < QgsGeographicCoordinateNumericFormat::AngleFormat >( mFormatComboBox->currentData().toInt() ) );
|
||||
if ( !mBlockSignals )
|
||||
emit changed();
|
||||
} );
|
||||
}
|
||||
|
||||
QgsGeographicCoordinateNumericFormatWidget::~QgsGeographicCoordinateNumericFormatWidget() = default;
|
||||
|
||||
void QgsGeographicCoordinateNumericFormatWidget::setFormat( QgsNumericFormat *format )
|
||||
{
|
||||
mFormat.reset( static_cast< QgsGeographicCoordinateNumericFormat * >( format ) );
|
||||
|
||||
mBlockSignals = true;
|
||||
mDecimalsSpinBox->setValue( mFormat->numberDecimalPlaces() );
|
||||
mShowTrailingZerosCheckBox->setChecked( mFormat->showTrailingZeros() );
|
||||
mShowDirectionalSuffixCheckBox->setChecked( mFormat->showDirectionalSuffix() );
|
||||
mShowLeadingZerosCheckBox->setChecked( mFormat->showLeadingZeros() );
|
||||
mShowLeadingZerosForDegreesCheckBox->setChecked( mFormat->showDegreeLeadingZeros() );
|
||||
mFormatComboBox->setCurrentIndex( mFormatComboBox->findData( static_cast< int >( mFormat->angleFormat() ) ) );
|
||||
mBlockSignals = false;
|
||||
}
|
||||
|
||||
QgsNumericFormat *QgsGeographicCoordinateNumericFormatWidget::format()
|
||||
{
|
||||
return mFormat->clone();
|
||||
}
|
||||
|
||||
//
|
||||
// QgsGeographicCoordinateNumericFormatDialog
|
||||
//
|
||||
|
||||
QgsGeographicCoordinateNumericFormatDialog::QgsGeographicCoordinateNumericFormatDialog( const QgsNumericFormat *format, QWidget *parent )
|
||||
: QDialog( parent )
|
||||
{
|
||||
setLayout( new QVBoxLayout() );
|
||||
mWidget = new QgsGeographicCoordinateNumericFormatWidget( format );
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Ok );
|
||||
|
||||
connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
|
||||
connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
|
||||
|
||||
layout()->addWidget( mWidget );
|
||||
layout()->addWidget( buttonBox );
|
||||
|
||||
connect( mWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
|
||||
|
||||
setObjectName( QStringLiteral( "QgsGeographicCoordinateNumericFormatDialog" ) );
|
||||
QgsGui::enableAutoGeometryRestore( this );
|
||||
}
|
||||
|
||||
QgsGeographicCoordinateNumericFormat *QgsGeographicCoordinateNumericFormatDialog::format()
|
||||
{
|
||||
return static_cast< QgsGeographicCoordinateNumericFormat * >( mWidget->format() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// QgsCurrencyNumericFormatWidget
|
||||
//
|
||||
|
||||
@ -162,6 +162,68 @@ class GUI_EXPORT QgsBearingNumericFormatDialog : public QDialog
|
||||
};
|
||||
|
||||
|
||||
#include "ui_qgsgeographiccoordinatenumericformatwidgetbase.h"
|
||||
|
||||
class QgsGeographicCoordinateNumericFormat;
|
||||
|
||||
/**
|
||||
* \ingroup gui
|
||||
* \class QgsGeographicCoordinateNumericFormatWidget
|
||||
* \brief A widget which allow control over the properties of a QgsGeographicCoordinateNumericFormat.
|
||||
* \since QGIS 3.26
|
||||
*/
|
||||
class GUI_EXPORT QgsGeographicCoordinateNumericFormatWidget : public QgsNumericFormatWidget, private Ui::QgsGeographicCoordinateNumericFormatWidgetBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsGeographicCoordinateNumericFormatWidget, initially showing the specified \a format.
|
||||
*/
|
||||
QgsGeographicCoordinateNumericFormatWidget( const QgsNumericFormat *format, QWidget *parent SIP_TRANSFERTHIS = nullptr );
|
||||
~QgsGeographicCoordinateNumericFormatWidget() override;
|
||||
|
||||
void setFormat( QgsNumericFormat *format ) override;
|
||||
|
||||
QgsNumericFormat *format() override SIP_FACTORY;
|
||||
|
||||
private:
|
||||
std::unique_ptr< QgsGeographicCoordinateNumericFormat > mFormat;
|
||||
bool mBlockSignals = false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup gui
|
||||
* \class QgsGeographicCoordinateNumericFormatDialog
|
||||
* \brief A dialog which allow control over the properties of a QgsGeographicCoordinateNumericFormat.
|
||||
* \since QGIS 3.26
|
||||
*/
|
||||
class GUI_EXPORT QgsGeographicCoordinateNumericFormatDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsGeographicCoordinateNumericFormatDialog, initially showing the specified \a format.
|
||||
*/
|
||||
QgsGeographicCoordinateNumericFormatDialog( const QgsNumericFormat *format, QWidget *parent SIP_TRANSFERTHIS = nullptr );
|
||||
|
||||
/**
|
||||
* Returns the format defined by the current settings in the dialog.
|
||||
*
|
||||
* Ownership of the returned object is transferred to the caller
|
||||
*/
|
||||
QgsGeographicCoordinateNumericFormat *format() SIP_FACTORY;
|
||||
|
||||
private:
|
||||
|
||||
QgsGeographicCoordinateNumericFormatWidget *mWidget = nullptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#include "ui_qgscurrencynumericformatwidgetbase.h"
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QgsGeographicCoordinateNumericFormatWidgetBase</class>
|
||||
<widget class="QgsPanelWidget" name="QgsGeographicCoordinateNumericFormatWidgetBase">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>420</width>
|
||||
<height>370</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="10" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="mShowDirectionalSuffixCheckBox">
|
||||
<property name="text">
|
||||
<string>Show directional suffix</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="mShowLeadingZerosForDegreesCheckBox">
|
||||
<property name="text">
|
||||
<string>Show leading zeros for degrees</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="mFormatComboBox"/>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="mShowLeadingZerosCheckBox">
|
||||
<property name="text">
|
||||
<string>Show leading zeros for minutes and seconds</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Decimal places</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QgsSpinBox" name="mDecimalsSpinBox">
|
||||
<property name="value">
|
||||
<number>6</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="mShowTrailingZerosCheckBox">
|
||||
<property name="text">
|
||||
<string>Show trailing zeros</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QgsSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>qgsspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsPanelWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qgspanelwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>mFormatComboBox</tabstop>
|
||||
<tabstop>mShowDirectionalSuffixCheckBox</tabstop>
|
||||
<tabstop>mShowLeadingZerosCheckBox</tabstop>
|
||||
<tabstop>mShowLeadingZerosForDegreesCheckBox</tabstop>
|
||||
<tabstop>mDecimalsSpinBox</tabstop>
|
||||
<tabstop>mShowTrailingZerosCheckBox</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@ -22,6 +22,7 @@ from qgis.core import (QgsFallbackNumericFormat,
|
||||
QgsNumericFormatRegistry,
|
||||
QgsNumericFormat,
|
||||
QgsFractionNumericFormat,
|
||||
QgsGeographicCoordinateNumericFormat,
|
||||
QgsReadWriteContext)
|
||||
from qgis.testing import start_app, unittest
|
||||
from qgis.PyQt.QtXml import QDomDocument
|
||||
@ -769,6 +770,653 @@ class TestQgsNumericFormat(unittest.TestCase):
|
||||
self.assertEqual(f3.useDedicatedUnicodeCharacters(), f.useDedicatedUnicodeCharacters())
|
||||
self.assertEqual(f3.useUnicodeSuperSubscript(), f.useUnicodeSuperSubscript())
|
||||
|
||||
def testGeographicFormat(self):
|
||||
""" test geographic formatter """
|
||||
f = QgsGeographicCoordinateNumericFormat()
|
||||
f.setNumberDecimalPlaces(3)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setAngleFormat(QgsGeographicCoordinateNumericFormat.AngleFormat.DegreesMinutes)
|
||||
f.setShowLeadingZeros(True)
|
||||
f.setShowDegreeLeadingZeros(True)
|
||||
|
||||
f2 = f.clone()
|
||||
self.assertIsInstance(f2, QgsGeographicCoordinateNumericFormat)
|
||||
|
||||
self.assertEqual(f2.numberDecimalPlaces(), f.numberDecimalPlaces())
|
||||
self.assertEqual(f2.showDirectionalSuffix(), f.showDirectionalSuffix())
|
||||
self.assertEqual(f2.angleFormat(), f.angleFormat())
|
||||
self.assertEqual(f2.showLeadingZeros(), f.showLeadingZeros())
|
||||
self.assertEqual(f2.showDegreeLeadingZeros(), f.showDegreeLeadingZeros())
|
||||
|
||||
doc = QDomDocument("testdoc")
|
||||
elem = doc.createElement("test")
|
||||
f2.writeXml(elem, doc, QgsReadWriteContext())
|
||||
|
||||
f3 = QgsNumericFormatRegistry().createFromXml(elem, QgsReadWriteContext())
|
||||
self.assertIsInstance(f3, QgsGeographicCoordinateNumericFormat)
|
||||
|
||||
self.assertEqual(f3.numberDecimalPlaces(), f.numberDecimalPlaces())
|
||||
self.assertEqual(f3.showDirectionalSuffix(), f.showDirectionalSuffix())
|
||||
self.assertEqual(f3.angleFormat(), f.angleFormat())
|
||||
self.assertEqual(f3.showLeadingZeros(), f.showLeadingZeros())
|
||||
self.assertEqual(f3.showDegreeLeadingZeros(), f.showDegreeLeadingZeros())
|
||||
|
||||
def testGeographicCoordinateFormatLongitudeDms(self):
|
||||
"""Test formatting longitude as DMS"""
|
||||
|
||||
f = QgsGeographicCoordinateNumericFormat()
|
||||
context = QgsNumericFormatContext()
|
||||
context.setInterpretation(QgsNumericFormatContext.Interpretation.Longitude)
|
||||
|
||||
f.setAngleFormat(QgsGeographicCoordinateNumericFormat.AngleFormat.DegreesMinutesSeconds)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowTrailingZeros(True)
|
||||
|
||||
self.assertEqual(f.formatDouble(80, context), "80°0′0.00″E")
|
||||
|
||||
# check precision
|
||||
f.setNumberDecimalPlaces(4)
|
||||
self.assertEqual(f.formatDouble(80, context), "80°0′0.0000″E")
|
||||
self.assertEqual(f.formatDouble(80.12345678, context), "80°7′24.4444″E")
|
||||
f.setNumberDecimalPlaces(0)
|
||||
self.assertEqual(f.formatDouble(80.12345678, context), "80°7′24″E")
|
||||
|
||||
# check if longitudes > 180 or <-180 wrap around
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(370, context),
|
||||
"10°0′0.00″E")
|
||||
self.assertEqual(f.formatDouble(-370, context),
|
||||
"10°0′0.00″W")
|
||||
self.assertEqual(f.formatDouble(181, context),
|
||||
"179°0′0.00″W")
|
||||
self.assertEqual(f.formatDouble(-181, context),
|
||||
"179°0′0.00″E")
|
||||
self.assertEqual(f.formatDouble(359, context),
|
||||
"1°0′0.00″W")
|
||||
self.assertEqual(f.formatDouble(-359, context),
|
||||
"1°0′0.00″E")
|
||||
|
||||
# should be no directional suffixes for 0 degree coordinates
|
||||
self.assertEqual(f.formatDouble(0, context),
|
||||
"0°0′0.00″")
|
||||
# should also be no directional suffix for 0 degree coordinates within specified precision
|
||||
self.assertEqual(f.formatDouble(-0.000001, context),
|
||||
"0°0′0.00″")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(
|
||||
f.formatDouble(-0.000001, context),
|
||||
"0°0′0.00360″W")
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(
|
||||
f.formatDouble(0.000001, context),
|
||||
"0°0′0.00″")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(
|
||||
f.formatDouble(0.000001, context),
|
||||
"0°0′0.00360″E")
|
||||
|
||||
# should be no directional suffixes for 180 degree longitudes
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(180, context),
|
||||
"180°0′0.00″")
|
||||
self.assertEqual(
|
||||
f.formatDouble(179.999999, context),
|
||||
"180°0′0.00″")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(
|
||||
f.formatDouble(179.999999, context),
|
||||
"179°59′59.99640″E")
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(
|
||||
f.formatDouble(180.000001, context),
|
||||
"180°0′0.00″")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(
|
||||
f.formatDouble(180.000001, context),
|
||||
"179°59′59.99640″W")
|
||||
|
||||
# test rounding does not create seconds >= 60
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(
|
||||
f.formatDouble(99.999999, context),
|
||||
"100°0′0.00″E")
|
||||
self.assertEqual(
|
||||
f.formatDouble(89.999999, context),
|
||||
"90°0′0.00″E")
|
||||
|
||||
# test without direction suffix
|
||||
f.setShowDirectionalSuffix(False)
|
||||
self.assertEqual(f.formatDouble(80, context), "80°0′0.00″")
|
||||
|
||||
# test 0 longitude
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(0, context), "0°0′0.00″")
|
||||
# test near zero longitude
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0′0.00″")
|
||||
# should be no "-" prefix for near-zero longitude when rounding to 2 decimal places
|
||||
self.assertEqual(
|
||||
f.formatDouble(-0.000001, context), "0°0′0.00″")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0′0.00360″")
|
||||
self.assertEqual(
|
||||
f.formatDouble(-0.000001, context), "-0°0′0.00360″")
|
||||
|
||||
# test with padding
|
||||
f.setShowLeadingZeros(True)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(80, context), "80°00′00.00″E")
|
||||
self.assertEqual(f.formatDouble(85.44, context), "85°26′24.00″E")
|
||||
self.assertEqual(f.formatDouble(0, context), "0°00′00.00″")
|
||||
self.assertEqual(
|
||||
f.formatDouble(-0.000001, context), "0°00′00.00″")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°00′00.00″")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(
|
||||
f.formatDouble(-0.000001, context), "0°00′00.00360″W")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°00′00.00360″E")
|
||||
|
||||
# with degree padding
|
||||
f.setShowDegreeLeadingZeros(True)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(100, context), "100°00′00.00″E")
|
||||
self.assertEqual(f.formatDouble(-100, context), "100°00′00.00″W")
|
||||
self.assertEqual(f.formatDouble(80, context), "080°00′00.00″E")
|
||||
self.assertEqual(f.formatDouble(-80, context), "080°00′00.00″W")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "005°26′24.00″E")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "005°26′24.00″W")
|
||||
f.setShowDirectionalSuffix(False)
|
||||
self.assertEqual(f.formatDouble(100, context), "100°00′00.00″")
|
||||
self.assertEqual(f.formatDouble(-100, context), "-100°00′00.00″")
|
||||
self.assertEqual(f.formatDouble(80, context), "080°00′00.00″")
|
||||
self.assertEqual(f.formatDouble(-80, context), "-080°00′00.00″")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "005°26′24.00″")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "-005°26′24.00″")
|
||||
|
||||
f.setShowTrailingZeros(False)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowDegreeLeadingZeros(False)
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5°26′24″E")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5°26′24″W")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5°26′27.64″W")
|
||||
|
||||
context.setDecimalSeparator('☕')
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5°26′24″E")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5°26′24″W")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5°26′27☕64″W")
|
||||
|
||||
def testGeographicCoordinateFormatLatitudeDms(self):
|
||||
"""Test formatting latitude as DMS"""
|
||||
|
||||
f = QgsGeographicCoordinateNumericFormat()
|
||||
context = QgsNumericFormatContext()
|
||||
context.setInterpretation(QgsNumericFormatContext.Interpretation.Latitude)
|
||||
|
||||
f.setAngleFormat(QgsGeographicCoordinateNumericFormat.AngleFormat.DegreesMinutesSeconds)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowTrailingZeros(True)
|
||||
|
||||
self.assertEqual(f.formatDouble(20, context), "20°0′0.00″N")
|
||||
|
||||
# check precision
|
||||
f.setNumberDecimalPlaces(4)
|
||||
self.assertEqual(f.formatDouble(20, context), "20°0′0.0000″N")
|
||||
self.assertEqual(f.formatDouble(20.12345678, context), "20°7′24.4444″N")
|
||||
f.setNumberDecimalPlaces(0)
|
||||
self.assertEqual(f.formatDouble(20.12345678, context), "20°7′24″N")
|
||||
|
||||
# check if latitudes > 90 or <-90 wrap around
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(190, context), "10°0′0.00″N")
|
||||
self.assertEqual(f.formatDouble(-190, context), "10°0′0.00″S")
|
||||
self.assertEqual(f.formatDouble(91, context), "89°0′0.00″S")
|
||||
self.assertEqual(f.formatDouble(-91, context), "89°0′0.00″N")
|
||||
self.assertEqual(f.formatDouble(179, context), "1°0′0.00″S")
|
||||
self.assertEqual(f.formatDouble(-179, context), "1°0′0.00″N")
|
||||
|
||||
# should be no directional suffixes for 0 degree coordinates
|
||||
self.assertEqual(f.formatDouble(0, context), "0°0′0.00″")
|
||||
# should also be no directional suffix for 0 degree coordinates within specified precision
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0′0.00″")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0′0.00360″N")
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°0′0.00″")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°0′0.00360″S")
|
||||
|
||||
# test rounding does not create seconds >= 60
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(89.999999, context), "90°0′0.00″N")
|
||||
|
||||
# test without direction suffix
|
||||
f.setShowDirectionalSuffix(False)
|
||||
self.assertEqual(f.formatDouble(20, context), "20°0′0.00″")
|
||||
|
||||
# test 0 latitude
|
||||
self.assertEqual(f.formatDouble(0, context), "0°0′0.00″")
|
||||
# test near zero lat/long
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0′0.00″")
|
||||
# should be no "-" prefix for near-zero latitude when rounding to 2 decimal places
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°0′0.00″")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0′0.00360″")
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "-0°0′0.00360″")
|
||||
|
||||
# test with padding
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowLeadingZeros(True)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
self.assertEqual(f.formatDouble(20, context), "20°00′00.00″N")
|
||||
self.assertEqual(f.formatDouble(85.44, context), "85°26′24.00″N")
|
||||
self.assertEqual(f.formatDouble(0, context), "0°00′00.00″")
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°00′00.00″")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°00′00.00″")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°00′00.00360″S")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°00′00.00360″N")
|
||||
|
||||
# with degree padding
|
||||
f.setShowDegreeLeadingZeros(True)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(80, context), "80°00′00.00″N")
|
||||
self.assertEqual(f.formatDouble(-80, context), "80°00′00.00″S")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "05°26′24.00″N")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "05°26′24.00″S")
|
||||
f.setShowDirectionalSuffix(False)
|
||||
self.assertEqual(f.formatDouble(80, context), "80°00′00.00″")
|
||||
self.assertEqual(f.formatDouble(-80, context), "-80°00′00.00″")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "05°26′24.00″")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "-05°26′24.00″")
|
||||
|
||||
f.setShowTrailingZeros(False)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowDegreeLeadingZeros(False)
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5°26′24″N")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5°26′24″S")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5°26′27.64″S")
|
||||
|
||||
context.setDecimalSeparator('☕')
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5°26′24″N")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5°26′24″S")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5°26′27☕64″S")
|
||||
|
||||
def testGeographicCoordinateFormatLongitudeMinutes(self):
|
||||
"""Test formatting longitude as DM"""
|
||||
|
||||
f = QgsGeographicCoordinateNumericFormat()
|
||||
context = QgsNumericFormatContext()
|
||||
context.setInterpretation(QgsNumericFormatContext.Interpretation.Longitude)
|
||||
|
||||
f.setAngleFormat(QgsGeographicCoordinateNumericFormat.AngleFormat.DegreesMinutes)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowTrailingZeros(True)
|
||||
|
||||
self.assertEqual(f.formatDouble(80, context), "80°0.00′E")
|
||||
|
||||
# check precision
|
||||
f.setNumberDecimalPlaces(4)
|
||||
self.assertEqual(f.formatDouble(80, context), "80°0.0000′E")
|
||||
self.assertEqual(f.formatDouble(80.12345678, context), "80°7.4074′E")
|
||||
f.setNumberDecimalPlaces(0)
|
||||
self.assertEqual(f.formatDouble(80.12345678, context), "80°7′E")
|
||||
|
||||
# check if longitudes > 180 or <-180 wrap around
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(370, context), "10°0.00′E")
|
||||
self.assertEqual(f.formatDouble(-370, context), "10°0.00′W")
|
||||
self.assertEqual(f.formatDouble(181, context), "179°0.00′W")
|
||||
self.assertEqual(f.formatDouble(-181, context), "179°0.00′E")
|
||||
self.assertEqual(f.formatDouble(359, context), "1°0.00′W")
|
||||
self.assertEqual(f.formatDouble(-359, context), "1°0.00′E")
|
||||
|
||||
# should be no directional suffixes for 0 degree coordinates
|
||||
self.assertEqual(f.formatDouble(0, context), "0°0.00′")
|
||||
# should also be no directional suffix for 0 degree coordinates within specified precision
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°0.00′")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0.00′")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°0.00006′W")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0.00006′E")
|
||||
|
||||
# test rounding does not create minutes >= 60
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(99.999999, context), "100°0.00′E")
|
||||
|
||||
# should be no directional suffixes for 180 degree longitudes
|
||||
self.assertEqual(f.formatDouble(180, context), "180°0.00′")
|
||||
|
||||
# should also be no directional suffix for 180 degree longitudes within specified precision
|
||||
self.assertEqual(f.formatDouble(180.000001, context), "180°0.00′")
|
||||
self.assertEqual(f.formatDouble(179.999999, context), "180°0.00′")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(180.000001, context), "179°59.99994′W")
|
||||
self.assertEqual(f.formatDouble(179.999999, context), "179°59.99994′E")
|
||||
|
||||
# test without direction suffix
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowDirectionalSuffix(False)
|
||||
self.assertEqual(f.formatDouble(80, context), "80°0.00′")
|
||||
# test 0 longitude
|
||||
self.assertEqual(f.formatDouble(0, context), "0°0.00′")
|
||||
# test near zero longitude
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0.00′")
|
||||
# should be no "-" prefix for near-zero longitude when rounding to 2 decimal places
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°0.00′")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0.00006′")
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "-0°0.00006′")
|
||||
|
||||
# test with padding
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowLeadingZeros(True)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
self.assertEqual(f.formatDouble(80, context), "80°00.00′E")
|
||||
self.assertEqual(f.formatDouble(0, context), "0°00.00′")
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°00.00′")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°00.00′")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°00.00006′W")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°00.00006′E")
|
||||
|
||||
# with degree padding
|
||||
f.setShowDegreeLeadingZeros(True)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(100, context), "100°00.00′E")
|
||||
self.assertEqual(f.formatDouble(-100, context), "100°00.00′W")
|
||||
self.assertEqual(f.formatDouble(80, context), "080°00.00′E")
|
||||
self.assertEqual(f.formatDouble(-80, context), "080°00.00′W")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "005°26.40′E")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "005°26.40′W")
|
||||
f.setShowDirectionalSuffix(False)
|
||||
self.assertEqual(f.formatDouble(100, context), "100°00.00′")
|
||||
self.assertEqual(f.formatDouble(-100, context), "-100°00.00′")
|
||||
self.assertEqual(f.formatDouble(80, context), "080°00.00′")
|
||||
self.assertEqual(f.formatDouble(-80, context), "-080°00.00′")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "005°26.40′")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "-005°26.40′")
|
||||
|
||||
f.setShowTrailingZeros(False)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowDegreeLeadingZeros(False)
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5°26.4′E")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5°26.4′W")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5°26.46′W")
|
||||
|
||||
context.setDecimalSeparator('☕')
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5°26☕4′E")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5°26☕4′W")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5°26☕46′W")
|
||||
|
||||
def testGeographicCoordinateFormatLatitudeMinutes(self):
|
||||
"""Test formatting latitude as DM"""
|
||||
|
||||
f = QgsGeographicCoordinateNumericFormat()
|
||||
context = QgsNumericFormatContext()
|
||||
context.setInterpretation(QgsNumericFormatContext.Interpretation.Latitude)
|
||||
|
||||
f.setAngleFormat(QgsGeographicCoordinateNumericFormat.AngleFormat.DegreesMinutes)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowTrailingZeros(True)
|
||||
|
||||
self.assertEqual(f.formatDouble(20, context), "20°0.00′N")
|
||||
|
||||
# check precision
|
||||
f.setNumberDecimalPlaces(4)
|
||||
self.assertEqual(f.formatDouble(20, context), "20°0.0000′N")
|
||||
self.assertEqual(f.formatDouble(20.12345678, context), "20°7.4074′N")
|
||||
f.setNumberDecimalPlaces(0)
|
||||
self.assertEqual(f.formatDouble(20.12345678, context), "20°7′N")
|
||||
|
||||
# check if latitudes > 90 or <-90 wrap around
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(190, context), "10°0.00′N")
|
||||
self.assertEqual(f.formatDouble(-190, context), "10°0.00′S")
|
||||
self.assertEqual(f.formatDouble(91, context), "89°0.00′S")
|
||||
self.assertEqual(f.formatDouble(-91, context), "89°0.00′N")
|
||||
self.assertEqual(f.formatDouble(179, context), "1°0.00′S")
|
||||
self.assertEqual(f.formatDouble(-179, context), "1°0.00′N")
|
||||
|
||||
# should be no directional suffixes for 0 degree coordinates
|
||||
self.assertEqual(f.formatDouble(0, context), "0°0.00′")
|
||||
# should also be no directional suffix for 0 degree coordinates within specified precision
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°0.00′")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0.00′")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°0.00006′S")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0.00006′N")
|
||||
|
||||
# test rounding does not create minutes >= 60
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(79.999999, context), "80°0.00′N")
|
||||
|
||||
# test without direction suffix
|
||||
f.setShowDirectionalSuffix(False)
|
||||
self.assertEqual(f.formatDouble(20, context), "20°0.00′")
|
||||
# test 0 latitude
|
||||
self.assertEqual(f.formatDouble(0, context), "0°0.00′")
|
||||
# test near zero latitude
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0.00′")
|
||||
# should be no "-" prefix for near-zero latitude when rounding to 2 decimal places
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°0.00′")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°0.00006′")
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "-0°0.00006′")
|
||||
|
||||
# test with padding
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowLeadingZeros(True)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
self.assertEqual(f.formatDouble(20, context), "20°00.00′N")
|
||||
self.assertEqual(f.formatDouble(0, context), "0°00.00′")
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°00.00′")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°00.00′")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0°00.00006′S")
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0°00.00006′N")
|
||||
|
||||
# with degree padding
|
||||
f.setShowDegreeLeadingZeros(True)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(80, context), "80°00.00′N")
|
||||
self.assertEqual(f.formatDouble(-80, context), "80°00.00′S")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "05°26.40′N")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "05°26.40′S")
|
||||
f.setShowDirectionalSuffix(False)
|
||||
self.assertEqual(f.formatDouble(80, context), "80°00.00′")
|
||||
self.assertEqual(f.formatDouble(-80, context), "-80°00.00′")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "05°26.40′")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "-05°26.40′")
|
||||
|
||||
f.setShowTrailingZeros(False)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowDegreeLeadingZeros(False)
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5°26.4′N")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5°26.4′S")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5°26.46′S")
|
||||
|
||||
context.setDecimalSeparator('☕')
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5°26☕4′N")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5°26☕4′S")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5°26☕46′S")
|
||||
|
||||
def testGeographicCoordinateFormatLongitudeDegrees(self):
|
||||
"""Test formatting longitude as decimal degrees"""
|
||||
|
||||
f = QgsGeographicCoordinateNumericFormat()
|
||||
context = QgsNumericFormatContext()
|
||||
context.setInterpretation(QgsNumericFormatContext.Interpretation.Longitude)
|
||||
|
||||
f.setAngleFormat(QgsGeographicCoordinateNumericFormat.AngleFormat.DecimalDegrees)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowTrailingZeros(True)
|
||||
|
||||
self.assertEqual(f.formatDouble(80, context), "80.00°E")
|
||||
|
||||
# check precision
|
||||
f.setNumberDecimalPlaces(4)
|
||||
self.assertEqual(f.formatDouble(80, context), "80.0000°E")
|
||||
self.assertEqual(f.formatDouble(80.12345678, context), "80.1235°E")
|
||||
f.setNumberDecimalPlaces(0)
|
||||
self.assertEqual(f.formatDouble(80.12345678, context), "80°E")
|
||||
|
||||
# check if longitudes > 180 or <-180 wrap around
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(370, context), "10.00°E")
|
||||
self.assertEqual(f.formatDouble(-370, context), "10.00°W")
|
||||
self.assertEqual(f.formatDouble(181, context), "179.00°W")
|
||||
self.assertEqual(f.formatDouble(-181, context), "179.00°E")
|
||||
self.assertEqual(f.formatDouble(359, context), "1.00°W")
|
||||
self.assertEqual(f.formatDouble(-359, context), "1.00°E")
|
||||
|
||||
# should be no directional suffixes for 0 degree coordinates
|
||||
self.assertEqual(f.formatDouble(0, context), "0.00°")
|
||||
# should also be no directional suffix for 0 degree coordinates within specified precision
|
||||
self.assertEqual(f.formatDouble(-0.00001, context), "0.00°")
|
||||
self.assertEqual(f.formatDouble(0.00001, context), "0.00°")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(-0.00001, context), "0.00001°W")
|
||||
self.assertEqual(f.formatDouble(0.00001, context), "0.00001°E")
|
||||
|
||||
# should be no directional suffixes for 180 degree longitudes
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(180, context), "180.00°")
|
||||
|
||||
# should also be no directional suffix for 180 degree longitudes within specified precision
|
||||
self.assertEqual(f.formatDouble(180.000001, context), "180.00°")
|
||||
self.assertEqual(f.formatDouble(179.999999, context), "180.00°")
|
||||
f.setNumberDecimalPlaces(6)
|
||||
self.assertEqual(f.formatDouble(180.000001, context), "179.999999°W")
|
||||
self.assertEqual(f.formatDouble(179.999999, context), "179.999999°E")
|
||||
|
||||
# test without direction suffix
|
||||
f.setShowDirectionalSuffix(False)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(80, context), "80.00°")
|
||||
# test 0 longitude
|
||||
self.assertEqual(f.formatDouble(0, context), "0.00°")
|
||||
# test near zero longitude
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0.00°")
|
||||
# should be no "-" prefix for near-zero longitude when rounding to 2 decimal places
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0.00°")
|
||||
f.setNumberDecimalPlaces(6)
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0.000001°")
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "-0.000001°")
|
||||
|
||||
# with degree padding
|
||||
f.setShowDegreeLeadingZeros(True)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
self.assertEqual(f.formatDouble(100, context), "100.00°E")
|
||||
self.assertEqual(f.formatDouble(-100, context), "100.00°W")
|
||||
self.assertEqual(f.formatDouble(80, context), "080.00°E")
|
||||
self.assertEqual(f.formatDouble(-80, context), "080.00°W")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "005.44°E")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "005.44°W")
|
||||
f.setShowDirectionalSuffix(False)
|
||||
self.assertEqual(f.formatDouble(100, context), "100.00°")
|
||||
self.assertEqual(f.formatDouble(-100, context), "-100.00°")
|
||||
self.assertEqual(f.formatDouble(80, context), "080.00°")
|
||||
self.assertEqual(f.formatDouble(-80, context), "-080.00°")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "005.44°")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "-005.44°")
|
||||
|
||||
f.setShowTrailingZeros(False)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowDegreeLeadingZeros(False)
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5.44°E")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5.44°W")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5.44°W")
|
||||
|
||||
context.setDecimalSeparator('☕')
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5☕44°E")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5☕44°W")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5☕44°W")
|
||||
|
||||
def testGeographicCoordinateFormatLatitudeDegrees(self):
|
||||
"""Test formatting latitude as decimal degrees"""
|
||||
|
||||
f = QgsGeographicCoordinateNumericFormat()
|
||||
context = QgsNumericFormatContext()
|
||||
context.setInterpretation(QgsNumericFormatContext.Interpretation.Latitude)
|
||||
|
||||
f.setAngleFormat(QgsGeographicCoordinateNumericFormat.AngleFormat.DecimalDegrees)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowTrailingZeros(True)
|
||||
|
||||
self.assertEqual(f.formatDouble(20, context), "20.00°N")
|
||||
|
||||
# check precision
|
||||
f.setNumberDecimalPlaces(4)
|
||||
self.assertEqual(f.formatDouble(20, context), "20.0000°N")
|
||||
self.assertEqual(f.formatDouble(20.12345678, context), "20.1235°N")
|
||||
f.setNumberDecimalPlaces(0)
|
||||
self.assertEqual(f.formatDouble(20.12345678, context), "20°N")
|
||||
|
||||
# check if latitudes > 90 or <-90 wrap around
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(190, context), "10.00°N")
|
||||
self.assertEqual(f.formatDouble(-190, context), "10.00°S")
|
||||
self.assertEqual(f.formatDouble(91, context), "89.00°S")
|
||||
self.assertEqual(f.formatDouble(-91, context), "89.00°N")
|
||||
self.assertEqual(f.formatDouble(179, context), "1.00°S")
|
||||
self.assertEqual(f.formatDouble(-179, context), "1.00°N")
|
||||
|
||||
# should be no directional suffixes for 0 degree coordinates
|
||||
self.assertEqual(f.formatDouble(0, context), "0.00°")
|
||||
# should also be no directional suffix for 0 degree coordinates within specified precision
|
||||
self.assertEqual(f.formatDouble(-0.00001, context), "0.00°")
|
||||
self.assertEqual(f.formatDouble(0.00001, context), "0.00°")
|
||||
f.setNumberDecimalPlaces(5)
|
||||
self.assertEqual(f.formatDouble(-0.00001, context), "0.00001°S")
|
||||
self.assertEqual(f.formatDouble(0.00001, context), "0.00001°N")
|
||||
|
||||
# test without direction suffix
|
||||
f.setShowDirectionalSuffix(False)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
self.assertEqual(f.formatDouble(80, context), "80.00°")
|
||||
# test 0 longitude
|
||||
self.assertEqual(f.formatDouble(0, context), "0.00°")
|
||||
# test near zero latitude
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0.00°")
|
||||
# should be no "-" prefix for near-zero latitude when rounding to 2 decimal places
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "0.00°")
|
||||
f.setNumberDecimalPlaces(6)
|
||||
self.assertEqual(f.formatDouble(0.000001, context), "0.000001°")
|
||||
self.assertEqual(f.formatDouble(-0.000001, context), "-0.000001°")
|
||||
|
||||
# with degree padding
|
||||
f.setShowDegreeLeadingZeros(True)
|
||||
f.setNumberDecimalPlaces(2)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
self.assertEqual(f.formatDouble(80, context), "80.00°N")
|
||||
self.assertEqual(f.formatDouble(-80, context), "80.00°S")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "05.44°N")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "05.44°S")
|
||||
f.setShowDirectionalSuffix(False)
|
||||
self.assertEqual(f.formatDouble(80, context), "80.00°")
|
||||
self.assertEqual(f.formatDouble(-80, context), "-80.00°")
|
||||
self.assertEqual(f.formatDouble(5.44, context), "05.44°")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "-05.44°")
|
||||
|
||||
f.setShowTrailingZeros(False)
|
||||
f.setShowDirectionalSuffix(True)
|
||||
f.setShowDegreeLeadingZeros(False)
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5.44°N")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5.44°S")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5.44°S")
|
||||
|
||||
context.setDecimalSeparator('☕')
|
||||
self.assertEqual(f.formatDouble(5.44, context), "5☕44°N")
|
||||
self.assertEqual(f.formatDouble(-5.44, context), "5☕44°S")
|
||||
self.assertEqual(f.formatDouble(-5.44101, context), "5☕44°S")
|
||||
|
||||
def testRegistry(self):
|
||||
registry = QgsNumericFormatRegistry()
|
||||
self.assertTrue(registry.formats())
|
||||
|
||||
@ -19,6 +19,7 @@ from qgis.core import (QgsFallbackNumericFormat,
|
||||
QgsPercentageNumericFormat,
|
||||
QgsScientificNumericFormat,
|
||||
QgsCurrencyNumericFormat,
|
||||
QgsGeographicCoordinateNumericFormat,
|
||||
QgsNumericFormatRegistry,
|
||||
QgsNumericFormat,
|
||||
QgsApplication)
|
||||
@ -189,6 +190,28 @@ class TestQgsNumericFormatGui(unittest.TestCase):
|
||||
self.assertEqual(new.showTrailingZeros(), original.showTrailingZeros())
|
||||
self.assertEqual(new.directionFormat(), original.directionFormat())
|
||||
|
||||
def testGeographicCoordinateFormat(self):
|
||||
w = QgsNumericFormatSelectorWidget()
|
||||
|
||||
original = QgsGeographicCoordinateNumericFormat()
|
||||
original.setNumberDecimalPlaces(4)
|
||||
original.setShowTrailingZeros(True)
|
||||
original.setAngleFormat(QgsGeographicCoordinateNumericFormat.AngleFormat.DegreesMinutes)
|
||||
original.setShowDirectionalSuffix(True)
|
||||
original.setShowLeadingZeros(True)
|
||||
original.setShowDegreeLeadingZeros(True)
|
||||
|
||||
w.setFormat(original)
|
||||
new = w.format()
|
||||
|
||||
self.assertIsInstance(new, QgsGeographicCoordinateNumericFormat)
|
||||
self.assertEqual(new.numberDecimalPlaces(), original.numberDecimalPlaces())
|
||||
self.assertEqual(new.showTrailingZeros(), original.showTrailingZeros())
|
||||
self.assertEqual(new.showDirectionalSuffix(), original.showDirectionalSuffix())
|
||||
self.assertEqual(new.angleFormat(), original.angleFormat())
|
||||
self.assertEqual(new.showLeadingZeros(), original.showLeadingZeros())
|
||||
self.assertEqual(new.showDegreeLeadingZeros(), original.showDegreeLeadingZeros())
|
||||
|
||||
def testPercentageFormat(self):
|
||||
w = QgsNumericFormatSelectorWidget()
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user