Клуб API Карт

Конструкция «Геокодер 4 точек + маршрут из этих точек»

Пост в архиве.
wir2al
3 июля 2012, 02:36

Ребята, выручайте.

 

Задача:

Есть 4 инпута с адресами, которые, выглядят примерно так: «2-я Бауманская, 5», «Тверская, 4», «Большая Никитская, 11», «Киевский вокзал». Необходимо произвести поиск в столице и области на карте этих адресов и вывести маршрут через эти адреса. Кол-во адресов может меняться от 2 до 4 (добавлением/удалением промежуточных точек типа Тверской и Большой Никитской). Метки адресов маршрута должны быть названы, причем не напрямую из инпутов (там может быть и грамматическая ошибка и строчные буквы), а обратным геокодированием точек маршрута (как на примере http://api.yandex.ru/maps/doc/jsapi/1.x/examples/geocodingreverse.html).

У меня получилась вот такая конструкция:  

 

ymaps.ready(init);
function init() {
        $('#map').empty();

        var myMap = new ymaps.Map("map", {
                center: [55.754178, 37.624678],
                zoom: 12,
                behaviors: ["default", "scrollZoom"]
            }, {
                minZoom: 10
            }
        );
        myMap.controls.add("zoomControl")
        .add("trafficControl");

        var from = document.getElementById("from").value;
        var to = document.getElementById("to").value;
        var through1 = document.getElementById("through1").value;
        var through2 = document.getElementById("through2").value;

        var myGeocoder = ymaps.geocode(from, {boundedBy: [[55.296944, 36.89326], [56.223008, 38.631846]], strictBounds: true, results: 1});
        myGeocoder.then(
            function (res) {
                if (res.geoObjects.getLength()) {
                    var geofrom = res.geoObjects.get(0);
                    var myGeocoder = ymaps.geocode([55.742904, 37.565767], {boundedBy: [[55.339351, 37.121501], [56.199283, 38.659999]], strictBounds: true, results: 1});
                    myGeocoder.then(
                        function (res) {
                            if (res.geoObjects.getLength()) {
                                var geoto = res.geoObjects.get(0);
                                var myRouter = ymaps.route([geofrom.geometry.getCoordinates(), geoto.geometry.getCoordinates()], {mapStateAutoApply: true});
                                myRouter.then(function(route) {
                                        var points = route.getWayPoints();
                                        points.get(0).options.set('preset', 'twirl#blueStretchyIcon');
                                        points.get(0).properties.set("iconContent", geofrom.geometry.getCoordinates());

                                            points.get(1).options.set('preset', 'twirl#redStretchyIcon');
                                            points.get(1).properties.set("iconContent", geoto.geometry.getCoordinates());

                                        route.getPaths().options.set({
                                            balloonContenBodyLayout: ymaps.templateLayoutFactory.createClass('$[properties.humanJamsTime]')
                                        });
                                        myMap.geoObjects.add(route);
                                    },
                                    function (error) {
                                        alert("Возникла ошибка: " + error.message);
                                    }
                                )
                            }
                        },
                        function (error) {
                            alert("Возникла ошибка: " + error.message);
                        }
                    )
                }
            },
            function (error) {
                alert("Возникла ошибка: " + error.message);
            }
        )        
};

 

 

 Т.е. для того, чтобы произвести геокодирование 2 адресов, приходится засовывать один геокодер в другой. С 4 адресами вообще ужас получится. Может можно как то это дело упростить?

P.S. [55.742904, 37.565767] — координаты, внутри которых производить поиск (прямоугольник содержащий в себе мск и область). Может можно как то задать в геокодере, чтобы искались адреса, наиболее близкие к центру мск?

22 комментария

Код выше может быть с ошибкой, т.к. в моем было много кода для расчетов длины маршрута, времени, стоимости поезки и т.д. и я его подчищал.

 

Т.е. для того, чтобы произвести геокодирование 2 адресов, приходится засовывать один геокодер в другой. С 4 адресами вообще ужас получится. Может можно как то это дело упростить?

лучше использовать класс множественного геокодирования

 

P.S. [55.742904, 37.565767] — координаты, внутри которых производить поиск (прямоугольник содержащий в себе мск и область). Может можно как то задать в геокодере, чтобы искались адреса, наиболее близкие к центру мск?

 

http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/geocode.xml

см. опцию boundedBy и strictBounds

 

см. опцию boundedBy и strictBounds

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

Их надо использовать для этого:

 

Может можно как то задать в геокодере, чтобы искались адреса, наиболее близкие к центру мск?

а как? я думал, можно только 2 координаты задать и искать внутри прямоугольника.

strictBounds можно не указывать, boundedBy тогда влияет на сортировку объектов - те что ближе к центру облсти будут в начале

Так, те что ближе к центру - в начале. Но поиск ведется только внутри квадрата? Или за его приделами тоже? Можно задать 1 точку в центре москвы? Или малюсенький квадратик?

за пределами тоже

область влияет только на сортировку

 

Можно задать область - точку

Т.е. вот так?

boundedBy: [[55.339351, 37.121501], [55.339351, 37.121501]], strictBounds: false

Координаты обоих углов совпадают — прямоугольник = точка. Так будет искать за пределами точки, но сначала будет показывать результаты, наиболее к ней приближенные?

Спасибо! Я знал, что есть выход :)

Получилась вот такая конструкция:

 

var multiGeocoder = new MultiplyGeocoder({boundedBy: [[55.339351, 37.121501], [56.199283, 38.659999]], strictBounds: true, results: 1});
        multiGeocoder
            .geocode([from, to, through1, through2])
                .then(
                    function (res) {
                        map.geoObjects.add(res.geoObjects);
                    },
                    function (err) {
                        console.log(err);
                    }
                );
               
        // Множественное геокодирование
        function MultiplyGeocoder(options) {
            this._options = options || {};
        }

        MultiplyGeocoder.prototype.geocode = function (requests, options) {
            var self = this,
                size = requests.length,
                promise = new ymaps.util.Promise(),
                result = {
                    geoObjects : new ymaps.GeoObjectArray()
                };

            requests.forEach(function (request, index) {
                ymaps.geocode(request, ymaps.util.extend({}, self._options, options))
                    .then(
                        function (response) {
                            var geoObject = response.geoObjects.get(0);

                            geoObject && result.geoObjects.add(geoObject, index);
                            --size || promise.resolve(result);
                        },
                        function (err) {
                            promise.reject(err);
                        }
                    );
            });
            return promise;
        };   

 

К сожалению не работает.

А что вы передаете в from, to, through1, through2?

var pnum = 2;

var from = document.getElementById("from").value;
var to = document.getElementById("to").value;
if(from == '') {from = '2-я Бауманская, 5';}
if(to == '') {to = 'Киевский вокзал';}
if(pnum == 1 || pnum == 2) {var through1 = document.getElementById("through1").value; if(through1 == '') {through1 = 'Тверская, 4';}}
if(pnum == 2) {var through2 = document.getElementById("through2").value; if(through2 == '') {through2 = 'Большая Никитская, 11';}}

Теперь и у меня работает, спасибо.

А как теперь отсюда вырвать координаты точек и результаты обратного геокодирования (названия улиц, номера домов и т.д.)? Для маршрутизатора.

Вы мне вот здесь ответили, но я дуб полный, извиняйте:

http://clubs.ya.ru/mapsapi/replies.xml?item_no=27382

 

.geocode([from, to, through1, through2])
                .then(
                    function (res) {
 var coords = [];
res.geoObjects.each(function (geoObject) {
    coords.push(geoObject.geometry.getCoordinates());
});
                    },

 

Что касается названий улиц, домов, и т.п.

достаете через geoObject.properties.get(_prop_name_);

как по той ссылке что вы привели

Спасибо большое, постараюсь разобраться.

Я правильно понял, что координаты будут в массеви coords? coords [0], coords [1] и т.д.?

И сразу последний вопрос. Можно ли в boundedBy задать сразу 2 условия, чтобы искало как можно ближе к точке и в тоже время не заходило за пределы второго условия (прямоугольника, содержащего в себе москву и область)? Сейчас все отлично работает с первым условием, но некоторые адреса ищет плохо. Например поисковая фраза «бутово», вместо района мск Южного Бутово показывает киевский район.

Он вроде бы так и должен работать, ты попробуй поиграться этими параметрами.

Что-то вообще странное происходит.

http://avangardtaxi.ru/test.php

Попробуйте пожалуйста ввести в первый инпут "бутово". Центр карты съезжает в Киев, а маршрут стоится без учета этого адреса.

я указал results: 1, это правильно? может надо 4, адресов то 4?

правильно

Только что заметил, что скрипт выдает разные расстояние и время прохождения маршрута на одни и те же запросы. Посмотрите пожалуйста:

Пообновляйте несколько раз страницу (можно просто понажимать ссылку «проложить маршрут»).

http://avangardtaxi.ru/test.php

 

код

 

ymaps.ready(init);
function init() {
        // Удаление контента div#map
        $('#map').empty();
       
        // Создание карты
        var myMap = new ymaps.Map("map", {
                center: [55.754178, 37.624678],
                zoom: 12,
                behaviors: ["default", "scrollZoom"]
            }, {
                minZoom: 10
            }
        );
       
        // Добавление элементов управления
        myMap.controls.add("zoomControl")
        .add("mapTools")
        //.add("typeSelector")
        .add("trafficControl");
       
        // Количество промежуточных точек маршрута
        var pnum = 2;
       
        // Считываем адреса маршрута из инпутов
        var from = document.getElementById("from").value;
        var to = document.getElementById("to").value;
        if(from == '') {from = '2-я Бауманская, 5';}
        if(to == '') {to = 'Киевский вокзал';}
        if(pnum == 1 || pnum == 2) {
            var through1 = document.getElementById("through1").value;
            if(through1 == '') {through1 = 'Тверская, 4';}
        }
        if(pnum == 2) {
            var through2 = document.getElementById("through2").value;
            if(through2 == '') {through2 = 'Большая Никитская, 11';}
        }
       
        // Множественное геокодирование
        var geos = [[from, to], [from, to, through1], [from, to, through1, through2]];
        $.getScript('https://raw.github.com/dimik/ymaps/master/multi-geocoder.js', function () {
            (new MultiplyGeocoder({boundedBy: [[55.755786, 37.617633], [55.755786, 37.617633]], strictBounds: false, results: 1}))
                .geocode(geos [pnum])
                    .then(
                        function (res) {
                            // Координаты
                            var coords = [];
                            res.geoObjects.each(function (geoObject) {
                                coords.push(geoObject.geometry.getCoordinates());
                            });
                           
                            // Название местности
                            var geofrom = res.geoObjects.get(0);
                            var fromname = geofrom.properties.get('name');
                            var geoto = res.geoObjects.get(1);
                            var toname = geoto.properties.get('name');
                            if(pnum == 1 || pnum == 2) {
                                var geothrough1 = res.geoObjects.get(2);
                                var through1name = geothrough1.properties.get('name');
                            }
                            if(pnum == 2) {
                                var geothrough2 = res.geoObjects.get(3);
                                var through2name = geothrough2.properties.get('name');
                            }
                           
                            // Прокладка маршрута
                            var allcoords = [[coords[0], coords[1]], [coords[0], coords[2], coords[1]], [coords[0], coords[2], coords[3], coords[1]]];
                            var myRouter = ymaps.route(allcoords [pnum], {
                                // Флаг, позволяющий автоматически установить центр и коэффициент масштабирования карты так, чтобы построенный маршрут был виден целиком.
                                mapStateAutoApply: true
                            });
                            myRouter.then(function(route) {
                                    // Стилизация линии маршрута
                                    route.options.set({
                                        strokeColor: 'FF0033',
                                        opacity: 0.5
                                    });
                                   
                                    // Настройки графики маршрута
                                    var points = route.getWayPoints();
                                    points.get(0).options.set('preset', 'twirl#lightblueStretchyIcon');
                                    points.get(1).options.set('preset', 'twirl#lightblueStretchyIcon');
                                    points.get(0).properties.set("iconContent", fromname);
                                    if(pnum == 0) {
                                        points.get(1).properties.set("iconContent", toname);
                                    } else if(pnum == 1) {
                                        points.get(2).options.set('preset', 'twirl#lightblueStretchyIcon');
                                        points.get(1).properties.set("iconContent", through1name);
                                        points.get(2).properties.set("iconContent", toname);
                                    } else if(pnum == 2) {
                                        points.get(2).options.set('preset', 'twirl#lightblueStretchyIcon');
                                        points.get(3).options.set('preset', 'twirl#lightblueStretchyIcon');
                                        points.get(1).properties.set("iconContent", through1name);
                                        points.get(2).properties.set("iconContent", through2name);
                                        points.get(3).properties.set("iconContent", toname);
                                    }
                                    myMap.geoObjects.add(route);
                                   
                                    // Функция для окончаний слов
                                    function endings(number, end1, end2, end3) {
                                        num100 = number % 100;
                                        num10 = number % 10;
                                        if(num100 >= 5 && num100                                             return ' '+end1;
                                        } else if(num10 == 0) {
                                            return ' '+end1;
                                        } else if(num10 == 1) {
                                            return ' '+end2;
                                        } else if(num10 >= 2 && num10                                             return ' '+end3;
                                        } else if(num10 >= 5 && num10                                             return ' '+end1;
                                        } else {
                                            return ' '+end3;
                                        }
                                    }
                                   
                                    // Расстояние маршрута
                                    fulllength = route.getLength();
                                    var length = '';
                                    var miles = Math.floor(fulllength/1000);
                                    var meters = Math.floor(fulllength-(miles*1000));
                                    miles2 = miles+endings(miles, 'километров', 'километр', 'километра');
                                    meters2 = meters+endings(meters, 'метров', 'метр', 'метра');
                                    if(miles > 1000 || meters > 1000) { length = 'ошибка';} else
                                    if(miles == 0 && meters == 0) {length = 'меньше метра';} else
                                    if(miles == 0 && meters != 0) {length = meters2;} else
                                    if(miles > 0 && miles < 5 && meters != 0) {length = miles2+' '+meters2;} else
                                    if(miles > 4 && meters != 0) {length = miles2;} else
                                    if(miles > 0 && meters == 0) {length = miles2;}
                                    document.getElementById("travellength").innerHTML = length+' ('+fulllength+' метров)';

                                    // Время в пути
                                    jamstime = route.getJamsTime();
                                    var time = '';
                                    var hours = Math.floor(jamstime/3600);
                                    var minutes = Math.floor(jamstime/60)%60;
                                    hours2 = hours+endings(hours, 'часов', 'час', 'часа');
                                    minutes2 = minutes+endings(minutes, 'минут', 'минуту', 'минуты');
                                    if(hours > 12 || minutes > 60) { time = 'ошибка';} else
                                    if(hours == 0 && minutes == 0) {time = 'меньше минуты';} else
                                    if(hours == 0 && minutes != 0) {time = minutes2;} else
                                    if(hours > 0 && minutes != 0) {time = hours2+' '+minutes2;} else
                                    if(hours > 0 && minutes == 0) {time = hours2;}
                                    document.getElementById("traveltime").innerHTML = time+' ('+jamstime+' секунд)';
                                   
                                    // Выбранные кузов и класс автомобиля
                                    var travelbody = document.getElementById("body").value;
                                    var travelclass = document.getElementById("class").value;
                                    var travelbodyclass = '';
                                    if(travelbody == '0' || travelbody == '1') {travelbodyclass = 'автомобиле '; if(travelclass == '0') {travelbodyclass += 'эконом';} else if(travelclass == '1') {travelbodyclass += 'комфорт';} else if(travelclass == '2') {travelbodyclass += 'бизнес';} travelbodyclass += '-класса'; if(travelbody == '1') {travelbodyclass += ' с кузовом универсал';}} else
                                    if(travelbody == '2') {travelbodyclass = 'минивэне';} else
                                    if(travelbody == '3') {travelbodyclass = 'джипе';} else
                                    if(travelbody == '4') {travelbodyclass = 'микроавтобусе';}
                                    document.getElementById("travelbodyclass").innerHTML = travelbodyclass;
                                   
                                    // Конечная стоимость поездки
                                    var cost = '';
                                    var rubles = 350;
                                    rubles2 = rubles+endings(rubles, 'рублей', 'рубль', 'рубля');
                                    if(rubles > 1000000) {cost = 'ошибка';} else
                                    {cost = rubles2;}
                                    document.getElementById("travelcost").innerHTML = cost;
                                },
                                function (error) {
                                    alert("При построении маршрута возникла ошибка: " + error.message);
                                }
                            )
                        },
                        function (err) {
                            console.log(err);
                        }
                    );
        });
};