Line data Source code
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 : constexpr int HEADER_SIZE = 1011;
37 :
38 : /************************************************************************/
39 : /* JDEMGetField() */
40 : /************************************************************************/
41 :
42 26 : static int JDEMGetField(const char *pszField, int nWidth)
43 :
44 : {
45 26 : char szWork[32] = {};
46 26 : CPLAssert(nWidth < static_cast<int>(sizeof(szWork)));
47 :
48 26 : strncpy(szWork, pszField, nWidth);
49 26 : szWork[nWidth] = '\0';
50 :
51 52 : return atoi(szWork);
52 : }
53 :
54 : /************************************************************************/
55 : /* JDEMGetAngle() */
56 : /************************************************************************/
57 :
58 16 : static double JDEMGetAngle(const char *pszField)
59 :
60 : {
61 16 : 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 16 : const int nDegree = nAngle / 10000;
68 16 : const int nMin = (nAngle / 100) % 100;
69 16 : const int nSec = nAngle % 100;
70 :
71 16 : 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 *m_fp = nullptr;
87 : GByte m_abyHeader[HEADER_SIZE];
88 : OGRSpatialReference m_oSRS{};
89 :
90 : public:
91 : JDEMDataset();
92 : ~JDEMDataset();
93 :
94 : static GDALDataset *Open(GDALOpenInfo *);
95 : static int Identify(GDALOpenInfo *);
96 :
97 : CPLErr GetGeoTransform(double *padfTransform) override;
98 : const OGRSpatialReference *GetSpatialRef() const override;
99 : };
100 :
101 : /************************************************************************/
102 : /* ==================================================================== */
103 : /* JDEMRasterBand */
104 : /* ==================================================================== */
105 : /************************************************************************/
106 :
107 : class JDEMRasterBand final : public GDALPamRasterBand
108 : {
109 : friend class JDEMDataset;
110 :
111 : int m_nRecordSize = 0;
112 : char *m_pszRecord = nullptr;
113 : bool m_bBufferAllocFailed = false;
114 :
115 : public:
116 : JDEMRasterBand(JDEMDataset *, int);
117 : ~JDEMRasterBand();
118 :
119 : virtual CPLErr IReadBlock(int, int, void *) override;
120 : };
121 :
122 : /************************************************************************/
123 : /* JDEMRasterBand() */
124 : /************************************************************************/
125 :
126 2 : JDEMRasterBand::JDEMRasterBand(JDEMDataset *poDSIn, int nBandIn)
127 : : // Cannot overflow as nBlockXSize <= 999.
128 2 : m_nRecordSize(poDSIn->GetRasterXSize() * 5 + 9 + 2)
129 : {
130 2 : poDS = poDSIn;
131 2 : nBand = nBandIn;
132 :
133 2 : eDataType = GDT_Float32;
134 :
135 2 : nBlockXSize = poDS->GetRasterXSize();
136 2 : nBlockYSize = 1;
137 2 : }
138 :
139 : /************************************************************************/
140 : /* ~JDEMRasterBand() */
141 : /************************************************************************/
142 :
143 4 : JDEMRasterBand::~JDEMRasterBand()
144 : {
145 2 : VSIFree(m_pszRecord);
146 4 : }
147 :
148 : /************************************************************************/
149 : /* IReadBlock() */
150 : /************************************************************************/
151 :
152 2 : CPLErr JDEMRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
153 : void *pImage)
154 :
155 : {
156 2 : JDEMDataset *poGDS = cpl::down_cast<JDEMDataset *>(poDS);
157 :
158 2 : if (m_pszRecord == nullptr)
159 : {
160 1 : if (m_bBufferAllocFailed)
161 0 : return CE_Failure;
162 :
163 1 : m_pszRecord = static_cast<char *>(VSI_MALLOC_VERBOSE(m_nRecordSize));
164 1 : if (m_pszRecord == nullptr)
165 : {
166 0 : m_bBufferAllocFailed = true;
167 0 : return CE_Failure;
168 : }
169 : }
170 :
171 2 : CPL_IGNORE_RET_VAL(
172 2 : VSIFSeekL(poGDS->m_fp, 1011 + m_nRecordSize * nBlockYOff, SEEK_SET));
173 :
174 2 : if (VSIFReadL(m_pszRecord, m_nRecordSize, 1, poGDS->m_fp) != 1)
175 : {
176 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot read scanline %d",
177 : nBlockYOff);
178 0 : return CE_Failure;
179 : }
180 :
181 2 : if (!EQUALN(reinterpret_cast<char *>(poGDS->m_abyHeader), m_pszRecord, 6))
182 : {
183 0 : CPLError(CE_Failure, CPLE_AppDefined,
184 : "JDEM Scanline corrupt. Perhaps file was not transferred "
185 : "in binary mode?");
186 0 : return CE_Failure;
187 : }
188 :
189 2 : if (JDEMGetField(m_pszRecord + 6, 3) != nBlockYOff + 1)
190 : {
191 0 : CPLError(CE_Failure, CPLE_AppDefined,
192 : "JDEM scanline out of order, JDEM driver does not "
193 : "currently support partial datasets.");
194 0 : return CE_Failure;
195 : }
196 :
197 6 : for (int i = 0; i < nBlockXSize; i++)
198 4 : static_cast<float *>(pImage)[i] =
199 4 : JDEMGetField(m_pszRecord + 9 + 5 * i, 5) * 0.1f;
200 :
201 2 : return CE_None;
202 : }
203 :
204 : /************************************************************************/
205 : /* ==================================================================== */
206 : /* JDEMDataset */
207 : /* ==================================================================== */
208 : /************************************************************************/
209 :
210 : /************************************************************************/
211 : /* JDEMDataset() */
212 : /************************************************************************/
213 :
214 2 : JDEMDataset::JDEMDataset()
215 : {
216 2 : std::fill_n(m_abyHeader, CPL_ARRAYSIZE(m_abyHeader), static_cast<GByte>(0));
217 2 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
218 2 : m_oSRS.importFromEPSG(4301); // Tokyo geographic CRS
219 2 : }
220 :
221 : /************************************************************************/
222 : /* ~JDEMDataset() */
223 : /************************************************************************/
224 :
225 4 : JDEMDataset::~JDEMDataset()
226 :
227 : {
228 2 : FlushCache(true);
229 2 : if (m_fp != nullptr)
230 2 : CPL_IGNORE_RET_VAL(VSIFCloseL(m_fp));
231 4 : }
232 :
233 : /************************************************************************/
234 : /* GetGeoTransform() */
235 : /************************************************************************/
236 :
237 0 : CPLErr JDEMDataset::GetGeoTransform(double *padfTransform)
238 :
239 : {
240 0 : const char *psHeader = reinterpret_cast<const char *>(m_abyHeader);
241 :
242 0 : const double dfLLLat = JDEMGetAngle(psHeader + 29);
243 0 : const double dfLLLong = JDEMGetAngle(psHeader + 36);
244 0 : const double dfURLat = JDEMGetAngle(psHeader + 43);
245 0 : const double dfURLong = JDEMGetAngle(psHeader + 50);
246 :
247 0 : padfTransform[0] = dfLLLong;
248 0 : padfTransform[3] = dfURLat;
249 0 : padfTransform[1] = (dfURLong - dfLLLong) / GetRasterXSize();
250 0 : padfTransform[2] = 0.0;
251 :
252 0 : padfTransform[4] = 0.0;
253 0 : padfTransform[5] = -1 * (dfURLat - dfLLLat) / GetRasterYSize();
254 :
255 0 : return CE_None;
256 : }
257 :
258 : /************************************************************************/
259 : /* GetSpatialRef() */
260 : /************************************************************************/
261 :
262 0 : const OGRSpatialReference *JDEMDataset::GetSpatialRef() const
263 :
264 : {
265 0 : return &m_oSRS;
266 : }
267 :
268 : /************************************************************************/
269 : /* Identify() */
270 : /************************************************************************/
271 :
272 52992 : int JDEMDataset::Identify(GDALOpenInfo *poOpenInfo)
273 :
274 : {
275 52992 : if (poOpenInfo->nHeaderBytes < HEADER_SIZE)
276 48755 : return FALSE;
277 :
278 : // Confirm that the header has what appears to be dates in the
279 : // expected locations.
280 : // Check if century values seem reasonable.
281 4237 : const char *psHeader = reinterpret_cast<char *>(poOpenInfo->pabyHeader);
282 4237 : if ((!STARTS_WITH_CI(psHeader + 11, "19") &&
283 4233 : !STARTS_WITH_CI(psHeader + 11, "20")) ||
284 9 : (!STARTS_WITH_CI(psHeader + 15, "19") &&
285 5 : !STARTS_WITH_CI(psHeader + 15, "20")) ||
286 4 : (!STARTS_WITH_CI(psHeader + 19, "19") &&
287 0 : !STARTS_WITH_CI(psHeader + 19, "20")))
288 : {
289 4233 : return FALSE;
290 : }
291 :
292 : // Check the extent too. In particular, that we are in the first quadrant,
293 : // as this is only for Japan.
294 4 : const double dfLLLat = JDEMGetAngle(psHeader + 29);
295 4 : const double dfLLLong = JDEMGetAngle(psHeader + 36);
296 4 : const double dfURLat = JDEMGetAngle(psHeader + 43);
297 4 : const double dfURLong = JDEMGetAngle(psHeader + 50);
298 4 : if (dfLLLat > 90 || dfLLLat < 0 || dfLLLong > 180 || dfLLLong < 0 ||
299 4 : dfURLat > 90 || dfURLat < 0 || dfURLong > 180 || dfURLong < 0 ||
300 4 : dfLLLat > dfURLat || dfLLLong > dfURLong)
301 : {
302 0 : return FALSE;
303 : }
304 :
305 4 : return TRUE;
306 : }
307 :
308 : /************************************************************************/
309 : /* Open() */
310 : /************************************************************************/
311 :
312 2 : GDALDataset *JDEMDataset::Open(GDALOpenInfo *poOpenInfo)
313 :
314 : {
315 : // Confirm that the header is compatible with a JDEM dataset.
316 2 : if (!Identify(poOpenInfo))
317 0 : return nullptr;
318 :
319 : // Confirm the requested access is supported.
320 2 : if (poOpenInfo->eAccess == GA_Update)
321 : {
322 0 : CPLError(CE_Failure, CPLE_NotSupported,
323 : "The JDEM driver does not support update access to existing "
324 : "datasets.");
325 0 : return nullptr;
326 : }
327 :
328 : // Check that the file pointer from GDALOpenInfo* is available.
329 2 : if (poOpenInfo->fpL == nullptr)
330 : {
331 0 : return nullptr;
332 : }
333 :
334 : // Create a corresponding GDALDataset.
335 4 : auto poDS = std::make_unique<JDEMDataset>();
336 :
337 : // Borrow the file pointer from GDALOpenInfo*.
338 2 : std::swap(poDS->m_fp, poOpenInfo->fpL);
339 :
340 : // Store the header (we have already checked it is at least HEADER_SIZE
341 : // byte large).
342 2 : memcpy(poDS->m_abyHeader, poOpenInfo->pabyHeader, HEADER_SIZE);
343 :
344 2 : const char *psHeader = reinterpret_cast<const char *>(poDS->m_abyHeader);
345 2 : poDS->nRasterXSize = JDEMGetField(psHeader + 23, 3);
346 2 : poDS->nRasterYSize = JDEMGetField(psHeader + 26, 3);
347 2 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
348 : {
349 0 : return nullptr;
350 : }
351 :
352 : // Create band information objects.
353 2 : poDS->SetBand(1, new JDEMRasterBand(poDS.get(), 1));
354 :
355 : // Initialize any PAM information.
356 2 : poDS->SetDescription(poOpenInfo->pszFilename);
357 2 : poDS->TryLoadXML();
358 :
359 : // Check for overviews.
360 2 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
361 :
362 2 : return poDS.release();
363 : }
364 :
365 : /************************************************************************/
366 : /* GDALRegister_JDEM() */
367 : /************************************************************************/
368 :
369 1520 : void GDALRegister_JDEM()
370 :
371 : {
372 1520 : if (GDALGetDriverByName("JDEM") != nullptr)
373 301 : return;
374 :
375 1219 : GDALDriver *poDriver = new GDALDriver();
376 :
377 1219 : poDriver->SetDescription("JDEM");
378 1219 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
379 1219 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Japanese DEM (.mem)");
380 1219 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/jdem.html");
381 1219 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "mem");
382 1219 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
383 :
384 1219 : poDriver->pfnOpen = JDEMDataset::Open;
385 1219 : poDriver->pfnIdentify = JDEMDataset::Identify;
386 :
387 1219 : GetGDALDriverManager()->RegisterDriver(poDriver);
388 : }
|