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