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

Save network area diagram metadata in JSON format #646

Merged
merged 9 commits into from
Oct 15, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* 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.diagram.metadata;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.powsybl.commons.json.JsonUtil;

/**
* @author Massimo Ferraro {@literal <massimo.ferraro@soft.it>}
*/
public abstract class AbstractMetadata {

public void writeJson(Path file) {
Objects.requireNonNull(file);
try (Writer writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
writeJson(writer);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

public void writeJson(Writer writer) {
Objects.requireNonNull(writer);
ObjectMapper objectMapper = JsonUtil.createObjectMapper();
try {
objectMapper.writerWithDefaultPrettyPrinter()
.writeValue(writer, this);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
*/
package com.powsybl.nad;

import com.powsybl.commons.PowsyblException;
import com.powsybl.diagram.metadata.AbstractMetadata;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.nad.build.iidm.NetworkGraphBuilder;
import com.powsybl.nad.build.iidm.VoltageLevelFilter;
import com.powsybl.nad.model.Graph;
import com.powsybl.nad.svg.SvgParameters;
import com.powsybl.nad.svg.SvgWriter;
import com.powsybl.nad.svg.metadata.DiagramMetadata;
import org.apache.commons.io.output.NullWriter;

import java.io.IOException;
import java.io.StringWriter;
Expand All @@ -37,66 +39,83 @@ public static void draw(Network network, Path svgFile) {
draw(network, svgFile, new NadParameters(), VoltageLevelFilter.NO_FILTER);
}

public static void draw(Network network, Writer writer) {
draw(network, writer, new NadParameters(), VoltageLevelFilter.NO_FILTER);
public static void draw(Network network, Writer writer, Writer metadataWriter) {
draw(network, writer, metadataWriter, new NadParameters(), VoltageLevelFilter.NO_FILTER);
}

public static void draw(Network network, Path svgFile, String voltageLevelId, int depth) {
draw(network, svgFile, new NadParameters(), VoltageLevelFilter.createVoltageLevelDepthFilter(network, voltageLevelId, depth));
}

public static void draw(Network network, Writer writer, String voltageLevelId, int depth) {
draw(network, writer, new NadParameters(), VoltageLevelFilter.createVoltageLevelDepthFilter(network, voltageLevelId, depth));
public static void draw(Network network, Writer writer, Writer metadataWriter, String voltageLevelId, int depth) {
draw(network, writer, metadataWriter, new NadParameters(), VoltageLevelFilter.createVoltageLevelDepthFilter(network, voltageLevelId, depth));
}

public static void draw(Network network, Path svgFile, List<String> voltageLevelIds) {
draw(network, svgFile, new NadParameters(), VoltageLevelFilter.createVoltageLevelsFilter(network, voltageLevelIds));
}

public static void draw(Network network, Writer writer, List<String> voltageLevelIds) {
draw(network, writer, new NadParameters(), VoltageLevelFilter.createVoltageLevelsFilter(network, voltageLevelIds));
public static void draw(Network network, Writer writer, Writer metadataWriter, List<String> voltageLevelIds) {
draw(network, writer, metadataWriter, new NadParameters(), VoltageLevelFilter.createVoltageLevelsFilter(network, voltageLevelIds));
}

public static void draw(Network network, Path svgFile, List<String> voltageLevelIds, int depth) {
draw(network, svgFile, new NadParameters(), VoltageLevelFilter.createVoltageLevelsDepthFilter(network, voltageLevelIds, depth));
}

public void draw(Network network, Writer writer, Predicate<VoltageLevel> voltageLevelFilter) {
draw(network, writer, new NadParameters(), voltageLevelFilter);
public static void draw(Network network, Writer writer, Writer metadataWriter, Predicate<VoltageLevel> voltageLevelFilter) {
draw(network, writer, metadataWriter, new NadParameters(), voltageLevelFilter);
}

public static void draw(Network network, Path svgFile, NadParameters param, Predicate<VoltageLevel> voltageLevelFilter) {
genericDraw(network, svgFile, param, voltageLevelFilter);
}
Objects.requireNonNull(network);
Objects.requireNonNull(svgFile);
Objects.requireNonNull(param);

public static void draw(Network network, Writer writer, NadParameters param, Predicate<VoltageLevel> voltageLevelFilter) {
genericDraw(network, writer, param, voltageLevelFilter);
Graph graph = getLayoutResult(network, param, voltageLevelFilter);
createSvgWriter(network, param).writeSvg(graph, svgFile);
createMetadata(graph, param).writeJson(getMetadataPath(svgFile));
}

private static void genericDraw(Network network, Object object, NadParameters param, Predicate<VoltageLevel> voltageLevelFilter) {
public static void draw(Network network, Writer writer, Writer metadataWriter, NadParameters param, Predicate<VoltageLevel> voltageLevelFilter) {
Objects.requireNonNull(network);
Objects.requireNonNull(object);
Objects.requireNonNull(writer);
Objects.requireNonNull(metadataWriter);
Objects.requireNonNull(param);
Objects.requireNonNull(voltageLevelFilter);

Graph graph = getLayoutResult(network, param, voltageLevelFilter);
createSvgWriter(network, param).writeSvg(graph, writer);
createMetadata(graph, param).writeJson(metadataWriter);
}

private static AbstractMetadata createMetadata(Graph graph, NadParameters param) {
return new DiagramMetadata(param.getLayoutParameters(), param.getSvgParameters()).addMetadata(graph);
}

private static Graph getLayoutResult(Network network, NadParameters param, Predicate<VoltageLevel> voltageLevelFilter) {
Objects.requireNonNull(voltageLevelFilter);
Graph graph = new NetworkGraphBuilder(network, voltageLevelFilter, param.getIdProviderFactory().create()).buildGraph();
param.getLayoutFactory().create().run(graph, param.getLayoutParameters());
SvgWriter svgWriter = new SvgWriter(param.getSvgParameters(), param.getStyleProviderFactory().create(network), param.createLabelProvider(network),
param.getLayoutParameters());

if (object instanceof Path svgFile) {
svgWriter.writeSvg(graph, svgFile);
} else if (object instanceof Writer writer) {
svgWriter.writeSvg(graph, writer);
} else {
throw new PowsyblException("Second argument is an instance of an unexpected class");
return graph;
}

private static SvgWriter createSvgWriter(Network network, NadParameters param) {
return new SvgWriter(param.getSvgParameters(), param.getStyleProviderFactory().create(network), param.createLabelProvider(network));
}

private static Path getMetadataPath(Path svgPath) {
Path dir = svgPath.toAbsolutePath().getParent();
String svgFileName = svgPath.getFileName().toString();
if (!svgFileName.endsWith(".svg")) {
svgFileName = svgFileName + ".svg";
}
return dir.resolve(svgFileName.replace(".svg", "_metadata.json"));
}

public String drawToString(Network network, SvgParameters svgParameters) {
public static String drawToString(Network network, SvgParameters svgParameters) {
try (StringWriter writer = new StringWriter()) {
NadParameters nadParameters = new NadParameters().setSvgParameters(svgParameters);
draw(network, writer, nadParameters, VoltageLevelFilter.NO_FILTER);
draw(network, writer, NullWriter.INSTANCE, nadParameters, VoltageLevelFilter.NO_FILTER);
return writer.toString();
} catch (IOException e) {
throw new UncheckedIOException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private void computeMultiBranchEdgesCoordinates(Graph graph, List<BranchEdge> ed
double angle = Math.atan2(dy, dx);

int nbForks = edges.size();
double forkAperture = svgParameters.getEdgesForkAperture();
double forkAperture = Math.toRadians(svgParameters.getEdgesForkAperture());
double forkLength = svgParameters.getEdgesForkLength();
double angleStep = forkAperture / (nbForks - 1);

Expand Down Expand Up @@ -135,7 +135,7 @@ private void loopEdgesHalfLayout(Graph graph, VoltageLevelNode node, SvgParamete
BranchEdge edge, BranchEdge.Side side, double angle, Point middle) {

int sideSign = side == BranchEdge.Side.ONE ? -1 : 1;
double startAngle = angle + sideSign * svgParameters.getLoopEdgesAperture() / 2;
double startAngle = angle + sideSign * Math.toRadians(svgParameters.getLoopEdgesAperture() / 2);
double radius = svgParameters.getTransformerCircleRadius();
double controlsDist = svgParameters.getLoopControlDistance();
boolean isTransformer = edge.isTransformerEdge();
Expand All @@ -161,7 +161,7 @@ private List<Double> computeLoopAngles(Graph graph, List<BranchEdge> loopEdges,
List<Double> loopAngles = new ArrayList<>();
if (!anglesOtherEdges.isEmpty()) {
anglesOtherEdges.add(anglesOtherEdges.get(0) + 2 * Math.PI);
double apertureWithMargin = svgParameters.getLoopEdgesAperture() * 1.2;
double apertureWithMargin = Math.toRadians(svgParameters.getLoopEdgesAperture() * 1.2);

double[] deltaAngles = new double[anglesOtherEdges.size() - 1];
int nbSeparatedSlots = 0;
Expand Down Expand Up @@ -218,7 +218,7 @@ private void computeLoopAnglesWhenEnoughSharedSlotsPresent(int initNbExcessiveRe
break;
}
int nbLoopsInDelta = Math.min(nbAvailableSlots, nbExcessiveRemaining + 1);
double extraSpace = deltaAngles[iSorted] - svgParameters.getLoopEdgesAperture() * nbLoopsInDelta; // extra space without margins
double extraSpace = deltaAngles[iSorted] - Math.toRadians(svgParameters.getLoopEdgesAperture()) * nbLoopsInDelta; // extra space without margins
double intraSpace = extraSpace / (nbLoopsInDelta + 1); // space between two loops and between non-loop edges and first/last loop
double angleStep = (anglesOtherEdges.get(iSorted + 1) - anglesOtherEdges.get(iSorted) - intraSpace) / nbLoopsInDelta;
double startAngle = anglesOtherEdges.get(iSorted) + intraSpace / 2 + angleStep / 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
*/
package com.powsybl.nad.svg;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* @author Florian Dupuy {@literal <florian.dupuy at rte-france.com>}
*/
Expand All @@ -16,7 +19,8 @@ public class Padding {
private double right;
private double bottom;

public Padding(double left, double top, double right, double bottom) {
@JsonCreator
public Padding(@JsonProperty("left") double left, @JsonProperty("top") double top, @JsonProperty("right") double right, @JsonProperty("bottom") double bottom) {
this.left = left;
this.top = top;
this.right = right;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ public class SvgParameters {
private double transformerCircleRadius = 20;
private double nodeHollowWidth = 15;
private double edgesForkLength = 80;
private double edgesForkAperture = Math.toRadians(60);
private double edgesForkAperture = 60;
private double edgeStartShift = 0;
private double unknownBusNodeExtraRadius = 10;
private double loopDistance = 120;
private double loopEdgesAperture = Math.toRadians(60);
private double loopEdgesAperture = 60;
private double loopControlDistance = 40;
private boolean edgeInfoAlongEdge = true;
private boolean edgeNameDisplayed = false;
Expand Down Expand Up @@ -239,24 +239,38 @@ public SvgParameters setNodeHollowWidth(double nodeHollowWidth) {
return this;
}

/**
* Set the aperture of the forks corresponding to parallel edges
* @param edgesForkApertureDegrees the aperture in degrees
*/
public SvgParameters setEdgesForkAperture(double edgesForkApertureDegrees) {
this.edgesForkAperture = edgesForkApertureDegrees;
return this;
}

/**
* Return the aperture of the forks corresponding to parallel edges, in degrees.
*/
public double getEdgesForkAperture() {
return edgesForkAperture;
}

public SvgParameters setEdgesForkAperture(double edgesForkApertureDegrees) {
this.edgesForkAperture = Math.toRadians(edgesForkApertureDegrees);
/**
* Set the aperture of the loop edges
* @param loopEdgesApertureDegrees the aperture in degrees
*/
public SvgParameters setLoopEdgesAperture(double loopEdgesApertureDegrees) {
this.loopEdgesAperture = loopEdgesApertureDegrees;
return this;
}

/**
* Return the aperture of the loop edges, in degrees.
*/
public double getLoopEdgesAperture() {
return loopEdgesAperture;
}

public SvgParameters setLoopEdgesAperture(double loopEdgesApertureDegrees) {
this.loopEdgesAperture = Math.toRadians(loopEdgesApertureDegrees);
return this;
}

public double getEdgesForkLength() {
return edgesForkLength;
}
Expand Down
Loading