Assets/vega.dsm.matrix.json

{
  "data": [
    {
      "name": "nodes",
      "transform": [{ "type": "formula", "as": "order", "expr": "datum.index" }],
      "values": []
    },
    {
      "name": "edges",
      "transform": [
        {
          "type": "lookup",
          "from": "nodes",
          "key": "index",
          "fields": ["source"],
          "as": ["sourceNode"]
        },
        {
          "type": "lookup",
          "from": "nodes",
          "key": "index",
          "fields": ["target"],
          "as": ["targetNode"]
        },
        {
          "type": "formula",
          "as": "sourceCluster",
          "expr": "datum.sourceNode.group"
        },
        {
          "type": "formula",
          "as": "targetCluster",
          "expr": "datum.targetNode.group"
        },
        {
          "type": "formula",
          "as": "clusterKey",
          "expr": "datum.sourceCluster * groupBase + datum.targetCluster"
        },
        {
          "type": "formula",
          "as": "clusterLabel",
          "expr": "toString(datum.sourceCluster) + '-' + toString(datum.targetCluster)"
        }
      ],
      "values": []
    },
    {
      "name": "children",
      "transform": [
        {
          "type": "filter",
          "expr": "datum.sourceNode.index === rowClick.index || datum.targetNode.index === columnClick.index"
        },
        {
          "type": "formula",
          "as": "targetNodeName",
          "expr": "datum.targetNode.name"
        },
        {
          "type": "formula",
          "as": "sourceNodeName",
          "expr": "datum.sourceNode.name"
        }
      ],
      "source": "edges"
    },
    {
      "name": "groupStats",
      "source": "nodes",
      "transform": [
        {
          "type": "aggregate",
          "ops": ["max"],
          "fields": ["group"],
          "as": ["groupMax"]
        }
      ]
    },
    {
      "name": "pairs",
      "source": "edges",
      "transform": [{ "type": "aggregate", "groupby": ["clusterKey"] }]
    }
  ],
  "marks": [
    {
      "from": { "data": "nodes" },
      "type": "rect",
      "encode": {
        "update": {
          "fill": { "value": "#cccccc" },
          "fillOpacity": { "value": 0.45 },
          "height": { "band": 1, "offset": -1, "scale": "position" },
          "width": { "band": 1, "offset": -1, "scale": "position" },
          "x": { "scale": "position", "field": "order" },
          "y": { "scale": "position", "field": "order" }
        }
      },
      "zindex": 0
    },
    {
      "from": { "data": "edges" },
      "type": "rect",
      "encode": {
        "update": {
          "fill": [
            {
              "test": "rowClick && rowClick.index != null && datum.sourceNode.index === rowClick.index",
              "value": "red"
            },
            {
              "test": "columnClick && columnClick.index != null && datum.targetNode.index === columnClick.index",
              "value": "red"
            },
            { "scale": "color", "field": "clusterKey" }
          ],
          "height": { "band": 1, "offset": -1, "scale": "position" },
          "width": { "band": 1, "offset": -1, "scale": "position" },
          "x": { "mult": { "signal": "cellSize" }, "field": "y" },
          "y": { "mult": { "signal": "cellSize" }, "field": "x" }
        }
      },
      "zindex": 10
    },
    {
      "from": { "data": "nodes" },
      "type": "rect",
      "encode": {
        "update": {
          "fill": [
            {
              "test": "rowClick && rowClick.index != null && datum.index === rowClick.index",
              "value": "lightsteelblue"
            }
          ],
          "fillOpacity": { "value": 0.3 },
          "height": { "band": 1, "scale": "position" },
          "width": { "signal": "width" },
          "x": { "value": 0 },
          "y": { "scale": "position", "field": "order" }
        }
      },
      "name": "rowHighlight"
    },
    {
      "from": { "data": "nodes" },
      "type": "rect",
      "encode": {
        "update": {
          "fill": [
            {
              "test": "rowClick && rowClick.index != null && indata('children', 'target', datum.index)",
              "value": "lightsteelblue"
            }
          ],
          "fillOpacity": { "value": 0.3 },
          "height": { "signal": "height" },
          "width": { "band": 1, "scale": "position" },
          "x": { "scale": "position", "field": "order" },
          "y": { "value": 0 }
        }
      },
      "name": "colHighlight"
    },
    {
      "from": { "data": "nodes" },
      "type": "rect",
      "encode": {
        "update": {
          "fill": [
            {
              "test": "columnClick && columnClick.index != null && datum.index === columnClick.index",
              "value": "lightsteelblue"
            }
          ],
          "fillOpacity": { "value": 0.3 },
          "height": { "signal": "width" },
          "width": { "band": 1, "scale": "position" },
          "x": { "scale": "position", "field": "order" },
          "y": { "value": 0 }
        }
      },
      "name": "colHighlightOnColumnClick"
    },
    {
      "from": { "data": "nodes" },
      "type": "rect",
      "encode": {
        "update": {
          "fill": [
            {
              "test": "columnClick && indata('children', 'source', datum.index)",
              "value": "lightsteelblue"
            }
          ],
          "fillOpacity": { "value": 0.3 },
          "height": { "band": 1, "scale": "position" },
          "width": { "signal": "height" },
          "x": { "value": 0 },
          "y": { "scale": "position", "field": "order" }
        }
      },
      "name": "rowHighlightOnColumnClick"
    },
    {
      "from": { "data": "nodes" },
      "type": "text",
      "encode": {
        "update": {
          "align": { "value": "left" },
          "angle": { "value": -90 },
          "baseline": { "value": "middle" },
          "fill": [
            {
              "test": "rowClick && indata('children', 'targetNodeName', datum.name)",
              "value": "red"
            },
            { "value": "black" }
          ],
          "fontSize": { "value": 10 },
          "text": { "field": "name" },
          "x": { "band": 0.5, "scale": "position", "field": "order" },
          "y": { "offset": -2 }
        }
      },
      "name": "columns"
    },
    {
      "from": { "data": "nodes" },
      "type": "text",
      "encode": {
        "update": {
          "align": { "value": "right" },
          "baseline": { "value": "middle" },
          "fill": [
            { "test": "datum === rowClick", "value": "steelblue" },
            {
              "test": "columnClick && indata('children', 'sourceNodeName', datum.name)",
              "value": "red"
            },
            { "value": "black" }
          ],
          "fontSize": { "value": 10 },
          "text": { "field": "name" },
          "x": { "offset": -2 },
          "y": { "band": 0.5, "scale": "position", "field": "order" }
        }
      },
      "name": "rows"
    }
  ],
  "scales": [
    {
      "domain": { "data": "nodes", "field": "order", "sort": true },
      "name": "position",
      "type": "band",
      "range": { "step": { "signal": "cellSize" } }
    },
    {
      "name": "color",
      "type": "ordinal",
      "domain": { "data": "edges", "field": "clusterKey", "sort": true },
      "range": {
        "scheme": { "signal": "colorSchemeName" },
        "count": { "signal": "schemeCount" }
      }
    }
  ],
  "signals": [
    {
      "name": "columnClick",
      "on": [
        {
          "events": { "markname": "columns", "type": "mouseover" },
          "update": "datum"
        },
        { "events": "mouseout", "update": "{}" }
      ],
      "value": {}
    },
    {
      "name": "rowClick",
      "on": [
        {
          "events": { "markname": "rows", "type": "mouseover" },
          "update": "datum"
        },
        { "events": "mouseout", "update": "{}" }
      ],
      "value": {}
    },
    { "name": "pairsCount", "update": "length(data('pairs'))" },
    {
      "name": "colorSchemeName",
      "update": "pairsCount <= 10 ? 'tableau10' : (pairsCount <= 12 ? 'set3' : 'category20')"
    },
    { "name": "schemeCount", "update": "min(pairsCount, 20)" },
    { "name": "groupBase", "update": "data('groupStats')[0].groupMax + 1" },
    { "name": "cellSize", "value": 10 },
    { "name": "count", "update": "length(data('nodes'))" },
    { "name": "width", "update": "span(range('position'))" },
    { "name": "height", "update": "width" }
  ],
  "legends": [
    {
      "fill": "color",
      "title": "Cluster pairs (src→dst)",
      "encode": {
        "labels": {
          "update": {
            "text": {
              "signal": "floor(datum.value / groupBase) + '-' + (datum.value % groupBase)"
            }
          }
        }
      }
    }
  ],
  "$schema": "https://vega.github.io/schema/vega/v6.json",
  "description": "A re-orderable DSM matrix.",
  "height": 1000,
  "padding": 2,
  "width": 1000
}