Rev 3408 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Copyright (C) 2007 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: Dec 1, 2007
*/
package uk.me.parabola.mkgmap.filters;
import java.util.ArrayList;
import java.util.List;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.mkgmap.general.MapElement;
import uk.me.parabola.mkgmap.general.MapLine;
/**
* This is a filter that smooths out lines at low resolutions. If the element
* has no size at all at the given resolution, then it is not passed on down
* the chain at all is excluded from the map at that resolution.
*
* @author Steve Ratcliffe
*/
public class SmoothingFilter
implements MapFilter
{
private static final int MIN_SPACING =
5;
private int shift
;
public void init
(FilterConfig config
) {
this.
shift = config.
getShift();
}
/**
* 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. We are assuming
* that there is not an excess of points at the highest resolution.
*
* <ol>
* <li>If there is just one point, the drop it.
* <li>Ff the element is too small altogether, then drop it.
* <li>If there are just two points the pass it on unchanged. This is
* probably a pretty common case.
* <li>The first point goes in unchanged.
* <li>Average points in groups so that they exceed the step size
* at the shifted resolution.
* </ol>
*
* @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
) {
MapLine line =
(MapLine
) element
;
// First off we don't touch things if at the highest level of detail
if (shift ==
0) {
next.
doFilter(element
);
return;
}
// If the line is not very long then just let it through. This is done
// mainly for the background polygons.
List<Coord
> points = line.
getPoints();
int n = points.
size();
if (n
<=
5) {
next.
doFilter(element
);
return;
}
// Create a new list to rewrite the points into.
List<Coord
> coords =
new ArrayList<Coord
>(n
);
// Get the step size, we want to place a point every time the
// average exceeds this size.
int stepsize = MIN_SPACING
<< shift
;
// Always add the first point
Coord last = points.
get(0);
coords.
add(last
);
// Average the rest
Average av =
new Average
(last, stepsize
);
for (int i =
1; i
< n
; i++
) {
Coord co = points.
get(i
);
av.
add(co
);
if (av.
isMoreThanStep()) {
Coord nco = av.
getAverageCoord();
coords.
add(nco
);
if (av.
pointCounter()>1) i--
;
last = nco
;
av.
reset(last
);
}
}
Coord end = points.
get(n -
1);
if (!last.
equals(end
))
coords.
add(end
);
MapLine newline = line.
copy();
newline.
setPoints(coords
);
next.
doFilter(newline
);
}
/**
* Class for averaging out points that are close together.
*/
private static class Average
{
private int count
;
private int startLat
;
private int startLon
;
private int avlat
;
private int avlon
;
private int step
;
private final int stepsize
;
Average
(Coord start,
int stepsize
) {
this.
startLat = start.
getLatitude();
this.
startLon = start.
getLongitude();
this.
stepsize = stepsize
;
}
public void add
(int lat,
int lon
) {
count++
;
this.
avlat += lat
;
this.
avlon += lon
;
step +=
Math.
abs(startLat - lat
);
step +=
Math.
abs(startLon - lon
);
}
public void reset
(Coord start
) {
this.
startLat = start.
getLatitude();
this.
startLon = start.
getLongitude();
step =
0;
count =
0;
avlat =
0;
avlon =
0;
}
public Coord getAverageCoord
() {
assert count
> 0;
return new Coord
(avlat / count, avlon / count
); // TODO high prec?
}
public void add
(Coord co
) {
add
(co.
getLatitude(), co.
getLongitude());
}
public boolean isMoreThanStep
() {
return (step
> stepsize
);
}
public int pointCounter
() {
return count
;
}
}
}