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

Geographical coordinates used as layout #592

Merged
merged 22 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a84b066
First attempt at using SubstationPosition extension
So-Fras Mar 5, 2024
2de82ce
Merge branch 'main' into geographical_layout
So-Fras Mar 19, 2024
900cacd
Merge branch 'main' into geographical_layout
So-Fras Mar 25, 2024
e48ab34
Fix wrong formula
So-Fras Mar 26, 2024
1e68a51
Add tests
So-Fras Mar 26, 2024
7f81bd9
Merge branch 'main' into geographical_layout
So-Fras Mar 26, 2024
6714432
Merge branch 'main' into geographical_layout
So-Fras Apr 2, 2024
4d812b3
Fix checkstyle
So-Fras Apr 2, 2024
acb0a8e
Merge branch 'main' into geographical_layout
So-Fras Apr 3, 2024
d254683
Merge branch 'main' into geographical_layout
So-Fras Apr 3, 2024
a50cd45
Redesign GeographicalLayoutFactory after review
So-Fras Apr 3, 2024
de74fe3
Replace last occurences of repulsionFactor with radiusFactor
So-Fras Apr 3, 2024
d5438ba
Merge branch 'main' into geographical_layout
So-Fras Apr 4, 2024
9c0fab0
First attempt at using Mercator projection
So-Fras Apr 4, 2024
f7b9fa4
Force layout centred on the initialPoints center of gravity
flo-dup Apr 4, 2024
116e795
Add a minus sign for projected y coordinate (y-axis oriented downwards)
So-Fras Apr 4, 2024
affc17c
Change default values for SCALING_FACTOR and RADIUS_FACTOR
So-Fras Apr 4, 2024
7801761
Remove implementation specific code
So-Fras Apr 4, 2024
f8a6439
Update test references
So-Fras Apr 4, 2024
f06158c
Add test to check preservation of relative positions
So-Fras Apr 4, 2024
de55fdd
Fix code smells
So-Fras Apr 4, 2024
24d2cc2
Merge branch 'main' into geographical_layout
flo-dup Apr 5, 2024
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
10 changes: 10 additions & 0 deletions diagram-test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,15 @@
<groupId>com.powsybl</groupId>
<artifactId>powsybl-iidm-extensions</artifactId>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-iidm-impl</artifactId>
flo-dup marked this conversation as resolved.
Show resolved Hide resolved
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-ieee-cdf-converter</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
12 changes: 12 additions & 0 deletions diagram-test/src/main/java/com/powsybl/diagram/test/Networks.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
package com.powsybl.diagram.test;

import com.powsybl.commons.extensions.Extendable;
import com.powsybl.ieeecdf.converter.IeeeCdfNetworkFactory;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.*;
import com.powsybl.iidm.network.impl.extensions.SubstationPositionAdderImplProvider;

import java.time.ZonedDateTime;
import java.util.Optional;
Expand Down Expand Up @@ -1706,6 +1708,16 @@ public static Network createNetworkWithBatteries() {
return network;
}

public static Network createIeee9NetworkWithOneMissingSubstationPosition() {
Network network = IeeeCdfNetworkFactory.create9();
new SubstationPositionAdderImplProvider().newAdder(network.getSubstation("S1")).withCoordinate(new Coordinate(2d, 3d)).add();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting the adder for an extension is done that way: substation.newExtension(SubstationPosition.class), which allows you not to be implementation-specific

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed! It seems to work with substation.newExtension(SubstationPositionAdder.class)

new SubstationPositionAdderImplProvider().newAdder(network.getSubstation("S2")).withCoordinate(new Coordinate(4d, 5d)).add();
new SubstationPositionAdderImplProvider().newAdder(network.getSubstation("S3")).withCoordinate(new Coordinate(6d, 7d)).add();
new SubstationPositionAdderImplProvider().newAdder(network.getSubstation("S5")).withCoordinate(new Coordinate(8d, 9d)).add();
new SubstationPositionAdderImplProvider().newAdder(network.getSubstation("S6")).withCoordinate(new Coordinate(10d, 11d)).add();
return network;
}

/**
* <PRE>
* l
Expand Down
4 changes: 4 additions & 0 deletions network-area-diagram/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
<groupId>com.powsybl</groupId>
<artifactId>powsybl-iidm-api</artifactId>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-iidm-extensions</artifactId>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-commons</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* 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 com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.Substation;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.iidm.network.extensions.Coordinate;
import com.powsybl.iidm.network.extensions.SubstationPosition;
import com.powsybl.nad.model.Point;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @author Sophie Frasnedo {@literal <sophie.frasnedo at rte-france.com>}
*/
public class GeographicalLayoutFactory extends FixedLayoutFactory implements LayoutFactory {

private static final int SCALING_FACTOR = 100;
private static final double RADIUS_FACTOR = 50;

public GeographicalLayoutFactory(Network network) {
this(network, SCALING_FACTOR, RADIUS_FACTOR, BasicForceLayout::new);
}

public GeographicalLayoutFactory(Network network, int scalingFactor, double radiusFactor, LayoutFactory layoutFactory) {
super(getFixedNodePosition(network, scalingFactor, radiusFactor), layoutFactory);
}

private static Map<String, Point> getFixedNodePosition(Network network, int scalingFactor, double radiusFactor) {
Map<String, Point> fixedNodePositionMap = new HashMap<>();
network.getSubstationStream().forEach(substation -> fillPositionMap(substation, fixedNodePositionMap, scalingFactor, radiusFactor));
return fixedNodePositionMap;
}

private static void fillPositionMap(Substation substation, Map<String, Point> fixedNodePositionMap, int scalingFactor, double radiusFactor) {
SubstationPosition substationPosition = substation.getExtension(SubstationPosition.class);
if (substationPosition != null) {
Coordinate coordinate = substationPosition.getCoordinate();
double latitude = coordinate.getLatitude();
double longitude = coordinate.getLongitude();

List<VoltageLevel> voltageLevelList = substation.getVoltageLevelStream().toList();
int voltageLevelListSize = voltageLevelList.size();

if (voltageLevelListSize == 1) {
String voltageLevelId = voltageLevelList.get(0).getId();
fixedNodePositionMap.put(voltageLevelId, new Point(longitude * scalingFactor, latitude * scalingFactor));
} else if (voltageLevelListSize > 1) {
//Deal with voltage levels within the same substation (and thus with the same coordinates)
double angle = 2 * Math.PI / voltageLevelListSize;
int i = 0;
for (VoltageLevel voltageLevel : voltageLevelList) {
double angleVoltageLevel = angle * i;
fixedNodePositionMap.put(voltageLevel.getId(), new Point(longitude * scalingFactor + radiusFactor * Math.cos(angleVoltageLevel), latitude * scalingFactor + radiusFactor * Math.sin(angleVoltageLevel)));
i++;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* 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 com.powsybl.diagram.test.Networks;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.TopologyKind;
import com.powsybl.nad.build.iidm.NetworkGraphBuilder;
import com.powsybl.nad.build.iidm.VoltageLevelFilter;
import com.powsybl.nad.model.Graph;
import com.powsybl.nad.model.Point;
import org.junit.jupiter.api.Test;

import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* @author Sophie Frasnedo {@literal <sophie.frasnedo at rte-france.com>}
*/
class LayoutWithGeographicalPositionsTest {

@Test
void layoutWithGeographicalPositionsTest() {
Network network = Networks.createIeee9NetworkWithOneMissingSubstationPosition();

Graph graph = new NetworkGraphBuilder(network, VoltageLevelFilter.NO_FILTER).buildGraph();
Layout forceLayout = new GeographicalLayoutFactory(network).create();
forceLayout.run(graph, new LayoutParameters());
Map<String, Point> actual = graph.getNodePositions();

assertEquals(300, actual.get("VL1").getX());
assertEquals(200, actual.get("VL1").getY());
assertEquals(500, actual.get("VL2").getX());
assertEquals(400, actual.get("VL2").getY());
}

@Test
void layoutWithGeographicalPositionsCustomisedParametersTest() {
Network network = Networks.createIeee9NetworkWithOneMissingSubstationPosition();

Graph graph = new NetworkGraphBuilder(network, VoltageLevelFilter.NO_FILTER).buildGraph();
Layout forceLayout = new GeographicalLayoutFactory(network, 200, 50d, BasicForceLayout::new).create();
forceLayout.run(graph, new LayoutParameters());
Map<String, Point> actual = graph.getNodePositions();

assertEquals(600, actual.get("VL1").getX());
assertEquals(400, actual.get("VL1").getY());
assertEquals(1000, actual.get("VL2").getX());
assertEquals(800, actual.get("VL2").getY());
}

@Test
void layoutWithGeographicalPositionsTwoVoltageLevelsInSameSubstationTest() {
Network network = Networks.createIeee9NetworkWithOneMissingSubstationPosition();
network.getSubstation("S1").newVoltageLevel().setNominalV(400d).setTopologyKind(TopologyKind.BUS_BREAKER).setId("VL1_1").add();
network.getSubstation("S1").newVoltageLevel().setNominalV(400d).setTopologyKind(TopologyKind.BUS_BREAKER).setId("VL1_2").add();
Graph graph = new NetworkGraphBuilder(network, VoltageLevelFilter.NO_FILTER).buildGraph();
Layout forceLayout = new GeographicalLayoutFactory(network, 100, 50d, BasicForceLayout::new).create();
forceLayout.run(graph, new LayoutParameters());
Map<String, Point> actual = graph.getNodePositions();

assertEquals(350, actual.get("VL1").getX());
assertEquals(200, actual.get("VL1").getY());
assertEquals(275, actual.get("VL1_1").getX());
assertEquals(243.3d, actual.get("VL1_1").getY(), 0.1);
assertEquals(275, actual.get("VL1_2").getX());
assertEquals(156.7, actual.get("VL1_2").getY(), 0.1);
}
}