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