Привет.
Этот пост будет небольшим, но надеюсь полезным. Сегодня я расскажу о том, как я реализовал поиск места на карте по его адресу, а также как я определяю текущее местоположение пользователя. Все это реализовано на основе 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