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  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_port.h"
31 #include "ogr_srs_api.h"
32 
33 #include <cmath>
34 #include <cstdio>
35 #include <cstdlib>
36 #include <cstring>
37 
38 #include "cpl_conv.h"
39 #include "cpl_error.h"
40 #include "ogr_core.h"
41 #include "ogr_spatialref.h"
42 
43 CPL_CVSID("$Id: ogr_srs_erm.cpp b1c9c12ad373e40b955162b45d704070d4ebf7b0 2019-06-19 16:50:15 +0200 Even Rouault $")
44 
45 /************************************************************************/
46 /*                         OSRImportFromERM()                           */
47 /************************************************************************/
48 
49 /**
50  * \brief Create OGR WKT from ERMapper projection definitions.
51  *
52  * This function is the same as OGRSpatialReference::importFromERM().
53  */
54 
OSRImportFromERM(OGRSpatialReferenceH hSRS,const char * pszProj,const char * pszDatum,const char * pszUnits)55 OGRErr OSRImportFromERM( OGRSpatialReferenceH hSRS, const char *pszProj,
56                          const char *pszDatum, const char *pszUnits )
57 
58 {
59     VALIDATE_POINTER1( hSRS, "OSRImportFromERM", OGRERR_FAILURE );
60 
61     return reinterpret_cast<OGRSpatialReference *>(hSRS)->
62         importFromERM(pszProj, pszDatum, pszUnits);
63 }
64 
65 /************************************************************************/
66 /*                           importFromERM()                            */
67 /************************************************************************/
68 
69 /**
70  * Create OGR WKT from ERMapper projection definitions.
71  *
72  * Generates an OGRSpatialReference definition from an ERMapper datum
73  * and projection name.  Based on the ecw_cs.wkt dictionary file from
74  * gdal/data.
75  *
76  * @param pszProj the projection name, such as "NUTM11" or "GEOGRAPHIC".
77  * @param pszDatum the datum name, such as "NAD83".
78  * @param pszUnits the linear units "FEET" or "METERS".
79  *
80  * @return OGRERR_NONE on success or OGRERR_UNSUPPORTED_SRS if not found.
81  */
82 
importFromERM(const char * pszProj,const char * pszDatum,const char * pszUnits)83 OGRErr OGRSpatialReference::importFromERM( const char *pszProj,
84                                            const char *pszDatum,
85                                            const char *pszUnits )
86 
87 {
88     Clear();
89 
90 /* -------------------------------------------------------------------- */
91 /*      do we have projection and datum?                                */
92 /* -------------------------------------------------------------------- */
93     if( EQUAL(pszProj, "RAW") )
94         return OGRERR_NONE;
95 
96 /* -------------------------------------------------------------------- */
97 /*      Do we have an EPSG coordinate system?                           */
98 /* -------------------------------------------------------------------- */
99 
100     if( STARTS_WITH_CI(pszProj, "EPSG:") )
101         return importFromEPSG( atoi(pszProj+5) );
102 
103     if( STARTS_WITH_CI(pszDatum, "EPSG:") )
104         return importFromEPSG( atoi(pszDatum+5) );
105 
106 
107     CPLString osGEOGCS = lookupInDict( "ecw_cs.wkt", pszDatum );
108     if( osGEOGCS.empty() )
109         return OGRERR_UNSUPPORTED_SRS;
110 
111 /* -------------------------------------------------------------------- */
112 /*      Set projection if we have it.                                   */
113 /* -------------------------------------------------------------------- */
114     if( !EQUAL(pszProj, "GEODETIC") )
115     {
116         CPLString osProjWKT = lookupInDict( "ecw_cs.wkt", pszProj );
117         if( osProjWKT.empty() || osProjWKT.back() != ']' )
118             return OGRERR_UNSUPPORTED_SRS;
119 
120         if( osProjWKT.find("LOCAL_CS[") == 0 )
121         {
122             return importFromWkt(osProjWKT);
123         }
124 
125         // Remove trailing ]
126         osProjWKT.resize(osProjWKT.size() - 1);
127 
128         // Remove any UNIT
129         auto nPos = osProjWKT.find(",UNIT");
130         if( nPos != std::string::npos )
131         {
132             osProjWKT.resize(nPos);
133         }
134 
135         // Insert GEOGCS
136         nPos = osProjWKT.find(",PROJECTION");
137         if( nPos == std::string::npos )
138             return OGRERR_UNSUPPORTED_SRS;
139 
140         osProjWKT = osProjWKT.substr(0, nPos) + ',' + osGEOGCS + osProjWKT.substr(nPos);
141 
142         if( EQUAL(pszUnits, "FEET") )
143             osProjWKT += ",UNIT[\"Foot_US\",0.3048006096012192]]";
144         else
145             osProjWKT += ",UNIT[\"Metre\",1.0]]";
146 
147         return importFromWkt(osProjWKT);
148     }
149     else
150     {
151         return importFromWkt(osGEOGCS);
152     }
153 }
154 
155 /************************************************************************/
156 /*                          OSRExportToERM()                            */
157 /************************************************************************/
158 /**
159  * \brief Convert coordinate system to ERMapper format.
160  *
161  * This function is the same as OGRSpatialReference::exportToERM().
162  */
OSRExportToERM(OGRSpatialReferenceH hSRS,char * pszProj,char * pszDatum,char * pszUnits)163 OGRErr OSRExportToERM( OGRSpatialReferenceH hSRS,
164                        char *pszProj, char *pszDatum, char *pszUnits )
165 
166 {
167     VALIDATE_POINTER1( hSRS, "OSRExportToERM", OGRERR_FAILURE );
168 
169     return reinterpret_cast<OGRSpatialReference *>(hSRS)->
170         exportToERM(pszProj, pszDatum, pszUnits);
171 }
172 
173 /************************************************************************/
174 /*                            exportToERM()                             */
175 /************************************************************************/
176 
177 /**
178  * Convert coordinate system to ERMapper format.
179  *
180  * @param pszProj 32 character buffer to receive projection name.
181  * @param pszDatum 32 character buffer to receive datum name.
182  * @param pszUnits 32 character buffer to receive units name.
183  *
184  * @return OGRERR_NONE on success, OGRERR_SRS_UNSUPPORTED if not translation is
185  * found, or OGRERR_FAILURE on other failures.
186  */
187 
exportToERM(char * pszProj,char * pszDatum,char * pszUnits)188 OGRErr OGRSpatialReference::exportToERM( char *pszProj, char *pszDatum,
189                                          char *pszUnits )
190 
191 {
192     const int BUFFER_SIZE = 32;
193     strcpy( pszProj, "RAW" );
194     strcpy( pszDatum, "RAW" );
195     strcpy( pszUnits, "METERS" );
196 
197     if( !IsProjected() && !IsGeographic() )
198         return OGRERR_UNSUPPORTED_SRS;
199 
200 /* -------------------------------------------------------------------- */
201 /*      Try to find the EPSG code.                                      */
202 /* -------------------------------------------------------------------- */
203     int nEPSGCode = 0;
204 
205     if( IsProjected() )
206     {
207         const char *pszAuthName = GetAuthorityName( "PROJCS" );
208 
209         if( pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") )
210         {
211             nEPSGCode = atoi(GetAuthorityCode( "PROJCS" ));
212         }
213     }
214     else if( IsGeographic() )
215     {
216         const char *pszAuthName = GetAuthorityName( "GEOGCS" );
217 
218         if( pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") )
219         {
220             nEPSGCode = atoi(GetAuthorityCode( "GEOGCS" ));
221         }
222     }
223 
224 /* -------------------------------------------------------------------- */
225 /*      Is our GEOGCS name already defined in ecw_cs.wkt?               */
226 /* -------------------------------------------------------------------- */
227     const char *pszWKTDatum = GetAttrValue( "DATUM" );
228 
229     if( pszWKTDatum != nullptr
230         && !lookupInDict( "ecw_cs.wkt", pszWKTDatum ).empty() )
231     {
232         strncpy( pszDatum, pszWKTDatum, BUFFER_SIZE );
233         pszDatum[BUFFER_SIZE-1] = '\0';
234     }
235 
236 /* -------------------------------------------------------------------- */
237 /*      Is this a "well known" geographic coordinate system?            */
238 /* -------------------------------------------------------------------- */
239     if( EQUAL(pszDatum, "RAW") )
240     {
241         int nEPSGGCSCode = GetEPSGGeogCS();
242 
243         if( nEPSGGCSCode == 4326 )
244             strcpy( pszDatum, "WGS84" );
245 
246         else if( nEPSGGCSCode == 4322 )
247             strcpy( pszDatum, "WGS72DOD" );
248 
249         else if( nEPSGGCSCode == 4267 )
250             strcpy( pszDatum, "NAD27" );
251 
252         else if( nEPSGGCSCode == 4269 )
253             strcpy( pszDatum, "NAD83" );
254 
255         else if( nEPSGGCSCode == 4277 )
256             strcpy( pszDatum, "OSGB36" );
257 
258         else if( nEPSGGCSCode == 4278 )
259             strcpy( pszDatum, "OSGB78" );
260 
261         else if( nEPSGGCSCode == 4201 )
262             strcpy( pszDatum, "ADINDAN" );
263 
264         else if( nEPSGGCSCode == 4202 )
265             strcpy( pszDatum, "AGD66" );
266 
267         else if( nEPSGGCSCode == 4203 )
268             strcpy( pszDatum, "AGD84" );
269 
270         else if( nEPSGGCSCode == 4209 )
271             strcpy( pszDatum, "ARC1950" );
272 
273         else if( nEPSGGCSCode == 4210 )
274             strcpy( pszDatum, "ARC1960" );
275 
276         else if( nEPSGGCSCode == 4275 )
277             strcpy( pszDatum, "NTF" );
278 
279         else if( nEPSGGCSCode == 4283 )
280             strcpy( pszDatum, "GDA94" );
281 
282         else if( nEPSGGCSCode == 4284 )
283             strcpy( pszDatum, "PULKOVO" );
284     }
285 
286 /* -------------------------------------------------------------------- */
287 /*      Are we working with a geographic (geodetic) coordinate system?  */
288 /* -------------------------------------------------------------------- */
289 
290     if( IsGeographic() )
291     {
292         if( EQUAL(pszDatum, "RAW") )
293             return OGRERR_UNSUPPORTED_SRS;
294         else
295         {
296             strcpy( pszProj, "GEODETIC" );
297             return OGRERR_NONE;
298         }
299     }
300 
301 /* -------------------------------------------------------------------- */
302 /*      Is this a UTM projection?                                       */
303 /* -------------------------------------------------------------------- */
304     int bNorth = FALSE;
305     int nZone = 0;
306 
307     nZone = GetUTMZone( &bNorth );
308     if( nZone > 0 )
309     {
310         if( EQUAL(pszDatum, "GDA94") && !bNorth && nZone >= 48 && nZone <= 58)
311         {
312             snprintf( pszProj, BUFFER_SIZE, "MGA%02d", nZone );
313         }
314         else
315         {
316             if( bNorth )
317                 snprintf( pszProj, BUFFER_SIZE, "NUTM%02d", nZone );
318             else
319                 snprintf( pszProj, BUFFER_SIZE, "SUTM%02d", nZone );
320         }
321     }
322 
323 /* -------------------------------------------------------------------- */
324 /*      Is our PROJCS name already defined in ecw_cs.wkt?               */
325 /* -------------------------------------------------------------------- */
326     else
327     {
328         const char *pszPROJCS = GetAttrValue( "PROJCS" );
329 
330         if( pszPROJCS != nullptr
331             && lookupInDict( "ecw_cs.wkt", pszPROJCS ).find("PROJCS") == 0 )
332         {
333             strncpy( pszProj, pszPROJCS, BUFFER_SIZE );
334             pszProj[BUFFER_SIZE-1] = '\0';
335         }
336     }
337 
338 /* -------------------------------------------------------------------- */
339 /*      If we have not translated it yet, but we have an EPSG code      */
340 /*      then use EPSG:n notation.                                       */
341 /* -------------------------------------------------------------------- */
342     if( (EQUAL(pszDatum, "RAW") || EQUAL(pszProj, "RAW")) && nEPSGCode != 0 )
343     {
344         snprintf( pszProj, BUFFER_SIZE, "EPSG:%d", nEPSGCode );
345         snprintf( pszDatum, BUFFER_SIZE, "EPSG:%d", nEPSGCode );
346     }
347 
348 /* -------------------------------------------------------------------- */
349 /*      Handle the units.                                               */
350 /* -------------------------------------------------------------------- */
351     const double dfUnits = GetLinearUnits();
352 
353     if( fabs(dfUnits-0.3048) < 0.0001 )
354         strcpy( pszUnits, "FEET" );
355     else
356         strcpy( pszUnits, "METERS" );
357 
358     if( EQUAL(pszProj, "RAW") )
359         return OGRERR_UNSUPPORTED_SRS;
360 
361     return OGRERR_NONE;
362 }
363