QGIS API Documentation 3.99.0-Master (752b475928d)
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
18#include <proj.h>
19
21#include "qgsprojutils.h"
22
23#include <QMessageBox>
24#include <QRegularExpression>
25#include <QRegularExpressionMatch>
26
27#include "moc_qgscrsdefinitionwidget.cpp"
28
30 : QWidget( parent )
31{
32 setupUi( this );
33
34 connect( mButtonCalculate, &QPushButton::clicked, this, &QgsCrsDefinitionWidget::pbnCalculate_clicked );
35 connect( mButtonCopyCRS, &QPushButton::clicked, this, &QgsCrsDefinitionWidget::pbnCopyCRS_clicked );
36 connect( mButtonValidate, &QPushButton::clicked, this, &QgsCrsDefinitionWidget::validateCurrent );
37
38 mFormatComboBox->addItem( tr( "WKT (Recommended)" ), static_cast<int>( Qgis::CrsDefinitionFormat::Wkt ) );
39 mFormatComboBox->addItem( tr( "Proj String (Legacy — Not Recommended)" ), static_cast<int>( Qgis::CrsDefinitionFormat::Proj ) );
40 mFormatComboBox->setCurrentIndex( mFormatComboBox->findData( static_cast<int>( Qgis::CrsDefinitionFormat::Wkt ) ) );
41
42 connect( mFormatComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsCrsDefinitionWidget::formatChanged );
43 connect( mTextEditParameters, &QPlainTextEdit::textChanged, this, &QgsCrsDefinitionWidget::crsChanged );
44}
45
47{
49 switch ( static_cast<Qgis::CrsDefinitionFormat>( mFormatComboBox->currentData().toInt() ) )
50 {
52 crs = QgsCoordinateReferenceSystem::fromWkt( mTextEditParameters->toPlainText() );
53 break;
54
56 crs = QgsCoordinateReferenceSystem::fromProj( mTextEditParameters->toPlainText() );
57 break;
58 }
59
60 crs.setNativeFormat( format() );
61 return crs;
62}
63
65{
66 setCrs( crs, crs.nativeFormat() );
67}
68
70{
71 switch ( nativeFormat )
72 {
74 whileBlocking( mTextEditParameters )->setPlainText( crs.toWkt( Qgis::CrsWktVariant::Preferred, false ) );
75 break;
77 whileBlocking( mTextEditParameters )->setPlainText( crs.toProj() );
78 break;
79 }
80
81 whileBlocking( mFormatComboBox )->setCurrentIndex( mFormatComboBox->findData( static_cast<int>( nativeFormat ) ) );
82 emit crsChanged();
83}
84
86{
87 return static_cast<Qgis::CrsDefinitionFormat>( mFormatComboBox->currentData().toInt() );
88}
89
91{
92 mFormatComboBox->setCurrentIndex( mFormatComboBox->findData( static_cast<int>( format ) ) );
93}
94
96{
97 return mTextEditParameters->toPlainText();
98}
99
100void QgsCrsDefinitionWidget::setDefinitionString( const QString &definition )
101{
102 mTextEditParameters->setPlainText( definition );
103}
104
105void QgsCrsDefinitionWidget::pbnCopyCRS_clicked()
106{
107 auto selector = std::make_unique<QgsProjectionSelectionDialog>( this );
108 if ( selector->exec() )
109 {
110 const QgsCoordinateReferenceSystem srs = selector->crs();
111
112 whileBlocking( mFormatComboBox )->setCurrentIndex( mFormatComboBox->findData( static_cast<int>( Qgis::CrsDefinitionFormat::Wkt ) ) );
113 mTextEditParameters->setPlainText( srs.toWkt( Qgis::CrsWktVariant::Preferred, true ) );
114 }
115}
116
117void QgsCrsDefinitionWidget::validateCurrent()
118{
119 const QString projDef = mTextEditParameters->toPlainText();
120
121 PJ_CONTEXT *context = QgsProjContext::get();
122
123 QgsScopedProjCollectingLogger projLogger;
125
126 switch ( static_cast<Qgis::CrsDefinitionFormat>( mFormatComboBox->currentData().toInt() ) )
127 {
129 {
130 PROJ_STRING_LIST warnings = nullptr;
131 PROJ_STRING_LIST grammarErrors = nullptr;
132 crs.reset( proj_create_from_wkt( context, projDef.toUtf8().constData(), nullptr, &warnings, &grammarErrors ) );
133 QStringList warningStrings;
134 QStringList grammarStrings;
135 for ( auto iter = warnings; iter && *iter; ++iter )
136 warningStrings << QString( *iter );
137 for ( auto iter = grammarErrors; iter && *iter; ++iter )
138 grammarStrings << QString( *iter );
139 proj_string_list_destroy( warnings );
140 proj_string_list_destroy( grammarErrors );
141
142 if ( crs )
143 {
144 QMessageBox::information( this, tr( "Custom Coordinate Reference System" ), tr( "This WKT projection definition is valid." ) );
145 }
146 else
147 {
148 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' ) );
149 }
150 break;
151 }
152
154 {
155 const QString projCrsString = projDef + ( projDef.contains( QStringLiteral( "+type=crs" ) ) ? QString() : QStringLiteral( " +type=crs" ) );
156 crs.reset( proj_create( context, projCrsString.toUtf8().constData() ) );
157 if ( crs )
158 {
159 QMessageBox::information( this, tr( "Custom Coordinate Reference System" ), tr( "This proj projection definition is valid." ) );
160 }
161 else
162 {
163 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ), tr( "This proj projection definition is not valid:" ) + QStringLiteral( "\n\n" ) + projLogger.errors().join( '\n' ) );
164 }
165 break;
166 }
167 }
168}
169
170void QgsCrsDefinitionWidget::formatChanged()
171{
172 QgsCoordinateReferenceSystem crs;
173 QString newFormatString;
174 switch ( static_cast<Qgis::CrsDefinitionFormat>( mFormatComboBox->currentData().toInt() ) )
175 {
177 {
178 crs.createFromWkt( multiLineWktToSingleLine( mTextEditParameters->toPlainText() ) );
179 if ( crs.isValid() )
180 newFormatString = crs.toProj();
181 break;
182 }
183
185 {
186 PJ_CONTEXT *pjContext = QgsProjContext::get();
187 QString proj = mTextEditParameters->toPlainText();
188 proj.replace( QLatin1String( "+type=crs" ), QString() );
189 proj += QLatin1String( " +type=crs" );
190 QgsProjUtils::proj_pj_unique_ptr crs( proj_create( QgsProjContext::get(), proj.toUtf8().constData() ) );
191 if ( crs )
192 {
193 const QByteArray multiLineOption = QStringLiteral( "MULTILINE=YES" ).toLocal8Bit();
194 const char *const options[] = { multiLineOption.constData(), nullptr };
195 newFormatString = QString( proj_as_wkt( pjContext, crs.get(), PJ_WKT2_2019, options ) );
196 }
197 break;
198 }
199 }
200 if ( !newFormatString.isEmpty() )
201 mTextEditParameters->setPlainText( newFormatString );
202}
203
204void QgsCrsDefinitionWidget::pbnCalculate_clicked()
205{
206 // We must check the prj def is valid!
207 QString projDef = mTextEditParameters->toPlainText();
208
209 // Get the WGS84 coordinates
210 bool okN, okE;
211 double latitude = mNorthWGS84Edit->text().toDouble( &okN );
212 double longitude = mEastWGS84Edit->text().toDouble( &okE );
213
214 if ( !okN || !okE )
215 {
216 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ), tr( "Latitude and Longitude must be in decimal form." ) );
217 mProjectedXLabel->clear();
218 mProjectedYLabel->clear();
219 return;
220 }
221
222 QgsCoordinateReferenceSystem target;
223 if ( static_cast<Qgis::CrsDefinitionFormat>( mFormatComboBox->currentData().toInt() ) == Qgis::CrsDefinitionFormat::Proj )
224 {
225 projDef = projDef + ( projDef.contains( QStringLiteral( "+type=crs" ) ) ? QString() : QStringLiteral( " +type=crs" ) );
226 target = QgsCoordinateReferenceSystem::fromProj( projDef );
227 }
228 else
229 {
230 target = QgsCoordinateReferenceSystem::fromWkt( projDef );
231 }
232
233 if ( !target.isValid() )
234 {
235 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ), tr( "This CRS projection definition is not valid." ) );
236 mProjectedXLabel->clear();
237 mProjectedYLabel->clear();
238 return;
239 }
240
241 QgsCoordinateReferenceSystem source;
242 try
243 {
244 if ( target.celestialBodyName() == QLatin1String( "Earth" ) )
245 {
246 source = QgsCoordinateReferenceSystem( "EPSG:4326" );
247 }
248 else
249 {
250 source = target.toGeographicCrs();
251 }
252 }
253 catch ( QgsNotSupportedException & )
254 {
255 source = target.toGeographicCrs();
256 }
257
258 const QgsCoordinateTransform transform( source, target, QgsCoordinateTransformContext() );
259 try
260 {
261 const QgsPointXY res = transform.transform( QgsPointXY( longitude, latitude ) );
262 const int precision = target.isGeographic() ? 7 : 4;
263 mProjectedXLabel->setText( QLocale().toString( res.x(), 'f', precision ) );
264 mProjectedYLabel->setText( QLocale().toString( res.y(), 'f', precision ) );
265 }
266 catch ( QgsCsException &e )
267 {
268 mProjectedXLabel->setText( tr( "Error" ) );
269 mProjectedYLabel->setText( tr( "Error" ) );
270 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ), e.what() );
271 }
272}
273
274QString QgsCrsDefinitionWidget::multiLineWktToSingleLine( const QString &wkt )
275{
276 QString res = wkt;
277 const thread_local QRegularExpression re( QStringLiteral( "\\s*\\n\\s*" ), QRegularExpression::MultilineOption );
278 res.replace( re, QString() );
279 return res;
280}
CrsDefinitionFormat
CRS definition formats.
Definition qgis.h:3891
@ Wkt
WKT format (always recommended over proj string format).
Definition qgis.h:3892
@ Proj
Proj string format.
Definition qgis.h:3893
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Definition qgis.h:2439
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
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.
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.
QString what() const
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.
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:6511
struct pj_ctx PJ_CONTEXT