diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml
index b06a35ede..57e0ec387 100644
--- a/.github/workflows/go-test.yml
+++ b/.github/workflows/go-test.yml
@@ -18,7 +18,7 @@ jobs:
     - name: Set up Go
       uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe
       with:
-        go-version: 1.21
+        go-version-file: ./go.mod
 
     - name: Build
       env:
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
index f2e412c7b..de2088160 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -11,8 +11,8 @@ jobs:
     name: golangci-lint
     runs-on: ubuntu-latest
     steps:
+      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
       - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe
         with:
-          go-version: 1.21
-      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
+          go-version-file: ./go.mod
       - uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc
diff --git a/cmd/analyzer/main_test.go b/cmd/analyzer/main_test.go
new file mode 100644
index 000000000..6f39b4c4b
--- /dev/null
+++ b/cmd/analyzer/main_test.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+	"strings"
+	"testing"
+)
+
+func Test_main(t *testing.T) {
+	tests := []struct {
+		name string
+		args string
+	}{
+		{"drawio_multi_vpc", "-output-file aaa.drawio -vpc-config ../../pkg/ibmvpc/examples/input_multiple_vpcs.json -format drawio"},
+		{"txt_multi_vpc", "-output-file aaa.txt -vpc-config ../../pkg/ibmvpc/examples/input_multiple_vpcs.json -format txt"},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if err := _main(strings.Split(tt.args, " ")); err != nil {
+				t.Errorf("_main(), name %s, error = %v", tt.name, err)
+			}
+		})
+	}
+}
diff --git a/pkg/common/genericSet.go b/pkg/common/genericSet.go
new file mode 100644
index 000000000..9435fbf76
--- /dev/null
+++ b/pkg/common/genericSet.go
@@ -0,0 +1,53 @@
+package common
+
+import (
+	"fmt"
+	"reflect"
+	"sort"
+	"strings"
+)
+
+// //////////////////////////////////////////////////////////////////////////////////////////////
+
+// a genericSet is a generic implementation of a set.
+// the main functionality of genericSet is asKey() - conversion to a string.
+// asKey() is needed for using genericSet as a key of a map
+// //////////////////////////////////////////////////////////////////////////////////////////////
+
+type GenericSet[T comparable] map[T]bool
+type SetAsKey string
+
+func (s GenericSet[T]) AsKey() SetAsKey {
+	ss := []string{}
+	for i := range s {
+		key := ""
+		rv := reflect.ValueOf(i)
+		if rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
+			key = fmt.Sprintf("%x", rv.Pointer())
+		} else {
+			key = fmt.Sprint(i)
+		}
+		ss = append(ss, key)
+	}
+	sort.Strings(ss)
+	return SetAsKey(strings.Join(ss, ","))
+}
+
+func (s GenericSet[T]) AsList() []T {
+	keys := make([]T, len(s))
+	i := 0
+	for k := range s {
+		keys[i] = k
+		i++
+	}
+	return keys
+}
+
+func (s GenericSet[T]) IsIntersect(s2 GenericSet[T]) bool {
+	for i := range s {
+		if (s2)[i] {
+			return true
+		}
+	}
+	return false
+}
diff --git a/pkg/drawio/abstractTreeNode.go b/pkg/drawio/abstractTreeNode.go
index b37dec67d..19c5d52fc 100644
--- a/pkg/drawio/abstractTreeNode.go
+++ b/pkg/drawio/abstractTreeNode.go
@@ -15,14 +15,15 @@ const (
 )
 
 type abstractTreeNode struct {
-	id       uint
-	x        int
-	y        int
-	name     string
-	width    int
-	height   int
-	parent   TreeNodeInterface
-	location *Location
+	id                uint
+	x                 int
+	y                 int
+	name              string
+	width             int
+	height            int
+	parent            TreeNodeInterface
+	location          *Location
+	doNotShowInDrawio bool
 }
 
 func (tn *abstractTreeNode) Label() string { return tn.name }
@@ -51,7 +52,8 @@ func (tn *abstractTreeNode) DrawioParent() TreeNodeInterface { return tn.parent
 
 func (tn *abstractTreeNode) setLocation(location *Location) { tn.location = location }
 func (tn *abstractTreeNode) setParent(p TreeNodeInterface)  { tn.parent = p }
-func (tn *abstractTreeNode) NotShownInDrawio() bool         { return false }
+func (tn *abstractTreeNode) NotShownInDrawio() bool         { return tn.doNotShowInDrawio }
+func (tn *abstractTreeNode) SetNotShownInDrawio()           { tn.doNotShowInDrawio = true }
 
 var idCounter uint = minID
 
diff --git a/pkg/drawio/createMapFile.go b/pkg/drawio/createMapFile.go
index 205f742fc..0562457e4 100644
--- a/pkg/drawio/createMapFile.go
+++ b/pkg/drawio/createMapFile.go
@@ -42,13 +42,17 @@ func (data *drawioData) ElementComment(tn TreeNodeInterface) string {
 // (the last in the file will be on top in the canvas)
 // 1. we put the lines at the top so they will overlap the icons
 // 2. we put the icons above the squares so we can mouse over it for tooltips
-// 3. we put the sgs in the bottom. if a sg is above a square, it will block the the tooltip of the children of the square.
+// 3. we put the sgs and the gs in the bottom.
+// (if a sg ot a gs is above a square, it will block the the tooltip of the children of the square.)
 func orderNodesForDrawio(nodes []TreeNodeInterface) []TreeNodeInterface {
-	var sg, sq, ln, ic, orderedNodes []TreeNodeInterface
+	var sg, sq, ln, ic, gs, orderedNodes []TreeNodeInterface
 	for _, tn := range nodes {
 		switch {
 		case reflect.TypeOf(tn).Elem() == reflect.TypeOf(PartialSGTreeNode{}):
 			sg = append(sg, tn)
+		case tn.IsSquare() && tn.(SquareTreeNodeInterface).IsGroupingSquare(),
+			tn.IsSquare() && tn.(SquareTreeNodeInterface).IsGroupSubnetsSquare():
+			gs = append(gs, tn)
 		case tn.IsSquare():
 			sq = append(sq, tn)
 		case tn.IsIcon():
@@ -57,6 +61,7 @@ func orderNodesForDrawio(nodes []TreeNodeInterface) []TreeNodeInterface {
 			ln = append(ln, tn)
 		}
 	}
+	orderedNodes = append(orderedNodes, gs...)
 	orderedNodes = append(orderedNodes, sg...)
 	orderedNodes = append(orderedNodes, sq...)
 	orderedNodes = append(orderedNodes, ic...)
@@ -64,8 +69,8 @@ func orderNodesForDrawio(nodes []TreeNodeInterface) []TreeNodeInterface {
 	return orderedNodes
 }
 
-func CreateDrawioConnectivityMapFile(network SquareTreeNodeInterface, outputFile string) error {
-	newLayout(network).layout()
+func CreateDrawioConnectivityMapFile(network SquareTreeNodeInterface, outputFile string, subnetMode bool) error {
+	newLayout(network, subnetMode).layout()
 
 	return writeDrawioFile(NewDrawioData(network), outputFile)
 }
diff --git a/pkg/drawio/drawio_test.go b/pkg/drawio/drawio_test.go
index f719fb6a9..cd1d371ae 100644
--- a/pkg/drawio/drawio_test.go
+++ b/pkg/drawio/drawio_test.go
@@ -8,31 +8,37 @@ import (
 
 func TestWithParsing(t *testing.T) {
 	n := createNetwork()
-	err := CreateDrawioConnectivityMapFile(n, "fake.drawio")
+	err := CreateDrawioConnectivityMapFile(n, "fake.drawio", false)
 	if err != nil {
 		fmt.Println("Error when calling CreateDrawioConnectivityMapFile():", err)
 	}
 	n = createNetwork2()
-	err = CreateDrawioConnectivityMapFile(n, "fake2.drawio")
+	err = CreateDrawioConnectivityMapFile(n, "fake2.drawio", false)
 	if err != nil {
 		fmt.Println("Error when calling CreateDrawioConnectivityMapFile():", err)
 	}
 	n = createNetworkGrouping()
-	err = CreateDrawioConnectivityMapFile(n, "grouping.drawio")
+	err = CreateDrawioConnectivityMapFile(n, "grouping.drawio", false)
 	if err != nil {
 		fmt.Println("Error when calling CreateDrawioConnectivityMapFile():", err)
 	}
+	n = createNetworkSubnetGrouping()
+	err = CreateDrawioConnectivityMapFile(n, "subnetGrouping.drawio", true)
+	if err != nil {
+		fmt.Println("Error when calling CreateDrawioConnectivityMapFile():", err)
+	}
+
 	n2 := NewNetworkTreeNode()
 	NewCloudTreeNode(n2, "empty Cloud")
 	NewPublicNetworkTreeNode(n2)
 	NewCloudTreeNode(n2, "empty cloud2")
-	err = CreateDrawioConnectivityMapFile(n2, "fake3.drawio")
+	err = CreateDrawioConnectivityMapFile(n2, "fake3.drawio", false)
 	if err != nil {
 		fmt.Println("Error when calling CreateDrawioConnectivityMapFile():", err)
 	}
 
 	n = createNetworkAllTypes()
-	err = CreateDrawioConnectivityMapFile(n, "all.drawio")
+	err = CreateDrawioConnectivityMapFile(n, "all.drawio", false)
 	if err != nil {
 		fmt.Println("Error when calling CreateDrawioConnectivityMapFile():", err)
 	}
@@ -278,6 +284,59 @@ func createNetworkAllTypes() SquareTreeNodeInterface {
 	return network
 }
 
+func createZone(zones *[][]SquareTreeNodeInterface, vpc *VpcTreeNode, size int, name string) {
+	zone := NewZoneTreeNode(vpc, name)
+	subnets := make([]SquareTreeNodeInterface, size)
+	*zones = append(*zones, subnets)
+	for i := 0; i < size; i++ {
+		sname := fmt.Sprint(name, i)
+		subnets[i] = NewSubnetTreeNode(zone, sname, "", "")
+	}
+}
+func createGroup(zones *[][]SquareTreeNodeInterface, vpc *VpcTreeNode, i1, i2, j1, j2 int) SquareTreeNodeInterface {
+	gr := []SquareTreeNodeInterface{}
+	for i := i1; i <= i2; i++ {
+		for j := j1; j <= j2; j++ {
+			gr = append(gr, (*zones)[i][j])
+		}
+	}
+	g := GroupedSubnetsSquare(vpc, gr)
+	g.(*GroupSubnetsSquareTreeNode).name = fmt.Sprintf("%d-%d,%d,%d", i1, i2, j1, j2)
+	return g
+}
+
+func createNetworkSubnetGrouping() SquareTreeNodeInterface {
+	network := NewNetworkTreeNode()
+	zones := &[][]SquareTreeNodeInterface{}
+	cloud1 := NewCloudTreeNode(network, "IBM Cloud")
+	publicNetwork := NewPublicNetworkTreeNode(network)
+	vpc1 := NewVpcTreeNode(cloud1, "vpc1")
+	for i := 0; i < 10; i++ {
+		createZone(zones, vpc1, 8, fmt.Sprintf("z%d", i))
+	}
+	groups := []SquareTreeNodeInterface{
+		createGroup(zones, vpc1, 0, 0, 0, 1),
+		createGroup(zones, vpc1, 1, 1, 0, 1),
+		createGroup(zones, vpc1, 0, 2, 0, 6),
+		createGroup(zones, vpc1, 0, 2, 4, 6),
+		createGroup(zones, vpc1, 3, 3, 1, 2),
+		createGroup(zones, vpc1, 2, 3, 1, 2),
+		createGroup(zones, vpc1, 0, 4, 0, 3),
+		createGroup(zones, vpc1, 0, 5, 0, 3),
+
+		createGroup(zones, vpc1, 6, 7, 0, 1),
+		createGroup(zones, vpc1, 6, 6, 2, 3),
+		createGroup(zones, vpc1, 7, 8, 1, 2),
+	}
+	NewConnectivityLineTreeNode(network, groups[0], groups[len(groups)-1], true, "gconn")
+
+	for _, gr := range groups {
+		i1 := NewInternetTreeNode(publicNetwork, "I "+gr.Label())
+		NewConnectivityLineTreeNode(network, gr, i1, true, "gconn "+gr.Label())
+	}
+	return network
+}
+
 func createNetworkGrouping() SquareTreeNodeInterface {
 	network := NewNetworkTreeNode()
 	publicNetwork := NewPublicNetworkTreeNode(network)
diff --git a/pkg/drawio/layout.go b/pkg/drawio/layout.go
index 787bbe011..7d448affc 100644
--- a/pkg/drawio/layout.go
+++ b/pkg/drawio/layout.go
@@ -10,6 +10,7 @@ import (
 // the input to the layout algorithm is the tree itself. the output is the geometry for each node in the drawio (x, y, height, width)
 // the steps:
 // 1. create a 2D matrix  - for each subnet icon, it set the location in the matrix (see details about layouting a subnet)
+// (when we are in subnet mode, we set the locations of the subnets, using the subnetsLayout struct. see subnetsLayout.go
 // 2. set the locations of the SG in the matrix, according to the locations of the icons
 // 3. add squares borders - resizing the matrix. adding rows and columns to the matrix, to be used as the borders of all squares
 // 4. set the locations of all the squares in the matrix
@@ -55,18 +56,24 @@ const (
 )
 
 type layoutS struct {
-	network SquareTreeNodeInterface
-	matrix  *layoutMatrix
+	network    SquareTreeNodeInterface
+	matrix     *layoutMatrix
+	subnetMode bool
 }
 
-func newLayout(network SquareTreeNodeInterface) *layoutS {
-	return &layoutS{network: network, matrix: newLayoutMatrix()}
+func newLayout(network SquareTreeNodeInterface, subnetMode bool) *layoutS {
+	return &layoutS{network: network, matrix: newLayoutMatrix(), subnetMode: subnetMode}
 }
 
 func (ly *layoutS) layout() {
 	// main layout algorithm:
 	// 1. create a 2D matrix  - for each subnet icon, it set the location in the matrix
-	ly.layoutSubnetsIcons()
+	// in case of subnet mode, set the locations of the subnets
+	if !ly.subnetMode {
+		ly.layoutSubnetsIcons()
+	} else {
+		ly.layoutSubnets()
+	}
 	ly.matrix.removeUnusedLayers()
 	// 2. set the locations of the SG in the matrix, according to the locations of the icons
 	ly.setSGLocations()
@@ -80,7 +87,9 @@ func (ly *layoutS) layout() {
 	// 6. set the geometry for each node in the drawio
 	ly.matrix.setLayersDistance()
 	ly.setGeometries()
-	newLayoutOverlap(ly.network).fixOverlapping()
+	if !ly.subnetMode {
+		newLayoutOverlap(ly.network).fixOverlapping()
+	}
 }
 
 // setDefaultLocation() set locations to squares
@@ -275,6 +284,9 @@ func (ly *layoutS) layoutSubnetsIcons() {
 					for _, group := range groups {
 						rowIndex, colIndex = ly.layoutGroupIcons(group, rowIndex, colIndex)
 					}
+					if rowIndex == subnet.Location().firstRow.index {
+						rowIndex++
+					}
 				}
 				colIndex++
 			}
@@ -288,6 +300,111 @@ func (ly *layoutS) layoutSubnetsIcons() {
 	}
 }
 
+func (ly *layoutS) layoutSubnets() {
+	sly := newSubnetsLayout(ly.network)
+	sly.layout()
+	ly.setSubnetsLocations(sly.subnetMatrix, sly.zonesCol)
+}
+
+func (ly *layoutS) setSubnetsLocations(subnetMatrix [][]TreeNodeInterface, zonesCol map[TreeNodeInterface]int) {
+	locatedSubnets := map[TreeNodeInterface]bool{}
+	for ri, row := range subnetMatrix {
+		for ci, s := range row {
+			if s != nil && s != fakeSubnet {
+				ly.setDefaultLocation(s.(SquareTreeNodeInterface), ri, ci)
+				locatedSubnets[s] = true
+			}
+		}
+	}
+	ly.setDefaultLocation(ly.network, 0, 0)
+	for _, cloud := range ly.network.(*NetworkTreeNode).clouds {
+		for _, vpc := range cloud.(*CloudTreeNode).vpcs {
+			for _, zone := range vpc.(*VpcTreeNode).zones {
+				if _, ok := zonesCol[zone]; !ok {
+					zonesCol[zone] = len(zonesCol)
+				}
+				rowIndex := 0
+				for _, subnet := range zone.(*ZoneTreeNode).subnets {
+					if !locatedSubnets[subnet] {
+						a := len(subnetMatrix)
+						for rowIndex < a && zonesCol[zone] < len(subnetMatrix[rowIndex]) && subnetMatrix[rowIndex][zonesCol[zone]] != nil {
+							rowIndex++
+						}
+						ly.setDefaultLocation(subnet, rowIndex, zonesCol[zone])
+						rowIndex++
+					}
+				}
+			}
+		}
+	}
+}
+
+////////////////////////////////////////////////////////////////////
+// resolveGroupedSubnetsOverlap() handles overlapping GroupSubnetsSquare.
+// it makes sure that the borders of two squares will not overlap each other.
+// the borders of two squares overlap if these two condition happened:
+// 1. they have the same first raw, or the same last raw, or the same first col or the same last col
+// 2. they have the same xOffset (since xOffset == yOffset == xEndOffset == yEndOffset)
+//
+// in case we find such a pair, we shrink the smaller one by increasing its offsets
+// we continue to look for such pairs till cant find any
+
+func (ly *layoutS) resolveGroupedSubnetsOverlap() {
+	allSubnetsSquares := map[*GroupSubnetsSquareTreeNode]bool{}
+	for _, tn := range getAllNodes(ly.network) {
+		if !tn.NotShownInDrawio() && tn.IsSquare() && tn.(SquareTreeNodeInterface).IsGroupSubnetsSquare() {
+			allSubnetsSquares[tn.(*GroupSubnetsSquareTreeNode)] = true
+		}
+	}
+	for foundOverlap := true; foundOverlap; {
+		foundOverlap = false
+		for tn1 := range allSubnetsSquares {
+			for tn2 := range allSubnetsSquares {
+				if tn1 == tn2 {
+					continue
+				}
+				l1 := tn1.Location()
+				l2 := tn2.Location()
+				if l1.firstRow == l2.firstRow || l1.firstCol == l2.firstCol || l1.lastRow == l2.lastRow || l1.lastCol == l2.lastCol {
+					if l1.xOffset == l2.xOffset {
+						toShrink := tn1
+						if len(tn2.groupedSubnets) < len(tn1.groupedSubnets) {
+							toShrink = tn2
+						}
+						toShrink.Location().xOffset += groupInnerBorderWidth
+						toShrink.Location().yOffset += groupInnerBorderWidth
+						toShrink.Location().xEndOffset += groupInnerBorderWidth
+						toShrink.Location().yEndOffset += groupInnerBorderWidth
+						foundOverlap = true
+					}
+				}
+			}
+		}
+	}
+}
+
+// since we do not have subnet icons, we set the subnets smaller and the GroupSubnetsSquare bigger
+func (ly *layoutS) setGroupedSubnetsOffset() {
+	for _, tn := range getAllNodes(ly.network) {
+		switch {
+		case tn.NotShownInDrawio():
+		case !tn.IsSquare():
+		case tn.(SquareTreeNodeInterface).IsSubnet():
+			tn.Location().xOffset = borderWidth
+			tn.Location().yOffset = borderWidth
+			tn.Location().xEndOffset = borderWidth
+			tn.Location().yEndOffset = borderWidth
+
+		case tn.(SquareTreeNodeInterface).IsGroupSubnetsSquare():
+			tn.Location().xOffset = -groupBorderWidth
+			tn.Location().yOffset = -groupBorderWidth
+			tn.Location().xEndOffset = -groupBorderWidth
+			tn.Location().yEndOffset = -groupBorderWidth
+		}
+	}
+	ly.resolveGroupedSubnetsOverlap()
+}
+
 // //////////////////////////////////////////////////////////////////////////////////////////
 // SG can have more than one squares. so setSGLocations() will add treeNodes of the kind PartialSGTreeNode
 // PartialSGTreeNode can not have more than one row. and can have only cell that contains icons that belong to the SG
@@ -400,10 +517,16 @@ func (ly *layoutS) setSquaresLocations() {
 					}
 				}
 			}
+			for _, groupSubnetsSquare := range vpc.(*VpcTreeNode).groupSubnetsSquares {
+				resolveSquareLocation(groupSubnetsSquare, 0, false)
+			}
 		}
 	}
 	ly.resolvePublicNetworkLocations()
 	resolveSquareLocation(ly.network, 1, false)
+	if ly.subnetMode {
+		ly.setGroupedSubnetsOffset()
+	}
 }
 
 // ////////////////////////////////////////////////////////////////////////////////////////
diff --git a/pkg/drawio/layoutOverlap.go b/pkg/drawio/layoutOverlap.go
index 2841c89cb..6cb17b269 100644
--- a/pkg/drawio/layoutOverlap.go
+++ b/pkg/drawio/layoutOverlap.go
@@ -91,7 +91,7 @@ func (lyO *layoutOverlap) handleGroupingLinesOverBorders() {
 			continue
 		}
 		line := n.(LineTreeNodeInterface)
-		if !line.Src().IsGroupingPoint() || !line.Dst().IsGroupingPoint() {
+		if !line.Src().(IconTreeNodeInterface).IsGroupingPoint() || !line.Dst().(IconTreeNodeInterface).IsGroupingPoint() {
 			continue
 		}
 		src, dst := line.Src().(*GroupPointTreeNode), line.Dst().(*GroupPointTreeNode)
@@ -137,8 +137,8 @@ func (lyO *layoutOverlap) handleLinesOverLines() {
 			if len(line1.Points()) != 0 || len(line2.Points()) != 0 {
 				continue
 			}
-			srcPoint := iconCenterPoint(line1.Src())
-			dstPoint := iconCenterPoint(line1.Dst())
+			srcPoint := iconCenterPoint(line1.Src().(IconTreeNodeInterface))
+			dstPoint := iconCenterPoint(line1.Dst().(IconTreeNodeInterface))
 			middlePoint := point{(srcPoint.X + dstPoint.X) / 2, (srcPoint.Y + dstPoint.Y) / 2}
 			BP := lyO.getBypassPoint(srcPoint, dstPoint, middlePoint, line1)
 			if BP != noPoint {
@@ -264,11 +264,11 @@ func (lyO *layoutOverlap) getOverlappedIcon(p1, p2 point, line LineTreeNodeInter
 
 // some methods to convert absolute point to relative, and vis versa:
 func getLineAbsolutePoints(line LineTreeNodeInterface) []point {
-	absPoints := []point{iconCenterPoint(line.Src())}
+	absPoints := []point{iconCenterPoint(line.Src().(IconTreeNodeInterface))}
 	for _, p := range line.Points() {
 		absPoints = append(absPoints, getAbsolutePoint(line, p))
 	}
-	absPoints = append(absPoints, iconCenterPoint(line.Dst()))
+	absPoints = append(absPoints, iconCenterPoint(line.Dst().(IconTreeNodeInterface)))
 	return absPoints
 }
 
diff --git a/pkg/drawio/lineTreeNode.go b/pkg/drawio/lineTreeNode.go
index e017ac400..06b5d0197 100644
--- a/pkg/drawio/lineTreeNode.go
+++ b/pkg/drawio/lineTreeNode.go
@@ -11,8 +11,8 @@ type point struct {
 // //////////////////////////////////////////////////////////////////////////////
 type LineTreeNodeInterface interface {
 	TreeNodeInterface
-	Src() IconTreeNodeInterface
-	Dst() IconTreeNodeInterface
+	Src() TreeNodeInterface
+	Dst() TreeNodeInterface
 	SrcID() uint
 	DstID() uint
 	Points() []point
@@ -24,8 +24,8 @@ type LineTreeNodeInterface interface {
 
 type abstractLineTreeNode struct {
 	abstractTreeNode
-	src    IconTreeNodeInterface
-	dst    IconTreeNodeInterface
+	src    TreeNodeInterface
+	dst    TreeNodeInterface
 	router IconTreeNodeInterface
 	points []point
 }
@@ -34,10 +34,10 @@ func (tn *abstractLineTreeNode) IsLine() bool {
 	return true
 }
 
-func (tn *abstractLineTreeNode) SrcID() uint                { return tn.src.ID() }
-func (tn *abstractLineTreeNode) DstID() uint                { return tn.dst.ID() }
-func (tn *abstractLineTreeNode) Src() IconTreeNodeInterface { return tn.src }
-func (tn *abstractLineTreeNode) Dst() IconTreeNodeInterface { return tn.dst }
+func (tn *abstractLineTreeNode) SrcID() uint            { return tn.src.ID() }
+func (tn *abstractLineTreeNode) DstID() uint            { return tn.dst.ID() }
+func (tn *abstractLineTreeNode) Src() TreeNodeInterface { return tn.src }
+func (tn *abstractLineTreeNode) Dst() TreeNodeInterface { return tn.dst }
 
 func (tn *abstractLineTreeNode) DrawioParent() TreeNodeInterface {
 	if tn.router != nil {
@@ -86,29 +86,24 @@ func NewConnectivityLineTreeNode(network SquareTreeNodeInterface,
 	src, dst TreeNodeInterface,
 	directed bool,
 	name string) *ConnectivityTreeNode {
-	var iconSrc, iconDst IconTreeNodeInterface
-	if src.IsSquare() {
-		iconSrc = NewGroupPointTreeNode(src.(SquareTreeNodeInterface), directed, true, "")
-	} else {
-		iconSrc = src.(IconTreeNodeInterface)
+	if src.IsSquare() && src.(SquareTreeNodeInterface).IsGroupingSquare() {
+		src = NewGroupPointTreeNode(src.(SquareTreeNodeInterface), directed, true, "")
 	}
-	if dst.IsSquare() {
-		iconDst = NewGroupPointTreeNode(dst.(SquareTreeNodeInterface), directed, false, "")
-	} else {
-		iconDst = dst.(IconTreeNodeInterface)
+	if dst.IsSquare() && dst.(SquareTreeNodeInterface).IsGroupingSquare() {
+		dst = NewGroupPointTreeNode(dst.(SquareTreeNodeInterface), directed, false, "")
 	}
-	if iconSrc.IsGroupingPoint() {
-		iconSrc.(*GroupPointTreeNode).setColleague(iconDst)
+	if src.IsIcon() && src.(IconTreeNodeInterface).IsGroupingPoint() {
+		src.(*GroupPointTreeNode).setColleague(dst.(IconTreeNodeInterface))
 	}
-	if iconDst.IsGroupingPoint() {
-		iconDst.(*GroupPointTreeNode).setColleague(iconSrc)
+	if dst.IsIcon() && dst.(IconTreeNodeInterface).IsGroupingPoint() {
+		dst.(*GroupPointTreeNode).setColleague(src.(IconTreeNodeInterface))
 	}
 
 	conn := ConnectivityTreeNode{
 		abstractLineTreeNode: abstractLineTreeNode{
 			abstractTreeNode: newAbstractTreeNode(network, name),
-			src:              iconSrc,
-			dst:              iconDst},
+			src:              src,
+			dst:              dst},
 		directed: directed}
 	network.addLineTreeNode(&conn)
 	return &conn
diff --git a/pkg/drawio/squareTreeNode.go b/pkg/drawio/squareTreeNode.go
index 64def812b..fdbf4be2a 100644
--- a/pkg/drawio/squareTreeNode.go
+++ b/pkg/drawio/squareTreeNode.go
@@ -8,7 +8,9 @@ type SquareTreeNodeInterface interface {
 	IconTreeNodes() []IconTreeNodeInterface
 	TagID() uint
 	DecoreID() uint
+	IsSubnet() bool
 	IsGroupingSquare() bool
+	IsGroupSubnetsSquare() bool
 }
 
 type abstractSquareTreeNode struct {
@@ -35,7 +37,9 @@ func (tn *abstractSquareTreeNode) IsSquare() bool { return true }
 func (tn *abstractSquareTreeNode) TagID() uint    { return tn.id + tagID }
 func (tn *abstractSquareTreeNode) DecoreID() uint { return tn.id + decoreID }
 
-func (tn *abstractSquareTreeNode) IsGroupingSquare() bool { return false }
+func (tn *abstractSquareTreeNode) IsSubnet() bool             { return false }
+func (tn *abstractSquareTreeNode) IsGroupingSquare() bool     { return false }
+func (tn *abstractSquareTreeNode) IsGroupSubnetsSquare() bool { return false }
 
 func calculateSquareGeometry(tn SquareTreeNodeInterface) {
 	location := tn.Location()
@@ -108,8 +112,9 @@ func (tn *CloudTreeNode) children() ([]SquareTreeNodeInterface, []IconTreeNodeIn
 // ////////////////////////////////////////////////////////////////////////////////////////
 type VpcTreeNode struct {
 	abstractSquareTreeNode
-	zones []SquareTreeNodeInterface
-	sgs   []SquareTreeNodeInterface
+	zones               []SquareTreeNodeInterface
+	sgs                 []SquareTreeNodeInterface
+	groupSubnetsSquares []SquareTreeNodeInterface
 }
 
 func NewVpcTreeNode(parent *CloudTreeNode, name string) *VpcTreeNode {
@@ -118,7 +123,7 @@ func NewVpcTreeNode(parent *CloudTreeNode, name string) *VpcTreeNode {
 	return &vpc
 }
 func (tn *VpcTreeNode) children() ([]SquareTreeNodeInterface, []IconTreeNodeInterface, []LineTreeNodeInterface) {
-	return append(tn.zones, tn.sgs...), tn.elements, tn.connections
+	return append(append(tn.zones, tn.sgs...), tn.groupSubnetsSquares...), tn.elements, tn.connections
 }
 
 ///////////////////////////////////////////////////////////////////////
@@ -200,6 +205,7 @@ func (tn *SubnetTreeNode) children() ([]SquareTreeNodeInterface, []IconTreeNodeI
 func (tn *SubnetTreeNode) Label() string {
 	return labels2Table([]string{tn.name, tn.cidr, tn.acl})
 }
+func (tn *SubnetTreeNode) IsSubnet() bool { return true }
 func (tn *SubnetTreeNode) SetACL(acl string) {
 	tn.acl = acl
 }
@@ -264,3 +270,46 @@ func (tn *GroupSquareTreeNode) setVisibility(visibility groupSquareVisibility) {
 func (tn *GroupSquareTreeNode) children() ([]SquareTreeNodeInterface, []IconTreeNodeInterface, []LineTreeNodeInterface) {
 	return nil, append(tn.elements, tn.groupedIcons...), tn.connections
 }
+
+// ////////////////////////////////////////////////////////////////////////////
+type GroupSubnetsSquareTreeNode struct {
+	abstractSquareTreeNode
+	groupedSubnets []SquareTreeNodeInterface
+}
+
+func GroupedSubnetsSquare(parent *VpcTreeNode, groupedSubnets []SquareTreeNodeInterface) SquareTreeNodeInterface {
+	sameZone, sameVpc := true, true
+	zone := groupedSubnets[0].Parent().(*ZoneTreeNode)
+	vpc := groupedSubnets[0].Parent().Parent().(*VpcTreeNode)
+	for _, subnet := range groupedSubnets {
+		if zone != subnet.Parent() {
+			sameZone = false
+		}
+		if vpc != subnet.Parent().Parent() {
+			sameVpc = false
+		}
+	}
+	if sameVpc {
+		allVpcSubnets := []SquareTreeNodeInterface{}
+		for _, z := range vpc.zones {
+			allVpcSubnets = append(allVpcSubnets, z.(*ZoneTreeNode).subnets...)
+		}
+		if len(groupedSubnets) == len(allVpcSubnets) {
+			return vpc
+		}
+	}
+	if sameZone && len(groupedSubnets) == len(zone.subnets) {
+		return zone
+	}
+	return newGroupSubnetsSquareTreeNode(parent, groupedSubnets)
+}
+
+func newGroupSubnetsSquareTreeNode(parent *VpcTreeNode, groupedSubnets []SquareTreeNodeInterface) *GroupSubnetsSquareTreeNode {
+	gs := GroupSubnetsSquareTreeNode{newAbstractSquareTreeNode(parent, ""), groupedSubnets}
+	parent.groupSubnetsSquares = append(parent.groupSubnetsSquares, &gs)
+	return &gs
+}
+func (tn *GroupSubnetsSquareTreeNode) children() ([]SquareTreeNodeInterface, []IconTreeNodeInterface, []LineTreeNodeInterface) {
+	return tn.groupedSubnets, tn.elements, tn.connections
+}
+func (tn *GroupSubnetsSquareTreeNode) IsGroupSubnetsSquare() bool { return true }
diff --git a/pkg/drawio/styles.go b/pkg/drawio/styles.go
index a891ce1e3..5e119a609 100644
--- a/pkg/drawio/styles.go
+++ b/pkg/drawio/styles.go
@@ -7,6 +7,7 @@ import (
 )
 
 const (
+	groupSquareStyle   = "rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#82b366;strokeWidth=6;perimeterSpacing=0;arcSize=12;gradientColor=none;opacity=70;"
 	niStyle            = "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+CjxkZWZzPgo8c3R5bGU+LmNscy0xe2ZpbGw6I2VlNTM5Njt9LmNscy0ye2ZpbGw6bm9uZTt9LmNscy0ze2ZpbGw6I2ZmZjt9PC9zdHlsZT4KPC9kZWZzPg0KPHJlY3QgY2xhc3M9ImNscy0xIiB4PSIwLjUiIHk9IjAuNSIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ii8+CjxyZWN0IGNsYXNzPSJjbHMtMiIgeD0iMTQuNSIgeT0iMTQuNSIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIi8+DQo8dGV4dCBmb250LXNpemU9IjMwIiBmaWxsPSJ3aGl0ZSIgeD0iOCIgeT0iMzUiPk5JPC90ZXh0Pgo8L3N2Zz4=;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;spacingTop=-7;"
 	vsiStyle           = "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMxOTgwMzg7fS5jbHMtMntmaWxsOiNmZmY7fS5jbHMtM3tmaWxsOm5vbmU7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIvPjxjaXJjbGUgY2xhc3M9ImNscy0yIiBjeD0iMTguODgiIGN5PSIyOC44OCIgcj0iMC42MyIvPjxyZWN0IGNsYXNzPSJjbHMtMiIgeD0iMTUuNzUiIHk9IjE4LjI1IiB3aWR0aD0iMi41IiBoZWlnaHQ9IjEuMjUiLz48cmVjdCBjbGFzcz0iY2xzLTIiIHg9IjE5LjUiIHk9IjE4LjI1IiB3aWR0aD0iMi41IiBoZWlnaHQ9IjEuMjUiLz48cmVjdCBjbGFzcz0iY2xzLTIiIHg9IjIzLjI1IiB5PSIxOC4yNSIgd2lkdGg9IjIuNSIgaGVpZ2h0PSIxLjI1Ii8+PHJlY3QgY2xhc3M9ImNscy0yIiB4PSIyNyIgeT0iMTguMjUiIHdpZHRoPSIyLjUiIGhlaWdodD0iMS4yNSIvPjxyZWN0IGNsYXNzPSJjbHMtMiIgeD0iMzAuNzUiIHk9IjE4LjI1IiB3aWR0aD0iMi41IiBoZWlnaHQ9IjEuMjUiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0zMiwzMkgxN2ExLjI1LDEuMjUsMCwwLDEtMS4yNS0xLjI1VjI3QTEuMjUsMS4yNSwwLDAsMSwxNywyNS43NUgzMkExLjI1LDEuMjUsMCwwLDEsMzMuMjUsMjd2My43NUExLjI1LDEuMjUsMCwwLDEsMzIsMzJaTTE3LDI3djMuNzVIMzJWMjdaIi8+PHJlY3QgY2xhc3M9ImNscy0zIiB4PSIxNC41IiB5PSIxNC41IiB3aWR0aD0iMjAiIGhlaWdodD0iMjAiLz48cmVjdCBjbGFzcz0iY2xzLTIiIHg9IjE1Ljc1IiB5PSIyMiIgd2lkdGg9IjE3LjUiIGhlaWdodD0iMS4yNSIvPjwvc3ZnPg==;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingTop=-7;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;"
 	resIPStyle         = "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+CjxkZWZzPgo8c3R5bGU+LmNscy0xe2ZpbGw6I2VlNTM5Njt9LmNscy0ye2ZpbGw6bm9uZTt9LmNscy0ze2ZpbGw6I2ZmZjt9PC9zdHlsZT4KPC9kZWZzPg0KPHJlY3QgY2xhc3M9ImNscy0xIiB4PSIwLjUiIHk9IjAuNSIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ii8+CjxyZWN0IGNsYXNzPSJjbHMtMiIgeD0iMTQuNSIgeT0iMTQuNSIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIi8+DQo8dGV4dCBmb250LXNpemU9IjIwIiBmaWxsPSJ3aGl0ZSIgeD0iNSIgeT0iMzIiPnJlc0lQPC90ZXh0Pgo8L3N2Zz4=;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;spacingTop=-7;"
@@ -21,23 +22,24 @@ const (
 )
 
 var styles = map[reflect.Type]string{
-	reflect.TypeOf(PublicNetworkTreeNode{}):   "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#1192E8;fillColor=none;",
-	reflect.TypeOf(CloudTreeNode{}):           "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#1192E8;fillColor=none;",
-	reflect.TypeOf(VpcTreeNode{}):             "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#1192E8;fillColor=none;",
-	reflect.TypeOf(ZoneTreeNode{}):            "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#878d96;fillColor=none;",
-	reflect.TypeOf(PartialSGTreeNode{}):       "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;fillColor=none;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#FA4D56;strokeWidth=1;",
-	reflect.TypeOf(SubnetTreeNode{}):          "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#1192E8;fillColor=none;",
-	reflect.TypeOf(GroupSquareTreeNode{}):     "rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#006633;strokeWidth=1;perimeterSpacing=0;arcSize=12;",
-	reflect.TypeOf(NITreeNode{}):              vsiStyle,
-	reflect.TypeOf(VsiTreeNode{}):             vsiStyle,
-	reflect.TypeOf(ResIPTreeNode{}):           vpeStyle,
-	reflect.TypeOf(VpeTreeNode{}):             vpeStyle,
-	reflect.TypeOf(GroupPointTreeNode{}):      "ellipse;whiteSpace=wrap;html=1;aspect=fixed;",
-	reflect.TypeOf(UserTreeNode{}):            "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOm5vbmU7fS5jbHMtMntmaWxsOiNmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IHg9IjAuNSIgeT0iMC41IiB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHJ4PSIyNCIvPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMTQuNSIgeT0iMTQuNSIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIi8+PHBhdGggaWQ9IkZpbGwtMyIgY2xhc3M9ImNscy0yIiBkPSJNMzAuOCwzMy44N0gyOVYyOS41OUEyLjYzLDIuNjMsMCwwLDAsMjYuMywyN0gyMi43QTIuNjMsMi42MywwLDAsMCwyMCwyOS41OXY0LjI4SDE4LjJWMjkuNTlhNC40MSw0LjQxLDAsMCwxLDQuNS00LjI4aDMuNmE0LjQxLDQuNDEsMCwwLDEsNC41LDQuMjhaIi8+PHBhdGggaWQ9IkZpbGwtNSIgY2xhc3M9ImNscy0yIiBkPSJNMjQuNSwxNS4wNUE0LjM5LDQuMzksMCwwLDAsMjAsMTkuMzNhNC41MSw0LjUxLDAsMCwwLDksMCw0LjM5LDQuMzksMCwwLDAtNC41LTQuMjhtMCwxLjcxYTIuNTcsMi41NywwLDEsMS0yLjcsMi41NywyLjY0LDIuNjQsMCwwLDEsMi43LTIuNTciLz48L3N2Zz4=;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;spacingTop=-7;",
-	reflect.TypeOf(GatewayTreeNode{}):         "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMxMTkyZTg7fS5jbHMtMntmaWxsOiNmZmY7fS5jbHMtM3tmaWxsOm5vbmU7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgcng9IjgiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0zMy41MSwyNS4zOGExLjIzLDEuMjMsMCwwLDAsMC0xLjc2TDI5Ljg5LDIwbDEuODEtMS43OWExLjI1LDEuMjUsMCwxLDAtLjU4LTIuMDksMS4yMiwxLjIyLDAsMCwwLS4zMiwxLjIyTDI5LDE5LjEybC0zLjYzLTMuNjNhMS4yMywxLjIzLDAsMCwwLTEuNzYsMEwyMCwxOS4xMWwtMS43OS0xLjgyYTEuMjQsMS4yNCwwLDEsMC0yLjA5LjU5LDEuMjIsMS4yMiwwLDAsMCwxLjIyLjMyTDE5LjEyLDIwbC0zLjYzLDMuNjNhMS4yMywxLjIzLDAsMCwwLDAsMS43NkwxOS4xMiwyOSwxNy4zNCwzMC44YTEuMjIsMS4yMiwwLDAsMC0xLjIyLjMyLDEuMjQsMS4yNCwwLDEsMCwyLjA5LjU5TDIwLDI5Ljg5bDMuNjIsMy42MmExLjIzLDEuMjMsMCwwLDAsMS43NiwwTDI5LDI5Ljg4bDEuNzksMS43OGExLjIyLDEuMjIsMCwwLDAsLjMyLDEuMjIsMS4yNCwxLjI0LDAsMSwwLC41OC0yLjA5TDI5Ljg5LDI5Wm0tOSw3LjI0TDE2LjM4LDI0LjVsOC4xMi04LjEyLDguMTIsOC4xMloiLz48cmVjdCBjbGFzcz0iY2xzLTMiIHg9IjE0LjUiIHk9IjE0LjUiIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIvPjxwYXRoIGNsYXNzPSJjbHMtMiIgZD0iTTI2LjM4LDIzLjI1SDIzLjI1VjIyYTEuMjUsMS4yNSwwLDAsMSwyLjUsMEgyN2EyLjUsMi41LDAsMCwwLTUsMHYxLjQyYTEuMjYsMS4yNiwwLDAsMC0uNjIsMS4wOHYzLjEyYTEuMjYsMS4yNiwwLDAsMCwxLjI0LDEuMjZoMy43NmExLjI2LDEuMjYsMCwwLDAsMS4yNC0xLjI2VjI0LjVBMS4yNSwxLjI1LDAsMCwwLDI2LjM4LDIzLjI1Wm0wLDQuMzdIMjIuNjJWMjQuNWgzLjc2WiIvPjwvc3ZnPg==;fontSize=14;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;spacingTop=-7;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;",
-	reflect.TypeOf(InternetTreeNode{}):        "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMxMTkyZTg7fS5jbHMtMntmaWxsOiNmZmY7fS5jbHMtM3tmaWxsOm5vbmU7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgcng9IjgiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0yNC41LDE1Ljc1YTguNzUsOC43NSwwLDEsMCw4Ljc1LDguNzVBOC43NSw4Ljc1LDAsMCwwLDI0LjUsMTUuNzVaTTMyLDIzLjg4SDI4LjI1YTE1LjE5LDE1LjE5LDAsMCwwLTEuNzQtNi42QTcuNSw3LjUsMCwwLDEsMzIsMjMuODhaTTI0LjUsMzJoLS40MkExMy43MiwxMy43MiwwLDAsMSwyMiwyNS4xMmg1QTEzLjYzLDEzLjYzLDAsMCwxLDI0Ljk0LDMyWk0yMiwyMy44OEExMy42MywxMy42MywwLDAsMSwyNC4wNiwxN2EzLjkzLDMuOTMsMCwwLDEsLjg0LDBBMTMuNjQsMTMuNjQsMCwwLDEsMjcsMjMuODhabS40OC02LjZhMTUuMTgsMTUuMTgsMCwwLDAtMS43Myw2LjZIMTdhNy41LDcuNSwwLDAsMSw1LjQ5LTYuNlpNMTcsMjUuMTJoMy43NWExNS4yLDE1LjIsMCwwLDAsMS43Miw2LjZBNy41Miw3LjUyLDAsMCwxLDE3LDI1LjEyWm05LjQ4LDYuNmExNS4xOSwxNS4xOSwwLDAsMCwxLjc0LTYuNkgzMkE3LjUsNy41LDAsMCwxLDI2LjUxLDMxLjcyWiIvPjxyZWN0IGlkPSJfVHJhbnNwYXJlbnRfUmVjdGFuZ2xlXyIgZGF0YS1uYW1lPSIgVHJhbnNwYXJlbnQgUmVjdGFuZ2xlICIgY2xhc3M9ImNscy0zIiB4PSIxNC41IiB5PSIxNC41IiB3aWR0aD0iMjAiIGhlaWdodD0iMjAiLz48L3N2Zz4=;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;spacingTop=-7;",
-	reflect.TypeOf(InternetServiceTreeNode{}): "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMxMTkyZTg7fS5jbHMtMntmaWxsOiNmZmY7fS5jbHMtM3tmaWxsOm5vbmU7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIvPjxwYXRoIGNsYXNzPSJjbHMtMiIgZD0iTTMxLjg3LDIwLjc1YTYuMjUsNi4yNSwwLDAsMC0xMi4yNi4wOCw0LjY4LDQuNjgsMCwwLDAsLjgzLDkuMjloLjk0VjI4Ljg3aC0uOTRBMy40MywzLjQzLDAsMCwxLDIwLjIsMjJsLjUyLDAsLjA2LS41MmE1LDUsMCwwLDEsOS44MS0uNzFaIi8+PHJlY3QgY2xhc3M9ImNscy0zIiB4PSIxNC41IiB5PSIxNC41IiB3aWR0aD0iMjAiIGhlaWdodD0iMjAiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0zMS4zNywyOS41YTEuODQsMS44NCwwLDAsMC0xLjIuNDVsLTIuNTYtMS41NGMwLS4wNSwwLS4xLDAtLjE2czAtLjExLDAtLjE2bDIuNTYtMS41NGExLjg2LDEuODYsMCwwLDAsMS4yLjQ1LDEuODgsMS44OCwwLDEsMC0xLjg3LTEuODgsMS40MiwxLjQyLDAsMCwwLDAsLjM2bC0yLjQ1LDEuNDZhMS44NiwxLjg2LDAsMCwwLTEuMzQtLjU3LDEuODgsMS44OCwwLDAsMCwwLDMuNzUsMS44NSwxLjg1LDAsMCwwLDEuMzQtLjU2TDI5LjU0LDMxYTEuNDUsMS40NSwwLDAsMCwwLC4zNSwxLjg4LDEuODgsMCwxLDAsMS44Ny0xLjg3Wm0wLTVhLjYzLjYzLDAsMSwxLS42Mi42MkEuNjMuNjMsMCwwLDEsMzEuMzcsMjQuNVptLTUuNjIsNC4zN2EuNjMuNjMsMCwwLDEtLjYzLS42Mi42NC42NCwwLDAsMSwuNjMtLjYzLjYzLjYzLDAsMCwxLC42Mi42M0EuNjIuNjIsMCwwLDEsMjUuNzUsMjguODdaTTMxLjM3LDMyYS42My42MywwLDEsMSwuNjMtLjYzQS42My42MywwLDAsMSwzMS4zNywzMloiLz48L3N2Zz4=;fontSize=14;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;spacingTop=-6;",
-	reflect.TypeOf(LogicalLineTreeNode{}):     "html=1;verticalAlign=middle;startArrow=oval;startFill=1;endArrow=oval;startSize=6;strokeColor=#000000;align=center;dashed=1;strokeWidth=2;horizontal=1;labelPosition=center;verticalLabelPosition=middle;endFill=1;rounded=0;",
+	reflect.TypeOf(PublicNetworkTreeNode{}):      "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#1192E8;fillColor=none;",
+	reflect.TypeOf(CloudTreeNode{}):              "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#1192E8;fillColor=none;",
+	reflect.TypeOf(VpcTreeNode{}):                "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#1192E8;fillColor=none;",
+	reflect.TypeOf(ZoneTreeNode{}):               "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#878d96;fillColor=none;",
+	reflect.TypeOf(PartialSGTreeNode{}):          "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;fillColor=none;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#FA4D56;strokeWidth=1;",
+	reflect.TypeOf(SubnetTreeNode{}):             "rounded=0;whiteSpace=wrap;html=1;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;spacingBottom=-28;spacingTop=0;labelPosition=-100;verticalLabelPosition=top;align=center;verticalAlign=bottom;spacingLeft=9;spacing=0;expand=0;recursiveResize=0;spacingRight=0;container=1;collapsible=0;strokeColor=#1192E8;fillColor=none;",
+	reflect.TypeOf(GroupSquareTreeNode{}):        groupSquareStyle,
+	reflect.TypeOf(GroupSubnetsSquareTreeNode{}): groupSquareStyle,
+	reflect.TypeOf(NITreeNode{}):                 vsiStyle,
+	reflect.TypeOf(VsiTreeNode{}):                vsiStyle,
+	reflect.TypeOf(ResIPTreeNode{}):              vpeStyle,
+	reflect.TypeOf(VpeTreeNode{}):                vpeStyle,
+	reflect.TypeOf(GroupPointTreeNode{}):         "ellipse;whiteSpace=wrap;html=1;aspect=fixed;",
+	reflect.TypeOf(UserTreeNode{}):               "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOm5vbmU7fS5jbHMtMntmaWxsOiNmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IHg9IjAuNSIgeT0iMC41IiB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHJ4PSIyNCIvPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMTQuNSIgeT0iMTQuNSIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIi8+PHBhdGggaWQ9IkZpbGwtMyIgY2xhc3M9ImNscy0yIiBkPSJNMzAuOCwzMy44N0gyOVYyOS41OUEyLjYzLDIuNjMsMCwwLDAsMjYuMywyN0gyMi43QTIuNjMsMi42MywwLDAsMCwyMCwyOS41OXY0LjI4SDE4LjJWMjkuNTlhNC40MSw0LjQxLDAsMCwxLDQuNS00LjI4aDMuNmE0LjQxLDQuNDEsMCwwLDEsNC41LDQuMjhaIi8+PHBhdGggaWQ9IkZpbGwtNSIgY2xhc3M9ImNscy0yIiBkPSJNMjQuNSwxNS4wNUE0LjM5LDQuMzksMCwwLDAsMjAsMTkuMzNhNC41MSw0LjUxLDAsMCwwLDksMCw0LjM5LDQuMzksMCwwLDAtNC41LTQuMjhtMCwxLjcxYTIuNTcsMi41NywwLDEsMS0yLjcsMi41NywyLjY0LDIuNjQsMCwwLDEsMi43LTIuNTciLz48L3N2Zz4=;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;spacingTop=-7;",
+	reflect.TypeOf(GatewayTreeNode{}):            "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMxMTkyZTg7fS5jbHMtMntmaWxsOiNmZmY7fS5jbHMtM3tmaWxsOm5vbmU7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgcng9IjgiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0zMy41MSwyNS4zOGExLjIzLDEuMjMsMCwwLDAsMC0xLjc2TDI5Ljg5LDIwbDEuODEtMS43OWExLjI1LDEuMjUsMCwxLDAtLjU4LTIuMDksMS4yMiwxLjIyLDAsMCwwLS4zMiwxLjIyTDI5LDE5LjEybC0zLjYzLTMuNjNhMS4yMywxLjIzLDAsMCwwLTEuNzYsMEwyMCwxOS4xMWwtMS43OS0xLjgyYTEuMjQsMS4yNCwwLDEsMC0yLjA5LjU5LDEuMjIsMS4yMiwwLDAsMCwxLjIyLjMyTDE5LjEyLDIwbC0zLjYzLDMuNjNhMS4yMywxLjIzLDAsMCwwLDAsMS43NkwxOS4xMiwyOSwxNy4zNCwzMC44YTEuMjIsMS4yMiwwLDAsMC0xLjIyLjMyLDEuMjQsMS4yNCwwLDEsMCwyLjA5LjU5TDIwLDI5Ljg5bDMuNjIsMy42MmExLjIzLDEuMjMsMCwwLDAsMS43NiwwTDI5LDI5Ljg4bDEuNzksMS43OGExLjIyLDEuMjIsMCwwLDAsLjMyLDEuMjIsMS4yNCwxLjI0LDAsMSwwLC41OC0yLjA5TDI5Ljg5LDI5Wm0tOSw3LjI0TDE2LjM4LDI0LjVsOC4xMi04LjEyLDguMTIsOC4xMloiLz48cmVjdCBjbGFzcz0iY2xzLTMiIHg9IjE0LjUiIHk9IjE0LjUiIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIvPjxwYXRoIGNsYXNzPSJjbHMtMiIgZD0iTTI2LjM4LDIzLjI1SDIzLjI1VjIyYTEuMjUsMS4yNSwwLDAsMSwyLjUsMEgyN2EyLjUsMi41LDAsMCwwLTUsMHYxLjQyYTEuMjYsMS4yNiwwLDAsMC0uNjIsMS4wOHYzLjEyYTEuMjYsMS4yNiwwLDAsMCwxLjI0LDEuMjZoMy43NmExLjI2LDEuMjYsMCwwLDAsMS4yNC0xLjI2VjI0LjVBMS4yNSwxLjI1LDAsMCwwLDI2LjM4LDIzLjI1Wm0wLDQuMzdIMjIuNjJWMjQuNWgzLjc2WiIvPjwvc3ZnPg==;fontSize=14;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;spacingTop=-7;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;",
+	reflect.TypeOf(InternetTreeNode{}):           "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMxMTkyZTg7fS5jbHMtMntmaWxsOiNmZmY7fS5jbHMtM3tmaWxsOm5vbmU7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgcng9IjgiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0yNC41LDE1Ljc1YTguNzUsOC43NSwwLDEsMCw4Ljc1LDguNzVBOC43NSw4Ljc1LDAsMCwwLDI0LjUsMTUuNzVaTTMyLDIzLjg4SDI4LjI1YTE1LjE5LDE1LjE5LDAsMCwwLTEuNzQtNi42QTcuNSw3LjUsMCwwLDEsMzIsMjMuODhaTTI0LjUsMzJoLS40MkExMy43MiwxMy43MiwwLDAsMSwyMiwyNS4xMmg1QTEzLjYzLDEzLjYzLDAsMCwxLDI0Ljk0LDMyWk0yMiwyMy44OEExMy42MywxMy42MywwLDAsMSwyNC4wNiwxN2EzLjkzLDMuOTMsMCwwLDEsLjg0LDBBMTMuNjQsMTMuNjQsMCwwLDEsMjcsMjMuODhabS40OC02LjZhMTUuMTgsMTUuMTgsMCwwLDAtMS43Myw2LjZIMTdhNy41LDcuNSwwLDAsMSw1LjQ5LTYuNlpNMTcsMjUuMTJoMy43NWExNS4yLDE1LjIsMCwwLDAsMS43Miw2LjZBNy41Miw3LjUyLDAsMCwxLDE3LDI1LjEyWm05LjQ4LDYuNmExNS4xOSwxNS4xOSwwLDAsMCwxLjc0LTYuNkgzMkE3LjUsNy41LDAsMCwxLDI2LjUxLDMxLjcyWiIvPjxyZWN0IGlkPSJfVHJhbnNwYXJlbnRfUmVjdGFuZ2xlXyIgZGF0YS1uYW1lPSIgVHJhbnNwYXJlbnQgUmVjdGFuZ2xlICIgY2xhc3M9ImNscy0zIiB4PSIxNC41IiB5PSIxNC41IiB3aWR0aD0iMjAiIGhlaWdodD0iMjAiLz48L3N2Zz4=;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;fontSize=14;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;spacingTop=-7;",
+	reflect.TypeOf(InternetServiceTreeNode{}):    "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMxMTkyZTg7fS5jbHMtMntmaWxsOiNmZmY7fS5jbHMtM3tmaWxsOm5vbmU7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIvPjxwYXRoIGNsYXNzPSJjbHMtMiIgZD0iTTMxLjg3LDIwLjc1YTYuMjUsNi4yNSwwLDAsMC0xMi4yNi4wOCw0LjY4LDQuNjgsMCwwLDAsLjgzLDkuMjloLjk0VjI4Ljg3aC0uOTRBMy40MywzLjQzLDAsMCwxLDIwLjIsMjJsLjUyLDAsLjA2LS41MmE1LDUsMCwwLDEsOS44MS0uNzFaIi8+PHJlY3QgY2xhc3M9ImNscy0zIiB4PSIxNC41IiB5PSIxNC41IiB3aWR0aD0iMjAiIGhlaWdodD0iMjAiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0zMS4zNywyOS41YTEuODQsMS44NCwwLDAsMC0xLjIuNDVsLTIuNTYtMS41NGMwLS4wNSwwLS4xLDAtLjE2czAtLjExLDAtLjE2bDIuNTYtMS41NGExLjg2LDEuODYsMCwwLDAsMS4yLjQ1LDEuODgsMS44OCwwLDEsMC0xLjg3LTEuODgsMS40MiwxLjQyLDAsMCwwLDAsLjM2bC0yLjQ1LDEuNDZhMS44NiwxLjg2LDAsMCwwLTEuMzQtLjU3LDEuODgsMS44OCwwLDAsMCwwLDMuNzUsMS44NSwxLjg1LDAsMCwwLDEuMzQtLjU2TDI5LjU0LDMxYTEuNDUsMS40NSwwLDAsMCwwLC4zNSwxLjg4LDEuODgsMCwxLDAsMS44Ny0xLjg3Wm0wLTVhLjYzLjYzLDAsMSwxLS42Mi42MkEuNjMuNjMsMCwwLDEsMzEuMzcsMjQuNVptLTUuNjIsNC4zN2EuNjMuNjMsMCwwLDEtLjYzLS42Mi42NC42NCwwLDAsMSwuNjMtLjYzLjYzLjYzLDAsMCwxLC42Mi42M0EuNjIuNjIsMCwwLDEsMjUuNzUsMjguODdaTTMxLjM3LDMyYS42My42MywwLDEsMSwuNjMtLjYzQS42My42MywwLDAsMSwzMS4zNywzMloiLz48L3N2Zz4=;fontSize=14;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;spacingTop=-6;",
+	reflect.TypeOf(LogicalLineTreeNode{}):        "html=1;verticalAlign=middle;startArrow=oval;startFill=1;endArrow=oval;startSize=6;strokeColor=#000000;align=center;dashed=1;strokeWidth=2;horizontal=1;labelPosition=center;verticalLabelPosition=middle;endFill=1;rounded=0;",
 	// reflect.TypeOf(EndPointTreeNode{}):        "shape=image;aspect=fixed;image=data:image/svg+xml,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OSA0OSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMxMTkyZTg7fS5jbHMtMntmaWxsOiNmZmY7fS5jbHMtM3tmaWxsOm5vbmU7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIvPjxwYXRoIGlkPSJ2cGNfZ3JhZGllbnRfYm90dG9tIiBkYXRhLW5hbWU9InZwYyBncmFkaWVudCBib3R0b20iIGNsYXNzPSJjbHMtMiIgZD0iTTI3LDMxLjM4SDE4Ljg4YTEuMjcsMS4yNywwLDAsMS0xLjI2LTEuMjVWMjJoMS4yNnY4LjEzSDI3WiIvPjxwYXRoIGlkPSJ2cGNfZ3JhZGllbnRfdG9wIiBkYXRhLW5hbWU9InZwYyBncmFkaWVudCB0b3AiIGNsYXNzPSJjbHMtMiIgZD0iTTMwLjEyLDI3aDEuMjZWMTguODhhMS4yNiwxLjI2LDAsMCwwLTEuMjYtMS4yNUgyMnYxLjI1aDguMTJaIi8+PHBhdGggaWQ9ImVuZHBvaW50cyIgY2xhc3M9ImNscy0yIiBkPSJNMjkuMTIsMjguMjVsLTIuNS0yLjVBMi4yNiwyLjI2LDAsMCwwLDI3LDI0LjUsMi41MSwyLjUxLDAsMCwwLDI0LjUsMjJhMi4xOSwyLjE5LDAsMCwwLTEuMjUuMzhsLTIuNS0yLjVWMTUuNzVoLTV2NWg0LjEzbDIuNSwyLjVBMi4yNiwyLjI2LDAsMCwwLDIyLDI0LjUsMi41MSwyLjUxLDAsMCwwLDI0LjUsMjdhMi4yNiwyLjI2LDAsMCwwLDEuMjUtLjM4bDIuNSwyLjV2NC4xM2g1di01Wk0xOS41LDE5LjVIMTdWMTdoMi41Wm01LDYuMjVhMS4yNSwxLjI1LDAsMSwxLDEuMjUtMS4yNUExLjI1LDEuMjUsMCwwLDEsMjQuNSwyNS43NVpNMzIsMzJIMjkuNVYyOS41SDMyWiIvPjxyZWN0IGNsYXNzPSJjbHMtMyIgeD0iMTQuNSIgeT0iMTQuNSIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIi8+PC9zdmc+;fontSize=14;fontFamily=IBM Plex Sans;fontSource=fonts%2FIBMPlexSans-Regular.woff;spacingTop=-7;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;",
 }
 var miniStyles = map[reflect.Type]string{
@@ -96,10 +98,10 @@ func connectivityStyle(con *ConnectivityTreeNode) string {
 	if con.directed {
 		endArrow = errorEndEdge
 	}
-	if con.Src().IsGroupingPoint() && !con.Src().(*GroupPointTreeNode).hasShownSquare() {
+	if con.Src().IsIcon() && con.Src().(IconTreeNodeInterface).IsGroupingPoint() && !con.Src().(*GroupPointTreeNode).hasShownSquare() {
 		startArrow = noneEndEdge
 	}
-	if con.Dst().IsGroupingPoint() && !con.Dst().(*GroupPointTreeNode).hasShownSquare() {
+	if con.IsIcon() && con.Dst().(IconTreeNodeInterface).IsGroupingPoint() && !con.Dst().(*GroupPointTreeNode).hasShownSquare() {
 		endArrow = noneEndEdge
 	}
 	if con.router != nil {
diff --git a/pkg/drawio/subnetsLayout.go b/pkg/drawio/subnetsLayout.go
new file mode 100644
index 000000000..e509f76b7
--- /dev/null
+++ b/pkg/drawio/subnetsLayout.go
@@ -0,0 +1,773 @@
+package drawio
+
+import (
+	"maps"
+	"sort"
+
+	"github.com/np-guard/vpc-network-config-analyzer/pkg/common"
+)
+
+// //////////////////////////////////////////////////////////////////////////////////////////////
+// subnetsLayout struct is a struct for layout subnets when the network is in subnet mode.
+// the input of the layout algorithm is the groups of subnets,
+// and the output is a matrix of subnets, representing the subnets location on the drawio canvas.
+// the location of the groups squares is determinate later by the location of the subnets.
+// the layout algorithm should make sure that all the group subnets, and only the group subnets should be inside the group squares.
+// since some of the subnets can be in the more than one group, the solution is not trivial.
+// and is some cases we must split a group to smaller groups, and delete the original group.
+//
+// the algorithm uses the concept of miniGroup:
+// a miniGroup is a set of subnets. all the subnets in a miniGroup are sharing same groups, and the same zone.
+// all the subnet in the miniGroup will be alongside each other, so instead of layout the subnets, we layout miniGroups
+// a group is not a set of subnets, but a set of miniGroups
+//
+// the main phases of the layout algorithm:
+// 1. sort the subnets to miniGroups, sort the miniGroups to their groups:
+//   the output is a list of groups, each group has a list of miniGroups
+// 2. create a tree of groups - the children of a group are set of groups that do not intersect, and hold only miniGroups of the group
+//    (in this phase new groups are created, by splitting  groups to smaller groups)
+// 3. layout the groups
+// 4. create new treeNodes of the new groupSquares and new connectors
+// //////////////////////////////////////////////////////////////////////////////////////////////
+
+type subnetSet = common.GenericSet[TreeNodeInterface]
+type groupTnSet = common.GenericSet[TreeNodeInterface]
+type groupSet = common.GenericSet[*groupDataS]
+type miniGroupSet = common.GenericSet[*miniGroupDataS]
+type setAsKey = common.SetAsKey
+
+/////////////////////////////////////////////////////////////////
+
+type miniGroupDataS struct {
+	subnets subnetSet
+	zone    TreeNodeInterface
+	located bool
+}
+
+// ///////////////////////////////////////////////////////////////////
+// groupsDataS is the struct representing a group.
+// they are creating if the first step, when sorting the miniGroups to groups.
+// some more are created when groups are split to smaller group
+// /////////////////////////////////////////////////////////////////
+type groupDataS struct {
+	// miniGroups - set of the miniGroups of the group
+	// subnets - set of all the subnets  of the group
+	miniGroups miniGroupSet
+	subnets    subnetSet
+	// treeNode - the relevant treeNode of the group, for most groups we already have a treeNode, for new groups, we create a new treeNode
+	treeNode TreeNodeInterface
+	// children - the children in the tree of groups
+	children groupSet
+	// toSplitGroups - toSplitGroups are all the subgroups of the group that will be split. these groups will not be te the groups tree
+	toSplitGroups groupSet
+	// splitFrom - if a group was created during splitting, splitFrom is the groups that the group was split from
+	// splitTo - if a group was split, splitTo is the groups that the group was split to
+	splitFrom groupSet
+	splitTo   groupSet
+}
+
+// fakeSubnet and fakeMiniGroup are used as space holders in the matrixes
+var fakeSubnet TreeNodeInterface = &SubnetTreeNode{}
+var fakeMiniGroup *miniGroupDataS = &miniGroupDataS{subnets: subnetSet{}}
+
+func newGroupDataS(miniGroups miniGroupSet, tn TreeNodeInterface) *groupDataS {
+	subnets := subnetSet{}
+	for miniGroup := range miniGroups {
+		for subnet := range miniGroup.subnets {
+			subnets[subnet] = true
+		}
+	}
+	return &groupDataS{
+		miniGroups:    miniGroups,
+		subnets:       subnets,
+		treeNode:      tn,
+		children:      groupSet{},
+		toSplitGroups: groupSet{},
+		splitFrom:     groupSet{},
+		splitTo:       groupSet{},
+	}
+}
+
+// /////////////////////////////////////////////////////////////////
+func (group *groupDataS) isInnerGroup(subGroup *groupDataS) bool {
+	if len(group.miniGroups) == len(subGroup.miniGroups) {
+		return false
+	}
+	for mg := range subGroup.miniGroups {
+		if !group.miniGroups[mg] {
+			return false
+		}
+	}
+	return true
+}
+
+func (group *groupDataS) getVpc() *VpcTreeNode {
+	if group.treeNode != nil {
+		return group.treeNode.Parent().(*VpcTreeNode)
+	}
+	for g := range group.splitFrom {
+		return g.getVpc()
+	}
+	return nil
+}
+
+func (group *groupDataS) reunion() {
+	for gr := range group.splitTo {
+		delete(gr.splitFrom, group)
+	}
+	group.splitTo = groupSet{}
+}
+
+// ////////////////////////////////////////////////////////////////////////
+type indexes struct {
+	row, col int
+}
+type subnetsLayout struct {
+	network           SquareTreeNodeInterface
+	groups            []*groupDataS
+	miniGroups        miniGroupSet
+	miniGroupsMatrix  [][]*miniGroupDataS
+	subnetMatrix      [][]TreeNodeInterface
+	subnetsIndexes    map[TreeNodeInterface]indexes
+	zonesCol          map[TreeNodeInterface]int
+	treeNodesToGroups map[TreeNodeInterface]*groupDataS
+	topFakeGroup      *groupDataS
+}
+
+func newSubnetsLayout(network SquareTreeNodeInterface) *subnetsLayout {
+	return &subnetsLayout{
+		network:           network,
+		miniGroups:        miniGroupSet{},
+		subnetsIndexes:    map[TreeNodeInterface]indexes{},
+		zonesCol:          map[TreeNodeInterface]int{},
+		treeNodesToGroups: map[TreeNodeInterface]*groupDataS{},
+	}
+}
+
+// layout() - the top function, with the four steps of the algorithm:
+func (ly *subnetsLayout) layout() {
+	// create a list of groups and miniGroups:
+	ly.createGroupsDataS()
+	ly.topFakeGroup = newGroupDataS(ly.miniGroups, nil)
+	// create the group tree:
+	ly.createGroupSubTree(ly.topFakeGroup)
+	// layout the groups:
+	ly.layoutGroups()
+	// create the new treeNodes:
+	ly.createNewTreeNodes()
+}
+
+// //////////////////////////////////////////////////////////
+// createGroupsDataS() - sorting the subnets to miniGroups and groups
+// the output is a list of miniGroups and list of groups. (each group holds a set of miniGroups)
+// a miniGroup is a set of subnets from the same zone, each subnet in the set are at the same set of groups.
+// phases:
+// 1. sort subnets to groups (map: subnet -> set of groups)
+// 2. sort sets of groups to set of subnets (map: group set -> subnet Set)
+// 3. create miniGroups
+// 4. create the groups
+func (ly *subnetsLayout) createGroupsDataS() {
+	subnetToGroups := ly.sortSubnets()
+	groupSetToSubnetSet := sortSubnetsByZoneAndGroups(subnetToGroups)
+	ly.createMiniGroups(groupSetToSubnetSet)
+	ly.createGroups(subnetToGroups)
+	sort.Slice(ly.groups, func(i, j int) bool {
+		return len(ly.groups[i].miniGroups) > len(ly.groups[j].miniGroups)
+	})
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+// createGroupSubTree() is a recursive method to create the tree of groups.
+// the root of the tree is a fake group that holds all the miniGroups
+// the creation of the tree might involve splitting groups to smaller new groups.
+// (the split group is not in the tree)
+// for example, consider the following list of groups:
+// (m1,m2,m3), (m2,m3,m4,m5), (m4,m5,m6), (m4, m5)
+// the algorithm will split (m2,m3,m4,m5). it will create a new group (m2,m3)
+// the tree will look like:
+// (m1,m2,m3,m4,m5,m6) -> (m1,m2,m3), (m4,m5,m6)
+// (m1,m2,m3) -> (m2,m3)
+// (m4,m5,m6) -> (m4,m5)
+
+// the main challenge of createGroupSubTree() is to choose which groups to split
+// the candidates are all the subgroups of the group, which are not a subgroup of other subgroup (aka nonSplitNotInnerGroups).
+// each iteration of the loop:
+//     - updates nonSplitNotInnerGroups,
+//     - choose a sub group to split,
+//     - continue the loop till all nonSplitNotInnerGroups do not intersect each other.
+// the subgroups that remained at nonSplitNotInnerGroups, are set to be the children of the group
+// the sub groups that was chosen to be split, are split to new groups at createGroupsFromSplitGroups()
+
+func (ly *subnetsLayout) createGroupSubTree(group *groupDataS) {
+	nonSplitGroups := ly.innerGroupsOfAGroup(group)
+	for innerGroup := range nonSplitGroups {
+		if len(innerGroup.splitTo) > 0 {
+			delete(nonSplitGroups, innerGroup)
+		}
+	}
+	for {
+		nonSplitNotInnerGroups := nonInnerGroups(nonSplitGroups)
+		intersectGroups := intersectGroups(nonSplitNotInnerGroups)
+		mostSharedGroup := chooseGroupToSplit(intersectGroups)
+		if mostSharedGroup == nil {
+			group.children = nonSplitNotInnerGroups
+			break
+		}
+		group.toSplitGroups[mostSharedGroup] = true
+		delete(nonSplitGroups, mostSharedGroup)
+	}
+
+	if len(group.toSplitGroups) > 0 {
+		ly.createGroupsFromSplitGroups(group)
+	}
+	for topInnerGroup := range group.children {
+		ly.createGroupSubTree(topInnerGroup)
+	}
+}
+
+// ///////////////////////////////////////////////////////////////////////////
+// ///////////////////////////////////////////////////////////////////////////
+// the output of the layout algorithm is a matrix of miniGroups, later converted to a matrix of subnets
+// each column is dedicated to a different zone
+// layoutGroups() has two main steps:
+// 1. calc the zone order - try to put zones that share the same group next to each other
+// 2. layout groups - a recursive call to set the miniGroups locations on the matrix
+// see documentation of calcZoneOrder() and layoutGroup()
+func (ly *subnetsLayout) layoutGroups() {
+	ly.calcZoneOrder()
+	ly.createMatrixes()
+	ly.layoutGroup(ly.topFakeGroup, 0)
+	ly.setSubnetsMatrix()
+}
+
+// ///////////////////////////////////////////////////////////////////////////
+// ///////////////////////////////////////////////////////////////////////////
+// createNewTreeNodes() do the follows:
+// check if a split group can somehow be shown on the canvas, if not, mark them as doNotShowOnDrawio
+// created treeNodes for the groups that was created during the splitting
+// creates new connections that replace the connections of groups that was split
+func (ly *subnetsLayout) createNewTreeNodes() {
+	ly.doNotShowSplitGroups()
+	ly.createNewGroupsTreeNodes()
+	ly.createNewLinesTreeNodes()
+}
+
+// ////////////////////////////////////////////////////////////////////////
+// ////////////////////////////////////////////////////////////////////////
+
+func (ly *subnetsLayout) sortSubnets() map[TreeNodeInterface]groupTnSet {
+	subnetToGroups := map[TreeNodeInterface]groupTnSet{}
+	for group := range ly.groupsTreeNodes() {
+		for _, subnet := range group.(*GroupSubnetsSquareTreeNode).groupedSubnets {
+			if _, ok := subnetToGroups[subnet]; !ok {
+				subnetToGroups[subnet] = groupTnSet{}
+			}
+			subnetToGroups[subnet][group] = true
+		}
+	}
+	return subnetToGroups
+}
+
+func (ly *subnetsLayout) groupsTreeNodes() groupTnSet {
+	allGroups := groupTnSet{}
+	for _, tn := range getAllNodes(ly.network) {
+		if tn.IsSquare() && tn.(SquareTreeNodeInterface).IsGroupSubnetsSquare() {
+			allGroups[tn] = true
+		}
+	}
+	return allGroups
+}
+
+func sortSubnetsByZoneAndGroups(subnetToGroups map[TreeNodeInterface]groupTnSet) map[setAsKey]map[TreeNodeInterface]subnetSet {
+	groupSetToSubnetSet := map[setAsKey]map[TreeNodeInterface]subnetSet{}
+	for subnet, groups := range subnetToGroups {
+		if _, ok := groupSetToSubnetSet[groups.AsKey()]; !ok {
+			groupSetToSubnetSet[groups.AsKey()] = map[TreeNodeInterface]subnetSet{}
+		}
+		zone := subnet.Parent()
+		if _, ok := groupSetToSubnetSet[groups.AsKey()][zone]; !ok {
+			groupSetToSubnetSet[groups.AsKey()][subnet.Parent()] = subnetSet{}
+		}
+		groupSetToSubnetSet[groups.AsKey()][zone][subnet] = true
+	}
+	return groupSetToSubnetSet
+}
+
+func (ly *subnetsLayout) createMiniGroups(groupSetToSubnetSet map[setAsKey]map[TreeNodeInterface]subnetSet) {
+	for _, zoneMiniGroup := range groupSetToSubnetSet {
+		for zone, miniGroup := range zoneMiniGroup {
+			miniGroupData := miniGroupDataS{subnets: miniGroup, zone: zone}
+			ly.miniGroups[&miniGroupData] = true
+		}
+	}
+}
+func (ly *subnetsLayout) createGroups(subnetToGroups map[TreeNodeInterface]groupTnSet) {
+	groupToMiniGroups := map[TreeNodeInterface]miniGroupSet{}
+	for miniGroup := range ly.miniGroups {
+		for subnet := range miniGroup.subnets {
+			for group := range subnetToGroups[subnet] {
+				if _, ok := groupToMiniGroups[group]; !ok {
+					groupToMiniGroups[group] = miniGroupSet{}
+				}
+				groupToMiniGroups[group][miniGroup] = true
+			}
+		}
+	}
+	for groupTn, miniGroups := range groupToMiniGroups {
+		groupData := newGroupDataS(miniGroups, groupTn)
+		ly.treeNodesToGroups[groupTn] = groupData
+		ly.groups = append(ly.groups, groupData)
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+func (ly *subnetsLayout) innerGroupsOfAGroup(group *groupDataS) groupSet {
+	allInnerGroups := groupSet{}
+	for _, group1 := range ly.groups {
+		if group.isInnerGroup(group1) {
+			allInnerGroups[group1] = true
+		}
+	}
+	return allInnerGroups
+}
+
+func nonInnerGroups(groups groupSet) groupSet {
+	nonInnerGroups := maps.Clone(groups)
+	for group1 := range groups {
+		for group2 := range groups {
+			if group2.isInnerGroup(group1) {
+				delete(nonInnerGroups, group1)
+			}
+		}
+	}
+	return nonInnerGroups
+}
+
+func intersectGroups(groups groupSet) map[*groupDataS]groupSet {
+	intersectGroups := map[*groupDataS]groupSet{}
+
+	for group1 := range groups {
+		for group2 := range groups {
+			if group1 != group2 {
+				if group1.miniGroups.IsIntersect(group2.miniGroups) {
+					if _, ok := intersectGroups[group1]; !ok {
+						intersectGroups[group1] = groupSet{}
+					}
+					intersectGroups[group1][group2] = true
+				}
+			}
+		}
+	}
+	return intersectGroups
+}
+
+func chooseGroupToSplit(intersectGroups map[*groupDataS]groupSet) *groupDataS {
+	bestSharingScore := 0
+	var mostSharedGroup *groupDataS
+	for sharedGroup, sharedGroups := range intersectGroups {
+		if len(sharedGroups) > bestSharingScore ||
+			(len(sharedGroups) == bestSharingScore && len(sharedGroup.miniGroups) < len(mostSharedGroup.miniGroups)) {
+			bestSharingScore = len(sharedGroups)
+			mostSharedGroup = sharedGroup
+		}
+	}
+	return mostSharedGroup
+}
+
+// ////////////////////////////////////////////////////////////////////////
+func (ly *subnetsLayout) createGroupsFromSplitGroups(group *groupDataS) {
+	miniGroupToGroupSet := ly.sortSplitMiniGroupsByGroupSet(group)
+	groupSetToMiniGroups, keysToGroupSet := groupSetToMiniGroups(miniGroupToGroupSet)
+
+	for groupsKey, miniGroups := range groupSetToMiniGroups {
+		groups := keysToGroupSet[groupsKey]
+		ly.newGroupFromSplitMiniGroups(group, miniGroups, groups)
+	}
+}
+
+func (ly *subnetsLayout) sortSplitMiniGroupsByGroupSet(group *groupDataS) map[*miniGroupDataS]groupSet {
+	splitMiniGroups := miniGroupSet{}
+	for splitGroup := range group.toSplitGroups {
+		for mn := range splitGroup.miniGroups {
+			splitMiniGroups[mn] = true
+		}
+	}
+	miniGroupToGroupSet := map[*miniGroupDataS]groupSet{}
+	for group := range ly.innerGroupsOfAGroup(group) {
+		for miniGroup := range group.miniGroups {
+			if splitMiniGroups[miniGroup] {
+				if _, ok := miniGroupToGroupSet[miniGroup]; !ok {
+					miniGroupToGroupSet[miniGroup] = groupSet{}
+				}
+				miniGroupToGroupSet[miniGroup][group] = true
+			}
+		}
+	}
+	return miniGroupToGroupSet
+}
+
+func groupSetToMiniGroups(miniGroupToGroupSet map[*miniGroupDataS]groupSet) (
+	groupSetToMiniGroups map[setAsKey]miniGroupSet,
+	keysToGroupSet map[setAsKey]groupSet) {
+	groupSetToMiniGroups = map[setAsKey]miniGroupSet{}
+	keysToGroupSet = map[setAsKey]groupSet{}
+	for miniGroup, groupSet := range miniGroupToGroupSet {
+		if _, ok := groupSetToMiniGroups[groupSet.AsKey()]; !ok {
+			groupSetToMiniGroups[groupSet.AsKey()] = miniGroupSet{}
+		}
+		groupSetToMiniGroups[groupSet.AsKey()][miniGroup] = true
+		keysToGroupSet[groupSet.AsKey()] = groupSet
+	}
+	return groupSetToMiniGroups, keysToGroupSet
+}
+
+func (ly *subnetsLayout) newGroupFromSplitMiniGroups(group *groupDataS, miniGroups miniGroupSet, groups groupSet) {
+	var newGroup *groupDataS
+	for _, gr := range ly.groups {
+		if maps.Equal(gr.miniGroups, miniGroups) {
+			newGroup = gr
+			break
+		}
+	}
+	if newGroup == nil {
+		newGroup = newGroupDataS(miniGroups, nil)
+		ly.groups = append(ly.groups, newGroup)
+
+		inTopGroup := false
+		for topGroup := range group.children {
+			if groups[topGroup] {
+				inTopGroup = true
+				break
+			}
+		}
+		if !inTopGroup {
+			group.children[newGroup] = true
+		}
+	}
+	for splitGroup := range group.toSplitGroups {
+		if groups[splitGroup] {
+			splitGroup.splitTo[newGroup] = true
+			newGroup.splitFrom[splitGroup] = true
+		}
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+// calcZoneOrder()  set the order of the zones in the matrix (every zone has one column)
+// the output of the is a list of zones
+// 1. for every pair of zones, calc the score of the pair
+// 2. start a slice of a size of two, with the pair that has the highest score
+// 3. in a loop  - choose the best pair (z1,z2) such as z1 not in the slice and z2 in beginning/end of the slice. add z1 to the slice
+
+func (ly *subnetsLayout) calcZoneOrder() {
+	zonesScores := ly.calcZonePairScores()
+	zoneOrder := []TreeNodeInterface{}
+	for len(zonesScores) > 0 {
+		zoneToAdd, addToRight := chooseZoneToAdd(zonesScores, zoneOrder)
+		if addToRight == 1 {
+			zoneOrder = append(zoneOrder, zoneToAdd)
+		} else {
+			zoneOrder = append([]TreeNodeInterface{zoneToAdd}, zoneOrder...)
+		}
+
+		if len(zoneOrder) > 2 {
+			if addToRight == 1 {
+				delete(zonesScores, zoneOrder[len(zoneOrder)-2])
+			} else {
+				delete(zonesScores, zoneOrder[1])
+			}
+		}
+		for _, zScores := range zonesScores {
+			delete(zScores, zoneToAdd)
+		}
+		for z, score := range zonesScores {
+			if len(score) == 0 {
+				delete(zonesScores, z)
+			}
+		}
+	}
+	for i, z := range zoneOrder {
+		ly.zonesCol[z] = i
+	}
+	for miniGroup := range ly.miniGroups {
+		if _, ok := ly.zonesCol[miniGroup.zone]; !ok {
+			ly.zonesCol[miniGroup.zone] = len(ly.zonesCol)
+		}
+	}
+}
+
+func (ly *subnetsLayout) calcZonePairScores() map[TreeNodeInterface]map[TreeNodeInterface]int {
+	zonesScores := map[TreeNodeInterface]map[TreeNodeInterface]int{}
+	for _, group := range ly.groups {
+		for miniGroup1 := range group.miniGroups {
+			for miniGroup2 := range group.miniGroups {
+				if miniGroup1.zone != miniGroup2.zone {
+					if _, ok := zonesScores[miniGroup1.zone]; !ok {
+						zonesScores[miniGroup1.zone] = map[TreeNodeInterface]int{}
+					}
+					zonesScores[miniGroup1.zone][miniGroup2.zone] += 1
+				}
+			}
+		}
+	}
+	return zonesScores
+}
+func chooseZoneToAdd(zonesScores map[TreeNodeInterface]map[TreeNodeInterface]int,
+	zoneOrder []TreeNodeInterface) (zoneToAdd TreeNodeInterface,
+	addToRight int) {
+	addToRight = 1
+	if len(zoneOrder) > 0 {
+		zonesAtEdges := []TreeNodeInterface{zoneOrder[0], zoneOrder[len(zoneOrder)-1]}
+		bestScores := []int{0, 0}
+		zonesWithBestScore := []TreeNodeInterface{nil, nil}
+		for i, zToChoose := range zonesAtEdges {
+			for z, score := range zonesScores[zToChoose] {
+				if bestScores[i] < score {
+					bestScores[i] = score
+					zonesWithBestScore[i] = z
+				}
+			}
+		}
+		if bestScores[0] > bestScores[1] {
+			addToRight = 0
+		}
+		if bestScores[addToRight] > 0 {
+			zoneToAdd = zonesWithBestScore[addToRight]
+		}
+	}
+	if zoneToAdd == nil {
+		// in case the zoneOrder is empty. or there are no score with one of the edge zones
+		bestScore := 0
+		for z, friendsScore := range zonesScores {
+			for _, score := range friendsScore {
+				if score > bestScore {
+					bestScore = score
+					zoneToAdd = z
+				}
+			}
+		}
+	}
+	return zoneToAdd, addToRight
+}
+
+func (ly *subnetsLayout) createMatrixes() {
+	ly.miniGroupsMatrix = make([][]*miniGroupDataS, len(ly.miniGroups))
+	for i := range ly.miniGroupsMatrix {
+		ly.miniGroupsMatrix[i] = make([]*miniGroupDataS, len(ly.zonesCol))
+	}
+	ly.subnetMatrix = make([][]TreeNodeInterface, len(ly.topFakeGroup.subnets))
+	for i := range ly.subnetMatrix {
+		ly.subnetMatrix[i] = make([]TreeNodeInterface, len(ly.zonesCol))
+	}
+}
+
+// /////////////////////////////////////////////////////////////////////////
+// /////////////////////////////////////////////////////////////////////////
+// layoutGroup() is a recursive method to layout the group
+// phases:
+// 1. calc min and max Clos
+// 2. calc the first row of the group
+// 3. layout all the children
+// 4. layout the miniGroups of the group
+// 5. calc the last group.
+// 6. fill the square [firstRow-lastRow, minCol-maxCol] with a fake miniGroups, as space holders
+func (ly *subnetsLayout) layoutGroup(group *groupDataS, parentFirstRow int) {
+	childrenOrder := group.children.AsList()
+	sort.Slice(childrenOrder, func(i, j int) bool {
+		return len(childrenOrder[i].miniGroups) > len(childrenOrder[j].miniGroups)
+	})
+
+	minZoneCol, maxZoneCol, firstRow := ly.calcGroupLayoutBorders(group, parentFirstRow)
+	for _, child := range childrenOrder {
+		ly.layoutGroup(child, firstRow)
+	}
+	for miniGroup := range group.miniGroups {
+		if miniGroup.located {
+			continue
+		}
+		emptyCellRow := firstRow
+		for ly.miniGroupsMatrix[emptyCellRow][ly.zonesCol[miniGroup.zone]] != nil {
+			emptyCellRow++
+		}
+		ly.miniGroupsMatrix[emptyCellRow][ly.zonesCol[miniGroup.zone]] = miniGroup
+		miniGroup.located = true
+	}
+
+	if group != ly.topFakeGroup {
+		lastRow := parentFirstRow
+		for rIndex := lastRow; rIndex < len(ly.miniGroupsMatrix); rIndex++ {
+			for cIndex := minZoneCol; cIndex <= maxZoneCol; cIndex++ {
+				if ly.miniGroupsMatrix[rIndex][cIndex] != nil {
+					lastRow = rIndex
+				}
+			}
+		}
+		for rIndex := firstRow; rIndex <= lastRow; rIndex++ {
+			for cIndex := minZoneCol; cIndex <= maxZoneCol; cIndex++ {
+				if ly.miniGroupsMatrix[rIndex][cIndex] == nil {
+					ly.miniGroupsMatrix[rIndex][cIndex] = fakeMiniGroup
+				}
+			}
+		}
+	}
+}
+
+func (ly *subnetsLayout) calcGroupLayoutBorders(group *groupDataS, parentFirstRow int) (minZoneCol, maxZoneCol, firstRow int) {
+	minZoneCol, maxZoneCol = len(ly.zonesCol), -1
+	for mg := range group.miniGroups {
+		if minZoneCol > ly.zonesCol[mg.zone] {
+			minZoneCol = ly.zonesCol[mg.zone]
+		}
+		if maxZoneCol < ly.zonesCol[mg.zone] {
+			maxZoneCol = ly.zonesCol[mg.zone]
+		}
+	}
+	firstRow = parentFirstRow
+	for rIndex := firstRow; rIndex < len(ly.miniGroupsMatrix); rIndex++ {
+		for cIndex := minZoneCol; cIndex <= maxZoneCol; cIndex++ {
+			if ly.miniGroupsMatrix[rIndex][cIndex] != nil {
+				firstRow = rIndex + 1
+			}
+		}
+	}
+	return minZoneCol, maxZoneCol, firstRow
+}
+
+func (ly *subnetsLayout) setSubnetsMatrix() {
+	rIndex := 0
+	for _, row := range ly.miniGroupsMatrix {
+		rowSize := 0
+		for colIndex, miniGroup := range row {
+			if miniGroup == nil {
+				continue
+			}
+			i := 0
+			if rowSize < len(miniGroup.subnets) {
+				rowSize = len(miniGroup.subnets)
+			}
+			for s := range miniGroup.subnets {
+				ly.subnetMatrix[rIndex+i][colIndex] = s
+				ly.subnetsIndexes[s] = indexes{rIndex + i, colIndex}
+				i++
+			}
+		}
+		for colIndex, miniGroup := range row {
+			if miniGroup == nil {
+				continue
+			}
+			for i := 0; i < rowSize; i++ {
+				if ly.subnetMatrix[rIndex+i][colIndex] == nil {
+					ly.subnetMatrix[rIndex+i][colIndex] = fakeSubnet
+				}
+			}
+		}
+		rIndex += rowSize
+	}
+}
+
+func (ly *subnetsLayout) doNotShowSplitGroups() {
+	for _, group := range ly.groups {
+		if len(group.splitTo) != 0 && group.treeNode != nil {
+			if ly.canShowGroup(group) {
+				group.reunion()
+				continue
+			}
+			group.treeNode.SetNotShownInDrawio()
+		}
+	}
+}
+
+func (ly *subnetsLayout) createNewGroupsTreeNodes() {
+	for _, group := range ly.groups {
+		if len(group.splitTo) == 0 && group.treeNode == nil && len(group.splitFrom) > 0 {
+			subnets := []SquareTreeNodeInterface{}
+			for miniGroup := range group.miniGroups {
+				for subnet := range miniGroup.subnets {
+					subnets = append(subnets, subnet.(SquareTreeNodeInterface))
+				}
+			}
+			if len(subnets) == 1 {
+				group.treeNode = subnets[0]
+			} else {
+				group.treeNode = GroupedSubnetsSquare(group.getVpc(), subnets)
+			}
+			ly.treeNodesToGroups[group.treeNode] = group
+		}
+	}
+}
+
+func (ly *subnetsLayout) createNewLinesTreeNodes() {
+	for _, con := range getAllNodes(ly.network) {
+		if !con.IsLine() {
+			continue
+		}
+		srcTn, dstTn := con.(LineTreeNodeInterface).Src(), con.(LineTreeNodeInterface).Dst()
+		if !srcTn.NotShownInDrawio() && !dstTn.NotShownInDrawio() {
+			continue
+		}
+		allSrcTns, allDstTns := ly.allSplitTreeNodes(srcTn), ly.allSplitTreeNodes(dstTn)
+		handledSrcTns := groupTnSet{}
+		for sTn := range allSrcTns {
+			for dTn := range allDstTns {
+				switch {
+				case allSrcTns[dTn] && allDstTns[sTn] && handledSrcTns[dTn]:
+				case allSrcTns[dTn] && allDstTns[sTn]:
+					NewConnectivityLineTreeNode(ly.network, sTn, dTn, false, con.(*ConnectivityTreeNode).name)
+				default:
+					NewConnectivityLineTreeNode(ly.network, sTn, dTn, con.(*ConnectivityTreeNode).directed, con.(*ConnectivityTreeNode).name)
+				}
+			}
+			handledSrcTns[sTn] = true
+		}
+		con.SetNotShownInDrawio()
+	}
+}
+
+func (ly *subnetsLayout) allSplitTreeNodes(tn TreeNodeInterface) groupTnSet {
+	group := ly.treeNodesToGroups[tn]
+	allTns := groupTnSet{tn: true}
+	if group != nil && len(group.splitTo) > 0 {
+		allTns = groupTnSet{}
+		for gr := range group.splitTo {
+			allTns[gr.treeNode] = true
+		}
+	}
+	return allTns
+}
+
+func (ly *subnetsLayout) canShowGroup(group *groupDataS) bool {
+	firstRow, firstCol, lastRow, lastCol := len(ly.subnetMatrix), len(ly.subnetMatrix[0]), -1, -1
+	for subnet := range group.subnets {
+		subnetIndexes := ly.subnetsIndexes[subnet]
+		if firstRow > subnetIndexes.row {
+			firstRow = subnetIndexes.row
+		}
+		if firstCol > subnetIndexes.col {
+			firstCol = subnetIndexes.col
+		}
+		if lastRow < subnetIndexes.row {
+			lastRow = subnetIndexes.row
+		}
+		if lastCol < subnetIndexes.col {
+			lastCol = subnetIndexes.col
+		}
+	}
+	nSubnets := 0
+	for r := firstRow; r <= lastRow; r++ {
+		for c := firstCol; c <= lastCol; c++ {
+			subnet := ly.subnetMatrix[r][c]
+			if subnet != nil && subnet != fakeSubnet {
+				if !group.subnets[subnet] {
+					return false
+				}
+				nSubnets++
+			}
+		}
+	}
+
+	return nSubnets == len(group.subnets)
+}
diff --git a/pkg/drawio/treeNodeInterface.go b/pkg/drawio/treeNodeInterface.go
index ce9369182..b60191f62 100644
--- a/pkg/drawio/treeNodeInterface.go
+++ b/pkg/drawio/treeNodeInterface.go
@@ -52,6 +52,7 @@ type TreeNodeInterface interface {
 	setParent(TreeNodeInterface)
 	setLocation(location *Location)
 	NotShownInDrawio() bool
+	SetNotShownInDrawio()
 	setID()
 
 	/////////////////////////////
diff --git a/pkg/ibmvpc/analysis_output_test.go b/pkg/ibmvpc/analysis_output_test.go
index 83b4bcc6f..ffcfd6bb4 100644
--- a/pkg/ibmvpc/analysis_output_test.go
+++ b/pkg/ibmvpc/analysis_output_test.go
@@ -156,6 +156,18 @@ var tests = []*vpcGeneralTest{
 		useCases: []vpcmodel.OutputUseCase{vpcmodel.AllSubnets},
 		format:   vpcmodel.Text,
 	},
+	{
+		name:     "acl_testing5",
+		useCases: []vpcmodel.OutputUseCase{vpcmodel.AllSubnets},
+		grouping: true,
+		format:   vpcmodel.DRAWIO,
+	},
+	{
+		name:     "demo_with_instances",
+		useCases: []vpcmodel.OutputUseCase{vpcmodel.AllSubnets},
+		grouping: true,
+		format:   vpcmodel.DRAWIO,
+	},
 	// batch1: cover all use-cases, with text output format , no grouping
 	{
 		name: "acl_testing3",
@@ -387,20 +399,20 @@ var formatsAvoidComparison = map[vpcmodel.OutFormat]bool{vpcmodel.ARCHDRAWIO: tr
 /*var formatsAvoidOutputGeneration = map[vpcmodel.OutFormat]bool{vpcmodel.ARCHDRAWIO: true, vpcmodel.DRAWIO: true}
 
 func TestAllWithGeneration(t *testing.T) {
-	// tests is the list of tests to run
-	for testIdx := range tests {
-		tt := tests[testIdx]
-		// todo - remove the following if when drawio is stable
-		if formatsAvoidOutputGeneration[tt.format] {
-			tt.mode = outputIgnore
-		} else {
-			tt.mode = outputGeneration
-		}
+// tests is the list of tests to run
+for testIdx := range tests {
+tt := tests[testIdx]
+// todo - remove the following if when drawio is stable
+if formatsAvoidOutputGeneration[tt.format] {
+tt.mode = outputIgnore
+} else {
+tt.mode = outputGeneration
+}
 		t.Run(tt.name, func(t *testing.T) {
-			t.Parallel()
-			tt.runTest(t)
-		})
-	}
+t.Parallel()
+tt.runTest(t)
+})
+}
 	fmt.Println("done")
 }*/
 
diff --git a/pkg/ibmvpc/ibmDrawioGenerator.go b/pkg/ibmvpc/ibmDrawioGenerator.go
index 926998845..e442b7185 100644
--- a/pkg/ibmvpc/ibmDrawioGenerator.go
+++ b/pkg/ibmvpc/ibmDrawioGenerator.go
@@ -5,6 +5,22 @@ import (
 	"github.com/np-guard/vpc-network-config-analyzer/pkg/vpcmodel"
 )
 
+func (v *VPC) ShowOnSubnetMode() bool                  { return true }
+func (z *Zone) ShowOnSubnetMode() bool                 { return true }
+func (s *Subnet) ShowOnSubnetMode() bool               { return true }
+func (sgl *SecurityGroupLayer) ShowOnSubnetMode() bool { return false }
+func (nl *NaclLayer) ShowOnSubnetMode() bool           { return true }
+func (ni *NetworkInterface) ShowOnSubnetMode() bool    { return false }
+func (n *IKSNode) ShowOnSubnetMode() bool              { return false }
+func (r *ReservedIP) ShowOnSubnetMode() bool           { return false }
+func (v *Vsi) ShowOnSubnetMode() bool                  { return false }
+func (v *Vpe) ShowOnSubnetMode() bool                  { return false }
+func (pgw *PublicGateway) ShowOnSubnetMode() bool      { return true }
+func (fip *FloatingIP) ShowOnSubnetMode() bool         { return false }
+
+// todo - support TransitGateway?
+func (tgw *TransitGateway) ShowOnSubnetMode() bool { return false }
+
 // implementations of the GenerateDrawioTreeNode() for resource defined in ibmvpc:
 func (v *VPC) GenerateDrawioTreeNode(gen *vpcmodel.DrawioGenerator) drawio.TreeNodeInterface {
 	return drawio.NewVpcTreeNode(gen.Cloud(), v.Name())
diff --git a/pkg/vpcmodel/drawioGenerator.go b/pkg/vpcmodel/drawioGenerator.go
index d6e628908..ddb78bb0b 100644
--- a/pkg/vpcmodel/drawioGenerator.go
+++ b/pkg/vpcmodel/drawioGenerator.go
@@ -8,6 +8,7 @@ import (
 type DrawioResourceIntf interface {
 	GenerateDrawioTreeNode(gen *DrawioGenerator) drawio.TreeNodeInterface
 	IsExternal() bool
+	ShowOnSubnetMode() bool
 }
 
 // DrawioGenerator is the struct that generate the drawio tree.
@@ -55,11 +56,23 @@ func (gen *DrawioGenerator) TreeNode(res DrawioResourceIntf) drawio.TreeNodeInte
 func (exn *ExternalNetwork) GenerateDrawioTreeNode(gen *DrawioGenerator) drawio.TreeNodeInterface {
 	return drawio.NewInternetTreeNode(gen.PublicNetwork(), exn.CidrStr)
 }
+func (exn *ExternalNetwork) ShowOnSubnetMode() bool     { return true }
+func (g *groupedEndpointsElems) ShowOnSubnetMode() bool { return true }
+func (g *groupedExternalNodes) ShowOnSubnetMode() bool  { return true }
+func (e *edgeInfo) ShowOnSubnetMode() bool              { return true }
 
 func (g *groupedEndpointsElems) GenerateDrawioTreeNode(gen *DrawioGenerator) drawio.TreeNodeInterface {
 	if len(*g) == 1 {
 		return gen.TreeNode((*g)[0])
 	}
+	if gen.TreeNode((*g)[0]).IsSquare() && gen.TreeNode((*g)[0]).(drawio.SquareTreeNodeInterface).IsSubnet() {
+		groupedSubnetsTNs := make([]drawio.SquareTreeNodeInterface, len(*g))
+		for i, node := range *g {
+			groupedSubnetsTNs[i] = gen.TreeNode(node).(drawio.SquareTreeNodeInterface)
+		}
+		vpcTn := groupedSubnetsTNs[0].Parent().Parent().(*drawio.VpcTreeNode)
+		return drawio.GroupedSubnetsSquare(vpcTn, groupedSubnetsTNs)
+	}
 	groupedIconsTNs := make([]drawio.IconTreeNodeInterface, len(*g))
 	for i, node := range *g {
 		groupedIconsTNs[i] = gen.TreeNode(node).(drawio.IconTreeNodeInterface)
diff --git a/pkg/vpcmodel/drawioOutput.go b/pkg/vpcmodel/drawioOutput.go
index 72063f36b..0179f3574 100644
--- a/pkg/vpcmodel/drawioOutput.go
+++ b/pkg/vpcmodel/drawioOutput.go
@@ -26,22 +26,34 @@ func (e *edgeInfo) IsExternal() bool {
 
 type DrawioOutputFormatter struct {
 	cConfig *VPCConfig
-	conn    *VPCConnectivity
+	conn    *GroupConnLines
 	gen     *DrawioGenerator
 	routers map[drawio.TreeNodeInterface]drawio.IconTreeNodeInterface
+	uc      OutputUseCase
 }
 
-func (d *DrawioOutputFormatter) init(cConfig *VPCConfig, conn *VPCConnectivity) {
+func (d *DrawioOutputFormatter) init(cConfig *VPCConfig, conn *GroupConnLines, uc OutputUseCase) {
 	d.cConfig = cConfig
 	d.conn = conn
+	d.uc = uc
 	d.gen = NewDrawioGenerator(cConfig.CloudName)
 	d.routers = map[drawio.TreeNodeInterface]drawio.IconTreeNodeInterface{}
 }
 
+func (d *DrawioOutputFormatter) writeOutputGeneric(outFile string) (
+	*SingleAnalysisOutput, error) {
+	d.createDrawioTree()
+	err := drawio.CreateDrawioConnectivityMapFile(d.gen.Network(), outFile, d.uc == AllSubnets)
+	return &SingleAnalysisOutput{}, err
+}
+
 func (d *DrawioOutputFormatter) createDrawioTree() {
 	d.createNodeSets()
-	d.createNodes()
-	d.createFilters()
+	if d.uc != AllSubnets {
+		// todo - support filters on subnet mode
+		d.createNodes()
+		d.createFilters()
+	}
 	d.createRouters()
 	if d.conn != nil {
 		d.createEdges()
@@ -50,13 +62,15 @@ func (d *DrawioOutputFormatter) createDrawioTree() {
 
 func (d *DrawioOutputFormatter) createNodeSets() {
 	for _, ns := range d.cConfig.NodeSets {
-		d.gen.TreeNode(ns)
+		if d.showResource(ns) {
+			d.gen.TreeNode(ns)
+		}
 	}
 }
 
 func (d *DrawioOutputFormatter) createNodes() {
 	for _, n := range d.cConfig.Nodes {
-		if !n.IsExternal() {
+		if !n.IsExternal() && d.showResource(n) {
 			d.gen.TreeNode(n)
 		}
 	}
@@ -64,16 +78,21 @@ func (d *DrawioOutputFormatter) createNodes() {
 
 func (d *DrawioOutputFormatter) createFilters() {
 	for _, fl := range d.cConfig.FilterResources {
-		d.gen.TreeNode(fl)
+		if d.showResource(fl) {
+			d.gen.TreeNode(fl)
+		}
 	}
 }
 
 func (d *DrawioOutputFormatter) createRouters() {
 	for _, r := range d.cConfig.RoutingResources {
-		rTn := d.gen.TreeNode(r)
-
-		for _, ni := range r.Src() {
-			d.routers[d.gen.TreeNode(ni)] = rTn.(drawio.IconTreeNodeInterface)
+		if d.showResource(r) {
+			rTn := d.gen.TreeNode(r)
+			for _, ni := range r.Src() {
+				if d.showResource(ni) {
+					d.routers[d.gen.TreeNode(ni)] = rTn.(drawio.IconTreeNodeInterface)
+				}
+			}
 		}
 	}
 }
@@ -85,7 +104,7 @@ func (d *DrawioOutputFormatter) createEdges() {
 		label string
 	}
 	isEdgeDirected := map[edgeKey]bool{}
-	for _, line := range d.conn.GroupedConnectivity.GroupedLines {
+	for _, line := range d.conn.GroupedLines {
 		src := line.src
 		dst := line.dst
 		e := edgeKey{src, dst, line.ConnLabel()}
@@ -99,16 +118,22 @@ func (d *DrawioOutputFormatter) createEdges() {
 	}
 	for e, directed := range isEdgeDirected {
 		ei := &edgeInfo{e.src, e.dst, e.label, directed}
-		cn := d.gen.TreeNode(ei).(*drawio.ConnectivityTreeNode)
-		if d.routers[cn.Src()] != nil && e.dst.IsExternal() {
-			cn.SetRouter(d.routers[cn.Src()], false)
-		}
-		if d.routers[cn.Dst()] != nil && e.src.IsExternal() {
-			cn.SetRouter(d.routers[cn.Dst()], true)
+		if d.showResource(ei) {
+			cn := d.gen.TreeNode(ei).(*drawio.ConnectivityTreeNode)
+			if d.routers[cn.Src()] != nil && e.dst.IsExternal() {
+				cn.SetRouter(d.routers[cn.Src()], false)
+			}
+			if d.routers[cn.Dst()] != nil && e.src.IsExternal() {
+				cn.SetRouter(d.routers[cn.Dst()], true)
+			}
 		}
 	}
 }
 
+func (d *DrawioOutputFormatter) showResource(res DrawioResourceIntf) bool {
+	return d.uc != AllSubnets || res.ShowOnSubnetMode()
+}
+
 func (d *DrawioOutputFormatter) WriteOutput(c1, c2 *VPCConfig,
 	conn *VPCConnectivity,
 	subnetsConn *VPCsubnetConnectivity,
@@ -116,16 +141,23 @@ func (d *DrawioOutputFormatter) WriteOutput(c1, c2 *VPCConfig,
 	outFile string,
 	grouping bool,
 	uc OutputUseCase) (*SingleAnalysisOutput, error) {
-	var err error
 	switch uc {
 	case AllEndpoints:
-		d.init(c1, conn)
-		d.createDrawioTree()
-		err = drawio.CreateDrawioConnectivityMapFile(d.gen.Network(), outFile)
-	case AllSubnets, SingleSubnet:
-		err = errors.New("SubnetLevel/SingleSubnet use case not supported for draw.io format")
+		var gConn *GroupConnLines
+		if conn != nil {
+			gConn = conn.GroupedConnectivity
+		}
+		d.init(c1, gConn, uc)
+	case AllSubnets:
+		var gConn *GroupConnLines
+		if subnetsConn != nil {
+			gConn = subnetsConn.GroupedConnectivity
+		}
+		d.init(subnetsConn.VPCConfig, gConn, uc)
+	default:
+		return &SingleAnalysisOutput{}, errors.New("use case is not currently supported for draw.io format")
 	}
-	return &SingleAnalysisOutput{}, err
+	return d.writeOutputGeneric(outFile)
 }
 
 // /////////////////////////////////////////////////////////////////
@@ -144,11 +176,5 @@ func (d *ArchDrawioOutputFormatter) WriteOutput(c1, c2 *VPCConfig,
 	outFile string,
 	grouping bool,
 	uc OutputUseCase) (*SingleAnalysisOutput, error) {
-	switch uc {
-	case AllEndpoints:
-		return d.DrawioOutputFormatter.WriteOutput(c1, c2, nil, nil, nil, outFile, grouping, uc)
-	case AllSubnets, SingleSubnet:
-		return d.DrawioOutputFormatter.WriteOutput(nil, c2, nil, nil, nil, outFile, grouping, uc)
-	}
-	return &SingleAnalysisOutput{}, nil
+	return d.DrawioOutputFormatter.WriteOutput(c1, c2, nil, nil, nil, outFile, grouping, uc)
 }
diff --git a/pkg/vpcmodel/grouping_test.go b/pkg/vpcmodel/grouping_test.go
index 852a34e70..8bd37cdf8 100644
--- a/pkg/vpcmodel/grouping_test.go
+++ b/pkg/vpcmodel/grouping_test.go
@@ -40,7 +40,8 @@ func (m *mockNetIntf) ZoneName() string {
 func (m *mockNetIntf) GenerateDrawioTreeNode(gen *DrawioGenerator) drawio.TreeNodeInterface {
 	return nil
 }
-func (m *mockNetIntf) IsExternal() bool { return m.isPublic }
+func (m *mockNetIntf) IsExternal() bool       { return m.isPublic }
+func (m *mockNetIntf) ShowOnSubnetMode() bool { return false }
 
 func (m *mockNetIntf) VPC() VPCResourceIntf {
 	return nil
@@ -77,7 +78,8 @@ func (m *mockSubnet) ZoneName() string {
 func (m *mockSubnet) GenerateDrawioTreeNode(gen *DrawioGenerator) drawio.TreeNodeInterface {
 	return nil
 }
-func (m *mockSubnet) IsExternal() bool { return false }
+func (m *mockSubnet) IsExternal() bool       { return false }
+func (m *mockSubnet) ShowOnSubnetMode() bool { return true }
 func (m *mockSubnet) VPC() VPCResourceIntf {
 	return nil
 }