1 /******************************************************************************
2  *
3  * Project:  OziExplorer .MAP Driver
4  * Purpose:  GDALDataset driver for OziExplorer .MAP files
5  * Author:   Jean-Claude Repetto, <jrepetto at @free dot fr>
6  *
7  ******************************************************************************
8  * Copyright (c) 2012, Jean-Claude Repetto
9  * Copyright (c) 2012, Even Rouault <even dot rouault at mines-paris dot org>
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 "gdal_pam.h"
31 #include "gdal_proxy.h"
32 #include "ogr_spatialref.h"
33 #include "ogr_geometry.h"
34 
35 CPL_CVSID("$Id: mapdataset.cpp 27559 2014-08-04 17:52:19Z rouault $");
36 
37 /************************************************************************/
38 /* ==================================================================== */
39 /*                                MAPDataset                            */
40 /* ==================================================================== */
41 /************************************************************************/
42 
43 class CPL_DLL MAPDataset : public GDALDataset
44 {
45     GDALDataset *poImageDS;
46 
47     char        *pszWKT;
48     int	        bGeoTransformValid;
49     double      adfGeoTransform[6];
50     int         nGCPCount;
51     GDAL_GCP	*pasGCPList;
52     OGRPolygon  *poNeatLine;
53     CPLString   osImgFilename;
54 
55   public:
56     MAPDataset();
57     virtual ~MAPDataset();
58 
59     virtual const char* GetProjectionRef();
60     virtual CPLErr      GetGeoTransform( double * );
61     virtual int GetGCPCount();
62     virtual const char *GetGCPProjection();
63     virtual const GDAL_GCP *GetGCPs();
64     virtual char **GetFileList();
65 
66     virtual int         CloseDependentDatasets();
67 
68     static GDALDataset *Open( GDALOpenInfo * );
69     static int Identify( GDALOpenInfo *poOpenInfo );
70 };
71 
72 /************************************************************************/
73 /* ==================================================================== */
74 /*                         MAPWrapperRasterBand                         */
75 /* ==================================================================== */
76 /************************************************************************/
77 class MAPWrapperRasterBand : public GDALProxyRasterBand
78 {
79   GDALRasterBand* poBaseBand;
80 
81   protected:
RefUnderlyingRasterBand()82     virtual GDALRasterBand* RefUnderlyingRasterBand() { return poBaseBand; }
83 
84   public:
MAPWrapperRasterBand(GDALRasterBand * poBaseBand)85     MAPWrapperRasterBand( GDALRasterBand* poBaseBand )
86         {
87             this->poBaseBand = poBaseBand;
88             eDataType = poBaseBand->GetRasterDataType();
89             poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
90         }
~MAPWrapperRasterBand()91     ~MAPWrapperRasterBand() {}
92 };
93 
94 /************************************************************************/
95 /* ==================================================================== */
96 /*                             MAPDataset                               */
97 /* ==================================================================== */
98 /************************************************************************/
99 
MAPDataset()100 MAPDataset::MAPDataset()
101 
102 {
103     poImageDS = NULL;
104     pszWKT = NULL;
105     adfGeoTransform[0] = 0.0;
106     adfGeoTransform[1] = 1.0;
107     adfGeoTransform[2] = 0.0;
108     adfGeoTransform[3] = 0.0;
109     adfGeoTransform[4] = 0.0;
110     adfGeoTransform[5] = 1.0;
111     nGCPCount = 0;
112     pasGCPList = NULL;
113     poNeatLine = NULL;
114     bGeoTransformValid = false;
115 }
116 
117 /************************************************************************/
118 /*                            ~MAPDataset()                             */
119 /************************************************************************/
120 
~MAPDataset()121 MAPDataset::~MAPDataset()
122 
123 {
124     if (poImageDS != NULL)
125     {
126         GDALClose( poImageDS );
127         poImageDS = NULL;
128     }
129     if (pszWKT != NULL)
130     {
131         CPLFree(pszWKT);
132         pszWKT = NULL;
133     }
134     if (nGCPCount)
135     {
136         GDALDeinitGCPs( nGCPCount, pasGCPList );
137         CPLFree(pasGCPList);
138     }
139 
140     if ( poNeatLine != NULL )
141     {
142         delete poNeatLine;
143         poNeatLine = NULL;
144     }
145 }
146 
147 /************************************************************************/
148 /*                       CloseDependentDatasets()                       */
149 /************************************************************************/
150 
CloseDependentDatasets()151 int MAPDataset::CloseDependentDatasets()
152 {
153     int bRet = GDALDataset::CloseDependentDatasets();
154     if (poImageDS != NULL)
155     {
156         GDALClose( poImageDS );
157         poImageDS = NULL;
158         bRet = TRUE;
159     }
160     return bRet;
161 }
162 
163 /************************************************************************/
164 /*                              Identify()                              */
165 /************************************************************************/
166 
Identify(GDALOpenInfo * poOpenInfo)167 int MAPDataset::Identify( GDALOpenInfo *poOpenInfo )
168 
169 {
170     if( poOpenInfo->nHeaderBytes < 200
171         || !EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"MAP") )
172         return FALSE;
173 
174     if( strstr((const char *) poOpenInfo->pabyHeader,"OziExplorer Map Data File") == NULL )
175         return FALSE;
176     else
177         return TRUE;
178 }
179 
180 /************************************************************************/
181 /*                                Open()                                */
182 /************************************************************************/
183 
184 #define MAX_GCP 30
185 
Open(GDALOpenInfo * poOpenInfo)186 GDALDataset *MAPDataset::Open( GDALOpenInfo * poOpenInfo )
187 {
188     if( !Identify( poOpenInfo ) )
189         return NULL;
190 
191 /* -------------------------------------------------------------------- */
192 /*      Confirm the requested access is supported.                      */
193 /* -------------------------------------------------------------------- */
194     if( poOpenInfo->eAccess == GA_Update )
195     {
196         CPLError( CE_Failure, CPLE_NotSupported,
197                   "The MAP driver does not support update access to existing"
198                   " datasets.\n" );
199         return NULL;
200     }
201 
202 /* -------------------------------------------------------------------- */
203 /*      Create a corresponding GDALDataset.                             */
204 /* -------------------------------------------------------------------- */
205 
206     MAPDataset *poDS = new MAPDataset();
207 
208 /* -------------------------------------------------------------------- */
209 /*      Try to load and parse the .MAP file.                            */
210 /* -------------------------------------------------------------------- */
211 
212     int bOziFileOK =
213          GDALLoadOziMapFile( poOpenInfo->pszFilename,
214                              poDS->adfGeoTransform,
215                              &poDS->pszWKT,
216                              &poDS->nGCPCount, &poDS->pasGCPList );
217 
218     if ( bOziFileOK && poDS->nGCPCount == 0 )
219          poDS->bGeoTransformValid = TRUE;
220 
221    /* We need to read again the .map file because the GDALLoadOziMapFile function
222       does not returns all required data . An API change is necessary : maybe in GDAL 2.0 ? */
223 
224     char	**papszLines;
225     int		iLine, nLines=0;
226 
227     papszLines = CSLLoad2( poOpenInfo->pszFilename, 200, 200, NULL );
228 
229     if ( !papszLines )
230         return NULL;
231 
232     nLines = CSLCount( papszLines );
233     if( nLines < 2 )
234     {
235         CSLDestroy(papszLines);
236         return NULL;
237     }
238 
239 /* -------------------------------------------------------------------- */
240 /*      We need to open the image in order to establish                 */
241 /*      details like the band count and types.                          */
242 /* -------------------------------------------------------------------- */
243     poDS->osImgFilename = papszLines[2];
244     VSIStatBufL sStat;
245     if (VSIStatL(poDS->osImgFilename, &sStat) != 0)
246     {
247         CPLString osPath = CPLGetPath(poOpenInfo->pszFilename);
248         if (CPLIsFilenameRelative(poDS->osImgFilename))
249         {
250             poDS->osImgFilename = CPLFormCIFilename(osPath, poDS->osImgFilename, NULL);
251         }
252         else
253         {
254             poDS->osImgFilename = CPLGetFilename(poDS->osImgFilename);
255             poDS->osImgFilename = CPLFormCIFilename(osPath, poDS->osImgFilename, NULL);
256         }
257     }
258 
259 /* -------------------------------------------------------------------- */
260 /*      Try and open the file.                                          */
261 /* -------------------------------------------------------------------- */
262     poDS->poImageDS = (GDALDataset *) GDALOpen(poDS->osImgFilename, GA_ReadOnly );
263     if( poDS->poImageDS == NULL || poDS->poImageDS->GetRasterCount() == 0)
264     {
265         CSLDestroy(papszLines);
266         delete poDS;
267         return NULL;
268     }
269 
270 /* -------------------------------------------------------------------- */
271 /*      Attach the bands.                                               */
272 /* -------------------------------------------------------------------- */
273     int iBand;
274 
275     poDS->nRasterXSize = poDS->poImageDS->GetRasterXSize();
276     poDS->nRasterYSize = poDS->poImageDS->GetRasterYSize();
277     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
278     {
279         delete poDS;
280         GDALClose( poDS->poImageDS );
281         return NULL;
282     }
283 
284     for( iBand = 1; iBand <= poDS->poImageDS->GetRasterCount(); iBand++ )
285         poDS->SetBand( iBand,
286                        new MAPWrapperRasterBand( poDS->poImageDS->GetRasterBand( iBand )) );
287 
288 /* -------------------------------------------------------------------- */
289 /*      Add the neatline/cutline, if required                           */
290 /* -------------------------------------------------------------------- */
291 
292     /* First, we need to check if it is necessary to define a neatline */
293     bool bNeatLine = false;
294     char **papszTok;
295     for ( iLine = 10; iLine < nLines; iLine++ )
296     {
297         if ( EQUALN(papszLines[iLine], "MMPXY,", 6) )
298         {
299             papszTok = CSLTokenizeString2( papszLines[iLine], ",",
300                                            CSLT_STRIPLEADSPACES
301                                            | CSLT_STRIPENDSPACES );
302 
303             if ( CSLCount(papszTok) != 4 )
304             {
305                 CSLDestroy(papszTok);
306                 continue;
307             }
308 
309             int x = CPLAtofM(papszTok[2]);
310             int y = CPLAtofM(papszTok[3]);
311             if (( x != 0 && x != poDS->nRasterXSize) || (y != 0 && y != poDS->nRasterYSize) )
312             {
313                 bNeatLine = true;
314                 CSLDestroy(papszTok);
315                 break;
316             }
317             CSLDestroy(papszTok);
318         }
319     }
320 
321     /* Create and fill the neatline polygon */
322     if (bNeatLine)
323     {
324         poDS->poNeatLine = new OGRPolygon();	/* Create a polygon to store the neatline */
325         OGRLinearRing* poRing = new OGRLinearRing();
326 
327         if ( poDS->bGeoTransformValid )        /* Compute the projected coordinates of the corners */
328         {
329             for ( iLine = 10; iLine < nLines; iLine++ )
330             {
331                 if ( EQUALN(papszLines[iLine], "MMPXY,", 6) )
332                 {
333                     papszTok = CSLTokenizeString2( papszLines[iLine], ",",
334                                                    CSLT_STRIPLEADSPACES
335                                                    | CSLT_STRIPENDSPACES );
336 
337                     if ( CSLCount(papszTok) != 4 )
338                     {
339                         CSLDestroy(papszTok);
340                         continue;
341                     }
342 
343                     double x = CPLAtofM(papszTok[2]);
344                     double y = CPLAtofM(papszTok[3]);
345                     double X = poDS->adfGeoTransform[0] + x * poDS->adfGeoTransform[1] +
346                                             y * poDS->adfGeoTransform[2];
347                     double Y = poDS->adfGeoTransform[3] + x * poDS->adfGeoTransform[4] +
348                                             y * poDS->adfGeoTransform[5];
349                     poRing->addPoint(X, Y);
350                     CPLDebug( "CORNER MMPXY", "%f, %f, %f, %f", x, y, X, Y);
351                     CSLDestroy(papszTok);
352                 }
353             }
354         }
355         else /* Convert the geographic coordinates to projected coordinates */
356         {
357             OGRSpatialReference oSRS;
358             OGRSpatialReference *poLatLong = NULL;
359             OGRCoordinateTransformation *poTransform = NULL;
360             OGRErr eErr;
361             char *pszWKT = poDS->pszWKT;
362 
363             if ( &poDS->pszWKT != NULL )
364             {
365                 eErr = oSRS.importFromWkt ( &pszWKT );
366                 if ( eErr == OGRERR_NONE )
367                     poLatLong = oSRS.CloneGeogCS();
368                     if ( poLatLong )
369                         poTransform = OGRCreateCoordinateTransformation( poLatLong, &oSRS );
370             }
371 
372             for ( iLine = 10; iLine < nLines; iLine++ )
373             {
374                 if ( EQUALN(papszLines[iLine], "MMPLL,", 6) )
375                 {
376                     CPLDebug( "MMPLL", "%s", papszLines[iLine] );
377                     char **papszTok = NULL;
378 
379                     papszTok = CSLTokenizeString2( papszLines[iLine], ",",
380                                                    CSLT_STRIPLEADSPACES
381                                                    | CSLT_STRIPENDSPACES );
382 
383                     if ( CSLCount(papszTok) != 4 )
384                     {
385                          CSLDestroy(papszTok);
386                          continue;
387                     }
388 
389                     double dfLon = CPLAtofM(papszTok[2]);
390                     double dfLat = CPLAtofM(papszTok[3]);
391 
392                     if ( poTransform )
393                         poTransform->Transform( 1, &dfLon, &dfLat );
394                     poRing->addPoint(dfLon, dfLat);
395                     CPLDebug( "CORNER MMPLL", "%f, %f", dfLon, dfLat);
396                     CSLDestroy(papszTok);
397                 }
398             }
399             if (poTransform)
400                 delete poTransform;
401             if (poLatLong)
402                 delete poLatLong;
403         }
404 
405         poRing->closeRings();
406         poDS->poNeatLine->addRingDirectly(poRing);
407 
408         char* pszNeatLineWkt = NULL;
409         poDS->poNeatLine->exportToWkt(&pszNeatLineWkt);
410         CPLDebug( "NEATLINE", "%s", pszNeatLineWkt);
411         poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt);
412         CPLFree(pszNeatLineWkt);
413     }
414 
415     CSLDestroy(papszLines);
416 
417     return( poDS );
418 }
419 
420 /************************************************************************/
421 /*                          GetProjectionRef()                          */
422 /************************************************************************/
423 
GetProjectionRef()424 const char* MAPDataset::GetProjectionRef()
425 {
426     return (pszWKT && nGCPCount == 0) ? pszWKT : "";
427 }
428 
429 /************************************************************************/
430 /*                          GetGeoTransform()                           */
431 /************************************************************************/
432 
GetGeoTransform(double * padfTransform)433 CPLErr MAPDataset::GetGeoTransform( double * padfTransform )
434 
435 {
436     memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
437 
438     return( (nGCPCount == 0) ? CE_None : CE_Failure );
439 }
440 
441 
442 /************************************************************************/
443 /*                           GetGCPCount()                              */
444 /************************************************************************/
445 
GetGCPCount()446 int MAPDataset::GetGCPCount()
447 {
448     return nGCPCount;
449 }
450 
451 /************************************************************************/
452 /*                          GetGCPProjection()                          */
453 /************************************************************************/
454 
GetGCPProjection()455 const char * MAPDataset::GetGCPProjection()
456 {
457     return (pszWKT && nGCPCount != 0) ? pszWKT : "";
458 }
459 
460 /************************************************************************/
461 /*                               GetGCPs()                              */
462 /************************************************************************/
463 
GetGCPs()464 const GDAL_GCP * MAPDataset::GetGCPs()
465 {
466     return pasGCPList;
467 }
468 
469 /************************************************************************/
470 /*                            GetFileList()                             */
471 /************************************************************************/
472 
GetFileList()473 char** MAPDataset::GetFileList()
474 {
475     char **papszFileList = GDALDataset::GetFileList();
476 
477     papszFileList = CSLAddString( papszFileList, osImgFilename );
478 
479     return papszFileList;
480 }
481 
482 /************************************************************************/
483 /*                          GDALRegister_MAP()                          */
484 /************************************************************************/
485 
GDALRegister_MAP()486 void GDALRegister_MAP()
487 
488 {
489     GDALDriver	*poDriver;
490 
491     if( GDALGetDriverByName( "MAP" ) == NULL )
492     {
493         poDriver = new GDALDriver();
494 
495         poDriver->SetDescription( "MAP" );
496         poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
497         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
498                                    "OziExplorer .MAP" );
499         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
500                                    "frmt_map.html" );
501 
502         poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
503 
504         poDriver->pfnOpen = MAPDataset::Open;
505         poDriver->pfnIdentify = MAPDataset::Identify;
506 
507         GetGDALDriverManager()->RegisterDriver( poDriver );
508     }
509 }
510 
511 
512