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
}