Last active
August 29, 2015 14:24
-
-
Save boxfrommars/7cbc4725c497a818cf28 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Константы для определения меры принадлежности точки | |
var ANGLE_MAX = 10; // максимаьное значение для меры угла (максимальное значение cos -- 1) | |
var ANGLE_POWER = 4; // чувствительность к углу (чем больше число, тем меньшая ошибка будет приводить к большему уменьшению меры угла) | |
var DISTANCE_COEFF = .17; // коэффициент подстройки (грубо говоря, сколько метров считаем за единицу меры) | |
var DISTANCE_POWER = 1; // чувствительность к расстоянию | |
var DISTANCE_MAX = 10; // максимаьное значение для меры расстояния | |
/** | |
* Скалярное произведение двух веторов | |
* | |
* @param vector1 {{lat: number, lng: number}} вектор | |
* @param vector2 {{lat: number, lng: number}} вектор | |
* @returns {number} | |
*/ | |
var dotProduct = function (vector1, vector2) { | |
return vector1.lat * vector2.lat + vector1.lng * vector2.lng; | |
}; | |
/** | |
* Вектор с началом в первой точке и с концом во второй | |
* | |
* @param point1 {{lat: number, lng: number}} точка | |
* @param point2 {{lat: number, lng: number}} точка | |
* @returns {{lat: number, lng: number}} | |
*/ | |
var pointsToVector = function (point1, point2) { | |
return { | |
lat: point2.lat - point1.lat, | |
lng: point2.lng - point1.lng | |
} | |
}; | |
/** | |
* Косинус угла между двумя рёбрами | |
* | |
* @param edge1 [{lat: number, lng: number}, {lat: number, lng: number}] ребро | |
* @param edge2 [{lat: number, lng: number}, {lat: number, lng: number}] ребро | |
* @returns {number} | |
*/ | |
var edgeCos = function (edge1, edge2) { | |
var vector1 = pointsToVector(edge1[0], edge1[1]); | |
var vector2 = pointsToVector(edge2[0], edge2[1]); | |
var dotProductV1V2 = dotProduct(vector1, vector2); | |
var dotProductV1V1 = dotProduct(vector1, vector1); | |
var dotProductV2V2 = dotProduct(vector2, vector2); | |
return dotProductV1V2 / (Math.sqrt(Math.abs(dotProductV1V1 * dotProductV2V2))); | |
}; | |
/** | |
* Расстояние от точки до точки | |
* | |
* @param point1 {{lat: number, lng: number}} точка | |
* @param point2 {{lat: number, lng: number}} точка | |
* @returns {number} | |
*/ | |
var pointToPointDistance = function (point1, point2) { | |
var vector = pointsToVector(point1, point2); | |
// корень из скалярного произведения вектора на самого себя | |
return Math.sqrt(dotProduct(vector, vector)); | |
}; | |
/** | |
* Расстояние от точки до ребра | |
* | |
* @param point {{lat: number, lng: number}} точка | |
* @param edge [{lat: number, lng: number}, {lat: number, lng: number}] ребро | |
* @returns {number} | |
*/ | |
var pointToEdgeDistance = function (point, edge) { // @TODO сделать проверку на одинаковость начала и конца ребра (влечёт деление на ноль) | |
// получаем два вектора из точки edge[0], один с концом в edge1, другой в point | |
var edgeVector = pointsToVector(edge[0], edge[1]); | |
var edgeStartPointVector = pointsToVector(edge[0], point); | |
// их скалярное произведение, на его основе мы будем решать -- до чего искать расстояние от данной точки: | |
var edgePointDotProduct = dotProduct(edgeStartPointVector, edgeVector); | |
// 1. edgePointDotProduct <= 0, расстояние от точки до ребра равно расстоянию от точки до начала ребра | |
if (edgePointDotProduct <= 0) { | |
return pointToPointDistance(point, edge[0]); | |
} | |
var edgeEdgeDotProduct = dotProduct(edgeVector, edgeVector); // можно хранить в толле, так как не зависит от текущего пути автомобиля | |
// 2. edgePointDotProduct >= edgeEdgeDotProduct, расстояние от точки до ребра равно расстоянию от точки до конца ребра | |
if (edgePointDotProduct >= edgeEdgeDotProduct) { | |
return pointToPointDistance(point, edge[1]); | |
} | |
// 3. расстояние от точки до ребра равно расстоянию от точки до прямой, на которой лежит ребра | |
var edgeLength = Math.sqrt(Math.abs(edgeDotProduct)); // может быть вычислено один раз для толла | |
return ( // заметим, что первые множители в каждой строке зависят только от ребра толла, поэтому могут быть вычислены заранее и один раз | |
(edge[0].lng - edge[1].lng) * point.lat + | |
(edge[1].lat - edge[0].lat) * point.lng + | |
edge[0].lat * edge[1].lng - edge[0].lng * edge[1].lat | |
) / edgeLength; | |
}; | |
/** | |
* Вычисляем меру принадлежности точки ребру толла | |
* Больше -- лучше | |
* | |
* @param point {{lat: number, lng: number}} текущая точка трека | |
* @param previousPoint {{lat: number, lng: number}} предыдущая точка трека | |
* @param tollEdge [{lat: number, lng: number}, {lat: number, lng: number}] ребро толла | |
* @returns {number} | |
*/ | |
var isOnEdgeMeasure = function (point, previousPoint, tollEdge) { | |
var distance = pointToEdgeDistance(point, tollEdge); | |
var cos = edgeCos(tollEdge, [previousPoint, point]); | |
var distanceMeasure = DISTANCE_MAX - DISTANCE_COEFF * Math.pow(distance, DISTANCE_POWER); | |
var angleMeasure = Math.abs(ANGLE_MAX * Math.pow(cos, ANGLE_POWER)) * (cos / Math.abs(cos)); | |
return distanceMeasure + angleMeasure; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment