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