Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: DRDC Configurable Airborne SAR Processor (COASP) data reader
4 : * Purpose: Support in GDAL for the DRDC COASP format data, both Metadata
5 : * and complex imagery.
6 : * Author: Philippe Vachon <philippe@cowpig.ca>
7 : * Notes: I have seen a grand total of 2 COASP scenes (3 sets of headers).
8 : * This is based on my best observations, some educated guesses and
9 : * such. So if you have a scene that doesn't work, send it to me
10 : * please and I will make it work... with violence.
11 : *
12 : ******************************************************************************
13 : * Copyright (c) 2007, Philippe Vachon
14 : * Copyright (c) 2009-2012, Even Rouault <even dot rouault at spatialys.com>
15 : *
16 : * Permission is hereby granted, free of charge, to any person obtaining a
17 : * copy of this software and associated documentation files (the "Software"),
18 : * to deal in the Software without restriction, including without limitation
19 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20 : * and/or sell copies of the Software, and to permit persons to whom the
21 : * Software is furnished to do so, subject to the following conditions:
22 : *
23 : * The above copyright notice and this permission notice shall be included
24 : * in all copies or substantial portions of the Software.
25 : *
26 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
29 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 : * DEALINGS IN THE SOFTWARE.
33 : ****************************************************************************/
34 :
35 : #include "cpl_conv.h"
36 : #include "cpl_port.h"
37 : #include "cpl_string.h"
38 : #include "cpl_vsi.h"
39 : #include "gdal_frmts.h"
40 : #include "gdal_priv.h"
41 :
42 : constexpr int TYPE_GENERIC = 0;
43 : constexpr int TYPE_GEOREF = 1;
44 :
45 : enum ePolarization
46 : {
47 : hh = 0,
48 : hv,
49 : vh,
50 : vv
51 : };
52 :
53 : /*******************************************************************
54 : * Declaration of the COASPMetadata classes *
55 : *******************************************************************/
56 :
57 : class COASPMetadataItem;
58 :
59 : class COASPMetadataReader
60 : {
61 : char **papszMetadata;
62 : int nMetadataCount;
63 : int nCurrentItem;
64 :
65 : public:
66 : explicit COASPMetadataReader(char *pszFname);
67 : ~COASPMetadataReader();
68 : COASPMetadataItem *GetNextItem();
69 : COASPMetadataItem *GetItem(int nItem);
70 : int GotoMetadataItem(const char *pszName);
71 :
72 : int GetCurrentItem() const
73 : {
74 : return nCurrentItem;
75 : }
76 : };
77 :
78 : /* Your average metadata item */
79 : class COASPMetadataItem
80 : {
81 : protected:
82 : char *pszItemName;
83 : char *pszItemValue;
84 :
85 : public:
86 0 : COASPMetadataItem() : pszItemName(nullptr), pszItemValue(nullptr)
87 : {
88 0 : }
89 :
90 : COASPMetadataItem(char *pszItemName, char *pszItemValue);
91 : ~COASPMetadataItem();
92 :
93 : char *GetItemName();
94 : char *GetItemValue();
95 :
96 : static int GetType()
97 : {
98 : return TYPE_GENERIC;
99 : }
100 : };
101 :
102 : /* Same as MetadataItem class except parses GCP properly and returns
103 : * a GDAL_GCP struct
104 : */
105 : class COASPMetadataGeorefGridItem : public COASPMetadataItem
106 : {
107 : #ifdef unused
108 : int nId;
109 : int nPixels;
110 : int nLines;
111 : double ndLat;
112 : double ndLong;
113 : #endif
114 :
115 : public:
116 : COASPMetadataGeorefGridItem(int nId, int nPixels, int nLines, double ndLat,
117 : double ndLong);
118 :
119 : static const char *GetItemName()
120 : {
121 : return "georef_grid";
122 : }
123 :
124 : static GDAL_GCP *GetItemValue();
125 :
126 : static int GetType()
127 : {
128 : return TYPE_GEOREF;
129 : }
130 : };
131 :
132 : /********************************************************************
133 : * ================================================================ *
134 : * Implementation of the COASPMetadataItem Classes *
135 : * ================================================================ *
136 : ********************************************************************/
137 :
138 0 : COASPMetadataItem::COASPMetadataItem(char *pszItemName_, char *pszItemValue_)
139 0 : : pszItemName(VSIStrdup(pszItemName_)),
140 0 : pszItemValue(VSIStrdup(pszItemValue_))
141 : {
142 0 : }
143 :
144 0 : COASPMetadataItem::~COASPMetadataItem()
145 : {
146 0 : CPLFree(pszItemName);
147 0 : CPLFree(pszItemValue);
148 0 : }
149 :
150 0 : char *COASPMetadataItem::GetItemName()
151 : {
152 0 : return VSIStrdup(pszItemName);
153 : }
154 :
155 0 : char *COASPMetadataItem::GetItemValue()
156 : {
157 0 : return VSIStrdup(pszItemValue);
158 : }
159 :
160 0 : COASPMetadataGeorefGridItem::COASPMetadataGeorefGridItem(int /*nIdIn*/,
161 : int /*nPixelsIn*/,
162 : int /*nLinesIn*/,
163 : double /*ndLatIn*/,
164 0 : double /*ndLongIn*/)
165 : #ifdef unused
166 : : nId(nIdIn), nPixels(nPixelsIn), nLines(nLinesIn), ndLat(ndLatIn),
167 : ndLong(ndLongIn)
168 : #endif
169 : {
170 0 : pszItemName = VSIStrdup("georef_grid");
171 0 : }
172 :
173 0 : GDAL_GCP *COASPMetadataGeorefGridItem::GetItemValue()
174 : {
175 0 : return nullptr;
176 : }
177 :
178 : /********************************************************************
179 : * ================================================================ *
180 : * Implementation of the COASPMetadataReader Class *
181 : * ================================================================ *
182 : ********************************************************************/
183 :
184 0 : COASPMetadataReader::COASPMetadataReader(char *pszFname)
185 0 : : papszMetadata(CSLLoad(pszFname)), nMetadataCount(0), nCurrentItem(0)
186 : {
187 0 : nMetadataCount = CSLCount(papszMetadata);
188 0 : }
189 :
190 0 : COASPMetadataReader::~COASPMetadataReader()
191 : {
192 0 : CSLDestroy(papszMetadata);
193 0 : }
194 :
195 0 : COASPMetadataItem *COASPMetadataReader::GetNextItem()
196 : {
197 0 : if (nCurrentItem < 0 || nCurrentItem >= nMetadataCount)
198 0 : return nullptr;
199 :
200 0 : COASPMetadataItem *poMetadata = nullptr;
201 :
202 0 : char **papszMDTokens = CSLTokenizeString2(papszMetadata[nCurrentItem], " ",
203 : CSLT_HONOURSTRINGS);
204 0 : char *pszItemName = papszMDTokens[0];
205 0 : if (STARTS_WITH_CI(pszItemName, "georef_grid") &&
206 0 : CSLCount(papszMDTokens) >= 8)
207 : {
208 : // georef_grid ( pixels lines ) ( lat long )
209 : // 0 1 2 3 4 5 6 7 8
210 0 : int nPixels = atoi(papszMDTokens[2]);
211 0 : int nLines = atoi(papszMDTokens[3]);
212 0 : double dfLat = CPLAtof(papszMDTokens[6]);
213 0 : double dfLong = CPLAtof(papszMDTokens[7]);
214 0 : poMetadata = new COASPMetadataGeorefGridItem(nCurrentItem, nPixels,
215 0 : nLines, dfLat, dfLong);
216 : }
217 : else
218 : {
219 0 : int nCount = CSLCount(papszMDTokens);
220 0 : if (nCount >= 2)
221 : {
222 0 : char *pszItemValue = CPLStrdup(papszMDTokens[1]);
223 0 : for (int i = 2; i < nCount; i++)
224 : {
225 0 : const size_t nSize =
226 0 : strlen(pszItemValue) + 1 + strlen(papszMDTokens[i]);
227 0 : pszItemValue = (char *)CPLRealloc(pszItemValue, nSize);
228 0 : snprintf(pszItemValue + strlen(pszItemValue),
229 0 : nSize - strlen(pszItemValue), " %s", papszMDTokens[i]);
230 : }
231 :
232 0 : poMetadata = new COASPMetadataItem(pszItemName, pszItemValue);
233 :
234 0 : CPLFree(pszItemValue);
235 : }
236 : }
237 0 : CSLDestroy(papszMDTokens);
238 0 : nCurrentItem++;
239 0 : return poMetadata;
240 : }
241 :
242 : /* Goto the first metadata item with a particular name */
243 0 : int COASPMetadataReader::GotoMetadataItem(const char *pszName)
244 : {
245 0 : nCurrentItem = CSLPartialFindString(papszMetadata, pszName);
246 0 : return nCurrentItem;
247 : }
248 :
249 : /*******************************************************************
250 : * Declaration of the COASPDataset class *
251 : *******************************************************************/
252 :
253 : class COASPRasterBand;
254 :
255 : /* A couple of observations based on the data I have available to me:
256 : * a) the headers don't really change, beyond indicating data sources
257 : * and such. As such, I only read the first header specified by the
258 : * user. Note that this is agnostic: you can specify hh, vv, vh, hv and
259 : * all the data needed will be immediately available.
260 : * b) Lots of GCPs are present in the headers. This is most excellent.
261 : * c) There is no documentation on this format. All the knowledge contained
262 : * herein is from harassing various Defence Scientists at DRDC Ottawa.
263 : */
264 :
265 : class COASPDataset final : public GDALDataset
266 : {
267 : friend class COASPRasterBand;
268 : VSILFILE *fpHdr; /* File pointer for the header file */
269 : VSILFILE *fpBinHH; /* File pointer for the binary matrix */
270 : VSILFILE *fpBinHV;
271 : VSILFILE *fpBinVH;
272 : VSILFILE *fpBinVV;
273 :
274 : char *pszFileName; /* line and mission ID, mostly, i.e. l27p7 */
275 :
276 : public:
277 0 : COASPDataset()
278 0 : : fpHdr(nullptr), fpBinHH(nullptr), fpBinHV(nullptr), fpBinVH(nullptr),
279 0 : fpBinVV(nullptr), pszFileName(nullptr)
280 : {
281 0 : }
282 :
283 : ~COASPDataset();
284 :
285 : static GDALDataset *Open(GDALOpenInfo *);
286 : static int Identify(GDALOpenInfo *poOpenInfo);
287 : };
288 :
289 : /********************************************************************
290 : * ================================================================ *
291 : * Declaration and implementation of the COASPRasterBand Class *
292 : * ================================================================ *
293 : ********************************************************************/
294 :
295 : class COASPRasterBand final : public GDALRasterBand
296 : {
297 : VSILFILE *fp;
298 : // int ePol;
299 : public:
300 : COASPRasterBand(COASPDataset *poDS, GDALDataType eDataType, int ePol,
301 : VSILFILE *fp);
302 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
303 : };
304 :
305 0 : COASPRasterBand::COASPRasterBand(COASPDataset *poDSIn, GDALDataType eDataTypeIn,
306 0 : int /*ePolIn*/, VSILFILE *fpIn)
307 0 : : fp(fpIn) /*,
308 : ePol(ePolIn)*/
309 : {
310 0 : poDS = poDSIn;
311 0 : eDataType = eDataTypeIn;
312 0 : nBlockXSize = poDS->GetRasterXSize();
313 0 : nBlockYSize = 1;
314 0 : }
315 :
316 0 : CPLErr COASPRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
317 : void *pImage)
318 : {
319 0 : if (this->fp == nullptr)
320 : {
321 0 : CPLError(CE_Fatal, CPLE_AppDefined, "File pointer freed unexpectedly");
322 0 : return CE_Fatal;
323 : }
324 :
325 : /* 8 bytes per pixel: 4 bytes I, 4 bytes Q */
326 : const vsi_l_offset nByteNum =
327 0 : static_cast<vsi_l_offset>(poDS->GetRasterXSize()) * 8 * nBlockYOff;
328 :
329 0 : VSIFSeekL(this->fp, nByteNum, SEEK_SET);
330 : int nReadSize =
331 0 : (GDALGetDataTypeSize(eDataType) / 8) * poDS->GetRasterXSize();
332 0 : VSIFReadL((char *)pImage, 1, nReadSize, this->fp);
333 :
334 : #ifdef CPL_LSB
335 0 : GDALSwapWords(pImage, 4, nBlockXSize * 2, 4);
336 : #endif
337 0 : return CE_None;
338 : }
339 :
340 : /********************************************************************
341 : * ================================================================ *
342 : * Implementation of the COASPDataset Class *
343 : * ================================================================ *
344 : ********************************************************************/
345 :
346 : /************************************************************************/
347 : /* ~COASPDataset() */
348 : /************************************************************************/
349 :
350 0 : COASPDataset::~COASPDataset()
351 : {
352 0 : CPLFree(pszFileName);
353 0 : if (fpHdr)
354 0 : VSIFCloseL(fpHdr);
355 0 : if (fpBinHH)
356 0 : VSIFCloseL(fpBinHH);
357 0 : if (fpBinHV)
358 0 : VSIFCloseL(fpBinHV);
359 0 : if (fpBinVH)
360 0 : VSIFCloseL(fpBinVH);
361 0 : if (fpBinVV)
362 0 : VSIFCloseL(fpBinVV);
363 0 : }
364 :
365 : /************************************************************************/
366 : /* Identify() */
367 : /************************************************************************/
368 :
369 50077 : int COASPDataset::Identify(GDALOpenInfo *poOpenInfo)
370 : {
371 50077 : if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 256)
372 46535 : return 0;
373 :
374 : // With a COASP .hdr file, the first line or so is: time_first_datarec
375 :
376 3542 : if (STARTS_WITH_CI((char *)poOpenInfo->pabyHeader, "time_first_datarec"))
377 0 : return 1;
378 :
379 3542 : return 0;
380 : }
381 :
382 : /************************************************************************/
383 : /* Open() */
384 : /************************************************************************/
385 :
386 0 : GDALDataset *COASPDataset::Open(GDALOpenInfo *poOpenInfo)
387 : {
388 0 : if (!COASPDataset::Identify(poOpenInfo))
389 0 : return nullptr;
390 :
391 : /* -------------------------------------------------------------------- */
392 : /* Confirm the requested access is supported. */
393 : /* -------------------------------------------------------------------- */
394 0 : if (poOpenInfo->eAccess == GA_Update)
395 : {
396 0 : CPLError(CE_Failure, CPLE_NotSupported,
397 : "The COASP driver does not support update access to existing"
398 : " datasets.\n");
399 0 : return nullptr;
400 : }
401 :
402 : /* Create a fresh dataset for us to work with */
403 0 : COASPDataset *poDS = new COASPDataset();
404 :
405 : /* Steal the file pointer for the header */
406 0 : poDS->fpHdr = poOpenInfo->fpL;
407 0 : poOpenInfo->fpL = nullptr;
408 :
409 0 : poDS->pszFileName = VSIStrdup(poOpenInfo->pszFilename);
410 :
411 : /* determine the file name prefix */
412 0 : char *pszBaseName = VSIStrdup(CPLGetBasename(poDS->pszFileName));
413 0 : char *pszDir = VSIStrdup(CPLGetPath(poDS->pszFileName));
414 0 : const char *pszExt = "rc";
415 0 : int nNull = static_cast<int>(strlen(pszBaseName)) - 1;
416 0 : if (nNull <= 0)
417 : {
418 0 : VSIFree(pszDir);
419 0 : VSIFree(pszBaseName);
420 0 : delete poDS;
421 0 : return nullptr;
422 : }
423 0 : char *pszBase = (char *)CPLMalloc(nNull);
424 0 : strncpy(pszBase, pszBaseName, nNull);
425 0 : pszBase[nNull - 1] = '\0';
426 0 : VSIFree(pszBaseName);
427 :
428 0 : char *psChan = strstr(pszBase, "hh");
429 0 : if (psChan == nullptr)
430 : {
431 0 : psChan = strstr(pszBase, "hv");
432 : }
433 0 : if (psChan == nullptr)
434 : {
435 0 : psChan = strstr(pszBase, "vh");
436 : }
437 0 : if (psChan == nullptr)
438 : {
439 0 : psChan = strstr(pszBase, "vv");
440 : }
441 :
442 0 : if (psChan == nullptr)
443 : {
444 0 : CPLError(CE_Failure, CPLE_AppDefined,
445 : "Unable to recognize file as COASP.");
446 0 : VSIFree(pszBase);
447 0 : VSIFree(pszDir);
448 0 : delete poDS;
449 0 : return nullptr;
450 : }
451 :
452 : /* Read Metadata, set GCPs as is appropriate */
453 0 : COASPMetadataReader oReader(poDS->pszFileName);
454 :
455 : /* Get Image X and Y widths */
456 0 : oReader.GotoMetadataItem("number_lines");
457 0 : COASPMetadataItem *poItem = oReader.GetNextItem();
458 0 : if (poItem == nullptr)
459 : {
460 0 : VSIFree(pszBase);
461 0 : VSIFree(pszDir);
462 0 : delete poDS;
463 0 : return nullptr;
464 : }
465 0 : char *nValue = poItem->GetItemValue();
466 0 : poDS->nRasterYSize = atoi(nValue);
467 0 : delete poItem;
468 0 : VSIFree(nValue);
469 :
470 0 : oReader.GotoMetadataItem("number_samples");
471 0 : poItem = oReader.GetNextItem();
472 0 : if (poItem == nullptr)
473 : {
474 0 : VSIFree(pszBase);
475 0 : VSIFree(pszDir);
476 0 : delete poDS;
477 0 : return nullptr;
478 : }
479 0 : nValue = poItem->GetItemValue();
480 0 : poDS->nRasterXSize = atoi(nValue);
481 0 : delete poItem;
482 0 : VSIFree(nValue);
483 :
484 0 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
485 : {
486 0 : VSIFree(pszBase);
487 0 : VSIFree(pszDir);
488 0 : delete poDS;
489 0 : return nullptr;
490 : }
491 :
492 : /* Horizontal transmit, horizontal receive */
493 0 : psChan[0] = 'h';
494 0 : psChan[1] = 'h';
495 0 : const char *pszFilename = CPLFormFilename(pszDir, pszBase, pszExt);
496 :
497 0 : poDS->fpBinHH = VSIFOpenL(pszFilename, "r");
498 :
499 0 : if (poDS->fpBinHH != nullptr)
500 : {
501 : /* Set raster band */
502 0 : poDS->SetBand(
503 0 : 1, new COASPRasterBand(poDS, GDT_CFloat32, hh, poDS->fpBinHH));
504 : }
505 :
506 : /* Horizontal transmit, vertical receive */
507 0 : psChan[0] = 'h';
508 0 : psChan[1] = 'v';
509 0 : pszFilename = CPLFormFilename(pszDir, pszBase, pszExt);
510 :
511 0 : poDS->fpBinHV = VSIFOpenL(pszFilename, "r");
512 :
513 0 : if (poDS->fpBinHV != nullptr)
514 : {
515 0 : poDS->SetBand(
516 0 : 2, new COASPRasterBand(poDS, GDT_CFloat32, hv, poDS->fpBinHV));
517 : }
518 :
519 : /* Vertical transmit, horizontal receive */
520 0 : psChan[0] = 'v';
521 0 : psChan[1] = 'h';
522 0 : pszFilename = CPLFormFilename(pszDir, pszBase, pszExt);
523 :
524 0 : poDS->fpBinVH = VSIFOpenL(pszFilename, "r");
525 :
526 0 : if (poDS->fpBinVH != nullptr)
527 : {
528 0 : poDS->SetBand(
529 0 : 3, new COASPRasterBand(poDS, GDT_CFloat32, vh, poDS->fpBinVH));
530 : }
531 :
532 : /* Vertical transmit, vertical receive */
533 0 : psChan[0] = 'v';
534 0 : psChan[1] = 'v';
535 0 : pszFilename = CPLFormFilename(pszDir, pszBase, pszExt);
536 :
537 0 : poDS->fpBinVV = VSIFOpenL(pszFilename, "r");
538 :
539 0 : if (poDS->fpBinVV != nullptr)
540 : {
541 0 : poDS->SetBand(
542 0 : 4, new COASPRasterBand(poDS, GDT_CFloat32, vv, poDS->fpBinVV));
543 : }
544 :
545 : /* Oops, missing all the data? */
546 0 : if (poDS->fpBinHH == nullptr && poDS->fpBinHV == nullptr &&
547 0 : poDS->fpBinVH == nullptr && poDS->fpBinVV == nullptr)
548 : {
549 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unable to find any data!");
550 0 : VSIFree(pszBase);
551 0 : VSIFree(pszDir);
552 0 : delete poDS;
553 0 : return nullptr;
554 : }
555 :
556 0 : if (poDS->GetRasterCount() == 4)
557 : {
558 0 : poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
559 : }
560 :
561 0 : VSIFree(pszBase);
562 0 : VSIFree(pszDir);
563 :
564 0 : return poDS;
565 : }
566 :
567 : /************************************************************************/
568 : /* GDALRegister_COASP() */
569 : /************************************************************************/
570 :
571 1512 : void GDALRegister_COASP()
572 : {
573 1512 : if (GDALGetDriverByName("COASP") != nullptr)
574 295 : return;
575 :
576 1217 : GDALDriver *poDriver = new GDALDriver();
577 :
578 1217 : poDriver->SetDescription("COASP");
579 1217 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
580 1217 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
581 1217 : "DRDC COASP SAR Processor Raster");
582 1217 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "hdr");
583 1217 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/coasp.html");
584 1217 : poDriver->pfnIdentify = COASPDataset::Identify;
585 1217 : poDriver->pfnOpen = COASPDataset::Open;
586 1217 : GetGDALDriverManager()->RegisterDriver(poDriver);
587 : }
|