В этой статье мы рассмотрим специальный плагин для фильтрации данных на диаграммах-расширениях Qlik Sense, который называется «лассо». В стандартных диаграммах он работает, а вот для фильтрации в Qlik Extensions – пригодится!
НА ЗАМЕТКУ! Фильтр работает на основе D3. Но его можно изменить и сделать так, чтобы он работал и в других визуализациях без использования этой библиотеки.
Лассо: D3 Lasso
Прежде чем начать работу с фильтром, давайте обратимся к плагину Лассо D3, который позволяет создавать SVG-элементы и определять, что будет внутри выделения. Так, мы можем выделить нужную область данных.
Плагин состоит из списка объектов, чтобы проверять, когда активировать функцию лассо. Список работает по принципу выборки D3. Объекты лассо – элементы, которые будут оцениваться внутри и вне текущего лассо. У каждого объекта есть два параметра: возможно и выбрано.
Лассо работает в трех состояниях, которые срабатывают как события:
- lasso_start: когда пользователь впервые начинает рисовать лассо
- lasso_draw: когда пользователь делает лассо
- lasso_end: когда пользователь завершил работу с лассо
В состоянии lasso_start, каждому объекту автоматически присваиваются параметры:
possible: false
selected: false
В lasso_draw, объекты проверяются от первоначального состояния к текущему, чтобы определить задействованы они а текущей выборке лассо. Если да – то истина, нет — ложь.
В lasso_end, в завершении, если объекты назначено состояние possible, то выборка переходит в состояние выбрано: истина.
С этими значениями мы можем использовать лассо и основы JavaScript, чтобы настроить пользовательский стиль и настроить работу функции лассо в Qlik Sense.
Лассо можно настроить по следующим характеристикам:
- items
выборка D3. Каждому элементу будет назначено состояние выборки;
- hoverSelect
определяет могут ли быть выделены объекты лассо;
- closePathSelect
определяет могут ли быть выбраны объекты выборкой смежный объектов;
- closePathDistance
число, которое определяет максимальное расстояние в пикселях между текушей выборкой лассо и создаваемой, для того чтобы выбрать элементы и сделать новое выделение;
- area
целевая область создания выборки лассо;
- on
тип события и функция этого события. Есть три типа событий: начало, рисование и конец.
Подробности в файле Readme.
Пузырьковая диаграмма: Расширение
Это расширение имеет одно измерение и три меры. Каждое измерение представляет круг. Мера 1 определяет положение круга по оси X, мера 2 определяет положение по оси Y, а мера 3 определяет размер круга. Изначально график выглядит таким образом:
Какие у нас есть настройки цветов в расширении. Сначала создадим div-блок. Если он уже создан, то удаляем его и создаем новый.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// Get the extension container properties var height = $element.height(), width = $element.width(), id = "container_" + layout.qInfo.qId; // Get the data var qMatrix = layout.qHyperCube.qDataPages[0].qMatrix; // Create or empty the chart container if(document.getElementById(id)) { $("#" + id).empty(); } else { $element.append($("<div />").attr("id",id).width(width).height(height)); } |
Далее определяем отступы, добавляем svg и рабочую область на основе отступов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// Define the plot margins var margin = { top:10, bottom:10, left:10, right:10 }; // Create the svg var svg = d3.select("#" + id).append("svg") .attr("width",width) .attr("height",height); // Create a plot transformed by the margins var plot = svg.append("g") .attr('transform','translate(' + margin.left + ',' + margin.top + ')'); |
Масштаб и положение осей, кругов устанавливается на основе данных расширения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// Create the x scale var x = d3.scale.linear() .domain([layout.qHyperCube.qMeasureInfo[0].qMin,layout.qHyperCube.qMeasureInfo[0].qMax]) .range([0, width - margin.left - margin.right]); // Create the y scale var y = d3.scale.linear() .domain([layout.qHyperCube.qMeasureInfo[1].qMin,layout.qHyperCube.qMeasureInfo[1].qMax]) .range([height-margin.top-margin.bottom, 0]); // Create the area scale var a = d3.scale.linear() .domain([layout.qHyperCube.qMeasureInfo[2].qMin,layout.qHyperCube.qMeasureInfo[2].qMax]) .range([1*Math.PI,100*Math.PI]); |
И наконец, рисуем круги на основе данных и масштаба.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Create the circles var circles = plot.selectAll('circle') .data(qMatrix) .enter() .append('circle') .attr('cx',function(d) {return x(d[1].qNum);}) .attr('cy',function(d) {return y(d[2].qNum);}) .attr('r',function(d) {return Math.sqrt(a(d[3].qNum)/Math.PI);}); |
Наш файл “style.css” определяет стиль расширения, в голубых оттенках с прозрачностью в 50 %.
1 2 3 4 5 6 7 |
.qv-object-BasicScatter circle { fill: steelblue; fill-opacity: .5; } |
Лассо и пузырьковая диаграмма
У нас должно будет получиться так:
Установка плагина
Для того чтобы использовать лассо в расширении, нам нужно установить плагин lasso.js. Установите прежде d3. Давайте внесем правки в код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
requirejs.config({ paths: { d3: "../extensions/basicscatterlasso/d3", lasso: "../extensions/basicscatterlasso/lasso" }, shim : { "lasso" : { "exports" : "lasso", "deps" : ["d3"] } } }); define( [ "text!./style.css", "lasso" ], |
В новом выражении requirejs, мы говорим, куда загрузить D3 и откуда брать лассо.
Добавляем CSS
Для того чтобы сделать лассо видимым, добавляем CSS. Далее добавим настройки для кругов, когда они включены в выборку лассо и нет:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
.qv-object-BasicScatterLasso circle.possible { stroke: black; stroke-width: 2px; } .qv-object-BasicScatterLasso circle.not-possible { fill: gray; fill-opacity: .25; } .lasso path { stroke: rgb(80,80,80); stroke-width:2px; } .lasso .drawn { fill-opacity:.05 ; } .lasso .loop_close { fill:none; stroke-dasharray: 4,4; } .lasso .origin { fill:#3399FF; fill-opacity:.5; |
Определяем область лассо
Далее определяем область, на которой могут настроить область выделения лассо.
Предположим, вот наш график:
Мы могли бы добавить невидимый квадрат на график и сделать его рабочей областью выделения. Получилось бы как-то так;
Но так квадрат накроет все элементы SVG и блокирует другие функции, которые мы настроили ранее.
Перемещаем квадрат на слой назад:
Но сейчас мы не можем выделять круги, а можем щелкать только рядом с ними:
Нам нужно сделать смешанные области выделения и рядом с кругом, и с возможностью щелкать по ним:
Для этого используем классы и D3. Назначим класс “.lassoable” для тех элементов, с которыми можно работать в выборке.
Сначала создадим квадрат поверх svg и назначим ему класс “lassoable”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Create a rectangle in the background for lassoing var bg = svg.append('rect') .attr('class','lassoable') .attr('x',0) .attr('y',0) .attr('width',width) .attr('height',height) .attr('opacity',0); |
Далее проработаем с классом “lassoable” для кругов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Create the circles var circles = plot.selectAll('circle') .data(qMatrix) .enter() .append('circle') .attr('class','lassoable') .attr('cx',function(d) {return x(d[1].qNum);}) .attr('cy',function(d) {return y(d[2].qNum);}) .attr('r',function(d) {return Math.sqrt(a(d[3].qNum)/Math.PI);}); |
Теперь можем определять рабочую область выделения.
Настройка лассо
Вот какие еще нужны настройки. Вставляем такой код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Define the lasso var lasso = d3.lasso() .closePathDistance(75) // max distance for the lasso loop to be closed .closePathSelect(true) // can items be selected by closing the path? .hoverSelect(true) // can items by selected by hovering over them? .area(svg.selectAll('.lassoable')) // a lasso can be drawn on the bg rectangle and any of the circles on top of it .items(circles) // the circles will be evaluated for lassoing .on("start",lasso_start) // lasso start function .on("draw",lasso_draw) // lasso draw function .on("end",lasso_end); // lasso end function |
Функции события лассо
Теперь определим события начала, рисования и завершения процесса выделения.
Для «начала» мы хотим установить состояние not-possible, поскольку на старте нет выборок:
1 2 3 4 5 6 7 |
function lasso_start() { lasso.items() .classed({"not-possible":true}); // style as not possible } |
Далее для рисования – у нас есть то, что пойдет в выборку, и то, что находится за ее пределами. Мы используем .filter(), чтобы отфильтровать объекты на основе пользовательских данных:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function lasso_draw() { // Style the possible dots lasso.items().filter(function(d) {return d.possible===true}) .classed({"not-possible":false,"possible":true}); // Style the not possible dot lasso.items().filter(function(d) {return d.possible===false}) .classed({"not-possible":true,"possible":false}); } |
Далее, в конце, мы хотим, чтобы у нас была выделена нужная область в приложении Qlik Sense.
Используя .filter(), назначим для выбранных объектов состояния истина. Далее мы используем карту .map(), чтобы получить доступ к каждому объекту и извлечь qElemNumbers из данных при помощи D3.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
var self = this; function lasso_end() { // Get all the lasso items that were "selected" by the user var selectedItems = lasso.items() .filter(function(d) { return d.selected; }); // Retrieve the dimension element numbers for the selected items var elemNos = selectedItems[0] .map(function(d) { return d.__data__[0].qElemNumber; }); // Filter these dimension values self.backendApi.selectValues(0,elemNos,true); } |
Создание лассо
Теперь, после того как мы создали лассо, нам нужно только настроить наш SVG. Поэтому в конце расширения добавляем следующее:
1 |
svg.call(lasso); |
Вот! Теперь у вас есть лассо для расширений!
Успехов вам в разработке!
Свежие комментарии