Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
844cca2
Represent subnetworks in network-area diagrams
quinarygio Jan 15, 2025
09236a1
Fix sonar issues
quinarygio Jan 21, 2025
5318844
Fix sonar issue
quinarygio Jan 21, 2025
bed6cc1
Move highlight logic to StyleProvider
quinarygio Jan 30, 2025
03a71a1
Svg separated highlight section
quinarygio Jan 31, 2025
8510e07
Fix sonar issue. Remove unnecessary attributes from highligh section
quinarygio Jan 31, 2025
cd69050
Fix sonar issues
quinarygio Jan 31, 2025
2333a90
Fix typo and increase code coverage.
quinarygio Feb 5, 2025
88a36fd
Merge branch 'main' into nad-subnetwork-highlight
So-Fras Feb 28, 2025
d2e7faa
Fix test after merge with main
So-Fras Feb 28, 2025
2f2dd4f
First attempt at documentation
So-Fras Mar 19, 2025
70e509f
Merge branch 'main' into nad-subnetwork-highlight
flo-dup Mar 25, 2025
4c8ef81
Update unit test references
flo-dup Mar 25, 2025
1a64e59
Merge branch 'main' into nad-subnetwork-highlight
flo-dup Mar 26, 2025
cf761b1
Merge branch 'main' into nad-subnetwork-highlight
flo-dup Mar 26, 2025
2d07b2a
Fix after merge
flo-dup Mar 26, 2025
df8fa8c
Fixes after review
quinarygio Mar 27, 2025
d799456
New fixes after review
quinarygio Mar 27, 2025
25eafde
Fix and renane svg parameter from highlightSubnetworks to highlightGraph
quinarygio Mar 28, 2025
8fecd1e
Add test to increase code coverage
quinarygio Mar 28, 2025
c87eaa2
Update doc
flo-dup Mar 28, 2025
34c57c1
Merge branch 'main' into nad-subnetwork-highlight
flo-dup Mar 28, 2025
b1b5968
Rephrase the style for subnetworks
flo-dup Mar 28, 2025
09eb63c
Rephrase again
flo-dup Mar 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2021, RTE (http://www.rte-france.com)
* Copyright (c) 2021-2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
Expand Down Expand Up @@ -45,20 +45,27 @@ public interface StyleProvider {
String STRETCHABLE_CLASS = CLASSES_PREFIX + "stretchable";
String GLUED_CLASS = CLASSES_PREFIX + "glued";
String GLUED_CENTER_CLASS = CLASSES_PREFIX + "glued-center";
String HIGHLIGHT_CLASS = CLASSES_PREFIX + "highlight";

List<String> getCssFilenames();

String getStyleDefs();

List<String> getNodeStyleClasses(Node node);

List<String> getHighlightNodeStyleClasses(Node node);

List<String> getNodeStyleClasses(BusNode busNode);

List<String> getEdgeStyleClasses(Edge edge);

List<String> getSideEdgeStyleClasses(BranchEdge edge, BranchEdge.Side side);

List<String> getHighlightSideEdgeStyleClasses(BranchEdge edge, BranchEdge.Side side);

List<String> getEdgeInfoStyles(EdgeInfo info);

List<String> getThreeWtNodeStyle(ThreeWtNode threeWtNode, ThreeWtEdge.Side one);

List<String> getHighlightThreeWtEdgStyleClasses(ThreeWtEdge edge);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2021, RTE (http://www.rte-france.com)
* Copyright (c) 2021-2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
Expand Down Expand Up @@ -55,7 +55,7 @@ public class SvgParameters {
private EdgeInfoEnum edgeInfoDisplayed = EdgeInfoEnum.ACTIVE_POWER;
private double pstArrowHeadSize = 8;
private String undefinedValueSymbol = "";

private boolean highlightSubnetwors;
Comment thread
So-Fras marked this conversation as resolved.
Outdated

public enum CssLocation {
INSERTED_IN_SVG, EXTERNAL_IMPORTED, EXTERNAL_NO_IMPORT
Expand Down Expand Up @@ -491,4 +491,13 @@ public SvgParameters setUndefinedValueSymbol(String undefinedValueSymbol) {
this.undefinedValueSymbol = undefinedValueSymbol;
return this;
}

public boolean isHighlightSubnetwors() {
Comment thread
So-Fras marked this conversation as resolved.
Outdated
return highlightSubnetwors;
}

public SvgParameters setHighlightSubnetwors(boolean highlightSubnetwors) {
Comment thread
So-Fras marked this conversation as resolved.
Outdated
this.highlightSubnetwors = highlightSubnetwors;
Comment thread
So-Fras marked this conversation as resolved.
Outdated
return this;
}
}
124 changes: 108 additions & 16 deletions network-area-diagram/src/main/java/com/powsybl/nad/svg/SvgWriter.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2021, RTE (http://www.rte-france.com)
* Copyright (c) 2021-2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
Expand Down Expand Up @@ -44,6 +44,7 @@ public class SvgWriter {
private static final String TABLE_ELEMENT_NAME = "table";
private static final String TABLE_ROW_ELEMENT_NAME = "tr";
private static final String TABLE_DATA_ELEMENT_NAME = "td";
private static final String USE_ELEMENT_NAME = "use";
private static final String ID_ATTRIBUTE = "id";
private static final String WIDTH_ATTRIBUTE = "width";
private static final String HEIGHT_ATTRIBUTE = "height";
Expand All @@ -57,6 +58,7 @@ public class SvgWriter {
private static final String Y_ATTRIBUTE = "y";
private static final String DY_ATTRIBUTE = "dy";
private static final String POINTS_ATTRIBUTE = "points";
private static final String HREF_ATTRIBUTE = "href";

private final SvgParameters svgParameters;
private final StyleProvider styleProvider;
Expand Down Expand Up @@ -103,18 +105,32 @@ private void writeSvg(Graph graph, OutputStream svgOs) {
XMLStreamWriter writer = XmlUtil.initializeWriter(true, INDENT, svgOs);
addSvgRoot(graph, writer);
addStyle(writer);
boolean higlightSubnetworks = this.svgParameters.isHighlightSubnetwors();
Comment thread
So-Fras marked this conversation as resolved.
Outdated
if (higlightSubnetworks) {
drawHighlightedSection(graph, writer);
}
drawVoltageLevelNodes(graph, writer);
drawBranchEdges(graph, writer);
drawThreeWtEdges(graph, writer);
drawThreeWtNodes(graph, writer);
drawTextEdges(graph, writer);
drawTextNodes(graph, writer);

writer.writeEndDocument();
} catch (XMLStreamException e) {
throw new UncheckedXmlStreamException(e);
}
}

private void drawHighlightedSection(Graph graph, XMLStreamWriter writer) throws XMLStreamException {
writer.writeStartElement(GROUP_ELEMENT_NAME);
writer.writeAttribute(CLASS_ATTRIBUTE, StyleProvider.HIGHLIGHT_CLASS);
drawHighlighVoltageLevelNodes(graph, writer);
Comment thread
flo-dup marked this conversation as resolved.
Outdated
drawHighlightBranchEdges(graph, writer);
drawHighlightThreeWtEdges(graph, writer);
writer.writeEndElement();
}

private void drawBranchEdges(Graph graph, XMLStreamWriter writer) throws XMLStreamException {
writer.writeStartElement(GROUP_ELEMENT_NAME);
writer.writeAttribute(CLASS_ATTRIBUTE, StyleProvider.BRANCH_EDGES_CLASS);
Expand All @@ -123,12 +139,22 @@ private void drawBranchEdges(Graph graph, XMLStreamWriter writer) throws XMLStre
writeId(writer, edge);
writeStyleClasses(writer, styleProvider.getEdgeStyleClasses(edge));
insertName(writer, edge::getName);

drawHalfEdge(graph, writer, edge, BranchEdge.Side.ONE);
drawHalfEdge(graph, writer, edge, BranchEdge.Side.TWO);

drawEdgeCenter(writer, edge);
writer.writeEndElement();
}
writer.writeEndElement();
}

private void drawHighlightBranchEdges(Graph graph, XMLStreamWriter writer) throws XMLStreamException {
writer.writeStartElement(GROUP_ELEMENT_NAME);
writer.writeAttribute(CLASS_ATTRIBUTE, StyleProvider.BRANCH_EDGES_CLASS);
for (BranchEdge edge : graph.getBranchEdges()) {
writer.writeStartElement(GROUP_ELEMENT_NAME);
writeStyleClasses(writer, styleProvider.getEdgeStyleClasses(edge));
drawHighlightHalfEdge(graph, writer, edge, BranchEdge.Side.ONE);
drawHighlightHalfEdge(graph, writer, edge, BranchEdge.Side.TWO);
writer.writeEndElement();
Comment thread
flo-dup marked this conversation as resolved.
Outdated
}
writer.writeEndElement();
Expand Down Expand Up @@ -243,6 +269,20 @@ private void drawThreeWtEdges(Graph graph, XMLStreamWriter writer) throws XMLStr
writer.writeEndElement();
}

private void drawHighlightThreeWtEdges(Graph graph, XMLStreamWriter writer) throws XMLStreamException {
List<ThreeWtEdge> threeWtEdges = graph.getThreeWtEdges();
if (threeWtEdges.isEmpty()) {
return;
}

writer.writeStartElement(GROUP_ELEMENT_NAME);
writer.writeAttribute(CLASS_ATTRIBUTE, StyleProvider.THREE_WT_EDGES_CLASS);
for (ThreeWtEdge edge : threeWtEdges) {
drawHighlightThreeWtEdge(writer, edge);
}
writer.writeEndElement();
}

private void drawHalfEdge(Graph graph, XMLStreamWriter writer, BranchEdge edge, BranchEdge.Side side) throws XMLStreamException {
// the half edge is only drawn if visible, but if the edge is a TwoWtEdge, the transformer is still drawn
if (!edge.isVisible(side) && !(edge.isTransformerEdge())) {
Expand All @@ -254,24 +294,50 @@ private void drawHalfEdge(Graph graph, XMLStreamWriter writer, BranchEdge edge,
if (edge.isVisible(side)) {
Optional<EdgeInfo> edgeInfo = labelProvider.getEdgeInfo(graph, edge, side);
if (!graph.isLoop(edge)) {
writer.writeEmptyElement(POLYLINE_ELEMENT_NAME);
writeStyleClasses(writer, StyleProvider.EDGE_PATH_CLASS, StyleProvider.STRETCHABLE_CLASS, StyleProvider.GLUED_CLASS + "-" + side.getNum());
writer.writeAttribute(POINTS_ATTRIBUTE, getPolylinePointsString(edge, side));
if (edgeInfo.isPresent()) {
drawBranchEdgeInfo(graph, writer, edge, side, edgeInfo.get());
}
drawHalfEdge(graph, writer, edge, side, edgeInfo);
Comment thread
flo-dup marked this conversation as resolved.
Outdated
} else {
writer.writeEmptyElement(PATH_ELEMENT_NAME);
writer.writeAttribute(CLASS_ATTRIBUTE, StyleProvider.EDGE_PATH_CLASS);
writer.writeAttribute(PATH_D_ATTRIBUTE, getLoopPathString(edge, side));
if (edgeInfo.isPresent()) {
drawLoopEdgeInfo(writer, edge, side, edgeInfo.get());
}
drawLoopEdge(writer, edge, side, edgeInfo);
Comment thread
flo-dup marked this conversation as resolved.
Outdated
}
}
writer.writeEndElement();
}

private void drawHalfEdge(Graph graph, XMLStreamWriter writer, BranchEdge edge, BranchEdge.Side side, Optional<EdgeInfo> edgeInfo) throws XMLStreamException {
writer.writeEmptyElement(POLYLINE_ELEMENT_NAME);
writeStyleClasses(writer, StyleProvider.EDGE_PATH_CLASS, StyleProvider.STRETCHABLE_CLASS, StyleProvider.GLUED_CLASS + "-" + side.getNum());
writer.writeAttribute(POINTS_ATTRIBUTE, getPolylinePointsString(edge, side));
if (edgeInfo.isPresent()) {
drawBranchEdgeInfo(graph, writer, edge, side, edgeInfo.get());
}
}

private void drawHighlightHalfEdge(Graph graph, XMLStreamWriter writer, BranchEdge edge, BranchEdge.Side side) throws XMLStreamException {
if (!edge.isVisible(side) && !(edge.isTransformerEdge())) {
return;
}
writer.writeStartElement(GROUP_ELEMENT_NAME);
writeStyleClasses(writer, styleProvider.getSideEdgeStyleClasses(edge, side));
if (edge.isVisible(side) && !graph.isLoop(edge)) {
drawHighlighHalfEdge(writer, edge, side);
Comment thread
flo-dup marked this conversation as resolved.
Outdated
}
writer.writeEndElement();
Comment thread
flo-dup marked this conversation as resolved.
Outdated
}

private void drawHighlighHalfEdge(XMLStreamWriter writer, BranchEdge edge, BranchEdge.Side side) throws XMLStreamException {
writer.writeEmptyElement(POLYLINE_ELEMENT_NAME);
writeStyleClasses(writer, styleProvider.getHighlightSideEdgeStyleClasses(edge, side), StyleProvider.STRETCHABLE_CLASS, StyleProvider.GLUED_CLASS + "-" + side.getNum());
writer.writeAttribute(POINTS_ATTRIBUTE, getPolylinePointsString(edge, side));
}

private void drawLoopEdge(XMLStreamWriter writer, BranchEdge edge, BranchEdge.Side side, Optional<EdgeInfo> edgeInfo) throws XMLStreamException {
writer.writeEmptyElement(PATH_ELEMENT_NAME);
writer.writeAttribute(CLASS_ATTRIBUTE, StyleProvider.EDGE_PATH_CLASS);
writer.writeAttribute(PATH_D_ATTRIBUTE, getLoopPathString(edge, side));
if (edgeInfo.isPresent()) {
drawLoopEdgeInfo(writer, edge, side, edgeInfo.get());
}
}

private String getPolylinePointsString(BranchEdge edge, BranchEdge.Side side) {
return getPolylinePointsString(edge.getPoints(side));
}
Expand Down Expand Up @@ -299,7 +365,6 @@ private void drawThreeWtEdge(Graph graph, XMLStreamWriter writer, ThreeWtEdge ed
writeId(writer, edge);
writeStyleClasses(writer, styleProvider.getEdgeStyleClasses(edge));
insertName(writer, edge::getName);

writer.writeEmptyElement(POLYLINE_ELEMENT_NAME);
writeStyleClasses(writer, StyleProvider.EDGE_PATH_CLASS, StyleProvider.STRETCHABLE_CLASS);
writer.writeAttribute(POINTS_ATTRIBUTE, getPolylinePointsString(edge));
Expand All @@ -308,7 +373,18 @@ private void drawThreeWtEdge(Graph graph, XMLStreamWriter writer, ThreeWtEdge ed
if (edgeInfo.isPresent()) {
drawThreeWtEdgeInfo(graph, writer, edge, edgeInfo.get());
}
writer.writeEndElement();
}

private void drawHighlightThreeWtEdge(XMLStreamWriter writer, ThreeWtEdge edge) throws XMLStreamException {
if (!edge.isVisible()) {
return;
}
writer.writeStartElement(GROUP_ELEMENT_NAME);
writeStyleClasses(writer, styleProvider.getEdgeStyleClasses(edge));
writer.writeEmptyElement(POLYLINE_ELEMENT_NAME);
writeStyleClasses(writer, styleProvider.getHighlightThreeWtEdgStyleClasses(edge), StyleProvider.STRETCHABLE_CLASS);
writer.writeAttribute(POINTS_ATTRIBUTE, getPolylinePointsString(edge));
writer.writeEndElement();
}

Expand Down Expand Up @@ -525,6 +601,22 @@ private void drawVoltageLevelNodes(Graph graph, XMLStreamWriter writer) throws X
writer.writeEndElement();
}

private void drawHighlighVoltageLevelNodes(Graph graph, XMLStreamWriter writer) throws XMLStreamException {
writer.writeStartElement(GROUP_ELEMENT_NAME);
writer.writeAttribute(CLASS_ATTRIBUTE, StyleProvider.VOLTAGE_LEVEL_NODES_CLASS);
for (VoltageLevelNode vlNode : graph.getVoltageLevelNodesStream().filter(VoltageLevelNode::isVisible).collect(Collectors.toList())) {
drawHighlightedNode(writer, vlNode);
}
writer.writeEndElement();
}

private void drawHighlightedNode(XMLStreamWriter writer, VoltageLevelNode vlNode) throws XMLStreamException {
writer.writeStartElement(USE_ELEMENT_NAME);
writer.writeAttribute(HREF_ATTRIBUTE, "#" + getPrefixedId(vlNode.getDiagramId()));
writeStyleClasses(writer, styleProvider.getHighlightNodeStyleClasses(vlNode));
writer.writeEndElement();
}

private void drawTextNodes(Graph graph, XMLStreamWriter writer) throws XMLStreamException {
writer.writeStartElement(GROUP_ELEMENT_NAME);
writer.writeAttribute(CLASS_ATTRIBUTE, StyleProvider.TEXT_NODES_CLASS);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* Copyright (c) 2022-2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
Expand All @@ -11,7 +11,11 @@
import com.powsybl.iidm.network.Bus;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.Terminal;
import com.powsybl.iidm.network.ThreeSides;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.nad.model.*;
import com.powsybl.nad.model.BranchEdge.Side;
import com.powsybl.nad.svg.AbstractStyleProvider;
import com.powsybl.nad.svg.StyleProvider;
import com.powsybl.nad.utils.iidm.IidmUtils;
Expand All @@ -24,14 +28,18 @@
public abstract class AbstractVoltageStyleProvider extends AbstractStyleProvider {

protected final Network network;
private final HashMap<String, String> subnetworksHighlightMap = new HashMap<>();
Comment thread
flo-dup marked this conversation as resolved.
Outdated
private final HashMap<String, String> subnetworkEquipmentMap = new HashMap<>();
Comment thread
flo-dup marked this conversation as resolved.
Outdated

protected AbstractVoltageStyleProvider(Network network) {
this.network = network;
buildSubnetworkMaps();
}

protected AbstractVoltageStyleProvider(Network network, BaseVoltagesConfig baseVoltageStyle) {
super(baseVoltageStyle);
this.network = network;
buildSubnetworkMaps();
}

@Override
Expand Down Expand Up @@ -117,4 +125,73 @@ protected Optional<String> getBaseVoltageStyle(BranchEdge edge, BranchEdge.Side
}

protected abstract Optional<String> getBaseVoltageStyle(Terminal terminal);

@Override
public List<String> getHighlightNodeStyleClasses(Node node) {
String subnetworkId = subnetworkEquipmentMap.get(node.getEquipmentId());
return List.of(subnetworksHighlightMap.get(subnetworkId));
}

@Override
public List<String> getHighlightSideEdgeStyleClasses(BranchEdge edge, BranchEdge.Side side) {
String subnetworkId = getSubnetworkId(edge, side);
return List.of(subnetworksHighlightMap.get(subnetworkId));
}

@Override
public List<String> getHighlightThreeWtEdgStyleClasses(ThreeWtEdge edge) {
String subnetworkId = getSubnetworkId(edge.getEquipmentId(), edge.getSide());
return List.of(subnetworksHighlightMap.get(subnetworkId));
}

private String getSubnetworkId(BranchEdge edge, Side side) {
Comment thread
flo-dup marked this conversation as resolved.
Outdated
String subnetworId = null;
Comment thread
flo-dup marked this conversation as resolved.
Outdated
switch (edge.getType()) {
case BranchEdge.LINE_EDGE:
TwoSides lineSide = side.equals(Side.ONE) ? TwoSides.ONE : TwoSides.TWO;
Comment thread
flo-dup marked this conversation as resolved.
Outdated
subnetworId = network.getLine(edge.getEquipmentId()).getTerminal(lineSide).getVoltageLevel().getParentNetwork().getId();
break;
case BranchEdge.TWO_WT_EDGE, BranchEdge.PST_EDGE:
TwoSides twSide = side.equals(Side.ONE) ? TwoSides.ONE : TwoSides.TWO;
subnetworId = network.getTwoWindingsTransformer(edge.getEquipmentId()).getTerminal(twSide).getVoltageLevel().getParentNetwork().getId();
break;
case BranchEdge.DANGLING_LINE_EDGE:
subnetworId = network.getDanglingLine(edge.getEquipmentId()).getTerminal().getVoltageLevel().getParentNetwork().getId();
break;
case BranchEdge.TIE_LINE_EDGE:
subnetworId = network.getTieLine(edge.getEquipmentId()).getTerminal(side.equals(Side.ONE) ? TwoSides.ONE : TwoSides.TWO).getVoltageLevel().getParentNetwork().getId();
break;
case BranchEdge.HVDC_LINE_EDGE:
subnetworId = network.getHvdcLine(edge.getEquipmentId()).getConverterStation(side.equals(Side.ONE) ? TwoSides.ONE : TwoSides.TWO).getTerminal().getVoltageLevel().getParentNetwork().getId();
break;
default:
break;
}
return subnetworId;
}

private String getSubnetworkId(String id, ThreeWtEdge.Side side) {
return network.getThreeWindingsTransformer(id).getLeg(ThreeSides.valueOf(side.name())).getTerminal().getVoltageLevel().getParentNetwork().getId();
}

private void buildSubnetworkMaps() {
List<VoltageLevel> voltageLevels = getVoltageLevels();
Comment thread
flo-dup marked this conversation as resolved.
Outdated
voltageLevels.forEach(vl -> addNode(vl.getId(), vl.getParentNetwork().getId()));
network.getDanglingLineStream().forEach(dl -> addNode(dl.getId(), dl.getTerminal().getVoltageLevel().getParentNetwork().getId()));
}

private void addNode(String id, String subnetworkId) {
subnetworkEquipmentMap.put(id, subnetworkId);
subnetworksHighlightMap.computeIfAbsent(subnetworkId, k -> getHighlightClass(subnetworksHighlightMap.size()));
}

private String getHighlightClass(int index) {
return StyleProvider.HIGHLIGHT_CLASS + "-" + index % 5;
}

private List<VoltageLevel> getVoltageLevels() {
return network.getVoltageLevelStream()
.sorted(Comparator.comparing(VoltageLevel::getId))
.toList();
}
}
Loading