1 /******************************************************************************
2  *
3  * Project:  Azavea Raster Grid format driver.
4  * Purpose:  Implements support for reading and writing Azavea Raster Grid
5  *           format.
6  * Author:   David Zwarg <dzwarg@azavea.com>
7  *
8  ******************************************************************************
9  * Copyright (c) 2012, David Zwarg <dzwarg@azavea.com>
10  * Copyright (c) 2012-2013, Even Rouault <even dot rouault at spatialys.com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "cpl_string.h"
32 #include "gdal_frmts.h"
33 #include "ogr_spatialref.h"
34 #include "rawdataset.h"
35 
36 #include "ogrgeojsonreader.h"
37 #include <limits>
38 
39 CPL_CVSID("$Id: argdataset.cpp f6099e5ed704166bf5cc113a053dd1b2725cb391 2020-03-22 11:20:10 +0100 Kai Pastor $")
40 
41 /************************************************************************/
42 /* ==================================================================== */
43 /*                              ARGDataset                              */
44 /* ==================================================================== */
45 /************************************************************************/
46 
47 class ARGDataset final: public RawDataset
48 {
49     VSILFILE *fpImage;  // image data file.
50     double adfGeoTransform[6];
51     char *pszFilename;
52 
53   public:
54     ARGDataset();
55     ~ARGDataset() override;
56 
57     CPLErr GetGeoTransform( double * padfTransform ) override;
58 
59     static int Identify( GDALOpenInfo * );
60     static GDALDataset *Open( GDALOpenInfo * );
61     static GDALDataset *CreateCopy( const char *, GDALDataset *, int,
62                                     char **, GDALProgressFunc, void *);
63     char **GetFileList(void) override;
64 };
65 
66 /************************************************************************/
67 /*                            ARGDataset()                              */
68 /************************************************************************/
69 
ARGDataset()70 ARGDataset::ARGDataset() :
71     fpImage(nullptr),
72     pszFilename(nullptr)
73 {
74     adfGeoTransform[0] = 0.0;
75     adfGeoTransform[1] = 1.0;
76     adfGeoTransform[2] = 0.0;
77     adfGeoTransform[3] = 0.0;
78     adfGeoTransform[4] = 0.0;
79     adfGeoTransform[5] = 1.0;
80 }
81 
82 /************************************************************************/
83 /*                            ~ARGDataset()                             */
84 /************************************************************************/
85 
~ARGDataset()86 ARGDataset::~ARGDataset()
87 
88 {
89     CPLFree(pszFilename);
90 
91     FlushCache();
92     if( fpImage != nullptr )
93         VSIFCloseL( fpImage );
94 }
95 
96 /************************************************************************/
97 /*                          GetGeoTransform()                           */
98 /************************************************************************/
99 
GetGeoTransform(double * padfTransform)100 CPLErr ARGDataset::GetGeoTransform( double * padfTransform )
101 
102 {
103     memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
104 
105     return CE_None;
106 }
107 
108 /************************************************************************/
109 /*                         GetJsonFilename()                            */
110 /************************************************************************/
GetJsonFilename(CPLString pszFilename)111 static CPLString GetJsonFilename(CPLString pszFilename)
112 {
113     return CPLSPrintf( "%s/%s.json", CPLGetDirname(pszFilename),
114                        CPLGetBasename(pszFilename) );
115 }
116 
117 /************************************************************************/
118 /*                           GetJsonObject()                            */
119 /************************************************************************/
GetJsonObject(CPLString pszFilename)120 static json_object * GetJsonObject(CPLString pszFilename)
121 {
122     CPLString osJSONFilename = GetJsonFilename(pszFilename);
123 
124     json_object *pJSONObject =
125         json_object_from_file(const_cast<char *>(osJSONFilename.c_str()));
126     if (pJSONObject == nullptr) {
127         CPLDebug("ARGDataset", "GetJsonObject(): Could not parse JSON file.");
128         return nullptr;
129     }
130 
131     return pJSONObject;
132 }
133 
134 /************************************************************************/
135 /*                          GetJsonValueStr()                           */
136 /************************************************************************/
GetJsonValueStr(json_object * pJSONObject,CPLString pszKey)137 static const char *GetJsonValueStr( json_object * pJSONObject,
138                                     CPLString pszKey )
139 {
140     json_object *pJSONItem =
141         CPL_json_object_object_get(pJSONObject, pszKey.c_str());
142     if( pJSONItem == nullptr )
143     {
144         CPLDebug("ARGDataset", "GetJsonValueStr(): "
145                  "Could not find '%s' in JSON.", pszKey.c_str());
146         return nullptr;
147     }
148 
149     return json_object_get_string(pJSONItem);
150 }
151 
152 /************************************************************************/
153 /*                          GetJsonValueDbl()                           */
154 /************************************************************************/
GetJsonValueDbl(json_object * pJSONObject,CPLString pszKey)155 static double GetJsonValueDbl( json_object * pJSONObject, CPLString pszKey )
156 {
157     const char *pszJSONStr = GetJsonValueStr(pJSONObject, pszKey.c_str());
158     if (pszJSONStr == nullptr) {
159         return std::numeric_limits<double>::quiet_NaN();
160     }
161     char *pszTmp = const_cast<char *>(pszJSONStr);
162     double dfTmp = CPLStrtod(pszJSONStr, &pszTmp);
163     if (pszTmp == pszJSONStr) {
164         CPLDebug("ARGDataset", "GetJsonValueDbl(): "
165                  "Key value is not a numeric value: %s:%s",
166                  pszKey.c_str(), pszTmp);
167         return std::numeric_limits<double>::quiet_NaN();
168     }
169 
170     return dfTmp;
171 }
172 
173 /************************************************************************/
174 /*                           GetJsonValueInt()                          */
175 /************************************************************************/
GetJsonValueInt(json_object * pJSONObject,CPLString pszKey)176 static int GetJsonValueInt( json_object *pJSONObject, CPLString pszKey )
177 {
178     const double dfTmp = GetJsonValueDbl(pJSONObject, pszKey.c_str());
179     if (CPLIsNan(dfTmp)) {
180         return -1;
181     }
182 
183     return static_cast<int>(dfTmp);
184 }
185 
186 /************************************************************************/
187 /*                            GetFileList()                             */
188 /************************************************************************/
GetFileList()189 char **ARGDataset::GetFileList()
190 {
191     char **papszFileList = GDALPamDataset::GetFileList();
192     CPLString osJSONFilename = GetJsonFilename(pszFilename);
193 
194     papszFileList = CSLAddString( papszFileList, osJSONFilename );
195 
196     return papszFileList;
197 }
198 
199 /************************************************************************/
200 /*                              Identify()                              */
201 /************************************************************************/
202 
Identify(GDALOpenInfo * poOpenInfo)203 int ARGDataset::Identify( GDALOpenInfo *poOpenInfo )
204 {
205 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
206     if (!EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "arg")) {
207         return FALSE;
208     }
209 #endif
210 
211     json_object *pJSONObject = GetJsonObject(poOpenInfo->pszFilename);
212     if (pJSONObject == nullptr) {
213         return FALSE;
214     }
215 
216     json_object_put(pJSONObject);
217     pJSONObject = nullptr;
218 
219     return TRUE;
220 }
221 
222 /************************************************************************/
223 /*                                Open()                                */
224 /************************************************************************/
Open(GDALOpenInfo * poOpenInfo)225 GDALDataset *ARGDataset::Open( GDALOpenInfo *poOpenInfo )
226 {
227     if ( !Identify( poOpenInfo ) || poOpenInfo->fpL == nullptr )
228         return nullptr;
229 
230 /* -------------------------------------------------------------------- */
231 /*      Confirm the requested access is supported.                      */
232 /* -------------------------------------------------------------------- */
233     if( poOpenInfo->eAccess == GA_Update )
234     {
235         CPLError( CE_Failure, CPLE_NotSupported,
236                   "The ARG driver does not support update access to existing"
237                   " datasets." );
238         return nullptr;
239     }
240 /* -------------------------------------------------------------------- */
241 /*      Check metadata settings in JSON.                                */
242 /* -------------------------------------------------------------------- */
243 
244     json_object *pJSONObject = GetJsonObject(poOpenInfo->pszFilename);
245 
246     if (pJSONObject == nullptr) {
247         CPLError(CE_Failure, CPLE_AppDefined, "Error parsing JSON.");
248         return nullptr;
249     }
250 
251     // get the type (always 'arg')
252     const char *pszJSONStr = GetJsonValueStr(pJSONObject, "type");
253     if (pszJSONStr == nullptr ) {
254         CPLError(CE_Failure, CPLE_AppDefined,
255             "The ARG 'type' is missing from the JSON file.");
256         json_object_put(pJSONObject);
257         pJSONObject = nullptr;
258         return nullptr;
259     }
260     else if (!EQUAL(pszJSONStr, "arg")) {
261         CPLError(CE_Failure, CPLE_AppDefined,
262             "The ARG 'type' is not recognized: '%s'.", pszJSONStr);
263         json_object_put(pJSONObject);
264         pJSONObject = nullptr;
265         return nullptr;
266     }
267 
268     double dfNoDataValue;
269     GDALDataType eType;
270     int nPixelOffset;
271 
272     // get the datatype
273     pszJSONStr = GetJsonValueStr(pJSONObject, "datatype");
274     if (pszJSONStr == nullptr) {
275         CPLError(CE_Failure, CPLE_AppDefined,
276             "The ARG 'datatype' is missing from the JSON file.");
277         json_object_put(pJSONObject);
278         pJSONObject = nullptr;
279         return nullptr;
280     }
281     else if (EQUAL(pszJSONStr, "int8")) {
282         CPLDebug("ARGDataset", "Open(): "
283             "int8 data is not supported in GDAL -- mapped to uint8");
284         eType = GDT_Byte;
285         nPixelOffset = 1;
286         dfNoDataValue = 128;
287     }
288     else if (EQUAL(pszJSONStr, "int16")) {
289         eType = GDT_Int16;
290         nPixelOffset = 2;
291         dfNoDataValue = -32767;
292     }
293     else if (EQUAL(pszJSONStr, "int32")) {
294         eType = GDT_Int32;
295         nPixelOffset = 4;
296         dfNoDataValue = -2e31;
297     }
298     else if (EQUAL(pszJSONStr, "uint8")) {
299         eType = GDT_Byte;
300         nPixelOffset = 1;
301         dfNoDataValue = 255;
302     }
303     else if (EQUAL(pszJSONStr, "uint16")) {
304         eType = GDT_UInt16;
305         nPixelOffset = 2;
306         dfNoDataValue = 65535;
307     }
308     else if (EQUAL(pszJSONStr, "uint32")) {
309         eType = GDT_UInt32;
310         nPixelOffset = 4;
311         dfNoDataValue = -2e31;
312     }
313     else if (EQUAL(pszJSONStr, "float32")) {
314         eType = GDT_Float32;
315         nPixelOffset = 4;
316         dfNoDataValue = std::numeric_limits<double>::quiet_NaN();
317     }
318     else if (EQUAL(pszJSONStr, "float64")) {
319         eType = GDT_Float64;
320         nPixelOffset = 8;
321         dfNoDataValue = std::numeric_limits<double>::quiet_NaN();
322     }
323     else {
324         if (EQUAL(pszJSONStr, "int64") ||
325             EQUAL(pszJSONStr, "uint64")) {
326             CPLError(CE_Failure, CPLE_AppDefined,
327                 "The ARG 'datatype' is unsupported in GDAL: '%s'.", pszJSONStr);
328         }
329         else {
330             CPLError(CE_Failure, CPLE_AppDefined,
331                 "The ARG 'datatype' is unknown: '%s'.", pszJSONStr);
332         }
333         json_object_put(pJSONObject);
334         pJSONObject = nullptr;
335         return nullptr;
336     }
337 
338     // get the xmin of the bounding box
339     const double dfXmin = GetJsonValueDbl(pJSONObject, "xmin");
340     if (CPLIsNan(dfXmin)) {
341         CPLError(CE_Failure, CPLE_AppDefined,
342             "The ARG 'xmin' is missing or invalid.");
343         json_object_put(pJSONObject);
344         pJSONObject = nullptr;
345         return nullptr;
346     }
347 
348     // get the ymin of the bounding box
349     const double dfYmin = GetJsonValueDbl(pJSONObject, "ymin");
350     if (CPLIsNan(dfYmin)) {
351         CPLError(CE_Failure, CPLE_AppDefined,
352             "The ARG 'ymin' is missing or invalid.");
353         json_object_put(pJSONObject);
354         pJSONObject = nullptr;
355         return nullptr;
356     }
357 
358     // get the xmax of the bounding boxfpL
359     const double dfXmax = GetJsonValueDbl(pJSONObject, "xmax");
360     if (CPLIsNan(dfXmax)) {
361         CPLError(CE_Failure, CPLE_AppDefined,
362             "The ARG 'xmax' is missing or invalid.");
363         json_object_put(pJSONObject);
364         pJSONObject = nullptr;
365         return nullptr;
366     }
367 
368     // get the ymax of the bounding box
369     const double dfYmax = GetJsonValueDbl(pJSONObject, "ymax");
370     if (CPLIsNan(dfYmax)) {
371         CPLError(CE_Failure, CPLE_AppDefined,
372             "The ARG 'ymax' is missing or invalid.");
373         json_object_put(pJSONObject);
374         pJSONObject = nullptr;
375         return nullptr;
376     }
377 
378     // get the cell width
379     const double dfCellwidth = GetJsonValueDbl(pJSONObject, "cellwidth");
380     if (CPLIsNan(dfCellwidth)) {
381         CPLError(CE_Failure, CPLE_AppDefined,
382             "The ARG 'cellwidth' is missing or invalid.");
383         json_object_put(pJSONObject);
384         pJSONObject = nullptr;
385         return nullptr;
386     }
387 
388     // get the cell height
389     const double dfCellheight = GetJsonValueDbl(pJSONObject, "cellheight");
390     if (CPLIsNan(dfCellheight)) {
391         CPLError(CE_Failure, CPLE_AppDefined,
392             "The ARG 'cellheight' is missing or invalid.");
393         json_object_put(pJSONObject);
394         pJSONObject = nullptr;
395         return nullptr;
396     }
397 
398     double dfXSkew = GetJsonValueDbl(pJSONObject, "xskew");
399     if (CPLIsNan(dfXSkew)) {
400         // not an error -- default to 0.0
401         dfXSkew = 0.0f;
402     }
403 
404     double dfYSkew = GetJsonValueDbl(pJSONObject, "yskew");
405     if (CPLIsNan(dfYSkew)) {
406         // not an error -- default to 0.0
407         dfYSkew = 0.0f;
408     }
409 
410     // get the rows
411     const int nRows = GetJsonValueInt(pJSONObject, "rows");
412     if (nRows < 0) {
413         CPLError(CE_Failure, CPLE_AppDefined,
414             "The ARG 'rows' is missing or invalid.");
415         json_object_put(pJSONObject);
416         pJSONObject = nullptr;
417         return nullptr;
418     }
419 
420     // get the columns
421     const int nCols = GetJsonValueInt(pJSONObject, "cols");
422     if (nCols < 0) {
423         CPLError(CE_Failure, CPLE_AppDefined,
424             "The ARG 'cols' is missing or invalid.");
425         json_object_put(pJSONObject);
426         pJSONObject = nullptr;
427         return nullptr;
428     }
429 
430     int nSrs = GetJsonValueInt(pJSONObject, "epsg");
431     if (nSrs < 0) {
432         // not an error -- default to web mercator
433         nSrs = 3857;
434     }
435 
436     OGRSpatialReference oSRS;
437     OGRErr nErr = oSRS.importFromEPSG(nSrs);
438     if (nErr != OGRERR_NONE) {
439         nErr = oSRS.importFromEPSG(3857);
440 
441         if (nErr == OGRERR_NONE) {
442             CPLDebug("ARGDataset", "Open(): "
443                      "The EPSG provided did not import cleanly. "
444                      "Defaulting to EPSG:3857");
445         }
446         else {
447             CPLError( CE_Failure, CPLE_AppDefined,
448                       "The 'epsg' value did not translate to a known "
449                       "spatial reference. "
450                       "Please check the 'epsg' value and try again.");
451 
452             json_object_put(pJSONObject);
453             pJSONObject = nullptr;
454 
455             return nullptr;
456         }
457     }
458 
459     char *pszWKT = nullptr;
460     nErr = oSRS.exportToWkt(&pszWKT);
461     if (nErr != OGRERR_NONE) {
462         CPLError(CE_Failure, CPLE_AppDefined,
463             "The spatial reference is known, but could not be set on the "
464             "dataset. Please check the 'epsg' value and try again.");
465 
466         json_object_put(pJSONObject);
467         pJSONObject = nullptr;
468         CPLFree(pszWKT);
469         return nullptr;
470     }
471 
472     // get the layer (always the file basename)
473     pszJSONStr = GetJsonValueStr(pJSONObject, "layer");
474     if (pszJSONStr == nullptr) {
475         CPLError(CE_Failure, CPLE_AppDefined,
476             "The ARG 'layer' is missing from the JSON file.");
477         json_object_put(pJSONObject);
478         pJSONObject = nullptr;
479         CPLFree(pszWKT);
480         return nullptr;
481     }
482 
483     char *pszLayer = CPLStrdup(pszJSONStr);
484 
485     // done with the json object now
486     json_object_put(pJSONObject);
487     pJSONObject = nullptr;
488 
489 /* -------------------------------------------------------------------- */
490 /*      Create a corresponding GDALDataset.                             */
491 /* -------------------------------------------------------------------- */
492     ARGDataset *poDS = new ARGDataset();
493 
494     poDS->pszFilename = CPLStrdup(poOpenInfo->pszFilename);
495     poDS->SetMetadataItem("LAYER",pszLayer,nullptr);
496     poDS->nRasterXSize = nCols;
497     poDS->nRasterYSize = nRows;
498     poDS->SetProjection( pszWKT );
499 
500     // done with the projection string
501     CPLFree(pszWKT);
502     CPLFree(pszLayer);
503 
504 /* -------------------------------------------------------------------- */
505 /*      Assume ownership of the file handled from the GDALOpenInfo.     */
506 /* -------------------------------------------------------------------- */
507     poDS->fpImage = poOpenInfo->fpL;
508     poOpenInfo->fpL = nullptr;
509 
510     poDS->adfGeoTransform[0] = dfXmin;
511     poDS->adfGeoTransform[1] = dfCellwidth;
512     poDS->adfGeoTransform[2] = dfXSkew;
513     poDS->adfGeoTransform[3] = dfYmax;
514     poDS->adfGeoTransform[4] = dfYSkew;
515     poDS->adfGeoTransform[5] = -dfCellheight;
516 
517 /* -------------------------------------------------------------------- */
518 /*      Create band information objects.                                */
519 /* -------------------------------------------------------------------- */
520 #ifdef CPL_LSB
521     bool bNative = false;
522 #else
523     bool bNative = true;
524 #endif
525 
526     RawRasterBand *poBand
527         = new RawRasterBand( poDS, 1, poDS->fpImage,
528                              0, nPixelOffset, nPixelOffset * nCols,
529                              eType, bNative, RawRasterBand::OwnFP::NO );
530     poDS->SetBand( 1, poBand );
531 
532     poBand->SetNoDataValue( dfNoDataValue );
533 
534 /* -------------------------------------------------------------------- */
535 /*      Initialize any PAM information.                                 */
536 /* -------------------------------------------------------------------- */
537     poDS->SetDescription( poOpenInfo->pszFilename );
538     poDS->TryLoadXML();
539 
540 /* -------------------------------------------------------------------- */
541 /*      Check for overviews.                                            */
542 /* -------------------------------------------------------------------- */
543     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
544 
545     return poDS;
546 }
547 
548 /************************************************************************/
549 /*                          CreateCopy()                                */
550 /************************************************************************/
CreateCopy(const char * pszFilename,GDALDataset * poSrcDS,int,char **,GDALProgressFunc,void *)551 GDALDataset *ARGDataset::CreateCopy( const char *pszFilename,
552                                      GDALDataset *poSrcDS,
553                                      int /* bStrict */ ,
554                                      char ** /* papszOptions */ ,
555                                      GDALProgressFunc /* pfnProgress */ ,
556                                      void * /*pProgressData */ )
557 {
558     const int nBands = poSrcDS->GetRasterCount();
559     if( nBands != 1 )
560     {
561         CPLError( CE_Failure, CPLE_NotSupported,
562               "ARG driver doesn't support %d bands.  Must be 1 band.", nBands );
563         return nullptr;
564     }
565 
566     CPLString pszDataType;
567     int nPixelOffset = 0;
568 
569     GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
570     if( eType == GDT_Unknown ||
571         eType == GDT_CInt16 ||
572         eType == GDT_CInt32 ||
573         eType == GDT_CFloat32 ||
574         eType == GDT_CFloat64 )
575     {
576         CPLError( CE_Failure, CPLE_NotSupported,
577                   "ARG driver doesn't support data type %s.",
578                   GDALGetDataTypeName(eType) );
579         return nullptr;
580     }
581     else if (eType == GDT_Int16) {
582         pszDataType = "int16";
583         nPixelOffset = 2;
584     }
585     else if (eType == GDT_Int32) {
586         pszDataType = "int32";
587         nPixelOffset = 4;
588     }
589     else if (eType == GDT_Byte) {
590         pszDataType = "uint8";
591         nPixelOffset = 1;
592     }
593     else if (eType == GDT_UInt16) {
594         pszDataType = "uint16";
595         nPixelOffset = 2;
596     }
597     else if (eType == GDT_UInt32) {
598         pszDataType = "uint32";
599         nPixelOffset = 4;
600     }
601     else if (eType == GDT_Float32) {
602         pszDataType = "float32";
603         nPixelOffset = 4;
604     }
605     else if (eType == GDT_Float64) {
606         pszDataType = "float64";
607         nPixelOffset = 8;
608     }
609 
610     double adfTransform[6];
611     poSrcDS->GetGeoTransform( adfTransform );
612 
613     const char *pszWKT = poSrcDS->GetProjectionRef();
614     OGRSpatialReference oSRS;
615     OGRErr nErr = oSRS.importFromWkt(pszWKT);
616     if (nErr != OGRERR_NONE) {
617         CPLError( CE_Failure, CPLE_NotSupported,
618               "Cannot import spatial reference WKT from source dataset.");
619         return nullptr;
620     }
621 
622     int nSrs = 0;
623     if (oSRS.GetAuthorityCode("PROJCS") != nullptr) {
624         nSrs = atoi(oSRS.GetAuthorityCode("PROJCS"));
625     }
626     else if (oSRS.GetAuthorityCode("GEOGCS") != nullptr) {
627         nSrs = atoi(oSRS.GetAuthorityCode("GEOGCS"));
628     }
629     else {
630         // could not determine projected or geographic code
631         // default to EPSG:3857 if no code could be found
632         nSrs = 3857;
633     }
634 
635     /********************************************************************/
636     /* Create JSON companion file.                                      */
637     /********************************************************************/
638     const CPLString osJSONFilename = GetJsonFilename(pszFilename);
639 
640     json_object *poJSONObject = json_object_new_object();
641 
642     char **pszTokens = poSrcDS->GetMetadata();
643     const char *pszLayer = CSLFetchNameValue(pszTokens, "LAYER");
644 
645     if ( pszLayer == nullptr) {
646         // Set the layer
647         json_object_object_add(poJSONObject, "layer", json_object_new_string(
648             CPLGetBasename(osJSONFilename)
649         ));
650     }
651     else {
652         // Set the layer
653         json_object_object_add(poJSONObject, "layer", json_object_new_string(
654             pszLayer
655         ));
656     }
657 
658     // Set the type
659     json_object_object_add(poJSONObject, "type", json_object_new_string("arg"));
660     // Set the datatype
661     json_object_object_add(poJSONObject, "datatype", json_object_new_string(pszDataType));
662 
663     const int nXSize = poSrcDS->GetRasterXSize();
664     const int nYSize = poSrcDS->GetRasterYSize();
665 
666     // Set the number of rows
667     json_object_object_add(poJSONObject, "rows", json_object_new_int(nYSize));
668     // Set the number of columns
669     json_object_object_add(poJSONObject, "cols", json_object_new_int(nXSize));
670     // Set the xmin
671     json_object_object_add(poJSONObject, "xmin", json_object_new_double(adfTransform[0]));
672     // Set the ymax
673     json_object_object_add(poJSONObject, "ymax", json_object_new_double(adfTransform[3]));
674     // Set the cellwidth
675     json_object_object_add(poJSONObject, "cellwidth", json_object_new_double(adfTransform[1]));
676     // Set the cellheight
677     json_object_object_add(poJSONObject, "cellheight", json_object_new_double(-adfTransform[5]));
678     // Set the xmax
679     json_object_object_add(poJSONObject, "xmax", json_object_new_double(adfTransform[0] + nXSize * adfTransform[1]));
680     // Set the ymin
681     json_object_object_add(poJSONObject, "ymin", json_object_new_double(adfTransform[3] + nYSize * adfTransform[5]));
682     // Set the xskew
683     json_object_object_add(poJSONObject, "xskew", json_object_new_double(adfTransform[2]));
684     // Set the yskew
685     json_object_object_add(poJSONObject, "yskew", json_object_new_double(adfTransform[4]));
686     if (nSrs > 0) {
687         // Set the epsg
688         json_object_object_add(poJSONObject, "epsg", json_object_new_int(nSrs));
689     }
690 
691     if (json_object_to_file(const_cast<char *>(osJSONFilename.c_str()), poJSONObject) < 0) {
692         CPLError( CE_Failure, CPLE_NotSupported,
693                   "ARG driver can't write companion file.");
694 
695         json_object_put(poJSONObject);
696         poJSONObject = nullptr;
697 
698         return nullptr;
699     }
700 
701     json_object_put(poJSONObject);
702     poJSONObject = nullptr;
703 
704     VSILFILE *fpImage = VSIFOpenL(pszFilename, "wb");
705     if (fpImage == nullptr)
706     {
707         CPLError( CE_Failure, CPLE_NotSupported,
708               "ARG driver can't create data file %s.", pszFilename);
709 
710         // remove JSON file
711         VSIUnlink( osJSONFilename.c_str() );
712 
713         return nullptr;
714     }
715 
716     // only 1 raster band
717     GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( 1 );
718 
719 #ifdef CPL_LSB
720     bool bNative = false;
721 #else
722     bool bNative = true;
723 #endif
724 
725     RawRasterBand *poDstBand = new RawRasterBand( fpImage, 0, nPixelOffset,
726                                                   nPixelOffset * nXSize, eType,
727                                                   bNative,
728                                                   nXSize, nYSize,
729                                                   RawRasterBand::OwnFP::NO);
730     poDstBand->SetAccess(GA_Update);
731 
732     int nXBlockSize, nYBlockSize;
733     poSrcBand->GetBlockSize(&nXBlockSize, &nYBlockSize);
734 
735     void *pabyData = CPLMalloc(nXBlockSize * nPixelOffset);
736 
737     // convert any blocks into scanlines
738     for (int nYBlock = 0; nYBlock * nYBlockSize < nYSize; nYBlock++) {
739         for (int nYScanline = 0; nYScanline < nYBlockSize; nYScanline++) {
740             if ((nYScanline+1) + nYBlock * nYBlockSize > poSrcBand->GetYSize() )
741             {
742                 continue;
743             }
744 
745             for (int nXBlock = 0; nXBlock * nXBlockSize < nXSize; nXBlock++) {
746                 int nXValid;
747 
748                 if( (nXBlock+1) * nXBlockSize > poSrcBand->GetXSize() )
749                     nXValid = poSrcBand->GetXSize() - nXBlock * nXBlockSize;
750                 else
751                     nXValid = nXBlockSize;
752 
753                 CPLErr eErr = poSrcBand->RasterIO(GF_Read, nXBlock * nXBlockSize,
754                     nYBlock * nYBlockSize + nYScanline, nXValid, 1, pabyData, nXBlockSize,
755                     1, eType, 0, 0, nullptr);
756 
757                 if (eErr != CE_None) {
758                     CPLError(CE_Failure, CPLE_AppDefined, "Error reading.");
759 
760                     CPLFree( pabyData );
761                     delete poDstBand;
762                     VSIFCloseL( fpImage );
763 
764                     return nullptr;
765                 }
766 
767                 eErr = poDstBand->RasterIO(GF_Write, nXBlock * nXBlockSize,
768                     nYBlock * nYBlockSize + nYScanline, nXValid, 1, pabyData, nXBlockSize,
769                     1, eType, 0, 0, nullptr);
770 
771                 if (eErr != CE_None) {
772                     CPLError(CE_Failure, CPLE_AppDefined, "Error writing.");
773 
774                     CPLFree( pabyData );
775                     delete poDstBand;
776                     VSIFCloseL( fpImage );
777 
778                     return nullptr;
779                 }
780             }
781         }
782     }
783 
784     CPLFree( pabyData );
785     delete poDstBand;
786     VSIFCloseL( fpImage );
787 
788     return reinterpret_cast<GDALDataset *>( GDALOpen( pszFilename, GA_ReadOnly ) );
789 }
790 
791 /************************************************************************/
792 /*                          GDALRegister_ARG()                          */
793 /************************************************************************/
794 
GDALRegister_ARG()795 void GDALRegister_ARG()
796 {
797     if( GDALGetDriverByName( "ARG" ) != nullptr )
798         return;
799 
800     GDALDriver *poDriver = new GDALDriver();
801 
802     poDriver->SetDescription( "ARG" );
803     poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
804     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
805                                "Azavea Raster Grid format" );
806     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
807                                "drivers/raster/arg.html" );
808     poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
809 
810     poDriver->pfnIdentify = ARGDataset::Identify;
811     poDriver->pfnOpen = ARGDataset::Open;
812     poDriver->pfnCreateCopy = ARGDataset::CreateCopy;
813 
814     GetGDALDriverManager()->RegisterDriver( poDriver );
815 }
816