QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
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
18#include "qgsprojutils.h"
19
20#include <QMessageBox>
21#include <QRegularExpression>
22#include <QRegularExpressionMatch>
23#include <proj.h>
24
26 : QWidget( parent )
27{
28 setupUi( this );
29
30 connect( mButtonCalculate, &QPushButton::clicked, this, &QgsCrsDefinitionWidget::pbnCalculate_clicked );
31 connect( mButtonCopyCRS, &QPushButton::clicked, this, &QgsCrsDefinitionWidget::pbnCopyCRS_clicked );
32 connect( mButtonValidate, &QPushButton::clicked, this, &QgsCrsDefinitionWidget::validateCurrent );
33
34 mFormatComboBox->addItem( tr( "WKT (Recommended)" ), static_cast< int >( Qgis::CrsDefinitionFormat::Wkt ) );
35 mFormatComboBox->addItem( tr( "Proj String (Legacy — Not Recommended)" ), static_cast< int >( Qgis::CrsDefinitionFormat::Proj ) );
36 mFormatComboBox->setCurrentIndex( mFormatComboBox->findData( static_cast< int >( Qgis::CrsDefinitionFormat::Wkt ) ) );
37
38 connect( mFormatComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsCrsDefinitionWidget::formatChanged );
39 connect( mTextEditParameters, &QPlainTextEdit::textChanged, this, &QgsCrsDefinitionWidget::crsChanged );
40}
41
43{
45 switch ( static_cast< Qgis::CrsDefinitionFormat >( mFormatComboBox->currentData().toInt() ) )
46 {
48 crs = QgsCoordinateReferenceSystem::fromWkt( mTextEditParameters->toPlainText() );
49 break;
50
52 crs = QgsCoordinateReferenceSystem::fromProj( mTextEditParameters->toPlainText() );
53 break;
54 }
55
57 return crs;
58}
59
64
66{
67 switch ( nativeFormat )
68 {
70 whileBlocking( mTextEditParameters )->setPlainText( crs.toWkt( Qgis::CrsWktVariant::Preferred, false ) );
71 break;
73 whileBlocking( mTextEditParameters )->setPlainText( crs.toProj() );
74 break;
75 }
76
77 whileBlocking( mFormatComboBox )->setCurrentIndex( mFormatComboBox->findData( static_cast< int >( nativeFormat ) ) );
78 emit crsChanged();
79}
80
82{
83 return static_cast< Qgis::CrsDefinitionFormat >( mFormatComboBox->currentData().toInt() );
84}
85
87{
88 mFormatComboBox->setCurrentIndex( mFormatComboBox->findData( static_cast< int >( format ) ) );
89}
90
92{
93 return mTextEditParameters->toPlainText();
94}
95
96void QgsCrsDefinitionWidget::setDefinitionString( const QString &definition )
97{
98 mTextEditParameters->setPlainText( definition );
99}
100
101void QgsCrsDefinitionWidget::pbnCopyCRS_clicked()
102{
103 std::unique_ptr< QgsProjectionSelectionDialog > selector = std::make_unique< QgsProjectionSelectionDialog >( this );
104 if ( selector->exec() )
105 {
106 const QgsCoordinateReferenceSystem srs = selector->crs();
107
108 whileBlocking( mFormatComboBox )->setCurrentIndex( mFormatComboBox->findData( static_cast< int >( Qgis::CrsDefinitionFormat::Wkt ) ) );
109 mTextEditParameters->setPlainText( srs.toWkt( Qgis::CrsWktVariant::Preferred, true ) );
110 }
111}
112
113void QgsCrsDefinitionWidget::validateCurrent()
114{
115 const QString projDef = mTextEditParameters->toPlainText();
116
117 PJ_CONTEXT *context = QgsProjContext::get();
118
121
122 switch ( static_cast< Qgis::CrsDefinitionFormat >( mFormatComboBox->currentData().toInt() ) )
123 {
125 {
126 PROJ_STRING_LIST warnings = nullptr;
127 PROJ_STRING_LIST grammarErrors = nullptr;
128 crs.reset( proj_create_from_wkt( context, projDef.toUtf8().constData(), nullptr, &warnings, &grammarErrors ) );
129 QStringList warningStrings;
130 QStringList grammarStrings;
131 for ( auto iter = warnings; iter && *iter; ++iter )
132 warningStrings << QString( *iter );
133 for ( auto iter = grammarErrors; iter && *iter; ++iter )
134 grammarStrings << QString( *iter );
135 proj_string_list_destroy( warnings );
136 proj_string_list_destroy( grammarErrors );
137
138 if ( crs )
139 {
140 QMessageBox::information( this, tr( "Custom Coordinate Reference System" ),
141 tr( "This WKT projection definition is valid." ) );
142 }
143 else
144 {
145 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ),
146 tr( "This WKT projection definition is not valid:" ) + QStringLiteral( "\n\n" ) + warningStrings.join( '\n' ) + grammarStrings.join( '\n' ) );
147 }
148 break;
149 }
150
152 {
153 const QString projCrsString = projDef + ( projDef.contains( QStringLiteral( "+type=crs" ) ) ? QString() : QStringLiteral( " +type=crs" ) );
154 crs.reset( proj_create( context, projCrsString.toUtf8().constData() ) );
155 if ( crs )
156 {
157 QMessageBox::information( this, tr( "Custom Coordinate Reference System" ),
158 tr( "This proj projection definition is valid." ) );
159 }
160 else
161 {
162 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ),
163 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{
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" ),
217 tr( "Latitude and Longitude must be in decimal form." ) );
218 mProjectedXLabel->clear();
219 mProjectedYLabel->clear();
220 return;
221 }
222
224 if ( static_cast< Qgis::CrsDefinitionFormat >( mFormatComboBox->currentData().toInt() ) == Qgis::CrsDefinitionFormat::Proj )
225 {
226 projDef = projDef + ( projDef.contains( QStringLiteral( "+type=crs" ) ) ? QString() : QStringLiteral( " +type=crs" ) );
227 target = QgsCoordinateReferenceSystem::fromProj( projDef );
228 }
229 else
230 {
231 target = QgsCoordinateReferenceSystem::fromWkt( projDef );
232 }
233
234 if ( !target.isValid() )
235 {
236 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ),
237 tr( "This CRS projection definition is not valid." ) );
238 mProjectedXLabel->clear();
239 mProjectedYLabel->clear();
240 return;
241 }
242
244 try
245 {
246 if ( target.celestialBodyName() == QLatin1String( "Earth" ) )
247 {
248 source = QgsCoordinateReferenceSystem( "EPSG:4326" );
249 }
250 else
251 {
252 source = target.toGeographicCrs();
253 }
254 }
255 catch ( QgsNotSupportedException & )
256 {
257 source = target.toGeographicCrs();
258 }
259
260 const QgsCoordinateTransform transform( source, target, QgsCoordinateTransformContext() );
261 try
262 {
263 const QgsPointXY res = transform.transform( QgsPointXY( longitude, latitude ) );
264 const int precision = target.isGeographic() ? 7 : 4;
265 mProjectedXLabel->setText( QLocale().toString( res.x(), 'f', precision ) );
266 mProjectedYLabel->setText( QLocale().toString( res.y(), 'f', precision ) );
267 }
268 catch ( QgsCsException &e )
269 {
270 mProjectedXLabel->setText( tr( "Error" ) );
271 mProjectedYLabel->setText( tr( "Error" ) );
272 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ),
273 e.what() );
274 }
275}
276
277QString QgsCrsDefinitionWidget::multiLineWktToSingleLine( const QString &wkt )
278{
279 QString res = wkt;
280 const thread_local QRegularExpression re( QStringLiteral( "\\s*\\n\\s*" ), QRegularExpression::MultilineOption );
281 res.replace( re, QString() );
282 return res;
283}
CrsDefinitionFormat
CRS definition formats.
Definition qgis.h:3605
@ 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:5821
struct projCtx_t PJ_CONTEXT
const QgsCoordinateReferenceSystem & crs
int precision