QGIS API Documentation 3.39.0-Master (47f7b3a4989)
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
113static void proj_collecting_logger( void *user_data, int /*level*/, const char *message )
114{
115 QStringList *dest = reinterpret_cast< QStringList * >( user_data );
116 QString messageString( message );
117 messageString.replace( QLatin1String( "internal_proj_create: " ), QString() );
118 dest->append( messageString );
119}
120
121void QgsCrsDefinitionWidget::validateCurrent()
122{
123 const QString projDef = mTextEditParameters->toPlainText();
124
125 PJ_CONTEXT *context = proj_context_create();
126
127 QStringList projErrors;
128 proj_log_func( context, &projErrors, proj_collecting_logger );
130
131 switch ( static_cast< Qgis::CrsDefinitionFormat >( mFormatComboBox->currentData().toInt() ) )
132 {
134 {
135 PROJ_STRING_LIST warnings = nullptr;
136 PROJ_STRING_LIST grammarErrors = nullptr;
137 crs.reset( proj_create_from_wkt( context, projDef.toUtf8().constData(), nullptr, &warnings, &grammarErrors ) );
138 QStringList warningStrings;
139 QStringList grammarStrings;
140 for ( auto iter = warnings; iter && *iter; ++iter )
141 warningStrings << QString( *iter );
142 for ( auto iter = grammarErrors; iter && *iter; ++iter )
143 grammarStrings << QString( *iter );
144 proj_string_list_destroy( warnings );
145 proj_string_list_destroy( grammarErrors );
146
147 if ( crs )
148 {
149 QMessageBox::information( this, tr( "Custom Coordinate Reference System" ),
150 tr( "This WKT projection definition is valid." ) );
151 }
152 else
153 {
154 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ),
155 tr( "This WKT projection definition is not valid:" ) + QStringLiteral( "\n\n" ) + warningStrings.join( '\n' ) + grammarStrings.join( '\n' ) );
156 }
157 break;
158 }
159
161 {
162 const QString projCrsString = projDef + ( projDef.contains( QStringLiteral( "+type=crs" ) ) ? QString() : QStringLiteral( " +type=crs" ) );
163 crs.reset( proj_create( context, projCrsString.toUtf8().constData() ) );
164 if ( crs )
165 {
166 QMessageBox::information( this, tr( "Custom Coordinate Reference System" ),
167 tr( "This proj projection definition is valid." ) );
168 }
169 else
170 {
171 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ),
172 tr( "This proj projection definition is not valid:" ) + QStringLiteral( "\n\n" ) + projErrors.join( '\n' ) );
173 }
174 break;
175 }
176 }
177
178 // reset logger to terminal output
179 proj_log_func( context, nullptr, nullptr );
180 proj_context_destroy( context );
181 context = nullptr;
182}
183
184void QgsCrsDefinitionWidget::formatChanged()
185{
187 QString newFormatString;
188 switch ( static_cast< Qgis::CrsDefinitionFormat >( mFormatComboBox->currentData().toInt() ) )
189 {
191 {
192 crs.createFromWkt( multiLineWktToSingleLine( mTextEditParameters->toPlainText() ) );
193 if ( crs.isValid() )
194 newFormatString = crs.toProj();
195 break;
196 }
197
199 {
200 PJ_CONTEXT *pjContext = QgsProjContext::get();
201 QString proj = mTextEditParameters->toPlainText();
202 proj.replace( QLatin1String( "+type=crs" ), QString() );
203 proj += QLatin1String( " +type=crs" );
204 QgsProjUtils::proj_pj_unique_ptr crs( proj_create( QgsProjContext::get(), proj.toUtf8().constData() ) );
205 if ( crs )
206 {
207 const QByteArray multiLineOption = QStringLiteral( "MULTILINE=YES" ).toLocal8Bit();
208 const char *const options[] = {multiLineOption.constData(), nullptr};
209 newFormatString = QString( proj_as_wkt( pjContext, crs.get(), PJ_WKT2_2019, options ) );
210 }
211 break;
212 }
213 }
214 if ( !newFormatString.isEmpty() )
215 mTextEditParameters->setPlainText( newFormatString );
216}
217
218void QgsCrsDefinitionWidget::pbnCalculate_clicked()
219{
220 // We must check the prj def is valid!
221 QString projDef = mTextEditParameters->toPlainText();
222
223 // Get the WGS84 coordinates
224 bool okN, okE;
225 double latitude = mNorthWGS84Edit->text().toDouble( &okN );
226 double longitude = mEastWGS84Edit->text().toDouble( &okE );
227
228 if ( !okN || !okE )
229 {
230 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ),
231 tr( "Latitude and Longitude must be in decimal form." ) );
232 mProjectedXLabel->clear();
233 mProjectedYLabel->clear();
234 return;
235 }
236
238 if ( static_cast< Qgis::CrsDefinitionFormat >( mFormatComboBox->currentData().toInt() ) == Qgis::CrsDefinitionFormat::Proj )
239 {
240 projDef = projDef + ( projDef.contains( QStringLiteral( "+type=crs" ) ) ? QString() : QStringLiteral( " +type=crs" ) );
241 target = QgsCoordinateReferenceSystem::fromProj( projDef );
242 }
243 else
244 {
245 target = QgsCoordinateReferenceSystem::fromWkt( projDef );
246 }
247
248 if ( !target.isValid() )
249 {
250 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ),
251 tr( "This CRS projection definition is not valid." ) );
252 mProjectedXLabel->clear();
253 mProjectedYLabel->clear();
254 return;
255 }
256
258 try
259 {
260 if ( target.celestialBodyName() == QLatin1String( "Earth" ) )
261 {
262 source = QgsCoordinateReferenceSystem( "EPSG:4326" );
263 }
264 else
265 {
266 source = target.toGeographicCrs();
267 }
268 }
269 catch ( QgsNotSupportedException & )
270 {
271 source = target.toGeographicCrs();
272 }
273
274 const QgsCoordinateTransform transform( source, target, QgsCoordinateTransformContext() );
275 try
276 {
277 const QgsPointXY res = transform.transform( QgsPointXY( longitude, latitude ) );
278 const int precision = target.isGeographic() ? 7 : 4;
279 mProjectedXLabel->setText( QLocale().toString( res.x(), 'f', precision ) );
280 mProjectedYLabel->setText( QLocale().toString( res.y(), 'f', precision ) );
281 }
282 catch ( QgsCsException &e )
283 {
284 mProjectedXLabel->setText( tr( "Error" ) );
285 mProjectedYLabel->setText( tr( "Error" ) );
286 QMessageBox::warning( this, tr( "Custom Coordinate Reference System" ),
287 e.what() );
288 }
289}
290
291QString QgsCrsDefinitionWidget::multiLineWktToSingleLine( const QString &wkt )
292{
293 QString res = wkt;
294 const thread_local QRegularExpression re( QStringLiteral( "\\s*\\n\\s*" ), QRegularExpression::MultilineOption );
295 res.replace( re, QString() );
296 return res;
297}
CrsDefinitionFormat
CRS definition formats.
Definition qgis.h:3322
@ 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.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5369
struct projCtx_t PJ_CONTEXT
const QgsCoordinateReferenceSystem & crs
int precision