Предисловие

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

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

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

Данное руководство предназначено для разработчиков приложений на платформе CUBA. Предполагается, что читатель ознакомлен с Руководством по разработке приложений.

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

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

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

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

Если у Вас имеются предложения по улучшению данного руководства, мы будем рады принять ваши pull request’ы и issues в исходниках документации на GitHub. Если вы увидели ошибку или несоответствие в документе, пожалуйста, форкните репозиторий и исправьте проблему. Заранее спасибо!

1. Установка

Для добавления аддона в проект выполните следующие действия:

  1. Кликните дважды Add-ons в дереве проекта CUBA.

    addons
  2. Перейдите на вкладку Marketplace и найдите аддон Charts.

    charts addon
  3. Кликните Install и затем Apply & Close.

    addon install
  4. Кликните Continue в появившемся диалоговом окне.

    addon continue

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

2. Отображение диаграмм

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

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

Для начала добавьте в проект аддон, как это описано в разделе Установка.

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

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

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

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

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

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

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

  • chart:allLabels – содержит вложенные элементы label, в которых можно указать текст надписи и его свойства. Надписи можно размещать в любом месте на диаграмме, например:

    charts allLabels
    Рисунок 1. chart:allLabels
    <chart:allLabels>
        <chart:label x="200"
                     y="700"
                     text="You can place a label at any position on the chart"
                     color="DARKBLUE"
                     align="CENTER"
                     rotation="-30"/>
    </chart:allLabels>
  • chart:balloon – задаёт настройки всплывающих пузырьковых подсказок, появляющихся при наведении курсора мыши на элемент данных диаграммы. Например:

    charts balloon
    Рисунок 2. chart:balloon
    <chart:graphs>
        <chart:graph balloonText="[[category]]: [[value]] m/s"
                     bullet="ROUND"
                     fillAlphas="0.3"
                     valueField="value"/>
    </chart:graphs>
    <chart:balloon adjustBorderColor="false"
                   color="WHITE"
                   horizontalPadding="10"
                   verticalPadding="8"/>

    Текст подсказки указывается в атрибуте balloonText для каждого графа.

    • additionalFields

      Для подстановки данных используются поля, уже использованные в диаграмме, такие как titleField, valueField, category, value, description, percents, open и т.п., а также теги HTML.

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

      В следующем примере title – это заголовок графа, category – значение по оси категорий, value значение по оси значений, а optional – атрибут сущности IncomeExpenses, загруженный для подстановки в balloonText:

      <chart:serialChart additionalFields="optional"
                         addClassNames="true"
                         categoryField="year"
                         dataContainer="incomeExpensesDc"
                         startDuration="1">
          <chart:graphs>
              <chart:graph alphaField="alpha"
                           balloonText="&lt;span style='font-size:12px;'&gt;[[title]] in [[category]]:&lt;br&gt; &lt;span style='font-size:20px;'&gt;[[value]]&lt;/span&gt; [[optional]]&lt;/span&gt;"
                           dashLengthField="dashLengthColumn"
                           title="Income"
                           type="COLUMN"
                           valueField="income"/>
              <...>
          </chart:graphs>
      </chart:serialChart>
      charts balloon additiional
      Рисунок 3. additionalFields

      Список дополнительных полей указывается декларативно через запятую:

      additionalFields="income,expense,vat"

      или можно передать список в контроллере экрана:

      List<String> fields = Arrays.asList("income", "expense", "vat");
      ganttChart.setAdditionalFields(fields);
  • chart:chartScrollbar (для диаграмм с типом SerialChart и XYChart) – полоса прокрутки диаграммы.

    • Можно использовать скроллбар для прокрутки конкретного графа:

      charts zoom
      Рисунок 4. chart:chartScrollbar
      <chart:chartScrollbar graph="g1"
                            autoGridCount="true"
                            scrollbarHeight="30"/>
    • Элемент chart:chartScrollbarSettings позволяет настроить конфигурацию полосы прокрутки:

      <chart:chartScrollbarSettings graph="stockGraph"
                                    usePeriod="10mm"
                                    position="TOP"/>
    • Кроме того, диаграммы с типом GanttChart могут дополнительно иметь элемент chart:valueScrollbar для прокрутки оси значений, в то время как chart:chartScrollbar будет использоваться для зуммирования оси категорий:

      charts valueScrollBar
      Рисунок 5. chart:valueScrollbar
      <chart:valueScrollbar autoGridCount="true"
                            color="BLACK"/>
      <chart:chartScrollbar autoGridCount="true"
                            color="DARKOLIVEGREEN"/>
  • chart:cursor – необязательный элемент для добавления курсора к диаграмме. Курсор следует за указателем мыши и отображает пузырьковые подсказки с данными в соответствующей точке диаграммы.

    charts cursor
    Рисунок 6. chart:cursor
    <chart:chartCursor cursorAlpha="1"
                       cursorColor="#258cbb"
                       cursorPosition="MOUSE"
                       limitToGraph="g1"
                       pan="true"
                       valueLineAlpha="0.2"
                       valueLineBalloonEnabled="true"
                       valueLineEnabled="true"
                       valueZoomable="true"/>
  • chart:data – необязательный элемент для добавления данных, используется преимущественно для прототипирования диаграмм.

  • chart:export – необязательный элемент для подключения функциональности экспорта диаграмм. Реализация экспорта по умолчанию представляет собой кнопку download в углу диаграммы:

    charts export
    Рисунок 7. chart:export
  • chart:guides – горизонтальные/вертикальные направляющие.

    charts guides
    Рисунок 8. chart:guides
    <chart:guides>
        <chart:guide category="2001"
                     dashLength="2"
                     fillAlpha="0.2"
                     fillColor="#CC0000"
                     inside="true"
                     label="fines for speeding increased"
                     labelRotation="90"
                     toCategory="2003"/>
        <chart:guide category="2007"
                     dashLength="2"
                     inside="true"
                     label="motorcycle fee introduced"
                     labelRotation="90"
                     lineAlpha="1"
                     lineColor="#CC0000"/>
    </chart:guides>
  • chart:legend – элемент, описывающий легенду диаграммы, например:

    charts legend
    Рисунок 9. chart:legend
    <chart:legend autoMargins="false"
                  marginRight="20"
                  markerType="CIRCLE"
                  position="RIGHT"
                  valueText="[[category]]: [[value]] %"/>
  • chart:nativeJsonJSON-конфигурация диаграммы.

  • chart:titles – заголовки осей, например:

    charts titles
    Рисунок 10. chart:titles
    <chart:titles>
        <chart:title alpha="1"
                     bold="true"
                     color="DARKSLATEGREY"
                     size="20"
                     tabIndex="0"
                     text="Main title"/>
        <chart:title alpha="0.5"
                     color="BISQUE"
                     size="12"
                     text="Subtitle"/>
    </chart:titles>
  • chart:responsive – подключение плагина, позволяющего сделать диаграммы отзывчивыми.

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

    Элемент responsive содержит вложенные элементы rules, в которых задаются правила отклика диаграмм. Вы можете настроить скрытие/отображение легенды, заголовков осей, разделителей, заголовков диаграмм, ползунков масштаба, перемещение подписей с осей внутрь графика и т.п.:

    <chart:responsive enabled="true">
        <chart:rules>
            <chart:rule maxWidth="400">
                <![CDATA[
                {
                    "precision": 2,
                    "legend": {
                      "enabled": false
                    },
                    "valueAxes": {
                      "inside": true
                    }
                }
                ]]>
            </chart:rule>
        </chart:rules>
    </chart:responsive>

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

Таким же образом можно описывать диаграммы в контроллере экрана. Можно как указывать отдельные свойства, так и добавлять составные объекты:

pieChart.setWidth("700px");
pieChart.setTitleField("description")
        .setValueField("value")
        .setStartAngle(312)
        .setLegend(new Legend()
                .setMarkerType(MarkerType.CIRCLE)
                .setPosition(LegendPosition.RIGHT)
                .setMarginRight(80))
        .addLabels(
                new Label()
                    .setText("Sample Chart")
                    .setSize(26)
                    .setBold(true)
                    .setAlign(Align.CENTER),
                new Label()
                    .setText("extra information")
                    .setAlign(Align.RIGHT))
        .setLabelTickColor(Color.GOLDENROD)
        .setColors(Arrays.asList(
                    Color.valueOf("#446493"),
                    Color.valueOf("#5E3D2C"),
                    Color.valueOf("#D0A557")))
        .setDataProvider(dataProvider);

Строки сообщений, используемых для отображения диаграмм, можно переопределить или локализовать в главном пакете сообщений модуля web. Несколько готовых локализаций доступны на CUBA GitHub.

2.2. Экспорт диаграмм

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

  • Download as…​ с доступными форматами: PNG, JPG, SVG, PDF

  • Save as…​ с доступными форматами: CSV, XLSX, JSON

  • Annotate…​, используемый для добавления заметок и векторных аннотаций. Информацию о плагине для создания аннотаций вы можете найти здесь.

  • Print, открывающий стандартное окно отправки страницы на печать.

charts export menu 1
Рисунок 11. Меню экспорта диаграмм

Меню экспорта может быть настроено для ограничения доступа пользователей к данным диаграммы, например:

<chart:export fileName="my-chart" position="TOP_RIGHT">
    <chart:menu>
        <chart:item label="PNG" title="Save as PNG" format="PNG"/>
        <chart:item label="JPG" title="Save as JPG" format="JPG"/>
    </chart:menu>
</chart:export>

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

charts export menu 2
Рисунок 12. Меню экспорта диаграмм

Вы можете задать следующие свойства экспорта:

  • backgroundColor – цвет фона экспортируемого изображения. По умолчанию #FFFFFF.

  • dataDateFormat – формат конвертации строк, содержащих даты, в объект даты. Применимо только к экспорту данных.

  • dateFormat – форматирование даты в подписи категории в указанном формате (только экспорт данных).

  • enabled – разрешает или запрещает возможность экспорта.

  • exportSelection – экспорт только выбранного диапазона данных. По умолчанию false.

  • exportTitles – заменяет имена полей их заголовками. По умолчанию false.

  • fileListener – если выбрано значение true, позволяет перетащить файл с изображением и добавить его к аннотации. По умолчанию false.

  • fileName – имя генерируемого файла (расширение будет добавлено в зависимости от выбранного типа экспорта).

  • keyListener – если выбрано значение true, слушатель нажатия клавиш позволит отменять/повторять введённые аннотации.

  • position – расположение кнопки экспорта. Доступные значения: TOP_LEFT, TOP_RIGHT (значение по умолчанию), BOTTOM_LEFT, BOTTOM_RIGHT.

  • removeImages – если выбрано значение true, при экспорте будут удалены лишние изображения, загруженные из различных источников.

Следующие свойства можно настроить для каждого конкретного формата экспорта:

JPG
  • quality – качество конечного изображения. Возможные значения 0 - 1. Значение по умолчанию 1.

PNG, JPG, SVG, PDF
  • multiplier – коэффициент масштабирования конечного изображения относительно исходного размера.

CSV
  • quotes – определяет, нужно ли заключать значения в кавычки. Значение по умолчанию true.

  • delimiter – символ, используемый в качестве разделителя. По умолчанию "," (запятая).

  • escape – определяет, нужно ли экранировать строки. Значение по умолчанию true.

  • withHeader – добавляет строку заголовков с именами колонок. Значение по умолчанию true.

XLSX
  • dateFormat – маска формата даты XLSX. Требует значения true для свойства parseDates в CategoryAxis.

  • stringify – приведение всего содержимого ячеек к строке. Значение по умолчанию false.

  • withHeader – добавляет строку заголовков с именами колонок. Значение по умолчанию true.

PDF
  • pageOrientation – ориентация страницы. По умолчанию PORTRAIT.

  • pageOrigin – показывает или скрывает источник генерируемого файла PDF. По умолчанию true.

  • pageSize – размер страницы PDF. По умолчанию A4.

Кроме того, вы можете переопределить строку сообщения label.saved.from в главном пакете сообщений.

PRINT
  • delay – задержка перед началом печати в секундах.

  • lossless – разрешает или запрещает оптимизацию изображения при печати. По умолчанию false.

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

Реализовано два варианта передачи данных в диаграмму: через интерфейс DataProvider и с использованием упрощённого API, позволяющего привязывать данные напрямую при помощи метода addData() и удобных конструкторов класса MapDataItem. Последний способ подходит для диаграмм, для которых не определен контейнер данных. В главе Пример работы с диаграммами проиллюстрированы все способы получения данных.

DataProvider:

Интерфейс DataProvider имеет две стандартные реализации: класс ListDataProvider и класс ContainerDataProvider.

  • ListDataProvider содержит список экземпляров DataItem, из которых будут браться данные для диаграммы. Существует несколько стандартных реализаций интерфейса DataItem:

    • EntityDataItem принимает экземпляр сущности, из которой будут браться данные для диаграммы:

      @Inject
      private PieChart chart;
      
      @Inject
      private Metadata metadata;
      
      @Subscribe
      protected void onInit(InitEvent event) {
          ListDataProvider dataProvider = new ListDataProvider();
          dataProvider.addItem(new EntityDataItem(valueDescription(75, "Sky")));
          dataProvider.addItem(new EntityDataItem(valueDescription(7, "Shady side of pyramid")));
          dataProvider.addItem(new EntityDataItem(valueDescription(18, "Sunny side of pyramid")));
      
          chart.setDataProvider(dataProvider);
      }
      
      private ValueDescription valueDescription(Integer value, String description) {
          ValueDescription entity = metadata.create(ValueDescription.class);
          entity.setValue(value);
          entity.setDescription(description);
          return entity;
      }
    • MapDataItem содержит набор пар ключ-значение, из которого будут браться данные для диаграммы:

      @Inject
      private PieChart chart;
      
      @Subscribe
      protected void onInit(InitEvent event) {
          ListDataProvider dataProvider = new ListDataProvider();
          dataProvider.addItem(new MapDataItem(
                  ImmutableMap.of("value", 75, "description", "Sky")));
          dataProvider.addItem(new MapDataItem(
                  ImmutableMap.of("value", 7, "description", "Shady side of pyramid")));
          dataProvider.addItem(new MapDataItem(
                  ImmutableMap.of("value", 18, "description", "Sunny side of pyramid")));
      
          chart.setDataProvider(dataProvider);
      }
    • SimpleDataItem принимает экземпляр любого public класса, из которого будут браться данные для диаграммы:

    @Inject
    private PieChart chart;
    
    @Subscribe
    protected void onInit(InitEvent event) {
        ListDataProvider dataProvider = new ListDataProvider();
        dataProvider.addItem(new SimpleDataItem(new ValueDescription(75, "Sky")));
        dataProvider.addItem(new SimpleDataItem(new ValueDescription(7, "Shady side of pyramid")));
        dataProvider.addItem(new SimpleDataItem(new ValueDescription(18, "Sunny side of pyramid")));
    
        chart.setDataProvider(dataProvider);
    }
    
    public class ValueDescription {
        private Integer value;
        private String description;
    
        public ValueDescription(Integer value, String description) {
            this.value = value;
            this.description = description;
        }
    
        public Integer getValue() {
            return value;
        }
    
        public String getDescription() {
            return description;
        }
    }
  • ContainerDataProvider используется для связи CollectionContainer с компонентом диаграммы.

    Предположим, имеется контейнер данных с загрузчиком, который получает список экземпляров сущности TransportCount. Ниже приведен фрагмент XML-дескриптора экрана:

    <data>
        <collection id="transportCountsDc"
                    class="com.company.sampler.entity.TransportCount"
                    view="_local">
            <loader id="transportCountsDl">
                    <query><![CDATA[select e from sampler_TransportCount e order by e.year]]></query>
            </loader>
        </collection>
    </data>
    <layout>
        <chart:serialChart id="stackedArea"
                           marginLeft="0"
                           marginTop="10"
                           plotAreaBorderAlpha="0"
                           height="100%"
                           width="100%">
            <!--...-->
        </chart:serialChart>
    </layout>

    В контроллере экрана определяется метод onInit(), который задает провайдер данных для диаграммы stackedArea. Провайдер данных создан на основе контейнера данных transportCountsDc.

    @UiController("sampler_StackedareaChartSample")
    @UiDescriptor("stackedarea-chart-sample.xml")
    @LoadDataBeforeShow
    public class StackedareaChartSample extends Screen {
    
        @Inject
        private CollectionContainer<TransportCount> transportCountsDc;
    
        @Inject
        private SerialChart stackedArea;
    
        @Subscribe
        private void onInit(InitEvent event) {
            stackedArea.setDataProvider(new ContainerDataProvider(transportCountsDc));
            stackedArea.setCategoryField("year");
        }
    }

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

Экземпляр DataProvider передается методу setDataProvider() конфигурации диаграммы. Данный способ предоставления данных для диаграммы наиболее универсален, однако требует создания экземпляров DataProvider или DataItem в коде контроллера экрана.

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

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

Элемент chart:data:

Этот способ удобен для быстрого прототипирования диаграмм. Элемент chart:data и вложенные в него элементы item позволяют вручную указать фиксированные значения прямо в XML-дескрипторе диаграммы, например:

<chart:pieChart id="pieChart"
                titleField="key"
                valueField="value">
    <chart:data>
        <chart:item>
            <chart:property name="key" value="piece of apple pie"/>
            <chart:property name="value" value="70" type="int"/>
        </chart:item>
        <chart:item>
            <chart:property name="key" value="piece of blueberry pie"/>
            <chart:property name="value" value="20" type="int"/>
        </chart:item>
        <chart:item>
            <chart:property name="key" value="piece of cherry pie"/>
            <chart:property name="value" value="10" type="int"/>
        </chart:item>
    </chart:data>
</chart:pieChart>

2.4. События

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

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

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

  • LegendLabelClickListener – щелчок по ярлыку легенды.

  • LegendMarkerClickListener – щелчок по маркеру легенды.

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

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

В подсистеме существует интерфейс SeriesBasedChart, содержащий методы zoomOut, zoomToIndexes и zoomToDates для манипуляций с осями диаграммы.

Подобные методы для управления осями значений также есть и в интерфейсе CoordinateChart: zoomOutValueAxes, zoomOutValueAxis, zoomOutValueAxis, zoomValueAxisToValues и zoomValueAxisToValues.

2.5. Типы диаграмм

Существует несколько типов диаграмм, поддерживаемых платформой.

charts hierarchy diagram
Рисунок 13. Иерархия видов диаграмм

Эти интерфейсы представлены в виде готовых компонентов:

Все компоненты содержат константу NAME, таким образом, поддерживается их создание с помощью UiComponents.

2.5.1. AngularGaugeChart

Компонент AngularGaugeChart позволяет вам создать диаграмму-спидометр.

gauge chart
Рисунок 14. GaugeChart

XML-имя компонента: chart:gaugeChart.

Элементы chart:gaugeChart:
  • arrows – содержит вложенные элементы arrow со стрелками осей.

    <chart:arrows>
        <chart:arrow value="60"/>
    </chart:arrows>
  • axes – содержит вложенные элементы axis для осей диаграммы.

    <chart:axes>
        <chart:axis id="blue"
                    axisColor="#67b7dc"
                    axisThickness="3"
                    gridInside="false"
                    inside="false"
                    radius="100%"
                    valueInterval="20"
                    tickColor="#67b7dc"/>
        <chart:axis labelsEnabled="true"
                    axisColor="#fdd400"
                    axisThickness="3"
                    endValue="160"
                    radius="80%"
                    valueInterval="20"
                    tickColor="#fdd400"/>
    </chart:axes>

    Элемент band позволяет разделить ось на несколько полос, как на изображении выше:

    <chart:axes>
        <chart:axis axisAlpha="0.2"
                    axisThickness="1"
                    bottomText="60 km/h"
                    bottomTextYOffset="-20"
                    endValue="220"
                    tickAlpha="0.2"
                    valueInterval="20">
            <chart:bands>
                <chart:band color="#84b761"
                            endValue="90"
                            startValue="0"/>
                <chart:band color="#fdd400"
                            endValue="130"
                            startValue="90"/>
                <chart:band color="#cc4748"
                            endValue="220"
                            innerRadius="95%"
                            startValue="130"/>
            </chart:bands>
        </chart:axis>
    </chart:axes>

    Атрибуты endValue и startValue определяют диапазон значений на шкале диаграммы, атрибут valueInterval задаёт шаг деления шкалы.

Слушатели событий AngularGaugeChart:
  • ChartClickListener – щелчок по холсту.

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

Для более подробной информации об этом типе диаграмм смотрите документацию AmCharts.

2.5.2. FunnelChart

Компонент FunnelChart позволяет вам создать пирамиды или конусы.

funnel chart
Рисунок 15. FunnelChart

XML-имя компонента: chart:funnelChart.

Связь с данными:
  1. Через контейнер данных.

    Вы можете указать для диаграммы контейнер данных с типом CollectionContainer, а затем указать нужные поля в атрибутах titleField и valueField элемента funnelChart:

    <chart:funnelChart id="ratingChart"
                       align="MIDDLE_CENTER"
                       dataContainer="ratingDc"
                       height="200px"
                       labelPosition="RIGHT"
                       labelText="[[title]]: [[value]]"
                       marginRight="120"
                       maxLabelWidth="110"
                       marginTop="20"
                       titleField="mechanic"
                       valueField="count"
                       width="500px">
    </chart:funnelChart>
  2. С помощью элемента chart:data.

    Элементы item позволяют вручную указать фиксированные значения диаграммы.

    <chart:funnelChart id="ratingChart"
                       titleField="mechanic"
                       valueField="count">
        <chart:data>
            <chart:item>
                <chart:property name="mechanic" value="Jack"/>
                <chart:property name="count" value="1" type="int"/>
            </chart:item>
            <chart:item>
                <chart:property name="mechanic" value="Bob"/>
                <chart:property name="count" value="2" type="int"/>
            </chart:item>
            <chart:item>
                <chart:property name="mechanic" value="Sam"/>
                <chart:property name="count" value="3" type="int"/>
            </chart:item>
        </chart:data>
    </chart:funnelChart>
Слушатели событий FunnelChart:
  • SliceClickListener – щелчок по элементу круговой диаграммы.

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

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

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

Для более подробной информации об этом типе диаграмм смотрите документацию AmCharts.

2.5.3. GanttChart

Компонент GanttChart позволяет вам создать диаграмму Ганта.

gantt chart
Рисунок 16. GanttChart

XML-имя компонента: chart:ganttChart.

Элементы chart:ganttChart:
  • categoryAxis – элемент, описывающий ось категорий.

  • graph – элемент, содержащий коллекцию вложенных элементов chart:graph, описывающих графы диаграммы.

    • атрибут type определяет тип графа и может быть: линией, столбцом, пунктиром, кривой, OHLC и японской свечой.

    • атрибут valueField указывает ключ из коллекции пар ключ-значение, полученной из data provider.

  • valueAxis – элемент, описывающий ось значений. Если данные диаграммы основаны на значениях дат или времени, для оси значений можно указать тип date.

Атрибуты chart:ganttChart:
  • segmentsField – поле сегментов диаграммы.

  • additionalSegmentFields – список дополнительных полей для сегментов, соответствующих некоторым атрибутам сущности, которые необходимо дополнительно загрузить из data provider. Этот атрибут используется аналогично атрибуту additionalFields.

  • endField/endDateField – конечное значение или конечная дата диаграммы.

  • startField/startDateField – начальное значение или начальная дата диаграммы.

  • startDate – начальная дата диаграммы, если выбран тип оси date.

  • categoryField – поле категорий диаграммы.

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

Вы можете указать для диаграммы контейнер данных с типом CollectionContainer. В примере ниже атрибуты start и end сущности указаны в качестве значений атрибутов startDateField и endDateField диаграммы:

<chart:ganttChart id="ganttChart"
                  additionalSegmentFields="task"
                  balloonDateFormat="JJ:NN"
                  brightnessStep="7"
                  categoryField="category"
                  colorField="color"
                  columnWidth="0.5"
                  dataContainer="taskSpansDc"
                  endDateField="end"
                  height="100%"
                  marginRight="70"
                  period="DAYS"
                  rotate="true"
                  segmentsField="segments"
                  startDate="2016-01-01"
                  startDateField="start"
                  theme="LIGHT"
                  width="100%">
    <chart:graph balloonText="&lt;strong&gt;[[task]]&lt;/strong&gt;: [[open]] - [[value]]"
                 fillAlphas="1"
                 lineAlpha="1"
                 lineColor="WHITE"/>
    <chart:valueAxis type="DATE"/>
    <chart:valueScrollbar autoGridCount="true"
                          color="BLACK"/>
    <chart:chartCursor cursorAlpha="0"
                       cursorColor="#55bb76"
                       fullWidth="true"
                       valueLineAlpha="0.5"
                       valueBalloonsEnabled="false"
                       valueLineBalloonEnabled="true"
                       valueLineEnabled="true"
                       valueZoomable="true"
                       zoomable="false"/>
    <chart:export/>
</chart:ganttChart>
Слушатели событий GanttChart:
  • AxisZoomListener – масштабирование оси графика.

  • CategoryItemClickListener – щелчок по категории на оси категорий.

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

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

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

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

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

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

  • GraphItemRightClickListener – щелчок по элементу графика правой клавишей мыши.

  • RollOutGraphListener – разворачивание графика.

  • RollOutGraphItemListener – разворачивание элемента графика.

  • RollOverGraphListener – сворачивание графика.

  • RollOverGraphItemListener – сворачивание элемента графика.

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

Для более подробной информации об этом типе диаграмм смотрите документацию AmCharts.

2.5.4. PieChart

Компонент PieChart позволяет вам создать круговые диаграммы.

pie chart
Рисунок 17. PieChart

XML-имя компонента: chart:pieChart.

Связь с данными:
  1. Через контейнер данных.

    Вы можете указать для диаграммы контейнер данных с типом CollectionContainer, а затем указать нужные поля в атрибутах titleField и valueField элемента pieChart:

    <chart:pieChart id="pieChart"
                    dataContainer="countryLitresDc"
                    height="100%"
                    titleField="country"
                    valueField="litres"
                    width="100%"/>
  2. С помощью элемента chart:data.

Слушатели событий PieChart:
  • ChartClickListener – щелчок по холсту.

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

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

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

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

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

Для более подробной информации об этом типе диаграмм смотрите документацию AmCharts.

2.5.5. RadarChart

Компонент RadarChart позволяет вам создать радиальные/сетчатые диаграммы.

radar chart
Рисунок 18. RadarChart

XML-имя компонента: chart:radarChart.

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

Вы можете указать для диаграммы контейнер данных с типом CollectionContainer, а затем указать нужные поля в качестве значений атрибутов categoryField элемента radarChart и valueField вложенного элемента graph:

<chart:radarChart id="radarChart"
                  categoryField="country"
                  dataContainer="countryLitresDc"
                  height="100%"
                  startDuration="2"
                  theme="LIGHT"
                  width="100%">
    <chart:graphs>
        <chart:graph balloonText="[[value]] litres of beer per year"
                     bullet="ROUND"
                     valueField="litres"/>
    </chart:graphs>
</chart:radarChart>
Слушатели событий RadarChart:
  • AxisZoomListener – масштабирование оси графика.

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

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

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

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

  • GraphItemRightClickListener – щелчок по элементу графика правой клавишей мыши.

  • RollOutGraphListener – разворачивание графика.

  • RollOutGraphItemListener – разворачивание элемента графика.

  • RollOverGraphListener – сворачивание графика.

  • RollOverGraphItemListener – сворачивание элемента графика.

Для более подробной информации об этом типе диаграмм смотрите AmCharts documentation.

2.5.6. SerialChart

Компонент SerialChart позволяет вам создать большое количество диаграмм: линейчатые, диаграммы с областями, гистограммы, диаграммы с накоплением и прочие. Такие диаграммы поддерживают несколько осей в простом или логарифмическом масштабе. Данные могут быть отображены на равных/неравных отрезках или на временной шкале.

line chart
Рисунок 19. SerialChart в виде линейного графика
column chart
Рисунок 20. SerialChart в виде гистограммы

XML-имя компонента: chart:serialChart.

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

Вы можете указать для диаграммы контейнер данных с типом CollectionContainer, а затем указать нужные поля в качестве значений атрибутов categoryField элемента serialChart и valueField вложенного элемента graph:

<chart:serialChart categoryField="date"
                   dataContainer="dateValueDc">
    <chart:graphs>
        <chart:graph valueField="value"
                     balloonText="[[value]]">
        </chart:graph>
    </chart:graphs>
    <chart:categoryAxis dashLength="1"
                        minorGridEnabled="true"/>
</chart:serialChart>
Слушатели событий SerialChart
  • AxisZoomListener – масштабирование оси графика.

  • CategoryItemClickListener – щелчок по категории на оси категорий.

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

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

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

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

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

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

  • GraphItemRightClickListener – щелчок по элементу графика правой клавишей мыши.

  • RollOutGraphListener – разворачивание графика.

  • RollOutGraphItemListener – разворачивание элемента графика.

  • RollOverGraphListener – сворачивание графика.

  • RollOverGraphItemListener – сворачивание элемента графика.

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

Для более подробной информации об этом типе диаграмм смотрите документацию AmCharts.

2.5.7. StockChartGroup

Компонент StockChartGroup позволяет вам создать диаграмму с накоплением, или фондовую диаграмму.

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

stock chart with datasets
Рисунок 21. StockChart с несколькими наборами данных

Фондовые диаграммы могут отображать различные типы аннотаций на графе или оси. Эти аннотации называются stock events.

stock chart with stockevents
Рисунок 22. StockChart содержащий StockEvents

Фондовые диаграммы поддерживают любое количество панелей. Каждая панель может иметь любое количество графов. Каждая панель – это отдельная диаграмма и базируется на SerialChart. Другими словами, панель может все то же самое, что и эта диаграмма.

stock chart with panels
Рисунок 23. StockChart с несколькими панелями
Слушатели событий StockChartGroup:
  • DataSetSelectorCompareListener – сравнение селекторов наборов данных.

  • DataSetSelectorSelectListener – выбор селектора набора данных.

  • DataSetSelectorUnCompareListener – отмена сравнения селекторов наборов данных.

  • PeriodSelectorChangeListener – выбор периода отображения при помощи селектора.

  • StockChartClickListener – щелчок по холсту фондовой диаграммы.

  • StockChartRightClickListener – щелчок по холсту фондовой диаграммы правой клавишей мыши.

  • StockEventClickListener – щелчок по событию фондовой диаграммы.

  • StockEventRollOutListener – разворачивание события фондовой диаграммы.

  • StockEventRollOverListener – сворачивание события фондовой диаграммы.

  • StockGraphClickListener – щелчок по фондовой диаграмме.

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

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

  • StockGraphItemRollOutListener – разворачивание элемента фондовой диаграммы.

  • StockGraphItemRollOverListener – сворачивание элемента фондовой диаграммы.

  • StockGraphRollOutListener – разворачивание фондовой диаграммы.

  • StockGraphRollOverListener – сворачивание элемента фондовой диаграммы.

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

2.5.8. XYChart

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

xy chart
Рисунок 24. XYChart

XML-имя компонента: chart:xyChart.

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

Вы можете указать для диаграммы контейнер данных с типом CollectionContainer, а затем определить атрибуты xField и yFields для вложенных элементов graph:

<chart:xyChart dataContainer="pointPairDc"
               startDuration="1">
    <chart:graphs>
        <chart:graph balloonText="x:[[x]] y:[[y]]"
                     xField="ax"
                     fillToAxis="x"
                     yField="ay"/>
        <chart:graph balloonText="x:[[x]] y:[[y]]"
                     fillToAxis="y"
                     xField="bx"
                     yField="by"/>
    </chart:graphs>
    <chart:valueAxes>
        <chart:axis id="x"
                    axisAlpha="0"
                    dashLength="1"
                    position="BOTTOM"
                    title="X Axis"/>
        <chart:axis id="y"
                    axisAlpha="0"
                    dashLength="1"
                    position="LEFT"
                    title="Y Axis"/>
    </chart:valueAxes>
</chart:xyChart>
Слушатели событий XYChart:
  • AxisZoomListener – масштабирование оси графика.

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

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

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

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

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

  • GraphItemRightClickListener – щелчок по элементу графика правой клавишей мыши.

  • RollOutGraphListener – разворачивание графика.

  • RollOutGraphItemListener – разворачивание элемента графика.

  • RollOverGraphListener – сворачивание графика.

  • RollOverGraphItemListener – сворачивание элемента графика.

Для более подробной информации об этом типе диаграмм смотрите документацию AmCharts.

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

В данной главе мы рассмотрим применение подсистемы отображения диаграмм.

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

  1. Запустите CUBA Studio, создайте новый проект, как это описано в разделе Создание нового проекта в Руководстве пользователя CUBA Studio, и назовите его sampler.

  2. Добавьте в проект аддон, как это описано в разделе Установка.

  3. Запустите CUBA → Build Tasks → Deploy. На этом этапе будет произведена сборка приложения, и оно будет развернуто на сервере Tomcat в подкаталоге build/tomcat.

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

2.6.2. Создание диаграммы с использованием Data Binding API

Для первого примера мы создадим максимально простую диаграмму, используя упрощённый API.

Добавим компонент chart к экрану. Чтобы наполнить диаграмму данными, используем метод addData(). В качестве параметра передадим экземпляры класса MapDataItem, содержащие пары ключ-значение:

<chart:pieChart id="pieChart"
                titleField="key"
                valueField="value"/>
@Inject
private PieChart pieChart;

@Subscribe
private void onBeforeShow(BeforeShowEvent event) {
    pieChart.addData(MapDataItem.of("key", "piece of apple pie",
                "value", 70),
            MapDataItem.of("key", "piece of blueberry pie",
                "value", 20),
            MapDataItem.of("key", "piece of cherry pie",
                "value", 10));
}
chart simple
Рисунок 25. Простая круговая диаграмма

2.6.3. Создание диаграммы с данными из сущности

В этом примере мы создадим диаграмму, похожую на 3D Stacked Column Chart из демо AmCharts. Эта диаграмма будет извлекать данные из базы данных, поэтому необходимо определить атрибут dataContainer. JavaScript код выглядит следующим образом:

var chart = AmCharts.makeChart("chartdiv", {
    "theme": "light",
    "type": "serial",
    "dataProvider": [{
        "country": "USA",
        "year2004": 3.5,
        "year2005": 4.2
    }, {
        "country": "UK",
        "year2004": 1.7,
        "year2005": 3.1
    }, {
        "country": "Canada",
        "year2004": 2.8,
        "year2005": 2.9
    }, {
        "country": "Japan",
        "year2004": 2.6,
        "year2005": 2.3
    }, {
        "country": "France",
        "year2004": 1.4,
        "year2005": 2.1
    }, {
        "country": "Brazil",
        "year2004": 2.6,
        "year2005": 4.9
    }, {
        "country": "Russia",
        "year2004": 6.4,
        "year2005": 7.2
    }, {
        "country": "India",
        "year2004": 8,
        "year2005": 7.1
    }, {
        "country": "China",
        "year2004": 9.9,
        "year2005": 10.1
    }],
    "valueAxes": [{
        "stackType": "3d",
        "unit": "%",
        "position": "left",
        "title": "GDP growth rate",
    }],
    "startDuration": 1,
    "graphs": [{
        "balloonText": "GDP grow in [[category]] (2004): <b>[[value]]</b>",
        "fillAlphas": 0.9,
        "lineAlpha": 0.2,
        "title": "2004",
        "type": "column",
        "valueField": "year2004"
    }, {
        "balloonText": "GDP grow in [[category]] (2005): <b>[[value]]</b>",
        "fillAlphas": 0.9,
        "lineAlpha": 0.2,
        "title": "2005",
        "type": "column",
        "valueField": "year2005"
    }],
    "plotAreaFillAlphas": 0.1,
    "depth3D": 60,
    "angle": 30,
    "categoryField": "country",
    "categoryAxis": {
        "gridPosition": "start"
    },
    "export": {
            "enabled": true
     }
});
2.6.3.1. Создание сущности

Создадим класс сущности CountryGrowth.

  1. Перейдите в секцию Data Model дерева CUBA и нажмите New → Entity в её контекстном меню. Появится диалоговое окно New CUBA Entity.

  2. В поле Entity name введите название класса сущности − CountryGrowth, выберите тип Not persistent в поле Entity type, после чего нажмите кнопку OK. В рабочей области откроется страница дизайнера сущности.

  3. Используя Entity Designer добавьте атрибуты:

    • country типа String

    • year2014 типа Double

    • year2015 типа Double

  4. Перейдите на вкладку Text. Здесь отображается исходный код класса CountryGrowth:

    package com.company.sampler.entity;
    
    import com.haulmont.chile.core.annotations.MetaClass;
    import com.haulmont.chile.core.annotations.MetaProperty;
    import com.haulmont.cuba.core.entity.BaseUuidEntity;
    
    @MetaClass(name = "sampler_CountryGrowth")
    public class CountryGrowth extends BaseUuidEntity {
        @MetaProperty
        protected String country;
    
        @MetaProperty
        protected Double year2014;
    
        @MetaProperty
        protected Double year2015;
    
        public Double getYear2015() {
            return year2015;
        }
    
        public void setYear2015(Double year2015) {
            this.year2015 = year2015;
        }
    
        public Double getYear2014() {
            return year2014;
        }
    
        public void setYear2014(Double year2014) {
            this.year2014 = year2014;
        }
    
        public String getCountry() {
            return country;
        }
    
        public void setCountry(String country) {
            this.country = country;
        }
    }

    Этот класс описывает неперсистентную сущность. Экземпляр этого класса содержит процент роста ВВП страны за 2014 и 2015 года.

На этом создание сущности CountryGrowth завершено.

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

Теперь создадим новый экран для отображения диаграммы.

  1. Выберите New → Screen в контекстном меню элемента Generic UI в дереве проекта. После этого на экране отобразится страница создания стандартных экранов сущности.

  2. В списке доступных шаблонов выберите Blank screen и нажмите Next.

  3. Введите значение column3d-chart.xml в поле Descriptor name и нажмите Next.

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

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
        caption="msg://caption"
        messagesPack="com.company.sampler.web"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
    <data>
        <collection id="countryGrowthDc"
                    class="com.company.sampler.entity.CountryGrowth"
                    view="_local"/>
    </data>
    <layout>
        <chart:serialChart id="chart"
                           angle="30"
                           categoryField="country"
                           dataContainer="countryGrowthDc"
                           depth3D="60"
                           height="100%"
                           plotAreaFillAlphas="0.1"
                           startDuration="1"
                           width="100%">
            <chart:categoryAxis gridPosition="START"/>
            <chart:valueAxes>
                <chart:axis position="LEFT"
                            stackType="BOX_3D"
                            title="GDP growth rate"
                            unit="%"/>
            </chart:valueAxes>
            <chart:graphs>
                <chart:graph id="graph2014"
                             balloonText="GDP grow in [[category]] (2014): &lt;b&gt;[[value]]&lt;/b&gt;"
                             fillAlphas="0.9"
                             lineAlpha="0.2"
                             title="2014"
                             type="COLUMN"
                             valueField="year2014"/>
                <chart:graph id="graph2015"
                             balloonText="GDP grow in [[category]] (2015): &lt;b&gt;[[value]]&lt;/b&gt;"
                             fillAlphas="09."
                             lineAlpha="0.2"
                             title="2015"
                             type="COLUMN"
                             valueField="year2015"/>
            </chart:graphs>
            <chart:export/>
        </chart:serialChart>
    </layout>
</window>

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

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

Диаграмма получает данные из контейнера countryGrowthDc, указанного в атрибуте dataContainer. Для отображения названий и значений используются атрибуты country, year2014 и year2015 сущности CountryGrowth, список экземпляров которой находится в контейнере данных.

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

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

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

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

  • plotAreaFillAlphas – степень непрозрачности области графика.

  • startDuration – длительность анимации в секундах.

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

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

  • chart:categoryAxis – элемент, описывающий ось категорий.

    • Атрибут gridPosition определяет будет ли линия сетки расположена по центру ячейки или от ее начала.

  • chart:valueAxes – элемент, описывающий вертикальные оси значений. В данном случае используется только одна ось, описываемая элементом chart:axis

    • Атрибут position задает положение оси значений относительно диаграммы.

    • Установка атрибуту stackType значения BOX_3D говорит о том, что колонки гистограммы будут расположены одна позади другой.

  • chart:graphs – элемент, описывающий графы диаграммы. Граф описывается элементом chart:graph.

    • Атрибут type задает тип графа и может быть: line, column, step line, smoothed line, ohlc и candlestick.

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

    • Атрибут fillAlphas задает степень непрозрачности заполнения.

    • Атрибут lineAlpha задает степень непрозрачности линии (или рамки колонки).

  • chart:export – добавляет возможность сохранить полученный график.

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

Откройте контроллер экрана Column3dChart и замените его содержимое на следующий код:

package com.company.sampler.web;

import com.company.sampler.entity.CountryGrowth;
import com.haulmont.cuba.gui.model.CollectionContainer;
import com.haulmont.cuba.gui.screen.Screen;
import com.haulmont.cuba.gui.screen.Subscribe;
import com.haulmont.cuba.gui.screen.UiController;
import com.haulmont.cuba.gui.screen.UiDescriptor;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@UiController("sampler_Column3dChart")
@UiDescriptor("column3d-chart.xml")
public class Column3dChart extends Screen {

    @Inject
    private CollectionContainer<CountryGrowth> countryGrowthDc;

    @Subscribe
    private void onInit(InitEvent event) {
        List<CountryGrowth> items = new ArrayList<>();
        items.add(countryGrowth("USA", 3.5, 4.2));
        items.add(countryGrowth("UK", 1.7, 3.1));
        items.add(countryGrowth("Canada", 2.8, 2.9));
        items.add(countryGrowth("Japan", 2.6, 2.3));
        items.add(countryGrowth("France", 1.4, 2.1));
        items.add(countryGrowth("Brazil", 2.6, 4.9));
        items.add(countryGrowth("Russia", 6.4, 7.2));
        items.add(countryGrowth("India", 8.0, 7.1));
        items.add(countryGrowth("China", 9.9, 10.1));
        countryGrowthDc.setItems(items);
    }

    private CountryGrowth countryGrowth(String country, double year2014, double year2015) {
        CountryGrowth cg = new CountryGrowth();
        cg.setCountry(country);
        cg.setYear2014(year2014);
        cg.setYear2015(year2015);
        return cg;
    }
}

В методе onInit происходит заполнение контейнера данных countryGrowthDc данными.


Результат

Посмотрим, как созданный нами экран выглядит в работающем приложении. Выполните команду CUBA → Start application server.

Зайдите в систему, использовав стандартные имя и пароль в окне логина. Откройте пункт меню Application → Column3dChart, и вы увидите диаграмму, как показано ниже:

column3d chart
Рисунок 26. Трехмерная гистограмма

2.6.4. Создание диаграммы с данными из DataProvider

В этом примере мы рассмотрим диаграмму, получающую данные из экземпляра DataProvider, создаваемого в контроллере экрана, поэтому атрибут dataContainer определять не нужно.

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

Создадим новый экран для отображения диаграммы.

  1. Выберите New → Screen в контекстном меню элемента Generic UI в дереве проекта. После этого на экране отобразится страница создания стандартных экранов сущности.

  2. В списке доступных шаблонов выберите Blank screen и нажмите Next.

  3. Введите значение stackedarea-chart.xml в поле Descriptor name и нажмите Next.

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

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
        caption="msg://caption"
        messagesPack="com.company.sampler.web"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
    <layout>
        <chart:serialChart id="chart"
                           categoryField="year"
                           height="100%"
                           marginLeft="0"
                           marginTop="10"
                           plotAreaBorderAlpha="0"
                           width="100%">
            <chart:chartCursor cursorAlpha="0"/>
            <chart:legend equalWidths="false"
                          periodValueText="total: [[value.sum]]"
                          position="TOP"
                          valueAlign="LEFT"
                          valueWidth="100"/>
            <chart:valueAxes>
                <chart:axis gridAlpha="0.07"
                            position="LEFT"
                            stackType="REGULAR"
                            title="Traffic incidents"/>
            </chart:valueAxes>
            <chart:graphs>
                <chart:graph fillAlphas="0.6"
                             hidden="true"
                             lineAlpha="0.4"
                             title="Cars"
                             valueField="cars"/>
                <chart:graph fillAlphas="0.6"
                             lineAlpha="0.4"
                             title="Motorcycles"
                             valueField="motorcycles"/>
                <chart:graph fillAlphas="0.6"
                             lineAlpha="0.4"
                             title="Bicycles"
                             valueField="bicycles"/>
            </chart:graphs>
            <chart:categoryAxis axisColor="#DADADA"
                                gridAlpha="0.07"
                                startOnAxis="true"/>
            <chart:export/>
        </chart:serialChart>
    </layout>
</window>

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

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

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

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

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

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

    • Атрибут cursorAlpha задает степень непрозрачности линий курсора.

  • chart:legend – определяет легенду графика.

    • Атрибут position определяет положение легенды относительно диаграммы.

    • Атрибут equalWidths определяет, должны ли все элементы легенды быть такой же ширины как самый широкий.

    • Атрибут periodValueText задает текст, который будет показан в значении легенды, когда пользователь не располагает указатель мыши ни над одним элементом данных. Теги должны состоять из пары значений – название поля (value / open / close / high / low) и значение периода, для которого должно быть показано значение – open / close / high / low / sum / average / count.

    • Атрибут valueAlign задает выравнивание значения. Возможные значения left и right.

    • Атрибут valueWidth задает ширину значения.

  • chart:valueAxes – элемент, описывающий вертикальные оси значений. В данном случае используется только одна ось, описываемая элементом chart:axis

    • Атрибут position задает положение оси значений относительно диаграммы.

    • Атрибут title задает заголовок оси значения.

    • Установка атрибуту stackType значения REGULAR говорит о том, что используется диаграмма с накоплением. По умолчанию значение этого атрибута – none, в таком случае используется диаграмма без накопления.

    • Атрибут gridAlpha задает степень непрозрачности линий сетки.

  • chart:graphs – элемент, описывающий графы диаграммы. Граф описывается элементом chart:graph.

    • Атрибут type задает тип графа и может быть: line, column, step line, smoothed line, ohlc и candlestick.

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

    • Атрибут fillAlphas задает степень непрозрачности заполнения.

    • Атрибут lineAlpha задает степень непрозрачности линии (или рамки колонки).

    • Атрибут hidden определяет, будет ли граф отображаться.

  • chart:categoryAxis – элемент, описывающий ось категорий.

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

    • Атрибут gridAlpha задает степень непрозрачности линий сетки.

    • Атрибут axisColor задает цвет оси.

  • chart:export – добавляет возможность сохранить полученный график.

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

Откройте контроллер экрана StackedareaChart замените его содержимое на следующий код:

package com.company.sampler.web;

import com.haulmont.charts.gui.components.charts.SerialChart;
import com.haulmont.charts.gui.data.DataItem;
import com.haulmont.charts.gui.data.ListDataProvider;
import com.haulmont.charts.gui.data.MapDataItem;
import com.haulmont.cuba.gui.screen.Screen;
import com.haulmont.cuba.gui.screen.Subscribe;
import com.haulmont.cuba.gui.screen.UiController;
import com.haulmont.cuba.gui.screen.UiDescriptor;

import javax.inject.Inject;

@UiController("sampler_StackedareaChart")
@UiDescriptor("stackedarea-chart.xml")
public class StackedareaChart extends Screen {
    @Inject
    private SerialChart chart;

    @Subscribe
    private void onInit(InitEvent event) {
        ListDataProvider dataProvider = new ListDataProvider();
        dataProvider.addItem(transportCount(1994, 1587, 650, 121));
        dataProvider.addItem(transportCount(1995, 1567, 683, 146));
        dataProvider.addItem(transportCount(1996, 1617, 691, 138));
        dataProvider.addItem(transportCount(1997, 1630, 642, 127));
        dataProvider.addItem(transportCount(1998, 1660, 699, 105));
        dataProvider.addItem(transportCount(1999, 1683, 721, 109));
        dataProvider.addItem(transportCount(2000, 1691, 737, 112));
        dataProvider.addItem(transportCount(2001, 1298, 680, 101));
        dataProvider.addItem(transportCount(2002, 1275, 664, 97));
        dataProvider.addItem(transportCount(2003, 1246, 648, 93));
        dataProvider.addItem(transportCount(2004, 1318, 697, 111));
        dataProvider.addItem(transportCount(2005, 1213, 633, 87));
        dataProvider.addItem(transportCount(2006, 1199, 621, 79));
        dataProvider.addItem(transportCount(2007, 1110, 210, 81));
        dataProvider.addItem(transportCount(2008, 1165, 232, 75));
        dataProvider.addItem(transportCount(2009, 1145, 219, 88));
        dataProvider.addItem(transportCount(2010, 1163, 201, 82));
        dataProvider.addItem(transportCount(2011, 1180, 285, 87));
        dataProvider.addItem(transportCount(2012, 1159, 277, 71));

        chart.setDataProvider(dataProvider);
    }

    private DataItem transportCount(int year, int cars, int motorcycles, int bicycles) {
        MapDataItem item = new MapDataItem();
        item.add("year", year);
        item.add("cars", cars);
        item.add("motorcycles", motorcycles);
        item.add("bicycles", bicycles);
        return item;
    }
}

В методе onInit происходит установка данных в диаграмму с накоплением. Диаграммы подобного типа показывают отношение отдельных составляющих к их совокупному значению.


Результат

Посмотрим, как созданный нами экран выглядит в работающем приложении. Выполните команду CUBA → Start application server.

Зайдите в систему, использовав стандартные имя и пароль в окне логина. Откройте пункт меню Application → StackedareaChart, и вы увидите диаграмму, как показано ниже:

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

2.6.5. Создание диаграммы с инкрементальным обновлением данных

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

Пример основан на тестовом приложении Sales, к которому мы добавим диаграмму для отображения динамики новых заказов, то есть создания новых экземпляров сущности Order.

  1. Скачайте приложение Sales и добавьте к нему компонент charts, следуя инструкции из раздела Настройка проекта приложения.

  2. Создайте в Studio новый пустой экран. Назовите его orders-history, так как в нём будет отображаться история создания новых заказов.

  3. Добавьте к экрану компонент serialChart. Чтобы реализовать инкрементальное обновление данных, необходимо создать контейнер данных с типом CollectionContainer и привязать к нему диаграмму. В этом примере мы не будем загружать данные из базы, вместо этого мы будем создавать тестовые данные на лету, поэтому загрузчик данных в контейнере создавать не нужно.

    Для оси категорий укажите атрибут date, для оси значений – атрибут amount.

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
            caption="msg://caption"
            messagesPack="com.company.sales.web"
            xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
        <data>
            <collection id="ordersDc"
                        class="com.company.sales.entity.Order"
                        view="_local"/>
        </data>
        <layout>
            <chart:serialChart id="orderHistoryChart"
                               categoryField="date"
                               dataContainer="ordersDc"
                               width="100%">
                <chart:graphs>
                    <chart:graph valueField="amount"/>
                </chart:graphs>
            </chart:serialChart>
        </layout>
    </window>
  4. Для обновления данных на лету используйте timer – специальный UI-компонент, который будет отправлять HTTP-запросы на сторону сервера.

    • Переключитесь на вкладку Designer.

    • В группе Non-visual components в палитре компонентов найдите Timer и перетащите его в область иерархии компонентов экрана.

    • Выделите компонент в иерархии и перейдите на вкладку Properties.

    • Задайте идентификатор таймера в поле id.

    • Допустим, мы хотим, чтобы данные обновлялись каждые 2 секунды, в этом случае в поле delay укажите значение 2000 миллисекунд.

    • В поле onTimer укажите имя метода Java – updateChart. Этот метод будет вызываться каждый раз при срабатывании события таймера. Сгенерируйте метод в контроллере экрана, нажав на кнопку >>.

    • Поставьте флажки repeating и autostart.

    chart incremental update
    Рисунок 28. Создание таймера
  5. Откройте контроллер экрана OrdersHistory. Для разработки логики работы таймера инжектируйте следующие зависимости: timeSource, metadata и контейнер данных для сущности Order. Мы будем генерировать новый экземпляр сущности Order с произвольным значением amount при каждом событии срабатывания таймера. Новый экземпляр добавляется к контейнеру данных с помощью метода getMutableItems().add().

    Инициализируйте диаграмму в методе onInit(), создав таким же образом исходный экземпляр сущности Order.

    package com.company.sales.web;
    
    import com.company.sales.entity.Order;
    import com.haulmont.cuba.core.global.Metadata;
    import com.haulmont.cuba.core.global.TimeSource;
    import com.haulmont.cuba.gui.components.Timer;
    import com.haulmont.cuba.gui.model.CollectionContainer;
    import com.haulmont.cuba.gui.screen.*;
    
    import javax.inject.Inject;
    import java.math.BigDecimal;
    import java.util.Random;
    
    @UiController("sales_OrdersHistory")
    @UiDescriptor("orders-history.xml")
    public class OrdersHistory extends Screen {
        @Inject
        private Metadata metadata;
        @Inject
        private TimeSource timeSource;
    
        @Inject
        private CollectionContainer<Order> ordersDc;
    
        private Random random = new Random(42);
    
        @Subscribe
        private void onInit(InitEvent event) {
            Order initialValue = metadata.create(Order.class);
            initialValue.setAmount(new BigDecimal(random.nextInt(1000) + 100));
            initialValue.setDate(timeSource.currentTimestamp());
    
            ordersDc.getMutableItems().add(initialValue);
        }
    
    
    
        public void updateChart(Timer source) {
            Order orderHistory = metadata.create(Order.class);
            orderHistory.setAmount(new BigDecimal(random.nextInt(1000) + 100));
            orderHistory.setDate(timeSource.currentTimestamp());;
            ordersDc.getMutableItems().add(orderHistory);
        }
    }

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

    chart incremental update 2
    Рисунок 29. Данные автоматически обновляются каждые 2 секунды
  6. Создайте экземпляр класса Queue для очереди заказов. При каждом срабатывании таймера созданный заказ будет добавлен наверх очереди itemsQueue. Когда размер очереди превышает 10 заказов, самый старый заказ удаляется.

    private Queue<Order> itemsQueue = new LinkedList<>();
    public void updateChart(Timer source) {
        Order orderHistory = metadata.create(Order.class);
        orderHistory.setAmount(new BigDecimal(random.nextInt(1000) + 100));
        orderHistory.setDate(timeSource.currentTimestamp());;
        ordersDc.getMutableItems().add(orderHistory);
    
        itemsQueue.add(orderHistory);
    
        if (itemsQueue.size() > 10) {
            Order item = itemsQueue.poll();
            ordersDc.getMutableItems().add(item);
        }
    }

Результат

Данные поступают в браузер инкрементально. Если открыть консоль разработчика в Chrome, на вкладке Network будет видно, что каждые 2 секунды страница отправляет HTTP-запрос на backend и в ответе получает очень маленький JSON, содержащий только операции add и remove со значениями поля amount. Это позволяет избежать повторной пересылки всех данных диаграммы.

chart incremental update 3
Рисунок 30. Одновременно отображаются только последние 10 заказов

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

Проиллюстрируем использование событий. Добавим в экран, созданный в разделе Контроллер экрана, обработку события нажатия на элемент графа. Откройте контроллер экрана и инжектируйте диаграмму. Также вам потребуется инжекция Notifications бина в целях показа уведомления:

@Inject
private Notifications notifications;

@Inject
private SerialChart chart;

Далее добавьте слушателя в конце метода onInit. Если график получает данные через DataProvider, для обработки нажатия на элемент графа используется метод getDataItemNN(). В данном примере компонент SerialChart привязан к контейнеру данных, поэтому для получения элемента используется другой метод: getEntityNN():

@Subscribe
private void onInit(InitEvent event) {
    chart.addGraphItemClickListener(graphItemClickEvent ->
            notifications.create()
                    .withCaption(itemClickEventInfo(graphItemClickEvent))
                    .withContentMode(ContentMode.HTML)
                    .show());
}

private String itemClickEventInfo(Chart.GraphItemClickEvent event) {
    CountryGrowth countryGrowth = (CountryGrowth) event.getEntityNN();
    return String.format("GDP grow in %s (%s): %.1f%%",
            countryGrowth.getCountry(),
            event.getGraphId().substring(5),
            "graph2014".equals(event.getGraphId()) ? countryGrowth.getYear2014() : countryGrowth.getYear2015());
}

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

chart with event
Рисунок 31. Диаграмма с обработкой события нажатия на элемент графа

2.6.7. Конфигурация с помощью JSON

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

Рассмотрим это на примере serialChart:

<chart:serialChart id="serialChart">
    <chart:valueAxes>
        <chart:axis axisAlpha="0" position="LEFT" title="Incidents"/>
    </chart:valueAxes>
    <chart:graphs>
        <chart:graph id="g1" bullet="ROUND" type="COLUMN" valueField="value"/>
    </chart:graphs>
    <chart:categoryAxis position="TOP" title="Time" labelsEnabled="false"/>
</chart:serialChart>

Для графика заданы определенные данные:

@Inject
private SerialChart serialChart;

@Subscribe
private void onInit(InitEvent event) {
    ListDataProvider serialChartDataProvider = new ListDataProvider();
    int[] serialChartData = {5, 7, 6, 9, 7, 8, 5, 6, 4, 6, 5, 7, 4, 5, 3, 4, 2, 0};

    for (int i = 0; i < serialChartData.length; i++) {
        serialChartDataProvider.addItem(graphData(serialChartData[i]));
    }

    serialChart.setDataProvider(serialChartDataProvider);
}

private DataItem graphData(int value) {
    MapDataItem item = new MapDataItem();
    item.add("value", value);
    return item;
}
chart custom json
Рисунок 32. SerialChart

Теперь мы можем изменить конфигурацию графика. Например, добавить заголовок:

serialChart.setNativeJson("{\n" +
        " \"titles\": [\n" +
        " {\n" +
        " \"size\": 15,\n" +
        " \"text\": \"Chart Title\"\n" +
        " }\n" +
        " ]\n" +
        "}");
chart custom json title
Рисунок 33. SerialChart с заголовком, заданным JSON

Также возможно задать JSON конфигурацию с помощью XML:

<chart:serialChart id="serialChart">
    <chart:nativeJson>
        <![CDATA[
        {
            "titles": [
                {
                    "size": 15,
                    "text": "Chart Title"
                }
            ]
        }
        ]]>
    </chart:nativeJson>
    <chart:valueAxes>
        <chart:axis axisAlpha="0" position="LEFT" title="Incidents"/>
    </chart:valueAxes>
    <chart:graphs>
        <chart:graph id="g1" bullet="ROUND" type="COLUMN" valueField="value"/>
    </chart:graphs>
    <chart:categoryAxis position="TOP" title="Time" labelsEnabled="false"/>
</chart:serialChart>

2.7. Замена версии AmCharts

Версию библиотеки AmCharts, включенную в платформу, можно заменить на другую. Для этого необходимо:

  1. Скачать исходный код charts и stock charts с сайта AmCharts.

  2. Объединить содержимое папок amcharts в одну папку.

  3. Скопировать папку amcharts в {project.rootDir}/modules/web/web/VAADIN/webjars

  4. Произвести развертывание приложения заново.

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

chart.setNativeJson("{\"valueScrollbar\":{\"autoGridCount\":true}}");

3. Отображение карт

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

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

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

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

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

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

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

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

    map demo marker
    Рисунок 35. Маркер карты
  • Отображение ломаных линий и полигонов (многоугольников).

    map demo polygon display
    Рисунок 36. Отображение ломаных линий
  • Рисование полигонов.

    map demo polygon draw
    Рисунок 37. Полигоны
  • Отображение тепловой карты (heat map).

    map demo heatmap
    Рисунок 38. Тепловая карта

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

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

  • Один из следующих параметров (смотрите подробную информацию об этих параметрах в документации Google Maps API):

    • charts.map.apiKey – browser key.

    • charts.map.clientId – client ID.

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

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

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

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

    • charts.map.apiVersion – определяет версию Google Maps API, которую следует использовать. Значение по умолчанию – 3.35. Для того чтобы использовать последнюю выпущенную версию Google Maps API, установите свойству charts.map.apiVersion значение 3, чтобы использовать экспериментальную версию – значение 3.exp. Больше информации о Google Maps API содержится в официальной документации.

    • charts.map.language - задаёт язык карты. Необходимо указывать полное название языка.

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

charts.map.apiKey = my_key
charts.map.defaultZoom = 13.0
charts.map.defaultLatitude = 51.5001
charts.map.defaultLongitude = -0.1262
charts.map.language = english

3.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;

@Subscribe
protected void onInit(InitEvent event) {
    GeoPoint center = map.createGeoPoint(53.490905, -2.249558);
    map.setCenter(center);
}
Методы настройки карты
  • fitToBounds() – задание минимального масштаба карты, при котором будет полностью отображена область, заданная северо-восточной и юго-западной координатами.

  • removePolygonVertex() – удаление вершины полигона.

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

  • setCenterBoundLimits() – задание возможных границ центра карты.

  • setCenterBoundLimitsEnabled() – включение режима, ограничивающего центр карты.

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

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

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

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

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

  • setRemoveMessage() – задание строки сообщения для удаления вершины полигона.

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

  • setVertexRemovingEnabled() – включение/выключение режима, разрешающего удаление вершин полигона.

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

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

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

Интерфейсы компонентов карты

Интерфейсы, перечисленные ниже, располагаются в пакете com.haulmont.charts.gui.map.model.

  • Circle – компонент для отображения круга. Компонент Circle имеет те же атрибуты, что Polygon, а его форма определяется двумя дополнительными атрибутами: center (GeoPoint) и radius. Для создания и размещения объекта на карте используются методы createCircle() и addCircle() интерфейса MapViewer.

    Circle circle = map.createCircle(center, 130.5);
    circle.setDraggable(true);;
    circle.setFillOpacity(0.5);
    map.addCircleOverlay(circle);
    Circle
    Рисунок 39. Объект Circle
  • 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);
  • GeoPoint – вспомогательный компонент, непосредственно не отображаемый на карте. Используется для задания параметров карты, таких как центр, границы, и для создания более сложных компонентов карты. Для создания объекта используется метод createGeoPoint() интерфейса MapViewer. Например:

    GeoPoint center = map.createGeoPoint(53.490905, -2.249558);
    map.setCenter(center);
  • HeatMapLayer – слой тепловой карты: предназначен для изображения плотности данных в различных географических точках. Степень плотности точек отображается с помощью цвета. По умолчанию области с высокой плотностью точек отображаются красным цветом, а области с низкой – зелёным. Для создания и размещения объекта на карте используются методы createHeatMapLayer() и addHeatMapLayer() интерфейса MapViewer. Например:

    HeatMapLayer heatMapLayer = map.createHeatMapLayer();
    List<GeoPoint> data = new ArrayList<>();
    data.add(map.createGeoPoint(53.450, -2.090));
    data.add(map.createGeoPoint(53.451, -2.095));
    data.add(map.createGeoPoint(53.452, -2.092));
    data.add(map.createGeoPoint(53.453, -2.093));
    data.add(map.createGeoPoint(53.454, -2.093));
    data.add(map.createGeoPoint(53.454, -2.092));
    data.add(map.createGeoPoint(53.453, -2.092));
    heatMapLayer.setData(data);
    map.addHeatMapLayer(heatMapLayer);
    HeatMap
    Рисунок 40. HeatMapLayer

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

  • InfoWindow – компонент карты для отображения информации во всплывающем окне. Для создания и размещения объекта на карте используются методы createInfoWindow() и openInfoWindow() интерфейса MapViewer. Например:

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

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

    map.addMarkerClickListener(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);
    });
    InfoWindow
    Рисунок 41. Информационное окно
  • Label – компонент для отображения текстовых подписей на карте.

    Label можно создать и поместить на карту с помощью методов createLabel() и addLabel() интерфейса MapViewer. Удалить его, в свою очередь, можно методом removeLabel(). Компонент поддерживает стили и разметку HTML .

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

    • value – строковое содержание подписи. Если выбран тип содержимого HTML, браузер выполнит парсинг этой строки для отображения содержимого.

    • position – объект, реализующий интерфейс GeoPoint, в котором содержатся географические координаты подписи.

    • contentType – выбор типа содержимого из двух возможных значений: PLAIN_TEXT и HTML (будет парситься браузером).

    • adjustment – устанавливает расположение подписи относительно маркера геолокации GeoPoint.

    • styleName – позволяет установить дополнительные стили подписи.

      Label label = map.createLabel();
      label.setValue("<span style=\"color: #ffffff; font-size: 24px;\">White label</span>");
      label.setPosition(map.createGeoPoint(42.955, 32.883));
      label.setAdjustment(Label.Adjustment.BOTTOM_CENTER);
      label.setContentType(Label.ContentType.HTML);
      map.addLabel(label);
      MapLabel
      Рисунок 42. Компонент Label
  • Marker – компонент для отметки места на карте. По умолчанию используется стандартный значок сервиса карт. Для создания и размещения объекта на карте используются методы createMarker() и addMarker() интерфейса MapViewer. Например:

    Marker marker = map.createMarker("My place", map.createGeoPoint(53.590905, -2.249558), true);
    marker.setClickable(true);
    map.addMarker(marker);

    Метод clearMarkers(), в свою очередь, удаляет с карты все отметки.

    Интерфейс MarkerImage добавляет на слой Marker изображение или пиктограмму.

    MarkerImage markerImage = map.createMarkerImage("https://www.cuba-platform.com/sites/logo.png");
    GeoPoint center = map.createGeoPoint(21.11, -76.20);
    markerImage.setSize(map.createSize(44, 44));
    markerImage.setOrigin(map.createPoint(0, 0));
    markerImage.setAnchor(map.createPoint(-5, 50));
    Marker marker = map.createMarker("Cuba", center, true, markerImage);
    map.addMarker(marker);
    MarkerImage
    Рисунок 43. Компонент Marker
  • Polyline – компонент для отображения ломаной линии. Для создания и размещения объекта на карте используются методы createPolyline() и addPolyline() интерфейса MapViewer. Например:

    List<GeoPoint> coordinates = new ArrayList<>();
    coordinates.add(map.createGeoPoint(53.4491, -1.9955));
    coordinates.add(map.createGeoPoint(53.6200, -1.9539));
    coordinates.add(map.createGeoPoint(53.4425, -1.6196));
    coordinates.add(map.createGeoPoint(53.1900, -1.4969));
    coordinates.add(map.createGeoPoint(53.1926, -1.6197));
    Polyline polyline = map.createPolyline(coordinates);
    polyline.setStrokeWeight(5);
    polyline.setStrokeOpacity(0.5);
    polyline.setStrokeColor("#7341f4");
    map.addPolyline(polyline);
    Polyline
    Рисунок 44. Компонент Polyline
  • Polygon – компонент для отображения полигона. Для создания и размещения объекта на карте используются методы createPolygon() и addPolygonOverlay() интерфейса MapViewer. Например:

    List<GeoPoint> coordinates = new ArrayList<>();
    coordinates.add(map.createGeoPoint(48.560579, 7.767876));
    coordinates.add(map.createGeoPoint(48.561386, 7.782791));
    coordinates.add(map.createGeoPoint(48.541940, 7.782861));
    coordinates.add(map.createGeoPoint(48.545641, 7.768749));
    Polygon p = map.createPolygon(coordinates, "#9CFBA9", 0.6, "#2CA860", 1.0, 2);
    map.addPolygonOverlay(p);
    Polygon
    Рисунок 45. Компонент Polygon
Слушатели событий

Слушатели, перечисленные ниже, располагаются в пакете com.haulmont.charts.gui.map.model.listeners.

  • CircleCenterChangeListener – изменение положения круга в режиме редактирования карты.

  • CircleCompleteListener – создание круга в режиме редактирования.

  • CircleRadiusChangeListener – изменение радиуса круга в режиме редактирования.

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

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

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

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

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

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

Слушатели щелчков левой кнопки мыши:

  • CircleClickListener – щелчок по кругу.

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

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

  • PolygonClickListener – щелчок по полигону.

Слушатели щелчков правой кнопки мыши:

  • CircleRightClickListener – щелчок по кругу.

  • MapRightCLickListener – щелчок по карте.

  • MarkerRightClickListener – щелчок по маркеру.

  • PolygonRightClickListener – щелчок по полигону.

Слушатели двойных щелчков:

  • MarkerDoubleClickListener – двойной щелчок по маркеру.

  • CircleDoubleClickListener – двойной щелчок по кругу.

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

4. Отображение PivotTable

PivotTable – табличный компонент для представления данных в виде сводной таблицы, в котором реализована возможность ручного перетаскивания её элементов для манипуляции данными и настройки таблицы непосредственно через 2D drag-and-drop UI. PivotTable полностью поддерживается библиотекой компонентов CUBA Studio.

PivotTable базируется на JavaScript библиотеке – https://github.com/nicolaskruchten/pivottable. На сайте автора библиотеки вы можете найти больше примеров компонента PivotTable: http://nicolas.kruchten.com/pivottable/examples/.

XML-имя компонента: pivotTable

Компонент реализован только для блока Web Client.

PivotTable 1
Рисунок 46. PivotTable

Пример описания таблицы в XML-дескрипторе экрана:

<chart:pivotTable id="tipsPivotTable"
                  dataContainer="tipsDc"
                  renderer="HEATMAP">
    <chart:properties>
        <chart:property name="row"/>
        <chart:property name="totalBill"/>
        <chart:property name="tip"/>
        <chart:property name="sex"/>
        <chart:property name="smoker"/>
        <chart:property name="day"/>
        <chart:property name="time"/>
        <chart:property name="size"/>
    </chart:properties>
    <chart:aggregation mode="SUM_OVER_SUM">
        <chart:property name="tip"/>
        <chart:property name="totalBill"/>
    </chart:aggregation>
    <chart:rows>
        <chart:row value="sex"/>
        <chart:row value="smoker"/>
    </chart:rows>
    <chart:columns>
        <chart:column value="day"/>
        <chart:column value="time"/>
    </chart:columns>
    <chart:sortersFunction>
        function(attr){
            if(attr=="Day"){
                return $.pivotUtilities.sortAs(["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]);
            }
        }
    </chart:sortersFunction>
</chart:pivotTable>
Элементы pivotTable
  • properties – коллекция ключ-значение атрибутов pivotTable, где ключ – имя атрибута в контейнере данных, а значение – его локализованное значение, используемое при отображении.

  • derivedProperties – используется для добавления новых генерированных атрибутов к контейнеру данных таблицы. Представляет собой коллекцию ключ-значение, где ключ – имя генерированного атрибута, а значение – JavaScript функция для генерации значения этого атрибута.

    • вложенные элементы derivedProperty должны иметь атрибут caption, который будет использоваться в качестве ключа.

    • элемент function задаёт значение для derivedProperty.

  • hiddenProperties – список атрибутов, которые не будут отображаться в UI. Значением может быть либо ключ из properties, либо произвольная строка, соответствующая имени одного из сгенерированных атрибутов (т.е.ключ из derivedProperties). Только для редактируемого pivotTable.

  • hiddenFromAggregations – список атрибутов, которые не будут отображаться в выпадающих списках для агрегирования.

  • hiddenFromDragDrop – список атрибутов, которые не будут доступны для drag-and-drop в настройках таблицы в UI.

  • columns – список атрибутов, которые будут использоваться в качестве колонок. Значением может быть либо ключ из properties, либо имя сгенерированного атрибута.

  • columnOrder – порядок отображения колонок рендерером таблицы.

  • rows – список атрибутов, которые будут использоваться в качестве строк. Значением может быть либо ключ из properties, либо имя сгенерированного атрибута.

  • rowOrder – порядок отображения строк рендерером таблицы.

  • exclusions – коллекция ключ-значение, где ключ – имя атрибутов (ключ из properties или имя сгенерированного атрибута), а значение – список значений этого атрибута, которые не будут использоваться при рендеринге. Только для редактируемого pivotTable.

  • inclusions – коллекция ключ-значение, где ключ – имя атрибутов (ключ из properties или имя сгенерированного атрибута), а значение – список значений этого атрибута, которые будут использоваться при рендеринге. Только для редактируемого pivotTable.

  • filterFunction – JavaScript код, который будет использоваться в качестве функции фильтрации.

  • renderers – описывает коллекцию рендереров, которые будут отображаться в списке доступных для выбора рендереров в UI.

    • атрибут default позволяет выбрать из списка предопределенных рендереров. Выбранный рендерер будет использоваться по умолчанию при загрузке компонента.

    • вложенный элемент renderer имеет один атрибут type – выбор из списка предопределенных рендереров: AREA_CHART, BAR_CHART, COL_HEATMAP, HEATMAP, HORIZONTAL_BAR_CHART, HORIZONTAL_STACKED_BAR_CHART, LINE_CHART, ROW_HEATMAP, SCATTER_CHART, STACKED_BAR_CHART, TABLE_BAR_CHART, TABLE, TREEMAP, TSV_EXPORT.

      Только для редактируемого pivotTable.

  • rendererOptions – описывает опции рендереров. В данный момент только два типа рендереров могут быть кастомизированы:

    • все виды heatmap. Можно задать JavaScript-функцию генерации цвета для ячеек.

    • все виды графиков. Можно задать размер графика.

  • sortersFunction – JavaScript код, который будет использоваться в качестве функции сортировки заголовков строк и колонок.

Элементы агрегирования pivotTable
  1. aggregation – настройки для функции, которая будет использоваться для агрегирования значений в каждой ячейке.

    Атрибуты aggregation:

    • mode – выбор из списка предопределенных функций.

    • caption – локализуемое значение, которое будет отображаться в UI.

    • custom – если true, то значение mode игнорируется, и будет использоваться JavaScript код, заданный во вложенном элементе function.

      Элементы aggregation:

    • function – JavaScript код, используемый в качестве функции агрегирования.

    • property – список атрибутов, которые будут использоваться в качестве входных параметров функции агрегирования. Значением может быть либо ключ из properties, либо имя сгенерированного атрибута. Только для нередактируемого pivotTable.

      Например:

      <chart:aggregation mode="SUM_OVER_SUM" custom="true">
          <chart:property name="tip"/>
          <chart:property name="Total Bill"/>
      </chart:aggregation>
  1. aggregationProperties – список атрибутов, которые будут установлены в выпадающих списках для значений агрегаторов. Значением может быть либо ключ из properties, либо имя сгенерированного атрибута. Только для редактируемого pivotTable.

    Например:

    <chart:aggregationProperties>
        <chart:property name="tip"/>
        <chart:property name="totalBill"/>
    </chart:aggregationProperties>
  1. aggregations – описывает коллекцию агрегаторов, которые будут отображаться в списке доступных для выбора агрегаторов в UI.

    Атрибуты aggregations:

    • атрибут default – выбор из списка предопределенных функций агрегирования. Выбранная функция агрегирования будет установлена по умолчанию при загрузке компонента.

    • вложенный элемент aggregation используется так же, как aggregation, но у него отсутствует вложенный элемент property. Только для редактируемого pivotTable.

      Например:

      <chart:aggregations default="COUNT">
          <chart:aggregation caption="Count"/>
          <chart:aggregation mode="SUM_OVER_SUM"/>
      </chart:aggregations>
Атрибуты pivotTable
  • dataContainer – ссылка на контейнер данных, указанный в секции data XML-дескриптора экрана. Контейнер должен иметь тип CollectionContainer.

  • editable – если true, в UI будут отображаться элементы для манипуляций с данными, в противном случае будут отображены только данные.

  • menuLimit – максимальное количество значений, отображаемых во всплывающем меню. Если значение превышено, появляется соответствующее сообщение. Только для редактируемого pivotTable.

  • renderer – выбор из списка предустановленных рендереров, служащих для отображения данных. Только для редактируемого pivotTable.

  • showColTotals – отображение суммы по каждому столбцу. Значение по умолчанию true. Только для табличных рендереров.

  • showRowTotals – отображение суммы по каждой строке. Значение по умолчанию true. Только для табличных рендереров.

  • showUI – видимость UI-элементов редактируемого pivotTable. Значение по умолчанию true.

  • autoSortUnusedProperties – определяет, будут ли неиспользованные атрибуты сортироваться в UI. Только для редактируемого pivotTable.

  • unusedPropertiesVertical – определяет, будут ли неиспользованные атрибуты отображаться вертикально (если true) или горизонтально (если false или по умолчанию). Если задано как число, тогда, если суммарная длина имен атрибутов в символах превысит это число, атрибуты будут показаны вертикально.

Слушатели pivotTable
  • addCellClickListener – добавляет слушатель кликов по ячейкам таблицы PivotTable. Событие CellClickEvent отрабатывает только для табличных типов рендереров (TABLE, HEATMAP, TABLE_BAR_CHART, COL_HEATMAP, ROW_HEATMAP).

    tipsPivotTableUI.addCellClickListener(event -> {
        showNotification("Value: " + event.getValue() + ",\n"
            + "Filters applied: " + event.getFilters());
    });
  • addRefreshListener – добавляет слушатель событий обновления PivotTable. Событие RefreshEvent отрабатывает только для редактируемой таблицы PivotTable.

    tipsPivotTableUI.addRefreshListener(event -> {
        showNotification("Row order :" + event.getRowOrder() + ",\n"
            + "Inclusions: " + event.getInclusions());
    });

    Событие RefreshEvent содержит в себе информацию о текущем состоянии таблицы: aggregation, aggregationProperties, columns, columnOrder, exclusions, inclusions, renderer, rowOrder, rows.



4.1. Примеры PivotTable

4.1.1. Создание PivotTable с данными из сущности

В этом примере мы создадим компоненет PivotTable, данные для которого будут извлекаться из сущностей, поэтому необходимо определить атрибут dataContainer.

4.1.1.1. Создание сущности

Сначала создадим перечисления. В секции Data Model дерева CUBA нажмите New → Enumeration и создайте следующие перечисления:

  • Sex со значениями: MALE, FEMALE

  • Smoker со значениями: YES, NO

  • Day со значениями: SUN, MON, TUE, WED, THU, FRI, SAT

  • Time со значениями: DINNER, LUNCH

Создадим класс сущности Tip.

  1. В секции Data Model дерева CUBA нажмите New → Entity. Появится диалоговое окно New CUBA Entity.

  2. В поле Entity name введите название класса сущности – Tip, выберите тип Not persistent в поле Entity type, после чего нажмите кнопку OK. В рабочей области откроется страница дизайнера сущности.

  3. Используя Entity Designer добавьте атрибуты:

    • row типа Integer

    • totalBill типа Double

    • sex типа Sex

    • smoker типа Smoker

    • day типа Day

    • time типа Time

    • size типа Integer

    • tip типа Double

  4. Перейдите на вкладку Text. Здесь отображается исходный код класса Tip.

На этом создание сущности Tip завершено.

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

Теперь создадим новый экран для отображения PivotTable.

  1. Выберите New → Screen в контекстном меню элемента Generic UI в дереве проекта. После этого на экране отобразится страница создания стандартных экранов сущности.

  2. В списке доступных шаблонов выберите Blank screen и нажмите Next.

  3. Введите значение pivot-sample-screen в поле Descriptor name field и нажмите Next.

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

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
       caption="msg://caption"
       messagesPack="com.company.sampler.web.screens"
       xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
   <data>
       <collection id="tipsDc"
                   class="com.company.sampler.entity.Tip"
                   view="_local"/>
   </data>
   <layout>
       <chart:pivotTable id="tipsPivotTable"
                         dataContainer="tipsDc"
                         renderer="HEATMAP">
           <chart:properties>
               <chart:property name="row"/>
               <chart:property name="totalBill"/>
               <chart:property name="tip"/>
               <chart:property name="sex"/>
               <chart:property name="smoker"/>
               <chart:property name="day"/>
               <chart:property name="time"/>
               <chart:property name="size"/>
           </chart:properties>
           <chart:aggregation mode="SUM_OVER_SUM">
               <chart:property name="tip"/>
               <chart:property name="totalBill"/>
           </chart:aggregation>
           <chart:rows>
               <chart:row value="sex"/>
               <chart:row value="smoker"/>
           </chart:rows>
           <chart:columns>
               <chart:column value="day"/>
               <chart:column value="time"/>
           </chart:columns>
           <chart:sortersFunction>
               function(attr){
               if(attr=="Day"){
               return $.pivotUtilities.sortAs(["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]);
               }
               }
           </chart:sortersFunction>
       </chart:pivotTable>
   </layout>
</window>
4.1.1.3. Контроллер экрана

Откройте контроллер экрана PivotSampleScreen и замените его содержимое на следующий код:

package com.company.sampler.web.screens;

import com.company.sampler.entity.*;
import com.haulmont.cuba.gui.model.CollectionContainer;
import com.haulmont.cuba.gui.screen.Screen;
import com.haulmont.cuba.gui.screen.Subscribe;
import com.haulmont.cuba.gui.screen.UiController;
import com.haulmont.cuba.gui.screen.UiDescriptor;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@UiController("sampler_PivotSampleScreen")
@UiDescriptor("pivot-sample-screen.xml")
public class PivotSampleScreen extends Screen {

   @Inject
   private CollectionContainer<Tip> tipsDc;

   @Subscribe
   protected void onInit(InitEvent event) {
       List<Tip> items = new ArrayList<>();
       items.add(tips(1, 16.99, 1.01, Sex.FEMALE, Smoker.NO, Day.FRI, Time.DINNER, 2));
       items.add(tips(2, 10.34, 1.66, Sex.FEMALE, Smoker.YES, Day.THU, Time.LUNCH, 3));
       items.add(tips(3, 21.01, 3.5, Sex.MALE, Smoker.YES, Day.FRI, Time.LUNCH, 3));
       items.add(tips(4, 23.68, 3.31, Sex.FEMALE, Smoker.NO, Day.MON, Time.DINNER, 2));
       items.add(tips(5, 24.59, 3.61, Sex.MALE, Smoker.NO, Day.TUE, Time.LUNCH, 4));
       tipsDc.setItems(items);
   }

   private Tip tips(int row, double totalBill, double tip, Sex sex, Smoker smoker, Day day, Time time, int size) {
       Tip tips = new Tip();
       tips.setRow(row);
       tips.setTotalBill(totalBill);
       tips.setTip(tip);
       tips.setSex(sex);
       tips.setSmoker(smoker);
       tips.setDay(day);
       tips.setTime(time);
       tips.setSize(size);
       return tips;
   }
}

В методе onInit происходит заполнение контейнера данных tipsDc данными.

4.1.2. Функции агрегирования и Генерированные атрибуты

Ниже представлен пример pivotTable, который отличается от примера, приведённого выше, добавленной функцией агрегирования и генерацией атрибутов в Java-контроллере экрана.

<chart:pivotTable id="tipsCustomAggregatorPivotTable"
                  dataContainer="tipsDc">
    <chart:properties>
        <chart:property name="row"/>
        <chart:property name="totalBill"/>
        <chart:property name="tip"/>
        <chart:property name="sex"/>
        <chart:property name="smoker"/>
        <chart:property name="day"/>
        <chart:property name="time"/>
        <chart:property name="size"/>
    </chart:properties>
    <chart:aggregation mode="SUM_OVER_SUM" custom="true">
        <chart:property name="tip"/>
        <chart:property name="Total Bill"/>
    </chart:aggregation>
    <chart:rows>
        <chart:row value="sex"/>
        <chart:row value="Smokes"/>
    </chart:rows>
    <chart:columns>
        <chart:column value="day"/>
        <chart:column value="time"/>
    </chart:columns>
</chart:pivotTable>

Функции сортировки и агрегирования могут быть заданы как в XML-дескрипторе, так и в Java-контроллере экрана. В этом примере JavaScript-функции передаются в качестве параметров конструктору класса JsFunction.

Генерированные сущности также могут быть созданы в контроллере.

public class PivotSampleScreen extends Screen {

    @Inject
    private PivotTable tipsCustomAggregatorPivotTable;

    @Subscribe
    protected void onInit(InitEvent event) {
        tipsCustomAggregatorPivotTable.setSortersFunction(
                new JsFunction("function(attr){if(attr == \"Day\"){return $.pivotUtilities.sortAs([\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\",\"Sun\"]);}}"));

        tipsCustomAggregatorPivotTable.getAggregation().setFunction(
                new JsFunction("$.pivotUtilities.aggregators[\"Sum\"]([\"Tip\"])"));

        DerivedProperties derivedProperties = new DerivedProperties();
        derivedProperties.addAttribute("Smokes",
                new JsFunction("function(record) {return record.Smoker == \"Yes\" ? \"True\" : \"False\";}"));
        tipsCustomAggregatorPivotTable.setDerivedProperties(derivedProperties);
    }
}

Результат:

PivotTable 2
Рисунок 47. Сводная таблица с функцией агрегирования

4.1.3. Редактируемый PivotTable

В следующем примере представлен редактируемый pivotTable с предустановленной функцией агрегирования. В редактируемом режиме можно изменять внешний вид (тип графика) и содержание таблицы (строки и колонки) непосредственно в UI.

<chart:pivotTable id="tipsPivotTableUI"
                  autoSortUnusedProperties="true"
                  dataContainer="tipsDc"
                  editable="true">
    <chart:properties>
        <chart:property name="row"/>
        <chart:property name="totalBill"/>
        <chart:property name="tip"/>
        <chart:property name="sex" localizedName="Sex"/>
        <chart:property name="smoker"/>
        <chart:property name="day"/>
        <chart:property name="time"/>
        <chart:property name="size"/>
    </chart:properties>
    <chart:hiddenProperties>
        <chart:property name="row"/>
    </chart:hiddenProperties>
    <chart:aggregationProperties>
        <chart:property name="tip"/>
        <chart:property name="totalBill"/>
    </chart:aggregationProperties>
    <chart:aggregations default="COUNT">
        <chart:aggregation caption="Count"/>
        <chart:aggregation mode="SUM_OVER_SUM"/>
    </chart:aggregations>
    <chart:renderers default="BAR_CHART"/>
    <chart:rows>
        <chart:row value="sex"/>
        <chart:row value="smoker"/>
    </chart:rows>
    <chart:columns>
        <chart:column value="day"/>
        <chart:column value="time"/>
    </chart:columns>
    <chart:sortersFunction>
        function(attr){
            if(attr=="Day"){
                return $.pivotUtilities.sortAs(["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]);
            }
        }
    </chart:sortersFunction>
</chart:pivotTable>

Результат:

PivotTable 3
Рисунок 48. Редактируемая сводная таблица

4.2. Экспорт данных из PivotTable

PivotTableExtension – это расширение компонента PivotTable, которое предоставляет API для скачивания таблицы с агрегированными данными в формате XLS-файла.

Для добавления этой функциональности необходимо создать экземпляр расширения в контроллере экрана с помощью конструктора класса расширения (т.е., не с помощью UiComponents), например:

@Inject
private PivotTable pivotTable;

private PivotTableExtension extension;

@Subscribe
private void onInit(InitEvent event) {
    extension = new WebPivotTableExtension(pivotTable)
}
Tip

Расширение поддерживает только следующие типы рендереров: TABLE, TABLE_BAR_CHART, HEATMAP, COL_HEATMAP, ROW_HEATMAP, кроме того, оно не получает данных о цвете ячеек таблицы.

Чтобы скачать таблицу как файл XLS, нужно вызвать метод exportTableToXls(), например, по клику на кнопку:

extension.exportTableToXls();

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

extension.setFileName("Orders of " + new Date());
Tip

Формат XLS имеет ограничение в 65536 строк в одном файле. Если PivotTable содержит более 65536 строк, при экспорте содержимое будет обрезано, и пользователь увидит соответствующее предупреждение.

Дополнительно, расширение PivotTableExtension предоставляет ещё два способа получения данных из PivotTable:

  • данные в формате JSON:

    extension.getPivotDataJSON();
  • сериализованный объект PivotData:

    extension.getPivotData();

4.3. Использование ShowPivotAction

ShowPivotAction – это особое действие, позволяющее экспортировать данные из компонентов – наследников ListComponent, таких как Table, Tree и DataGrid, и представлять их в виде сводной таблицы. По сути, это простой инструмент для BI-анализа, не требующий установки дополнительных компонентов приложения.

Действие ShowPivotAction можно создать программно в контроллере экрана и присвоить его некоторой кнопке, к примеру:

ShowPivotAction showPivotAction = new ShowPivotAction(tipsGroupTable);
exportButton.setAction(showPivotAction);

ShowPivotAction предоставляет два способа экспорта данных: только выделенные строки и все строки. Если в таблице не выделена ни одна строка, диалоговое окно выбора не будет показано, и по умолчанию будут экспортированы все строки.

После нажатия кнопки в новой вкладке появится редактируемая таблица PivotTable. По умолчанию в этой таблице отображаются все атрибуты, включенные в контейнер данных компонента, кроме следующих:

  • атрибуты-коллекции;

  • байтовые массивы;

  • атрибуты с типом UUID;

  • аннотированные @SystemLevel.

Если вы хотите исключить некоторые атрибуты или, наоборот, включить выборочно только часть из них, используйте методы fluent API:

  • withIncludedProperties() для включения атрибутов, например:

    ShowPivotAction showPivotAction = new ShowPivotAction(tipsGroupTable)
            .withIncludedProperties(Arrays.asList("sex", "smoker", "day", "totalBill"));
  • withExcludedProperties() для исключения атрибутов, например:

    ShowPivotAction showPivotAction = new ShowPivotAction(tipsGroupTable)
            .withExcludedProperties(Arrays.asList("sex", "smoker"));

Эти методы принимают список имен атрибутов. Некорректные имена будут проигнорированы.

Вы можете изменить конфигурацию сводной таблицы по умолчанию, использовав метод withNativeJson(), который принимает конфигурацию в формате JSON-строки. Обратите внимание, что в нём используются локализованные имена атрибутов:

ShowPivotAction showPivotAction = new ShowPivotAction(tipsGroupTable)
        .withNativeJson("{" +
                " \"cols\": [\"Time\", \"Day\"]," +
                " \"rows\": [\"Sex\", \"Smoker\"]," +
                " \"editable\": false," +
                " \"renderer\": \"heatmap\"," +
                " \"aggregation\": {" +
                " \"id\": \"d8fc3fdf-730d-c94f-a0c8-72a9ce3dcb3a\"," +
                " \"mode\": \"count\"," +
                " \"properties\": [\"Sex\"]" +
                " }" +
                " }");

Ниже приведена структура JSON для нередактируемой сводной таблицы:

{
        "cols": ["localized property", "localized property"],
        "rows": ["localized property"],
        "editable": false,
        "renderer": "heatmap",
        "aggregation": {
                "id": "d8fc3fdf-730d-c94f-a0c8-72a9ce3dcb3a",
                "mode": "sumOverSum",
                "properties": ["localized property", "localized property"]
        }
}

А здесь – структура JSON для редактируемой сводной таблицы:

{
        "cols": ["localized property"],
        "rows": ["localized property"],
        "editable": true,
        "renderers": {
                "selectedRenderer": "barChart"
        },
        "autoSortUnusedProperties": true,
        "aggregationProperties": ["localized property", "localized property"],
        "aggregations": {
                "selectedAggregation": "count",
                "aggregations": [{
                        "id": "647780f0-c6d0-6ade-a63a-542b5c8cdbd5",
                        "mode": "count",
                        "caption": "Count"
                }, {
                        "id": "c2663238-2654-67f0-2dec-add6962d867c",
                        "mode": "sumOverSum"
                }]
        }
}

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

. . .