1 /******************************************************************************
2  *
3  * Project:  JDEM Reader
4  * Purpose:  All code for Japanese DEM Reader
5  * Author:   Frank Warmerdam, warmerdam@pobox.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2000, Frank Warmerdam <warmerdam@pobox.com>
9  * Copyright (c) 2009-2012, Even Rouault <even dot rouault at spatialys.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_port.h"
31 #include "gdal_frmts.h"
32 #include "gdal_pam.h"
33 
34 #include <algorithm>
35 
36 CPL_CVSID("$Id: jdemdataset.cpp f6099e5ed704166bf5cc113a053dd1b2725cb391 2020-03-22 11:20:10 +0100 Kai Pastor $")
37 
38 /************************************************************************/
39 /*                            JDEMGetField()                            */
40 /************************************************************************/
41 
JDEMGetField(const char * pszField,int nWidth)42 static int JDEMGetField( const char *pszField, int nWidth )
43 
44 {
45     char szWork[32] = {};
46     CPLAssert(nWidth < static_cast<int>(sizeof(szWork)));
47 
48     strncpy(szWork, pszField, nWidth);
49     szWork[nWidth] = '\0';
50 
51     return atoi(szWork);
52 }
53 
54 /************************************************************************/
55 /*                            JDEMGetAngle()                            */
56 /************************************************************************/
57 
JDEMGetAngle(const char * pszField)58 static double JDEMGetAngle( const char *pszField )
59 
60 {
61     const int nAngle = JDEMGetField(pszField, 7);
62 
63     // Note, this isn't very general purpose, but it would appear
64     // from the field widths that angles are never negative.  Nice
65     // to be a country in the "first quadrant".
66 
67     const int nDegree = nAngle / 10000;
68     const int nMin = (nAngle / 100) % 100;
69     const int nSec = nAngle % 100;
70 
71     return nDegree + nMin / 60.0 + nSec / 3600.0;
72 }
73 
74 /************************************************************************/
75 /* ==================================================================== */
76 /*                              JDEMDataset                             */
77 /* ==================================================================== */
78 /************************************************************************/
79 
80 class JDEMRasterBand;
81 
82 class JDEMDataset final: public GDALPamDataset
83 {
84     friend class JDEMRasterBand;
85 
86     VSILFILE    *fp;
87     GByte       abyHeader[1012];
88 
89   public:
90                      JDEMDataset();
91                     ~JDEMDataset();
92 
93     static GDALDataset *Open( GDALOpenInfo * );
94     static int Identify( GDALOpenInfo * );
95 
96     CPLErr GetGeoTransform( double * padfTransform ) override;
97     const char *_GetProjectionRef() override;
GetSpatialRef() const98     const OGRSpatialReference* GetSpatialRef() const override {
99         return GetSpatialRefFromOldGetProjectionRef();
100     }
101 };
102 
103 /************************************************************************/
104 /* ==================================================================== */
105 /*                            JDEMRasterBand                             */
106 /* ==================================================================== */
107 /************************************************************************/
108 
109 class JDEMRasterBand final: public GDALPamRasterBand
110 {
111     friend class JDEMDataset;
112 
113     int          nRecordSize;
114     char        *pszRecord;
115     bool         bBufferAllocFailed;
116 
117   public:
118                 JDEMRasterBand( JDEMDataset *, int );
119     virtual ~JDEMRasterBand();
120 
121     virtual CPLErr IReadBlock( int, int, void * ) override;
122 };
123 
124 /************************************************************************/
125 /*                           JDEMRasterBand()                            */
126 /************************************************************************/
127 
JDEMRasterBand(JDEMDataset * poDSIn,int nBandIn)128 JDEMRasterBand::JDEMRasterBand( JDEMDataset *poDSIn, int nBandIn ) :
129     // Cannot overflow as nBlockXSize <= 999.
130     nRecordSize(poDSIn->GetRasterXSize() * 5 + 9 + 2),
131     pszRecord(nullptr),
132     bBufferAllocFailed(false)
133 {
134     poDS = poDSIn;
135     nBand = nBandIn;
136 
137     eDataType = GDT_Float32;
138 
139     nBlockXSize = poDS->GetRasterXSize();
140     nBlockYSize = 1;
141 }
142 
143 /************************************************************************/
144 /*                          ~JDEMRasterBand()                            */
145 /************************************************************************/
146 
~JDEMRasterBand()147 JDEMRasterBand::~JDEMRasterBand() { VSIFree(pszRecord); }
148 
149 /************************************************************************/
150 /*                             IReadBlock()                             */
151 /************************************************************************/
152 
IReadBlock(CPL_UNUSED int nBlockXOff,int nBlockYOff,void * pImage)153 CPLErr JDEMRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff,
154                                    int nBlockYOff,
155                                    void * pImage )
156 
157 {
158     JDEMDataset *poGDS = static_cast<JDEMDataset *>(poDS);
159 
160     if (pszRecord == nullptr)
161     {
162         if (bBufferAllocFailed)
163             return CE_Failure;
164 
165         pszRecord = static_cast<char *>(VSI_MALLOC_VERBOSE(nRecordSize));
166         if (pszRecord == nullptr)
167         {
168             bBufferAllocFailed = true;
169             return CE_Failure;
170         }
171     }
172 
173     CPL_IGNORE_RET_VAL(
174         VSIFSeekL(poGDS->fp, 1011 + nRecordSize * nBlockYOff, SEEK_SET));
175 
176     CPL_IGNORE_RET_VAL(VSIFReadL(pszRecord, 1, nRecordSize, poGDS->fp));
177 
178     if( !EQUALN(reinterpret_cast<char *>(poGDS->abyHeader), pszRecord, 6) )
179     {
180         CPLError(CE_Failure, CPLE_AppDefined,
181                  "JDEM Scanline corrupt.  Perhaps file was not transferred "
182                  "in binary mode?");
183         return CE_Failure;
184     }
185 
186     if( JDEMGetField(pszRecord + 6, 3) != nBlockYOff + 1 )
187     {
188         CPLError(CE_Failure, CPLE_AppDefined,
189                  "JDEM scanline out of order, JDEM driver does not "
190                  "currently support partial datasets.");
191         return CE_Failure;
192     }
193 
194     for( int i = 0; i < nBlockXSize; i++ )
195         static_cast<float *>(pImage)[i] =
196             JDEMGetField(pszRecord + 9 + 5 * i, 5) * 0.1f;
197 
198     return CE_None;
199 }
200 
201 /************************************************************************/
202 /* ==================================================================== */
203 /*                              JDEMDataset                             */
204 /* ==================================================================== */
205 /************************************************************************/
206 
207 /************************************************************************/
208 /*                            JDEMDataset()                             */
209 /************************************************************************/
210 
JDEMDataset()211 JDEMDataset::JDEMDataset() :
212     fp(nullptr)
213 {
214     std::fill_n(abyHeader, CPL_ARRAYSIZE(abyHeader), static_cast<GByte>(0));
215 }
216 
217 /************************************************************************/
218 /*                           ~JDEMDataset()                             */
219 /************************************************************************/
220 
~JDEMDataset()221 JDEMDataset::~JDEMDataset()
222 
223 {
224     FlushCache();
225     if( fp != nullptr )
226         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
227 }
228 
229 /************************************************************************/
230 /*                          GetGeoTransform()                           */
231 /************************************************************************/
232 
GetGeoTransform(double * padfTransform)233 CPLErr JDEMDataset::GetGeoTransform( double *padfTransform )
234 
235 {
236     const char *psHeader = reinterpret_cast<char *>(abyHeader);
237 
238     const double dfLLLat = JDEMGetAngle(psHeader + 29);
239     const double dfLLLong = JDEMGetAngle(psHeader + 36);
240     const double dfURLat = JDEMGetAngle(psHeader + 43);
241     const double dfURLong = JDEMGetAngle(psHeader + 50);
242 
243     padfTransform[0] = dfLLLong;
244     padfTransform[3] = dfURLat;
245     padfTransform[1] = (dfURLong - dfLLLong) / GetRasterXSize();
246     padfTransform[2] = 0.0;
247 
248     padfTransform[4] = 0.0;
249     padfTransform[5] = -1 * (dfURLat - dfLLLat) / GetRasterYSize();
250 
251     return CE_None;
252 }
253 
254 /************************************************************************/
255 /*                          GetProjectionRef()                          */
256 /************************************************************************/
257 
_GetProjectionRef()258 const char *JDEMDataset::_GetProjectionRef()
259 
260 {
261     return
262         "GEOGCS[\"Tokyo\",DATUM[\"Tokyo\","
263         "SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,"
264         "AUTHORITY[\"EPSG\",7004]],TOWGS84[-148,507,685,0,0,0,0],"
265         "AUTHORITY[\"EPSG\",6301]],PRIMEM[\"Greenwich\",0,"
266         "AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,"
267         "AUTHORITY[\"EPSG\",9108]],AUTHORITY[\"EPSG\",4301]]";
268 }
269 
270 /************************************************************************/
271 /*                              Identify()                              */
272 /************************************************************************/
273 
Identify(GDALOpenInfo * poOpenInfo)274 int JDEMDataset::Identify( GDALOpenInfo *poOpenInfo )
275 
276 {
277     // Confirm that the header has what appears to be dates in the
278     // expected locations.  Sadly this is a relatively weak test.
279     if( poOpenInfo->nHeaderBytes < 50 )
280         return FALSE;
281 
282     // Check if century values seem reasonable.
283     const char *psHeader = reinterpret_cast<char *>(poOpenInfo->pabyHeader);
284     if( (!STARTS_WITH_CI(psHeader + 11, "19") &&
285          !STARTS_WITH_CI(psHeader + 11, "20")) ||
286         (!STARTS_WITH_CI(psHeader + 15, "19") &&
287          !STARTS_WITH_CI(psHeader + 15, "20")) ||
288         (!STARTS_WITH_CI(psHeader + 19, "19") &&
289          !STARTS_WITH_CI(psHeader + 19, "20")) )
290     {
291         return FALSE;
292     }
293 
294     return TRUE;
295 }
296 
297 /************************************************************************/
298 /*                                Open()                                */
299 /************************************************************************/
300 
Open(GDALOpenInfo * poOpenInfo)301 GDALDataset *JDEMDataset::Open( GDALOpenInfo *poOpenInfo )
302 
303 {
304     // Confirm that the header is compatible with a JDEM dataset.
305     if (!Identify(poOpenInfo))
306         return nullptr;
307 
308     // Confirm the requested access is supported.
309     if( poOpenInfo->eAccess == GA_Update )
310     {
311         CPLError(CE_Failure, CPLE_NotSupported,
312                  "The JDEM driver does not support update access to existing "
313                  "datasets.");
314         return nullptr;
315     }
316 
317     // Check that the file pointer from GDALOpenInfo* is available.
318     if( poOpenInfo->fpL == nullptr )
319     {
320         return nullptr;
321     }
322 
323     // Create a corresponding GDALDataset.
324     JDEMDataset *poDS = new JDEMDataset();
325 
326     // Borrow the file pointer from GDALOpenInfo*.
327     poDS->fp = poOpenInfo->fpL;
328     poOpenInfo->fpL = nullptr;
329 
330     // Read the header.
331     CPL_IGNORE_RET_VAL(VSIFReadL(poDS->abyHeader, 1, 1012, poDS->fp));
332 
333     const char *psHeader = reinterpret_cast<char *>(poDS->abyHeader);
334     poDS->nRasterXSize = JDEMGetField(psHeader + 23, 3);
335     poDS->nRasterYSize = JDEMGetField(psHeader + 26, 3);
336     if( poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0 )
337     {
338         CPLError(CE_Failure, CPLE_AppDefined,
339                  "Invalid dimensions : %d x %d",
340                  poDS->nRasterXSize, poDS->nRasterYSize);
341         delete poDS;
342         return nullptr;
343     }
344 
345     // Create band information objects.
346     poDS->SetBand(1, new JDEMRasterBand(poDS, 1));
347 
348     // Initialize any PAM information.
349     poDS->SetDescription(poOpenInfo->pszFilename);
350     poDS->TryLoadXML();
351 
352     // Check for overviews.
353     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
354 
355     return poDS;
356 }
357 
358 /************************************************************************/
359 /*                          GDALRegister_JDEM()                         */
360 /************************************************************************/
361 
GDALRegister_JDEM()362 void GDALRegister_JDEM()
363 
364 {
365     if( GDALGetDriverByName("JDEM") != nullptr )
366         return;
367 
368     GDALDriver *poDriver = new GDALDriver();
369 
370     poDriver->SetDescription("JDEM");
371     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
372     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Japanese DEM (.mem)");
373     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/jdem.html");
374     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "mem");
375     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
376 
377     poDriver->pfnOpen = JDEMDataset::Open;
378     poDriver->pfnIdentify = JDEMDataset::Identify;
379 
380     GetGDALDriverManager()->RegisterDriver(poDriver);
381 }
382