Search in sources :

Example 91 with Coord

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);
        }
    }
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord)

Example 92 with Coord

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);
    }
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) MapLine(uk.me.parabola.mkgmap.general.MapLine) ArrayList(java.util.ArrayList)

Example 93 with Coord

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;
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) MapLine(uk.me.parabola.mkgmap.general.MapLine) ArrayList(java.util.ArrayList)

Example 94 with Coord

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);
    }
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) MapLine(uk.me.parabola.mkgmap.general.MapLine) ArrayList(java.util.ArrayList) MapRoad(uk.me.parabola.mkgmap.general.MapRoad) MapShape(uk.me.parabola.mkgmap.general.MapShape)

Example 95 with Coord

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;
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) ArrayList(java.util.ArrayList)

Aggregations

Coord (uk.me.parabola.imgfmt.app.Coord)178 ArrayList (java.util.ArrayList)71 Way (uk.me.parabola.mkgmap.reader.osm.Way)31 MapPoint (uk.me.parabola.mkgmap.general.MapPoint)27 List (java.util.List)23 MapLine (uk.me.parabola.mkgmap.general.MapLine)16 Area (uk.me.parabola.imgfmt.app.Area)15 MapShape (uk.me.parabola.mkgmap.general.MapShape)15 CoordNode (uk.me.parabola.imgfmt.app.CoordNode)13 MapExitPoint (uk.me.parabola.mkgmap.general.MapExitPoint)13 Node (uk.me.parabola.mkgmap.reader.osm.Node)13 HashMap (java.util.HashMap)12 IdentityHashMap (java.util.IdentityHashMap)11 Test (org.junit.Test)11 MapRoad (uk.me.parabola.mkgmap.general.MapRoad)9 Long2ObjectOpenHashMap (it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap)8 Area (java.awt.geom.Area)8 HashSet (java.util.HashSet)8 IntArrayList (it.unimi.dsi.fastutil.ints.IntArrayList)5 LinkedHashMap (java.util.LinkedHashMap)5