Скрипт местоположения пользователей голоса на Google Map (Урок+Демо) и почему важна стандартизация данных в профиле и постах
Совместив JS API голоса и GoogleMaps удалось сделать интересный плагин, который своим примером может задать вектор для идей разработки и еще на один шаг приблизит голос к росту собственной экосистемы и взаимодействию со множеством сервисов.
Демо работы скрипта доступно на странице https://golos.rubtc.info/usermap/
Исходник можно взять тут https://github.com/vikxx/golos/blob/master/usermap.html
Исходники и инструкцию как создать такое на своем хостинге или локально в браузере я напишу ниже, но сначала лирическое отступление ко второй части заголовка "почему важна стандартизация, валидация и здравая логика данных в голосе".
Если вы заметили, на карте всего несколько человек. Это первые попавшиеся пользователи с корректно заполненным полем профиля "Место нахождения", а изначальная идея плагина была чуть более полезнее простого отображения чек-инов адуитории:
Первоночальная суть идеи была в том, что бы отобразить местоположения делегатов, а именно города серверов их нод.
Это бы помогло будущим делегатам правильно раположить свою ноду (локацию сервера) основываясь на существующей широте покрытия, говоря простым языком - чем ближе нода территориально к пользователю, тем быстрее она работает. Кроме того, такое наглядное отображение нод в реальном времени может участвовать в качестве хорошего промо-материала для инвесторов и просто привлечения новых пользователей. Но все разбилось о то, что поле местоположения заполнено всего у нескольких делегатов. И даже эти несколько заполнили в свойственной творческим людям манере :) Например "Планета Земля", "Млечный Путь" или "Въ Блокчейне", таким образом, если брать эти данные для геотагов, то кого-то вполне может отобразить в стрип-баре под названием в "Млечный Путь" (Хотя, в контексте @siski и молочных желез вполне себе истина :). Плюнув на делегатов, я решил посмотреть среди обычных пользователей, но и там нашлось много фантазеров впихнувшых невпихуемое в местоположение, пришлось для примера выбрать только несколько случайных человек с корректными данными.
Валидация данных, чем раньше, тем лучше.
По собственному опыту могу уверенно заявить - чем раньше команда сделает жесткую валидацию РАЗНЫХ данных от пользователей, тем лучше. Т.е. если имеем поле для местоположения - там должна быть возможность вабрать и указать из списка определенный город, а в случае ввода произвольного текста - не принимать данные. Такую же практику нужно применять к любой новой официальной фиче для клиента. Если не делать это своевременно, то планы на будущее разработчиков из мыслей о развитии превратятся в рутинные миграции БД и форматирование мусорных данных.
Но еще больше пользы от стандартизации данных - это дружелюбность API голоса, которая позволит сторонним разработчикам строить на основе блокчейна голоса настолько гибкие приложения, что голос вырастет из блогов в основу всего RU блокчейна.
Создание плагина для отображения местоположения пользователей
Скрипт который я буду описывать ниже выполняет следующую задачу:
Берется массив аккаунтов пользователей, далее из каждого аккаунта мы берем содержимое поля "место нахождения", после используя Google Maps мы выводим все местоположения на карте, подписывая маркеры логинами и краткой информацией из профиля.
Для работы скрипта нам нужно оформить html страницу и подключить необходимые библиотеки, а именно:
- Jquery
- API Google Maps
- Адаптация Steemjs.com для голоса ( github.com/ontofractal/golosjs)
https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js
https://maps.googleapis.com/maps/api/js
https://golos.rubtc.info/g.js
Не забываем в html странице оставить div элемент, куда будут грузиться карты
google-container
Я опущу описание косметической части, которая отвечает за цветовую палитры самой карты. В двух словах - стилизуем цвета и убираем не нужные элементы с карты так, что бы она выглядела под стать вашему цветовому оформлению. Для этого поместим все стили в переменную, которую укажем позднее в вызове карты.
Этот кусок кода можно пропустить:
Переходим к основной части скрипта
Как писал выше, наш список имен я выбрал случайно, основаясь на том, что пользователь заполнил правильно поле местоположения, мы будем рассматривать этот в ручную прописанный список, но в конце поста опишу как сделать выборку пользователей автоматически и по разным критериям.
Итак, помещаем наших ребят в массив names:
var names =['vood.one','alexna','kuna','dr2073','dhrms','zaria','ropox','radomir'];
Прописываем ряд глобальных переменных, которые будем использовать из разных функций и циклов
var geocoder,map,infowindow,i,marker,address = [],icon=[],username=[];
Описываем и наполняем базовыми параметрами функцию инициализации Google карты
initialize();
function initialize() {
geocoder = new google.maps.Geocoder();
infowindow = new google.maps.InfoWindow();
var latlng = new google.maps.LatLng(56.0251514,37.0447503);
var mapOptions = {
center: latlng,
zoom: $map_zoom,
panControl: false,
zoomControl: false,
mapTypeControl: false,
streetViewControl: false,
mapTypeId: google.maps.MapTypeId.ROADMAP,
styles: style
}
map = new google.maps.Map(document.getElementById("google-container"), mapOptions);
}
Карта пока что пустая, в качестве центра указана Москва, отключены всякие гугловские свистелки, в параметр styles мы вставили наши ранее описанные цветовые стили, пустую карту вывели в div с id google-container.
Теперь нужно отметить на карте пользователей из массива выше. Для этого нам нужно пройтись по их аккаунтам и собрать все поля с местоположениями, что бы это сделать, мы будем использовать websocket API голоса/стима ( https://steemjs.com/ )
Воспользуемся вызовом getAccounts, в котором укажем наш массив с именами names
steem.api.getAccounts(names, function(err, result) {
var count = 0;
for (i = 0; i < result.length; i++) {
// Тут будем проставлять маркеры на карту
}
Внутри цикла у нас появятся данные аккаунтов выбранных пользователей и нам нужно их рассортировать по переменным и массивам
var metas = JSON.parse(result[i].json_metadata); // Получаем json_metadata из каждого аккаунта
username.push(result[i].name); // Получаем имена аккаунтов
address.push(metas.profile.location); // Массив с содержимым поля "местоположение"
icon.push(metas.profile.profile_image); // Аватарки
Данные готовы, в username у нас список имен, address - то что написано в поле местоположения, icon - аватарки.
Вывод пользователей на карту возможен благодря функционалу геокодинга
https://developers.google.com/maps/documentation/javascript/geocoding
Маркер в любом случае будет выводится на основе широты и долготы, но так как пользователи не указывают широту и долготу, нам придется вычислять ее на стороне гугла геокодингом. В этом есть свои минусы - если у пользователя в строке местоположения указан бред - гугл найдет ближайший по смыслу бред на карте и укажет его координаты. Например если у пользователя указано в местоположении "на деревне у бабушки", гугл поставит маркер на найденом объекте по своему усмотреннию и едва ли это отразит идею как пользователя, так и веб-сервиса. Потому хотелось бы, что бы в клиенте была жестко указанно, в каком фомате указывать данные.
Ищем широту и долготу указанных пользователями данных с помощью функции geocode
geocoder.geocode( {'address': address[i]}, function(results, status) {
Мы находимся в цикле, потому в address у нас будет свое значение для каждого из пользователей. Из этого значения гугл получит широту и долготу с помощью results[0].geometry.location и мы укажем это в параметры position куда и следует прописывать широту с долготой для маркеров.
geocoder.geocode( {'address': address[i]}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var marker = new google.maps.Marker({
map: map,
visible: true,
label: username[count],
icon:'https://golos.io/images/favicons/favicon.ico',
position: results[0].geometry.location
});
}count++
});
Кроме этого у нас есть label - надпись на маркере - подпишем их именами пользователей.
Icon - это иконки маркера, возьмем favico голоса.
Так же мы сделаем так, что бы при нажатии на маркер открывалось больше информации об авторе:
var infowindow = new google.maps.InfoWindow({content:'Какой-то текст связанный с автором'})
Вместо какого-то текста мы выведем данные, которые будут выглядеть вот так:
Выведем аватар, ник и локацию. Локацию для того, что бы было понятно, насколько верно геокодер гугла понял значение пользователя. То есть если гугл неправильно поставил маркер, мы сможем увидеть, что имел в ввиду пользователь в своем поле и почему гугл решил отметить в какой-то локации.
Вывод данных в инфобокс сделаем так:
content: '<img src="'+icon[count]+'"/> @'+username[count]+' указал локацию: '+address[count]
И повесим на событие нажатия на маркер функцию раскрытия инфобокса
marker.addListener('click', function() {infowindow.open(map, marker);});
Еще немного косметических функций, в которых стилизуем кнопки зума под расцветку голоса:
function CustomZoomControl(controlDiv, map) {
var controlUIzoomIn= document.getElementById('cd-zoom-in'),
controlUIzoomOut= document.getElementById('cd-zoom-out');
controlDiv.appendChild(controlUIzoomIn);
controlDiv.appendChild(controlUIzoomOut);
google.maps.event.addDomListener(controlUIzoomIn, 'click', function() {
map.setZoom(map.getZoom()+2)
});
google.maps.event.addDomListener(controlUIzoomOut, 'click', function() {
map.setZoom(map.getZoom()-1)
});
}
var zoomControlDiv = document.createElement('div'),
zoomControl = new CustomZoomControl(zoomControlDiv, map);
map.controls[google.maps.ControlPosition.LEFT_TOP].push(zoomControlDiv);
Готово!
Теперь мы видим местоположение пользователей на карте.
Не всех, а только тех, которых указали сами: var names =['vood.one','alexna','kuna','dr2073','dhrms','zaria','ropox','radomir'];
Вывести других/всех/по виду/репутации и т.д. можно обращаясь к разным вызовам API steemjs.com, но главная загвоздка в том, что мало-кто правильно указывает данные в профиле. Надеюсь это исправится со временем, тогда можно будет получать список имен разными способами
В качестве примера - получим всех делегатов в порядке рейтинга их нод:
steem.api.getWitnessesByVote('', 100, function(err, result) {
var names =[];
for (var i = 0; i < result.length; i++){
names.push(result[i].owner);
}
}
Теперь в нашем names делегаты. И просто используем names как в основном примере.
Или можем использовать lookup и найти например 1000 пользователей, юзернэйм которых похож на ник siski
steem.api.lookupAccounts('siski', 1000, function(err, result) {
names.push(result[i].name);
});
В заключение
Некоторые речевые обороты в посте могут заставить вас думать, что я подговариваю вас указывать реальные данные местоположения в блокчейне, что в некотором роде претит идеалогии децентрализации. Но на самом деле я призываю к другому - помогать развитию голоса создавая новые точки входа для разработчиков на блокчейн. В чем-то опередить стим, в чем-то не отставать от глобальных трендов.
Не забывайте, что благополучие каждого автора зависит от благополучия всей сети голоса!