|
| 1 | +/** |
| 2 | + * Copyright (c) 2025, RTE (http://www.rte-france.com) |
| 3 | + * This Source Code Form is subject to the terms of the Mozilla Public |
| 4 | + * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 5 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 6 | + * SPDX-License-Identifier: MPL-2.0 |
| 7 | + */ |
| 8 | +package com.powsybl.nad.svg; |
| 9 | + |
| 10 | +import com.powsybl.nad.model.BranchEdge; |
| 11 | +import com.powsybl.nad.model.BusNode; |
| 12 | +import com.powsybl.nad.model.Edge; |
| 13 | +import com.powsybl.nad.model.ThreeWtEdge; |
| 14 | + |
| 15 | +import java.util.*; |
| 16 | +import java.util.stream.Collectors; |
| 17 | +import java.util.stream.Stream; |
| 18 | + |
| 19 | +/** |
| 20 | + * Enables the customization of the style of NAD elements: Bus nodes, branch-edges and three-winding-transformers edges. |
| 21 | + * |
| 22 | + * <p> |
| 23 | + * NAD elements'style data is defined in the CustomStyleProvider constructor's map parameters. |
| 24 | + * |
| 25 | + * <p> |
| 26 | + * The busNodesStyles map is indexed by the bus ID and defines the style for the bus nodes. |
| 27 | + * In the map, a node style is declared in a BusNodeStyles record: fill, edge and edgeWidth are the fill color, the edge color and the edge size for the node, respectively. |
| 28 | + * |
| 29 | + * <p> |
| 30 | + * The edgesStyles map is indexed by the branch ID and defines the style for the edges. |
| 31 | + * In the map, the edge style is declared in a EdgeStyles record: edge1, width1 and dash1 are the color, the size and a dash pattern for the first half edge, respectively. |
| 32 | + * Edge2, width2 and dash2 are the color, the size and a dash pattern for the second half edge. |
| 33 | + * |
| 34 | + * <p> |
| 35 | + * The threeWtsStyles map is index by the three-winding-transformer ID and defines the style for the transformer’s legs. |
| 36 | + * In the map, the style is declared in a ThreeWtStyles record: edge1, width1, dash1, edge2, width2 and dash2, edge3, width23 and dash3, |
| 37 | + * are the color, the size and a dash pattern for the three legs of the transformer. |
| 38 | + * |
| 39 | + * <p> |
| 40 | + * Note that the edge size is a string, it can be specified in pixel (e.g, 4px). |
| 41 | + * A dash pattern is a string with a sequence of comma and/or white space separated lengths and percentages, that specify the lengths of alternating dashes and gaps in the edge. |
| 42 | + * Elements that do not have a style specified in the parameters will be displayed with a default style. |
| 43 | + * |
| 44 | + * @author Christian Biasuzzi {@literal <christian.biasuzzi at soft.it>} |
| 45 | + */ |
| 46 | +public class CustomStyleProvider extends AbstractStyleProvider { |
| 47 | + |
| 48 | + final Map<String, BusNodeStyles> busNodesStyles; |
| 49 | + final Map<String, EdgeStyles> edgesStyles; |
| 50 | + final Map<String, ThreeWtStyles> threeWtsStyles; |
| 51 | + |
| 52 | + public record BusNodeStyles(String fill, String edge, String edgeWidth) { |
| 53 | + } |
| 54 | + |
| 55 | + public record EdgeStyles(String edge1, String width1, String dash1, String edge2, String width2, |
| 56 | + String dash2) { |
| 57 | + } |
| 58 | + |
| 59 | + public record ThreeWtStyles(String edge1, String width1, String dash1, String edge2, String width2, |
| 60 | + String dash2, String edge3, String width3, String dash3) { |
| 61 | + } |
| 62 | + |
| 63 | + private record EdgeStyle(String stroke, String strokeWidth, String dash) { |
| 64 | + } |
| 65 | + |
| 66 | + public CustomStyleProvider(Map<String, BusNodeStyles> busNodesStyles, Map<String, EdgeStyles> edgesStyles, |
| 67 | + Map<String, ThreeWtStyles> threeWtsStyles) { |
| 68 | + this.busNodesStyles = Objects.requireNonNull(busNodesStyles); |
| 69 | + this.edgesStyles = Objects.requireNonNull(edgesStyles); |
| 70 | + this.threeWtsStyles = Objects.requireNonNull(threeWtsStyles); |
| 71 | + } |
| 72 | + |
| 73 | + @Override |
| 74 | + public List<String> getCssFilenames() { |
| 75 | + return Collections.singletonList("customStyle.css"); |
| 76 | + } |
| 77 | + |
| 78 | + @Override |
| 79 | + public String getBusNodeStyle(BusNode busNode) { |
| 80 | + BusNodeStyles style = busNodesStyles.get(busNode.getEquipmentId()); |
| 81 | + if (style != null) { |
| 82 | + List<String> parts = new ArrayList<>(); |
| 83 | + if (style.fill() != null) { |
| 84 | + parts.add(String.format("background:%s; fill:%s;", style.fill(), style.fill())); |
| 85 | + } |
| 86 | + if (style.edge() != null) { |
| 87 | + parts.add(String.format("stroke:%s;", style.edge())); |
| 88 | + parts.add(String.format("border: solid %s %s;", style.edge(), style.edgeWidth() != null ? style.edgeWidth() : "1px")); |
| 89 | + } |
| 90 | + if (style.edgeWidth() != null) { |
| 91 | + parts.add(String.format("stroke-width:%s;", style.edgeWidth())); |
| 92 | + } |
| 93 | + return parts.isEmpty() ? null : String.join(" ", parts); |
| 94 | + } |
| 95 | + return null; |
| 96 | + } |
| 97 | + |
| 98 | + private EdgeStyle getEdgeStyle(EdgeStyles styles, BranchEdge.Side side) { |
| 99 | + return (side == BranchEdge.Side.ONE) |
| 100 | + ? new EdgeStyle(styles.edge1(), styles.width1(), styles.dash1()) |
| 101 | + : new EdgeStyle(styles.edge2(), styles.width2(), styles.dash2()); |
| 102 | + } |
| 103 | + |
| 104 | + private String formatEdgeStyle(EdgeStyle lineStyle) { |
| 105 | + return Stream.of( |
| 106 | + Optional.ofNullable(lineStyle.stroke()).map(stroke -> String.format("stroke:%s;", stroke)).orElse(null), |
| 107 | + Optional.ofNullable(lineStyle.strokeWidth()).map(width -> String.format("stroke-width:%s;", width)).orElse(null), |
| 108 | + Optional.ofNullable(lineStyle.dash()).map(dash -> String.format("stroke-dasharray:%s;", dash)).orElse(null) |
| 109 | + ) |
| 110 | + .filter(Objects::nonNull) |
| 111 | + .collect(Collectors.joining(" ")); |
| 112 | + } |
| 113 | + |
| 114 | + private EdgeStyle getThreeWtStyle(ThreeWtStyles styles, ThreeWtEdge.Side side) { |
| 115 | + return switch (side) { |
| 116 | + case ONE -> new EdgeStyle(styles.edge1(), styles.width1(), styles.dash1()); |
| 117 | + case TWO -> new EdgeStyle(styles.edge2(), styles.width2(), styles.dash2()); |
| 118 | + case THREE -> new EdgeStyle(styles.edge3(), styles.width3(), styles.dash3()); |
| 119 | + }; |
| 120 | + } |
| 121 | + |
| 122 | + @Override |
| 123 | + public String getSideEdgeStyle(BranchEdge edge, BranchEdge.Side side) { |
| 124 | + return Optional.ofNullable(edgesStyles.get(edge.getEquipmentId())) |
| 125 | + .map(styles -> formatEdgeStyle(getEdgeStyle(styles, side))) |
| 126 | + .orElse(null); |
| 127 | + } |
| 128 | + |
| 129 | + @Override |
| 130 | + public String getThreeWtEdgeStyle(ThreeWtEdge threeWtEdge) { |
| 131 | + ThreeWtEdge.Side side = threeWtEdge.getSide(); |
| 132 | + return Optional.ofNullable(threeWtsStyles.get(threeWtEdge.getEquipmentId())) |
| 133 | + .map(styles -> formatEdgeStyle(getThreeWtStyle(styles, side))) |
| 134 | + .orElse(null); |
| 135 | + } |
| 136 | + |
| 137 | + @Override |
| 138 | + public List<String> getEdgeInfoStyleClasses(EdgeInfo info) { |
| 139 | + List<String> styles = new LinkedList<>(); |
| 140 | + info.getDirection().ifPresent(direction -> styles.add( |
| 141 | + CLASSES_PREFIX + (direction == EdgeInfo.Direction.OUT ? "state-out" : "state-in"))); |
| 142 | + return styles; |
| 143 | + } |
| 144 | + |
| 145 | + @Override |
| 146 | + protected boolean isDisconnected(ThreeWtEdge threeWtEdge) { |
| 147 | + return false; |
| 148 | + } |
| 149 | + |
| 150 | + @Override |
| 151 | + protected boolean isDisconnected(BranchEdge branchEdge) { |
| 152 | + return false; |
| 153 | + } |
| 154 | + |
| 155 | + @Override |
| 156 | + protected boolean isDisconnected(BranchEdge edge, BranchEdge.Side side) { |
| 157 | + return false; |
| 158 | + } |
| 159 | + |
| 160 | + @Override |
| 161 | + protected Optional<String> getBaseVoltageStyle(Edge edge) { |
| 162 | + return Optional.empty(); |
| 163 | + } |
| 164 | + |
| 165 | + @Override |
| 166 | + protected Optional<String> getBaseVoltageStyle(BranchEdge edge, BranchEdge.Side side) { |
| 167 | + return Optional.empty(); |
| 168 | + } |
| 169 | + |
| 170 | + @Override |
| 171 | + protected Optional<String> getBaseVoltageStyle(ThreeWtEdge threeWtEdge) { |
| 172 | + return Optional.empty(); |
| 173 | + } |
| 174 | +} |
0 commit comments