]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.imports/src/org/simantics/district/imports/DistrictImportUtils.java
Make it possible to import CSV data via SCL & create models
[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                     e.printStackTrace();
351                 } finally {
352                     Layer0Utils.setDependenciesIndexingDisabled(graph, false);
353                 }
354             }
355         });
356     }
357
358     public static void importEdges(CSVImportModel model) throws NoSuchAuthorityCodeException, FactoryException, DatabaseException {
359
360         Path csvFile = model.getSource();
361         char delim = model.getDelimiter();
362
363         
364         int startXCoordColumnIndex = model.getStartXCoordIndex();
365         int startYCoordColumnIndex = model.getStartYCoordIndex();
366         int startZValueColumnIndex = model.getStartZCoordIndex();
367         int endXCoordColumnIndex = model.getEndXCoordIndex();
368         int endYCoordColumnIndex = model.getEndYCoordIndex();
369         int endZValueColumnIndex = model.getEndZCoordIndex();
370         int diameterColumnIndex= model.getDiameterIndex();
371         int outerDiameterColumnIndex = model.getOuterDiamterIndex();
372         int nominalMassFlowIndex = model.getNominalMassFlowIndex();
373         int tGroundIndex = model.gettGroundIndex();
374         int edgeFlowAreaIndex = model.getEdgeFlowAreaIndex();
375         int kReturnIndex = model.getkReturnIndex();
376         int kSupplyIndex = model.getkSupplyIndex();
377         int lengthIndex = model.getLengthIndex();
378         int detailedGeometryIndex = model.getDetailedGeometryIndex();
379         
380         int mappingColumn = model.getComponentMappingIndex();
381         int idColumn = model.getIdIndex();
382         
383         double padding = model.getEdgePadding();
384
385         String sourceEPSGCRS = model.getSourceCRS();
386         
387         MathTransform transform = null;
388         boolean doTransform = false;
389         // if sourceEPSGCRS is empty || null then ignore transformation
390         if (sourceEPSGCRS != null && !sourceEPSGCRS.isEmpty()) {
391             CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEPSGCRS);
392             CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326");
393             transform = CRS.findMathTransform(sourceCRS, targetCRS, true);
394             doTransform = true;
395         }
396         final boolean actualDoTransform = doTransform;
397         final MathTransform actualTransform = transform;
398         
399         double halfPadding = padding / 2;
400         
401         Quadtree vv = Simantics.getSession().syncRequest(new UniqueRead<Quadtree>() {
402
403             @Override
404             public Quadtree perform(ReadGraph graph) throws DatabaseException {
405                 Collection<Resource> vertices = graph.syncRequest(new ObjectsWithType(model.getParentDiagram(), Layer0.getInstance(graph).ConsistsOf, DistrictNetworkResource.getInstance(graph).Vertex));
406                 Quadtree vv = new Quadtree();
407                 for (Resource vertex : vertices) {
408                     double[] coords = graph.getRelatedValue2(vertex, DiagramResource.getInstance(graph).HasLocation, Bindings.DOUBLE_ARRAY);
409                     double x1 = coords[0] - halfPadding;
410                     double y1= coords[1] - halfPadding;
411                     double x2 = coords[0] + halfPadding;
412                     double y2= coords[1] + halfPadding;
413                     Envelope e = new Envelope(x1, x2, y1, y2);
414                     vv.insert(e, new ResourceVertex(vertex, coords, true));
415                 }
416                 return vv;
417             }
418         });
419
420         Simantics.getSession().syncRequest(new WriteRequest() {
421             
422             @Override
423             public void perform(WriteGraph graph) throws DatabaseException {
424                 try {
425                     Layer0Utils.setDependenciesIndexingDisabled(graph, true);
426                     graph.markUndoPoint();
427
428                     DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
429                     
430                     DistrictImportUtils.consumeCSV(csvFile, delim, true, row -> {
431                         try {
432                             String mappingValue = row.get(mappingColumn);
433                             
434                             String startXCoords = row.get(startXCoordColumnIndex);
435                             String startYCoords = row.get(startYCoordColumnIndex);
436                             String startZCoords = row.get(startZValueColumnIndex);
437                             String endXCoords = row.get(endXCoordColumnIndex);
438                             String endYCoords = row.get(endYCoordColumnIndex);
439                             String endZCoords = row.get(endZValueColumnIndex);
440                             
441                             double startXCoord = Double.parseDouble(startXCoords); // make negative
442                             double startYCoord = Double.parseDouble(startYCoords);
443                             double startZCoord = Double.parseDouble(startZCoords);
444                             
445                             double endXCoord = Double.parseDouble(endXCoords); // make negative
446                             double endYCoord = Double.parseDouble(endYCoords);
447                             double endZCoord = Double.parseDouble(endZCoords);
448                             
449                             double[] startCoords;
450                             double[] endCoords;
451                             if (actualDoTransform) {
452                                 DirectPosition2D startTargetPos = new DirectPosition2D();
453                                 DirectPosition2D startSourcePos = new DirectPosition2D(startXCoord, startYCoord);
454                                 DirectPosition startRes = actualTransform.transform(startSourcePos, startTargetPos);
455                                 startCoords = startRes.getCoordinate();
456                                 
457                                 DirectPosition2D endTargetPos = new DirectPosition2D();
458                                 DirectPosition2D endSourcePos = new DirectPosition2D(endXCoord, endYCoord);
459                                 DirectPosition endRes = actualTransform.transform(endSourcePos, endTargetPos);
460                                 endCoords = endRes.getCoordinate();
461                             } else {
462                                 startCoords = new double[] { startXCoord , startYCoord };
463                                 endCoords = new double[] { endXCoord , endYCoord };
464                             }
465                             
466                             // Switch to (longitude, latitude)
467                             flipAxes(startCoords);
468                             flipAxes(endCoords);
469                             
470                             Optional<Resource> oedge = DNEdgeBuilder.create(graph, vv, model.getParentDiagram(), model.getComponentMappings().get(mappingValue), startCoords, startZCoord, endCoords, endZCoord, new double[0], padding, true);
471                             if (oedge.isPresent()) {
472                                 Resource edge = oedge.get();
473                                 writeStringValue(graph, row, idColumn, edge, DN.HasId);
474                                 
475                                 writeValue(graph, row, diameterColumnIndex, edge, DN.Edge_HasDiameter);
476                                 writeValue(graph, row, outerDiameterColumnIndex, edge, DN.Edge_HasOuterDiameter);
477                                 writeValue(graph, row, nominalMassFlowIndex, edge, DN.Edge_HasNominalMassFlow);
478                                 writeValue(graph, row, tGroundIndex, edge, DN.Edge_HasTGround);
479                                 writeValue(graph, row, kReturnIndex, edge, DN.Edge_HasKReturn);
480                                 writeValue(graph, row, kSupplyIndex, edge, DN.Edge_HasKSupply);
481                                 writeValue(graph, row, edgeFlowAreaIndex, edge, DN.Edge_HasFlowArea);
482                                 writeValue(graph, row, lengthIndex, edge, DN.Edge_HasLength);
483                                 writeDoubleArrayFromString(graph, row, detailedGeometryIndex, edge, DN.Edge_HasGeometry, actualTransform);
484                             }
485                             
486                             return true;
487                         } catch (DatabaseException | MismatchedDimensionException | TransformException e) {
488                             throw new RuntimeException(e);
489                         }
490                     });
491                 } catch (IOException e) {
492                     LOGGER.error("Could not import edges {}", model.getSource(), e);
493                 } finally {
494                     Layer0Utils.setDependenciesIndexingDisabled(graph, false);
495                 }
496             }
497         });
498     }
499     
500     private static void flipAxes(double[] coords) {
501         double tmp = coords[0];
502         coords[0] = coords[1];
503         coords[1] = tmp;
504     }
505
506     private static void writeValue(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation) throws DatabaseException {
507         if (index != -1) {
508             String stringValue = row.get(index);
509             if (!stringValue.isEmpty()) {
510                 try {
511                     if (stringValue.startsWith("\"") && stringValue.endsWith("\"")) {
512                         stringValue = stringValue.substring(1, stringValue.length() - 1);
513                     }
514                     graph.claimLiteral(subject, relation, Double.parseDouble(stringValue), Bindings.DOUBLE);
515                 } catch (NumberFormatException e) {
516                     throw new DatabaseException(e);
517                 }
518             }
519         }
520     }
521     
522     private static void writeStringValue(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation) throws DatabaseException {
523         if (index != -1) {
524             String stringValue = row.get(index);
525             if (!stringValue.isEmpty()) {
526                 try {
527                     graph.claimLiteral(subject, relation, stringValue, Bindings.STRING);
528                 } catch (NumberFormatException e) {
529                     throw new DatabaseException(e);
530                 }
531             }
532         }
533     }
534
535     private static void writeDoubleArrayFromString(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation, MathTransform actualTransform) throws DatabaseException, MismatchedDimensionException, TransformException {
536         if (index != -1) {
537             String stringValue = row.get(index);
538             if (!stringValue.isEmpty()) {
539                 if (stringValue.startsWith("\"") && stringValue.endsWith("\"")) {
540                     stringValue = stringValue.substring(1, stringValue.length() - 1);
541                 }
542                 String[] coordPairs = stringValue.split(";");
543                 ArrayList<Double> dd = new ArrayList<>(coordPairs.length * 2);
544                 for (int i = 0; i < coordPairs.length; i++) {
545                     String coordPair = coordPairs[i];
546                     String[] p = coordPair.split(" ");
547                     double x = Double.parseDouble(p[0]);
548                     double y = Double.parseDouble(p[1]);
549                     if (actualTransform != null) {
550                         DirectPosition2D targetPos = new DirectPosition2D();
551                         DirectPosition2D sourcePos = new DirectPosition2D(y, x);
552                         DirectPosition res = actualTransform.transform(sourcePos, targetPos);
553                         double[] coords = res.getCoordinate();
554                         x = coords[1];
555                         y = coords[0];
556                     }
557                     dd.add(x);
558                     dd.add(y);
559                 }
560                 double[] detailedGeometryCoords = new double[dd.size()];
561                 for (int i = 0; i < dd.size(); i++) {
562                     double d = dd.get(i);
563                     detailedGeometryCoords[i] = d;
564                 }
565                 try {
566                     graph.claimLiteral(subject, relation, detailedGeometryCoords, Bindings.DOUBLE_ARRAY);
567                 } catch (NumberFormatException e) {
568                     throw new DatabaseException(e);
569                 }
570             }
571         }
572     }
573 }