templates/Map/page.html.twig line 1

Open in your IDE?
  1. {% extends 'grid.html.twig' %}
  2. {% block mainContainer %}
  3.    {# {{ form(form) }}#}
  4.     <div id="map_form"></div>
  5.     <p>
  6.         <button type="button" id="map_refresh" disabled>Обновить карту</button>
  7.         <button type="button" id="map_reset" disabled>Сбросить фильтры</button>
  8.         <span id="map_status" role="status" aria-live="polite">Загрузка карты…</span>
  9.     </p>
  10.     <img src="{{ asset('/static/preloader.gif') }}" id="preloader" style="display: none;" />
  11.     <div id="map"
  12.          data-city-uri-idenity="{{ cityUriIdentity }}"
  13.          data-city-latitude="{{ cityLatitude }}"
  14.          data-city-longitude="{{ cityLongitude }}"
  15.          data-map-context="{{ mapContext }}"
  16.          style="width:100%; height:500px;"></div>
  17. {% endblock %}
  18. {% block javascripts %}
  19.     <script src="{{ asset('bundles/fosjsrouting/js/router.min.js') }}"></script>
  20.     <script src="{{ path('fos_js_routing_js', { callback: 'fos.Router.setData' }) }}"></script>
  21.     <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
  22.     <script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU" type="text/javascript"></script>
  23.     <script type="text/javascript">
  24.         ;var RoutingDecorator = {
  25.             generate: function (name, params) {
  26.                 const orig_name = name;
  27.                 try {
  28.                     Routing.getRoute(name)
  29.                 } catch(error) {
  30.                     name = name + '.' + '{{ app.request.locale }}';
  31.                     try {
  32.                         Routing.getRoute(name)
  33.                     } catch(error) {
  34.                         name = orig_name;
  35.                     }
  36.                 }
  37.                 return Routing.generate(name, params);
  38.             }
  39.         };
  40.         ;(function () {
  41.             document.addEventListener('DOMContentLoaded', _ => {
  42.                 var avatarPath = '{{ asset('', 'profile_media_avatar') }}';
  43.                 var photoPath = '{{ asset('', 'profile_media') }}';
  44.                 var thumbPath = '{{ asset('', 'saloon_media_thumb') }}';
  45.                 var mapContainer = document.querySelector('#map');
  46.                 var preloader = document.querySelector('#preloader');
  47.                 var refreshButton = document.querySelector('#map_refresh');
  48.                 var resetButton = document.querySelector('#map_reset');
  49.                 var mapStatus = document.querySelector('#map_status');
  50.                 var loading = false;
  51.                 var mapContext = mapContainer.getAttribute('data-map-context');
  52.                 var cityUriIdentity = mapContainer.getAttribute('data-city-uri-idenity');
  53.                 var cityLatitude = mapContainer.getAttribute('data-city-latitude');
  54.                 var cityLongitude = mapContainer.getAttribute('data-city-longitude');
  55.                 var formName = 'filter_map_form';
  56.                 var multipleCities = {{ multipleCities }};
  57.                 var routesUrl = '/js/fos_js_routes{{ multipleCities ? '_city_prefixed' }}.json';
  58.                 if (typeof ymaps === 'undefined') {
  59.                     mapStatus.textContent = 'Не удалось загрузить Яндекс.Карты. Перезагрузите страницу.';
  60.                     return;
  61.                 }
  62.                 var mapLoadTimeout = setTimeout(() => {
  63.                     mapStatus.textContent = 'Не удалось загрузить Яндекс.Карты. Перезагрузите страницу.';
  64.                 }, 15000);
  65.                 loadRoutes();
  66.                 var myMap, clusterer;
  67.                 ymaps.ready(function () {
  68.                     clearTimeout(mapLoadTimeout);
  69.                     myMap = new ymaps.Map('map', {
  70.                             center: [cityLatitude, cityLongitude],
  71.                             zoom: 9,
  72.                             behaviors: ['default', 'scrollZoom']
  73.                         }, {
  74.                             searchControlProvider: 'yandex#search'
  75.                         });
  76.                     clusterer = new ymaps.Clusterer({
  77.                             preset: 'islands#invertedVioletClusterIcons',//Через кластеризатор можно указать только стили кластеров,стили для меток нужно назначать каждой метке отдельно.
  78.                             groupByCoordinates: false,//Ставим true, если хотим кластеризовать только точки с одинаковыми координатами.
  79.                             //Опции кластеров указываем в кластеризаторе с префиксом "cluster".
  80.                             clusterDisableClickZoom: true,
  81.                             clusterHideIconOnBalloonOpen: false,
  82.                             geoObjectHideIconOnBalloonOpen: false
  83.                     });
  84.                     // Можно менять опции кластеризатора после создания.
  85.                     clusterer.options.set({
  86.                         gridSize: 80,
  87.                         clusterDisableClickZoom: true
  88.                     });
  89.                     loadForm();
  90.                     refreshButton.addEventListener('click', function () {
  91.                         if (document.querySelector('form[name="' + formName + '"]')) {
  92.                             filterData();
  93.                         } else {
  94.                             loadForm();
  95.                         }
  96.                     });
  97.                     resetButton.addEventListener('click', function () {
  98.                         document.querySelector('form[name="' + formName + '"]').reset();
  99.                         filterData();
  100.                     });
  101.                 });
  102.                 function loadRoutes() {
  103.                     $.ajax({
  104.                         url:routesUrl,
  105.                         dataType: "json",
  106.                         async: false,
  107.                         success: function (routes) {
  108.                             Routing.setRoutingData(routes);
  109.                         },
  110.                         error: function (xhr, ajaxOptions, thrownError) {}
  111.                     });
  112.                 }
  113.                 function setFormSubmitListener() {
  114.                     document.querySelector("form[name="+formName+"]").addEventListener('submit', function(e){
  115.                         e.preventDefault();
  116.                         if(!clusterer)
  117.                             return;
  118.                         filterData();
  119.                         return false;
  120.                     });
  121.                 }
  122.                 function loadForm() {
  123.                     setLoading(true);
  124.                     fetch('{{ path('map.form', {city: cityUriIdentity}) }}', {
  125.                         mode: "cors",
  126.                         importance: "low",
  127.                         method: "POST",
  128.                         cache: 'no-store',
  129.                         headers: {
  130.                             "Content-Type": "application/x-www-form-urlencoded",
  131.                             "X-Requested-With": "XMLHttpRequest"
  132.                         },
  133.                         body: new URLSearchParams({mapContext: mapContext}).toString(),
  134.                     }).then(response => {
  135.                         if (!response.ok) {
  136.                             throw new Error('Cannot load map form');
  137.                         }
  138.                         return response.text();
  139.                     }).then(response => {
  140.                         document.querySelector('#map_form').innerHTML = response;
  141.                         setFormSubmitListener();
  142.                         setLoading(false);
  143.                         filterData();
  144.                     }).catch(() => {
  145.                         showError();
  146.                         setLoading(false);
  147.                     });
  148.                 }
  149.                 function setLoading(value) {
  150.                     loading = value;
  151.                     preloader.style.display = value ? 'block' : 'none';
  152.                     refreshButton.disabled = value;
  153.                     resetButton.disabled = value || !document.querySelector('form[name="' + formName + '"]');
  154.                     if (value) {
  155.                         mapStatus.textContent = 'Загрузка…';
  156.                     }
  157.                 }
  158.                 function showError() {
  159.                     mapStatus.textContent = 'Не удалось обновить карту. Нажмите «Обновить карту», чтобы повторить запрос.';
  160.                 }
  161.                 function filterData() {
  162.                     if (loading) {
  163.                         return;
  164.                     }
  165.                     setLoading(true);
  166.                     var data = {};
  167.                     $("form[name="+formName+"]").serializeArray().forEach((object)=>{
  168.                         //"подгоняем" данные под формат, ожидаемый формой при сабмите
  169.                         var fieldName = object.name.toString().replace(formName + '[', '').replace(']', '');
  170.                         var clearFieldName = fieldName.substr(0, fieldName.indexOf('['));
  171.                         if(~fieldName.indexOf('[]')) {
  172.                             checkArrayIsInFormObject(clearFieldName, data);
  173.                             data[clearFieldName].push(object.value);
  174.                         } else if(~fieldName.indexOf('[m')) {
  175.                             if(!object.value)
  176.                                 return;
  177.                             checkObjectIsInFormObject(clearFieldName, data);
  178.                             if(~fieldName.indexOf('[min]'))
  179.                                 data[clearFieldName]['min'] = object.value;
  180.                             if(~fieldName.indexOf('[max]'))
  181.                                 data[clearFieldName]['max'] = object.value;
  182.                         } else {
  183.                             data[fieldName] = object.value;
  184.                         }
  185.                     });
  186.                     $.ajax({
  187.                         url:'{{ path('map.filter', {city:cityUriIdentity}) }}',
  188.                         type: "POST",
  189.                         dataType: "json",
  190.                         cache: false,
  191.                         data: {
  192.                             "form": JSON.stringify(data),
  193.                             "mapContext": mapContext,
  194.                             "scale": myMap.getZoom(),
  195.                         },
  196.                         async: true,
  197.                     }).then(getDetailsData).then(points => {
  198.                         updateMap(points);
  199.                         mapStatus.textContent = 'Анкеты: ' + points.profiles.length + '. Салоны: ' + points.saloons.length + '.';
  200.                     }).catch(showError).always(() => setLoading(false));
  201.                 }
  202.                 function checkArrayIsInFormObject(name, formObject) {
  203.                     if(false == formObject.hasOwnProperty(name))
  204.                         formObject[name] = [];
  205.                 }
  206.                 function checkObjectIsInFormObject(name, formObject) {
  207.                     if(false == formObject.hasOwnProperty(name))
  208.                         formObject[name] = {};
  209.                 }
  210.                 function getDetailsData(data) {
  211.                     const profilesRawData = data.profiles || [];
  212.                     const saloonsRawData = data.saloons || [];
  213.                     const profilesAllId = profilesRawData.map(_data => _data[0]).flat();
  214.                     const saloonsAllId = saloonsRawData.map(_data => _data[0]).flat();
  215.                     const profileArray = Array.isArray(profilesAllId) ? profilesAllId : [];
  216.                     const saloonIdArray = Array.isArray(saloonsAllId) ? saloonsAllId : [];
  217.                     const requestBody = {};
  218.                     requestBody.profiles = profileArray;
  219.                     requestBody.saloons = saloonIdArray;
  220.                     return fetch('{{ path('map.detail', {city:cityUriIdentity}) }}', {
  221.                         method: "POST",
  222.                         cache: 'no-store',
  223.                         headers: {
  224.                             'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  225.                             'X-Requested-With': 'XMLHttpRequest',
  226.                         },
  227.                         body: new URLSearchParams(requestBody).toString()
  228.                     })
  229.                     .then(response => {
  230.                         if (!response.ok) {
  231.                             throw new Error('Cannot load map details');
  232.                         }
  233.                         return response.json();
  234.                     });
  235.                 }
  236.                 function updateMap(points) {
  237.                     clusterer.removeAll();
  238.                     if (!points || (points.profiles.length == 0 && points.saloons.length == 0)) {
  239.                         myMap.setCenter([cityLatitude, cityLongitude], 9);
  240.                         return;
  241.                     }
  242.                     const mergedPoints = [...points.profiles, ...points.saloons];
  243.                     var getImgUrl = function(path, imgType) {
  244.                         if(path[0] == '/')
  245.                             return path;
  246.                             console.log(imgType)
  247.                             console.log(photoPath)
  248.                             console.log(thumbPath)
  249.                         var assetPackagePath = '';
  250.                         if(imgType == 'avatar')
  251.                             assetPackagePath = avatarPath;
  252.                         else if(imgType == 'photo')
  253.                             assetPackagePath = photoPath; // /media/profiles/vp/rd/vprdP4ShlKUpkOB.jpg
  254.                         else if(imgType == 'thumb')
  255.                             assetPackagePath = thumbPath; // /media/saloons/thumb///.jpg
  256.                         return `${assetPackagePath}${path.slice(0, 2).toLowerCase()}/${path.slice(2, 4).toLowerCase()}/${path}.jpg`;
  257.                     };
  258.                     var getPointData = function (point) {
  259.                         const isProfile = point[0] == 1;
  260.                         const isSaloon = point[0] == 2;
  261.                         let url = '';
  262.                         const uri = point[3];
  263.                         if(isProfile) {
  264.                             url = RoutingDecorator.generate(
  265.                                 'profile_preview.page',
  266.                                 multipleCities ? {city: cityUriIdentity, profile: uri} : {profile: uri}
  267.                             );
  268.                         } else if(isSaloon) {
  269.                             url = RoutingDecorator.generate(
  270.                                 'saloon_preview.page',
  271.                                 multipleCities ? {city: cityUriIdentity, saloon: uri} : {saloon: uri}
  272.                             );
  273.                         }
  274.                         var processProfileShowsUrl = '{{ path('map.increment_profile_shows', multipleCities ? {city:cityUriIdentity} : {}) }}';
  275.                         return {
  276.                             balloonContentHeader: '',
  277.                             balloonContentBody: '<p><img src="' + getImgUrl(point[5], isProfile ? 'avatar' : 'thumb') + '" width="100" /></p>' + '<font size=3><b><a target="_blank" href="' + url + '">Профиль</a></b></font>'
  278.                                 + '<img style="display:none;" width="0" height="0" src="' + processProfileShowsUrl + '?id=' + (isProfile ? point[9] : null)+'" />', //не убирать, функционально важно
  279.                             balloonContentFooter: '<font size=1>' + point[8] + ',' + point[9] + ',' + point[10] + '</strong>',
  280.                             clusterCaption: '<strong>' + (isSaloon ? 'Салон ' : '') + point[4] + '</strong>'
  281.                         };
  282.                     };
  283.                     var getPointOptions = function (index, type) {
  284.                         return {
  285.                             preset: type == 'profile' ? 'islands#violetIcon' : 'islands#redIcon'
  286.                         };
  287.                     };
  288.                     var geoObjects = [];
  289.                     for(var i = 0, len = mergedPoints.length; i < len; i++) {
  290.                         const _point = mergedPoints[i];
  291.                         geoObjects[i] = new ymaps.Placemark([_point[1], _point[2]], getPointData(_point), getPointOptions(i, _point[0] == 1));
  292.                     }
  293.                     //В кластеризатор можно добавить javascript-массив меток (не геоколлекцию) или одну метку.
  294.                     clusterer.add(geoObjects);
  295.                     myMap.geoObjects.add(clusterer);
  296.                     // Спозиционируем карту так, чтобы на ней были видны все объекты.
  297.                     if (mergedPoints.length === 1) {
  298.                         myMap.setCenter([mergedPoints[0][1], mergedPoints[0][2]], 14);
  299.                     } else {
  300.                         myMap.setBounds(clusterer.getBounds(), {
  301.                             checkZoomRange: true,
  302.                             zoomMargin: 40
  303.                         }).then(() => {
  304.                             if (myMap.getZoom() > 14) {
  305.                                 myMap.setZoom(14);
  306.                             }
  307.                         });
  308.                     }
  309.                 }
  310.             });
  311.         })();
  312.     </script>
  313. {% endblock %}