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