В этом примере показано, как задать класс для визуализации данных расписания с помощью двух осей с интерактивными функциями. Верхние оси имеют панорамирование и масштабирование, включенное вдоль размерность x, поэтому пользователь может изучить необходимую область. Нижние оси отображают график на протяжении всей временной области значений. Нижняя ось также отображает светло-синее временное окно, которое указывает временную область значений в верхних осях. Класс задает следующие свойства, методы и локальные функции.
Свойства:
Data
- общедоступное и зависимое свойство, в котором хранится расписание.
TimeLimits
- общественная собственность, устанавливающий пределы верхних осей и ширину временного окна в нижних осях.
SavedData
- Защищенное свойство, которое позволяет пользователю сохранять и загружать образцы графика и сохранять данные.
TopAxes
и BottomAxes
- Частная собственность, которые хранят объекты осей.
TopLine
и BottomLine
- Частная собственность, которые хранят объекты линий.
TimeWindow
- Объект закрашенной фигуры, отображаемый в нижних осях, который указывает временную область значений верхних осей.
Методы:
set.Data
и get.Data
- Разрешить пользователю сохранять и загружать образцы графика и сохранять данные.
setup
- Выполняется один раз при создании графика. Он конфигурирует размещение и оси, объекты линий и объект закрашенной фигуры.
update
- Выполняется после setup
метод и после изменения пользователем одного или нескольких свойств на графике.
panZoom
- обновляет временные пределы графика, когда пользователь панорамирует или масштабирует в пределах верхней оси. Это приводит к обновлению временного окна с учетом новых пределов.
click
- Пересчитывает пределы времени, когда пользователь кликает нижние оси.
Локальные функции:
updateDataTipTemplate
- Вызывается изнутри в update
способ. Он создает строки в всплывающих подсказках, которые соответствуют переменным в расписании.
mustHaveOneNumericVariable
- Проверяет Data
свойство. Эта функция гарантирует, что заданное пользователем расписание имеет по крайней мере одну числовую переменную.
Чтобы определить класс, скопируйте следующий код в редактор и сохраните его с именем TimeTableChart.m
в папке с возможностью записи.
classdef TimeTableChart < matlab.graphics.chartcontainer.ChartContainer properties (Dependent) Data timetable {mustHaveOneNumericVariable} = ... timetable(datetime.empty(0,1),zeros(0,1)) end properties TimeLimits (1,2) datetime = [NaT NaT] end properties (Access = protected) SavedData timetable = timetable(datetime.empty(0,1),zeros(0,1)) end properties (Access = private, Transient, NonCopyable) TopAxes matlab.graphics.axis.Axes TopLine matlab.graphics.chart.primitive.Line BottomAxes matlab.graphics.axis.Axes BottomLine matlab.graphics.chart.primitive.Line TimeWindow matlab.graphics.primitive.Patch end methods function set.Data(obj, tbl) % Reset the time limits if the row times have changed. oldTimes = obj.SavedData.Properties.RowTimes; newTimes = tbl.Properties.RowTimes; if ~isequal(oldTimes, newTimes) obj.TimeLimits = [NaT NaT]; end % Store the new table. obj.SavedData = tbl; end function tbl = get.Data(obj) tbl = obj.SavedData; end end methods (Access = protected) function setup(obj) % Create two axes. The top axes is 3x taller than bottom axes. tcl = getLayout(obj); tcl.GridSize = [4 1]; obj.TopAxes = nexttile(tcl, 1, [3 1]); obj.BottomAxes = nexttile(tcl, 4); % Add a shared toolbar on the layout, which removes the % toolbar from the individual axes. axtoolbar(tcl, 'default'); % Create one line to show the zoomed-in data. obj.TopLine = plot(obj.TopAxes, NaT, NaN); % Create one line to show an overview of the data, and disable % HitTest so the ButtonDownFcn on the bottom axes works. obj.BottomLine = plot(obj.BottomAxes, NaT, NaN, ... 'HitTest', 'off'); % Create a patch to show the current time limits. obj.TimeWindow = patch(obj.BottomAxes, ... 'Faces', 1:4, ... 'Vertices', NaN(4,2), ... 'FaceColor', obj.TopLine.Color, ... 'FaceAlpha', 0.3, ... 'EdgeColor', 'none', ... 'HitTest', 'off'); % Constrain axes panning/zooming to only the X-dimension. obj.TopAxes.Interactions = [ ... dataTipInteraction; panInteraction('Dimensions','x'); rulerPanInteraction('Dimensions','x'); zoomInteraction('Dimensions','x')]; % Disable pan/zoom on the bottom axes. obj.BottomAxes.Interactions = []; % Add a listener to XLim to respond to zoom events. addlistener(obj.TopAxes, 'XLim', 'PostSet', @(~, ~) panZoom(obj)); % Add a callback for clicks on the bottom axes. obj.BottomAxes.ButtonDownFcn = @(~, ~) click(obj); end function update(obj) % Extract the time data from the table. tbl = obj.Data; t = tbl.Properties.RowTimes; % Extract the numeric variables from the table. S = vartype('numeric'); numericTbl = tbl(:,S); % Update the data on both lines. set([obj.BottomLine obj.TopLine], 'XData', t, 'YData', numericTbl{:,1}); % Create a dataTipTextRow for each variable in the timetable. updateDataTipTemplate(obj.TopLine, tbl) % Update the top axes limits. obj.TopAxes.YLimMode = 'auto'; if obj.TimeLimits(1) < obj.TimeLimits(2) obj.TopAxes.XLim = obj.TimeLimits; else % Current time limits are invalid, so set XLimMode to auto and % let the axes calculate limits based on available data. obj.TopAxes.XLimMode = 'auto'; obj.TimeLimits = obj.TopAxes.XLim; end % Update time window to reflect the new time limits. xLimits = ruler2num(obj.TimeLimits, obj.BottomAxes.XAxis); yLimits = obj.BottomAxes.YLim; obj.TimeWindow.Vertices = [xLimits([1 1 2 2]); yLimits([1 2 2 1])]'; end function panZoom(obj) % When XLim on the top axes changes, update the time limits. obj.TimeLimits = obj.TopAxes.XLim; end function click(obj) % When clicking on the bottom axes, recenter the time limits. % Find the center of the click using CurrentPoint. center = obj.BottomAxes.CurrentPoint(1,1); % Convert from numeric units into datetime using num2ruler. center = num2ruler(center, obj.BottomAxes.XAxis); % Find the width of the current time limits. width = diff(obj.TimeLimits); % Recenter the current time limits. obj.TimeLimits = center + [-1 1]*width/2; end end end function updateDataTipTemplate(obj, tbl) % Create a dataTipTextRow for each variable in the timetable. timeVariable = tbl.Properties.DimensionNames{1}; rows = dataTipTextRow(timeVariable, tbl.(timeVariable)); for n = 1:numel(tbl.Properties.VariableNames) rows(n+1,1) = dataTipTextRow(... tbl.Properties.VariableNames{n}, tbl{:,n}); end obj.DataTipTemplate.DataTipRows = rows; end function mustHaveOneNumericVariable(tbl) % Validation function for Data property. S = vartype('numeric'); if width(tbl(:,S)) < 1 error('TimeTableChart:InvalidTable', ... 'Table must have at least one numeric variable.') end end
После сохранения файла класса создайте образец графика. В этом случае используйте график, чтобы изучить несколько недель в течение года данных о велосипедном движении.
bikeTbl = readtimetable('BicycleCounts.csv'); bikeTbl = bikeTbl(169:8954,:); tlimits = [datetime(2015,8,6) datetime(2015,8,27)]; TimeTableChart('Data',bikeTbl,'TimeLimits',tlimits);