/*
* Copyright (C) 2015 Gerd Petermann
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 or
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package uk.me.parabola.mkgmap.osmstyle.housenumber;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.general.CityInfo;
import uk.me.parabola.mkgmap.general.MapRoad;
import uk.me.parabola.mkgmap.general.ZipCodeInfo;
import uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByNumComparator;
import uk.me.parabola.mkgmap.osmstyle.housenumber.HousenumberGenerator.HousenumberMatchByPosComparator;
/**
* Helper class to combine house numbers with MapRoad instances
* @author Gerd Petermann
*
*/
public class HousenumberRoad
{
private static final Logger log =
Logger.
getLogger(HousenumberRoad.
class);
private String streetName
;
private final MapRoad road
;
private CityInfo roadCityInfo
;
private ZipCodeInfo roadZipCode
;
private ExtNumbers extNumbersHead
;
private final List<HousenumberMatch
> houseNumbers
;
private boolean changed
;
private boolean isRandom
;
private boolean removeGaps
;
private LinkedHashSet<String> furtherNames
;
public HousenumberRoad
(MapRoad r, CityInfo ci,
List<HousenumberMatch
> potentialNumbersThisRoad
) {
this.
streetName = r.
getStreet();
this.
road = r
;
this.
roadCityInfo = ci
;
this.
houseNumbers =
new ArrayList<>(potentialNumbersThisRoad
);
for (HousenumberMatch house : houseNumbers
){
house.
setHousenumberRoad(this);
}
}
/**
* Remember a place name so that the labels are set accordingly.
* @param name the place name
*/
public void addPlaceName
(String name
) {
if (streetName ==
null) {
streetName = name
;
return;
}
if (streetName.
equals(name
))
return;
if (getRoad
().
isNamedByIdentifyServiceRoad()) {
// forget the name, the road is probably not a service road for a named road
// when a nearby house number is used with addr:place
streetName = name
;
getRoad
().
setNamedByIdentifyServiceRoad(false);
return;
}
if (furtherNames ==
null){
furtherNames =
new LinkedHashSet<>();
}
furtherNames.
add(name
);
}
public String getName
(){
return streetName
;
}
public void buildIntervals
() {
houseNumbers.
sort(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<>();
List<HousenumberMatch
> rightNumbers =
new ArrayList<>();
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
);
leftNumbers.
sort(new HousenumberMatchByPosComparator
());
rightNumbers.
sort(new HousenumberMatchByPosComparator
());
int currNodePos =
0;
int nodeIndex =
0;
int prevNumberNodeIndex =
0;
int prevNodePos =
0;
extNumbersHead =
null;
ExtNumbers currNumbers =
null;
assert road.
getPoints().
get(0).
isNumberNode();
for (Coord p : road.
getPoints()) {
// An ordinary point in the road.
if (!p.
isNumberNode()) {
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++
;
}
}
/**
* Try to detect groups of houses with continues numbers
* which should be attached to a zero-length segment.
* Very useful when a service road connects eg.
* numbers 7..15 to the named road, but also for just two numbers.
* @param depth
* @param leftNumbers
* @param rightNumbers
*/
private void detectGroups
(List<HousenumberMatch
> leftNumbers,
List<HousenumberMatch
> rightNumbers
) {
List<HousenumberGroup
> groups =
new ArrayList<>();
for (int side =
0; side
< 2; side++
){
boolean left = side ==
0;
List<HousenumberMatch
> houses = left
? leftNumbers : rightNumbers
;
HousenumberGroup group =
null;
for (int j =
1; j
< houses.
size(); j++
){
HousenumberMatch house = houses.
get(j
);
if (group ==
null){
if (house.
isInterpolated())
continue;
HousenumberMatch predHouse = houses.
get(j-
1);
int deltaNum = predHouse.
getHousenumber() - house.
getHousenumber();
if (Math.
abs(deltaNum
) > 2)
continue;
if (HousenumberGroup.
housesFormAGroup(predHouse, house
))
group =
new HousenumberGroup
(this, houses.
subList(j-
1, j+
1));
} else {
if (!group.
tryAddHouse(house
)) {
if(group.
verify())
groups.
add(group
);
group =
null;
}
}
}
if (group
!=
null && group.
verify()){
groups.
add(group
);
}
}
if (groups.
isEmpty())
return;
boolean nodesAdded =
false;
for (HousenumberGroup group : groups
){
int oldNumPoints = getRoad
().
getPoints().
size();
if (nodesAdded
){
if (!group.
recalcPositions())
continue;
}
if (group.
findSegment(streetName, groups
)){
nodesAdded =
true;
if (log.
isDebugEnabled())
log.
debug("added",getRoad
().
getPoints().
size() - oldNumPoints,
"number node(s) at",group.
linkNode,
"for group",group,
"in road",getRoad
());
oldNumPoints = getRoad
().
getPoints().
size();
int minSeg = group.
minSeg;
for (HousenumberMatch house :
this.
houseNumbers){
if (house.
getSegment() >= minSeg
)
HousenumberGenerator.
findClosestRoadSegment(house, getRoad
());
}
group.
recalcPositions();
} else {
if(group.
linkNode !=
null){
if (log.
isDebugEnabled())
log.
debug("used existing zero-length-segment at",group.
linkNode,
"for group",group,
"in road",getRoad
());
}
}
}
return;
}
/**
*/
public void checkIntervals
(){
if (extNumbersHead ==
null)
return;
boolean anyChanges =
false;
extNumbersHead.
detectRandom();
for (int loop =
0; loop
< 10; loop++
){
if (loop
> 4){
// TODO: 3,4,5 ?
setRandom
(true);
}
setChanged
(false);
extNumbersHead = extNumbersHead.
checkSingleChainSegments(streetName, removeGaps
);
extNumbersHead = extNumbersHead.
checkChainPlausibility(streetName, houseNumbers
);
if (isChanged
())
anyChanges =
true;
else
break;
}
setChanged
(anyChanges
);
}
/**
* Identify duplicate numbers and ignore those which are close together
* and those which are probably wrong.
*/
private void filterRealDuplicates
() {
List<HousenumberMatch
> toIgnore =
new ArrayList<>();
final int TO_SEARCH =
6;
int oddLeft =
0, oddRight =
0, evenLeft =
0, evenRight =
0;
for (HousenumberMatch house: houseNumbers
){
if (house.
isIgnored())
continue;
if (house.
isLeft()){
if (house.
getHousenumber() % 2 ==
0)
evenLeft++
;
else
oddLeft++
;
} else {
if (house.
getHousenumber() % 2 ==
0)
evenRight++
;
else
oddRight++
;
}
}
HousenumberMatch usedForCalc =
null;
for (int i =
1; i
< houseNumbers.
size(); i++
){
HousenumberMatch house1 = houseNumbers.
get(i -
1);
HousenumberMatch house2 = houseNumbers.
get(i
);
if (!house1.
getSign().
equals(house2.
getSign())) {
usedForCalc =
null;
} else {
if (!house1.
isEqualAddress(house2
))
continue;
// found a duplicate address (e.g. 2 and 2 or 1b and 1b in same road,city etc.)
double distBetweenHouses = house2.
getLocation().
distance(house1.
getLocation());
double distToUsed =
(usedForCalc ==
null) ? distBetweenHouses : house2.
getLocation().
distance(usedForCalc.
getLocation());
if (usedForCalc ==
null)
usedForCalc =
(house1.
getDistance() < house2.
getDistance()) ? house1 : house2
;
else {
house1 = usedForCalc
;
}
boolean sameSide =
(house2.
isLeft() == house1.
isLeft());
if (log.
isDebugEnabled())
log.
debug("analysing duplicate address",streetName,house1.
getSign(),
"for road with id",getRoad
().
getRoadDef().
getId());
if (sameSide
&& (distBetweenHouses
< 100 || distToUsed
< 100)){
HousenumberMatch obsolete = house1 == usedForCalc
? house2 : house1
;
if (log.
isDebugEnabled())
log.
debug("house",obsolete,obsolete.
toBrowseURL(),
"is close to other element and on the same road side, is ignored");
toIgnore.
add(obsolete
);
continue;
}
if (!sameSide
){
if (log.
isDebugEnabled())
log.
debug("oddLeft, oddRight, evenLeft, evenRight:",oddLeft, oddRight, evenLeft, evenRight
);
HousenumberMatch wrongSide =
null;
if (house2.
getHousenumber() % 2 ==
0){
if (evenLeft ==
1 && (oddLeft
> 1 || evenRight
> 0 && oddRight ==
0)){
wrongSide = house2.
isLeft() ? house2: house1
;
}
if (evenRight ==
1 && (oddRight
> 1 || evenLeft
> 0 && oddLeft ==
0)){
wrongSide =
!house2.
isLeft() ? house2: house1
;
}
} else {
if (oddLeft ==
1 && (evenLeft
> 1 || oddRight
> 0 && evenRight ==
0)){
wrongSide = house2.
isLeft() ? house2: house1
;
}
if (oddRight ==
1 && (evenRight
> 1 || oddLeft
> 0 && evenLeft ==
0)){
wrongSide =
!house2.
isLeft() ? house2: house1
;
}
}
if (wrongSide
!=
null){
if (log.
isDebugEnabled())
log.
debug("house",streetName,wrongSide.
getSign(),
"from",wrongSide.
toBrowseURL(),
"seems to be wrong, is ignored");
toIgnore.
add(wrongSide
);
continue;
}
}
double[] sumDist =
new double[2];
double[] sumDistSameSide =
new double[2];
int[] confirmed =
new int[2];
int[] falsified =
new int[2];
int[] found =
new int[2];
List<HousenumberMatch
> dups =
Arrays.
asList(house2, house1
);
for (int k =
0; k
< dups.
size(); k++
){
HousenumberMatch other, curr
;
if (k ==
0){
curr = dups.
get(0);
other = dups.
get(1);
} else {
curr = dups.
get(1);
other = dups.
get(0);
}
int pos = houseNumbers.
indexOf(curr
);
int left = pos -
1;
int right = pos +
1;
HousenumberMatch nearHouse
;
int stillToFind = TO_SEARCH
;
while (stillToFind
> 0){
int oldDone = stillToFind
;
if (left
>=
0){
nearHouse = houseNumbers.
get(left
);
if (nearHouse
!= other
){
double dist = curr.
getLocation().
distance(nearHouse.
getLocation());
sumDist
[k
] += dist
;
if (nearHouse.
isLeft() == curr.
isLeft()){
sumDistSameSide
[k
] += dist
;
}
if (curr.
getHousenumber() == nearHouse.
getHousenumber()){
if (dist
< 20)
confirmed
[k
]++
;
} else {
if (dist
< 10 )
falsified
[k
]++
;
}
}
--left
;
stillToFind--
;
if (stillToFind ==
0)
break;
}
if (right
< houseNumbers.
size()){
nearHouse = houseNumbers.
get(right
);
if (nearHouse
!= other
){
double dist = curr.
getLocation().
distance(nearHouse.
getLocation());
sumDist
[k
] += dist
;
if (nearHouse.
isLeft() == curr.
isLeft()){
sumDistSameSide
[k
] += dist
;
}
if (curr.
getHousenumber() == nearHouse.
getHousenumber()){
if (dist
< 40)
confirmed
[k
]++
;
} else {
if (dist
< 10 )
falsified
[k
]++
;
}
}
stillToFind--
;
right++
;
}
if (oldDone == stillToFind
)
break;
}
found
[k
] = TO_SEARCH -
1 - stillToFind
;
}
if (log.
isDebugEnabled()){
log.
debug("dup check 1:", streetName, house1, house1.
toBrowseURL());
log.
debug("dup check 2:", streetName, house2, house2.
toBrowseURL());
log.
debug("confirmed",
Arrays.
toString(confirmed
),
"falsified",
Arrays.
toString(falsified
),
"sum-dist",
Arrays.
toString(sumDist
),
"sum-dist-same-side",
Arrays.
toString(sumDistSameSide
));
}
HousenumberMatch bad =
null;
if (confirmed
[1] > 0 && confirmed
[0] ==
0 && falsified
[1] ==
0)
bad = dups.
get(0);
else if (confirmed
[0] > 0 && confirmed
[1] ==
0 && falsified
[0] ==
0)
bad = dups.
get(1);
else if (found
[0] > 3 && sumDist
[0] > sumDist
[1] && sumDistSameSide
[0] > sumDistSameSide
[1])
bad = dups.
get(0);
else if (found
[1] > 3 && sumDist
[1] > sumDist
[0] && sumDistSameSide
[1] > sumDistSameSide
[0])
bad = dups.
get(1);
if (bad
!=
null){
toIgnore.
add(bad
);
} else {
if (log.
isDebugEnabled())
log.
debug("duplicate house number, don't know which one to use, ignoring both");
toIgnore.
add(house1
);
toIgnore.
add(house2
);
house2.
setIgnored(true);
house1.
setIgnored(true);
}
}
}
for (HousenumberMatch house : toIgnore
){
if (log.
isInfoEnabled())
log.
info("duplicate housenumber",streetName,house.
getSign(),
"is ignored for road with id",house.
getRoad().
getRoadDef().
getId(),
",house:",house.
toBrowseURL());
houseNumbers.
remove(house
);
}
}
/**
* Identify groups of buildings with numbers like 1a,1b,1c.
* The list in housenumbers is sorted so that 2 appears before 2a and
* 2b appears before 2c.
* XXX This is quite aggressive, maybe we have to add more logic here.
*/
private void filterGroups
() {
if (houseNumbers.
size() <=
1)
return;
HousenumberMatch prev = houseNumbers.
get(0);
HousenumberMatch used =
null;
for (int i =
1; i
< houseNumbers.
size(); i++
){
HousenumberMatch house = houseNumbers.
get(i
);
if (house.
getHousenumber() != prev.
getHousenumber())
used =
null;
else {
if (used ==
null)
used = prev
;
if (prev.
getSign().
equals(house.
getSign()) && !prev.
isEqualAddress(house
)) {
// we want to keep these duplicates
} else {
house.
setIgnored(true);
if (log.
isInfoEnabled())
log.
info("using",streetName,used.
getSign(),
"in favor of",house.
getSign(),
"as target for address search");
}
}
prev = house
;
}
}
public void checkWrongRoadAssignmments
(HousenumberRoad other
) {
if (this.
extNumbersHead ==
null || other.
extNumbersHead ==
null)
return;
for (int loop =
0; loop
< 10; loop++
){
boolean changed =
false;
ExtNumbers head1 =
this.
extNumbersHead;
for (ExtNumbers en1 = head1
; en1
!=
null; en1 = en1.
next){
if (changed
)
break;
if (!en1.
hasNumbers())
continue;
ExtNumbers head2 = other.
extNumbersHead;
for (ExtNumbers en2 = head2
; en2
!=
null; en2 = en2.
next){
if (changed
)
break;
if (!en2.
hasNumbers())
continue;
int res = ExtNumbers.
checkIntervals(streetName, en1, en2
);
switch (res
) {
case ExtNumbers.
OK_NO_CHANGES:
case ExtNumbers.
NOT_OK_KEEP:
break;
case ExtNumbers.
OK_AFTER_CHANGES:
changed =
true;
this.
setChanged(true);
other.
setChanged(true);
break;
case ExtNumbers.
NOT_OK_TRY_SPLIT:
if (en1.
needsSplit()){
ExtNumbers test = en1.
tryChange(ExtNumbers.
SR_FIX_ERROR);
if (test
!= en1
){
changed =
true;
if (test.
prev ==
null){
this.
extNumbersHead = test
;
}
}
}
if (en2.
needsSplit()){
ExtNumbers test = en2.
tryChange(ExtNumbers.
SR_FIX_ERROR);
if (test
!= en2
){
changed =
true;
if (test.
prev ==
null){
other.
extNumbersHead = test
;
}
}
}
break;
case ExtNumbers.
NOT_OK_STOP:
return;
default:
log.
error("can't fix",en1,en2
);
}
}
}
if (!changed
)
break;
}
}
public void setNumbers
() {
if (extNumbersHead ==
null || extNumbersHead.
getNumberList().
isEmpty())
return;
// make sure that the name we used for the cluster is also attached to the road
if (streetName ==
null){
log.
error("found no name for road with housenumbers, implement a move to the next named road ?",road
);
return;
}
String[] labels = road.
getLabels();
boolean found =
false;
for (String label : labels
){
if (label ==
null)
break;
if (streetName.
equals(label
))
found =
true;
}
if (!found
){
if (labels
[0] ==
null){
// add empty label so that the address search name doesn't appear in the map
// when the original road did not have any label
labels
[0] =
"";
}
for (int i =
1; i
< labels.
length; i++
){
if (labels
[i
] ==
null){
labels
[i
] = streetName
;
log.
info("added label",streetName,
"for",road,
"Labels are now:",
Arrays.
toString(labels
));
found =
true;
break;
}
}
}
if (!found
){
int last = labels.
length-
1;
String droppedLabel = labels
[last
];
labels
[last
] = streetName
;
if (droppedLabel
!=
null){
if (log.
isInfoEnabled())
log.
info("dropped label",droppedLabel,
"for",road,
"in preference to correct address search. Labels are now:",
Arrays.
toString(labels
));
}
}
if (furtherNames
!=
null){
boolean changed =
false;
for (String furtherName : furtherNames
){
if (road.
getLabelPos(furtherName
) == -
1) {
if (road.
addLabel(furtherName
))
changed =
true;
else {
log.
warn("could not add further label",furtherName,
"for",road
);
}
}
}
if (changed
){
log.
info("added further labels for",road,
"Labels are now:",
Arrays.
toString(labels
));
}
}
if (road.
getZip() ==
null && roadZipCode
!=
null){
road.
setZip(roadZipCode.
getZipCode());
}
road.
setNumbers(extNumbersHead.
getNumberList());
}
public MapRoad getRoad
(){
return road
;
}
public CityInfo getRoadCityInfo
() {
return roadCityInfo
;
}
public ZipCodeInfo getRoadZipCode
() {
return roadZipCode
;
}
public boolean isChanged
() {
return changed
;
}
public void setChanged
(boolean changed
) {
this.
changed = changed
;
}
public boolean isRandom
() {
return isRandom
;
}
public void setRandom
(boolean isRandom
) {
if (!this.
isRandom)
if (log.
isDebugEnabled())
log.
debug("detected random case",
this);
this.
isRandom = isRandom
;
}
public void setRemoveGaps
(boolean b
) {
removeGaps =
true;
}
public boolean getRemoveGaps
() {
return removeGaps
;
}
/**
*
*/
public void improveSearchResults
() {
ExtNumbers curr = extNumbersHead
;
while (curr
!=
null) {
ExtNumbers en = curr.
splitLargeGaps();
if (en
!= curr
) {
if (en.
hasNumbers() && en.
next !=
null && en.
next.
hasNumbers())
setChanged
(true);
else {
ExtNumbers test = en.
hasNumbers() ? en : en.
next;
if (!test.
getNumbers().
isSimilar(curr.
getNumbers()))
setChanged
(true);
}
if (curr.
prev ==
null)
extNumbersHead = en
;
curr = en
;
continue;
}
curr = curr.
next;
}
}
public String toString
(){
return getRoad
().
toString() +
" " + houseNumbers
;
}
/**
* Check if street name is set, if not, try to find one.
* Identify those houses which are assigned to this road because it was the closest,
* but can't be correct because street name doesn't match.
*
* @param nodeId2RoadLists maps node ids to the {@link MapRoad} that use the corresponding nodes.
* @return
*/
public List<HousenumberMatch
> checkStreetName
(Int2ObjectOpenHashMap
<HashSet<MapRoad
>> nodeId2RoadLists
) {
List<HousenumberMatch
> noWrongHouses =
Collections.
emptyList();
List<HousenumberMatch
> wrongHouses =
Collections.
emptyList();
double minDist =
Double.
MAX_VALUE;
double maxDist =
0;
if (!houseNumbers.
isEmpty()) {
HashMap<String,
Integer>possibleStreetNamesFromHouses =
new HashMap<>();
HashMap<String,
Integer>possiblePlaceNamesFromHouses =
new HashMap<>();
for (HousenumberMatch house : houseNumbers
){
if (house.
getDistance() > maxDist
)
maxDist = house.
getDistance();
if (house.
getDistance() < minDist
)
minDist = house.
getDistance();
String potentialName = house.
getStreet();
if (potentialName
!=
null){
Integer oldCount = possibleStreetNamesFromHouses.
put(potentialName,
1);
if (oldCount
!=
null)
possibleStreetNamesFromHouses.
put(potentialName, oldCount +
1);
}
String placeName = house.
getPlace();
if (placeName
!=
null){
Integer oldCount = possiblePlaceNamesFromHouses.
put(placeName,
1);
if (oldCount
!=
null)
possiblePlaceNamesFromHouses.
put(placeName, oldCount +
1);
}
}
HashSet<String> connectedRoadNames =
new HashSet<>();
for (Coord co : road.
getPoints()){
if (co.
getId() ==
0)
continue;
HashSet<MapRoad
> connectedRoads = nodeId2RoadLists.
get(co.
getId());
for (MapRoad r : connectedRoads
){
if (r.
getStreet() !=
null)
connectedRoadNames.
add(r.
getStreet());
}
}
if (streetName
!=
null){
if (possibleStreetNamesFromHouses.
isEmpty()){
// ok, houses have no street name
return noWrongHouses
;
}
if (possibleStreetNamesFromHouses.
size() ==
1){
if (possibleStreetNamesFromHouses.
containsKey(streetName
)){
// ok, houses have same name as street
return noWrongHouses
;
}
}
}
if (possibleStreetNamesFromHouses.
isEmpty()){
// neither road not houses tell us a street name
if (furtherNames
!=
null && furtherNames.
size() > 0){
Iterator<String> iter = furtherNames.
iterator();
streetName = iter.
next();
iter.
remove();
if (furtherNames.
isEmpty())
furtherNames =
null;
}
return noWrongHouses
;
}
if (streetName ==
null){
if (possibleStreetNamesFromHouses.
size() ==
1){
String potentialName = possibleStreetNamesFromHouses.
keySet().
iterator().
next();
boolean nameOK =
false;
if (connectedRoadNames.
contains(potentialName
))
nameOK =
true;
else if (houseNumbers.
size() > 1){
nameOK =
true;
} else if (maxDist
<=
10){
nameOK =
true;
}
if (nameOK
){
streetName = potentialName
;
return noWrongHouses
; // all good, return empty list
}
} else {
List<String> matchingNames =
new ArrayList<>();
for (Entry
<String,
Integer> entry : possibleStreetNamesFromHouses.
entrySet()){
String name = entry.
getKey();
if (connectedRoadNames.
contains(name
)){
matchingNames.
add(name
);
}
}
if (matchingNames.
size() ==
1){
streetName = matchingNames.
get(0);
}
}
}
// if we get here we have no usable street name
wrongHouses =
new ArrayList<>();
Iterator<HousenumberMatch
> iter = houseNumbers.
iterator();
while (iter.
hasNext()){
HousenumberMatch house = iter.
next();
if (streetName
!=
null){
if (house.
getStreet() ==
null || streetName.
equalsIgnoreCase(house.
getStreet()))
continue;
} else if (house.
getPlace() !=
null)
continue;
double bestDist =
Double.
MAX_VALUE;
HousenumberMatch best =
null;
for (MapRoad altRoad : house.
getAlternativeRoads()){
if (house.
getStreet() !=
null){
if (house.
getStreet().
equals(altRoad.
getStreet())){
HousenumberMatch test =
new HousenumberMatch
(house
);
HousenumberGenerator.
findClosestRoadSegment(test, altRoad
);
if (test.
getDistance() < bestDist
){
best = test
;
bestDist = test.
getDistance();
}
}
}
}
iter.
remove();
if (best
!=
null){
best.
calcRoadSide();
wrongHouses.
add(best
);
} else {
log.
warn("found no plausible road for address",house.
getStreet(),house,house.
toBrowseURL());
}
}
}
return wrongHouses
;
}
public void addHouse
(HousenumberMatch house
) {
if (extNumbersHead
!=
null){
log.
error("internal error: trying to add house to road that was already processed",
this.
getRoad(),house
);
}
house.
setHousenumberRoad(this);
houseNumbers.
add(house
);
}
public List<HousenumberMatch
> getHouses
() {
return houseNumbers
;
}
public void setZipCodeInfo
(ZipCodeInfo zipInfo
) {
roadZipCode = zipInfo
;
}
}