46
46
#define RAD2DEG (r ) (180.0 * (r) / M_PI)
47
47
#define POW2 (x ) ((x)*(x))
48
48
49
+ QReadWriteLock QgsDistanceArea::sEllipsoidCacheLock ;
50
+ QHash< QString, QgsDistanceArea::EllipsoidParameters > QgsDistanceArea::sEllipsoidCache ;
49
51
50
52
QgsDistanceArea::QgsDistanceArea ()
51
53
{
@@ -83,22 +85,35 @@ void QgsDistanceArea::setSourceAuthId( const QString &authId )
83
85
84
86
bool QgsDistanceArea::setEllipsoid ( const QString &ellipsoid )
85
87
{
86
- QString radius, parameter2;
87
- //
88
- // SQLITE3 stuff - get parameters for selected ellipsoid
89
- //
90
- sqlite3 *myDatabase = nullptr ;
91
- const char *myTail = nullptr ;
92
- sqlite3_stmt *myPreparedStatement = nullptr ;
93
- int myResult;
94
-
95
88
// Shortcut if ellipsoid is none.
96
89
if ( ellipsoid == GEO_NONE )
97
90
{
98
91
mEllipsoid = GEO_NONE;
99
92
return true ;
100
93
}
101
94
95
+ // check cache
96
+ sEllipsoidCacheLock .lockForRead ();
97
+ QHash< QString, EllipsoidParameters >::const_iterator cacheIt = sEllipsoidCache .constFind ( ellipsoid );
98
+ if ( cacheIt != sEllipsoidCache .constEnd () )
99
+ {
100
+ // found a match in the cache
101
+ sEllipsoidCacheLock .unlock ();
102
+ if ( cacheIt.value ().valid )
103
+ {
104
+ mEllipsoid = ellipsoid;
105
+ setFromParams ( cacheIt.value () );
106
+ return true ;
107
+ }
108
+ else
109
+ {
110
+ return false ;
111
+ }
112
+ }
113
+ sEllipsoidCacheLock .unlock ();
114
+
115
+ EllipsoidParameters params;
116
+
102
117
// Check if we have a custom projection, and set from text string.
103
118
// Format is "PARAMETER:<semi-major axis>:<semi minor axis>
104
119
// Numbers must be with (optional) decimal point and no other separators (C locale)
@@ -111,54 +126,80 @@ bool QgsDistanceArea::setEllipsoid( const QString &ellipsoid )
111
126
double semiMinor = paramList[2 ].toDouble ( & semiMinorOk );
112
127
if ( semiMajorOk && semiMinorOk )
113
128
{
114
- return setEllipsoid ( semiMajor, semiMinor );
129
+ params.semiMajor = semiMajor;
130
+ params.semiMinor = semiMinor;
131
+ params.useCustomParameters = true ;
115
132
}
116
133
else
117
134
{
118
- return false ;
135
+ params. valid = false ;
119
136
}
137
+
138
+ sEllipsoidCacheLock .lockForWrite ();
139
+ sEllipsoidCache .insert ( ellipsoid, params );
140
+ sEllipsoidCacheLock .unlock ();
141
+ if ( params.valid )
142
+ setFromParams ( params );
143
+ return params.valid ;
120
144
}
121
145
146
+ // cache miss - get from database
147
+
148
+ QString radius, parameter2;
149
+ //
150
+ // SQLITE3 stuff - get parameters for selected ellipsoid
151
+ //
152
+ sqlite3 *database = nullptr ;
153
+ const char *tail = nullptr ;
154
+ sqlite3_stmt *preparedStatement = nullptr ;
122
155
// Continue with PROJ.4 list of ellipsoids.
123
156
124
157
// check the db is available
125
- myResult = sqlite3_open_v2 ( QgsApplication::srsDatabaseFilePath ().toUtf8 ().data (), &myDatabase , SQLITE_OPEN_READONLY, nullptr );
126
- if ( myResult )
158
+ int result = sqlite3_open_v2 ( QgsApplication::srsDatabaseFilePath ().toUtf8 ().data (), &database , SQLITE_OPEN_READONLY, nullptr );
159
+ if ( result )
127
160
{
128
- QgsMessageLog::logMessage ( QObject::tr ( " Can't open database: %1" ).arg ( sqlite3_errmsg ( myDatabase ) ) );
161
+ QgsMessageLog::logMessage ( QObject::tr ( " Can't open database: %1" ).arg ( sqlite3_errmsg ( database ) ) );
129
162
// XXX This will likely never happen since on open, sqlite creates the
130
163
// database if it does not exist.
131
164
return false ;
132
165
}
133
166
// Set up the query to retrieve the projection information needed to populate the ELLIPSOID list
134
- QString mySql = " select radius, parameter2 from tbl_ellipsoid where acronym='" + ellipsoid + ' \' ' ;
135
- myResult = sqlite3_prepare ( myDatabase, mySql .toUtf8 (), mySql .toUtf8 ().length (), &myPreparedStatement , &myTail );
167
+ QString sql = " select radius, parameter2 from tbl_ellipsoid where acronym='" + ellipsoid + ' \' ' ;
168
+ result = sqlite3_prepare ( database, sql .toUtf8 (), sql .toUtf8 ().length (), &preparedStatement , &tail );
136
169
// XXX Need to free memory from the error msg if one is set
137
- if ( myResult == SQLITE_OK )
170
+ if ( result == SQLITE_OK )
138
171
{
139
- if ( sqlite3_step ( myPreparedStatement ) == SQLITE_ROW )
172
+ if ( sqlite3_step ( preparedStatement ) == SQLITE_ROW )
140
173
{
141
- radius = QString ( reinterpret_cast < const char * >( sqlite3_column_text ( myPreparedStatement , 0 ) ) );
142
- parameter2 = QString ( reinterpret_cast < const char * >( sqlite3_column_text ( myPreparedStatement , 1 ) ) );
174
+ radius = QString ( reinterpret_cast < const char * >( sqlite3_column_text ( preparedStatement , 0 ) ) );
175
+ parameter2 = QString ( reinterpret_cast < const char * >( sqlite3_column_text ( preparedStatement , 1 ) ) );
143
176
}
144
177
}
145
178
// close the sqlite3 statement
146
- sqlite3_finalize ( myPreparedStatement );
147
- sqlite3_close ( myDatabase );
179
+ sqlite3_finalize ( preparedStatement );
180
+ sqlite3_close ( database );
148
181
149
182
// row for this ellipsoid wasn't found?
150
183
if ( radius.isEmpty () || parameter2.isEmpty () )
151
184
{
152
185
QgsDebugMsg ( QString ( " setEllipsoid: no row in tbl_ellipsoid for acronym '%1'" ).arg ( ellipsoid ) );
186
+ params.valid = false ;
187
+ sEllipsoidCacheLock .lockForWrite ();
188
+ sEllipsoidCache .insert ( ellipsoid, params );
189
+ sEllipsoidCacheLock .unlock ();
153
190
return false ;
154
191
}
155
192
156
193
// get major semiaxis
157
194
if ( radius.left ( 2 ) == QLatin1String ( " a=" ) )
158
- mSemiMajor = radius.midRef ( 2 ).toDouble ();
195
+ params. semiMajor = radius.midRef ( 2 ).toDouble ();
159
196
else
160
197
{
161
198
QgsDebugMsg ( QString ( " setEllipsoid: wrong format of radius field: '%1'" ).arg ( radius ) );
199
+ params.valid = false ;
200
+ sEllipsoidCacheLock .lockForWrite ();
201
+ sEllipsoidCache .insert ( ellipsoid, params );
202
+ sEllipsoidCacheLock .unlock ();
162
203
return false ;
163
204
}
164
205
@@ -167,21 +208,25 @@ bool QgsDistanceArea::setEllipsoid( const QString &ellipsoid )
167
208
// second one must be computed using formula: invf = a/(a-b)
168
209
if ( parameter2.left ( 2 ) == QLatin1String ( " b=" ) )
169
210
{
170
- mSemiMinor = parameter2.midRef ( 2 ).toDouble ();
171
- mInvFlattening = mSemiMajor / ( mSemiMajor - mSemiMinor );
211
+ params. semiMinor = parameter2.midRef ( 2 ).toDouble ();
212
+ params. inverseFlattening = params. semiMajor / ( params. semiMajor - params. semiMinor );
172
213
}
173
214
else if ( parameter2.left ( 3 ) == QLatin1String ( " rf=" ) )
174
215
{
175
- mInvFlattening = parameter2.midRef ( 3 ).toDouble ();
176
- mSemiMinor = mSemiMajor - ( mSemiMajor / mInvFlattening );
216
+ params. inverseFlattening = parameter2.midRef ( 3 ).toDouble ();
217
+ params. semiMinor = params. semiMajor - ( params. semiMajor / params. inverseFlattening );
177
218
}
178
219
else
179
220
{
180
221
QgsDebugMsg ( QString ( " setEllipsoid: wrong format of parameter2 field: '%1'" ).arg ( parameter2 ) );
222
+ params.valid = false ;
223
+ sEllipsoidCacheLock .lockForWrite ();
224
+ sEllipsoidCache .insert ( ellipsoid, params );
225
+ sEllipsoidCacheLock .unlock ();
181
226
return false ;
182
227
}
183
228
184
- QgsDebugMsg ( QString ( " setEllipsoid: a=%1, b=%2, 1/f=%3" ).arg ( mSemiMajor ).arg ( mSemiMinor ).arg ( mInvFlattening ) );
229
+ QgsDebugMsg ( QString ( " setEllipsoid: a=%1, b=%2, 1/f=%3" ).arg ( params. semiMajor ).arg ( params. semiMinor ).arg ( params. inverseFlattening ) );
185
230
186
231
187
232
// get spatial ref system for ellipsoid
@@ -193,29 +238,31 @@ bool QgsDistanceArea::setEllipsoid( const QString &ellipsoid )
193
238
// familiar with the code (should also give a more descriptive name to the generated CRS)
194
239
if ( destCRS.srsid () == 0 )
195
240
{
196
- QString myName = QStringLiteral ( " * %1 (%2)" )
197
- .arg ( QObject::tr ( " Generated CRS" , " A CRS automatically generated from layer info get this prefix for description" ),
198
- destCRS.toProj4 () );
199
- destCRS.saveAsUserCrs ( myName );
241
+ QString name = QStringLiteral ( " * %1 (%2)" )
242
+ .arg ( QObject::tr ( " Generated CRS" , " A CRS automatically generated from layer info get this prefix for description" ),
243
+ destCRS.toProj4 () );
244
+ destCRS.saveAsUserCrs ( name );
200
245
}
201
246
//
202
247
203
248
// set transformation from project CRS to ellipsoid coordinates
204
- mCoordTransform . setDestinationCrs ( destCRS ) ;
249
+ params. crs = destCRS ;
205
250
206
- mEllipsoid = ellipsoid;
251
+ sEllipsoidCacheLock .lockForWrite ();
252
+ sEllipsoidCache .insert ( ellipsoid, params );
253
+ sEllipsoidCacheLock .unlock ();
207
254
208
- // precalculate some values for area calculations
209
- computeAreaInit ();
255
+ mEllipsoid = ellipsoid;
210
256
257
+ setFromParams ( params );
211
258
return true ;
212
259
}
213
260
214
261
// Inverse flattening is calculated with invf = a/(a-b)
215
262
// Also, b = a-(a/invf)
216
263
bool QgsDistanceArea::setEllipsoid ( double semiMajor, double semiMinor )
217
264
{
218
- mEllipsoid = QStringLiteral ( " PARAMETER:%1:%2" ).arg ( semiMajor ).arg ( semiMinor );
265
+ mEllipsoid = QStringLiteral ( " PARAMETER:%1:%2" ).arg ( qgsDoubleToString ( semiMajor ) ) .arg ( qgsDoubleToString ( semiMinor ) );
219
266
mSemiMajor = semiMajor;
220
267
mSemiMinor = semiMinor;
221
268
mInvFlattening = mSemiMajor / ( mSemiMajor - mSemiMinor );
@@ -787,6 +834,22 @@ void QgsDistanceArea::computeAreaInit()
787
834
m_E = -m_E;
788
835
}
789
836
837
+ void QgsDistanceArea::setFromParams ( const QgsDistanceArea::EllipsoidParameters ¶ms )
838
+ {
839
+ if ( params.useCustomParameters )
840
+ {
841
+ setEllipsoid ( params.semiMajor , params.semiMinor );
842
+ }
843
+ else
844
+ {
845
+ mSemiMajor = params.semiMajor ;
846
+ mSemiMinor = params.semiMinor ;
847
+ mInvFlattening = params.inverseFlattening ;
848
+ mCoordTransform .setDestinationCrs ( params.crs );
849
+ // precalculate some values for area calculations
850
+ computeAreaInit ();
851
+ }
852
+ }
790
853
791
854
double QgsDistanceArea::computePolygonArea ( const QList<QgsPoint> &points ) const
792
855
{
0 commit comments