use of uk.me.parabola.mkgmap.general.MapShape 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.MapShape in project mkgmap by openstreetmap.
the class OverviewBuilder method addMapCoverageArea.
/**
* Add an area that shows the area covered by a detailed map. This can
* be an arbitary shape, although at the current time we only support
* rectangles.
*
* @param finfo Information about a detail map.
*/
private void addMapCoverageArea(FileInfo finfo) {
Area bounds = finfo.getBounds();
List<Coord> points = bounds.toCoords();
for (Coord co : points) {
overviewSource.addToBounds(co);
}
// Create the tile coverage rectangle
MapShape bg = new MapShape();
bg.setType(0x4a);
bg.setPoints(points);
bg.setMinResolution(0);
bg.setName(finfo.getDescription() + '\u001d' + finfo.getMapname());
overviewSource.addShape(bg);
}
use of uk.me.parabola.mkgmap.general.MapShape 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);
}
use of uk.me.parabola.mkgmap.general.MapShape in project mkgmap by openstreetmap.
the class LineSplitterFilter method doFilter.
/**
* If the line is short enough then we just pass it on straight away.
* Otherwise we cut it into pieces that are short enough and hand them
* on.
*
* @param element A map element.
* @param next This is used to pass the possibly transformed element onward.
*/
public void doFilter(MapElement element, MapFilterChain next) {
// We do not deal with shapes.
assert !(element instanceof MapShape) && element instanceof MapLine;
MapLine line = (MapLine) element;
List<Coord> points = line.getPoints();
int npoints = points.size();
if (npoints <= MAX_POINTS_IN_LINE) {
next.doFilter(element);
return;
}
log.debug("line has too many points, splitting");
if (line.isRoad() && level == 0 && isRoutable && log.isDebugEnabled()) {
log.debug("Way " + ((MapRoad) line).getRoadDef() + " has more than " + MAX_POINTS_IN_LINE + " points and is about to be split");
}
boolean last = false;
int wantedSize = (npoints < 2 * MAX_POINTS_IN_LINE) ? npoints / 2 + 1 : MAX_POINTS_IN_LINE;
int pos = 0;
while (true) {
if (pos == 0)
log.debug("saving first part");
else if (!last)
log.debug("saving next part");
else
log.debug("saving final part");
MapLine l = line.copy();
l.setPoints(new ArrayList<>(points.subList(pos, pos + wantedSize)));
if (wantedSize < MAX_POINTS_IN_LINE / 2)
log.error("size?", npoints, pos, wantedSize);
if (!last && line instanceof MapRoad)
((MapRoad) l).setSegmentsFollowing(true);
next.doFilter(l);
if (last)
break;
// we start with the last point of previous part
pos += wantedSize - 1;
int remaining = npoints - pos;
// make sure that the last parts have enough points
if (remaining <= MAX_POINTS_IN_LINE) {
last = true;
wantedSize = remaining;
} else if (remaining < 2 * MAX_POINTS_IN_LINE)
wantedSize = remaining / 2 + 1;
}
}
use of uk.me.parabola.mkgmap.general.MapShape in project mkgmap by openstreetmap.
the class PolygonSplitterBase method split.
/**
* Split the given shape and place the resulting shapes in the outputs list.
* @param shape The original shape (that is too big).
* @param outputs The output list.
*/
protected void split(MapShape shape, List<MapShape> outputs) {
int dividingLine = 0;
boolean isLongitude = false;
Area bounds = shape.getBounds();
if (bounds.getWidth() > bounds.getHeight()) {
isLongitude = true;
Area[] tmpAreas = bounds.split(2, 1, shift);
dividingLine = tmpAreas != null ? tmpAreas[0].getMaxLong() : (bounds.getMinLong() + bounds.getWidth() / 2);
} else {
Area[] tmpAreas = bounds.split(1, 2, shift);
dividingLine = tmpAreas != null ? tmpAreas[0].getMaxLat() : (bounds.getMinLat() + bounds.getHeight() / 2);
}
List<List<Coord>> subShapePoints = new ArrayList<>();
ShapeSplitter.splitShape(shape.getPoints(), dividingLine << Coord.DELTA_SHIFT, isLongitude, subShapePoints, subShapePoints, null);
for (List<Coord> subShape : subShapePoints) {
MapShape s = shape.copy();
s.setPoints(subShape);
outputs.add(s);
}
}
Aggregations