/*
* Copyright (C) 2013.
*
* 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.Collections;
import java.util.List;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.mkgmap.general.MapRoad;
import uk.me.parabola.mkgmap.reader.osm.Way;
/**
* Stores the matching data between a housenumber and its road.
* @author WanMil
*/
public class HousenumberMatch extends HousenumberElem {
private MapRoad road;
private HousenumberRoad housenumberRoad;
private double distance = Double.POSITIVE_INFINITY;
private int segment = -1;
private boolean left;
private double segmentFrac;
private boolean ignored;
private boolean isDuplicate;
private boolean interpolated;
private int moved;
// distance in m between closest point on road and the point that is found in the address search
private double searchDist = Double.NaN;
private boolean isFarDuplicate;
private HousenumberGroup group;
private List<MapRoad> alternativeRoads;
private int intervalInfoRefs; // counter
public HousenumberMatch(HousenumberElem he) {
super(he);
}
public MapRoad getRoad() {
return road;
}
public void setRoad(MapRoad road) {
this.road = road;
}
/**
* Retrieves the distance to the road.
* @return distance in m
*/
public double getDistance() {
return distance;
}
/**
* Sets the distance to the road
* @param distance distance in m
*/
public void setDistance(double distance) {
this.distance = distance;
}
/**
* Retrieves the segment number the house number belongs to.
* @return the segment number
*/
public int getSegment() {
return segment;
}
/**
* Sets the segment number the house number belongs to.
* @param segment the segment number
*/
public void setSegment(int segment) {
this.segment = segment;
}
public boolean isLeft() {
return left;
}
/**
* Sets if the house number is on the left or right side of the street.
* @param left {@code true} left side; {@code false} right side
*/
public void setLeft(boolean left) {
this.left = left;
}
/**
* Retrieve the relative position of this house number within the segement
* of the related road.
* @return the relative position within the roads segment
*/
public double getSegmentFrac() {
return segmentFrac;
}
/**
* Sets the relative position of this house number within its segment
* of the related road.
* @param segmentFrac relative position within the segment
*/
public void setSegmentFrac(double segmentFrac) {
this.segmentFrac = segmentFrac;
}
public boolean hasAlternativeRoad() {
return alternativeRoads != null && alternativeRoads.isEmpty() == false;
}
public boolean isIgnored() {
return ignored;
}
public void setIgnored(boolean ignored) {
this.ignored = ignored;
}
public boolean isDuplicate() {
return isDuplicate;
}
public void setDuplicate(boolean isDuplicate) {
this.isDuplicate = isDuplicate;
}
public boolean isInterpolated() {
return interpolated;
}
public void setInterpolated(boolean interpolated) {
this.interpolated = interpolated;
}
public int getMoved() {
return moved;
}
public void incMoved() {
this.moved++;
}
public double getSearchDist() {
return searchDist;
}
public void setSearchDist(double searchDist) {
this.searchDist = searchDist;
}
public String toString() {
String s1 = String.valueOf(getHousenumber());
if (getSign().length() > 2 + s1.length())
return s1 + "("+segment+")";
return getSign() + "("+segment+")";
}
public void setFarDuplicate(boolean b) {
this.isFarDuplicate = b;
}
public boolean isFarDuplicate() {
return isFarDuplicate;
}
/**
* @return either an existing point on the road
* or the calculated perpendicular. In the latter case
* the highway count is zero.
*
*/
public Coord getClosestPointOnRoad(){
if (segmentFrac <= 0)
return getRoad().getPoints().get(segment);
if (segmentFrac >= 1)
return getRoad().getPoints().get(segment+1);
Coord c1 = getRoad().getPoints().get(segment);
Coord c2 = getRoad().getPoints().get(segment+1);
return c1.makeBetweenPoint(c2, segmentFrac);
}
/**
* @param other a different house on the same road
* @return the distance in m between the perpendiculars on the road
* of two houses.
*/
public double getDistOnRoad(HousenumberMatch other) {
if (getRoad() != other.getRoad()){
assert false : "cannot compute distance on road for different roads";
}
List<Coord> points = getRoad().getPoints();
HousenumberMatch house1 = this;
HousenumberMatch house2 = other;
if (house1.segment > house2.segment || house1.segment == house2.segment && house1.segmentFrac > house2.segmentFrac){
house1 = other;
house2 = this;
}
int s1 = house1.segment;
int s2 = house2.segment;
double distOnRoad = 0;
while (s1 < s2){
double segLen = points.get(s1).distance(points.get(s1 + 1));
if (s1 == house1.getSegment() && house1.getSegmentFrac() > 0){
// rest of first segment
distOnRoad += Math.max(0, 1-house1.getSegmentFrac()) * segLen;
} else
distOnRoad += segLen;
s1++;
}
double segLen = points.get(s1).distance(points.get(s1 + 1));
if (house2.getSegmentFrac() > 0)
distOnRoad += Math.min(1, house2.getSegmentFrac()) * segLen;
if (house1.getSegmentFrac() > 0 && s1 == house1.segment)
distOnRoad -= Math.min(1, house1.getSegmentFrac()) * segLen;
return distOnRoad;
}
public HousenumberRoad getHousenumberRoad() {
return housenumberRoad;
}
public void setHousenumberRoad(HousenumberRoad housenumberRoad) {
this.housenumberRoad = housenumberRoad;
}
public void setGroup(HousenumberGroup housenumberBlock) {
this.group = housenumberBlock;
}
public HousenumberGroup getGroup() {
return group;
}
public void addAlternativeRoad(MapRoad road2) {
if (alternativeRoads == null){
alternativeRoads = new ArrayList<>();
}
alternativeRoads.add(road2);
}
public List<MapRoad> getAlternativeRoads() {
if (alternativeRoads == null)
return Collections.emptyList();
return alternativeRoads;
}
public void forgetAlternativeRoads(){
alternativeRoads = null;
}
public int getIntervalInfoRefs() {
return intervalInfoRefs;
}
public void incIntervalInfoRefs() {
intervalInfoRefs++;
}
public void decIntervalInfoRefs() {
if (intervalInfoRefs > 0)
--intervalInfoRefs;
}
public boolean isDirectlyConnected(HousenumberMatch other){
if (getElement() instanceof Way && other.getElement() instanceof Way){
List<Coord> s1 = ((Way) getElement()).getPoints();
List<Coord> s2 = ((Way) other.getElement()).getPoints();
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);
}
for (int i = 0; i+1 < s1.size(); i++){
Coord co = s1.get(i);
if (co.isPartOfShape2())
return true;
}
}
return false;
}
public void calcRoadSide(){
if (getRoad() == null)
return;
Coord c1 = getRoad().getPoints().get(getSegment());
Coord c2 = getRoad().getPoints().get(getSegment()+1);
setLeft(HousenumberGenerator.isLeft(c1, c2, getLocation()));
}
public boolean isEqualAddress(HousenumberElem other){
if (getRoad() != other.getRoad())
return false;
if (getSign().equals(other.getSign()) == false)
return false;
if (getZipCode() != null && other.getZipCode() != null){
if (getZipCode().equals(other.getZipCode()) == false)
return false;
}
if (getCityInfo() != null && other.getCityInfo() != null){
if (getCityInfo().equals(other.getCityInfo()) == false)
return false;
}
return true;
}
}