use of uk.me.parabola.mkgmap.general.MapLine in project mkgmap by openstreetmap.
the class LineSplitterFilterTest method test.
private void test(List<Coord> points) {
MapLine l = new MapLine();
l.setPoints(points);
FilterConfig config = new FilterConfig() {
{
setResolution(24);
setLevel(0);
}
};
LayerFilterChain chain = new LayerFilterChain(config);
LineSplitterFilter filter = new LineSplitterFilter();
filter.init(config);
chain.addFilter(filter);
TestFilter testFilter = new TestFilter(l);
chain.addFilter(testFilter);
chain.doFilter(l);
testFilter.check();
}
use of uk.me.parabola.mkgmap.general.MapLine in project mkgmap by openstreetmap.
the class MapArea method addSize.
/**
* Add an estimate of the size that will be required to hold this element
* if it should be displayed at the given resolution. We also keep track
* of the number of <i>active</i> elements here ie elements that will be
* shown because they are at a resolution at least as great as the resolution
* of the area.
*
* @param el The element containing the minimum resolution that it will be
* displayed at.
* @param kind What kind of element this is KIND_POINT etc.
*/
private void addSize(MapElement el, int kind) {
int res = el.getMinResolution();
if (res > areaResolution || res > MAX_RESOLUTION)
return;
++splittableCount;
int numPoints;
int numElements;
switch(kind) {
case POINT_KIND:
case XT_POINT_KIND:
// Points are predictably less than 10 bytes.
sizes[kind] += 9;
if (!el.hasExtendedType()) {
if (((MapPoint) el).isCity())
nActiveIndPoints++;
else
nActivePoints++;
}
break;
case LINE_KIND:
case XT_LINE_KIND:
// Estimate the size taken by lines and shapes as a constant plus
// a factor based on the number of points.
numPoints = PredictFilterPoints.predictedMaxNumPoints(((MapLine) el).getPoints(), areaResolution, // assume MapBuilder.doRoads is true. subDiv.getZoom().getLevel() == 0 is maximum resolution
((MapLine) el).isRoad() && areaResolution == MAX_RESOLUTION);
if (numPoints <= 1 && !((MapLine) el).isRoad())
return;
numElements = 1 + ((numPoints - 1) / LineSplitterFilter.MAX_POINTS_IN_LINE);
// very pessimistic, typically less than 2 bytes are needed for one point
sizes[kind] += numElements * 11 + numPoints * 4;
if (!el.hasExtendedType())
nActiveLines += numElements;
break;
case SHAPE_KIND:
case XT_SHAPE_KIND:
// see canSplit() above
++splittableCount;
// Estimate the size taken by lines and shapes as a constant plus
// a factor based on the number of points.
numPoints = PredictFilterPoints.predictedMaxNumPoints(((MapShape) el).getPoints(), areaResolution, false);
if (numPoints <= 3)
return;
numElements = 1 + ((numPoints - 1) / PolygonSplitterFilter.MAX_POINT_IN_ELEMENT);
// very pessimistic, typically less than 2 bytes are needed for one point
sizes[kind] += numElements * 11 + numPoints * 4;
if (!el.hasExtendedType())
nActiveShapes += numElements;
break;
default:
log.error("should not be here");
assert false;
break;
}
}
use of uk.me.parabola.mkgmap.general.MapLine in project mkgmap by openstreetmap.
the class MapArea method addLines.
/**
* Add the lines, making sure that they are not too big for resolution
* that we are working with.
* @param src The map data.
* @param resolution The current resolution of the layer.
*/
private void addLines(MapDataSource src, final int resolution) {
// Split lines for size, such that it is appropriate for the
// resolution that it is at.
MapFilterChain chain = new MapFilterChain() {
public void doFilter(MapElement element) {
MapLine line = (MapLine) element;
addLine(line);
}
};
LineSizeSplitterFilter filter = new LineSizeSplitterFilter();
FilterConfig config = new FilterConfig();
config.setResolution(resolution);
config.setBounds(bounds);
filter.init(config);
for (MapLine l : src.getLines()) {
if (l.getMaxResolution() < resolution)
continue;
// %%% ??? if not appearing at this level no need to filter
filter.doFilter(l, chain);
}
}
use of uk.me.parabola.mkgmap.general.MapLine in project mkgmap by openstreetmap.
the class OverviewBuilder method readLines.
/**
* Read the lines from the .img file and add them to the overview map.
* We read from the least detailed level (apart from the empty one).
*
* @param mapReader Map reader on the detailed .img file.
*/
private void readLines(MapReader mapReader) {
Zoom[] levels = mapReader.getLevels();
for (int l = 1; l < levels.length; l++) {
int min = levels[l].getLevel();
int res = levels[l].getResolution();
List<Polyline> lineList = mapReader.linesForLevel(min);
// System.out.println(lineList.size() + " lines in lowest resolution " + levels[1].getResolution());
for (Polyline line : lineList) {
if (log.isDebugEnabled())
log.debug("got line", line);
MapLine ml = new MapLine();
List<Coord> points = line.getPoints();
if (log.isDebugEnabled())
log.debug("line point list", points);
if (points.size() < 2)
continue;
ml.setType(line.getType());
if (line.getLabel() != null)
ml.setName(line.getLabel().getText());
ml.setMaxResolution(res);
ml.setMinResolution(res);
ml.setPoints(points);
overviewSource.addLine(ml);
}
}
}
use of uk.me.parabola.mkgmap.general.MapLine in project mkgmap by openstreetmap.
the class LinePreparerFilter method doFilter.
/**
* @param element A map element that will be a line or a polygon.
* @param next This is used to pass the element onward.
*/
public void doFilter(MapElement element, MapFilterChain next) {
MapLine line = (MapLine) element;
int numPoints = line.getPoints().size();
if (line instanceof MapShape && numPoints >= PolygonSplitterFilter.MAX_POINT_IN_ELEMENT)
throw new MustSplitException();
boolean first = true;
int minPointsRequired = (element instanceof MapShape) ? 3 : 2;
if (minPointsRequired == 3 && line.getPoints().get(0).equals(line.getPoints().get(numPoints - 1)))
++minPointsRequired;
int lastLat = 0;
int lastLong = 0;
int numPointsEncoded = 1;
// fields to keep track of the largest delta values
int[] maxBits = { 0, 0 };
int[] maxBits2nd = { 0, 0 };
int[] maxBitsPos = { 0, 0 };
for (int i = 0; i < numPoints; i++) {
Coord co = line.getPoints().get(i);
int lat = subdiv.roundLatToLocalShifted(co.getLatitude());
int lon = subdiv.roundLonToLocalShifted(co.getLongitude());
if (first) {
lastLat = lat;
lastLong = lon;
first = false;
continue;
}
// compute normalized differences
// -2^(shift-1) <= dx, dy < 2^(shift-1)
// XXX: relies on the fact that java integers are 32 bit signed
final int offset = 8 + shift;
int dx = (lon - lastLong) << offset >> offset;
int dy = (lat - lastLat) << offset >> offset;
lastLong = lon;
lastLat = lat;
if (dx == 0 && dy == 0) {
if (!line.isRoad() || (co.getId() == 0 && co.isNumberNode() == false))
continue;
}
++numPointsEncoded;
if (numPointsEncoded >= minPointsRequired && element instanceof MapShape == false)
break;
// find out largest and 2nd largest delta for both dx and dy
for (int k = 0; k < 2; k++) {
int nBits = LinePreparer.bitsNeeded((k == 0) ? dx : dy);
if (nBits > maxBits2nd[k]) {
if (nBits > maxBits[k]) {
maxBits2nd[k] = maxBits[k];
maxBits[k] = nBits;
maxBitsPos[k] = i;
} else
maxBits2nd[k] = nBits;
}
}
}
if (numPointsEncoded < minPointsRequired)
return;
if (minPointsRequired >= 3) {
// check if we can optimise shape by rotating
// so that the line segment that requires the highest number of bits
// is not encoded and thus fewer bits
// are required for all points
// TODO: maybe add additional points to further reduce max. delta values
// or reverse order if largest delta is negative
int maxReduction = 0;
int rotation = 0;
for (int k = 0; k < 2; k++) {
int delta = maxBits[k] - maxBits2nd[k];
// prefer largest delta, then smallest rotation
if (delta > maxReduction || delta == maxReduction && rotation > maxBitsPos[k]) {
maxReduction = delta;
rotation = maxBitsPos[k];
}
}
/*
int savedBits = (numPoints-1 * maxReduction);
if (savedBits > 100){
System.out.println("rotation of shape saves " + savedBits + " bits");
}
*/
if (rotation != 0) {
List<Coord> points = line.getPoints();
if (minPointsRequired == 4)
points.remove(numPoints - 1);
Collections.rotate(points, -rotation);
if (minPointsRequired == 4)
points.add(points.get(0));
}
}
next.doFilter(element);
}
Aggregations