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

Предисловие

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

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

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

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

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

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

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

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

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

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

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

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

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

1.1. Добавление диаграмм в проект

Для использования диаграмм в Вашем проекте, необходимо активировать элемент charts в списке App components на экране редактирования свойств проекта (секция Project properties, кнопка Edit) в CUBA Studio.

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

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

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

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

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

  • chart:xyChart - XYChart

  • chart:serialChart - SerialChart

  • chart:pieChart - PieChart

  • chart:funnelChart - FunnelChart

  • chart:gaugeChart - AngularGaugeChart

  • chart:radarChart - RadarChart

  • chart:ganttChart - GanttChart

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

Все атрибуты конфигурации могут иметь значение 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")))
        .setDatasource(valueDescriptionsDs);

Диаграммы можно сделать отзывчивыми с помощью плагина 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>

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

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

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

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

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

charts export menu 1

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

<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

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

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

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

    • EntityDataItem принимает экземпляр сущности.

    • MapDataItem содержит набор пар ключ-значение.

    • SimpleDataItem принимает экземпляр любого public класса.

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

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

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

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

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

1.4. События

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

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

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

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

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

Для каждого вида диаграмм также доступны свои типы слушателей:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

StockChartGroup
  • DataSetSelectorCompareListener - сравнение селекторов наборов данных.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@Inject
private Chart pieChart;
((PieChart)pieChart).addSliceClickListener(event -> {});

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

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

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

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

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

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

  2. Откройте окно свойств проекта Project propertiesEdit и в списке App components включите проект charts, затем сохраните изменения. Studio предложит пересоздать скрипты Gradle - согласитесь.

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

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

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

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

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

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

<chart:pieChart id="pieChart"
                titleField="key"
                valueField="value"/>
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

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

В этом примере мы создадим диаграмму похожую на 3D Stacked Column Chart из демо AmCharts. 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
     }
});
1.5.3.1. Создание сущности
  1. Откройте кладку DATA MODEL в CUBA Studio и нажмите кнопку New → Entity.

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

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

    • country типа String

    • year2014 типа Double

    • year2015 типа Double

  4. Откройте вкладку Source, чтобы увидеть сгенерированный код:

    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.AbstractNotPersistentEntity;
    
    @MetaClass(name = "sampler$CountryGrowth")
    public class CountryGrowth extends AbstractNotPersistentEntity {
    
        @MetaProperty
        protected String country;
    
        @MetaProperty
        protected Double year2014;
    
        @MetaProperty
        protected Double year2015;
    
        public void setCountry(String country) {
            this.country = country;
        }
    
        public String getCountry() {
            return country;
        }
    
        public void setYear2014(Double year2014) {
            this.year2014 = year2014;
        }
    
        public Double getYear2014() {
            return year2014;
        }
    
        public void setYear2015(Double year2015) {
            this.year2015 = year2015;
        }
    
        public Double getYear2015() {
            return year2015;
        }
    }

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

  5. Нажмите кнопку OK, чтобы сохранить сущность и закрыть экран дизайнера.

1.5.3.2. Создание диаграммы
1.5.3.2.1. XML-дескриптор экрана

Откройте в CUBA Studio вкладку GENERIC UI и создайте экран в модуле web. Введите значение column3d-chart.xml в поле Descriptor. В полях Id, Controller Name и Messages Pack будут сгенерированы подходящие значения. Сохраните изменения. Далее перейдите на вкладку XML и замените ее содержимое на следующий код:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://caption"
        class="com.company.sampler.web.screens.Column3dChart"
        messagesPack="com.company.sampler.web.screens"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
    <dsContext>
        <collectionDatasource id="countryGrowthDs"
                              class="com.company.sampler.entity.CountryGrowth"
                              refreshMode="NEVER"/>
    </dsContext>
    <layout>
        <chart:serialChart id="chart"
                           angle="30"
                           categoryField="country"
                           datasource="countryGrowthDs"
                           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"
    ...
    >

Диаграмма получает данные из источника countryGrowthDs, указанного в атрибуте datasource. Для отображения названий и значений используются атрибуты 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, olhc и candlestick.

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

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

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

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

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

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

package com.company.sampler.web.screens;

import com.company.sampler.entity.CountryGrowth;
import com.haulmont.cuba.gui.components.AbstractWindow;
import com.haulmont.cuba.gui.data.CollectionDatasource;

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

public class Column3dChart extends AbstractWindow {
    @Inject
    private CollectionDatasource<CountryGrowth, UUID> countryGrowthDs;

    @Override
    public void init(Map<String, Object> params) {
        countryGrowthDs.refresh();

        countryGrowthDs.includeItem(countryGrowth("USA", 3.5, 4.2));
        countryGrowthDs.includeItem(countryGrowth("UK", 1.7, 3.1));
        countryGrowthDs.includeItem(countryGrowth("Canada", 2.8, 2.9));
        countryGrowthDs.includeItem(countryGrowth("Japan", 2.6, 2.3));
        countryGrowthDs.includeItem(countryGrowth("France", 1.4, 2.1));
        countryGrowthDs.includeItem(countryGrowth("Brazil", 2.6, 4.9));
        countryGrowthDs.includeItem(countryGrowth("Russia", 6.4, 7.2));
        countryGrowthDs.includeItem(countryGrowth("India", 8.0, 7.1));
        countryGrowthDs.includeItem(countryGrowth("China", 9.9, 10.1));
    }

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

В методе init(Map<String, Object> params) происходит заполнение источника данных countryGrowthDs данными. Метод refresh() производит инициализацию источника данных. Этот метод необходимо вызвать, несмотря на атрибут refreshMode="NEVER", установленный в XML-дескрипторе.

1.5.3.3. Результат
  1. Откройте ссылку Open web menu на вкладке GENERIC UI в CUBA Studio.

  2. Убедитесь, что в меню application добавлен элемент column3d-chart. Это происходит автоматически при создании нового экрана из Studio.

  3. Выполните комманду RunStart application server.

После входа в приложение и открытия экрана из меню приложения вы увидите диаграмму, как показано ниже:

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

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

Данная диаграмма получает данные из экземпляра DataProvider, создаваемого в контроллере экрана, поэтому атрибут datasource не определен.

1.5.4.1. Создание диаграммы
1.5.4.1.1. XML-дескриптор экрана

Откройте в CUBA Studio вкладку Screens и создайте экран в модуле web. Введите значение com/company/sampler/web/screens/stackedarea-chart.xml в поле Descriptor. В полях Id, Controller Name и Messages Pack будут сгенерированы подходящие значения. Сохраните изменения. Далее перейдите на вкладку XML и замените ее содержимое на следующий код:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://caption"
        class="com.company.sampler.web.screens.StackedAreaChart"
        messagesPack="com.company.sampler.web.screens"
        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, olhc и candlestick.

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

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

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

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

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

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

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

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

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

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

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

package com.company.sampler.web.screens;

import com.haulmont.charts.gui.amcharts.model.charts.SerialChart;
import com.haulmont.charts.gui.amcharts.model.data.DataItem;
import com.haulmont.charts.gui.amcharts.model.data.ListDataProvider;
import com.haulmont.charts.gui.amcharts.model.data.MapDataItem;
import com.haulmont.charts.gui.components.charts.Chart;
import com.haulmont.cuba.gui.components.AbstractWindow;

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

public class StackedAreaChart extends AbstractWindow {
    @Inject
    private Chart chart;

    @Override
    public void init(Map<String, Object> params) {
        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));

        serialChart.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;
    }
}

В методе init(Map<String, Object> params) происходит установка данных в диаграмму с накоплением. Диаграммы подобного типа показывают отношение отдельных составляющих к их совокупному значению.

1.5.4.2. Результат
  1. Откройте вкладку Main Menu в CUBA Studio и нажмите кнопку edit для web-menu.xml.

  2. Выберите элемент application и нажмите кнопку new.

  3. В диалоге создания элемента меню выберите stackedarea-chart в поле id и нажмите add.

  4. Выполните комманду RunStart application server.

После входа в приложение и открытия экрана из меню приложения вы увидите диаграмму, как показано ниже:

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

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

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

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

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

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

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

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

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
            caption="msg://caption"
            class="com.company.sales.web.screens.OrdersHistory"
            messagesPack="com.company.sales.web.screens"
            xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
        <dsContext>
            <collectionDatasource id="ordersDs"
                                  class="com.company.sales.entity.Order"
                                  allowCommit="false"
                                  refreshMode="NEVER"/>
        </dsContext>
        <dialogMode height="600"
                    width="800"/>
        <layout expand="orderHistoryChart"
                spacing="true">
            <chart:serialChart id="orderHistoryChart"
                               categoryField="date"
                               datasource="ordersDs"
                               width="100%">
                <chart:graphs>
                    <chart:graph valueField="amount"/>
                </chart:graphs>
            </chart:serialChart>
        </layout>
    </window>
  4. Для обновления данных на лету используйте timer - специальный UI-компонент, который будет отправлять HTTP-запросы на сторону сервера.

    Отройте вкладку Properties дизайнера экрана и нажмите на кнопку Timers, чтобы добавить таймер на экран. Заполните поле id. Допустим, мы хотим, чтобы данные обновлялись каждые 2 секунды, в этом случае в поле delay укажем значение 2000 миллисекунд.

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

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

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

    public class OrdersHistory extends AbstractWindow {
        @Inject
        private Metadata metadata;
        @Inject
        private TimeSource timeSource;
        @Inject
        private CollectionDatasource<Order, UUID> ordersDs;
    
        private Random random = new Random(42);
    
        @Override
        public void init(Map<String, Object> params) {
            super.init(params);
    
            Order initialValue = metadata.create(Order.class);
            initialValue.setAmount(new BigDecimal(random.nextInt(1000) + 100));
            initialValue.setDate(timeSource.currentTimestamp());
    
            ordersDs.includeItem(initialValue);
        }
    
        public void updateChart(Timer source) {
            Order orderHistory = metadata.create(Order.class);
            orderHistory.setAmount(new BigDecimal(random.nextInt(1000) + 100));
            orderHistory.setDate(timeSource.currentTimestamp());;
    
            ordersDs.includeItem(orderHistory);
        }
    }

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

    chart incremental update 2
    Рисунок 4. Данные автоматически обновляются каждые 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());;
    
        ordersDs.includeItem(orderHistory);
    
        itemsQueue.add(orderHistory);
    
        if (itemsQueue.size() > 10) {
            Order item = itemsQueue.poll();
            ordersDs.excludeItem(item);
        }
    }

Результат

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

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

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

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

@Inject
private SerialChart chart;

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

serialChart.addGraphItemClickListener(event -> {
        CountryGrowth countryGrowth = (CountryGrowth) event.getEntityNN();
        String message = String.format("GDP grow in %s (%s): %.1f%%",
        countryGrowth.getCountry(),
        event.getGraphId().substring(5),
        "graph2014".equals(event.getGraphId()) ? countryGrowth.getYear2014() : countryGrowth.getYear2015());
        showNotification(message, NotificationType.HUMANIZED_HTML);
});

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

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

1.5.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
protected Chart serialChart;

@Override
public void init(Map<String, Object> params) {
    super.init(params);

    ListDataProvider serialChartDataProvider = new ListDataProvider();
    int[] serialChartChartData = {5, 7, 6, 9, 7, 8, 5, 6, 4, 6, 5, 7, 4, 5, 3, 4, 2, 0};

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

    serialChartConfiguration.setDataProvider(serialChartDataProvider);
}
chart custom json

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

serialChart.setNativeJson("{\n" +
        " \"titles\": [\n" +
        " {\n" +
        " \"size\": 15,\n" +
        " \"text\": \"Chart Title\"\n" +
        " }\n" +
        " ]\n" +
        "}");
chart custom json title

Также возможно задать 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>

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

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

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

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

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

1.6.1. SerialChart

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

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

1.6.2. PieChart

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

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

1.6.3. XYChart

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

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

1.6.4. FunnelChart

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

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

1.6.5. RadarChart

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

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

1.6.6. AngularGaugeChart

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

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

1.6.7. GanttChart

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

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

1.6.8. StockChartGroup

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

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

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

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

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

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

stock chart with panels
Рисунок 18. StockChart с несколькими панелями

1.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}}");

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

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

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

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

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

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

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

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

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

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

    map demo polygon display
  • Рисование полигонов.

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

    map demo heatmap

2.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 - долгота центра карты по умолчанию.

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

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

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

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

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

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

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

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

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

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

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

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

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

@Inject
private MapViewer map;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      GeoPoint center = map.createGeoPoint(53.490905, -2.249558);
      map.setCenter(center);
    • 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\">White label</span>");
      label.setPosition(mapViewer.createGeoPoint(53.914567, -2.668279));
      label.setAdjustment(Label.Adjustment.BOTTOM_CENTER);
      label.setContentType(Label.ContentType.HTML);
      map.addLabel(label);
    • Marker - компонент для отметки места на карте. По умолчанию используется стандартная иконка сервиса карт. Для создания и размещения объекта на карте используются методы createMarker() и addMarker() интерфейса MapViewer. Например:

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

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

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

    • 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);
      });
    • HeatMapLayer - слой тепловой карты: предназначен для изображения плотности данных в различных географических точках. Степень плотности точек отображается с помощью цвета. По умолчанию области с высокой плотностью точек отображаются красным цветом, а области с низкой - зелёным. Для создания и размещения объекта на карте используются методы createHeatMapLayer() и addHeatMapLayer() интерфейса MapViewer. Например:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3. Отображение 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

PivotTable

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

PivotTable 1

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

<chart:pivotTable id="tipsPivotTable"
                  datasource="tipsDs"
                  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.

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

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

  • aggregation - настройки для функции, которая будет использоваться для агрегирования значений в каждой ячейке.

    атрибуты aggregation:

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

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

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

    элементы aggregation:

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

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

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

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

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

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

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

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

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

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

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

    • вложенный элемент renderer имеет один атрибут type - выбор из списка предопределенных рендереров.

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

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

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

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

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

атрибуты pivotTable

  • datasource - ссылка на источник данных, указанный в блоке dsContext XML-дескриптора экрана. Источник должен иметь тип collectionDatasource.

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

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

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

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

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



3.1. Примеры PivotTable

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

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

<chart:pivotTable id="tipsCustomAggregatorPivotTable"
                  datasource="tipsDs">
    <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 AbstractWindow {

    @Inject
    private PivotTable tipsCustomAggregatorPivotTable;

    @Override
    public void init(Map<String, Object> params) {
        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

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

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

<chart:pivotTable id="tipsPivotTableUI"
                  autoSortUnusedProperties="true"
                  datasource="tipsDs"
                  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
. . .