Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set layout node positions getting them from JSON metadata in network area diagram #656

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -6,6 +6,7 @@
*/
package com.powsybl.nad.layout;

import com.powsybl.nad.layout.AbstractLayout.TextPosition;
import com.powsybl.nad.model.Point;

import java.util.Map;
Expand All @@ -18,13 +19,23 @@ public class FixedLayoutFactory implements LayoutFactory {

private final Map<String, Point> fixedPositions;
private final LayoutFactory layoutFactory;
private final Map<String, TextPosition> textNodesWithFixedPosition;

public FixedLayoutFactory(Map<String, Point> fixedPositions) {
this(fixedPositions, BasicFixedLayout::new);
}

public FixedLayoutFactory(Map<String, Point> fixedPositions, LayoutFactory layoutFactory) {
this(fixedPositions, Map.of(), layoutFactory);
}

public FixedLayoutFactory(Map<String, Point> fixedPositions, Map<String, TextPosition> textNodesWithFixedPosition) {
this(fixedPositions, textNodesWithFixedPosition, BasicFixedLayout::new);
}

public FixedLayoutFactory(Map<String, Point> fixedPositions, Map<String, TextPosition> textNodesWithFixedPosition, LayoutFactory layoutFactory) {
this.fixedPositions = Objects.requireNonNull(fixedPositions);
this.textNodesWithFixedPosition = Objects.requireNonNull(textNodesWithFixedPosition);
this.layoutFactory = Objects.requireNonNull(layoutFactory);
}

Expand All @@ -33,6 +44,7 @@ public Layout create() {
Layout layout = layoutFactory.create();
layout.setInitialNodePositions(fixedPositions);
layout.setNodesWithFixedPosition(fixedPositions.keySet());
textNodesWithFixedPosition.forEach((k, v) -> layout.setTextNodeFixedPosition(k, v.topLeftPosition(), v.edgeConnection()));
return layout;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* Copyright (c) 2024, 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/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.nad.layout;

import java.io.InputStream;
import java.io.Reader;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import com.powsybl.nad.layout.AbstractLayout.TextPosition;
import com.powsybl.nad.model.Point;
import com.powsybl.nad.svg.metadata.DiagramMetadata;

/**
* @author Massimo Ferraro {@literal <massimo.ferraro at soft.it>}
*/
public final class FixedLayoutFactoryUtils {

private FixedLayoutFactoryUtils() {
}

private static Map<String, Point> getFixedPositions(DiagramMetadata diagramMetadata) {
Map<String, Point> fixedPositions = new HashMap<>();
diagramMetadata.getNodesMetadata()
.forEach(node -> fixedPositions.put(node.getEquipmentId(),
new Point(node.getX(),
node.getY())));
return fixedPositions;
}

private static Map<String, TextPosition> getTextNodesWithFixedPosition(DiagramMetadata diagramMetadata) {
Map<String, TextPosition> textNodesWithFixedPosition = new HashMap<>();
diagramMetadata.getTextNodesMetadata()
.forEach(textNode -> textNodesWithFixedPosition.put(textNode.getEquipmentId(),
new TextPosition(new Point(textNode.getShiftX(),
textNode.getShiftY()),
new Point(textNode.getConnectionShiftX(),
textNode.getConnectionShiftY()))));
return textNodesWithFixedPosition;
}

public static FixedLayoutFactory create(InputStream metadataIS, LayoutFactory layoutFactory) {
Objects.requireNonNull(metadataIS);
DiagramMetadata diagramMetadata = DiagramMetadata.parseJson(metadataIS);
return new FixedLayoutFactory(getFixedPositions(diagramMetadata), getTextNodesWithFixedPosition(diagramMetadata), layoutFactory);
}

public static FixedLayoutFactory create(InputStream metadataIS) {
Objects.requireNonNull(metadataIS);
DiagramMetadata diagramMetadata = DiagramMetadata.parseJson(metadataIS);
return new FixedLayoutFactory(getFixedPositions(diagramMetadata), getTextNodesWithFixedPosition(diagramMetadata));
}

public static FixedLayoutFactory create(Path metadataFile, LayoutFactory layoutFactory) {
Objects.requireNonNull(metadataFile);
DiagramMetadata diagramMetadata = DiagramMetadata.parseJson(metadataFile);
return new FixedLayoutFactory(getFixedPositions(diagramMetadata), getTextNodesWithFixedPosition(diagramMetadata), layoutFactory);
}

public static FixedLayoutFactory create(Path metadataFile) {
Objects.requireNonNull(metadataFile);
DiagramMetadata diagramMetadata = DiagramMetadata.parseJson(metadataFile);
return new FixedLayoutFactory(getFixedPositions(diagramMetadata), getTextNodesWithFixedPosition(diagramMetadata));
}

public static FixedLayoutFactory create(Reader metadataReader, LayoutFactory layoutFactory) {
Objects.requireNonNull(metadataReader);
DiagramMetadata diagramMetadata = DiagramMetadata.parseJson(metadataReader);
return new FixedLayoutFactory(getFixedPositions(diagramMetadata), getTextNodesWithFixedPosition(diagramMetadata), layoutFactory);
}

public static FixedLayoutFactory create(Reader metadataReader) {
Objects.requireNonNull(metadataReader);
DiagramMetadata diagramMetadata = DiagramMetadata.parseJson(metadataReader);
return new FixedLayoutFactory(getFixedPositions(diagramMetadata), getTextNodesWithFixedPosition(diagramMetadata));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,19 @@
import com.powsybl.iidm.network.Network;
import com.powsybl.nad.build.iidm.NetworkGraphBuilder;
import com.powsybl.nad.build.iidm.VoltageLevelFilter;
import com.powsybl.nad.layout.AbstractLayout.TextPosition;
import com.powsybl.nad.model.Graph;
import com.powsybl.nad.model.Point;

import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -98,4 +107,85 @@ void testBasicFixedLayoutFallback() {
assertEquals(0, actual.get("VL8").getX());
assertEquals(0, actual.get("VL8").getY());
}

@Test
void testMetadataUtils() throws URISyntaxException, IOException {
Network network = Networks.createTwoVoltageLevels();
Path metadataFile = Paths.get(getClass().getResource("/two-voltage-levels_metadata.json").toURI());

Layout layout = new FixedLayoutFactory(new HashMap<>()).create();
testEmptyLayout(layout, network);

layout = FixedLayoutFactoryUtils.create(metadataFile).create();
testMetadataLayout(layout, network);
layout = FixedLayoutFactoryUtils.create(metadataFile, BasicFixedLayout::new).create();
testMetadataLayout(layout, network);

try (InputStream metadataIS = Files.newInputStream(metadataFile)) {
layout = FixedLayoutFactoryUtils.create(metadataIS).create();
testMetadataLayout(layout, network);
}
try (InputStream metadataIS = Files.newInputStream(metadataFile)) {
layout = FixedLayoutFactoryUtils.create(metadataIS, BasicFixedLayout::new).create();
testMetadataLayout(layout, network);
}

try (Reader metadataReader = Files.newBufferedReader(metadataFile)) {
layout = FixedLayoutFactoryUtils.create(metadataReader).create();
testMetadataLayout(layout, network);
}
try (Reader metadataReader = Files.newBufferedReader(metadataFile)) {
layout = FixedLayoutFactoryUtils.create(metadataReader, BasicFixedLayout::new).create();
testMetadataLayout(layout, network);
}
}

void testEmptyLayout(Layout layout, Network network) {
Graph graph = new NetworkGraphBuilder(network, VoltageLevelFilter.NO_FILTER).buildGraph();
layout.run(graph, new LayoutParameters());
Map<String, Point> nodePositions = graph.getNodePositions();
checkNodePosition(nodePositions.get("dl1"), 0, 0);
checkNodePosition(nodePositions.get("vl1"), 0, 0);
checkNodePosition(nodePositions.get("vl2"), 0, 0);
Map<String, TextPosition> textNodesPositions = getTextNodesPositions(graph);
checkNodeShift(nodePositions.get("vl1"), textNodesPositions.get("vl1").topLeftPosition(), 100, -40);
checkNodeShift(nodePositions.get("vl1"), textNodesPositions.get("vl1").edgeConnection(), 100, -15);
checkNodeShift(nodePositions.get("vl2"), textNodesPositions.get("vl2").topLeftPosition(), 100, -40);
checkNodeShift(nodePositions.get("vl2"), textNodesPositions.get("vl2").edgeConnection(), 100, -15);
}

void testMetadataLayout(Layout layout, Network network) {
Graph graph = new NetworkGraphBuilder(network, VoltageLevelFilter.NO_FILTER).buildGraph();
layout.run(graph, new LayoutParameters());
Map<String, Point> nodePositions = graph.getNodePositions();
checkNodePosition(nodePositions.get("dl1"), -49.12, 317.14);
checkNodePosition(nodePositions.get("vl1"), -56.06, -318.7);
checkNodePosition(nodePositions.get("vl2"), -230.42, 1.18);
Map<String, TextPosition> textNodesPositions = getTextNodesPositions(graph);
checkNodeShift(nodePositions.get("vl1"), textNodesPositions.get("vl1").topLeftPosition(), 80, -30);
checkNodeShift(nodePositions.get("vl1"), textNodesPositions.get("vl1").edgeConnection(), 80, -5);
checkNodeShift(nodePositions.get("vl2"), textNodesPositions.get("vl2").topLeftPosition(), 80, -30);
checkNodeShift(nodePositions.get("vl2"), textNodesPositions.get("vl2").edgeConnection(), 80, -5);
}

Map<String, TextPosition> getTextNodesPositions(Graph graph) {
Map<String, TextPosition> textNodesPositions = new HashMap<>();
graph.getTextEdgesMap()
.values()
.forEach(nodePair -> textNodesPositions.put(nodePair.getFirst().getEquipmentId(),
new TextPosition(nodePair.getSecond().getPosition(),
nodePair.getSecond().getEdgeConnection())));
return textNodesPositions;
}

void checkNodePosition(Point point, double x, double y) {
assertEquals(x, point.getX());
assertEquals(y, point.getY());
}

void checkNodeShift(Point point, Point shiftedPoint, double shiftX, double shiftY) {
Point expectedPoint = point.shift(shiftX, shiftY);
assertEquals(expectedPoint.getX(), shiftedPoint.getX());
assertEquals(expectedPoint.getY(), shiftedPoint.getY());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
{
"layoutParameters" : {
"textNodesForceLayout" : false,
"springRepulsionFactorForceLayout" : 0.0,
"textNodeFixedShift" : {
"x" : 100.0,
"y" : -40.0
},
"maxSteps" : 1000,
"textNodeEdgeConnectionYShift" : 25.0
},
"svgParameters" : {
"diagramPadding" : {
"left" : 200.0,
"top" : 200.0,
"right" : 200.0,
"bottom" : 200.0
},
"insertNameDesc" : false,
"svgWidthAndHeightAdded" : false,
"cssLocation" : "INSERTED_IN_SVG",
"sizeConstraint" : "FIXED_SCALE",
"fixedWidth" : -1,
"fixedHeight" : -1,
"fixedScale" : 0.2,
"arrowShift" : 30.0,
"arrowLabelShift" : 19.0,
"converterStationWidth" : 70.0,
"voltageLevelCircleRadius" : 30.0,
"fictitiousVoltageLevelCircleRadius" : 15.0,
"transformerCircleRadius" : 20.0,
"nodeHollowWidth" : 15.0,
"edgesForkLength" : 80.0,
"edgesForkAperture" : 60.0,
"edgeStartShift" : 0.0,
"unknownBusNodeExtraRadius" : 10.0,
"loopDistance" : 120.0,
"loopEdgesAperture" : 60.0,
"loopControlDistance" : 40.0,
"edgeInfoAlongEdge" : true,
"edgeNameDisplayed" : false,
"interAnnulusSpace" : 5.0,
"svgPrefix" : "",
"idDisplayed" : false,
"substationDescriptionDisplayed" : false,
"arrowHeight" : 10.0,
"busLegend" : true,
"voltageLevelDetails" : false,
"languageTag" : "en",
"voltageValuePrecision" : 1,
"powerValuePrecision" : 0,
"angleValuePrecision" : 1,
"currentValuePrecision" : 0,
"edgeInfoDisplayed" : "ACTIVE_POWER",
"pstArrowHeadSize" : 8.0,
"undefinedValueSymbol" : ""
},
"busNodes" : [ {
"svgId" : "1",
"equipmentId" : "vl1_0",
"nbNeighbours" : 0,
"index" : 0,
"vlNode" : "0"
}, {
"svgId" : "3",
"equipmentId" : "vl2_0",
"nbNeighbours" : 0,
"index" : 0,
"vlNode" : "2"
}, {
"svgId" : "6",
"equipmentId" : "dl1",
"nbNeighbours" : 0,
"index" : 0,
"vlNode" : "5"
} ],
"nodes" : [ {
"svgId" : "0",
"equipmentId" : "vl1",
"x" : -56.06,
"y" : -318.7
}, {
"svgId" : "2",
"equipmentId" : "vl2",
"x" : -230.42,
"y" : 1.18
}, {
"svgId" : "5",
"equipmentId" : "dl1",
"x" : -49.12,
"y" : 317.14
} ],
"edges" : [ {
"svgId" : "4",
"equipmentId" : "l1",
"node1" : "0",
"node2" : "2",
"busNode1" : "1",
"busNode2" : "3",
"type" : "LineEdge"
}, {
"svgId" : "7",
"equipmentId" : "dl1",
"node1" : "2",
"node2" : "5",
"busNode1" : "3",
"busNode2" : "6",
"type" : "DanglingLineEdge"
} ],
"textNodes" : [ {
"svgId" : "0-textnode",
"equipmentId" : "vl1",
"vlNode" : "0",
"shiftX" : 80.0,
"shiftY" : -30.0,
"connectionShiftX" : 80.0,
"connectionShiftY" : -5.0
}, {
"svgId" : "2-textnode",
"equipmentId" : "vl2",
"vlNode" : "2",
"shiftX" : 80.0,
"shiftY" : -30.0,
"connectionShiftX" : 80.0,
"connectionShiftY" : -5.0
} ]
}