Skip to content

Commit

Permalink
[SLD] Add current info feature (#505)
Browse files Browse the repository at this point in the history
* feature: add current feeder info arrow
* new management of edge info for network area diagram: only one type possible among active power, reactive power and current
* Change dominant baseline for label: mathematical instead of middle
* add unit tests
* update test references

Signed-off-by: Hervé <herve.quarton@soprasteria.com>

Co-authored-by: Sophie Frasnedo <sophie.frasnedo@rte-france.com>
  • Loading branch information
2 people authored and BenoitJeanson committed May 4, 2023
1 parent 1e840c2 commit 04a3d89
Show file tree
Hide file tree
Showing 129 changed files with 1,090 additions and 9,207 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ public class ValueFormatter {

private final int powerValuePrecision;
private final int voltageValuePrecision;
private final int currentValuePrecision;
private final int angleValuePrecision;
private final DecimalFormat format;
private final String undefinedValueSymbol;

public ValueFormatter(int powerValuePrecision, int voltageValuePrecision, int angleValuePrecision, Locale locale, String undefinedValueSymbol) {
public ValueFormatter(int powerValuePrecision, int voltageValuePrecision, int currentValuePrecision, int angleValuePrecision, Locale locale, String undefinedValueSymbol) {
this.powerValuePrecision = powerValuePrecision;
this.voltageValuePrecision = voltageValuePrecision;
this.currentValuePrecision = currentValuePrecision;
this.angleValuePrecision = angleValuePrecision;
this.format = new DecimalFormat();
format.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(locale));
Expand Down Expand Up @@ -63,4 +65,14 @@ private void setFractionDigits(int precision) {
format.setMaximumFractionDigits(precision);
format.setMinimumFractionDigits(precision);
}

public String formatCurrent(double current, String unit) {
setFractionDigits(currentValuePrecision);
String valueFormatted = Double.isNaN(current) ? undefinedValueSymbol : format.format(current);
return unit.isEmpty() ? valueFormatted : (valueFormatted + " " + unit);
}

public String formatCurrent(double current) {
return formatCurrent(current, "");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import com.powsybl.commons.config.BaseVoltagesConfig;
import com.powsybl.nad.model.*;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UncheckedIOException;
Expand All @@ -22,6 +24,8 @@
*/
public abstract class AbstractStyleProvider implements StyleProvider {

protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractStyleProvider.class);

private final BaseVoltagesConfig baseVoltagesConfig;

protected AbstractStyleProvider() {
Expand Down Expand Up @@ -87,11 +91,22 @@ public List<String> getSideEdgeStyleClasses(BranchEdge edge, BranchEdge.Side sid
@Override
public List<String> getEdgeInfoStyles(EdgeInfo info) {
List<String> styles = new LinkedList<>();
if (info.getInfoType().equals(EdgeInfo.ACTIVE_POWER)) {
styles.add(CLASSES_PREFIX + "active");
} else if (info.getInfoType().equals(EdgeInfo.REACTIVE_POWER)) {
styles.add(CLASSES_PREFIX + "reactive");
String infoType = info.getInfoType();
switch (infoType) {
case EdgeInfo.ACTIVE_POWER:
styles.add(CLASSES_PREFIX + "active");
break;
case EdgeInfo.REACTIVE_POWER:
styles.add(CLASSES_PREFIX + "reactive");
break;
case EdgeInfo.CURRENT:
styles.add(CLASSES_PREFIX + "current");
break;
default:
LOGGER.warn("The \"{}\" type of information is not handled", infoType);
break;
}

info.getDirection().ifPresent(direction -> styles.add(
CLASSES_PREFIX + (direction == EdgeInfo.Direction.IN ? "state-in" : "state-out")));
return styles;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
public class EdgeInfo {
public static final String ACTIVE_POWER = "ActivePower";
public static final String REACTIVE_POWER = "ReactivePower";
public static final String CURRENT = "Current";

private final String infoType;
private final Direction arrowDirection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
import com.powsybl.nad.model.*;

import java.util.List;
import java.util.Optional;

/**
* @author Florian Dupuy <florian.dupuy at rte-france.com>
*/
public interface LabelProvider {
List<EdgeInfo> getEdgeInfos(Graph graph, BranchEdge edge, BranchEdge.Side side);
Optional<EdgeInfo> getEdgeInfo(Graph graph, BranchEdge edge, BranchEdge.Side side);

List<EdgeInfo> getEdgeInfos(Graph graph, ThreeWtEdge edge);
Optional<EdgeInfo> getEdgeInfo(Graph graph, ThreeWtEdge edge);

String getLabel(Edge edge);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,12 @@ public class SvgParameters {
private int voltageValuePrecision = 1;
private int powerValuePrecision = 0;
private int angleValuePrecision = 1;
private int currentValuePrecision = 0;
private EdgeInfoEnum edgeInfoDisplayed = EdgeInfoEnum.ACTIVE_POWER;
private double pstArrowHeadSize = 8;
private String undefinedValueSymbol = "";


public enum CssLocation {
INSERTED_IN_SVG, EXTERNAL_IMPORTED, EXTERNAL_NO_IMPORT
}
Expand Down Expand Up @@ -103,6 +106,8 @@ public SvgParameters(SvgParameters other) {
this.voltageValuePrecision = other.voltageValuePrecision;
this.powerValuePrecision = other.powerValuePrecision;
this.angleValuePrecision = other.angleValuePrecision;
this.currentValuePrecision = other.currentValuePrecision;
this.edgeInfoDisplayed = other.edgeInfoDisplayed;
this.pstArrowHeadSize = other.pstArrowHeadSize;
this.undefinedValueSymbol = other.undefinedValueSymbol;
}
Expand Down Expand Up @@ -429,6 +434,15 @@ public SvgParameters setPowerValuePrecision(int powerValuePrecision) {
return this;
}

public int getCurrentValuePrecision() {
return currentValuePrecision;
}

public SvgParameters setCurrentValuePrecision(int currentValuePrecision) {
this.currentValuePrecision = currentValuePrecision;
return this;
}

public int getAngleValuePrecision() {
return angleValuePrecision;
}
Expand All @@ -439,7 +453,22 @@ public SvgParameters setAngleValuePrecision(int angleValuePrecision) {
}

public ValueFormatter createValueFormatter() {
return new ValueFormatter(powerValuePrecision, voltageValuePrecision, angleValuePrecision, Locale.forLanguageTag(languageTag), undefinedValueSymbol);
return new ValueFormatter(powerValuePrecision, voltageValuePrecision, currentValuePrecision, angleValuePrecision, Locale.forLanguageTag(languageTag), undefinedValueSymbol);
}

public enum EdgeInfoEnum {
ACTIVE_POWER,
REACTIVE_POWER,
CURRENT;
}

public EdgeInfoEnum getEdgeInfoDisplayed() {
return this.edgeInfoDisplayed;
}

public SvgParameters setEdgeInfoDisplayed(EdgeInfoEnum edgeInfoDisplayed) {
this.edgeInfoDisplayed = edgeInfoDisplayed;
return this;
}

public double getPstArrowHeadSize() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,16 +255,21 @@ private void drawHalfEdge(Graph graph, XMLStreamWriter writer, BranchEdge edge,
writer.writeAttribute(ID_ATTRIBUTE, getPrefixedId(edge.getDiagramId() + "." + side.getNum()));
writeStyleClasses(writer, styleProvider.getSideEdgeStyleClasses(edge, side));
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));
drawBranchEdgeInfo(graph, writer, edge, side, labelProvider.getEdgeInfos(graph, edge, side));
if (edgeInfo.isPresent()) {
drawBranchEdgeInfo(graph, writer, edge, side, edgeInfo.get());
}
} else {
writer.writeEmptyElement(PATH_ELEMENT_NAME);
writer.writeAttribute(CLASS_ATTRIBUTE, StyleProvider.EDGE_PATH_CLASS);
writer.writeAttribute(PATH_D_ATTRIBUTE, getLoopPathString(edge, side));
drawLoopEdgeInfo(writer, edge, side, labelProvider.getEdgeInfos(graph, edge, side));
if (edgeInfo.isPresent()) {
drawLoopEdgeInfo(writer, edge, side, edgeInfo.get());
}
}
}
writer.writeEndElement();
Expand Down Expand Up @@ -302,7 +307,10 @@ private void drawThreeWtEdge(Graph graph, XMLStreamWriter writer, ThreeWtEdge ed
writeStyleClasses(writer, StyleProvider.EDGE_PATH_CLASS, StyleProvider.STRETCHABLE_CLASS);
writer.writeAttribute(POINTS_ATTRIBUTE, getPolylinePointsString(edge));

drawThreeWtEdgeInfo(graph, writer, edge, labelProvider.getEdgeInfos(graph, edge));
Optional<EdgeInfo> edgeInfo = labelProvider.getEdgeInfo(graph, edge);
if (edgeInfo.isPresent()) {
drawThreeWtEdgeInfo(graph, writer, edge, edgeInfo.get());
}

writer.writeEndElement();
}
Expand Down Expand Up @@ -339,46 +347,47 @@ private void draw3WtWinding(ThreeWtEdge edge, ThreeWtNode threeWtNode, XMLStream
writer.writeAttribute(CIRCLE_RADIUS_ATTRIBUTE, getFormattedValue(svgParameters.getTransformerCircleRadius()));
}

private void drawLoopEdgeInfo(XMLStreamWriter writer, BranchEdge edge, BranchEdge.Side side, List<EdgeInfo> edgeInfos) throws XMLStreamException {
drawEdgeInfo(writer, edgeInfos, edge.getPoints(side).get(1), edge.getEdgeStartAngle(side));
private void drawLoopEdgeInfo(XMLStreamWriter writer, BranchEdge edge, BranchEdge.Side side, EdgeInfo edgeInfo) throws XMLStreamException {
drawEdgeInfo(writer, edgeInfo, edge.getPoints(side).get(1), edge.getEdgeStartAngle(side));
}

private void drawBranchEdgeInfo(Graph graph, XMLStreamWriter writer, BranchEdge edge, BranchEdge.Side side, List<EdgeInfo> edgeInfos) throws XMLStreamException {
private void drawBranchEdgeInfo(Graph graph, XMLStreamWriter writer, BranchEdge edge, BranchEdge.Side side, EdgeInfo edgeInfo) throws XMLStreamException {
VoltageLevelNode vlNode = graph.getVoltageLevelNode(edge, side);
BusNode busNode = graph.getBusGraphNode(edge, side);
List<String> additionalStyles = List.of(StyleProvider.GLUED_CLASS + "-" + side.getNum());
drawEdgeInfo(writer, additionalStyles, edgeInfos, getArrowCenter(vlNode, busNode, edge.getPoints(side)), edge.getEdgeEndAngle(side));
drawEdgeInfo(writer, additionalStyles, edgeInfo, getArrowCenter(vlNode, busNode, edge.getPoints(side)), edge.getEdgeEndAngle(side));
}

private void drawThreeWtEdgeInfo(Graph graph, XMLStreamWriter writer, ThreeWtEdge edge, List<EdgeInfo> edgeInfos) throws XMLStreamException {
private void drawThreeWtEdgeInfo(Graph graph, XMLStreamWriter writer, ThreeWtEdge edge, EdgeInfo edgeInfo) throws XMLStreamException {
VoltageLevelNode vlNode = graph.getVoltageLevelNode(edge);
BusNode busNode = graph.getBusGraphNode(edge);
List<String> additionalStyles = List.of(StyleProvider.GLUED_CLASS + "-1");
drawEdgeInfo(writer, additionalStyles, edgeInfos, getArrowCenter(vlNode, busNode, edge.getPoints()), edge.getEdgeAngle());
drawEdgeInfo(writer, additionalStyles, edgeInfo, getArrowCenter(vlNode, busNode, edge.getPoints()), edge.getEdgeAngle());
}

private void drawEdgeInfo(XMLStreamWriter writer, List<EdgeInfo> edgeInfos, Point infoCenter, double edgeAngle) throws XMLStreamException {
drawEdgeInfo(writer, Collections.emptyList(), edgeInfos, infoCenter, edgeAngle);
private void drawEdgeInfo(XMLStreamWriter writer, EdgeInfo edgeInfo, Point infoCenter, double edgeAngle) throws XMLStreamException {
drawEdgeInfo(writer, Collections.emptyList(), edgeInfo, infoCenter, edgeAngle);
}

private void drawEdgeInfo(XMLStreamWriter writer, List<String> additionalStyles, List<EdgeInfo> edgeInfos, Point infoCenter, double edgeAngle) throws XMLStreamException {
private void drawEdgeInfo(XMLStreamWriter writer, List<String> additionalStyles, EdgeInfo edgeInfo, Point infoCenter, double edgeAngle) throws XMLStreamException {

writer.writeStartElement(GROUP_ELEMENT_NAME);
writeStyleClasses(writer, additionalStyles, StyleProvider.EDGE_INFOS_CLASS);
writer.writeAttribute(TRANSFORM_ATTRIBUTE, getTranslateString(infoCenter));
for (EdgeInfo info : edgeInfos) {
writer.writeStartElement(GROUP_ELEMENT_NAME);
writeStyleClasses(writer, styleProvider.getEdgeInfoStyles(info));
drawInAndOutArrows(writer, edgeAngle);
Optional<String> externalLabel = info.getExternalLabel();
if (externalLabel.isPresent()) {
drawLabel(writer, externalLabel.get(), edgeAngle, true);
}
Optional<String> internalLabel = info.getInternalLabel();
if (internalLabel.isPresent()) {
drawLabel(writer, internalLabel.get(), edgeAngle, false);
}
writer.writeEndElement();

writer.writeStartElement(GROUP_ELEMENT_NAME);
writeStyleClasses(writer, styleProvider.getEdgeInfoStyles(edgeInfo));
drawInAndOutArrows(writer, edgeAngle);
Optional<String> externalLabel = edgeInfo.getExternalLabel();
if (externalLabel.isPresent()) {
drawLabel(writer, externalLabel.get(), edgeAngle, true);
}
Optional<String> internalLabel = edgeInfo.getInternalLabel();
if (internalLabel.isPresent()) {
drawLabel(writer, internalLabel.get(), edgeAngle, false);
}
writer.writeEndElement();

writer.writeEndElement();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
* @author Florian Dupuy <florian.dupuy at rte-france.com>
Expand All @@ -34,33 +35,40 @@ public DefaultLabelProvider(Network network, SvgParameters svgParameters) {
}

@Override
public List<EdgeInfo> getEdgeInfos(Graph graph, BranchEdge edge, BranchEdge.Side side) {
public Optional<EdgeInfo> getEdgeInfo(Graph graph, BranchEdge edge, BranchEdge.Side side) {
Terminal terminal = IidmUtils.getTerminalFromEdge(network, edge, side);
return getEdgeInfos(terminal);
return getEdgeInfo(terminal);
}

@Override
public List<EdgeInfo> getEdgeInfos(Graph graph, ThreeWtEdge edge) {
public Optional<EdgeInfo> getEdgeInfo(Graph graph, ThreeWtEdge edge) {
ThreeWindingsTransformer transformer = network.getThreeWindingsTransformer(edge.getEquipmentId());
if (transformer == null) {
throw new PowsyblException("Unknown three windings transformer '" + edge.getEquipmentId() + "'");
}
Terminal terminal = transformer.getTerminal(IidmUtils.getIidmSideFromThreeWtEdgeSide(edge.getSide()));
return getEdgeInfos(terminal);
return getEdgeInfo(terminal);
}

@Override
public String getLabel(Edge edge) {
return edge.getEquipmentId();
}

private List<EdgeInfo> getEdgeInfos(Terminal terminal) {
private Optional<EdgeInfo> getEdgeInfo(Terminal terminal) {
if (terminal == null) {
return Collections.emptyList();
return Optional.empty();
}
switch (svgParameters.getEdgeInfoDisplayed()) {
case ACTIVE_POWER:
return Optional.of(new EdgeInfo(EdgeInfo.ACTIVE_POWER, terminal.getP(), valueFormatter::formatPower));
case REACTIVE_POWER:
return Optional.of(new EdgeInfo(EdgeInfo.REACTIVE_POWER, terminal.getQ(), valueFormatter::formatPower));
case CURRENT:
return Optional.of(new EdgeInfo(EdgeInfo.CURRENT, terminal.getI(), valueFormatter::formatCurrent));
default:
return Optional.empty();
}
return List.of(
new EdgeInfo(EdgeInfo.ACTIVE_POWER, terminal.getP(), valueFormatter::formatPower),
new EdgeInfo(EdgeInfo.REACTIVE_POWER, terminal.getQ(), valueFormatter::formatPower));
}

@Override
Expand Down
3 changes: 1 addition & 2 deletions network-area-diagram/src/main/resources/nominalStyle.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@
.nad-state-out .nad-arrow-in {visibility: hidden}
.nad-state-in .nad-arrow-out {visibility: hidden}
.nad-active path {stroke: none; fill: #546e7a}
.nad-active {visibility: visible}
.nad-reactive {visibility: hidden}
.nad-reactive path {stroke: none; fill: #0277bd}
.nad-current path {stroke: none; fill: #bd4802}
.nad-text-background {flood-color: #90a4aeaa}
.nad-text-nodes {font: 25px serif; fill: black; dominant-baseline: central}
.nad-text-nodes foreignObject {overflow: visible; color: black}
Expand Down
3 changes: 1 addition & 2 deletions network-area-diagram/src/main/resources/topologicalStyle.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@
.nad-state-out .nad-arrow-in {visibility: hidden}
.nad-state-in .nad-arrow-out {visibility: hidden}
.nad-active path {stroke: none; fill: #546e7a}
.nad-active {visibility: visible}
.nad-reactive {visibility: hidden}
.nad-reactive path {stroke: none; fill: #0277bd}
.nad-current path {stroke: none; fill: #bd4802}
.nad-text-background {flood-color: #90a4aeaa}
.nad-text-nodes {font: 25px serif; fill: black; dominant-baseline: central}
.nad-text-nodes foreignObject {overflow: visible; color: black}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
* @author Florian Dupuy <florian.dupuy at rte-france.com>
Expand All @@ -47,13 +46,13 @@ protected StyleProvider getStyleProvider(Network network) {
protected LabelProvider getLabelProvider(Network network) {
return new DefaultLabelProvider(network, getSvgParameters()) {
@Override
public List<EdgeInfo> getEdgeInfos(Graph graph, BranchEdge edge, BranchEdge.Side side) {
return Collections.singletonList(new EdgeInfo("test", EdgeInfo.Direction.OUT, internalLabel, externalLabel));
public Optional<EdgeInfo> getEdgeInfo(Graph graph, BranchEdge edge, BranchEdge.Side side) {
return Optional.of(new EdgeInfo("test", EdgeInfo.Direction.OUT, internalLabel, externalLabel));
}

@Override
public List<EdgeInfo> getEdgeInfos(Graph graph, ThreeWtEdge edge) {
return Collections.singletonList(new EdgeInfo("test", EdgeInfo.Direction.IN, internalLabel, externalLabel));
public Optional<EdgeInfo> getEdgeInfo(Graph graph, ThreeWtEdge edge) {
return Optional.of(new EdgeInfo("test", EdgeInfo.Direction.IN, internalLabel, externalLabel));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ void testIEEE30() {
}

@Test

void testIEEE14() {
Network network = IeeeCdfNetworkFactory.create14Solved();
assertEquals(toString("/IEEE_14_bus.svg"), generateSvgString(network, "/IEEE_14_bus.svg"));
Expand Down
Loading

0 comments on commit 04a3d89

Please sign in to comment.