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