/*
* 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
);
}
}