use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.
the class DouglasPeuckerFilter method douglasPeucker.
/**
* Reduces point density by Douglas-Peucker algorithm
*
* @param points The list of points to simplify.
* @param startIndex First index of segment. The point with this index will not be changed
* @param endIndex Last index of segment. The point with this index will not be changed
* @param allowedError Maximal allowed error to be introduced by simplification.
* returns number of removed points.
*/
protected void douglasPeucker(List<Coord> points, int startIndex, int endIndex, double allowedError) {
if (startIndex >= endIndex)
return;
// Highest distance
double maxDistance = 0;
// Index of highest distance
int maxIndex = endIndex;
Coord a = points.get(startIndex);
Coord b = points.get(endIndex);
// handle also closed or nearly closed lines and spikes on straight lines
for (int i = endIndex - 1; i > startIndex; i--) {
Coord p = points.get(i);
double distance = p.shortestDistToLineSegment(a, b);
if (distance > maxDistance) {
maxDistance = distance;
maxIndex = i;
}
}
if (maxDistance > allowedError) {
// Call recursive for both parts
douglasPeucker(points, maxIndex, endIndex, allowedError);
douglasPeucker(points, startIndex, maxIndex, allowedError);
} else {
// Remove the end-point if it is the same as the start point
if (a.highPrecEquals(b) && points.get(endIndex).preserved() == false)
endIndex++;
if (endIndex - startIndex > 4) {
// faster than many repeated remove actions
points.subList(startIndex + 1, endIndex).clear();
return;
}
// Remove the points in between
for (int i = endIndex - 1; i > startIndex; i--) {
points.remove(i);
}
}
}
use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.
the class DouglasPeuckerFilter method doFilter.
/**
* This applies to both lines and polygons. We are going to smooth out
* the points in the line so that you do not get jaggies.
*
* @param element A map element that will be a line or a polygon.
* @param next This is used to pass the possibly transformed element onward.
*/
public void doFilter(MapElement element, MapFilterChain next) {
// First off we don't touch things if at the highest level of detail
if (resolution == 24) {
// XXX 24 is not necessarily the highest level.
next.doFilter(element);
return;
}
MapLine line = (MapLine) element;
List<Coord> points = line.getPoints();
// Create a new list to rewrite the points into. Don't alter the original one
List<Coord> coords = new ArrayList<>(points.size());
coords.addAll(points);
// Loop runs downwards, as the list length gets modified while running
int endIndex = coords.size() - 1;
for (int i = endIndex - 1; i > 0; i--) {
Coord p = coords.get(i);
// TODO: Should consider only nodes connected to roads visible at current resolution.
if (p.preserved()) {
// point is "preserved", don't remove it
douglasPeucker(coords, i, endIndex, maxErrorDistance);
endIndex = i;
}
}
// Simplify the rest
douglasPeucker(coords, 0, endIndex, maxErrorDistance);
if (coords.size() == points.size())
// nothing changed, no need to copy
next.doFilter(line);
else {
MapLine newline = line.copy();
newline.setPoints(coords);
next.doFilter(newline);
}
}
use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.
the class LineMergeFilter method merge.
// TODO: This routine has a side effect: it modifies some of the MapLine instances
// instead of creating copies. It seems that this has no bad effect, but it is not clean
public List<MapLine> merge(List<MapLine> lines, int res) {
// better use LinkedList??
linesMerged = new ArrayList<MapLine>(lines.size());
for (MapLine line : lines) {
if (line.getMinResolution() > res || line.getMaxResolution() < res)
continue;
if (line.isRoad()) {
linesMerged.add(line);
continue;
}
boolean isMerged = false;
List<Coord> points = line.getPoints();
Coord start = points.get(0);
Coord end = points.get(points.size() - 1);
// (can the end of current line connected to an existing line?)
for (MapLine line2 : startPoints.get(end)) {
if (line.isSimilar(line2)) {
addPointsAtStart(line2, points);
// both lines has to be merged and one of them dropped)
for (MapLine line1 : endPoints.get(start)) {
if (line2.isSimilar(line1) && // don't make a closed loop a double loop
!line2.equals(line1)) {
mergeLines(line1, line2);
break;
}
}
isMerged = true;
break;
}
}
if (isMerged)
continue;
// (can the start of current line connected to an existing line?)
for (MapLine line2 : endPoints.get(start)) {
if (line.isSimilar(line2)) {
addPointsAtEnd(line2, points);
isMerged = true;
break;
}
}
if (isMerged)
continue;
// No matching, create a copy of line
MapLine l = line.copy();
// use better LinkedList for performance?
List<Coord> p = new ArrayList<Coord>(line.getPoints());
l.setPoints(p);
addLine(l);
}
return linesMerged;
}
use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.
the class LineSizeSplitterFilter method doFilter.
/**
* Keep track of the max dimensions of a line and split when they get too
* big.
*
* @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;
if (line.getBounds().getMaxDimension() < maxSize) {
next.doFilter(element);
return;
}
if (line instanceof MapRoad) {
MapRoad road = ((MapRoad) line);
log.error("Way " + road.getRoadDef() + " has a max dimension of " + line.getBounds().getMaxDimension() + " and is about to be split (routing will be broken)");
}
// ensure that all single lines do not exceed the maximum size
// use a slightly decreased max size (-10) to get better results
// in the subdivision creation
List<Coord> points = splitLinesToMaxSize(line.getPoints(), maxSize - 10);
log.debug("line bbox too big, splitting");
MapLine l = line.copy();
List<Coord> coords = new ArrayList<Coord>();
boolean first = true;
/**
* Class to keep track of the dimensions.
*/
class Dim {
private int minLat;
private int minLong;
private int maxLat;
private int maxLong;
Dim() {
reset();
}
private void reset() {
minLat = Integer.MAX_VALUE;
minLong = Integer.MAX_VALUE;
maxLat = Integer.MIN_VALUE;
maxLong = Integer.MIN_VALUE;
}
private void addToBounds(Coord co) {
int lat = co.getLatitude();
if (lat < minLat)
minLat = lat;
if (lat > maxLat)
maxLat = lat;
int lon = co.getLongitude();
if (lon < minLong)
minLong = lon;
if (lon > maxLong)
maxLong = lon;
}
private int getMaxDim() {
int dx = maxLong - minLong;
int dy = maxLat - minLat;
return Math.max(dx, dy);
}
}
Dim dim = new Dim();
Coord prev = null;
// Add points while not too big and then start again with a fresh line.
for (Coord co : points) {
dim.addToBounds(co);
if (dim.getMaxDim() > maxSize) {
if (first)
log.debug("bigness saving first part");
else
log.debug("bigness saving next part");
l.setPoints(coords);
next.doFilter(l);
l = line.copy();
first = false;
dim.reset();
coords = new ArrayList<Coord>();
coords.add(prev);
dim.addToBounds(prev);
dim.addToBounds(co);
}
coords.add(co);
prev = co;
}
assert coords.size() > 1;
if (coords.size() > 1) {
log.debug("bigness saving a final part");
l.setPoints(coords);
next.doFilter(l);
}
}
use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.
the class LineSizeSplitterFilter method splitLinesToMaxSize.
/**
* If two points of a line are too far from each other, add points between them
* so that the bounding box of each pair of points is smaller than the allowed
* maximum.
* @param coords the list of points
* @param maxSize the allowed bounding box height and width
* @return a reference to a new list of points
*/
private static List<Coord> splitLinesToMaxSize(List<Coord> coords, int maxSize) {
List<Coord> testedCoords = new ArrayList<Coord>(coords);
int posToTest = coords.size() - 2;
while (posToTest >= 0) {
Coord p1 = testedCoords.get(posToTest);
Coord p2 = testedCoords.get(posToTest + 1);
int width = Math.abs(p1.getLongitude() - p2.getLongitude());
int height = Math.abs(p1.getLatitude() - p2.getLatitude());
if (width > maxSize || height > maxSize) {
testedCoords.add(posToTest + 1, p1.makeBetweenPoint(p2, 0.5));
++posToTest;
} else
--posToTest;
}
return testedCoords;
}
Aggregations