Свернуть все | Развернуть все | Скрыть панель

Содержание

Платформа CUBA. Отображение диаграмм и карт

Версия 5.6    Более новая версия доступна в разделе документации.


Содержание

Предисловие
1. Отображение диаграмм
1.1. Конфигурация диаграмм
1.2. Связь с данными
1.3. События
1.4. Пример работы с диаграммами
1.4.1. Настройка проекта приложения
1.4.2. Создание сервиса для извлечения данных
1.4.3. Создание диаграмм
1.4.4. Использование событий
2. Отображение карт
2.1. Возможности отображения карты
2.2. Настройка проекта приложения
2.3. Компонент MapViewer

Предисловие

Данный документ содержит описание двух функциональных модулей платформы CUBA - подсистем отображения диаграмм и географических карт. Эти подсистемы реализованы в одном базовом проекте платформы - charts, и могут быть подключены в прикладной проект только вместе.

Функциональность отображения диаграмм и карт в настоящий момент доступна только в веб клиенте приложения.

1. Целевая аудитория

Данное руководство предназначено для разработчиков приложений на платформе CUBA. Предполагается, что читатель ознакомлен с Руководством по разработке приложений, доступным по адресу www.cuba-platform.ru/manual.

2. Дополнительные материалы

Настоящее Руководство, а также другая документация по платформе CUBA доступны по адресу www.cuba-platform.ru/manual.

Подсистема отображения диаграмм платформы CUBA основана на библиотеке AmCharts, поэтому знакомство с ее устройством будет полезным. См. www.amcharts.com/.

3. Обратная связь

Если у Вас имеются предложения по улучшению данного руководства, обратитесь, пожалуйста, в службу поддержки по адресу ru.cuba-platform.com/support/topics.

При обнаружении ошибки в документации укажите, пожалуйста, номер главы и приведите небольшой участок окружающего текста для облегчения поиска.

Глава 1. Отображение диаграмм

Подсистема отображения диаграмм платформы CUBA поддерживает большое количество типов диаграмм: круговые, линейные, пузырьковые, лепестковые, диаграммы с накоплением и прочие. Имеется возможность экспорта диаграмм. Для большинства типов диаграмм поддерживается прокрутка и зуммирование. На момент написания настоящего руководства подсистема отображения диаграмм работает только в блоке Web Client для Vaadin7.

Библиотека AmCharts, на которой основана реализация подсистемы отображения диаграмм, распространяется по лицензии, позволяющей использовать ее бесплатно при сохранении ссылки на сайт библиотеки. Для своего проекта Вы можете купить лицензию на AmCharts и убрать ссылку.

1.1. Конфигурация диаграмм

Для отображения диаграмм используется компонент Chart, являющийся универсальным холстом. Вид диаграммы задается его свойством configuration типа AbstractChart.

Рисунок 1. Иерархия видов диаграмм

Иерархия видов диаграмм

Диаграммы можно описывать в XML-дескрипторе экрана. Для этого необходимо подключить соответствующий namespace:

<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd"
        ...>

Соответствие элементов XML видам диаграмм:

  • chart:xyChart - XYChart

  • chart:serialChart - SerialChart

  • chart:pieChart - PieChart

  • chart:funnelChart - FunnelChart

  • chart:gaugeChart - AngularGaugeChart

  • chart:radarChart - RadarChart

Каждый вид диаграммы имеет свой набор атрибутов и методов, которые повторяют функционал соответствующих диаграмм библиотеки AmCharts. Документация по свойствам и методам диаграмм находится по адресу docs.amcharts.com/3/javascriptcharts.

Все атрибуты конфигурации могут иметь значение null, вместо таких значений будут использоваться значения по умолчанию (кроме случаев, указанных в документации AmCharts).

1.2. Связь с данными

Реализовано два варианта передачи данных в диаграмму: через интерфейс DataProvider, или через механизм источников данных.

  • Интерфейс DataProvider имеет стандартную реализацию: класс ListDataProvider. Он содержит список экземпляров DataItem, каждый из которых содержит набор пар ключ-значение. Экземпляр DataProvider передается методу setDataProvider() конфигурации диаграммы. Данный способ предоставления данных для диаграммы наиболее универсален, однако требует создания экземпляров DataProvider и DataItem в коде контроллера экрана.

  • Источник данных типа CollectionDatasource устанавливается для компонента Chart вызовом метода setDatasource(). Данный вариант требует наличия сущности, представляющей данные диаграммы. Он удобен, когда такая сущность уже есть в модели данных приложения, а также когда данные диаграммы нужно отобразить и в виде таблицы.

В главе Раздел 1.4, «Пример работы с диаграммами» проиллюстрированы оба способа получения данных.

Используемые для отображения свойства сущности или значения, содержащиеся в экземпляре DataProvider, задаются в атрибутах диаграммы, причем атрибуты различаются для разных типов диаграмм. Например для компонента chart:pieChart необходимо задать атрибуты valueField и titleField. В качестве значений могут выступать типы Integer, Long, Double, String, Boolean, Date.

Динамическое добавление данных в существующий график не поддерживается, возможно только полностью перерисовать график с новыми данными. Для этого необходимо воспользоваться методом repaint().

1.3. События

Имеется возможность настроить реакцию на различные типы событий. Доступны следующие типы слушателей событий:

  • AxisZoomListener - масштабирование оси графика.

  • ChartClickListener - щелчок по холсту.

  • RightClickListener - щелчок по холсту правой клавишей мыши.

  • CursorPeriodSelectListener - выбор периода отображения курсором.

  • CursorZoomListener - масштабирование области графика курсором.

  • GraphClickListener - щелчок по графику.

  • GraphItemClickListener - щелчок по элементу графика.

  • LegendItemHideListener - скрытие элемента легенды.

  • LegendItemShowListener - показ элемента легенды.

  • LegendItemClickListener - щелчок по элементу легенды.

  • SliceClickListener - щелчок по элементу круговой диаграммы.

  • SliceRightClickListener - щелчок по элементу круговой диаграммы правой клавишей мыши.

  • SlicePullInListener - элемент круговой диаграммы соединён с диаграммой.

  • SlicePullOutListener - элемент круговой диаграммы отсоединён от диаграммы.

  • ZoomListener - масштабирование холста.

Пример использования событий проиллюстрирован в разделе Раздел 1.4.4, «Использование событий».

1.4. Пример работы с диаграммами

В данной главе мы рассмотрим применение подсистемы отображения диаграмм в приложении-примере Библиотека, который может быть загружен с помощью CUBA Studio.

1.4.1. Настройка проекта приложения

  1. Запустите CUBA Studio, перейдите в окно Open project > Samples и загрузите проект Library.

  2. Откройте проект Library в Studio.

  3. Откройте окно свойств проекта Project properties -> Edit и в списке Base projects включите проект charts, затем сохраните изменения. Studio предложит пересоздать скрипты Gradle - согласитесь.

  4. Запустите Run -> Deploy. На этом этапе будет произведена сборка приложения, и оно будет развернуто на сервере Tomcat в подкаталоге build/tomcat.

  5. Создайте базу данных приложения, запустив Run -> Create database.

  6. Запустите Build -> Create or update IDEA project files чтобы создать проектные файлы для IntelliJ IDEA.

После выполнения вышеописанных действий функциональность для отображения диаграмм подключена к приложению и готова к работе.

1.4.2. Создание сервиса для извлечения данных

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

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

    Создайте в пакете com.sample.library.entity модуля global класс BooksByGenre:

    package com.sample.library.entity;
    
    import com.haulmont.chile.core.annotations.MetaClass;
    import com.haulmont.chile.core.annotations.MetaProperty;
    import com.haulmont.cuba.core.entity.AbstractNotPersistentEntity;
    
    @MetaClass(name = "library$BooksByGenre")
    public class BooksByGenre extends AbstractNotPersistentEntity {
    
        @MetaProperty
        private String genre;
    
        @MetaProperty
        private Long countOfBooks;
    
        public String getGenre() {
            return genre;
        }
    
        public void setGenre(String genre) {
            this.genre = genre;
        }
    
        public Long getCountOfBooks() {
            return countOfBooks;
        }
    
        public void setCountOfBooks(Long countOfBooks) {
            this.countOfBooks = countOfBooks;
        }
    }
    

    Экземпляр этого класса содержит количество книг определенного жанра. Далее необходимо зарегистрировать класс сущности в файле metadata.xml модуля global:

    <metadata-model root-package="com.sample.library">
            <class>com.sample.library.entity.BooksByGenre</class>
        </metadata-model>
  2. Для создания интерфейса и класса сервиса можно воспользоваться CUBA Studio. Для этого откройте вкладку Services на панели навигатора и нажмите на кнопку New. В открывшемся окне установите в поле Interface значение com.sample.library.service.StatisticsService. В полях Bean и Service name будут сгенерированы подходящие значения com.sample.library.service.StatisticsServiceBean и library_StatisticsService соответственно. Сохраните изменения.

    Откройте интерфейс сервиса StatisticsService в IDE и объявите в нем методы получения данных из базы:

    package com.sample.library.service;
    
    import com.sample.library.entity.BooksByGenre;
    import java.util.List;
    import java.util.Map;
    
    public interface StatisticsService {
        String NAME = "library_StatisticsService";
    
        public List<BooksByGenre> getCountOfBooksByGenre();
    
        public List<String> getTopPublishers(int count);
    
        public Map<Integer, Map<String, Long>> getCountOfBooksByPublisherAndYear();
    }
    
  3. Далее откройте класс сервиса StatisticsServiceBean и замените его содержимое на следующий код:

    package com.sample.library.service;
    
    import com.google.common.collect.Lists;
    import com.haulmont.cuba.core.EntityManager;
    import com.haulmont.cuba.core.Persistence;
    import com.haulmont.cuba.core.Transaction;
    import com.sample.library.entity.BooksByGenre;
    import org.springframework.stereotype.Service;
    
    import javax.inject.Inject;
    import java.util.ArrayList;
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;
    
    @Service(StatisticsService.NAME)
    public class StatisticsServiceBean implements StatisticsService {
    
        @Inject
        private Persistence persistence;
    
    
        @Override
        public List<BooksByGenre> getCountOfBooksByGenre() {
            List<BooksByGenre> result = new ArrayList<>();
            String query = "select ltype.name, count(book) " +
                           "from library$Book book join book.literatureType ltype " +
                           "group by ltype.name order by ltype.name";
            Transaction transaction = persistence.createTransaction();
            try {
                EntityManager em = persistence.getEntityManager();
                List<Object[]> resultList = em.createQuery(query, Object[].class).getResultList();
    
                for (Object[] row : resultList) {
                    BooksByGenre entity = new BooksByGenre();
                    entity.setGenre((String) row[0]);
                    entity.setCountOfBooks((Long) row[1]);
                    result.add(entity);
                }
            } finally {
                transaction.end();
            }
    
            return result;
        }
    
        @Override
        public List<String> getTopPublishers(final int count) {
            List<String> result = Lists.newArrayList();
            String query = "select instance.bookPublication.publisher.name, count(instance) " +
                    "from library$BookInstance instance " +
                    "group by instance.bookPublication.publisher.name order by count(instance) desc";
            Transaction transaction = persistence.createTransaction();
            try {
                EntityManager em = persistence.getEntityManager();
                List resultList = em.createQuery(query).getResultList();
    
                for (int i = 0; i < resultList.size(); i++) {
                    if (i == count) {
                        break;
                    }
                    Object[] data = (Object[]) resultList.get(i);
                    result.add((String) data[0]);
                }
            } finally {
                transaction.end();
            }
    
            return result;
        }
    
        @Override
        public Map<Integer, Map<String, Long>> getCountOfBooksByPublisherAndYear() {
            Map<Integer, Map<String, Long>> result = new LinkedHashMap<>();
            String query = "select instance.bookPublication.publisher.name, instance.bookPublication.year, count(instance) " +
                    "from library$BookInstance instance " +
                    "group by instance.bookPublication.year, instance.bookPublication.publisher.name " +
                    "order by instance.bookPublication.year, instance.bookPublication.publisher.name";
            Transaction transaction = persistence.createTransaction();
            try {
                EntityManager em = persistence.getEntityManager();
                List resultList = em.createQuery(query).getResultList();
    
                for (Object row : resultList) {
                    Object[] data = (Object[]) row;
    
                    String publisher = (String) data[0];
                    Integer year = (Integer) data[1];
                    Long quantity = (Long) data[2];
    
                    if (result.get(year) == null) {
                        result.put(year, new LinkedHashMap<String, Long>());
                    }
                    result.get(year).put(publisher, quantity);
                }
            } finally {
                transaction.end();
            }
    
            return result;
        }
    }
    

    Метод getCountOfBooksByGenre() возвращает количество книг каждого жанра в виде списка сущностей BooksByGenre. Метод getTopPublishers(int count) сортирует издателей по убыванию количества экземпляров выпущенных ими книг, находящихся в библиотеке, и возвращает первые count издателей. Метод getCountOfBooksByPublisherAndYear() возвращает количество книг, выпущенных издателями в определенном году.

1.4.3. Создание диаграмм

1.4.3.1. XML-дескриптор экрана

Откройте в CUBA Studio вкладку Screens и создайте экран в модуле web. Введите значение com/sample/library/web/charts/statistics.xml в поле Reference. В полях Id, Controller Name и Messages Pack будут сгенерированы подходящие значения. Сохраните изменения. Далее перейдите на вкладку XML и замените ее содержимое на следующий код:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://statistic"
        class="com.sample.library.web.charts.statistics.Statistics"
        messagesPack="com.sample.library.web.charts.statistics"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
    <dsContext>
        <collectionDatasource id="pieDs"
                              class="com.sample.library.entity.BooksByGenre"
                              refreshMode="NEVER"
                              view="_local"/>
    </dsContext>
    <layout spacing="true">
        <tabSheet height="100%"
                  width="100%">
            <tab id="pieChartTab"
                 caption="msg://booksByGenres">
                <hbox expand="pieChart"
                      height="100%"
                      margin="true"
                      spacing="true"
                      width="100%">
                    <chart:pieChart id="pieChart"
                                    angle="30"
                                    balloonText="[[genre]] - [[percents]]%"
                                    datasource="pieDs"
                                    depth3D="15"
                                    height="100%"
                                    titleField="genre"
                                    valueField="countOfBooks"
                                    width="100%">
                        <chart:exportConfig menuTop="0px">
                            <chart:menuItems>
                                <chart:menu format="PNG"
                                            icon="VAADIN/resources/amcharts/images/export.png"/>
                            </chart:menuItems>
                        </chart:exportConfig>
                    </chart:pieChart>
                    <table columnControlVisible="false"
                           height="100%"
                           width="300px">
                        <columns>
                            <column id="genre"/>
                            <column id="countOfBooks"/>
                        </columns>
                        <rows datasource="pieDs"/>
                    </table>
                </hbox>
            </tab>
            <tab id="stackedChartTab"
                 caption="msg://countOfBooksByPublisherAndYear"
                 spacing="true">
                <chart:serialChart id="stackedChart"
                                   categoryField="year"
                                   height="100%"
                                   plotAreaFillAlphas="0.1"
                                   width="100%">
                    <chart:chartCursor/>
                    <chart:legend markerType="TRIANGLE_RIGHT"
                                  position="TOP"
                                  valueAlign="LEFT"/>
                    <chart:categoryAxis startOnAxis="true"
                                        title="msg://year"/>
                    <chart:valueAxes>
                        <chart:axis gridAlpha="0.07"
                                    position="LEFT"
                                    stackType="REGULAR"
                                    title="msg://countOfBooks"/>
                    </chart:valueAxes>
                    <chart:exportConfig menuTop="0px">
                        <chart:menuItems>
                            <chart:menu format="PNG"
                                        icon="VAADIN/resources/amcharts/images/export.png"/>
                        </chart:menuItems>
                    </chart:exportConfig>
                </chart:serialChart>
            </tab>
        </tabSheet>
    </layout>
</window>

В корневой элемент дескриптора экрана добавлен атрибут xmlns:chart:

<window xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd"
    ...
    >

Созданный нами экран содержит панель с двумя вкладками. На первой находится круговая диаграмма, представляющая распределение книг по жанрам:

<chart:pieChart id="pieChart"
                    angle="30"
                    balloonText="[[genre]] - [[percents]]%"
                    datasource="pieDs"
                    depth3D="15"
                    height="100%"
                    titleField="genre"
                    valueField="countOfBooks"
                    width="100%">
        <chart:exportConfig menuTop="0px">
            <chart:menuItems>
                <chart:menu format="PNG"
                            icon="VAADIN/resources/amcharts/images/export.png"/>
            </chart:menuItems>
        </chart:exportConfig>
    </chart:pieChart>

Даграмма получает данные из источника pieDs, указанного в атрибуте datasource. Для отображения названий и значений используются атрибуты genre и countOfBooks сущности BooksByGenre, список экземпляров которой находится в источнике данных. С источником данных соединена также таблица, поэтому она отображает те же данные, что и диаграмма.

Компонент pieChart содержит следующие атрибуты:

  • angle - определяет угол наклона диаграммы. Может принимать значения от 0 до 90.

  • balloonText - определяет текст всплывающей подсказки при наведении на отделяемую часть диаграммы. Доступны для использования тэги [[value]], [[title]], [[persents]], [[description]], а также ключи из DataItem, список которых хранится в экземпляре DataProvider, либо имена атрибутов сущности в источнике данных.

  • depth3D - толщина диграммы. При использовании совместно с атрибутом angle позволяет создать эффект объема.

  • titleField - ключ из набора пар, содержащихся в объектах DataItem, список которых хранится в экземпляре DataProvider, по которому будет взято значение для заголовка сектора в круговой диаграмме.

  • valueField - ключ из набора пар, содержащихся в объектах DataItem, список которых хранится в экземпляре DataProvider, по которому будет взято значение для сектора.

Компонент pieChart содержит следующие элементы:

  • chart:legend - определяет легенду графика. Атрибут position определяет положение легенды относительно диаграммы, markerType - форму маркера, помечающего информацию о каждом секторе диаграммы.

  • chart:exportConfig - добавляет возможность сохранить полученный график. Атрибуты menuTop, menuLeft, menuRight, menuBottom позволяют задать положение кнопки сохранения. В примере кнопка располагается в правом верхнем углу диаграммы.

    Элемент chart:menuItems содержит настройки сохранения. Кроме используемого в примере формата png поддерживается сохранение в форматах jpg, svg и pdf. Атрибут icon содержит путь к изображению, которое будет использоваться в качестве кнопки импорта.

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

<chart:serialChart id="stackedChart"
                       categoryField="year"
                       height="100%"
                       width="100%">
       <chart:chartCursor/>
       <chart:legend markerType="TRIANGLE_RIGHT"
                     position="TOP"
                     valueAlign="LEFT"/>
       <chart:categoryAxis startOnAxis="true"
                           title="msg://year"/>
       <chart:valueAxes>
          <chart:axis position="LEFT"
                      stackType="REGULAR"
                      title="msg://countOfBooks"/>
       </chart:valueAxes>
       <chart:exportConfig menuTop="0px">
          <chart:menuItems>
             <chart:menu format="PNG"
                         icon="VAADIN/resources/amcharts/images/export.png"/>
          </chart:menuItems>
       </chart:exportConfig>
    </chart:serialChart>

Эта диаграмма получает данные через DataProvider, созданный в контроллере (см. ниже), поэтому атрибут datasource не указан.

Атрибуты chart:serialChart:

  • categoryField - ключ из набора пар, содержащихся в объектах DataItem, список которых хранится в экземпляре DataProvider, по которому будут взяты значения для подписи оси категорий.

Элементы chart:serialChart:

  • chart:chartCursor - необязательный элемент, добавляющий на график курсор, который следует за движениями мыши и отображает всплывающую подсказку со значением графика, соответствующим точке нахождения курсора.

  • chart:categoryAxis - элемент, описывающий ось категорий. Установка атрибуту startOnAxis значения true дает указание начинать отрисовывать график сразу от оси значений. По умолчанию этот атрибут имеет значение false. В этом случае между осью значений и графиком имеется некоторый помежуток. Атрибут title задает заголовок оси категорий.

  • chart:valueAxes - элемент, описывающий вертикальные оси значений. В данном случае используется только одна ось, описываемая элементом chart:axis. Атрибут position задает положение оси значений относительно диаграммы. Установка атрибуту stackType значения REGULAR говорит о том, что используется диаграмма с накоплением. По умолчанию значение этого атрибута - none, в таком случае используется диаграмма без накопления.

1.4.3.2. Контроллер экрана

Перейдите на вкладку Controller и замените ее содержимое на следующий код:

package com.sample.library.web.charts.statistics;

import com.haulmont.charts.gui.amcharts.model.Graph;
import com.haulmont.charts.gui.amcharts.model.GraphType;
import com.haulmont.charts.gui.amcharts.model.charts.SerialChart;
import com.haulmont.charts.gui.amcharts.model.data.ListDataProvider;
import com.haulmont.charts.gui.amcharts.model.data.MapDataItem;
import com.haulmont.charts.gui.components.charts.Chart;
import com.haulmont.cuba.gui.components.AbstractWindow;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.sample.library.entity.BooksByGenre;
import com.sample.library.service.StatisticsService;

import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class Statistics extends AbstractWindow {

    @Inject
    private CollectionDatasource<BooksByGenre, UUID> pieDs;

    @Inject
    private Chart stackedChart;

    @Inject
    private StatisticsService statisticsService;

    public void init(Map<String, Object> param) {
        initPieChart();
        initStackedChart();
    }

    private void initPieChart() {
        pieDs.refresh();

        List<BooksByGenre> booksByGenreList = statisticsService.getCountOfBooksByGenre();
        for (BooksByGenre booksByGenre : booksByGenreList) {
            pieDs.includeItem(booksByGenre);
        }
    }

    private void initStackedChart() {
        List<String> allPublishers = statisticsService.getTopPublishers(5);
        Map<Integer, Map<String, Long>> structuredData = statisticsService.getCountOfBooksByPublisherAndYear();

        ListDataProvider dataProvider = new ListDataProvider();

        for (Map.Entry<Integer, Map<String, Long>> entry : structuredData.entrySet()) {
            MapDataItem mapDataItem = new MapDataItem().add("year", entry.getKey());

            for (String publisher : allPublishers) {
                if (entry.getValue().containsKey(publisher)) {
                    mapDataItem.add(publisher, entry.getValue().get(publisher));
                } else {
                    mapDataItem.add(publisher, 0);
                }
            }
            dataProvider.addItem(mapDataItem);
        }

        SerialChart stackedChartConfiguration = (SerialChart) stackedChart.getConfiguration();
        stackedChartConfiguration.setDataProvider(dataProvider);

        Graph[] graphs = new Graph[allPublishers.size()];
        int i = 0;
        for (String publisher : allPublishers) {
            Graph publisherGraph = new Graph();
            publisherGraph.setFillAlphas(0.6);
            publisherGraph.setLineAlpha(0.4);
            publisherGraph.setTitle(publisher);
            publisherGraph.setType(GraphType.LINE);
            publisherGraph.setValueField(publisher);
            publisherGraph.setBalloonText(publisher + " - [[year]] year: [[" + publisher + "]] books");

            graphs[i] = publisherGraph;
            i++;
        }
        stackedChartConfiguration.addGraphs(graphs);
    }
}

В методе initPieChart() происходит заполнение источника данных pieDs данными, полученными из сервиса. Метод refresh() производит инициализацию источника данных. Этот метод необходимо вызвать, несмотря на атрибут refreshMode="NEVER", установленный в XML-дескрипторе.

В методе initStackedChart(List<String> allPublishers) происходит установка данных в диаграмму с накоплением. Диаграммы подобного типа показывают отношение отдельных составляющих к их совокупному значению. В метод передается список издателей, выпустивших наибольшее количество экземпляров книг, находящихся в библиотеке. Для каждого издателя строится график, представляющий количество книг, выпущенных в разные годы. Графику соответствует экземпляр класса Graph. Рассмотрим методы настройки графиков:

  • setFillAlphas(0.6) - устанавливает степень непрозрачности заливки.

  • setLineAlpha(0.4) - устанавливает толщину линии графика. Допустимые значения от 0 до 1.

  • setTitle(publisher) - устанавливает заголовок графика.

  • setType(GraphType.LINE) - устанавливает тип отбражения данных.

  • setValueField(publisher) - ключ из набора пар, содержащихся в объектах DataItem, список которых хранится в экземпляре DataProvider, по которому будет взято значение для графика.

  • setBaloonText(publisher + " - [[year]] year: [[" + publisher + "]] books") - устанавливает значение всплывающей подсказки.

1.4.3.3. Скриншоты диаграмм

Посмотрим, как созданный нами экран выглядит в работающем приложении. Добавьте созданный экран в меню, затем пересоберите проект командой Run -> Restart application server и зайдите в систему. Откройте экран со статистикой.

Рисунок 2. Круговая диаграмма

Круговая диаграмма

Рисунок 3. Диаграмма с накоплением

Диаграмма с накоплением

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

1.4.4. Использование событий

Проиллюстрируем использование событий. Добавим в экран, созданный в разделе Раздел 1.4.3, «Создание диаграмм», обработку отделения элемента круговой диаграммы пользователем. Откройте XML-дескриптор экрана в IDE, затем инжектируйте диаграмму:

@Inject
    private Chart pieChart;

Далее добавьте слушателя в методе initPieChart():

pieChart.addSlicePullOutListener(new Chart.SlicePullOutListener() {
    @Override
    public void onPullOut(Chart.SlicePullOutEvent event) {
        BooksByGenre booksByGenre = (BooksByGenre) event.getItem();
        String msg = booksByGenre.getGenre() + ": " + booksByGenre.getCountOfBooks() + " book(s)";
        showNotification(msg, NotificationType.HUMANIZED);
    }
});

Для просмотра результата пересоберите проект командой Run -> Restart application server и зайдите в систему. Откройте экран и нажмите на один из элементов круговой диаграммы для его отделения.

Рисунок 4. Диаграмма с обработкой события отделения элемента

Диаграмма с обработкой события отделения элемента

Глава 2. Отображение карт

Подсистема отображения карт платформы CUBA основана на интеграции со сторонним поставщиком сервиса карт. На данный момент поддерживается только сервис Google Maps.

2.1. Возможности отображения карты

  • Реакция на события:

    • Щелчок мышью.

    • Перемещение и зуммирование карты.

    • Щелчок по маркеру и его перетаскивание.

    • Закрытие всплывающего окна.

  • Установка маркеров. Маркер может быть фиксированным или с перетаскиваемым пользователем. Маркер может обрабатывать щелчки мыши и передавать соответствующее событие в код экрана.

  • Отображение ломаных линий и полигонов (многоугольников).

  • Рисование полигонов.

  • Отображение тепловой карты (heat map).

2.2. Настройка проекта приложения

Для отображения карт в проект приложения необходимо подключить базовый проект charts, как это описано для подсистемы отображения диаграмм. Кроме того, необходимо установить следующие свойства приложения блока Web Client:

  • Обязательный параметр - ключ доступа к API поставщика карты. В случае использования бесплатного ключа это свойство cuba.charts.map.freeApiKey. В случае наличия коммерческой лицензии должны быть установлены следующие свойства:

    • cuba.charts.map.useBusinessApiKey = true - признак использования коммерческого ключа.

    • cuba.charts.map.businessApiKey - ключ доступа к API.

    • cuba.charts.map.clientId - идентификатор клиента, в зависимости от поставщика может быть обязательным к заполнению. Обязателен для Google Maps.

  • Необязательные параметры:

    • cuba.charts.map.defaultZoom - масштаб карты (zoom) по умолчанию.

    • cuba.charts.map.defaultLatitude - широта центра карты по умолчанию.

    • cuba.charts.map.defaultLongitude - долгота центра карты по умолчанию.

    • cuba.charts.map.provider - поставщик сервиса карт, по умолчанию google.

Пример содержимого файла web-app.properties:

cuba.charts.map.freeApiKey = my_key
cuba.charts.map.defaultZoom = 13.0
cuba.charts.map.defaultLatitude = 51.5001
cuba.charts.map.defaultLongitude = -0.1262

2.3. Компонент MapViewer

Для отображения карт в экранах приложения используется компонент com.haulmont.charts.gui.components.map.MapViewer.

Для подключения компонента в XML-дескриптор экрана в корневом элементе необходимо объявить пространство имен chart:

<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd"
        ...>

XML-имя компонента: mapViewer. Пример объявления компонента:

<layout>
    <vbox id="mapBox" height="100%">
        <chart:mapViewer id="map" width="100%" height="100%"/>
    </vbox>
</layout>

В XML-дескрипторе можно задать следующие параметры компонента:

  • id, width, height - стандартные параметры компонентов.

  • mapType - тип карты, соответствующий перечислению MapViewer.Type: roadmap, satellite, hybrid, terrain. По умолчанию выбирается roadmap.

  • vendor - поставщик сервиса. На данный момент поддерживается только значение google.

Основная настройка карты и ее компонентов производится в контроллере экрана. Для этого достаточно инжектировать компонент, объявленный в XML-дескрипторе:

@Inject
private MapViewer map;

@Override
public void init(Map<String, Object> params) {
    GeoPoint center = map.createGeoPoint(53.490905, -2.249558);
    map.setCenter(center);
}
  • Методы настройки карты:

    • setZoom() - задание масштаба карты.

    • setCenter() - задание центра карты.

    • setVisibleAreaBoundLimitsEnabled() - включение режима, ограничивающего видимую область карты.

    • setVisibleAreaBoundLimits() - задание границ видимости карты.

    • fitToBounds() - задание минимального масштаба карты, при котором будет полностью отображена область, заданная северо-восточной и юго-западной коордианатами.

    • setMaxZoom() - задание максимального доступного масштаба.

    • setMinZoom() - задание минимального доступного масштаба.

    • setDraggable() - включение/выключение режима перетаскивания карты.

    • setKeyboardShortcutsEnabled() - включение/выключение сочетаний клавиш.

    • setScrollWheelEnabled() - включение/выключение изменения масштаба карты с помощью колесика мыши.

    • setMapType() - задание типа карты.

  • Интерфейсы компонентов карты (располагаются в пакете com.haulmont.charts.gui.map.model):

    • GeoPoint - вспомогательный компонент, непосредственно не отображаемый на карте. Используется для задания параметров карты, таких как центр, границы, и для создания более сложных компонентов карты. Для создания объекта используется метод createGeoPoint() интерфейса MapViewer. Например:

      GeoPoint center = map.createGeoPoint(53.490905, -2.249558);
      map.setCenter(center);
    • Marker - компонент для отметки места на карте. По умолчанию используется стандартная иконка сервиса карт. Для создания и размещения объекта на карте используются методы createMarker() и addMarker() интерфейса MapViewer. Например:

      Marker marker = map.createMarker("My place", map.createGeoPoint(53.590905, -2.249558), true);
      marker.setClickable(true);
      map.addMarker(marker);
    • Polyline - компонент для отображения ломаной линии. Для создания и размещения объекта на карте используются методы createPolyline() и addPolyline() интерфейса MapViewer. Например:

      List<GeoPoint> coordinates = new ArrayList<>();
      coordinates.add(map.createGeoPoint(53.49, -2.54));
      coordinates.add(map.createGeoPoint(53.49, -2.22));
      coordinates.add(map.createGeoPoint(53.89, -2.22));
      coordinates.add(map.createGeoPoint(53.99, -2.94));
      Polyline polyline = map.createPolyline(coordinates);
      map.addPolyline(polyline);
    • Polygon - компонент для отображения полигона. Для создания и размещения объекта на карте используются методы createPolygon() и addPolygonOverlay() интерфейса MapViewer. Например:

      List<GeoPoint> coordinates = new ArrayList<>();
      coordinates.add(map.createGeoPoint(53.49, -2.54));
      coordinates.add(map.createGeoPoint(53.49, -2.22));
      coordinates.add(map.createGeoPoint(53.89, -2.22));
      coordinates.add(map.createGeoPoint(53.99, -2.94));
      Polygon p = map.createPolygon(coordinates, "#9CFBA9", 0.6, "#2CA860", 1.0, 2);
      map.addPolygonOverlay(p);
    • InfoWindow - компонент карты для отображения информации во всплывающем окне. Для создания и размещения объекта на карте используются методы createInfoWindow() и openInfoWindow() интерфейса MapViewer. Например:

      InfoWindow w = map.createInfoWindow("Some text");
      map.openInfoWindow(w);

      Информационное окно может быть привязано к маркеру, например:

      map.addMarkerClickListener(new MarkerClickListener() {
          @Override
          public void onClick(MarkerClickEvent event) {
              Marker marker = event.getMarker();
              String caption = String.format("Marker clicked: %.2f, %.2f", 
                      marker.getPosition().getLatitude(),
                      marker.getPosition().getLongitude());
              InfoWindow w = map.createInfoWindow(caption, marker);
              map.openInfoWindow(w);
          }
      });
    • HeatMapLayer - слой тепловой карты: предназначен для изображения плотности данных в различных географических точках. Степень плотности точек отображается с помощью цвета. По умолчанию области с высокой плотностью точек отображаются красным цветом, а области с низкой - зелёным. Для создания и размещения объекта на карте используются методы createHeatMapLayer() и addHeatMapLayer() интерфейса MapViewer. Например:

      HeatMapLayer heatMapLayer = map.createHeatMapLayer();
      List<GeoPoint> data = new ArrayList<>();
      data.add(map.createGeoPoint(53.450, -2.00));
      data.add(map.createGeoPoint(53.451, -2.00));
      data.add(map.createGeoPoint(53.452, -2.00));
      data.add(map.createGeoPoint(53.453, -2.00));
      data.add(map.createGeoPoint(53.454, -2.00));        
      heatMapLayer.setData(data);
      map.addHeatMapLayer(heatMapLayer);

      Данные добавленного на карту слоя тепловой карты могут быть изменены с помощью дополнительного вызова метода setData(). Заново добавлять слой на карту при этом не требуется.

    • DrawingOptions - компонент поддержки рисования. В данный момент поддерживается только рисование полигонов. Режим рисования будет включен если в MapViewer передан экземпляр DrawingOptions. Пример использования:

      DrawingOptions options = new DrawingOptions();
      PolygonOptions polygonOptions = new PolygonOptions(true, true, "#993366", 0.6);
      ControlOptions controlOptions = new ControlOptions(
          Position.TOP_CENTER, Arrays.asList(OverlayType.POLYGON));
      options.setEnableDrawingControl(true);
      options.setPolygonOptions(polygonOptions);
      options.setDrawingControlOptions(controlOptions);
      options.setInitialDrawingMode(OverlayType.POLYGON);
      map.setDrawingOptions(options);
  • Слушатели событий (располагаются в пакете com.haulmont.charts.gui.map.model.listeners):

    • MapMoveListener - перемещение карты с зажаток клавишей мыши.

    • MapClickListener - щелчок по карте.

    • MarkerClickListener - щелчок по маркеру.

    • MarkerDragListener - перетаскивание маркера.

    • InfoWindowClosedListener - закрытие информационного окна.

    • PolygonCompleteListener - создание полигона в режиме редактирования.

    • PolygonEditListener - редактирование полигона (перемещение или добавление вершины существующего полигона).

    • MapInitListener - завершение инициализации карты: вызывается один раз после первоначальной загрузки карты, когда тайлы загружены и координаты доступны.

Для более подробной информации о методах и параметрах компонентов карты см. соответствующие JavaDocs.