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 53583 : int MAPDataset::Identify(GDALOpenInfo *poOpenInfo)
150 :
151 : {
152 57462 : if (poOpenInfo->nHeaderBytes < 200 ||
153 3879 : !poOpenInfo->IsExtensionEqualToCI("MAP"))
154 53586 : 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 : ReportUpdateNotSupportedByDriver("MAP");
178 0 : return nullptr;
179 : }
180 :
181 : /* -------------------------------------------------------------------- */
182 : /* Create a corresponding GDALDataset. */
183 : /* -------------------------------------------------------------------- */
184 :
185 0 : MAPDataset *poDS = new MAPDataset();
186 :
187 : /* -------------------------------------------------------------------- */
188 : /* Try to load and parse the .MAP file. */
189 : /* -------------------------------------------------------------------- */
190 :
191 0 : char *pszWKT = nullptr;
192 0 : bool bOziFileOK = CPL_TO_BOOL(
193 0 : GDALLoadOziMapFile(poOpenInfo->pszFilename, poDS->adfGeoTransform,
194 : &pszWKT, &poDS->nGCPCount, &poDS->pasGCPList));
195 0 : if (pszWKT)
196 : {
197 0 : poDS->m_oSRS.importFromWkt(pszWKT);
198 0 : CPLFree(pszWKT);
199 : }
200 :
201 0 : if (bOziFileOK && poDS->nGCPCount == 0)
202 0 : poDS->bGeoTransformValid = TRUE;
203 :
204 : /* We need to read again the .map file because the GDALLoadOziMapFile
205 : function does not returns all required data . An API change is necessary
206 : : maybe in GDAL 2.0 ? */
207 :
208 0 : char **papszLines = CSLLoad2(poOpenInfo->pszFilename, 200, 200, nullptr);
209 :
210 0 : if (!papszLines)
211 : {
212 0 : delete poDS;
213 0 : return nullptr;
214 : }
215 :
216 0 : const int nLines = CSLCount(papszLines);
217 0 : if (nLines < 3)
218 : {
219 0 : delete poDS;
220 0 : CSLDestroy(papszLines);
221 0 : return nullptr;
222 : }
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* We need to open the image in order to establish */
226 : /* details like the band count and types. */
227 : /* -------------------------------------------------------------------- */
228 0 : poDS->osImgFilename = papszLines[2];
229 :
230 0 : const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
231 0 : if (CPLIsFilenameRelative(poDS->osImgFilename))
232 : {
233 : poDS->osImgFilename =
234 0 : CPLFormCIFilenameSafe(osPath, poDS->osImgFilename, nullptr);
235 : }
236 : else
237 : {
238 : VSIStatBufL sStat;
239 0 : if (VSIStatL(poDS->osImgFilename, &sStat) != 0)
240 : {
241 0 : poDS->osImgFilename = CPLGetFilename(poDS->osImgFilename);
242 : poDS->osImgFilename =
243 0 : CPLFormCIFilenameSafe(osPath, poDS->osImgFilename, nullptr);
244 : }
245 : }
246 :
247 : /* -------------------------------------------------------------------- */
248 : /* Try and open the file. */
249 : /* -------------------------------------------------------------------- */
250 0 : poDS->poImageDS =
251 0 : GDALDataset::FromHandle(GDALOpen(poDS->osImgFilename, GA_ReadOnly));
252 0 : if (poDS->poImageDS == nullptr || poDS->poImageDS->GetRasterCount() == 0)
253 : {
254 0 : CSLDestroy(papszLines);
255 0 : delete poDS;
256 0 : return nullptr;
257 : }
258 :
259 : /* -------------------------------------------------------------------- */
260 : /* Attach the bands. */
261 : /* -------------------------------------------------------------------- */
262 0 : poDS->nRasterXSize = poDS->poImageDS->GetRasterXSize();
263 0 : poDS->nRasterYSize = poDS->poImageDS->GetRasterYSize();
264 0 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
265 : {
266 0 : CSLDestroy(papszLines);
267 0 : GDALClose(poDS->poImageDS);
268 0 : delete poDS;
269 0 : return nullptr;
270 : }
271 :
272 0 : for (int iBand = 1; iBand <= poDS->poImageDS->GetRasterCount(); iBand++)
273 0 : poDS->SetBand(iBand, new MAPWrapperRasterBand(
274 0 : poDS->poImageDS->GetRasterBand(iBand)));
275 :
276 : /* -------------------------------------------------------------------- */
277 : /* Add the neatline/cutline, if required */
278 : /* -------------------------------------------------------------------- */
279 :
280 : /* First, we need to check if it is necessary to define a neatline */
281 0 : bool bNeatLine = false;
282 0 : for (int iLine = 10; iLine < nLines; iLine++)
283 : {
284 0 : if (STARTS_WITH_CI(papszLines[iLine], "MMPXY,"))
285 : {
286 : char **papszTok =
287 0 : CSLTokenizeString2(papszLines[iLine], ",",
288 : CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
289 :
290 0 : if (CSLCount(papszTok) != 4)
291 : {
292 0 : CSLDestroy(papszTok);
293 0 : continue;
294 : }
295 :
296 0 : const int x = atoi(papszTok[2]);
297 0 : const int y = atoi(papszTok[3]);
298 0 : if ((x != 0 && x != poDS->nRasterXSize) ||
299 0 : (y != 0 && y != poDS->nRasterYSize))
300 : {
301 0 : bNeatLine = true;
302 0 : CSLDestroy(papszTok);
303 0 : break;
304 : }
305 0 : CSLDestroy(papszTok);
306 : }
307 : }
308 :
309 : /* Create and fill the neatline polygon */
310 0 : if (bNeatLine)
311 : {
312 0 : poDS->poNeatLine =
313 0 : new OGRPolygon(); /* Create a polygon to store the neatline */
314 0 : OGRLinearRing *poRing = new OGRLinearRing();
315 :
316 0 : if (poDS->bGeoTransformValid) /* Compute the projected coordinates of
317 : the corners */
318 : {
319 0 : for (int iLine = 10; iLine < nLines; iLine++)
320 : {
321 0 : if (STARTS_WITH_CI(papszLines[iLine], "MMPXY,"))
322 : {
323 0 : char **papszTok = CSLTokenizeString2(
324 0 : papszLines[iLine], ",",
325 : CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
326 :
327 0 : if (CSLCount(papszTok) != 4)
328 : {
329 0 : CSLDestroy(papszTok);
330 0 : continue;
331 : }
332 :
333 0 : const double x = CPLAtofM(papszTok[2]);
334 0 : const double y = CPLAtofM(papszTok[3]);
335 0 : const double X = poDS->adfGeoTransform[0] +
336 0 : x * poDS->adfGeoTransform[1] +
337 0 : y * poDS->adfGeoTransform[2];
338 0 : const double Y = poDS->adfGeoTransform[3] +
339 0 : x * poDS->adfGeoTransform[4] +
340 0 : y * poDS->adfGeoTransform[5];
341 0 : poRing->addPoint(X, Y);
342 0 : CPLDebug("CORNER MMPXY", "%f, %f, %f, %f", x, y, X, Y);
343 0 : CSLDestroy(papszTok);
344 : }
345 : }
346 : }
347 : else /* Convert the geographic coordinates to projected coordinates */
348 : {
349 0 : OGRCoordinateTransformation *poTransform = nullptr;
350 0 : if (!poDS->m_oSRS.IsEmpty())
351 : {
352 0 : OGRSpatialReference *poLongLat = poDS->m_oSRS.CloneGeogCS();
353 0 : if (poLongLat)
354 : {
355 0 : poLongLat->SetAxisMappingStrategy(
356 : OAMS_TRADITIONAL_GIS_ORDER);
357 0 : poTransform = OGRCreateCoordinateTransformation(
358 0 : poLongLat, &poDS->m_oSRS);
359 0 : delete poLongLat;
360 : }
361 : }
362 :
363 0 : for (int iLine = 10; iLine < nLines; iLine++)
364 : {
365 0 : if (STARTS_WITH_CI(papszLines[iLine], "MMPLL,"))
366 : {
367 0 : CPLDebug("MMPLL", "%s", papszLines[iLine]);
368 :
369 0 : char **papszTok = CSLTokenizeString2(
370 0 : papszLines[iLine], ",",
371 : CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
372 :
373 0 : if (CSLCount(papszTok) != 4)
374 : {
375 0 : CSLDestroy(papszTok);
376 0 : continue;
377 : }
378 :
379 0 : double dfLon = CPLAtofM(papszTok[2]);
380 0 : double dfLat = CPLAtofM(papszTok[3]);
381 :
382 0 : if (poTransform)
383 0 : poTransform->Transform(1, &dfLon, &dfLat);
384 0 : poRing->addPoint(dfLon, dfLat);
385 0 : CPLDebug("CORNER MMPLL", "%f, %f", dfLon, dfLat);
386 0 : CSLDestroy(papszTok);
387 : }
388 : }
389 0 : if (poTransform)
390 0 : delete poTransform;
391 : }
392 :
393 0 : poRing->closeRings();
394 0 : poDS->poNeatLine->addRingDirectly(poRing);
395 :
396 0 : char *pszNeatLineWkt = nullptr;
397 0 : poDS->poNeatLine->exportToWkt(&pszNeatLineWkt);
398 0 : CPLDebug("NEATLINE", "%s", pszNeatLineWkt);
399 0 : poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt);
400 0 : CPLFree(pszNeatLineWkt);
401 : }
402 :
403 0 : CSLDestroy(papszLines);
404 :
405 0 : return poDS;
406 : }
407 :
408 : /************************************************************************/
409 : /* GetSpatialRef() */
410 : /************************************************************************/
411 :
412 0 : const OGRSpatialReference *MAPDataset::GetSpatialRef() const
413 : {
414 0 : return (!m_oSRS.IsEmpty() && nGCPCount == 0) ? &m_oSRS : nullptr;
415 : }
416 :
417 : /************************************************************************/
418 : /* GetGeoTransform() */
419 : /************************************************************************/
420 :
421 0 : CPLErr MAPDataset::GetGeoTransform(double *padfTransform)
422 :
423 : {
424 0 : memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
425 :
426 0 : return (nGCPCount == 0) ? CE_None : CE_Failure;
427 : }
428 :
429 : /************************************************************************/
430 : /* GetGCPCount() */
431 : /************************************************************************/
432 :
433 0 : int MAPDataset::GetGCPCount()
434 : {
435 0 : return nGCPCount;
436 : }
437 :
438 : /************************************************************************/
439 : /* GetGCPSpatialRef() */
440 : /************************************************************************/
441 :
442 0 : const OGRSpatialReference *MAPDataset::GetGCPSpatialRef() const
443 : {
444 0 : return (!m_oSRS.IsEmpty() && nGCPCount != 0) ? &m_oSRS : nullptr;
445 : }
446 :
447 : /************************************************************************/
448 : /* GetGCPs() */
449 : /************************************************************************/
450 :
451 0 : const GDAL_GCP *MAPDataset::GetGCPs()
452 : {
453 0 : return pasGCPList;
454 : }
455 :
456 : /************************************************************************/
457 : /* GetFileList() */
458 : /************************************************************************/
459 :
460 0 : char **MAPDataset::GetFileList()
461 : {
462 0 : char **papszFileList = GDALDataset::GetFileList();
463 :
464 0 : papszFileList = CSLAddString(papszFileList, osImgFilename);
465 :
466 0 : return papszFileList;
467 : }
468 :
469 : /************************************************************************/
470 : /* GDALRegister_MAP() */
471 : /************************************************************************/
472 :
473 1686 : void GDALRegister_MAP()
474 :
475 : {
476 1686 : if (GDALGetDriverByName("MAP") != nullptr)
477 302 : return;
478 :
479 1384 : GDALDriver *poDriver = new GDALDriver();
480 :
481 1384 : poDriver->SetDescription("MAP");
482 1384 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
483 1384 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "OziExplorer .MAP");
484 1384 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/map.html");
485 :
486 1384 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
487 :
488 1384 : poDriver->pfnOpen = MAPDataset::Open;
489 1384 : poDriver->pfnIdentify = MAPDataset::Identify;
490 :
491 1384 : GetGDALDriverManager()->RegisterDriver(poDriver);
492 : }
|