/*
* 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 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 test.files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import uk.me.parabola.imgfmt.app.BitReader;
import uk.me.parabola.imgfmt.app.BufferedImgFileReader;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.CoordNode;
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.lbl.LBLFileReader;
import uk.me.parabola.imgfmt.app.lbl.POIRecord;
import uk.me.parabola.imgfmt.app.trergn.RGNHeader;
import uk.me.parabola.imgfmt.app.trergn.Subdivision;
import uk.me.parabola.imgfmt.fs.ImgChannel;
import uk.me.parabola.util.EnhancedProperties;
import test.elements.Line;
import test.elements.Point;
import test.elements.Polygon;
/**
* Read in the RGN file. This is independent of RgnFileReader in mkgmap
* since we want to read more and do extra checking. Plus having an
* independent implementation for testing is good.
*
* @author Steve Ratcliffe
*/
public class RgnFile
extends ImgReader
{
private static final int FLAG_NETINFO = 0x800000
;
private static final int FLAG_EXTRA = 0x400000
;
private static final int MASK_LABEL = 0x3fffff
;
private final RGNHeader rgnHeader
;
private LBLFileReader lblFile
;
private NetFile netFile
;
public RgnFile
(ImgChannel chan
) {
rgnHeader =
new RGNHeader
();
setHeader
(rgnHeader
);
setReader
(new BufferedImgFileReader
(chan
));
rgnHeader.
readHeader(getReader
());
}
public void config
(EnhancedProperties props
) {
//config = props;
}
/**
* Get a list of all points for the given subdivision. This includes
* both the indexed points section and the points section.
*
* The numbering of the points carries through the sections.
* @param sd The subdivision that we are interested in.
* @return A list of all points for the subdiv.
*/
public List<Point> pointsForSubdiv
(Subdivision sd
) {
if (!sd.
hasIndPoints() && !sd.
hasPoints())
return Collections.
emptyList();
RgnOffsets rgnOffsets = getOffsets
(sd
);
ArrayList<Point> list =
new ArrayList<>();
// Even though the indexed points are after the points, the numbering
// starts with 1 for the first indexed point and carries on into the
// points section.
fetchPointsCommon
(sd, rgnOffsets.
getIndPointStart(), rgnOffsets.
getIndPointEnd(), list
);
fetchPointsCommon
(sd, rgnOffsets.
getPointStart(), rgnOffsets.
getPointEnd(), list
);
return list
;
}
/**
* The indexed points and the points sections are both read just the same.
*/
private void fetchPointsCommon
(Subdivision sd,
long start,
long end,
List<Point> points
) {
position
(start
);
ImgFileReader reader = getReader
();
int number = points.
size() +
1;
while (position
() < end
) {
Point p =
new Point(sd
);
byte t = reader.
get();
int val = reader.
get3u();
boolean hasSubtype =
false;
if ((val
& 0x800000
) !=
0)
hasSubtype =
true;
boolean hasPoi =
false;
if ((val
& 0x400000
) !=
0)
hasPoi =
true;
Label l
;
int labelOffset = val
& 0x3fffff
;
if (hasPoi
) {
POIRecord record = lblFile.
fetchPoi(labelOffset
);
if (record
!=
null) {
l = record.
getNameLabel();
p.
setPOIRecord(record
);
} else
l = lblFile.
fetchLabel(0);
} else {
l = lblFile.
fetchLabel(labelOffset
);
}
p.
setLabel(l
);
p.
setDeltaLong((short)reader.
get2u());
p.
setDeltaLat((short)reader.
get2u());
if (hasSubtype
) {
byte st = reader.
get();
p.
setType(((t
& 0xff
) << 8) |
(st
& 0xff
));
//p.setHasSubtype(true);
} else {
p.
setType((t
& 0xff
) << 8);
}
p.
setNumber(number++
);
points.
add(p
);
}
}
public List<Line> linesForSubdiv
(Subdivision div
) {
if (!div.
hasPolylines())
return Collections.
emptyList();
RgnOffsets rgnOffsets = getOffsets
(div
);
ArrayList<Line> list =
new ArrayList<>();
int start = rgnOffsets.
getLineStart();
int end = rgnOffsets.
getLineEnd();
position
(start
);
int count =
1;
while (position
() < end
) {
Line line =
new Line(div
);
line.
setLineNumber(count++
);
if (readLineCommon
(div, line
))
break;
list.
add(line
);
}
return list
;
}
public List<Polygon> shapesForSubdiv
(Subdivision div
) {
if (!div.
hasPolygons())
return Collections.
emptyList();
RgnOffsets rgnOffsets = getOffsets
(div
);
ArrayList<Polygon> list =
new ArrayList<>();
int start = rgnOffsets.
getPolygonStart();
int end = rgnOffsets.
getPolygonEnd();
position
(start
);
int count =
1;
while (position
() < end
) {
Polygon shape =
new Polygon(div
);
shape.
setLineNumber(++count
);
if (readLineCommon
(div, shape
))
break;
list.
add(shape
);
}
return list
;
}
private boolean readLineCommon
(Subdivision div,
Line line
) {
ImgFileReader reader = getReader
();
byte type = reader.
get();
line.
setType(type
& 0x3f
);
int labelOffset = reader.
get3u();
boolean extra =
(labelOffset
& FLAG_EXTRA
) !=
0;
line.
setNodeFlags(extra
);
Label label
;
if ((labelOffset
& FLAG_NETINFO
) ==
0) {
label = lblFile.
fetchLabel(labelOffset
& MASK_LABEL
);
} else {
int netoff = labelOffset
& 0x3fffff
;
line.
setNetOffset(netoff
);
RoadData road = netFile.
getRoad(netoff
);
label = road.
getLabel(0);
}
line.
setLabel(label
);
short dlon =
(short) reader.
get2u();
line.
setDeltaLong(dlon
);
short dlat =
(short) reader.
get2u();
line.
setDeltaLat(dlat
);
int currLon = fixDelta
(div, div.
getLongitude(), dlon
);
int currLat = fixDelta
(div, div.
getLatitude(), dlat
);
int len
;
if ((type
& 0x80
) ==
0)
len = reader.
get1u();
else
len = reader.
get2u();
int base = reader.
get1u();
int xbase =
2;
int n = base
& 0xf
;
if (n
<=
9)
xbase += n
;
else
xbase +=
(2 * n
) -
9;
n =
(base
>>> 4) & 0xf
;
int ybase =
2;
if (n
<=
9)
ybase += n
;
else
ybase +=
(2 * n
) -
9;
if (len ==
0) {
System.
out.
println("Line with no data");
return true;
}
byte[] bytes = reader.
get(len
);
BitReader br =
new BitReader
(bytes
);
boolean xneg =
false;
boolean xsame = br.
get1();
if (xsame
) {
xneg = br.
get1();
} else
xbase++
;
boolean ysame = br.
get1();
boolean yneg =
false;
if (ysame
) {
yneg = br.
get1();
} else
ybase++
;
if (extra
) {
boolean firstextra = br.
get1();
// This is always false in our maps, but presumably serves a purpose.
if (firstextra
)
System.
out.
println("First extra bit is true");
}
Coord co
;
if (extra
)
co =
new CoordNode
(currLat, currLon,
1,
false,
false);
else
co =
new Coord
(currLat, currLon
);
line.
addCoord(co
);
Coord lastCoord = co
;
int bitPerPoint =
8 * len -
((extra
? 1 :
0) + xbase + ybase
);
while (br.
getBitPosition() <= bitPerPoint
) {
int dx
;
if (xsame
) {
dx = br.
get(xbase
);
if (xneg
)
dx = -dx
;
} else {
dx = br.
sget2(xbase
);
}
int dy
;
if (ysame
) {
dy = br.
get(ybase
);
if (yneg
)
dy = -dy
;
} else {
dy = br.
sget2(ybase
);
}
currLat += dy
<< div.
getShift();
currLon += dx
<< div.
getShift();
boolean isnode =
false;
if (extra
) {
isnode = br.
get1();
//fmt.format(", node=%b", isnode);
}
if (isnode
) {
co =
new CoordNode
(currLat, currLon,
1,
false,
false); // XXX boundary and id
} else {
co =
new Coord
(currLat, currLon
);
}
if ((dx
!=
0 || dy
!=
0) || lastCoord.
getId() !=
0 || isnode || line.
getCoords().
size() < 2)
line.
addCoord(co
);
lastCoord = co
;
}
return false;
}
private int fixDelta
(Subdivision subdiv,
int base,
short dlat
) {
return base +
(dlat
<< subdiv.
getShift());
}
/**
* Get the offsets to the points, lines etc in RGN for the given subdiv.
* @param sd The subdivision is needed to work out the starting points.
* @return An Offsets class that allows you to obtain the offsets.
*/
private RgnOffsets getOffsets
(Subdivision sd
) {
int off = sd.
getStartRgnPointer();
position
(rgnHeader.
getDataOffset() + off
);
return new RgnOffsets
(sd
);
}
public void setLblFile
(LBLFileReader lblFile
) {
this.
lblFile = lblFile
;
}
public void setNetFile
(NetFile netFile
) {
this.
netFile = netFile
;
}
/**
* Class to hold the start and end points of point, lines etc within
* the area for a given subdivision in the RGN data.
*/
private class RgnOffsets
{
private final int pointOffset
;
private int pointEnd
;
private int indPointOffset
;
private int indPointEnd
;
private int lineOffset
;
private int lineEnd
;
private int polygonOffset
;
private int polygonEnd
;
private final int start
;
private int headerLen
;
/**
* Calculate the offsets for the given subdivision.
* After this is called the position will be set after any pointers that
* exist at the beginning of the area.
*
* @param sd The subdivision.
*/
private RgnOffsets
(Subdivision sd
) {
ImgFileReader reader = getReader
();
start =
(int) position
();
pointOffset =
0;
if (sd.
needsIndPointPtr()) {
indPointOffset = reader.
get2u();
headerLen +=
2;
}
if (sd.
needsPolylinePtr()) {
lineOffset = reader.
get2u();
headerLen +=
2;
}
if (sd.
needsPolygonPtr()) {
polygonOffset = reader.
get2u();
headerLen +=
2;
}
if (sd.
hasPoints()) {
if (sd.
hasIndPoints())
pointEnd = indPointOffset
;
else if (sd.
hasPolylines())
pointEnd = lineOffset
;
else if (sd.
hasPolygons())
pointEnd = polygonOffset
;
else
pointEnd = sd.
getEndRgnPointer() - sd.
getStartRgnPointer();
}
if (sd.
hasIndPoints()) {
if (sd.
hasPolylines())
indPointEnd = lineOffset
;
else if (sd.
hasPolygons())
indPointEnd = polygonOffset
;
else
indPointEnd = sd.
getEndRgnPointer() - sd.
getStartRgnPointer();
}
if (sd.
hasPolylines()) {
if (sd.
hasPolygons())
lineEnd = polygonOffset
;
else
lineEnd = sd.
getEndRgnPointer() - sd.
getStartRgnPointer();
}
if (sd.
hasPolygons()) {
polygonEnd = sd.
getEndRgnPointer() - sd.
getStartRgnPointer();
}
}
public String toString
() {
return String.
format("rgn div offsets: %x-%x/%x-%x/%x-%x/%x-%x",
pointOffset, pointEnd, indPointOffset, indPointEnd,
lineOffset, lineEnd, polygonOffset, polygonEnd
);
}
public long getPointStart
() {
return pointOffset ==
0 ? start + headerLen : start + pointOffset
;
}
public long getPointEnd
() {
return start + pointEnd
;
}
public long getIndPointStart
() {
return indPointOffset ==
0 ? start + headerLen : start + indPointOffset
;
}
public long getIndPointEnd
() {
return start + indPointEnd
;
}
public int getLineStart
() {
return lineOffset ==
0? start + headerLen: start + lineOffset
;
}
public int getLineEnd
() {
return start + lineEnd
;
}
public int getPolygonStart
() {
return polygonOffset ==
0? start + headerLen: start + polygonOffset
;
}
public int getPolygonEnd
() {
return start + polygonEnd
;
}
}
}