]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.imports/src/org/simantics/district/imports/DistrictImportUtils.java
b3b997b3ef3303104382247a6842880782290355
[simantics/district.git] / org.simantics.district.imports / src / org / simantics / district / imports / DistrictImportUtils.java
1 package org.simantics.district.imports;
2
3 import java.io.IOException;
4 import java.nio.file.Files;
5 import java.nio.file.Path;
6 import java.util.ArrayList;
7 import java.util.Collection;
8 import java.util.HashMap;
9 import java.util.HashSet;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Optional;
14 import java.util.Set;
15 import java.util.concurrent.atomic.AtomicInteger;
16 import java.util.function.Function;
17
18 import org.apache.commons.csv.CSVFormat;
19 import org.apache.commons.csv.CSVParser;
20 import org.apache.commons.csv.CSVRecord;
21 import org.geotools.geometry.DirectPosition2D;
22 import org.geotools.referencing.CRS;
23 import org.opengis.geometry.DirectPosition;
24 import org.opengis.geometry.MismatchedDimensionException;
25 import org.opengis.referencing.FactoryException;
26 import org.opengis.referencing.NoSuchAuthorityCodeException;
27 import org.opengis.referencing.crs.CoordinateReferenceSystem;
28 import org.opengis.referencing.operation.MathTransform;
29 import org.opengis.referencing.operation.TransformException;
30 import org.simantics.Simantics;
31 import org.simantics.databoard.Bindings;
32 import org.simantics.db.ReadGraph;
33 import org.simantics.db.Resource;
34 import org.simantics.db.WriteGraph;
35 import org.simantics.db.common.request.ObjectsWithType;
36 import org.simantics.db.common.request.UniqueRead;
37 import org.simantics.db.common.request.WriteRequest;
38 import org.simantics.db.exception.DatabaseException;
39 import org.simantics.db.layer0.util.Layer0Utils;
40 import org.simantics.diagram.stubs.DiagramResource;
41 import org.simantics.district.network.DNEdgeBuilder;
42 import org.simantics.district.network.DistrictNetworkUtil;
43 import org.simantics.district.network.ontology.DistrictNetworkResource;
44 import org.simantics.layer0.Layer0;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.simantics.district.network.DistrictNetworkUtil.ResourceVertex;
48
49 import com.vividsolutions.jts.geom.Envelope;
50 import com.vividsolutions.jts.index.quadtree.Quadtree;
51
52 public class DistrictImportUtils {
53
54     private DistrictImportUtils() { }
55
56     private static final Logger LOGGER = LoggerFactory.getLogger(DistrictImportUtils.class);
57     
58     public static Resource importCSVAsLayer(Path csvFile) throws IOException {
59         
60         try (CSVParser parser = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(Files.newBufferedReader(csvFile))) {
61             Map<String, Integer> header = parser.getHeaderMap();
62             System.out.println(header);
63         }
64         return null;
65     }
66     
67     public static Map<String, Integer> readCSVHeader(Path source, char delimiter, boolean firstAsHeader) throws IOException {
68         return readCSVHeader(source, CSVFormat.newFormat(delimiter), firstAsHeader);
69     }
70     
71     public static Map<String, Integer> readCSVHeader(Path source, CSVFormat format, boolean firstAsHeader) throws IOException {
72         if (firstAsHeader)
73             format = format.withFirstRecordAsHeader();
74         try (CSVParser parser = format.parse(Files.newBufferedReader(source))) {
75             return parser.getHeaderMap();
76         }
77     }
78
79     public static Map<String, Character> getSupportedCSVDelimiterFormats() {
80         Map<String, Character> delimiters = new HashMap<>();
81         delimiters.put("Comma", ',');
82         delimiters.put("Semicolon", ';');
83         delimiters.put("Tabulator", '\t');
84         return delimiters;
85     }
86
87     public static List<Map<String, String>> readRows(Path source, CSVFormat format, boolean firstAsHeader, int amount) throws IOException {
88         if (firstAsHeader)
89             format = format.withFirstRecordAsHeader();
90         try (CSVParser parser = format.parse(Files.newBufferedReader(source))) {
91             int start = 0;
92             List<Map<String, String>> results = new ArrayList<>(amount);
93             Iterator<CSVRecord> iter = parser.iterator();
94             while (start < amount && iter.hasNext()) {
95                 CSVRecord record = iter.next();
96                 results.add(record.toMap());
97                 start++;
98             }
99             return results;
100         }
101     }
102
103     public static List<CSVRecord> readRows(Path source, char delim, boolean firstAsHeader, int rowAmount) throws IOException {
104         List<CSVRecord> results = new ArrayList<>();
105         AtomicInteger count = new AtomicInteger(0);
106         consumeCSV(source, delim, firstAsHeader, t -> {
107             results.add(t);
108             int current = count.incrementAndGet();
109             return current < rowAmount;
110         });
111         return results;
112     }
113     
114     public static void consumeCSV(Path source, char delim, boolean firstAsHeader, Function<CSVRecord, Boolean> consumer) throws IOException {
115         CSVFormat format = CSVFormat.newFormat(delim);
116         if (firstAsHeader) {
117             format = format.withFirstRecordAsHeader();
118         }
119         try (CSVParser parser = format.parse(Files.newBufferedReader(source))) {
120             Iterator<CSVRecord> records = parser.iterator();
121             while (records.hasNext()) {
122                 Boolean cont = consumer.apply(records.next());
123                 if (!cont) {
124                     break;
125                 }
126             }
127         }
128     }
129
130     
131     public static Map<CSVHeader, List<String>> readCSVHeaderAndRows(Path source, char delimiter, boolean firstAsHeader, int amount) throws IOException {
132         Map<CSVHeader, List<String>> results = new HashMap<>();
133         CSVFormat format = CSVFormat.newFormat(delimiter);
134         if (firstAsHeader)
135             format = format.withFirstRecordAsHeader();
136         try (CSVParser parser = format.parse(Files.newBufferedReader(source))) {
137             Map<String, Integer> headers = parser.getHeaderMap();
138             if (headers != null && !headers.isEmpty()) {
139                 for (int index = 0; index < headers.size(); index++) {
140                     for (String head : headers.keySet()) {
141                         results.put(new CSVHeader(head, index), new ArrayList<>());
142                     }
143                 }
144             }
145             
146             Iterator<CSVRecord> records = parser.iterator();
147             int rows = 0;
148             while (rows < amount && records.hasNext()) {
149                 CSVRecord record = records.next();
150                 for (int j = 0; j < record.size(); j++) {
151                     String value = record.get(j);
152                     String header = Integer.toString(j);
153                     CSVHeader csvHeader = new CSVHeader(header, j);
154                     List<String> vals = results.get(csvHeader);
155                     if (vals == null) {
156                         vals = new ArrayList<>();
157                         results.put(csvHeader, vals);
158                     }
159                     vals.add(value);
160                 }
161                 rows++;
162             }
163         }
164         return results;
165     }
166     
167     public static class CSVHeader {
168
169         private final String header;
170         private final int index;
171         
172         public CSVHeader(String header, int index) {
173             this.header = header;
174             this.index = index;
175         }
176
177         public String getHeader() {
178             return header;
179         }
180
181         public int getIndex() {
182             return index;
183         }
184         
185         @Override
186         public int hashCode() {
187             final int prime = 31;
188             int result = 1;
189             result = prime * result + ((header == null) ? 0 : header.hashCode());
190             result = prime * result + index;
191             return result;
192         }
193
194         @Override
195         public boolean equals(Object obj) {
196             if (this == obj)
197                 return true;
198             if (obj == null)
199                 return false;
200             if (getClass() != obj.getClass())
201                 return false;
202             CSVHeader other = (CSVHeader) obj;
203             if (header == null) {
204                 if (other.header != null)
205                     return false;
206             } else if (!header.equals(other.header))
207                 return false;
208             if (index != other.index)
209                 return false;
210             return true;
211         }
212     }
213
214     public static Collection<String> readDistinctValuesOfColumn(Path source, char delim, int mappingIndex) throws IOException {
215         Set<String> results = new HashSet<>();
216         CSVFormat format = CSVFormat.newFormat(delim);
217         try (CSVParser parser = format.parse(Files.newBufferedReader(source))) {
218             Iterator<CSVRecord> records = parser.iterator();
219             if (records.hasNext())
220                 records.next();
221             while (records.hasNext()) {
222                 CSVRecord row = records.next();
223                 String value = row.get(mappingIndex);
224                 results.add(value);
225             }
226         }
227         return results;
228     }
229
230     public static void importVertices(CSVImportModel model) throws NoSuchAuthorityCodeException, FactoryException, DatabaseException {
231
232         Path csvFile = model.getSource();
233         char delim = model.getDelimiter();
234
235         int xCoordColumnIndex = model.getXCoordIndex();
236         int yCoordColumnIndex = model.getYCoordIndex();
237         int zCoordColumnIndex = model.getZCoordIndex();
238         int supplyTempColumnIndex = model.getSupplyTempIndex();
239         int returnTempColumnIndex = model.getReturnTempIndex();
240         int supplyPressureColumnIndex = model.getSupplyPressureIndex();
241         int returnPressureColumnIndex = model.getReturnPressureIndex();
242         int dpIndex = model.getDeltaPressureIndex();
243         int dtIndex = model.getDeltaTemperatureIndex();
244         int heatPowerIndex = model.getHeatPowerIndex();
245         int valvePositionIndex = model.getValvePositionIndx();
246         int nominalHeadMIndex = model.getNominalHeadMIndex();
247         int nominalHeadBIndex = model.getNominalHeadBIndex();
248         int nominalFlowIndex = model.getNominalFlowIndex();
249         int maximumHeadMIndex = model.getMaximumHeadMIndex();
250         int heatLoadDsIndex = model.getHeatLoadDsIndex();
251         int massFlowIndex = model.getMassFlowIndex();
252         int volFlowIndex = model.getVolFlowIndex();
253         int velocityIndex = model.getVelocityIndex();
254         int flowAreaIndex = model.getFlowAreaIndex();
255         int nominalPressureLossIndex = model.getNominalPressureLossIndex();
256         int addressIndex = model.getAddressIndex();
257         
258         int mappingColumn = model.getComponentMappingIndex();
259         int idColumn = model.getIdIndex();
260         
261         String sourceEPSGCRS = model.getSourceCRS();
262         
263         MathTransform transform = null;
264         boolean doTransform = false;
265         // if sourceEPSGCRS is empty || null then ignore transformation
266         if (sourceEPSGCRS != null && !sourceEPSGCRS.isEmpty()) {
267             CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEPSGCRS);
268             CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326");
269             transform = CRS.findMathTransform(sourceCRS, targetCRS, true);
270             doTransform = true;
271         }
272         final boolean actualDoTransform = doTransform;
273         final MathTransform actualTransform = transform;
274         
275         Simantics.getSession().syncRequest(new WriteRequest() {
276             
277             @Override
278             public void perform(WriteGraph graph) throws DatabaseException {
279                 try {
280                     Layer0Utils.setDependenciesIndexingDisabled(graph, true);
281                     graph.markUndoPoint();
282                     
283                     DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
284
285                     DistrictImportUtils.consumeCSV(csvFile, delim, true, (Function<CSVRecord, Boolean>) row -> {
286                         try {
287                             String mappingValue = row.get(mappingColumn);
288                             
289                             String xCoords = row.get(xCoordColumnIndex);
290                             String yCoords = row.get(yCoordColumnIndex);
291                             double xCoord = Double.parseDouble(xCoords);
292                             double yCoord = Double.parseDouble(yCoords);
293                             
294                             double z = 0;
295                             if (zCoordColumnIndex != -1) {
296                                 String zs = row.get(zCoordColumnIndex);
297                                 
298                                 if (!zs.isEmpty()) {
299                                     try {
300                                         z = Double.parseDouble(zs);
301                                     } catch (NumberFormatException e1) {
302                                         throw new DatabaseException(e1);
303                                     }
304                                 }
305                             }
306                 
307                             double[] coords;
308                             if (actualDoTransform) {
309                                 DirectPosition2D targetPos = new DirectPosition2D();
310                                 DirectPosition2D sourcePos = new DirectPosition2D(xCoord, yCoord);
311                                 DirectPosition res = actualTransform.transform(sourcePos, targetPos);
312                                 coords = res.getCoordinate();
313                             } else {
314                                 coords = new double[] { xCoord, yCoord };
315                             }
316                             
317                             // Switch to (longitude, latitude)
318                             flipAxes(coords);
319                             
320                             Resource vertex = DistrictNetworkUtil.createVertex(graph, model.getParentDiagram(), coords, z, model.getComponentMappings().get(mappingValue));
321                             
322                             writeStringValue(graph, row, idColumn, vertex, DN.HasId);
323                             
324                             writeValue(graph, row, supplyTempColumnIndex, vertex, DN.Vertex_HasSupplyTemperature);
325                             writeValue(graph, row, returnTempColumnIndex, vertex, DN.Vertex_HasReturnTemperature);
326                             writeValue(graph, row, supplyPressureColumnIndex, vertex, DN.Vertex_HasSupplyPressure);
327                             writeValue(graph, row, returnPressureColumnIndex, vertex, DN.Vertex_HasReturnPressure);
328                             writeValue(graph, row, dpIndex, vertex, DN.Vertex_HasDeltaPressure);
329                             writeValue(graph, row, dtIndex, vertex, DN.Vertex_HasDeltaTemperature);
330                             writeValue(graph, row, heatPowerIndex, vertex, DN.Vertex_HasHeatPower);
331                             writeValue(graph, row, valvePositionIndex, vertex, DN.Vertex_HasValvePosition);
332                             writeValue(graph, row, nominalHeadMIndex, vertex, DN.Vertex_HasNominalHeadM);
333                             writeValue(graph, row, nominalHeadBIndex, vertex, DN.Vertex_HasNominalHeadB);
334                             writeValue(graph, row, nominalFlowIndex, vertex, DN.Vertex_HasNominalFlow);
335                             writeValue(graph, row, maximumHeadMIndex, vertex, DN.Vertex_HasMaximumHeadM);
336                             writeValue(graph, row, heatLoadDsIndex, vertex, DN.Vertex_HasHeatLoadDs);
337                             writeValue(graph, row, massFlowIndex, vertex, DN.Vertex_HasMassFlow);
338                             writeValue(graph, row, volFlowIndex, vertex, DN.Vertex_HasVolFlow);
339                             writeValue(graph, row, velocityIndex, vertex, DN.Vertex_HasVelocity);
340                             writeValue(graph, row, flowAreaIndex, vertex, DN.Vertex_HasFlowArea);
341                             writeValue(graph, row, nominalPressureLossIndex, vertex, DN.Vertex_HasNominalPressureLoss);
342                             writeStringValue(graph, row, addressIndex, vertex, DN.Vertex_HasAddress);
343                         } catch (DatabaseException | MismatchedDimensionException | TransformException e) {
344                             throw new RuntimeException(e);
345                         }
346                         return true;
347                     });
348                     
349                 } catch (IOException e) {
350                     LOGGER.error("Could not import", e);
351                     throw new DatabaseException(e);
352                 } finally {
353                     Layer0Utils.setDependenciesIndexingDisabled(graph, false);
354                 }
355             }
356         });
357     }
358
359     public static void importEdges(CSVImportModel model) throws NoSuchAuthorityCodeException, FactoryException, DatabaseException {
360
361         Path csvFile = model.getSource();
362         char delim = model.getDelimiter();
363
364         
365         int startXCoordColumnIndex = model.getStartXCoordIndex();
366         int startYCoordColumnIndex = model.getStartYCoordIndex();
367         int startZValueColumnIndex = model.getStartZCoordIndex();
368         int endXCoordColumnIndex = model.getEndXCoordIndex();
369         int endYCoordColumnIndex = model.getEndYCoordIndex();
370         int endZValueColumnIndex = model.getEndZCoordIndex();
371         int diameterColumnIndex= model.getDiameterIndex();
372         int outerDiameterColumnIndex = model.getOuterDiamterIndex();
373         int nominalMassFlowIndex = model.getNominalMassFlowIndex();
374         int tGroundIndex = model.gettGroundIndex();
375         int edgeFlowAreaIndex = model.getEdgeFlowAreaIndex();
376         int kReturnIndex = model.getkReturnIndex();
377         int kSupplyIndex = model.getkSupplyIndex();
378         int lengthIndex = model.getLengthIndex();
379         int detailedGeometryIndex = model.getDetailedGeometryIndex();
380         
381         int mappingColumn = model.getComponentMappingIndex();
382         int idColumn = model.getIdIndex();
383         
384         double padding = model.getEdgePadding();
385
386         String sourceEPSGCRS = model.getSourceCRS();
387         
388         MathTransform transform = null;
389         boolean doTransform = false;
390         // if sourceEPSGCRS is empty || null then ignore transformation
391         if (sourceEPSGCRS != null && !sourceEPSGCRS.isEmpty()) {
392             CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEPSGCRS);
393             CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326");
394             transform = CRS.findMathTransform(sourceCRS, targetCRS, true);
395             doTransform = true;
396         }
397         final boolean actualDoTransform = doTransform;
398         final MathTransform actualTransform = transform;
399         
400         double halfPadding = padding / 2;
401         
402         Quadtree vv = Simantics.getSession().syncRequest(new UniqueRead<Quadtree>() {
403
404             @Override
405             public Quadtree perform(ReadGraph graph) throws DatabaseException {
406                 Collection<Resource> vertices = graph.syncRequest(new ObjectsWithType(model.getParentDiagram(), Layer0.getInstance(graph).ConsistsOf, DistrictNetworkResource.getInstance(graph).Vertex));
407                 Quadtree vv = new Quadtree();
408                 for (Resource vertex : vertices) {
409                     double[] coords = graph.getRelatedValue2(vertex, DiagramResource.getInstance(graph).HasLocation, Bindings.DOUBLE_ARRAY);
410                     double x1 = coords[0] - halfPadding;
411                     double y1= coords[1] - halfPadding;
412                     double x2 = coords[0] + halfPadding;
413                     double y2= coords[1] + halfPadding;
414                     Envelope e = new Envelope(x1, x2, y1, y2);
415                     vv.insert(e, new ResourceVertex(vertex, coords, true));
416                 }
417                 return vv;
418             }
419         });
420
421         Simantics.getSession().syncRequest(new WriteRequest() {
422             
423             @Override
424             public void perform(WriteGraph graph) throws DatabaseException {
425                 try {
426                     Layer0Utils.setDependenciesIndexingDisabled(graph, true);
427                     graph.markUndoPoint();
428
429                     DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
430                     
431                     DistrictImportUtils.consumeCSV(csvFile, delim, true, row -> {
432                         try {
433                             String mappingValue = row.get(mappingColumn);
434                             
435                             String startXCoords = row.get(startXCoordColumnIndex);
436                             String startYCoords = row.get(startYCoordColumnIndex);
437                             String startZCoords = row.get(startZValueColumnIndex);
438                             String endXCoords = row.get(endXCoordColumnIndex);
439                             String endYCoords = row.get(endYCoordColumnIndex);
440                             String endZCoords = row.get(endZValueColumnIndex);
441                             
442                             double startXCoord = Double.parseDouble(startXCoords); // make negative
443                             double startYCoord = Double.parseDouble(startYCoords);
444                             double startZCoord = Double.parseDouble(startZCoords);
445                             
446                             double endXCoord = Double.parseDouble(endXCoords); // make negative
447                             double endYCoord = Double.parseDouble(endYCoords);
448                             double endZCoord = Double.parseDouble(endZCoords);
449                             
450                             double[] startCoords;
451                             double[] endCoords;
452                             if (actualDoTransform) {
453                                 DirectPosition2D startTargetPos = new DirectPosition2D();
454                                 DirectPosition2D startSourcePos = new DirectPosition2D(startXCoord, startYCoord);
455                                 DirectPosition startRes = actualTransform.transform(startSourcePos, startTargetPos);
456                                 startCoords = startRes.getCoordinate();
457                                 
458                                 DirectPosition2D endTargetPos = new DirectPosition2D();
459                                 DirectPosition2D endSourcePos = new DirectPosition2D(endXCoord, endYCoord);
460                                 DirectPosition endRes = actualTransform.transform(endSourcePos, endTargetPos);
461                                 endCoords = endRes.getCoordinate();
462                             } else {
463                                 startCoords = new double[] { startXCoord , startYCoord };
464                                 endCoords = new double[] { endXCoord , endYCoord };
465                             }
466                             
467                             // Switch to (longitude, latitude)
468                             flipAxes(startCoords);
469                             flipAxes(endCoords);
470                             
471                             Optional<Resource> oedge = DNEdgeBuilder.create(graph, vv, model.getParentDiagram(), model.getComponentMappings().get(mappingValue), startCoords, startZCoord, endCoords, endZCoord, new double[0], padding, true);
472                             if (oedge.isPresent()) {
473                                 Resource edge = oedge.get();
474                                 writeStringValue(graph, row, idColumn, edge, DN.HasId);
475                                 
476                                 writeValue(graph, row, diameterColumnIndex, edge, DN.Edge_HasDiameter);
477                                 writeValue(graph, row, outerDiameterColumnIndex, edge, DN.Edge_HasOuterDiameter);
478                                 writeValue(graph, row, nominalMassFlowIndex, edge, DN.Edge_HasNominalMassFlow);
479                                 writeValue(graph, row, tGroundIndex, edge, DN.Edge_HasTGround);
480                                 writeValue(graph, row, kReturnIndex, edge, DN.Edge_HasKReturn);
481                                 writeValue(graph, row, kSupplyIndex, edge, DN.Edge_HasKSupply);
482                                 writeValue(graph, row, edgeFlowAreaIndex, edge, DN.Edge_HasFlowArea);
483                                 writeValue(graph, row, lengthIndex, edge, DN.Edge_HasLength);
484                                 writeDoubleArrayFromString(graph, row, detailedGeometryIndex, edge, DN.Edge_HasGeometry, actualTransform);
485                             }
486                             
487                             return true;
488                         } catch (DatabaseException | MismatchedDimensionException | TransformException e) {
489                             throw new RuntimeException(e);
490                         }
491                     });
492                 } catch (IOException e) {
493                     LOGGER.error("Could not import edges {}", model.getSource(), e);
494                 } finally {
495                     Layer0Utils.setDependenciesIndexingDisabled(graph, false);
496                 }
497             }
498         });
499     }
500     
501     private static void flipAxes(double[] coords) {
502         double tmp = coords[0];
503         coords[0] = coords[1];
504         coords[1] = tmp;
505     }
506
507     private static void writeValue(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation) throws DatabaseException {
508         if (index != -1) {
509             String stringValue = row.get(index);
510             if (!stringValue.isEmpty()) {
511                 try {
512                     if (stringValue.startsWith("\"") && stringValue.endsWith("\"")) {
513                         stringValue = stringValue.substring(1, stringValue.length() - 1);
514                     }
515                     graph.claimLiteral(subject, relation, Double.parseDouble(stringValue), Bindings.DOUBLE);
516                 } catch (NumberFormatException e) {
517                     throw new DatabaseException(e);
518                 }
519             }
520         }
521     }
522     
523     private static void writeStringValue(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation) throws DatabaseException {
524         if (index != -1) {
525             String stringValue = row.get(index);
526             if (!stringValue.isEmpty()) {
527                 try {
528                     graph.claimLiteral(subject, relation, stringValue, Bindings.STRING);
529                 } catch (NumberFormatException e) {
530                     throw new DatabaseException(e);
531                 }
532             }
533         }
534     }
535
536     private static void writeDoubleArrayFromString(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation, MathTransform actualTransform) throws DatabaseException, MismatchedDimensionException, TransformException {
537         if (index != -1) {
538             String stringValue = row.get(index);
539             if (!stringValue.isEmpty()) {
540                 if (stringValue.startsWith("\"") && stringValue.endsWith("\"")) {
541                     stringValue = stringValue.substring(1, stringValue.length() - 1);
542                 }
543                 String[] coordPairs = stringValue.split(";");
544                 ArrayList<Double> dd = new ArrayList<>(coordPairs.length * 2);
545                 for (int i = 0; i < coordPairs.length; i++) {
546                     String coordPair = coordPairs[i];
547                     String[] p = coordPair.split(" ");
548                     double x = Double.parseDouble(p[0]);
549                     double y = Double.parseDouble(p[1]);
550                     if (actualTransform != null) {
551                         DirectPosition2D targetPos = new DirectPosition2D();
552                         DirectPosition2D sourcePos = new DirectPosition2D(y, x);
553                         DirectPosition res = actualTransform.transform(sourcePos, targetPos);
554                         double[] coords = res.getCoordinate();
555                         x = coords[1];
556                         y = coords[0];
557                     }
558                     dd.add(x);
559                     dd.add(y);
560                 }
561                 double[] detailedGeometryCoords = new double[dd.size()];
562                 for (int i = 0; i < dd.size(); i++) {
563                     double d = dd.get(i);
564                     detailedGeometryCoords[i] = d;
565                 }
566                 try {
567                     graph.claimLiteral(subject, relation, detailedGeometryCoords, Bindings.DOUBLE_ARRAY);
568                 } catch (NumberFormatException e) {
569                     throw new DatabaseException(e);
570                 }
571             }
572         }
573     }
574 }