Клуб API Карт

Маршруты и ближайшие точки к нему

haravista
31 октября 2013, 11:52

Задача: построить маршрут по заданным точкам, вывести на карту ближайшие к нему Достопримечательности, при выборе точки достпопримечательности скорректировать маршрут.

 

Стадия 1: построить маршрут - сделано.

Стадия 2: определить точки удаленные от маршрута не более чем 1-10 км (редактируемо) - сделано

Мое решение: построить полигон окружающий маршрут, и через geoQuery определять точки находящиеся внутри этого полигона.

Строю полигон по точкам поворота маршрута, однако трассы и шоссе идут не по прямой, т.е. сам маршрут  выходит за рамки полигона.


 

 var routeFilter = {
        _routePolygon: null,// ссылка на объект полигона
        _resultCollection: null, // результатирующая коллекция точек внутри полигона маршрута
        _distance: 4000, // расстояние от точек маршрута до края полигона
        _routePoints: [],// набор точек сегментов маршрута
        _routePolygonPoints: [],// набор точек полигона
        _lengthTolerance: 200,// терпимость расстояний сегмента полилинии
        _route: null,// сссылка на маршрут
        _map: null, // ссылка на карту
        buildRoutePolygon: function (route) {
// назначем ссылку на маршрут если он передан в функцию
            if (typeof route == 'object') {
                this._route = route;
            }
// удаляем полигон с карты, если до того мы уже его строили
            if (this._routePolygon !== null) {
                myMap.geoObjects.remove(this._routePolygon);
            }
// не строим полигон, если расстояние маршрута до его края меньше/равно 0
            if (this._lengthTolerance <= 0) return false;
// если нет набора координат полигона с текущей удаленностью от маршрута (терпимостью расстояний), вычислем его точки
            if (typeof this._routePolygonPoints[this._lengthTolerance] == 'undefined') {
                this._routePoints = [];// сбрасываем точки маршрута
                var lastPoint = false;
                var way;
                var segments;
                var lastLength = 0;
                var segmentDefinition = [];
// Получаем массив путей.
                for (var i = 0; i < this._route.getPaths().getLength(); i++) {
                    way = this._route.getPaths().get(i);
                    segments = way.getSegments();
// получаем сегменты путей
                    for (var j = 0; j < segments.length; j++) {
// получаем координаты полилинии данного сегмента
                        segmentDefinition = segments[j].getCoordinates();
                        if (i == 0 && j == 0) {// start point
                            this._routePoints.push(segmentDefinition[0]);
                        }
                        $.each(segmentDefinition, function (index, point) {
                            if (index > 0) {
// если это последняя точка маршрута, то мы должны ее добавить в любом случае
                                if (routeFilter._route.getPaths().getLength() <= (i + 1)
                                    && segments.length <= (j + 1)
                                    && segmentDefinition.length <= (index + 1)) {
                                    lastPoint = true;
                                }
                                var distance = ymaps.coordSystem.geo.getDistance(segmentDefinition[index - 1], point);
                                if ((distance + lastLength) >= routeFilter._lengthTolerance || lastPoint) {
                                    routeFilter._routePoints.push(point);
                                    lastLength = 0;
                                } else {// skip too close points
                                    lastLength += distance;
                                }
                            }
                        });
                    }
                }
// сохраняем точки полигона
                this._routePolygonPoints[this._lengthTolerance] = this.buildPolygonByPoints(this._routePoints);
            }

            this._routePolygon = new ymaps.Polygon([
                this._routePolygonPoints[this._lengthTolerance]
            ], {}, {'visible': true});
            this._map.geoObjects.add(this._routePolygon);
        },
        setIntersectPoints: function(geoObjectsCollection){
            var result = ymaps.geoQuery(geoObjectsCollection).searchIntersect(this._routePolygon).addTo(this._resultCollection);
        },
        getResultCollection: function(){
          return this._resultCollection;
        },
        buildPolygonByPoints: function (points) {
            var result = [];
            var top_line = [];
            var bottom_line = [];
            var start = [];
            var end = [];
            var prevPoint = null;
            var coords;
            $.each(points, function (index, point) {
                if (prevPoint == null) prevPoint = point;
                else if (index == 1) {// start
                    coords = routeFilter.getDistantPoints(prevPoint, point, 1);
                    top_line.push(coords['t']);
                    bottom_line.push(coords['b']);
                    start = coords['r'];
                }
                if (points.length <= (index + 1)) {// end
                    coords = routeFilter.getDistantPoints(prevPoint, point, -1);
                    end = coords['r'];
                    top_line.push(coords['t']);
                    bottom_line.push(coords['b']);
                } else if (index > 0) {// medium
                    coords = routeFilter.getDistantPoints(prevPoint, point, 0);
                    top_line.push(coords['t']);
                    bottom_line.push(coords['b']);
                }

            });

            result.push(start);
            result = result.concat(bottom_line);
            result.push(end);
            result = result.concat(top_line.reverse());
            return result;
        },
        getDistantPoints: function (point1, point2, withEndPoint) {
            var points = [];
            var geoDirection = ymaps.coordSystem.geo.solveInverseProblem(point1, point2);
            var direction = geoDirection.startDirection;
// direction => [sin(a), cos(a)]
            var d1_reverse = [direction[0] * (-1), direction[1] * (-1)];// sin(a+pi) = -sin(a), cos(a+pi) = -cos(a)
            var d1_bottom = [direction[1], direction[0] * (-1)];        // sin(a+pi/2) = cos(a), cos(a+pi/2) = -sin(a)
            var d1_top = [direction[1] * (-1), direction[0]];           // sin(a-pi/2) = -cos(a), cos(a-pi/2) = sin(a)
            if (withEndPoint == 1) {
// reverse 1
                points['r'] = ymaps.coordSystem.geo.solveDirectProblem(point1, d1_reverse, this._distance).endPoint;
// top 1
                points['t'] = ymaps.coordSystem.geo.solveDirectProblem(point1, d1_top, this._distance).endPoint;
// bottom 1
                points['b'] = ymaps.coordSystem.geo.solveDirectProblem(point1, d1_bottom, this._distance).endPoint;
                return points;
            }
// top 2
            points['t'] = ymaps.coordSystem.geo.solveDirectProblem(point2, d1_top, this._distance).endPoint;
// bottom 2
            points['b'] = ymaps.coordSystem.geo.solveDirectProblem(point2, d1_bottom, this._distance).endPoint;

            if (withEndPoint == -1) {
// reverse 2
                points['r'] = ymaps.coordSystem.geo.solveDirectProblem(point2, direction, this._distance).endPoint;
            }
            return points;
        },
        setDistance: function(multiplier){// km
            this._distance = multiplier * 1000;
            this._lengthTolerance = multiplier * 50;
        },
        clearResult: function(){
            if(this._resultCollection !== null){
                this._map.geoObjects.remove(this._resultCollection);
            }
            this._resultCollection = new ymaps.GeoObjectCollection();
            this._map.geoObjects.add(this._resultCollection);
        },
        setMap: function(mapObject){
            this._map = mapObject;
        }
    };

 

UPD Ура, я понял, что segments[j].getCoordinates(); возвращает координаты полилинии маршрута, их и беру для построения полигона.

Обратите внимание: geoQuery включен только в текущую версию карт (2.0), но не в stable

3 комментария
Подписаться на комментарии к посту

Вы можете брать сами линии маршрута через  route.getPaths() и считать расстояние от точек прямо до этих полилиний.

Построить полигон и по нему сравнивать - математика быстрее.

Еще актуально?