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