]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.network/src/org/simantics/district/network/DistrictNetworkUtil.java
Fix elevation bounding box profile shift & elevation transform fixes
[simantics/district.git] / org.simantics.district.network / src / org / simantics / district / network / DistrictNetworkUtil.java
1 package org.simantics.district.network;
2
3 import java.awt.geom.Rectangle2D;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.stream.Collectors;
9 import java.util.stream.Stream;
10
11 import org.simantics.databoard.Bindings;
12 import org.simantics.datatypes.literal.RGB;
13 import org.simantics.datatypes.literal.RGB.Integer;
14 import org.simantics.db.ReadGraph;
15 import org.simantics.db.Resource;
16 import org.simantics.db.WriteGraph;
17 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
18 import org.simantics.db.common.request.IndexRoot;
19 import org.simantics.db.common.request.ResourceRead;
20 import org.simantics.db.common.utils.OrderedSetUtils;
21 import org.simantics.db.exception.BindingException;
22 import org.simantics.db.exception.DatabaseException;
23 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
24 import org.simantics.db.exception.ServiceException;
25 import org.simantics.db.indexing.IndexUtils;
26 import org.simantics.db.layer0.request.PossibleVariable;
27 import org.simantics.db.layer0.variable.Variable;
28 import org.simantics.diagram.stubs.DiagramResource;
29 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
30 import org.simantics.diagram.synchronization.graph.layer.GraphLayer;
31 import org.simantics.diagram.synchronization.graph.layer.IGraphLayerUtil;
32 import org.simantics.district.network.ontology.DistrictNetworkResource;
33 import org.simantics.layer0.Layer0;
34 import org.simantics.maps.elevation.server.SingletonTiffTileInterface;
35 import org.simantics.maps.elevation.server.prefs.MapsElevationServerPreferences;
36 import org.simantics.modeling.ModelingResources;
37 import org.simantics.modeling.adapters.NewCompositeActionFactory;
38 import org.simantics.operation.Layer0X;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 public class DistrictNetworkUtil {
43
44     private static final Logger LOGGER = LoggerFactory.getLogger(DistrictNetworkUtil.class);
45
46     public static Resource createEdge(WriteGraph graph, Resource composite, double[] detailedGeometryCoords) throws DatabaseException {
47         return createEdge(graph, composite, graph.getPossibleObject(composite, DistrictNetworkResource.getInstance(graph).EdgeDefaultMapping), detailedGeometryCoords);
48     }
49
50     public static Resource createEdge(WriteGraph graph, Resource composite, Resource mapping, double[] detailedGeometryCoords) throws DatabaseException {
51         Layer0 L0 = Layer0.getInstance(graph);
52         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
53         if (mapping == null) {
54             mapping = graph.getSingleObject(composite, DN.EdgeDefaultMapping);
55         }
56         
57         Resource edge = graph.newResource();
58         graph.claim(edge, L0.InstanceOf, DN.Edge);
59         
60         graph.claim(edge, DN.HasMapping, null, mapping);
61         
62         OrderedSetUtils.addFirst(graph, composite, edge);
63         graph.claim(composite, L0.ConsistsOf, L0.PartOf, edge);
64         
65         claimFreshElementName(graph, composite, edge);
66         
67         // We need to put GraphLayer to newLayers so...
68         for (Resource layer : graph.getObjects(composite, DiagramResource.getInstance(graph).HasLayer)) {
69             IGraphLayerUtil layerUtil = graph.adapt(graph.getSingleObject(layer, Layer0.getInstance(graph).InstanceOf), IGraphLayerUtil.class);
70             
71             GraphLayer gl = layerUtil.loadLayer(graph, layer);
72             gl.forEachTag(tag -> {
73                 DiagramGraphUtil.tag(graph, edge, tag, true);
74             });
75         }
76         
77         // add detailed geometry (if any)
78         graph.claimLiteral(edge, DN.Edge_HasGeometry, detailedGeometryCoords, Bindings.DOUBLE_ARRAY);
79         return edge;
80     }
81
82     /**
83      * @param graph
84      * @param composite
85      * @param coords
86      * @param elevation Double.MAX_VALUE to fetch elevation from elevation server (if enabled and has data)
87      * @return
88      * @throws DatabaseException
89      */
90     public static Resource createVertex(WriteGraph graph, Resource composite, double[] coords, double elevation) throws DatabaseException {
91         Resource defaultVertexMapping = graph.getPossibleObject(composite, DistrictNetworkResource.getInstance(graph).VertexDefaultMapping);
92         return createVertex(graph, composite, coords, elevation, defaultVertexMapping);
93     }
94
95     /**
96      * @param graph
97      * @param composite
98      * @param coords
99      * @param elevation Double.MAX_VALUE to fetch elevation from elevation server (if enabled and has data)
100      * @param mapping
101      * @return
102      * @throws DatabaseException
103      */
104     public static Resource createVertex(WriteGraph graph, Resource composite, double[] coords, double elevation, Resource mapping) throws DatabaseException {
105         // Double.MAX_VALUE is our secret to lookup elevation from elevation server
106         if (elevation == Double.MAX_VALUE) {
107             // ok, resolve from server or default to 0
108             if (MapsElevationServerPreferences.useElevationServer()) {
109                 // ok! we use new elevation API to resolve possible elevations for the starting points
110                 try {
111                     elevation = SingletonTiffTileInterface.lookup(coords[1], coords[0]).doubleValue();
112                 } catch (Exception ee) {
113                     LOGGER.error("Could not get elevation from tiff interface", ee);
114                 }
115             } else {
116                 elevation = 0;
117             }
118         }
119         
120         Layer0 L0 = Layer0.getInstance(graph);
121         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
122         DiagramResource DIA = DiagramResource.getInstance(graph);
123         Resource vertex = graph.newResource();
124         graph.claim(vertex, L0.InstanceOf, DN.Vertex);
125         graph.claimLiteral(vertex, DIA.HasLocation, coords);
126         graph.claimLiteral(vertex, DN.Vertex_HasElevation, elevation, Bindings.DOUBLE);
127         
128         graph.claim(vertex, DN.HasMapping, null, mapping);
129         
130         OrderedSetUtils.add(graph, composite, vertex);
131         graph.claim(composite, L0.ConsistsOf, L0.PartOf, vertex);
132         
133         claimFreshElementName(graph, composite, vertex);
134         
135         // We need to put GraphLayer to newLayers so...
136         for (Resource layer : graph.getObjects(composite, DiagramResource.getInstance(graph).HasLayer)) {
137             IGraphLayerUtil layerUtil = graph.adapt(graph.getSingleObject(layer, Layer0.getInstance(graph).InstanceOf), IGraphLayerUtil.class);
138             
139             GraphLayer gl = layerUtil.loadLayer(graph, layer);
140             gl.forEachTag(tag -> {
141                 DiagramGraphUtil.tag(graph, vertex, tag, true);
142             });
143         }
144         
145         return vertex;
146     }
147     
148     public static Resource joinVertices(WriteGraph graph, Collection<Resource> vertices) throws DatabaseException {
149         if (vertices.isEmpty())
150             throw new IllegalArgumentException("vertices-collection should not be empty for joining vertices!");
151         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
152         Iterator<Resource> verticeIterator = vertices.iterator();
153         Resource master = verticeIterator.next();
154         while (verticeIterator.hasNext()) {
155             Resource slave = verticeIterator.next();
156             Resource composite = graph.getSingleObject(slave, Layer0.getInstance(graph).PartOf);
157             Collection<Resource> startVertexEdges = graph.getObjects(slave, DN.HasStartVertex_Inverse);
158             for (Resource startVertexEdge : startVertexEdges) {
159                 graph.deny(startVertexEdge, DN.HasStartVertex);
160                 graph.claim(startVertexEdge, DN.HasStartVertex, master);
161             }
162             Collection<Resource> endVertexEdges = graph.getObjects(slave, DN.HasEndVertex_Inverse);
163             for (Resource endVertexEdge : endVertexEdges) {
164                 graph.deny(endVertexEdge, DN.HasEndVertex);
165                 graph.claim(endVertexEdge, DN.HasEndVertex, master);
166             }
167             OrderedSetUtils.remove(graph, composite, slave);
168             // Remove ConsistsOf statement
169             graph.deny(composite, Layer0.getInstance(graph).ConsistsOf, slave);
170         }
171         return master;
172     }
173     
174     public static double calculateDistance(ReadGraph graph, Resource startVertex, Resource endVertex) throws DatabaseException {
175         Layer0 L0 = Layer0.getInstance(graph);
176         Resource startComposite = graph.getSingleObject(startVertex, L0.PartOf);
177         Resource endComposite = graph.getSingleObject(endVertex, L0.PartOf);
178         if (!startComposite.equalsResource(endComposite)) {
179             throw new DatabaseException("Can not calculate distance between vertices on different composites! " + startVertex + " -> " + endVertex);
180         }
181         Resource crs = graph.getSingleObject(startComposite, DistrictNetworkResource.getInstance(graph).HasSpatialRefSystem);
182         
183         CRS crsClass = graph.adapt(crs, CRS.class);
184         
185         double[] startCoords = graph.getRelatedValue2(startVertex, DiagramResource.getInstance(graph).HasLocation, Bindings.DOUBLE_ARRAY);
186         double[] endCoords = graph.getRelatedValue2(endVertex, DiagramResource.getInstance(graph).HasLocation, Bindings.DOUBLE_ARRAY);
187         
188         return crsClass.calculateDistance(startCoords, endCoords);
189     }
190     
191     public static final String claimFreshElementName(WriteGraph graph, Resource diagram, Resource element) throws DatabaseException {
192         Layer0 L0 = Layer0.getInstance(graph);
193         DiagramResource DIA = DiagramResource.getInstance(graph);
194         // Get name prefix from diagram
195         String namePrefix = graph.getPossibleRelatedValue2(diagram, Layer0X.getInstance(graph).HasGeneratedNamePrefix);
196         if (namePrefix == null)
197             namePrefix = "";
198         // Give running name to element and increment the counter attached to the diagram.
199         Long l = graph.getPossibleRelatedValue(diagram, DIA.HasModCount, Bindings.LONG);
200         if (l == null)
201             l = Long.valueOf(0L);
202         String name = namePrefix + l.toString();
203         graph.claimLiteral(element, L0.HasName, name, Bindings.STRING);
204         graph.claimLiteral(diagram, DIA.HasModCount, ++l, Bindings.LONG);
205         return name;
206     }
207
208     public static Resource getDiagramElement(ReadGraph graph, Resource component) throws DatabaseException {
209         if (component == null)
210             return null;
211         DiagramResource DIA = DiagramResource.getInstance(graph);
212         if (graph.isInstanceOf(component, DIA.Element))
213             return component;
214         ModelingResources MOD = ModelingResources.getInstance(graph);
215         Resource element = graph.getPossibleObject(component, MOD.ComponentToElement);
216         return element != null && graph.isInstanceOf(element, DIA.Element) ? element : null;
217     }
218
219     public static Resource getMappedElement(ReadGraph graph, Resource element) throws DatabaseException {
220         if (element == null)
221             return null;
222         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
223         return graph.getPossibleObject(element, DN.MappedComponent);
224     }
225
226     public static Resource getMappedComponent(ReadGraph graph, Resource element) throws DatabaseException {
227         if (element == null)
228             return null;
229         Resource mappedElement = getMappedElement(graph, element);
230         if (mappedElement == null)
231             return null;
232         ModelingResources MOD = ModelingResources.getInstance(graph);
233         return graph.getPossibleObject(mappedElement, MOD.ElementToComponent);
234     }
235     
236     public static Resource getMappedComponentCached(ReadGraph graph, Resource vertex) throws DatabaseException {
237         return graph.syncRequest(new MappedComponentRequest(vertex), TransientCacheListener.instance());
238     }
239
240     public static Resource getMappedDNElement(ReadGraph graph, Resource element) throws DatabaseException {
241         if (element == null)
242             return null;
243         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
244         return graph.getPossibleObject(element, DN.MappedFromElement);
245     }
246
247     public static Variable toMappedConfigurationModule(ReadGraph graph, Resource input) throws DatabaseException {
248         if (input == null)
249             return null;
250
251         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
252         if (graph.isInstanceOf(input, DN.Element)) {
253             Resource mappedElement = getMappedElement(graph, input);
254             if (mappedElement == null)
255                 return null;
256
257             ModelingResources MOD = ModelingResources.getInstance(graph);
258             Resource mappedComponent = graph.getPossibleObject(mappedElement, MOD.ElementToComponent);
259             if (mappedComponent == null)
260                 return null;
261
262             return graph.syncRequest(new PossibleVariable(mappedComponent));
263         }
264         return null;
265     }
266
267     public static void toggleDrawMap(WriteGraph graph, Resource diagram) throws ManyObjectsForFunctionalRelationException, BindingException, ServiceException {
268         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
269         Boolean current = graph.getPossibleRelatedValue(diagram, DN.Diagram_drawMapEnabled, Bindings.BOOLEAN);
270         if (current == null)
271             current = true;
272         graph.claimLiteral(diagram, DN.Diagram_drawMapEnabled, !current, Bindings.BOOLEAN);
273     }
274
275     public static Boolean drawMapEnabled(ReadGraph graph, Resource diagram) throws ManyObjectsForFunctionalRelationException, BindingException, ServiceException {
276         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
277         Boolean current = graph.getPossibleRelatedValue(diagram, DN.Diagram_drawMapEnabled, Bindings.BOOLEAN);
278         return current != null ? current : true;
279     }
280
281     public static void changeMapBackgroundColor(WriteGraph graph, Resource diagram, Integer integer) throws DatabaseException {
282         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
283         graph.claimLiteral(diagram, DN.Diagram_backgroundColor, integer, Bindings.getBindingUnchecked(RGB.Integer.class));
284     }
285     
286     public static Boolean trackChangesEnabled(ReadGraph graph, Resource diagram) throws DatabaseException {
287         if (diagram != null && graph.hasStatement(diagram)) {
288             return Boolean.TRUE.equals(graph.getPossibleRelatedValue(diagram,
289                 DistrictNetworkResource.getInstance(graph).Diagram_trackChangesEnabled));
290         } else {
291             return false;
292         }
293     }
294
295     public static RGB.Integer backgroundColor(ReadGraph graph, Resource diagram) throws DatabaseException {
296         return graph.getPossibleRelatedValue(diagram,
297                 DistrictNetworkResource.getInstance(graph).Diagram_backgroundColor,
298                 Bindings.getBindingUnchecked(RGB.Integer.class));
299     }
300     
301     public static Resource createNetworkDiagram(WriteGraph graph, Resource target, Resource compositeType, String defaultName, Resource defaultEdgeMapping, Resource defaultVertexMapping, Resource rightClickVertexMapping, Resource leftClickVertexMapping, Resource crs) throws DatabaseException {
302         Resource composite = NewCompositeActionFactory.createComposite(graph, target, defaultName, compositeType);
303
304         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
305         Resource diagram = graph.getSingleObject(composite, ModelingResources.getInstance(graph).CompositeToDiagram);
306         graph.claim(diagram, DN.EdgeDefaultMapping, defaultEdgeMapping);
307         graph.claim(diagram, DN.VertexDefaultMapping, defaultVertexMapping);
308         graph.claim(diagram, DN.RightClickDefaultMapping, rightClickVertexMapping);
309         graph.claim(diagram, DN.LeftClickDefaultMapping, leftClickVertexMapping);
310         graph.claim(diagram, DN.HasSpatialRefSystem, crs);
311         
312         // Generated name prefix from composite name
313         String compositeName = graph.getRelatedValue2(composite, Layer0.getInstance(graph).HasName, Bindings.STRING);
314         graph.claimLiteral(diagram, Layer0X.getInstance(graph).HasGeneratedNamePrefix, "N" + compositeName.substring(compositeName.length() - 1, compositeName.length()));
315         
316         return composite;
317     }
318
319     public static final class MappedComponentRequest extends ResourceRead<Resource> {
320         public MappedComponentRequest(Resource element) {
321             super(element);
322         }
323
324         @Override
325         public Resource perform(ReadGraph graph) throws DatabaseException {
326             return getMappedComponent(graph, resource);
327         }
328     }
329
330     public static class ResourceVertex {
331         
332         public final boolean isConsumer;
333         public final Resource vertex;
334         public final double[] coords;
335         
336         public ResourceVertex(Resource vertex, double[] coords, boolean isConsumer) {
337             this.vertex = vertex;
338             this.coords = coords;
339             this.isConsumer = isConsumer;
340         }
341     }
342     
343     public static void changeMappingType(WriteGraph graph, Resource newMapping, List<Resource> elements) throws DatabaseException {
344         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
345         for (Resource element : elements) {
346             graph.deny(element, DN.HasMapping);
347             graph.claim(element, DN.HasMapping, newMapping);
348         }
349     }
350
351     public static Stream<Resource> findDNElementsById(ReadGraph graph, Resource context, String idToFind) throws DatabaseException {
352         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); 
353         return IndexUtils.findByType(graph,
354             graph.syncRequest(new IndexRoot(context)),
355             DN.Element
356         ).stream().filter(element -> {
357             try {
358                 String id = graph.getPossibleRelatedValue(element, DN.HasId, Bindings.STRING);
359                 return id != null && id.contains(idToFind);
360             } catch (DatabaseException e) {
361                 LOGGER.error("Could not read id for element {]", element, e);
362                 return false;
363             }
364         });
365     }
366     
367     public static Resource findDNElementById(ReadGraph graph, Resource context, String idToFind) throws DatabaseException {
368         List<Resource> elements = findDNElementsById(graph, context, idToFind).collect(Collectors.toList());
369         if (elements.size() == 1) {
370             return elements.iterator().next();
371         }
372         return null;
373     }
374     
375     public static List<Resource> findDNElementByXYCoordinates(ReadGraph graph, Resource context, double lat, double lon, double padding) throws DatabaseException {
376         DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
377         DiagramResource DIA = DiagramResource.getInstance(graph);
378         List<Resource> results = new ArrayList<>();
379         Collection<Resource> vertices = IndexUtils.findByType(graph, graph.syncRequest(new IndexRoot(context)), DN.Vertex);
380         Rectangle2D rect = new Rectangle2D.Double(lat, lon, padding, padding);
381         for (Resource vertex : vertices) {
382             double[] location = graph.getRelatedValue(vertex, DIA.HasLocation, Bindings.DOUBLE_ARRAY);
383             if (rect.contains(location[0], location[1])) {
384                 results.add(vertex);
385             }
386         }
387         return results;
388     }
389 }