Search in sources :

Example 1 with NumberStyle

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

the class ExtNumbers method calcSearchPositions.

/**
 * Try to simulate a Garmin address search for each number covered by the interval.
 * @param fullLength
 * @param searchPositions filled by this routine
 * @return false if calculation failed
 */
private boolean calcSearchPositions(double fullLength, TreeMap<Integer, Double> searchPositions) {
    Numbers ivl = getNumbers();
    for (int side = 0; side < 2; side++) {
        boolean left = side == 0;
        NumberStyle style = ivl.getNumberStyle(left);
        if (style != NumberStyle.NONE) {
            int start = ivl.getStart(left);
            int end = ivl.getEnd(left);
            int step = style == NumberStyle.BOTH ? 1 : 2;
            if (step != 1 && start % 2 != end % 2) {
                log.error("internal error, bad interval in optimization", this);
                return false;
            }
            if (start == end) {
                searchPositions.put(start, fullLength / 2);
            } else {
                int parts = Math.abs(end - start) / step;
                double partLen = fullLength / parts;
                if (start > end)
                    step = -step;
                int hn = start;
                double dist = 0;
                while (true) {
                    searchPositions.put(hn, dist);
                    if (hn == end)
                        break;
                    dist += partLen;
                    hn += step;
                }
                if (parts > 1)
                    assert Math.abs(fullLength - dist) < 0.1;
            }
        }
    }
    return true;
}
Also used : Numbers(uk.me.parabola.imgfmt.app.net.Numbers) NumberStyle(uk.me.parabola.imgfmt.app.net.NumberStyle)

Example 2 with NumberStyle

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

the class ExtNumbers method findGoodSplitPos.

private void findGoodSplitPos() {
    badNum = -1;
    worstHouse = null;
    boolean multipleZipOrCity = false;
    for (int side = 0; side < 2; side++) {
        boolean left = side == 0;
        List<HousenumberMatch> houses = getHouses(left);
        if (houses.size() <= 1)
            continue;
        if (multipleZipOrCity(left))
            multipleZipOrCity = true;
        for (HousenumberMatch house : houses) {
            int hn = house.getHousenumber();
            if (countOccurence(houses, hn) > 1)
                continue;
            ExtNumbers modIvl = simulateRemovalOfHouseNumber(hn, left);
            if (modIvl.isPlausible()) {
                badNum = hn;
                if (log.isDebugEnabled())
                    log.debug("splitpos details: single remove of", badNum, "results in plausible interval");
                return;
            }
        }
    }
    if (multipleZipOrCity)
        // unlikely
        return;
    // log.debug("did not yet find good split position");
    Numbers ivl = getNumbers();
    int[] firstBad = { -1, -1 };
    int[] lastBad = { -1, -1 };
    for (int side = 0; side < 2; side++) {
        boolean left = (side == 0);
        int step = 2;
        if (ivl.getNumberStyle(left) == NumberStyle.BOTH)
            step = 1;
        int s = ivl.getStart(left);
        int e = ivl.getEnd(left);
        int s2 = ivl.getStart(!left);
        int e2 = ivl.getEnd(!left);
        NumberStyle style2 = ivl.getNumberStyle(!left);
        for (int hn = Math.min(s, e); hn <= Math.max(s, e); hn += step) {
            if (style2 == NumberStyle.EVEN && hn % 2 == 1 || style2 == NumberStyle.ODD && hn % 2 == 0) {
                if (firstBad[side] < 0)
                    firstBad[side] = hn;
                lastBad[side] = hn;
                continue;
            }
            if (hn < Math.min(s2, e2) || hn > Math.max(s2, e2)) {
                if (firstBad[side] < 0)
                    firstBad[side] = hn;
                lastBad[side] = hn;
            }
        }
    }
    if (firstBad[0] == lastBad[0]) {
        badNum = firstBad[0];
        if (badNum >= 0)
            return;
    }
    if (firstBad[1] == lastBad[1]) {
        badNum = firstBad[1];
        if (badNum >= 0)
            return;
    }
    badNum = Math.max(firstBad[0], lastBad[0]);
    if (badNum == -1)
        badNum = Math.min(firstBad[1], lastBad[1]);
    if (log.isDebugEnabled())
        log.debug("splitpos details", Arrays.toString(firstBad), Arrays.toString(lastBad), "gives badNum", badNum);
}
Also used : Numbers(uk.me.parabola.imgfmt.app.net.Numbers) NumberStyle(uk.me.parabola.imgfmt.app.net.NumberStyle)

Example 3 with NumberStyle

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

the class ExtNumbers method fillNumbers.

/**
 * Apply the given house numbers to the numbers object.
 * @param left {@code true} the left side of the street; {@code false} the right side of the street
 */
private void fillNumbers(boolean left) {
    NumberStyle style = NumberStyle.NONE;
    List<HousenumberMatch> houses = getHouses(left);
    if (houses.isEmpty() == false) {
        Set<CityInfo> cityInfos = new HashSet<>();
        Set<ZipCodeInfo> zipCodes = new HashSet<>();
        // get the sublist of house numbers
        boolean even = false;
        boolean odd = false;
        boolean inOrder = true;
        boolean inc = false;
        boolean dec = false;
        HousenumberMatch highest, lowest;
        lowest = highest = houses.get(0);
        Int2IntOpenHashMap distinctNumbers = new Int2IntOpenHashMap();
        int numHouses = houses.size();
        HousenumberMatch pred = null;
        for (int i = 0; i < numHouses; i++) {
            HousenumberMatch house = houses.get(i);
            if (house.getCityInfo() != null && house.getCityInfo().isEmpty() == false)
                cityInfos.add(house.getCityInfo());
            if (house.getZipCode() != null && house.getZipCode().getZipCode() != null)
                zipCodes.add(house.getZipCode());
            int num = house.getHousenumber();
            if (!hasGaps)
                distinctNumbers.put(num, 1);
            if (num > highest.getHousenumber())
                highest = house;
            if (num < lowest.getHousenumber())
                lowest = house;
            if (num % 2 == 0) {
                even = true;
            } else {
                odd = true;
            }
            if (pred != null) {
                int diff = num - pred.getHousenumber();
                if (diff > 0)
                    inc = true;
                else if (diff < 0)
                    dec = true;
            }
            pred = house;
        }
        if (even && odd) {
            style = NumberStyle.BOTH;
        } else if (even) {
            style = NumberStyle.EVEN;
        } else {
            style = NumberStyle.ODD;
        }
        int highestNum = highest.getHousenumber();
        int lowestNum = lowest.getHousenumber();
        int start = houses.get(0).getHousenumber();
        int end = houses.get(numHouses - 1).getHousenumber();
        // from low to high
        boolean increasing = false;
        if (dec & inc)
            inOrder = false;
        if (start == end && highestNum - lowestNum != 0) {
            if (prev != null) {
                int lastEnd = prev.getNumbers().getEnd(left);
                if (lastEnd <= lowestNum)
                    increasing = true;
            } else if (next != null) {
                int nextStart = next.getNumbers().getStart(left);
                if (highestNum < nextStart)
                    increasing = true;
            } else {
                increasing = true;
            }
        } else if (start != highestNum && start != lowestNum || end != highestNum && end != lowestNum) {
            inOrder = false;
            if (start <= end)
                increasing = true;
        } else if (start < end) {
            increasing = true;
        }
        if (increasing) {
            start = lowestNum;
            end = highestNum;
        } else {
            start = highestNum;
            end = lowestNum;
        }
        if (!hasGaps) {
            int step = (style == NumberStyle.BOTH) ? 1 : 2;
            for (int n = lowestNum + step; n < highestNum; n += step) {
                if (distinctNumbers.containsKey(n))
                    continue;
                hasGaps = true;
                break;
            }
        }
        RoadSide rs = (left) ? leftSide : rightSide;
        numbers.setNumbers(left, style, start, end);
        rs.multipleCities = (cityInfos.size() > 1);
        rs.multipleZipCodes = (zipCodes.size() > 1);
        if (cityInfos.size() == 1) {
            CityInfo ci = cityInfos.iterator().next();
            if (ci.isEmpty() == false) {
                if (ci.equals(housenumberRoad.getRoadCityInfo()) == false)
                    numbers.setCityInfo(left, ci);
            }
        }
        if (zipCodes.size() == 1) {
            ZipCodeInfo zipCodeInfo = zipCodes.iterator().next();
            if (zipCodeInfo.getZipCode() != null) {
                if (zipCodeInfo.equals(housenumberRoad.getRoadZipCode()) == false) {
                    // we found a zip code and the road doesn't yet have one, use it for the whole road
                    if (housenumberRoad.getRoadZipCode() == null) {
                        housenumberRoad.setZipCodeInfo(zipCodeInfo);
                    } else
                        numbers.setZipCode(left, zipCodeInfo);
                }
            }
        }
        rs.notInOrder = !inOrder;
    }
}
Also used : CityInfo(uk.me.parabola.mkgmap.general.CityInfo) NumberStyle(uk.me.parabola.imgfmt.app.net.NumberStyle) ZipCodeInfo(uk.me.parabola.mkgmap.general.ZipCodeInfo) Int2IntOpenHashMap(it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap) HashSet(java.util.HashSet)

Example 4 with NumberStyle

use of uk.me.parabola.imgfmt.app.net.NumberStyle 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)

Example 5 with NumberStyle

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

the class ExtNumbers method checkMoveTo.

private static HousenumberMatch checkMoveTo(HousenumberMatch house, ExtNumbers other, boolean otherLeft) {
    HousenumberMatch test = new HousenumberMatch(house);
    Numbers otherIvl = other.getNumbers();
    int oStart = otherIvl.getStart(otherLeft);
    int oEnd = otherIvl.getEnd(otherLeft);
    // check if it fits into en2
    if (house.getHousenumber() <= Math.min(oStart, oEnd) || house.getHousenumber() >= Math.max(oStart, oEnd))
        return test;
    boolean even = house.getHousenumber() % 2 == 0;
    NumberStyle oStyle = otherIvl.getNumberStyle(otherLeft);
    if (oStyle == NumberStyle.EVEN && !even || oStyle == NumberStyle.ODD && even)
        return test;
    HousenumberGenerator.findClosestRoadSegment(test, other.getRoad(), other.startInRoad, other.endInRoad);
    if (test.getDistance() <= HousenumberGenerator.MAX_DISTANCE_TO_ROAD) {
        Coord c1 = other.getRoad().getPoints().get(test.getSegment());
        Coord c2 = other.getRoad().getPoints().get(test.getSegment() + 1);
        if (c1.highPrecEquals(c2) || otherLeft == HousenumberGenerator.isLeft(c1, c2, house.getLocation())) {
            test.setLeft(otherLeft);
            return test;
        }
    }
    test.setRoad(null);
    return test;
}
Also used : Numbers(uk.me.parabola.imgfmt.app.net.Numbers) Coord(uk.me.parabola.imgfmt.app.Coord) NumberStyle(uk.me.parabola.imgfmt.app.net.NumberStyle)

Aggregations

NumberStyle (uk.me.parabola.imgfmt.app.net.NumberStyle)5 Numbers (uk.me.parabola.imgfmt.app.net.Numbers)4 Coord (uk.me.parabola.imgfmt.app.Coord)2 Int2IntOpenHashMap (it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap)1 ArrayList (java.util.ArrayList)1 HashSet (java.util.HashSet)1 CityInfo (uk.me.parabola.mkgmap.general.CityInfo)1 ZipCodeInfo (uk.me.parabola.mkgmap.general.ZipCodeInfo)1 HousenumberMatchByPosComparator (uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByPosComparator)1