Coverage Report

Created: 2025-06-13 06:18

/src/gdal/ogr/ogrmitabspatialref.cpp
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: MIT
2
// Copyright 1999-2003, Daniel Morissette
3
// Copyright (c) 1999-2001, Frank Warmerdam
4
// Implementation translation between MIF CoordSys format, and
5
// and OGRSpatialRef format.
6
7
/*! @cond Doxygen_Suppress */
8
9
#include "ogrmitabspatialref.h"
10
11
#include "cpl_port.h"
12
13
#include <algorithm>
14
#include <cmath>
15
#include <cstddef>
16
#include <cstdio>
17
#include <cstdlib>
18
#include <cstring>
19
20
#include "cpl_conv.h"
21
#include "cpl_error.h"
22
#include "cpl_string.h"
23
#include "ogr_spatialref.h"
24
#include "ogr_srs_api.h"
25
26
typedef struct
27
{
28
    int nDatumEPSGCode;
29
    int nMapInfoDatumID;
30
    const char *pszOGCDatumName;
31
    int nEllipsoid;
32
    double dfShiftX;
33
    double dfShiftY;
34
    double dfShiftZ;
35
    double dfDatumParm0; /* RotX */
36
    double dfDatumParm1; /* RotY */
37
    double dfDatumParm2; /* RotZ */
38
    double dfDatumParm3; /* Scale Factor */
39
    double dfDatumParm4; /* Prime Meridian */
40
} MapInfoDatumInfo;
41
42
typedef struct
43
{
44
    int nMapInfoId;
45
    const char *pszMapinfoName;
46
    double dfA;             /* semi major axis in meters */
47
    double dfInvFlattening; /* Inverse flattening */
48
} MapInfoSpheroidInfo;
49
50
/**********************************************************************
51
 * MapInfo Units string to numeric ID conversion
52
 **********************************************************************/
53
typedef struct
54
{
55
    int nUnitId;
56
    const char *pszAbbrev;
57
} MapInfoUnitsInfo;
58
59
static const MapInfoUnitsInfo gasUnitsList[] = {
60
    {0, "mi"},        {1, "km"},          {2, "in"},  {3, "ft"},
61
    {4, "yd"},        {5, "mm"},          {6, "cm"},  {7, "m"},
62
    {8, "survey ft"}, {8, "survey foot"},  // alternate
63
    {13, nullptr},    {9, "nmi"},         {30, "li"}, {31, "ch"},
64
    {32, "rd"},       {-1, nullptr}};
65
66
/**********************************************************************
67
 *                       TABUnitIdToString()
68
 *
69
 * Return the MIF units name for specified units id.
70
 * Return "" if no match found.
71
 *
72
 * The returned string should not be freed by the caller.
73
 **********************************************************************/
74
static const char *TABUnitIdToString(int nId)
75
0
{
76
0
    const MapInfoUnitsInfo *psList = gasUnitsList;
77
78
0
    while (psList->nUnitId != -1)
79
0
    {
80
0
        if (psList->nUnitId == nId)
81
0
            return psList->pszAbbrev;
82
0
        psList++;
83
0
    }
84
85
0
    return "";
86
0
}
87
88
/**********************************************************************
89
 *                       TABUnitIdFromString()
90
 *
91
 * Return the units ID for specified MIF units name
92
 *
93
 * Returns -1 if no match found.
94
 **********************************************************************/
95
static int TABUnitIdFromString(const char *pszName)
96
0
{
97
0
    if (pszName == nullptr)
98
0
        return 13;
99
100
0
    const MapInfoUnitsInfo *psList = gasUnitsList;
101
102
0
    while (psList->nUnitId != -1)
103
0
    {
104
0
        if (psList->pszAbbrev != nullptr && EQUAL(psList->pszAbbrev, pszName))
105
0
            return psList->nUnitId;
106
0
        psList++;
107
0
    }
108
109
0
    return -1;
110
0
}
111
112
/* -------------------------------------------------------------------- */
113
/*      This table was automatically generated by doing translations    */
114
/*      between mif and tab for each datum, and extracting the          */
115
/*      parameters from the tab file.  The EPSG codes and OGC names     */
116
/*      were added afterwards and may be incomplete or inaccurate.       */
117
/* -------------------------------------------------------------------- */
118
119
extern const MapInfoDatumInfo asDatumInfoList[];
120
extern const MapInfoSpheroidInfo asSpheroidInfoList[];
121
122
/* EPSG code, MapInfo datum ID (or 9999), OGC Name, datum parameters... */
123
const MapInfoDatumInfo asDatumInfoList[] = {
124
125
    {0, 104, "WGS_1984", 28, 0, 0, 0, 0, 0, 0, 0, 0},
126
    {6269, 74, "North_American_Datum_1983", 0, 0, 0, 0, 0, 0, 0, 0, 0},
127
128
    {0, 0, "", 29, 0, 0, 0, 0, 0, 0, 0, 0},  // Datum ignore
129
130
    {6201, 1, "Adindan", 6, -162, -12, 206, 0, 0, 0, 0, 0},
131
    {6205, 2, "Afgooye", 3, -43, -163, 45, 0, 0, 0, 0, 0},
132
    {6204, 3, "Ain_el_Abd_1970", 4, -150, -251, -2, 0, 0, 0, 0, 0},
133
    {0, 4, "Anna_1_Astro_1965", 2, -491, -22, 435, 0, 0, 0, 0, 0},
134
    {6209, 5, "Arc_1950", 15, -143, -90, -294, 0, 0, 0, 0, 0},
135
    {6210, 6, "Arc_1960", 6, -160, -8, -300, 0, 0, 0, 0, 0},
136
    {0, 7, "Ascension_Islands", 4, -207, 107, 52, 0, 0, 0, 0, 0},
137
    {0, 8, "Astro_Beacon_E", 4, 145, 75, -272, 0, 0, 0, 0, 0},
138
    {0, 9, "Astro_B4_Sorol_Atoll", 4, 114, -116, -333, 0, 0, 0, 0, 0},
139
    {0, 10, "Astro_Dos_71_4", 4, -320, 550, -494, 0, 0, 0, 0, 0},
140
    {0, 11, "Astronomic_Station_1952", 4, 124, -234, -25, 0, 0, 0, 0, 0},
141
    {6202, 12, "Australian_Geodetic_Datum_66", 2, -133, -48, 148, 0, 0, 0, 0,
142
     0},
143
    {6203, 13, "Australian_Geodetic_Datum_84", 2, -134, -48, 149, 0, 0, 0, 0,
144
     0},
145
    {0, 14, "Bellevue_Ign", 4, -127, -769, 472, 0, 0, 0, 0, 0},
146
    {6216, 15, "Bermuda_1957", 7, -73, 213, 296, 0, 0, 0, 0, 0},
147
    {6218, 16, "Bogota", 4, 307, 304, -318, 0, 0, 0, 0, 0},
148
    {6221, 17, "Campo_Inchauspe", 4, -148, 136, 90, 0, 0, 0, 0, 0},
149
    {0, 18, "Canton_Astro_1966", 4, 298, -304, -375, 0, 0, 0, 0, 0},
150
    {6222, 19, "Cape", 6, -136, -108, -292, 0, 0, 0, 0, 0},
151
    {6717, 20, "Cape_Canaveral", 7, -2, 150, 181, 0, 0, 0, 0, 0},
152
    {6223, 21, "Carthage", 6, -263, 6, 431, 0, 0, 0, 0, 0},
153
    {6672, 22, "Chatham_1971", 4, 175, -38, 113, 0, 0, 0, 0, 0},
154
    {6224, 23, "Chua", 4, -134, 229, -29, 0, 0, 0, 0, 0},
155
    {6225, 24, "Corrego_Alegre", 4, -206, 172, -6, 0, 0, 0, 0, 0},
156
    {6211, 25, "Batavia", 10, -377, 681, -50, 0, 0, 0, 0, 0},
157
    {0, 26, "Dos_1968", 4, 230, -199, -752, 0, 0, 0, 0, 0},
158
    {6719, 27, "Easter_Island_1967", 4, 211, 147, 111, 0, 0, 0, 0, 0},
159
    {6230, 28, "European_Datum_1950", 4, -87, -98, -121, 0, 0, 0, 0, 0},
160
    {6668, 29, "European_Datum_1979", 4, -86, -98, -119, 0, 0, 0, 0, 0},
161
    {6233, 30, "Gandajika_1970", 4, -133, -321, 50, 0, 0, 0, 0, 0},
162
    {6272, 31, "New_Zealand_GD49", 4, 84, -22, 209, 0, 0, 0, 0, 0},
163
    {6272, 31, "New_Zealand_Geodetic_Datum_1949", 4, 84, -22, 209, 0, 0, 0, 0,
164
     0},
165
    {0, 32, "GRS_67", 21, 0, 0, 0, 0, 0, 0, 0, 0},
166
    {0, 33, "GRS_80", 0, 0, 0, 0, 0, 0, 0, 0, 0},
167
    {6171, 33, "Reseau_Geodesique_Francais_1993", 0, 0, 0, 0, 0, 0, 0, 0, 0},
168
    {6619, 33, "SWEREF99", 0, 0, 0, 0, 0, 0, 0, 0, 0},
169
    {6675, 34, "Guam_1963", 7, -100, -248, 259, 0, 0, 0, 0, 0},
170
    {0, 35, "Gux_1_Astro", 4, 252, -209, -751, 0, 0, 0, 0, 0},
171
    {6254, 36, "Hito_XVIII_1963", 4, 16, 196, 93, 0, 0, 0, 0, 0},
172
    {6658, 37, "Hjorsey_1955", 4, -73, 46, -86, 0, 0, 0, 0, 0},
173
    {6738, 38, "Hong_Kong_1963", 4, -156, -271, -189, 0, 0, 0, 0, 0},
174
    {6236, 39, "Hu_Tzu_Shan", 4, -634, -549, -201, 0, 0, 0, 0, 0},
175
    {0, 40, "Indian_Thailand_Vietnam", 11, 214, 836, 303, 0, 0, 0, 0, 0},
176
    {0, 41, "Indian_Bangladesh", 11, 289, 734, 257, 0, 0, 0, 0, 0},
177
    {6299, 42, "Ireland_1965", 13, 506, -122, 611, 0, 0, 0, 0, 0},
178
    {0, 43, "ISTS_073_Astro_1969", 4, 208, -435, -229, 0, 0, 0, 0, 0},
179
    {6725, 44, "Johnston_Island_1961", 4, 191, -77, -204, 0, 0, 0, 0, 0},
180
    {6244, 45, "Kandawala", 11, -97, 787, 86, 0, 0, 0, 0, 0},
181
    {0, 46, "Kerguyelen_Island", 4, 145, -187, 103, 0, 0, 0, 0, 0},
182
    {6245, 47, "Kertau", 17, -11, 851, 5, 0, 0, 0, 0, 0},
183
    {0, 48, "L_C_5_Astro", 7, 42, 124, 147, 0, 0, 0, 0, 0},
184
    {6251, 49, "Liberia_1964", 6, -90, 40, 88, 0, 0, 0, 0, 0},
185
    {0, 50, "Luzon_Phillippines", 7, -133, -77, -51, 0, 0, 0, 0, 0},
186
    {0, 51, "Luzon_Mindanao_Island", 7, -133, -79, -72, 0, 0, 0, 0, 0},
187
    {6256, 52, "Mahe_1971", 6, 41, -220, -134, 0, 0, 0, 0, 0},
188
    {0, 53, "Marco_Astro", 4, -289, -124, 60, 0, 0, 0, 0, 0},
189
    {6262, 54, "Massawa", 10, 639, 405, 60, 0, 0, 0, 0, 0},
190
    {6261, 55, "Merchich", 16, 31, 146, 47, 0, 0, 0, 0, 0},
191
    {0, 56, "Midway_Astro_1961", 4, 912, -58, 1227, 0, 0, 0, 0, 0},
192
    {6263, 57, "Minna", 6, -92, -93, 122, 0, 0, 0, 0, 0},
193
    {0, 58, "Nahrwan_Masirah_Island", 6, -247, -148, 369, 0, 0, 0, 0, 0},
194
    {0, 59, "Nahrwan_Un_Arab_Emirates", 6, -249, -156, 381, 0, 0, 0, 0, 0},
195
    {0, 60, "Nahrwan_Saudi_Arabia", 6, -231, -196, 482, 0, 0, 0, 0, 0},
196
    {6271, 61, "Naparima_1972", 4, -2, 374, 172, 0, 0, 0, 0, 0},
197
    {6267, 62, "NAD_1927", 7, -8, 160, 176, 0, 0, 0, 0, 0},
198
    {6267, 62, "North_American_Datum_1927", 7, -8, 160, 176, 0, 0, 0, 0, 0},
199
    {0, 63, "NAD_27_Alaska", 7, -5, 135, 172, 0, 0, 0, 0, 0},
200
    {0, 64, "NAD_27_Bahamas", 7, -4, 154, 178, 0, 0, 0, 0, 0},
201
    {0, 65, "NAD_27_San_Salvador", 7, 1, 140, 165, 0, 0, 0, 0, 0},
202
    {0, 66, "NAD_27_Canada", 7, -10, 158, 187, 0, 0, 0, 0, 0},
203
    {0, 67, "NAD_27_Canal_Zone", 7, 0, 125, 201, 0, 0, 0, 0, 0},
204
    {0, 68, "NAD_27_Caribbean", 7, -7, 152, 178, 0, 0, 0, 0, 0},
205
    {0, 69, "NAD_27_Central_America", 7, 0, 125, 194, 0, 0, 0, 0, 0},
206
    {0, 70, "NAD_27_Cuba", 7, -9, 152, 178, 0, 0, 0, 0, 0},
207
    {0, 71, "NAD_27_Greenland", 7, 11, 114, 195, 0, 0, 0, 0, 0},
208
    {0, 72, "NAD_27_Mexico", 7, -12, 130, 190, 0, 0, 0, 0, 0},
209
    {0, 73, "NAD_27_Michigan", 8, -8, 160, 176, 0, 0, 0, 0, 0},
210
    {0, 75, "Observatorio_1966", 4, -425, -169, 81, 0, 0, 0, 0, 0},
211
    {0, 76, "Old_Egyptian", 22, -130, 110, -13, 0, 0, 0, 0, 0},
212
    {6135, 77, "Old_Hawaiian", 7, 61, -285, -181, 0, 0, 0, 0, 0},
213
    {0, 78, "Oman", 6, -346, -1, 224, 0, 0, 0, 0, 0},
214
    {6277, 79, "OSGB_1936", 9, 375, -111, 431, 0, 0, 0, 0, 0},
215
    {0, 80, "Pico_De_Las_Nieves", 4, -307, -92, 127, 0, 0, 0, 0, 0},
216
    {6729, 81, "Pitcairn_Astro_1967", 4, 185, 165, 42, 0, 0, 0, 0, 0},
217
    {6248, 82, "Provisional_South_American", 4, -288, 175, -376, 0, 0, 0, 0, 0},
218
    {6139, 83, "Puerto_Rico", 7, 11, 72, -101, 0, 0, 0, 0, 0},
219
    {6614, 84, "Qatar_National", 4, -128, -283, 22, 0, 0, 0, 0, 0},
220
    {6287, 85, "Qornoq", 4, 164, 138, -189, 0, 0, 0, 0, 0},
221
    {6627, 86, "Reunion", 4, 94, -948, -1262, 0, 0, 0, 0, 0},
222
    {6265, 87, "Monte_Mario", 4, -225, -65, 9, 0, 0, 0, 0, 0},
223
    {0, 88, "Santo_Dos", 4, 170, 42, 84, 0, 0, 0, 0, 0},
224
    {0, 89, "Sao_Braz", 4, -203, 141, 53, 0, 0, 0, 0, 0},
225
    {6292, 90, "Sapper_Hill_1943", 4, -355, 16, 74, 0, 0, 0, 0, 0},
226
    {6293, 91, "Schwarzeck", 14, 616, 97, -251, 0, 0, 0, 0, 0},
227
    {6618, 92, "South_American_Datum_1969", 24, -57, 1, -41, 0, 0, 0, 0, 0},
228
    {0, 93, "South_Asia", 19, 7, -10, -26, 0, 0, 0, 0, 0},
229
    {0, 94, "Southeast_Base", 4, -499, -249, 314, 0, 0, 0, 0, 0},
230
    {0, 95, "Southwest_Base", 4, -104, 167, -38, 0, 0, 0, 0, 0},
231
    {6298, 96, "Timbalai_1948", 11, -689, 691, -46, 0, 0, 0, 0, 0},
232
    {6301, 97, "Tokyo", 10, -128, 481, 664, 0, 0, 0, 0, 0},
233
    {0, 98, "Tristan_Astro_1968", 4, -632, 438, -609, 0, 0, 0, 0, 0},
234
    {6731, 99, "Viti_Levu_1916", 6, 51, 391, -36, 0, 0, 0, 0, 0},
235
    {0, 100, "Wake_Entiwetok_1960", 23, 101, 52, -39, 0, 0, 0, 0, 0},
236
    {0, 101, "WGS_60", 26, 0, 0, 0, 0, 0, 0, 0, 0},
237
    {6760, 102, "WGS_66", 27, 0, 0, 0, 0, 0, 0, 0, 0},
238
    {6322, 103, "WGS_1972", 1, 0, 8, 10, 0, 0, 0, 0, 0},
239
    {6322, 103, "World_Geodetic_System_1972", 1, 0, 8, 10, 0, 0, 0, 0, 0},
240
    {6326, 104, "WGS_1984", 28, 0, 0, 0, 0, 0, 0, 0, 0},
241
    {6309, 105, "Yacare", 4, -155, 171, 37, 0, 0, 0, 0, 0},
242
    {6311, 106, "Zanderij", 4, -265, 120, -358, 0, 0, 0, 0, 0},
243
    {0, 107, "NTF", 30, -168, -60, 320, 0, 0, 0, 0, 0},
244
    {6231, 108, "European_Datum_1987", 4, -83, -96, -113, 0, 0, 0, 0, 0},
245
    {0, 109, "Netherlands_Bessel", 10, 593, 26, 478, 0, 0, 0, 0, 0},
246
    {0, 110, "Belgium_Hayford", 4, 81, 120, 129, 0, 0, 0, 0, 0},
247
    {0, 111, "NWGL_10", 1, -1, 15, 1, 0, 0, 0, 0, 0},
248
    {6124, 112, "Rikets_koordinatsystem_1990", 10, 498, -36, 568, 0, 0, 0, 0,
249
     0},
250
    {0, 113, "Lisboa_DLX", 4, -303, -62, 105, 0, 0, 0, 0, 0},
251
    {0, 114, "Melrica_1973_D73", 4, -223, 110, 37, 0, 0, 0, 0, 0},
252
    {6258, 115, "European_Terrestrial_Reference_System_1989", 0, 0, 0, 0, 0, 0,
253
     0, 0, 0},
254
    {6258, 115, "Euref_89", 0, 0, 0, 0, 0, 0, 0, 0, 0},
255
    {6180, 115, "Estonia_1997", 0, 0, 0, 0, 0, 0, 0, 0, 0},
256
    {6283, 116, "GDA94", 0, 0, 0, 0, 0, 0, 0, 0, 0},
257
    {6283, 116, "Geocentric_Datum_of_Australia_1994", 0, 0, 0, 0, 0, 0, 0, 0,
258
     0},
259
    {6167, 117, "NZGD2000", 0, 0, 0, 0, 0, 0, 0, 0, 0},
260
    {6167, 117, "New_Zealand_Geodetic_Datum_2000", 0, 0, 0, 0, 0, 0, 0, 0, 0},
261
    {6169, 118, "America_Samoa", 7, -115, 118, 426, 0, 0, 0, 0, 0},
262
    {0, 119, "Antigua_Astro_1965", 6, -270, 13, 62, 0, 0, 0, 0, 0},
263
    {6713, 120, "Ayabelle_Lighthouse", 6, -79, -129, 145, 0, 0, 0, 0, 0},
264
    {6219, 121, "Bukit_Rimpah", 10, -384, 664, -48, 0, 0, 0, 0, 0},
265
    {0, 122, "Estonia_1937", 10, 374, 150, 588, 0, 0, 0, 0, 0},
266
    {6155, 123, "Dabola", 6, -83, 37, 124, 0, 0, 0, 0, 0},
267
    {6736, 124, "Deception_Island", 6, 260, 12, -147, 0, 0, 0, 0, 0},
268
    {0, 125, "Fort_Thomas_1955", 6, -7, 215, 225, 0, 0, 0, 0, 0},
269
    {0, 126, "Graciosa_base_1948", 4, -104, 167, -38, 0, 0, 0, 0, 0},
270
    {6255, 127, "Herat_North", 4, -333, -222, 114, 0, 0, 0, 0, 0},
271
    {0, 128, "Hermanns_Kogel", 10, 682, -203, 480, 0, 0, 0, 0, 0},
272
    {6240, 129, "Indian", 50, 283, 682, 231, 0, 0, 0, 0, 0},
273
    {6239, 130, "Indian_1954", 11, 217, 823, 299, 0, 0, 0, 0, 0},
274
    {6131, 131, "Indian_1960", 11, 198, 881, 317, 0, 0, 0, 0, 0},
275
    {6240, 132, "Indian_1975", 11, 210, 814, 289, 0, 0, 0, 0, 0},
276
    {6238, 133, "Indonesian_Datum_1974", 4, -24, -15, 5, 0, 0, 0, 0, 0},
277
    {0, 134, "ISTS061_Astro_1968", 4, -794, 119, -298, 0, 0, 0, 0, 0},
278
    {0, 135, "Kusaie_Astro_1951", 4, 647, 1777, -1124, 0, 0, 0, 0, 0},
279
    {6250, 136, "Leigon", 6, -130, 29, 364, 0, 0, 0, 0, 0},
280
    {0, 137, "Montserrat_Astro_1958", 6, 174, 359, 365, 0, 0, 0, 0, 0},
281
    {6266, 138, "Mporaloko", 6, -74, -130, 42, 0, 0, 0, 0, 0},
282
    {0, 139, "North_Sahara_1959", 6, -186, -93, 310, 0, 0, 0, 0, 0},
283
    {0, 140, "Observatorio_Met_1939", 4, -425, -169, 81, 0, 0, 0, 0, 0},
284
    {6620, 141, "Point_58", 6, -106, -129, 165, 0, 0, 0, 0, 0},
285
    {6282, 142, "Pointe_Noire", 6, -148, 51, -291, 0, 0, 0, 0, 0},
286
    {6615, 143, "Porto_Santo_1936", 4, -499, -249, 314, 0, 0, 0, 0, 0},
287
    {6616, 144, "Selvagem_Grande_1938", 4, -289, -124, 60, 0, 0, 0, 0, 0},
288
    {0, 145, "Sierra_Leone_1960", 6, -88, 4, 101, 0, 0, 0, 0, 0},
289
    {6156, 146, "S_JTSK_Ferro", 10, 589, 76, 480, 0, 0, 0, 0, 0},
290
    {6297, 147, "Tananarive_1925", 4, -189, -242, -91, 0, 0, 0, 0, 0},
291
    {6811, 148, "Voirol_1874", 6, -73, -247, 227, 0, 0, 0, 0, 0},
292
    {0, 149, "Virol_1960", 6, -123, -206, 219, 0, 0, 0, 0, 0},
293
    {6148, 150, "Hartebeesthoek94", 28, 0, 0, 0, 0, 0, 0, 0, 0},
294
    {6122, 151, "ATS77", 51, 0, 0, 0, 0, 0, 0, 0, 0},
295
    {6612, 152, "JGD2000", 0, 0, 0, 0, 0, 0, 0, 0, 0},
296
    {0, 153, "HGRS87", 0, -199.87, 74.79, 246.62, 0, 0, 0, 0, 0},
297
    {6214, 154, "Beijing 1954", 3, -31.4, 144.3, 81.2, 0, 0, 0, 0, 0},
298
    {6754, 155, "Libya (LGD 2006)", 4, 208.4058, 109.8777, 2.5764, 0, 0, 0, 0,
299
     0},
300
    {6317, 156, "Dealul Piscului 1970", 3, 28, -121, -77, 0, 0, 0, 0, 0},
301
    {0, 157, "WGS_1984", 54, 0, 0, 0, 0, 0, 0, 0, 0},  // Google merc
302
    {6150, 158, "CH1903+ datum for Switzerland", 10, 674.374, 15.056, 405.346,
303
     0, 0, 0, 0, 0},
304
    {0, 159, "Schwarzeck (updated) datum for Namibia", 14, 616.8, 103.3, -256.9,
305
     0, 0, 0, 0, 0},
306
    {0, 161, "NOAA GCS_Sphere", 55, 0, 0, 0, 0, 0, 0, 0, 0},
307
    // Ellipsoid 40 got from https://docs.precisely.com/docs/sftw/mapinfo-pro/v2021/en-us/pdf/mapinfo-pro-v2021-user-guide.pdf
308
    // Ellipsoid 40 is "Everest (India 1956)", 6377301.243, 300.80174
309
    // but EPSG uses "Everest 1830 (RSO 1969)",6377295.664,300.8017
310
    {6751, 164, "Kertau (RSO)", 40, -11, 851, 5, 0, 0, 0, 0, 0},
311
    {0, 1000, "DHDN_Potsdam_Rauenberg", 10, 582, 105, 414, -1.04, -0.35, 3.08,
312
     8.3, 0},
313
    {6284, 1001, "Pulkovo_1942", 3, 24, -123, -94, -0.02, 0.25, 0.13, 1.1, 0},
314
    {6807, 1002, "NTF_Paris_Meridian", 30, -168, -60, 320, 0, 0, 0, 0,
315
     2.337229166667},
316
    {6149, 1003, "Switzerland_CH_1903", 10, 660.077, 13.551, 369.344, 0.804816,
317
     0.577692, 0.952236, 5.66, 0},
318
    {6237, 1004, "Hungarian_Datum_1972", 21, -56, 75.77, 15.31, -0.37, -0.2,
319
     -0.21, -1.01, 0},
320
    {0, 1005, "Cape_7_Parameter", 28, -134.73, -110.92, -292.66, 0, 0, 0, 1, 0},
321
    {6203, 1006, "AGD84_7_Param_Aust", 2, -117.763, -51.51, 139.061, -0.292,
322
     -0.443, -0.277, -0.191, 0},
323
    {0, 1007, "AGD66_7_Param_ACT", 2, -129.193, -41.212, 130.73, -0.246, -0.374,
324
     -0.329, -2.955, 0},
325
    {0, 1008, "AGD66_7_Param_TAS", 2, -120.271, -64.543, 161.632, -0.2175,
326
     0.0672, 0.1291, 2.4985, 0},
327
    {0, 1009, "AGD66_7_Param_VIC_NSW", 2, -119.353, -48.301, 139.484, -0.415,
328
     -0.26, -0.437, -0.613, 0},
329
    {6272, 1010, "NZGD_7_Param_49", 4, 59.47, -5.04, 187.44, -0.47, 0.1, -1.024,
330
     -4.5993, 0},
331
    {0, 1011, "Rikets_Tri_7_Param_1990", 10, 419.3836, 99.3335, 591.3451,
332
     -0.850389, -1.817277, 7.862238, -0.99496, 0},
333
    {0, 1012, "Russia_PZ90", 52, -1.08, -0.27, -0.9, 0, 0, -0.16, -0.12, 0},
334
    {0, 1013, "Russia_SK42", 52, 23.92, -141.27, -80.9, 0, -0.35, -0.82, -0.12,
335
     0},
336
    {0, 1014, "Russia_SK95", 52, 24.82, -131.21, -82.66, 0, 0, -0.16, -0.12, 0},
337
    {6301, 1015, "Tokyo", 10, -146.414, 507.337, 680.507, 0, 0, 0, 0, 0},
338
    {6123, 1016, "Kartastokoordinaattijarjestelma_1966", 4, -96.062, -82.428,
339
     -121.754, -4.801, -0.345, 1.376, 1.496, 0},
340
    {6610, 1017, "Xian 1980", 53, 24, -123, -94, -0.02, -0.25, 0.13, 1.1, 0},
341
    {0, 1018, "Lithuanian Pulkovo 1942", 4, -40.59527, -18.54979, -69.33956,
342
     -2.508, -1.8319, 2.6114, -4.2991, 0},
343
    {6313, 1019, "Belgian 1972 7 Parameter", 4, -99.059, 53.322, -112.486,
344
     -0.419, 0.83, -1.885, 0.999999, 0},
345
    {6818, 1020, "S-JTSK with Ferro prime meridian", 10, 589, 76, 480, 0, 0, 0,
346
     0, -17.666666666667},
347
    {1031, 1021, "Serbia datum MGI 1901", 10, 574.027, 170.175, 401.545,
348
     4.88786, -0.66524, -13.24673, 6.88933, 0},
349
    {0, 1022, "North Sahara 7-parameter", 6, -38.7086, -128.8054, 118.8837,
350
     0.83822, 7.38459, -1.57989, 3.9904, 0},
351
    {0, 1023, "Hungarian Projection System (EOV) - updated", 21, 52.684,
352
     -71.194, -13.975, 0.312, 0.1063, 0.3729, 1.0191, 0},
353
    {1052, 1024, "S-JTSK (Krovak) Coordinate system - updated", 10, 570.6934,
354
     85.6936, 462.8393, -4.99825, -1.58663, -5.26114, 3.5430155, 0},
355
    {0, 1025, "JTSK03 (Slovak Republic)", 10, 485.014055, 169.473618,
356
     483.842943, -7.78625453, -4.39770887, -4.10248899, 0, 0},
357
    {1168, 1028, "Geocentric Datum of Australia 2020", 0, -0.06155, 0.01087,
358
     0.04019, 0.0394924, 0.0327221, 0.0328979, 0.009994, 0},
359
    // For some weird reason, MapInfo uses nEllipsoid=8 "Clarke 1866 (modified
360
    // for Michigan)" cf
361
    // https://docs.precisely.com/docs/sftw/mapinfo-pro/v2021.1/en-us/pdf/mapinfo-pro-v2021.1-release-notes.pdf
362
    // page 8 whereas EPSG uses the regular Clarke 1866 ellipsoid.
363
    {6683, 1031, "Philippine Reference System 1992", 8, -127.62, -67.24, -47.04,
364
     -3.068, 4.903, 1.578, -1.06, 0},
365
    {0, 9999, "Bosnia-Herzegovina", 10, 472.8677, 187.8769, 544.7084,
366
     -5.76198422, -5.3222842, 12.80666941, 1.54517287, 0},
367
    {6181, 9999, "Luxembourg 1930 / Gauss", 4, -192.986, 13.673, -39.309,
368
     0.4099, 2.9332, -2.6881, 0.43, 0},
369
    {1168, 9999, "Geocentric Datum of Australia 2020", 0, -0.06155, 0.01087,
370
     0.04019, 0.0394924, 0.0327221, 0.0328979, 0.009994, 0},
371
    {-1, -1, nullptr, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
372
373
/* -------------------------------------------------------------------- */
374
/*      This table was hand entered from Appendix I of the mapinfo 6    */
375
/*      manuals.                                                        */
376
/* -------------------------------------------------------------------- */
377
378
const MapInfoSpheroidInfo asSpheroidInfoList[] = {
379
    {9, "Airy 1930", 6377563.396, 299.3249646},
380
    {13, "Airy 1930 (modified for Ireland 1965", 6377340.189, 299.3249646},
381
    {51, "ATS77 (Average Terrestrial System 1977)", 6378135, 298.257},
382
    {2, "Australian", 6378160.0, 298.25},
383
    {10, "Bessel 1841", 6377397.155, 299.1528128},
384
    {35, "Bessel 1841 (modified for NGO 1948)", 6377492.0176, 299.15281},
385
    {14, "Bessel 1841 (modified for Schwarzeck)", 6377483.865, 299.1528128},
386
    {36, "Clarke 1858", 6378293.639, 294.26068},
387
    {7, "Clarke 1866", 6378206.4, 294.9786982},
388
    {8, "Clarke 1866 (modified for Michigan)", 6378450.047484481, 294.9786982},
389
    {6, "Clarke 1880", 6378249.145, 293.465},
390
    {15, "Clarke 1880 (modified for Arc 1950)", 6378249.145326, 293.4663076},
391
    {30, "Clarke 1880 (modified for IGN)", 6378249.2, 293.4660213},
392
    {37, "Clarke 1880 (modified for Jamaica)", 6378249.136, 293.46631},
393
    {16, "Clarke 1880 (modified for Merchich)", 6378249.2, 293.46598},
394
    {38, "Clarke 1880 (modified for Palestine)", 6378300.79, 293.46623},
395
    {39, "Everest (Brunei and East Malaysia)", 6377298.556, 300.8017},
396
    {11, "Everest (India 1830)", 6377276.345, 300.8017},
397
    {40, "Everest (India 1956)", 6377301.243, 300.80174},
398
    {50, "Everest (Pakistan)", 6377309.613, 300.8017},
399
    {17, "Everest (W. Malaysia and Singapore 1948)", 6377304.063, 300.8017},
400
    {48, "Everest (West Malaysia 1969)", 6377304.063, 300.8017},
401
    {18, "Fischer 1960", 6378166.0, 298.3},
402
    {19, "Fischer 1960 (modified for South Asia)", 6378155.0, 298.3},
403
    {20, "Fischer 1968", 6378150.0, 298.3},
404
    {21, "GRS 67", 6378160.0, 298.247167427},
405
    {0, "GRS 80", 6378137.0, 298.257222101},
406
    {56, "GSK2011", 6378136.5, 298.2564151},
407
    {5, "Hayford", 6378388.0, 297.0},
408
    {22, "Helmert 1906", 6378200.0, 298.3},
409
    {23, "Hough", 6378270.0, 297.0},
410
    {31, "IAG 75", 6378140.0, 298.257222},
411
    {41, "Indonesian", 6378160.0, 298.247},
412
    {4, "International 1924", 6378388.0, 297.0},
413
    {49, "Irish (WOFO)", 6377542.178, 299.325},
414
    {3, "Krassovsky", 6378245.0, 298.3},
415
    {32, "MERIT 83", 6378137.0, 298.257},
416
    {33, "New International 1967", 6378157.5, 298.25},
417
    {42, "NWL 9D", 6378145.0, 298.25},
418
    {43, "NWL 10D", 6378135.0, 298.26},
419
    {44, "OSU86F", 6378136.2, 298.25722},
420
    {45, "OSU91A", 6378136.3, 298.25722},
421
    {46, "Plessis 1817", 6376523.0, 308.64},
422
    {52, "PZ90", 6378136.0, 298.257839303},
423
    {57, "PZ90.11", 6378136.0, 298.25784},
424
    {24, "South American", 6378160.0, 298.25},
425
    {12, "Sphere", 6370997.0, 0.0},
426
    {47, "Struve 1860", 6378297.0, 294.73},
427
    {34, "Walbeck", 6376896.0, 302.78},
428
    {25, "War Office", 6378300.583, 296.0},
429
    {26, "WGS 60", 6378165.0, 298.3},
430
    {27, "WGS 66", 6378145.0, 298.25},
431
    {1, "WGS 72", 6378135.0, 298.26},
432
    {28, "WGS 84", 6378137.0, 298.257223563},
433
    {29, "WGS 84 (MAPINFO Datum 0)", 6378137.01, 298.257223563},
434
    {54, "WGS 84 (MAPINFO Datum 157)", 6378137.01, 298.257223563},
435
    {-1, nullptr, 0.0, 0.0}};
436
437
/* For LCC, standard parallel 1 and 2 can be switched indifferently */
438
/* So the MapInfo order and the EPSG order are not generally identical */
439
/* which may cause recognition problems when reading in MapInfo */
440
/* This table contains the parameters in the order expected by MapInfo */
441
typedef struct
442
{
443
    int nEPSGCode;
444
    int bReverseStdP;
445
    int nMapInfoDatumID;
446
    double dfCenterLong;
447
    double dfCenterLat;
448
    double dfStdP1;
449
    double dfStdP2;
450
} MapInfoLCCSRS;
451
452
static const MapInfoLCCSRS asMapInfoLCCSRSList[] = {
453
    {2154, 1, 33, 3, 46.5, 44, 49},
454
    {2154, 1, 33, 3, 46.5, 44, 49.00000000001},
455
    {2154, 1, 33, 3, 46.5, 44, 49.00000000002},
456
    {2225, 1, 74, -122, 39.3333333333, 40, 41.6666666667},
457
    {2226, 1, 74, -122, 37.6666666667, 38.3333333333, 39.8333333333},
458
    {2227, 1, 74, -120.5, 36.5, 37.0666666667, 38.4333333333},
459
    {2228, 1, 74, -119, 35.3333333333, 36, 37.25},
460
    {2229, 1, 74, -118, 33.5, 34.0333333333, 35.4666666667},
461
    {2230, 1, 74, -116.25, 32.1666666667, 32.7833333333, 33.8833333333},
462
    {2231, 1, 74, -105.5, 39.3333333333, 39.7166666667, 40.7833333333},
463
    {2232, 1, 74, -105.5, 37.8333333333, 38.45, 39.75},
464
    {2233, 1, 74, -105.5, 36.6666666667, 37.2333333333, 38.4333333333},
465
    {2234, 1, 74, -72.75, 40.8333333333, 41.2, 41.8666666667},
466
    {2238, 1, 74, -84.5, 29, 29.5833333333, 30.75},
467
    {2246, 0, 74, -84.25, 37.5, 37.9666666667, 38.9666666667},
468
    {2247, 1, 74, -85.75, 36.3333333333, 36.7333333333, 37.9333333333},
469
    {2248, 1, 74, -77, 37.6666666667, 38.3, 39.45},
470
    {2249, 1, 74, -71.5, 41, 41.7166666667, 42.6833333333},
471
    {2250, 1, 74, -70.5, 41, 41.2833333333, 41.4833333333},
472
    {2251, 1, 74, -87, 44.7833333333, 45.4833333333, 47.0833333333},
473
    {2252, 1, 74, -84.3666666667, 43.3166666667, 44.1833333333, 45.7},
474
    {2253, 1, 74, -84.3666666667, 41.5, 42.1, 43.6666666667},
475
    {2256, 1, 74, -109.5, 44.25, 45, 49},
476
    {2263, 1, 74, -74, 40.1666666667, 40.6666666667, 41.0333333333},
477
    {2264, 1, 74, -79, 33.75, 34.3333333333, 36.1666666667},
478
    {2265, 1, 74, -100.5, 47, 47.4333333333, 48.7333333333},
479
    {2266, 1, 74, -100.5, 45.6666666667, 46.1833333333, 47.4833333333},
480
    {2267, 1, 74, -98, 35, 35.5666666667, 36.7666666667},
481
    {2268, 1, 74, -98, 33.3333333333, 33.9333333333, 35.2333333333},
482
    {2269, 1, 74, -120.5, 43.6666666667, 44.3333333333, 46},
483
    {2270, 1, 74, -120.5, 41.6666666667, 42.3333333333, 44},
484
    {2271, 1, 74, -77.75, 40.1666666667, 40.8833333333, 41.95},
485
    {2272, 1, 74, -77.75, 39.3333333333, 39.9333333333, 40.9666666667},
486
    {2273, 1, 74, -81, 31.8333333333, 32.5, 34.8333333333},
487
    {2274, 1, 74, -86, 34.3333333333, 35.25, 36.4166666667},
488
    {2275, 1, 74, -101.5, 34, 34.65, 36.1833333333},
489
    {2276, 1, 74, -98.5, 31.6666666667, 32.1333333333, 33.9666666667},
490
    {2277, 1, 74, -100.3333333333, 29.6666666667, 30.1166666667, 31.8833333333},
491
    {2278, 1, 74, -99, 27.8333333333, 28.3833333333, 30.2833333333},
492
    {2279, 1, 74, -98.5, 25.6666666667, 26.1666666667, 27.8333333333},
493
    {2280, 1, 74, -111.5, 40.3333333333, 40.7166666667, 41.7833333333},
494
    {2281, 1, 74, -111.5, 38.3333333333, 39.0166666667, 40.65},
495
    {2282, 1, 74, -111.5, 36.6666666667, 37.2166666667, 38.35},
496
    {2283, 1, 74, -78.5, 37.6666666667, 38.0333333333, 39.2},
497
    {2284, 1, 74, -78.5, 36.3333333333, 36.7666666667, 37.9666666667},
498
    {2285, 1, 74, -120.8333333333, 47, 47.5, 48.7333333333},
499
    {2286, 1, 74, -120.5, 45.3333333333, 45.8333333333, 47.3333333333},
500
    {2287, 1, 74, -90, 45.1666666667, 45.5666666667, 46.7666666667},
501
    {2288, 1, 74, -90, 43.8333333333, 44.25, 45.5},
502
    {2289, 1, 74, -90, 42, 42.7333333333, 44.0666666667},
503
    {26740, 1, 63, -176, 51, 51.8333333333, 53.8333333333},
504
    {26741, 1, 62, -122, 39.3333333333, 40, 41.6666666667},
505
    {26742, 1, 62, -122, 37.6666666667, 38.3333333333, 39.8333333333},
506
    {26743, 1, 62, -120.5, 36.5, 37.0666666667, 38.4333333333},
507
    {26744, 1, 62, -119, 35.3333333333, 36, 37.25},
508
    {26745, 1, 62, -118, 33.5, 34.0333333333, 35.4666666667},
509
    {26746, 1, 62, -116.25, 32.1666666667, 32.7833333333, 33.8833333333},
510
    {26747, 1, 62, -118.3333333333, 34.1333333333, 33.8666666667,
511
     34.4166666667},
512
    {26751, 1, 62, -92, 34.3333333333, 34.9333333333, 36.2333333333},
513
    {26752, 1, 62, -92, 32.6666666667, 33.3, 34.7666666667},
514
    {26753, 0, 62, -105.5, 39.3333333333, 39.7166666667, 40.7833333333},
515
    {26754, 1, 62, -105.5, 37.8333333333, 38.45, 39.75},
516
    {26755, 1, 62, -105.5, 36.6666666667, 37.2333333333, 38.4333333333},
517
    {26756, 1, 62, -72.75, 40.8333333333, 41.2, 41.8666666667},
518
    {26760, 1, 62, -84.5, 29, 29.5833333333, 30.75},
519
    {26775, 1, 62, -93.5, 41.5, 42.0666666667, 43.2666666667},
520
    {26776, 1, 62, -93.5, 40, 40.6166666667, 41.7833333333},
521
    {26777, 1, 62, -98, 38.3333333333, 38.7166666667, 39.7833333333},
522
    {26778, 0, 62, -98.5, 36.6666666667, 38.5666666667, 37.2666666667},
523
    {26779, 0, 62, -84.25, 37.5, 37.9666666667, 38.9666666667},
524
    {26780, 0, 62, -85.75, 36.3333333333, 36.7333333333, 37.9333333333},
525
    {26781, 0, 62, -92.5, 30.6666666667, 31.1666666667, 32.6666666667},
526
    {26785, 0, 62, -77, 37.8333333333, 38.3, 39.45},
527
    {26786, 0, 62, -71.5, 41, 41.7166666667, 42.6833333333},
528
    {26788, 0, 73, -87, 44.7833333333, 45.4833333333, 47.0833333333},
529
    {26789, 0, 73, -84.3333333333, 43.3166666667, 44.1833333333, 45.7},
530
    {26790, 0, 73, -84.3333333333, 41.5, 42.1, 43.6666666667},
531
    {26791, 0, 62, -93.1, 46.5, 47.0333333333, 48.6333333333},
532
    {26792, 0, 62, -94.25, 45, 45.6166666667, 47.05},
533
    {26793, 0, 62, -94, 43, 43.7833333333, 45.2166666667},
534
    {26940, 1, 74, -176, 51, 51.8333333333, 53.8333333333},
535
    {26941, 1, 74, -122, 39.3333333333, 40, 41.6666666667},
536
    {26942, 1, 74, -122, 37.6666666667, 38.3333333333, 39.8333333333},
537
    {26943, 1, 74, -120.5, 36.5, 37.0666666667, 38.4333333333},
538
    {26944, 1, 74, -119, 35.3333333333, 36, 37.25},
539
    {26945, 1, 74, -118, 33.5, 34.0333333333, 35.4666666667},
540
    {26946, 1, 74, -116.25, 32.1666666667, 32.7833333333, 33.8833333333},
541
    {26951, 1, 74, -92, 34.3333333333, 34.9333333333, 36.2333333333},
542
    {26952, 1, 74, -92, 32.6666666667, 33.3, 34.7666666667},
543
    {26953, 1, 74, -105.5, 39.3333333333, 39.7166666667, 40.7833333333},
544
    {26954, 1, 74, -105.5, 37.8333333333, 38.45, 39.75},
545
    {26955, 1, 74, -105.5, 36.6666666667, 37.2333333333, 38.4333333333},
546
    {26956, 1, 74, -72.75, 40.8333333333, 41.2, 41.8666666667},
547
    {26960, 1, 74, -84.5, 29, 29.5833333333, 30.75},
548
    {26975, 1, 74, -93.5, 41.5, 42.0666666667, 43.2666666667},
549
    {26976, 1, 74, -93.5, 40, 40.6166666667, 41.7833333333},
550
    {26977, 1, 74, -98, 38.3333333333, 38.7166666667, 39.7833333333},
551
    {26978, 0, 74, -98.5, 36.6666666667, 38.5666666667, 37.2666666667},
552
    {26980, 1, 74, -85.75, 36.3333333333, 36.7333333333, 37.9333333333},
553
    {26981, 1, 74, -92.5, 30.5, 31.1666666667, 32.6666666667},
554
    {26982, 1, 74, -91.3333333333, 28.5, 29.3, 30.7},
555
    {26985, 1, 74, -77, 37.6666666667, 38.3, 39.45},
556
    {26986, 1, 74, -71.5, 41, 41.7166666667, 42.6833333333},
557
    {26987, 1, 74, -70.5, 41, 41.2833333333, 41.4833333333},
558
    {26988, 1, 74, -87, 44.7833333333, 45.4833333333, 47.0833333333},
559
    {26989, 1, 74, -84.3666666667, 43.3166666667, 44.1833333333, 45.7},
560
    {26990, 1, 74, -84.3666666667, 41.5, 42.1, 43.6666666667},
561
    {26991, 1, 74, -93.1, 46.5, 47.0333333333, 48.6333333333},
562
    {26992, 1, 74, -94.25, 45, 45.6166666667, 47.05},
563
    {26993, 1, 74, -94, 43, 43.7833333333, 45.2166666667},
564
    {3111, 0, 116, 145, -37, -36, -38},
565
    {31370, 1, 1019, 4.3674866667, 90, 49.8333339000, 51.1666672333},
566
    {32001, 1, 62, -109.5, 47, 47.85, 48.7166666667},
567
    {32002, 1, 62, -109.5, 45.8333333333, 46.45, 47.8833333333},
568
    {32003, 1, 62, -109.5, 44, 44.8666666667, 46.4},
569
    {32005, 0, 62, -100, 41.3333333333, 41.85, 42.8166666667},
570
    {32006, 0, 62, -99.5, 39.6666666667, 40.2833333333, 41.7166666667},
571
    {32018, 1, 62, -74, 40.5, 40.6666666667, 41.0333333333},
572
    {32019, 0, 62, -79, 33.75, 34.3333333333, 36.1666666667},
573
    {32020, 0, 62, -100.5, 47, 47.4333333333, 48.7333333333},
574
    {32021, 0, 62, -100.5, 45.6666666667, 46.1833333333, 47.4833333333},
575
    {32022, 0, 62, -82.5, 39.6666666667, 40.4333333333, 41.7},
576
    {32023, 0, 62, -82.5, 38, 38.7333333333, 40.0333333333},
577
    {32024, 0, 62, -98, 35, 35.5666666667, 36.7666666667},
578
    {32025, 0, 62, -98, 33.3333333333, 33.9333333333, 35.2333333333},
579
    {32026, 0, 62, -120.5, 43.6666666667, 44.3333333333, 46},
580
    {32027, 0, 62, -120.5, 41.6666666667, 42.3333333333, 44},
581
    {32028, 0, 62, -77.75, 40.1666666667, 40.8833333333, 41.95},
582
    {32031, 0, 62, -81, 33, 33.7666666667, 34.9666666667},
583
    {32033, 0, 62, -81, 31.8333333333, 32.3333333333, 33.6666666667},
584
    {32034, 0, 62, -100, 43.8333333333, 44.4166666667, 45.6833333333},
585
    {32035, 0, 62, -100.3333333333, 42.3333333333, 42.8333333333, 44.4},
586
    {32036, 0, 62, -86, 34.6666666667, 35.25, 36.4166666667},
587
    {32037, 0, 62, -101.5, 34, 34.65, 36.1833333333},
588
    {32038, 0, 62, -97.5, 31.6666666667, 32.1333333333, 33.9666666667},
589
    {32039, 0, 62, -100.3333333333, 29.6666666667, 30.1166666667,
590
     31.8833333333},
591
    {32040, 0, 62, -99, 27.8333333333, 28.3833333333, 30.2833333333},
592
    {32041, 0, 62, -98.5, 25.6666666667, 26.1666666667, 27.8333333333},
593
    {32042, 0, 62, -111.5, 40.3333333333, 40.7166666667, 41.7833333333},
594
    {32043, 0, 62, -111.5, 38.3333333333, 39.0166666667, 40.65},
595
    {32044, 0, 62, -111.5, 36.6666666667, 37.2166666667, 38.35},
596
    {32046, 0, 62, -78.5, 37.6666666667, 38.0333333333, 39.2},
597
    {32047, 0, 62, -78.5, 36.3333333333, 36.7666666667, 37.9666666667},
598
    {32048, 0, 62, -120.8333333333, 47, 47.5, 48.7333333333},
599
    {32049, 0, 62, -120.5, 45.3333333333, 45.8333333333, 47.3333333333},
600
    {32050, 0, 62, -79.5, 38.5, 39, 40.25},
601
    {32051, 0, 62, -81, 37, 37.4833333333, 38.8833333333},
602
    {32052, 0, 62, -90, 45.1666666667, 45.5666666667, 46.7666666667},
603
    {32053, 0, 62, -90, 43.8333333333, 44.25, 45.5},
604
    {32054, 0, 62, -90, 42, 42.7333333333, 44.0666666667},
605
    {32059, 0, 62, -66.4333333333, 18.4333333333, 18.0333333333, 18.4333333333},
606
    {32060, 0, 62, -66.4333333333, 18.4333333333, 18.0333333333, 18.4333333333},
607
    {32100, 1, 74, -109.5, 44.25, 45, 49},
608
    {32104, 1, 74, -100, 39.8333333333, 40, 43},
609
    {32118, 1, 74, -74, 40.1666666667, 40.6666666667, 41.0333333333},
610
    {32119, 1, 74, -79, 33.75, 34.3333333333, 36.1666666667},
611
    {32120, 1, 74, -100.5, 47, 47.4333333333, 48.7333333333},
612
    {32121, 1, 74, -100.5, 45.6666666667, 46.1833333333, 47.4833333333},
613
    {32122, 1, 74, -82.5, 39.6666666667, 40.4333333333, 41.7},
614
    {32123, 1, 74, -82.5, 38, 38.7333333333, 40.0333333333},
615
    {32124, 1, 74, -98, 35, 35.5666666667, 36.7666666667},
616
    {32125, 1, 74, -98, 33.3333333333, 33.9333333333, 35.2333333333},
617
    {32126, 1, 74, -120.5, 43.6666666667, 44.3333333333, 46},
618
    {32127, 1, 74, -120.5, 41.6666666667, 42.3333333333, 44},
619
    {32128, 1, 74, -77.75, 40.1666666667, 40.8833333333, 41.95},
620
    {32129, 1, 74, -77.75, 39.3333333333, 39.9333333333, 40.9666666667},
621
    {32133, 1, 74, -81, 31.8333333333, 32.5, 34.8333333333},
622
    {32134, 1, 74, -100, 43.8333333333, 44.4166666667, 45.6833333333},
623
    {32135, 1, 74, -100.3333333333, 42.3333333333, 42.8333333333, 44.4},
624
    {32136, 1, 74, -86, 34.3333333333, 35.25, 36.4166666667},
625
    {32137, 1, 74, -101.5, 34, 34.65, 36.1833333333},
626
    {32138, 1, 74, -98.5, 31.6666666667, 32.1333333333, 33.9666666667},
627
    {32139, 1, 74, -100.3333333333, 29.6666666667, 30.1166666667,
628
     31.8833333333},
629
    {32140, 1, 74, -99, 27.8333333333, 28.3833333333, 30.2833333333},
630
    {32141, 1, 74, -98.5, 25.6666666667, 26.1666666667, 27.8333333333},
631
    {32142, 1, 74, -111.5, 40.3333333333, 40.7166666667, 41.7833333333},
632
    {32143, 1, 74, -111.5, 38.3333333333, 39.0166666667, 40.65},
633
    {32144, 1, 74, -111.5, 36.6666666667, 37.2166666667, 38.35},
634
    {32146, 1, 74, -78.5, 37.6666666667, 38.0333333333, 39.2},
635
    {32147, 1, 74, -78.5, 36.3333333333, 36.7666666667, 37.9666666667},
636
    {32148, 1, 74, -120.8333333333, 47, 47.5, 48.7333333333},
637
    {32149, 1, 74, -120.5, 45.3333333333, 45.8333333333, 47.3333333333},
638
    {32150, 1, 74, -79.5, 38.5, 39, 40.25},
639
    {32151, 1, 74, -81, 37, 37.4833333333, 38.8833333333},
640
    {32152, 1, 74, -90, 45.1666666667, 45.5666666667, 46.7666666667},
641
    {32153, 1, 74, -90, 43.8333333333, 44.25, 45.5},
642
    {32154, 1, 74, -90, 42, 42.7333333333, 44.0666666667},
643
    {32161, 1, 74, -66.4333333333, 17.8333333333, 18.0333333333, 18.4333333333},
644
    {3300, 1, 115, 24, 57.51755394, 58, 59.33333333},
645
    {3301, 1, 115, 24, 57.51755393056, 58, 59.33333333},
646
    {3797, 0, 66, -70, 44, 50, 46},
647
    {3798, 0, 74, -70, 44, 50, 46},
648
    {3799, 0, 74, -70, 44, 50, 46},
649
    {3942, 0, 33, 3, 42, 41.25, 42.75},
650
    {3943, 0, 33, 3, 43, 42.25, 43.75},
651
    {3944, 0, 33, 3, 44, 43.25, 44.75},
652
    {3945, 0, 33, 3, 45, 44.25, 45.75},
653
    {3946, 0, 33, 3, 46, 45.25, 46.75},
654
    {3947, 0, 33, 3, 47, 46.25, 47.75},
655
    {3948, 0, 33, 3, 48, 47.25, 48.75},
656
    {3949, 0, 33, 3, 49, 48.25, 49.75},
657
    {3950, 0, 33, 3, 50, 49.25, 50.75},
658
    {42101, 0, 104, -95, 0, 49, 77},
659
    {42103, 0, 104, -100, 0, 33, 45},
660
    {42304, 0, 74, -95, 49, 49, 77},
661
    {0, 0, 0, 110, 10, 25, 40},
662
    {0, 0, 0, 132.5, -10, -21.5, -33.5},
663
    {0, 0, 0, 25, 35, 40, 65},
664
    {0, 0, 0, 47.5, 25, 15, 35},
665
    {0, 0, 0, 95, 40, 20, 60},
666
    {0, 0, 1002, 0, 42.165, 41.5603877778, 42.76766333},
667
    {0, 0, 1002, 0, 42.165, 41.5603877778, 42.767663333},
668
    {0, 0, 1002, 0, 42.165, 41.560387778, 42.76766333},
669
    {0, 0, 1002, 0, 42.165, 41.560387778, 42.767663333},
670
    {0, 0, 1002, 0, 42.165, 41.56038778, 42.76766333},
671
    {0, 0, 1002, 0, 42.165, 41.560387840948, 42.76766346965},
672
    {0, 0, 1002, 0, 44.1, 43.199291275544, 44.996093814511},
673
    {0, 0, 1002, 0, 44.1, 43.1992913889, 44.99609389},
674
    {0, 0, 1002, 0, 44.1, 43.199291389, 44.99609389},
675
    {0, 0, 1002, 0, 44.1, 43.19929139, 44.99609389},
676
    {0, 0, 1002, 0, 46.8, 45.8989188889, 47.69601444},
677
    {0, 0, 1002, 0, 46.8, 45.898918889, 47.69601444},
678
    {0, 0, 1002, 0, 46.8, 45.89891889, 47.69601444},
679
    {0, 0, 1002, 0, 46.8, 45.898918964419, 47.696014502038},
680
    {0, 0, 1002, 0, 49.5, 48.5985227778, 50.39591167},
681
    {0, 0, 1002, 0, 49.5, 48.598522778, 50.39591167},
682
    {0, 0, 1002, 0, 49.5, 48.59852278, 50.39591167},
683
    {0, 0, 1002, 0, 49.5, 48.598522847174, 50.395911631678},
684
    {0, 0, 1005, 23, -23, -18, -32},
685
    {0, 0, 1022, 2.7, 36, 37.575, 34.425},
686
    {0, 0, 104, 13.33333333, 47.5, 46, 49},
687
    {0, 0, 104, 13.33333333, 48, 46, 49},
688
    {0, 0, 104, -19, 65, 64.25, 65.75},
689
    {0, 0, 104, 36.0, 25.0, 37.5, 40.5},
690
    {0, 0, 104, 36, 25, 37.5, 40.5},
691
    {0, 0, 104, 70, -50, -68.5, -74.5},
692
    {0, 0, 110, 4.367975, 90, 49.8333333333, 51.1666666667},
693
    {0, 0, 115, 10, 52, 35, 45},
694
    {0, 0, 116, 135, -24, -18, -36},
695
    {0, 0, 116, 135, -32, -28, -36},
696
    {0, 0, 12, 135, -24, -18, -36},
697
    {0, 0, 12, 145, -37, -36, -38},
698
    {0, 0, 13, 135, -24, -18, -36},
699
    {0, 0, 19, 23, -23, -18, -32},
700
    {0, 0, 28, 17, 29.77930555, 42, 56},
701
    {0, 0, 28, 19, 29.77930555, 42, 56},
702
    {0, 0, 28, 36.0, 25.0, 37.5, 40.5},
703
    {0, 0, 33, 13.5, 0, 52.6666666667, 55.3333333333},
704
    {0, 0, 33, 15, 0, 56.5, 60.5},
705
    {0, 0, 33, 15, 0, 58, 66},
706
    {0, 0, 33, 15, 0, 63.5, 67.5},
707
    {0, 0, 33, 15.5, 0, 56.6666666667, 59.3333333333},
708
    {0, 0, 33, 15.5, 0, 60.6666666667, 63.3333333333},
709
    {0, 0, 33, 16.5, 0, 60.6666666667, 63.3333333333},
710
    {0, 0, 33, 18.5, 0, 64.6666666667, 67.3333333333},
711
    {0, 0, 33, 19, 0, 64.6666666667, 67.3333333333},
712
    {0, 0, 55, -5.4, 22.5, 20.9075742561, 24.0921050540},
713
    {0, 0, 55, -5.4, 26.1, 24.5075340813, 27.6921073632},
714
    {0, 0, 55, -5.4, 29.7, 28.1063294800, 31.2932791054},
715
    {0, 0, 55, -5.4, 33.3, 31.72786641202, 34.8717272112},
716
    {0, 0, 62, -70.5, 41, 41.2833333333, 41.4833333333},
717
    {0, 0, 62, -77.75, 39.3333333333, 39.9333333333, 40.9666666667},
718
    {0, 0, 62, -91.3333333333, 25.6666666667, 26.1666666667, 27.8333333333},
719
    {0, 0, 62, -91.3333333333, 28.6666666667, 29.3, 30.67},
720
    {0, 0, 62, -96, 23, 20, 60},
721
    {0, 0, 62, -96, 23, 33, 45},
722
    {0, 0, 62, -96, 39, 33, 45},
723
    {0, 0, 66, -68.5, 44, 46, 60},
724
    {0, 0, 74, -100.3333333333, 42.3333333333, 42.8333333333, 44.4},
725
    {0, 0, 74, -100, 39.8333333333, 40, 43},
726
    {0, 0, 74, -100, 43.8333333333, 44.4166666667, 45.6833333333},
727
    {0, 0, 74, -109.5, 44.25, 45, 49},
728
    {0, 0, 74, -111.5, 36.6666666667, 37.2166666667, 38.35},
729
    {0, 0, 74, -111.5, 38.3333333333, 39.0166666667, 40.65},
730
    {0, 0, 74, -111.5, 40.3333333333, 40.7166666667, 41.7833333333},
731
    {0, 0, 74, -120.5, 41.6666666667, 42.3333333333, 44},
732
    {0, 0, 74, -120.5, 43.6666666667, 44.3333333333, 46},
733
    {0, 0, 74, -176, 51, 51.8333333333, 53.8333333333},
734
    {0, 0, 74, -66.4333333333, 17.8333333333, 18.0333333333, 18.4333333333},
735
    {0, 0, 74, -68.5, 44, 46, 60},
736
    {0, 0, 74, -79.5, 38.5, 39, 40.25},
737
    {0, 0, 74, -81, 31.8333333333, 32.5, 34.8333333333},
738
    {0, 0, 74, -81, 37, 37.4833333333, 38.8833333333},
739
    {0, 0, 74, -82.5, 38, 38.7333333333, 40.0333333333},
740
    {0, 0, 74, -82.5, 39.6666666667, 40.4333333333, 41.7},
741
    {0, 0, 74, -84.25, 37.5, 37.9666666667, 38.9666666667},
742
    {0, 0, 74, -84.3666666667, 41.5, 42.1, 43.6666666667},
743
    {0, 0, 74, -84.3666666667, 43.3166666667, 44.1833333333, 45.7},
744
    {0, 0, 74, -87, 44.7833333333, 45.4833333333, 47.0833333333},
745
    {0, 0, 74, -91.3333333333, 25.5, 26.1666666667, 27.8333333333},
746
    {0, 0, 74, -91.3333333333, 28.5, 29.3, 30.7},
747
    {0, 0, 74, -92, 32.6666666667, 33.3, 34.7666666667},
748
    {0, 0, 74, -92, 34.3333333333, 34.9333333333, 36.2333333333},
749
    {0, 0, 74, -92.5, 30.5, 31.1666666667, 32.6666666667},
750
    {0, 0, 74, -93.1, 46.5, 47.0333333333, 48.6333333333},
751
    {0, 0, 74, -93.5, 40, 40.6166666667, 41.7833333333},
752
    {0, 0, 74, -93.5, 41.5, 42.0666666667, 43.2666666667},
753
    {0, 0, 74, -94.25, 45, 45.6166666667, 47.05},
754
    {0, 0, 74, -94, 43, 43.7833333333, 45.2166666667},
755
    {0, 0, 74, -98, 38.3333333333, 38.7166666667, 39.7833333333},
756
    {0, 0, 74, -98.5, 36.6666666667, 38.5666666667, 37.2666666667},
757
};
758
759
/**********************************************************************
760
 *                   TABFile::GetSpatialRefFromTABProj()
761
 **********************************************************************/
762
763
static bool TAB_EQUAL(double a, double b)
764
0
{
765
0
    return std::fabs(a - b) < 1.0e-10;
766
0
}
767
768
OGRSpatialReference *
769
TABFileGetSpatialRefFromTABProj(const TABProjInfo &sTABProj)
770
0
{
771
    /*-----------------------------------------------------------------
772
     * Get the units name, and translation factor.
773
     *----------------------------------------------------------------*/
774
0
    const char *pszUnitsName = nullptr;
775
0
    const char *pszUnitsConv = nullptr;
776
    /* double      dfConv = 1.0; */
777
778
0
    switch (sTABProj.nUnitsId)
779
0
    {
780
0
        case 0:
781
0
            pszUnitsName = "Mile";
782
0
            pszUnitsConv = "1609.344";
783
0
            break;
784
785
0
        case 1:
786
0
            pszUnitsName = "Kilometer";
787
0
            pszUnitsConv = "1000.0";
788
0
            break;
789
790
0
        case 2:
791
0
            pszUnitsName = "IINCH";
792
0
            pszUnitsConv = "0.0254";
793
0
            break;
794
795
0
        case 3:
796
0
            pszUnitsName = SRS_UL_FOOT;
797
0
            pszUnitsConv = SRS_UL_FOOT_CONV;
798
0
            break;
799
800
0
        case 4:
801
0
            pszUnitsName = "IYARD";
802
0
            pszUnitsConv = "0.9144";
803
0
            break;
804
805
0
        case 5:
806
0
            pszUnitsName = "Millimeter";
807
0
            pszUnitsConv = "0.001";
808
0
            break;
809
810
0
        case 6:
811
0
            pszUnitsName = "Centimeter";
812
0
            pszUnitsConv = "0.01";
813
0
            break;
814
815
0
        case 7:
816
0
            pszUnitsName = SRS_UL_METER;
817
0
            pszUnitsConv = "1.0";
818
0
            break;
819
820
0
        case 8:
821
0
            pszUnitsName = SRS_UL_US_FOOT;
822
0
            pszUnitsConv = SRS_UL_US_FOOT_CONV;
823
0
            break;
824
825
0
        case 9:
826
0
            pszUnitsName = SRS_UL_NAUTICAL_MILE;
827
0
            pszUnitsConv = SRS_UL_NAUTICAL_MILE_CONV;
828
0
            break;
829
830
0
        case 30:
831
0
            pszUnitsName = SRS_UL_LINK;
832
0
            pszUnitsConv = SRS_UL_LINK_CONV;
833
0
            break;
834
835
0
        case 31:
836
0
            pszUnitsName = SRS_UL_CHAIN;
837
0
            pszUnitsConv = SRS_UL_CHAIN_CONV;
838
0
            break;
839
840
0
        case 32:
841
0
            pszUnitsName = SRS_UL_ROD;
842
0
            pszUnitsConv = SRS_UL_ROD_CONV;
843
0
            break;
844
845
0
        default:
846
0
            pszUnitsName = SRS_UL_METER;
847
0
            pszUnitsConv = "1.0";
848
0
            break;
849
0
    }
850
851
    /* dfConv = CPLAtof(pszUnitsConv); */
852
853
    /*-----------------------------------------------------------------
854
     * Transform them into an OGRSpatialReference.
855
     *----------------------------------------------------------------*/
856
0
    OGRSpatialReference *poSpatialRef = new OGRSpatialReference;
857
0
    poSpatialRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
858
859
    /*-----------------------------------------------------------------
860
     * Handle the PROJCS style projections, but add the datum later.
861
     *----------------------------------------------------------------*/
862
0
    switch (sTABProj.nProjId)
863
0
    {
864
0
        case 0:
865
0
            poSpatialRef->SetLocalCS("Nonearth");
866
0
            poSpatialRef->SetLinearUnits(pszUnitsName, CPLAtof(pszUnitsConv));
867
0
            break;
868
869
            /*--------------------------------------------------------------
870
             * lat/long .. just add the GEOGCS later.
871
             *-------------------------------------------------------------*/
872
0
        case 1:
873
0
            break;
874
875
            /*--------------------------------------------------------------
876
             * Cylindrical Equal Area
877
             *-------------------------------------------------------------*/
878
0
        case 2:
879
0
            poSpatialRef->SetCEA(
880
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
881
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3]);
882
0
            break;
883
884
            /*--------------------------------------------------------------
885
             * Lambert Conic Conformal
886
             *-------------------------------------------------------------*/
887
0
        case 3:
888
0
            poSpatialRef->SetLCC(
889
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
890
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
891
0
                sTABProj.adProjParams[4], sTABProj.adProjParams[5]);
892
0
            break;
893
894
            /*--------------------------------------------------------------
895
             * Lambert Azimuthal Equal Area
896
             *-------------------------------------------------------------*/
897
0
        case 4:
898
0
        case 29:
899
0
            poSpatialRef->SetLAEA(sTABProj.adProjParams[1],
900
0
                                  sTABProj.adProjParams[0], 0.0, 0.0);
901
0
            break;
902
903
            /*--------------------------------------------------------------
904
             * Azimuthal Equidistant (Polar aspect only)
905
             *-------------------------------------------------------------*/
906
0
        case 5:
907
0
        case 28:
908
0
            poSpatialRef->SetAE(sTABProj.adProjParams[1],
909
0
                                sTABProj.adProjParams[0], 0.0, 0.0);
910
0
            break;
911
912
            /*--------------------------------------------------------------
913
             * Equidistant Conic
914
             *-------------------------------------------------------------*/
915
0
        case 6:
916
0
            poSpatialRef->SetEC(
917
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
918
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
919
0
                sTABProj.adProjParams[4], sTABProj.adProjParams[5]);
920
0
            break;
921
922
            /*--------------------------------------------------------------
923
             * Hotine Oblique Mercator
924
             *-------------------------------------------------------------*/
925
0
        case 7:
926
0
            poSpatialRef->SetHOM(
927
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
928
0
                sTABProj.adProjParams[2], 90.0, sTABProj.adProjParams[3],
929
0
                sTABProj.adProjParams[4], sTABProj.adProjParams[5]);
930
0
            break;
931
932
            /*--------------------------------------------------------------
933
             * Hotine Oblique Mercator with Angle from Rectified to Skew Grid
934
             *-------------------------------------------------------------*/
935
0
        case 35:
936
0
            poSpatialRef->SetHOM(
937
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
938
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
939
0
                sTABProj.adProjParams[4], sTABProj.adProjParams[5],
940
0
                sTABProj.adProjParams[6]);
941
0
            break;
942
943
            /*--------------------------------------------------------------
944
             * Transverse Mercator
945
             *-------------------------------------------------------------*/
946
0
        case 8:
947
0
        case 34:  // Extended Transverse Mercator
948
0
            poSpatialRef->SetTM(
949
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
950
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
951
0
                sTABProj.adProjParams[4]);
952
0
            break;
953
954
            /*----------------------------------------------------------------
955
             * Transverse Mercator,(modified for Danish System 34 Jylland-Fyn)
956
             *---------------------------------------------------------------*/
957
0
        case 21:
958
            // poSpatialRef->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_21,
959
0
            poSpatialRef->SetTM(
960
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
961
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
962
0
                sTABProj.adProjParams[4]);
963
0
            break;
964
965
            /*--------------------------------------------------------------
966
             * Transverse Mercator,(modified for Danish System 34 Sjaelland)
967
             *-------------------------------------------------------------*/
968
0
        case 22:
969
            // poSpatialRef->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_22,
970
0
            poSpatialRef->SetTM(
971
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
972
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
973
0
                sTABProj.adProjParams[4]);
974
0
            break;
975
976
            /*----------------------------------------------------------------
977
             * Transverse Mercator,(modified for Danish System 34/45 Bornholm)
978
             *---------------------------------------------------------------*/
979
0
        case 23:
980
            // poSpatialRef->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_23,
981
0
            poSpatialRef->SetTM(
982
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
983
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
984
0
                sTABProj.adProjParams[4]);
985
0
            break;
986
987
            /*--------------------------------------------------------------
988
             * Transverse Mercator,(modified for Finnish KKJ)
989
             *-------------------------------------------------------------*/
990
0
        case 24:
991
            // poSpatialRef->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_24,
992
0
            poSpatialRef->SetTM(
993
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
994
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
995
0
                sTABProj.adProjParams[4]);
996
0
            break;
997
998
            /*--------------------------------------------------------------
999
             * Albers Conic Equal Area
1000
             *-------------------------------------------------------------*/
1001
0
        case 9:
1002
0
            poSpatialRef->SetACEA(
1003
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
1004
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
1005
0
                sTABProj.adProjParams[4], sTABProj.adProjParams[5]);
1006
0
            break;
1007
1008
            /*--------------------------------------------------------------
1009
             * Mercator
1010
             *-------------------------------------------------------------*/
1011
0
        case 10:
1012
0
            poSpatialRef->SetMercator(0.0, sTABProj.adProjParams[0], 1.0, 0.0,
1013
0
                                      0.0);
1014
0
            break;
1015
1016
            /*--------------------------------------------------------------
1017
             * Miller Cylindrical
1018
             *-------------------------------------------------------------*/
1019
0
        case 11:
1020
0
            poSpatialRef->SetMC(0.0, sTABProj.adProjParams[0], 0.0, 0.0);
1021
0
            break;
1022
1023
            /*--------------------------------------------------------------
1024
             * Robinson
1025
             *-------------------------------------------------------------*/
1026
0
        case 12:
1027
0
            poSpatialRef->SetRobinson(sTABProj.adProjParams[0], 0.0, 0.0);
1028
0
            break;
1029
1030
            /*--------------------------------------------------------------
1031
             * Mollweide
1032
             *-------------------------------------------------------------*/
1033
0
        case 13:
1034
0
            poSpatialRef->SetMollweide(sTABProj.adProjParams[0], 0.0, 0.0);
1035
0
            break;
1036
1037
            /*--------------------------------------------------------------
1038
             * Eckert IV
1039
             *-------------------------------------------------------------*/
1040
0
        case 14:
1041
0
            poSpatialRef->SetEckertIV(sTABProj.adProjParams[0], 0.0, 0.0);
1042
0
            break;
1043
1044
            /*--------------------------------------------------------------
1045
             * Eckert VI
1046
             *-------------------------------------------------------------*/
1047
0
        case 15:
1048
0
            poSpatialRef->SetEckertVI(sTABProj.adProjParams[0], 0.0, 0.0);
1049
0
            break;
1050
1051
            /*--------------------------------------------------------------
1052
             * Sinusoidal
1053
             *-------------------------------------------------------------*/
1054
0
        case 16:
1055
0
            poSpatialRef->SetSinusoidal(sTABProj.adProjParams[0], 0.0, 0.0);
1056
0
            break;
1057
1058
            /*--------------------------------------------------------------
1059
             * Gall Stereographic
1060
             *-------------------------------------------------------------*/
1061
0
        case 17:
1062
0
            poSpatialRef->SetGS(sTABProj.adProjParams[0], 0.0, 0.0);
1063
0
            break;
1064
1065
            /*--------------------------------------------------------------
1066
             * New Zealand Map Grid
1067
             *-------------------------------------------------------------*/
1068
0
        case 18:
1069
0
            poSpatialRef->SetNZMG(
1070
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
1071
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3]);
1072
0
            break;
1073
1074
            /*--------------------------------------------------------------
1075
             * Lambert Conic Conformal (Belgium)
1076
             *-------------------------------------------------------------*/
1077
0
        case 19:
1078
0
            poSpatialRef->SetLCCB(
1079
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
1080
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
1081
0
                sTABProj.adProjParams[4], sTABProj.adProjParams[5]);
1082
0
            break;
1083
1084
            /*--------------------------------------------------------------
1085
             * Stereographic
1086
             *-------------------------------------------------------------*/
1087
0
        case 20:
1088
0
            poSpatialRef->SetStereographic(
1089
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
1090
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
1091
0
                sTABProj.adProjParams[4]);
1092
0
            break;
1093
1094
            /*--------------------------------------------------------------
1095
             * Swiss Oblique Mercator / Cylindrical
1096
             *-------------------------------------------------------------*/
1097
0
        case 25:
1098
0
            poSpatialRef->SetSOC(
1099
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
1100
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3]);
1101
0
            break;
1102
1103
            /*--------------------------------------------------------------
1104
             * Regional Mercator (regular mercator with a latitude).
1105
             *-------------------------------------------------------------*/
1106
0
        case 26:
1107
0
            poSpatialRef->SetMercator2SP(sTABProj.adProjParams[1], 0.0,
1108
0
                                         sTABProj.adProjParams[0], 0.0, 0.0);
1109
0
            break;
1110
1111
            /*--------------------------------------------------------------
1112
             * Polyconic
1113
             *-------------------------------------------------------------*/
1114
0
        case 27:
1115
0
            poSpatialRef->SetPolyconic(
1116
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
1117
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3]);
1118
0
            break;
1119
1120
            /*--------------------------------------------------------------
1121
             * Cassini/Soldner
1122
             *-------------------------------------------------------------*/
1123
0
        case 30:
1124
0
            poSpatialRef->SetCS(
1125
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
1126
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3]);
1127
0
            break;
1128
1129
            /*--------------------------------------------------------------
1130
             * Oblique Stereographic
1131
             *-------------------------------------------------------------*/
1132
0
        case 31:
1133
0
            poSpatialRef->SetOS(
1134
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
1135
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3],
1136
0
                sTABProj.adProjParams[4]);
1137
0
            break;
1138
1139
            /*--------------------------------------------------------------
1140
             * Krovak
1141
             *-------------------------------------------------------------*/
1142
0
        case 32:
1143
0
            poSpatialRef->SetKrovak(
1144
0
                sTABProj.adProjParams[1],   // dfCenterLat
1145
0
                sTABProj.adProjParams[0],   // dfCenterLong
1146
0
                sTABProj.adProjParams[3],   // dfAzimuth
1147
0
                sTABProj.adProjParams[2],   // dfPseudoStdParallelLat
1148
0
                1.0,                        // dfScale
1149
0
                sTABProj.adProjParams[4],   // dfFalseEasting
1150
0
                sTABProj.adProjParams[5]);  // dfFalseNorthing
1151
0
            break;
1152
1153
            /*--------------------------------------------------------------
1154
             * Equidistant Cylindrical / Equirectangular
1155
             *-------------------------------------------------------------*/
1156
0
        case 33:
1157
0
            poSpatialRef->SetEquirectangular(
1158
0
                sTABProj.adProjParams[1], sTABProj.adProjParams[0],
1159
0
                sTABProj.adProjParams[2], sTABProj.adProjParams[3]);
1160
0
            break;
1161
1162
0
        default:
1163
0
            poSpatialRef->SetProjection(CPLSPrintf(
1164
0
                "Unhandled MapInfo projection method %d", sTABProj.nProjId));
1165
0
            break;
1166
0
    }
1167
1168
    /*-----------------------------------------------------------------
1169
     * Local (nonearth) coordinate systems have no Geographic relationship
1170
     * so we just return from here.
1171
     *----------------------------------------------------------------*/
1172
0
    if (sTABProj.nProjId == 0)
1173
0
        return poSpatialRef;
1174
1175
    /*-----------------------------------------------------------------
1176
     * Set the datum.  We are only given the X, Y and Z shift for
1177
     * the datum, so for now we just synthesize a name from this.
1178
     * It would be better if we could lookup a name based on the shift.
1179
     *
1180
     * Since we have already encountered files in which adDatumParams[] values
1181
     * were in the order of 1e-150 when they should have actually been zeros,
1182
     * we will use an epsilon in our scan instead of looking for equality.
1183
     *----------------------------------------------------------------*/
1184
0
    const MapInfoDatumInfo *psDatumInfo = nullptr;
1185
1186
0
    for (int iDatumInfo = 0; asDatumInfoList[iDatumInfo].nMapInfoDatumID != -1;
1187
0
         iDatumInfo++)
1188
0
    {
1189
0
        psDatumInfo = asDatumInfoList + iDatumInfo;
1190
1191
0
        if (TAB_EQUAL(psDatumInfo->nEllipsoid, sTABProj.nEllipsoidId) &&
1192
0
            ((sTABProj.nDatumId > 0 &&
1193
0
              sTABProj.nDatumId == psDatumInfo->nMapInfoDatumID) ||
1194
0
             (sTABProj.nDatumId <= 0 &&
1195
0
              TAB_EQUAL(psDatumInfo->dfShiftX, sTABProj.dDatumShiftX) &&
1196
0
              TAB_EQUAL(psDatumInfo->dfShiftY, sTABProj.dDatumShiftY) &&
1197
0
              TAB_EQUAL(psDatumInfo->dfShiftZ, sTABProj.dDatumShiftZ) &&
1198
0
              TAB_EQUAL(psDatumInfo->dfDatumParm0, sTABProj.adDatumParams[0]) &&
1199
0
              TAB_EQUAL(psDatumInfo->dfDatumParm1, sTABProj.adDatumParams[1]) &&
1200
0
              TAB_EQUAL(psDatumInfo->dfDatumParm2, sTABProj.adDatumParams[2]) &&
1201
0
              TAB_EQUAL(psDatumInfo->dfDatumParm3, sTABProj.adDatumParams[3]) &&
1202
0
              TAB_EQUAL(psDatumInfo->dfDatumParm4, sTABProj.adDatumParams[4]))))
1203
0
            break;
1204
1205
0
        psDatumInfo = nullptr;
1206
0
    }
1207
1208
0
    char szDatumName[200] = {};
1209
0
    if (psDatumInfo == nullptr)
1210
0
    {
1211
0
        if (sTABProj.adDatumParams[0] == 0.0 &&
1212
0
            sTABProj.adDatumParams[1] == 0.0 &&
1213
0
            sTABProj.adDatumParams[2] == 0.0 &&
1214
0
            sTABProj.adDatumParams[3] == 0.0 &&
1215
0
            sTABProj.adDatumParams[4] == 0.0)
1216
0
        {
1217
0
            snprintf(szDatumName, sizeof(szDatumName),
1218
0
                     "MIF 999,%u,%.15g,%.15g,%.15g", sTABProj.nEllipsoidId,
1219
0
                     sTABProj.dDatumShiftX, sTABProj.dDatumShiftY,
1220
0
                     sTABProj.dDatumShiftZ);
1221
0
        }
1222
0
        else
1223
0
        {
1224
0
            snprintf(
1225
0
                szDatumName, sizeof(szDatumName),
1226
0
                "MIF 9999,%u,%.15g,%.15g,%.15g,%.15g,%.15g,%.15g,%.15g,%.15g",
1227
0
                sTABProj.nEllipsoidId, sTABProj.dDatumShiftX,
1228
0
                sTABProj.dDatumShiftY, sTABProj.dDatumShiftZ,
1229
0
                sTABProj.adDatumParams[0], sTABProj.adDatumParams[1],
1230
0
                sTABProj.adDatumParams[2], sTABProj.adDatumParams[3],
1231
0
                sTABProj.adDatumParams[4]);
1232
0
        }
1233
0
    }
1234
0
    else if (strlen(psDatumInfo->pszOGCDatumName) > 0)
1235
0
    {
1236
0
        CPLStrlcpy(szDatumName, psDatumInfo->pszOGCDatumName,
1237
0
                   sizeof(szDatumName));
1238
0
    }
1239
0
    else
1240
0
    {
1241
0
        snprintf(szDatumName, sizeof(szDatumName), "MIF %d",
1242
0
                 psDatumInfo->nMapInfoDatumID);
1243
0
    }
1244
1245
    /*-----------------------------------------------------------------
1246
     * Set the spheroid.
1247
     *----------------------------------------------------------------*/
1248
0
    double dfSemiMajor = 0.0;
1249
0
    double dfInvFlattening = 0.0;
1250
0
    const char *pszSpheroidName = nullptr;
1251
1252
0
    for (int i = 0; asSpheroidInfoList[i].nMapInfoId != -1; i++)
1253
0
    {
1254
0
        if (asSpheroidInfoList[i].nMapInfoId == sTABProj.nEllipsoidId)
1255
0
        {
1256
0
            dfSemiMajor = asSpheroidInfoList[i].dfA;
1257
0
            dfInvFlattening = asSpheroidInfoList[i].dfInvFlattening;
1258
0
            pszSpheroidName = asSpheroidInfoList[i].pszMapinfoName;
1259
0
            break;
1260
0
        }
1261
0
    }
1262
1263
    // use WGS 84 if nothing is known.
1264
0
    if (pszSpheroidName == nullptr)
1265
0
    {
1266
0
        pszSpheroidName = "unknown";
1267
0
        dfSemiMajor = 6378137.0;
1268
0
        dfInvFlattening = 298.257223563;
1269
0
    }
1270
1271
    /*-----------------------------------------------------------------
1272
     * Set the prime meridian.
1273
     *----------------------------------------------------------------*/
1274
0
    double dfPMOffset = 0.0;
1275
0
    const char *pszPMName = "Greenwich";
1276
1277
0
    if (/*sTABProj.nDatumId == 9999 ||*/ sTABProj.adDatumParams[4] != 0.0)
1278
0
    {
1279
0
        dfPMOffset = sTABProj.adDatumParams[4];
1280
1281
0
        if (fabs(dfPMOffset - 2.337229166667) < 1e-10)
1282
0
            pszPMName = "Paris";
1283
0
        else
1284
0
            pszPMName = "non-Greenwich";
1285
0
    }
1286
1287
    /*-----------------------------------------------------------------
1288
     * Create a GEOGCS definition.
1289
     *----------------------------------------------------------------*/
1290
1291
0
    poSpatialRef->SetGeogCS("unnamed", szDatumName, pszSpheroidName,
1292
0
                            dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset,
1293
0
                            SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV));
1294
1295
0
    if (psDatumInfo != nullptr)
1296
0
    {
1297
0
        if (CPLTestBool(
1298
0
                CPLGetConfigOption("MITAB_SET_TOWGS84_ON_KNOWN_DATUM", "NO")))
1299
0
        {
1300
0
            poSpatialRef->SetTOWGS84(
1301
0
                psDatumInfo->dfShiftX, psDatumInfo->dfShiftY,
1302
0
                psDatumInfo->dfShiftZ,
1303
0
                psDatumInfo->dfDatumParm0 == 0
1304
0
                    ? 0
1305
0
                    : -psDatumInfo->dfDatumParm0, /* avoids 0 to be transformed
1306
                                                     into -0 */
1307
0
                psDatumInfo->dfDatumParm1 == 0 ? 0 : -psDatumInfo->dfDatumParm1,
1308
0
                psDatumInfo->dfDatumParm2 == 0 ? 0 : -psDatumInfo->dfDatumParm2,
1309
0
                psDatumInfo->dfDatumParm3);
1310
0
        }
1311
0
    }
1312
0
    else
1313
0
    {
1314
0
        poSpatialRef->SetTOWGS84(
1315
0
            sTABProj.dDatumShiftX, sTABProj.dDatumShiftY, sTABProj.dDatumShiftZ,
1316
0
            sTABProj.adDatumParams[0] == 0 ? 0 : -sTABProj.adDatumParams[0],
1317
0
            sTABProj.adDatumParams[1] == 0 ? 0 : -sTABProj.adDatumParams[1],
1318
0
            sTABProj.adDatumParams[2] == 0 ? 0 : -sTABProj.adDatumParams[2],
1319
0
            sTABProj.adDatumParams[3]);
1320
0
    }
1321
1322
    /*-----------------------------------------------------------------
1323
     * Special case for Google Mercator (datum=157, ellipse=54, gdal #4115)
1324
     *----------------------------------------------------------------*/
1325
0
    if (sTABProj.nProjId == 10 && sTABProj.nDatumId == 157 &&
1326
0
        sTABProj.nEllipsoidId == 54)
1327
0
    {
1328
0
        poSpatialRef->SetNode("PROJCS", "WGS 84 / Pseudo-Mercator");
1329
0
        poSpatialRef->SetExtension(
1330
0
            "PROJCS", "PROJ4",
1331
0
            "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 "
1332
0
            "+y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  +no_defs");
1333
0
    }
1334
1335
    /*-----------------------------------------------------------------
1336
     * Special case for France Lambert-93
1337
     *----------------------------------------------------------------*/
1338
0
    if (sTABProj.nProjId == 3 && sTABProj.nDatumId == 33 &&
1339
0
        sTABProj.nEllipsoidId == 0 &&
1340
0
        TAB_EQUAL(poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0),
1341
0
                  3.0) &&
1342
0
        TAB_EQUAL(poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0),
1343
0
                  46.5))
1344
0
    {
1345
0
        poSpatialRef->SetNode("PROJCS", "RGF93 / Lambert-93");
1346
0
        poSpatialRef->SetNode("PROJCS|GEOGCS", "RGF93");
1347
0
        poSpatialRef->SetNode("PROJCS|GEOGCS|DATUM",
1348
0
                              "Reseau_Geodesique_Francais_1993");
1349
0
    }
1350
1351
0
    if (sTABProj.nProjId == 3)
1352
0
    {
1353
        // If the LCC_2SP can be turned into a LCC_1SP that has the same
1354
        // latitude of origin, then it is a better candidate
1355
0
        OGRSpatialReference *poLCC1SP = poSpatialRef->convertToOtherProjection(
1356
0
            SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP);
1357
0
        if (poLCC1SP)
1358
0
        {
1359
0
            if (TAB_EQUAL(
1360
0
                    poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,
1361
0
                                                  0.0),
1362
0
                    poLCC1SP->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)))
1363
0
            {
1364
0
                delete poSpatialRef;
1365
0
                poSpatialRef = poLCC1SP;
1366
0
            }
1367
0
            else
1368
0
            {
1369
0
                delete poLCC1SP;
1370
0
            }
1371
0
        }
1372
0
    }
1373
1374
    /* For LCC, standard parallel 1 and 2 can be switched indifferently */
1375
    /* So the MapInfo order and the EPSG order are not generally identical */
1376
    /* which may cause recognition problems when reading in MapInfo */
1377
0
    int nEPSGCandidateCode = 0;
1378
0
    if (sTABProj.nProjId == 3)
1379
0
    {
1380
0
        double dfCenterLong = sTABProj.adProjParams[0];
1381
0
        double dfCenterLat = sTABProj.adProjParams[1];
1382
0
        double dfStdP1 = sTABProj.adProjParams[2];
1383
0
        double dfStdP2 = sTABProj.adProjParams[3];
1384
1385
0
        for (size_t i = 0;
1386
0
             i < sizeof(asMapInfoLCCSRSList) / sizeof(asMapInfoLCCSRSList[0]);
1387
0
             i++)
1388
0
        {
1389
0
            if (sTABProj.nDatumId == asMapInfoLCCSRSList[i].nMapInfoDatumID &&
1390
0
                TAB_EQUAL(dfCenterLong, asMapInfoLCCSRSList[i].dfCenterLong) &&
1391
0
                TAB_EQUAL(dfCenterLat, asMapInfoLCCSRSList[i].dfCenterLat))
1392
0
            {
1393
0
                if (TAB_EQUAL(dfStdP1, asMapInfoLCCSRSList[i].dfStdP1) &&
1394
0
                    (TAB_EQUAL(dfStdP2, asMapInfoLCCSRSList[i].dfStdP2) ||
1395
                     // EPSG:3301 "Estonian Coordinate System of 1997"
1396
                     // MapInfo uses a less accurate value of StdP2 than EPSG
1397
0
                     (TAB_EQUAL(asMapInfoLCCSRSList[i].dfStdP2, 59.33333333) &&
1398
0
                      TAB_EQUAL(dfStdP2, 59.3333333333333))))
1399
0
                {
1400
0
                    if (nEPSGCandidateCode)
1401
0
                    {
1402
0
                        poSpatialRef->SetAuthority("PROJCS", "EPSG",
1403
0
                                                   nEPSGCandidateCode);
1404
0
                        nEPSGCandidateCode = 0;
1405
0
                        break;
1406
0
                    }
1407
0
                    if (asMapInfoLCCSRSList[i].bReverseStdP)
1408
0
                    {
1409
0
                        CPLDebug("MITAB",
1410
0
                                 "Switching standard parallel 1 and 2");
1411
0
                        poSpatialRef->SetLCC(
1412
0
                            sTABProj.adProjParams[3], sTABProj.adProjParams[2],
1413
0
                            sTABProj.adProjParams[1], sTABProj.adProjParams[0],
1414
0
                            sTABProj.adProjParams[4], sTABProj.adProjParams[5]);
1415
0
                    }
1416
0
                    if (asMapInfoLCCSRSList[i].nEPSGCode > 0)
1417
0
                        nEPSGCandidateCode = asMapInfoLCCSRSList[i].nEPSGCode;
1418
0
                    else
1419
0
                        break;
1420
0
                }
1421
0
            }
1422
0
        }
1423
0
    }
1424
1425
    /*-----------------------------------------------------------------
1426
     * Apply linear units. Do that only after all above manipulations of
1427
     * projection parameters.
1428
     *----------------------------------------------------------------*/
1429
0
    if (sTABProj.nProjId != 0 && sTABProj.nProjId != 1 &&
1430
0
        CPLAtof(pszUnitsConv) != 1)
1431
0
    {
1432
0
        poSpatialRef->SetTargetLinearUnits(nullptr, pszUnitsName,
1433
0
                                           CPLAtof(pszUnitsConv));
1434
0
    }
1435
1436
0
    if (nEPSGCandidateCode)
1437
0
    {
1438
0
        OGRSpatialReference oTmp;
1439
0
        oTmp.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1440
0
        if (oTmp.importFromEPSG(nEPSGCandidateCode) == OGRERR_NONE)
1441
0
        {
1442
0
            *poSpatialRef = std::move(oTmp);
1443
0
        }
1444
0
    }
1445
1446
    /*-----------------------------------------------------------------
1447
     * Special case for Philippine Reference System 1992, to override
1448
     * the MapInfo ellipsoid=8 "Clarke 1866 (modified for Michigan)"
1449
     * by the regular Clarke 1866 of EPSG
1450
     *----------------------------------------------------------------*/
1451
0
    if (sTABProj.nDatumId == 1031 && sTABProj.nEllipsoidId == 8)
1452
0
    {
1453
0
        OGRSpatialReference oSRS_EPSG_4683;
1454
0
        if (oSRS_EPSG_4683.importFromEPSG(4683) == OGRERR_NONE)
1455
0
            poSpatialRef->CopyGeogCSFrom(&oSRS_EPSG_4683);
1456
0
    }
1457
1458
    /*-----------------------------------------------------------------
1459
     * Special case for Kertau (RSO), to override
1460
     * the MapInfo ellipsoid=40 "Everest (India 1956)", 6377301.243, 300.80174
1461
     * with EPSG's "Everest 1830 (RSO 1969)",6377295.664,300.8017
1462
     *----------------------------------------------------------------*/
1463
0
    if (sTABProj.nDatumId == 164 && sTABProj.nEllipsoidId == 40)
1464
0
    {
1465
0
        OGRSpatialReference oSRS_EPSG_4751;
1466
0
        if (oSRS_EPSG_4751.importFromEPSG(4751) == OGRERR_NONE)
1467
0
            poSpatialRef->CopyGeogCSFrom(&oSRS_EPSG_4751);
1468
0
    }
1469
1470
0
    return poSpatialRef;
1471
0
}
1472
1473
static int MITABGetCustomDatum(const OGRSpatialReference *poSpatialRef,
1474
                               TABProjInfo &sTABProj)
1475
0
{
1476
0
    double adfTOWGS[7] = {0};
1477
0
    if (OGRERR_NONE != poSpatialRef->GetTOWGS84(
1478
0
                           adfTOWGS, sizeof(adfTOWGS) / sizeof(adfTOWGS[0])))
1479
0
    {
1480
0
        return FALSE;
1481
0
    }
1482
0
    sTABProj.nDatumId = 9999;
1483
0
    sTABProj.dDatumShiftX = adfTOWGS[0];
1484
0
    sTABProj.dDatumShiftY = adfTOWGS[1];
1485
0
    sTABProj.dDatumShiftZ = adfTOWGS[2];
1486
0
    sTABProj.adDatumParams[0] = -adfTOWGS[3];
1487
0
    sTABProj.adDatumParams[1] = -adfTOWGS[4];
1488
0
    sTABProj.adDatumParams[2] = -adfTOWGS[5];
1489
0
    sTABProj.adDatumParams[3] = adfTOWGS[6];
1490
1491
0
    int nSpheroidId = -1;
1492
1493
0
    const char *pszWKTSpheroid = poSpatialRef->GetAttrValue("SPHEROID");
1494
0
    for (int i = 0; asSpheroidInfoList[i].nMapInfoId != -1; i++)
1495
0
    {
1496
0
        if (EQUAL(pszWKTSpheroid, asSpheroidInfoList[i].pszMapinfoName))
1497
0
        {
1498
0
            nSpheroidId = asSpheroidInfoList[i].nMapInfoId;
1499
0
            break;
1500
0
        }
1501
0
    }
1502
1503
0
    if (nSpheroidId == -1)
1504
0
    {
1505
0
        double adSemiMajor = poSpatialRef->GetSemiMajor();
1506
0
        double adInvFlattening = poSpatialRef->GetInvFlattening();
1507
1508
0
        for (int i = 0; asSpheroidInfoList[i].nMapInfoId != -1; i++)
1509
0
        {
1510
0
            if (CPLIsEqual(adSemiMajor, asSpheroidInfoList[i].dfA) &&
1511
0
                CPLIsEqual(adInvFlattening,
1512
0
                           asSpheroidInfoList[i].dfInvFlattening))
1513
0
            {
1514
0
                nSpheroidId = asSpheroidInfoList[i].nMapInfoId;
1515
0
                break;
1516
0
            }
1517
0
        }
1518
0
    }
1519
0
    if (nSpheroidId == -1)
1520
0
    {
1521
0
        CPLDebug(
1522
0
            "MITAB",
1523
0
            "Cannot find MapInfo spheroid matching %s. Defaulting to WGS 84",
1524
0
            pszWKTSpheroid);
1525
0
        nSpheroidId = 28; /* WGS 84 */
1526
0
    }
1527
0
    sTABProj.nEllipsoidId = static_cast<GByte>(nSpheroidId);
1528
0
    return TRUE;
1529
0
}
1530
1531
int TABFileGetTABProjFromSpatialRef(const OGRSpatialReference *poSpatialRef,
1532
                                    TABProjInfo &sTABProj, int &nParamCount)
1533
0
{
1534
    /*-----------------------------------------------------------------
1535
     * Initialize TABProjInfo
1536
     *----------------------------------------------------------------*/
1537
0
    sTABProj.nProjId = 0;
1538
0
    sTABProj.nEllipsoidId = 0; /* how will we set this? */
1539
0
    sTABProj.nUnitsId = 7;
1540
0
    sTABProj.adProjParams[0] = sTABProj.adProjParams[1] = 0.0;
1541
0
    sTABProj.adProjParams[2] = sTABProj.adProjParams[3] = 0.0;
1542
0
    sTABProj.adProjParams[4] = sTABProj.adProjParams[5] = 0.0;
1543
1544
0
    sTABProj.nDatumId = 0;
1545
0
    sTABProj.dDatumShiftX = 0.0;
1546
0
    sTABProj.dDatumShiftY = 0.0;
1547
0
    sTABProj.dDatumShiftZ = 0.0;
1548
0
    sTABProj.adDatumParams[0] = 0.0;
1549
0
    sTABProj.adDatumParams[1] = 0.0;
1550
0
    sTABProj.adDatumParams[2] = 0.0;
1551
0
    sTABProj.adDatumParams[3] = 0.0;
1552
0
    sTABProj.adDatumParams[4] = 0.0;
1553
1554
0
    sTABProj.nAffineFlag = 0;
1555
0
    sTABProj.nAffineUnits = 7;
1556
0
    sTABProj.dAffineParamA = 0.0;
1557
0
    sTABProj.dAffineParamB = 0.0;
1558
0
    sTABProj.dAffineParamC = 0.0;
1559
0
    sTABProj.dAffineParamD = 0.0;
1560
0
    sTABProj.dAffineParamE = 0.0;
1561
0
    sTABProj.dAffineParamF = 0.0;
1562
1563
    /*-----------------------------------------------------------------
1564
     * Get the linear units and conversion.
1565
     *----------------------------------------------------------------*/
1566
0
    const char *pszLinearUnits = nullptr;
1567
0
    double dfLinearConv = poSpatialRef->GetLinearUnits(&pszLinearUnits);
1568
0
    if (dfLinearConv == 0.0)
1569
0
        dfLinearConv = 1.0;
1570
1571
    // Get datum information
1572
0
    const char *pszWKTDatum = poSpatialRef->GetAttrValue("DATUM");
1573
1574
0
    const auto GetDatumCode = [](const OGRSpatialReference *poSRS)
1575
0
    {
1576
0
        const char *pszDatumAuthority = poSRS->GetAuthorityName("DATUM");
1577
0
        const char *pszDatumCode = poSRS->GetAuthorityCode("DATUM");
1578
0
        if (pszDatumCode && pszDatumAuthority &&
1579
0
            EQUAL(pszDatumAuthority, "EPSG"))
1580
0
        {
1581
0
            return atoi(pszDatumCode);
1582
0
        }
1583
0
        return -1;
1584
0
    };
1585
1586
0
    int nDatumEPSGCode = GetDatumCode(poSpatialRef);
1587
0
    if (nDatumEPSGCode < 0)
1588
0
    {
1589
0
        const auto GetDatumCodeFromCRSIndirect =
1590
0
            [&GetDatumCode](const OGRSpatialReference *poSRS,
1591
0
                            const char *pszNode)
1592
0
        {
1593
0
            const char *pszAuthorityName = poSRS->GetAuthorityName(pszNode);
1594
0
            const char *pszAuthorityCode = poSRS->GetAuthorityCode(pszNode);
1595
0
            if (pszAuthorityName && pszAuthorityCode)
1596
0
            {
1597
0
                OGRSpatialReference oSRSTmp;
1598
0
                if (oSRSTmp.SetFromUserInput(CPLSPrintf(
1599
0
                        "%s:%s", pszAuthorityName, pszAuthorityCode)) ==
1600
0
                    OGRERR_NONE)
1601
0
                {
1602
0
                    return GetDatumCode(&oSRSTmp);
1603
0
                }
1604
0
            }
1605
0
            return -1;
1606
0
        };
1607
1608
        // When the CRS is built from WKT2 CRS string, the DATUM code will
1609
        // typically be absent from the CRS string.
1610
        // Try to get the AUTHORITY:CODE from the CRS to instantiate
1611
        // a temporary CRS and get its DATUM code.
1612
0
        nDatumEPSGCode = GetDatumCodeFromCRSIndirect(poSpatialRef, nullptr);
1613
0
        if (nDatumEPSGCode < 0 && !poSpatialRef->IsGeographic())
1614
0
        {
1615
            // If there's no AUTHORITY:CODE on the CRS, then retry with its
1616
            // geographic CRS
1617
0
            nDatumEPSGCode =
1618
0
                GetDatumCodeFromCRSIndirect(poSpatialRef, "GEOGCS");
1619
0
        }
1620
0
    }
1621
1622
    /*-----------------------------------------------------------------
1623
     * Transform the projection and projection parameters.
1624
     *----------------------------------------------------------------*/
1625
0
    const char *pszProjection = poSpatialRef->GetAttrValue("PROJECTION");
1626
0
    double *params = sTABProj.adProjParams;
1627
0
    nParamCount = 0;
1628
1629
0
    if (pszProjection == nullptr &&
1630
0
        poSpatialRef->GetAttrNode("GEOGCS") == nullptr)
1631
0
    {
1632
        /* nonearth */
1633
0
        sTABProj.nProjId = 0;
1634
0
    }
1635
1636
0
    else if (pszProjection == nullptr)
1637
0
    {
1638
0
        sTABProj.nProjId = 1;
1639
0
    }
1640
1641
0
    else if (EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA))
1642
0
    {
1643
0
        sTABProj.nProjId = 9;
1644
0
        params[0] =
1645
0
            poSpatialRef->GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
1646
0
        params[1] =
1647
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
1648
0
        params[2] =
1649
0
            poSpatialRef->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
1650
0
        params[3] =
1651
0
            poSpatialRef->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
1652
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1653
0
        params[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1654
0
        nParamCount = 6;
1655
0
    }
1656
1657
0
    else if (EQUAL(pszProjection, SRS_PT_AZIMUTHAL_EQUIDISTANT))
1658
0
    {
1659
0
        sTABProj.nProjId = 5;
1660
0
        params[0] =
1661
0
            poSpatialRef->GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
1662
0
        params[1] =
1663
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
1664
0
        params[2] = 90.0;
1665
0
        nParamCount = 3;
1666
1667
0
        if (std::abs((std::abs(params[1]) - 90)) > 0.001)
1668
0
            sTABProj.nProjId = 28;
1669
0
    }
1670
1671
0
    else if (EQUAL(pszProjection, SRS_PT_CYLINDRICAL_EQUAL_AREA))
1672
0
    {
1673
0
        sTABProj.nProjId = 2;
1674
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1675
0
        params[1] =
1676
0
            poSpatialRef->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
1677
0
        nParamCount = 2;
1678
0
    }
1679
1680
0
    else if (EQUAL(pszProjection, SRS_PT_ECKERT_IV))
1681
0
    {
1682
0
        sTABProj.nProjId = 14;
1683
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1684
0
        nParamCount = 1;
1685
0
    }
1686
1687
0
    else if (EQUAL(pszProjection, SRS_PT_ECKERT_VI))
1688
0
    {
1689
0
        sTABProj.nProjId = 15;
1690
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1691
0
        nParamCount = 1;
1692
0
    }
1693
1694
0
    else if (EQUAL(pszProjection, SRS_PT_EQUIDISTANT_CONIC))
1695
0
    {
1696
0
        sTABProj.nProjId = 6;
1697
0
        params[0] =
1698
0
            poSpatialRef->GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
1699
0
        params[1] =
1700
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
1701
0
        params[2] =
1702
0
            poSpatialRef->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
1703
0
        params[3] =
1704
0
            poSpatialRef->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
1705
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1706
0
        params[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1707
0
        nParamCount = 6;
1708
0
    }
1709
1710
0
    else if (EQUAL(pszProjection, SRS_PT_GALL_STEREOGRAPHIC))
1711
0
    {
1712
0
        sTABProj.nProjId = 17;
1713
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1714
0
        nParamCount = 1;
1715
0
    }
1716
1717
0
    else if (EQUAL(pszProjection, SRS_PT_HOTINE_OBLIQUE_MERCATOR))
1718
0
    {
1719
0
        if (std::abs(poSpatialRef->GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,
1720
0
                                                   90.0) -
1721
0
                     90.0) < 1e-8)
1722
0
        {
1723
0
            sTABProj.nProjId = 7;
1724
0
            params[0] =
1725
0
                poSpatialRef->GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
1726
0
            params[1] =
1727
0
                poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
1728
0
            params[2] = poSpatialRef->GetNormProjParm(SRS_PP_AZIMUTH, 0.0);
1729
0
            params[3] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
1730
0
            params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1731
0
            params[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1732
0
            nParamCount = 6;
1733
0
        }
1734
0
        else
1735
0
        {
1736
0
            sTABProj.nProjId = 35;
1737
0
            params[0] =
1738
0
                poSpatialRef->GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
1739
0
            params[1] =
1740
0
                poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
1741
0
            params[2] = poSpatialRef->GetNormProjParm(SRS_PP_AZIMUTH, 0.0);
1742
0
            params[3] =
1743
0
                poSpatialRef->GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE, 0.0);
1744
0
            params[4] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
1745
0
            params[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1746
0
            params[6] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1747
0
            nParamCount = 7;
1748
0
        }
1749
0
    }
1750
1751
0
    else if (EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA))
1752
0
    {
1753
0
        sTABProj.nProjId = 4;
1754
0
        params[0] =
1755
0
            poSpatialRef->GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
1756
0
        params[1] =
1757
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
1758
0
        params[2] = 90.0;
1759
0
        nParamCount = 3;
1760
1761
0
        if (std::abs((std::abs(params[1]) - 90)) > 0.001)
1762
0
            sTABProj.nProjId = 29;
1763
0
    }
1764
1765
0
    else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
1766
0
    {
1767
0
        sTABProj.nProjId = 3;
1768
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1769
0
        params[1] =
1770
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1771
0
        params[2] =
1772
0
            poSpatialRef->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
1773
0
        params[3] =
1774
0
            poSpatialRef->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
1775
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1776
0
        params[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1777
0
        nParamCount = 6;
1778
0
    }
1779
1780
0
    else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
1781
0
    {
1782
0
        OGRSpatialReference *poOtherSRS =
1783
0
            poSpatialRef->convertToOtherProjection(
1784
0
                SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP);
1785
0
        if (poOtherSRS)
1786
0
        {
1787
0
            sTABProj.nProjId = 3;
1788
0
            params[0] =
1789
0
                poOtherSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1790
0
            params[1] =
1791
0
                poOtherSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1792
0
            params[2] =
1793
0
                poOtherSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
1794
0
            params[3] =
1795
0
                poOtherSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
1796
0
            params[4] = poOtherSRS->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1797
0
            params[5] = poOtherSRS->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1798
0
            nParamCount = 6;
1799
0
            delete poOtherSRS;
1800
0
        }
1801
0
    }
1802
1803
0
    else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM))
1804
0
    {
1805
0
        sTABProj.nProjId = 19;
1806
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1807
0
        params[1] =
1808
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1809
0
        params[2] =
1810
0
            poSpatialRef->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
1811
0
        params[3] =
1812
0
            poSpatialRef->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
1813
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1814
0
        params[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1815
0
        nParamCount = 6;
1816
0
    }
1817
1818
0
    else if (EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
1819
0
    {
1820
0
        sTABProj.nProjId = 10;
1821
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1822
0
        params[1] =
1823
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1824
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
1825
0
        nParamCount = 1;  // FIXME for MIF export ?
1826
1827
0
        if (params[1] != 0.0)
1828
0
        {
1829
0
            sTABProj.nProjId = 26;
1830
0
            nParamCount = 2;  // FIXME for MIF export ?
1831
0
        }
1832
0
    }
1833
1834
0
    else if (EQUAL(pszProjection, SRS_PT_MERCATOR_2SP))
1835
0
    {
1836
0
        sTABProj.nProjId = 26;
1837
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1838
0
        params[1] =
1839
0
            poSpatialRef->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
1840
0
        nParamCount = 2;  // FIXME for MIF export ?
1841
0
    }
1842
1843
0
    else if (EQUAL(pszProjection, SRS_PT_MILLER_CYLINDRICAL))
1844
0
    {
1845
0
        sTABProj.nProjId = 11;
1846
0
        params[0] =
1847
0
            poSpatialRef->GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
1848
0
        nParamCount = 1;
1849
0
    }
1850
1851
0
    else if (EQUAL(pszProjection, SRS_PT_MOLLWEIDE))
1852
0
    {
1853
0
        sTABProj.nProjId = 13;
1854
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1855
0
        nParamCount = 1;
1856
0
    }
1857
1858
0
    else if (EQUAL(pszProjection, SRS_PT_NEW_ZEALAND_MAP_GRID))
1859
0
    {
1860
0
        sTABProj.nProjId = 18;
1861
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1862
0
        params[1] =
1863
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1864
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1865
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1866
0
        nParamCount = 4;
1867
0
    }
1868
1869
0
    else if (EQUAL(pszProjection, SRS_PT_SWISS_OBLIQUE_CYLINDRICAL))
1870
0
    {
1871
0
        sTABProj.nProjId = 25;
1872
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1873
0
        params[1] =
1874
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1875
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1876
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1877
0
        nParamCount = 4;
1878
0
    }
1879
1880
    // Swiss Oblique expressed as Hotine Oblique Mercator Azimuth Center
1881
0
    else if (EQUAL(pszProjection,
1882
0
                   SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER) &&
1883
0
             std::abs(poSpatialRef->GetNormProjParm(SRS_PP_AZIMUTH, 90.0) -
1884
0
                      90.0) < 1e-8 &&
1885
0
             std::abs(poSpatialRef->GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,
1886
0
                                                    90.0) -
1887
0
                      90.0) < 1e-8 &&
1888
0
             std::abs(poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) -
1889
0
                      1.0) < 1e-8)
1890
0
    {
1891
0
        sTABProj.nProjId = 25;
1892
0
        params[0] =
1893
0
            poSpatialRef->GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
1894
0
        params[1] =
1895
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
1896
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1897
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1898
0
        nParamCount = 4;
1899
0
    }
1900
1901
0
    else if (EQUAL(pszProjection, SRS_PT_ROBINSON))
1902
0
    {
1903
0
        sTABProj.nProjId = 12;
1904
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1905
0
        nParamCount = 1;
1906
0
    }
1907
1908
0
    else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
1909
0
    {
1910
0
        sTABProj.nProjId = 16;
1911
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1912
0
        nParamCount = 1;
1913
0
    }
1914
1915
0
    else if (EQUAL(pszProjection, SRS_PT_STEREOGRAPHIC))
1916
0
    {
1917
0
        sTABProj.nProjId = 20;
1918
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1919
0
        params[1] =
1920
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1921
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
1922
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1923
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1924
0
        nParamCount = 5;
1925
0
    }
1926
1927
0
    else if (EQUAL(pszProjection, SRS_PT_OBLIQUE_STEREOGRAPHIC))
1928
0
    {
1929
0
        sTABProj.nProjId = 31;
1930
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1931
0
        params[1] =
1932
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1933
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
1934
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1935
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1936
0
        nParamCount = 5;
1937
0
    }
1938
1939
0
    else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
1940
0
    {
1941
0
        sTABProj.nProjId = 8;
1942
0
        if ((pszWKTDatum &&
1943
0
             EQUAL(pszWKTDatum, "Kartastokoordinaattijarjestelma_1966")) ||
1944
0
            nDatumEPSGCode == 6123)
1945
0
        {
1946
            // Special case for Finnish KKJ
1947
0
            sTABProj.nProjId = 24;
1948
0
        }
1949
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1950
0
        params[1] =
1951
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1952
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
1953
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1954
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1955
0
        nParamCount = 5;
1956
0
    }
1957
1958
0
    else if (EQUAL(pszProjection,
1959
0
                   SRS_PT_TRANSVERSE_MERCATOR_MI_21))  // Encom 2003
1960
0
    {
1961
0
        sTABProj.nProjId = 21;
1962
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1963
0
        params[1] =
1964
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1965
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
1966
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1967
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1968
0
        nParamCount = 5;
1969
0
    }
1970
1971
0
    else if (EQUAL(pszProjection,
1972
0
                   SRS_PT_TRANSVERSE_MERCATOR_MI_22))  // Encom 2003
1973
0
    {
1974
0
        sTABProj.nProjId = 22;
1975
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1976
0
        params[1] =
1977
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1978
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
1979
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1980
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1981
0
        nParamCount = 5;
1982
0
    }
1983
1984
0
    else if (EQUAL(pszProjection,
1985
0
                   SRS_PT_TRANSVERSE_MERCATOR_MI_23))  // Encom 2003
1986
0
    {
1987
0
        sTABProj.nProjId = 23;
1988
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
1989
0
        params[1] =
1990
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
1991
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
1992
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
1993
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1994
0
        nParamCount = 5;
1995
0
    }
1996
1997
0
    else if (EQUAL(pszProjection,
1998
0
                   SRS_PT_TRANSVERSE_MERCATOR_MI_24))  // Encom 2003
1999
0
    {
2000
0
        sTABProj.nProjId = 24;
2001
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
2002
0
        params[1] =
2003
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
2004
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
2005
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
2006
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
2007
0
        nParamCount = 5;
2008
0
    }
2009
2010
0
    else if (EQUAL(pszProjection, SRS_PT_CASSINI_SOLDNER))
2011
0
    {
2012
0
        sTABProj.nProjId = 30;
2013
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
2014
0
        params[1] =
2015
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
2016
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
2017
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
2018
0
        nParamCount = 4;
2019
0
    }
2020
2021
0
    else if (EQUAL(pszProjection, SRS_PT_POLYCONIC))
2022
0
    {
2023
0
        sTABProj.nProjId = 27;
2024
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
2025
0
        params[1] =
2026
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
2027
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
2028
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
2029
0
        nParamCount = 4;
2030
0
    }
2031
2032
0
    else if (EQUAL(pszProjection, SRS_PT_KROVAK))
2033
0
    {
2034
0
        sTABProj.nProjId = 32;
2035
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
2036
0
        params[1] =
2037
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
2038
0
        params[2] =
2039
0
            poSpatialRef->GetNormProjParm(SRS_PP_PSEUDO_STD_PARALLEL_1, 0.0);
2040
0
        params[3] = poSpatialRef->GetNormProjParm(SRS_PP_AZIMUTH, 0.0);
2041
0
        params[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
2042
0
        params[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
2043
0
        nParamCount = 6;
2044
0
    }
2045
2046
0
    else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
2047
0
    {
2048
0
        sTABProj.nProjId = 33;
2049
0
        params[0] = poSpatialRef->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
2050
0
        params[1] =
2051
0
            poSpatialRef->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
2052
0
        params[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
2053
0
        params[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
2054
0
        nParamCount = 4;
2055
0
    }
2056
2057
0
    else
2058
0
    {
2059
0
        CPLError(CE_Warning, CPLE_AppDefined,
2060
0
                 "No translation from %s to MapInfo known", pszProjection);
2061
0
    }
2062
2063
    /* ==============================================================
2064
     * Translate Datum and Ellipsoid
2065
     * ============================================================== */
2066
0
    const MapInfoDatumInfo *psDatumInfo = nullptr;
2067
2068
    /*-----------------------------------------------------------------
2069
     * Default to WGS84 if we have no datum at all.
2070
     *----------------------------------------------------------------*/
2071
0
    if (pszWKTDatum == nullptr)
2072
0
    {
2073
0
        CPLDebug("MITAB",
2074
0
                 "Cannot find MapInfo datum matching %d. Defaulting to WGS 84",
2075
0
                 nDatumEPSGCode);
2076
0
        psDatumInfo = asDatumInfoList + 0; /* WGS 84 */
2077
        // From MIF export code. FIXME?
2078
        // if( nProjection == 1 )
2079
        //    nProjection = 0;
2080
0
    }
2081
2082
    /*-----------------------------------------------------------------
2083
     * We know the MIF datum number, and need to look it up to
2084
     * translate into datum parameters.
2085
     *----------------------------------------------------------------*/
2086
0
    else if (STARTS_WITH_CI(pszWKTDatum, "MIF ") &&
2087
0
             atoi(pszWKTDatum + 4) != 999 && atoi(pszWKTDatum + 4) != 9999)
2088
0
    {
2089
0
        int nDatum = atoi(pszWKTDatum + 4);
2090
0
        for (int i = 0; asDatumInfoList[i].nMapInfoDatumID != -1; i++)
2091
0
        {
2092
0
            if (nDatum == asDatumInfoList[i].nMapInfoDatumID)
2093
0
            {
2094
0
                psDatumInfo = asDatumInfoList + i;
2095
0
                break;
2096
0
            }
2097
0
        }
2098
2099
0
        if (psDatumInfo == nullptr)
2100
0
        {
2101
0
            CPLDebug(
2102
0
                "MITAB",
2103
0
                "Cannot find MapInfo datum matching %s. Defaulting to WGS 84",
2104
0
                pszWKTDatum);
2105
0
            psDatumInfo = asDatumInfoList + 0; /* WGS 84 */
2106
0
        }
2107
0
    }
2108
2109
    /*-----------------------------------------------------------------
2110
     * We have the MIF datum parameters, and apply those directly.
2111
     *----------------------------------------------------------------*/
2112
0
    else if (STARTS_WITH_CI(pszWKTDatum, "MIF ") &&
2113
0
             (atoi(pszWKTDatum + 4) == 999 || atoi(pszWKTDatum + 4) == 9999))
2114
0
    {
2115
0
        sTABProj.nDatumId = static_cast<GInt16>(atoi(pszWKTDatum + 4));
2116
0
        char **papszFields =
2117
0
            CSLTokenizeStringComplex(pszWKTDatum + 4, ",", FALSE, TRUE);
2118
2119
0
        if (CSLCount(papszFields) >= 5)
2120
0
        {
2121
0
            sTABProj.nEllipsoidId = static_cast<GByte>(atoi(papszFields[1]));
2122
0
            sTABProj.dDatumShiftX = CPLAtof(papszFields[2]);
2123
0
            sTABProj.dDatumShiftY = CPLAtof(papszFields[3]);
2124
0
            sTABProj.dDatumShiftZ = CPLAtof(papszFields[4]);
2125
0
        }
2126
2127
0
        if (CSLCount(papszFields) >= 10)
2128
0
        {
2129
0
            sTABProj.adDatumParams[0] = CPLAtof(papszFields[5]);
2130
0
            sTABProj.adDatumParams[1] = CPLAtof(papszFields[6]);
2131
0
            sTABProj.adDatumParams[2] = CPLAtof(papszFields[7]);
2132
0
            sTABProj.adDatumParams[3] = CPLAtof(papszFields[8]);
2133
0
            sTABProj.adDatumParams[4] = CPLAtof(papszFields[9]);
2134
0
        }
2135
2136
0
        if (CSLCount(papszFields) < 5)
2137
0
        {
2138
0
            CPLDebug(
2139
0
                "MITAB",
2140
0
                "Cannot find MapInfo datum matching %s. Defaulting to WGS 84",
2141
0
                pszWKTDatum);
2142
0
            psDatumInfo = asDatumInfoList + 0; /* WGS 84 */
2143
0
        }
2144
2145
0
        CSLDestroy(papszFields);
2146
0
    }
2147
2148
    /*-----------------------------------------------------------------
2149
     * We have a "real" datum name, and possibly an EPSG code for the
2150
     * datum.  Try to look it up (using EPSG code first) and get the
2151
     * parameters.  If we don't find it with either just use WGS84.
2152
     *----------------------------------------------------------------*/
2153
0
    else
2154
0
    {
2155
0
        for (int i = 0; asDatumInfoList[i].nMapInfoDatumID != -1; i++)
2156
0
        {
2157
0
            if ((nDatumEPSGCode > 0 &&
2158
0
                 asDatumInfoList[i].nDatumEPSGCode == nDatumEPSGCode) ||
2159
0
                EQUAL(pszWKTDatum, asDatumInfoList[i].pszOGCDatumName))
2160
0
            {
2161
0
                psDatumInfo = asDatumInfoList + i;
2162
0
                break;
2163
0
            }
2164
0
        }
2165
2166
0
        if (psDatumInfo == nullptr &&
2167
0
            !MITABGetCustomDatum(poSpatialRef, sTABProj))
2168
0
        {
2169
0
            CPLDebug("MITAB",
2170
0
                     "Cannot find MapInfo datum matching %s,%d. Defaulting to "
2171
0
                     "WGS 84",
2172
0
                     pszWKTDatum, nDatumEPSGCode);
2173
0
            psDatumInfo = asDatumInfoList + 0; /* WGS 84 */
2174
0
        }
2175
0
    }
2176
2177
0
    if (psDatumInfo != nullptr)
2178
0
    {
2179
0
        sTABProj.nEllipsoidId = static_cast<GByte>(psDatumInfo->nEllipsoid);
2180
0
        sTABProj.nDatumId = static_cast<GInt16>(psDatumInfo->nMapInfoDatumID);
2181
0
        sTABProj.dDatumShiftX = psDatumInfo->dfShiftX;
2182
0
        sTABProj.dDatumShiftY = psDatumInfo->dfShiftY;
2183
0
        sTABProj.dDatumShiftZ = psDatumInfo->dfShiftZ;
2184
0
        sTABProj.adDatumParams[0] = psDatumInfo->dfDatumParm0;
2185
0
        sTABProj.adDatumParams[1] = psDatumInfo->dfDatumParm1;
2186
0
        sTABProj.adDatumParams[2] = psDatumInfo->dfDatumParm2;
2187
0
        sTABProj.adDatumParams[3] = psDatumInfo->dfDatumParm3;
2188
0
        sTABProj.adDatumParams[4] = psDatumInfo->dfDatumParm4;
2189
2190
        /* For LCC, standard parallel 1 and 2 can be switched indifferently */
2191
        /* So the MapInfo order and the EPSG order are not generally identical
2192
         */
2193
        /* which may cause recognition problems when reading in MapInfo */
2194
0
        if (sTABProj.nProjId == 3)
2195
0
        {
2196
0
            const double dfCenterLong = params[0];
2197
0
            const double dfCenterLat = params[1];
2198
0
            const double dfStdP1 = params[2];
2199
0
            const double dfStdP2 = params[3];
2200
2201
            // EPSG:3301 "Estonian Coordinate System of 1997"
2202
0
            if (std::fabs(dfCenterLong - 24) <= 1e-10 &&
2203
0
                std::fabs(dfCenterLat - 57.51755393056) <= 1e-10 &&
2204
                // MapInfo uses a less accurate value of StdP2 than EPSG
2205
0
                std::fabs(dfStdP1 - 59.33333333) <= 1e-8 &&
2206
0
                std::fabs(dfStdP2 - 58) <= 1e-8)
2207
0
            {
2208
0
                CPLDebug("MITAB", "Switching standard parallel 1 and 2");
2209
0
                std::swap(params[2], params[3]);
2210
0
            }
2211
0
            else
2212
0
            {
2213
0
                for (size_t i = 0; i < sizeof(asMapInfoLCCSRSList) /
2214
0
                                           sizeof(asMapInfoLCCSRSList[0]);
2215
0
                     i++)
2216
0
                {
2217
0
                    if (sTABProj.nDatumId ==
2218
0
                            asMapInfoLCCSRSList[i].nMapInfoDatumID &&
2219
0
                        TAB_EQUAL(dfCenterLong,
2220
0
                                  asMapInfoLCCSRSList[i].dfCenterLong) &&
2221
0
                        TAB_EQUAL(dfCenterLat,
2222
0
                                  asMapInfoLCCSRSList[i].dfCenterLat))
2223
0
                    {
2224
0
                        if (TAB_EQUAL(dfStdP1,
2225
0
                                      asMapInfoLCCSRSList[i].dfStdP1) &&
2226
0
                            TAB_EQUAL(dfStdP2, asMapInfoLCCSRSList[i].dfStdP2))
2227
0
                        {
2228
0
                            break;
2229
0
                        }
2230
0
                        else if (TAB_EQUAL(dfStdP1,
2231
0
                                           asMapInfoLCCSRSList[i].dfStdP2) &&
2232
0
                                 TAB_EQUAL(dfStdP2,
2233
0
                                           asMapInfoLCCSRSList[i].dfStdP1))
2234
0
                        {
2235
0
                            CPLDebug("MITAB",
2236
0
                                     "Switching standard parallel 1 and 2");
2237
0
                            std::swap(params[2], params[3]);
2238
0
                            break;
2239
0
                        }
2240
0
                    }
2241
0
                }
2242
0
            }
2243
0
        }
2244
0
    }
2245
2246
    // Google Merc
2247
0
    const char *pszAuthorityName = nullptr;
2248
0
    const char *pszAuthorityCode = nullptr;
2249
0
    const char *pszExtension = nullptr;
2250
0
    if (((pszAuthorityName = poSpatialRef->GetAuthorityName(nullptr)) !=
2251
0
             nullptr &&
2252
0
         EQUAL(pszAuthorityName, "EPSG") &&
2253
0
         (pszAuthorityCode = poSpatialRef->GetAuthorityCode(nullptr)) !=
2254
0
             nullptr &&
2255
0
         atoi(pszAuthorityCode) == 3857) ||
2256
0
        ((pszExtension = poSpatialRef->GetExtension(nullptr, "PROJ4")) !=
2257
0
             nullptr &&
2258
0
         (EQUAL(pszExtension, "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 "
2259
0
                              "+lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m "
2260
0
                              "+nadgrids=@null +wktext  +no_defs") ||
2261
0
          EQUAL(pszExtension,
2262
0
                "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 "
2263
0
                "+y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs"))))
2264
0
    {
2265
0
        sTABProj.nDatumId = 157;
2266
0
        sTABProj.nEllipsoidId = 54;
2267
0
    }
2268
2269
    /*-----------------------------------------------------------------
2270
     * Translate the units
2271
     *----------------------------------------------------------------*/
2272
0
    if (sTABProj.nProjId == 1 || pszLinearUnits == nullptr)
2273
0
        sTABProj.nUnitsId = 13;
2274
0
    else if (dfLinearConv == 1000.0)
2275
0
        sTABProj.nUnitsId = 1;
2276
0
    else if (dfLinearConv == 0.0254 || EQUAL(pszLinearUnits, "Inch") ||
2277
0
             EQUAL(pszLinearUnits, "IINCH"))
2278
0
        sTABProj.nUnitsId = 2;
2279
0
    else if (fabs(dfLinearConv - CPLAtof(SRS_UL_FOOT_CONV)) <
2280
0
                 1e-15 * dfLinearConv ||
2281
0
             EQUAL(pszLinearUnits, SRS_UL_FOOT))
2282
0
        sTABProj.nUnitsId = 3;
2283
0
    else if (EQUAL(pszLinearUnits, "YARD") || EQUAL(pszLinearUnits, "IYARD") ||
2284
0
             dfLinearConv == 0.9144)
2285
0
        sTABProj.nUnitsId = 4;
2286
0
    else if (dfLinearConv == 0.001)
2287
0
        sTABProj.nUnitsId = 5;
2288
0
    else if (dfLinearConv == 0.01)
2289
0
        sTABProj.nUnitsId = 6;
2290
0
    else if (dfLinearConv == 1.0)
2291
0
        sTABProj.nUnitsId = 7;
2292
0
    else if (fabs(dfLinearConv - CPLAtof(SRS_UL_US_FOOT_CONV)) <
2293
0
                 1e-15 * dfLinearConv ||
2294
0
             EQUAL(pszLinearUnits, SRS_UL_US_FOOT))
2295
0
        sTABProj.nUnitsId = 8;
2296
0
    else if (dfLinearConv == 1852.0 ||
2297
0
             EQUAL(pszLinearUnits, SRS_UL_NAUTICAL_MILE))
2298
0
        sTABProj.nUnitsId = 9;
2299
0
    else if (EQUAL(pszLinearUnits, SRS_UL_LINK) ||
2300
0
             EQUAL(pszLinearUnits, "GUNTERLINK"))
2301
0
        sTABProj.nUnitsId = 30;
2302
0
    else if (EQUAL(pszLinearUnits, SRS_UL_CHAIN) ||
2303
0
             EQUAL(pszLinearUnits, "GUNTERCHAIN"))
2304
0
        sTABProj.nUnitsId = 31;
2305
0
    else if (EQUAL(pszLinearUnits, SRS_UL_ROD))
2306
0
        sTABProj.nUnitsId = 32;
2307
0
    else if (EQUAL(pszLinearUnits, "Mile") || EQUAL(pszLinearUnits, "IMILE"))
2308
0
        sTABProj.nUnitsId = 0;
2309
0
    else
2310
0
        sTABProj.nUnitsId = 7;
2311
2312
0
    return 0;
2313
0
}
2314
2315
extern const MapInfoDatumInfo asDatumInfoList[];
2316
extern const MapInfoSpheroidInfo asSpheroidInfoList[];
2317
2318
/************************************************************************/
2319
/*                      MITABCoordSys2SpatialRef()                      */
2320
/*                                                                      */
2321
/*      Convert a MIF COORDSYS string into a new OGRSpatialReference    */
2322
/*      object.                                                         */
2323
/************************************************************************/
2324
2325
OGRSpatialReference *MITABCoordSys2SpatialRef(const char *pszCoordSys)
2326
2327
0
{
2328
0
    TABProjInfo sTABProj;
2329
0
    if (MITABCoordSys2TABProjInfo(pszCoordSys, &sTABProj) < 0)
2330
0
        return nullptr;
2331
0
    OGRSpatialReference *poSR = TABFileGetSpatialRefFromTABProj(sTABProj);
2332
2333
    // Report on translation.
2334
0
    char *pszWKT = nullptr;
2335
2336
0
    poSR->exportToWkt(&pszWKT);
2337
0
    if (pszWKT != nullptr)
2338
0
    {
2339
0
        CPLDebug("MITAB", "This CoordSys value:\n%s\nwas translated to:\n%s",
2340
0
                 pszCoordSys, pszWKT);
2341
0
        CPLFree(pszWKT);
2342
0
    }
2343
2344
0
    return poSR;
2345
0
}
2346
2347
/************************************************************************/
2348
/*                      MITABSpatialRef2CoordSys()                      */
2349
/*                                                                      */
2350
/*      Converts a OGRSpatialReference object into a MIF COORDSYS       */
2351
/*      string.                                                         */
2352
/*                                                                      */
2353
/*      The function returns a newly allocated string that should be    */
2354
/*      CPLFree()'d by the caller.                                      */
2355
/************************************************************************/
2356
2357
char *MITABSpatialRef2CoordSys(const OGRSpatialReference *poSR)
2358
2359
0
{
2360
0
    if (poSR == nullptr)
2361
0
        return nullptr;
2362
2363
0
    TABProjInfo sTABProj;
2364
0
    int nParamCount = 0;
2365
0
    TABFileGetTABProjFromSpatialRef(poSR, sTABProj, nParamCount);
2366
2367
    // Do coordsys lookup.
2368
0
    double dXMin = 0.0;
2369
0
    double dYMin = 0.0;
2370
0
    double dXMax = 0.0;
2371
0
    double dYMax = 0.0;
2372
0
    bool bHasBounds = false;
2373
0
    if (sTABProj.nProjId > 1 &&
2374
0
        MITABLookupCoordSysBounds(&sTABProj, dXMin, dYMin, dXMax, dYMax, true))
2375
0
    {
2376
0
        bHasBounds = true;
2377
0
    }
2378
2379
    // Translate the units.
2380
0
    const char *pszMIFUnits = TABUnitIdToString(sTABProj.nUnitsId);
2381
2382
    // Build coordinate system definition.
2383
0
    CPLString osCoordSys;
2384
2385
0
    if (sTABProj.nProjId != 0)
2386
0
    {
2387
0
        osCoordSys.Printf("Earth Projection %d", sTABProj.nProjId);
2388
0
    }
2389
0
    else
2390
0
    {
2391
0
        osCoordSys.Printf("NonEarth Units");
2392
0
    }
2393
2394
    // Append Datum.
2395
0
    if (sTABProj.nProjId != 0)
2396
0
    {
2397
0
        osCoordSys += CPLSPrintf(", %d", sTABProj.nDatumId);
2398
2399
0
        if (sTABProj.nDatumId == 999 || sTABProj.nDatumId == 9999)
2400
0
        {
2401
0
            osCoordSys +=
2402
0
                CPLSPrintf(", %d, %.15g, %.15g, %.15g", sTABProj.nEllipsoidId,
2403
0
                           sTABProj.dDatumShiftX, sTABProj.dDatumShiftY,
2404
0
                           sTABProj.dDatumShiftZ);
2405
0
        }
2406
2407
0
        if (sTABProj.nDatumId == 9999)
2408
0
        {
2409
0
            osCoordSys +=
2410
0
                CPLSPrintf(", %.15g, %.15g, %.15g, %.15g, %.15g",
2411
0
                           sTABProj.adDatumParams[0], sTABProj.adDatumParams[1],
2412
0
                           sTABProj.adDatumParams[2], sTABProj.adDatumParams[3],
2413
0
                           sTABProj.adDatumParams[4]);
2414
0
        }
2415
0
    }
2416
2417
    // Append units.
2418
0
    if (sTABProj.nProjId != 1 && pszMIFUnits != nullptr)
2419
0
    {
2420
0
        if (sTABProj.nProjId != 0)
2421
0
            osCoordSys += ",";
2422
2423
0
        osCoordSys += CPLSPrintf(" \"%s\"", pszMIFUnits);
2424
0
    }
2425
2426
    // Append Projection Params.
2427
0
    for (int iParam = 0; iParam < nParamCount; iParam++)
2428
0
        osCoordSys += CPLSPrintf(", %.15g", sTABProj.adProjParams[iParam]);
2429
2430
    // Append user bounds.
2431
0
    if (bHasBounds)
2432
0
    {
2433
0
        if (fabs(dXMin - floor(dXMin + 0.5)) < 1e-8 &&
2434
0
            fabs(dYMin - floor(dYMin + 0.5)) < 1e-8 &&
2435
0
            fabs(dXMax - floor(dXMax + 0.5)) < 1e-8 &&
2436
0
            fabs(dYMax - floor(dYMax + 0.5)) < 1e-8)
2437
0
        {
2438
0
            osCoordSys +=
2439
0
                CPLSPrintf(" Bounds (%d, %d) (%d, %d)", static_cast<int>(dXMin),
2440
0
                           static_cast<int>(dYMin), static_cast<int>(dXMax),
2441
0
                           static_cast<int>(dYMax));
2442
0
        }
2443
0
        else
2444
0
        {
2445
0
            osCoordSys += CPLSPrintf(" Bounds (%f, %f) (%f, %f)", dXMin, dYMin,
2446
0
                                     dXMax, dYMax);
2447
0
        }
2448
0
    }
2449
2450
    // Report on translation.
2451
0
    char *pszWKT = nullptr;
2452
2453
0
    poSR->exportToWkt(&pszWKT);
2454
0
    if (pszWKT != nullptr)
2455
0
    {
2456
0
        CPLDebug("MITAB", "This WKT Projection:\n%s\n\ntranslates to:\n%s",
2457
0
                 pszWKT, osCoordSys.c_str());
2458
0
        CPLFree(pszWKT);
2459
0
    }
2460
2461
0
    return CPLStrdup(osCoordSys.c_str());
2462
0
}
2463
2464
/************************************************************************/
2465
/*                      MITABExtractCoordSysBounds                      */
2466
/*                                                                      */
2467
/* Return true if MIF coordsys string contains a BOUNDS parameter and   */
2468
/* Set x/y min/max values.                                              */
2469
/************************************************************************/
2470
2471
bool MITABExtractCoordSysBounds(const char *pszCoordSys, double &dXMin,
2472
                                double &dYMin, double &dXMax, double &dYMax)
2473
2474
0
{
2475
0
    if (pszCoordSys == nullptr)
2476
0
        return false;
2477
2478
0
    char **papszFields =
2479
0
        CSLTokenizeStringComplex(pszCoordSys, " ,()", TRUE, FALSE);
2480
2481
0
    int iBounds = CSLFindString(papszFields, "Bounds");
2482
2483
0
    if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields))
2484
0
    {
2485
0
        dXMin = CPLAtof(papszFields[++iBounds]);
2486
0
        dYMin = CPLAtof(papszFields[++iBounds]);
2487
0
        dXMax = CPLAtof(papszFields[++iBounds]);
2488
0
        dYMax = CPLAtof(papszFields[++iBounds]);
2489
0
        CSLDestroy(papszFields);
2490
0
        return true;
2491
0
    }
2492
2493
0
    CSLDestroy(papszFields);
2494
0
    return false;
2495
0
}
2496
2497
/**********************************************************************
2498
 *                     MITABCoordSys2TABProjInfo()
2499
 *
2500
 * Convert a MIF COORDSYS string into a TABProjInfo structure.
2501
 *
2502
 * Returns 0 on success, -1 on error.
2503
 **********************************************************************/
2504
int MITABCoordSys2TABProjInfo(const char *pszCoordSys, TABProjInfo *psProj)
2505
2506
0
{
2507
    // Set all fields to zero, equivalent of NonEarth Units "mi"
2508
0
    memset(psProj, 0, sizeof(TABProjInfo));
2509
2510
0
    if (pszCoordSys == nullptr)
2511
0
        return -1;
2512
2513
    // Parse the passed string into words.
2514
0
    while (*pszCoordSys == ' ')
2515
0
        pszCoordSys++;  // Eat leading spaces.
2516
0
    if (STARTS_WITH_CI(pszCoordSys, "CoordSys") && pszCoordSys[8] != '\0')
2517
0
        pszCoordSys += 9;
2518
2519
0
    char **papszFields =
2520
0
        CSLTokenizeStringComplex(pszCoordSys, " ,", TRUE, FALSE);
2521
2522
    // Clip off Bounds information.
2523
0
    int iBounds = CSLFindString(papszFields, "Bounds");
2524
2525
0
    while (iBounds != -1 && papszFields[iBounds] != nullptr)
2526
0
    {
2527
0
        CPLFree(papszFields[iBounds]);
2528
0
        papszFields[iBounds] = nullptr;
2529
0
        iBounds++;
2530
0
    }
2531
2532
    // Fetch the projection.
2533
0
    char **papszNextField = nullptr;
2534
2535
0
    if (CSLCount(papszFields) >= 3 && EQUAL(papszFields[0], "Earth") &&
2536
0
        EQUAL(papszFields[1], "Projection"))
2537
0
    {
2538
0
        int nProjId = atoi(papszFields[2]);
2539
0
        if (nProjId >= 3000)
2540
0
            nProjId -= 3000;
2541
0
        else if (nProjId >= 2000)
2542
0
            nProjId -= 2000;
2543
0
        else if (nProjId >= 1000)
2544
0
            nProjId -= 1000;
2545
2546
0
        psProj->nProjId = static_cast<GByte>(nProjId);
2547
0
        papszNextField = papszFields + 3;
2548
0
    }
2549
0
    else if (CSLCount(papszFields) >= 2 && EQUAL(papszFields[0], "NonEarth"))
2550
0
    {
2551
        // NonEarth Units "..." Bounds (x, y) (x, y)
2552
0
        psProj->nProjId = 0;
2553
0
        papszNextField = papszFields + 2;
2554
2555
0
        if (papszNextField[0] != nullptr && EQUAL(papszNextField[0], "Units"))
2556
0
            papszNextField++;
2557
0
    }
2558
0
    else
2559
0
    {
2560
        // Invalid projection string ???
2561
0
        if (CSLCount(papszFields) > 0)
2562
0
            CPLError(CE_Warning, CPLE_IllegalArg,
2563
0
                     "Failed parsing CoordSys: '%s'", pszCoordSys);
2564
0
        CSLDestroy(papszFields);
2565
0
        return -1;
2566
0
    }
2567
2568
    // Fetch the datum information.
2569
0
    int nDatum = 0;
2570
2571
0
    if (psProj->nProjId != 0 && CSLCount(papszNextField) > 0)
2572
0
    {
2573
0
        nDatum = atoi(papszNextField[0]);
2574
0
        papszNextField++;
2575
0
    }
2576
2577
0
    if ((nDatum == 999 || nDatum == 9999) && CSLCount(papszNextField) >= 4)
2578
0
    {
2579
0
        psProj->nEllipsoidId = static_cast<GByte>(atoi(papszNextField[0]));
2580
0
        psProj->dDatumShiftX = CPLAtof(papszNextField[1]);
2581
0
        psProj->dDatumShiftY = CPLAtof(papszNextField[2]);
2582
0
        psProj->dDatumShiftZ = CPLAtof(papszNextField[3]);
2583
0
        papszNextField += 4;
2584
2585
0
        if (nDatum == 9999 && CSLCount(papszNextField) >= 5)
2586
0
        {
2587
0
            psProj->adDatumParams[0] = CPLAtof(papszNextField[0]);
2588
0
            psProj->adDatumParams[1] = CPLAtof(papszNextField[1]);
2589
0
            psProj->adDatumParams[2] = CPLAtof(papszNextField[2]);
2590
0
            psProj->adDatumParams[3] = CPLAtof(papszNextField[3]);
2591
0
            psProj->adDatumParams[4] = CPLAtof(papszNextField[4]);
2592
0
            papszNextField += 5;
2593
0
        }
2594
0
    }
2595
0
    else if (nDatum != 999 && nDatum != 9999)
2596
0
    {
2597
        // Find the datum, and collect its parameters if possible.
2598
0
        const MapInfoDatumInfo *psDatumInfo = nullptr;
2599
2600
0
        int iDatum = 0;  // Used after for.
2601
0
        for (; asDatumInfoList[iDatum].nMapInfoDatumID != -1; iDatum++)
2602
0
        {
2603
0
            if (asDatumInfoList[iDatum].nMapInfoDatumID == nDatum)
2604
0
            {
2605
0
                psDatumInfo = asDatumInfoList + iDatum;
2606
0
                break;
2607
0
            }
2608
0
        }
2609
2610
0
        if (asDatumInfoList[iDatum].nMapInfoDatumID == -1)
2611
0
        {
2612
            // Use WGS84.
2613
0
            psDatumInfo = asDatumInfoList + 0;
2614
0
        }
2615
2616
0
        if (psDatumInfo != nullptr)
2617
0
        {
2618
0
            psProj->nEllipsoidId = static_cast<GByte>(psDatumInfo->nEllipsoid);
2619
0
            psProj->nDatumId =
2620
0
                static_cast<GInt16>(psDatumInfo->nMapInfoDatumID);
2621
0
            psProj->dDatumShiftX = psDatumInfo->dfShiftX;
2622
0
            psProj->dDatumShiftY = psDatumInfo->dfShiftY;
2623
0
            psProj->dDatumShiftZ = psDatumInfo->dfShiftZ;
2624
0
            psProj->adDatumParams[0] = psDatumInfo->dfDatumParm0;
2625
0
            psProj->adDatumParams[1] = psDatumInfo->dfDatumParm1;
2626
0
            psProj->adDatumParams[2] = psDatumInfo->dfDatumParm2;
2627
0
            psProj->adDatumParams[3] = psDatumInfo->dfDatumParm3;
2628
0
            psProj->adDatumParams[4] = psDatumInfo->dfDatumParm4;
2629
0
        }
2630
0
    }
2631
2632
    // Fetch the units string.
2633
0
    if (CSLCount(papszNextField) > 0)
2634
0
    {
2635
0
        if (isdigit(static_cast<unsigned char>(papszNextField[0][0])))
2636
0
        {
2637
0
            psProj->nUnitsId = static_cast<GByte>(atoi(papszNextField[0]));
2638
0
        }
2639
0
        else
2640
0
        {
2641
0
            psProj->nUnitsId =
2642
0
                static_cast<GByte>(TABUnitIdFromString(papszNextField[0]));
2643
0
        }
2644
0
        papszNextField++;
2645
0
    }
2646
2647
    // Finally the projection parameters.
2648
0
    for (int iParam = 0; iParam < 7 && CSLCount(papszNextField) > 0; iParam++)
2649
0
    {
2650
0
        psProj->adProjParams[iParam] = CPLAtof(papszNextField[0]);
2651
0
        papszNextField++;
2652
0
    }
2653
2654
0
    CSLDestroy(papszFields);
2655
2656
0
    return 0;
2657
0
}
2658
2659
/*! @endcond */