Лучшие статьи и кейсы стартапов
Включить уведомления
Дадим сигнал, когда появится
что-то суперстоящее.
Спасибо, не надо
Вопросы Проекты Вакансии
Первая служебная сеть для бюджетников России и карта средних зарплат по областям
Рекомендуем
Продвинуть свой проект
Лучшие проекты за неделю
36
Эбиа

Эбиа

www.ebia.ru

16
Enlite

Enlite

enlited.ru

15
likearea

likearea

smm.li

14
Amarket

Amarket

amarket.io

14
Relap

Relap

relap.io

12
RockinRobin

RockinRobin

www.rockinrobin.co

11
Perezvoni.com

Perezvoni.com

perezvoni.com

11
Cookiezz

Cookiezz

cookiezz.com.ua

11
ПРОМКА24

ПРОМКА24

promka24.com

Показать следующие
Рейтинг проектов
Подписывайтесь на Спарк во ВКонтакте

Интерактивная карта средних зарплат по регионам России

618 4 В избранное Сохранено
Авторизуйтесь
Вход с паролем
Сложность: средняя. Необходимое время: 30 мин. В статье представлена инструкция по созданию своего компонента для движка InstantCMS2. В конце статьи приведена ссылка на архив с исходным кодом этой инструкции.

Туториал: компонент интерактивной SVG картограммы для InstantCMS 2

Сложность: средняя.

Необходимое время: 30 мин.

В статье представлена инструкция по созданию своего компонента для движка InstantCMS2. В конце статьи приведена ссылка на архив с исходным кодом и содержимым всех файлов из этой инструкции. Пример внешнего вида компонента, который можно создать, используя данный туториал, представлен на иллюстрации (картинка кликабельна).

95098f82c79d4e488865fc43c16cd4ee.jpg

Для начала несколько слов про движок соц.сети / сообщества / блогосоциальной сети InstantCMS2. Эта бесплатная CMS может являться отличным компромиссом, возможно, лучшим из существующих.

В базовой версии уже заложен более богатый функционал по сравнению с LiveStreet CMS.

Скриншот сравнения функционала не привожу, потому что по ссылке дана информация не по самой последней версии движка InstantCMS.

Достоинства и недостатки InstantCMS2

Из минусов - количество модулей, дополнений, тем для данного движка достаточно ограничено. Качество технической поддержки немного хромает. Живого активного сообщества вокруг данного движка нет, а регистрация на форуме вообще только по приглашению. Но все эти минусы с лихвой перекрывает факт бесплатности движка InstantCMS 2.

Из плюсов - из коробки предоставляется сразу: форум, профили пользователей с возможность добавления в друзья, статьи, блоги, новости, группы, фотоальбомы, статичные страницы сайта. А также комментарии, ленты RSS, поля RSS, возможность настраивать главное меню, нормальная модерация и вполне удобная админка.

Скачать движок InstantCMS 2 с функцией авто-установки можно с официального сайта проекта.

Процесс установки хорошо документирован и интуитивно понятен.

Свой компонент для InstantCMS2

Перейдем непосредственно к вопросу написания отдельного компонента.

Для создания нового компонента создайте папку, в которой будет ваш компонент (назовем его newcomponent), в директории \SiteDirectory\system\controllers\, т.е полный адрес к созданной директории будет \SiteDirectory\system\controllers\newcomponent\ - все буквы в названии компонента должны быть строчными, это важно!

Далее в этой папке создаем файл frontend.php - это главный файл, без которого компонент не будет работать.

В этом файле создаем класс с таким же названием. Название класса совпадает с названием папки. И этот класс наследуется от системного класса cmsFrontend.

В этом классе мы имеем возможность добавлять методы, описывающие действия компонента.

Что такое действия компонента? Давайте взглянем на следующее изображение:

5feb26b076394b32baad5ff8891a2557.png

Каждый адрес страницы состоит из нескольких сегментов:

  1. /controller - Название компонента.
  2. /action - Название действия. Каждый компонент может иметь несколько действий внутри себя.
  3. /p1/p2/p3/... - Любое количество параметров, необходимых для этого действия.

Как определяется действие компонента? Определяется публичный метод в классе компонента, который называется actionНазваниеДействияСБольшойБуквы. Для главной страницы компонента siteaddress.ru/newcomponent/ необходимо определить метод actionIndex(). Для внутренней страницы компонента siteaddress.ru/newcomponent/act/ необходимо определить метод actionAct().

Файл frontend.php

<?php

class newcomponent extends cmsFrontend {

    public function actionIndex() {
        
        $template = cmsTemplate::getInstance();
        
        $template->render('index');
        
    }

    public function actionAct() {
        
        $errors = false;
        
        $form = $this->getForm('newForm');
        
        $is_submitted = $this->request->has('submit');
        
        $newForm = $form->parse($this->request, $is_submitted);
        
        if ($is_submitted){
            $errors = $form->validate($this, $newForm);
            
            if (!errors) {
                $choropleth = $this->model->getChoropleth($newForm);
            }
            
            if (!errors) {
                cmsUser::addSessionMessage(LANG_FORM_ERRORS, 'error');
            } 
        }
        
        $template = cmsTemplate::getInstance();
        
        $template->render('act', array(
            'form' => $form,
            'errors' => $errors,
            'newForm' => $newForm
        ));
        
    }

}

?>

Внимательный читатель заметил использование метода $this->model->getChoropleth().

Для использования методов модели в директории \SiteDirectory\system\controllers\newcomponent\ создаем файл model.php

Однако читать данные мы будем из файлов, поэтому поставим здесь заглушку. Описание файла модели приведено с целью обучения.

Файл model.php

<?php

class modelNewComponent extends cmsModel {

    public function getChoropleth($average_zarplata) {
        
        $choropleth = array();
        
        return $choropleth;
        
    }

}

?>

Строка $template->render('index'); определяет вывод настоящего шаблона, который должен быть создан в директории \SiteDirectory\templates\default\controllers\newcomponent\. Где \default - название используемой темы на сайте (можно найти и скачать новую тему с сайта сообщества instantcms и изменить используемую тему через админку), папку \newcomponent необходимо будет создать самостоятельно, это папка для шаблонов нового компонента.

В этой папке должен быть создан файл index.tpl.php для главной страницы компонента, и act.tpl.php - для внутренней.

Файл index.tpl.php

<?php

    $this->setPageTitle('Заголовок страницы в названии окна браузера');
    $this->addBreadcrumb('Название страницы в цепи хлебных крошек');
    
    $this->addToolButton(array(
        'class' => 'item',
        'title' => 'Название кнопки в меню действий для перехода на внутреннюю страницу компонента',
        'href' => $this->href_to('act')
    ));
    
?>

<h1>Главный заголовок страницы</h1>
<p>Содержание страницы</p>

Внутренняя страница компонента будет содержать форму выбора параметров.

Для начала создадим папку \forms\ в папке нашего компонента \SiteDirectory\system\controllers\newcomponent\.

В директории \SiteDirectory\system\controllers\newcomponent\forms\ создаем файл form_newForm.php

Форма будет очень простой, она предлагает пользователю выбрать два параметра из выпадающих списков.

Файл form_newForm.php

<?php

class formNewcomponentnewform extends cmsForm {

    public function init() {
        
        return array(
        
            array(
                'type' => 'fieldset',
                'childs' => array (
                    new fieldList('par1', array(
                        'title' => 'Параметр1',
                        'items' => array (
                            "ТекстовыйИдентификатор1"     =>       "ТекстовыйПараметр1",
                            "ТекстовыйИдентификатор2"     =>       "ТекстовыйПараметр2"

                        )
                    )),
                    new fieldList('par2', array(
                        'title' => 'Параметр2',
                        'items' => array (
                            1     =>       "1",
                            2     =>       "2"

                        )
                    ))
                
                )
                
            )
        
        );
        
    }

}

?>

Интерактивная SVG картограмма

После этого перейдем к созданию шаблона для внутренней страницы компонента /act - создаем файл act.tpl.php и размещаем его в директории \SiteDirectory\templates\default\controllers\newcomponent\.

Для создания уникального сервиса воспользуемся разработкой пользователя @KoGor (пользуясь случаем, хочу передать огромную благодарность за проведенный @KoGor 'ом труд и хорошо оформленную и интуитивно понятную статью) - инфограммой карты Российской Федерации с распределением по регионам.

В результате, у нас должна получится примерно такая приятная карта России:

e4220a0b0fcc4f06b710b42265ccce78.png

Файл act.tpl.php

<?php

    $this->setPageTitle('Заголовок страницы в названии окна браузера');
    $this->addBreadcrumb('Название главной страницы компонента в цепи хлебных крошек', $this->href_to(''));
    $this->addBreadcrumb('Название страницы в цепи хлебных крошек');

    $arr_par1_id = array('ТекстовыйИдентификатор1'    =>    1    ,
                        'ТекстовыйИдентификатор2'    =>    2        
                    );


    $filename='/upload/zarplata-'.$arr_par1_id[$_GET['par1']].'-'.$_GET['par2'].'.csv';    
    if (!isset ($_GET['par1']) || !isset ($_GET['par2'])) $filename='/upload/zarplata-1-1.csv';

    $this->renderForm($form, $newForm, array(
        'action' => '',
        'method' => 'get',
        'toolbar' => false
    ), $errors);
?>

  <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
  <script type="text/javascript" src="http://d3js.org/queue.v1.min.js"></script>
  <script type="text/javascript" src="http://d3js.org/topojson.v0.min.js"></script>
  <!-- <script type="text/javascript" src="http://d3js.org/topojson.v1.min.js"></script> -->
  
<style>

path {
  stroke:white;
  stroke-width: 1px;
}

body {
  font-family: Arial, sans-serif;
}

.city {
  font: 10px sans-serif;
  font-weight: bold;
}

.legend {
  font-size: 12px;
}

div.tooltip {   
  position: absolute;           
  text-align: center;           
  width: 150px;                  
  height: 25px;                 
  padding: 2px;             
  font-size: 10px;     
  background: #FFFFE0;
  border: 1px;      
  border-radius: 8px;           
  pointer-events: none;         
}        
</style>

  <script type="text/javascript">
  var width = 720,
  height = 375;

  // Setting color domains(intervals of values) for our map

  var color_domain = [10000, 15000, 20000, 30000, 50000]
  var ext_color_domain = [0, 10000, 15000, 20000, 30000, 50000]
  var legend_labels = ["до 10000 руб.", "10000-15000 руб.", "15000-20000 руб.", "20000-30000 руб.", "30000-50000 руб.", "от 50000 руб."]              
  var color = d3.scale.threshold()
  .domain(color_domain)
  .range(["#ff1300", "#ff4e40", "#ff7d73", "#ffba00", "#ffcb40", "#adfcad"]);

  var div = d3.select("form").append("div")   
  .attr("class", "tooltip")               
  .style("opacity", 0);

  var svg = d3.select("form").append("svg")
  .attr("width", width)
  .attr("height", height)
  .style("margin", "10px auto");

  var projection = d3.geo.albers()
  .rotate([-105, 0])
  .center([-10, 65])
  .parallels([52, 64])
  .scale(500)
  .translate([width / 2, height / 2]);

  var path = d3.geo.path().projection(projection);

  //Reading map file and data

  queue()
  .defer(d3.json, "/upload/russia.json")
  .defer(d3.csv, "<?php echo $filename; ?>")
  .await(ready);

  //Start of Choropleth drawing

  function ready(error, map, data) {
   var rateById = {};
   var nameById = {};

   data.forEach(function(d) {
    rateById[d.RegionCode] = +d.AverageZarplata;
    nameById[d.RegionCode] = d.RegionName;
  });

  //Drawing Choropleth

  svg.append("g")
  .attr("class", "region")
  .selectAll("path")
  .data(topojson.object(map, map.objects.russia).geometries)
  //.data(topojson.feature(map, map.objects.russia).features) <-- in case topojson.v1.js
  .enter().append("path")
  .attr("d", path)
  .style("fill", function(d) {
    return color(rateById[d.properties.region]); 
  })
  .style("opacity", 0.8)

  //Adding mouseevents
  .on("mouseover", function(d) {
    d3.select(this).transition().duration(300).style("opacity", 1);
    div.transition().duration(300)
    .style("opacity", 1)
    div.text(nameById[d.properties.region] + " : " + rateById[d.properties.region])
    .style("left", (d3.event.pageX) + "px")
    .style("top", (d3.event.pageY -30) + "px");
  })
  .on("mouseout", function() {
    d3.select(this)
    .transition().duration(300)
    .style("opacity", 0.8);
    div.transition().duration(300)
    .style("opacity", 0);
  })
  
   // Adding cities on the map

  d3.tsv("/upload/cities.tsv", function(error, data) {
    var city = svg.selectAll("g.city")
    .data(data)
    .enter()
    .append("g")
    .attr("class", "city")
    .attr("transform", function(d) { return "translate(" + projection([d.lon, d.lat]) + ")"; });

    city.append("circle")
    .attr("r", 3)
    .style("fill", "lime")
    .style("opacity", 0.75);

    city.append("text")
    .attr("x", 5)
    .text(function(d) { return d.City; });
  });
  
  }; // <-- End of Choropleth drawing
 
  //Adding legend for our Choropleth

  var legend = svg.selectAll("g.legend")
  .data(ext_color_domain)
  .enter().append("g")
  .attr("class", "legend");

  var ls_w = 20, ls_h = 20;

  legend.append("rect")
  .attr("x", 20)
  .attr("y", function(d, i){ return height - (i*ls_h) - 2*ls_h;})
  .attr("width", ls_w)
  .attr("height", ls_h)
  .style("fill", function(d, i) { return color(d); })
  .style("opacity", 0.8);

  legend.append("text")
  .attr("x", 50)
  .attr("y", function(d, i){ return height - (i*ls_h) - ls_h - 4;})
  .text(function(d, i){ return legend_labels[i]; });

  </script>

Исходные данные для картограммы

Для того, чтобы карта заработала, остался последний шаг. Размещаем файлы cities.tsv, russia.json, zarplata-1-1.csv, zarplata-1-2.csv, zarplata-2-1.csv, zarplata-2-2.csv (приведены в архиве, ссылка на который есть в конце статьи) в директории \SiteDirectory\upload\.

Заходим по адресу siteaddress.ru/newcomponent/act/ - здесь все регионы на карте России подкрашены темно-серым цветом и при наведении появляется название региона - NaN. Для отображения каких-нибудь реальных данных замените значения в последнем столбце .csv файлов на численные данные.

Демо на ЗарплатаБюджетников.РФ

Подобный модуль разработан мною для сайта ЗарплатаБюджетников.РФ в разделе Карта зарплат. Демо модуля можно посмотреть по ссылке.

Бонус

Напоследок, небольшой хинт. В дефолтном шаблоне по умолчанию в InstantCMS 2 боковая колонка вместе с меню действий пропадает при уменьшении ширины окна браузера. Но на мобильных девайсах исчезновение боковой колонки и меню действий очень не удобно, т.к. у пользователей пропадает довольно таки много возможных действий. Для изменения этой ситуации можно проделать следующее. Найдите в директории \templates\default\css\ файл theme-layout.css, и замените в нем кусочек кода

/* Media Queries ============================================================ */

@media screen and (max-width: 980px) {
    #body section { width:100% !important; }
    #body aside { display:none; }
}

@media screen and (max-width: 800px) {
    #body section { width:100% !important; }
    #body aside { display:none; }

на

/* Media Queries ============================================================ */

@media screen and (max-width: 980px) {
    #body section { width:100% !important; }
    #body aside { width:100% !important; }
}

@media screen and (max-width: 800px) {
    #body section { width:100% !important; }
    #body aside { width:100% !important; }

Т.е. по факту необходимо исправить всего 2 строчки #body aside { display:none; } на #body aside { width:100% !important; } - после этого боковая колонка при уменьшении ширины браузера будет съезжать в основную колонку после находящегося в нем контента (перед футером).

Исходный код

Все описанные скрипты и файлы можно скачать в архиве, который нужно будет просто загрузить и распаковать в корневую директорию вашего сайта, работающего на InstantCMS2.

P.S. О замеченных опечатках, ошибках или неточностях прошу писать в личные сообщения.

+1
Первые Новые Популярные
Taras Nikitin
Хоть бы уведомление какое выводили если данных нет для отображения, а то я уж грешным делом подумал, что вы в верстке или коде где-то ошиблись, когда никакой карты не увидел с параметрами "Все" и "2015". Вместо карты была просто белая область экрана.
Ёщкин кот! Так у вас ещё и если даных нет, например при выборке по "Работники прокуратуры" и "2014" то на карте выводятся NaN (это хорошо я знаю что такое NaN, а как люди будут с этим жить - не понятно).
Сырая у вас система в части карты, а заметка то о карте. Эх.
Ответить
ЗарплатаБюджетников.РФ
Первая служебная сеть для бюджетников России и карта средних зарплат по областям
Шайхутдинов Артур
Спасибо за обратную связь! Постараюсь сделать сервис поудобнее.
Ответить
LitTime
Ваш проводник в мире литературы
Скребнёв Алексей
У вас не хватает на карте Крыма.
Живу в Смоленске, совсем недавно видел данные РБК, что средняя зарплата у нас в районе 16к. Стало лучше?
Ответить
ЗарплатаБюджетников.РФ
Первая служебная сеть для бюджетников России и карта средних зарплат по областям
Шайхутдинов Артур
Да, пока точной векторной карты нет по причине отсутствия данных Росреестра, но данные по Республике Крым усреднены совместно с Краснодарским краем (ближайший регион) - это число можно увидеть в данных Красндарского края.
Ответить
Выбрать файл
Читайте далее
Загружаем…
Блог проекта
Расскажите историю о создании или развитии проекта, поиске команды, проблемах и решениях
Написать
Личный блог
Продвигайте свои услуги или личный бренд через интересные кейсы и статьи
Написать