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