/* * Copyright (C) 2009. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 or * version 2 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 uk.me.parabola.imgfmt.app.trergn; import java.util.ArrayList; import java.util.List; import uk.me.parabola.imgfmt.app.Area; import uk.me.parabola.imgfmt.app.BufferedImgFileReader; import uk.me.parabola.imgfmt.app.ImgFileReader; import uk.me.parabola.imgfmt.app.ImgReader; import uk.me.parabola.imgfmt.app.Label; import uk.me.parabola.imgfmt.app.Section; import uk.me.parabola.imgfmt.app.labelenc.CharacterDecoder; import uk.me.parabola.imgfmt.app.labelenc.CodeFunctions; import uk.me.parabola.imgfmt.app.labelenc.DecodedText; import uk.me.parabola.imgfmt.app.lbl.LBLFileReader; import uk.me.parabola.imgfmt.fs.ImgChannel; import uk.me.parabola.util.EnhancedProperties; /** * This is the file that contains the overview of the map. There * can be different zoom levels and each level of zoom has an * associated set of subdivided areas. Each of these areas then points * into the RGN file. * * The main focus of mkgmap is creating files, there are plenty of applications * that read and display the data, reading is implemented only to the * extent required to support creating the various auxiliary files etc. * * @author Steve Ratcliffe */ public class TREFileReader extends ImgReader { private Zoom[] mapLevels; private Subdivision[][] levelDivs; private static final Subdivision[] EMPTY_SUBDIVISIONS = new Subdivision[0]; private final TREHeader header = new TREHeader(); public TREFileReader(ImgChannel chan) { setHeader(header); setReader(new BufferedImgFileReader(chan)); header.readHeader(getReader()); readMapLevels(); readSubdivs(); readExtTypeOffsetsRecords(); } public Area getBounds() { return header.getBounds(); } public Zoom[] getMapLevels() { return mapLevels; } /** * Return the subdivisions for the given level. * @param level The level, 0 being the most detailed. There may not be * a level zero in the map. * @return The subdivisions for the level. Never returns null; a zero length * array is returned if there is no such level. */ public Subdivision[] subdivForLevel(int level) { for (int i = 0; i < mapLevels.length; i++) { if (mapLevels[i].getLevel() == level) { return levelDivs[i]; } } return EMPTY_SUBDIVISIONS; } /** * Read in the subdivisions. There are a set of subdivision for each level. */ private void readSubdivs() { ImgFileReader reader = getReader(); int start = header.getSubdivPos(); int end = start + header.getSubdivSize(); reader.position(start); int subdivNum = 1; int lastRgnOffset = reader.getu3(); for (int count = 0; count < levelDivs.length && reader.position() < end; count++) { Subdivision[] divs = levelDivs[count]; Zoom zoom = mapLevels[count]; if (divs == null) break; for (int i = 0; i < divs.length; i++) { int flags = reader.get(); int lon = reader.get3(); int lat = reader.get3(); int width = reader.getChar() & 0x7fff; int height = reader.getChar() & 0xffff; if (count < levelDivs.length-1) reader.getChar(); int endRgnOffset = reader.getu3(); SubdivData subdivData = new SubdivData(flags, lat, lon, 2*width, 2*height, lastRgnOffset, endRgnOffset); Subdivision subdiv = Subdivision.readSubdivision(mapLevels[count], subdivData); subdiv.setNumber(subdivNum++); divs[i] = subdiv; zoom.addSubdivision(subdiv); lastRgnOffset = endRgnOffset; } } } /** * Read the extended type info for the sub divisions. Corresponds to {@link TREFile#writeExtTypeOffsetsRecords()}. */ private void readExtTypeOffsetsRecords() { ImgFileReader reader = getReader(); int start = header.getExtTypeOffsetsPos(); int end = start + header.getExtTypeOffsetsSize(); int skipBytes = header.getExtTypeSectionSize() - 13; reader.position(start); Subdivision sd = null; Subdivision sdPrev = null; for (int count = 0; count < levelDivs.length && reader.position() < end; count++) { Subdivision[] divs = levelDivs[count]; if (divs == null) break; for (int i = 0; i < divs.length; i++) { sdPrev = sd; sd = divs[i]; sd.readExtTypeOffsetsRecord(reader, sdPrev); if (skipBytes > 0) reader.get(skipBytes); } } if(sd != null) { sd.readLastExtTypeOffsetsRecord(reader); if (skipBytes > 0) reader.get(skipBytes); } } /** * Read the map levels. This is needed to make sense of the subdivision * data. Unlike in the write case, we just keep an array of zoom levels * as found, there is no correspondence between the array index and level. */ private void readMapLevels() { ImgFileReader reader = getReader(); int levelsPos = header.getMapLevelsPos(); int levelsSize = header.getMapLevelsSize(); reader.position(levelsPos); List<Subdivision[]> levelDivs = new ArrayList<Subdivision[]>(); List<Zoom> mapLevels = new ArrayList<Zoom>(); int end = levelsPos + levelsSize; while (reader.position() < end) { int level = reader.get(); int nbits = reader.get(); int ndivs = reader.getChar(); Subdivision[] divs = new Subdivision[ndivs]; levelDivs.add(divs); level &= 0x7f; Zoom z = new Zoom(level, nbits); mapLevels.add(z); } this.levelDivs = levelDivs.toArray(new Subdivision[levelDivs.size()][]); this.mapLevels = mapLevels.toArray(new Zoom[mapLevels.size()]); } public void config(EnhancedProperties props) { header.config(props); } public String[] getMapInfo(int codePage) { CodeFunctions funcs = CodeFunctions.createEncoderForLBL(0, codePage); CharacterDecoder decoder = funcs.getDecoder(); // First do the ones in the TRE header gap ImgFileReader reader = getReader(); reader.position(header.getHeaderLength()); List<String> msgs = new ArrayList<>(); while (reader.position() < header.getHeaderLength() + header.getMapInfoSize()) { byte[] m = reader.getZString(); decoder.reset(); for (byte b : m) decoder.addByte(b); DecodedText text = decoder.getText(); String text1 = text.getText(); msgs.add(text1); } return msgs.toArray(new String[msgs.size()]); } public String[] getCopyrights(LBLFileReader lblReader) { Section sect = header.getCopyrightSection(); ImgFileReader reader = getReader(); List<String> msgs = new ArrayList<>(); long pos = sect.getPosition(); while (pos < sect.getEndPos()) { reader.position(pos); int offset = reader.get3(); Label label = lblReader.fetchLabel(offset); if (label != null) { msgs.add(label.getText()); } pos += sect.getItemSize(); } return msgs.toArray(new String[msgs.size()]); } }