/*
* Copyright (C) 2010.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package test.display;
import uk.me.parabola.imgfmt.app.ImgFileReader;
/**
* Standalone program to display the DEM file. This stores a
* digital elevation model used to display shaded surface and profiles of tracks.
*
* @author Ronny Klier
*
*/
public class DemDisplay
extends CommonDisplay
{
private class DemTile
{
private DemSection section
;
private int tileNumberLat
;
private int tileNumberLon
;
private int offset
; // offset from section.dataOffset2
private int dataLength
; // number of bytes for compressed elevation data
private short baseHeight
; // base or minimum height in this tile
private short differenceHeight
; // maximum height above baseHeight (elevation?)
DemTile
(DemSection parent,
int numLat,
int numLon
) {
section = parent
;
tileNumberLat = numLat
;
tileNumberLon = numLon
;
}
void readHeader
(ImgFileReader reader,
int baseOffset
) {
Displayer d =
new Displayer
(reader
);
d.
setTitle("DEM Tile Header");
DisplayItem item = d.
item();
item.
addText("Tile row %1$s column %2$s", tileNumberLat, tileNumberLon
);
reader.
position(section.
dataOffset +
(section.
tilesLon * tileNumberLat + tileNumberLon
) * section.
tileDescSize);
switch (section.
offsetSize) {
case 1:
offset = d.
byteValue("offset in data section");
break;
case 2:
offset = d.
shortValue("offset in data section");
break;
case 3:
offset = d.
int3Value("offset in data section");
break;
}
offset += baseOffset
;
if (1 == section.
baseSize) {
baseHeight = d.
byteValue("minimum height");
} else {
baseHeight = d.
shortValue("minimum height");
}
if (1 == section.
differenceSize) {
differenceHeight = d.
byteValue("elevation");
} else {
differenceHeight = d.
shortValue("elevation");
}
d.
print(outStream
);
}
void setDataLength
(int nextOffset
) {
dataLength = nextOffset - offset
;
if (dataLength
< 0)
dataLength =
0;
}
public void printRawData
(ImgFileReader reader
) {
reader.
position(offset
);
Displayer d =
new Displayer
(reader
);
d.
setTitle(String.
format("Raw data for tile row %1$s column %2$s", tileNumberLat, tileNumberLon
));
d.
rawValue(dataLength,
"real DEM data");
d.
print(outStream
);
}
}
private class DemSection
{
private int num
;
private int pointsPerLat
;
private int pointsPerLon
;
private int tilesLat
;
private int tilesLon
;
private byte offsetSize
;
private byte baseSize
;
private byte differenceSize
;
private short tileDescSize
;
private int dataOffset
;
private int dataOffset2
;
private int pointsDistanceLat
;
private int pointsDistanceLon
;
private int top
;
private int left
;
private DemTile
[] tiles
;
protected void print
(ImgFileReader reader
) {
readHeader
(reader
);
}
private void readHeader
(ImgFileReader reader
) {
Displayer d =
new Displayer
(reader
);
d.
setTitle("DEM Sector Header");
num = d.
shortValue("#"); //0x00 - 0x01
pointsPerLat = d.
intValue("Points per tile (latitude"); //0x02 - 0x05
pointsPerLon = d.
intValue("Points per tile (longitude)");//0x06 - 0x09
d.
intValue("unknown integer1"); //0x0a - 0x0d
d.
intValue("unknown integer2"); //0x0e - 0x11
d.
shortValue("unknown short"); //0x12 - 0x13
tilesLon = d.
intValue("Tiles per longitude") +
1; //0x14 - 0x17
tilesLat = d.
intValue("Tiles per latitude") +
1; //0x18 - 0x1B
short recordDesc = d.
shortValue("Flags for tile description"); //0x1C - 0x1D
DisplayItem item = d.
item();
offsetSize =
(byte)((recordDesc
& 3) +
1);
baseSize =
(byte)(((recordDesc
& 4) >> 2) +
1);
differenceSize =
(byte)(((recordDesc
& 8) >> 3) +
1);
item.
addText("Data offset has %s bytes", offsetSize
);
item.
addText("Base height has %s bytes", baseSize
);
item.
addText("Height difference has %s bytes", differenceSize
);
tileDescSize = d.
shortValue("Size of tile description");//0x1E - 0x1F
dataOffset = d.
intValue("offset of first tile description");// 0x20 - 0x23
dataOffset2 = d.
intValue("offset of first tiles data"); //0x24 - 0x27
left = d.
intValue("western bound"); //0x28-0x2B
top = d.
intValue("northern bound"); //0x2C - 0x2F
d.
item().
addText("In degrees lat,lon: %.4f,%.4f",
DemDisplay.
toDegrees(top
),
DemDisplay.
toDegrees(left
));
pointsDistanceLat = d.
intValue("map units between points (latitude)"); //0x30 - 0x33
pointsDistanceLon = d.
intValue("map units between points (longitude)"); //0x34 - 0x37
d.
shortValue("minimum height"); //0x38 - 0x39
d.
shortValue("maximum height"); //0x3A - 0x3B
d.
print(outStream
);
tiles =
new DemTile
[tilesLat
* tilesLon
];
// now read the tile description
for (int i =
0; i
< tiles.
length; ++i
) {
tiles
[i
] =
new DemTile
(this, i / tilesLon, i
% tilesLon
);
tiles
[i
].
readHeader(reader, dataOffset2
);
if (i
!=
0) {
tiles
[i-
1].
setDataLength(tiles
[i
].
offset);
}
// data length of last tile has to be set after reading next section
}
}
private void setLastDataLength
(int nextSectionOffset
) {
tiles
[tiles.
length -
1].
setDataLength(nextSectionOffset
);
}
public void printRawData
(ImgFileReader reader
) {
Displayer d =
new Displayer
(reader
);
d.
setTitle(String.
format("Printing raw data of sector %s", num
));
for (int i =
0; i
< tiles.
length; ++i
) {
tiles
[i
].
printRawData(reader
);
}
d.
print(outStream
);
}
}
private DemSection
[] sections
;
private int sectionOffset
;
private short sectionSize
;
private boolean useMeters
;
@
Override
protected void print
() {
readCommonHeader
();
readFileHeader
();
}
private void readFileHeader
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("DEM Header");
useMeters =
1 !=
(d.
intValue("Flags ?") & 1);
DisplayItem item = d.
item();
if (useMeters
) {
item.
addText("elevation is given in meters");
} else {
item.
addText("elevation is given in feet");
}
sections =
new DemSection
[d.
shortValue("Number of zoom levels/sectors")];
d.
intValue("unknown integer");
sectionSize = d.
shortValue("size of sector headers");
sectionOffset = d.
intValue("offset of first sector header");
if (getHeaderLen
() > 0x25
) {
d.
intValue("unknown integer at 0x25");
}
d.
print(outStream
);
reader.
position(sectionOffset
);
for (int i =
0; i
< sections.
length; i++
) {
reader.
position(sectionOffset + sectionSize
* i
);
sections
[i
] =
new DemSection
();
sections
[i
].
print(reader
);
if (i
!=
0) {
// data of last tile of previous section ends before first tile of this section
sections
[i-
1].
setLastDataLength(sections
[i
].
dataOffset2);
}
if (i -
1 == sections.
length) {
// data of last tile of last section ends before begin of section description
sections
[i
].
setLastDataLength(sectionOffset
);
}
}
// now iterate a second time over section and tiles and print out raw data
for (int i =
0; i
< sections.
length; ++i
) {
sections
[i
].
printRawData(reader
);
}
}
private static double toDegrees
(int val
) {
return (double) val
* (360.0 /
4294967296.0);
}
public static void main
(String[] args
) {
if (args.
length < 1) {
System.
err.
println("Usage: demdisplay <filename>");
System.
exit(1);
}
CommonDisplay td =
new DemDisplay
();
td.
display(args
[0],
"DEM");
}
}