Синхронизировать несколько осей по невидимому атрибуту

Ситуация

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

data = A[rand(100, 3), sort(rand(100, 1))]

где data(:,1) координаты x, data(:,2) координаты y, data(:,3) высота и data(:,4) время записи.

Я мог бы легко построить эти данные, используя

pxy = subplot(2,2,1)            % Plot for X-Y-Data
plot(data(:,1), data(:,2))
ptx = subplot(2,2,2)            % Plot for T-X-Data
plot(data(:,4), data(:,1))
pty = subplot(2,2,3)            % ... and so on
plot(data(:,4), data(:,2))
pth = subplot(2,2,4)
plot(data(:,4), data(:,3))

Теперь для просмотра фигур было бы здорово синхронизировать оси.

Первые мысли о возможностях

Тривиальный подход может заключаться в использовании linkaxes для оси времени, например

linkaxes([ptx, pty, pth], 'x')

Однако это оставляет график xy без изменений. Итак, более слабый вопрос будет заключаться в том, как связать ось Y ptx с осью X pxy.

Но давайте еще сложнее:

Собственно вопрос

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

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

Любая идея, как это сделать?

Оптимальное решение по-прежнему позволяет изменять эти базовые графики, добавляя дополнительные данные с помощью hold on.


person Thilo    schedule 04.05.2012    source источник


Ответы (2)


(Теоретическая) проблема с тем, что вы хотите, заключается в том, что pxy должен быть обратимым, а в дискретной области (matlab дискретен) это становится еще менее ясным.

Предположим, у вас есть две точки P1=(x1,y1) и P2=(x2,y2), которые находятся рядом друг с другом в pxy; какому окну осей на pth вы бы хотели, чтобы окно осей pxy соответствовало, если и P1, и P2 видны в pxy? Пт1 или Пт2? Вы должны определить некоторые правила принятия решений для этого... возможно, в зависимости от того, на какой из двух (P1 или P2) вы на самом деле нажимаете.

Практически вам нужно запускать функции для изменения осей, когда пользователь меняет масштаб на pxy, это можно сделать, как описано здесь: http://www.mathworks.com/matlabcentral/answers/21627-triggering-изменениянаграфикесфункциеймасштабирования

person Gunther Struyf    schedule 04.05.2012
comment
Спасибо за ответ. Я изучаю методы, на которые вы указали мне прямо сейчас. Обратная связь будет дана как можно скорее. - person Thilo; 04.05.2012
comment
Что касается более теоретических вопросов: мой подход состоял бы в том, чтобы использовать следующий алгоритм: определить все точки данных P_i, которые видны в pxy, и вычислить min(P_i(t)) так же как max(P_i(t)). Это будут пределы по оси X для pth. Такой подход, безусловно, не всегда дает наилучшие результаты, однако природа моих данных предполагает, что в большинстве интересных случаев этого должно быть достаточно. - person Thilo; 04.05.2012
comment
если ваши данные монотонны, их будет довольно легко инвертировать. Следовательно, ваш подход также будет точным. Вы можете придумать множество случаев, которые сработают, а также много случаев, которые не сработают. - person Gunther Struyf; 04.05.2012

В результате подсказки Гюнтера я смог решить проблему самостоятельно. Поскольку ответ Гюнтера по-прежнему требует довольно много исследований, ниже я привожу свое собственное решение на тот случай, если кто-то когда-нибудь наткнется на подобную проблему.

Во-первых, я добавил большую часть необходимых данных как UserData в объект оси. Моя сюжетная функция (только для двух графиков) выглядит примерно так:

sb1 = subplot(1, 2, 1);
plot(data(:,1), data(:,2));
axis equal;
sb2 = subplot(1, 2, 2);
plot(data(:,4), data(:,3));
set(sb1, 'UserData', struct('projection', 'xy', 'data', data, 'link', [sb2]));
set(sb2, 'UserData', struct('projection', 'th', 'data', data, 'link', [sb1]));
panzoom(sb1, 'setlimits');   % Those two lines ensure that the zoom limits won't get changed
panzoom(sb2, 'setlimits');   % over time.

Теперь я установил обработчик моей функции масштабирования:

z = zoom;
set(z, 'ActionPostCallback', @Track.synchronizePlots);
z = pan;
set(z, 'ActionPostCallback', @Track.synchronizePlots);

И, наконец, это место, где происходит волшебство:

function synchronizePlots(obj, ax)
    ax = ax.Axes;           
    ud = get(ax, 'UserData');

    if ud.projection == 'xy'
        % That is the part discussed in the comments above,
        % which is, as I freely admit, not very sensible on a strict
        % mathematical point of view. However, the result is good enough for my
        % purpose
        xrange = get(ax, 'XLim');
        yrange = get(ax, 'YLim');
        pointsvisible = ud.data(1,:) >= xrange(1) & ...
                        ud.data(1,:) <= xrange(2) & ...
                        ud.data(2,:) >= yrange(1) & ...
                        ud.data(2,:) <= yrange(2);
        r = [min(ud.data(4, pointsvisible)), max(ud.data(4, pointsvisible))];
        if length(r) == 0  % The trick above may fail if there is no point in the zoom region.
            return         % in that case we just do nothing.
        end
    else
        r = get(ax, 'XLim');  % Straightforward
    end


    for a = ud.link  % The function does not care about the number of figures that have to be changed.
        linkud = get(a, 'UserData');

        if linkud.projection == 'xy'
            % Again, changing the xy-plot is that only part we have to work.
            pointsintime = linkud.data(4,:) >= r(1) & ...
                           linkud.data(4,:) <= r(2);
            xrange = [min(linkud.data(1, pointsintime)), ...
                      max(linkud.data(1, pointsintime))];
            yrange = [min(linkud.data(2, pointsintime)), ...
                      max(linkud.data(2, pointsintime))];
            if length(xrange) > 0
                 set(a, 'XLim', xrange);
                 set(a, 'YLim', yrange);
                 axis(a, 'equal');
            end
        else
            set(a, 'XLim', r);
        end
    end

Надеюсь, это поможет кому-то.

person Thilo    schedule 04.05.2012