Skip to content

Commit 9868b50

Browse files
Optimize lines coordinates fetching (#71)
Store line coordinates in a json string Signed-off-by: Franck LECUYER <[email protected]>
1 parent c8b5b68 commit 9868b50

File tree

11 files changed

+173
-52
lines changed

11 files changed

+173
-52
lines changed

geo-data-extensions/src/main/java/org/gridsuite/geodata/extensions/Coordinate.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414
@NoArgsConstructor
1515
@AllArgsConstructor
16+
@Builder
1617
@Getter
1718
@Setter
1819
@ToString

geo-data-server/src/main/java/org/gridsuite/geodata/server/GeoDataController.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,16 @@ public ResponseEntity<List<LineGeoData>> getLines(@RequestParam UUID networkUuid
7373
@PostMapping(value = "/substations")
7474
@Operation(summary = "Save substations geographical data")
7575
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Substations geographical data have been correctly saved")})
76-
public void saveSubstations(@RequestBody List<SubstationGeoData> substationGeoData) {
76+
public ResponseEntity<Void> saveSubstations(@RequestBody List<SubstationGeoData> substationGeoData) {
7777
geoDataService.saveSubstations(substationGeoData);
78+
return ResponseEntity.ok().build();
7879
}
7980

8081
@PostMapping(value = "/lines")
8182
@Operation(summary = "Save lines geographical data")
8283
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Lines geographical data have been correctly saved")})
83-
public void saveLines(@RequestBody List<LineGeoData> linesGeoData) {
84+
public ResponseEntity<Void> saveLines(@RequestBody List<LineGeoData> linesGeoData) {
8485
geoDataService.saveLines(linesGeoData);
86+
return ResponseEntity.ok().build();
8587
}
8688
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Copyright (c) 2022, 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+
*/
7+
package org.gridsuite.geodata.server;
8+
9+
import org.springframework.http.HttpStatus;
10+
11+
import java.util.Objects;
12+
13+
/**
14+
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
15+
*/
16+
public class GeoDataException extends RuntimeException {
17+
18+
public enum Type {
19+
PARSING_ERROR(HttpStatus.INTERNAL_SERVER_ERROR);
20+
21+
public final HttpStatus status;
22+
23+
HttpStatus getStatus() {
24+
return status;
25+
}
26+
27+
Type(HttpStatus status) {
28+
this.status = status;
29+
}
30+
}
31+
32+
private final Type type;
33+
34+
public GeoDataException(Type type, Exception cause) {
35+
super(Objects.requireNonNull(type.name()) + " : " + ((cause.getMessage() == null) ? cause.getClass().getName() : cause.getMessage()), cause);
36+
this.type = type;
37+
}
38+
39+
Type getType() {
40+
return type;
41+
}
42+
}

geo-data-server/src/main/java/org/gridsuite/geodata/server/GeoDataService.java

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
package org.gridsuite.geodata.server;
88

9+
import com.fasterxml.jackson.core.JsonProcessingException;
10+
import com.fasterxml.jackson.databind.ObjectMapper;
911
import org.gridsuite.geodata.extensions.Coordinate;
1012
import org.gridsuite.geodata.extensions.SubstationPosition;
1113
import org.gridsuite.geodata.server.dto.LineGeoData;
@@ -35,6 +37,8 @@ public class GeoDataService {
3537

3638
private static final Logger LOGGER = LoggerFactory.getLogger(GeoDataService.class);
3739

40+
private ObjectMapper mapper = new ObjectMapper();
41+
3842
@Value("${network-geo-data.iterations:5}")
3943
private int maxIterations;
4044

@@ -227,16 +231,21 @@ void saveSubstations(List<SubstationGeoData> substationsGeoData) {
227231
void saveLines(List<LineGeoData> linesGeoData) {
228232
LOGGER.info("Saving {} lines geo data", linesGeoData.size());
229233

230-
List<LineEntity> linesEntities = new ArrayList<>(linesGeoData.size());
231-
for (LineGeoData l : linesGeoData) {
232-
if (l.getCountry1() == l.getCountry2()) {
233-
linesEntities.add(LineEntity.create(l, true));
234-
} else {
235-
linesEntities.add(LineEntity.create(l, true));
236-
linesEntities.add(LineEntity.create(l, false));
234+
try {
235+
List<LineEntity> linesEntities = new ArrayList<>(linesGeoData.size());
236+
for (LineGeoData l : linesGeoData) {
237+
String coords = mapper.writeValueAsString(l.getCoordinates());
238+
if (l.getCountry1() == l.getCountry2()) {
239+
linesEntities.add(LineEntity.create(l, true, coords));
240+
} else {
241+
linesEntities.add(LineEntity.create(l, true, coords));
242+
linesEntities.add(LineEntity.create(l, false, coords));
243+
}
237244
}
245+
lineRepository.saveAll(linesEntities);
246+
} catch (JsonProcessingException e) {
247+
throw new GeoDataException(GeoDataException.Type.PARSING_ERROR, e);
238248
}
239-
lineRepository.saveAll(linesEntities);
240249
}
241250

242251
boolean emptyOrEquals(String emptyable, String s) {
@@ -311,38 +320,42 @@ public List<LineGeoData> getLines(Network network, Set<Country> countries) {
311320
Objects.requireNonNull(countries);
312321

313322
StopWatch stopWatch = StopWatch.createStarted();
314-
Set<String> lineIds = new TreeSet<>();
315323

316-
network.getLines().forEach(l -> lineIds.add(l.getId()));
324+
List<Line> lines = network.getLineStream().collect(Collectors.toList());
325+
317326
// read lines from DB
318-
Map<String, LineGeoData> linesGeoDataDb = lineRepository.findAllById(lineIds).stream().collect(Collectors.toMap(LineEntity::getId, this::toDto));
327+
Set<String> lineIds = lines.stream().map(Line::getId).collect(Collectors.toSet());
328+
Map<String, LineGeoData> linesGeoDataDb = lineRepository.findAllById(lineIds).stream().collect(Collectors.toMap(LineEntity::getId, this::toDto));
319329

320-
List<Line> lines = network.getLineStream().collect(Collectors.toList());
321330
// we also want the destination substation (so we add the neighbouring country)
322331
Set<Country> countryAndNextTo =
323332
lines.stream().flatMap(line -> line.getTerminals().stream().map(term -> term.getVoltageLevel().getSubstation().orElseThrow().getNullableCountry()).filter(Objects::nonNull))
324-
.collect(Collectors.toSet());
333+
.collect(Collectors.toSet());
325334
Map<String, SubstationGeoData> substationGeoDataDb = getSubstationMap(network, countryAndNextTo);
326335
List<LineGeoData> lineGeoData = lines.stream().map(line -> getLineGeoDataWithEndSubstations(linesGeoDataDb, substationGeoDataDb, line))
327-
.filter(Objects::nonNull).collect(Collectors.toList());
328-
LOGGER.info("{} lines read from DB in {} ms", linesGeoDataDb.size(), stopWatch.getTime(TimeUnit.MILLISECONDS));
336+
.filter(Objects::nonNull).collect(Collectors.toList());
337+
LOGGER.info("{} lines read from DB in {} ms", linesGeoDataDb.size(), stopWatch.getTime(TimeUnit.MILLISECONDS));
329338

330339
return lineGeoData;
331340
}
332341

333-
private LineGeoData toDto(LineEntity lineEntity) {
334-
return new LineGeoData(lineEntity.getId(), toDtoCountry(lineEntity.getCountry()),
335-
toDtoCountry(lineEntity.getOtherCountry()), lineEntity.getSubstationStart(), lineEntity.getSubstationEnd(),
336-
toDto(lineEntity.getCoordinates())
337-
);
342+
public LineGeoData toDto(LineEntity lineEntity) {
343+
try {
344+
return new LineGeoData(lineEntity.getId(), toDtoCountry(lineEntity.getCountry()),
345+
toDtoCountry(lineEntity.getOtherCountry()), lineEntity.getSubstationStart(), lineEntity.getSubstationEnd(),
346+
toDto(lineEntity.getCoordinates())
347+
);
348+
} catch (JsonProcessingException e) {
349+
throw new GeoDataException(GeoDataException.Type.PARSING_ERROR, e);
350+
}
338351
}
339352

340353
private Country toDtoCountry(String country) {
341354
return Country.valueOf(country);
342355
}
343356

344-
private List<Coordinate> toDto(List<CoordinateEmbeddable> coordinates) {
345-
return coordinates.stream().map(e -> new Coordinate(e.getLat(), e.getLon())).collect(Collectors.toList());
357+
private List<Coordinate> toDto(String coordinates) throws JsonProcessingException {
358+
return mapper.readValue(coordinates, List.class);
346359
}
347360

348361
private Map<String, SubstationGeoData> getSubstationMap(Network network, Set<Country> countries) {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Copyright (c) 2022, 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+
*/
7+
package org.gridsuite.geodata.server;
8+
9+
import org.springframework.http.ResponseEntity;
10+
import org.springframework.web.bind.annotation.ControllerAdvice;
11+
import org.springframework.web.bind.annotation.ExceptionHandler;
12+
13+
/**
14+
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
15+
*/
16+
@ControllerAdvice
17+
public class RestResponseEntityExceptionHandler {
18+
19+
@ExceptionHandler(value = {GeoDataException.class})
20+
protected ResponseEntity<Object> handleException(Exception exception) {
21+
GeoDataException geoDataException = (GeoDataException) exception;
22+
return ResponseEntity
23+
.status(geoDataException.getType().getStatus())
24+
.body(geoDataException.getMessage());
25+
}
26+
}

geo-data-server/src/main/java/org/gridsuite/geodata/server/repositories/LineEntity.java

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,11 @@
99
import org.gridsuite.geodata.server.dto.LineGeoData;
1010
import lombok.*;
1111

12-
import javax.persistence.CollectionTable;
1312
import javax.persistence.Column;
14-
import javax.persistence.ElementCollection;
1513
import javax.persistence.Entity;
16-
import javax.persistence.FetchType;
17-
import javax.persistence.ForeignKey;
1814
import javax.persistence.Id;
1915
import javax.persistence.Index;
20-
import javax.persistence.OrderColumn;
2116
import javax.persistence.Table;
22-
import java.util.List;
2317

2418
/**
2519
* @author Chamseddine Benhamed <chamseddine.benhamed at rte-france.com>
@@ -54,21 +48,18 @@ public class LineEntity {
5448
@Builder.Default
5549
private String substationEnd = "";
5650

57-
@Column
58-
@OrderColumn
59-
@CollectionTable(foreignKey = @ForeignKey(name = "lineEntity_coordinate_fk"), indexes = @Index(name = "lineEntity_coordinate_id_index", columnList = "line_entity_id"))
60-
@ElementCollection(fetch = FetchType.LAZY)
61-
private List<CoordinateEmbeddable> coordinates;
51+
@Column(columnDefinition = "TEXT")
52+
private String coordinates;
6253

63-
public static LineEntity create(LineGeoData l, boolean side1) {
54+
public static LineEntity create(LineGeoData l, boolean side1, String coordinates) {
6455
return LineEntity.builder()
6556
.country(side1 ? l.getCountry1().toString() : l.getCountry2().toString())
6657
.otherCountry(side1 ? l.getCountry2().toString() : l.getCountry1().toString())
6758
.side1(side1)
6859
.id(l.getId())
6960
.substationStart(l.getSubstationStart())
7061
.substationEnd(l.getSubstationEnd())
71-
.coordinates(CoordinateEmbeddable.create(l.getCoordinates()))
62+
.coordinates(coordinates)
7263
.build();
7364
}
7465
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
2+
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-3.10.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.10.xsd">
3+
<changeSet author="lecuyerfra (generated)" id="1649075101209-1">
4+
<addColumn tableName="line_entity">
5+
<column name="coordinates" type="varchar" value="[]"/>
6+
</addColumn>
7+
</changeSet>
8+
<changeSet author="lecuyerfra (generated)" id="1649075101209-2">
9+
<dropTable tableName="LINE_ENTITY_COORDINATES"/>
10+
</changeSet>
11+
</databaseChangeLog>

geo-data-server/src/main/resources/db/changelog/db.changelog-master.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ databaseChangeLog:
33
- include:
44
file: changesets/changelog_2022-01-31T09:57:01Z.xml
55
relativeToChangelogFile: true
6+
7+
- include:
8+
file: changesets/changelog_2022-04-04T12:24:52Z.xml
9+
relativeToChangelogFile: true

geo-data-server/src/test/java/org/gridsuite/geodata/server/GeoDataControllerTest.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
import static com.powsybl.network.store.model.NetworkStoreApi.VERSION;
3737
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
38+
import static org.mockito.ArgumentMatchers.any;
3839
import static org.mockito.BDDMockito.given;
3940
import static org.springframework.http.MediaType.APPLICATION_JSON;
4041
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -54,9 +55,6 @@ public class GeoDataControllerTest {
5455
@Autowired
5556
private MockMvc mvc;
5657

57-
@MockBean
58-
private GeoDataService geoDataService;
59-
6058
@MockBean
6159
private NetworkStoreService service;
6260

@@ -129,4 +127,16 @@ public void test() throws Exception {
129127
.content(toString(GEO_DATA_LINES)))
130128
.andExpect(status().isOk());
131129
}
130+
131+
@Test
132+
public void testGetLinesError() throws Exception {
133+
UUID networkUuid = UUID.fromString("7928181c-7977-4592-ba19-88027e4254e4");
134+
135+
given(service.getNetwork(networkUuid)).willReturn(EurostagTutorialExample1Factory.create());
136+
given(lineRepository.findAllById(any())).willThrow(new GeoDataException(GeoDataException.Type.PARSING_ERROR, new RuntimeException("Error parsing")));
137+
138+
mvc.perform(get("/" + VERSION + "/lines?networkUuid=" + networkUuid)
139+
.contentType(APPLICATION_JSON))
140+
.andExpect(status().isInternalServerError());
141+
}
132142
}

geo-data-server/src/test/java/org/gridsuite/geodata/server/GeoDataServiceTest.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package org.gridsuite.geodata.server;
88

9+
import com.fasterxml.jackson.core.JsonProcessingException;
910
import com.fasterxml.jackson.databind.ObjectMapper;
1011
import com.google.common.collect.ImmutableList;
1112
import org.gridsuite.geodata.extensions.Coordinate;
@@ -49,7 +50,7 @@ public class GeoDataServiceTest {
4950
GeoDataService geoDataService;
5051

5152
@Before
52-
public void setUp() {
53+
public void setUp() throws JsonProcessingException {
5354
lineRepository.deleteAll();
5455
substationRepository.deleteAll();
5556
List<SubstationEntity> substationEntities = new ArrayList<>();
@@ -80,7 +81,7 @@ public void setUp() {
8081
.id("NHV2_NHV5")
8182
.country("BE")
8283
.otherCountry("FR")
83-
.coordinates(Arrays.asList(new CoordinateEmbeddable(2, 1), new CoordinateEmbeddable(2.5, 1), new CoordinateEmbeddable(3, 1)))
84+
.coordinates(objectMapper.writeValueAsString(Arrays.asList(new CoordinateEmbeddable(2, 1), new CoordinateEmbeddable(2.5, 1), new CoordinateEmbeddable(3, 1))))
8485
.build());
8586

8687
lineEntities.add(LineEntity.builder()
@@ -89,7 +90,7 @@ public void setUp() {
8990
.otherCountry("FR")
9091
.substationStart("P2")
9192
.substationEnd("P3")
92-
.coordinates(Arrays.asList(new CoordinateEmbeddable(3, 1), new CoordinateEmbeddable(5, 6), new CoordinateEmbeddable(2, 7)))
93+
.coordinates(objectMapper.writeValueAsString(Arrays.asList(new CoordinateEmbeddable(3, 1), new CoordinateEmbeddable(5, 6), new CoordinateEmbeddable(2, 7))))
9394
.build());
9495

9596
lineEntities.add(LineEntity.builder()
@@ -98,7 +99,7 @@ public void setUp() {
9899
.otherCountry("FR")
99100
.substationStart("P2")
100101
.substationEnd("P3")
101-
.coordinates(Arrays.asList(new CoordinateEmbeddable(3, 1), new CoordinateEmbeddable(5, 6), new CoordinateEmbeddable(2, 7)))
102+
.coordinates(objectMapper.writeValueAsString(Arrays.asList(new CoordinateEmbeddable(3, 1), new CoordinateEmbeddable(5, 6), new CoordinateEmbeddable(2, 7))))
102103
.build());
103104

104105
lineEntities.add(LineEntity.builder()
@@ -107,7 +108,7 @@ public void setUp() {
107108
.otherCountry("FR")
108109
.substationStart("OOUPS")
109110
.substationEnd("P3")
110-
.coordinates(Arrays.asList(new CoordinateEmbeddable(3, 1), new CoordinateEmbeddable(5, 6), new CoordinateEmbeddable(2, 7)))
111+
.coordinates(objectMapper.writeValueAsString(Arrays.asList(new CoordinateEmbeddable(3, 1), new CoordinateEmbeddable(5, 6), new CoordinateEmbeddable(2, 7))))
111112
.build());
112113

113114
lineRepository.saveAll(lineEntities);
@@ -211,6 +212,20 @@ public void testNonExisting() {
211212
linesGeoData.stream().anyMatch(s -> notexistline.getId().equals(s.getId())));
212213
}
213214

215+
@Test
216+
public void testLineCoordinatesError() {
217+
LineEntity lineEntity = LineEntity.create(LineGeoData.builder()
218+
.id("idLine")
219+
.country1(Country.FR)
220+
.country2(Country.BE)
221+
.substationStart("substation1")
222+
.substationEnd("substation2")
223+
.build(), true, "coordinates_error");
224+
225+
assertThrows(GeoDataException.class, () ->
226+
geoDataService.toDto(lineEntity));
227+
}
228+
214229
private Network createGeoDataNetwork() {
215230
Network network = EurostagTutorialExample1Factory.create();
216231

0 commit comments

Comments
 (0)