Skip to content

Instantly share code, notes, and snippets.

@Atmden
Created July 31, 2018 15:05
Show Gist options
  • Save Atmden/73d9b2b2e2b61faf7028ef60438786e2 to your computer and use it in GitHub Desktop.
Save Atmden/73d9b2b2e2b61faf7028ef60438786e2 to your computer and use it in GitHub Desktop.
Sleepingowl YandexMap
/**
* Класс контрола "центр карты".
* @class
* @name CrossControl
*/
function CrossControl(options) {
this.events = new ymaps.event.Manager();
this.options = new ymaps.option.Manager();
}
ymaps.ready(function () {
/**
* Макет контрола.
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/templateLayoutFactory.xml
* @class
* @name CrossControl.Layout
*/
CrossControl.Layout = ymaps.templateLayoutFactory.createClass(
'<div class="cross-control" style="right:$[options.position.right]px; top:$[options.position.top]px;"></div>'
);
});
/**
* @lends CrossControl.prototype
*/
CrossControl.prototype = {
/**
* @constructor
*/
constructor: CrossControl,
/**
* Устанавливает родительский объект.
* @function
* @name CrossControl.setParent
* @param {IControlParent} parent Родительский объект.
* @returns {CrossControl} Возвращает ссылку на себя.
*/
setParent: function (parent) {
this.parent = parent;
if(parent) {
var map = this._map = parent.getMap();
this._setPosition(map.container.getSize());
this._setupListeners();
/**
* Передаем в макет контрола данные о его опциях.
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/ILayout.xml#constructor-summary
*/
this.layout = new this.constructor.Layout({ options: this.options });
/**
* Контрол будет добавляться в pane событий, чтобы исключить интерактивность.
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/ILayout.xml#setParentElement
*/
this.layout.setParentElement(map.panes.get('events').getElement());
}
else {
this.layout.setParentElement(null);
this._clearListeners();
}
return this;
},
/**
* Возвращает ссылку на родительский объект.
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/IControl.xml#getParent
* @function
* @name CrossControl.getParent
* @returns {IControlParent} Ссылка на родительский объект.
*/
getParent: function () {
return this.parent;
},
/**
* Устанавливает контролу опцию "position".
* @function
* @private
* @name CrossControl._setPosition
* @param {Array} size Размер контейнера карты.
*/
_setPosition: function (size) {
// -8, так как картинка 16х16
this.options.set('position', {
top: size[1] / 2 - 8,
right: size[0] / 2 - 8
});
},
_onPositionChange: function (e) {
this._setPosition(e.get('newSize'));
},
_setupListeners: function () {
this._map.container.events
.add('sizechange', this._onPositionChange, this);
},
_clearListeners: function () {
if(this._map) {
this._map.container.events
.remove('sizechange', this._onPositionChange, this);
}
}
};
/**
* Класс кнопки определения местоположения пользователя.
* с помощью Geolocation API.
* @see http://www.w3.org/TR/geolocation-API/
* @class
* @name GeolocationButton
* @param {Object} params Данные для кнопки и параметры к Geolocation API.
* @param {Object} options Опции кнопки.
*/
function GeolocationButton(params, options) {
GeolocationButton.superclass.constructor.call(this, params, options);
// Расширяем опции по умолчанию теми, что передали в конструкторе.
this._options = ymaps.util.extend({
// Не центрировать карту.
noCentering: false,
// Не ставить метку.
noPlacemark: false,
// Не показывать точность определения местоположения.
noAccuracy: false,
// Режим получения наиболее точных данных.
enableHighAccuracy: true,
// Максимальное время ожидания ответа (в миллисекундах).
timeout: 10000,
// Максимальное время жизни полученных данных (в миллисекундах).
maximumAge: 1000
}, params.geolocationOptions);
}
ymaps.ready(function () {
ymaps.util.augment(GeolocationButton, ymaps.control.Button, {
/**
* Метод будет вызван при добавлении кнопки на карту.
* @function
* @name GeolocationButton.onAddToMap
* @param {ymaps.Map} map Карта на которую добавляется кнопка.
*/
onAddToMap: function () {
GeolocationButton.superclass.onAddToMap.apply(this, arguments);
ymaps.option.presetStorage.add('geolocation#icon', {
iconImageHref: 'man.png',
iconImageSize: [27, 26],
iconImageOffset: [-10, -24]
});
this.hint = new GeolocationButtonHint(this);
// Обрабатываем клик на кнопке.
this.events.add('click', this._onBtnClick, this);
},
/**
* Метод будет вызван при удалении кнопки с карты.
* @function
* @name GeolocationButton.onRemoveFromMap
* @param {ymaps.Map} map Карта с которой удаляется кнопка.
*/
onRemoveFromMap: function () {
this.events.remove('click', this._onBtnClick, this);
this.hint = null;
ymaps.option.presetStorage.remove('geolocation#icon');
GeolocationButton.superclass.onRemoveFromMap.apply(this, arguments);
},
/**
* Обработчик клика на кнопке.
* @function
* @private
* @name GeolocationButton._onBtnClick
* @param {ymaps.Event} e Объект события.
*/
_onBtnClick: function (e) {
// Меняем иконку кнопки на прелоадер.
this.toggleIconImage('/img/loader.gif');
if(navigator.geolocation) {
// Запрашиваем текущие координаты устройства.
navigator.geolocation.getCurrentPosition(
ymaps.util.bind(this._onGeolocationSuccess, this),
ymaps.util.bind(this._onGeolocationError, this),
this._options
);
}
else {
this.handleGeolocationError('Ваш броузер не поддерживает GeolocationAPI.');
}
},
/**
* Обработчик успешного завершения геолокации.
* @function
* @private
* @name GeolocationButton._onGeolocationSuccess
* @param {Object} position Объект, описывающий текущее местоположение.
*/
_onGeolocationSuccess: function (position) {
this.handleGeolocationResult(position);
// Меняем иконку кнопки обратно
this.toggleIconImage('/img/wifi.png');
},
/**
* Обработчик ошибки геолокации.
* @function
* @name GeolocationButton._onGeolocationError
* @param {Object} error Описание причины ошибки.
*/
_onGeolocationError: function (error) {
this.handleGeolocationError('Точное местоположение определить не удалось.');
// Меняем иконку кнопки обратно.
this.toggleIconImage('/img/wifi.png');
if(console) {
console.warn('GeolocationError: ' + GeolocationButton.ERRORS[error.code - 1]);
}
},
/**
* Обработка ошибки геолокации.
* @function
* @name GeolocationButton.handleGeolocationError
* @param {Object|String} err Описание ошибки.
*/
handleGeolocationError: function (err) {
this.hint
.show(err.toString())
.hide(2000);
},
/**
* Меняет иконку кнопки.
* @function
* @name GeolocationButton.toggleIconImage
* @param {String} image Путь до изображения.
*/
toggleIconImage: function (image) {
this.data.set('image', image);
},
/**
* Обработка результата геолокации.
* @function
* @name GeolocationButton.handleGeolocationResult
* @param {Object} position Результат геолокации.
*/
handleGeolocationResult: function (position) {
var location = [position.coords.latitude, position.coords.longitude],
accuracy = position.coords.accuracy,
map = this.getMap(),
options = this._options,
placemark = this._placemark,
circle = this._circle;
// Смена центра карты (если нужно)
if(!options.noCentering) {
map.setCenter(location, 15, {
checkZoomRange: true
});
}
// Установка метки по координатам местоположения (если нужно).
if(!options.noPlacemark) {
// Удаляем старую метку.
if(placemark) {
map.geoObjects.remove(placemark);
}
this._placemark = placemark = new ymaps.Placemark(location, {}, { preset: 'geolocation#icon' });
map.geoObjects.add(placemark);
// Показываем адрес местоположения в хинте метки.
this.getLocationInfo(placemark);
}
// Показываем точность определения местоположения (если нужно).
if(!options.noAccuracy) {
// Удаляем старую точность.
if(circle) {
map.geoObjects.remove(circle);
}
this._circle = circle = new ymaps.Circle([location, accuracy], {}, { opacity: 0.5 });
map.geoObjects.add(circle);
}
},
/**
* Получение адреса по координатам метки.
* @function
* @name GeolocationButton.getLocationInfo
* @param {ymaps.Placemark} point Метка для которой ищем адрес.
*/
getLocationInfo: function (point) {
ymaps.geocode(point.geometry.getCoordinates())
.then(function (res) {
var result = res.geoObjects.get(0);
if(result) {
point.properties.set('hintContent', result.properties.get('name'));
}
});
}
});
});
/**
* Человекопонятное описание кодов ошибок.
* @static
*/
GeolocationButton.ERRORS = [
'permission denied',
'position unavailable',
'timeout'
];
/**
* Класс хинта кнопки геолокации, будем использовать для отображения ошибок.
* @class
* @name GeolocationButtonHint
* @param {GeolocationButton} button Экземпляр класса кнопки.
*/
function GeolocationButtonHint(button) {
this._button = button;
this._map = button.getMap();
this._offset = { left: 35, top: -18 };
}
/**
* Отображает хинт справа от кнопки.
* @function
* @name GeolocationButtonHint.show
* @param {String} text
* @returns {GeolocationButtonHint}
*/
GeolocationButtonHint.prototype.show = function (text) {
var map = this._map,
offset = this._offset,
buttonPagePixels = this._button.getLayout().getElement().getBoundingClientRect(),
pagePixels = [buttonPagePixels.left + offset.left, buttonPagePixels.top + offset.top],
globalPixels = map.converter.pageToGlobal(pagePixels),
position = map.options.get('projection').fromGlobalPixels(globalPixels, map.getZoom());
this._hint = map.hint.show(position, text);
return this;
};
/**
* Прячет хинт с нужной задержкой.
* @function
* @name GeolocationButtonHint.hide
* @param {Number} timeout Задержка в миллисекундах.
* @returns {GeolocationButtonHint}
*/
GeolocationButtonHint.prototype.hide = function (timeout) {
var hint = this._hint;
if(hint) {
setTimeout(function () {
hint.hide();
}, timeout);
}
return this;
};
/**
* Класс Инструмент определения координат.
* @class
* @name LocationTool
* @param {ymaps.Map} map Карта.
*/
function LocationTool(map, placemark, color,title,hint,content) {
this._domView = new LocationTool.DOMView();
this._mapView = new LocationTool.MapView(map, placemark, color,title,hint,content);
this._monitor = new ymaps.Monitor(this._mapView.state);
this._setupMonitor();
this._initDOMView();
}
/**
* @lends LocationTool.prototype
*/
LocationTool.prototype = {
/**
* @constructor
*/
constructor: LocationTool,
/**
* Инициализирует DOMView начальными значениями карты.
* @private
* @function
* @name LocationTool._initView
*/
_initDOMView: function () {
this._domView.render(this._mapView.state.getAll());
},
/**
* Настраиваем монитор для наблюдения за интересующими нас полями.
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/Monitor.xml
* @private
* @function
* @name LocationTool._setupMonitor
*/
_setupMonitor: function () {
this._monitor
.add(['mapCenter', 'mapZoom', 'markerPosition'], this._onMapViewStateChange, this);
},
/**
* Останавливаем наблюдение.
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/Monitor.xml#removeAll
* @private
* @function
* @name LocationTool._clearMonitor
*/
_clearMonitor: function () {
this._monitor
.removeAll();
},
/**
* Обработчик изменения полей.
* @private
* @function
* @name LocationTool._onMapViewStateChange
*/
_onMapViewStateChange: function (data) {
this._domView.render(data);
}
};
/**
* Класс отображения на карте Инструмента определения координат.
* @class
* @name MapView
* @param {ymaps.Map} map Карта.
*/
LocationTool.MapView = function (map, placemark, color,title,hint,content) {
this._map = map;
// Интервал обновления данных (millisec) при кинетическом движении карты.
this._updateTimeout = 10;
this._marker = this._createDraggableMarker(placemark, color,title,hint,content);
map.geoObjects.add(this._marker);
this.state = new ymaps.data.Manager({
mapCenter: map.getCenter(),
mapZoom: map.getZoom(),
markerPosition: placemark
});
this._attachHandlers();
};
/**
* @lends MapView.prototype
*/
LocationTool.MapView.prototype = {
/**
* @constructor
*/
constructor: LocationTool.MapView,
/**
* Навешиваем обработчики.
* @function
* @private
* @name MapView._attachHandlers
*/
_attachHandlers: function () {
this._map.events
.add('boundschange', this._onMapBoundsChange, this)
.add('actiontick', this._onMapAction, this)
/* Во время плавного движения карты, у браузеров поддерживающих CSS3 Transition,
* actiontick не кидается, поэтому используем этот прием через setInterval.
*/
.add('actionbegin', this._onMapActionBegin, this)
.add('actionend', this._onMapActionEnd, this);
this._marker.events
.add('drag', this._onMarkerDrag, this);
},
/**
* Снимаем обработчики.
* @function
* @private
* @name MapView._detachHandlers
*/
_detachHandlers: function () {
this._marker.events
.remove('drag', this._onMarkerDrag, this);
this._map.events
.remove('boundschange', this._onMapBoundsChange, this)
.remove('actiontick', this._onMapAction, this)
.remove('actionbegin', this._onMapActionBegin, this)
.remove('actionend', this._onMapActionEnd, this);
},
/**
* Обработчик перетаскивания метки.
* @function
* @private
* @name MapView._onMarkerDrag
* @param {ymaps.Event} e Объект-событие
*/
_onMarkerDrag: function (e) {
this.state.set({
markerPosition: e.get('target').geometry.getCoordinates()
});
},
/**
* Обработчик начала плавного движения карты.
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/Map.xml#event-actionbegin
* @function
* @private
* @name MapView._onMapActionBegin
* @param {ymaps.Event} e Объект-событие
*/
_onMapActionBegin: function (e) {
if(this._intervalId) {
return;
}
this._intervalId = window.setInterval(
ymaps.util.bind(this._onMapAction, this),
this._updateTimeout
);
},
/**
* Обработчик окончания плавного движения карты.
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/Map.xml#event-actionend
* @function
* @private
* @name MapView._onMapActionEnd
* @param {ymaps.Event} e Объект-событие
*/
_onMapActionEnd: function (e) {
window.clearInterval(this._intervalId);
this._intervalId = null;
},
/**
* Обработчик исполнения нового шага плавного движения.
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/Map.xml#event-actiontick
* @function
* @private
* @name MapView._onMapAction
* @param {ymaps.Event} e Объект-событие
*/
_onMapAction: function (e) {
/**
* Определяет состояние карты в момент ее плавного движения.
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/map.action.Manager.xml#getCurrentState
*/
var state = this._map.action.getCurrentState(),
zoom = state.zoom,
/**
* Преобразует пиксельные координаты на указанном уровне масштабирования в координаты проекции (геокоординаты).
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/IProjection.xml#fromGlobalPixels
*/
center = this._map.options.get('projection').fromGlobalPixels(
state.globalPixelCenter, zoom
);
this.state.set({
mapCenter: center,
mapZoom: zoom
});
},
/**
* Обработчик события изменения области просмотра карты (в результате изменения центра или уровня масштабирования)
* @see http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/Map.xml#event-boundschange
* @function
* @private
* @name MapView._onMapBoundsChange
* @param {ymaps.Event} e Объект-событие
*/
_onMapBoundsChange: function (e) {
this.state.set({
mapCenter: e.get('newCenter'),
mapZoom: e.get('newZoom')
});
},
/**
* Создание перетаскиваемого маркера.
* @function
* @private
* @name MapView._createDraggableMarker
*/
_createDraggableMarker: function (placemark, color,title,hint,content) {
return new ymaps.Placemark(placemark, {
iconcontent: title,
balloonContent: content,
hintContent: 'Перетащите метку'
}, {
preset: color,
draggable: true,
});
}
};
/**
* Класс DOM-отображения Инструмента определения координат.
* @class
* @name DOMView
*/
LocationTool.DOMView = function () {
this._element = $('form');
}
/**
* @lends DOMView.prototype
*/
LocationTool.DOMView.prototype = {
/**
* @constructor
*/
constructor: LocationTool.DOMView,
/**
* Отображаем изменений данных в DOM-структуре.
* @function
* @name DOMView.render
* @param {Object} data Объект с полями "mapCenter", "mapZoom" и "markerPosition".
*/
render: function (data) {
$.each(data, $.proxy(this._setData, this));
},
/**
* Очистка DOM-отображения.
* @function
* @private
* @name DOMView.clear
*/
clear: function () {
this._element.remove();
},
/**
* Форматируем координату до 6-ти точек после запятой.
* @function
* @private
* @name DOMView._toFixedNumber
* @param {Number|String} coords Широта или Долгота.
* @returns {Number} Число фиксированной длины.
*/
_toFixedNumber: function (coords) {
return Number(coords).toFixed(8);
},
/**
* Обновление значений полей формы.
* @function
* @private
* @name DOMView._setData
* @param {String} id Идентификатор поля.
* @param {Number|String} value Новое значение поля.
*/
_setData: function (id, value) {
this._element
.find('#' + id)
.val(
$.isArray(value)?
$.map(value, this._toFixedNumber).join(', ') : value
);
}
};
<?php
use App\Map;
use \App\Langs;
use SleepingOwl\Admin\Model\ModelConfiguration;
use Illuminate\Database\Eloquent\Model;
AdminSection::registerModel(Map::class, function (ModelConfiguration $model) {
$model->setTitle('Схема проезда');
$model->disableDeleting();
$model->onDisplay(function () {
$display = AdminDisplay::table()->paginate(10);
$display->setApply(function ($query) {
$query->orderBy('order', 'asc');
});
$display->setHtmlAttribute('class', 'table-info table-hover');
$display->setColumns([
AdminColumn::link('translation_ru.page_name')
->setLabel('Наименование страницы')
->setWidth('250px'),
AdminColumn::datetime('updated_at')->setLabel('Последние изменения'),
AdminColumnEditable::checkbox('online')->setLabel('Активная страница'),
AdminColumn::order()
->setLabel('Порядок показа в меню')
->setHtmlAttribute('class', 'text-center')
->setWidth('200px'),
]);
return $display;
});
// Create And Edit
$model->onCreateAndEdit(function () {
$langs = Langs::all();
$tabs = AdminDisplay::tabbed();
$tabs->setTabs(function ($id) use ($langs) {
$tabs = [];
foreach ($langs as $lang) {
$relationship = 'translation_' . $lang->locale;
$tabs[] = AdminDisplay::tab(AdminForm::elements([
AdminFormElement::text($relationship . '.iconcontent', 'Текст на метке')->required(),
AdminFormElement::text($relationship . '.hintContent', 'Текст при наведении на метку'),
AdminFormElement::text($relationship . '.balloonContent', 'Текст при нажатии на метку'),
]))->setLabel($lang->full_name);
}
return $tabs;
});
$map = Map::find(1);
$array_map = [
'markerPosition' => $map->markerPosition,
'mapZoom' => $map->mapZoom,
'mapCenter' => $map->mapCenter,
'color' => $map->preset,
'hint' => $map->hintContent,
'title' => $map->iconcontent,
'content' => $map->balloonContent
];
$form = AdminForm::panel()->addBody([
AdminFormElement::view('mapyandexextend',$array_map),
AdminFormElement::text('markerPosition', 'Координаты позиции метки'),
AdminFormElement::text('mapZoom', 'Масштаб карты'),
AdminFormElement::text('mapCenter', 'Координаты центра карты'),
AdminFormElement::checkbox('online', 'Показывать схему проезда на странице контактов'),
AdminFormElement::select('preset', 'Выберите цвет метки на карте')
->setOptions([
'islands#blueStretchyIcon' => 'Синий',
'islands#lightblueStretchyIcon' => 'Светло-синий',
'islands#darkBlueStretchyIcon' => 'Темно-синий',
'islands#greenStretchyIcon' => 'Зеленый',
'islands#darkgreenStretchyIcon' => 'Темно-зеленый',
'islands#yellowStretchyIcon' => 'Желтый',
'islands#orangeStretchyIcon' => 'Оранжевый',
'islands#darkorangeStretchyIcon' => 'Темно-оранжевый',
'islands#pinkStretchyIcon' => 'Розовый',
'islands#redStretchyIcon' => 'Красный',
'islands#violetStretchyIcon' => 'Фиолетовый',
'islands#greyStretchyIcon' => 'Серый',
'islands#brownStretchyIcon' => 'Коричневый'
])->required(),
])->addheader($tabs);
$form->getButtons()
->hideSaveAndCloseButton();
return $form;
});
$model->updated(function ($config, Model $content) {
redirect('/admin');
});
// $model->updating(function ($config, Model $content) {
// dd($_POST);
// });
$model->creating(function ($config, Model $content) {
onCreatingAndUpdatingMap($config, $content);
});
function onCreatingAndUpdatingMap($config, Model $content)
{
foreach ($content->getRelations() as $key => $relation) {
if (!strpos($key, 'translation')) {
$locale = substr($key, 12);
// $lang_id = Lang::where('locale', $locale)->first()->id;
$content->{$key}->locale = $locale;
// $content->{$key}->updated_at =time();
}
}
}
});
<div class="panel" style="padding: 10px; margin: 5px;">
{{--<script src="//yandex.st/jquery/1.8.0/jquery.min.js" type="text/javascript"></script>--}}
<script src="https://api-maps.yandex.ru/2.0-stable/?load=package.standard&lang=ru-RU" type="text/javascript"></script>
<script src="/js/location-tool.js" type="text/javascript"></script>
<script src="/js/cross-control.js" type="text/javascript"></script>
<script src="/js/geolocation-button.js" type="text/javascript"></script>
<script type="text/javascript">
ymaps.ready(function () {
var myMap = new ymaps.Map('YMapsID', {
center: [{{ $mapCenter }}],
zoom: {{ $mapZoom }},
behaviors: ['default', 'scrollZoom']
});
myMap.controls
.add('mapTools')
.add(new CrossControl)
.add('zoomControl')
.add('typeSelector', {top: 5, right: 5})
.add(new ymaps.control.SearchControl({noPlacemark: true}), {top: 5, left: 200});
new LocationTool(myMap, [{{$markerPosition}}], "{{ $color }}", "{{ $title }}", "{{ $hint }}", "{{ $content }}");
});
</script>
<style type="text/css">
#YMapsID {
width: 900px;
height: 400px;
}
.hero-unit p {
line-height: 20px;
}
.cross-control {
background: url(/img/center.gif) no-repeat;
position: absolute;
width: 16px;
height: 16px;
}
.centered{
margin: 0 auto;
}
</style>
<div class="centered text-center"><strong>Перетащите метку на карте в нужное место.</strong></div>
<div class="centered text-center" id="YMapsID"></div>
</div>
@Atmden
Copy link
Author

Atmden commented Jul 31, 2018

image
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment