Skip to content

Commit ce13121

Browse files
authored
[NAD] Display svg component as edge info (#784)
[NAD] Display svg component as edge info (#784) * [NAD] Display svg component as edge info * Fix after merge * Fix sonar issues * Draw both icon an labels. Fix centered icons * Increase code coverage * Increase code coverage * Refactor to remove code duplication --------- Signed-off-by: Giovanni Ferrari <giovanni.ferrari@soft.it>
1 parent 67bb2d6 commit ce13121

9 files changed

Lines changed: 354 additions & 12 deletions

File tree

network-area-diagram/src/main/java/com/powsybl/nad/svg/EdgeInfo.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,27 @@ public class EdgeInfo {
2929
private final Direction arrowDirection;
3030
private final String labelA;
3131
private final String labelB;
32+
private final String componentType;
3233

3334
public EdgeInfo(String infoTypeA, String infoTypeB, Direction arrowDirection, String labelA, String labelB) {
35+
this(infoTypeA, infoTypeB, arrowDirection, labelA, labelB, null);
36+
}
37+
38+
public EdgeInfo(String infoTypeA, String infoTypeB, Direction arrowDirection, String labelA, String labelB, String componentType) {
3439
this.infoTypeB = infoTypeB;
3540
this.infoTypeA = infoTypeA;
3641
this.arrowDirection = arrowDirection;
3742
this.labelA = labelA;
3843
this.labelB = labelB;
44+
this.componentType = componentType;
3945
}
4046

4147
public EdgeInfo(String infoTypeA, String infoTypeB, double referenceValue, String labelA, String labelB) {
42-
this(infoTypeA, infoTypeB, getArrowDirection(referenceValue), labelA, labelB);
48+
this(infoTypeA, infoTypeB, getArrowDirection(referenceValue), labelA, labelB, null);
49+
}
50+
51+
public EdgeInfo(String infoTypeA, String infoTypeB, double referenceValue, String labelA, String labelB, String componentType) {
52+
this(infoTypeA, infoTypeB, getArrowDirection(referenceValue), labelA, labelB, componentType);
4353
}
4454

4555
private static Direction getArrowDirection(double value) {
@@ -77,6 +87,10 @@ public Optional<String> getLabelB() {
7787
return Optional.ofNullable(labelB);
7888
}
7989

90+
public Optional<String> getComponentType() {
91+
return Optional.ofNullable(componentType);
92+
}
93+
8094
/**
8195
* Returns the main info type.
8296
* @return the main info type. By default, the info type of the side 2.

network-area-diagram/src/main/java/com/powsybl/nad/svg/SvgWriter.java

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.powsybl.commons.PowsyblException;
1010
import com.powsybl.commons.exceptions.UncheckedXmlStreamException;
1111
import com.powsybl.commons.xml.XmlUtil;
12+
import com.powsybl.diagram.components.ComponentSize;
1213
import com.powsybl.diagram.util.CssUtil;
1314
import com.powsybl.nad.library.NadComponentLibrary;
1415
import com.powsybl.nad.model.*;
@@ -218,23 +219,42 @@ private void insertSvgComponent(Injection injection, XMLStreamWriter writer) thr
218219
writer.writeStartElement(GROUP_ELEMENT_NAME);
219220
writer.writeAttribute(TRANSFORM_ATTRIBUTE, getTranslateString(injection.getIconOrigin(svgParameters.getInjectionCircleRadius())));
220221

221-
Result result = new SAXResult(new SvgContentHandlerToXMLStreamWriter(writer));
222222
String componentType = injection.getComponentType();
223+
writeSvgComponent(writer, componentType, "Cannot insert SVG for injection of type " + injection.getType());
224+
}
225+
226+
private void drawComponentOnBranchEdgeMiddle(XMLStreamWriter writer, String componentType) throws XMLStreamException {
227+
writer.writeStartElement(GROUP_ELEMENT_NAME);
228+
ComponentSize componentSize = componentLibrary.getComponentsSize().getOrDefault(componentType, null);
229+
230+
if (componentSize == null) {
231+
throw new PowsyblException("Cannot find size for component of type " + componentType);
232+
}
233+
Point trans = new Point(-componentSize.width() / 2, -componentSize.height() / 2);
234+
writer.writeAttribute(TRANSFORM_ATTRIBUTE, getTranslateString(trans));
235+
236+
writeSvgComponent(writer, componentType, "Cannot insert SVG component of type " + componentType + " on branch edge middle");
237+
}
238+
239+
private void writeSvgComponent(XMLStreamWriter writer, String componentType, String errorMessage) throws XMLStreamException {
240+
Result result = new SAXResult(new SvgContentHandlerToXMLStreamWriter(writer));
223241
writeStyleClasses(writer, componentLibrary.getComponentStyleClass(componentType).map(List::of).orElse(List.of()));
224242

225243
try {
226244
Transformer transformer = componentLibrary.getSvgTransformer();
227245
Map<String, List<Element>> subComponents = componentLibrary.getSvgElements(componentType);
228-
for (Map.Entry<String, List<Element>> scEntry : subComponents.entrySet()) {
229-
List<String> edgeStyleClasses = componentLibrary.getSubComponentStyleClass(componentType, scEntry.getKey())
246+
if (subComponents != null) {
247+
for (Map.Entry<String, List<Element>> scEntry : subComponents.entrySet()) {
248+
List<String> edgeStyleClasses = componentLibrary.getSubComponentStyleClass(componentType, scEntry.getKey())
230249
.map(List::of).orElse(List.of());
231-
writeStyleClasses(writer, edgeStyleClasses);
232-
for (Element element : scEntry.getValue()) {
233-
transformer.transform(new DOMSource(element), result);
250+
writeStyleClasses(writer, edgeStyleClasses);
251+
for (Element element : scEntry.getValue()) {
252+
transformer.transform(new DOMSource(element), result);
253+
}
234254
}
235255
}
236256
} catch (TransformerException e) {
237-
throw new PowsyblException("Cannot insert SVG for injection of type " + injection.getType(), e);
257+
throw new PowsyblException(errorMessage, e);
238258
}
239259
writer.writeEndElement();
240260
}
@@ -590,7 +610,7 @@ private <E> void drawEdgeInfoOrLabel(XMLStreamWriter writer,
590610
if (checkIfEdgeInfoIsEmpty(edgeInfo)) {
591611
return;
592612
}
593-
if (edgeInfo.getDirection().isPresent() || edgeInfo.getLabelB().isPresent() && edgeInfo.getLabelA().isPresent()) {
613+
if (edgeInfo.getDirection().isPresent() || edgeInfo.getComponentType().isPresent() || edgeInfo.getLabelB().isPresent() && edgeInfo.getLabelA().isPresent()) {
594614
drawEdgeInfo(writer, svgEdgeInfo, point, edgeAngle);
595615
} else {
596616
String label = edgeInfo.getLabelB().orElse(edgeInfo.getLabelA().orElse(null));
@@ -655,7 +675,13 @@ private void drawEdgeInfo(XMLStreamWriter writer, SvgEdgeInfo svgEdgeInfo, Point
655675
writer.writeStartElement(GROUP_ELEMENT_NAME);
656676
writer.writeAttribute(ID_ATTRIBUTE, svgEdgeInfo.svgId());
657677
writer.writeAttribute(TRANSFORM_ATTRIBUTE, getTranslateString(infoCenter));
658-
drawArrow(writer, edgeInfo, edgeAngle);
678+
679+
Optional<String> componentType = edgeInfo.getComponentType();
680+
if (componentType.isPresent()) {
681+
this.drawComponentOnBranchEdgeMiddle(writer, componentType.get());
682+
} else {
683+
drawArrow(writer, edgeInfo, edgeAngle);
684+
}
659685
Optional<String> label2 = edgeInfo.getLabelB();
660686
if (label2.isPresent()) {
661687
drawLabel(writer, label2.get(), edgeAngle, true, styleProvider.getEdgeInfoStyleClasses(edgeInfo.getInfoTypeB()));

network-area-diagram/src/main/java/com/powsybl/nad/svg/metadata/DiagramMetadata.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ private static EdgeInfoMetadata createEdgeInfoMetadata(SvgEdgeInfo svgEdgeInfo)
199199
edgeInfo.getInfoTypeB(),
200200
edgeInfo.getDirection().map(Enum::name).orElse(null),
201201
edgeInfo.getLabelA().orElse(null),
202-
edgeInfo.getLabelB().orElse(null));
202+
edgeInfo.getLabelB().orElse(null),
203+
edgeInfo.getComponentType().orElse(null));
203204
}
204205

205206
private String getPrefixedId(String id) {

network-area-diagram/src/main/java/com/powsybl/nad/svg/metadata/EdgeInfoMetadata.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
package com.powsybl.nad.svg.metadata;
99

10+
import com.fasterxml.jackson.annotation.JsonCreator;
1011
import com.fasterxml.jackson.annotation.JsonInclude;
1112
import com.fasterxml.jackson.annotation.JsonProperty;
1213

@@ -21,19 +22,23 @@ public class EdgeInfoMetadata {
2122
private final String direction;
2223
private final String labelA;
2324
private final String labelB;
25+
private final String componentType;
2426

27+
@JsonCreator
2528
public EdgeInfoMetadata(@JsonProperty("svgId") String svgId,
2629
@JsonProperty("infoTypeA") String infoTypeA,
2730
@JsonProperty("infoTypeB") String infoTypeB,
2831
@JsonProperty("direction") String direction,
2932
@JsonProperty("labelA") String labelA,
30-
@JsonProperty("labelB") String labelB) {
33+
@JsonProperty("labelB") String labelB,
34+
@JsonProperty("componentType") String componentType) {
3135
this.svgId = svgId;
3236
this.infoTypeA = infoTypeA;
3337
this.infoTypeB = infoTypeB;
3438
this.direction = direction;
3539
this.labelA = labelA;
3640
this.labelB = labelB;
41+
this.componentType = componentType;
3742
}
3843

3944
@JsonProperty("svgId")
@@ -65,4 +70,9 @@ public String getLabelA() {
6570
public String getLabelB() {
6671
return labelB;
6772
}
73+
74+
@JsonProperty("componentType")
75+
public String getComponentType() {
76+
return componentType;
77+
}
6878
}

network-area-diagram/src/main/resources/DefaultLibrary/components.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,34 @@
8383
],
8484
"styleClass": "nad-svc"
8585
},
86+
{
87+
"type": "LOCK",
88+
"size": {
89+
"width": 50.0,
90+
"height": 50.0
91+
},
92+
"subComponents": [
93+
{
94+
"name": "LOCK",
95+
"fileName": "lock.svg"
96+
}
97+
],
98+
"styleClass": "nad-lock"
99+
},
100+
{
101+
"type": "FLASH",
102+
"size": {
103+
"width": 50.0,
104+
"height": 50.0
105+
},
106+
"subComponents": [
107+
{
108+
"name": "FLASH",
109+
"fileName": "flash.svg"
110+
}
111+
],
112+
"styleClass": "nad-flash"
113+
},
86114
{
87115
"type": "UNKNOWN_COMPONENT",
88116
"size": {
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* Copyright (c) 2022, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
package com.powsybl.nad.svg;
8+
9+
import com.powsybl.commons.PowsyblException;
10+
import com.powsybl.diagram.test.Networks;
11+
import com.powsybl.iidm.network.Network;
12+
import com.powsybl.nad.AbstractTest;
13+
import com.powsybl.nad.build.iidm.IntIdProvider;
14+
import com.powsybl.nad.build.iidm.NetworkGraphBuilder;
15+
import com.powsybl.nad.build.iidm.VoltageLevelFilter;
16+
import com.powsybl.nad.layout.BasicForceLayout;
17+
import com.powsybl.nad.layout.LayoutParameters;
18+
import com.powsybl.nad.model.BranchEdge;
19+
import com.powsybl.nad.model.Graph;
20+
import com.powsybl.nad.model.ThreeWtEdge;
21+
import com.powsybl.nad.svg.iidm.DefaultLabelProvider;
22+
import com.powsybl.nad.svg.iidm.NominalVoltageStyleProvider;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Test;
25+
26+
import static org.junit.jupiter.api.Assertions.assertThrows;
27+
import static org.junit.jupiter.api.Assertions.assertTrue;
28+
29+
import java.io.StringWriter;
30+
import java.util.Optional;
31+
32+
/**
33+
* @author Giovanni Ferrari {@literal <giovanni.ferrari at soft.it>}
34+
*/
35+
class EdgeInfoSvgComponentTest extends AbstractTest {
36+
37+
private String internalLabel;
38+
private String externalLabel;
39+
private String side1Label;
40+
private String side2Label;
41+
42+
@BeforeEach
43+
void setup() {
44+
setLayoutParameters(new LayoutParameters());
45+
setSvgParameters(new SvgParameters()
46+
.setSvgWidthAndHeightAdded(true)
47+
.setFixedWidth(800)
48+
.setArrowPathIn("M-20 -10 H20 L0 10z")
49+
.setArrowPathOut("M-5 10 H5 L0 -10z"));
50+
}
51+
52+
@Override
53+
protected StyleProvider getStyleProvider(Network network) {
54+
return new NominalVoltageStyleProvider(network);
55+
}
56+
57+
@Override
58+
protected LabelProvider getLabelProvider(Network network) {
59+
return new DefaultLabelProvider(network, getSvgParameters()) {
60+
61+
@Override
62+
public Optional<EdgeInfo> getBranchEdgeInfo(String branchId, BranchEdge.Side side, String branchType) {
63+
return Optional.of(new EdgeInfo("test", "test", EdgeInfo.Direction.OUT, internalLabel, externalLabel, "FLASH"));
64+
}
65+
66+
@Override
67+
public Optional<EdgeInfo> getBranchEdgeInfo(String branchId, String branchType) {
68+
return Optional.of(new EdgeInfo("test", "test", 1, side1Label, side2Label, "LOCK"));
69+
}
70+
71+
@Override
72+
public Optional<EdgeInfo> getThreeWindingTransformerEdgeInfo(String threeWindingTransformerId, ThreeWtEdge.Side side) {
73+
return Optional.of(new EdgeInfo("test", "test", EdgeInfo.Direction.IN, internalLabel, externalLabel));
74+
}
75+
};
76+
}
77+
78+
@Test
79+
void testSvgComponentEdgeInfo() {
80+
Network network = Networks.createTwoVoltageLevels();
81+
internalLabel = "int";
82+
externalLabel = "ext";
83+
side1Label = "side1";
84+
side2Label = "side2";
85+
getSvgParameters().setEdgeInfoAlongEdge(false)
86+
.setArrowShift(50)
87+
.setArrowLabelShift(25);
88+
assertSvgEquals("/edge_info_components.svg", network);
89+
}
90+
91+
@Test
92+
void testUnknownEdgeInfoCopmponent() {
93+
Network network = Networks.createTwoVoltageLevels();
94+
LabelProvider mylabelProvider = new DefaultLabelProvider(network, getSvgParameters()) {
95+
96+
@Override
97+
public Optional<EdgeInfo> getBranchEdgeInfo(String branchId, BranchEdge.Side side, String branchType) {
98+
return Optional.of(new EdgeInfo("test", "test", EdgeInfo.Direction.OUT, internalLabel, externalLabel, "UNKNOWN"));
99+
}
100+
101+
@Override
102+
public Optional<EdgeInfo> getBranchEdgeInfo(String branchId, String branchType) {
103+
return Optional.of(new EdgeInfo("test", "test", EdgeInfo.Direction.OUT, side1Label, side2Label, "UNKNOWN"));
104+
}
105+
106+
@Override
107+
public Optional<EdgeInfo> getThreeWindingTransformerEdgeInfo(String threeWindingTransformerId, ThreeWtEdge.Side side) {
108+
return Optional.of(new EdgeInfo("test", "test", EdgeInfo.Direction.IN, internalLabel, externalLabel));
109+
}
110+
};
111+
Graph graph = new NetworkGraphBuilder(network, VoltageLevelFilter.NO_FILTER, mylabelProvider, getLayoutParameters(), new IntIdProvider()).buildGraph();
112+
BasicForceLayout layout = new BasicForceLayout();
113+
layout.run(graph, getLayoutParameters());
114+
StringWriter writer = new StringWriter();
115+
SvgWriter svgWriter = new SvgWriter(getSvgParameters(), getStyleProvider(network), getComponentLibrary(), getEdgeRouting());
116+
PowsyblException e = assertThrows(PowsyblException.class, () -> svgWriter.writeSvg(graph, writer));
117+
assertTrue(e.getMessage().contains("Cannot find size for component of type UNKNOWN"));
118+
}
119+
120+
}

0 commit comments

Comments
 (0)