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

Просмотров: 2883

Задача: в мобильном приложении, которое открывает сайт на 1С-Битрикс внутри себя, нужно определить местоположение пользователя и показать близжайшие к нему рестораны и кафе из базы данных сайта

Рестораны- это элементы инфоблока местоположение которых задано через свойство привязка к Яндекс Картам, в котором хранятся (через запятую) широта и долгота метки, в виде строки.

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

Выводить будем с помощью API Яндекс Карт c их же помощью стилизуем внешний вид меток. Что бы получилось что-то такое.

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

Само приложение работает на Apache Cordova (кстати, можете посмотреть мой новый видео-курс
Создание мобильного приложения на Apache Cordova), соответственно мы имеем полный доступ к GPS датчику и можем определить местоположение пользователя, вплоть до дома.

Определяем местоположение пользователя с датчиков телефона.

Тут все просто: устанавливаем плагин cordova-plugin-geolocation и не забываем установить плагин cordova-plugin-remote-injection, что бы можно было вызывать плагины Cordova с подключаемого сайта.

cordova plugin add cordova-plugin-geolocation
cordova plugin add cordova-plugin-remote-injection

Теперь мы можем получить данные с GPS/Глонасс датчиков телефона, тут много всякого- но нам нужны только широта и долгота (высота, скорость движения и.т.д. нам не нужны). Создаем две переменные (latitude и longitude) с нужными данными:

document.addEventListener("deviceready", onDeviceReady, false);
    function onDeviceReady() {
        var onSuccess = function (position) {
            var latitude = position.coords.latitude;
            var longitude = position.coords.longitude;
        };
        navigator.geolocation.getCurrentPosition(onSuccess, onError);
    }

Получаем список элементов со свойством "привязка к Яндекс Картам"

Теперь получим список всех ресторанов, самое главное получить свойство (в моем случае) с кодом ATT_MAP. Также получим название ресторана, что бы вывести его в метке.

Получать будем методом CIBlockElement::GetList по старинке, дабы не городить лишние запросы к свойствам элементов на D7, да и работает быстрее. Строку с широтой и долготой сразу раздробим с помощью explode (разбивает строку с помощью разделителя)

CModule::IncludeModule('iblock');
$restorants = CIBlockElement::GetList(
    false,
    array('IBLOCK_ID' => '4'),
    false,
    false,
    array('NAME', 'PROPERTY_ATT_MAP')
);
while($ar_fields = $dbRestorants->Fetch()){ 
$mapPosition = explode(',', $ar_fields['PROPERTY_ATT_MAP_VALUE']);
?>
	Ресторан <?=$ar_fields['NAME']?>
	Широта: <?=$mapPosition[0]?>
	Долгота: <?=$mapPosition[1]?>              
<?}?>

Выводим все данные с помощью API Яндекс карт

Теперь, это все нужно скомпоновать и вывести на карту. API у Яндекс карт достатсочно обширное и подробно описанное. Я буду использовать только указание типа меток и метки буду использовать из стандартного набора.

Все комментарии внутри кода:

<?
CModule::IncludeModule('iblock');
$restorants = CIBlockElement::GetList(
    false,
    array('IBLOCK_ID' => '4'),
    false,
    false,
    array('NAME', 'PROPERTY_ATT_MAP')
);
?>

<!-- Подключаем Яндекс Карты -->
<script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU&apikey=ВАШ_КЛЮЧ_ОТ_API"></script>

<script>
document.addEventListener("deviceready", onDeviceReady, false);
 // Все делаем по готовности устройства
function onDeviceReady() {
	var onSuccess = function (position) {
    	var latitude = position.coords.latitude;
    	var longitude = position.coords.longitude;
            
            // Инициализируем карты
            ymaps.ready(init);

            function init() {
                var myMap = new ymaps.Map("map", {
                	// Центруем карту по геопозиции пользователя
                        center: [latitude, longitude],
                        // Приближение карты до улиц
                        zoom: 12,
                        // Показываем кнопки масштабирования карты
                        controls: ['zoomControl']
            }),

            myMap.geoObjects
            		// В центр карты (позиция пользователя) добавляем метку 
            		// с фразой 'Вы здесь'
                    .add(new ymaps.Placemark([latitude, longitude], {
                        iconCaption: 'Вы здесь'
                    }, {
                    	// Вид метки "запятая"
                        preset: 'islands#greenDotIconWithCaption',
                        // зеленого цвета
                        iconColor: '#0D9200'
                    }))
                    <?
                    // Вывод ресторанов (получаем выше CIBlockElement::GetList)
                    while($ar_fields = $restorants->Fetch()){ 
                    $mapPosition = explode(',', $ar_fields['PROPERTY_ATT_MAP_VALUE']);
                    ?>
                    // Добавляем метку с ресторанам по его координатам
                    .add(new ymaps.Placemark([<?=$mapPosition[0]?>, <?=$mapPosition[1]?>], {
                    	// При клике на метку выводится название ресторана
                    	// тут можно использовать и ссылку, на детальное описание (CODE или DETAIL_PAGE_URL)
                        balloonContent: 'Ресторан: <?=$ar_fields['NAME']?>'
                    }, {
                    	// Вид метки "круг" синего цвета
                        preset: 'islands#blueCircleDotIconWithCaption',
                        // Размер иконки
                        iconCaptionMaxWidth: '20'
                    }))
                <?}?>
            }
        };
        navigator.geolocation.getCurrentPosition(onSuccess, onError);
    }
</script>
<!-- выводим карту, пока грузится можно сделать прелоадер как фон -->
<div id="map"></div>

В общем-то и все. Карта заполнит собой div#map - пока подгружается карта, можно задать ему фон со спиннером, аля "идет загрузка".

Самих меток и их настроек (размеры, цвета, заготовки) достаточно много. Посмотреть параметры и примеры можно в документации Яндекс Карт. Если хотим сделать собственную метку, со своей картинкой, используем такой пример:

myPlacemark = new ymaps.Placemark(myMap.getCenter(), {
    hintContent: 'Заголовок метки',
    balloonContent: 'Текст при клике'
}, {
    // Тип картинка
    iconLayout: 'default#image',
    // Путь к собственной картинке
    iconImageHref: 'images/ico.gif',
    // Размер в пикселях
    iconImageSize: [30, 42],
    // Смещение в пикселях - что бы лучше выставить
    iconImageOffset: [-5, -38]
}),
Михаил Базаров 19.05.2022
Что бы показать балун с максимумом информации, можно воспользоваться вот таким примером
Внутри балуна можно назначить свои классы для элементов и сверстать как того требует дизайн сайта.



Код
 <?
while($ar_fields = $restorants->GetNext()){ // Вывод ресторанов (получаем выше CIBlockElement::GetList)
$mapPosition = explode(',', $ar_fields['PROPERTY_ATT_MAP_VALUE']);
$img_path = CFile::GetPath($ar_fields["PREVIEW_PICTURE"]);
$wFrom = $ar_fields['PROPERTY_ATT_ORDER_FROM_VALUE'];
$wTo = $ar_fields['PROPERTY_ATT_ORDER_TO_VALUE'];
?>
    .add(new ymaps.Placemark([<?=$mapPosition[0]?>, <?=$mapPosition[1]?>], {
          balloonContentHeader: '<strong><?=$ar_fields['NAME']?></strong>' +
               '<span><b>Время работы:</b><br> <?echo $wFrom ? 'c ' . $wFrom . ':00 ' : '';?> <?echo $wTo ? 'до ' . $wTo . ':00': '';?></span>',
          balloonContentBody: '<img class="baloon_img" src="<?=$img_path?>" width="120">',
          balloonContentFooter: '<a href="/mobileapp/restorants/?ELEMENT_ID=<?=$ar_fields['ID']?>">Посмотреть меню</a>' +
              '<a href="/mobileapp/restorants/?ELEMENT_ID=<?=$ar_fields['ID']?>">Бронирование столика</a>',
          hintContent: 'пустой блок'
}, {
preset: 'islands#blueCircleDotIconWithCaption',
iconCaptionMaxWidth: '20'
}))
<?}?>