Search in sources :

Example 1 with HousenumberMatchByPosComparator

use of uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByPosComparator in project mkgmap by openstreetmap.

the class ExtNumbers method splitInterval.

/**
 * Split an interval to remove overlaps
 * 1) detect the optimal split position
 * 2) calculate the new intervals
 * @return
 */
private ExtNumbers splitInterval() {
    if (log.isDebugEnabled())
        log.debug("trying to split", this, "so that", badNum, "is not contained");
    boolean doSplit = false;
    Numbers origNumbers = getNumbers();
    if (origNumbers.countMatches(badNum) == 0) {
        if (log.isDebugEnabled())
            log.debug("badNum", badNum, "is not contained in", this);
        return this;
    }
    // create an test interval to find out which side contains the  bad number
    Numbers testNumbers = new Numbers();
    testNumbers.setNumbers(Numbers.LEFT, origNumbers.getLeftNumberStyle(), origNumbers.getLeftStart(), origNumbers.getLeftEnd());
    boolean left = (testNumbers.countMatches(badNum) > 0);
    List<HousenumberMatch> before = new ArrayList<>();
    List<HousenumberMatch> after = new ArrayList<>();
    List<HousenumberMatch> toSplit = getHouses(left);
    boolean inc = (origNumbers.getEnd(left) > origNumbers.getStart(left));
    BitSet segmentsBefore = new BitSet();
    BitSet segmentsAfter = new BitSet();
    for (HousenumberMatch house : toSplit) {
        List<HousenumberMatch> target;
        if (house.getHousenumber() < badNum) {
            target = inc ? before : after;
        } else if (house.getHousenumber() > badNum) {
            target = inc ? after : before;
        } else {
            int s = origNumbers.getStart(left);
            target = (s == badNum) ? before : after;
        }
        target.add(house);
        if (target == before) {
            segmentsBefore.set(house.getSegment());
        } else {
            segmentsAfter.set(house.getSegment());
        }
    }
    if (before.isEmpty() || after.isEmpty())
        return this;
    if (log.isDebugEnabled())
        log.debug("todo: find best method to separate", before, "and", after);
    HousenumberMatch house1 = before.get(before.size() - 1);
    HousenumberMatch house2 = after.get(0);
    List<HousenumberMatch> testOrder = new ArrayList<>();
    testOrder.add(house1);
    testOrder.add(house2);
    Collections.sort(testOrder, new HousenumberMatchByPosComparator());
    if (testOrder.get(0) != house1) {
        log.info("order indicates random case or missing road!", this);
        housenumberRoad.setRandom(true);
    }
    int splitSegment = -1;
    if (house1.getSegment() != house2.getSegment()) {
        // simple case: change point
        if (log.isDebugEnabled())
            log.debug("simple case: change point to number node between", house1, house2);
        // what point is best?, use beginning of 2nd for now
        splitSegment = house2.getSegment();
        doSplit = true;
    } else {
        int seg = house1.getSegment();
        Coord c1 = getRoad().getPoints().get(seg);
        Coord c2 = getRoad().getPoints().get(seg + 1);
        double segmentLength = c1.distance(c2);
        Coord toAdd = null;
        boolean addOK = true;
        double wantedFraction = (house1.getSegmentFrac() + house2.getSegmentFrac()) / 2;
        // handle cases where perpendicular is not on the road
        if (wantedFraction <= 0) {
            wantedFraction = 0;
            toAdd = new Coord(c1);
        } else if (wantedFraction >= 1) {
            wantedFraction = 1;
            toAdd = new Coord(c2);
        }
        double usedFraction = wantedFraction;
        if (toAdd == null) {
            Coord wanted = c1.makeBetweenPoint(c2, wantedFraction);
            log.debug("possible solution: split segment with length", formatLen(segmentLength), "near", formatLen(wantedFraction * segmentLength));
            toAdd = rasterLineNearPoint(c1, c2, wanted, true);
            if (toAdd != null) {
                if (toAdd.equals(c1)) {
                    toAdd = new Coord(c1);
                    usedFraction = 0.0;
                } else if (toAdd.equals(c2)) {
                    toAdd = new Coord(c2);
                    usedFraction = 0;
                } else {
                    addOK = checkLineDistortion(c1, c2, toAdd);
                    if (addOK)
                        usedFraction = HousenumberGenerator.getFrac(c1, c2, toAdd);
                    else
                        toAdd = null;
                }
            }
        }
        if (toAdd == null) {
            double len1 = wantedFraction * segmentLength;
            double len2 = (1 - wantedFraction) * segmentLength;
            if (Math.min(len1, len2) < MAX_LOCATE_ERROR) {
                if (len1 < len2) {
                    toAdd = new Coord(c1);
                    usedFraction = 0.0;
                } else {
                    toAdd = new Coord(c2);
                    usedFraction = 1.0;
                }
            }
        }
        if (toAdd == null) {
            log.error("internal error, cannot split", this);
        }
        if (toAdd != null) {
            if (log.isDebugEnabled()) {
                log.debug("solution: split segment with length", formatLen(segmentLength), "at", formatLen(usedFraction * segmentLength));
                double distToLine = toAdd.getDisplayedCoord().distToLineSegment(c1.getDisplayedCoord(), c2.getDisplayedCoord());
                log.info("adding number node at", toAdd.toDegreeString(), "to split, dist to line is", formatLen(distToLine));
            }
            doSplit = true;
            splitSegment = seg + 1;
            addAsNumberNode(splitSegment, toAdd);
            this.endInRoad++;
            for (HousenumberMatch house : before) {
                if (house.getSegment() >= seg) {
                    HousenumberGenerator.findClosestRoadSegment(house, getRoad(), seg, splitSegment);
                }
            }
            for (HousenumberMatch house : after) {
                if (house.getSegment() < splitSegment)
                    HousenumberGenerator.findClosestRoadSegment(house, getRoad(), splitSegment, splitSegment + 1);
                else
                    house.setSegment(house.getSegment() + 1);
            }
            // the other side
            recalcHousePositions(getHouses(!left));
        }
    }
    if (doSplit) {
        ExtNumbers en1 = split(splitSegment);
        ExtNumbers en2 = en1.next;
        if (en1.getHouses(Numbers.LEFT).size() + en2.getHouses(Numbers.LEFT).size() != getHouses(Numbers.LEFT).size() || en1.getHouses(Numbers.RIGHT).size() + en2.getHouses(Numbers.RIGHT).size() != getHouses(Numbers.RIGHT).size()) {
            log.error("internal error, lost houses");
        }
        log.info("number node added in street", getRoad(), getNumbers(), "==>", en1.getNumbers(), "+", en2.getNumbers());
        return en1;
    }
    return this;
}
Also used : Numbers(uk.me.parabola.imgfmt.app.net.Numbers) Coord(uk.me.parabola.imgfmt.app.Coord) HousenumberMatchByPosComparator(uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByPosComparator) ArrayList(java.util.ArrayList) BitSet(java.util.BitSet)

Example 2 with HousenumberMatchByPosComparator

use of uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByPosComparator in project mkgmap by openstreetmap.

the class HousenumberRoad method buildIntervals.

public void buildIntervals() {
    Collections.sort(houseNumbers, new HousenumberMatchByNumComparator());
    if (log.isInfoEnabled())
        log.info("Initial housenumbers for", road, "in", road.getCity(), houseNumbers);
    filterRealDuplicates();
    filterGroups();
    if (houseNumbers.isEmpty())
        return;
    List<HousenumberMatch> leftNumbers = new ArrayList<HousenumberMatch>();
    List<HousenumberMatch> rightNumbers = new ArrayList<HousenumberMatch>();
    for (HousenumberMatch house : houseNumbers) {
        if (house.getRoad() == null || house.isIgnored()) {
            continue;
        }
        if (house.getHousenumberRoad() != this || house.getHousenumberRoad().getRoad() != house.getRoad()) {
            log.error("internal error, road links are not correct", house.toBrowseURL());
        }
        if (house.isLeft()) {
            leftNumbers.add(house);
        } else {
            rightNumbers.add(house);
        }
    }
    detectGroups(leftNumbers, rightNumbers);
    Collections.sort(leftNumbers, new HousenumberMatchByPosComparator());
    Collections.sort(rightNumbers, new HousenumberMatchByPosComparator());
    int currNodePos = 0;
    int nodeIndex = 0;
    int prevNumberNodeIndex = 0;
    int prevNodePos = 0;
    extNumbersHead = null;
    ExtNumbers currNumbers = null;
    for (Coord p : road.getPoints()) {
        if (currNodePos == 0) {
            if (road.skipAddToNOD() == false)
                assert p instanceof CoordNode;
        }
        // An ordinary point in the road.
        if (p.isNumberNode() == false) {
            currNodePos++;
            continue;
        }
        // The first time round, this is guaranteed to be a CoordNode
        if (currNodePos == 0) {
            nodeIndex++;
            currNodePos++;
            continue;
        }
        // Now we have a CoordNode and it is not the first one.
        ExtNumbers numbers = new ExtNumbers(this);
        numbers.setNodeIndex(prevNumberNodeIndex);
        int leftUsed = numbers.setNumbers(leftNumbers, prevNodePos, currNodePos, true);
        int rightUsed = numbers.setNumbers(rightNumbers, prevNodePos, currNodePos, false);
        prevNodePos = currNodePos;
        // maintain chain
        numbers.prev = currNumbers;
        if (currNumbers != null)
            currNumbers.next = numbers;
        else {
            extNumbersHead = numbers;
        }
        currNumbers = numbers;
        leftNumbers.subList(0, leftUsed).clear();
        rightNumbers.subList(0, rightUsed).clear();
        prevNumberNodeIndex = nodeIndex;
        nodeIndex++;
        currNodePos++;
    }
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) HousenumberMatchByPosComparator(uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByPosComparator) HousenumberMatchByNumComparator(uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByNumComparator) ArrayList(java.util.ArrayList) CoordNode(uk.me.parabola.imgfmt.app.CoordNode)

Example 3 with HousenumberMatchByPosComparator

use of uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByPosComparator in project mkgmap by openstreetmap.

the class ExtNumbers method checkIntervals.

/**
 * Check if two intervals are overlapping  (all combinations of left + right)
 * @param streetName
 * @param en1
 * @param en2
 * @return true if something was changed
 */
public static int checkIntervals(String streetName, ExtNumbers en1, ExtNumbers en2) {
    if (en1.getRoad() != en2.getRoad()) {
        Coord cs1 = en1.getRoad().getPoints().get(en1.startInRoad);
        Coord ce1 = en1.getRoad().getPoints().get(en1.endInRoad);
        Coord ce2 = en2.getRoad().getPoints().get(en2.endInRoad);
        if (ce2 == cs1 || ce2 == ce1) {
            ExtNumbers help = en1;
            en1 = en2;
            en2 = help;
        }
    }
    boolean allOK = true;
    Numbers ivl1 = en1.getNumbers();
    Numbers ivl2 = en2.getNumbers();
    for (int i = 0; i < 2; i++) {
        boolean left1 = i == 0;
        NumberStyle style1 = ivl1.getNumberStyle(left1);
        if (style1 == NumberStyle.NONE)
            continue;
        int s1 = ivl1.getStart(left1);
        int e1 = ivl1.getEnd(left1);
        for (int j = 0; j < 2; j++) {
            boolean left2 = (j == 0);
            NumberStyle style2 = ivl2.getNumberStyle(left2);
            if (style2 == NumberStyle.NONE)
                continue;
            int s2 = ivl2.getStart(left2);
            int e2 = ivl2.getEnd(left2);
            boolean ok = true;
            if (style1 == style2 || style1 == NumberStyle.BOTH || style2 == NumberStyle.BOTH)
                ok = checkIntervalBoundaries(s1, e1, s2, e2, left1 == left2 && en1.getRoad() == en2.getRoad());
            if (ok)
                continue;
            if (en1.getRoad() != en2.getRoad() && en1.hasGaps == false && en2.hasGaps == false) {
                allOK = false;
                continue;
            }
            if (s1 == e1) {
                if (en1.getHouses(left1).get(0).isFarDuplicate()) {
                    allOK = false;
                    continue;
                }
            }
            if (s2 == e2) {
                if (en2.getHouses(left2).get(0).isFarDuplicate()) {
                    allOK = false;
                    continue;
                }
            }
            List<HousenumberMatch> houses1 = en1.getHouses(left1);
            List<HousenumberMatch> houses2 = en2.getHouses(left2);
            if (log.isInfoEnabled()) {
                log.info("detected unplausible combination of intervals in", streetName, s1 + ".." + e1, "and", s2 + ".." + e2, "houses:", (left1 ? "left:" : "right"), houses1, (left2 ? "left:" : "right"), houses2, (en1.getRoad() == en2.getRoad() ? "in road " + en1.getRoad() : "road id(s):" + en1.getRoad().getRoadDef().getId() + ", " + en2.getRoad().getRoadDef().getId()));
            }
            double smallestDelta = Double.POSITIVE_INFINITY;
            HousenumberMatch bestMoveOrig = null;
            HousenumberMatch bestMoveMod = null;
            ExtNumbers bestRemove = null;
            List<HousenumberMatch> possibleRemoves1 = new ArrayList<>();
            List<HousenumberMatch> possibleRemoves2 = new ArrayList<>();
            if (en1.housenumberRoad.isRandom() == false && en2.housenumberRoad.isRandom() == false) {
                // check if we can move a house from en1 to en2
                for (HousenumberMatch house : houses1) {
                    if (house.getGroup() != null)
                        continue;
                    int n = house.getHousenumber();
                    if (countOccurence(houses1, n) > 1)
                        continue;
                    if (n == s1 || n == e1) {
                        Numbers modNumbers = en1.simulateRemovalOfHouseNumber(n, left1).getNumbers();
                        int s1Mod = modNumbers.getStart(left1);
                        int e1Mod = modNumbers.getEnd(left1);
                        NumberStyle modStyle = modNumbers.getNumberStyle(left1);
                        boolean ok2 = true;
                        if (modStyle == style2 || modStyle == NumberStyle.BOTH || style2 == NumberStyle.BOTH)
                            ok2 = checkIntervalBoundaries(s1Mod, e1Mod, s2, e2, left1 == left2 && en1.getRoad() == en2.getRoad());
                        if (ok2) {
                            // the intervals don't overlap if house is removed from en1
                            if (houses1.size() > 1)
                                possibleRemoves1.add(house);
                            // check if it fits into en2
                            HousenumberMatch test = checkMoveTo(house, en2, left2);
                            if (test.getRoad() != null) {
                                double deltaDist = test.getDistance() - house.getDistance();
                                if (deltaDist < smallestDelta) {
                                    bestMoveMod = test;
                                    bestMoveOrig = house;
                                    smallestDelta = deltaDist;
                                    bestRemove = en1;
                                }
                            }
                        }
                    }
                }
                for (HousenumberMatch house : houses2) {
                    if (house.getGroup() != null)
                        continue;
                    int n = house.getHousenumber();
                    if (countOccurence(houses2, n) > 1)
                        continue;
                    if (n == s2 || n == e2) {
                        Numbers modNumbers = en2.simulateRemovalOfHouseNumber(n, left2).getNumbers();
                        int s2Mod = modNumbers.getStart(left2);
                        int e2Mod = modNumbers.getEnd(left2);
                        NumberStyle modStyle = modNumbers.getNumberStyle(left2);
                        boolean ok2 = true;
                        if (modStyle == style1 || modStyle == NumberStyle.BOTH || style1 == NumberStyle.BOTH)
                            ok2 = checkIntervalBoundaries(s1, e1, s2Mod, e2Mod, left1 == left2 && en1.getRoad() == en2.getRoad());
                        if (ok2) {
                            // the intervals don't overlap if house is removed from en2
                            if (houses2.size() > 1)
                                possibleRemoves2.add(house);
                            // check if it fits into en1
                            HousenumberMatch test = checkMoveTo(house, en1, left1);
                            if (test.getRoad() != null) {
                                double deltaDist = test.getDistance() - house.getDistance();
                                if (deltaDist < smallestDelta) {
                                    bestMoveMod = test;
                                    bestMoveOrig = house;
                                    smallestDelta = deltaDist;
                                    bestRemove = en2;
                                }
                            }
                        }
                    }
                }
                if (bestMoveMod != null) {
                    if (bestMoveOrig.isDuplicate()) {
                        log.warn("duplicate number causes problems", streetName, bestMoveOrig.getSign(), bestMoveOrig.toBrowseURL());
                    }
                    List<HousenumberMatch> fromHouses, toHouses;
                    ExtNumbers from, to;
                    if (bestRemove == en1) {
                        from = en1;
                        to = en2;
                        fromHouses = houses1;
                        toHouses = houses2;
                        bestMoveOrig.setLeft(left2);
                    } else {
                        from = en2;
                        to = en1;
                        fromHouses = houses2;
                        toHouses = houses1;
                        bestMoveOrig.setLeft(left1);
                    }
                    if (bestMoveOrig.getMoved() >= 3) {
                        bestMoveMod = null;
                        bestMoveOrig = null;
                        bestRemove.housenumberRoad.setRandom(true);
                    } else {
                        if (log.isInfoEnabled()) {
                            if (to.getRoad() == from.getRoad())
                                log.info("moving", streetName, bestMoveOrig.getSign(), bestMoveOrig.getElement().toBrowseURL(), "from", fromHouses, "to", toHouses, "in road", to.getRoad());
                            else
                                log.info("moving", streetName, bestMoveOrig.getSign(), bestMoveOrig.getElement().toBrowseURL(), "from", fromHouses, "in road", from.getRoad(), "to", toHouses, "in road", to.getRoad());
                        }
                        bestMoveOrig.incMoved();
                        bestMoveOrig.setRoad(to.getRoad());
                        bestMoveOrig.setHousenumberRoad(to.housenumberRoad);
                        bestMoveOrig.setSegment(bestMoveMod.getSegment());
                        bestMoveOrig.setDistance(bestMoveMod.getDistance());
                        bestMoveOrig.setSegmentFrac(bestMoveMod.getSegmentFrac());
                        from.housenumberRoad.getHouses().remove(bestMoveOrig);
                        fromHouses.remove(bestMoveOrig);
                        toHouses.add(bestMoveOrig);
                        Collections.sort(toHouses, new HousenumberMatchByPosComparator());
                        en1.reset();
                        en2.reset();
                        en1.setNumbers(houses1, en1.startInRoad, en1.endInRoad, left1);
                        en2.setNumbers(houses2, en2.startInRoad, en2.endInRoad, left2);
                        return OK_AFTER_CHANGES;
                    }
                }
            }
            ExtNumbers toSplit = null;
            int splitNum = -1;
            int delta1 = Math.abs(e1 - s1);
            int delta2 = Math.abs(e2 - s2);
            if (delta1 > 0 && delta2 > 0) {
                if (en1.hasGaps != en2.hasGaps) {
                    if (en1.hasGaps) {
                        if (possibleRemoves1.isEmpty() == false)
                            splitNum = possibleRemoves1.get(0).getHousenumber();
                        toSplit = en1;
                    } else {
                        if (possibleRemoves2.isEmpty() == false)
                            splitNum = possibleRemoves2.get(0).getHousenumber();
                        toSplit = en2;
                    }
                } else if (possibleRemoves1.size() == 1) {
                    splitNum = possibleRemoves1.get(0).getHousenumber();
                    toSplit = en1;
                } else if (possibleRemoves2.size() == 1) {
                    splitNum = possibleRemoves2.get(0).getHousenumber();
                    toSplit = en2;
                } else if (possibleRemoves1.size() > 0) {
                    splitNum = possibleRemoves1.get(0).getHousenumber();
                    toSplit = en1;
                } else if (possibleRemoves2.size() > 0) {
                    splitNum = possibleRemoves2.get(0).getHousenumber();
                    toSplit = en2;
                } else {
                    // intervals are overlapping, a single remove doesn't help
                    if (ivl1.isContained(s2, left1) && ivl1.isContained(e2, left1)) {
                        // en2 is completely in en1
                        toSplit = en1;
                        splitNum = s2;
                    } else if (ivl2.isContained(s1, left2) && ivl2.isContained(e1, left2)) {
                        // en1 is completely in en2
                        toSplit = en2;
                        splitNum = s1;
                    } else {
                        if (ivl1.isContained(s2, left1)) {
                            toSplit = en1;
                            splitNum = s2;
                        } else if (ivl1.isContained(e2, left1)) {
                            toSplit = en1;
                            splitNum = e2;
                        } else if (ivl2.isContained(s1, left2)) {
                            toSplit = en2;
                            splitNum = s1;
                        } else if (ivl2.isContained(e1, left2)) {
                            toSplit = en2;
                            splitNum = e1;
                        } else if (style1 == NumberStyle.BOTH) {
                            toSplit = en1;
                        } else if (style2 == NumberStyle.BOTH) {
                            toSplit = en2;
                        } else {
                            toSplit = (delta1 >= delta2) ? en1 : en2;
                        }
                    }
                }
            } else if (delta1 == 0 && delta2 > 0 && countOccurence(houses2, s1) == 0) {
                toSplit = en2;
                splitNum = s1;
            } else if (delta2 == 0 && delta1 > 0 && countOccurence(houses1, s2) == 0) {
                toSplit = en1;
                splitNum = s2;
            }
            if (toSplit != null) {
                toSplit.worstHouse = null;
                toSplit.badNum = splitNum;
                toSplit.setNeedsSplit(true);
                return NOT_OK_TRY_SPLIT;
            }
            allOK = false;
        }
    }
    if (allOK)
        return OK_NO_CHANGES;
    return NOT_OK_KEEP;
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) Numbers(uk.me.parabola.imgfmt.app.net.Numbers) HousenumberMatchByPosComparator(uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByPosComparator) NumberStyle(uk.me.parabola.imgfmt.app.net.NumberStyle) ArrayList(java.util.ArrayList)

Aggregations

ArrayList (java.util.ArrayList)3 Coord (uk.me.parabola.imgfmt.app.Coord)3 HousenumberMatchByPosComparator (uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByPosComparator)3 Numbers (uk.me.parabola.imgfmt.app.net.Numbers)2 BitSet (java.util.BitSet)1 CoordNode (uk.me.parabola.imgfmt.app.CoordNode)1 NumberStyle (uk.me.parabola.imgfmt.app.net.NumberStyle)1 HousenumberMatchByNumComparator (uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByNumComparator)1