Rev 231 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* 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");
}
}