Using FacetZoom: Arranging discrete data

This entry describes the code for integrating FacetZoom into an application in order to arrange discrete data (e.g. photos) according to a non time-based facet (e.g. location, person, etc.).

Internally, the FacetZoom widget utilizes the Qt QGraphicsView class for visualization. Consequently, it offers methods in its interface which are best suited for data visualizations which make use of QGraphicsView as well. The interface class is called DataPlacementControlInterface.

To receive a notification every time the FacetZoom widget is modified and the data visualization needs to be updated, a placementChangedSignal is emitted on the FacetZoom widget. This signal can be connected to only on the FacetZoom class as Qt does not permit offering Signals in virtual (interface) classes. Therefore, we can connect a customViewInstance subclass of QGraphicsView to a FacetZoom instance fzInstance as follows:

connect(fzInstance,
    SIGNAL(placementChangedSignal(DataPlacementControlInterface*)),
    vizInstance,
    SLOT(dpcChangedSlot(DataPlacementControlInterface*)));

 

Now, every time dpcChangedSlot is invoked by an emission of placementChangedSignal, the vizInstance receives a pointer to the emitting FacetZoom widget along with the call. This way, the visualization instance is able to retrieve the information needed to arrange the data in its view.

Arranging items in the visualization

As FacetZoom is datatype-independent, each cell in a FacetZoom instance is identified by an abstract unique number (uniqueness is guaranteed across all levels within the instance). Therefore, a mapping from the metadata values, which were used to construct the FacetZoom instance, to the cell identifiers can be retrieved. Using these identifiers, information on the position and extend of cells can be retrieved. Assuming that the DataPlacementControlInterface is declared as dpc in the dpcChangedSlot method of the visualization class, the code for retrieving the mapping would be as follows:

// mapping of metadata values to cell IDs on each FacetZoom level
QMap<int, QMap<ComparableVariant, ulong> > levelAndValueToIdMap
    = dpc->getLevelAndValueToIdMap();
const int level = dpc->getDominantLevel();
// metadata to cell ID mapping on the focused level
QMap<ComparableVariant, ulong> valueToIdMap
    = levelAndValueToIdMap[level];

 

The ComparableVariant class extends Qt’s QVariant with a < comparison operator. For non time-based metadata, a ComparableVariant is constructed from a QString corresponding to a cell in the FacetZoom instance. With these mappings, the cell ID and thus position and extent corresponding to an item’s (e.g. photo’s) metadata can be retrieved and applied to a QGraphicsItem item in the visualization:

const ulong cellId = valueToIdMap.value(facetValue);
QRectF dataSceneRect = dpc->toSceneRect(cellId);
// set the item's position
item->setPos(dataSceneRect.x(), item->y());
// set the item's scale
item->scale(dataSceneRect.width() / item->width(), 1);

 

Updating the visualization view

With the items placed according to the FacetZoom instance, the data visualization’s view needs to be realigned to match the FacetZoom widget’s view. This is done by matching the screen position of the visualization view’s scene origin to the FacetZoom view’s scene origin. The DataPlacementControlInterface provides methods for retrieving the required information. However, due to a limitation of Qt’s QGraphicsView implementation (views are automatically constrained to their scene’s sceneRect()), the visualization view’s scene rectangle must be first grown to at least the size of the FacetZoom’s scene rectangle.

// grow visualization scene
this->setSceneRect(this->sceneRect() | dpc->getSceneRect());
// get screen coordinate of the dpc's scene position 0
float dpcViewPos = dpc->getViewOriginPosX();
QTransform vizmatTrans = this->transform();
// calculate new view position from difference to dpc
dpcViewPos = vizmatTrans.dx() +
    (dpcViewPos - mapFromScene(0,0).rx());
// update the view's transformation matrix
vizmatTrans.setMatrix(
    vizmatTrans.m11(), vizmatTrans.m12(), vizmatTrans.m13(),
    vizmatTrans.m21(), vizmatTrans.m22(), vizmatTrans.m23(),
    dpcViewPos, vizmatTrans.dy(), vizmatTrans.m33());
// apply the transformation matrix
this->setTransform(vizmatTrans);

 

The resulting application could then look as follows:

For any implementation questions or suggestions, please leave a comment below.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s