Skip to content

Instantly share code, notes, and snippets.

@Atmden
Created July 31, 2018 15:05
Show Gist options
  • Select an option

  • Save Atmden/73d9b2b2e2b61faf7028ef60438786e2 to your computer and use it in GitHub Desktop.

Select an option

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

Atmden commented Jul 31, 2018

Copy link
Copy Markdown
Author

image
image

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