Search in sources :

Example 86 with Coord

use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.

the class ShapeMergeFilter method findCommonCoords.

/**
 * Find the common Coord instances and save their positions for both shapes.
 * @param s1 shape 1
 * @param s2 shape 2
 * @param s1PositionsToCheck will contain common positions in shape 1
 * @param s2PositionsToCheck will contain common positions in shape 2
 */
private static void findCommonCoords(List<Coord> s1, List<Coord> s2, IntArrayList s1PositionsToCheck, IntArrayList s2PositionsToCheck) {
    Map<Coord, Integer> s2PosMap = new IdentityHashMap<>(s2.size() - 1);
    for (int i = 0; i + 1 < s1.size(); i++) {
        Coord co = s1.get(i);
        co.setPartOfShape2(false);
    }
    for (int i = 0; i + 1 < s2.size(); i++) {
        Coord co = s2.get(i);
        co.setPartOfShape2(true);
        s2PosMap.put(co, i);
    }
    int start = 0;
    while (start < s1.size()) {
        Coord co = s1.get(start);
        if (!co.isPartOfShape2())
            break;
        start++;
    }
    int pos = start + 1;
    int tested = 0;
    while (true) {
        if (pos + 1 >= s1.size())
            pos = 0;
        Coord co = s1.get(pos);
        if (++tested >= s1.size())
            break;
        if (co.isPartOfShape2()) {
            s1PositionsToCheck.add(pos);
            Integer posInSh2 = s2PosMap.get(co);
            assert posInSh2 != null;
            s2PositionsToCheck.add(posInSh2);
        }
        pos++;
    }
    return;
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) IdentityHashMap(java.util.IdentityHashMap)

Example 87 with Coord

use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.

the class ShapeMergeFilter method tryMerge.

/**
 * Merge ShapeHelpers. Calls itself recursively.
 * @param pattern a MapShape
 * @param similarShapes {@link ShapeHelper} instances created from similar {@link MapShape}.
 * This list is modified if shapes were merged.
 */
private void tryMerge(MapShape pattern, List<ShapeHelper> similarShapes) {
    if (similarShapes.size() <= 1)
        return;
    List<ShapeHelper> noMerge = new ArrayList<>();
    BitSet toMerge = new BitSet(similarShapes.size());
    // abuse highway count to find identical points in different shapes
    similarShapes.forEach(sh -> sh.getPoints().forEach(Coord::resetHighwayCount));
    similarShapes.forEach(sh -> sh.getPoints().forEach(Coord::incHighwayCount));
    // decrement counter for duplicated start/end node
    similarShapes.forEach(sh -> sh.getPoints().get(0).decHighwayCount());
    // points with count > 1 are probably shared by different shapes, collect the shapes
    IdentityHashMap<Coord, BitSet> coord2Shape = new IdentityHashMap<>();
    BitSet[] candidates = new BitSet[similarShapes.size()];
    for (int i = 0; i < similarShapes.size(); i++) {
        ShapeHelper sh0 = similarShapes.get(i);
        List<Coord> sharedPoints = new ArrayList<>();
        for (int j = 1; j < sh0.getPoints().size(); j++) {
            Coord c = sh0.getPoints().get(j);
            if (c.getHighwayCount() > 1) {
                sharedPoints.add(c);
            }
        }
        if (sharedPoints.size() == 0 || sh0.getPoints().size() - sharedPoints.size() > PolygonSplitterFilter.MAX_POINT_IN_ELEMENT) {
            // merge will not work
            noMerge.add(sh0);
            continue;
        }
        assert candidates[i] == null;
        candidates[i] = new BitSet();
        BitSet curr = candidates[i];
        curr.set(i);
        toMerge.set(i);
        for (Coord c : sharedPoints) {
            BitSet set = coord2Shape.get(c);
            if (set == null) {
                set = new BitSet();
                coord2Shape.put(c, set);
            } else {
                for (int j = set.nextSetBit(0); j >= 0; j = set.nextSetBit(j + 1)) {
                    candidates[j].set(i);
                }
                curr.or(set);
            }
            set.set(i);
        }
    }
    if (coord2Shape.isEmpty()) {
        // nothing to do
        return;
    }
    List<ShapeHelper> next = new ArrayList<>();
    boolean merged = false;
    BitSet done = new BitSet();
    BitSet delayed = new BitSet();
    for (int i = toMerge.nextSetBit(0); i >= 0; i = toMerge.nextSetBit(i + 1)) {
        if (done.get(i))
            continue;
        BitSet all = candidates[i];
        if (all.cardinality() <= 1) {
            if (!all.isEmpty())
                delayed.set(i);
            continue;
        }
        all.andNot(done);
        if (all.isEmpty())
            continue;
        List<ShapeHelper> result = new ArrayList<>();
        for (int j = all.nextSetBit(0); j >= 0; j = all.nextSetBit(j + 1)) {
            ShapeHelper sh = similarShapes.get(j);
            int oldSize = result.size();
            result = addWithConnectedHoles(result, sh, pattern.getType());
            if (result.size() < oldSize + 1) {
                merged = true;
                log.debug("shape with id", sh.id, "was merged", (oldSize + 1 - result.size()), " time(s) at resolution", resolution);
            }
        }
        // XXX : not exact, there may be other combinations of shapes which can be merged
        // e.g. merge of shape 1 + 2 may not work but 2 and 3 could still be candidates.
        done.or(all);
        next.addAll(result);
    }
    delayed.andNot(done);
    if (!delayed.isEmpty()) {
        for (int i = delayed.nextSetBit(0); i >= 0; i = delayed.nextSetBit(i + 1)) {
            noMerge.add(similarShapes.get(i));
        }
    }
    similarShapes.clear();
    similarShapes.addAll(noMerge);
    if (merged)
        tryMerge(pattern, next);
    // Maybe add final step which calls addWithConnectedHoles for all remaining shapes
    // this will find a few more merges but is still slow for maps with lots of islands
    similarShapes.addAll(next);
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) IdentityHashMap(java.util.IdentityHashMap) ArrayList(java.util.ArrayList) IntArrayList(it.unimi.dsi.fastutil.ints.IntArrayList) BitSet(java.util.BitSet)

Example 88 with Coord

use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.

the class ShapeMergeFilter method tryMerge.

/**
 * Find out if two shapes have common points. If yes, merge them.
 * @param sh1 1st shape1
 * @param sh2 2st shape2
 * @return merged shape or 1st shape if no common point found or {@code dupShape}
 * if both shapes describe the same area.
 */
private ShapeHelper tryMerge(ShapeHelper sh1, ShapeHelper sh2) {
    // both clockwise or both ccw ?
    boolean sameDir = sh1.areaTestVal > 0 && sh2.areaTestVal > 0 || sh1.areaTestVal < 0 && sh2.areaTestVal < 0;
    List<Coord> points1, points2;
    if (sh2.getPoints().size() > sh1.getPoints().size()) {
        points1 = sh2.getPoints();
        points2 = sh1.getPoints();
    } else {
        points1 = sh1.getPoints();
        points2 = sh2.getPoints();
    }
    // find all coords that are common in the two shapes
    IntArrayList sh1PositionsToCheck = new IntArrayList();
    IntArrayList sh2PositionsToCheck = new IntArrayList();
    findCommonCoords(points1, points2, sh1PositionsToCheck, sh2PositionsToCheck);
    if (sh1PositionsToCheck.isEmpty()) {
        return sh1;
    }
    if (sh2PositionsToCheck.size() + 1 >= points2.size()) {
        // or a piece that fills a hole
        if (points1.size() == points2.size() && Math.abs(sh1.areaTestVal) == Math.abs(sh2.areaTestVal)) {
            // XXX this might fail if one of the shapes is self intersecting
            return DUP_SHAPE;
        }
    }
    List<Coord> merged = null;
    if (points1.size() + points2.size() - 2 * sh1PositionsToCheck.size() < PolygonSplitterFilter.MAX_POINT_IN_ELEMENT) {
        merged = mergeLongestSequence(points1, points2, sh1PositionsToCheck, sh2PositionsToCheck, sameDir);
        if (merged.isEmpty())
            return DUP_SHAPE;
        if (merged.get(0) != merged.get(merged.size() - 1))
            merged = null;
        else if (merged.size() > PolygonSplitterFilter.MAX_POINT_IN_ELEMENT) {
            // don't merge because merged polygon would be split again
            log.info("merge rejected: merged shape has too many points " + merged.size());
            merged = null;
        }
    }
    ShapeHelper shm = null;
    if (merged != null) {
        shm = new ShapeHelper(merged);
        if (Math.abs(shm.areaTestVal) != Math.abs(sh1.areaTestVal) + Math.abs(sh2.areaTestVal)) {
            log.warn("merging shapes skipped for shapes near", points1.get(sh1PositionsToCheck.getInt(0)).toOSMURL(), "(maybe overlapping shapes?)");
            merged = null;
            shm = null;
        } else {
            if (log.isInfoEnabled()) {
                log.info("merge of shapes near", points1.get(sh1PositionsToCheck.getInt(0)).toOSMURL(), "reduces number of points from", (points1.size() + points2.size()), "to", merged.size());
            }
        }
    }
    if (shm != null)
        return shm;
    if (merged == null)
        return sh1;
    return null;
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) IntArrayList(it.unimi.dsi.fastutil.ints.IntArrayList)

Example 89 with Coord

use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.

the class ShapeMergeFilter method mergeSimilar.

/**
 * Merge similar shapes.
 * @param similar list of similar shapes
 * @param mergedShapes list to which the shapes are added
 */
private void mergeSimilar(List<MapShape> similar, List<MapShape> mergedShapes) {
    if (similar.size() == 1) {
        mergedShapes.addAll(similar);
        return;
    }
    List<ShapeHelper> list = new ArrayList<>();
    MapShape s1 = similar.get(0);
    for (MapShape ms : similar) {
        ShapeHelper sh = new ShapeHelper(ms.getPoints());
        sh.id = ms.getOsmid();
        list.add(sh);
    }
    tryMerge(s1, list);
    for (ShapeHelper sh : list) {
        MapShape newShape = s1.copy();
        assert sh.getPoints().get(0) == sh.getPoints().get(sh.getPoints().size() - 1);
        if (sh.id == 0) {
            // this shape is the result of a merge
            List<Coord> optimizedPoints = WrongAngleFixer.fixAnglesInShape(sh.getPoints());
            if (optimizedPoints.isEmpty())
                continue;
            newShape.setPoints(optimizedPoints);
            newShape.setOsmid(FakeIdGenerator.makeFakeId());
        } else {
            newShape.setPoints(sh.getPoints());
            newShape.setOsmid(sh.id);
        }
        mergedShapes.add(newShape);
    }
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) ArrayList(java.util.ArrayList) IntArrayList(it.unimi.dsi.fastutil.ints.IntArrayList) MapShape(uk.me.parabola.mkgmap.general.MapShape)

Example 90 with Coord

use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.

the class ShapeMergeFilter method calcAreaSizeTestVal.

/**
 * Calculate the high precision area size test value.
 * @param points
 * @return area size in high precision map units * 2.
 * The value is >= 0 if the shape is clockwise, else < 0
 */
public static long calcAreaSizeTestVal(List<Coord> points) {
    if (points.size() < 4)
        // straight line cannot enclose an area
        return 0;
    if (points.get(0).highPrecEquals(points.get(points.size() - 1)) == false) {
        log.error("shape is not closed");
        return 0;
    }
    Iterator<Coord> polyIter = points.iterator();
    Coord c2 = polyIter.next();
    long signedAreaSize = 0;
    while (polyIter.hasNext()) {
        Coord c1 = c2;
        c2 = polyIter.next();
        signedAreaSize += (long) (c2.getHighPrecLon() + c1.getHighPrecLon()) * (c1.getHighPrecLat() - c2.getHighPrecLat());
    }
    if (Math.abs(signedAreaSize) < SINGLE_POINT_AREA) {
        log.debug("very small shape near", points.get(0).toOSMURL(), "signed area in high prec map units:", signedAreaSize);
    }
    return signedAreaSize;
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord)

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