Skip to content

Instantly share code, notes, and snippets.

@boxfrommars
Last active August 29, 2015 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boxfrommars/7cbc4725c497a818cf28 to your computer and use it in GitHub Desktop.
Save boxfrommars/7cbc4725c497a818cf28 to your computer and use it in GitHub Desktop.
// Константы для определения меры принадлежности точки
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