Геокодирование адреса в координаты и геолокация текущего местоположения в OpenLayers

в 9:02, , рубрики: javascript, openlayers, OpenStreetMap, Веб-разработка, метки: ,

Привет.

Этот пост будет небольшим, но надеюсь полезным. Сегодня я расскажу о том, как я реализовал поиск места на карте по его адресу, а также как я определяю текущее местоположение пользователя. Все это реализовано на основе OpenLayers 2.

Я рассчитываю на то, что читатель уже знаком с азами работы в OpenLayers и карта со слоями уже создана. Если нет, то пишите в комментарии, я попробую все подробно рассказать.

На первом месте стоит геолокация. IMHO, благодаря OL этот вопрос не является трудоемким.

Сначала создадим нужный объект:

var control = new OpenLayers.Control.Geolocate(
           bind: false,
           watch: true,
           geolocationOptions: {
                    enableHighAccuracy: true,
                    maximumAge: 0,
                    timeout: 15000
                });

и добавим его на карту (OpenLayersMap — это собственно созданный объект карты после конструктора OpenLayers.Map(options)):

OpenLayersMap.addControl(control);

С этим разобрались, но что же дальше. Дальше нужно его активировать. Опять-таки, все просто:

control.activate();

И последнее что я добавил — это обработчик события, когда местоположение найдено. Для этого регистрируем событие и указываем функцию для обработки этого события:

control.events.on({
"locationupdated": function(e) {
                OpenLayersFunc.Events.GeolocateOnLocationUpdated(e);
                }
})

Теперь подготовим слой для отображения наших элементов:

var layer = new OpenLayers.Layer.Vector('Geolocate', {
           styleMap: new OpenLayers.StyleMap(
                    new OpenLayers.Style({
                                  fillColor: '#000',
                                  fillOpacity: 0.1,
                                  strokeWidth: 0})
                    )
});
OpenLayersMap.addLayer(layer);

Осталось написать функцию OpenLayersFunc.Events.GeolocateOnLocationUpdated. Сам принцип действия таков. Когда срабатывает событие «locationupdated», вызывается наша функция, в которую передается event этого события. А уже в этой функции мы изгаляемся над ним, как хотим. Код я постарался прокомментировать, так что просто смотрим:

var vector = OpenLayersMap.getLayersByName('Geolocate')[0];
vector.removeAllFeatures(); //очистили слой геолокации (чтобы не было дубляжей)
var polygon = OpenLayers.Geometry.Polygon.createRegularPolygon(new OpenLayers.Geometry.Point(e.point.x, e.point.y), e.position.coords.accuracy / 2, 40, 0);
var circle = new OpenLayers.Feature.Vector(polygon); //создали геометрию круга вокруг точки нашего местоположения и создали объект слоя
var point = new OpenLayers.Feature.Vector(e.point, {}, {
            graphicName: 'cross',
            strokeColor: '#f00',
            strokeWidth: 1,
            fillOpacity: 0,
            pointRadius: 8
        }); //также создаем саму точку
vector.addFeatures([point, circle]); //и добавляем круг с точкой на слой
if (OpenLayersFunc.Events.GeolocateFirstRun) { //это просто глобальная переменная, чтобы определить запускаем ли мы в первый раз
            pulsate(circle); //а это уже красота, смотрим дальше
            OpenLayersFunc.Events.GeolocateFirstRun = false;
        }

Функция pulsate добавляет анимацию пульсирования на круг. В параметре мы передаем объект нашего круга — circle. Смотрится довольно эффектно:

var pulsate = function(feature) {
            var point = feature.geometry.getCentroid();
            var bounds = feature.geometry.getBounds();
            var radius = Math.abs((bounds.right - bounds.left) / 2);
            var count = 0;
            var grow = 'up';
            var resize = function() {
                if (count > 16) {
                    clearInterval(window.resizeInterval);
                }
                var interval = radius * 0.03;
                var ratio = interval / radius;
                switch (count) {
                    case 4:
                    case 12:
                        grow = 'down';
                        break;
                    case 8:
                        grow = 'up';
                        break;
                }
                if (grow !== 'up') {
                    ratio = -Math.abs(ratio);
                }
                feature.geometry.resize(1 + ratio, point);
                vector.drawFeature(feature);
                count++;
            };
            window.resizeInterval = window.setInterval(resize, 50, point, radius);
        };

Весь код данной реализации под спойлером:

Геолокация

var layer = new OpenLayers.Layer.Vector('Geolocate', {
           styleMap: new OpenLayers.StyleMap(
                    new OpenLayers.Style({
                                  fillColor: '#000',
                                  fillOpacity: 0.1,
                                  strokeWidth: 0})
                    )
});
OpenLayersMap.addLayer(layer);
var control = new OpenLayers.Control.Geolocate(
           bind: false,
           watch: true,
           geolocationOptions: {
                    enableHighAccuracy: true,
                    maximumAge: 0,
                    timeout: 15000
                });
OpenLayersMap.addControl(control);
control.activate();
OpenLayersFunc.Events.GeolocateFirstRun = true;
OpenLayersFunc.Events.GeolocateOnLocationUpdated = function(e) {
        var vector = OpenLayersTools.Layers.GetLayerByName('Geolocate');
        var pulsate = function(feature) {
            var point = feature.geometry.getCentroid();
            var bounds = feature.geometry.getBounds();
            var radius = Math.abs((bounds.right - bounds.left) / 2);
            var count = 0;
            var grow = 'up';
            var resize = function() {
                if (count > 16) {
                    clearInterval(window.resizeInterval);
                }
                var interval = radius * 0.03;
                var ratio = interval / radius;
                switch (count) {
                    case 4:
                    case 12:
                        grow = 'down';
                        break;
                    case 8:
                        grow = 'up';
                        break;
                }
                if (grow !== 'up') {
                    ratio = -Math.abs(ratio);
                }
                feature.geometry.resize(1 + ratio, point);
                vector.drawFeature(feature);
                count++;
            };
            window.resizeInterval = window.setInterval(resize, 50, point, radius);
        };
        OpenLayersTools.Layers.ClearLayers(['Geolocate']);
        var polygon = OpenLayers.Geometry.Polygon.createRegularPolygon(new OpenLayers.Geometry.Point(e.point.x, e.point.y), e.position.coords.accuracy / 2, 40, 0);
        var circle = new OpenLayers.Feature.Vector(polygon);
        var point = new OpenLayers.Feature.Vector(e.point, {}, {
            graphicName: 'cross',
            strokeColor: '#f00',
            strokeWidth: 1,
            fillOpacity: 0,
            pointRadius: 8
        });
        vector.addFeatures([point, circle]);
        if (OpenLayersFunc.Events.GeolocateFirstRun) {
            pulsate(circle);
            OpenLayersFunc.Events.GeolocateFirstRun = false;
        }
    };
control.events.on({
"locationupdated": function(e) {
                OpenLayersFunc.Events.GeolocateOnLocationUpdated(e);
                }
})

Перейдем теперь к очень похожей вещи — геокодирование. Я использовал апи от гуглов и не жалею. Начнем:

Сначала создали еще один слой для отображения маркера с попапом:

var layer = new OpenLayers.Layer.Vector('Geocoder', {
           styleMap: new OpenLayers.StyleMap(
                    new OpenLayers.Style({
                                 externalGraphic: '../images/geocoder_marker.png',
                                 graphicWidth: 32,
                                 graphicHeight: 32,
                                 graphicYOffset: -32
                    )
});

Здесь все намного проще. Одна функция и та, от jQuery, чтобы сделать все красиво. Создаем объект геокодирования, делаем запрос гуглам, получаем результат и парсим для jquery-ui.autocomplete на source. А на select вешаем центрирование карты на этот адрес и добавляем маркер с окошком адреса:

var geocoder = new google.maps.Geocoder(); //создаем объект геокодирования
    $('#top-search-field').autocomplete({ //подключаем плагин к нашему полю ввода
        source: function(request, response) { //заполняем автодополнение результатами возможных адресов
            geocoder.geocode({'address': request.term}, function(results, status) { //отправляем введенный текст
                response($.map(results, function(item) { //и получаем список возможных вариантов адресов
                    return { //какую мы и генерируем в объект для autocomplete
                        label: item.formatted_address,
                        value: item.formatted_address,
                        latitude: item.geometry.location.lat(),
                        longitude: item.geometry.location.lng()
                    };
                }));
            });
        },
        select: function(event, ui) { //в случае выбора некого пункта
            var layer = OpenLayersMap.getLayersByName('Geocoder')[0];
            layer.removeAllFeatures(); //очищаем слой от предыдущих объектов
            var point_popup = new OpenLayers.LonLat(ui.item.longitude, ui.item.latitude); //создаем координаты для попапа
            point_popup.transform(new OpenLayers.Projection("EPSG:4326"), OpenLayersMap.getProjectionObject()); //и переводим в другую проекцию
            var point = new OpenLayers.Geometry.Point(ui.item.longitude, ui.item.latitude); //создаем точку
            point.transform(new OpenLayers.Projection("EPSG:4326"), OpenLayersMap.getProjectionObject());
            var feature = new OpenLayers.Feature.Vector(point); //создаем объект точки
            var popup = new OpenLayers.Popup.FramedCloud('geocoder_marker', point_popup, new OpenLayers.Size(200, 50), ui.item.label, null, false, true); //создаем попап
            OpenLayersTools.BaseFunc.CenterMap(ui.item.longitude, ui.item.latitude, 'EPSG:4326'); //центрируем на координатах адреса
            popup.closeOnMove = true; //выставляем свойство, при движении карты закрыть попап
            OpenLayersMap.addPopup(popup);
            layer.addFeatures(feature); //и добавляем все это на карту и слой
        }
    });

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

P.S. Попробовал вот такое написать, не знаю, получилось или нет. Но все равно жду конструктивной критики, пожелания :)

Автор: ghaiklor

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js