/src/gdal/ogr/ogr_srs_erm.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: Implement ERMapper projection conversions. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com> |
9 | | * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "ogr_srs_api.h" |
16 | | |
17 | | #include <cmath> |
18 | | #include <cstdio> |
19 | | #include <cstdlib> |
20 | | #include <cstring> |
21 | | |
22 | | #include "cpl_conv.h" |
23 | | #include "cpl_error.h" |
24 | | #include "ogr_core.h" |
25 | | #include "ogr_spatialref.h" |
26 | | |
27 | | /************************************************************************/ |
28 | | /* OSRImportFromERM() */ |
29 | | /************************************************************************/ |
30 | | |
31 | | /** |
32 | | * \brief Create OGR WKT from ERMapper projection definitions. |
33 | | * |
34 | | * This function is the same as OGRSpatialReference::importFromERM(). |
35 | | */ |
36 | | |
37 | | OGRErr OSRImportFromERM(OGRSpatialReferenceH hSRS, const char *pszProj, |
38 | | const char *pszDatum, const char *pszUnits) |
39 | | |
40 | 0 | { |
41 | 0 | VALIDATE_POINTER1(hSRS, "OSRImportFromERM", OGRERR_FAILURE); |
42 | | |
43 | 0 | return reinterpret_cast<OGRSpatialReference *>(hSRS)->importFromERM( |
44 | 0 | pszProj, pszDatum, pszUnits); |
45 | 0 | } |
46 | | |
47 | | /************************************************************************/ |
48 | | /* importFromERM() */ |
49 | | /************************************************************************/ |
50 | | |
51 | | /** |
52 | | * Create OGR WKT from ERMapper projection definitions. |
53 | | * |
54 | | * Generates an OGRSpatialReference definition from an ERMapper datum |
55 | | * and projection name. Based on the ecw_cs.wkt dictionary file from |
56 | | * gdal/data. |
57 | | * |
58 | | * @param pszProj the projection name, such as "NUTM11" or "GEOGRAPHIC". |
59 | | * @param pszDatum the datum name, such as "NAD83". |
60 | | * @param pszUnits the linear units "FEET" or "METERS". |
61 | | * |
62 | | * @return OGRERR_NONE on success or OGRERR_UNSUPPORTED_SRS if not found. |
63 | | */ |
64 | | |
65 | | OGRErr OGRSpatialReference::importFromERM(const char *pszProj, |
66 | | const char *pszDatum, |
67 | | const char *pszUnits) |
68 | | |
69 | 1 | { |
70 | 1 | Clear(); |
71 | | |
72 | | /* -------------------------------------------------------------------- */ |
73 | | /* do we have projection and datum? */ |
74 | | /* -------------------------------------------------------------------- */ |
75 | 1 | if (EQUAL(pszProj, "RAW")) |
76 | 1 | return OGRERR_NONE; |
77 | | |
78 | | /* -------------------------------------------------------------------- */ |
79 | | /* Do we have an EPSG coordinate system? */ |
80 | | /* -------------------------------------------------------------------- */ |
81 | | |
82 | 0 | if (STARTS_WITH_CI(pszProj, "EPSG:")) |
83 | 0 | return importFromEPSG(atoi(pszProj + 5)); |
84 | | |
85 | 0 | if (STARTS_WITH_CI(pszDatum, "EPSG:")) |
86 | 0 | return importFromEPSG(atoi(pszDatum + 5)); |
87 | | |
88 | 0 | CPLString osGEOGCS = lookupInDict("ecw_cs.wkt", pszDatum); |
89 | 0 | if (osGEOGCS.empty()) |
90 | 0 | return OGRERR_UNSUPPORTED_SRS; |
91 | | |
92 | | /* -------------------------------------------------------------------- */ |
93 | | /* Set projection if we have it. */ |
94 | | /* -------------------------------------------------------------------- */ |
95 | 0 | if (!EQUAL(pszProj, "GEODETIC")) |
96 | 0 | { |
97 | 0 | CPLString osProjWKT = lookupInDict("ecw_cs.wkt", pszProj); |
98 | 0 | if (osProjWKT.empty() || osProjWKT.back() != ']') |
99 | 0 | return OGRERR_UNSUPPORTED_SRS; |
100 | | |
101 | 0 | if (osProjWKT.find("LOCAL_CS[") == 0) |
102 | 0 | { |
103 | 0 | return importFromWkt(osProjWKT); |
104 | 0 | } |
105 | | |
106 | | // Remove trailing ] |
107 | 0 | osProjWKT.pop_back(); |
108 | | |
109 | | // Remove any UNIT |
110 | 0 | auto nPos = osProjWKT.find(",UNIT"); |
111 | 0 | if (nPos != std::string::npos) |
112 | 0 | { |
113 | 0 | osProjWKT.resize(nPos); |
114 | 0 | } |
115 | | |
116 | | // Insert GEOGCS |
117 | 0 | nPos = osProjWKT.find(",PROJECTION"); |
118 | 0 | if (nPos == std::string::npos) |
119 | 0 | return OGRERR_UNSUPPORTED_SRS; |
120 | | |
121 | 0 | osProjWKT = |
122 | 0 | osProjWKT.substr(0, nPos) + ',' + osGEOGCS + osProjWKT.substr(nPos); |
123 | |
|
124 | 0 | if (EQUAL(pszUnits, "FEET")) |
125 | 0 | osProjWKT += ",UNIT[\"Foot_US\",0.3048006096012192]]"; |
126 | 0 | else |
127 | 0 | osProjWKT += ",UNIT[\"Metre\",1.0]]"; |
128 | |
|
129 | 0 | return importFromWkt(osProjWKT); |
130 | 0 | } |
131 | 0 | else |
132 | 0 | { |
133 | 0 | return importFromWkt(osGEOGCS); |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | | /************************************************************************/ |
138 | | /* OSRExportToERM() */ |
139 | | /************************************************************************/ |
140 | | /** |
141 | | * \brief Convert coordinate system to ERMapper format. |
142 | | * |
143 | | * This function is the same as OGRSpatialReference::exportToERM(). |
144 | | */ |
145 | | OGRErr OSRExportToERM(OGRSpatialReferenceH hSRS, char *pszProj, char *pszDatum, |
146 | | char *pszUnits) |
147 | | |
148 | 0 | { |
149 | 0 | VALIDATE_POINTER1(hSRS, "OSRExportToERM", OGRERR_FAILURE); |
150 | | |
151 | 0 | return reinterpret_cast<OGRSpatialReference *>(hSRS)->exportToERM( |
152 | 0 | pszProj, pszDatum, pszUnits); |
153 | 0 | } |
154 | | |
155 | | /************************************************************************/ |
156 | | /* exportToERM() */ |
157 | | /************************************************************************/ |
158 | | |
159 | | /** |
160 | | * Convert coordinate system to ERMapper format. |
161 | | * |
162 | | * @param pszProj 32 character buffer to receive projection name. |
163 | | * @param pszDatum 32 character buffer to receive datum name. |
164 | | * @param pszUnits 32 character buffer to receive units name. |
165 | | * |
166 | | * @return OGRERR_NONE on success, OGRERR_SRS_UNSUPPORTED if not translation is |
167 | | * found, or OGRERR_FAILURE on other failures. |
168 | | */ |
169 | | |
170 | | OGRErr OGRSpatialReference::exportToERM(char *pszProj, char *pszDatum, |
171 | | char *pszUnits) |
172 | | |
173 | 0 | { |
174 | 0 | const int BUFFER_SIZE = 32; |
175 | 0 | strcpy(pszProj, "RAW"); |
176 | 0 | strcpy(pszDatum, "RAW"); |
177 | 0 | strcpy(pszUnits, "METERS"); |
178 | |
|
179 | 0 | if (!IsProjected() && !IsGeographic()) |
180 | 0 | return OGRERR_UNSUPPORTED_SRS; |
181 | | |
182 | | /* -------------------------------------------------------------------- */ |
183 | | /* Try to find the EPSG code. */ |
184 | | /* -------------------------------------------------------------------- */ |
185 | 0 | int nEPSGCode = 0; |
186 | |
|
187 | 0 | if (IsProjected()) |
188 | 0 | { |
189 | 0 | const char *pszAuthName = GetAuthorityName("PROJCS"); |
190 | |
|
191 | 0 | if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg")) |
192 | 0 | { |
193 | 0 | nEPSGCode = atoi(GetAuthorityCode("PROJCS")); |
194 | 0 | } |
195 | 0 | } |
196 | 0 | else if (IsGeographic()) |
197 | 0 | { |
198 | 0 | const char *pszAuthName = GetAuthorityName("GEOGCS"); |
199 | |
|
200 | 0 | if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg")) |
201 | 0 | { |
202 | 0 | nEPSGCode = atoi(GetAuthorityCode("GEOGCS")); |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | | /* -------------------------------------------------------------------- */ |
207 | | /* Is our GEOGCS name already defined in ecw_cs.wkt? */ |
208 | | /* -------------------------------------------------------------------- */ |
209 | 0 | const char *pszWKTDatum = GetAttrValue("DATUM"); |
210 | |
|
211 | 0 | if (pszWKTDatum != nullptr && |
212 | 0 | !lookupInDict("ecw_cs.wkt", pszWKTDatum).empty()) |
213 | 0 | { |
214 | 0 | strncpy(pszDatum, pszWKTDatum, BUFFER_SIZE); |
215 | 0 | pszDatum[BUFFER_SIZE - 1] = '\0'; |
216 | 0 | } |
217 | | |
218 | | /* -------------------------------------------------------------------- */ |
219 | | /* Is this a "well known" geographic coordinate system? */ |
220 | | /* -------------------------------------------------------------------- */ |
221 | 0 | if (EQUAL(pszDatum, "RAW")) |
222 | 0 | { |
223 | 0 | int nEPSGGCSCode = GetEPSGGeogCS(); |
224 | |
|
225 | 0 | if (nEPSGGCSCode == 4326) |
226 | 0 | strcpy(pszDatum, "WGS84"); |
227 | | |
228 | 0 | else if (nEPSGGCSCode == 4322) |
229 | 0 | strcpy(pszDatum, "WGS72DOD"); |
230 | | |
231 | 0 | else if (nEPSGGCSCode == 4267) |
232 | 0 | strcpy(pszDatum, "NAD27"); |
233 | | |
234 | 0 | else if (nEPSGGCSCode == 4269) |
235 | 0 | strcpy(pszDatum, "NAD83"); |
236 | | |
237 | 0 | else if (nEPSGGCSCode == 4277) |
238 | 0 | strcpy(pszDatum, "OSGB36"); |
239 | | |
240 | 0 | else if (nEPSGGCSCode == 4278) |
241 | 0 | strcpy(pszDatum, "OSGB78"); |
242 | | |
243 | 0 | else if (nEPSGGCSCode == 4201) |
244 | 0 | strcpy(pszDatum, "ADINDAN"); |
245 | | |
246 | 0 | else if (nEPSGGCSCode == 4202) |
247 | 0 | strcpy(pszDatum, "AGD66"); |
248 | | |
249 | 0 | else if (nEPSGGCSCode == 4203) |
250 | 0 | strcpy(pszDatum, "AGD84"); |
251 | | |
252 | 0 | else if (nEPSGGCSCode == 4209) |
253 | 0 | strcpy(pszDatum, "ARC1950"); |
254 | | |
255 | 0 | else if (nEPSGGCSCode == 4210) |
256 | 0 | strcpy(pszDatum, "ARC1960"); |
257 | | |
258 | 0 | else if (nEPSGGCSCode == 4275) |
259 | 0 | strcpy(pszDatum, "NTF"); |
260 | | |
261 | 0 | else if (nEPSGGCSCode == 4283) |
262 | 0 | strcpy(pszDatum, "GDA94"); |
263 | | |
264 | 0 | else if (nEPSGGCSCode == 4284) |
265 | 0 | strcpy(pszDatum, "PULKOVO"); |
266 | | |
267 | 0 | else if (nEPSGGCSCode == 7844) |
268 | 0 | strcpy(pszDatum, "GDA2020"); |
269 | 0 | } |
270 | | |
271 | | /* -------------------------------------------------------------------- */ |
272 | | /* Are we working with a geographic (geodetic) coordinate system? */ |
273 | | /* -------------------------------------------------------------------- */ |
274 | |
|
275 | 0 | if (IsGeographic()) |
276 | 0 | { |
277 | 0 | if (EQUAL(pszDatum, "RAW")) |
278 | 0 | return OGRERR_UNSUPPORTED_SRS; |
279 | 0 | else |
280 | 0 | { |
281 | 0 | strcpy(pszProj, "GEODETIC"); |
282 | 0 | return OGRERR_NONE; |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | | /* -------------------------------------------------------------------- */ |
287 | | /* Is this a UTM projection? */ |
288 | | /* -------------------------------------------------------------------- */ |
289 | 0 | int bNorth = FALSE; |
290 | 0 | int nZone = 0; |
291 | |
|
292 | 0 | nZone = GetUTMZone(&bNorth); |
293 | 0 | if (nZone > 0) |
294 | 0 | { |
295 | 0 | if ((EQUAL(pszDatum, "GDA94") || EQUAL(pszDatum, "GDA2020")) && |
296 | 0 | !bNorth && nZone >= 48 && nZone <= 58) |
297 | 0 | { |
298 | 0 | snprintf(pszProj, BUFFER_SIZE, "MGA%02d", nZone); |
299 | 0 | } |
300 | 0 | else |
301 | 0 | { |
302 | 0 | if (bNorth) |
303 | 0 | snprintf(pszProj, BUFFER_SIZE, "NUTM%02d", nZone); |
304 | 0 | else |
305 | 0 | snprintf(pszProj, BUFFER_SIZE, "SUTM%02d", nZone); |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | /* -------------------------------------------------------------------- */ |
310 | | /* Is our PROJCS name already defined in ecw_cs.wkt? */ |
311 | | /* -------------------------------------------------------------------- */ |
312 | 0 | else |
313 | 0 | { |
314 | 0 | const char *pszPROJCS = GetAttrValue("PROJCS"); |
315 | |
|
316 | 0 | if (pszPROJCS != nullptr && |
317 | 0 | lookupInDict("ecw_cs.wkt", pszPROJCS).find("PROJCS") == 0) |
318 | 0 | { |
319 | 0 | strncpy(pszProj, pszPROJCS, BUFFER_SIZE); |
320 | 0 | pszProj[BUFFER_SIZE - 1] = '\0'; |
321 | 0 | } |
322 | 0 | } |
323 | | |
324 | | /* -------------------------------------------------------------------- */ |
325 | | /* If we have not translated it yet, but we have an EPSG code */ |
326 | | /* then use EPSG:n notation. */ |
327 | | /* -------------------------------------------------------------------- */ |
328 | 0 | if ((EQUAL(pszDatum, "RAW") || EQUAL(pszProj, "RAW")) && nEPSGCode != 0) |
329 | 0 | { |
330 | 0 | snprintf(pszProj, BUFFER_SIZE, "EPSG:%d", nEPSGCode); |
331 | 0 | snprintf(pszDatum, BUFFER_SIZE, "EPSG:%d", nEPSGCode); |
332 | 0 | } |
333 | | |
334 | | /* -------------------------------------------------------------------- */ |
335 | | /* Handle the units. */ |
336 | | /* -------------------------------------------------------------------- */ |
337 | 0 | const double dfUnits = GetLinearUnits(); |
338 | |
|
339 | 0 | if (fabs(dfUnits - 0.3048) < 0.0001) |
340 | 0 | strcpy(pszUnits, "FEET"); |
341 | 0 | else |
342 | 0 | strcpy(pszUnits, "METERS"); |
343 | |
|
344 | 0 | if (EQUAL(pszProj, "RAW")) |
345 | 0 | return OGRERR_UNSUPPORTED_SRS; |
346 | | |
347 | 0 | return OGRERR_NONE; |
348 | 0 | } |