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

Add geographical coordinates to network for NAD drawing and use GeographicalLayout #102

Merged
merged 17 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
23 changes: 21 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<!-- FIXME: powsybl-network-store modules'version is overloaded in the dependencies section.The overloads and this property below have to be removed at next powsybl-ws-dependencies.version upgrade -->
souissimai marked this conversation as resolved.
Show resolved Hide resolved
<powsybl-network-store.version>1.11.1</powsybl-network-store.version>
<powsybl-ws-dependencies.version>2.10.0</powsybl-ws-dependencies.version>
</properties>

Expand Down Expand Up @@ -85,7 +86,25 @@
<dependencyManagement>
<dependencies>
<!-- overrides of imports -->

<!-- overrides of imports -->
<!-- FIXME: to be removed at next powsybl-ws-dependencies upgrade -->
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-network-store-iidm-impl</artifactId>
<version>${powsybl-network-store.version}</version>
</dependency>
<!-- FIXME: to be removed at next powsybl-ws-dependencies upgrade -->
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-network-store-client</artifactId>
<version>${powsybl-network-store.version}</version>
</dependency>
<!-- FIXME: to be removed at next powsybl-ws-dependencies upgrade -->
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-network-store-model</artifactId>
<version>${powsybl-network-store.version}</version>
</dependency>
<!-- imports -->
<dependency>
<groupId>com.powsybl</groupId>
Expand Down
153 changes: 153 additions & 0 deletions src/main/java/com/powsybl/nad/build/iidm/VoltageLevelFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/**
* 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/.
*/
package com.powsybl.nad.build.iidm;

import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.nad.utils.iidm.IidmUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
* @author Maissa Souissi {<maissa.souissi at rte-france.com>}
*/
public class VoltageLevelFilter implements Predicate<VoltageLevel> {
souissimai marked this conversation as resolved.
Show resolved Hide resolved

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

public static final Predicate<VoltageLevel> NO_FILTER = voltageLevel -> true;

private static final String UNKNOWN_VOLTAGE_LEVEL = "Unknown voltage level id '";

private final Set<VoltageLevel> voltageLevels;

public VoltageLevelFilter(Set<VoltageLevel> voltageLevels) {
this.voltageLevels = voltageLevels;
}

public int getNbVoltageLevels() {
return voltageLevels.size();
}

public Set<VoltageLevel> getVoltageLevels() {
return voltageLevels;
}

souissimai marked this conversation as resolved.
Show resolved Hide resolved
@Override
public boolean test(VoltageLevel voltageLevel) {
return voltageLevels.contains(voltageLevel);
}

public static VoltageLevelFilter createVoltageLevelsDepthFilter(Network network, List<String> voltageLevelIds, int depth) {
return createVoltageLevelFilterWithPredicate(network, voltageLevelIds, depth, NO_FILTER);
}

public static VoltageLevelFilter createVoltageLevelFilterWithPredicate(Network network, List<String> voltageLevelIds, int depth, Predicate<VoltageLevel> voltageLevelPredicate) {
Objects.requireNonNull(network);
Objects.requireNonNull(voltageLevelIds);
Set<VoltageLevel> startingSet = new HashSet<>();

for (String voltageLevelId : voltageLevelIds) {
VoltageLevel vl = network.getVoltageLevel(voltageLevelId);
if (vl == null) {
throw new PowsyblException(UNKNOWN_VOLTAGE_LEVEL + voltageLevelId + "'");
}
if (!voltageLevelPredicate.test(vl)) {
LOGGER.warn("vl '{}' does not comply with the predicate", voltageLevelId);
}
startingSet.add(vl);
}
Set<VoltageLevel> voltageLevels = new HashSet<>();
VoltageLevelFilter.traverseVoltageLevels(startingSet, depth, voltageLevels, voltageLevelPredicate);
return new VoltageLevelFilter(voltageLevels);
}

public static Collection<VoltageLevel> getNextDepthVoltageLevels(Network network, List<VoltageLevel> voltageLevels) {
List<String> voltageLevelIds = voltageLevels.stream().map(VoltageLevel::getId).collect(Collectors.toList());
VoltageLevelFilter voltageLevelFilter = createVoltageLevelsDepthFilter(network, voltageLevelIds, 1);
Set<VoltageLevel> voltageLevelSet = new HashSet<>(voltageLevelFilter.getVoltageLevels());
voltageLevels.forEach(voltageLevelSet::remove);
return voltageLevelSet;
}

private static void traverseVoltageLevels(Set<VoltageLevel> voltageLevelsDepth, int depth, Set<VoltageLevel> visitedVoltageLevels, Predicate<VoltageLevel> predicate) {
if (depth < 0) {
return;
}
Set<VoltageLevel> nextDepthVoltageLevels = new HashSet<>();
for (VoltageLevel vl : voltageLevelsDepth) {
if (!visitedVoltageLevels.contains(vl)) {
visitedVoltageLevels.add(vl);
vl.visitEquipments(new VlVisitor(nextDepthVoltageLevels, visitedVoltageLevels, predicate));
}
}
traverseVoltageLevels(nextDepthVoltageLevels, depth - 1, visitedVoltageLevels, predicate);
}

private static class VlVisitor extends DefaultTopologyVisitor {
private final Set<VoltageLevel> nextDepthVoltageLevels;
private final Set<VoltageLevel> visitedVoltageLevels;
private final Predicate<VoltageLevel> voltageLevelPredicate;

public VlVisitor(Set<VoltageLevel> nextDepthVoltageLevels, Set<VoltageLevel> visitedVoltageLevels, Predicate<VoltageLevel> voltageLevelPredicate) {
this.nextDepthVoltageLevels = nextDepthVoltageLevels;
this.visitedVoltageLevels = visitedVoltageLevels;
this.voltageLevelPredicate = voltageLevelPredicate;
}

@Override
public void visitLine(Line line, TwoSides side) {
visitBranch(line, side);
}

@Override
public void visitTwoWindingsTransformer(TwoWindingsTransformer twt, TwoSides side) {
visitBranch(twt, side);
}

@Override
public void visitThreeWindingsTransformer(ThreeWindingsTransformer twt, ThreeSides side) {
if (side == ThreeSides.ONE) {
visitTerminal(twt.getTerminal(ThreeSides.TWO));
visitTerminal(twt.getTerminal(ThreeSides.THREE));
} else if (side == ThreeSides.TWO) {
visitTerminal(twt.getTerminal(ThreeSides.ONE));
visitTerminal(twt.getTerminal(ThreeSides.THREE));
} else {
visitTerminal(twt.getTerminal(ThreeSides.ONE));
visitTerminal(twt.getTerminal(ThreeSides.TWO));
}
}

@Override
public void visitHvdcConverterStation(HvdcConverterStation<?> converterStation) {
converterStation.getOtherConverterStation().ifPresent(c -> visitTerminal(c.getTerminal()));
}

private void visitBranch(Branch<?> branch, TwoSides side) {
visitTerminal(branch.getTerminal(IidmUtils.getOpposite(side)));
}

private void visitTerminal(Terminal terminal) {
VoltageLevel voltageLevel = terminal.getVoltageLevel();
if (!visitedVoltageLevels.contains(voltageLevel) && voltageLevelPredicate.test(voltageLevel)) {
nextDepthVoltageLevels.add(voltageLevel);
}
}

@Override
public void visitDanglingLine(DanglingLine danglingLine) {
if (danglingLine.isPaired()) {
danglingLine.getTieLine().ifPresent(tieline -> visitBranch(tieline, tieline.getSide(danglingLine.getTerminal())));
}
}
}
}
139 changes: 139 additions & 0 deletions src/main/java/com/powsybl/sld/server/GeoDataService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* 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/.
*/

package com.powsybl.sld.server;

/**
* @author Maissa SOUISSI <maissa.souissi at rte-france.com>
*/

import com.fasterxml.jackson.databind.ObjectMapper;
import com.powsybl.iidm.network.Line;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.Substation;
import com.powsybl.iidm.network.extensions.LinePosition;
import com.powsybl.iidm.network.extensions.LinePositionAdder;
import com.powsybl.iidm.network.extensions.SubstationPosition;
import com.powsybl.iidm.network.extensions.SubstationPositionAdder;
import com.powsybl.sld.server.dto.Coordinate;
import com.powsybl.sld.server.dto.LineGeoData;
import com.powsybl.sld.server.dto.SubstationGeoData;
import com.powsybl.sld.server.utils.GeoDataUtils;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class GeoDataService {

public static final String QUERY_PARAM_SUBSTATION_ID = "substationId";
public static final String GEO_DATA_API_VERSION = "v1";
public static final String QUERY_PARAM_VARIANT_ID = "variantId";
public static final String NETWORK_UUID = "networkUuid";
static final String QUERY_PARAM_LINE_ID = "lineId";
souissimai marked this conversation as resolved.
Show resolved Hide resolved
static final String LINES = "lines";
souissimai marked this conversation as resolved.
Show resolved Hide resolved
static final String SUBSTATIONS = "substations";
private static final String DELIMITER = "/";
private final RestTemplate restTemplate;
@Setter
private String geoDataServerBaseUri;

public GeoDataService(@Value("${gridsuite.services.geo-data-server.base-uri:http://geo-data-server/}") String geoDataServerBaseUri,
RestTemplate restTemplate) {
this.geoDataServerBaseUri = geoDataServerBaseUri;
this.restTemplate = restTemplate;
}

private String getGeoDataServerURI() {
return this.geoDataServerBaseUri + DELIMITER + GEO_DATA_API_VERSION + DELIMITER;
}

public String getLinesGraphics(UUID networkUuid, String variantId, List<String> linesIds) {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(getGeoDataServerURI() + LINES)
.queryParam(NETWORK_UUID, networkUuid);

if (!StringUtils.isBlank(variantId)) {
uriComponentsBuilder.queryParam(QUERY_PARAM_VARIANT_ID, variantId);
}

if (linesIds != null) {
uriComponentsBuilder.queryParam(QUERY_PARAM_LINE_ID, linesIds);
}

var path = uriComponentsBuilder
.buildAndExpand()
.toUriString();

return restTemplate.getForObject(path, String.class);
}

public String getSubstationsGraphics(UUID networkUuid, String variantId, List<String> substationsIds) {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(getGeoDataServerURI() + SUBSTATIONS)
.queryParam(NETWORK_UUID, networkUuid);

if (!StringUtils.isBlank(variantId)) {
uriComponentsBuilder.queryParam(QUERY_PARAM_VARIANT_ID, variantId);
}

if (substationsIds != null) {
uriComponentsBuilder.queryParam(QUERY_PARAM_SUBSTATION_ID, substationsIds);
}

var path = uriComponentsBuilder
.buildAndExpand()
.toUriString();

return restTemplate.getForObject(path, String.class);
}

public void assignSubstationGeoData(Network network, UUID networkUuid, String variantId, List<Substation> substations) {
souissimai marked this conversation as resolved.
Show resolved Hide resolved

List<SubstationGeoData> substationsGeoData = GeoDataUtils.fromStringToSubstationGeoData(getSubstationsGraphics(networkUuid, variantId, null), new ObjectMapper());
Map<String, Coordinate> substationGeoDataMap = substationsGeoData.stream()
.collect(Collectors.toMap(SubstationGeoData::getId, SubstationGeoData::getCoordinate));

for (Substation substation : substations) {
if (network.getSubstation(substation.getId()).getExtension(SubstationPosition.class) == null) {
com.powsybl.sld.server.dto.Coordinate coordinate = substationGeoDataMap.get(substation.getId());
if (coordinate != null) {
network.getSubstation(substation.getId())
.newExtension(SubstationPositionAdder.class)
.withCoordinate(new com.powsybl.iidm.network.extensions.Coordinate(coordinate.getLat(), coordinate.getLon()))
.add();
}
}

}
}

public void assignLineGeoData(Network network, UUID networkUuid, String variantId, List<Line> lines) {
souissimai marked this conversation as resolved.
Show resolved Hide resolved
List<LineGeoData> linesGeoData = GeoDataUtils.fromStringToLineGeoData(getLinesGraphics(networkUuid, variantId, null), new ObjectMapper());
Map<String, List<com.powsybl.sld.server.dto.Coordinate>> lineGeoDataMap = linesGeoData.stream()
.collect(Collectors.toMap(LineGeoData::getId, LineGeoData::getCoordinates));
for (Line line : lines) {
if (network.getLine(line.getId()).getExtension(LinePosition.class) == null) {
List<com.powsybl.sld.server.dto.Coordinate> coordinates = lineGeoDataMap.get(line.getId());
if (coordinates != null) {
network.getLine(line.getId())
.newExtension(LinePositionAdder.class)
.withCoordinates(coordinates)
.add();
}
}
}
}
}


Loading
Loading