/* * Copyright (C) 2008 Steve Ratcliffe * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License 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. * * * Author: Steve Ratcliffe * Create date: 06-Jul-2008 */ package uk.me.parabola.imgfmt.app.net; import java.util.ArrayList; import java.util.Collections; import java.util.List; import uk.me.parabola.imgfmt.app.BufferedImgFileReader; import uk.me.parabola.imgfmt.app.BufferedImgFileWriter; import uk.me.parabola.imgfmt.app.ImgFile; import uk.me.parabola.imgfmt.app.ImgFileWriter; import uk.me.parabola.imgfmt.app.Section; import uk.me.parabola.imgfmt.app.SectionWriter; import uk.me.parabola.imgfmt.fs.ImgChannel; import uk.me.parabola.log.Logger; /** * The NOD file that contains routing information. * * NOD1 contains several groups of routing nodes. * NOD2 contains road data with links into NOD1. * * NOD1 contains links back to NET (and NET contains links to NOD2). So there * is a loop and we have to write one section first, retaining the offsets * and then go back and fill in offsets that were found later. * * I'm choosing to this with Table A, as the records are fixed size and so * we can write them blank the first time and then go back and fix them * up, once the NET offsets are known. * * So we are writing NOD first before NET and NOD1 before NOD2. Once NET is * written then go back to Table A and fix the label offsets in RGN. * * @author Steve Ratcliffe */ public class NODFile extends ImgFile { private static final Logger log = Logger.getLogger(NODFile.class); private final NODHeader nodHeader = new NODHeader(); private List<RouteCenter> centers = new ArrayList<RouteCenter>(); private List<RoadDef> roads = new ArrayList<RoadDef>(); private List<RouteNode> boundary = new ArrayList<RouteNode>(); public NODFile(ImgChannel chan, boolean write) { setHeader(nodHeader); if (write) { setWriter(new BufferedImgFileWriter(chan)); position(NODHeader.HEADER_LEN); } else { setReader(new BufferedImgFileReader(chan)); nodHeader.readHeader(getReader()); } } public void write() { writeNodes(); writeRoadData(); writeBoundary(); writeHighClassBoundary(); } public void writePost() { ImgFileWriter writer = new SectionWriter(getWriter(), nodHeader.getNodeSection()); for (RouteCenter rc : centers) { rc.writePost(writer); } // Refresh the header position(0); getHeader().writeHeader(getWriter()); } /** * Write the nodes (NOD 1). This is done first as the offsets into * this section are needed to write NOD2. */ private void writeNodes() { ImgFileWriter writer = getWriter(); nodHeader.setNodeStart(writer.position()); Section section = nodHeader.getNodeSection(); writer = new SectionWriter(writer, section); int[] classBoundaries = nodHeader.getClassBoundaries(); for (RouteCenter cp : centers){ cp.write(writer, classBoundaries); } for (int i = 4; i >= 0; --i){ if (classBoundaries[i] > writer.position()) classBoundaries[i] = writer.position(); } nodHeader.setNodeSize(writer.position()); log.debug("the nod offset", Integer.toHexString(getWriter().position())); Section.close(writer); } /** * Write the road data (NOD2). */ private void writeRoadData() { log.info("writeRoadData"); ImgFileWriter writer = new SectionWriter(getWriter(), nodHeader.getRoadSection()); boolean debug = log.isDebugEnabled(); for (RoadDef rd : roads) { if(debug) log.debug("wrting nod2", writer.position()); rd.writeNod2(writer); } if(debug) log.debug("ending nod2", writer.position()); nodHeader.setRoadSize(writer.position()); } /** * Write the boundary node table (NOD3). */ private void writeBoundary() { log.info("writeBoundary"); Collections.sort(boundary); ImgFileWriter writer = new SectionWriter(getWriter(), nodHeader.getBoundarySection()); boolean debug = log.isDebugEnabled(); for (RouteNode node : boundary) { if(debug) log.debug("wrting nod3", writer.position()); node.writeNod3OrNod4(writer); } if(debug) log.debug("ending nod3", writer.position()); nodHeader.setBoundarySize(writer.position()); } /** * Write the high class boundary node table (NOD4). * Like NOD3, but contains only nodes on roads with class > 0 */ private void writeHighClassBoundary() { log.info("writeBoundary"); // Collections.sort(boundary); // already sorted for NOD3 Section section = nodHeader.getHighClassBoundary(); int pos = section.getPosition(); pos = (pos + 0x200) & ~0x1ff; // align on 0x200 int numBytesToWrite = pos - section.getPosition(); for (int i = 0; i < numBytesToWrite; i++) getWriter().put((byte)0); section.setPosition(pos); ImgFileWriter writer = new SectionWriter(getWriter(), section); boolean debug = log.isDebugEnabled(); for (RouteNode node : boundary) { if (node.getNodeClass() == 0) continue; if(debug) log.debug("wrting nod4", writer.position()); node.writeNod3OrNod4(writer); } if(debug) log.debug("ending nod4", writer.position()); nodHeader.setHighClassBoundarySize(writer.position()); } public void setNetwork(List<RouteCenter> centers, List<RoadDef> roads, List<RouteNode> boundary) { this.centers = centers; this.roads = roads; this.boundary = boundary; } public void setDriveOnLeft(boolean dol) { nodHeader.setDriveOnLeft(dol); } }