QGIS API Documentation 3.41.0-Master (d5b93354e9c)
Loading...
Searching...
No Matches
qgscrsdefinitionwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscrsdefinitionwidget.cpp
3 ---------------------
4 begin : December 2021
5 copyright : (C) 2021 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "moc_qgscrsdefinitionwidget.cpp"
19#include "qgsprojutils.h"
20
21#include <QMessageBox>
22#include <QRegularExpression>
23#include <QRegularExpressionMatch>
24#include <proj.h>
25
27 : QWidget( parent )
28{
29 setupUi( this );
30
31 connect( mButtonCalculate, &QPushButton::clicked, this, &QgsCrsDefinitionWidget::pbnCalculate_clicked );
32 connect( mButtonCopyCRS, &QPushButton::clicked, this, &QgsCrsDefinitionWidget::pbnCopyCRS_clicked );
33 connect( mButtonValidate, &QPushButton::clicked, this, &QgsCrsDefinitionWidget::validateCurrent );
34
35 mFormatComboBox->addItem( tr( "WKT (Recommended)" ), static_cast<int>( Qgis::CrsDefinitionFormat::Wkt ) );
36 mFormatComboBox->addItem( tr( "Proj String (Legacy — Not Recommended)" ), static_cast<int>( Qgis::CrsDefinitionFormat::Proj ) );
37 mFormatComboBox->setCurrentIndex( mFormatComboBox->findData( static_cast<int>( Qgis::CrsDefinitionFormat::Wkt ) ) );
38
39 connect( mFormatComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsCrsDefinitionWidget::formatChanged );
40 connect( mTextEditParameters, &QPlainTextEdit::textChanged, this, &QgsCrsDefinitionWidget::crsChanged );
41}
42
44{
46 switch ( static_cast<Qgis::CrsDefinitionFormat>( mFormatComboBox->currentData().toInt() ) )
47 {
49 crs = QgsCoordinateReferenceSystem::fromWkt( mTextEditParameters->toPlainText() );
50 break;
51
53 crs = QgsCoordinateReferenceSystem::fromProj( mTextEditParameters->toPlainText() );
54 break;
55 }
56
58 return crs;
59}
60
65
67{
68 switch ( nativeFormat )
69 {
71 whileBlocking( mTextEditParameters )->setPlainText( crs.toWkt( Qgis::CrsWktVariant::Preferred, false ) );
72 break;
74 whileBlocking( mTextEditParameters )->setPlainText( crs.toProj() );
75 break;
76 }
77
78 whileBlocking( mFormatComboBox )->setCurrentIndex( mFormatComboBox->findData( static_cast<int>( nativeFormat ) ) );
79 emit crsChanged();
80}
81
83{
84 return static_cast<Qgis::CrsDefinitionFormat>( mFormatComboBox->currentData().toInt() );
85}
86
88{
89 mFormatComboBox->setCurrentIndex( mFormatComboBox->findData( static_cast<int>( format ) ) );
90}
91
93{
94 return mTextEditParameters->toPlainText();
95}
96
97void QgsCrsDefinitionWidget::setDefinitionString( const QString &definition )
98{
99 mTextEditParameters->setPlainText( definition );
100}
101
102void QgsCrsDefinitionWidget::pbnCopyCRS_clicked()
103{
104 std::unique_ptr<QgsProjectionSelectionDialog> selector = std::make_unique<QgsProjectionSelectionDialog>( this );
105 if ( selector->exec() )
106 {
107 const QgsCoordinateReferenceSystem srs = selector->crs();
108
109 whileBlocking( mFormatComboBox )->setCurrentIndex( mFormatComboBox->findData( static_cast<int>( Qgis::CrsDefinitionFormat::Wkt ) ) );
110 mTextEditParameters->setPlainText( srs.toWkt( Qgis::CrsWktVariant::Preferred, true ) );
111 }
112}
113
114void QgsCrsDefinitionWidget::validateCurrent()
115{
116 const QString projDef = mTextEditParameters->toPlainText();
117
118 PJ_CONTEXT *context = QgsProjContext::get();
119
122
123 switch ( static_cast<Qgis::CrsDefinitionFormat>( mFormatComboBox->currentData().toInt() ) )
124 {
126 {
127 PROJ_STRING_LIST warnings = nullptr;
128 PROJ_STRING_LIST grammarErrors = nullptr;
129 crs.reset( proj_create_from_wkt( context, projDef.toUtf8().constData(), nullptr, &warnings, &grammarErrors ) );
130 QStringList warningStrings;
131 QStringList grammarStrings;
132 for ( auto iter = warnings; iter && *iter; ++iter )
133 warningStrings << QString( *iter );
134 for ( auto iter = grammarErrors; iter && *iter; ++iter )
135 grammarStrings << QString( *iter );
136 proj_string_list_destroy( warnings );
137 proj_string_list_destroy( grammarErrors );
138
139 if ( crs )
140 {
141 QMessageBox::information( this, tr( "Custom Coordinate Reference System" ), tr( "This WKT projection definition is valid." ) );
142 }
143 else
144 {
145 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ), tr( "This WKT projection definition is not valid:" ) + QStringLiteral( "\n\n" ) + warningStrings.join( '\n' ) + grammarStrings.join( '\n' ) );
146 }
147 break;
148 }
149
151 {
152 const QString projCrsString = projDef + ( projDef.contains( QStringLiteral( "+type=crs" ) ) ? QString() : QStringLiteral( " +type=crs" ) );
153 crs.reset( proj_create( context, projCrsString.toUtf8().constData() ) );
154 if ( crs )
155 {
156 QMessageBox::information( this, tr( "Custom Coordinate Reference System" ), tr( "This proj projection definition is valid." ) );
157 }
158 else
159 {
160 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ), tr( "This proj projection definition is not valid:" ) + QStringLiteral( "\n\n" ) + projLogger.errors().join( '\n' ) );
161 }
162 break;
163 }
164 }
165}
166
167void QgsCrsDefinitionWidget::formatChanged()
168{
170 QString newFormatString;
171 switch ( static_cast<Qgis::CrsDefinitionFormat>( mFormatComboBox->currentData().toInt() ) )
172 {
174 {
175 crs.createFromWkt( multiLineWktToSingleLine( mTextEditParameters->toPlainText() ) );
176 if ( crs.isValid() )
177 newFormatString = crs.toProj();
178 break;
179 }
180
182 {
183 PJ_CONTEXT *pjContext = QgsProjContext::get();
184 QString proj = mTextEditParameters->toPlainText();
185 proj.replace( QLatin1String( "+type=crs" ), QString() );
186 proj += QLatin1String( " +type=crs" );
187 QgsProjUtils::proj_pj_unique_ptr crs( proj_create( QgsProjContext::get(), proj.toUtf8().constData() ) );
188 if ( crs )
189 {
190 const QByteArray multiLineOption = QStringLiteral( "MULTILINE=YES" ).toLocal8Bit();
191 const char *const options[] = { multiLineOption.constData(), nullptr };
192 newFormatString = QString( proj_as_wkt( pjContext, crs.get(), PJ_WKT2_2019, options ) );
193 }
194 break;
195 }
196 }
197 if ( !newFormatString.isEmpty() )
198 mTextEditParameters->setPlainText( newFormatString );
199}
200
201void QgsCrsDefinitionWidget::pbnCalculate_clicked()
202{
203 // We must check the prj def is valid!
204 QString projDef = mTextEditParameters->toPlainText();
205
206 // Get the WGS84 coordinates
207 bool okN, okE;
208 double latitude = mNorthWGS84Edit->text().toDouble( &okN );
209 double longitude = mEastWGS84Edit->text().toDouble( &okE );
210
211 if ( !okN || !okE )
212 {
213 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ), tr( "Latitude and Longitude must be in decimal form." ) );
214 mProjectedXLabel->clear();
215 mProjectedYLabel->clear();
216 return;
217 }
218
220 if ( static_cast<Qgis::CrsDefinitionFormat>( mFormatComboBox->currentData().toInt() ) == Qgis::CrsDefinitionFormat::Proj )
221 {
222 projDef = projDef + ( projDef.contains( QStringLiteral( "+type=crs" ) ) ? QString() : QStringLiteral( " +type=crs" ) );
223 target = QgsCoordinateReferenceSystem::fromProj( projDef );
224 }
225 else
226 {
227 target = QgsCoordinateReferenceSystem::fromWkt( projDef );
228 }
229
230 if ( !target.isValid() )
231 {
232 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ), tr( "This CRS projection definition is not valid." ) );
233 mProjectedXLabel->clear();
234 mProjectedYLabel->clear();
235 return;
236 }
237
239 try
240 {
241 if ( target.celestialBodyName() == QLatin1String( "Earth" ) )
242 {
243 source = QgsCoordinateReferenceSystem( "EPSG:4326" );
244 }
245 else
246 {
247 source = target.toGeographicCrs();
248 }
249 }
250 catch ( QgsNotSupportedException & )
251 {
252 source = target.toGeographicCrs();
253 }
254
255 const QgsCoordinateTransform transform( source, target, QgsCoordinateTransformContext() );
256 try
257 {
258 const QgsPointXY res = transform.transform( QgsPointXY( longitude, latitude ) );
259 const int precision = target.isGeographic() ? 7 : 4;
260 mProjectedXLabel->setText( QLocale().toString( res.x(), 'f', precision ) );
261 mProjectedYLabel->setText( QLocale().toString( res.y(), 'f', precision ) );
262 }
263 catch ( QgsCsException &e )
264 {
265 mProjectedXLabel->setText( tr( "Error" ) );
266 mProjectedYLabel->setText( tr( "Error" ) );
267 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ), e.what() );
268 }
269}
270
271QString QgsCrsDefinitionWidget::multiLineWktToSingleLine( const QString &wkt )
272{
273 QString res = wkt;
274 const thread_local QRegularExpression re( QStringLiteral( "\\s*\\n\\s*" ), QRegularExpression::MultilineOption );
275 res.replace( re, QString() );
276 return res;
277}
CrsDefinitionFormat
CRS definition formats.
Definition qgis.h:3658
@ Wkt
WKT format (always recommended over proj string format)
@ Proj
Proj string format.
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool createFromWkt(const QString &wkt)
Sets this CRS using a WKT definition.
QString toProj() const
Returns a Proj string representation of this CRS.
Qgis::CrsDefinitionFormat nativeFormat() const
Returns the native format for the CRS definition.
void setNativeFormat(Qgis::CrsDefinitionFormat format)
Sets the native format for the CRS definition.
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
QString celestialBodyName() const
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
void setDefinitionString(const QString &definition)
Sets the current definition string.
QString definitionString() const
Returns the current definition string.
void setFormat(Qgis::CrsDefinitionFormat format)
Sets the CRS format.
void crsChanged()
Emitted when the CRS defined in the widget is changed.
QgsCrsDefinitionWidget(QWidget *parent=nullptr)
Constructor for QgsCrsDefinitionWidget, with the specified parent widget.
Qgis::CrsDefinitionFormat format() const
Returns the selected CRS format.
QgsCoordinateReferenceSystem crs() const
Returns the current CRS as defined in the widget.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the current crs to display in the widget.
Custom exception class for Coordinate Reference System related exceptions.
QString what() const
Custom exception class which is raised when an operation is not supported.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
std::unique_ptr< PJ, ProjPJDeleter > proj_pj_unique_ptr
Scoped Proj PJ object.
Scoped object for temporary swapping to an error-collecting PROJ log function.
QStringList errors() const
Returns the (possibly empty) list of collected errors.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5928
struct projCtx_t PJ_CONTEXT
const QgsCoordinateReferenceSystem & crs
int precision