use of uk.me.parabola.imgfmt.app.net.Numbers 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;
}
use of uk.me.parabola.imgfmt.app.net.Numbers in project mkgmap by openstreetmap.
the class ExtNumbers method getNumberList.
/**
* Return the intervals in the format used for the writer routines
* @return
*/
public List<Numbers> getNumberList() {
// do we have numbers?
boolean foundNumbers = false;
for (ExtNumbers curr = this; curr != null; curr = curr.next) {
if (curr.hasNumbers()) {
foundNumbers = true;
break;
}
}
if (!foundNumbers)
return null;
List<Numbers> list = new ArrayList<>();
boolean headerWasReported = false;
for (ExtNumbers curr = this; curr != null; curr = curr.next) {
if (curr.hasNumbers() == false)
continue;
list.add(curr.getNumbers());
if (log.isInfoEnabled()) {
if (headerWasReported == false) {
MapRoad road = curr.getRoad();
if (road.getStreet() == null && road.getName() == null)
log.info("final numbers for", road, curr.housenumberRoad.getName(), "in", road.getCity());
else
log.info("final numbers for", road, "in", road.getCity());
headerWasReported = true;
}
Numbers cn = curr.getNumbers();
log.info("Left: ", cn.getLeftNumberStyle(), cn.getIndex(), "Start:", cn.getLeftStart(), "End:", cn.getLeftEnd(), "numbers " + curr.getHouses(Numbers.LEFT));
log.info("Right:", cn.getRightNumberStyle(), cn.getIndex(), "Start:", cn.getRightStart(), "End:", cn.getRightEnd(), "numbers " + curr.getHouses(Numbers.RIGHT));
}
}
return list;
}
use of uk.me.parabola.imgfmt.app.net.Numbers in project mkgmap by openstreetmap.
the class ExtNumbers method getNumbers.
public Numbers getNumbers() {
if (numbers == null) {
numbers = new Numbers();
numbers.setIndex(nodeIndex);
fillNumbers(Numbers.LEFT);
fillNumbers(Numbers.RIGHT);
if (!numbers.isEmpty()) {
// TODO : remove
verify(getHouses(Numbers.LEFT));
// TODO : remove
verify(getHouses(Numbers.RIGHT));
}
}
return numbers;
}
use of uk.me.parabola.imgfmt.app.net.Numbers 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;
}
use of uk.me.parabola.imgfmt.app.net.Numbers 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);
}
Aggregations