Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: OGRSpatialReference translation from OziExplorer
5 : * georeferencing information.
6 : * Author: Andrey Kiselev, dron@ak4719.spb.edu
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2009, Andrey Kiselev <dron@ak4719.spb.edu>
10 : * Copyright (c) 2009-2012, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "cpl_port.h"
32 : #include "ogr_spatialref.h"
33 :
34 : #include <cstdlib>
35 : #include <cstring>
36 :
37 : #include "cpl_conv.h"
38 : #include "cpl_csv.h"
39 : #include "cpl_error.h"
40 : #include "cpl_string.h"
41 : #include "ogr_core.h"
42 : #include "ogr_srs_api.h"
43 :
44 : /************************************************************************/
45 : /* OSRImportFromOzi() */
46 : /************************************************************************/
47 :
48 : /**
49 : * Import coordinate system from OziExplorer projection definition.
50 : *
51 : * This function will import projection definition in style, used by
52 : * OziExplorer software.
53 : *
54 : * Note: another version of this function with a different signature existed
55 : * in GDAL 1.X.
56 : *
57 : * @param hSRS spatial reference object.
58 : * @param papszLines Map file lines. This is an array of strings containing
59 : * the whole OziExplorer .MAP file. The array is terminated by a NULL pointer.
60 : *
61 : * @return OGRERR_NONE on success or an error code in case of failure.
62 : *
63 : * @since OGR 2.0
64 : */
65 :
66 3 : OGRErr OSRImportFromOzi(OGRSpatialReferenceH hSRS,
67 : const char *const *papszLines)
68 :
69 : {
70 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromOzi", OGRERR_FAILURE);
71 :
72 3 : return OGRSpatialReference::FromHandle(hSRS)->importFromOzi(papszLines);
73 : }
74 :
75 : /************************************************************************/
76 : /* importFromOzi() */
77 : /************************************************************************/
78 :
79 : /**
80 : * Import coordinate system from OziExplorer projection definition.
81 : *
82 : * This method will import projection definition in style, used by
83 : * OziExplorer software.
84 : *
85 : * @param papszLines Map file lines. This is an array of strings containing
86 : * the whole OziExplorer .MAP file. The array is terminated by a NULL pointer.
87 : *
88 : * @return OGRERR_NONE on success or an error code in case of failure.
89 : *
90 : * @since OGR 1.10
91 : */
92 :
93 3 : OGRErr OGRSpatialReference::importFromOzi(const char *const *papszLines)
94 : {
95 : const char *pszDatum;
96 3 : const char *pszProj = nullptr;
97 3 : const char *pszProjParams = nullptr;
98 :
99 3 : Clear();
100 :
101 3 : const int nLines = CSLCount(papszLines);
102 3 : if (nLines < 5)
103 0 : return OGRERR_NOT_ENOUGH_DATA;
104 :
105 3 : pszDatum = papszLines[4];
106 :
107 9 : for (int iLine = 5; iLine < nLines; iLine++)
108 : {
109 6 : if (STARTS_WITH_CI(papszLines[iLine], "Map Projection"))
110 : {
111 3 : pszProj = papszLines[iLine];
112 : }
113 3 : else if (STARTS_WITH_CI(papszLines[iLine], "Projection Setup"))
114 : {
115 3 : pszProjParams = papszLines[iLine];
116 : }
117 : }
118 :
119 3 : if (!(pszDatum && pszProj && pszProjParams))
120 0 : return OGRERR_NOT_ENOUGH_DATA;
121 :
122 : /* -------------------------------------------------------------------- */
123 : /* Operate on the basis of the projection name. */
124 : /* -------------------------------------------------------------------- */
125 3 : char **papszProj = CSLTokenizeStringComplex(pszProj, ",", TRUE, TRUE);
126 : char **papszProjParams =
127 3 : CSLTokenizeStringComplex(pszProjParams, ",", TRUE, TRUE);
128 3 : char **papszDatum = nullptr;
129 :
130 3 : if (CSLCount(papszProj) < 2)
131 : {
132 0 : goto not_enough_data;
133 : }
134 :
135 3 : if (STARTS_WITH_CI(papszProj[1], "Latitude/Longitude"))
136 : {
137 : // Do nothing.
138 : }
139 1 : else if (STARTS_WITH_CI(papszProj[1], "Mercator"))
140 : {
141 0 : if (CSLCount(papszProjParams) < 6)
142 0 : goto not_enough_data;
143 0 : double dfScale = CPLAtof(papszProjParams[3]);
144 : // If unset, default to scale = 1.
145 0 : if (papszProjParams[3][0] == 0)
146 0 : dfScale = 1;
147 0 : SetMercator(CPLAtof(papszProjParams[1]), CPLAtof(papszProjParams[2]),
148 0 : dfScale, CPLAtof(papszProjParams[4]),
149 0 : CPLAtof(papszProjParams[5]));
150 : }
151 1 : else if (STARTS_WITH_CI(papszProj[1], "Transverse Mercator"))
152 : {
153 0 : if (CSLCount(papszProjParams) < 6)
154 0 : goto not_enough_data;
155 0 : SetTM(CPLAtof(papszProjParams[1]), CPLAtof(papszProjParams[2]),
156 0 : CPLAtof(papszProjParams[3]), CPLAtof(papszProjParams[4]),
157 0 : CPLAtof(papszProjParams[5]));
158 : }
159 1 : else if (STARTS_WITH_CI(papszProj[1], "Lambert Conformal Conic"))
160 : {
161 1 : if (CSLCount(papszProjParams) < 8)
162 0 : goto not_enough_data;
163 1 : SetLCC(CPLAtof(papszProjParams[6]), CPLAtof(papszProjParams[7]),
164 1 : CPLAtof(papszProjParams[1]), CPLAtof(papszProjParams[2]),
165 1 : CPLAtof(papszProjParams[4]), CPLAtof(papszProjParams[5]));
166 : }
167 0 : else if (STARTS_WITH_CI(papszProj[1], "Sinusoidal"))
168 : {
169 0 : if (CSLCount(papszProjParams) < 6)
170 0 : goto not_enough_data;
171 0 : SetSinusoidal(CPLAtof(papszProjParams[2]), CPLAtof(papszProjParams[4]),
172 0 : CPLAtof(papszProjParams[5]));
173 : }
174 0 : else if (STARTS_WITH_CI(papszProj[1], "Albers Equal Area"))
175 : {
176 0 : if (CSLCount(papszProjParams) < 8)
177 0 : goto not_enough_data;
178 0 : SetACEA(CPLAtof(papszProjParams[6]), CPLAtof(papszProjParams[7]),
179 0 : CPLAtof(papszProjParams[1]), CPLAtof(papszProjParams[2]),
180 0 : CPLAtof(papszProjParams[4]), CPLAtof(papszProjParams[5]));
181 : }
182 0 : else if (STARTS_WITH_CI(papszProj[1],
183 0 : "(UTM) Universal Transverse Mercator") &&
184 : nLines > 5)
185 : {
186 : // Look for the UTM zone in the calibration point data.
187 0 : int iLine = 5; // Used after for.
188 0 : for (; iLine < nLines; iLine++)
189 : {
190 0 : if (STARTS_WITH_CI(papszLines[iLine], "Point"))
191 : {
192 0 : char **papszTok = CSLTokenizeString2(papszLines[iLine], ",",
193 : CSLT_ALLOWEMPTYTOKENS |
194 : CSLT_STRIPLEADSPACES |
195 : CSLT_STRIPENDSPACES);
196 0 : if (CSLCount(papszTok) < 17 || EQUAL(papszTok[2], "") ||
197 0 : EQUAL(papszTok[13], "") || EQUAL(papszTok[14], "") ||
198 0 : EQUAL(papszTok[15], "") || EQUAL(papszTok[16], ""))
199 : {
200 0 : CSLDestroy(papszTok);
201 0 : continue;
202 : }
203 0 : SetUTM(atoi(papszTok[13]), EQUAL(papszTok[16], "N"));
204 0 : CSLDestroy(papszTok);
205 0 : break;
206 : }
207 : }
208 0 : if (iLine == nLines) // Try to guess the UTM zone.
209 : {
210 0 : float fMinLongitude = 1000.0f;
211 0 : float fMaxLongitude = -1000.0f;
212 0 : float fMinLatitude = 1000.0f;
213 0 : float fMaxLatitude = -1000.0f;
214 0 : bool bFoundMMPLL = false;
215 0 : for (iLine = 5; iLine < nLines; iLine++)
216 : {
217 0 : if (STARTS_WITH_CI(papszLines[iLine], "MMPLL"))
218 : {
219 0 : char **papszTok = CSLTokenizeString2(
220 0 : papszLines[iLine], ",",
221 : CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
222 : CSLT_STRIPENDSPACES);
223 0 : if (CSLCount(papszTok) < 4)
224 : {
225 0 : CSLDestroy(papszTok);
226 0 : continue;
227 : }
228 : const float fLongitude =
229 0 : static_cast<float>(CPLAtofM(papszTok[2]));
230 : const float fLatitude =
231 0 : static_cast<float>(CPLAtofM(papszTok[3]));
232 0 : CSLDestroy(papszTok);
233 :
234 0 : bFoundMMPLL = true;
235 :
236 0 : if (fMinLongitude > fLongitude)
237 0 : fMinLongitude = fLongitude;
238 0 : if (fMaxLongitude < fLongitude)
239 0 : fMaxLongitude = fLongitude;
240 0 : if (fMinLatitude > fLatitude)
241 0 : fMinLatitude = fLatitude;
242 0 : if (fMaxLatitude < fLatitude)
243 0 : fMaxLatitude = fLatitude;
244 : }
245 : }
246 0 : const float fMedianLatitude = (fMinLatitude + fMaxLatitude) / 2;
247 0 : const float fMedianLongitude = (fMinLongitude + fMaxLongitude) / 2;
248 0 : if (bFoundMMPLL && fMaxLatitude <= 90)
249 : {
250 0 : int nUtmZone = 0;
251 0 : if (fMedianLatitude >= 56 && fMedianLatitude <= 64 &&
252 0 : fMedianLongitude >= 3 && fMedianLongitude <= 12)
253 0 : nUtmZone = 32; // Norway exception.
254 0 : else if (fMedianLatitude >= 72 && fMedianLatitude <= 84 &&
255 0 : fMedianLongitude >= 0 && fMedianLongitude <= 42)
256 : // Svalbard exception.
257 0 : nUtmZone =
258 0 : static_cast<int>((fMedianLongitude + 3) / 12) * 2 + 31;
259 : else
260 0 : nUtmZone =
261 0 : static_cast<int>((fMedianLongitude + 180) / 6) + 1;
262 0 : SetUTM(nUtmZone, fMedianLatitude >= 0);
263 : }
264 : else
265 : {
266 0 : CPLDebug("OSR_Ozi", "UTM Zone not found");
267 : }
268 0 : }
269 : }
270 0 : else if (STARTS_WITH_CI(papszProj[1], "(I) France Zone I"))
271 : {
272 0 : SetLCC1SP(49.5, 2.337229167, 0.99987734, 600000, 1200000);
273 : }
274 0 : else if (STARTS_WITH_CI(papszProj[1], "(II) France Zone II"))
275 : {
276 0 : SetLCC1SP(46.8, 2.337229167, 0.99987742, 600000, 2200000);
277 : }
278 0 : else if (STARTS_WITH_CI(papszProj[1], "(III) France Zone III"))
279 : {
280 0 : SetLCC1SP(44.1, 2.337229167, 0.99987750, 600000, 3200000);
281 : }
282 0 : else if (STARTS_WITH_CI(papszProj[1], "(IV) France Zone IV"))
283 : {
284 0 : SetLCC1SP(42.165, 2.337229167, 0.99994471, 234.358, 4185861.369);
285 : }
286 :
287 : /*
288 : * Note: The following projections have not been implemented yet
289 : *
290 : */
291 :
292 : /*
293 : else if( STARTS_WITH_CI(papszProj[1], "(BNG) British National Grid") )
294 : {
295 : }
296 : else if( STARTS_WITH_CI(papszProj[1], "(IG) Irish Grid") )
297 : {
298 : }
299 :
300 : else if( STARTS_WITH_CI(papszProj[1], "(NZG) New Zealand Grid") )
301 : {
302 : }
303 : else if( STARTS_WITH_CI(papszProj[1], "(NZTM2) New Zealand TM 2000") )
304 : {
305 : }
306 : else if( STARTS_WITH_CI(papszProj[1], "(SG) Swedish Grid") )
307 : {
308 : }
309 : else if( STARTS_WITH_CI(papszProj[1], "(SUI) Swiss Grid") )
310 : {
311 : }
312 : else if( STARTS_WITH_CI(papszProj[1], "(A)Lambert Azimuthual Equal
313 : Area") )
314 : {
315 : }
316 : else if( STARTS_WITH_CI(papszProj[1], "(EQC) Equidistant Conic") )
317 : {
318 : }
319 : else if( STARTS_WITH_CI(papszProj[1], "Polyconic (American)") )
320 : {
321 : }
322 : else if( STARTS_WITH_CI(papszProj[1], "Van Der Grinten") )
323 : {
324 : }
325 : else if( STARTS_WITH_CI(papszProj[1], "Vertical Near-Sided Perspective")
326 : )
327 : {
328 : }
329 : else if( STARTS_WITH_CI(papszProj[1], "(WIV) Wagner IV") )
330 : {
331 : }
332 : else if( STARTS_WITH_CI(papszProj[1], "Bonne") )
333 : {
334 : }
335 : else if( STARTS_WITH_CI(papszProj[1],
336 : "(MT0) Montana State Plane Zone 2500") )
337 : {
338 : }
339 : else if( STARTS_WITH_CI(papszProj[1], "ITA1) Italy Grid Zone 1") )
340 : {
341 : }
342 : else if( STARTS_WITH_CI(papszProj[1], "ITA2) Italy Grid Zone 2") )
343 : {
344 : }
345 : else if( STARTS_WITH_CI(papszProj[1],
346 : "(VICMAP-TM) Victoria Aust.(pseudo AMG)") )
347 : {
348 : }
349 : else if( STARTS_WITH_CI(papszProj[1], "VICGRID) Victoria Australia") )
350 : {
351 : }
352 : else if( STARTS_WITH_CI(papszProj[1],
353 : "(VG94) VICGRID94 Victoria Australia") )
354 : {
355 : }
356 : else if( STARTS_WITH_CI(papszProj[1], "Gnomonic") )
357 : {
358 : }
359 : */
360 : else
361 : {
362 0 : CPLDebug("OSR_Ozi", "Unsupported projection: \"%s\"", papszProj[1]);
363 0 : SetLocalCS(
364 0 : CPLString().Printf(R"("Ozi" projection "%s")", papszProj[1]));
365 : }
366 :
367 : /* -------------------------------------------------------------------- */
368 : /* Try to translate the datum/spheroid. */
369 : /* -------------------------------------------------------------------- */
370 3 : papszDatum = CSLTokenizeString2(
371 : pszDatum, ",",
372 : CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
373 3 : if (papszDatum == nullptr)
374 0 : goto not_enough_data;
375 :
376 3 : if (!IsLocal())
377 : {
378 : /* --------------------------------------------------------------------
379 : */
380 : /* Verify that we can find the CSV file containing the datums */
381 : /* --------------------------------------------------------------------
382 : */
383 3 : if (CSVScanFileByName(CSVFilename("ozi_datum.csv"), "EPSG_DATUM_CODE",
384 3 : "4326", CC_Integer) == nullptr)
385 : {
386 0 : CPLError(CE_Failure, CPLE_OpenFailed,
387 : "Unable to open OZI support file %s. "
388 : "Try setting the GDAL_DATA environment variable to point "
389 : "to the directory containing OZI csv files.",
390 : CSVFilename("ozi_datum.csv"));
391 0 : goto other_error;
392 : }
393 :
394 : /* --------------------------------------------------------------------
395 : */
396 : /* Search for matching datum */
397 : /* --------------------------------------------------------------------
398 : */
399 3 : const char *pszOziDatum = CSVFilename("ozi_datum.csv");
400 : CPLString osDName = CSVGetField(pszOziDatum, "NAME", papszDatum[0],
401 3 : CC_ApproxString, "NAME");
402 3 : if (osDName.empty())
403 : {
404 0 : CPLError(CE_Failure, CPLE_AppDefined,
405 : "Failed to find datum %s in ozi_datum.csv.",
406 : papszDatum[0]);
407 0 : goto other_error;
408 : }
409 :
410 : const int nDatumCode =
411 3 : atoi(CSVGetField(pszOziDatum, "NAME", papszDatum[0],
412 : CC_ApproxString, "EPSG_DATUM_CODE"));
413 :
414 3 : if (nDatumCode > 0) // There is a matching EPSG code
415 : {
416 4 : OGRSpatialReference oGCS;
417 2 : oGCS.importFromEPSG(nDatumCode);
418 2 : CopyGeogCSFrom(&oGCS);
419 : }
420 : else // We use the parameters from the CSV files
421 : {
422 : CPLString osEllipseCode =
423 : CSVGetField(pszOziDatum, "NAME", papszDatum[0], CC_ApproxString,
424 1 : "ELLIPSOID_CODE");
425 1 : const double dfDeltaX = CPLAtof(CSVGetField(
426 : pszOziDatum, "NAME", papszDatum[0], CC_ApproxString, "DELTAX"));
427 1 : const double dfDeltaY = CPLAtof(CSVGetField(
428 : pszOziDatum, "NAME", papszDatum[0], CC_ApproxString, "DELTAY"));
429 1 : const double dfDeltaZ = CPLAtof(CSVGetField(
430 : pszOziDatum, "NAME", papszDatum[0], CC_ApproxString, "DELTAZ"));
431 :
432 : /* --------------------------------------------------------------------
433 : */
434 : /* Verify that we can find the CSV file containing the
435 : * ellipsoids. */
436 : /* --------------------------------------------------------------------
437 : */
438 1 : if (CSVScanFileByName(CSVFilename("ozi_ellips.csv"),
439 : "ELLIPSOID_CODE", "20",
440 1 : CC_Integer) == nullptr)
441 : {
442 0 : CPLError(
443 : CE_Failure, CPLE_OpenFailed,
444 : "Unable to open OZI support file %s. "
445 : "Try setting the GDAL_DATA environment variable to point "
446 : "to the directory containing OZI csv files.",
447 : CSVFilename("ozi_ellips.csv"));
448 0 : goto other_error;
449 : }
450 :
451 : /* --------------------------------------------------------------------
452 : */
453 : /* Lookup the ellipse code. */
454 : /* --------------------------------------------------------------------
455 : */
456 1 : const char *pszOziEllipse = CSVFilename("ozi_ellips.csv");
457 :
458 : CPLString osEName =
459 : CSVGetField(pszOziEllipse, "ELLIPSOID_CODE", osEllipseCode,
460 1 : CC_ApproxString, "NAME");
461 1 : if (osEName.empty())
462 : {
463 0 : CPLError(CE_Failure, CPLE_AppDefined,
464 : "Failed to find ellipsoid %s in ozi_ellips.csv.",
465 : osEllipseCode.c_str());
466 0 : goto other_error;
467 : }
468 :
469 : const double dfA =
470 1 : CPLAtof(CSVGetField(pszOziEllipse, "ELLIPSOID_CODE",
471 : osEllipseCode, CC_ApproxString, "A"));
472 : const double dfInvF =
473 1 : CPLAtof(CSVGetField(pszOziEllipse, "ELLIPSOID_CODE",
474 : osEllipseCode, CC_ApproxString, "INVF"));
475 :
476 : /* --------------------------------------------------------------------
477 : */
478 : /* Create geographic coordinate system. */
479 : /* --------------------------------------------------------------------
480 : */
481 1 : SetGeogCS(osDName, osDName, osEName, dfA, dfInvF);
482 1 : SetTOWGS84(dfDeltaX, dfDeltaY, dfDeltaZ);
483 : }
484 : }
485 :
486 : /* -------------------------------------------------------------------- */
487 : /* Grid units translation */
488 : /* -------------------------------------------------------------------- */
489 3 : if (IsLocal() || IsProjected())
490 1 : SetLinearUnits(SRS_UL_METER, 1.0);
491 :
492 3 : CSLDestroy(papszProj);
493 3 : CSLDestroy(papszProjParams);
494 3 : CSLDestroy(papszDatum);
495 :
496 3 : return OGRERR_NONE;
497 :
498 0 : not_enough_data:
499 :
500 0 : CSLDestroy(papszProj);
501 0 : CSLDestroy(papszProjParams);
502 0 : CSLDestroy(papszDatum);
503 :
504 0 : return OGRERR_NOT_ENOUGH_DATA;
505 :
506 0 : other_error:
507 :
508 0 : CSLDestroy(papszProj);
509 0 : CSLDestroy(papszProjParams);
510 0 : CSLDestroy(papszDatum);
511 :
512 0 : return OGRERR_FAILURE;
513 : }
|