From: Tuukka Lehtonen Date: Fri, 6 Sep 2019 08:56:11 +0000 (+0300) Subject: Merge remote-tracking branch 'origin/master' into release/1.35.2 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=fb4e5f02481e918807be83e87239a4d91b4343f2;hp=7d92384ac5455e67e69c02bff9a53e1f0cc256b2;p=simantics%2Fdistrict.git Merge remote-tracking branch 'origin/master' into release/1.35.2 Conflicts resolved: org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportModel.java org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizard.java org.simantics.district.network.ontology/graph/DistrictNetworkProfiles.pgraph org.simantics.district.network.ontology/src/org/simantics/district/network/ontology/DistrictNetworkResource.java org.simantics.district.network.ui/META-INF/MANIFEST.MF org.simantics.district.network.ui/adapters.xml org.simantics.district.network.ui/fragment.e4xmi org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictTransformUtil.java org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/NetworkElementActionMenuContribution.java org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkEdgeNode.java org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkVertexNode.java org.simantics.district.network/scl/Simantics/District/Algorithm.scl org.simantics.district.network/src/org/simantics/district/network/DistrictNetworkUtil.java org.simantics.district.network/src/org/simantics/district/network/profile/ArrowLengthStyle.java gitlab #60 --- diff --git a/org.simantics.district.feature/build.properties b/org.simantics.district.feature/build.properties index 82ab19c6..191f8907 100644 --- a/org.simantics.district.feature/build.properties +++ b/org.simantics.district.feature/build.properties @@ -1 +1,2 @@ -bin.includes = feature.xml +bin.includes = feature.xml +root=rootFiles diff --git a/org.simantics.district.feature/feature.xml b/org.simantics.district.feature/feature.xml index f08da0a1..c27c5d6d 100644 --- a/org.simantics.district.feature/feature.xml +++ b/org.simantics.district.feature/feature.xml @@ -77,4 +77,18 @@ version="0.0.0" unpack="false"/> + + + + diff --git a/org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV.py b/org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV.py new file mode 100644 index 00000000..e4d51508 --- /dev/null +++ b/org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV.py @@ -0,0 +1,402 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +*************************************************************************** +""" + +from PyQt5.QtCore import (QCoreApplication, QVariant) +from qgis.core import (QgsProcessing, + QgsFeatureSink, + QgsFeature, + QgsProcessingException, + QgsProcessingAlgorithm, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterFeatureSink, + QgsProcessingParameterVectorLayer, + QgsProcessingParameterDistance, + QgsVectorDataProvider, + QgsFields, + QgsField, + QgsUnitTypes) +import processing +import re +from operator import itemgetter + +class SpanCoordinatesAlgorithm(QgsProcessingAlgorithm): + """ + This is an example algorithm that takes a vector layer and + creates a new identical one. + + It is meant to be used as an example of how to create your own + algorithms and explain methods and variables used to do it. An + algorithm like this will be available in all elements, and there + is not need for additional work. + + All Processing algorithms should extend the QgsProcessingAlgorithm + class. + """ + + # Constants used to refer to parameters and outputs. They will be + # used when calling the algorithm from another algorithm, or when + # calling from the QGIS console. + + PIPE = 'PIPE' + SPAN = 'SPAN' + TOLERANCE = 'TOLERANCE' + MINIMUM_LENGTH = 'MINIMUM_LENGTH' + OUTPUT = 'OUTPUT' + + # Constants for feature field names + FATHER_ID = 'FatherId' + LINESTRING = 'LineString' + + # RegExp for DN values in + DN_PATTERN = re.compile(r"DN(\d+)(-.*)?") + + def tr(self, string): + """ + Returns a translatable string with the self.tr() function. + """ + return QCoreApplication.translate('Processing', string) + + def createInstance(self): + return SpanCoordinatesAlgorithm() + + def name(self): + """ + Returns the algorithm name, used for identifying the algorithm. This + string should be fixed for the algorithm, and must not be localised. + The name should be unique within each provider. Names should contain + lowercase alphanumeric characters only and no spaces or other + formatting characters. + """ + return 'calculateDistrictCSV' + + def displayName(self): + """ + Returns the translated algorithm name, which should be used for any + user-visible display of the algorithm name. + """ + return self.tr('Calculate CSV for Apros District') + + def group(self): + """ + Returns the name of the group this algorithm belongs to. This string + should be localised. + """ + return self.tr('District network scripts') + + def groupId(self): + """ + Returns the unique ID of the group this algorithm belongs to. This + string should be fixed for the algorithm, and must not be localised. + The group id should be unique within each provider. Group id should + contain lowercase alphanumeric characters only and no spaces or other + formatting characters. + """ + return 'districtscripts' + + def shortHelpString(self): + """ + Returns a localised short helper string for the algorithm. This string + should provide a basic description about what the algorithm does and the + parameters and outputs associated with it.. + """ + return self.tr("Calculate a string of coordinate points for a point span") + + def initAlgorithm(self, config=None): + """ + Here we define the inputs and output of the algorithm, along + with some other properties. + """ + + # We add the input vector features source. It can have any kind of + # geometry. + self.addParameter( + QgsProcessingParameterVectorLayer( + self.PIPE, + self.tr('Pipeline layer'), + [QgsProcessing.TypeVectorLine] + ) + ) + + self.addParameter( + QgsProcessingParameterVectorLayer( + self.SPAN, + self.tr('Point span layer'), + [QgsProcessing.TypeVectorLine] + ) + ) + + tol = QgsProcessingParameterDistance( + self.TOLERANCE, + self.tr('Location tolerance'), + 0.001, + minValue = 0.0 + ) + tol.setDefaultUnit(QgsUnitTypes.DistanceMeters) + self.addParameter( tol ) + + dist = QgsProcessingParameterDistance( + self.MINIMUM_LENGTH, + self.tr('Minimum span length'), + 0.25, + minValue = 0.0 + ) + dist.setDefaultUnit(QgsUnitTypes.DistanceMeters) + self.addParameter( dist ) + + self.addParameter( + QgsProcessingParameterFeatureSink( + self.OUTPUT, + self.tr('Output layer'), + QgsProcessing.TypeVectorLine + ) + ) + + def processAlgorithm(self, parameters, context, feedback): + """ + Here is where the processing itself takes place. + """ + + # Retrieve the feature source and sink. The 'dest_id' variable is used + # to uniquely identify the feature sink, and must be included in the + # dictionary returned by the processAlgorithm function. + pipe = self.parameterAsLayer( + parameters, + self.PIPE, + context + ) + + span = self.parameterAsLayer( + parameters, + self.SPAN, + context + ) + + eps = self.parameterAsDouble( + parameters, + self.TOLERANCE, + context + ) + + minLength = self.parameterAsDouble( + parameters, + self.MINIMUM_LENGTH, + context + ) + + feedback.pushInfo('Tolerance: {} m\nMinimum span length: {} m'.format(eps, minLength)) + + sourceFields = span.fields() + sourceNames = sourceFields.names() + outputFields = QgsFields(sourceFields) + if not ('x1' in sourceNames): outputFields.append(QgsField('x1', QVariant.Double)) + if not ('y1' in sourceNames): outputFields.append(QgsField('y1', QVariant.Double)) + if not ('z1' in sourceNames): outputFields.append(QgsField('z1', QVariant.Double)) + if not ('x2' in sourceNames): outputFields.append(QgsField('x2', QVariant.Double)) + if not ('y2' in sourceNames): outputFields.append(QgsField('y2', QVariant.Double)) + if not ('z2' in sourceNames): outputFields.append(QgsField('z2', QVariant.Double)) + if not ('Length' in sourceNames): outputFields.append(QgsField('Length', QVariant.Double)) + if not ('LineString' in sourceNames): outputFields.append(QgsField('LineString', QVariant.String)) + if not ('DimensionDN' in sourceNames): outputFields.append(QgsField('DimensionDN', QVariant.Int)) + if not ('PipeStruct' in sourceNames): outputFields.append(QgsField('PipeStruct', QVariant.String)) + + (output, outputId) = self.parameterAsSink( + parameters, + self.OUTPUT, + context, + outputFields + ) + + # If source was not found, throw an exception to indicate that the algorithm + # encountered a fatal error. The exception text can be any string, but in this + # case we use the pre-built invalidSourceError method to return a standard + # helper text for when a source cannot be evaluated + if pipe is None: + raise QgsProcessingException(self.invalidSourceError(parameters, self.PIPE)) + if span is None: + raise QgsProcessingException(self.invalidSourceError(parameters, self.SPAN)) + + # Compute the number of steps to display within the progress bar and + # get features from source + total = 100.0 / pipe.featureCount() if pipe.featureCount() else 0 + + # Dictionary from span feature ids to lengths + lengths = dict() + + # Dictionary from span feature ids to lists of lists of QgsPoint objects + strings = dict() + + # Dictionary for the PipeStruct field values + types = dict() + + spanFeatures = span.getFeatures() + pipeFeatures = pipe.getFeatures() + + for counter, feature in enumerate(pipeFeatures): + if feedback.isCanceled(): break + + geometry = feature.geometry() + if geometry == None: continue + + fatherID = feature[self.FATHER_ID] + + # Length + myLength = feature['Length'] + if myLength == None: + myLength = geometry.length() + + oldLength = lengths.get(fatherID, 0.0) + lengths[fatherID] = oldLength + myLength + + # Segment points + pointList = strings.get(fatherID, []) + # feedback.pushInfo('Point list: {}'.format(pointList)) + mylist = [] + + vertices = geometry.vertices() + while vertices.hasNext(): + mylist.append(vertices.next()) + + # feedback.pushInfo('Feature {}, Father {}, Points: {}'.format(feature['Id'], fatherID, ";".join(map(lambda x: '{} {}'.format(x.x(), x.y()), mylist)))) + + pointList.append(mylist) + strings[fatherID] = pointList + + # Store the value of PipeStruct + t = feature['PipeStruct'] + tt = types.get(fatherID, {}) + types[fatherID] = tt + c = tt.get(t, 0) + c += myLength + tt[t] = c + + # Update the progress bar + feedback.setProgress(int(counter * total)) + + if feedback.isCanceled(): + return + + feedback.pushInfo('Done') + + #span.startEditing() + + feedback.pushInfo('Started editing') + feedback.pushInfo(str(spanFeatures)) + + for feature in spanFeatures: + if feedback.isCanceled(): break + + #feedback.pushInfo(str(feature)) + id = feature['Id'] + #feedback.pushInfo(str(id)) + + # Length + myLength = feature['Length'] + + # Ignore short stumps + if myLength <= minLength: + continue + + # Vertices + mypoints = list(feature.geometry().vertices()) + mylist = strings.get(id, None) + if mylist == None: + feedback.pushInfo('No points for feature {}'.format(id)) + mylist = [mypoints] + + #feedback.pushInfo('Points: {}'.format("|".join(map(lambda x: ";".join(('{} {}'.format(p.x(), p.y()) for p in x)), mylist)))) + + head = feature.geometry().vertices().next() + resultList = [head] + + #feedback.pushInfo(str(resultList)) + + i = next((i for i, x in enumerate(mylist) if head.distance(x[0]) <= eps), None) + if i == None: + mylist = list(map(lambda x: list(reversed(x)), mylist)) + i = next((i for i, x in enumerate(mylist) if head.distance(x[0]) <= eps), None) + if i == None: + feedback.pushInfo('Warning: No matching start vertex for feature {}'.format(id)) + mylist = [mypoints] + i = 0 + + vertices = mylist.pop(i) + + while i != None: + tail = vertices[-1] + resultList.extend(vertices[1:]) + if tail.distance(mypoints[-1]) <= eps: + break + + i = next((i for i, x in enumerate(mylist) if tail.distance(x[0]) <= eps), None) + if i != None: + vertices = mylist.pop(i) + else: + i = next((i for i, x in enumerate(mylist) if tail.distance(x[-1]) <= eps), None) + if i != None: + vertices = list(reversed(mylist.pop(i))) + + # feedback.pushInfo(str(resultList)) + + # Convert to string + result = ";".join(('{} {}'.format(p.x(), p.y()) for p in resultList)) + + # feedback.pushInfo('Feature {}: {}'.format(id, result)) + + outputFeature = QgsFeature() + outputFeature.setFields(outputFields) + for i, x in enumerate(feature.attributes()): + fieldName = sourceFields[i].name() + outputFeature[fieldName] = x + + outputFeature['x1'] = feature['x1'] + outputFeature['y1'] = feature['y1'] + outputFeature['z1'] = feature['z1'] + outputFeature['x2'] = feature['x2'] + outputFeature['y2'] = feature['y2'] + outputFeature['z2'] = feature['z2'] + outputFeature['Length'] = feature['Length'] # myLength + outputFeature['LineString'] = result + + # Handle pipe type codes + mytypes = list(types.get(id, {}).items()) + if len(mytypes) == 0: + feedback.pushInfo('No type codes for feature {}'.format(id)) + else: + if len(mytypes) > 1: + mytypes.sort(key = itemgetter(1)) + feedback.pushInfo('No unique type code for feature {}: {}'.format(id, mytypes)) + outputFeature['PipeStruct'] = mytypes[-1][0] + + label = feature['Label'] + m = self.DN_PATTERN.fullmatch(label) + if m: + outputFeature['DimensionDN'] = int(m.group(1)) + + output.addFeature(outputFeature) + + feedback.pushInfo('Loop done') + + #if feedback.isCanceled(): + # span.rollBack() + #else: + # span.commitChanges() + + feedback.pushInfo('Changes committed') + + # Return the results of the algorithm. In this case our only result is + # the feature sink which contains the processed features, but some + # algorithms may return multiple feature sinks, calculated numeric + # statistics, etc. These should all be included in the returned + # dictionary, with keys matching the feature corresponding parameter + # or output names. + return {self.OUTPUT: outputId} diff --git a/org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV2.py b/org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV2.py new file mode 100644 index 00000000..bb4bc8be --- /dev/null +++ b/org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV2.py @@ -0,0 +1,415 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +*************************************************************************** +""" + +from PyQt5.QtCore import (QCoreApplication, QVariant) +from qgis.core import (QgsProcessing, + QgsFeatureSink, + QgsFeature, + QgsProcessingException, + QgsProcessingAlgorithm, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterFeatureSink, + QgsProcessingParameterVectorLayer, + QgsProcessingParameterDistance, + QgsVectorDataProvider, + QgsFields, + QgsField, + QgsUnitTypes) +import processing +import re +from operator import itemgetter + +class SpanCoordinatesAlgorithm2(QgsProcessingAlgorithm): + """ + This is an example algorithm that takes a vector layer and + creates a new identical one. + + It is meant to be used as an example of how to create your own + algorithms and explain methods and variables used to do it. An + algorithm like this will be available in all elements, and there + is not need for additional work. + + All Processing algorithms should extend the QgsProcessingAlgorithm + class. + """ + + # Constants used to refer to parameters and outputs. They will be + # used when calling the algorithm from another algorithm, or when + # calling from the QGIS console. + + PIPE = 'PIPE' + SPAN = 'SPAN' + TOLERANCE = 'TOLERANCE' + MINIMUM_LENGTH = 'MINIMUM_LENGTH' + OUTPUT = 'OUTPUT' + + # Constants for feature field names + FATHER_ID = 'FatherId' + LINESTRING = 'LineString' + + # RegExp for DN values in + DN_PATTERN = re.compile(r"DN(\d+)(-.*)?") + + def tr(self, string): + """ + Returns a translatable string with the self.tr() function. + """ + return QCoreApplication.translate('Processing', string) + + def createInstance(self): + return SpanCoordinatesAlgorithm2() + + def name(self): + """ + Returns the algorithm name, used for identifying the algorithm. This + string should be fixed for the algorithm, and must not be localised. + The name should be unique within each provider. Names should contain + lowercase alphanumeric characters only and no spaces or other + formatting characters. + """ + return 'calculateDistrictCSV2' + + def displayName(self): + """ + Returns the translated algorithm name, which should be used for any + user-visible display of the algorithm name. + """ + return self.tr('Calculate CSV for Apros District (No coordinates)') + + def group(self): + """ + Returns the name of the group this algorithm belongs to. This string + should be localised. + """ + return self.tr('District network scripts') + + def groupId(self): + """ + Returns the unique ID of the group this algorithm belongs to. This + string should be fixed for the algorithm, and must not be localised. + The group id should be unique within each provider. Group id should + contain lowercase alphanumeric characters only and no spaces or other + formatting characters. + """ + return 'districtscripts' + + def shortHelpString(self): + """ + Returns a localised short helper string for the algorithm. This string + should provide a basic description about what the algorithm does and the + parameters and outputs associated with it.. + """ + return self.tr("Calculate a string of coordinate points for a point span") + + def initAlgorithm(self, config=None): + """ + Here we define the inputs and output of the algorithm, along + with some other properties. + """ + + # We add the input vector features source. It can have any kind of + # geometry. + self.addParameter( + QgsProcessingParameterVectorLayer( + self.PIPE, + self.tr('Pipeline layer'), + [QgsProcessing.TypeVectorLine] + ) + ) + + self.addParameter( + QgsProcessingParameterVectorLayer( + self.SPAN, + self.tr('Point span layer'), + [QgsProcessing.TypeVectorLine] + ) + ) + + tol = QgsProcessingParameterDistance( + self.TOLERANCE, + self.tr('Location tolerance'), + 0.001, + minValue = 0.0 + ) + tol.setDefaultUnit(QgsUnitTypes.DistanceMeters) + self.addParameter( tol ) + + dist = QgsProcessingParameterDistance( + self.MINIMUM_LENGTH, + self.tr('Minimum span length'), + 0.25, + minValue = 0.0 + ) + dist.setDefaultUnit(QgsUnitTypes.DistanceMeters) + self.addParameter( dist ) + + self.addParameter( + QgsProcessingParameterFeatureSink( + self.OUTPUT, + self.tr('Output layer'), + QgsProcessing.TypeVectorLine + ) + ) + + def processAlgorithm(self, parameters, context, feedback): + """ + Here is where the processing itself takes place. + """ + + # Retrieve the feature source and sink. The 'dest_id' variable is used + # to uniquely identify the feature sink, and must be included in the + # dictionary returned by the processAlgorithm function. + pipe = self.parameterAsLayer( + parameters, + self.PIPE, + context + ) + + span = self.parameterAsLayer( + parameters, + self.SPAN, + context + ) + + eps = self.parameterAsDouble( + parameters, + self.TOLERANCE, + context + ) + + minLength = self.parameterAsDouble( + parameters, + self.MINIMUM_LENGTH, + context + ) + + feedback.pushInfo('Tolerance: {} m\nMinimum span length: {} m'.format(eps, minLength)) + + sourceFields = span.fields() + sourceNames = sourceFields.names() + outputFields = QgsFields(sourceFields) + if not ('x1' in sourceNames): outputFields.append(QgsField('x1', QVariant.Double)) + if not ('y1' in sourceNames): outputFields.append(QgsField('y1', QVariant.Double)) + if not ('z1' in sourceNames): outputFields.append(QgsField('z1', QVariant.Double)) + if not ('x2' in sourceNames): outputFields.append(QgsField('x2', QVariant.Double)) + if not ('y2' in sourceNames): outputFields.append(QgsField('y2', QVariant.Double)) + if not ('z2' in sourceNames): outputFields.append(QgsField('z2', QVariant.Double)) + if not ('Length' in sourceNames): outputFields.append(QgsField('Length', QVariant.Double)) + if not ('LineString' in sourceNames): outputFields.append(QgsField('LineString', QVariant.String)) + if not ('DimensionDN' in sourceNames): outputFields.append(QgsField('DimensionDN', QVariant.Int)) + if not ('PipeStruct' in sourceNames): outputFields.append(QgsField('PipeStruct', QVariant.String)) + + (output, outputId) = self.parameterAsSink( + parameters, + self.OUTPUT, + context, + outputFields + ) + + # If source was not found, throw an exception to indicate that the algorithm + # encountered a fatal error. The exception text can be any string, but in this + # case we use the pre-built invalidSourceError method to return a standard + # helper text for when a source cannot be evaluated + if pipe is None: + raise QgsProcessingException(self.invalidSourceError(parameters, self.PIPE)) + if span is None: + raise QgsProcessingException(self.invalidSourceError(parameters, self.SPAN)) + + # Compute the number of steps to display within the progress bar and + # get features from source + total = 100.0 / pipe.featureCount() if pipe.featureCount() else 0 + + # Dictionary from span feature ids to lengths + lengths = dict() + + # Dictionary from span feature ids to lists of lists of QgsPoint objects + strings = dict() + + # Dictionary for the PipeStruct field values + types = dict() + + spanFeatures = span.getFeatures() + pipeFeatures = pipe.getFeatures() + + for counter, feature in enumerate(pipeFeatures): + if feedback.isCanceled(): break + + geometry = feature.geometry() + if geometry == None: continue + + fatherID = feature[self.FATHER_ID] + + # Length + myLength = None + if feature.fields().lookupField('Length') != -1: + myLength = feature['Length'] + if myLength == None: + myLength = geometry.length() + + oldLength = lengths.get(fatherID, 0.0) + lengths[fatherID] = oldLength + myLength + + # Segment points + pointList = strings.get(fatherID, []) + # feedback.pushInfo('Point list: {}'.format(pointList)) + mylist = [] + + vertices = geometry.vertices() + while vertices.hasNext(): + mylist.append(vertices.next()) + + # feedback.pushInfo('Feature {}, Father {}, Points: {}'.format(feature['Id'], fatherID, ";".join(map(lambda x: '{} {}'.format(x.x(), x.y()), mylist)))) + + pointList.append(mylist) + strings[fatherID] = pointList + + # Store the value of PipeStruct + t = feature['PipeStruct'] + tt = types.get(fatherID, {}) + types[fatherID] = tt + c = tt.get(t, 0) + c += myLength + tt[t] = c + + # Update the progress bar + feedback.setProgress(int(counter * total)) + + if feedback.isCanceled(): + return + + feedback.pushInfo('Done') + + #span.startEditing() + + feedback.pushInfo('Started editing') + feedback.pushInfo(str(spanFeatures)) + + for feature in spanFeatures: + if feedback.isCanceled(): break + + f = feature.fields() + + #feedback.pushInfo(str(feature)) + id = feature['Id'] + #feedback.pushInfo(str(id)) + + # Length + myLength = None + if f.lookupField('Length') != -1: + myLength = feature['Length'] + if myLength == None: + myLength = feature.geometry().length() + + # Ignore short stumps + if myLength <= minLength: + continue + + # Vertices + mypoints = list(feature.geometry().vertices()) + mylist = strings.get(id, None) + if mylist == None: + feedback.pushInfo('No points for feature {}'.format(id)) + mylist = [mypoints] + + #feedback.pushInfo('Points: {}'.format("|".join(map(lambda x: ";".join(('{} {}'.format(p.x(), p.y()) for p in x)), mylist)))) + + head = feature.geometry().vertices().next() + resultList = [head] + + #feedback.pushInfo(str(resultList)) + + i = next((i for i, x in enumerate(mylist) if head.distance(x[0]) <= eps), None) + if i == None: + mylist = list(map(lambda x: list(reversed(x)), mylist)) + i = next((i for i, x in enumerate(mylist) if head.distance(x[0]) <= eps), None) + if i == None: + feedback.pushInfo('Warning: No matching start vertex for feature {}'.format(id)) + mylist = [mypoints] + i = 0 + + vertices = mylist.pop(i) + + while i != None: + tail = vertices[-1] + resultList.extend(vertices[1:]) + if tail.distance(mypoints[-1]) <= eps: + break + + i = next((i for i, x in enumerate(mylist) if tail.distance(x[0]) <= eps), None) + if i != None: + vertices = mylist.pop(i) + else: + i = next((i for i, x in enumerate(mylist) if tail.distance(x[-1]) <= eps), None) + if i != None: + vertices = list(reversed(mylist.pop(i))) + + # feedback.pushInfo(str(resultList)) + + # Convert to string + result = ";".join(('{} {}'.format(p.x(), p.y()) for p in resultList)) + + # feedback.pushInfo('Feature {}: {}'.format(id, result)) + + outputFeature = QgsFeature() + outputFeature.setFields(outputFields) + for i, x in enumerate(feature.attributes()): + fieldName = sourceFields[i].name() + outputFeature[fieldName] = x + + vts = list(feature.geometry().vertices()) + p1 = vts[0] + p2 = vts[-1] + + outputFeature['x1'] = feature['x1'] if f.lookupField('x1') != -1 else p1.x() + outputFeature['y1'] = feature['y1'] if f.lookupField('y1') != -1 else p1.y() + outputFeature['z1'] = feature['z1'] if f.lookupField('z1') != -1 else p1.z() + outputFeature['x2'] = feature['x2'] if f.lookupField('x2') != -1 else p2.x() + outputFeature['y2'] = feature['y2'] if f.lookupField('y2') != -1 else p2.y() + outputFeature['z2'] = feature['z2'] if f.lookupField('z2') != -1 else p2.z() + outputFeature['Length'] = feature['Length'] if f.lookupField('Length') != -1 else feature.geometry().length() + outputFeature['LineString'] = result + + # Handle pipe type codes + mytypes = list(types.get(id, {}).items()) + if len(mytypes) == 0: + feedback.pushInfo('No type codes for feature {}'.format(id)) + else: + if len(mytypes) > 1: + mytypes.sort(key = itemgetter(1)) + feedback.pushInfo('No unique type code for feature {}: {}'.format(id, mytypes)) + outputFeature['PipeStruct'] = mytypes[-1][0] + + if f.lookupField('Label') != -1: + label = feature['Label'] + m = self.DN_PATTERN.fullmatch(label) + if m: + outputFeature['DimensionDN'] = int(m.group(1)) + + output.addFeature(outputFeature) + + feedback.pushInfo('Loop done') + + #if feedback.isCanceled(): + # span.rollBack() + #else: + # span.commitChanges() + + feedback.pushInfo('Changes committed') + + # Return the results of the algorithm. In this case our only result is + # the feature sink which contains the processed features, but some + # algorithms may return multiple feature sinks, calculated numeric + # statistics, etc. These should all be included in the returned + # dictionary, with keys matching the feature corresponding parameter + # or output names. + return {self.OUTPUT: outputId} diff --git a/org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV3.py b/org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV3.py new file mode 100644 index 00000000..5b372557 --- /dev/null +++ b/org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV3.py @@ -0,0 +1,412 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +*************************************************************************** +""" + +from PyQt5.QtCore import (QCoreApplication, QVariant) +from qgis.core import (QgsProcessing, + QgsFeatureSink, + QgsFeature, + QgsProcessingException, + QgsProcessingAlgorithm, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterFeatureSink, + QgsProcessingParameterVectorLayer, + QgsProcessingParameterDistance, + QgsVectorDataProvider, + QgsFields, + QgsField, + QgsUnitTypes) +import processing +import re +from operator import itemgetter + +class SpanCoordinatesAlgorithm3(QgsProcessingAlgorithm): + """ + This is an example algorithm that takes a vector layer and + creates a new identical one. + + It is meant to be used as an example of how to create your own + algorithms and explain methods and variables used to do it. An + algorithm like this will be available in all elements, and there + is not need for additional work. + + All Processing algorithms should extend the QgsProcessingAlgorithm + class. + """ + + # Constants used to refer to parameters and outputs. They will be + # used when calling the algorithm from another algorithm, or when + # calling from the QGIS console. + + PIPE = 'PIPE' + SPAN = 'SPAN' + TOLERANCE = 'TOLERANCE' + MINIMUM_LENGTH = 'MINIMUM_LENGTH' + OUTPUT = 'OUTPUT' + + # Constants for feature field names + FATHER_ID = 'FatherId' + LINESTRING = 'LineString' + + # RegExp for DN values in + DN_PATTERN = re.compile(r"DN(\d+)(-.*)?") + + def tr(self, string): + """ + Returns a translatable string with the self.tr() function. + """ + return QCoreApplication.translate('Processing', string) + + def createInstance(self): + return SpanCoordinatesAlgorithm3() + + def name(self): + """ + Returns the algorithm name, used for identifying the algorithm. This + string should be fixed for the algorithm, and must not be localised. + The name should be unique within each provider. Names should contain + lowercase alphanumeric characters only and no spaces or other + formatting characters. + """ + return 'calculateDistrictCSV3' + + def displayName(self): + """ + Returns the translated algorithm name, which should be used for any + user-visible display of the algorithm name. + """ + return self.tr('Calculate CSV for Apros District (TechTypeData)') + + def group(self): + """ + Returns the name of the group this algorithm belongs to. This string + should be localised. + """ + return self.tr('District network scripts') + + def groupId(self): + """ + Returns the unique ID of the group this algorithm belongs to. This + string should be fixed for the algorithm, and must not be localised. + The group id should be unique within each provider. Group id should + contain lowercase alphanumeric characters only and no spaces or other + formatting characters. + """ + return 'districtscripts' + + def shortHelpString(self): + """ + Returns a localised short helper string for the algorithm. This string + should provide a basic description about what the algorithm does and the + parameters and outputs associated with it.. + """ + return self.tr("Calculate a string of coordinate points for a point span") + + def initAlgorithm(self, config=None): + """ + Here we define the inputs and output of the algorithm, along + with some other properties. + """ + + # We add the input vector features source. It can have any kind of + # geometry. + self.addParameter( + QgsProcessingParameterVectorLayer( + self.PIPE, + self.tr('Pipeline layer'), + [QgsProcessing.TypeVectorLine] + ) + ) + + self.addParameter( + QgsProcessingParameterVectorLayer( + self.SPAN, + self.tr('Point span layer'), + [QgsProcessing.TypeVectorLine] + ) + ) + + tol = QgsProcessingParameterDistance( + self.TOLERANCE, + self.tr('Location tolerance'), + 0.001, + minValue = 0.0 + ) + tol.setDefaultUnit(QgsUnitTypes.DistanceMeters) + self.addParameter( tol ) + + dist = QgsProcessingParameterDistance( + self.MINIMUM_LENGTH, + self.tr('Minimum span length'), + 0.25, + minValue = 0.0 + ) + dist.setDefaultUnit(QgsUnitTypes.DistanceMeters) + self.addParameter( dist ) + + self.addParameter( + QgsProcessingParameterFeatureSink( + self.OUTPUT, + self.tr('Output layer'), + QgsProcessing.TypeVectorLine + ) + ) + + def processAlgorithm(self, parameters, context, feedback): + """ + Here is where the processing itself takes place. + """ + + # Retrieve the feature source and sink. The 'dest_id' variable is used + # to uniquely identify the feature sink, and must be included in the + # dictionary returned by the processAlgorithm function. + pipe = self.parameterAsLayer( + parameters, + self.PIPE, + context + ) + + span = self.parameterAsLayer( + parameters, + self.SPAN, + context + ) + + eps = self.parameterAsDouble( + parameters, + self.TOLERANCE, + context + ) + + minLength = self.parameterAsDouble( + parameters, + self.MINIMUM_LENGTH, + context + ) + + feedback.pushInfo('Tolerance: {} m\nMinimum span length: {} m'.format(eps, minLength)) + + sourceFields = span.fields() + sourceNames = sourceFields.names() + outputFields = QgsFields(sourceFields) + if not ('x1' in sourceNames): outputFields.append(QgsField('x1', QVariant.Double)) + if not ('y1' in sourceNames): outputFields.append(QgsField('y1', QVariant.Double)) + if not ('z1' in sourceNames): outputFields.append(QgsField('z1', QVariant.Double)) + if not ('x2' in sourceNames): outputFields.append(QgsField('x2', QVariant.Double)) + if not ('y2' in sourceNames): outputFields.append(QgsField('y2', QVariant.Double)) + if not ('z2' in sourceNames): outputFields.append(QgsField('z2', QVariant.Double)) + if not ('Length' in sourceNames): outputFields.append(QgsField('Length', QVariant.Double)) + if not ('LineString' in sourceNames): outputFields.append(QgsField('LineString', QVariant.String)) + if not ('TechTypeId' in sourceNames): outputFields.append(QgsField('TechTypeId', QVariant.String)) + if not ('PipeStruct' in sourceNames): outputFields.append(QgsField('PipeStruct', QVariant.String)) + + (output, outputId) = self.parameterAsSink( + parameters, + self.OUTPUT, + context, + outputFields + ) + + # If source was not found, throw an exception to indicate that the algorithm + # encountered a fatal error. The exception text can be any string, but in this + # case we use the pre-built invalidSourceError method to return a standard + # helper text for when a source cannot be evaluated + if pipe is None: + raise QgsProcessingException(self.invalidSourceError(parameters, self.PIPE)) + if span is None: + raise QgsProcessingException(self.invalidSourceError(parameters, self.SPAN)) + + # Compute the number of steps to display within the progress bar and + # get features from source + total = 100.0 / pipe.featureCount() if pipe.featureCount() else 0 + + # Dictionary from span feature ids to lengths + lengths = dict() + + # Dictionary from span feature ids to lists of lists of QgsPoint objects + strings = dict() + + # Dictionary for the PipeStruct field values + types = dict() + + spanFeatures = span.getFeatures() + pipeFeatures = pipe.getFeatures() + + for counter, feature in enumerate(pipeFeatures): + if feedback.isCanceled(): break + + geometry = feature.geometry() + if geometry == None: continue + + fatherID = feature[self.FATHER_ID] + + # Length + myLength = None + if feature.fields().lookupField('Length') != -1: + myLength = feature['Length'] + if myLength == None: + myLength = geometry.length() + + oldLength = lengths.get(fatherID, 0.0) + lengths[fatherID] = oldLength + myLength + + # Segment points + pointList = strings.get(fatherID, []) + # feedback.pushInfo('Point list: {}'.format(pointList)) + mylist = [] + + vertices = geometry.vertices() + while vertices.hasNext(): + mylist.append(vertices.next()) + + # feedback.pushInfo('Feature {}, Father {}, Points: {}'.format(feature['Id'], fatherID, ";".join(map(lambda x: '{} {}'.format(x.x(), x.y()), mylist)))) + + pointList.append(mylist) + strings[fatherID] = pointList + + # Store the value of PipeStruct + t = feature['PipeStruct'] + tt = types.get(fatherID, {}) + types[fatherID] = tt + c = tt.get(t, 0) + c += myLength + tt[t] = c + + # Update the progress bar + feedback.setProgress(int(counter * total)) + + if feedback.isCanceled(): + return + + feedback.pushInfo('Done') + + #span.startEditing() + + feedback.pushInfo('Started editing') + feedback.pushInfo(str(spanFeatures)) + + for feature in spanFeatures: + if feedback.isCanceled(): break + + f = feature.fields() + + #feedback.pushInfo(str(feature)) + id = feature['Id'] + #feedback.pushInfo(str(id)) + + # Length + myLength = None + if f.lookupField('Length') != -1: + myLength = feature['Length'] + if myLength == None: + myLength = feature.geometry().length() + + # Ignore short stumps + if myLength <= minLength: + continue + + # Vertices + mypoints = list(feature.geometry().vertices()) + mylist = strings.get(id, None) + if mylist == None: + feedback.pushInfo('No points for feature {}'.format(id)) + mylist = [mypoints] + + #feedback.pushInfo('Points: {}'.format("|".join(map(lambda x: ";".join(('{} {}'.format(p.x(), p.y()) for p in x)), mylist)))) + + head = feature.geometry().vertices().next() + resultList = [head] + + #feedback.pushInfo(str(resultList)) + + i = next((i for i, x in enumerate(mylist) if head.distance(x[0]) <= eps), None) + if i == None: + mylist = list(map(lambda x: list(reversed(x)), mylist)) + i = next((i for i, x in enumerate(mylist) if head.distance(x[0]) <= eps), None) + if i == None: + feedback.pushInfo('Warning: No matching start vertex for feature {}'.format(id)) + mylist = [mypoints] + i = 0 + + vertices = mylist.pop(i) + + while i != None: + tail = vertices[-1] + resultList.extend(vertices[1:]) + if tail.distance(mypoints[-1]) <= eps: + break + + i = next((i for i, x in enumerate(mylist) if tail.distance(x[0]) <= eps), None) + if i != None: + vertices = mylist.pop(i) + else: + i = next((i for i, x in enumerate(mylist) if tail.distance(x[-1]) <= eps), None) + if i != None: + vertices = list(reversed(mylist.pop(i))) + + # feedback.pushInfo(str(resultList)) + + # Convert to string + result = ";".join(('{} {}'.format(p.x(), p.y()) for p in resultList)) + + # feedback.pushInfo('Feature {}: {}'.format(id, result)) + + outputFeature = QgsFeature() + outputFeature.setFields(outputFields) + for i, x in enumerate(feature.attributes()): + fieldName = sourceFields[i].name() + outputFeature[fieldName] = x + + vts = list(feature.geometry().vertices()) + p1 = vts[0] + p2 = vts[-1] + + outputFeature['x1'] = feature['x1'] if f.lookupField('x1') != -1 else p1.x() + outputFeature['y1'] = feature['y1'] if f.lookupField('y1') != -1 else p1.y() + outputFeature['z1'] = feature['z1'] if f.lookupField('z1') != -1 else p1.z() + outputFeature['x2'] = feature['x2'] if f.lookupField('x2') != -1 else p2.x() + outputFeature['y2'] = feature['y2'] if f.lookupField('y2') != -1 else p2.y() + outputFeature['z2'] = feature['z2'] if f.lookupField('z2') != -1 else p2.z() + outputFeature['Length'] = feature['Length'] if f.lookupField('Length') != -1 else feature.geometry().length() + outputFeature['LineString'] = result + + # Handle pipe type codes + mytypes = list(types.get(id, {}).items()) + if len(mytypes) == 0: + feedback.pushInfo('No type codes for feature {}'.format(id)) + else: + if len(mytypes) > 1: + mytypes.sort(key = itemgetter(1)) + feedback.pushInfo('No unique type code for feature {}: {}'.format(id, mytypes)) + outputFeature['PipeStruct'] = mytypes[-1][0] + + if f.lookupField('TechTypeId') != -1: + outputFeature['TechTypeId'] = feature['TechTypeId'] + + output.addFeature(outputFeature) + + feedback.pushInfo('Loop done') + + #if feedback.isCanceled(): + # span.rollBack() + #else: + # span.commitChanges() + + feedback.pushInfo('Changes committed') + + # Return the results of the algorithm. In this case our only result is + # the feature sink which contains the processed features, but some + # algorithms may return multiple feature sinks, calculated numeric + # statistics, etc. These should all be included in the returned + # dictionary, with keys matching the feature corresponding parameter + # or output names. + return {self.OUTPUT: outputId} diff --git a/org.simantics.district.geotools/.classpath b/org.simantics.district.geotools/.classpath index b7283c77..29b4b7aa 100644 --- a/org.simantics.district.geotools/.classpath +++ b/org.simantics.district.geotools/.classpath @@ -1,5 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.simantics.district.geotools/META-INF/MANIFEST.MF b/org.simantics.district.geotools/META-INF/MANIFEST.MF index f8eee2dd..1cdbfcaa 100644 --- a/org.simantics.district.geotools/META-INF/MANIFEST.MF +++ b/org.simantics.district.geotools/META-INF/MANIFEST.MF @@ -19,8 +19,69 @@ Bundle-ClassPath: lib/commons-pool-1.5.4.jar, lib/jts-1.13.jar, ., lib/gt-epsg-hsql-16.0.jar, - lib/hsqldb-2.3.4.jar -Export-Package: org.geotools.geometry, + lib/hsqldb-2.3.4.jar, + lib/commons-io-2.1.jar, + lib/gt-api-16.0.jar, + lib/gt-coverage-16.0.jar, + lib/gt-geotiff-16.0.jar, + lib/gt-main-16.0.jar, + lib/guava-17.0.jar, + lib/imageio-ext-geocore-1.1.16.jar, + lib/imageio-ext-streams-1.1.16.jar, + lib/imageio-ext-tiff-1.1.16.jar, + lib/imageio-ext-utilities-1.1.16.jar, + lib/jai_codec-1.1.3.jar, + lib/jai_imageio-1.1.jar, + lib/jdom-1.1.3.jar, + lib/jt-affine-1.0.11.jar, + lib/jt-algebra-1.0.11.jar, + lib/jt-bandcombine-1.0.11.jar, + lib/jt-bandmerge-1.0.11.jar, + lib/jt-bandselect-1.0.11.jar, + lib/jt-binarize-1.0.11.jar, + lib/jt-border-1.0.11.jar, + lib/jt-buffer-1.0.11.jar, + lib/jt-classifier-1.0.11.jar, + lib/jt-colorconvert-1.0.11.jar, + lib/jt-colorindexer-1.0.11.jar, + lib/jt-crop-1.0.11.jar, + lib/jt-errordiffusion-1.0.11.jar, + lib/jt-format-1.0.11.jar, + lib/jt-imagefunction-1.0.11.jar, + lib/jt-iterators-1.0.11.jar, + lib/jt-lookup-1.0.11.jar, + lib/jt-mosaic-1.0.11.jar, + lib/jt-nullop-1.0.11.jar, + lib/jt-orderdither-1.0.11.jar, + lib/jt-piecewise-1.0.11.jar, + lib/jt-rescale-1.0.11.jar, + lib/jt-rlookup-1.0.11.jar, + lib/jt-scale-1.0.11.jar, + lib/jt-stats-1.0.11.jar, + lib/jt-translate-1.0.11.jar, + lib/jt-utilities-1.0.11.jar, + lib/jt-utils-1.4.0.jar, + lib/jt-vectorbin-1.0.11.jar, + lib/jt-warp-1.0.11.jar, + lib/jt-zonal-1.0.11.jar, + lib/jt-zonalstats-1.4.0.jar +Export-Package: com.vividsolutions.jts, + com.vividsolutions.jts.geom, + com.vividsolutions.jts.geom.impl, + com.vividsolutions.jts.geom.prep, + com.vividsolutions.jts.geom.util, + com.vividsolutions.jts.geomgraph, + com.vividsolutions.jts.geomgraph.index, + com.vividsolutions.jts.index.quadtree, + com.vividsolutions.jts.index.strtree, + it.geosolutions.imageio.stream.input.spi, + it.geosolutions.imageio.stream.output.spi, + org.geotools.coverage, + org.geotools.coverage.grid, + org.geotools.coverage.grid.io, + org.geotools.gce.geotiff, + org.geotools.geometry, + org.geotools.geometry.jts, org.geotools.referencing, org.opengis.geometry, org.opengis.referencing, diff --git a/org.simantics.district.geotools/build.properties b/org.simantics.district.geotools/build.properties index efaa0a7f..8741e47a 100644 --- a/org.simantics.district.geotools/build.properties +++ b/org.simantics.district.geotools/build.properties @@ -2,4 +2,49 @@ source.. = src/ output.. = bin/ bin.includes = META-INF/,\ .,\ - lib/ + lib/,\ + lib/commons-io-2.1.jar,\ + lib/gt-api-16.0.jar,\ + lib/gt-coverage-16.0.jar,\ + lib/gt-geotiff-16.0.jar,\ + lib/gt-main-16.0.jar,\ + lib/guava-17.0.jar,\ + lib/imageio-ext-geocore-1.1.16.jar,\ + lib/imageio-ext-streams-1.1.16.jar,\ + lib/imageio-ext-tiff-1.1.16.jar,\ + lib/imageio-ext-utilities-1.1.16.jar,\ + lib/jai_codec-1.1.3.jar,\ + lib/jai_imageio-1.1.jar,\ + lib/jdom-1.1.3.jar,\ + lib/jt-affine-1.0.11.jar,\ + lib/jt-algebra-1.0.11.jar,\ + lib/jt-bandcombine-1.0.11.jar,\ + lib/jt-bandmerge-1.0.11.jar,\ + lib/jt-bandselect-1.0.11.jar,\ + lib/jt-binarize-1.0.11.jar,\ + lib/jt-border-1.0.11.jar,\ + lib/jt-buffer-1.0.11.jar,\ + lib/jt-classifier-1.0.11.jar,\ + lib/jt-colorconvert-1.0.11.jar,\ + lib/jt-colorindexer-1.0.11.jar,\ + lib/jt-crop-1.0.11.jar,\ + lib/jt-errordiffusion-1.0.11.jar,\ + lib/jt-format-1.0.11.jar,\ + lib/jt-imagefunction-1.0.11.jar,\ + lib/jt-iterators-1.0.11.jar,\ + lib/jt-lookup-1.0.11.jar,\ + lib/jt-mosaic-1.0.11.jar,\ + lib/jt-nullop-1.0.11.jar,\ + lib/jt-orderdither-1.0.11.jar,\ + lib/jt-piecewise-1.0.11.jar,\ + lib/jt-rescale-1.0.11.jar,\ + lib/jt-rlookup-1.0.11.jar,\ + lib/jt-scale-1.0.11.jar,\ + lib/jt-stats-1.0.11.jar,\ + lib/jt-translate-1.0.11.jar,\ + lib/jt-utilities-1.0.11.jar,\ + lib/jt-utils-1.4.0.jar,\ + lib/jt-vectorbin-1.0.11.jar,\ + lib/jt-warp-1.0.11.jar,\ + lib/jt-zonal-1.0.11.jar,\ + lib/jt-zonalstats-1.4.0.jar diff --git a/org.simantics.district.geotools/lib/commons-io-2.1.jar b/org.simantics.district.geotools/lib/commons-io-2.1.jar new file mode 100644 index 00000000..b5c7d692 Binary files /dev/null and b/org.simantics.district.geotools/lib/commons-io-2.1.jar differ diff --git a/org.simantics.district.geotools/lib/gt-api-16.0.jar b/org.simantics.district.geotools/lib/gt-api-16.0.jar new file mode 100644 index 00000000..e38314e3 Binary files /dev/null and b/org.simantics.district.geotools/lib/gt-api-16.0.jar differ diff --git a/org.simantics.district.geotools/lib/gt-coverage-16.0.jar b/org.simantics.district.geotools/lib/gt-coverage-16.0.jar new file mode 100644 index 00000000..76e22407 Binary files /dev/null and b/org.simantics.district.geotools/lib/gt-coverage-16.0.jar differ diff --git a/org.simantics.district.geotools/lib/gt-geotiff-16.0.jar b/org.simantics.district.geotools/lib/gt-geotiff-16.0.jar new file mode 100644 index 00000000..5979a2f8 Binary files /dev/null and b/org.simantics.district.geotools/lib/gt-geotiff-16.0.jar differ diff --git a/org.simantics.district.geotools/lib/gt-main-16.0.jar b/org.simantics.district.geotools/lib/gt-main-16.0.jar new file mode 100644 index 00000000..fbf20724 Binary files /dev/null and b/org.simantics.district.geotools/lib/gt-main-16.0.jar differ diff --git a/org.simantics.district.geotools/lib/guava-17.0.jar b/org.simantics.district.geotools/lib/guava-17.0.jar new file mode 100644 index 00000000..661fc747 Binary files /dev/null and b/org.simantics.district.geotools/lib/guava-17.0.jar differ diff --git a/org.simantics.district.geotools/lib/imageio-ext-geocore-1.1.16.jar b/org.simantics.district.geotools/lib/imageio-ext-geocore-1.1.16.jar new file mode 100644 index 00000000..394e94a3 Binary files /dev/null and b/org.simantics.district.geotools/lib/imageio-ext-geocore-1.1.16.jar differ diff --git a/org.simantics.district.geotools/lib/imageio-ext-streams-1.1.16.jar b/org.simantics.district.geotools/lib/imageio-ext-streams-1.1.16.jar new file mode 100644 index 00000000..72b8dff4 Binary files /dev/null and b/org.simantics.district.geotools/lib/imageio-ext-streams-1.1.16.jar differ diff --git a/org.simantics.district.geotools/lib/imageio-ext-tiff-1.1.16.jar b/org.simantics.district.geotools/lib/imageio-ext-tiff-1.1.16.jar new file mode 100644 index 00000000..a629affc Binary files /dev/null and b/org.simantics.district.geotools/lib/imageio-ext-tiff-1.1.16.jar differ diff --git a/org.simantics.district.geotools/lib/imageio-ext-utilities-1.1.16.jar b/org.simantics.district.geotools/lib/imageio-ext-utilities-1.1.16.jar new file mode 100644 index 00000000..dd60adbf Binary files /dev/null and b/org.simantics.district.geotools/lib/imageio-ext-utilities-1.1.16.jar differ diff --git a/org.simantics.district.geotools/lib/jai_codec-1.1.3.jar b/org.simantics.district.geotools/lib/jai_codec-1.1.3.jar new file mode 100644 index 00000000..056ecd3a Binary files /dev/null and b/org.simantics.district.geotools/lib/jai_codec-1.1.3.jar differ diff --git a/org.simantics.district.geotools/lib/jai_imageio-1.1.jar b/org.simantics.district.geotools/lib/jai_imageio-1.1.jar new file mode 100644 index 00000000..571aa199 Binary files /dev/null and b/org.simantics.district.geotools/lib/jai_imageio-1.1.jar differ diff --git a/org.simantics.district.geotools/lib/jdom-1.1.3.jar b/org.simantics.district.geotools/lib/jdom-1.1.3.jar new file mode 100644 index 00000000..a2877278 Binary files /dev/null and b/org.simantics.district.geotools/lib/jdom-1.1.3.jar differ diff --git a/org.simantics.district.geotools/lib/jt-affine-1.0.11.jar b/org.simantics.district.geotools/lib/jt-affine-1.0.11.jar new file mode 100644 index 00000000..6a450869 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-affine-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-algebra-1.0.11.jar b/org.simantics.district.geotools/lib/jt-algebra-1.0.11.jar new file mode 100644 index 00000000..7aef8401 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-algebra-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-bandcombine-1.0.11.jar b/org.simantics.district.geotools/lib/jt-bandcombine-1.0.11.jar new file mode 100644 index 00000000..1d38bb07 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-bandcombine-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-bandmerge-1.0.11.jar b/org.simantics.district.geotools/lib/jt-bandmerge-1.0.11.jar new file mode 100644 index 00000000..e88ce446 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-bandmerge-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-bandselect-1.0.11.jar b/org.simantics.district.geotools/lib/jt-bandselect-1.0.11.jar new file mode 100644 index 00000000..32b09037 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-bandselect-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-binarize-1.0.11.jar b/org.simantics.district.geotools/lib/jt-binarize-1.0.11.jar new file mode 100644 index 00000000..12d8be0b Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-binarize-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-border-1.0.11.jar b/org.simantics.district.geotools/lib/jt-border-1.0.11.jar new file mode 100644 index 00000000..c868ff2c Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-border-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-buffer-1.0.11.jar b/org.simantics.district.geotools/lib/jt-buffer-1.0.11.jar new file mode 100644 index 00000000..66d6febd Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-buffer-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-classifier-1.0.11.jar b/org.simantics.district.geotools/lib/jt-classifier-1.0.11.jar new file mode 100644 index 00000000..82f0ff85 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-classifier-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-colorconvert-1.0.11.jar b/org.simantics.district.geotools/lib/jt-colorconvert-1.0.11.jar new file mode 100644 index 00000000..7d0fb178 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-colorconvert-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-colorindexer-1.0.11.jar b/org.simantics.district.geotools/lib/jt-colorindexer-1.0.11.jar new file mode 100644 index 00000000..b4074b45 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-colorindexer-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-crop-1.0.11.jar b/org.simantics.district.geotools/lib/jt-crop-1.0.11.jar new file mode 100644 index 00000000..073151c0 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-crop-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-errordiffusion-1.0.11.jar b/org.simantics.district.geotools/lib/jt-errordiffusion-1.0.11.jar new file mode 100644 index 00000000..687f37e6 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-errordiffusion-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-format-1.0.11.jar b/org.simantics.district.geotools/lib/jt-format-1.0.11.jar new file mode 100644 index 00000000..a9c748a7 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-format-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-imagefunction-1.0.11.jar b/org.simantics.district.geotools/lib/jt-imagefunction-1.0.11.jar new file mode 100644 index 00000000..136c2d50 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-imagefunction-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-iterators-1.0.11.jar b/org.simantics.district.geotools/lib/jt-iterators-1.0.11.jar new file mode 100644 index 00000000..5ed3d28c Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-iterators-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-lookup-1.0.11.jar b/org.simantics.district.geotools/lib/jt-lookup-1.0.11.jar new file mode 100644 index 00000000..cb13ed39 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-lookup-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-mosaic-1.0.11.jar b/org.simantics.district.geotools/lib/jt-mosaic-1.0.11.jar new file mode 100644 index 00000000..fd93d10f Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-mosaic-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-nullop-1.0.11.jar b/org.simantics.district.geotools/lib/jt-nullop-1.0.11.jar new file mode 100644 index 00000000..10402a88 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-nullop-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-orderdither-1.0.11.jar b/org.simantics.district.geotools/lib/jt-orderdither-1.0.11.jar new file mode 100644 index 00000000..7781b21b Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-orderdither-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-piecewise-1.0.11.jar b/org.simantics.district.geotools/lib/jt-piecewise-1.0.11.jar new file mode 100644 index 00000000..ea16e9d9 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-piecewise-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-rescale-1.0.11.jar b/org.simantics.district.geotools/lib/jt-rescale-1.0.11.jar new file mode 100644 index 00000000..1dd0ecfc Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-rescale-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-rlookup-1.0.11.jar b/org.simantics.district.geotools/lib/jt-rlookup-1.0.11.jar new file mode 100644 index 00000000..4f1bfcb1 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-rlookup-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-scale-1.0.11.jar b/org.simantics.district.geotools/lib/jt-scale-1.0.11.jar new file mode 100644 index 00000000..6ff872af Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-scale-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-stats-1.0.11.jar b/org.simantics.district.geotools/lib/jt-stats-1.0.11.jar new file mode 100644 index 00000000..179021e5 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-stats-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-translate-1.0.11.jar b/org.simantics.district.geotools/lib/jt-translate-1.0.11.jar new file mode 100644 index 00000000..2d1b416e Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-translate-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-utilities-1.0.11.jar b/org.simantics.district.geotools/lib/jt-utilities-1.0.11.jar new file mode 100644 index 00000000..dca7dd3f Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-utilities-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-utils-1.4.0.jar b/org.simantics.district.geotools/lib/jt-utils-1.4.0.jar new file mode 100644 index 00000000..99afd47d Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-utils-1.4.0.jar differ diff --git a/org.simantics.district.geotools/lib/jt-vectorbin-1.0.11.jar b/org.simantics.district.geotools/lib/jt-vectorbin-1.0.11.jar new file mode 100644 index 00000000..afd31948 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-vectorbin-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-warp-1.0.11.jar b/org.simantics.district.geotools/lib/jt-warp-1.0.11.jar new file mode 100644 index 00000000..90ee42aa Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-warp-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-zonal-1.0.11.jar b/org.simantics.district.geotools/lib/jt-zonal-1.0.11.jar new file mode 100644 index 00000000..ba9540c9 Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-zonal-1.0.11.jar differ diff --git a/org.simantics.district.geotools/lib/jt-zonalstats-1.4.0.jar b/org.simantics.district.geotools/lib/jt-zonalstats-1.4.0.jar new file mode 100644 index 00000000..3c52f32e Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-zonalstats-1.4.0.jar differ diff --git a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizard.java b/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizard.java index 8c087210..a4d29fe1 100644 --- a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizard.java +++ b/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizard.java @@ -1,12 +1,7 @@ package org.simantics.district.imports.ui; import java.lang.reflect.InvocationTargetException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import org.apache.commons.csv.CSVRecord; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.IStructuredSelection; @@ -14,27 +9,8 @@ import org.eclipse.jface.wizard.Wizard; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.ui.IImportWizard; import org.eclipse.ui.IWorkbench; -import org.geotools.geometry.DirectPosition2D; -import org.geotools.referencing.CRS; -import org.opengis.geometry.DirectPosition; -import org.opengis.geometry.MismatchedDimensionException; -import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.opengis.referencing.operation.MathTransform; -import org.opengis.referencing.operation.TransformException; -import org.simantics.Simantics; -import org.simantics.databoard.Bindings; -import org.simantics.db.Resource; -import org.simantics.db.WriteGraph; -import org.simantics.db.common.request.ObjectsWithType; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.request.Write; -import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.district.imports.CSVImportModel; import org.simantics.district.imports.DistrictImportUtils; -import org.simantics.district.network.DistrictNetworkUtil; -import org.simantics.district.network.ontology.DistrictNetworkResource; -import org.simantics.district.network.ui.DNEdgeBuilder; -import org.simantics.district.network.ui.DNEdgeBuilder.ResourceVertex; -import org.simantics.layer0.Layer0; import org.simantics.utils.ui.ExceptionUtils; public class CSVImportWizard extends Wizard implements IImportWizard { @@ -63,203 +39,13 @@ public class CSVImportWizard extends Wizard implements IImportWizard { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { - Path csvFile = model.getSource(); - char delim = model.getDelimiter(); + monitor.beginTask("Importing CSV", 1); - List rows = DistrictImportUtils.readRows(csvFile, delim, -1); - monitor.beginTask("Importing CSV", rows.size()); - - // Path wktFile = model.getWKTFile(); - - int xCoordColumnIndex = model.getXCoordIndex(); - int yCoordColumnIndex = model.getYCoordIndex(); - int zCoordColumnIndex = model.getZCoordIndex(); - int supplyTempColumnIndex = model.getSupplyTempIndex(); - int returnTempColumnIndex = model.getReturnTempIndex(); - int supplyPressureColumnIndex = model.getSupplyPressureIndex(); - int returnPressureColumnIndex = model.getReturnPressureIndex(); - int dpIndex = model.getDeltaPressureIndex(); - int dtIndex = model.getDeltaTemperatureIndex(); - int heatPowerIndex = model.getHeatPowerIndex(); - int valvePositionIndex = model.getValvePositionIndx(); - int nominalHeadMIndex = model.getNominalHeadMIndex(); - int nominalHeadBIndex = model.getNominalHeadBIndex(); - int nominalFlowIndex = model.getNominalFlowIndex(); - int maximumHeadMIndex = model.getMaximumHeadMIndex(); - int heatLoadDsIndex = model.getHeatLoadDsIndex(); - int massFlowIndex = model.getMassFlowIndex(); - int volFlowIndex = model.getVolFlowIndex(); - int velocityIndex = model.getVelocityIndex(); - int flowAreaIndex = model.getFlowAreaIndex(); - int nominalPressureLossIndex = model.getNominalPressureLossIndex(); - int addressIndex = model.getAddressIndex(); - - int startXCoordColumnIndex = model.getStartXCoordIndex(); - int startYCoordColumnIndex = model.getStartYCoordIndex(); - int startZValueColumnIndex = model.getStartZCoordIndex(); - int endXCoordColumnIndex = model.getEndXCoordIndex(); - int endYCoordColumnIndex = model.getEndYCoordIndex(); - int endZValueColumnIndex = model.getEndZCoordIndex(); - int diameterColumnIndex= model.getDiameterIndex(); - int outerDiameterColumnIndex = model.getOuterDiamterIndex(); - int nominalMassFlowIndex = model.getNominalMassFlowIndex(); - int tGroundIndex = model.gettGroundIndex(); - int edgeFlowAreaIndex = model.getEdgeFlowAreaIndex(); - int kReturnIndex = model.getkReturnIndex(); - int kSupplyIndex = model.getkSupplyIndex(); - int lengthIndex = model.getLengthIndex(); - int detailedGeometryIndex = model.getDetailedGeometryIndex(); - - int mappingColumn = model.getComponentMappingIndex(); - int idColumn = model.getIdIndex(); - - double padding = model.getEdgePadding(); - - String sourceEPSGCRS = model.getSourceCRS(); - - MathTransform transform = null; - boolean doTransform = false; - // if sourceEPSGCRS is empty || null then ignore transformation - if (sourceEPSGCRS != null && !sourceEPSGCRS.isEmpty()) { - CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEPSGCRS); - CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326"); - transform = CRS.findMathTransform(sourceCRS, targetCRS, true); - doTransform = true; + if (model.isVertexImport()) { + DistrictImportUtils.importVertices(model); + } else { + DistrictImportUtils.importEdges(model); } - final boolean actualDoTransform = doTransform; - final MathTransform actualTransform = transform; - Simantics.getSession().syncRequest(new Write() { - - @Override - public void perform(WriteGraph graph) throws DatabaseException { - graph.markUndoPoint(); - - DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); - - Collection vertices = graph.syncRequest(new ObjectsWithType(model.getParentDiagram(), Layer0.getInstance(graph).ConsistsOf, DistrictNetworkResource.getInstance(graph).Vertex)); - List vv = new ArrayList<>(vertices.size()); - for (Resource vertex : vertices) { - double[] existingCoords = graph.getRelatedValue2(vertex, DiagramResource.getInstance(graph).HasLocation, Bindings.DOUBLE_ARRAY); - vv.add(new ResourceVertex(vertex, existingCoords)); - } - - for (int k = 1; k < rows.size(); k++) { - CSVRecord row = rows.get(k); - - String mappingValue = row.get(mappingColumn); - - try { - if (model.isVertexImport()) { - String xCoords = row.get(xCoordColumnIndex); - String yCoords = row.get(yCoordColumnIndex); - double xCoord = Double.parseDouble(xCoords); - double yCoord = Double.parseDouble(yCoords); - - double z = 0; - if (zCoordColumnIndex != -1) { - String zs = row.get(zCoordColumnIndex); - - if (!zs.isEmpty()) { - try { - z = Double.parseDouble(zs); - } catch (NumberFormatException e) { - throw new DatabaseException(e); - } - } - } - - double[] coords; - if (actualDoTransform) { - DirectPosition2D targetPos = new DirectPosition2D(); - DirectPosition2D sourcePos = new DirectPosition2D(xCoord, yCoord); - DirectPosition res = actualTransform.transform(sourcePos, targetPos); - coords = res.getCoordinate(); - } else { - coords = new double[] { xCoord, yCoord }; - } - - // Switch to (longitude, latitude) - flipAxes(coords); - - Resource vertex = DistrictNetworkUtil.createVertex(graph, model.getParentDiagram(), coords, model.getComponentMappings().get(mappingValue)); - - writeStringValue(graph, row, idColumn, vertex, DN.HasId); - - graph.claimLiteral(vertex, DN.Vertex_HasElevation, z, Bindings.DOUBLE); - - writeValue(graph, row, supplyTempColumnIndex, vertex, DN.Vertex_HasSupplyTemperature); - writeValue(graph, row, returnTempColumnIndex, vertex, DN.Vertex_HasReturnTemperature); - writeValue(graph, row, supplyPressureColumnIndex, vertex, DN.Vertex_HasSupplyPressure); - writeValue(graph, row, returnPressureColumnIndex, vertex, DN.Vertex_HasReturnPressure); - writeValue(graph, row, dpIndex, vertex, DN.Vertex_HasDeltaPressure); - writeValue(graph, row, dtIndex, vertex, DN.Vertex_HasDeltaTemperature); - writeValue(graph, row, heatPowerIndex, vertex, DN.Vertex_HasHeatPower); - writeValue(graph, row, valvePositionIndex, vertex, DN.Vertex_HasValvePosition); - writeValue(graph, row, nominalHeadMIndex, vertex, DN.Vertex_HasNominalHeadM); - writeValue(graph, row, nominalHeadBIndex, vertex, DN.Vertex_HasNominalHeadB); - writeValue(graph, row, nominalFlowIndex, vertex, DN.Vertex_HasNominalFlow); - writeValue(graph, row, maximumHeadMIndex, vertex, DN.Vertex_HasMaximumHeadM); - writeValue(graph, row, heatLoadDsIndex, vertex, DN.Vertex_HasHeatLoadDs); - writeValue(graph, row, massFlowIndex, vertex, DN.Vertex_HasMassFlow); - writeValue(graph, row, volFlowIndex, vertex, DN.Vertex_HasVolFlow); - writeValue(graph, row, velocityIndex, vertex, DN.Vertex_HasVelocity); - writeValue(graph, row, flowAreaIndex, vertex, DN.Vertex_HasFlowArea); - writeValue(graph, row, nominalPressureLossIndex, vertex, DN.Vertex_HasNominalPressureLoss); - writeStringValue(graph, row, addressIndex, vertex, DN.Vertex_HasAddress); - - } else { - String startXCoords = row.get(startXCoordColumnIndex); - String startYCoords = row.get(startYCoordColumnIndex); - String endXCoords = row.get(endXCoordColumnIndex); - String endYCoords = row.get(endYCoordColumnIndex); - - double startXCoord = Double.parseDouble(startXCoords); // make negative - double startYCoord = Double.parseDouble(startYCoords); - - double endXCoord = Double.parseDouble(endXCoords); // make negative - double endYCoord = Double.parseDouble(endYCoords); - - double[] startCoords; - double[] endCoords; - if (actualDoTransform) { - DirectPosition2D startTargetPos = new DirectPosition2D(); - DirectPosition2D startSourcePos = new DirectPosition2D(startXCoord, startYCoord); - DirectPosition startRes = actualTransform.transform(startSourcePos, startTargetPos); - startCoords = startRes.getCoordinate(); - - DirectPosition2D endTargetPos = new DirectPosition2D(); - DirectPosition2D endSourcePos = new DirectPosition2D(endXCoord, endYCoord); - DirectPosition endRes = actualTransform.transform(endSourcePos, endTargetPos); - endCoords = endRes.getCoordinate(); - } else { - startCoords = new double[] { startXCoord , startYCoord }; - endCoords = new double[] { endXCoord , endYCoord }; - } - - // Switch to (longitude, latitude) - flipAxes(startCoords); - flipAxes(endCoords); - - Resource edge = DNEdgeBuilder.create(graph, vv, model.getParentDiagram(), model.getComponentMappings().get(mappingValue), startCoords, endCoords, padding, true); - writeStringValue(graph, row, idColumn, edge, DN.HasId); - - writeValue(graph, row, diameterColumnIndex, edge, DN.Edge_HasDiameter); - writeValue(graph, row, outerDiameterColumnIndex, edge, DN.Edge_HasOuterDiameter); - writeValue(graph, row, nominalMassFlowIndex, edge, DN.Edge_HasNominalMassFlow); - writeValue(graph, row, tGroundIndex, edge, DN.Edge_HasTGround); - writeValue(graph, row, kReturnIndex, edge, DN.Edge_HasKReturn); - writeValue(graph, row, kSupplyIndex, edge, DN.Edge_HasKSupply); - writeValue(graph, row, edgeFlowAreaIndex, edge, DN.Edge_HasFlowArea); - writeValue(graph, row, lengthIndex, edge, DN.Edge_HasLength); - writeDoubleArrayFromString(graph, row, detailedGeometryIndex, edge, DN.Edge_HasGeometry, actualTransform); - } - } catch (MismatchedDimensionException | TransformException | DatabaseException e) { - throw new DatabaseException(e); - } - monitor.worked(1); - } - } - }); } catch (Exception e) { throw new InvocationTargetException(e); } @@ -278,72 +64,5 @@ public class CSVImportWizard extends Wizard implements IImportWizard { } } - private static void flipAxes(double[] coords) { - double tmp = coords[0]; - coords[0] = coords[1]; - coords[1] = tmp; - } - private static void writeValue(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation) throws DatabaseException { - if (index != -1) { - String stringValue = row.get(index); - if (!stringValue.isEmpty()) { - try { - graph.claimLiteral(subject, relation, Double.parseDouble(stringValue), Bindings.DOUBLE); - } catch (NumberFormatException e) { - throw new DatabaseException(e); - } - } - } - } - - private static void writeStringValue(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation) throws DatabaseException { - if (index != -1) { - String stringValue = row.get(index); - if (!stringValue.isEmpty()) { - try { - graph.claimLiteral(subject, relation, stringValue, Bindings.STRING); - } catch (NumberFormatException e) { - throw new DatabaseException(e); - } - } - } - } - - private static void writeDoubleArrayFromString(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation, MathTransform actualTransform) throws DatabaseException, MismatchedDimensionException, TransformException { - if (index != -1) { - String stringValue = row.get(index); - if (!stringValue.isEmpty()) { - stringValue = stringValue.substring(1, stringValue.length() - 1); - String[] coordPairs = stringValue.split(";"); - ArrayList dd = new ArrayList<>(coordPairs.length * 2); - for (int i = 0; i < coordPairs.length; i++) { - String coordPair = coordPairs[i]; - String[] p = coordPair.split(" "); - double x = Double.parseDouble(p[0]); - double y = Double.parseDouble(p[1]); - if (actualTransform != null) { - DirectPosition2D targetPos = new DirectPosition2D(); - DirectPosition2D sourcePos = new DirectPosition2D(y, x); - DirectPosition res = actualTransform.transform(sourcePos, targetPos); - double[] coords = res.getCoordinate(); - x = coords[1]; - y = coords[0]; - } - dd.add(x); - dd.add(y); - } - double[] detailedGeometryCoords = new double[dd.size()]; - for (int i = 0; i < dd.size(); i++) { - double d = dd.get(i); - detailedGeometryCoords[i] = d; - } - try { - graph.claimLiteral(subject, relation, detailedGeometryCoords, Bindings.DOUBLE_ARRAY); - } catch (NumberFormatException e) { - throw new DatabaseException(e); - } - } - } - } } diff --git a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizardFirstPage.java b/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizardFirstPage.java index 9ba7e2da..a0cc023c 100644 --- a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizardFirstPage.java +++ b/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizardFirstPage.java @@ -22,6 +22,7 @@ import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.exception.DatabaseException; +import org.simantics.district.imports.CSVImportModel; import org.simantics.district.network.ui.function.Functions; import org.simantics.modeling.ModelingResources; import org.simantics.utils.ui.widgets.FileOrDirectorySelectionWidget; diff --git a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizardPage.java b/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizardPage.java index 552f7e60..268ed310 100644 --- a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizardPage.java +++ b/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizardPage.java @@ -33,6 +33,7 @@ import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.geotools.referencing.CRS; +import org.simantics.district.imports.CSVImportModel; import org.simantics.district.imports.ui.controls.DynamicComboFieldEditor; public class CSVImportWizardPage extends WizardPage { @@ -52,12 +53,12 @@ public class CSVImportWizardPage extends WizardPage { // Common for vertex and edge private DynamicComboFieldEditor componentMappingSelector; private DynamicComboFieldEditor idSelector; - private DynamicComboFieldEditor labelSelector; // For vertex import private DynamicComboFieldEditor xCoordSelector; private DynamicComboFieldEditor yCoordSelector; private DynamicComboFieldEditor zValueSelector; + private DynamicComboFieldEditor altElevationValueSelector; private DynamicComboFieldEditor supplyTempValueSelector; private DynamicComboFieldEditor returnTempValueSelector; @@ -66,6 +67,7 @@ public class CSVImportWizardPage extends WizardPage { private DynamicComboFieldEditor dpSelector; private DynamicComboFieldEditor dtSelector; private DynamicComboFieldEditor heatPowerSelector; + private DynamicComboFieldEditor peakPowerSelector; private DynamicComboFieldEditor nominalHeadMSelector; private DynamicComboFieldEditor nominalHeadBSelector; private DynamicComboFieldEditor nominalFlowSelector; @@ -300,8 +302,8 @@ public class CSVImportWizardPage extends WizardPage { @Override public void modifyText(ModifyEvent e) { try { - double padding = Double.parseDouble(""); - model.setEdgePapping(padding); + double padding = Double.parseDouble(edgeConnectionPadding.getText()); + model.setEdgePadding(padding); } catch (NumberFormatException ee) { // ignore } @@ -388,6 +390,22 @@ public class CSVImportWizardPage extends WizardPage { validatePageComplete(); } }); + + altElevationValueSelector = new DynamicComboFieldEditor("altElevation", "Alternative Elevation", parent); + altElevationValueSelector.addComboListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + widgetDefaultSelected(e); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + model.setAltElevationIndex(Integer.parseInt(altElevationValueSelector.getValue())); + validatePageComplete(); + } + }); + supplyTempValueSelector = new DynamicComboFieldEditor("tempValue", "Supply Temperature value", parent); supplyTempValueSelector.addComboListener(new SelectionListener() { @@ -486,6 +504,20 @@ public class CSVImportWizardPage extends WizardPage { validatePageComplete(); } }); + peakPowerSelector = new DynamicComboFieldEditor("peakPowerValue", "Peak Power", parent); + peakPowerSelector.addComboListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + widgetDefaultSelected(e); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + model.setPeakPowerIndex(Integer.parseInt(peakPowerSelector.getValue())); + validatePageComplete(); + } + }); nominalHeadMSelector = new DynamicComboFieldEditor("nominalHeadMValue", "nominalHeadM", parent); nominalHeadMSelector.addComboListener(new SelectionListener() { @@ -925,6 +957,7 @@ public class CSVImportWizardPage extends WizardPage { xCoordSelector.updateCombo(namesAndValues); yCoordSelector.updateCombo(namesAndValues); zValueSelector.updateCombo(namesAndValues); + altElevationValueSelector.updateCombo(namesAndValues); supplyTempValueSelector.updateCombo(namesAndValues); returnTempValueSelector.updateCombo(namesAndValues); supplyPressureValueSelector.updateCombo(namesAndValues); @@ -967,7 +1000,7 @@ public class CSVImportWizardPage extends WizardPage { headerIndexAndValues.clear(); try { - List rows = model.getRows(5); + List rows = model.getRows(5, false); for (int k = 0; k < rows.size(); k++) { CSVRecord row = rows.get(k); @@ -1006,7 +1039,7 @@ public class CSVImportWizardPage extends WizardPage { if (model.isVertexImport()) setPageComplete(model.getXCoordIndex() != -1 && model.getYCoordIndex() != -1 && model.getComponentMappingIndex() != -1); else - setPageComplete(model.getStartXCoordIndex() != 1 && model.getStartYCoordIndex() != -1 && model.getEndXCoordIndex() != -1 && model.getEndYCoordIndex() != -1 && model.getComponentMappingIndex() != -1); + setPageComplete(model.getStartXCoordIndex() != -1 && model.getStartYCoordIndex() != -1 && model.getEndXCoordIndex() != -1 && model.getEndYCoordIndex() != -1 && model.getComponentMappingIndex() != -1); } } diff --git a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/ComponentMappingPage.java b/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/ComponentMappingPage.java index c74603be..7b87b29f 100644 --- a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/ComponentMappingPage.java +++ b/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/ComponentMappingPage.java @@ -23,6 +23,7 @@ import org.simantics.db.Resource; import org.simantics.db.common.request.IndexRoot; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.exception.DatabaseException; +import org.simantics.district.imports.CSVImportModel; import org.simantics.district.imports.DistrictImportUtils; import org.simantics.district.network.ui.function.Functions; @@ -32,7 +33,6 @@ public class ComponentMappingPage extends WizardPage { private Composite composite; private Composite childComposite; protected Map componentMappings; - private Collection distinctMappingIvalues; private Collection distinctMappingIndexColumnValues; public ComponentMappingPage(CSVImportModel model) { diff --git a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/controls/DynamicComboFieldEditor.java b/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/controls/DynamicComboFieldEditor.java index 5fc8cad3..f3bb8c72 100644 --- a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/controls/DynamicComboFieldEditor.java +++ b/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/controls/DynamicComboFieldEditor.java @@ -66,6 +66,7 @@ public class DynamicComboFieldEditor extends FieldEditor { * * @return true if it is ok, and false otherwise */ + @SuppressWarnings("unused") private boolean checkArray(String[][] table) { if (table == null) { return false; diff --git a/org.simantics.district.imports/META-INF/MANIFEST.MF b/org.simantics.district.imports/META-INF/MANIFEST.MF index b04e9223..747c33cb 100644 --- a/org.simantics.district.imports/META-INF/MANIFEST.MF +++ b/org.simantics.district.imports/META-INF/MANIFEST.MF @@ -4,7 +4,13 @@ Bundle-Name: Simantics District Imports Bundle-SymbolicName: org.simantics.district.imports Bundle-Version: 1.0.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Require-Bundle: org.simantics.db +Require-Bundle: org.simantics.db, + org.simantics, + org.simantics.district.geotools, + org.simantics.district.network.ontology, + org.simantics.district.network, + org.simantics.diagram.ontology, + org.slf4j.api Export-Package: org.apache.commons.csv, org.simantics.district.imports Bundle-ClassPath: lib/commons-csv-1.4.jar, diff --git a/org.simantics.district.imports/build.properties b/org.simantics.district.imports/build.properties index 78db4425..4e38199d 100644 --- a/org.simantics.district.imports/build.properties +++ b/org.simantics.district.imports/build.properties @@ -1,5 +1,6 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - lib/commons-csv-1.4.jar +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + lib/commons-csv-1.4.jar,\ + scl/ diff --git a/org.simantics.district.imports/scl/Simantics/District/Import.scl b/org.simantics.district.imports/scl/Simantics/District/Import.scl new file mode 100644 index 00000000..a3d36f4c --- /dev/null +++ b/org.simantics.district.imports/scl/Simantics/District/Import.scl @@ -0,0 +1,47 @@ +import "Files" +import "Map" as Map +import "Simantics/DB" + +importJava "org.simantics.district.imports.CSVImportModel" where + data CSVImportModel + + @JavaName "" + csvImportModel :: () -> CSVImportModel + + setParentDiagram :: CSVImportModel -> Resource -> () + setSource :: CSVImportModel -> Path -> () + setDelimiter :: CSVImportModel -> Character -> () + + // Vertex + setXCoordIndex :: CSVImportModel -> Integer -> () + setYCoordIndex :: CSVImportModel -> Integer -> () + setZCoordIndex :: CSVImportModel -> Integer -> () + setAltElevationIndex :: CSVImportModel -> Integer -> () + setHeatLoadDsIndex :: CSVImportModel -> Integer -> () + setHeatPowerIndex :: CSVImportModel -> Integer -> () + setPeakPowerIndex :: CSVImportModel -> Integer -> () + + // Edge + setStartXCoordIndex :: CSVImportModel -> Integer -> () + setStartYCoordIndex :: CSVImportModel -> Integer -> () + setStartZCoordIndex :: CSVImportModel -> Integer -> () + setEndXCoordIndex :: CSVImportModel -> Integer -> () + setEndYCoordIndex :: CSVImportModel -> Integer -> () + setEndZCoordIndex :: CSVImportModel -> Integer -> () + + setLengthIndex :: CSVImportModel -> Integer -> () + detailedGeometryIndex :: CSVImportModel -> Integer -> () + setDiameterIndex :: CSVImportModel -> Integer -> () + setOuterDiameterIndex :: CSVImportModel -> Integer -> () + setEdgePadding :: CSVImportModel -> Double -> () + + // Common + setSourceCRS :: CSVImportModel -> String -> () + setIdIndex :: CSVImportModel -> Integer -> () + setAddressIndex :: CSVImportModel -> Integer -> () + setComponentMappingIndex :: CSVImportModel -> Integer -> () + setComponentMappings :: CSVImportModel -> String -> Resource -> () + +importJava "org.simantics.district.imports.DistrictImportUtils" where + importVertices :: CSVImportModel -> () + importEdges :: CSVImportModel -> () diff --git a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportModel.java b/org.simantics.district.imports/src/org/simantics/district/imports/CSVImportModel.java similarity index 93% rename from org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportModel.java rename to org.simantics.district.imports/src/org/simantics/district/imports/CSVImportModel.java index 93646ded..9f85225f 100644 --- a/org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportModel.java +++ b/org.simantics.district.imports/src/org/simantics/district/imports/CSVImportModel.java @@ -1,4 +1,4 @@ -package org.simantics.district.imports.ui; +package org.simantics.district.imports; import java.io.IOException; import java.nio.file.Path; @@ -7,9 +7,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVRecord; import org.simantics.db.Resource; -import org.simantics.district.imports.DistrictImportUtils; import org.simantics.district.imports.DistrictImportUtils.CSVHeader; public class CSVImportModel { @@ -28,6 +28,7 @@ public class CSVImportModel { private int xCoordIndex = -1; private int yCoordIndex = -1; private int zCoordIndex = -1; + private int altElevationIndex = -1; // Edge import private int startXCoordIndex = -1; @@ -69,9 +70,10 @@ public class CSVImportModel { private int idIndex = -1; private double edgePadding = 0.0001; // default private int valvePositionIndx = -1; - private int addressIndex; - private int lengthIndex; - private int detailedGeometryIndex; + private int addressIndex = -1; + private int lengthIndex = -1; + private int detailedGeometryIndex = -1; + private int peakPowerIndex = -1; // Third page @@ -95,9 +97,9 @@ public class CSVImportModel { return source; } - public List getRows(int amount) throws IOException { + public List getRows(int amount, boolean readFirstAsHeader) throws IOException { if (source != null) - return DistrictImportUtils.readRows(source, delimiter, amount); + return DistrictImportUtils.readRows(source, delimiter, readFirstAsHeader, amount); else return Collections.emptyList(); } @@ -132,7 +134,7 @@ public class CSVImportModel { public List> readRows(int amount) throws IOException { if (source != null) - return DistrictImportUtils.readRows(source, delimiter, readFirstAsHeader, amount); + return DistrictImportUtils.readRows(source, CSVFormat.newFormat(delimiter), readFirstAsHeader, amount); else return Collections.emptyList(); } @@ -461,7 +463,7 @@ public class CSVImportModel { this.idIndex = idIndex; } - public void setEdgePapping(double edgePadding) { + public void setEdgePadding(double edgePadding) { this.edgePadding = edgePadding; } @@ -500,4 +502,20 @@ public class CSVImportModel { public int getDetailedGeometryIndex() { return detailedGeometryIndex; } + + public int getAlternativeElevationIndex() { + return altElevationIndex; + } + + public void setAltElevationIndex(int parseInt) { + this.altElevationIndex = parseInt; + } + + public void setPeakPowerIndex(int parseInt) { + this.peakPowerIndex = parseInt; + } + + public int getPeakPowerIndex() { + return peakPowerIndex; + } } diff --git a/org.simantics.district.imports/src/org/simantics/district/imports/DistrictImportUtils.java b/org.simantics.district.imports/src/org/simantics/district/imports/DistrictImportUtils.java index 6b22746c..7e2a0e1e 100644 --- a/org.simantics.district.imports/src/org/simantics/district/imports/DistrictImportUtils.java +++ b/org.simantics.district.imports/src/org/simantics/district/imports/DistrictImportUtils.java @@ -10,17 +10,51 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; +import org.geotools.geometry.DirectPosition2D; +import org.geotools.referencing.CRS; +import org.opengis.geometry.DirectPosition; +import org.opengis.geometry.MismatchedDimensionException; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.NoSuchAuthorityCodeException; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.operation.MathTransform; +import org.opengis.referencing.operation.TransformException; +import org.simantics.Simantics; +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ObjectsWithType; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.util.Layer0Utils; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.district.network.DNEdgeBuilder; +import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.layer0.Layer0; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.simantics.district.network.DistrictNetworkUtil.ResourceVertex; + +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.index.quadtree.Quadtree; public class DistrictImportUtils { private DistrictImportUtils() { } + private static final Logger LOGGER = LoggerFactory.getLogger(DistrictImportUtils.class); + public static Resource importCSVAsLayer(Path csvFile) throws IOException { try (CSVParser parser = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(Files.newBufferedReader(csvFile))) { @@ -50,10 +84,6 @@ public class DistrictImportUtils { return delimiters; } - public static List> readRows(Path source, char delimiter, boolean firstAsHeader, int amount) throws IOException { - return readRows(source, CSVFormat.newFormat(delimiter), firstAsHeader, amount); - } - public static List> readRows(Path source, CSVFormat format, boolean firstAsHeader, int amount) throws IOException { if (firstAsHeader) format = format.withFirstRecordAsHeader(); @@ -70,25 +100,31 @@ public class DistrictImportUtils { } } - public static List readRows(Path source, char delim, int rowAmount) throws IOException { + public static List readRows(Path source, char delim, boolean firstAsHeader, int rowAmount) throws IOException { List results = new ArrayList<>(); + AtomicInteger count = new AtomicInteger(0); + consumeCSV(source, delim, firstAsHeader, t -> { + results.add(t); + int current = count.incrementAndGet(); + return current < rowAmount; + }); + return results; + } + + public static void consumeCSV(Path source, char delim, boolean firstAsHeader, Function consumer) throws IOException { CSVFormat format = CSVFormat.newFormat(delim); + if (firstAsHeader) { + format = format.withFirstRecordAsHeader(); + } try (CSVParser parser = format.parse(Files.newBufferedReader(source))) { Iterator records = parser.iterator(); - int rows = 0; - if (rowAmount == -1) { - while (records.hasNext()) { - results.add(records.next()); - rows++; - } - } else { - while (rows < rowAmount && records.hasNext()) { - results.add(records.next()); - rows++; + while (records.hasNext()) { + Boolean cont = consumer.apply(records.next()); + if (!cont) { + break; } } } - return results; } @@ -191,4 +227,354 @@ public class DistrictImportUtils { return results; } + public static void importVertices(CSVImportModel model) throws NoSuchAuthorityCodeException, FactoryException, DatabaseException { + + Path csvFile = model.getSource(); + char delim = model.getDelimiter(); + + int xCoordColumnIndex = model.getXCoordIndex(); + int yCoordColumnIndex = model.getYCoordIndex(); + int zCoordColumnIndex = model.getZCoordIndex(); + int altElevationIndex = model.getAlternativeElevationIndex(); + int supplyTempColumnIndex = model.getSupplyTempIndex(); + int returnTempColumnIndex = model.getReturnTempIndex(); + int supplyPressureColumnIndex = model.getSupplyPressureIndex(); + int returnPressureColumnIndex = model.getReturnPressureIndex(); + int dpIndex = model.getDeltaPressureIndex(); + int dtIndex = model.getDeltaTemperatureIndex(); + int heatPowerIndex = model.getHeatPowerIndex(); + int peakPowerIndex = model.getPeakPowerIndex(); + int valvePositionIndex = model.getValvePositionIndx(); + int nominalHeadMIndex = model.getNominalHeadMIndex(); + int nominalHeadBIndex = model.getNominalHeadBIndex(); + int nominalFlowIndex = model.getNominalFlowIndex(); + int maximumHeadMIndex = model.getMaximumHeadMIndex(); + int heatLoadDsIndex = model.getHeatLoadDsIndex(); + int massFlowIndex = model.getMassFlowIndex(); + int volFlowIndex = model.getVolFlowIndex(); + int velocityIndex = model.getVelocityIndex(); + int flowAreaIndex = model.getFlowAreaIndex(); + int nominalPressureLossIndex = model.getNominalPressureLossIndex(); + int addressIndex = model.getAddressIndex(); + + int mappingColumn = model.getComponentMappingIndex(); + int idColumn = model.getIdIndex(); + + String sourceEPSGCRS = model.getSourceCRS(); + + MathTransform transform = null; + boolean doTransform = false; + // if sourceEPSGCRS is empty || null then ignore transformation + if (sourceEPSGCRS != null && !sourceEPSGCRS.isEmpty()) { + CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEPSGCRS); + CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326"); + transform = CRS.findMathTransform(sourceCRS, targetCRS, true); + doTransform = true; + } + final boolean actualDoTransform = doTransform; + final MathTransform actualTransform = transform; + + Simantics.getSession().syncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + try { + Layer0Utils.setDependenciesIndexingDisabled(graph, true); + graph.markUndoPoint(); + + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + + DistrictImportUtils.consumeCSV(csvFile, delim, true, (Function) row -> { + try { + String mappingValue = row.get(mappingColumn); + + String xCoords = row.get(xCoordColumnIndex); + String yCoords = row.get(yCoordColumnIndex); + double xCoord = Double.parseDouble(xCoords); + double yCoord = Double.parseDouble(yCoords); + + double z = 0; + if (zCoordColumnIndex != -1) { + String zs = row.get(zCoordColumnIndex); + + if (!zs.isEmpty()) { + try { + z = Double.parseDouble(zs); + } catch (NumberFormatException e1) { + throw new DatabaseException(e1); + } + } + } + + double[] coords; + if (actualDoTransform) { + DirectPosition2D targetPos = new DirectPosition2D(); + DirectPosition2D sourcePos = new DirectPosition2D(xCoord, yCoord); + DirectPosition res = actualTransform.transform(sourcePos, targetPos); + coords = res.getCoordinate(); + } else { + coords = new double[] { xCoord, yCoord }; + } + + // Switch to (longitude, latitude) + flipAxes(coords); + + Resource vertex = DistrictNetworkUtil.createVertex(graph, model.getParentDiagram(), coords, z, model.getComponentMappings().get(mappingValue)); + + writeStringValue(graph, row, idColumn, vertex, DN.HasId); + + writeValue(graph, row, altElevationIndex, vertex, DN.Vertex_HasAltElevation); + + writeValue(graph, row, supplyTempColumnIndex, vertex, DN.Vertex_HasSupplyTemperature); + writeValue(graph, row, returnTempColumnIndex, vertex, DN.Vertex_HasReturnTemperature); + writeValue(graph, row, supplyPressureColumnIndex, vertex, DN.Vertex_HasSupplyPressure); + writeValue(graph, row, returnPressureColumnIndex, vertex, DN.Vertex_HasReturnPressure); + writeValue(graph, row, dpIndex, vertex, DN.Vertex_HasDeltaPressure); + writeValue(graph, row, dtIndex, vertex, DN.Vertex_HasDeltaTemperature); + writeValue(graph, row, heatPowerIndex, vertex, DN.Vertex_HasHeatPower); + writeValue(graph, row, peakPowerIndex, vertex, DN.Vertex_HasPeakPower); + writeValue(graph, row, valvePositionIndex, vertex, DN.Vertex_HasValvePosition); + writeValue(graph, row, nominalHeadMIndex, vertex, DN.Vertex_HasNominalHeadM); + writeValue(graph, row, nominalHeadBIndex, vertex, DN.Vertex_HasNominalHeadB); + writeValue(graph, row, nominalFlowIndex, vertex, DN.Vertex_HasNominalFlow); + writeValue(graph, row, maximumHeadMIndex, vertex, DN.Vertex_HasMaximumHeadM); + writeValue(graph, row, heatLoadDsIndex, vertex, DN.Vertex_HasHeatLoadDs); + writeValue(graph, row, massFlowIndex, vertex, DN.Vertex_HasMassFlow); + writeValue(graph, row, volFlowIndex, vertex, DN.Vertex_HasVolFlow); + writeValue(graph, row, velocityIndex, vertex, DN.Vertex_HasVelocity); + writeValue(graph, row, flowAreaIndex, vertex, DN.Vertex_HasFlowArea); + writeValue(graph, row, nominalPressureLossIndex, vertex, DN.Vertex_HasNominalPressureLoss); + writeStringValue(graph, row, addressIndex, vertex, DN.Vertex_HasAddress); + } catch (DatabaseException | MismatchedDimensionException | TransformException e) { + throw new RuntimeException(e); + } + return true; + }); + + } catch (IOException e) { + LOGGER.error("Could not import", e); + throw new DatabaseException(e); + } finally { + Layer0Utils.setDependenciesIndexingDisabled(graph, false); + } + } + }); + } + + public static void importEdges(CSVImportModel model) throws NoSuchAuthorityCodeException, FactoryException, DatabaseException { + + Path csvFile = model.getSource(); + char delim = model.getDelimiter(); + + + int startXCoordColumnIndex = model.getStartXCoordIndex(); + int startYCoordColumnIndex = model.getStartYCoordIndex(); + int startZValueColumnIndex = model.getStartZCoordIndex(); + int endXCoordColumnIndex = model.getEndXCoordIndex(); + int endYCoordColumnIndex = model.getEndYCoordIndex(); + int endZValueColumnIndex = model.getEndZCoordIndex(); + int diameterColumnIndex= model.getDiameterIndex(); + int outerDiameterColumnIndex = model.getOuterDiamterIndex(); + int nominalMassFlowIndex = model.getNominalMassFlowIndex(); + int tGroundIndex = model.gettGroundIndex(); + int edgeFlowAreaIndex = model.getEdgeFlowAreaIndex(); + int kReturnIndex = model.getkReturnIndex(); + int kSupplyIndex = model.getkSupplyIndex(); + int lengthIndex = model.getLengthIndex(); + int detailedGeometryIndex = model.getDetailedGeometryIndex(); + + int mappingColumn = model.getComponentMappingIndex(); + int idColumn = model.getIdIndex(); + + double padding = model.getEdgePadding(); + + String sourceEPSGCRS = model.getSourceCRS(); + + MathTransform transform = null; + boolean doTransform = false; + // if sourceEPSGCRS is empty || null then ignore transformation + if (sourceEPSGCRS != null && !sourceEPSGCRS.isEmpty()) { + CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEPSGCRS); + CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326"); + transform = CRS.findMathTransform(sourceCRS, targetCRS, true); + doTransform = true; + } + final boolean actualDoTransform = doTransform; + final MathTransform actualTransform = transform; + + double halfPadding = padding / 2; + + Quadtree vv = Simantics.getSession().syncRequest(new UniqueRead() { + + @Override + public Quadtree perform(ReadGraph graph) throws DatabaseException { + Collection vertices = graph.syncRequest(new ObjectsWithType(model.getParentDiagram(), Layer0.getInstance(graph).ConsistsOf, DistrictNetworkResource.getInstance(graph).Vertex)); + Quadtree vv = new Quadtree(); + for (Resource vertex : vertices) { + double[] coords = graph.getRelatedValue2(vertex, DiagramResource.getInstance(graph).HasLocation, Bindings.DOUBLE_ARRAY); + double x1 = coords[0] - halfPadding; + double y1= coords[1] - halfPadding; + double x2 = coords[0] + halfPadding; + double y2= coords[1] + halfPadding; + Envelope e = new Envelope(x1, x2, y1, y2); + vv.insert(e, new ResourceVertex(vertex, coords, true)); + } + return vv; + } + }); + + Simantics.getSession().syncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + try { + Layer0Utils.setDependenciesIndexingDisabled(graph, true); + graph.markUndoPoint(); + + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + + DistrictImportUtils.consumeCSV(csvFile, delim, true, row -> { + try { + String mappingValue = row.get(mappingColumn); + + String startXCoords = row.get(startXCoordColumnIndex); + String startYCoords = row.get(startYCoordColumnIndex); + String startZCoords = row.get(startZValueColumnIndex); + String endXCoords = row.get(endXCoordColumnIndex); + String endYCoords = row.get(endYCoordColumnIndex); + String endZCoords = row.get(endZValueColumnIndex); + + double startXCoord = Double.parseDouble(startXCoords); // make negative + double startYCoord = Double.parseDouble(startYCoords); + double startZCoord = Double.parseDouble(startZCoords); + + double endXCoord = Double.parseDouble(endXCoords); // make negative + double endYCoord = Double.parseDouble(endYCoords); + double endZCoord = Double.parseDouble(endZCoords); + + double[] startCoords; + double[] endCoords; + if (actualDoTransform) { + DirectPosition2D startTargetPos = new DirectPosition2D(); + DirectPosition2D startSourcePos = new DirectPosition2D(startXCoord, startYCoord); + DirectPosition startRes = actualTransform.transform(startSourcePos, startTargetPos); + startCoords = startRes.getCoordinate(); + + DirectPosition2D endTargetPos = new DirectPosition2D(); + DirectPosition2D endSourcePos = new DirectPosition2D(endXCoord, endYCoord); + DirectPosition endRes = actualTransform.transform(endSourcePos, endTargetPos); + endCoords = endRes.getCoordinate(); + } else { + startCoords = new double[] { startXCoord , startYCoord }; + endCoords = new double[] { endXCoord , endYCoord }; + } + + // Switch to (longitude, latitude) + flipAxes(startCoords); + flipAxes(endCoords); + + Optional oedge = DNEdgeBuilder.create(graph, vv, model.getParentDiagram(), model.getComponentMappings().get(mappingValue), startCoords, startZCoord, endCoords, endZCoord, new double[0], padding, true); + if (oedge.isPresent()) { + Resource edge = oedge.get(); + writeStringValue(graph, row, idColumn, edge, DN.HasId); + + writeValue(graph, row, diameterColumnIndex, edge, DN.Edge_HasDiameter); + writeValue(graph, row, outerDiameterColumnIndex, edge, DN.Edge_HasOuterDiameter); + writeValue(graph, row, nominalMassFlowIndex, edge, DN.Edge_HasNominalMassFlow); + writeValue(graph, row, tGroundIndex, edge, DN.Edge_HasTGround); + writeValue(graph, row, kReturnIndex, edge, DN.Edge_HasKReturn); + writeValue(graph, row, kSupplyIndex, edge, DN.Edge_HasKSupply); + writeValue(graph, row, edgeFlowAreaIndex, edge, DN.Edge_HasFlowArea); + writeValue(graph, row, lengthIndex, edge, DN.Edge_HasLength); + writeDoubleArrayFromString(graph, row, detailedGeometryIndex, edge, DN.Edge_HasGeometry, actualTransform); + } + + return true; + } catch (DatabaseException | MismatchedDimensionException | TransformException e) { + throw new RuntimeException(e); + } + }); + } catch (IOException e) { + LOGGER.error("Could not import edges {}", model.getSource(), e); + } finally { + Layer0Utils.setDependenciesIndexingDisabled(graph, false); + } + } + }); + } + + private static void flipAxes(double[] coords) { + double tmp = coords[0]; + coords[0] = coords[1]; + coords[1] = tmp; + } + + private static void writeValue(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation) throws DatabaseException { + if (index != -1) { + String stringValue = row.get(index); + if (!stringValue.isEmpty()) { + try { + if (stringValue.startsWith("\"") && stringValue.endsWith("\"")) { + stringValue = stringValue.substring(1, stringValue.length() - 1); + } + graph.claimLiteral(subject, relation, Double.parseDouble(stringValue), Bindings.DOUBLE); + } catch (NumberFormatException e) { + LOGGER.error("Could not parse {} {} {} {}", row, index, subject, relation, e); + //throw new DatabaseException(e); + } + } + } + } + + private static void writeStringValue(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation) throws DatabaseException { + if (index != -1) { + String stringValue = row.get(index); + if (!stringValue.isEmpty()) { + try { + graph.claimLiteral(subject, relation, stringValue, Bindings.STRING); + } catch (NumberFormatException e) { + throw new DatabaseException(e); + } + } + } + } + + private static void writeDoubleArrayFromString(WriteGraph graph, CSVRecord row, int index, Resource subject, Resource relation, MathTransform actualTransform) throws DatabaseException, MismatchedDimensionException, TransformException { + if (index != -1) { + String stringValue = row.get(index); + if (!stringValue.isEmpty()) { + if (stringValue.startsWith("\"") && stringValue.endsWith("\"")) { + stringValue = stringValue.substring(1, stringValue.length() - 1); + } + String[] coordPairs = stringValue.split(";"); + ArrayList dd = new ArrayList<>(coordPairs.length * 2); + for (int i = 0; i < coordPairs.length; i++) { + String coordPair = coordPairs[i]; + String[] p = coordPair.split(" "); + double x = Double.parseDouble(p[0]); + double y = Double.parseDouble(p[1]); + if (actualTransform != null) { + DirectPosition2D targetPos = new DirectPosition2D(); + DirectPosition2D sourcePos = new DirectPosition2D(y, x); + DirectPosition res = actualTransform.transform(sourcePos, targetPos); + double[] coords = res.getCoordinate(); + x = coords[1]; + y = coords[0]; + } + dd.add(x); + dd.add(y); + } + double[] detailedGeometryCoords = new double[dd.size()]; + for (int i = 0; i < dd.size(); i++) { + double d = dd.get(i); + detailedGeometryCoords[i] = d; + } + try { + graph.claimLiteral(subject, relation, detailedGeometryCoords, Bindings.DOUBLE_ARRAY); + } catch (NumberFormatException e) { + throw new DatabaseException(e); + } + } + } + } } diff --git a/org.simantics.district.maps/META-INF/MANIFEST.MF b/org.simantics.district.maps/META-INF/MANIFEST.MF index c7858653..5a516748 100644 --- a/org.simantics.district.maps/META-INF/MANIFEST.MF +++ b/org.simantics.district.maps/META-INF/MANIFEST.MF @@ -13,7 +13,8 @@ Require-Bundle: org.simantics.scenegraph, org.eclipse.ui.ide, org.eclipse.ui.workbench, org.simantics.district.geotools;bundle-version="1.0.0", - org.slf4j.api;bundle-version="1.7.25" + org.slf4j.api;bundle-version="1.7.25", + org.simantics.maps.elevation.server Export-Package: org.simantics.maps, org.simantics.maps.debug, org.simantics.maps.eclipse, diff --git a/org.simantics.district.maps/src/org/simantics/maps/MapScalingTransform.java b/org.simantics.district.maps/src/org/simantics/maps/MapScalingTransform.java index e29e8e55..7a47777d 100644 --- a/org.simantics.district.maps/src/org/simantics/maps/MapScalingTransform.java +++ b/org.simantics.district.maps/src/org/simantics/maps/MapScalingTransform.java @@ -7,7 +7,8 @@ public class MapScalingTransform { private MapScalingTransform() { } public static final double scale = 256.0d / 360.0d; - public static final AffineTransform INSTANCE = new AffineTransform(scale, 0, 0, scale, 0, 0); + public static final AffineTransform INSTANCE = AffineTransform.getScaleInstance(scale, scale); + public static final AffineTransform INVERSE = AffineTransform.getScaleInstance(1/scale, 1/scale); public static double getScaleX() { return INSTANCE.getScaleX(); diff --git a/org.simantics.district.maps/src/org/simantics/maps/sg/MapAttributionNode.java b/org.simantics.district.maps/src/org/simantics/maps/sg/MapAttributionNode.java index d97d22eb..b2d28c0b 100644 --- a/org.simantics.district.maps/src/org/simantics/maps/sg/MapAttributionNode.java +++ b/org.simantics.district.maps/src/org/simantics/maps/sg/MapAttributionNode.java @@ -29,8 +29,6 @@ public class MapAttributionNode extends G2DNode { Color originalColor = g2d.getColor(); g2d.transform(transform); - AffineTransform tr = g2d.getTransform(); - g2d.setTransform(new AffineTransform()); // do the rendering magic diff --git a/org.simantics.district.maps/src/org/simantics/maps/sg/MapLocationZoomInfoNode.java b/org.simantics.district.maps/src/org/simantics/maps/sg/MapLocationZoomInfoNode.java index 9e027ecb..790ebbe0 100644 --- a/org.simantics.district.maps/src/org/simantics/maps/sg/MapLocationZoomInfoNode.java +++ b/org.simantics.district.maps/src/org/simantics/maps/sg/MapLocationZoomInfoNode.java @@ -13,7 +13,7 @@ import java.util.Locale; import org.simantics.g2d.participant.MouseUtil; import org.simantics.g2d.participant.MouseUtil.MouseInfo; -import org.simantics.maps.MapScalingTransform; +import org.simantics.maps.elevation.server.SingletonTiffTileInterface; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.utils.DPIUtil; @@ -36,8 +36,6 @@ public class MapLocationZoomInfoNode extends G2DNode { Color originalColor = g2d.getColor(); g2d.transform(transform); - AffineTransform tr = g2d.getTransform(); - g2d.setTransform(new AffineTransform()); // do the rendering magic @@ -51,7 +49,6 @@ public class MapLocationZoomInfoNode extends G2DNode { if (bounds == null) return; // FIXME - int zoomLevel = MapScalingTransform.zoomLevel(ot); MouseInfo mouseInfo = util.getMouseInfo(0); double startLat; @@ -67,7 +64,7 @@ public class MapLocationZoomInfoNode extends G2DNode { startLat = 0; startLon = 0; } - + Number zoomLevel = SingletonTiffTileInterface.lookup(startLat, startLon); String str = "X: " + formatValue(startLon, MAX_DIGITS) + ", Y: " + formatValue(startLat, MAX_DIGITS) + ", Z: " + zoomLevel; g2d.setFont(rulerFont); FontMetrics fm = g2d.getFontMetrics(); diff --git a/org.simantics.district.maps/src/org/simantics/maps/sg/MapNode.java b/org.simantics.district.maps/src/org/simantics/maps/sg/MapNode.java index 7ca092cf..ae418de4 100644 --- a/org.simantics.district.maps/src/org/simantics/maps/sg/MapNode.java +++ b/org.simantics.district.maps/src/org/simantics/maps/sg/MapNode.java @@ -61,8 +61,8 @@ public class MapNode extends G2DNode implements ITileListener { private static final Logger LOGGER = LoggerFactory.getLogger(MapNode.class); - private final double MAP_SCALE = 1; - private final int MAX_TILE_LEVEL = 19; + //private final double MAP_SCALE = 1; + //private final int MAX_TILE_LEVEL = 19; private final int TILE_PIXEL_SIZE = 256; private final int VIEWBOX_QUIET_TIME = 500; diff --git a/org.simantics.district.maps/src/org/simantics/maps/sg/MapScaleNode.java b/org.simantics.district.maps/src/org/simantics/maps/sg/MapScaleNode.java index dc6b393d..e506e11d 100644 --- a/org.simantics.district.maps/src/org/simantics/maps/sg/MapScaleNode.java +++ b/org.simantics.district.maps/src/org/simantics/maps/sg/MapScaleNode.java @@ -16,6 +16,7 @@ import org.geotools.referencing.GeodeticCalculator; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.simantics.scenegraph.g2d.G2DNode; +import org.simantics.scenegraph.utils.DPIUtil; import org.simantics.scenegraph.utils.GridUtils; public class MapScaleNode extends G2DNode { @@ -47,7 +48,7 @@ public class MapScaleNode extends G2DNode { double offsetY = tr.getTranslateY(); g.setTransform(new AffineTransform()); - Font rulerFont = new Font("Tahoma", Font.PLAIN, 9); + Font rulerFont = new Font("Tahoma", Font.PLAIN, DPIUtil.upscale(9));; //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setStroke(new BasicStroke(1)); @@ -141,7 +142,7 @@ public class MapScaleNode extends G2DNode { private static final transient int MAX_DIGITS = 0; private static final transient double EPSILON = 0.01; private static final transient double TRIM_THRESHOLD_MAX_VALUE = Math.pow(10, 2); - private static final transient String[] SI_UNIT_LARGE_PREFIXES = { "m", "km" }; + //private static final transient String[] SI_UNIT_LARGE_PREFIXES = { "m", "km" }; private static final transient double[] SCALE_VALUES = { 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 50000000 }; diff --git a/org.simantics.district.network.ontology/graph/DistrictNetwork.pgraph b/org.simantics.district.network.ontology/graph/DistrictNetwork.pgraph index d9ab6d6e..bb3130eb 100644 --- a/org.simantics.district.network.ontology/graph/DistrictNetwork.pgraph +++ b/org.simantics.district.network.ontology/graph/DistrictNetwork.pgraph @@ -39,6 +39,8 @@ DN.Element -- DN.Vertex.HasElevation @defProperty "Elevation" L0.Double + >-- DN.Vertex.HasAltElevation + @defProperty "Alternative Elevation" L0.Double >-- DN.Vertex.HasAddress @defProperty "Address" L0.String >-- DN.Vertex.HasSupplyTemperature @@ -54,7 +56,9 @@ DN.Vertex -- DN.Vertex.HasDeltaTemperature @defProperty "Delta temperature" L0.Double >-- DN.Vertex.HasHeatPower - @defProperty "Heat Power" L0.Double + @defProperty "Heat Power" L0.Double + >-- DN.Vertex.HasPeakPower + @defProperty "Peak Power" L0.Double >-- DN.Vertex.HasNominalHeadM @defProperty "NominalHeadM" L0.Double >-- DN.Vertex.HasNominalHeadB @@ -76,7 +80,41 @@ DN.Vertex -- DN.Vertex.HasValvePosition @defProperty "Valve position" L0.Double >-- DN.Vertex.HasNominalPressureLoss - @defProperty "Nominal Pressure Loss" L0.Double + @defProperty "Nominal Pressure Loss" L0.Double + >-- DN.Vertex.HasPumpInReturnLine + @defProperty "Pump in Return Line" L0.Double + >-- DN.Vertex.HasHeadPumpMaximum + @defProperty "Head pump maximum" L0.Double + >-- DN.Vertex.HasHeadPumpNominal + @defProperty "Head pump nominal" L0.Double + >-- DN.Vertex.HasFrequencyConverterControlled + @defProperty "Frequency Converter Controlled" L0.Double + >-- DN.Vertex.HasInternalValveMeasurement + @defProperty "Internal Valve Measurement" L0.Double + >-- DN.Vertex.HasPumpMassFlowNominal + @defProperty "Pump Mass Flow Nominal" L0.Double + >-- DN.Vertex.HasPumpMeMax + @defProperty "Pump Me Max" L0.Double + >-- DN.Vertex.HasPumpMeMin + @defProperty "Pump Me Min" L0.Double + >-- DN.Vertex.HasPumpSpeedMax + @defProperty "Pump Speed Max" L0.Double + >-- DN.Vertex.HasPumpSpeedMin + @defProperty "Pump Speed Min" L0.Double + >-- DN.Vertex.HasValveReturnLine + @defProperty "Valve Return Line" L0.Double + >-- DN.Vertex.HasValveMeMax + @defProperty "Valve Me Max" L0.Double + >-- DN.Vertex.HasValveMeMin + @defProperty "Valve Me Min" L0.Double + >-- DN.Vertex.HasValveMinPosition + @defProperty "Valve Min Position" L0.Double + >-- DN.Vertex.HasValveOutletMode + @defProperty "Valve Outlet Mode" L0.Double + >-- DN.Vertex.HasValvePressLossNominal + @defProperty "Valve Press Loss Nominal" L0.Double + >-- DN.Vertex.HasOpeningTime + @defProperty "Opening Time" L0.Double @L0.assert DN.Vertex.HasElevation 0.0 DN.Edge -- DN.Diagram.Visualisations.ShowColorBars + @defProperty "Show Color Bars" L0.Boolean + >-- DN.Diagram.Visualisations.ShowColorBarTicks + @defProperty "Show Color Bar Ticks" L0.Boolean + >-- DN.Diagram.Visualisations.ColorBarLocation + @defProperty "Color Bar Location" L0.String + >-- DN.Diagram.Visualisations.ColorBarSize + @defProperty "Color Bar Size" L0.String + >-- DN.Diagram.Visualisations.ShowSizeBars + @defProperty "Show Size Bars" L0.Boolean + >-- DN.Diagram.Visualisations.ShowSizeBarTicks + @defProperty "Show Size Bar Ticks" L0.Boolean + >-- DN.Diagram.Visualisations.SizeBarLocation + @defProperty "Size Bar Location" L0.String + >-- DN.Diagram.Visualisations.SizeBarSize + @defProperty "Size Bar Size" L0.String + +DN.Diagram.Visualisations.ColorContribution -- DN.Mapping.VertexMapping.ElevationAttribute --> L0.String -- DN.Mapping.VertexMapping.ElevationAltAttribute --> L0.String -- DN.Mapping.VertexMapping.SupplyTemperatureAttribute --> L0.String -- DN.Mapping.VertexMapping.ReturnTemperatureAttribute --> L0.String -- DN.Mapping.VertexMapping.SupplyPressureAttribute --> L0.String -- DN.Mapping.VertexMapping.dpAttribute --> L0.String -- DN.Mapping.VertexMapping.dtAttribute --> L0.String -- DN.Mapping.VertexMapping.HeatPowerAttribute --> L0.String -- DN.Mapping.VertexMapping.PeakPowerAttribute --> L0.String -- DN.Mapping.VertexMapping.NominalHeadMAttribute --> L0.String -- DN.Mapping.VertexMapping.NominalHeadBAttribute --> L0.String -- DN.Mapping.VertexMapping.NominalFlowAttribute --> L0.String -- DN.Mapping.VertexMapping.NominalPressureLossAttribute --> L0.String -- DN.Mapping.VertexMapping.NominalMassFlowAttribute --> L0.String -- DN.Mapping.VertexMapping.HeadPumpMaximumAttribute --> L0.String -- DN.Mapping.VertexMapping.HeadPumpNominalAttribute --> L0.String -- DN.Mapping.VertexMapping.FrequencyConverterControlledAttribute --> L0.String -- DN.Mapping.VertexMapping.InternalValveMeasurementAttribute --> L0.String -- DN.Mapping.VertexMapping.PumpInReturnLineAttribute --> L0.String -- DN.Mapping.VertexMapping.PumpMassFlowNominalAttribute --> L0.String -- DN.Mapping.VertexMapping.PumpMeMaxAttribute --> L0.String -- DN.Mapping.VertexMapping.PumpMeMinAttribute --> L0.String -- DN.Mapping.VertexMapping.PumpSpeedMaxAttribute --> L0.String -- DN.Mapping.VertexMapping.PumpSpeedMinAttribute --> L0.String -- DN.Mapping.VertexMapping.ValveReturnLineAttribute --> L0.String -- DN.Mapping.VertexMapping.ValveMeMaxAttribute --> L0.String -- DN.Mapping.VertexMapping.ValveMeMinAttribute --> L0.String -- DN.Mapping.VertexMapping.ValveMinPositionAttribute --> L0.String -- DN.Mapping.VertexMapping.ValveOutletModeAttribute --> L0.String -- DN.Mapping.VertexMapping.ValvePressLossNominalAttribute --> L0.String -- DN.Mapping.VertexMapping.OpeningTimeAttribute --> L0.String -- DN.Mapping.EdgeMapping.LengthAttribute --> L0.String -- DN.Mapping.EdgeMapping.DiameterAttribute --> L0.String -- DN.Mapping.EdgeMapping.TGroundAttribute --> L0.String -- DN.Mapping.EdgeMapping.ElevationAttribute --> L0.String -- DN.Diagram.drawMapEnabled ==> "Boolean" -- DN.Diagram.hasVisualisation --> DN.Diagram.Visualisations -- DN.Diagram.hasActiveVisualisation --> DN.Diagram.Visualisations -- DN.Diagram.profileUpdateInterval ==> "Long" + class="org.simantics.district.network.ui.styles.ConnectionLineStyle"> + + + + + + + + diff --git a/org.simantics.district.network.ui/fragment.e4xmi b/org.simantics.district.network.ui/fragment.e4xmi index fb9bde4c..89afdc5d 100644 --- a/org.simantics.district.network.ui/fragment.e4xmi +++ b/org.simantics.district.network.ui/fragment.e4xmi @@ -9,6 +9,14 @@ + + + + + + + + @@ -19,6 +27,13 @@ + + + + + + + @@ -29,5 +44,11 @@ + + + + + + diff --git a/org.simantics.district.network.ui/plugin.xml b/org.simantics.district.network.ui/plugin.xml index f33e345a..b8a712d8 100644 --- a/org.simantics.district.network.ui/plugin.xml +++ b/org.simantics.district.network.ui/plugin.xml @@ -32,6 +32,30 @@ name="District Network Breakdown" restorable="true"> + + + + + + diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramEditor.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramEditor.java index 9862cde2..7805ea1b 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramEditor.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramEditor.java @@ -1,5 +1,6 @@ package org.simantics.district.network.ui; +import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.PartInitException; import org.simantics.modeling.ui.diagramEditor.DiagramEditor; import org.simantics.modeling.ui.diagramEditor.DiagramViewer; @@ -7,9 +8,16 @@ import org.simantics.modeling.ui.diagramEditor.DiagramViewer; public class DistrictDiagramEditor extends DiagramEditor { public static final String ID = "org.simantics.district.network.ui.diagrameditor"; - + @Override protected DiagramViewer createViewer() throws PartInitException { return new DistrictDiagramViewer(); } + + @Override + public void partHidden(IWorkbenchPartReference partRef) { + // we do not want to have disposer for district diagram viewer + // to add disposer with default settings uncomment the following line + // super.partHidden(partRef); + } } diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramViewer.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramViewer.java index 6175ace7..f0305fad 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramViewer.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramViewer.java @@ -2,6 +2,8 @@ package org.simantics.district.network.ui; import java.awt.Color; import java.awt.geom.AffineTransform; +import java.util.Collections; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; @@ -14,8 +16,15 @@ import org.simantics.db.exception.DatabaseException; import org.simantics.db.procedure.Listener; import org.simantics.diagram.ui.DiagramModelHints; import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.district.network.ui.participants.DNPointerInteractor; +import org.simantics.district.network.ui.participants.DynamicVisualisationContributionsParticipant; +import org.simantics.district.network.ui.participants.ElevationServerParticipant; import org.simantics.district.network.ui.participants.MapRulerPainter; +import org.simantics.district.network.visualisations.model.ColorBarOptions; +import org.simantics.district.network.visualisations.model.DynamicColorContribution; +import org.simantics.district.network.visualisations.model.DynamicSizeContribution; +import org.simantics.district.network.visualisations.model.SizeBarOptions; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.impl.CanvasContext; import org.simantics.g2d.diagram.handler.PickRequest.PickFilter; @@ -33,15 +42,19 @@ import org.simantics.maps.MapScalingTransform; import org.simantics.maps.eclipse.MapPainter; import org.simantics.maps.sg.commands.MapCommands; import org.simantics.modeling.ui.diagramEditor.DiagramViewer; +import org.simantics.scenegraph.g2d.events.command.Command; import org.simantics.scenegraph.g2d.events.command.CommandEvent; import org.simantics.scenegraph.g2d.events.command.Commands; import org.simantics.utils.datastructures.hints.IHintContext; +import org.simantics.utils.datastructures.hints.IHintContext.Key; +import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DistrictDiagramViewer extends DiagramViewer { - private static final Logger LOGGER = LoggerFactory.getLogger(DistrictDiagramViewer.class); + @SuppressWarnings("unused") + private static final Logger LOGGER = LoggerFactory.getLogger(DistrictDiagramViewer.class); @Override protected void addDiagramParticipants(ICanvasContext ctx) { @@ -54,6 +67,8 @@ public class DistrictDiagramViewer extends DiagramViewer { ctx.add(new MapPainter(tr)); ctx.add(new NetworkDrawingParticipant(tr)); + ctx.add(new ElevationServerParticipant(tr)); + ctx.add(new DynamicVisualisationContributionsParticipant(tr)); } protected String getPopupId() { @@ -102,6 +117,10 @@ public class DistrictDiagramViewer extends DiagramViewer { // super.loadPageSettings() does async-db-operations setupDrawMapEnabled(); setupBackgroundColor(); + setupColoringObjects(); + setupColorBarOptions(); + setupSizingObjects(); + setupSizeBarOptions(); } private void setupDrawMapEnabled() { @@ -115,7 +134,7 @@ public class DistrictDiagramViewer extends DiagramViewer { result -> queueBackgroundColorChangeEvent(result), () -> DistrictDiagramViewer.this.isDisposed())); } - + private void queueBackgroundColorChangeEvent(RGB.Integer result) { if (result != null) { Color backgroundColor = new Color(result.red, result.green, result.blue); @@ -124,6 +143,72 @@ public class DistrictDiagramViewer extends DiagramViewer { } } + private void setupColoringObjects() { + sessionContext.getSession().asyncRequest(new ColoringObjectsRequest(getInputResource()), new ColoringObjectsListener( + result -> queueColoringObjectsChangeEvent(result), + () -> DistrictDiagramViewer.this.isDisposed())); + } + + + private void setupColorBarOptions() { + sessionContext.getSession().asyncRequest(new ColorBarOptionsRequest(getInputResource()), new ColorBarOptionsListener( + result -> queueColorBarOptionsChangeEvent(result), + () -> DistrictDiagramViewer.this.isDisposed())); + } + + private void setupSizingObjects() { + sessionContext.getSession().asyncRequest(new SizingObjectsRequest(getInputResource()), new SizingObjectsListener( + result -> queueSizingObjectsChangeEvent(result), + () -> DistrictDiagramViewer.this.isDisposed())); + } + + + private void setupSizeBarOptions() { + sessionContext.getSession().asyncRequest(new SizeBarOptionsRequest(getInputResource()), new SizeBarOptionsListener( + result -> queueSizeBarOptionsChangeEvent(result), + () -> DistrictDiagramViewer.this.isDisposed())); + } + + public static final Key KEY_MAP_COLOR_BAR_OPTIONS = new KeyOf(ColorBarOptions.class, "colorBarOptions"); + public static final Command MAP_COLOR_BAR_OPTIONS_CHANGE = new Command("colorBarOptionsChange"); + public static final Key KEY_MAP_SIZE_BAR_OPTIONS = new KeyOf(SizeBarOptions.class, "sizeBarOptions"); + public static final Command MAP_SIZE_BAR_OPTIONS_CHANGE = new Command("sizeBarOptionsChange"); + + public static final Key KEY_MAP_COLORING_OBJECTS = new KeyOf(Map.class, "coloringObjects"); + public static final Command MAP_COLORING_OBJECTS_CHANGE = new Command("coloringObjectsChange"); + + public static final Key KEY_MAP_SIZING_OBJECTS = new KeyOf(Map.class, "sizingObjects"); + public static final Command MAP_SIZING_OBJECTS_CHANGE = new Command("sizingObjectsChange"); + + + private void queueColoringObjectsChangeEvent(Map result) { + if (result != null) { + canvasContext.getDefaultHintContext().setHint(KEY_MAP_COLORING_OBJECTS, result); + canvasContext.getEventQueue().queueEvent(new CommandEvent(canvasContext, System.currentTimeMillis(), MAP_COLORING_OBJECTS_CHANGE)); + } + } + + private void queueColorBarOptionsChangeEvent(ColorBarOptions result) { + if (result != null) { + canvasContext.getDefaultHintContext().setHint(KEY_MAP_COLOR_BAR_OPTIONS, result); + canvasContext.getEventQueue().queueEvent(new CommandEvent(canvasContext, System.currentTimeMillis(), MAP_COLOR_BAR_OPTIONS_CHANGE)); + } + } + + private void queueSizingObjectsChangeEvent(Map result) { + if (result != null) { + canvasContext.getDefaultHintContext().setHint(KEY_MAP_SIZING_OBJECTS, result); + canvasContext.getEventQueue().queueEvent(new CommandEvent(canvasContext, System.currentTimeMillis(), MAP_SIZING_OBJECTS_CHANGE)); + } + } + + private void queueSizeBarOptionsChangeEvent(SizeBarOptions result) { + if (result != null) { + canvasContext.getDefaultHintContext().setHint(KEY_MAP_SIZE_BAR_OPTIONS, result); + canvasContext.getEventQueue().queueEvent(new CommandEvent(canvasContext, System.currentTimeMillis(), MAP_SIZE_BAR_OPTIONS_CHANGE)); + } + } + private static class DrawMapEnabledRequest extends UnaryRead { public DrawMapEnabledRequest(Resource diagram) { @@ -178,7 +263,7 @@ public class DistrictDiagramViewer extends DiagramViewer { private static class MapBackgroundColorListener implements Listener { - private static final Logger LOGGER = LoggerFactory.getLogger(DrawMapEnabledListener.class); + private static final Logger LOGGER = LoggerFactory.getLogger(MapBackgroundColorListener.class); private Consumer callback; private Supplier isDisposed; @@ -203,4 +288,184 @@ public class DistrictDiagramViewer extends DiagramViewer { return isDisposed.get(); } } + + private static class ColorBarOptionsRequest extends UnaryRead { + + public ColorBarOptionsRequest(Resource diagram) { + super(diagram); + } + + @Override + public ColorBarOptions perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Resource activeVisualisation = graph.getPossibleObject(parameter, DN.Diagram_hasActiveVisualisation); + if (activeVisualisation != null) { + return DistrictNetworkUtil.colorBarOptions(graph, activeVisualisation); + } + return ColorBarOptions.useDefault(); + } + } + + private static class ColoringObjectsRequest extends UnaryRead> { + + public ColoringObjectsRequest(Resource diagram) { + super(diagram); + } + + @Override + public Map perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Resource activeVisualisation = graph.getPossibleObject(parameter, DN.Diagram_hasActiveVisualisation); + if (activeVisualisation != null) { + return DistrictNetworkUtil.colorContributions(graph, activeVisualisation); + } + return Collections.emptyMap(); + } + } + + private static class ColoringObjectsListener implements Listener> { + + private static final Logger LOGGER = LoggerFactory.getLogger(ColoringObjectsListener.class); + + private Consumer> callback; + private Supplier isDisposed; + + public ColoringObjectsListener(Consumer> callback, Supplier isDisposed) { + this.callback = callback; + this.isDisposed = isDisposed; + } + + @Override + public void execute(Map result) { + callback.accept(result); + } + + @Override + public void exception(Throwable t) { + LOGGER.error("Could not listen ColorBarOptions", t); + } + + @Override + public boolean isDisposed() { + return isDisposed.get(); + } + } + + private static class ColorBarOptionsListener implements Listener { + + private static final Logger LOGGER = LoggerFactory.getLogger(ColorBarOptionsListener.class); + + private Consumer callback; + private Supplier isDisposed; + + public ColorBarOptionsListener(Consumer callback, Supplier isDisposed) { + this.callback = callback; + this.isDisposed = isDisposed; + } + + @Override + public void execute(ColorBarOptions result) { + callback.accept(result); + } + + @Override + public void exception(Throwable t) { + LOGGER.error("Could not listen ColorBarOptions", t); + } + + @Override + public boolean isDisposed() { + return isDisposed.get(); + } + } + + private static class SizeBarOptionsRequest extends UnaryRead { + + public SizeBarOptionsRequest(Resource diagram) { + super(diagram); + } + + @Override + public SizeBarOptions perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Resource activeVisualisation = graph.getPossibleObject(parameter, DN.Diagram_hasActiveVisualisation); + if (activeVisualisation != null) { + return DistrictNetworkUtil.sizeBarOptions(graph, activeVisualisation); + } + return SizeBarOptions.useDefault(); + } + } + + private static class SizeBarOptionsListener implements Listener { + + private static final Logger LOGGER = LoggerFactory.getLogger(SizeBarOptionsListener.class); + + private Consumer callback; + private Supplier isDisposed; + + public SizeBarOptionsListener(Consumer callback, Supplier isDisposed) { + this.callback = callback; + this.isDisposed = isDisposed; + } + + @Override + public void execute(SizeBarOptions result) { + callback.accept(result); + } + + @Override + public void exception(Throwable t) { + LOGGER.error("Could not listen SizeBarOptions", t); + } + + @Override + public boolean isDisposed() { + return isDisposed.get(); + } + } + + private static class SizingObjectsRequest extends UnaryRead> { + + public SizingObjectsRequest(Resource diagram) { + super(diagram); + } + + @Override + public Map perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Resource activeVisualisation = graph.getPossibleObject(parameter, DN.Diagram_hasActiveVisualisation); + if (activeVisualisation != null) { + return DistrictNetworkUtil.sizeContributions(graph, activeVisualisation); + } + return Collections.emptyMap(); + } + } + + private static class SizingObjectsListener implements Listener> { + + private static final Logger LOGGER = LoggerFactory.getLogger(SizingObjectsListener.class); + + private Consumer> callback; + private Supplier isDisposed; + + public SizingObjectsListener(Consumer> callback, Supplier isDisposed) { + this.callback = callback; + this.isDisposed = isDisposed; + } + + @Override + public void execute(Map result) { + callback.accept(result); + } + + @Override + public void exception(Throwable t) { + LOGGER.error("Could not listen ColorBarOptions", t); + } + + @Override + public boolean isDisposed() { + return isDisposed.get(); + } + } } diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictPanZoomRotateHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictPanZoomRotateHandler.java index 05866bda..ac0d22e3 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictPanZoomRotateHandler.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictPanZoomRotateHandler.java @@ -114,8 +114,6 @@ public class DistrictPanZoomRotateHandler extends PanZoomRotateHandler { } private boolean zoomToPage() { - int currentZoomLevel = MapScalingTransform.zoomLevel(util.getTransform()); - util.setTransform(new AffineTransform(2,0,0,2,270,270)); return true; } diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictTransformUtil.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictTransformUtil.java index e71acf0a..4f4583d1 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictTransformUtil.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictTransformUtil.java @@ -54,8 +54,11 @@ public class DistrictTransformUtil extends TransformUtil { int tarZoomLevel = MapScalingTransform.zoomLevel(tar); if (tarZoomLevel < 20 && tarZoomLevel > 0) { toBeX = Math.pow(2.0, tarZoomLevel); - } - else { + } else if (tarZoomLevel > 20) { + toBeX = Math.pow(2.0, 20); + } else if (tarZoomLevel < 0) { + toBeX = 2; + } else { toBeX = targetX; } diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/NetworkDrawingParticipant.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/NetworkDrawingParticipant.java index 69d1f724..bd81bd01 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/NetworkDrawingParticipant.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/NetworkDrawingParticipant.java @@ -3,11 +3,15 @@ package org.simantics.district.network.ui; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.List; +import org.simantics.district.network.ui.adapters.DistrictNetworkEdgeElement; import org.simantics.district.network.ui.adapters.DistrictNetworkVertexElement; +import org.simantics.district.network.ui.nodes.DistrictNetworkEdgeNode; import org.simantics.district.network.ui.nodes.DistrictNetworkVertexNode; +import org.simantics.district.network.ui.nodes.HoverSensitiveNode; import org.simantics.district.network.ui.nodes.NetworkDrawingNode; import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit; @@ -18,12 +22,15 @@ import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant; import org.simantics.g2d.element.IElement; import org.simantics.scenegraph.Node; import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.g2d.IG2DNode; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; public class NetworkDrawingParticipant extends AbstractDiagramParticipant { - @Dependency + public static final String NETWORK_DRAWING_NODE = "networkDrawingNode"; + + @Dependency PickContext pick; /** @@ -48,7 +55,7 @@ public class NetworkDrawingParticipant extends AbstractDiagramParticipant { @SGInit public void initSG(G2DParentNode parent) { - node = parent.addNode("networkDrawingNode", NetworkDrawingNode.class); + node = parent.addNode(NETWORK_DRAWING_NODE, NetworkDrawingNode.class); node.setTransform(transform); node.setNetworkDrawingParticipant(this); } @@ -58,34 +65,62 @@ public class NetworkDrawingParticipant extends AbstractDiagramParticipant { node.setDiagram(newDiagram); } - public boolean pickHoveredElement(Point2D currentMousePos) { - PickRequest req = new PickRequest(currentMousePos).context(getContext()); + public boolean pickHoveredElement(Point2D currentMousePos, boolean isConnectionTool) { + PickRequest req = new PickRequest(new Rectangle2D.Double(currentMousePos.getX(), currentMousePos.getY(), 1e-8, 1e-8)).context(getContext()); List pickables = new ArrayList(); pick.pick(diagram, req, pickables); List snap = new ArrayList<>(diagram.getSnapshot()); - snap.removeAll(pickables); + // snap.removeAll(pickables); boolean changed = false; - for (IElement sn : snap) { - Node node = sn.getHint(DistrictNetworkVertexElement.KEY_DN_VERTEX_NODE); + changed = hoverVertexNodes(snap, false, isConnectionTool, changed, currentMousePos); + changed = hoverEdgeNodes(snap, false, isConnectionTool, changed, currentMousePos); + changed = hoverVertexNodes(pickables, true, isConnectionTool, changed, currentMousePos); + changed = hoverEdgeNodes(pickables, true, isConnectionTool, changed, currentMousePos); + return changed; + } + + private boolean hoverVertexNodes(List elements, boolean hover, boolean isConnectionTool, boolean changed, Point2D p) { + for (IElement elem : elements) { + Node node = elem.getHint(DistrictNetworkVertexElement.KEY_DN_VERTEX_NODE); if (node instanceof DistrictNetworkVertexNode) { - if (((DistrictNetworkVertexNode) node).hover(false) && !changed) { - changed = true; + changed = ((DistrictNetworkVertexNode) node).hover(hover, isConnectionTool) || changed; + if (hover) + ((DistrictNetworkVertexNode) node).setMousePosition(p); + } + } + return changed; + } + + private boolean hoverEdgeNodes(List elements, boolean hover, boolean isConnectionTool, boolean changed, Point2D p) { + for (IElement elem : elements) { + Node node = elem.getHint(DistrictNetworkEdgeElement.KEY_DN_EDGE_NODE); + if (node instanceof DistrictNetworkEdgeNode) { + for (IG2DNode n : ((DistrictNetworkEdgeNode) node).getNodes()) { + if (n instanceof HoverSensitiveNode) { + changed = ((HoverSensitiveNode)n).hover(hover, isConnectionTool) || changed; + if (hover) + ((HoverSensitiveNode)n).setMousePosition(p); + } } } } - + return changed; + } + + public boolean isHoveringOverNode(Point2D currentMousePos) { + PickRequest req = new PickRequest(currentMousePos).context(getContext()); + List pickables = new ArrayList(); + pick.pick(diagram, req, pickables); for (IElement elem : pickables) { Node node = elem.getHint(DistrictNetworkVertexElement.KEY_DN_VERTEX_NODE); if (node instanceof DistrictNetworkVertexNode) { - if (((DistrictNetworkVertexNode) node).hover(true) && !changed) { - changed = true; - } + return true; } } - return changed; + return false; } public AffineTransform getTransform() { diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/OpenDiagramFromConfigurationAdapter.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/OpenDiagramFromConfigurationAdapter.java index 1c49acbc..e4e97d59 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/OpenDiagramFromConfigurationAdapter.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/OpenDiagramFromConfigurationAdapter.java @@ -1,26 +1,26 @@ -package org.simantics.district.network.ui; - -import java.util.Collections; - -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.exception.DatabaseException; -import org.simantics.district.network.ontology.DistrictNetworkResource; - -public class OpenDiagramFromConfigurationAdapter - extends org.simantics.modeling.ui.diagramEditor.OpenDiagramFromConfigurationAdapter { - - @Override - public boolean canHandle(ReadGraph g, Resource r) throws DatabaseException { - Resource diagram = getDiagram(g, r, Collections.emptySet()); - if (diagram == null) - return false; - DistrictNetworkResource DN = DistrictNetworkResource.getInstance(g); - return g.isInstanceOf(diagram, DN.Diagram) && !isLocked(g, diagram); - } - - @Override - protected String getEditorId() { - return DistrictDiagramEditor.ID; - } -} +package org.simantics.district.network.ui; + +import java.util.Collections; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.district.network.ontology.DistrictNetworkResource; + +public class OpenDiagramFromConfigurationAdapter + extends org.simantics.modeling.ui.diagramEditor.OpenDiagramFromConfigurationAdapter { + + @Override + public boolean canHandle(ReadGraph g, Resource r) throws DatabaseException { + Resource diagram = getDiagram(g, r, Collections.emptySet()); + if (diagram == null) + return false; + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(g); + return g.isInstanceOf(diagram, DN.Diagram) && !isLocked(g, diagram); + } + + @Override + protected String getEditorId(ReadGraph g, Resource composite) throws DatabaseException { + return DistrictDiagramEditor.ID; + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/adapters/DistrictNetworkEdgeElement.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/adapters/DistrictNetworkEdgeElement.java index 34424739..ef2e21fe 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/adapters/DistrictNetworkEdgeElement.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/adapters/DistrictNetworkEdgeElement.java @@ -4,6 +4,8 @@ import java.awt.Color; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Collection; @@ -29,6 +31,7 @@ import org.simantics.g2d.element.handler.impl.SimpleElementLayers; import org.simantics.maps.MapScalingTransform; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.nodes.SVGNode; +import org.simantics.scenegraph.utils.NodeUtil; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; import org.slf4j.Logger; @@ -129,7 +132,7 @@ public class DistrictNetworkEdgeElement { Rectangle2D bounds = getBounds(s); switch (policy) { case PICK_CONTAINED_OBJECTS: return pickContainedObjects(edge, bounds); - case PICK_INTERSECTING_OBJECTS: return pickIntersectingObjects(edge, bounds); + case PICK_INTERSECTING_OBJECTS: return pickIntersectingObjects(e, edge, bounds); } return false; } @@ -162,21 +165,54 @@ public class DistrictNetworkEdgeElement { return eminx >= bsminx && eminy >= boundsMinY && emaxx <= bsmaxx && emaxy <= boundsMaxY; } - private boolean pickIntersectingObjects(DistrictNetworkEdge edge, Rectangle2D bounds) { - double tolerance = (bounds.getHeight() + bounds.getHeight()) * 1 / MapScalingTransform.getScaleX(); - Line2D line = new Line2D.Double(edge.getStartPoint(), edge.getEndPoint()); + private boolean pickIntersectingObjects(IElement e, DistrictNetworkEdge edge, Rectangle2D bounds) { + double dx = bounds.getWidth() / MapScalingTransform.getScaleX(); + double dy = bounds.getHeight() / MapScalingTransform.getScaleY(); + + // Half the diagonal + half of the line width + DistrictNetworkEdgeNode node = e.getHint(KEY_DN_EDGE_NODE); + AffineTransform at = NodeUtil.getLocalToGlobalTransform(node); + + Path2D path = node.getPath(); + if (path == null) + return false; + + double lineWidth = node.getStrokeWidth(at, true); + double tolerance = Math.sqrt(dx * dx + dy * dy) / 2 + lineWidth / 2; + double sx = bounds.getCenterX() / MapScalingTransform.getScaleX(); double sy = bounds.getCenterY() / MapScalingTransform.getScaleY(); - double ssx = ModelledCRS.xToLongitude(sx); - double ssy = ModelledCRS.yToLatitude(-sy); // Invert for Simantics diagram coordinate system - double distSq = line.ptSegDistSq(ssx, ssy); -// System.out.println("s: " + sx + ", " + sy); -// System.out.println("ss: " + ssx + ", " + ssy); -// System.out.println("p1: " + edge.getStartPoint()); -// System.out.println("p2: " + edge.getEndPoint()); -// System.out.println("line: " + "(" + line.getX1() + ", " + line.getY1() + ", " + line.getX2() + ", " + line.getY2() + ")"); -// System.out.println("distance from line is " + Math.sqrt(distSq) + " with tolerance " + tolerance); - return distSq <= tolerance * tolerance; + + double coords[] = new double[6]; + Point2D prevPoint = new Point2D.Double(), curPoint = new Point2D.Double(); + Line2D line = new Line2D.Double(); + for (PathIterator it = path.getPathIterator(null); !it.isDone(); it.next()) { + int type = it.currentSegment(coords); + switch (type) { + case PathIterator.SEG_MOVETO: + curPoint.setLocation(coords[0], coords[1]); + break; + case PathIterator.SEG_LINETO: + prevPoint.setLocation(curPoint); + curPoint.setLocation(coords[0], coords[1]); + line.setLine(prevPoint, curPoint); + double distSq = line.ptSegDistSq(sx, sy); +// System.out.println("s: " + sx + ", " + sy); +// System.out.println("ss: " + ssx + ", " + ssy); +// System.out.println("p1: " + edge.getStartPoint()); +// System.out.println("p2: " + edge.getEndPoint()); +// System.out.println("line: " + "(" + line.getX1() + ", " + line.getY1() + ", " + line.getX2() + ", " + line.getY2() + ")"); +// System.out.println("distance from line is " + Math.sqrt(distSq) + " with tolerance " + tolerance); + if (distSq <= tolerance * tolerance) + return true; + break; + default: + LOGGER.error("Invalid edge path", new IllegalStateException()); + return false; + } + } + + return false; } private Rectangle2D getBounds(Shape shape) { diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/adapters/DistrictNetworkVertexElement.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/adapters/DistrictNetworkVertexElement.java index a989c561..1ff5eefa 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/adapters/DistrictNetworkVertexElement.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/adapters/DistrictNetworkVertexElement.java @@ -115,9 +115,6 @@ public class DistrictNetworkVertexElement { public boolean pickTest(IElement e, Shape s, PickPolicy policy) { DistrictNetworkVertexNode node = e.getHint(KEY_DN_VERTEX_NODE); Rectangle2D boundsInLocal = node.getBounds(); - ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(node); - AffineTransform canvasTransform = ctx.getHintStack().getHint(Hints.KEY_CANVAS_TRANSFORM); - Rectangle2D scaledBounds = new Rectangle2D.Double(boundsInLocal.getX(), boundsInLocal.getY(), boundsInLocal.getWidth() / canvasTransform.getScaleX() * 2, boundsInLocal.getHeight() / canvasTransform.getScaleY() * 2); Rectangle2D bounds = getBounds(s); switch (policy) { case PICK_CONTAINED_OBJECTS: diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeMappingTypeHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeMappingTypeHandler.java index 1047dc13..3f32f445 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeMappingTypeHandler.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeMappingTypeHandler.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; import javax.inject.Named; @@ -48,6 +49,7 @@ import org.simantics.db.exception.ValidationException; import org.simantics.db.layer0.SelectionHints; import org.simantics.db.procedure.Procedure; import org.simantics.db.request.Read; +import org.simantics.district.network.DistrictNetworkUtil; import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.district.network.ui.function.Functions; import org.simantics.district.network.ui.internal.Activator; @@ -149,12 +151,10 @@ public class ChangeMappingTypeHandler { DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); for (Map.Entry> entry : results.entrySet()) { - Resource newMapping = entry.getKey(); - Collection elements = entry.getValue(); - for (NamedResource element : elements) { - graph.deny(element.getResource(), DN.HasMapping); - graph.claim(element.getResource(), DN.HasMapping, newMapping); - } + List elements = entry.getValue().stream() + .map(NamedResource::getResource) + .collect(Collectors.toList()); + DistrictNetworkUtil.changeMappingType(graph, entry.getKey(), elements); } } }); @@ -183,18 +183,12 @@ public class ChangeMappingTypeHandler { private Map> possibleMappings = new HashMap<>(); - private Resource defaultVertexMapping; - protected SelectMappingDialog(Shell parentShell, CompletableFuture>> elements) { super(parentShell); this.elements = elements; setTitle("Change mappings"); } - public Resource getDefaultVertexMapping() { - return defaultVertexMapping; - } - @Override protected Control createDialogArea(Composite parent) { composite = (Composite) super.createDialogArea(parent); diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeRoutePointToVertexHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeRoutePointToVertexHandler.java new file mode 100644 index 00000000..f9e37c08 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeRoutePointToVertexHandler.java @@ -0,0 +1,215 @@ +package org.simantics.district.network.ui.contributions; + +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Point2D; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import javax.inject.Named; + +import org.eclipse.core.commands.ParameterizedCommand; +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IEditorPart; +import org.simantics.Simantics; +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.SelectionHints; +import org.simantics.db.layer0.util.RemoverUtil; +import org.simantics.db.request.Read; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.district.network.DNEdgeBuilder; +import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.ModelledCRS; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.ui.DistrictDiagramEditor; +import org.simantics.district.network.ui.NetworkDrawingParticipant; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.participant.MouseUtil; +import org.simantics.g2d.participant.MouseUtil.MouseInfo; +import org.simantics.maps.elevation.server.SingletonTiffTileInterface; +import org.simantics.maps.elevation.server.prefs.MapsElevationServerPreferences; +import org.simantics.ui.workbench.e4.E4WorkbenchUtils; +import org.simantics.utils.threads.ThreadUtils; +import org.simantics.utils.ui.ISelectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ChangeRoutePointToVertexHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(ChangeRoutePointToVertexHandler.class); + static List elements; + static boolean cut = true; + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + List elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); + if (elements.size() != 1) + return false; + try { + return Simantics.getSession().syncRequest(new Read() { + + @Override + public Boolean perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + for (Resource selection : elements) { + if (!graph.isInstanceOf(selection, DN.Edge)) { + return false; + } + } + return true; + } + }); + } catch (DatabaseException e) { + LOGGER.error("Could not evaluate if mapping can be changed for selection {}", elements, e); + return false; + } + } + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart mActiveEditorPart, @Named(IServiceConstants.ACTIVE_SELECTION) Object selection, ParameterizedCommand command) { + final List elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); + + Resource edge = elements.get(0); + try { + Point2D mouseClicked = mouseClickedOnEditor(mActiveEditorPart); + if (mouseClicked != null) { + + IEditorPart activeEditorPart = E4WorkbenchUtils.getActiveIEditorPart(mActiveEditorPart); + if (activeEditorPart == null) + return; + if (!(activeEditorPart instanceof DistrictDiagramEditor)) + return; + DistrictDiagramEditor editor = (DistrictDiagramEditor) activeEditorPart; + Resource diagram = editor.getInputResource(); + + Simantics.getSession().asyncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + double[] detailedGeom = graph.getPossibleRelatedValue(edge, DN.Edge_HasGeometry, Bindings.DOUBLE_ARRAY); + if (detailedGeom != null) { + double closestDistance = Double.MAX_VALUE; + Point2D closestPoint = null; + int j = 0; + for (int i = 0; i < detailedGeom.length; i += 2) { + double x = detailedGeom[i]; + double y = detailedGeom[i + 1]; + Point2D currentPoint = new Point2D.Double(x, y); + double currentDistance = mouseClicked.distance(currentPoint); + if (currentDistance < closestDistance) { + closestDistance = currentDistance; + closestPoint = currentPoint; + j = i; + } + } + + final Point2D finalClosestPoint = closestPoint; + + DiagramResource DIA = DiagramResource.getInstance(graph); + Resource currentStartVertex = graph.getSingleObject(edge, DN.HasStartVertex); + double[] currentStartVertexCoords = graph.getRelatedValue(currentStartVertex, DIA.HasLocation, Bindings.DOUBLE_ARRAY); + Resource currentEndVertex = graph.getSingleObject(edge, DN.HasEndVertex); + double[] currentEndVertexCoords = graph.getRelatedValue(currentEndVertex, DIA.HasLocation, Bindings.DOUBLE_ARRAY); + + double[] detailedLeftEdgeGeometryCoords = new double[j]; + for (int k = 0; k < j; k += 2) { + double x = detailedGeom[k]; + double y = detailedGeom[k + 1]; + detailedLeftEdgeGeometryCoords[k] = x; + detailedLeftEdgeGeometryCoords[k + 1] = y; + } + + double[] detailedRightEdgeGeometryCoords = new double[detailedGeom.length - j - 2]; + int i = 0; + for (int k = j + 2; k < detailedGeom.length; k += 2) { + double x = detailedGeom[k]; + double y = detailedGeom[k + 1]; + detailedRightEdgeGeometryCoords[i++] = x; + detailedRightEdgeGeometryCoords[i++] = y; + } + + Simantics.getSession().asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + RemoverUtil.remove(graph, edge); + + ThreadUtils.getNonBlockingWorkExecutor().schedule(() -> { + Simantics.getSession().asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + // we might have closest point + if (finalClosestPoint != null) { + Resource mapping = graph.getSingleObject(diagram, DistrictNetworkResource.getInstance(graph).VertexDefaultMapping); + double x = finalClosestPoint.getX(); + double y = finalClosestPoint.getY(); + double[] midVertexCoords = new double[] { x, y }; + + Resource createdVertex = DistrictNetworkUtil.createVertex(graph, diagram, midVertexCoords, Double.MAX_VALUE, mapping); + + Optional leftEdge = DNEdgeBuilder.create(graph, diagram, currentStartVertexCoords, Double.MAX_VALUE, midVertexCoords, Double.MAX_VALUE, detailedLeftEdgeGeometryCoords, 0.001); + Optional rightEdge = DNEdgeBuilder.create(graph, diagram, midVertexCoords, Double.MAX_VALUE, currentEndVertexCoords, Double.MAX_VALUE, detailedRightEdgeGeometryCoords, 0.001); + } + } + }); + }, 500, TimeUnit.MILLISECONDS); + + } + }); + + } else { + // no can do - or it would be possible to split the edge here in to two different edges + } + } + }); + } else { + LOGGER.warn("No mouseClicked for editor {}", mActiveEditorPart); + } + } catch (InvocationTargetException e) { + LOGGER.error("Could not change route point to vertex", e); + } + } + + private static Point2D mouseClickedOnEditor(MPart mActiveEditorPart) throws InvocationTargetException { + IEditorPart activeEditorPart = E4WorkbenchUtils.getActiveIEditorPart(mActiveEditorPart); + if (activeEditorPart == null) + return null; + if (!(activeEditorPart instanceof DistrictDiagramEditor)) + return null; + DistrictDiagramEditor editor = (DistrictDiagramEditor) activeEditorPart; + + ICanvasContext ctx = editor.getAdapter(ICanvasContext.class); + NetworkDrawingParticipant drawingParticipant = ctx.getAtMostOneItemOfClass(NetworkDrawingParticipant.class); + AffineTransform drawingTransform = drawingParticipant.getTransform(); + MouseUtil util = ctx.getAtMostOneItemOfClass(MouseUtil.class); + MouseInfo mouseInfo = util.getMousePressedInfo(0); + if (mouseInfo == null) { + return null; + } + Point2D canvasPosition = mouseInfo.canvasPosition; + Point2D transformed = null; + try { + transformed = drawingTransform.inverseTransform(canvasPosition, new Point2D.Double()); + } catch (NoninvertibleTransformException e) { + LOGGER.error("Could not create inverse transform of {}", drawingTransform, e); + throw new InvocationTargetException(e); + } + double x = ModelledCRS.xToLongitude(transformed.getX()); + double y = ModelledCRS.yToLatitude(-transformed.getY()); + return new Point2D.Double(x, y); + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeVertexToRoutePointHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeVertexToRoutePointHandler.java new file mode 100644 index 00000000..cd713417 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeVertexToRoutePointHandler.java @@ -0,0 +1,172 @@ +package org.simantics.district.network.ui.contributions; + +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Point2D; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import javax.inject.Named; + +import org.eclipse.core.commands.ParameterizedCommand; +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IEditorPart; +import org.simantics.Simantics; +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.SelectionHints; +import org.simantics.db.layer0.util.RemoverUtil; +import org.simantics.db.request.Read; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.district.network.DNEdgeBuilder; +import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.ModelledCRS; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.ui.DistrictDiagramEditor; +import org.simantics.district.network.ui.NetworkDrawingParticipant; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.participant.MouseUtil; +import org.simantics.g2d.participant.MouseUtil.MouseInfo; +import org.simantics.maps.elevation.server.SingletonTiffTileInterface; +import org.simantics.maps.elevation.server.prefs.MapsElevationServerPreferences; +import org.simantics.ui.workbench.e4.E4WorkbenchUtils; +import org.simantics.utils.threads.ThreadUtils; +import org.simantics.utils.ui.ISelectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ChangeVertexToRoutePointHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(ChangeVertexToRoutePointHandler.class); + static List elements; + static boolean cut = true; + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + List elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); + if (elements.size() != 1) + return false; + try { + return Simantics.getSession().syncRequest(new Read() { + + @Override + public Boolean perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + for (Resource selection : elements) { + if (graph.isInstanceOf(selection, DN.Vertex)) { + Collection startingEdge = graph.getObjects(selection, DN.HasStartVertex_Inverse); + Collection endingEdge = graph.getObjects(selection, DN.HasEndVertex_Inverse); + return startingEdge.size() == 1 && endingEdge.size() == 1; + } + } + return false; + } + }); + } catch (DatabaseException e) { + LOGGER.error("Could not evaluate if mapping can be changed for selection {}", elements, e); + return false; + } + } + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart mActiveEditorPart, @Named(IServiceConstants.ACTIVE_SELECTION) Object selection, ParameterizedCommand command) { + final List elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); + + Resource vertex = elements.get(0); + try { + Point2D mouseClicked = mouseClickedOnEditor(mActiveEditorPart); + if (mouseClicked != null) { + + IEditorPart activeEditorPart = E4WorkbenchUtils.getActiveIEditorPart(mActiveEditorPart); + if (activeEditorPart == null) + return; + if (!(activeEditorPart instanceof DistrictDiagramEditor)) + return; + DistrictDiagramEditor editor = (DistrictDiagramEditor) activeEditorPart; + Resource diagram = editor.getInputResource(); + + Simantics.getSession().asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + DiagramResource DIA = DiagramResource.getInstance(graph); + // we support only single start & end edge + Resource startingEdge = graph.getSingleObject(vertex, DN.HasEndVertex_Inverse); + Resource endingEdge = graph.getSingleObject(vertex, DN.HasStartVertex_Inverse); + double[] startingDetailed = graph.getPossibleRelatedValue(startingEdge, DN.Edge_HasGeometry); + double[] endingDetailed = graph.getPossibleRelatedValue(endingEdge, DN.Edge_HasGeometry); + Resource endingVertex = graph.getSingleObject(endingEdge, DN.HasEndVertex); + int length = startingDetailed.length + 2 + endingDetailed.length; + double[] newDetailed = new double[length]; + for (int i = 0; i < startingDetailed.length; i++) { + double d = startingDetailed[i]; + newDetailed[i] = d; + } + double[] coords = graph.getRelatedValue2(vertex, DIA.HasLocation); + int currentVertexIndex = startingDetailed.length; + newDetailed[currentVertexIndex] = coords[0]; + newDetailed[currentVertexIndex + 1] = coords[1]; + int f = startingDetailed.length + 2; + for (int i = 0; i < endingDetailed.length; i++) { + double d = endingDetailed[i]; + newDetailed[f + i] = d; + } + graph.deny(startingEdge, DN.Edge_HasGeometry); + graph.claimLiteral(startingEdge, DN.Edge_HasGeometry, newDetailed, Bindings.DOUBLE_ARRAY); + + graph.deny(startingEdge, DN.HasEndVertex); + graph.claim(startingEdge, DN.HasEndVertex, endingVertex); + + RemoverUtil.remove(graph, vertex); + } + }); + } else { + LOGGER.warn("No mouseClicked for editor {}", mActiveEditorPart); + } + } catch (InvocationTargetException e) { + LOGGER.error("Could not change route point to vertex", e); + } + } + + private static Point2D mouseClickedOnEditor(MPart mActiveEditorPart) throws InvocationTargetException { + IEditorPart activeEditorPart = E4WorkbenchUtils.getActiveIEditorPart(mActiveEditorPart); + if (activeEditorPart == null) + return null; + if (!(activeEditorPart instanceof DistrictDiagramEditor)) + return null; + DistrictDiagramEditor editor = (DistrictDiagramEditor) activeEditorPart; + + ICanvasContext ctx = editor.getAdapter(ICanvasContext.class); + NetworkDrawingParticipant drawingParticipant = ctx.getAtMostOneItemOfClass(NetworkDrawingParticipant.class); + AffineTransform drawingTransform = drawingParticipant.getTransform(); + MouseUtil util = ctx.getAtMostOneItemOfClass(MouseUtil.class); + MouseInfo mouseInfo = util.getMousePressedInfo(0); + if (mouseInfo == null) { + return null; + } + Point2D canvasPosition = mouseInfo.canvasPosition; + Point2D transformed = null; + try { + transformed = drawingTransform.inverseTransform(canvasPosition, new Point2D.Double()); + } catch (NoninvertibleTransformException e) { + LOGGER.error("Could not create inverse transform of {}", drawingTransform, e); + throw new InvocationTargetException(e); + } + double x = ModelledCRS.xToLongitude(transformed.getX()); + double y = ModelledCRS.yToLatitude(-transformed.getY()); + return new Point2D.Double(x, y); + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/CopyDistrictVertexHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/CopyDistrictVertexHandler.java new file mode 100644 index 00000000..9365a8c0 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/CopyDistrictVertexHandler.java @@ -0,0 +1,62 @@ +package org.simantics.district.network.ui.contributions; + +import java.util.List; + +import javax.inject.Named; + +import org.eclipse.core.commands.ParameterizedCommand; +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.viewers.ISelection; +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.SelectionHints; +import org.simantics.db.request.Read; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.utils.ui.ISelectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CopyDistrictVertexHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(CopyDistrictVertexHandler.class); + static List elements; + static boolean cut = true; + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + List elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); + if (elements.size() != 1) + return false; + try { + return Simantics.getSession().syncRequest(new Read() { + + @Override + public Boolean perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + for (Resource selection : elements) { + if (!graph.isInstanceOf(selection, DN.Element)) { + return false; + } + } + return true; + } + }); + } catch (DatabaseException e) { + LOGGER.error("Could not evaluate if mapping can be changed for selection {}", elements, e); + return false; + } + } + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_SELECTION) Object selection, ParameterizedCommand command) { + final List elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); + // we store these to a static variable for pasting.. maybe not the best solution + CopyDistrictVertexHandler.elements = elements; + Object cut = command.getParameterMap().get("org.simantics.district.network.ui.commandparameter.0"); + CopyDistrictVertexHandler.cut = cut != null; + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/NetworkElementActionMenuContribution.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/NetworkElementActionMenuContribution.java index ec03ab87..e29d11a2 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/NetworkElementActionMenuContribution.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/NetworkElementActionMenuContribution.java @@ -14,6 +14,7 @@ import org.eclipse.e4.ui.model.application.ui.menu.MMenu; import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement; import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory; import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.swt.widgets.Display; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; @@ -30,10 +31,12 @@ import org.simantics.db.layer0.variable.Variables; import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; +import org.simantics.modeling.ui.scl.SCLScripts; import org.simantics.scl.compiler.top.ValueNotFound; import org.simantics.scl.osgi.SCLOsgi; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.function.Function; +import org.simantics.scl.runtime.reporting.SCLReportingHandler; import org.simantics.scl.runtime.tuple.Tuple2; import org.simantics.utils.ui.ISelectionUtils; import org.slf4j.Logger; @@ -44,7 +47,7 @@ public class NetworkElementActionMenuContribution { static private Logger LOGGER = LoggerFactory.getLogger(NetworkElementActionMenuContribution.class); @AboutToShow - public void aboutToShow(@Named(IServiceConstants.ACTIVE_SELECTION) Object selection, List items) { + public void aboutToShow(@Named(IServiceConstants.ACTIVE_SELECTION) Object selection, Display display, List items) { final List vertices = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); if (vertices.size() != 1) return; @@ -67,7 +70,7 @@ public class NetworkElementActionMenuContribution { List children = subMenu.getChildren(); subMenu.setLabel("Component Actions"); items.add(subMenu); - + for (Tuple2 action : actions) { String label = (String) action.c0; @SuppressWarnings("rawtypes") @@ -91,6 +94,48 @@ public class NetworkElementActionMenuContribution { @Execute public void execute() { + // Handler that only opens the SCL script console on demand + SCLReportingHandler handler = new SCLReportingHandler() { + + private SCLReportingHandler handler; + + // Get a handler for the SCL script console view + private SCLReportingHandler getHandler() { + if (handler == null) { + handler = SCLScripts.getOrCreateConsoleCommandSession().second; + } + return handler; + } + + @Override + public void printError(String error) { + display.asyncExec(() -> { + getHandler().printError(error); + }); + } + + @Override + public void printCommand(String command) { + display.asyncExec(() -> { + getHandler().printCommand(command); + }); + } + + @Override + public void print(String text) { + display.asyncExec(() -> { + getHandler().print(text); + }); + } + + @Override + public void didWork(double amount) { + display.asyncExec(() -> { + getHandler().didWork(amount); + }); + } + }; + Simantics.getSession().asyncRequest(new WriteRequest() { @SuppressWarnings("unchecked") @Override @@ -105,9 +150,18 @@ public class NetworkElementActionMenuContribution { graph.markUndoPoint(); Layer0Utils.addCommentMetadata(graph, label + " for " + v.getName(graph)); - Simantics.applySCLWrite(graph, function, v); + SCLContext context = SCLContext.getCurrent(); + Object oldHandler = context.put(SCLReportingHandler.REPORTING_HANDLER, handler); + try { + Simantics.applySCLWrite(graph, function, v); + } + finally { + context.put(SCLReportingHandler.REPORTING_HANDLER, oldHandler); + } } - }, (DatabaseException e) -> LOGGER.error("Running command " + label + " for " + vertex + " failed", e)); + }, (DatabaseException e) -> { + if (e != null) LOGGER.error("Running command " + label + " for " + vertex + " failed", e); + }); } }; diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/PasteDistrictVertexHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/PasteDistrictVertexHandler.java new file mode 100644 index 00000000..7fadac3f --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/PasteDistrictVertexHandler.java @@ -0,0 +1,364 @@ +package org.simantics.district.network.ui.contributions; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javax.inject.Named; + +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.viewers.ISelection; +import org.simantics.Simantics; +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Statement; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.SelectionHints; +import org.simantics.db.layer0.util.RemoverUtil; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.db.request.Read; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.utils.threads.ThreadUtils; +import org.simantics.utils.ui.ISelectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PasteDistrictVertexHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(PasteDistrictVertexHandler.class); + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + List elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); + if (elements.size() < 1) + return false; + try { + return Simantics.getSession().syncRequest(new Read() { + + @Override + public Boolean perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + for (Resource selection : elements) { + if (!graph.isInstanceOf(selection, DN.Element)) { + return false; + } + } + return true; + } + }); + } catch (DatabaseException e) { + LOGGER.error("Could not evaluate if mapping can be changed for selection {}", elements, e); + return false; + } + } + + private static Resource doCopy(WriteGraph graph, Resource diagram, Resource target, Resource source) throws DatabaseException { + DiagramResource DIA = DiagramResource.getInstance(graph); + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + double[] location = graph.getRelatedValue(target, DIA.HasLocation, Bindings.DOUBLE_ARRAY); + double elevation = graph.getRelatedValue(target, DN.Vertex_HasElevation, Bindings.DOUBLE); + Resource vertex = DistrictNetworkUtil.createVertex(graph, diagram, location, elevation); + copySourceToTarget(graph, source, vertex); + return vertex; + } + + private static void copySourceToTarget(WriteGraph graph, Resource source, Resource target) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + graph.deny(target, DN.HasMapping); + + Collection statements = graph.getStatements(source, L0.HasProperty); + Collection assertedStatements = graph.getAssertedStatements(source, L0.HasProperty); + statements.removeAll(assertedStatements); + for (Statement stm : statements) { + if (!graph.hasStatement(target, stm.getPredicate())) { + graph.claim(target, stm.getPredicate(), stm.getObject()); + } + } + } + + private static void copyVertexInverses(WriteGraph graph, Resource sourceElement, Resource targetElement) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Collection sourceStartVertex_inverse = graph.getObjects(sourceElement, DN.HasStartVertex_Inverse); + Collection sourceEndVertex_inverse = graph.getObjects(sourceElement, DN.HasEndVertex_Inverse); + Collection targetStartVertex_inverse = graph.getObjects(targetElement, DN.HasStartVertex_Inverse); + Collection targetEndVertex_inverse = graph.getObjects(targetElement, DN.HasEndVertex_Inverse); + graph.deny(sourceElement, DN.HasStartVertex_Inverse); + graph.deny(sourceElement, DN.HasEndVertex_Inverse); + graph.deny(targetElement, DN.HasStartVertex_Inverse); + graph.deny(targetElement, DN.HasEndVertex_Inverse); + for (Resource startVertexInverse : sourceStartVertex_inverse) { + graph.claim(targetElement, DN.HasStartVertex_Inverse, startVertexInverse); + } + for (Resource targetVertexInverse : targetStartVertex_inverse) { + graph.claim(sourceElement, DN.HasStartVertex_Inverse, targetVertexInverse); + } + for (Resource endVertexInverse : sourceEndVertex_inverse) { + graph.claim(targetElement, DN.HasEndVertex_Inverse, endVertexInverse); + } + for (Resource targetVertexInverse : targetEndVertex_inverse) { + graph.claim(sourceElement, DN.HasEndVertex_Inverse, targetVertexInverse); + } + } + + private static void deleteExistingTarget(WriteGraph graph, Resource target) throws DatabaseException { + RemoverUtil.remove(graph, target); + } + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_SELECTION) Object selection) { + final List elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); + final List copiedElements = CopyDistrictVertexHandler.elements; // TODO: this could be implemented more nicely with clipboard ? + boolean cut = CopyDistrictVertexHandler.cut; + + Resource targetElement = elements.get(0); + Resource sourceElement = copiedElements.get(0); + + try { + Map sourceCopyAttributes = new HashMap<>(); + Map targetCopyAttributes = new HashMap<>(); + Simantics.getSession().syncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + + Resource sourceMappedElement = graph.getPossibleObject(sourceElement, DN.MappedComponent); + sourceCopyAttributes.putAll(copyAttributes(graph, sourceElement, sourceMappedElement)); + Resource targetMappedElement = graph.getPossibleObject(targetElement, DN.MappedComponent); + targetCopyAttributes.putAll(copyAttributes(graph, targetElement, targetMappedElement)); + if (sourceMappedElement != null && cut) + RemoverUtil.remove(graph, sourceMappedElement); + if (targetMappedElement != null) + RemoverUtil.remove(graph, targetMappedElement); + } + }); + ThreadUtils.getNonBlockingWorkExecutor().schedule(() -> { + try { + Simantics.getSession().syncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + + Resource diagram = graph.getSingleObject(sourceElement, L0.PartOf); + Resource newTarget = doCopy(graph, diagram, targetElement, sourceElement); + if (cut) { + Resource newSource = doCopy(graph, diagram, sourceElement, targetElement); + for (Map.Entry attrValue : targetCopyAttributes.entrySet()) { + graph.claimLiteral(newSource, attrValue.getKey(), attrValue.getValue()); + } + copyVertexInverses(graph, sourceElement, newSource); + deleteExistingTarget(graph, sourceElement); + } + for (Map.Entry attrValue : sourceCopyAttributes.entrySet()) { + graph.claimLiteral(newTarget, attrValue.getKey(), attrValue.getValue()); + } + copyVertexInverses(graph, targetElement, newTarget); + deleteExistingTarget(graph, targetElement); + } + }); + } catch (DatabaseException e) { + e.printStackTrace(); + } + }, 500, TimeUnit.MILLISECONDS); + +// ThreadUtils.getNonBlockingWorkExecutor().schedule(() -> { +// Simantics.getSession().asyncRequest(new WriteRequest() { +// +// @Override +// public void perform(WriteGraph graph) throws DatabaseException { +// DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); +// //copyMapping(graph, DN, sourceElement, targetElement); +// copyLocation(graph, DN, sourceElement, targetElement, cut); +// copyVertexInverses(graph, DN, sourceElement, targetElement, cut); +// copyElevation(graph, DN, sourceElement, targetElement, cut); +// } +// +// private void copyMapping(WriteGraph graph, DistrictNetworkResource DN, Resource sourceElement, Resource targetElement) throws DatabaseException { +// Resource sourceMapping = graph.getSingleObject(sourceElement, DN.Mapping); +// Resource targetMapping = graph.getSingleObject(targetElement, DN.Mapping); +// if (cut) { +// graph.deny(sourceElement, DN.Mapping); +// graph.claim(sourceElement, DN.Mapping, targetMapping); +// } +// graph.deny(targetElement, DN.Mapping); +// graph.claim(targetElement, DN.Mapping, sourceMapping); +// } +// +// private void copyElevation(WriteGraph graph, DistrictNetworkResource DN, Resource sourceElement, Resource targetElement, boolean cut) throws DatabaseException { +// double sourceElevation = graph.getRelatedValue(sourceElement, DN.Vertex_HasElevation, Bindings.DOUBLE); +// double targetElevation = graph.getRelatedValue(targetElement, DN.Vertex_HasElevation, Bindings.DOUBLE); +// if (cut) { +// graph.deny(sourceElement, DN.Vertex_HasElevation); +// graph.claimLiteral(sourceElement, DN.Vertex_HasElevation, targetElevation, Bindings.DOUBLE); +// } +// graph.deny(targetElement, DN.Vertex_HasElevation); +// graph.claimLiteral(targetElement, DN.Vertex_HasElevation, sourceElevation, Bindings.DOUBLE); +// } +// +// private void copyLocation(WriteGraph graph, DistrictNetworkResource DN, Resource sourceElement, Resource targetElement, boolean cut) throws DatabaseException { +// DiagramResource DIA = DiagramResource.getInstance(graph); +// double[] sourceLocation = graph.getRelatedValue(sourceElement, DIA.HasLocation, Bindings.DOUBLE_ARRAY); +// double[] targetLocation = graph.getRelatedValue(targetElement, DIA.HasLocation, Bindings.DOUBLE_ARRAY); +// if (cut) { +// graph.deny(sourceElement, DIA.HasLocation); +// graph.claimLiteral(sourceElement, DIA.HasLocation, targetLocation, Bindings.DOUBLE_ARRAY); +// } +// graph.deny(targetElement, DIA.HasLocation); +// graph.claimLiteral(targetElement, DIA.HasLocation, sourceLocation, Bindings.DOUBLE_ARRAY); +// } +// + +// +// +// }); +// }, 500, TimeUnit.MILLISECONDS); + + } catch (DatabaseException e) { + e.printStackTrace(); + } + + } + + private static Map copyAttributes(WriteGraph graph, Resource sourceElement, Resource mappedElement) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + + Resource mapping = graph.getSingleObject(sourceElement, DN.HasMapping); + Collection statements = graph.getStatements(mapping, L0.HasProperty); + Collection mappingAttributeStatements = statements.stream().filter(stm -> { + try { + Resource predicate = stm.getPredicate(); + Resource hasDomain = graph.getPossibleObject(predicate, L0.HasDomain); + if (hasDomain != null && hasDomain.equals(DN.Mapping_VertexMapping)) { + // filter out x and y and z + Resource vertexAttribute = attributeMappingToVertexAttribute(graph, DN, predicate); + return vertexAttribute != null; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; + }).collect(Collectors.toList()); + Resource component = graph.getPossibleObject(mappedElement, ModelingResources.getInstance(graph).ElementToComponent); + Variable variable = Variables.getVariable(graph, component); + Map predValues = new HashMap<>(); + for (Statement stm : mappingAttributeStatements) { + String propertyName = graph.getPossibleValue(stm.getObject()); + if (propertyName != null) { + Object propertyValue = variable.getPossiblePropertyValue(graph, propertyName); + if (propertyValue != null) { + predValues.put(attributeMappingToVertexAttribute(graph, DN, stm.getPredicate()), propertyValue); + } else { + System.err.println("no property value for " + variable.getURI(graph) + " and property " + propertyName); + } + } else { + System.err.println("stm.getObject() is not string " + stm.getObject()); + } + } + return predValues; + } + + private static Resource attributeMappingToVertexAttribute(ReadGraph graph, DistrictNetworkResource DN, Resource attribute) throws DatabaseException { + Resource attr = null; + if (attribute.equals(DN.Mapping_VertexMapping_ElevationAttribute)) + attr = null; // ignore elevation as well + else if (attribute.equals(DN.Mapping_VertexMapping_AddressAttribute)) + attr = DN.Vertex_HasAddress; + else if (attribute.equals(DN.Mapping_VertexMapping_DeltaPressureAttribute)) + attr = DN.Vertex_HasDeltaPressure; + else if (attribute.equals(DN.Mapping_VertexMapping_DeltaTemperatureAttribute)) + attr = DN.Vertex_HasDeltaTemperature; + else if (attribute.equals(DN.Mapping_VertexMapping_dpAttribute)) + attr = DN.Vertex_HasDeltaPressure; + else if (attribute.equals(DN.Mapping_VertexMapping_dtAttribute)) + attr = DN.Vertex_HasDeltaTemperature; + else if (attribute.equals(DN.Mapping_VertexMapping_FlowAreaAttribute)) + attr = DN.Vertex_HasFlowArea; + else if (attribute.equals(DN.Mapping_VertexMapping_HeatLoadDsAttribute)) + attr = DN.Vertex_HasHeatLoadDs; + else if (attribute.equals(DN.Mapping_VertexMapping_HeatPowerAttribute)) + attr = DN.Vertex_HasHeatPower; + else if (attribute.equals(DN.Mapping_VertexMapping_MassFlowAttribute)) + attr = DN.Vertex_HasMassFlow; + else if (attribute.equals(DN.Mapping_VertexMapping_MaximumHeadMAttribute)) + attr = DN.Vertex_HasMaximumHeadM; + else if (attribute.equals(DN.Mapping_VertexMapping_NominalFlowAttribute)) + attr = DN.Vertex_HasNominalFlow; + else if (attribute.equals(DN.Mapping_VertexMapping_NominalHeadBAttribute)) + attr = DN.Vertex_HasNominalHeadB_Inverse; + else if (attribute.equals(DN.Mapping_VertexMapping_NominalHeadMAttribute)) + attr = DN.Vertex_HasNominalHeadM; + else if (attribute.equals(DN.Mapping_VertexMapping_NominalMassFlowAttribute)) + attr = DN.Vertex_HasNominalFlow; + else if (attribute.equals(DN.Mapping_VertexMapping_NominalPressureLossAttribute)) + attr = DN.Vertex_HasNominalPressureLoss; + else if (attribute.equals(DN.Mapping_VertexMapping_ReturnPressureAttribute)) + attr = DN.Vertex_HasReturnPressure; + else if (attribute.equals(DN.Mapping_VertexMapping_ReturnTemperatureAttribute)) + attr = DN.Vertex_HasReturnTemperature; + else if (attribute.equals(DN.Mapping_VertexMapping_SupplyPressureAttribute)) + attr = DN.Vertex_HasSupplyPressure; + else if (attribute.equals(DN.Mapping_VertexMapping_SupplyTemperatureAttribute)) + attr = DN.Vertex_HasSupplyTemperature; + else if (attribute.equals(DN.Mapping_VertexMapping_ValvePositionAttribute)) + attr = DN.Vertex_HasValvePosition; + else if (attribute.equals(DN.Mapping_VertexMapping_VelocityAttribute)) + attr = DN.Vertex_HasVelocity; + else if (attribute.equals(DN.Mapping_VertexMapping_VolFlowAttribute)) + attr = DN.Vertex_HasVolFlow; + else if (attribute.equals(DN.Mapping_VertexMapping_PumpInReturnLineAttribute)) + attr = DN.Vertex_HasPumpInReturnLine; + else if (attribute.equals(DN.Mapping_VertexMapping_HeadPumpMaximumAttribute)) + attr = DN.Vertex_HasHeadPumpMaximum; + else if (attribute.equals(DN.Mapping_VertexMapping_HeadPumpNominalAttribute)) + attr = DN.Vertex_HasHeadPumpNominal; + else if (attribute.equals(DN.Mapping_VertexMapping_FrequencyConverterControlledAttribute)) + attr = DN.Vertex_HasFrequencyConverterControlled; + else if (attribute.equals(DN.Mapping_VertexMapping_InternalValveMeasurementAttribute)) + attr = DN.Vertex_HasInternalValveMeasurement; + else if (attribute.equals(DN.Mapping_VertexMapping_PumpMassFlowNominalAttribute)) + attr = DN.Vertex_HasPumpMassFlowNominal; + else if (attribute.equals(DN.Mapping_VertexMapping_PumpMeMaxAttribute)) + attr = DN.Vertex_HasPumpMeMax; + else if (attribute.equals(DN.Mapping_VertexMapping_PumpMeMinAttribute)) + attr = DN.Vertex_HasPumpMeMin; + else if (attribute.equals(DN.Mapping_VertexMapping_PumpSpeedMaxAttribute)) + attr = DN.Vertex_HasPumpSpeedMax; + else if (attribute.equals(DN.Mapping_VertexMapping_PumpSpeedMinAttribute)) + attr = DN.Vertex_HasPumpSpeedMin; + else if (attribute.equals(DN.Mapping_VertexMapping_ValveReturnLineAttribute)) + attr = DN.Vertex_HasValveReturnLine; + else if (attribute.equals(DN.Mapping_VertexMapping_ValveMeMaxAttribute)) + attr = DN.Vertex_HasValveMeMax; + else if (attribute.equals(DN.Mapping_VertexMapping_ValveMeMinAttribute)) + attr = DN.Vertex_HasValveMeMin; + else if (attribute.equals(DN.Mapping_VertexMapping_ValveMinPositionAttribute)) + attr = DN.Vertex_HasValveMinPosition; + else if (attribute.equals(DN.Mapping_VertexMapping_ValveOutletModeAttribute)) + attr = DN.Vertex_HasValveOutletMode; + else if (attribute.equals(DN.Mapping_VertexMapping_ValvePressLossNominalAttribute)) + attr = DN.Vertex_HasValvePressLossNominal; + else if (attribute.equals(DN.Mapping_VertexMapping_OpeningTimeAttribute)) + attr = DN.Vertex_HasOpeningTime; + else if (attribute.equals(DN.Mapping_VertexMapping_XAttribute)) + attr = null; // ignore this! + else if (attribute.equals(DN.Mapping_VertexMapping_YAttribute)) + attr = null; // ignore this! + return attr; + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/function/Functions.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/function/Functions.java index 60dd3e22..cc05a173 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/function/Functions.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/function/Functions.java @@ -40,6 +40,7 @@ import org.simantics.db.common.request.IndexRoot; import org.simantics.db.common.request.ObjectsWithType; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.common.request.WriteResultRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.RuntimeDatabaseException; import org.simantics.db.exception.ServiceException; @@ -49,6 +50,7 @@ import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.db.layer0.variable.Variables.Role; import org.simantics.db.procedure.Procedure; +import org.simantics.district.network.DistrictNetworkUtil; import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; @@ -93,14 +95,25 @@ public class Functions { @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object defaultVertexMappingModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { - System.out.println(graph.getURI(resource)); - System.out.println(context.getURI(graph)); - Resource diagram = resolveElement(graph, context); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); return baseMappingModifier(graph, diagram, DN.VertexDefaultMapping, DN.Mapping_VertexMapping, context); } - + + @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") + public static Object rightClickVertexMappingModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { + Resource diagram = resolveElement(graph, context); + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + return baseMappingModifier(graph, diagram, DN.RightClickDefaultMapping, DN.Mapping_VertexMapping, context); + } + + @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") + public static Object leftClickVertexMappingModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { + Resource diagram = resolveElement(graph, context); + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + return baseMappingModifier(graph, diagram, DN.LeftClickDefaultMapping, DN.Mapping_VertexMapping, context); + } + @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object mappingModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { @@ -183,8 +196,6 @@ public class Functions { @SCLValue(type = "ReadGraph -> Resource -> a -> b") public static Object enumerationValues(ReadGraph graph, Resource resource, Object context) throws DatabaseException { Variable var = (Variable) context; - System.out.println(graph.getURI(resource)); - System.out.println(var.getURI(graph)); return Collections.emptyList(); } @@ -217,8 +228,10 @@ public class Functions { private static class DefaultMappingsDialog extends SelectionStatusDialog { private Combo vertexMappingCombo; + private Combo rightClickMappingCombo; + private Combo leftClickMappingCombo; private Combo edgeMappingCombo; - private Combo crsCombo; + //private Combo crsCombo; private Composite composite; private Resource configuration; @@ -229,10 +242,12 @@ public class Functions { private Resource defaultVertexMapping; private Resource defaultEdgeMapping; - private Resource defaultCRS; + private Resource rightClickVertexMapping; + private Resource leftClickVertexMapping; + //private Resource defaultCRS; - private Combo compositeMappingCombo; - private Combo componentMappingCombo; + //private Combo compositeMappingCombo; + //private Combo componentMappingCombo; protected DefaultMappingsDialog(Shell parentShell, Resource configuration) { super(parentShell); @@ -244,6 +259,14 @@ public class Functions { return defaultVertexMapping; } + public Resource getRightClickVertexMapping() { + return rightClickVertexMapping; + } + + public Resource getLeftClickVertexMapping() { + return leftClickVertexMapping; + } + public Resource getDefaultEdgeMapping() { return defaultEdgeMapping; } @@ -253,8 +276,8 @@ public class Functions { composite = (Composite) super.createDialogArea(parent); createMappingsGroup(composite); - createExistingCompositeGroup(composite); - createCRSSettingsGroup(composite); + //createExistingCompositeGroup(composite); + //createCRSSettingsGroup(composite); // compute default values Simantics.getSession().asyncRequest(new ReadRequest() { @@ -272,18 +295,22 @@ public class Functions { composite.getDisplay().asyncExec(() -> { vertexMappingCombo.setItems(vertexMappings.keySet().toArray(new String[vertexMappings.size()])); + rightClickMappingCombo.setItems(vertexMappings.keySet().toArray(new String[vertexMappings.size()])); + leftClickMappingCombo.setItems(vertexMappings.keySet().toArray(new String[vertexMappings.size()])); edgeMappingCombo.setItems(edgeMappings.keySet().toArray(new String[edgeMappings.size()])); - crsCombo.setItems(crss.keySet().toArray(new String[crss.size()])); + //crsCombo.setItems(crss.keySet().toArray(new String[crss.size()])); - compositeMappingCombo.setItems(composites.keySet().toArray(new String[composites.size()])); + //compositeMappingCombo.setItems(composites.keySet().toArray(new String[composites.size()])); vertexMappingCombo.select(0); + rightClickMappingCombo.select(0); + leftClickMappingCombo.select(0); edgeMappingCombo.select(0); - crsCombo.select(0); + //crsCombo.select(0); - if (!composites.isEmpty()) - compositeMappingCombo.select(0); + //if (!composites.isEmpty()) + // compositeMappingCombo.select(0); }); } @@ -329,7 +356,19 @@ public class Functions { vertexMappingCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); GridDataFactory.fillDefaults().grab(true, false).applyTo(vertexMappingCombo); - + + Label rightClickMappingLabel = new Label(cmposite, SWT.NONE); + rightClickMappingLabel.setText("Default right click mapping"); + + rightClickMappingCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).applyTo(vertexMappingCombo); + + Label leftClickMappingLabel = new Label(cmposite, SWT.NONE); + leftClickMappingLabel.setText("Default left click mapping"); + + leftClickMappingCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).applyTo(vertexMappingCombo); + Label edgeMappingLabel = new Label(cmposite, SWT.NONE); edgeMappingLabel.setText("Default edge mapping"); @@ -351,22 +390,22 @@ public class Functions { Label compositeMappingLabel = new Label(cmposite, SWT.NONE); compositeMappingLabel.setText("Select composite"); - compositeMappingCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); - GridDataFactory.fillDefaults().grab(true, false).applyTo(compositeMappingCombo); - compositeMappingCombo.addSelectionListener(new SelectionAdapter() { - - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - recalculateMappapleComponents(); - } - }); - - Label compojnentMappingLabel = new Label(cmposite, SWT.NONE); - compojnentMappingLabel.setText("Select component"); +// compositeMappingCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); +// GridDataFactory.fillDefaults().grab(true, false).applyTo(compositeMappingCombo); +// compositeMappingCombo.addSelectionListener(new SelectionAdapter() { +// +// @Override +// public void widgetSelected(SelectionEvent e) { +// super.widgetSelected(e); +// recalculateMappapleComponents(); +// } +// }); - componentMappingCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); - GridDataFactory.fillDefaults().grab(true, false).applyTo(componentMappingCombo); +// Label compojnentMappingLabel = new Label(cmposite, SWT.NONE); +// compojnentMappingLabel.setText("Select component"); +// +// componentMappingCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); +// GridDataFactory.fillDefaults().grab(true, false).applyTo(componentMappingCombo); } protected void recalculateMappapleComponents() { @@ -397,9 +436,9 @@ public class Functions { Label vertexMappingLabel = new Label(cmposite, SWT.NONE); vertexMappingLabel.setText("Default CRS"); - crsCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); - GridData textData = new GridData(SWT.FILL, SWT.CENTER, true, false); - crsCombo.setLayoutData(textData); + //crsCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); + //GridData textData = new GridData(SWT.FILL, SWT.CENTER, true, false); + //crsCombo.setLayoutData(textData); } @@ -407,11 +446,13 @@ public class Functions { protected void computeResult() { defaultVertexMapping = vertexMappings.get(vertexMappingCombo.getItem(vertexMappingCombo.getSelectionIndex())); defaultEdgeMapping = edgeMappings.get(edgeMappingCombo.getItem(edgeMappingCombo.getSelectionIndex())); - defaultCRS = crss.get(crsCombo.getItem(crsCombo.getSelectionIndex())); + rightClickVertexMapping = vertexMappings.get(rightClickMappingCombo.getItem(rightClickMappingCombo.getSelectionIndex())); + leftClickVertexMapping = vertexMappings.get(leftClickMappingCombo.getItem(leftClickMappingCombo.getSelectionIndex())); + //defaultCRS = crss.get(crsCombo.getItem(crsCombo.getSelectionIndex())); } public Resource getCRS() { - return defaultCRS; + return crss.get("EPSG_4326"); // this is only supported } } @@ -424,36 +465,34 @@ public class Functions { procedure.execute(null); return; } - Simantics.getSession().asyncRequest( - NewCompositeActionFactory.createCompositeRequest(target, defaultName, compositeType), - new Procedure() { - @Override - public void execute(Resource composite) { - Simantics.getSession().asyncRequest(new WriteRequest() { - - @Override - public void perform(WriteGraph graph) throws DatabaseException { - DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); - Resource diagram = graph.getSingleObject(composite, ModelingResources.getInstance(graph).CompositeToDiagram); - graph.claim(diagram, DN.EdgeDefaultMapping, dialog.getDefaultEdgeMapping()); - graph.claim(diagram, DN.VertexDefaultMapping, dialog.getDefaultVertexMapping()); - graph.claim(diagram, DN.HasSpatialRefSystem, dialog.getCRS()); - - // Generated name prefix from composite name - String compositeName = graph.getRelatedValue2(composite, Layer0.getInstance(graph).HasName, Bindings.STRING); - graph.claimLiteral(diagram, Layer0X.getInstance(graph).HasGeneratedNamePrefix, "N" + compositeName.substring(compositeName.length() - 1, compositeName.length())); - } - }); - DefaultActions.asyncPerformDefaultAction(Simantics.getSession(), composite, false, false, true); - procedure.execute(composite); - } - - @Override - public void exception(Throwable t) { - LOGGER.error("Failed to create composite, see exception for details.", t); - procedure.exception(t); - } - }); + Simantics.getSession().asyncRequest(new WriteResultRequest() { + + @Override + public Resource perform(WriteGraph graph) throws DatabaseException { + return DistrictNetworkUtil.createNetworkDiagram(graph, target, compositeType, defaultName, + dialog.getDefaultEdgeMapping(), + dialog.getDefaultVertexMapping(), + dialog.getRightClickVertexMapping(), + dialog.getLeftClickVertexMapping(), + dialog.getCRS() + ); + } + + + }, new Procedure() { + + @Override + public void execute(Resource composite) { + DefaultActions.asyncPerformDefaultAction(Simantics.getSession(), composite, false, false, true); + procedure.execute(composite); + } + + @Override + public void exception(Throwable t) { + LOGGER.error("Failed to create composite, see exception for details.", t); + procedure.exception(t); + } + }); } public static Collection getDistrictDiagrams(ReadGraph graph) throws DatabaseException { diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/internal/Activator.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/internal/Activator.java index 9c56d543..72d7cf80 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/internal/Activator.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/internal/Activator.java @@ -1,13 +1,80 @@ package org.simantics.district.network.ui.internal; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.core.services.events.IEventBroker; +import org.eclipse.e4.ui.internal.workbench.E4Workbench; +import org.eclipse.swt.widgets.Display; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventHandler; import org.osgi.util.tracker.ServiceTracker; +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.ui.DistrictNetworkUIUtil; import org.simantics.district.network.ui.breakdown.SubgraphProvider; import org.simantics.district.route.RouteService; +import org.simantics.modeling.ModelingResources; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Activator extends AbstractUIPlugin { + private static final class HighlightSelectionEventHandler implements EventHandler { + @Override + public void handleEvent(Event event) { + Object data = event.getProperty("org.eclipse.e4.data"); + if (data instanceof Variable[]) { + Variable[] propertyTableComponents = (Variable[]) data; + try { + // convert components to dh components + List dnElements = Simantics.getSession().syncRequest(new UniqueRead>() { + + @Override + public List perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + ModelingResources MOD = ModelingResources.getInstance(graph); + return Arrays.asList(propertyTableComponents).stream().map(var -> { + try { + Resource res = var.getRepresents(graph); + Resource element = graph.getSingleObject(res, MOD.ComponentToElement); + return graph.getSingleObject(element, DN.MappedFromElement); + } catch (Exception e) { + String varURI = var.toString(); + try { + varURI = var.getURI(graph); + } catch (DatabaseException ex) { + LOGGER.error("Unable to resole uri for {}", var, ex); + } + LOGGER.error("Could not get dn element for {}", varURI, e); + return null; + } + }).collect(Collectors.toList()); + } + }); + DistrictNetworkUIUtil.openDNDiagramWithSelection(Display.getDefault(), dnElements); + } catch (DatabaseException e) { + LOGGER.error("Could not convert variables to dn elements", propertyTableComponents, e); + } + } + } + } + + // This is hard coded for now.. the same value is used for multipropertytable to + // send the events we are here listening and interested in + private static final String PROPERTY_TABLE_HIGHLIGHT = "PROPERTY_TABLE_HIGHLIGHT"; + + private static final Logger LOGGER = LoggerFactory.getLogger(Activator.class); + public static final String PLUGIN_ID = "org.simantics.district.network.ui"; private static Activator instance; @@ -16,6 +83,8 @@ public class Activator extends AbstractUIPlugin { private ServiceTracker subgraphProviderTracker; private ServiceTracker routeServiceTracker; + private HighlightSelectionEventHandler eventHandler; + @Override public void start(BundleContext context) throws Exception { Activator.instance = this; @@ -25,13 +94,15 @@ public class Activator extends AbstractUIPlugin { subgraphProviderTracker.open(); routeServiceTracker = new ServiceTracker<>(context, RouteService.class.getName(), null); routeServiceTracker.open(); + + initializeEventListener(); } @Override public void stop(BundleContext context) throws Exception { subgraphProviderTracker.close(); routeServiceTracker.close(); - + deinitializeEventListener(); Activator.instance = null; Activator.context = null; } @@ -52,4 +123,26 @@ public class Activator extends AbstractUIPlugin { return routeServiceTracker.getService(); } + private void initializeEventListener() { + @SuppressWarnings("restriction") + IEclipseContext contxt = E4Workbench.getServiceContext(); + IEventBroker broker = contxt.get(IEventBroker.class); + eventHandler = new HighlightSelectionEventHandler(); + if (broker != null) { + broker.subscribe(PROPERTY_TABLE_HIGHLIGHT, eventHandler); + } else { + LOGGER.info("EventBroker is somehow null for {}", this); + } + } + + private void deinitializeEventListener() { + @SuppressWarnings("restriction") + IEclipseContext contxt = E4Workbench.getServiceContext(); + IEventBroker broker = contxt.get(IEventBroker.class); + if (broker != null) { + broker.unsubscribe(eventHandler); + } else { + LOGGER.info("EventBroker is somehow null for {}", this); + } + } } diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DeferredNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DeferredNode.java new file mode 100644 index 00000000..c2afab76 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DeferredNode.java @@ -0,0 +1,7 @@ +package org.simantics.district.network.ui.nodes; + +import java.awt.Graphics2D; + +public interface DeferredNode { + void renderDeferred(Graphics2D g); +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DeferredRenderingNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DeferredRenderingNode.java new file mode 100644 index 00000000..f2430718 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DeferredRenderingNode.java @@ -0,0 +1,43 @@ +package org.simantics.district.network.ui.nodes; + +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +import org.simantics.scenegraph.g2d.G2DNode; +import org.simantics.utils.datastructures.Pair; + +public class DeferredRenderingNode extends G2DNode { + + private static final long serialVersionUID = 1L; + + List> queue = new LinkedList<>(); + + @Override + public void render(Graphics2D g2d) { + AffineTransform currentTransform = g2d.getTransform(); + + ListIterator> listIterator = queue.listIterator(); + while (listIterator.hasNext()) { + Pair node = listIterator.next(); + listIterator.remove(); + + g2d.setTransform(node.first); + node.second.renderDeferred(g2d); + } + + g2d.setTransform(currentTransform); + } + + @Override + public Rectangle2D getBoundsInLocal() { + return null; + } + + public void deferNode(AffineTransform transform, DeferredNode node) { + queue.add(new Pair<>(transform, node)); + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkEdgeNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkEdgeNode.java index 21f3d741..a089ecaf 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkEdgeNode.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkEdgeNode.java @@ -16,6 +16,7 @@ import org.simantics.district.network.ui.adapters.DistrictNetworkEdgeElementFact import org.simantics.maps.MapScalingTransform; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.ISelectionPainterNode; +import org.simantics.scenegraph.INode.PropertySetter; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.nodes.SVGNode; @@ -50,6 +51,8 @@ public class DistrictNetworkEdgeNode extends G2DParentNode implements ISelection private transient Rectangle2D symbolRect; private transient AffineTransform symbolTransform; + private boolean hidden = false; + private Double arrowLength; private static double startX; @@ -70,93 +73,121 @@ public class DistrictNetworkEdgeNode extends G2DParentNode implements ISelection g2d.transform(getTransform()); } - Object aaHint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING); - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - Color oldColor = g2d.getColor(); - BasicStroke oldStroke = (BasicStroke) g2d.getStroke(); - - BasicStroke bs = null; + double scale = 1.0; if (scaleStroke) { - double scale = GeometryUtils.getScale(g2d.getTransform()); - scale = Math.max(10000, Math.min(scale, 50000)); - double str = stroke != null ? Math.abs(stroke) : 1.0; - bs = GeometryUtils.scaleStroke(STROKE, (float) (str / scale)); - } else { - bs = STROKE; + AffineTransform tr = g2d.getTransform(); + scale = DistrictNetworkNodeUtils.getScale(tr); } - int zoomLevel = MapScalingTransform.zoomLevel(ot); - path = calculatePath(edge, path, zoomLevel > 15); - - if (isSelected()) { - g2d.setColor(SELECTION_COLOR); - g2d.setStroke(GeometryUtils.scaleStroke(bs, 4f)); - g2d.draw(path); - } - - g2d.setColor(dynamicColor != null ? dynamicColor : color); - g2d.setStroke(bs); - g2d.draw(path); - - // Draw arrow - if (arrowLength != null) { - g2d.setColor(Color.BLACK); - g2d.setStroke(new BasicStroke(bs.getLineWidth(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - - double l = arrowLength; - double w = 2 * (double) bs.getLineWidth() * Math.signum(l); - if (Math.abs(w) > Math.abs(l)) w = l; - double offset = 2 * (double) bs.getLineWidth(); - - double centerX = (startX + endX) / 2, centerY = (startY + endY) / 2; - double deltaX = endX - startX, deltaY = endY - startY; - double length = Math.sqrt(deltaX * deltaX + deltaY * deltaY); - deltaX /= length; - deltaY /= length; - - double x0 = centerX - l/2 * deltaX + offset * deltaY; - double y0 = centerY - l/2 * deltaY - offset * deltaX; - double x1 = centerX + (l/2 - w) * deltaX + offset * deltaY; - double y1 = centerY + (l/2 - w) * deltaY - offset * deltaX; + + if (!hidden) { + Object aaHint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + Color oldColor = g2d.getColor(); + BasicStroke oldStroke = (BasicStroke) g2d.getStroke(); + + BasicStroke bs = null; + if (scaleStroke) { + bs = GeometryUtils.scaleStroke(STROKE, getStrokeWidth(scale)); + } else { + bs = STROKE; + } - g2d.draw(new Line2D.Double(x0, y0, x1, y1)); + int zoomLevel = MapScalingTransform.zoomLevel(ot); + path = calculatePath(edge, path, zoomLevel > 15); + + if (isSelected()) { + g2d.setColor(SELECTION_COLOR); + g2d.setStroke(GeometryUtils.scaleAndOffsetStrokeWidth(bs, 1.f, (float)(2 * STROKE.getLineWidth() / scale))); + g2d.draw(path); + } + + g2d.setColor(dynamicColor != null ? dynamicColor : color); + g2d.setStroke(bs); + g2d.draw(path); + + // Draw arrow + if (arrowLength != null) { + g2d.setColor(Color.BLACK); + float lw = STROKE.getLineWidth() / (float)scale; + g2d.setStroke(new BasicStroke(lw, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + + double l = arrowLength; + double w = 2 * (double) lw * Math.signum(l); + if (Math.abs(w) > Math.abs(l)) w = l; + double offset = 2 * (double) lw; + + double centerX = (startX + endX) / 2, centerY = (startY + endY) / 2; + double deltaX = endX - startX, deltaY = endY - startY; + double length = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + deltaX /= length; + deltaY /= length; + + double x0 = centerX - l/2 * deltaX + offset * deltaY; + double y0 = centerY - l/2 * deltaY - offset * deltaX; + double x1 = centerX + (l/2 - w) * deltaX + offset * deltaY; + double y1 = centerY + (l/2 - w) * deltaY - offset * deltaX; + + g2d.draw(new Line2D.Double(x0, y0, x1, y1)); + + Path2D path = new Path2D.Double(); + path.moveTo(x1 + w * deltaX, y1 + w * deltaY); + path.lineTo(x1 + w * deltaY, y1 - w * deltaX); + path.lineTo(x1 - w * deltaY, y1 + w * deltaX); + path.closePath(); + g2d.fill(path); + } - Path2D path = new Path2D.Double(); - path.moveTo(x1 + w * deltaX, y1 + w * deltaY); - path.lineTo(x1 + w * deltaY, y1 - w * deltaX); - path.lineTo(x1 - w * deltaY, y1 + w * deltaX); - path.closePath(); - g2d.fill(path); + // Reset + g2d.setStroke(oldStroke); + g2d.setColor(oldColor); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaHint); } - - // Reset - g2d.setStroke(oldStroke); - g2d.setColor(oldColor); - - // Render SVG symbol - double viewScaleRecip = 10; - if (scaleStroke) { - double scale = GeometryUtils.getScale(g2d.getTransform()); - scale = Math.max(10000, Math.min(scale, 50000)); - viewScaleRecip /= scale; - } - - Point2D p = getCenterPoint(); - symbolRect = DistrictNetworkNodeUtils.calculateDrawnGeometry(p, NORMAL, symbolRect, viewScaleRecip); - symbolTransform = DistrictNetworkNodeUtils.getTransformToRectangle(symbolRect, symbolTransform); - + for (INode nn : getNodes()) { G2DNode g2dNode = (G2DNode)nn; - g2dNode.setTransform(symbolTransform); + if (g2dNode instanceof SVGNode) { + // Render SVG symbol + double viewScaleRecip = 10; + if (scaleStroke) { + viewScaleRecip /= scale; + } + + Point2D p = getCenterPoint(); + symbolRect = DistrictNetworkNodeUtils.calculateDrawnGeometry(p, NORMAL, symbolRect, viewScaleRecip); + symbolTransform = DistrictNetworkNodeUtils.getTransformToRectangle(symbolRect, symbolTransform); + + g2dNode.setTransform(symbolTransform); + } g2dNode.render(g2d); } - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaHint); - if (ot != null) g2d.setTransform(ot); } + public float getStrokeWidth(AffineTransform tr, boolean selection) { + double scale = DistrictNetworkNodeUtils.getScale(tr); + float width = STROKE.getLineWidth() * getStrokeWidth(scale); + if (selection) width = width + (float) (2 * STROKE.getLineWidth() / scale); + return width; + } + + private float getStrokeWidth(double scale) { + if (scaleStroke) { + double str = stroke != null ? Math.abs(stroke) : 1.0; + float strokeWidth = (float) (str / scale); + return strokeWidth; + } + else { + return 1.f; + } + } + + public Path2D getPath() { + return path; + } + private Point2D getCenterPoint() { if (centerPoint == null) centerPoint = new Point2D.Double(); @@ -262,4 +293,9 @@ public class DistrictNetworkEdgeNode extends G2DParentNode implements ISelection ((SVGNode)nn).setData(value); } + + @PropertySetter(value = "hidden") + public void setHidden(Boolean value) { + this.hidden = value; + } } diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkHoverInfoNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkHoverInfoNode.java new file mode 100644 index 00000000..7a75ad5f --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkHoverInfoNode.java @@ -0,0 +1,144 @@ +package org.simantics.district.network.ui.nodes; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; + +import org.simantics.district.network.ui.styles.DistrictNetworkHoverInfoStyle; +import org.simantics.maps.MapScalingTransform; +import org.simantics.scenegraph.ParentNode; +import org.simantics.scenegraph.g2d.G2DNode; +import org.simantics.scenegraph.g2d.nodes.spatial.RTreeNode; +import org.simantics.scenegraph.utils.DPIUtil; +import org.simantics.scenegraph.utils.NodeUtil; +import org.simantics.scl.runtime.Lists; +import org.simantics.scl.runtime.tuple.Tuple3; + +public class DistrictNetworkHoverInfoNode extends G2DNode implements HoverSensitiveNode, DeferredNode { + + private static final Font FONT = new Font("Tahoma", Font.PLAIN, (int)(DPIUtil.upscale(9) * MapScalingTransform.getScaleY() + 0.5)); + + private static final long serialVersionUID = 1L; + + public static final String NODE_KEY = "DISTRICT_NETWORK_HOVER_INFO"; + + private static final int W1 = 50; + private static final int W2 = 30; + private static final int PAD = 5; + + private List labels; + + @SuppressWarnings("unused") + private Point2D origin; + + private boolean hover = false; + + private Point2D mousePosition; + + private static AtomicReference activeNode = new AtomicReference<>(); + + @Override + public void render(Graphics2D g) { + if (!hover || activeNode.get() != this) + return; + + ParentNode root = (ParentNode) NodeUtil.getNearestParentOfType(this, RTreeNode.class); + DeferredRenderingNode deferred = root != null ? (DeferredRenderingNode) root.getNode(DistrictNetworkHoverInfoStyle.HOVER_INFO_DEFERRED) : null; + if (deferred != null) + deferred.deferNode(g.getTransform(), this); + else + renderDeferred(g); + } + + @Override + public void renderDeferred(Graphics2D g) { + AffineTransform ot = g.getTransform(); + Font of = g.getFont(); + doRender(g); + g.setFont(of); + g.setTransform(ot); + } + + private void doRender(Graphics2D g) { + g.transform(MapScalingTransform.INVERSE); + g.translate(mousePosition.getX(), mousePosition.getY()); + //g.translate(origin.getX(), origin.getY()); + double scale = 1.5 / g.getTransform().getScaleX(); + g.scale(scale, scale); + + g.setFont(FONT); + double rowHeight = g.getFontMetrics().getHeight() * 1.1; + + // let's calculate the max width + Optional max = labels.stream().map(t -> g.getFontMetrics().stringWidth((String) t.c2)).max(Comparator.naturalOrder()); + int width = max.orElse(10); + g.setColor(Color.WHITE); + int totalHeight = (int)Math.round(rowHeight * labels.size()); + g.fillRect(-(W1 + PAD + W2 + 5), -(totalHeight + (int)Math.round(rowHeight)), (W1 + PAD + W2 + width + 10), totalHeight + 5); + + g.setColor(Color.BLACK); + + for (Tuple3 t : labels) { + g.translate(0.f, -rowHeight); + + if (t.c0 != null) { + g.drawString((String) t.c0, -(W1 + PAD + W2), 0.f); + } + + if (t.c1 != null) { + int width1 = g.getFontMetrics().stringWidth((String) t.c1); + g.drawString((String) t.c1, - width1, 0.f); + } + + if (t.c2 != null) { + g.drawString((String) t.c2, PAD, 0.f); + } + } + } + + @Override + public Rectangle2D getBoundsInLocal() { + return null; + } + + @SuppressWarnings("unchecked") + public void setLabels(List list) { + this.labels = Lists.reverse(list); + } + + public void setOrigin(Point2D origin) { + this.origin = origin; + } + + @Override + public boolean hover(boolean hover, boolean isConnectionTool) { + hover = hover && activeNode.updateAndGet(current -> current == null ? this : current) == this; + boolean changed = hover != this.hover; + this.hover = hover; + + if (changed) { + if (!hover) activeNode.updateAndGet(current -> current == this ? null : current); + repaint(); + } + + return changed; + } + + @Override + public void setMousePosition(Point2D p) { + mousePosition = p; + } + + @Override + public void delete() { + super.delete(); + activeNode.getAndUpdate(current -> current == this ? null : current); + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkNodeUtils.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkNodeUtils.java index c48693ac..0d300d05 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkNodeUtils.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkNodeUtils.java @@ -39,9 +39,13 @@ public class DistrictNetworkNodeUtils { public static double calculateScaleRecip(AffineTransform tr) { int zoomLevel = MapScalingTransform.zoomLevel(tr); - double scale = GeometryUtils.getScale(tr); - double sqrt = Math.sqrt(scale / zoomLevel); - double viewScaleRecip = (sqrt / zoomLevel); - return viewScaleRecip; + return 1.0 / (getScale(tr) * Math.sqrt(zoomLevel)); + } + + static double getScale(AffineTransform tr) { + double scale; + scale = GeometryUtils.getScale(tr); + scale = Math.max(4096, scale); //Math.min(scale, 32768)); + return scale; } } diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkStaticInfoNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkStaticInfoNode.java new file mode 100644 index 00000000..b86d0acb --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkStaticInfoNode.java @@ -0,0 +1,111 @@ +package org.simantics.district.network.ui.nodes; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.simantics.district.network.ui.styles.DistrictNetworkStaticInfoStyle; +import org.simantics.maps.MapScalingTransform; +import org.simantics.scenegraph.ParentNode; +import org.simantics.scenegraph.g2d.G2DNode; +import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.g2d.nodes.spatial.RTreeNode; +import org.simantics.scenegraph.utils.DPIUtil; +import org.simantics.scenegraph.utils.NodeUtil; + +public class DistrictNetworkStaticInfoNode extends G2DNode implements DeferredNode { + + private static final Font FONT = new Font("Tahoma", Font.PLAIN, (int)(DPIUtil.upscale(9) * MapScalingTransform.getScaleY() + 0.5)); + + private static final long serialVersionUID = 1L; + + private static final Point2D UNIT_X = new Point2D.Double(1.0, 0.0); + + public static final String NODE_KEY = "DISTRICT_NETWORK_STATIC_INFO"; + + String info = null; + Point2D origin = new Point2D.Double(); + Point2D direction = UNIT_X; + + @Override + public void render(Graphics2D g) { + ParentNode root = (ParentNode) NodeUtil.getNearestParentOfType(this, RTreeNode.class); + DeferredRenderingNode deferred = root != null ? (DeferredRenderingNode) root.getNode(DistrictNetworkStaticInfoStyle.STATIC_INFO_DEFERRED) : null; + if (deferred != null) + deferred.deferNode(g.getTransform(), this); + else + renderDeferred(g); + } + + @Override + public void renderDeferred(Graphics2D g) { + if (info == null || "".equals(info)) + return; + + AffineTransform oldTransform = g.getTransform(); + + // There has to be a better way to calculate the zoom level in this context... + AffineTransform baseTransform; + try { + baseTransform = ((G2DParentNode)getParent()).getTransform().createInverse(); + } catch (NoninvertibleTransformException e) { + baseTransform = new AffineTransform(); + } + + baseTransform.preConcatenate(oldTransform); + int zoomLevel = MapScalingTransform.zoomLevel(baseTransform); + if (zoomLevel < 15) + return; + + Font oldFont = g.getFont(); + Color oldColor = g.getColor(); + Object oldAA = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + doRender(g); + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAA); + g.setTransform(oldTransform); + g.setColor(oldColor); + g.setFont(oldFont); + } + + private void doRender(Graphics2D g) { + g.translate(origin.getX(), origin.getY()); + double scale = 1.5 / g.getTransform().getScaleX(); + g.scale(scale, scale); + + g.setFont(FONT); + int width1 = g.getFontMetrics().stringWidth(info); +// int height = g.getFontMetrics().getHeight(); + + g.transform(AffineTransform.getRotateInstance(direction.getX(), direction.getY())); + g.translate(0, 10); + +// g.setColor(Color.WHITE); +// g.fillRect(-width1/2 - 5, -height-2, width1+10, height+4); + + g.setColor(Color.BLACK); + + g.drawString(info, -width1/2, 0); + } + + @Override + public Rectangle2D getBoundsInLocal() { + return null; + } + + public void setLocation(Point2D origin, Point2D direction) { + this.origin = origin; + this.direction = direction; + } + + public void setInfo(String info) { + this.info = info; + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkVertexNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkVertexNode.java index 221ed714..2e2b4293 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkVertexNode.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkVertexNode.java @@ -9,30 +9,32 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.simantics.district.network.ui.adapters.DistrictNetworkVertex; +import org.simantics.maps.MapScalingTransform; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.ISelectionPainterNode; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.g2d.IG2DNode; import org.simantics.scenegraph.g2d.nodes.SVGNode; import org.simantics.scenegraph.utils.GeometryUtils; import org.simantics.scenegraph.utils.NodeUtil; -public class DistrictNetworkVertexNode extends G2DParentNode implements ISelectionPainterNode { +public class DistrictNetworkVertexNode extends G2DParentNode implements ISelectionPainterNode, HoverSensitiveNode { //private static final Logger LOGGER = LoggerFactory.getLogger(DistrictNetworkVertexNode.class); private static final long serialVersionUID = -2641639101400236719L; - private static final double left = -0.00005; + private static final double left = -15; private static final double top = left; - public static final double width = 0.0001; + public static final double width = 30; private static final double height = width; private static final BasicStroke STROKE = new BasicStroke((float)width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); private static final Color SELECTION_COLOR = new Color(255, 0, 255, 96); private static final Rectangle2D NORMAL = new Rectangle2D.Double(left, top, width, height); - private static final Rectangle2D HOVERED = new Rectangle2D.Double(left * 3, top * 3, width * 3, height * 3); + private static final Rectangle2D HOVERED = new Rectangle2D.Double(left * 2, top * 2, width * 2, height * 2); private DistrictNetworkVertex vertex; @@ -47,7 +49,9 @@ public class DistrictNetworkVertexNode extends G2DParentNode implements ISelecti private transient Rectangle2D rect; private transient AffineTransform symbolTransform; - private double nodeSize = 1; + private double nodeSize = 1.0; + + private boolean hidden = false; @Override public void init() { @@ -56,9 +60,6 @@ public class DistrictNetworkVertexNode extends G2DParentNode implements ISelecti @Override public void render(Graphics2D g2d) { - if (nodeSize <= 0.0) - return; - AffineTransform ot = null; AffineTransform t = getTransform(); if (t != null && !t.isIdentity()) { @@ -66,53 +67,59 @@ public class DistrictNetworkVertexNode extends G2DParentNode implements ISelecti g2d.transform(t); } - Object oaaHint = null; - Object aaHint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING); - if (aaHint != RenderingHints.VALUE_ANTIALIAS_OFF) { - oaaHint = aaHint; - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - } - - Color oldColor = g2d.getColor(); - Color newColor = dynamicColor != null ? dynamicColor : color; - boolean changeColor = !oldColor.equals(newColor); - + // Translate lat and lon to X and Y + Point2D p = point = DistrictNetworkNodeUtils.calculatePoint2D(vertex.getPoint(), point); + double viewScaleRecip = 1; if (scaleStroke) { viewScaleRecip *= DistrictNetworkNodeUtils.calculateScaleRecip(g2d.getTransform()); } - double scaleRecip = viewScaleRecip * nodeSize; - - // Translate lat and lon to X and Y - Point2D p = point = DistrictNetworkNodeUtils.calculatePoint2D(vertex.getPoint(), point); - Rectangle2D toDraw = rect = DistrictNetworkNodeUtils.calculateDrawnGeometry(p, hover ? HOVERED : NORMAL, rect, scaleRecip); - - if (NodeUtil.isSelected(this, 1)) { - changeColor = true; - g2d.setColor(SELECTION_COLOR); - BasicStroke ss = GeometryUtils.scaleStroke(STROKE, (float) (viewScaleRecip*0.5)); - g2d.setStroke(ss); - g2d.draw(toDraw); - } - - // render - if (changeColor) - g2d.setColor(newColor); - g2d.fill(toDraw); - // Reset settings - if (changeColor) - g2d.setColor(oldColor); - if (oaaHint != null) - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaHint); + if (!hidden && nodeSize > 0.0) { + Object oaaHint = null; + Object aaHint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + if (aaHint != RenderingHints.VALUE_ANTIALIAS_OFF) { + oaaHint = aaHint; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + } + + Color oldColor = g2d.getColor(); + Color newColor = dynamicColor != null ? dynamicColor : color; + boolean changeColor = !oldColor.equals(newColor); + + double scaleRecip = viewScaleRecip * nodeSize; + + Rectangle2D toDraw = rect = DistrictNetworkNodeUtils.calculateDrawnGeometry(p, hover ? HOVERED : NORMAL, rect, scaleRecip); + + if (NodeUtil.isSelected(this, 1)) { + changeColor = true; + g2d.setColor(SELECTION_COLOR); + BasicStroke ss = GeometryUtils.scaleStroke(STROKE, (float)viewScaleRecip); + g2d.setStroke(ss); + g2d.draw(toDraw); + } + + // render + if (changeColor) + g2d.setColor(newColor); + g2d.fill(toDraw); + + // Reset settings + if (changeColor) + g2d.setColor(oldColor); + if (oaaHint != null) + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaHint); + } // Render SVG symbol for (INode nn : getNodes()) { G2DNode g2dNode = (G2DNode)nn; if (nn instanceof SVGNode) { + Rectangle2D toDraw = rect = DistrictNetworkNodeUtils.calculateDrawnGeometry(p, hover ? HOVERED : NORMAL, rect, viewScaleRecip); symbolTransform = DistrictNetworkNodeUtils.getTransformToRectangle(toDraw, symbolTransform); g2dNode.setTransform(symbolTransform); } + g2dNode.render(g2d); } @@ -127,6 +134,7 @@ public class DistrictNetworkVertexNode extends G2DParentNode implements ISelecti @Override public Rectangle2D getBoundsInLocal() { + updateBounds(); return bounds; } @@ -151,12 +159,17 @@ public class DistrictNetworkVertexNode extends G2DParentNode implements ISelecti private Rectangle2D calculateBounds(Rectangle2D rect) { Point2D calcPoint = DistrictNetworkNodeUtils.calculatePoint2D(vertex.getPoint(), point); - AffineTransform at = getTransform(); + AffineTransform at = NodeUtil.getLocalToGlobalTransform(this); + at.concatenate(MapScalingTransform.INSTANCE); double x = calcPoint.getX(); double y = calcPoint.getY(); - double widthh = width / at.getScaleX(); - double heighth = height / at.getScaleY(); - return new Rectangle2D.Double(x - widthh, y - heighth, widthh * 2, heighth * 2).getBounds2D(); + double scaleRecip = DistrictNetworkNodeUtils.calculateScaleRecip(at); + double widthh = width * scaleRecip * nodeSize; + double heighth = height * scaleRecip * nodeSize; + if (rect == null) + rect = new Rectangle2D.Double(); + rect.setRect(x - widthh/2, y - heighth/2, widthh, heighth); + return rect; } public void setVertex(DistrictNetworkVertex vertex) { @@ -164,16 +177,28 @@ public class DistrictNetworkVertexNode extends G2DParentNode implements ISelecti updateBounds(); } - public boolean hover(boolean hover) { -// if (hover && LOGGER.isDebugEnabled()) -// LOGGER.debug("Hovering " + this); - boolean changed = false; - if (this.hover != hover) { - this.hover = hover; - changed = true; + @Override + public boolean hover(boolean hover, boolean isConnectionTool) { + // Only react to hover when the connection tool is active + boolean doHover = hover && isConnectionTool; + boolean changed = this.hover != doHover; + this.hover = doHover; + + for (IG2DNode child : getNodes()) { + if (child instanceof HoverSensitiveNode) + changed = ((HoverSensitiveNode)child).hover(hover, isConnectionTool) || changed; } + return changed; } + + @Override + public void setMousePosition(Point2D p) { + for (IG2DNode child : getNodes()) { + if (child instanceof HoverSensitiveNode) + ((HoverSensitiveNode) child).setMousePosition(p); + } + } public void setColor(Color color) { this.color = color; @@ -210,4 +235,8 @@ public class DistrictNetworkVertexNode extends G2DParentNode implements ISelecti this.dynamicColor = color; } + @PropertySetter(value = "hidden") + public void setHidden(Boolean value) { + this.hidden = value; + } } diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DynamicVisualisationContributionsNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DynamicVisualisationContributionsNode.java new file mode 100644 index 00000000..53d7862c --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DynamicVisualisationContributionsNode.java @@ -0,0 +1,344 @@ +package org.simantics.district.network.ui.nodes; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.simantics.district.network.visualisations.model.ColorBarOptions; +import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsLocation; +import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsSize; +import org.simantics.district.network.visualisations.model.DynamicColorContribution; +import org.simantics.district.network.visualisations.model.DynamicColorMap; +import org.simantics.district.network.visualisations.model.DynamicColorMap.RGBIntensity; +import org.simantics.district.network.visualisations.model.DynamicSizeContribution; +import org.simantics.district.network.visualisations.model.DynamicSizeMap; +import org.simantics.district.network.visualisations.model.SizeBarOptions; +import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsLocation; +import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsSize; +import org.simantics.scenegraph.g2d.G2DNode; +import org.simantics.scenegraph.utils.DPIUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DynamicVisualisationContributionsNode extends G2DNode { + + private static final Logger LOGGER = LoggerFactory.getLogger(DynamicVisualisationContributionsNode.class); + + public static final String ID = "dynamicVisualisationContributionsNode"; + + private static final long serialVersionUID = 7400966702826774761L; + + protected boolean enabled = true; + + private Map dynamicColoringContributions; + private Map dynamicSizingContributions; + + private ColorBarOptions colorBarsOptions = ColorBarOptions.useDefault(); + private SizeBarOptions sizeBarsOptions = SizeBarOptions.useDefault(); + + @Override + public void render(Graphics2D g2d) { + if (!enabled) + return; + + AffineTransform ot = g2d.getTransform(); + Color oldColor = g2d.getColor(); + + try { + g2d.transform(transform); + g2d.setTransform(new AffineTransform()); + + Rectangle2D bounds = g2d.getClipBounds(); + if (bounds == null) + return; // FIXME + + renderColors(g2d); + renderSizes(g2d); + } finally { + g2d.setColor(oldColor); + g2d.setTransform(ot); + } + } + + private double colorBarBoxWidth = 300; + private double colorBarBoxHeight = 50; + private double colorBarBoxPadding = 20; + + private void renderColors(Graphics2D g2d) { + if (colorBarsOptions == null || !colorBarsOptions.isShowColorBars()) { + return; + } + ColorBarsLocation location = colorBarsOptions.getLocation(); + ColorBarsSize size = colorBarsOptions.getSize(); + double colorBarBoxLeft = getColorBarBoxLeft(g2d, location, size); + double colorBarBoxTopInitial = getColorBarBoxTop(g2d, location, size); + double colorBarBoxWidth = getColorBarBoxWidth(size); + double colorBarBoxHeight = getColorBarBoxHeight(size); + + Rectangle2D bounds = g2d.getClipBounds(); + if (bounds == null) + return; // FIXME + + int i = 0; + + if (dynamicColoringContributions != null) { + for (Entry object : dynamicColoringContributions.entrySet()) { + DynamicColorContribution cc = object.getValue(); + + if (!cc.isUsed()) + break; + + double min = cc.getDefaultMin(); + double max = cc.getDefaultMax(); + String unit = cc.getUnit(); + String label = cc.getLabel(); + + DynamicColorMap map = cc.getDefaultColorMap(); + + double colorBarBoxTop = (colorBarBoxTopInitial + (colorBarBoxHeight * i)); + i++; + + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f)); + g2d.setColor(new Color(0.9f, 0.9f, 0.9f, 0.95f)); + + Rectangle2D vertical = new Rectangle2D.Double(colorBarBoxLeft, colorBarBoxTop, colorBarBoxWidth, colorBarBoxHeight); + g2d.fill(vertical); + + double colorVerticalLeft = colorBarBoxLeft + 5; + double colorVerticalTop = colorBarBoxTop + 15; + double colorVerticalHeigth = colorBarBoxHeight - 30; + List intensities = map.getIntensities(); + double colorVerticalWidth = (colorBarBoxWidth - 10) / intensities.size(); + + Font rulerFont = new Font("Tahoma", Font.PLAIN, DPIUtil.upscale(9)); + g2d.setFont(rulerFont); + g2d.setColor(Color.BLACK); + + String str = Double.toString(max); + g2d.drawString(str, (int)(colorBarBoxLeft + colorBarBoxWidth - 30), (int)(colorBarBoxTop + colorBarBoxHeight)); + + str = Double.toString(min); + g2d.drawString(str, (int)colorBarBoxLeft + 1, (int)(colorBarBoxTop + colorBarBoxHeight)); + + + for (RGBIntensity intensity : intensities) { + + g2d.setColor(new Color((float)intensity.getRed(), (float)intensity.getGreen(), (float)intensity.getBlue(), 1f)); + Rectangle2D colorVertical = new Rectangle2D.Double(colorVerticalLeft, colorVerticalTop, colorVerticalWidth, colorVerticalHeigth); + g2d.fill(colorVertical); + colorVerticalLeft = colorVerticalLeft + colorVerticalWidth; + } + g2d.setColor(Color.BLACK); + str = object.getKey() + " - " + label + " [" + unit + "]"; + g2d.drawString(str, (int)colorBarBoxLeft + 5, (int)colorBarBoxTop + 10); + } + } + } + + private double getColorBarBoxTop(Graphics2D g2d, ColorBarsLocation location, ColorBarsSize size) { + Rectangle2D bounds = g2d.getClipBounds(); + if (bounds == null) + throw new IllegalStateException(); + switch (location) { + case NORTH: { + return colorBarBoxPadding; + } + case SOUTH: { + return bounds.getMaxY() - colorBarBoxPadding; + } + case EAST: + case WEST: { + return (bounds.getMaxY() / 2) - (getColorBarBoxHeight(size) / 2); + } + default: + return 0; + } + } + + private double getColorBarBoxLeft(Graphics2D g2d, ColorBarsLocation location, ColorBarsSize size) { + Rectangle2D bounds = g2d.getClipBounds(); + if (bounds == null) + throw new IllegalStateException(); + switch (location) { + case EAST: { + double right = bounds.getMaxX() - colorBarBoxPadding; + return right - getColorBarBoxWidth(size); + } + case WEST: { + return colorBarBoxPadding; + } + case NORTH: + case SOUTH: { + double left = (bounds.getMaxX() / 2) - (getColorBarBoxWidth(size) / 2); + return left; + } + default: + return 0; + } + } + + private double getColorBarBoxWidth(ColorBarsSize size) { + return size.getSize() * colorBarBoxWidth; + } + + private double getColorBarBoxHeight(ColorBarsSize size) { + return size.getSize() * colorBarBoxHeight; + } + + private void renderSizes(Graphics2D g2d) { + if (sizeBarsOptions == null || !sizeBarsOptions.isShowSizeBars()) { + return; + } + SizeBarsLocation location = sizeBarsOptions.getLocation(); + SizeBarsSize sizeb = sizeBarsOptions.getSize(); + double sizeBarBoxLeft = getSizeBarBoxLeft(g2d, location, sizeb); + double sizeBarBoxTopInitial = getSizeBarBoxTop(g2d, location, sizeb); + double sizeBarBoxWidth = getSizeBarBoxWidth(sizeb); + double sizeBarBoxHeight = getSizeBarBoxHeight(sizeb); + + Rectangle2D bounds = g2d.getClipBounds(); + if (bounds == null) + return; // FIXME + + int i = 0; + + for (Entry object : dynamicSizingContributions.entrySet()) { + DynamicSizeContribution cc = object.getValue(); + + double min = cc.getDefaultMin(); + double max = cc.getDefaultMax(); + String unit = cc.getUnit(); + String label = cc.getLabel(); + + DynamicSizeMap map = cc.getDefaultSizeMap(); + + List sizes = map.getSizes(); + + double sizeBarBoxTop = (sizeBarBoxTopInitial + (colorBarBoxHeight * i)); + i++; +// double backgroundBoxPaddingRight = 20; +// double backgroundBoxHeight = 50; +// double backgroundBoxWidth = 300; +// double backgroundBoxRight = bounds.getMaxX() - backgroundBoxPaddingRight; +// double backgroundBoxLeft = backgroundBoxRight - backgroundBoxWidth; +// double backgroundBoxTop = bounds.getMaxY();// + (initialPosition + (position * i)); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f)); + g2d.setColor(new Color(0.9f, 0.9f, 0.9f, 0.95f)); + + Rectangle2D vertical = new Rectangle2D.Double(sizeBarBoxLeft, sizeBarBoxTop, colorBarBoxWidth, colorBarBoxHeight); + g2d.fill(vertical); + + double sizeVerticalLeft = sizeBarBoxLeft + 5; + double sizeVerticalTop = sizeBarBoxTop + 15; + double sizeVerticalHeigth = colorBarBoxHeight - 30; + + double sizeVerticalWidth = (sizeBarBoxWidth - 10) / sizes.size(); + + Font rulerFont = new Font("Tahoma", Font.PLAIN, DPIUtil.upscale(9)); + g2d.setFont(rulerFont); + g2d.setColor(Color.BLACK); + + String str = Double.toString(max); + g2d.drawString(str, (int)(sizeBarBoxLeft + sizeBarBoxWidth - 30), (int)(sizeBarBoxTop + sizeBarBoxHeight)); + + str = Double.toString(min); + g2d.drawString(str, (int)sizeBarBoxLeft + 1, (int)(sizeBarBoxTop + sizeBarBoxHeight)); + + + g2d.setColor(new Color((float)0, (float)0, (float)0.8, 0.8f)); + for (Double size: sizes) { + + Ellipse2D ellipse = new Ellipse2D.Double(sizeVerticalLeft + sizeVerticalWidth / 2, (sizeVerticalTop), size * sizeVerticalHeigth, size * sizeVerticalHeigth); + g2d.fill(ellipse); + + sizeVerticalLeft = sizeVerticalLeft + sizeVerticalWidth; + } + g2d.setColor(Color.BLACK); + str = object.getKey() + " - " + label + " [" + unit + "]"; + g2d.drawString(str, (int)sizeBarBoxLeft + 5, (int)sizeBarBoxTop + 10); + } + } + + private double getSizeBarBoxTop(Graphics2D g2d, SizeBarsLocation location, SizeBarsSize size) { + Rectangle2D bounds = g2d.getClipBounds(); + if (bounds == null) + throw new IllegalStateException(); + switch (location) { + case NORTH: { + return colorBarBoxPadding; + } + case SOUTH: { + return bounds.getMaxY() - colorBarBoxPadding; + } + case EAST: + case WEST: { + return (bounds.getMaxY() / 2) - (getSizeBarBoxHeight(size) / 2); + } + default: + return 0; + } + } + + private double getSizeBarBoxLeft(Graphics2D g2d, SizeBarsLocation location, SizeBarsSize size) { + Rectangle2D bounds = g2d.getClipBounds(); + if (bounds == null) + throw new IllegalStateException(); + switch (location) { + case EAST: { + double right = bounds.getMaxX() - colorBarBoxPadding; + return right - getSizeBarBoxWidth(size); + } + case WEST: { + return colorBarBoxPadding; + } + case NORTH: + case SOUTH: { + double left = (bounds.getMaxX() / 2) - (getSizeBarBoxWidth(size) / 2); + return left; + } + default: + return 0; + } + } + + private double getSizeBarBoxWidth(SizeBarsSize size) { + return size.getSize() * colorBarBoxWidth; + } + + private double getSizeBarBoxHeight(SizeBarsSize size) { + return size.getSize() * colorBarBoxHeight; + } + + @Override + public Rectangle2D getBoundsInLocal() { + return null; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public void setDynamicColoringObjects(Map dynamicColoringObjects) { + this.dynamicColoringContributions = dynamicColoringObjects; + } + + public void setColorBarOptions(ColorBarOptions colorBarsOptions) { + this.colorBarsOptions = colorBarsOptions; + } + + public void setDynamicSizingObjects(Map dynamicSizingObjects) { + this.dynamicSizingContributions = dynamicSizingObjects; + } + + public void setSizeBarOptions(SizeBarOptions sizeBarOptions) { + this.sizeBarsOptions = sizeBarOptions; + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/ElevationServerNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/ElevationServerNode.java new file mode 100644 index 00000000..ea9f9863 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/ElevationServerNode.java @@ -0,0 +1,81 @@ +package org.simantics.district.network.ui.nodes; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Collection; +import java.util.Collections; + +import org.simantics.scenegraph.g2d.G2DNode; +import org.simantics.scenegraph.utils.GeometryUtils; + +public class ElevationServerNode extends G2DNode { + + private static final long serialVersionUID = 3832908017166017921L; + + private static final Stroke DASHED_STROKE = new BasicStroke(2.0f, + BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, + 4.0f, new float[]{4.0f}, 0.0f); + + private static final Color BLUE_ALPHA = new Color(0, 0, 255, 100); + + public static final String ID = "elevationServerNode"; + + private Collection rectangles; + + @Override + public void render(Graphics2D g2d) { + + Color old = g2d.getColor(); + Stroke oldStroke = g2d.getStroke(); + + g2d.setColor(BLUE_ALPHA); + BasicStroke stroke = GeometryUtils.scaleStroke(DASHED_STROKE, (float) (1.0 / GeometryUtils.getScale(g2d.getTransform()))); + g2d.setStroke(stroke); + + double scale = getTransform().getScaleY(); + + for (Rectangle2D rect : getRectangles()) { + + Point2D point = new Point2D.Double(rect.getY(), rect.getX()); + Point2D result = DistrictNetworkNodeUtils.calculatePoint2D(point, null); + + Point2D point2 = new Point2D.Double(rect.getY() + rect.getHeight(), rect.getX() + rect.getWidth()); + Point2D result2 = DistrictNetworkNodeUtils.calculatePoint2D(point2, null); + + double x = result.getX() * scale; + double y = result.getY() * scale; + double width = result2.getX() * scale - result.getX() * scale; + double height = result2.getY() * scale - result.getY() * scale; + + Rectangle2D translated = new Rectangle2D.Double(x, y - Math.abs(height), width, Math.abs(height)); + g2d.draw(translated); + } + + g2d.setStroke(oldStroke); + g2d.setColor(old); + } + + @Override + public Rectangle2D getBoundsInLocal() { + Rectangle2D bounds = new Rectangle2D.Double(); + for (Rectangle2D rect : getRectangles()) { + bounds.add(rect); + } + return bounds; + } + + public void setRectangles(Collection rectangles) { + this.rectangles = rectangles; + } + + public Collection getRectangles() { + if (rectangles == null) + return Collections.emptyList(); + return rectangles; + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/HoverSensitiveNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/HoverSensitiveNode.java new file mode 100644 index 00000000..cd6631be --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/HoverSensitiveNode.java @@ -0,0 +1,8 @@ +package org.simantics.district.network.ui.nodes; + +import java.awt.geom.Point2D; + +public interface HoverSensitiveNode { + boolean hover(boolean hover, boolean isConnectionTool); + default void setMousePosition(Point2D p) {}; +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/NetworkDrawingNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/NetworkDrawingNode.java index 674702b8..2cd87d69 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/NetworkDrawingNode.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/NetworkDrawingNode.java @@ -11,21 +11,27 @@ import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.TimeUnit; import org.simantics.Simantics; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.exception.DatabaseException; +import org.simantics.db.request.Write; import org.simantics.diagram.elements.DiagramNodeUtil; import org.simantics.diagram.ui.DiagramModelHints; +import org.simantics.district.network.DNEdgeBuilder; +import org.simantics.district.network.DistrictNetworkUtil; import org.simantics.district.network.ModelledCRS; -import org.simantics.district.network.ui.DNEdgeBuilder; +import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.district.network.ui.NetworkDrawingParticipant; import org.simantics.g2d.canvas.Hints; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.IToolMode; import org.simantics.g2d.diagram.IDiagram; +import org.simantics.maps.elevation.server.SingletonTiffTileInterface; +import org.simantics.maps.elevation.server.prefs.MapsElevationServerPreferences; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.g2d.events.EventTypes; import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent; @@ -35,18 +41,27 @@ import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent; import org.simantics.scenegraph.utils.GeometryUtils; import org.simantics.scenegraph.utils.NodeUtil; +import org.simantics.utils.threads.ThreadUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class NetworkDrawingNode extends G2DNode { + private static final Logger LOGGER = LoggerFactory.getLogger(NetworkDrawingNode.class); + + static class DrawingNode { + + private List routeNodes = new ArrayList<>(); + } + private static final long serialVersionUID = -3475301184009620573L; private Point2D currentMousePos = null; - - private List nodes = new ArrayList<>(); - private Resource diagramResource; + private List nodes = new ArrayList<>(); + private DrawingNode currentRouteNode = null; - private boolean committed; + private Resource diagramResource; private NetworkDrawingParticipant participant; @@ -58,6 +73,7 @@ public class NetworkDrawingNode extends G2DNode { 4.0f, new float[]{4.0f}, 0.0f); private static final Color BLUE_ALPHA = new Color(0, 0, 255, 100); + private static final Color RED_ALPHA = new Color(255, 0, 0, 100); private boolean scaleStroke = true; @@ -82,36 +98,47 @@ public class NetworkDrawingNode extends G2DNode { public void render(Graphics2D g2d) { if (nodes.isEmpty()) return; - - Path2D path = new Path2D.Double(); - Iterator nodeIter = nodes.iterator(); - if (nodeIter.hasNext()) { - Point2D node = nodeIter.next(); - path.moveTo(node.getX(), node.getY()); - } - while (nodeIter.hasNext()) { - Point2D node = nodeIter.next(); - path.lineTo(node.getX(), node.getY()); - } - if (currentMousePos != null) - path.lineTo(currentMousePos.getX(), currentMousePos.getY()); - + Color old = g2d.getColor(); Stroke oldStroke = g2d.getStroke(); - - if (DASHED_STROKE != null) { - if (scaleStroke && DASHED_STROKE instanceof BasicStroke) { - BasicStroke bs = GeometryUtils.scaleStroke(DASHED_STROKE, (float) (1.0 / GeometryUtils.getScale(g2d.getTransform()))); - g2d.setStroke(bs); - } else { - g2d.setStroke(DASHED_STROKE); + + Iterator dnodeIterator = nodes.iterator(); + while (dnodeIterator.hasNext()) { + Path2D path = new Path2D.Double(); + DrawingNode dnode = dnodeIterator.next(); + Iterator nodeIter = dnode.routeNodes.iterator(); + if (nodeIter.hasNext()) { + Point2D node = nodeIter.next(); + path.moveTo(node.getX(), node.getY()); } + while (nodeIter.hasNext()) { + Point2D node = nodeIter.next(); + path.lineTo(node.getX(), node.getY()); + } + if (!dnodeIterator.hasNext()) { + if (currentMousePos != null) + path.lineTo(currentMousePos.getX(), currentMousePos.getY()); + } + + if (DASHED_STROKE != null) { + if (scaleStroke && DASHED_STROKE instanceof BasicStroke) { + BasicStroke bs = GeometryUtils.scaleStroke(DASHED_STROKE, (float) (1.0 / GeometryUtils.getScale(g2d.getTransform()))); + g2d.setStroke(bs); + } else { + g2d.setStroke(DASHED_STROKE); + } + } + + g2d.setColor(BLUE_ALPHA); + g2d.draw(path); + + g2d.setColor(RED_ALPHA); + BasicStroke stroke = GeometryUtils.scaleStroke(DASHED_STROKE, (float) (1.0 / GeometryUtils.getScale(g2d.getTransform()))); + g2d.setStroke(stroke); + Point2D currentPoint = path.getCurrentPoint(); + g2d.draw(new Rectangle2D.Double(currentPoint.getX() - 0.0001 / 2, currentPoint.getY() - 0.0001 / 2, 0.0001, 0.0001)); } - g2d.setColor(BLUE_ALPHA); - - g2d.draw(path); - g2d.setStroke(oldStroke); g2d.setColor(old); } @@ -131,35 +158,52 @@ public class NetworkDrawingNode extends G2DNode { // nodes to path2d IToolMode mode = getToolMode(); if (mode == Hints.CONNECTTOOL || e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK)) { - Point2D start = null; - Point2D end = null; - Iterator nodeIter = nodes.iterator(); - while (nodeIter.hasNext()) { - if (end == null) { - start = nodeIter.next(); - if (!nodeIter.hasNext()) { - break; - } - } else { - start = end; - } + // ok, new routenode starts from here + Point2D localPos = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double()); + Point2D.Double pos = new Point2D.Double(localPos.getX(), localPos.getY()); + if (currentRouteNode != null) { + //currentRouteNode.routeNodes.add(pos); + currentRouteNode = new DrawingNode(); + currentRouteNode.routeNodes.add(pos); + nodes.add(currentRouteNode); + } else { + // ok, this must be creation of dh_point + double scale = getTransform().getScaleY(); + double x = ModelledCRS.xToLongitude(pos.getX() / scale); + double y = ModelledCRS.yToLatitude(-pos.getY() / scale); - end = nodeIter.next(); + boolean leftButton = e.button == MouseEvent.LEFT_BUTTON; - createEdge(start, end); + ThreadUtils.getNonBlockingWorkExecutor().schedule(() -> { + Simantics.getSession().asyncRequest(new Write() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + graph.markUndoPoint(); + Resource mapping = null; + if (leftButton) { + mapping = graph.getSingleObject(diagramResource, DistrictNetworkResource.getInstance(graph).LeftClickDefaultMapping); + } else { + mapping = graph.getSingleObject(diagramResource, DistrictNetworkResource.getInstance(graph).RightClickDefaultMapping); + } + if (mapping == null) { + mapping = graph.getSingleObject(diagramResource, DistrictNetworkResource.getInstance(graph).VertexDefaultMapping); + } + DistrictNetworkUtil.createVertex(graph, diagramResource, new double[] { x, y }, Double.MAX_VALUE, mapping); + } + }); + }, 100, TimeUnit.MILLISECONDS); } - - nodes.clear(); - committed = true; - repaint(); - return true; } return super.mouseDoubleClicked(e); } - private void createEdge(Point2D start, Point2D end) { + private void createEdge(DrawingNode node) { + + Point2D start = node.routeNodes.get(0); + Point2D end = node.routeNodes.get(node.routeNodes.size() - 1); double currentPadding = DistrictNetworkVertexNode.width; AffineTransform test = getTransform(); @@ -188,15 +232,24 @@ public class NetworkDrawingNode extends G2DNode { double[] startCoords = new double[] { startLon, startLat }; double[] endCoords = new double[] { endLon, endLat }; + double[] detailedGeometryCoords = new double[node.routeNodes.size() * 2]; + int i = 0; + for (Point2D p : node.routeNodes) { + double lat = ModelledCRS.yToLatitude(-p.getY() / scaleY); + double lon = ModelledCRS.xToLongitude(p.getX() / scaleX); + detailedGeometryCoords[i++] = lon; + detailedGeometryCoords[i++] = lat; + } + DNEdgeBuilder builder = new DNEdgeBuilder(diagramResource, diagram); Simantics.getSession().asyncRequest(new WriteRequest() { - + @Override public void perform(WriteGraph graph) throws DatabaseException { - builder.create(graph, startCoords, endCoords, padding); + builder.create(graph, startCoords, Double.MAX_VALUE, endCoords, Double.MAX_VALUE, detailedGeometryCoords, padding); } }); - + } @Override @@ -204,22 +257,38 @@ public class NetworkDrawingNode extends G2DNode { // check ToolMode IToolMode mode = getToolMode(); if (mode == Hints.CONNECTTOOL || e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK)) { - if (committed) { - committed = false; - return false; - } if (e.button == MouseEvent.RIGHT_BUTTON && !nodes.isEmpty()) { nodes.remove(nodes.size() - 1); } else if (e.button == MouseEvent.LEFT_BUTTON) { Point2D localPos = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double()); - nodes.add(new Point2D.Double(localPos.getX(), localPos.getY())); + if (currentRouteNode == null && canStartEdge(localPos)) { + // ok, we can start from here + currentRouteNode = new DrawingNode(); + currentRouteNode.routeNodes.add(new Point2D.Double(localPos.getX(), localPos.getY())); + nodes.add(currentRouteNode); + } else if (currentRouteNode != null && canStartEdge(localPos)) { + // let's commit our new routenode + currentRouteNode.routeNodes.add(new Point2D.Double(localPos.getX(), localPos.getY())); + Iterator nodeIter = nodes.iterator(); + while (nodeIter.hasNext()) { + createEdge(nodeIter.next()); + } + currentRouteNode = null; + nodes.clear(); + } else if (currentRouteNode != null) { + currentRouteNode.routeNodes.add(new Point2D.Double(localPos.getX(), localPos.getY())); + } } repaint(); return true; } return super.mouseClicked(e); } - + + private boolean canStartEdge(Point2D currentPos) { + return participant.isHoveringOverNode(currentPos); + } + private IToolMode getToolMode() { return participant.getHint(Hints.KEY_TOOL); } @@ -227,28 +296,28 @@ public class NetworkDrawingNode extends G2DNode { @Override protected boolean mouseMoved(MouseMovedEvent e) { IToolMode mode = getToolMode(); - if (mode == Hints.CONNECTTOOL || e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK)) { - boolean repaint = false; - Point2D p = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double()); - if (participant.pickHoveredElement(p)) { - repaint = true; - } - if (!nodes.isEmpty()) { - currentMousePos = p; - - repaint(); - return true; - } - currentMousePos = null; - if (repaint == true) - repaint(); + boolean repaint = false; + Point2D p = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double()); + boolean isConnectionTool = mode == Hints.CONNECTTOOL || e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK); + if (participant.pickHoveredElement(p, isConnectionTool)) { + repaint = true; } + if (!nodes.isEmpty()) { + currentMousePos = p; + + repaint(); + return true; + } + currentMousePos = null; + if (repaint == true) + repaint(); return super.mouseMoved(e); } @Override protected boolean keyPressed(KeyPressedEvent e) { if (e.keyCode == java.awt.event.KeyEvent.VK_ESCAPE) { + currentRouteNode = null; nodes.clear(); repaint(); return true; diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/DynamicVisualisationContributionsParticipant.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/DynamicVisualisationContributionsParticipant.java new file mode 100644 index 00000000..69086062 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/DynamicVisualisationContributionsParticipant.java @@ -0,0 +1,116 @@ +package org.simantics.district.network.ui.participants; + +import java.awt.geom.AffineTransform; +import java.util.Map; + +import org.simantics.district.network.ui.DistrictDiagramViewer; +import org.simantics.district.network.ui.nodes.DynamicVisualisationContributionsNode; +import org.simantics.district.network.visualisations.model.ColorBarOptions; +import org.simantics.district.network.visualisations.model.DynamicColorContribution; +import org.simantics.district.network.visualisations.model.DynamicSizeContribution; +import org.simantics.district.network.visualisations.model.SizeBarOptions; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant; +import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit; +import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; +import org.simantics.scenegraph.g2d.events.command.CommandEvent; +import org.simantics.utils.datastructures.hints.HintListenerAdapter; +import org.simantics.utils.datastructures.hints.IHintContext.Key; +import org.simantics.utils.datastructures.hints.IHintListener; +import org.simantics.utils.datastructures.hints.IHintObservable; + +public class DynamicVisualisationContributionsParticipant extends AbstractCanvasParticipant { + + IHintListener hintListener = new HintListenerAdapter() { + public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { + ICanvasContext cc = getContext(); + if (cc != null) { + updateNode(); + cc.getContentContext().setDirty(); + } + } + }; + + private DynamicVisualisationContributionsNode node; + private AffineTransform transform; + + public DynamicVisualisationContributionsParticipant(AffineTransform tr) { + this.transform = tr; + } + + @Override + public void addedToContext(ICanvasContext ctx) { + super.addedToContext(ctx); + getHintStack().addKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_COLORING_OBJECTS, hintListener); + getHintStack().addKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_COLOR_BAR_OPTIONS, hintListener); + getHintStack().addKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_SIZING_OBJECTS, hintListener); + getHintStack().addKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_SIZE_BAR_OPTIONS, hintListener); + } + + @Override + public void removedFromContext(ICanvasContext ctx) { + getHintStack().removeKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_COLORING_OBJECTS, hintListener); + getHintStack().removeKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_COLOR_BAR_OPTIONS, hintListener); + getHintStack().removeKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_SIZING_OBJECTS, hintListener); + getHintStack().removeKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_SIZE_BAR_OPTIONS, hintListener); + super.removedFromContext(ctx); + } + + @SGInit + public void initSG(G2DParentNode parent) { + node = parent.addNode(DynamicVisualisationContributionsNode.ID, DynamicVisualisationContributionsNode.class); + node.setTransform(transform); + node.setEnabled(true); + node.setZIndex(1000); + } + + @EventHandler(priority = 0) + protected boolean handleKeyEvent(CommandEvent e) { + if (e.command.equals(DistrictDiagramViewer.MAP_COLOR_BAR_OPTIONS_CHANGE)) { + System.out.println(e); + return true; + } + return false; + } + +// @Override +// protected boolean handleCommand(CommandEvent e) { +// if (e.command.equals(DistrictDiagramViewer.MAP_COLOR_BAR_OPTIONS_CHANGE)) { +// ICanvasContext context = (ICanvasContext) e.getContext(); +// ColorBarOptions options = context.getHintStack().getHint(DistrictDiagramViewer.KEY_MAP_COLOR_BAR_OPTIONS); +// this.colorBarsOptions = options; +// repaint(); +// return true; +// } else { +// return super.handleCommand(e); +// } +// } + + protected void updateNode() { + node.setDynamicColoringObjects(getDynamicColoringObjects()); + node.setColorBarOptions(getColorBarOptions()); + node.setDynamicSizingObjects(getDynamicSizingObjects()); + node.setSizeBarOptions(getSizeBarOptions()); + } + + private Map getDynamicColoringObjects() { + Map objects = getHint(DistrictDiagramViewer.KEY_MAP_COLORING_OBJECTS); + return objects; + } + + private ColorBarOptions getColorBarOptions() { + ColorBarOptions options = getHint(DistrictDiagramViewer.KEY_MAP_COLOR_BAR_OPTIONS); + return options; + } + + private Map getDynamicSizingObjects() { + Map objects = getHint(DistrictDiagramViewer.KEY_MAP_SIZING_OBJECTS); + return objects; + } + + private SizeBarOptions getSizeBarOptions() { + SizeBarOptions options = getHint(DistrictDiagramViewer.KEY_MAP_SIZE_BAR_OPTIONS); + return options; + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/ElevationServerParticipant.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/ElevationServerParticipant.java new file mode 100644 index 00000000..d5f02bb2 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/ElevationServerParticipant.java @@ -0,0 +1,26 @@ +package org.simantics.district.network.ui.participants; + +import java.awt.geom.AffineTransform; + +import org.simantics.district.network.ui.nodes.ElevationServerNode; +import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit; +import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant; +import org.simantics.scenegraph.g2d.G2DParentNode; + +public class ElevationServerParticipant extends AbstractDiagramParticipant { + + private ElevationServerNode node; + + private AffineTransform transform; + + public ElevationServerParticipant(AffineTransform transform) { + this.transform = transform; + } + + @SGInit + public void initSG(G2DParentNode parent) { + node = parent.addNode(ElevationServerNode.ID, ElevationServerNode.class); + node.setTransform(transform); + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/ConnectionLineStyle.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/ConnectionLineStyle.java new file mode 100644 index 00000000..b74cbba6 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/ConnectionLineStyle.java @@ -0,0 +1,199 @@ +package org.simantics.district.network.ui.styles; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.procedure.adapter.TransientCacheListener; +import org.simantics.db.common.request.ResourceRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.util.Layer0Utils; +import org.simantics.diagram.profile.StyleBase; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.ui.nodes.DistrictNetworkNodeUtils; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.scenegraph.INode; +import org.simantics.scenegraph.g2d.G2DNode; +import org.simantics.scenegraph.profile.EvaluationContext; +import org.simantics.scenegraph.profile.common.ProfileVariables; +import org.simantics.scenegraph.utils.GeometryUtils; +import org.simantics.scl.compiler.top.ValueNotFound; +import org.simantics.scl.osgi.SCLOsgi; +import org.simantics.scl.runtime.SCLContext; +import org.simantics.scl.runtime.function.Function1; +import org.simantics.structural.stubs.StructuralResource2; + +public class ConnectionLineStyle extends StyleBase> { + + public static class ConnectionLineNode extends G2DNode { + private static final BasicStroke STROKE = new BasicStroke(1.f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.f, new float[] {4.f, 2.f}, 0.f); + private static final Color[] colors = { Color.RED, Color.GREEN, Color.BLUE, Color.ORANGE, Color.CYAN, Color.PINK }; + + private float strokeWidth; + private Line2D[] lines; + + public ConnectionLineNode() { + super(); + } + + private static final long serialVersionUID = 1L; + + @Override + public Rectangle2D getBoundsInLocal() { + return null; + } + + @Override + public Rectangle2D getBoundsInLocal(boolean b) { + return null; + } + + @Override + public Rectangle2D getBounds() { + return null; + } + + public void setStrokeWidth(float w) { + strokeWidth = w; + } + + public void setPoints(List result) { + Point2D p0 = DistrictNetworkNodeUtils.calculatePoint2D(result.get(0), null); + lines = new Line2D[result.size() - 1]; + for (int i = 1; i < result.size(); i++) + { + Point2D p = result.get(i); + lines[i-1] = p != null ? new Line2D.Double(p0, DistrictNetworkNodeUtils.calculatePoint2D(p, null)) : null; + } + } + + @Override + public void render(Graphics2D g2d) { + if (lines == null || lines.length == 0) + return; + + // Keep fixed line width on screen + float scaleRecip = (float) GeometryUtils.getScale(g2d.getTransform()); + g2d.setStroke(GeometryUtils.scaleStroke(STROKE, strokeWidth / scaleRecip)); + + for (int i = 0; i < lines.length; i++) { + if (lines[i] != null) { + g2d.setColor(colors[i % colors.length]); + g2d.draw(lines[i]); + } + } + } + } + + @Override + public List calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem) + throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + ModelingResources MOD = ModelingResources.getInstance(graph); + StructuralResource2 STR = StructuralResource2.getInstance(graph); + + Resource vertex = groupItem; + if (!graph.isInstanceOf(vertex, DN.Vertex)) + return Collections.emptyList(); + + double[] coords = graph.getRelatedValue(vertex, DiagramResource.getInstance(graph).HasLocation); + + Resource component = DistrictNetworkUtil.getMappedComponentCached(graph, vertex); + if (component == null) + return Collections.emptyList(); + + Resource componentType = graph.getPossibleType(component, STR.Component); + if (componentType == null) + return Collections.emptyList(); + + Function1> fun = getConnectedComponentsFunctionCached(graph, componentType); + if (fun == null) + return Collections.emptyList(); + + List components = Simantics.applySCLRead(graph, fun, component); + + if (components == null || components.isEmpty()) + return Collections.emptyList(); + + List result = new ArrayList<>(components.size() + 1); + result.add(new Point2D.Double(coords[0], coords[1])); + for (Resource comp : components) { + Resource e = comp != null ? graph.getPossibleObject(comp, MOD.ComponentToElement) : null; + Resource mappingElement = e != null ? graph.getPossibleObject(e, DN.MappedFromElement) : null; + if (mappingElement != null) { + double[] coords2 = graph.getRelatedValue(mappingElement, DiagramResource.getInstance(graph).HasLocation); + result.add(new Point2D.Double(coords2[0], coords2[1])); + } + else { + result.add(null); + } + } + + return result; + } + + @Override + public void applyStyleForNode(EvaluationContext observer, INode parent, List result) { + if (result == null || result.size() < 2) { + ProfileVariables.denyChild(parent, "*", "districtNetworkConnection"); + return; + } + + ConnectionLineNode node = ProfileVariables.claimChild(parent, "*", "districtNetworkConnection", ConnectionLineNode.class, observer); + if (node == null) + return; + + node.setPoints(result); + node.setZIndex(0); + node.setStrokeWidth(2.f); + } + + @Override + protected void cleanupStyleForNode(EvaluationContext evaluationContext, INode parent) { + ProfileVariables.denyChild(parent, "*", "districtNetworkConnection"); + } + + private static Function1> getConnectedComponentsFunctionCached(ReadGraph graph, Resource componentType) + throws DatabaseException { + return graph.syncRequest(new ConnectedComponentsFunctionRequest(componentType), TransientCacheListener.instance()); + } + + private static final class ConnectedComponentsFunctionRequest + extends ResourceRead>> { + public ConnectedComponentsFunctionRequest(Resource resource) { + super(resource); + } + + @SuppressWarnings("unchecked") + @Override + public Function1> perform(ReadGraph graph) throws DatabaseException { + Resource actionsModule = Layer0Utils.getPossibleChild(graph, resource, "Actions"); + if (actionsModule == null || !graph.isInstanceOf(actionsModule, Layer0.getInstance(graph).SCLModule)) + return null; + + String uri = graph.getURI(actionsModule); + SCLContext sclContext = SCLContext.getCurrent(); + Object oldGraph = sclContext.get("graph"); + try { + sclContext.put("graph", graph); + return (Function1>) SCLOsgi.MODULE_REPOSITORY.getValue(uri, "getConnectedComponents"); + } catch (ValueNotFound e1) { + return null; + } finally { + sclContext.put("graph", oldGraph); + } + } + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/DistrictNetworkHoverInfoStyle.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/DistrictNetworkHoverInfoStyle.java new file mode 100644 index 00000000..77be9c90 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/DistrictNetworkHoverInfoStyle.java @@ -0,0 +1,201 @@ +package org.simantics.district.network.ui.styles; + +import java.awt.geom.Point2D; +import java.util.Collections; +import java.util.List; + +import org.simantics.Simantics; +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.procedure.adapter.TransientCacheListener; +import org.simantics.db.common.request.ResourceRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.exception.MissingVariableException; +import org.simantics.db.layer0.exception.MissingVariableValueException; +import org.simantics.db.layer0.exception.PendingVariableException; +import org.simantics.db.layer0.util.Layer0Utils; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.diagram.profile.StyleBase; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.ui.nodes.DeferredRenderingNode; +import org.simantics.district.network.ui.nodes.DistrictNetworkHoverInfoNode; +import org.simantics.district.network.ui.nodes.DistrictNetworkNodeUtils; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.scenegraph.INode; +import org.simantics.scenegraph.ParentNode; +import org.simantics.scenegraph.g2d.nodes.spatial.RTreeNode; +import org.simantics.scenegraph.profile.EvaluationContext; +import org.simantics.scenegraph.profile.common.ProfileVariables; +import org.simantics.scenegraph.utils.NodeUtil; +import org.simantics.scl.compiler.top.ValueNotFound; +import org.simantics.scl.osgi.SCLOsgi; +import org.simantics.scl.runtime.SCLContext; +import org.simantics.scl.runtime.function.Function1; +import org.simantics.scl.runtime.tuple.Tuple3; +import org.simantics.structural.stubs.StructuralResource2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DistrictNetworkHoverInfoStyle extends StyleBase { + + public static final String HOVER_INFO_DEFERRED = "hoverInfo"; + + private static final Logger LOGGER = LoggerFactory.getLogger(DistrictNetworkHoverInfoStyle.class); + + private static final String ACTIONS_MODULE = "Actions"; + private static final String HOVER_CONTRIBUTION = "hoverContribution"; + + public class StyleResult { + Point2D origin; + List labels; + + public StyleResult(Point2D origin, List labels) { + this.origin = origin; + this.labels = labels; + } + + public Point2D getOrigin() { + return origin; + } + + public List getLabels() { + return labels; + } + } + + public DistrictNetworkHoverInfoStyle(Resource style) throws DatabaseException { + super(style); + } + + String currentRowKey; + + protected Resource getConfigurationComponent(ReadGraph graph, Resource element) throws DatabaseException { + ModelingResources MOD = ModelingResources.getInstance(graph); + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + + Resource mappedElement = graph.getPossibleObject(element, DN.MappedComponent); + return mappedElement != null ? graph.getPossibleObject(mappedElement, MOD.ElementToComponent) : null; + } + + @Override + public StyleResult calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, + Resource mapElement, Variable configuration) throws DatabaseException { + DiagramResource DIA = DiagramResource.getInstance(graph); + StructuralResource2 STR = StructuralResource2.getInstance(graph); + + String variableURI = graph.getPossibleRelatedValue(runtimeDiagram, DIA.RuntimeDiagram_HasVariable, Bindings.STRING); + Variable activeVariable = org.simantics.db.layer0.variable.Variables.getPossibleVariable(graph, variableURI); + if (activeVariable == null) + return null; + + Resource module = DistrictNetworkUtil.getMappedComponentCached(graph, mapElement); + if (module == null) + return null; + + Resource moduleType = graph.getPossibleType(module, STR.Component); + if (moduleType == null) + return null; + + Function1> function = getUCTextGridFunctionCached(graph, moduleType); + if (function == null) + return null; + + List result; + try { + Variable variable = Variables.getVariable(graph, module); + Variable moduleVariable = Variables.possibleActiveVariable(graph, variable); + if (moduleVariable == null) + moduleVariable = variable; + + result = Simantics.applySCLRead(graph, function, moduleVariable); + } catch (PendingVariableException | MissingVariableValueException e) { + result = Collections.singletonList(new Tuple3("", "", "")); + } catch (MissingVariableException e) { + // the requested variable is missing from the UC + String message = e.getMessage(); + LOGGER.warn("Missing variable for calculating style with function {} {}", function, message); + result = Collections.singletonList(new Tuple3("<" + message +">", "", "")); + } + + Point2D point; + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + if (graph.isInstanceOf(mapElement, DN.Vertex)) { + double[] coords = graph.getRelatedValue(mapElement, DIA.HasLocation); + point = DistrictNetworkNodeUtils.calculatePoint2D(new Point2D.Double(coords[0], coords[1]), null); + } + else if (graph.isInstanceOf(mapElement, DN.Edge)) { + Resource v1 = graph.getSingleObject(mapElement, DN.HasStartVertex); + double[] coords1 = graph.getRelatedValue(v1, DIA.HasLocation); + Resource v2 = graph.getSingleObject(mapElement, DN.HasEndVertex); + double[] coords2 = graph.getRelatedValue(v2, DIA.HasLocation); + point = DistrictNetworkNodeUtils.calculatePoint2D(new Point2D.Double((coords1[0] + coords2[0]) / 2, (coords1[1] + coords2[1]) / 2), null); + } + else { + return null; + } + + return new StyleResult(point, result); + } + + @Override + public void applyStyleForNode(EvaluationContext observer, INode parent, StyleResult results) { + if (results == null) { + cleanupStyleForNode(observer, parent); + return; + } + + DistrictNetworkHoverInfoNode node = ProfileVariables.claimChild(parent, "*", DistrictNetworkHoverInfoNode.NODE_KEY, DistrictNetworkHoverInfoNode.class, observer); + if (node == null) + return; + + ParentNode root = (ParentNode) NodeUtil.getNearestParentOfType(parent, RTreeNode.class); + if (root != null) { + DeferredRenderingNode deferred = ProfileVariables.claimChild(root, "", HOVER_INFO_DEFERRED, DeferredRenderingNode.class, observer); + deferred.setZIndex(Integer.MAX_VALUE); + } + + node.setLabels(results.getLabels()); + node.setOrigin(results.getOrigin()); + } + + @Override + protected void cleanupStyleForNode(EvaluationContext observer, INode node) { + ProfileVariables.denyChild(node, "*", DistrictNetworkHoverInfoNode.NODE_KEY); + } + + private static Function1> getUCTextGridFunctionCached(ReadGraph graph, Resource componentType) + throws DatabaseException { + return graph.syncRequest(new UCTextGridFunctionRequest(componentType), TransientCacheListener.instance()); + } + + private static final class UCTextGridFunctionRequest extends ResourceRead>> { + public UCTextGridFunctionRequest(Resource resource) { + super(resource); + } + + @SuppressWarnings("unchecked") + @Override + public Function1> perform(ReadGraph graph) throws DatabaseException { + Resource actionsModule = Layer0Utils.getPossibleChild(graph, resource, ACTIONS_MODULE); + if (actionsModule == null || !graph.isInstanceOf(actionsModule, Layer0.getInstance(graph).SCLModule)) + return null; + + String uri = graph.getURI(actionsModule); + SCLContext sclContext = SCLContext.getCurrent(); + Object oldGraph = sclContext.get("graph"); + try { + sclContext.put("graph", graph); + return (Function1>) SCLOsgi.MODULE_REPOSITORY.getValue(uri, HOVER_CONTRIBUTION); + } catch (ValueNotFound e1) { + return null; + } finally { + sclContext.put("graph", oldGraph); + } + } + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/DistrictNetworkStaticInfoStyle.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/DistrictNetworkStaticInfoStyle.java new file mode 100644 index 00000000..27c2fc84 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/DistrictNetworkStaticInfoStyle.java @@ -0,0 +1,203 @@ +package org.simantics.district.network.ui.styles; + +import java.awt.geom.Point2D; +import java.util.Set; + +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.procedure.adapter.TransientCacheListener; +import org.simantics.db.common.request.ResourceRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.exception.MissingVariableException; +import org.simantics.db.layer0.exception.MissingVariableValueException; +import org.simantics.db.layer0.exception.PendingVariableException; +import org.simantics.db.layer0.util.Layer0Utils; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.diagram.profile.StyleBase; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.profile.MidBranchEdgeSetRequest; +import org.simantics.district.network.ui.nodes.DeferredRenderingNode; +import org.simantics.district.network.ui.nodes.DistrictNetworkNodeUtils; +import org.simantics.district.network.ui.nodes.DistrictNetworkStaticInfoNode; +import org.simantics.layer0.Layer0; +import org.simantics.scenegraph.INode; +import org.simantics.scenegraph.ParentNode; +import org.simantics.scenegraph.g2d.nodes.spatial.RTreeNode; +import org.simantics.scenegraph.profile.EvaluationContext; +import org.simantics.scenegraph.profile.common.ProfileVariables; +import org.simantics.scenegraph.utils.NodeUtil; +import org.simantics.scl.compiler.top.ValueNotFound; +import org.simantics.scl.osgi.SCLOsgi; +import org.simantics.scl.runtime.SCLContext; +import org.simantics.scl.runtime.function.Function1; +import org.simantics.structural.stubs.StructuralResource2; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +public class DistrictNetworkStaticInfoStyle extends StyleBase { + + private static final Logger LOGGER = LoggerFactory.getLogger(DistrictNetworkStaticInfoStyle.class); + + private static final String ACTIONS_MODULE = "Actions"; + private static final String PIPELINE_INFO = "pipelineInfo"; + + public static final String STATIC_INFO_DEFERRED = "staticInfo"; + + public static class StyleResult { + public StyleResult(Point2D p1, Point2D p2, String info) { + this.p1 = p1; + this.p2 = p2; + this.info = info; + } + + public Point2D p1; + public Point2D p2; + public String info; + } + + public DistrictNetworkStaticInfoStyle(Resource style) { + super(style); + } + + @Override + public StyleResult calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource mapElement) + throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + + boolean isEdge = graph.isInstanceOf(mapElement, DN.Edge); + boolean isVertex = graph.isInstanceOf(mapElement, DN.Vertex); + if (!isEdge && !isVertex) + return null; + + if (isEdge) { + Resource diagram = graph.getSingleObject(mapElement, Layer0.getInstance(graph).PartOf); + Set edgesToUse = graph.syncRequest(new MidBranchEdgeSetRequest(diagram), TransientCacheListener.instance()); + if (!edgesToUse.contains(mapElement)) + return null; + } + + DiagramResource DIA = DiagramResource.getInstance(graph); + StructuralResource2 STR = StructuralResource2.getInstance(graph); + + Resource module = DistrictNetworkUtil.getMappedComponentCached(graph, mapElement); + if (module == null) + return null; + + Resource moduleType = graph.getPossibleType(module, STR.Component); + if (moduleType == null) + return null; + + Function1 function = getUCPipelineInfoFunctionCached(graph, moduleType); + if (function == null) + return null; + + String result; + try { + Variable variable = Variables.getVariable(graph, module); + Variable moduleVariable = Variables.possibleActiveVariable(graph, variable); + if (moduleVariable == null) + moduleVariable = variable; + + result = Simantics.applySCLRead(graph, function, moduleVariable); + } catch (PendingVariableException | MissingVariableValueException e) { + result = null; + } catch (MissingVariableException e) { + // the requested variable is missing from the UC + String message = e.getMessage(); + LOGGER.warn("Missing variable for calculating style with function {} {}", function, message); + result = "<" + message + ">"; + } + + if (isVertex) { + double[] coords = graph.getRelatedValue(mapElement, DIA.HasLocation); + Point2D p = DistrictNetworkNodeUtils.calculatePoint2D(new Point2D.Double(coords[0], coords[1]), null); + return new StyleResult(p, p, result); + } + else if (isEdge) { + Resource v1 = graph.getSingleObject(mapElement, DN.HasStartVertex); + double[] coords1 = graph.getRelatedValue(v1, DIA.HasLocation); + Resource v2 = graph.getSingleObject(mapElement, DN.HasEndVertex); + double[] coords2 = graph.getRelatedValue(v2, DIA.HasLocation); + Point2D p1 = DistrictNetworkNodeUtils.calculatePoint2D(new Point2D.Double(coords1[0], coords1[1]), null); + Point2D p2 = DistrictNetworkNodeUtils.calculatePoint2D(new Point2D.Double(coords2[0], coords2[1]), null); + + return new StyleResult(p1, p2, result); + } + + return null; + } + + @Override + public void applyStyleForNode(EvaluationContext evaluationContext, INode parent, StyleResult result) { + if (result == null) { + cleanupStyleForNode(evaluationContext, parent); + return; + } + + ParentNode root = (ParentNode) NodeUtil.getNearestParentOfType(parent, RTreeNode.class); + if (root != null) { + DeferredRenderingNode deferred = ProfileVariables.claimChild(root, "", STATIC_INFO_DEFERRED, DeferredRenderingNode.class, evaluationContext); + deferred.setZIndex(Integer.MAX_VALUE-1); + } + + DistrictNetworkStaticInfoNode node = ProfileVariables.claimChild(parent, "*", DistrictNetworkStaticInfoNode.NODE_KEY, DistrictNetworkStaticInfoNode.class, evaluationContext); + if (node == null) + return; + + Point2D p1 = result.p1; + Point2D p2 = result.p2; + + if (p1.equals(p2)) { + node.setLocation(p1, new Point2D.Double(1.0, 0.0)); + } + else { + double sign = Math.signum(p1.getX() - p2.getX()); + Point2D.Double origin = new Point2D.Double(0.5 * (p1.getX() + p2.getX()), 0.5 * (p1.getY() + p2.getY())); + Point2D direction = new Point2D.Double(0.5 * sign * (p1.getX() - p2.getX()), 0.5 * sign * (p1.getY() - p2.getY())); + + node.setLocation(origin, direction); + } + + node.setInfo(result.info); + } + + private static Function1 getUCPipelineInfoFunctionCached(ReadGraph graph, Resource componentType) + throws DatabaseException { + return graph.syncRequest(new UCPipelineInfoRequest(componentType), TransientCacheListener.instance()); + } + + private static final class UCPipelineInfoRequest extends ResourceRead> { + public UCPipelineInfoRequest(Resource resource) { + super(resource); + } + + @SuppressWarnings("unchecked") + @Override + public Function1 perform(ReadGraph graph) throws DatabaseException { + Resource actionsModule = Layer0Utils.getPossibleChild(graph, resource, ACTIONS_MODULE); + if (actionsModule == null || !graph.isInstanceOf(actionsModule, Layer0.getInstance(graph).SCLModule)) + return null; + + String uri = graph.getURI(actionsModule); + SCLContext sclContext = SCLContext.getCurrent(); + Object oldGraph = sclContext.get("graph"); + try { + sclContext.put("graph", graph); + return (Function1) SCLOsgi.MODULE_REPOSITORY.getValue(uri, PIPELINE_INFO); + } catch (ValueNotFound e1) { + return null; + } finally { + sclContext.put("graph", oldGraph); + } + } + } + + @Override + protected void cleanupStyleForNode(EvaluationContext evaluationContext, INode node) { + ProfileVariables.denyChild(node, "*", DistrictNetworkStaticInfoNode.NODE_KEY); + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/ElevationRectangleStyle.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/ElevationRectangleStyle.java new file mode 100644 index 00000000..59506e75 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/ElevationRectangleStyle.java @@ -0,0 +1,68 @@ +package org.simantics.district.network.ui.styles; + +import java.awt.geom.Rectangle2D; +import java.util.Collection; +import java.util.Collections; + +import org.simantics.db.RequestProcessor; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.district.network.ui.nodes.ElevationServerNode; +import org.simantics.maps.elevation.server.SingletonTiffTileInterface; +import org.simantics.scenegraph.g2d.IG2DNode; +import org.simantics.scenegraph.profile.EvaluationContext; +import org.simantics.scenegraph.profile.Group; +import org.simantics.scenegraph.profile.Style; +import org.simantics.scenegraph.profile.common.ObserverGroupListener; +import org.simantics.scenegraph.utils.NodeUtil; + +public class ElevationRectangleStyle implements Style { + + private ObserverGroupListener listener = null; + private double priority; + + @Override + public void activate(RequestProcessor backend, Resource runtimeDiagram, Resource entry, Group group, EvaluationContext observer) throws DatabaseException { + if (listener != null && !listener.isDisposed()) + return; + listener = new ObserverGroupListener(this, group, observer); + group.trackItems(backend, runtimeDiagram, listener); + } + + @Override + public void deactivate(Resource runtimeDiagram, Resource entry, Group group, EvaluationContext observer) { + if (listener != null && !listener.isDisposed()) { + setRectangles(observer, Collections.emptyList()); + listener.dispose(); + observer.update(); + } + } + + @Override + public void apply(Resource entry, Group group, EvaluationContext observer) { + apply2(entry, observer); + } + + @Override + public void apply2(Object item, EvaluationContext observer) { + setRectangles(observer, SingletonTiffTileInterface.getBoundingBoxes()); + } + + private void setRectangles(EvaluationContext observer, Collection rectangles) { + IG2DNode node = NodeUtil.getNearestChildByClass(observer.getSceneGraph(), ElevationServerNode.class); + if (node != null) { + ((ElevationServerNode) node).setRectangles(rectangles); + } + } + + @Override + public void setPriority(double priority) { + this.priority = priority; + } + + @Override + public double getPriority() { + return priority; + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ColumnHeaderTableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ColumnHeaderTableDataProvider.java new file mode 100644 index 00000000..530870b2 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ColumnHeaderTableDataProvider.java @@ -0,0 +1,31 @@ +package org.simantics.district.network.ui.table; + +import org.eclipse.nebula.widgets.nattable.data.IDataProvider; + +public class ColumnHeaderTableDataProvider implements IDataProvider { + + @Override + public Object getDataValue(int columnIndex, int rowIndex) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setDataValue(int columnIndex, int rowIndex, Object newValue) { + // TODO Auto-generated method stub + + } + + @Override + public int getColumnCount() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getRowCount() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataAction.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataAction.java new file mode 100644 index 00000000..dc8c684a --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataAction.java @@ -0,0 +1,80 @@ +package org.simantics.district.network.ui.table; + +import java.util.Set; + +import org.eclipse.nebula.widgets.nattable.NatTable; +import org.eclipse.nebula.widgets.nattable.coordinate.Range; +import org.eclipse.nebula.widgets.nattable.ui.action.IKeyAction; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.events.KeyEvent; + +public class CustomPasteDataAction implements IKeyAction { + + private DistrictCSVTable table; + + public CustomPasteDataAction(DistrictCSVTable table) { + this.table = table; + } + + @Override + public void run(NatTable natTable, KeyEvent event) { + Object data = table.cpb.getContents(TextTransfer.getInstance()); + if (data instanceof String) { + String textData = (String) data; + String[][] fullData = computeData(textData); + + int[] cols = table.selectionLayer.getSelectedColumnPositions(); + int firstCol = cols[0]; + int column = table.selectionLayer.getColumnIndexByPosition(firstCol); + Set ranges = table.selectionLayer.getSelectedRowPositions(); + if (!ranges.isEmpty()) { + int rowPosition = ranges.iterator().next().start; + int[] columns = new int[fullData.length]; + columns[0] = column; + for (int i = 1; i < fullData.length; i++) + columns[i] = table.selectionLayer.getColumnIndexByPosition(firstCol + i); + table.bodyDataLayer.doCommand(new CustomPasteDataCommand(table.bodyDataLayer, columns, rowPosition, fullData)); + } + } + } + + private static String[][] computeData(String textData) { + String separator; + if (textData.contains(",") && !textData.contains(";")) { + separator = ","; + } else if (textData.contains(";") && !textData.contains("\t")) { + separator = ";"; + } else { + separator = "\\t"; + } + + textData = textData.replaceAll("\\r", ""); + + String[] rows = textData.split("\\n"); + + String[][] cells = new String[rows.length][]; + for(int i=0;i diagrams = new HashMap<>(); + private Map vertexMappings = new HashMap<>(); + + private Composite composite; + private Combo networkDiagramSelectionCombo; + private Combo sourceCRSCombo; + private Combo vertexMappingCombo; + private String sourceCRS; + private Resource diagram; + private Resource mapping; + + public SelectionDialog(Shell parent) { + super(parent); + } + + public Resource getMappingType() { + return mapping; + } + + public Resource getTargetDiagram() { + return diagram; + } + + public String getSourceCRS() { + return "EPSG:" + sourceCRS; + } + + @Override + protected Control createDialogArea(Composite parent) { + composite = (Composite) super.createDialogArea(parent); + createMappingsGroup(composite); + computeContent(); + return composite; + } + + private void computeContent() { + Simantics.getSession().asyncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph graph) throws DatabaseException { + + Resource indexRoot = graph.sync(new IndexRoot(graph.sync(new PossibleActiveModel(Simantics.getProjectResource())))); + vertexMappings = Functions.getVertexMappings(graph, indexRoot); + + Collection diagramss = Functions.getDistrictDiagrams(graph); + ModelingResources MOD = ModelingResources.getInstance(graph); + Resource projectResource = Simantics.getProjectResource(); + String projectURI = graph.getURI(projectResource); + for (Resource diagram : diagramss) { + Resource composite = graph.getSingleObject(diagram, MOD.DiagramToComposite); + String compositeURI = graph.getURI(composite); + String path = compositeURI.replace(projectURI, ""); + diagrams.put(path, diagram); + } + + composite.getDisplay().asyncExec(() -> { + + vertexMappingCombo.setItems(vertexMappings.keySet().toArray(new String[vertexMappings.size()])); + + networkDiagramSelectionCombo.setItems(diagrams.keySet().toArray(new String[diagrams.size()])); + if (diagrams.size() > 0) { + networkDiagramSelectionCombo.select(0); + } + Set codes = CRS.getSupportedCodes("EPSG"); + sourceCRSCombo.setItems(codes.toArray(new String[codes.size()])); + sourceCRSCombo.addModifyListener(new ModifyListener() { + + @Override + public void modifyText(ModifyEvent e) { + String currentText = sourceCRSCombo.getText(); + if (codes.contains(currentText)) { + // Select this + String[] items = sourceCRSCombo.getItems(); + int i; + for (i = 0; i < items.length; i++) { + String item = items[i]; + if (currentText.equals(item)) { + break; + } + } + if (i != 0) { + sourceCRSCombo.select(i); + } else { + System.err.println("Should not happen"); + } + } + } + }); + }); + } + }); + } + + private void createMappingsGroup(Composite parent) { + Group group= new Group(parent, SWT.NONE); + group.setFont(parent.getFont()); + group.setText("Select Diagram & CRS"); + GridDataFactory.fillDefaults().grab(true, false).applyTo(group); + group.setLayout(new GridLayout(1, false)); + + Composite cmposite = new Composite(group, SWT.NONE); + cmposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + cmposite.setLayout(new GridLayout(2, false)); + + Label vertexMappingLabel = new Label(cmposite, SWT.NONE); + vertexMappingLabel.setText("Select Vertex Mapping"); + + vertexMappingCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).applyTo(vertexMappingCombo); + + Label selectNetworkDiagramLabel = new Label(cmposite, SWT.NONE); + selectNetworkDiagramLabel.setText("Select Network Diagram"); + + networkDiagramSelectionCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).applyTo(networkDiagramSelectionCombo); + + Label label = new Label(cmposite, SWT.NONE); + label.setText("Select Source Coordinate Reference System"); + + sourceCRSCombo = new Combo(cmposite, SWT.NONE); + sourceCRSCombo.setToolTipText("Select the coordinate reference system that is used in the source material for possible transformation to target coordinate reference system (EPSG:4326)"); + + GridDataFactory.fillDefaults().grab(true, false).applyTo(sourceCRSCombo); + } + + @Override + protected void computeResult() { + mapping = vertexMappings.get(vertexMappingCombo.getItem(vertexMappingCombo.getSelectionIndex())); + diagram = diagrams.get(networkDiagramSelectionCombo.getItem(networkDiagramSelectionCombo.getSelectionIndex())); + sourceCRS = sourceCRSCombo.getItem(sourceCRSCombo.getSelectionIndex()); + } + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/PasteDataCommandHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/PasteDataCommandHandler.java new file mode 100644 index 00000000..06e9fbd5 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/PasteDataCommandHandler.java @@ -0,0 +1,47 @@ +package org.simantics.district.network.ui.table; + +import java.util.Collection; + +import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler; +import org.eclipse.nebula.widgets.nattable.layer.DataLayer; +import org.eclipse.nebula.widgets.nattable.layer.event.ColumnVisualUpdateEvent; +import org.eclipse.nebula.widgets.nattable.layer.event.StructuralRefreshEvent; +import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; +import org.eclipse.swt.dnd.Clipboard; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PasteDataCommandHandler extends AbstractLayerCommandHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(PasteDataCommandHandler.class); + + private final SelectionLayer selectionLayer; + private final DataLayer dataLayer; + private final Clipboard cpb; + private final TableDataProvider provider; + + public PasteDataCommandHandler(TableDataProvider provider, DataLayer dataLayer, SelectionLayer selectionLayer, Clipboard cpb) { + this.provider = provider; + this.dataLayer = dataLayer; + this.selectionLayer = selectionLayer; + this.cpb = cpb; + } + + @Override + public Class getCommandClass() { + return CustomPasteDataCommand.class; + } + + @Override + protected boolean doCommand(CustomPasteDataCommand command) { + String[][] fullData = command.data; + int pasteRow = command.pasteRow; + Collection pasteColumn = command.getColumnPositions(); + if (pasteRow > -1) { + provider.setDataValues(pasteColumn, pasteRow, fullData); + dataLayer.fireLayerEvent(new StructuralRefreshEvent(dataLayer)); + } + return true; + } +} + diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/RowHeaderTableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/RowHeaderTableDataProvider.java new file mode 100644 index 00000000..b85f6e37 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/RowHeaderTableDataProvider.java @@ -0,0 +1,33 @@ +package org.simantics.district.network.ui.table; + +import org.eclipse.nebula.widgets.nattable.data.IDataProvider; + +public class RowHeaderTableDataProvider implements IDataProvider { + + protected final IDataProvider bodyDataProvider; + + public RowHeaderTableDataProvider(IDataProvider bodyDataProvider) { + this.bodyDataProvider = bodyDataProvider; + } + + @Override + public int getColumnCount() { + return 1; + } + + @Override + public int getRowCount() { + return this.bodyDataProvider.getRowCount(); + } + + @Override + public Object getDataValue(int columnIndex, int rowIndex) { + return Integer.valueOf(rowIndex + 1); + } + + @Override + public void setDataValue(int columnIndex, int rowIndex, Object newValue) { + throw new UnsupportedOperationException(); + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/TableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/TableDataProvider.java new file mode 100644 index 00000000..a8e2a016 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/TableDataProvider.java @@ -0,0 +1,56 @@ +package org.simantics.district.network.ui.table; + +import java.util.Collection; + +import org.eclipse.nebula.widgets.nattable.data.IDataProvider; + +public class TableDataProvider implements IDataProvider { + + private String[][] data = null; + + @Override + public Object getDataValue(int columnIndex, int rowIndex) { + if (data == null) { + return null; + } else { + return data[rowIndex][columnIndex]; + } + } + + @Override + public void setDataValue(int columnIndex, int rowIndex, Object newValue) { + + } + + @Override + public int getColumnCount() { + if (data == null) { + return 10; + } else { + return data[0].length; + } + } + + @Override + public int getRowCount() { + if (data == null) { + return 10; + } else { + return data.length; + } + } + + public boolean isEditable(int columnIndex, int rowIndex) { + return false; + } + + public void setDataValues(Collection pasteColumn, int pasteRow, String[][] fullData) { + // start always from row index 0 and column index 0 + this.data = fullData; + } + + public String[][] getCurrentData() { + return data; + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsUI.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsUI.java new file mode 100644 index 00000000..031e5029 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsUI.java @@ -0,0 +1,1008 @@ +package org.simantics.district.network.ui.visualisations; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IEditorPart; +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.NamedResource; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.procedure.Listener; +import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.profile.ActiveDynamicVisualisationsRequest; +import org.simantics.district.network.profile.DynamicVisualisationsRequest; +import org.simantics.district.network.visualisations.DynamicVisualisationsContributions; +import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicColoringObject; +import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicSizingObject; +import org.simantics.district.network.visualisations.model.ColorBarOptions; +import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsLocation; +import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsSize; +import org.simantics.district.network.visualisations.model.DynamicColorContribution; +import org.simantics.district.network.visualisations.model.DynamicColorMap; +import org.simantics.district.network.visualisations.model.DynamicSizeContribution; +import org.simantics.district.network.visualisations.model.DynamicSizeMap; +import org.simantics.district.network.visualisations.model.DynamicVisualisation; +import org.simantics.district.network.visualisations.model.SizeBarOptions; +import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsLocation; +import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsSize; +import org.simantics.ui.workbench.IResourceEditorPart; +import org.simantics.utils.datastructures.Pair; +import org.simantics.utils.ui.workbench.WorkbenchUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DynamicVisualisationsUI extends Composite { + + private static final Logger LOGGER = LoggerFactory.getLogger(DynamicVisualisationsUI.class); + + private Resource diagramResource; + private VisualisationListener listener; + private DynamicVisualisation visualisation; + + private Button showSizeButton; + private Button sizeTicksButton; + private Combo sizeLocationCombo; + private Combo sizeSizeCombo; + private Button showColorButton; + private Button colorTicksButton; + private Combo colorLocationCombo; + private Combo colorSizeCombo; + + private Combo templateSelectionCombo; + + private List>> colorSuppliers; + + public DynamicVisualisationsUI(Composite parent, int style) { + super(parent, style); + + defaultInitializeUI(); + } + + private void defaultInitializeUI() { + GridDataFactory.fillDefaults().grab(true, true).applyTo(this); + GridLayoutFactory.fillDefaults().numColumns(1).margins(5, 5).applyTo(this); + + Composite selectionComposite = new Composite(this, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).applyTo(selectionComposite); + GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(selectionComposite); + + Label templateNameLabel = new Label(selectionComposite, SWT.NONE); + templateNameLabel.setText("Visualisation template"); + templateSelectionCombo = new Combo(selectionComposite, SWT.READ_ONLY); + GridDataFactory.fillDefaults().grab(true, false).applyTo(templateSelectionCombo); + templateSelectionCombo.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + String item = templateSelectionCombo.getItem(templateSelectionCombo.getSelectionIndex()); + for (NamedResource template : visualisations) { + if (item.equals(template.getName())) { + Simantics.getSession().asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + DistrictNetworkUtil.setActiveVisualisation(graph, diagramResource, template.getResource()); + } + }); + break; + } + } + } + }); + + Composite coloringObjectsComposite = new Composite(this, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).applyTo(coloringObjectsComposite); + GridLayoutFactory.fillDefaults().numColumns(1).applyTo(coloringObjectsComposite); + initializeColoringObjects(coloringObjectsComposite); + + Composite colorBarsComposite = new Composite(this, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).applyTo(colorBarsComposite); + GridLayoutFactory.fillDefaults().numColumns(1).applyTo(colorBarsComposite); + initializeColorBars(colorBarsComposite); + + Composite objectSizesComposite = new Composite(this, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).applyTo(objectSizesComposite); + GridLayoutFactory.fillDefaults().numColumns(1).applyTo(objectSizesComposite); + initializeObjectSizes(objectSizesComposite); + + Composite sizeBarsComposite = new Composite(this, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).applyTo(sizeBarsComposite); + GridLayoutFactory.fillDefaults().numColumns(1).applyTo(sizeBarsComposite); + initializeSizeBars(sizeBarsComposite); + + Button saveVisualisationTemplateButton = new Button(this, SWT.NONE); + saveVisualisationTemplateButton.setText("Save"); + saveVisualisationTemplateButton.setEnabled(visualisation == null || visualisation.getVisualisationResource() != null); + saveVisualisationTemplateButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + persistVisualisationTemplate(visualisation.getName(), Optional.of(visualisation.getVisualisationResource())); + } + }); + + Button saveVisualisationTemplateAsButton = new Button(this, SWT.NONE); + saveVisualisationTemplateAsButton.setText("Save as visualisation template"); + saveVisualisationTemplateAsButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + showSaveVisualisationTemplateDialog(e.widget.getDisplay().getActiveShell()); + } + }); + + } + + private void showSaveVisualisationTemplateDialog(Shell shell) { + + InputDialog dialog = new InputDialog(shell, "Save visualisation template", "Give template a name", "", new IInputValidator() { + + @Override + public String isValid(String newText) { + if (newText == null || newText.isEmpty()) + return "Name cannot be empty"; + return null; + } + }); + + if (dialog.open() == Dialog.OK) { + String name = dialog.getValue(); + persistVisualisationTemplate(name, Optional.empty()); + } + } + + private void persistVisualisationTemplate(String templateName, Optional existing) { + + List> colorCollect = colorSuppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList()); + + String colorLocation = colorLocationCombo.getItem(colorLocationCombo.getSelectionIndex()); + String colorSize = colorSizeCombo.getItem(colorSizeCombo.getSelectionIndex()); + + ColorBarOptions colorBarOptions = new ColorBarOptions() + .showColorBars(showColorButton.getSelection()) + .showColorBarsTicks(colorTicksButton.getSelection()) + .withLocation(ColorBarsLocation.valueOf(colorLocation)) + .withSize(ColorBarsSize.valueOf(colorSize)); + + List> sizeCollect = sizeSuppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList()); + + String sizeLocation = sizeLocationCombo.getItem(sizeLocationCombo.getSelectionIndex()); + String sizeSize = sizeSizeCombo.getItem(sizeSizeCombo.getSelectionIndex()); + + SizeBarOptions sizeBarOptions = new SizeBarOptions() + .showSizeBars(showSizeButton.getSelection()) + .showSizeBarsTicks(sizeTicksButton.getSelection()) + .withLocation(SizeBarsLocation.valueOf(sizeLocation)) + .withSize(SizeBarsSize.valueOf(sizeSize)); + + Simantics.getSession().asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Resource exist; + if (existing.isPresent()) { + exist = existing.get(); + } else { + exist = DistrictNetworkUtil.createVisualisation(graph, diagramResource, templateName); + } + + DistrictNetworkUtil.setColorContributions(graph, exist, colorCollect); + + DistrictNetworkUtil.setColorBarOptions(graph, exist, colorBarOptions); + DistrictNetworkUtil.setSizeContributions(graph, exist, sizeCollect); + DistrictNetworkUtil.setSizeBarOptions(graph, exist, sizeBarOptions); + } + }); + } + + private void initializeColoringObjects(Composite parent) { + Group group = new Group(parent, SWT.NONE); + group.setText("Coloring Objects"); + GridDataFactory.fillDefaults().grab(true, false).applyTo(group); + GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group); + + { + createColoringObjectHeaderRow(group); + } + colorSuppliers = new ArrayList<>(); + { + try { + Collection result = Simantics.getSession().syncRequest(new UniqueRead>() { + + @Override + public Collection perform(ReadGraph graph) throws DatabaseException { + return DynamicVisualisationsContributions.dynamicColoringObjects(graph); + } + }); + + for (DynamicColoringObject object : result) { + colorSuppliers.add(createColoringObjectRow(group, object)); + } + + } catch (DatabaseException e) { + LOGGER.error("Could not create coloring objecst", e); + } + } + { + Button applyButton = new Button(group, SWT.NONE); + applyButton.setText("Apply"); + applyButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + List> collect = colorSuppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList()); + Simantics.getSession().asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + DistrictNetworkUtil.setColorContributions(graph, visualisation.getVisualisationResource(), collect); + } + }); + } + }); + } + } + + private void createColoringObjectHeaderRow(Composite parent) { + + Label label = new Label(parent, SWT.NONE); + label.setText("Label"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Used"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Variable"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Min"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Max"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Unit"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("ColorMap"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Default"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + } + + private Map coloringRows = new HashMap<>(); + private Map sizingRows = new HashMap<>(); + + private VisualisationsListener visualisationsListener; + + private Collection visualisations; + + private List>> sizeSuppliers; + + private static class ColoringObjectRow { + + private final Label label; + private final Button usedButton; + private final Combo variableCombo; + private final Text minText; + private final Text maxText; + private final Label unit; + private final Combo colorMapCombo; + private final Button defaultButton; + + public ColoringObjectRow(Label label, Button usedButton, Combo variableCombo, Text minText, Text maxText, Label unit, + Combo colorMapCombo, Button defaultButton) { + super(); + this.label = label; + this.usedButton = usedButton; + this.variableCombo = variableCombo; + this.minText = minText; + this.maxText = maxText; + this.unit = unit; + this.colorMapCombo = colorMapCombo; + this.defaultButton = defaultButton; + } + + public void update(DynamicColorContribution colorContribution) { + String[] items = variableCombo.getItems(); + for (int i = 0; i < items.length; i++) { + if (colorContribution.getLabel().equals(items[i])) { + variableCombo.select(i); + break; + } + } + minText.setText(Double.toString(colorContribution.getDefaultMin())); + maxText.setText(Double.toString(colorContribution.getDefaultMax())); + unit.setText(colorContribution.getUnit()); + + // color map only supports single for now + colorMapCombo.setItems(colorContribution.getDefaultColorMap().getLabel()); + colorMapCombo.select(0); +// String[] colorItems = colorMapCombo.getItems(); +// for (int i = 0; i < colorItems.length; i++) { +// +// if (colorContribution.getDefaultColorMap().getLabel().equals(colorItems[i])) { +// colorMapCombo.select(i); +// break; +// } +// } + usedButton.setSelection(colorContribution.isUsed()); + defaultButton.setSelection(colorContribution.isUseDefault()); + + minText.setEnabled(!colorContribution.isUseDefault()); + maxText.setEnabled(!colorContribution.isUseDefault()); + colorMapCombo.setEnabled(!colorContribution.isUseDefault()); + } + } + + private static class SizingObjectRow { + + private final Label label; + private final Button usedButton; + private final Combo variableCombo; + private final Text minText; + private final Text maxText; + private final Label unit; + private final Combo sizeMapCombo; + private final Button defaultButton; + + public SizingObjectRow(Label label, Button usedButton, Combo variableCombo, Text minText, Text maxText, Label unit, + Combo sizeMapCombo, Button defaultButton) { + super(); + this.label = label; + this.usedButton = usedButton; + this.variableCombo = variableCombo; + this.minText = minText; + this.maxText = maxText; + this.unit = unit; + this.sizeMapCombo = sizeMapCombo; + this.defaultButton = defaultButton; + } + + public void update(DynamicSizeContribution sizeContribution) { + String[] items = variableCombo.getItems(); + for (int i = 0; i < items.length; i++) { + if (sizeContribution.getLabel().equals(items[i])) { + variableCombo.select(i); + break; + } + } + minText.setText(Double.toString(sizeContribution.getDefaultMin())); + maxText.setText(Double.toString(sizeContribution.getDefaultMax())); + unit.setText(sizeContribution.getUnit()); + + // color map only supports single for now + sizeMapCombo.setItems(sizeContribution.getDefaultSizeMap().getLabel()); + sizeMapCombo.select(0); +// String[] colorItems = colorMapCombo.getItems(); +// for (int i = 0; i < colorItems.length; i++) { +// +// if (colorContribution.getDefaultColorMap().getLabel().equals(colorItems[i])) { +// colorMapCombo.select(i); +// break; +// } +// } + } + } + + private Supplier> createColoringObjectRow(Composite parent, DynamicColoringObject object) { + Label label = new Label(parent, SWT.NONE); + label.setText(object.getColoringObject().getName()); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label); + + Map colorContributions = object.getColorContributions(); + + Button usedButton = new Button(parent, SWT.CHECK); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton); + + Combo variableCombo = new Combo(parent, SWT.READ_ONLY); + variableCombo.setItems(colorContributions.keySet().toArray(new String[colorContributions.size()])); + + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo); + + Text minText = new Text(parent, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(minText); + + Text maxText = new Text(parent, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(maxText); + + Label unit = new Label(parent, SWT.NONE); + unit.setText(""); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(unit); + + Combo colorMapCombo = new Combo(parent, SWT.READ_ONLY); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(colorMapCombo); + + Button defaultButton = new Button(parent, SWT.CHECK); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton); + defaultButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + int index = variableCombo.getSelectionIndex(); + if (index >= 0) { + String key = variableCombo.getItem(index); + DynamicColorContribution cont = colorContributions.get(key); + + minText.setText(Double.toString(cont.getDefaultMin())); + minText.setEnabled(!defaultButton.getSelection()); + maxText.setText(Double.toString(cont.getDefaultMax())); + maxText.setEnabled(!defaultButton.getSelection()); + unit.setText(cont.getUnit()); + + colorMapCombo.setItems(cont.getDefaultColorMap().getLabel()); + colorMapCombo.select(0); + colorMapCombo.setEnabled(!defaultButton.getSelection()); + } + } + }); + + variableCombo.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + // handle update for others + int index = variableCombo.getSelectionIndex(); + if (index >= 0) { + String key = variableCombo.getItem(index); + DynamicColorContribution cont = colorContributions.get(key); + + if (minText.getText().isEmpty()) { + minText.setText(Double.toString(cont.getDefaultMin())); + } + if (maxText.getText().isEmpty()) { + maxText.setText(Double.toString(cont.getDefaultMax())); + } + unit.setText(cont.getUnit()); + + colorMapCombo.setItems(cont.getDefaultColorMap().getLabel()); + colorMapCombo.select(0); + + defaultButton.setSelection(true); + } + } + }); + + coloringRows.put(object.getColoringObject().getName(), new ColoringObjectRow(label, usedButton, variableCombo, minText, maxText, unit, colorMapCombo, defaultButton)); + + return new Supplier>() { + + @Override + public Pair get() { + int selectionIndex = variableCombo.getSelectionIndex(); + if (selectionIndex >= 0) { + String key = variableCombo.getItem(selectionIndex); + DynamicColorContribution cont = colorContributions.get(key); + if (cont != null) { + String colorMap = colorMapCombo.getItem(colorMapCombo.getSelectionIndex()); + try { + Map colorMaps = Simantics.getSession().syncRequest(new UniqueRead>() { + + @Override + public Map perform(ReadGraph graph) throws DatabaseException { + return DynamicVisualisationsContributions.dynamicColorMaps(graph); + } + }); + DynamicColorMap dColorMap = colorMaps.get(colorMap); + String label = variableCombo.getItem(variableCombo.getSelectionIndex()); + + DynamicColorContribution dcc = new DynamicColorContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText())); + dcc.setUsed(usedButton.getSelection()); + dcc.setUseDefault(defaultButton.getSelection()); + + return Pair.make(object.getColoringObject().getName(), dcc); + } catch (DatabaseException e) { + LOGGER.error("Could not get DynamicColorContribution", e); + } + } + } + return null; + } + }; + } + + private void createSizingObjectHeaderRow(Composite parent) { + + Label label = new Label(parent, SWT.NONE); + label.setText("Label"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Used"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Variable"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Min"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Max"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Unit"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("SizeMap"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + + label = new Label(parent, SWT.NONE); + label.setText("Default"); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label); + } + + private Supplier> createSizingObjectRow(Composite parent, DynamicSizingObject object) { + Label label = new Label(parent, SWT.NONE); + label.setText(object.getSizingObject().getName()); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label); + + Map sizeContributions = object.getSizeContributions(); + + Button usedButton = new Button(parent, SWT.CHECK); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton); + + Combo variableCombo = new Combo(parent, SWT.READ_ONLY); + variableCombo.setItems(sizeContributions.keySet().toArray(new String[sizeContributions.size()])); + + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo); + + Text minText = new Text(parent, SWT.BORDER); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(minText); + + Text maxText = new Text(parent, SWT.BORDER); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(maxText); + + Label unit = new Label(parent, SWT.NONE); + unit.setText(""); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(unit); + + Combo sizeMapCombo = new Combo(parent, SWT.READ_ONLY); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(sizeMapCombo); + + Button defaultButton = new Button(parent, SWT.CHECK); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton); + + variableCombo.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + // handle update for others + String key = variableCombo.getItem(variableCombo.getSelectionIndex()); + DynamicSizeContribution cont = sizeContributions.get(key); + + if (minText.getText().isEmpty()) { + minText.setText(Double.toString(cont.getDefaultMin())); + } + if (maxText.getText().isEmpty()) { + maxText.setText(Double.toString(cont.getDefaultMax())); + } + unit.setText(cont.getUnit()); + + sizeMapCombo.setItems(cont.getDefaultSizeMap().getLabel()); + sizeMapCombo.select(0); + + defaultButton.setSelection(true); + } + }); + + sizingRows.put(object.getSizingObject().getName(), new SizingObjectRow(label, usedButton, variableCombo, minText, maxText, unit, sizeMapCombo, defaultButton)); + + return new Supplier>() { + + @Override + public Pair get() { + int selectionIndex = variableCombo.getSelectionIndex(); + if (selectionIndex >= 0) { + String key = variableCombo.getItem(selectionIndex); + DynamicSizeContribution cont = sizeContributions.get(key); + if (cont != null) { + String sizeMap = sizeMapCombo.getItem(sizeMapCombo.getSelectionIndex()); + try { + Map sizeMaps = Simantics.getSession().syncRequest(new UniqueRead>() { + + @Override + public Map perform(ReadGraph graph) throws DatabaseException { + return DynamicVisualisationsContributions.dynamicSizeMaps(graph); + } + }); + DynamicSizeMap dColorMap = sizeMaps.get(sizeMap); + String label = variableCombo.getItem(variableCombo.getSelectionIndex()); + + DynamicSizeContribution dsc = new DynamicSizeContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText())); + dsc.setUsed(usedButton.getSelection()); + dsc.setUseDefault(defaultButton.getSelection()); + + return Pair.make(object.getSizingObject().getName(), dsc); + } catch (DatabaseException e) { + LOGGER.error("Could not get DynamicColorContribution", e); + } + } + } + return null; + } + }; + } + + private void initializeColorBars(Composite parent) { + Group group = new Group(parent, SWT.NONE); + group.setText("Color Bars"); + GridDataFactory.fillDefaults().grab(true, false).applyTo(group); + GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(group); + + createColorBars(group); + } + + private void createColorBars(Composite parent) { + + showColorButton = new Button(parent, SWT.CHECK); + showColorButton.setText("Show"); + + colorTicksButton = new Button(parent, SWT.CHECK); + colorTicksButton.setText("Ticks"); + + Label label = new Label(parent, SWT.NONE); + label.setText("Location"); + colorLocationCombo = new Combo(parent, SWT.READ_ONLY); + colorLocationCombo.setItems(Stream.of(ColorBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new)); + + label = new Label(parent, SWT.NONE); + label.setText("Size"); + colorSizeCombo = new Combo(parent, SWT.READ_ONLY); + colorSizeCombo.setItems(Stream.of(ColorBarsSize.values()).map(size -> size.toString()).toArray(String[]::new)); + + Button applyButton = new Button(parent, SWT.NONE); + applyButton.setText("Apply"); + + applyButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + // persist changes + IEditorPart activeEditor = WorkbenchUtils.getActiveEditor(); + if (activeEditor instanceof IResourceEditorPart) { + + String colorLocation = colorLocationCombo.getItem(colorLocationCombo.getSelectionIndex()); + String colorSize = colorSizeCombo.getItem(colorSizeCombo.getSelectionIndex()); + + ColorBarOptions options = new ColorBarOptions() + .showColorBars(showColorButton.getSelection()) + .showColorBarsTicks(colorTicksButton.getSelection()) + .withLocation(ColorBarsLocation.valueOf(colorLocation)) + .withSize(ColorBarsSize.valueOf(colorSize)); + + Simantics.getSession().asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + DistrictNetworkUtil.setColorBarOptions(graph, visualisation.getVisualisationResource(), options); + } + }); + } + } + }); + } + + private void initializeObjectSizes(Composite parent) { + Group group = new Group(parent, SWT.NONE); + group.setText("Object Sizes"); + GridDataFactory.fillDefaults().grab(true, false).applyTo(group); + GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group); + + { + createSizingObjectHeaderRow(group); + createObjectSizes(group); + } + } + + private void createObjectSizes(Composite parent) { + + sizeSuppliers = new ArrayList<>(); + try { + Collection resultSizing = Simantics.getSession().syncRequest(new UniqueRead>() { + + @Override + public Collection perform(ReadGraph graph) throws DatabaseException { + return DynamicVisualisationsContributions.dynamicSizingObjects(graph); + } + }); + + for (DynamicSizingObject object : resultSizing) { + sizeSuppliers.add(createSizingObjectRow(parent, object)); + } + } catch (DatabaseException e) { + LOGGER.error("Could not create object sizes", e); + } + + { + Button applyButton = new Button(parent, SWT.NONE); + applyButton.setText("Apply"); + applyButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + List> collect = sizeSuppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList()); + Simantics.getSession().asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + DistrictNetworkUtil.setSizeContributions(graph, visualisation.getVisualisationResource(), collect); + } + }); + } + }); + } + } + + private void initializeSizeBars(Composite parent) { + Group group = new Group(parent, SWT.NONE); + group.setText("Size Bars"); + GridDataFactory.fillDefaults().grab(true, false).applyTo(group); + GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(group); + + createSizeBars(group); + } + + private void createSizeBars(Composite parent) { + showSizeButton = new Button(parent, SWT.CHECK); + showSizeButton.setText("Show"); + + sizeTicksButton = new Button(parent, SWT.CHECK); + sizeTicksButton.setText("Ticks"); + + Label label = new Label(parent, SWT.NONE); + label.setText("Location"); + sizeLocationCombo = new Combo(parent, SWT.READ_ONLY); + sizeLocationCombo.setItems(Stream.of(SizeBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new)); + + label = new Label(parent, SWT.NONE); + label.setText("Size"); + sizeSizeCombo = new Combo(parent, SWT.READ_ONLY); + sizeSizeCombo.setItems(Stream.of(SizeBarsSize.values()).map(size -> size.toString()).toArray(String[]::new)); + + Button applyButton = new Button(parent, SWT.READ_ONLY); + applyButton.setText("Apply"); + + applyButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + // persist changes + IEditorPart activeEditor = WorkbenchUtils.getActiveEditor(); + if (activeEditor instanceof IResourceEditorPart) { + + String sizeLocation = sizeLocationCombo.getItem(sizeLocationCombo.getSelectionIndex()); + String sizeSize = sizeSizeCombo.getItem(sizeSizeCombo.getSelectionIndex()); + + SizeBarOptions options = new SizeBarOptions() + .showSizeBars(showSizeButton.getSelection()) + .showSizeBarsTicks(sizeTicksButton.getSelection()) + .withLocation(SizeBarsLocation.valueOf(sizeLocation)) + .withSize(SizeBarsSize.valueOf(sizeSize)); + + Simantics.getSession().asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + DistrictNetworkUtil.setSizeBarOptions(graph, visualisation.getVisualisationResource(), options); + } + }); + } + } + }); + } + + public void setDiagramResource(Resource diagramResource) { + if (this.diagramResource != diagramResource) { + this.diagramResource = diagramResource; + updateListening(); + } + } + + private void updateListening() { + if (visualisationsListener != null) + visualisationsListener.dispose(); + visualisationsListener = new VisualisationsListener(this); + Simantics.getSession().asyncRequest(new DynamicVisualisationsRequest(diagramResource), visualisationsListener); + + if (listener != null) + listener.dispose(); + listener = new VisualisationListener(this); + Simantics.getSession().asyncRequest(new ActiveDynamicVisualisationsRequest(diagramResource), listener); + } + + private static class VisualisationsListener implements Listener> { + + private static final Logger LOGGER = LoggerFactory.getLogger(VisualisationsListener.class); + + private boolean disposed; + private DynamicVisualisationsUI ui; + + public VisualisationsListener(DynamicVisualisationsUI ui) { + this.ui = ui; + } + + @Override + public void execute(Collection result) { + ui.updateVisualisations(result); + } + + @Override + public void exception(Throwable t) { + LOGGER.error("Could not listen visualisation", t); + } + + @Override + public boolean isDisposed() { + return disposed || ui.isDisposed(); + } + + public void dispose() { + this.disposed = true; + } + } + + private static class VisualisationListener implements Listener { + + private static final Logger LOGGER = LoggerFactory.getLogger(VisualisationListener.class); + + private boolean disposed; + private DynamicVisualisationsUI ui; + + public VisualisationListener(DynamicVisualisationsUI ui) { + this.ui = ui; + } + + @Override + public void execute(DynamicVisualisation result) { + ui.updateVisualisation(result); + } + + @Override + public void exception(Throwable t) { + LOGGER.error("Could not listen visualisation", t); + } + + @Override + public boolean isDisposed() { + return disposed || ui.isDisposed(); + } + + public void dispose() { + this.disposed = true; + } + } + + public void updateVisualisation(DynamicVisualisation result) { + this.visualisation = result; + if (visualisation != null) { + Display.getDefault().asyncExec(() -> { + if (getParent().isDisposed()) + return; + + + String[] items = templateSelectionCombo.getItems(); + for (int i = 0; i < items.length; i++) { + if (visualisation.getName().equals(items[i])) { + templateSelectionCombo.select(i); + break; + } + } + + Map colorContributions = visualisation.getColorContributions(); + for (Entry entry : colorContributions.entrySet()) { + + ColoringObjectRow coloringObjectRow = coloringRows.get(entry.getKey()); + if (coloringObjectRow != null) { + + coloringObjectRow.update(entry.getValue()); + + } else { + LOGGER.info("No coloring object visualisation row for key {}", entry.getKey()); + } + } + ColorBarOptions colorOptions = visualisation.getColorBarOptions(); + showColorButton.setSelection(colorOptions.isShowColorBars()); + colorTicksButton.setSelection(colorOptions.isShowColorBarsTicks()); + for (int i = 0; i < colorLocationCombo.getItems().length; i++) { + String item = colorLocationCombo.getItem(i); + if (item.equals(colorOptions.getLocation().toString())) { + colorLocationCombo.select(i); + break; + } + } + for (int i = 0; i < colorSizeCombo.getItems().length; i++) { + String item = colorSizeCombo.getItem(i); + if (item.equals(colorOptions.getSize().toString())) { + colorSizeCombo.select(i); + break; + } + } + + Map sizeContributions = visualisation.getSizeContributions(); + for (Entry entry : sizeContributions.entrySet()) { + + SizingObjectRow sizingObjectRow = sizingRows.get(entry.getKey()); + if (sizingObjectRow != null) { + + sizingObjectRow.update(entry.getValue()); + + } else { + LOGGER.info("No sizing object visualisation row for key {}", entry.getKey()); + } + } + SizeBarOptions sizeOptions = visualisation.getSizeBarOptions(); + showSizeButton.setSelection(sizeOptions.isShowSizeBars()); + sizeTicksButton.setSelection(sizeOptions.isShowSizeBarsTicks()); + for (int i = 0; i < sizeLocationCombo.getItems().length; i++) { + String item = sizeLocationCombo.getItem(i); + if (item.equals(sizeOptions.getLocation().toString())) { + sizeLocationCombo.select(i); + break; + } + } + for (int i = 0; i < sizeSizeCombo.getItems().length; i++) { + String item = sizeSizeCombo.getItem(i); + if (item.equals(sizeOptions.getSize().toString())) { + sizeSizeCombo.select(i); + break; + } + } + }); + } + } + + public void updateVisualisations(Collection result) { + this.visualisations = result; + + Display.getDefault().asyncExec(() -> { + if (getParent().isDisposed()) + return; + templateSelectionCombo.setItems(visualisations.stream().map(NamedResource::getName).collect(Collectors.toList()).toArray(new String[visualisations.size()])); + }); + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsView.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsView.java new file mode 100644 index 00000000..d98a3787 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsView.java @@ -0,0 +1,86 @@ +package org.simantics.district.network.ui.visualisations; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.e4.ui.workbench.modeling.IPartListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.internal.e4.compatibility.CompatibilityEditor; +import org.simantics.district.network.ui.DistrictDiagramEditor; +import org.simantics.ui.workbench.IResourceEditorInput; +import org.simantics.utils.ui.workbench.WorkbenchUtils; + +public class DynamicVisualisationsView { + + @Inject + ESelectionService selectionService; + + @Inject + EPartService partService; + + private DynamicVisualisationsUI ui; + + @PostConstruct + public void postConstruct(Composite parent) { + ui = new DynamicVisualisationsUI(parent, 0); + + IEditorPart editor = WorkbenchUtils.getActiveEditor(); + if (editor instanceof DistrictDiagramEditor) + setDiagramResource(editor.getEditorInput()); + + partService.addPartListener(partListener); + } + + @PreDestroy + public void dispose() { + partService.removePartListener(partListener); + ui.dispose(); + ui = null; + } + + private void setDiagramResourceFromCompatibilityEditor(MPart part) { + if (part.getObject() instanceof CompatibilityEditor) { + CompatibilityEditor editor = (CompatibilityEditor) part.getObject(); + IEditorPart editorPart = editor.getEditor(); + setDiagramResource(editorPart.getEditorInput()); + } + } + + private void setDiagramResource(IEditorInput input) { + if (input instanceof IResourceEditorInput) { + ui.setDiagramResource(((IResourceEditorInput) input).getResource()); + } + } + + private IPartListener partListener = new IPartListener() { + + @Override + public void partVisible(MPart part) { + setDiagramResourceFromCompatibilityEditor(part); + } + + @Override + public void partHidden(MPart part) { + } + + @Override + public void partDeactivated(MPart part) { + } + + @Override + public void partBroughtToTop(MPart part) { + setDiagramResourceFromCompatibilityEditor(part); + } + + @Override + public void partActivated(MPart part) { + setDiagramResourceFromCompatibilityEditor(part); + } + }; +} diff --git a/org.simantics.district.network/META-INF/MANIFEST.MF b/org.simantics.district.network/META-INF/MANIFEST.MF index 58fd3ab4..77e7c464 100644 --- a/org.simantics.district.network/META-INF/MANIFEST.MF +++ b/org.simantics.district.network/META-INF/MANIFEST.MF @@ -15,7 +15,13 @@ Require-Bundle: org.simantics.db, org.simantics.diagram, org.simantics.scenegraph.profile;bundle-version="1.0.0", org.simantics;bundle-version="1.0.0", - org.slf4j.api + org.slf4j.api, + org.simantics.maps.elevation.server;bundle-version="1.0.0", + org.simantics.modeling, + org.simantics.db.indexing, + org.simantics.scl.osgi Export-Package: org.simantics.district.network, org.simantics.district.network.changeset, - org.simantics.district.network.profile + org.simantics.district.network.profile, + org.simantics.district.network.visualisations, + org.simantics.district.network.visualisations.model diff --git a/org.simantics.district.network/adapters.xml b/org.simantics.district.network/adapters.xml index fddb3b74..dc464c88 100644 --- a/org.simantics.district.network/adapters.xml +++ b/org.simantics.district.network/adapters.xml @@ -12,6 +12,9 @@ + + diff --git a/org.simantics.district.network/scl/Simantics/District.scl b/org.simantics.district.network/scl/Simantics/District.scl index 2f46d361..0e79ef77 100644 --- a/org.simantics.district.network/scl/Simantics/District.scl +++ b/org.simantics.district.network/scl/Simantics/District.scl @@ -82,8 +82,8 @@ translateElement elem = do () importJava "org.simantics.district.network.DistrictNetworkUtil" where - createVertex :: Resource -> Vector Double -> Resource -> Resource - createEdge :: Resource -> Resource -> Resource + createVertex :: Resource -> Vector Double -> Double -> Resource -> Resource + createEdge :: Resource -> Resource -> Vector Double -> Resource """ Tries to look for the Resource representing the configuration component @@ -148,3 +148,11 @@ The result list can be smaller than the input resource list, even empty. """ dnElementsMappedToComponents :: [Resource] -> [Resource] dnElementsMappedToComponents mappedComponents = mapMaybe possibleDNElementMappedToComponent mappedComponents + + +importJava "org.simantics.district.network.DistrictNetworkUtil" where + createNetworkDiagram :: Resource -> Resource -> String -> Resource -> Resource -> Resource -> Resource -> Resource -> Resource + changeMappingType :: Resource -> [Resource] -> () + findDNElementById :: Resource -> String -> Maybe Resource + findDNElementByXYCoordinates :: Resource -> Double -> Double -> Double -> [Resource] + diff --git a/org.simantics.district.network/scl/Simantics/District/Algorithm.scl b/org.simantics.district.network/scl/Simantics/District/Algorithm.scl index 259ad087..c03ea796 100644 --- a/org.simantics.district.network/scl/Simantics/District/Algorithm.scl +++ b/org.simantics.district.network/scl/Simantics/District/Algorithm.scl @@ -118,6 +118,20 @@ reportDisconnectedSubnetworks vertexThreshold (diagram, subgraphs) = do print "" forI (sortStrings (map showEdge es)) (\i s -> print "* e\(i): \(s)") +""" +Get a set of vertices that acts as break points between network branches. +""" +branchPoints :: Resource -> [Resource] +branchPoints networkDiagram = runProc let + in + filter isBranchPoint vertices + where + all = if isInstanceOf networkDiagram DIA.Diagram + then networkDiagram # L0.ConsistsOf + else singleObject networkDiagram MOD.CompositeToDiagram # L0.ConsistsOf + vertices = filter (flip isInstanceOf DN.Vertex) all + isBranchPoint v = length (v # DN.HasEndVertex_Inverse) != 1 || length (v # DN.HasStartVertex_Inverse) != 1 + """ Get a set of the edges that are at the middle points of each span between branch points. """ diff --git a/org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/ColorContribution.scl b/org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/ColorContribution.scl new file mode 100644 index 00000000..800aa2b8 --- /dev/null +++ b/org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/ColorContribution.scl @@ -0,0 +1,21 @@ +// Example: (�p supply�, �S_PC�, �PO11_PRESSURE�, �bar(g)�, 0.1, 1.0, �summer�, 50.0, 120.0) +// Above �p supply� is label and there may be several UC types contributing to the same label. Default colorMap �summer�. + +importJava "org.simantics.district.network.visualisations.model.DynamicColorMap" where + data DynamicColorMap + + @JavaName "" + dynamicColorMap :: String -> [RGBIntensity] -> DynamicColorMap + +importJava "org.simantics.district.network.visualisations.model.DynamicColorMap$RGBIntensity" where + data RGBIntensity + + @JavaName "" + rgbIntensity :: Double -> Double -> Double -> RGBIntensity + +importJava "org.simantics.district.network.visualisations.model.DynamicColorContribution" where + data DynamicColorContribution + + @JavaName "" + dynamicColorContribution :: String -> String -> String -> String -> Double -> Double -> DynamicColorMap -> Double -> Double -> DynamicColorContribution + diff --git a/org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/ColorMap.scl b/org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/ColorMap.scl new file mode 100644 index 00000000..e69de29b diff --git a/org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/SizeContribution.scl b/org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/SizeContribution.scl new file mode 100644 index 00000000..e6b2ed38 --- /dev/null +++ b/org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/SizeContribution.scl @@ -0,0 +1,13 @@ +// Example: (�supply flow�, �S_FLOW�, �SP_VALUE�, �kg/s�, 1.0, 0.0, �linearSizesMedium�, 10.0, 1500.0) + +importJava "org.simantics.district.network.visualisations.model.DynamicSizeMap" where + data DynamicSizeMap + + @JavaName "" + dynamicSizeMap :: String -> [Double] -> DynamicSizeMap + +importJava "org.simantics.district.network.visualisations.model.DynamicSizeContribution" where + data DynamicSizeContribution + + @JavaName "" + dynamicSizeContribution :: String -> String -> String -> String -> Double -> Double -> DynamicSizeMap -> Double -> Double -> DynamicSizeContribution diff --git a/org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/SizeMap.scl b/org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/SizeMap.scl new file mode 100644 index 00000000..e69de29b diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DNEdgeBuilder.java b/org.simantics.district.network/src/org/simantics/district/network/DNEdgeBuilder.java similarity index 61% rename from org.simantics.district.network.ui/src/org/simantics/district/network/ui/DNEdgeBuilder.java rename to org.simantics.district.network/src/org/simantics/district/network/DNEdgeBuilder.java index 0df72c51..3f869649 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/DNEdgeBuilder.java +++ b/org.simantics.district.network/src/org/simantics/district/network/DNEdgeBuilder.java @@ -1,9 +1,9 @@ -package org.simantics.district.network.ui; +package org.simantics.district.network; import java.awt.geom.Rectangle2D; -import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Optional; import org.simantics.databoard.Bindings; import org.simantics.db.Resource; @@ -19,13 +19,20 @@ import org.simantics.diagram.synchronization.IModifiableSynchronizationContext; import org.simantics.diagram.synchronization.SynchronizationHints; import org.simantics.diagram.synchronization.graph.GraphSynchronizationHints; import org.simantics.diagram.synchronization.graph.layer.GraphLayerManager; -import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.DistrictNetworkUtil.ResourceVertex; import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.g2d.diagram.IDiagram; import org.simantics.layer0.Layer0; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.index.quadtree.Quadtree; public class DNEdgeBuilder { - + + private static final Logger LOGGER = LoggerFactory.getLogger(DNEdgeBuilder.class); + private Resource diagramResource; private IDiagram diagram; private GraphLayerManager glm; @@ -38,26 +45,37 @@ public class DNEdgeBuilder { glm = context.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER); } - public static Resource create(WriteGraph graph, Resource diagramResource, double[] start, double[] end, double padding) throws DatabaseException { + public static Optional create(WriteGraph graph, Resource diagramResource, double[] start, double startElevation, double[] end, double endElevation, double[] detailedGeometryCoords, double padding) throws DatabaseException { Collection vertices = graph.syncRequest(new ObjectsWithType(diagramResource, Layer0.getInstance(graph).ConsistsOf, DistrictNetworkResource.getInstance(graph).Vertex)); - List vv = new ArrayList<>(vertices.size()); + double halfPadding = padding / 2; + + Quadtree vv = new Quadtree(); for (Resource vertex : vertices) { - double[] existingCoords = graph.getRelatedValue2(vertex, DiagramResource.getInstance(graph).HasLocation, Bindings.DOUBLE_ARRAY); - vv.add(new ResourceVertex(vertex, existingCoords)); + double[] coords = graph.getRelatedValue2(vertex, DiagramResource.getInstance(graph).HasLocation, Bindings.DOUBLE_ARRAY); + double x1 = coords[0] - halfPadding; + double y1= coords[1] - halfPadding; + double x2 = coords[0] + halfPadding; + double y2= coords[1] + halfPadding; + Envelope e = new Envelope(x1, x2, y1, y2); + vv.insert(e, new ResourceVertex(vertex, coords, false)); } - return create(graph, vv, diagramResource, null, start, end, padding, false); + return create(graph, vv, diagramResource, null, start, startElevation, end, endElevation, detailedGeometryCoords, padding, true); } - public static Resource create(WriteGraph graph, Collection vertices, Resource diagramResource, Resource mapping, double[] start, double[] end, double padding, boolean writeElevationToEdgeFromPoints) throws DatabaseException { - + public static Optional create(WriteGraph graph, Quadtree vertices, Resource diagramResource, Resource mapping, double[] start, double startElevation, double[] end, double endElevation, double[] detailedGeometryCoords, double padding, boolean writeElevationToEdgeFromPoints) throws DatabaseException { DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + + // 2. Add vertices + Resource startVertex = getOrCreateVertex(graph, diagramResource, vertices, start, startElevation, padding, null); + Resource endVertex = getOrCreateVertex(graph, diagramResource, vertices, end, endElevation, padding, startVertex); + if (startVertex.equals(endVertex)) { + LOGGER.info("Circular edges are not supported, startVertex: {}, endVertex: {}", startVertex, endVertex); + return Optional.empty(); + } // 1. Get diagram edge to construct - Resource edge = getOrCreateEdge(graph, diagramResource, mapping); + Resource edge = getOrCreateEdge(graph, diagramResource, mapping, detailedGeometryCoords); - // 2. Add vertices - Resource startVertex = getOrCreateVertex(graph, diagramResource, vertices, start, padding, null); - Resource endVertex = getOrCreateVertex(graph, diagramResource, vertices, end, padding, startVertex); if (writeElevationToEdgeFromPoints) { graph.claimLiteral(edge, DN.Edge_HasElevation, calculateElevationFromVertices(graph, startVertex, endVertex), Bindings.DOUBLE); } @@ -76,7 +94,7 @@ public class DNEdgeBuilder { // }); // } // - return edge; + return Optional.of(edge); } private static double calculateElevationFromVertices(WriteGraph graph, Resource startVertex, Resource endVertex) throws ManyObjectsForFunctionalRelationException, BindingException, ServiceException { DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); @@ -88,15 +106,14 @@ public class DNEdgeBuilder { return 0; } - public void create(WriteGraph graph, double[] start, double[] end, double padding) throws DatabaseException { - - Resource edge = create(graph, diagramResource, start, end, padding); + public void create(WriteGraph graph, double[] start, double startElevation, double[] end, double endElevation, double[] detailedGeometryCoords, double padding) throws DatabaseException { + Optional edge = create(graph, diagramResource, start, startElevation, end, endElevation, detailedGeometryCoords, padding); // 7. Put the element on all the currently active layers if possible. if (glm != null) { - putOnActiveLayer(graph, edge); + putOnActiveLayer(graph, edge.get()); } - Layer0Utils.addCommentMetadata(graph, "Added edge " + edge); + Layer0Utils.addCommentMetadata(graph, "Added edge " + edge.get()); graph.markUndoPoint(); } @@ -105,40 +122,39 @@ public class DNEdgeBuilder { glm.putElementOnVisibleLayers(diagram, graph, res); } - private static Resource getOrCreateVertex(WriteGraph graph, Resource diagramResource, Collection vertices, double[] coords, double padding, Resource startVertex) throws DatabaseException { + private static Resource getOrCreateVertex(WriteGraph graph, Resource diagramResource, Quadtree qtree, double[] coords, double elevation, double padding, Resource startVertex) throws DatabaseException { Resource vertex = null; double halfPadding = padding / 2; double maxDistance = Double.MAX_VALUE; + double x1 = coords[0] - halfPadding; + double y1= coords[1] - halfPadding; + double x2 = coords[0] + halfPadding; + double y2= coords[1] + halfPadding; + Envelope e = new Envelope(x1, x2, y1, y2); + + List result = qtree.query(e); + @SuppressWarnings("unchecked") + List vertices = (List) result; for (ResourceVertex vertx : vertices) { Rectangle2D existing = new Rectangle2D.Double(vertx.coords[0] - halfPadding, vertx.coords[1] - halfPadding, padding, padding); - Rectangle2D tobecreated = new Rectangle2D.Double(coords[0] - halfPadding, coords[1] - halfPadding, padding, padding); + Rectangle2D tobecreated = new Rectangle2D.Double(x1, y1, padding, padding); if (existing.intersects(tobecreated)) { double dist = Math.sqrt((Math.pow(coords[0] - vertx.coords[0], 2) + (Math.pow(coords[1] - vertx.coords[1], 2)))); - if (dist <= maxDistance && !vertx.vertex.equals(startVertex)) { + if (dist <= maxDistance && vertx.vertex != startVertex) { vertex = vertx.vertex; maxDistance = dist; } } } if (vertex == null) { - vertex = DistrictNetworkUtil.createVertex(graph, diagramResource, coords); - vertices.add(new ResourceVertex(vertex, coords)); + vertex = DistrictNetworkUtil.createVertex(graph, diagramResource, coords, elevation); + qtree.insert(e, new ResourceVertex(vertex, coords, false)); } return vertex; } - private static Resource getOrCreateEdge(WriteGraph graph, Resource diagramResource, Resource mapping) throws DatabaseException { - return DistrictNetworkUtil.createEdge(graph, diagramResource, mapping); + private static Resource getOrCreateEdge(WriteGraph graph, Resource diagramResource, Resource mapping, double[] detailedGeometryCoords) throws DatabaseException { + return DistrictNetworkUtil.createEdge(graph, diagramResource, mapping, detailedGeometryCoords); } - public static class ResourceVertex { - - final Resource vertex; - final double[] coords; - - public ResourceVertex(Resource vertex, double[] coords) { - this.vertex = vertex; - this.coords = coords; - } - } } diff --git a/org.simantics.district.network/src/org/simantics/district/network/DistrictNetworkUtil.java b/org.simantics.district.network/src/org/simantics/district/network/DistrictNetworkUtil.java index 24a77575..6a7b4537 100644 --- a/org.simantics.district.network/src/org/simantics/district/network/DistrictNetworkUtil.java +++ b/org.simantics.district.network/src/org/simantics/district/network/DistrictNetworkUtil.java @@ -1,7 +1,15 @@ package org.simantics.district.network; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.simantics.databoard.Bindings; import org.simantics.datatypes.literal.RGB; @@ -10,12 +18,14 @@ import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.procedure.adapter.TransientCacheListener; +import org.simantics.db.common.request.IndexRoot; import org.simantics.db.common.request.ResourceRead; import org.simantics.db.common.utils.OrderedSetUtils; import org.simantics.db.exception.BindingException; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ManyObjectsForFunctionalRelationException; import org.simantics.db.exception.ServiceException; +import org.simantics.db.indexing.IndexUtils; import org.simantics.db.layer0.request.PossibleVariable; import org.simantics.db.layer0.variable.Variable; import org.simantics.diagram.stubs.DiagramResource; @@ -23,17 +33,36 @@ import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; import org.simantics.diagram.synchronization.graph.layer.GraphLayer; import org.simantics.diagram.synchronization.graph.layer.IGraphLayerUtil; import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.visualisations.DynamicVisualisationsContributions; +import org.simantics.district.network.visualisations.model.ColorBarOptions; +import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsLocation; +import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsSize; +import org.simantics.district.network.visualisations.model.DynamicColorContribution; +import org.simantics.district.network.visualisations.model.DynamicColorMap; +import org.simantics.district.network.visualisations.model.DynamicSizeContribution; +import org.simantics.district.network.visualisations.model.DynamicSizeMap; +import org.simantics.district.network.visualisations.model.SizeBarOptions; +import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsLocation; +import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsSize; import org.simantics.layer0.Layer0; +import org.simantics.maps.elevation.server.SingletonTiffTileInterface; +import org.simantics.maps.elevation.server.prefs.MapsElevationServerPreferences; import org.simantics.modeling.ModelingResources; +import org.simantics.modeling.adapters.NewCompositeActionFactory; import org.simantics.operation.Layer0X; +import org.simantics.utils.datastructures.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class DistrictNetworkUtil { - public static Resource createEdge(WriteGraph graph, Resource composite) throws DatabaseException { - return createEdge(graph, composite, graph.getPossibleObject(composite, DistrictNetworkResource.getInstance(graph).EdgeDefaultMapping)); + private static final Logger LOGGER = LoggerFactory.getLogger(DistrictNetworkUtil.class); + + public static Resource createEdge(WriteGraph graph, Resource composite, double[] detailedGeometryCoords) throws DatabaseException { + return createEdge(graph, composite, graph.getPossibleObject(composite, DistrictNetworkResource.getInstance(graph).EdgeDefaultMapping), detailedGeometryCoords); } - public static Resource createEdge(WriteGraph graph, Resource composite, Resource mapping) throws DatabaseException { + public static Resource createEdge(WriteGraph graph, Resource composite, Resource mapping, double[] detailedGeometryCoords) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); if (mapping == null) { @@ -60,21 +89,56 @@ public class DistrictNetworkUtil { }); } + // add detailed geometry (if any) + graph.claimLiteral(edge, DN.Edge_HasGeometry, detailedGeometryCoords, Bindings.DOUBLE_ARRAY); return edge; } - public static Resource createVertex(WriteGraph graph, Resource composite, double[] coords) throws DatabaseException { + /** + * @param graph + * @param composite + * @param coords + * @param elevation Double.MAX_VALUE to fetch elevation from elevation server (if enabled and has data) + * @return + * @throws DatabaseException + */ + public static Resource createVertex(WriteGraph graph, Resource composite, double[] coords, double elevation) throws DatabaseException { Resource defaultVertexMapping = graph.getPossibleObject(composite, DistrictNetworkResource.getInstance(graph).VertexDefaultMapping); - return createVertex(graph, composite, coords, defaultVertexMapping); + return createVertex(graph, composite, coords, elevation, defaultVertexMapping); } - public static Resource createVertex(WriteGraph graph, Resource composite, double[] coords, Resource mapping) throws DatabaseException { + /** + * @param graph + * @param composite + * @param coords + * @param elevation Double.MAX_VALUE to fetch elevation from elevation server (if enabled and has data) + * @param mapping + * @return + * @throws DatabaseException + */ + public static Resource createVertex(WriteGraph graph, Resource composite, double[] coords, double elevation, Resource mapping) throws DatabaseException { + // Double.MAX_VALUE is our secret to lookup elevation from elevation server + if (elevation == Double.MAX_VALUE) { + // ok, resolve from server or default to 0 + if (MapsElevationServerPreferences.useElevationServer()) { + // ok! we use new elevation API to resolve possible elevations for the starting points + try { + elevation = SingletonTiffTileInterface.lookup(coords[1], coords[0]).doubleValue(); + } catch (Exception ee) { + LOGGER.error("Could not get elevation from tiff interface", ee); + } + } else { + elevation = 0; + } + } + Layer0 L0 = Layer0.getInstance(graph); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); DiagramResource DIA = DiagramResource.getInstance(graph); Resource vertex = graph.newResource(); graph.claim(vertex, L0.InstanceOf, DN.Vertex); graph.claimLiteral(vertex, DIA.HasLocation, coords); + graph.claimLiteral(vertex, DN.Vertex_HasElevation, elevation, Bindings.DOUBLE); graph.claim(vertex, DN.HasMapping, null, mapping); @@ -248,6 +312,24 @@ public class DistrictNetworkUtil { DistrictNetworkResource.getInstance(graph).Diagram_backgroundColor, Bindings.getBindingUnchecked(RGB.Integer.class)); } + + public static Resource createNetworkDiagram(WriteGraph graph, Resource target, Resource compositeType, String defaultName, Resource defaultEdgeMapping, Resource defaultVertexMapping, Resource rightClickVertexMapping, Resource leftClickVertexMapping, Resource crs) throws DatabaseException { + Resource composite = NewCompositeActionFactory.createComposite(graph, target, defaultName, compositeType); + + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Resource diagram = graph.getSingleObject(composite, ModelingResources.getInstance(graph).CompositeToDiagram); + graph.claim(diagram, DN.EdgeDefaultMapping, defaultEdgeMapping); + graph.claim(diagram, DN.VertexDefaultMapping, defaultVertexMapping); + graph.claim(diagram, DN.RightClickDefaultMapping, rightClickVertexMapping); + graph.claim(diagram, DN.LeftClickDefaultMapping, leftClickVertexMapping); + graph.claim(diagram, DN.HasSpatialRefSystem, crs); + + // Generated name prefix from composite name + String compositeName = graph.getRelatedValue2(composite, Layer0.getInstance(graph).HasName, Bindings.STRING); + graph.claimLiteral(diagram, Layer0X.getInstance(graph).HasGeneratedNamePrefix, "N" + compositeName.substring(compositeName.length() - 1, compositeName.length())); + + return composite; + } public static final class MappedComponentRequest extends ResourceRead { public MappedComponentRequest(Resource element) { @@ -260,4 +342,250 @@ public class DistrictNetworkUtil { } } + public static class ResourceVertex { + + public final boolean isConsumer; + public final Resource vertex; + public final double[] coords; + + public ResourceVertex(Resource vertex, double[] coords, boolean isConsumer) { + this.vertex = vertex; + this.coords = coords; + this.isConsumer = isConsumer; + } + } + + public static void changeMappingType(WriteGraph graph, Resource newMapping, List elements) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + for (Resource element : elements) { + graph.deny(element, DN.HasMapping); + graph.claim(element, DN.HasMapping, newMapping); + } + } + + public static Stream findDNElementsById(ReadGraph graph, Resource context, String idToFind) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + return IndexUtils.findByType(graph, + graph.syncRequest(new IndexRoot(context)), + DN.Element + ).stream().filter(element -> { + try { + String id = graph.getPossibleRelatedValue(element, DN.HasId, Bindings.STRING); + return id != null && id.contains(idToFind); + } catch (DatabaseException e) { + LOGGER.error("Could not read id for element {]", element, e); + return false; + } + }); + } + + public static Resource findDNElementById(ReadGraph graph, Resource context, String idToFind) throws DatabaseException { + List elements = findDNElementsById(graph, context, idToFind).collect(Collectors.toList()); + if (elements.size() == 1) { + return elements.iterator().next(); + } + return null; + } + + public static List findDNElementByXYCoordinates(ReadGraph graph, Resource context, double lat, double lon, double padding) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + DiagramResource DIA = DiagramResource.getInstance(graph); + List results = new ArrayList<>(); + Collection vertices = IndexUtils.findByType(graph, graph.syncRequest(new IndexRoot(context)), DN.Vertex); + Rectangle2D rect = new Rectangle2D.Double(lat, lon, padding, padding); + for (Resource vertex : vertices) { + double[] location = graph.getRelatedValue(vertex, DIA.HasLocation, Bindings.DOUBLE_ARRAY); + if (rect.contains(location[0], location[1])) { + results.add(vertex); + } + } + return results; + } + + public static ColorBarOptions colorBarOptions(ReadGraph graph, Resource visualisation) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + if (visualisation != null) { + String colorBarLocation = graph.getPossibleRelatedValue(visualisation, DN.Diagram_Visualisations_ColorBarLocation, Bindings.STRING); + String colorBarSize = graph.getPossibleRelatedValue(visualisation, DN.Diagram_Visualisations_ColorBarSize, Bindings.STRING); + Boolean show = graph.getPossibleRelatedValue(visualisation, DN.Diagram_Visualisations_ShowColorBars, Bindings.BOOLEAN); + Boolean showTicks = graph.getPossibleRelatedValue(visualisation, DN.Diagram_Visualisations_ShowColorBarTicks, Bindings.BOOLEAN); + if (colorBarLocation != null) { + return new ColorBarOptions() + .showColorBars(show != null ? show : false) + .showColorBarsTicks(showTicks != null ? showTicks : false) + .withLocation(ColorBarsLocation.valueOf(colorBarLocation)) + .withSize(ColorBarsSize.valueOf(colorBarSize)); + } + } + return ColorBarOptions.useDefault(); + } + + public static void setColorBarOptions(WriteGraph graph, Resource visualisation, ColorBarOptions options) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + graph.claimLiteral(visualisation, DN.Diagram_Visualisations_ColorBarLocation, options.getLocation().toString(), Bindings.STRING); + graph.claimLiteral(visualisation, DN.Diagram_Visualisations_ColorBarSize, options.getSize().toString(), Bindings.STRING); + graph.claimLiteral(visualisation, DN.Diagram_Visualisations_ShowColorBars, options.isShowColorBars(), Bindings.BOOLEAN); + graph.claimLiteral(visualisation, DN.Diagram_Visualisations_ShowColorBarTicks, options.isShowColorBarsTicks(), Bindings.BOOLEAN); + } + + public static Resource createVisualisation(WriteGraph graph, Resource diagram, String visualisationName) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Resource visualisation = graph.newResource(); + graph.claim(visualisation, L0.InstanceOf, DN.Diagram_Visualisations); + graph.claimLiteral(visualisation, L0.HasName, visualisationName); + graph.claim(diagram, DN.Diagram_hasVisualisation, visualisation); + return visualisation; + } + + public static SizeBarOptions sizeBarOptions(ReadGraph graph, Resource visualisation) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + if (visualisation != null) { + String sizeBarLocation = graph.getPossibleRelatedValue(visualisation, DN.Diagram_Visualisations_SizeBarLocation, Bindings.STRING); + String sizeBarSize = graph.getPossibleRelatedValue(visualisation, DN.Diagram_Visualisations_SizeBarSize, Bindings.STRING); + Boolean show = graph.getPossibleRelatedValue(visualisation, DN.Diagram_Visualisations_ShowSizeBars, Bindings.BOOLEAN); + Boolean showTicks = graph.getPossibleRelatedValue(visualisation, DN.Diagram_Visualisations_ShowSizeBarTicks, Bindings.BOOLEAN); + if (sizeBarLocation != null) { + return new SizeBarOptions() + .showSizeBars(show != null ? show : false) + .showSizeBarsTicks(showTicks != null ? showTicks : false) + .withLocation(SizeBarsLocation.valueOf(sizeBarLocation)) + .withSize(SizeBarsSize.valueOf(sizeBarSize)); + } + } + return SizeBarOptions.useDefault(); + } + + public static void setSizeBarOptions(WriteGraph graph, Resource visualisation, SizeBarOptions options) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + graph.claimLiteral(visualisation, DN.Diagram_Visualisations_SizeBarLocation, options.getLocation().toString(), Bindings.STRING); + graph.claimLiteral(visualisation, DN.Diagram_Visualisations_SizeBarSize, options.getSize().toString(), Bindings.STRING); + graph.claimLiteral(visualisation, DN.Diagram_Visualisations_ShowSizeBars, options.isShowSizeBars(), Bindings.BOOLEAN); + graph.claimLiteral(visualisation, DN.Diagram_Visualisations_ShowSizeBarTicks, options.isShowSizeBarsTicks(), Bindings.BOOLEAN); + } + + public static Map colorContributions(ReadGraph graph, Resource visualisation) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + if (visualisation == null) { + return Collections.emptyMap(); + } + Map contributions = new HashMap<>(); + + Map colorMaps = DynamicVisualisationsContributions.dynamicColorMaps(graph); + + Collection colorContributions = graph.getObjects(visualisation, DN.Diagram_Visualisations_colorContributions); + for (Resource colorContribution : colorContributions) { + String ucName = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionContributorName, Bindings.STRING); + String label = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionLabel, Bindings.STRING); + String moduleName = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionModuleName, Bindings.STRING); + String attributeName = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionModuleAttribute, Bindings.STRING); + String unit = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionUnit, Bindings.STRING); + Double variableGain = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionVariableGain, Bindings.DOUBLE); + Double variableBias = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionVariableBias, Bindings.DOUBLE); + String dynamicColorMap = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionDefaultColorMap, Bindings.STRING); + Double defaultMin = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionDefaultMin, Bindings.DOUBLE); + Double defaultMax = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionDefaultMax, Bindings.DOUBLE); + Boolean used = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionUsed, Bindings.BOOLEAN); + Boolean useDefault = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_colorContributionUseDefault, Bindings.BOOLEAN); + DynamicColorContribution dynamicColorContribution = new DynamicColorContribution(label, moduleName, attributeName, unit, variableGain, variableBias, colorMaps.get(dynamicColorMap), defaultMin, defaultMax); + dynamicColorContribution.setUsed(used != null ? used : false); + dynamicColorContribution.setUseDefault(useDefault != null ? useDefault : false); + contributions.put(ucName, dynamicColorContribution); + } + return contributions; + } + + public static void setColorContributions(WriteGraph graph, Resource visualisation, List> collect) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Layer0 L0 = Layer0.getInstance(graph); + + graph.deny(visualisation, DN.Diagram_Visualisations_colorContributions); + for (Pair coll : collect) { + Resource colorContribution = graph.newResource(); + graph.claim(colorContribution, L0.InstanceOf, DN.Diagram_Visualisations_ColorContribution); + graph.claimLiteral(colorContribution, L0.HasName, coll.first); + + DynamicColorContribution contr = coll.second; + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionContributorName, coll.first); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionLabel, contr.getLabel()); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionModuleName, contr.getModuleName()); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionModuleAttribute, contr.getAttributeName()); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionUnit, contr.getUnit()); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionVariableGain, contr.getVariableGain()); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionVariableBias, contr.getVariableBias()); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionDefaultColorMap, contr.getDefaultColorMap().getLabel()); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionDefaultMin, contr.getDefaultMin()); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionDefaultMax, contr.getDefaultMax()); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionUsed, contr.isUsed()); + graph.claimLiteral(colorContribution, DN.Diagram_Visualisations_colorContributionUseDefault, contr.isUseDefault()); + + graph.claim(visualisation, DN.Diagram_Visualisations_colorContributions, colorContribution); + } + } + + public static Map sizeContributions(ReadGraph graph, Resource visualisation) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + if (visualisation == null) { + return Collections.emptyMap(); + } + Map contributions = new HashMap<>(); + + Map sizeMaps = DynamicVisualisationsContributions.dynamicSizeMaps(graph); + + Collection colorContributions = graph.getObjects(visualisation, DN.Diagram_Visualisations_sizeContributions); + for (Resource colorContribution : colorContributions) { + String ucName = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionContributorName, Bindings.STRING); + String label = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionLabel, Bindings.STRING); + String moduleName = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionModuleName, Bindings.STRING); + String attributeName = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionModuleAttribute, Bindings.STRING); + String unit = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionUnit, Bindings.STRING); + Double variableGain = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionVariableGain, Bindings.DOUBLE); + Double variableBias = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionVariableBias, Bindings.DOUBLE); + String dynamicSizeMap = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionDefaultSizeMap, Bindings.STRING); + Double defaultMin = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionDefaultMin, Bindings.DOUBLE); + Double defaultMax = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionDefaultMax, Bindings.DOUBLE); + Boolean used = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionUsed, Bindings.BOOLEAN); + Boolean useDefault = graph.getRelatedValue(colorContribution, DN.Diagram_Visualisations_sizeContributionUseDefault, Bindings.BOOLEAN); + DynamicSizeContribution dsc = new DynamicSizeContribution(label, moduleName, attributeName, unit, variableGain, variableBias, sizeMaps.get(dynamicSizeMap), defaultMin, defaultMax); + dsc.setUsed(used != null ? used : false); + dsc.setUseDefault(useDefault != null ? useDefault : false); + contributions.put(ucName, dsc); + } + return contributions; + } + + public static void setSizeContributions(WriteGraph graph, Resource visualisation, List> collect) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Layer0 L0 = Layer0.getInstance(graph); + + graph.deny(visualisation, DN.Diagram_Visualisations_sizeContributions); + for (Pair coll : collect) { + Resource sizeContribution = graph.newResource(); + graph.claim(sizeContribution, L0.InstanceOf, DN.Diagram_Visualisations_SizeContribution); + graph.claimLiteral(sizeContribution, L0.HasName, coll.first); + + DynamicSizeContribution contr = coll.second; + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionContributorName, coll.first); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionLabel, contr.getLabel()); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionModuleName, contr.getModuleName()); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionModuleAttribute, contr.getAttributeName()); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionUnit, contr.getUnit()); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionVariableGain, contr.getVariableGain()); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionVariableBias, contr.getVariableBias()); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionDefaultSizeMap, contr.getDefaultSizeMap().getLabel()); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionDefaultMin, contr.getDefaultMin()); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionDefaultMax, contr.getDefaultMax()); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionUsed, contr.isUsed()); + graph.claimLiteral(sizeContribution, DN.Diagram_Visualisations_sizeContributionUseDefault, contr.isUseDefault()); + + graph.claim(visualisation, DN.Diagram_Visualisations_sizeContributions, sizeContribution); + } + } + + public static void setActiveVisualisation(WriteGraph graph, Resource diagram, Resource visualisationTemplate) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + graph.deny(diagram, DN.Diagram_hasActiveVisualisation); + graph.claim(diagram, DN.Diagram_hasActiveVisualisation, visualisationTemplate); + } + } diff --git a/org.simantics.district.network/src/org/simantics/district/network/changeset/DistrictChangeListener.java b/org.simantics.district.network/src/org/simantics/district/network/changeset/DistrictChangeListener.java index 39227a87..7cdb9c7d 100644 --- a/org.simantics.district.network/src/org/simantics/district/network/changeset/DistrictChangeListener.java +++ b/org.simantics.district.network/src/org/simantics/district/network/changeset/DistrictChangeListener.java @@ -1,6 +1,5 @@ package org.simantics.district.network.changeset; -import java.util.Arrays; import java.util.Map; import org.simantics.db.MetadataI; @@ -23,7 +22,8 @@ import org.slf4j.LoggerFactory; public class DistrictChangeListener extends GenericChangeListener { - private static final Logger LOGGER = LoggerFactory.getLogger(DistrictChangeListener.class); + @SuppressWarnings("unused") + private static final Logger LOGGER = LoggerFactory.getLogger(DistrictChangeListener.class); @Override public void onEvent(ReadGraph graph, MetadataI metadata, DependencyChanges event) throws DatabaseException { diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/ActiveDynamicVisualisationsRequest.java b/org.simantics.district.network/src/org/simantics/district/network/profile/ActiveDynamicVisualisationsRequest.java new file mode 100644 index 00000000..52cfd61c --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/ActiveDynamicVisualisationsRequest.java @@ -0,0 +1,43 @@ +package org.simantics.district.network.profile; + +import java.util.Map; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.request.ResourceRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.district.network.DistrictNetworkUtil; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.visualisations.model.ColorBarOptions; +import org.simantics.district.network.visualisations.model.DynamicColorContribution; +import org.simantics.district.network.visualisations.model.DynamicSizeContribution; +import org.simantics.district.network.visualisations.model.DynamicVisualisation; +import org.simantics.district.network.visualisations.model.SizeBarOptions; +import org.simantics.layer0.Layer0; + +/** + * @author Jani Simomaa + */ +public class ActiveDynamicVisualisationsRequest extends ResourceRead { + + public ActiveDynamicVisualisationsRequest(Resource diagram) { + super(diagram); + } + + @Override + public DynamicVisualisation perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Resource visualisationResource = graph.getPossibleObject(resource, DN.Diagram_hasActiveVisualisation); + if (visualisationResource != null) { + String name = graph.getRelatedValue(visualisationResource, Layer0.getInstance(graph).HasName); + Map colorContributions = DistrictNetworkUtil.colorContributions(graph, visualisationResource); + ColorBarOptions colorBarOptions = DistrictNetworkUtil.colorBarOptions(graph, visualisationResource); + Map sizeContributions = DistrictNetworkUtil.sizeContributions(graph, visualisationResource); + SizeBarOptions sizeBarOptions = DistrictNetworkUtil.sizeBarOptions(graph, visualisationResource); + DynamicVisualisation visualisation = new DynamicVisualisation(name, visualisationResource, colorContributions, colorBarOptions, sizeContributions, sizeBarOptions); + return visualisation; + } + return null; + } + +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/ArrowLengthStyle.java b/org.simantics.district.network/src/org/simantics/district/network/profile/ArrowLengthStyle.java index 2e8c3458..d180d77b 100644 --- a/org.simantics.district.network/src/org/simantics/district/network/profile/ArrowLengthStyle.java +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/ArrowLengthStyle.java @@ -1,14 +1,11 @@ package org.simantics.district.network.profile; -import java.util.HashSet; -import java.util.List; import java.util.Set; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.procedure.adapter.TransientCacheListener; -import org.simantics.db.common.request.ResourceRead; import org.simantics.db.exception.DatabaseException; import org.simantics.layer0.Layer0; import org.simantics.scenegraph.INode; @@ -20,7 +17,6 @@ import org.simantics.scenegraph.profile.common.ProfileVariables; public class ArrowLengthStyle extends ThrottledStyleBase { private static final Double PENDING = Double.NaN; - private static final Double ONE = 1.0; @Override public Double calculateThrottledStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem) throws DatabaseException { @@ -33,18 +29,13 @@ public class ArrowLengthStyle extends ThrottledStyleBase { // Prevent PendingVariableExceptions from coming through boolean wasSynchronous = graph.setSynchronous(true); try { - Double length = ONE; if (ds.arrowLengthProperty.isPresent()) { - length = Simantics.applySCLRead(graph, ds.arrowLengthProperty.get(), groupItem); - // System.out.println("read thickness: " + thickness + " : " + ds.arrowLengthProperty); - if (length == null) { - length = ONE; - } else { - length = length * ds.arrowLengthGain + ds.arrowLengthBias; - } + Double length = Simantics.applySCLRead(graph, ds.arrowLengthProperty.get(), groupItem); + return length != null ? length * ds.arrowLengthGain + ds.arrowLengthBias : null; + } + else { + return null; } - - return length; } finally { graph.setSynchronous(wasSynchronous); @@ -71,17 +62,4 @@ public class ArrowLengthStyle extends ThrottledStyleBase { for (INode nn : n.getNodes()) ProfileVariables.claimNodeProperty(nn, "arrowLength", null, evaluationContext); } - - private static final class MidBranchEdgeSetRequest extends ResourceRead> { - private MidBranchEdgeSetRequest(Resource resource) { - super(resource); - } - - @Override - public Set perform(ReadGraph graph) throws DatabaseException { - List edges = Simantics.applySCL("Simantics/District/Algorithm", "midBranchEdges", graph, resource); - return new HashSet<>(edges); - } - } - } diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/DNElementColorStyle.java b/org.simantics.district.network/src/org/simantics/district/network/profile/DNElementColorStyle.java index f8f6ba5c..fe0a1fe7 100644 --- a/org.simantics.district.network/src/org/simantics/district/network/profile/DNElementColorStyle.java +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/DNElementColorStyle.java @@ -1,17 +1,28 @@ package org.simantics.district.network.profile; import java.awt.Color; +import java.util.Map; import org.simantics.Simantics; +import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; import org.simantics.db.exception.DatabaseException; -import org.simantics.diagram.profile.StyleBase; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.visualisations.model.DynamicColorContribution; +import org.simantics.district.network.visualisations.model.DynamicColorMap; +import org.simantics.district.network.visualisations.model.DynamicVisualisation; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.g2d.nodes.SingleElementNode; import org.simantics.scenegraph.profile.EvaluationContext; import org.simantics.scenegraph.profile.common.ProfileVariables; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Tuukka Lehtonen @@ -19,14 +30,69 @@ import org.simantics.scenegraph.profile.common.ProfileVariables; */ public class DNElementColorStyle extends ThrottledStyleBase { - private static final boolean DEBUG = false; + private static final Logger LOGGER = LoggerFactory.getLogger(DNElementColorStyle.class); + + private static final boolean DEBUG = false; @Override public Color calculateThrottledStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem) throws DatabaseException { - DiagramSettings ds = graph.syncRequest(new DiagramSettingsRequest(runtimeDiagram), TransientCacheAsyncListener.instance()); - // Prevent PendingVariableExceptions from coming through - boolean wasSynchronous = graph.setSynchronous(true); - try { + + DynamicVisualisation dv = graph.syncRequest(new RuntimeDynamicVisualisationsRequest(runtimeDiagram), + TransientCacheAsyncListener.instance()); + + DiagramSettings ds = graph.syncRequest(new DiagramSettingsRequest(runtimeDiagram), TransientCacheAsyncListener.instance()); + // Prevent PendingVariableExceptions from coming through + boolean wasSynchronous = graph.setSynchronous(true); + try { + if (dv != null) { + Layer0 L0 = Layer0.getInstance(graph); + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + ModelingResources MOD = ModelingResources.getInstance(graph); + Resource mapping = graph.getSingleObject(groupItem, DN.HasMapping); + + Map colorContributions = dv.getColorContributions(); + String mappingName = graph.getRelatedValue(mapping, L0.HasName); + DynamicColorContribution dcc = colorContributions.get(mappingName); + if (dcc != null && dcc.isUsed()) { + + Resource mappedComponent = graph.getPossibleObject(groupItem, DN.MappedComponent); + if (mappedComponent != null) { + + Resource component = graph.getSingleObject(mappedComponent, MOD.ElementToComponent); + Variable variable = Variables.getVariable(graph, component); + Variable possibleActiveVariable = Variables.possibleActiveVariable(graph, variable); + if (possibleActiveVariable != null) { + + Variable module = possibleActiveVariable.getPossibleChild(graph, dcc.getModuleName()); + if (module != null) { + Variable attribute = module.getPossibleProperty(graph, dcc.getAttributeName()); + if (attribute != null) { + Double possibleValue = attribute.getPossibleValue(graph, Bindings.DOUBLE); + if (possibleValue != null) { + // here we do the adjusting according to spec in #15038 + double adjustedValue = possibleValue.doubleValue() * dcc.getVariableGain() + dcc.getVariableBias(); + DynamicColorMap defaultColorMap = dcc.getDefaultColorMap(); + Color color = defaultColorMap.getColor(adjustedValue, dcc.getDefaultMin(), dcc.getDefaultMax()); + return color; + } else { + LOGGER.warn("No value for {}", attribute.getURI(graph)); + } + } else { + LOGGER.warn("Wrong attribute name {} for {} !!", dcc.getAttributeName(), module.getURI(graph)); + } + } else { + LOGGER.warn("Wrong modulename {} for {} !!", dcc.getModuleName(), possibleActiveVariable.getURI(graph)); + } + } else { + LOGGER.debug("No active experiment for {}", variable.getURI(graph)); + } + } else { + LOGGER.debug("No mapped component for {} to calculate dynamic color style", groupItem); + } + } + } + + // the old implementation here if (ds.elementColoringFunction.isPresent()) { if (DEBUG) System.out.print("elementColoringFunction: " + ds.elementColoringFunction + "(" + groupItem + "): "); diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/DNElementSizeStyle.java b/org.simantics.district.network/src/org/simantics/district/network/profile/DNElementSizeStyle.java new file mode 100644 index 00000000..990aa4d2 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/DNElementSizeStyle.java @@ -0,0 +1,127 @@ +package org.simantics.district.network.profile; + +import java.util.Map; + +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.visualisations.model.DynamicSizeContribution; +import org.simantics.district.network.visualisations.model.DynamicSizeMap; +import org.simantics.district.network.visualisations.model.DynamicVisualisation; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.scenegraph.INode; +import org.simantics.scenegraph.g2d.G2DSceneGraph; +import org.simantics.scenegraph.g2d.nodes.SingleElementNode; +import org.simantics.scenegraph.profile.EvaluationContext; +import org.simantics.scenegraph.profile.common.ProfileVariables; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jani Simomaa + */ +public class DNElementSizeStyle extends ThrottledStyleBase { + + private static final Logger LOGGER = LoggerFactory.getLogger(DNElementSizeStyle.class); + private static final boolean DEBUG = false; + + private static final Double PENDING = Double.NaN; + private static final Double ONE = 1.0; + + @Override + public Double calculateThrottledStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem) throws DatabaseException { + + DynamicVisualisation dv = graph.syncRequest(new RuntimeDynamicVisualisationsRequest(runtimeDiagram), + TransientCacheAsyncListener.instance()); + + // Prevent PendingVariableExceptions from coming through + boolean wasSynchronous = graph.setSynchronous(true); + try { + if (dv != null) { + Layer0 L0 = Layer0.getInstance(graph); + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + ModelingResources MOD = ModelingResources.getInstance(graph); + Resource mapping = graph.getSingleObject(groupItem, DN.HasMapping); + + Map sizeContributions = dv.getSizeContributions(); + String mappingName = graph.getRelatedValue(mapping, L0.HasName); + DynamicSizeContribution dsc = sizeContributions.get(mappingName); + if (dsc != null && dsc.isUsed()) { + + Resource mappedComponent = graph.getPossibleObject(groupItem, DN.MappedComponent); + if (mappedComponent != null) { + + Resource component = graph.getSingleObject(mappedComponent, MOD.ElementToComponent); + Variable variable = Variables.getVariable(graph, component); + Variable possibleActiveVariable = Variables.possibleActiveVariable(graph, variable); + if (possibleActiveVariable != null) { + + Variable module = possibleActiveVariable.getPossibleChild(graph, dsc.getModuleName()); + if (module != null) { + Variable attribute = module.getPossibleProperty(graph, dsc.getAttributeName()); + if (attribute != null) { + Double possibleValue = attribute.getPossibleValue(graph, Bindings.DOUBLE); + if (possibleValue != null) { + // here we do the adjusting according to spec in #15038 + double adjustedValue = possibleValue.doubleValue() * dsc.getVariableGain() + dsc.getVariableBias(); + DynamicSizeMap defaultSizeMap = dsc.getDefaultSizeMap(); + double size = defaultSizeMap.getSize(adjustedValue, dsc.getDefaultMin(), dsc.getDefaultMax()); + return size; + } else { + LOGGER.warn("No value for {}", attribute.getURI(graph)); + } + } else { + LOGGER.warn("Wrong attribute name {} for {} !!", dsc.getAttributeName(), module.getURI(graph)); + } + } else { + LOGGER.warn("Wrong modulename {} for {} !!", dsc.getModuleName(), possibleActiveVariable.getURI(graph)); + } + } else { + LOGGER.debug("No active experiment for {}", variable.getURI(graph)); + } + } else { + LOGGER.debug("No mapped component for {} to calculate dynamic size style", groupItem); + } + } + } + } + finally { + graph.setSynchronous(wasSynchronous); + } + return null; + } + + @Override + public void applyThrottledStyleForNode(EvaluationContext observer, INode node, Double value) { + //System.out.println("apply: " + node + " : " + value); + SingleElementNode n = (SingleElementNode) node; + if (value == PENDING) { + ((G2DSceneGraph)node.getRootNode()).setPending(node); + } else { + ((G2DSceneGraph)node.getRootNode()).clearPending(node); + } + if (value == null) + value = ONE; + for (INode nn : n.getNodes()) { + ProfileVariables.claimNodeProperty(nn, "size", value, observer); + ProfileVariables.claimNodeProperty(nn, "stroke", value, observer); + } + } + + @Override + protected void cleanupStyleForNode(EvaluationContext evaluationContext, INode node) { + ((G2DSceneGraph)node.getRootNode()).clearPending(node); + SingleElementNode n = (SingleElementNode) node; + for (INode nn : n.getNodes()) { + ProfileVariables.claimNodeProperty(nn, "size", ONE, evaluationContext); + ProfileVariables.claimNodeProperty(nn, "stroke", ONE, evaluationContext); + } + } + +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/DynamicVisualisationsRequest.java b/org.simantics.district.network/src/org/simantics/district/network/profile/DynamicVisualisationsRequest.java new file mode 100644 index 00000000..3c187a10 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/DynamicVisualisationsRequest.java @@ -0,0 +1,38 @@ +package org.simantics.district.network.profile; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.NamedResource; +import org.simantics.db.common.request.ResourceRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.layer0.Layer0; + +/** + * @author Jani Simomaa + */ +public class DynamicVisualisationsRequest extends ResourceRead> { + + public DynamicVisualisationsRequest(Resource diagram) { + super(diagram); + } + + @Override + public Collection perform(ReadGraph graph) throws DatabaseException { + DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Collection visualisationResources = graph.getObjects(resource, DN.Diagram_hasVisualisation); + List results = new ArrayList<>(); + if (visualisationResources != null && !visualisationResources.isEmpty()) { + for (Resource visualisationResource : visualisationResources) { + String name = graph.getRelatedValue(visualisationResource, Layer0.getInstance(graph).HasName); + results.add(new NamedResource(name, visualisationResource)); + } + } + return results; + } + +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/HideStyle.java b/org.simantics.district.network/src/org/simantics/district/network/profile/HideStyle.java index 8e053a06..d5f57c82 100644 --- a/org.simantics.district.network/src/org/simantics/district/network/profile/HideStyle.java +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/HideStyle.java @@ -7,6 +7,7 @@ import org.simantics.diagram.profile.StyleBase; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.g2d.nodes.SingleElementNode; import org.simantics.scenegraph.profile.EvaluationContext; +import org.simantics.scenegraph.profile.common.ProfileVariables; /** * @author Tuukka Lehtonen @@ -19,15 +20,17 @@ public class HideStyle extends StyleBase { } @Override - public void applyStyleForNode(EvaluationContext observer, INode node, Boolean result) { + public void applyStyleForNode(EvaluationContext evaluationContext, INode node, Boolean result) { SingleElementNode n = (SingleElementNode) node; - n.setVisible(false); + for (INode nn : n.getNodes()) + ProfileVariables.claimNodeProperty(nn, "hidden", true, evaluationContext); } - + @Override protected void cleanupStyleForNode(EvaluationContext evaluationContext, INode node) { SingleElementNode n = (SingleElementNode) node; - n.setVisible(true); + for (INode nn : n.getNodes()) + ProfileVariables.claimNodeProperty(nn, "hidden", false, evaluationContext); } @Override diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/MidBranchEdgeSetRequest.java b/org.simantics.district.network/src/org/simantics/district/network/profile/MidBranchEdgeSetRequest.java new file mode 100644 index 00000000..c1a9394f --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/MidBranchEdgeSetRequest.java @@ -0,0 +1,23 @@ +package org.simantics.district.network.profile; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.request.ResourceRead; +import org.simantics.db.exception.DatabaseException; + +public class MidBranchEdgeSetRequest extends ResourceRead> { + public MidBranchEdgeSetRequest(Resource resource) { + super(resource); + } + + @Override + public Set perform(ReadGraph graph) throws DatabaseException { + List edges = Simantics.applySCL("Simantics/District/Algorithm", "midBranchEdges", graph, resource); + return new HashSet<>(edges); + } +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/RuntimeDynamicVisualisationsRequest.java b/org.simantics.district.network/src/org/simantics/district/network/profile/RuntimeDynamicVisualisationsRequest.java new file mode 100644 index 00000000..05ed85bd --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/RuntimeDynamicVisualisationsRequest.java @@ -0,0 +1,30 @@ +package org.simantics.district.network.profile; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; +import org.simantics.db.common.request.ResourceRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.district.network.visualisations.model.DynamicVisualisation; + +/** + * @author Jani Simomaa + */ +public class RuntimeDynamicVisualisationsRequest extends ResourceRead { + + public RuntimeDynamicVisualisationsRequest(Resource runtimeDiagram) { + super(runtimeDiagram); + } + + @Override + public DynamicVisualisation perform(ReadGraph graph) throws DatabaseException { + DiagramResource DIA = DiagramResource.getInstance(graph); + Resource diagram = graph.getPossibleObject(resource, DIA.RuntimeDiagram_HasConfiguration); + if (diagram != null) { + return graph.syncRequest(new ActiveDynamicVisualisationsRequest(diagram), TransientCacheAsyncListener.instance()); + } + return null; + } + +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/ThrottledStyleBase.java b/org.simantics.district.network/src/org/simantics/district/network/profile/ThrottledStyleBase.java index 67ca2595..93af11c4 100644 --- a/org.simantics.district.network/src/org/simantics/district/network/profile/ThrottledStyleBase.java +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/ThrottledStyleBase.java @@ -38,7 +38,7 @@ public abstract class ThrottledStyleBase extends StyleBase> resultListener; diff --git a/org.simantics.district.network/src/org/simantics/district/network/visualisations/DynamicVisualisationsContributions.java b/org.simantics.district.network/src/org/simantics/district/network/visualisations/DynamicVisualisationsContributions.java new file mode 100644 index 00000000..45e52fb5 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/visualisations/DynamicVisualisationsContributions.java @@ -0,0 +1,278 @@ +package org.simantics.district.network.visualisations; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.simantics.NameLabelUtil; +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.NamedResource; +import org.simantics.db.common.request.ObjectsWithSupertype; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.util.Layer0Utils; +import org.simantics.district.network.visualisations.model.DynamicColorContribution; +import org.simantics.district.network.visualisations.model.DynamicColorMap; +import org.simantics.district.network.visualisations.model.DynamicSizeContribution; +import org.simantics.district.network.visualisations.model.DynamicSizeMap; +import org.simantics.layer0.Layer0; +import org.simantics.scl.compiler.top.ValueNotFound; +import org.simantics.scl.osgi.SCLOsgi; +import org.simantics.scl.runtime.SCLContext; +import org.simantics.scl.runtime.tuple.Tuple0; +import org.simantics.structural.stubs.StructuralResource2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DynamicVisualisationsContributions { + + private static final Logger LOGGER = LoggerFactory.getLogger(DynamicVisualisationsContributions.class); + + private static final String COMMON_DYNAMIC_VISUALISATIONS_MODULE = "CommonDynamicVisualisations"; + private static final String COLOR_MAP_CONTRIBUTION = "colorMapContribution"; + private static final String SIZE_MAP_CONTRIBUTION = "sizeMapContribution"; + + private static final String DYNAMIC_VISUALISATIONS_CONTRIBUTION_MODULE = "DynamicVisualisationsContribution"; + private static final String COLOR_CONTRIBUTION = "colorContribution"; + private static final String SIZE_CONTRIBUTION = "sizeContribution"; + + public static Map dynamicColorMaps(ReadGraph graph) throws DatabaseException { + List sharedOntologies = Simantics.applySCL("Simantics/SharedOntologies", "getSharedOntologies", graph, Tuple0.INSTANCE); + + Map results = new HashMap<>(); + Layer0 L0 = Layer0.getInstance(graph); + for (Resource sharedOntology : sharedOntologies) { + Resource sclModule = Layer0Utils.getPossibleChild(graph, sharedOntology, L0.SCLModule, COMMON_DYNAMIC_VISUALISATIONS_MODULE); + if (sclModule != null) { + String moduleURI = graph.getURI(sclModule); + Object oldGraph = SCLContext.getCurrent().get("graph"); + try { + // let's put the graph to SCLContext for resolving the color maps + SCLContext.getCurrent().put("graph", graph); + @SuppressWarnings("unchecked") + List result = (List) SCLOsgi.MODULE_REPOSITORY.getValue(moduleURI, COLOR_MAP_CONTRIBUTION); + + for (DynamicColorMap colorMap : result) { + results.put(colorMap.getLabel(), colorMap); + } + } catch (ValueNotFound e) { + e.printStackTrace(); + } finally { + SCLContext.getCurrent().put("graph", oldGraph); + } + } + } + return results; + } + + public static Map dynamicSizeMaps(ReadGraph graph) throws DatabaseException { + List sharedOntologies = Simantics.applySCL("Simantics/SharedOntologies", "getSharedOntologies", graph, Tuple0.INSTANCE); + + Map results = new HashMap<>(); + Layer0 L0 = Layer0.getInstance(graph); + for (Resource sharedOntology : sharedOntologies) { + Resource sclModule = Layer0Utils.getPossibleChild(graph, sharedOntology, L0.SCLModule, COMMON_DYNAMIC_VISUALISATIONS_MODULE); + if (sclModule != null) { + String moduleURI = graph.getURI(sclModule); + Object oldGraph = SCLContext.getCurrent().get("graph"); + try { + // let's put the graph to SCLContext for resolving the color maps + SCLContext.getCurrent().put("graph", graph); + @SuppressWarnings("unchecked") + List result = (List) SCLOsgi.MODULE_REPOSITORY.getValue(moduleURI, SIZE_MAP_CONTRIBUTION); + + for (DynamicSizeMap sizeMap : result) { + results.put(sizeMap.getLabel(), sizeMap); + } + } catch (ValueNotFound e) { + e.printStackTrace(); + } finally { + SCLContext.getCurrent().put("graph", oldGraph); + } + } + } + return results; + } + + public static Collection dynamicColoringObjects(ReadGraph graph) throws DatabaseException { + + List sharedOntologies = Simantics.applySCL("Simantics/SharedOntologies", "getSharedOntologies", graph, Tuple0.INSTANCE); + + List results = new ArrayList<>(); + + for (Resource sharedOntology : sharedOntologies) { + Collection findByType = graph.syncRequest(new ObjectsWithSupertype(sharedOntology, Layer0.getInstance(graph).ConsistsOf, StructuralResource2.getInstance(graph).Component)); + //Collection findByType = QueryIndexUtils.searchByType(graph, sharedOntology, ); + for (Resource find : findByType) { + NamedResource moduleType = new NamedResource(NameLabelUtil.modalName(graph, find), find); + DynamicColoringObject dynamicColoringObject = dynamicColoringObject(graph, moduleType); + if (dynamicColoringObject != null) + results.add(dynamicColoringObject); + } + } + return results; + } + + public static Collection dynamicSizingObjects(ReadGraph graph) throws DatabaseException { + + List sharedOntologies = Simantics.applySCL("Simantics/SharedOntologies", "getSharedOntologies", graph, Tuple0.INSTANCE); + + List results = new ArrayList<>(); + + for (Resource sharedOntology : sharedOntologies) { + Collection findByType = graph.syncRequest(new ObjectsWithSupertype(sharedOntology, Layer0.getInstance(graph).ConsistsOf, StructuralResource2.getInstance(graph).Component)); + //Collection findByType = QueryIndexUtils.searchByType(graph, sharedOntology, ); + for (Resource find : findByType) { + NamedResource moduleType = new NamedResource(NameLabelUtil.modalName(graph, find), find); + DynamicSizingObject dynamicSizingObject = dynamicSizingObject(graph, moduleType); + if (dynamicSizingObject != null) + results.add(dynamicSizingObject); + } + } + return results; + } + + private static DynamicColoringObject dynamicColoringObject(ReadGraph graph, NamedResource moduleType) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + Resource sclModule = Layer0Utils.getPossibleChild(graph, moduleType.getResource(), L0.SCLModule, DYNAMIC_VISUALISATIONS_CONTRIBUTION_MODULE); + if (sclModule != null) { + String moduleURI = graph.getURI(sclModule); + return new DynamicColoringObject(moduleType, getDynamicColorContributionSupplier(moduleURI, COLOR_CONTRIBUTION)); + } + return null; + } + +// private static DynamicColoringMap dynamicColoringMap(ReadGraph graph, NamedResource moduleType) throws DatabaseException { +// Layer0 L0 = Layer0.getInstance(graph); +// Resource sclModule = Layer0Utils.getPossibleChild(graph, moduleType.getResource(), L0.SCLModule, DYNAMIC_VISUALISATIONS_CONTRIBUTION_MODULE); +// if (sclModule != null) { +// String moduleURI = graph.getURI(sclModule); +// return new DynamicColoringMap(moduleType, getDynamicColoringMapSupplier(moduleURI, COLOR_CONTRIBUTION)); +// } +// return null; +// } + + private static DynamicSizingObject dynamicSizingObject(ReadGraph graph, NamedResource moduleType) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + Resource sclModule = Layer0Utils.getPossibleChild(graph, moduleType.getResource(), L0.SCLModule, DYNAMIC_VISUALISATIONS_CONTRIBUTION_MODULE); + if (sclModule != null) { + String moduleURI = graph.getURI(sclModule); + return new DynamicSizingObject(moduleType, getDynamicSizeContributionSupplier(moduleURI, SIZE_CONTRIBUTION)); + } + return null; + } + + private static Supplier> getDynamicColorMapSupplier(String uri, String expressionText) { + return () -> { + try { + @SuppressWarnings("unchecked") + List result = (List) SCLOsgi.MODULE_REPOSITORY.getValue(uri, expressionText); + return result.stream();//result.stream().map(DynamicColorContribution::fromTuple9); + } catch (ValueNotFound e) { + LOGGER.error("Could not find contributions", e); + //throw new RuntimeException(e); + return Stream.empty(); + } + }; + } + + private static Supplier> getDynamicColorContributionSupplier(String uri, String expressionText) { + return () -> { + try { + @SuppressWarnings("unchecked") + List result = (List) SCLOsgi.MODULE_REPOSITORY.getValue(uri, expressionText); + return result.stream();//result.stream().map(DynamicColorContribution::fromTuple9); + } catch (ValueNotFound e) { + LOGGER.error("Could not find contributions", e); + //throw new RuntimeException(e); + return Stream.empty(); + } + }; + } + + private static Supplier> getDynamicSizeContributionSupplier(String uri, String expressionText) { + return () -> { + try { + @SuppressWarnings("unchecked") + List result = (List) SCLOsgi.MODULE_REPOSITORY.getValue(uri, expressionText); + return result.stream();//result.stream().map(DynamicColorContribution::fromTuple9); + } catch (ValueNotFound e) { + LOGGER.error("Could not find contributions", e); + //throw new RuntimeException(e); + return Stream.empty(); + } + }; + } + + public static class DynamicColoringObject { + + private final NamedResource coloringObject; + private final Supplier> colorContributionSupplier; + private Map colorContributions; + + public DynamicColoringObject(NamedResource coloringObject, Supplier> colorContributionSupplier) { + this.coloringObject = coloringObject; + this.colorContributionSupplier = colorContributionSupplier; + } + + public NamedResource getColoringObject() { + return coloringObject; + } + + public Map getColorContributions() { + if (colorContributions == null) + colorContributions = colorContributionSupplier.get().collect(Collectors.toMap(c -> c.getLabel(), c -> c)); + return colorContributions; + } + } + + public static class DynamicColoringMap { + + private final NamedResource coloringObject; + private final Supplier> colorContributionSupplier; + private Map colorContributions; + + public DynamicColoringMap(NamedResource coloringObject, Supplier> colorContributionSupplier) { + this.coloringObject = coloringObject; + this.colorContributionSupplier = colorContributionSupplier; + } + + public NamedResource getColoringObject() { + return coloringObject; + } + + public Map getColorContributions() { + if (colorContributions == null) + colorContributions = colorContributionSupplier.get().collect(Collectors.toMap(c -> c.getLabel(), c -> c)); + return colorContributions; + } + } + + public static class DynamicSizingObject { + + private final NamedResource sizingObject; + private final Supplier> sizeContributionSupplier; + private Map sizeContributions; + + public DynamicSizingObject(NamedResource coloringObject, Supplier> sizeContributionSupplier) { + this.sizingObject = coloringObject; + this.sizeContributionSupplier = sizeContributionSupplier; + } + + public NamedResource getSizingObject() { + return sizingObject; + } + + public Map getSizeContributions() { + if (sizeContributions == null) + sizeContributions = sizeContributionSupplier.get().collect(Collectors.toMap(c -> c.getLabel(), c -> c)); + return sizeContributions; + } + } +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/visualisations/VisualisationColoringObject.java b/org.simantics.district.network/src/org/simantics/district/network/visualisations/VisualisationColoringObject.java new file mode 100644 index 00000000..c13bcd41 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/visualisations/VisualisationColoringObject.java @@ -0,0 +1,56 @@ +package org.simantics.district.network.visualisations; + +public class VisualisationColoringObject { + + private String label; + private boolean used; + private String variable; + private double min; + private double max; + private String unit; + private String colorMap; + private boolean isDefault; + + public VisualisationColoringObject(String label, boolean used, String variable, double min, double max, String unit, String colorMap, boolean isDefault) { + this.label = label; + this.used = used; + this.variable = variable; + this.min = min; + this.max = max; + this.unit = unit; + this.colorMap = colorMap; + this.isDefault = isDefault; + } + + public String getLabel() { + return label; + } + + public boolean isUsed() { + return used; + } + + public String getVariable() { + return variable; + } + + public double getMin() { + return min; + } + + public double getMax() { + return max; + } + + public String getUnit() { + return unit; + } + + public String getColorMap() { + return colorMap; + } + + public boolean isDefault() { + return isDefault; + } +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/ColorBarOptions.java b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/ColorBarOptions.java new file mode 100644 index 00000000..a57ad913 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/ColorBarOptions.java @@ -0,0 +1,67 @@ +package org.simantics.district.network.visualisations.model; + +public class ColorBarOptions { + + private boolean showColorBars; + private boolean showColorBarsTicks; + private ColorBarsLocation location; + private ColorBarsSize size; + + public boolean isShowColorBars() { + return showColorBars; + } + + public ColorBarOptions showColorBars(boolean show) { + this.showColorBars = show; + return this; + } + + public boolean isShowColorBarsTicks() { + return showColorBarsTicks; + } + + public ColorBarOptions showColorBarsTicks(boolean show) { + this.showColorBarsTicks = show; + return this; + } + + public ColorBarsLocation getLocation() { + return location; + } + + public ColorBarOptions withLocation(ColorBarsLocation location) { + this.location = location; + return this; + } + + public ColorBarsSize getSize() { + return size; + } + + public ColorBarOptions withSize(ColorBarsSize size) { + this.size = size; + return this; + } + + public enum ColorBarsLocation { + NORTH, EAST, SOUTH, WEST + } + + public enum ColorBarsSize { + SMALL(1), MEDIUM(2), LARGE(3); + + int size; + + ColorBarsSize(int size) { + this.size = size; + } + + public int getSize() { + return size; + } + } + + public static ColorBarOptions useDefault() { + return new ColorBarOptions().showColorBars(true).withLocation(ColorBarsLocation.EAST).withSize(ColorBarsSize.SMALL); + } +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicColorContribution.java b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicColorContribution.java new file mode 100644 index 00000000..2839bcc3 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicColorContribution.java @@ -0,0 +1,83 @@ +package org.simantics.district.network.visualisations.model; + +public class DynamicColorContribution { + + private String label; + private String moduleName; + private String attributeName; + private String unit; + private double variableGain; + private double variableBias; + private DynamicColorMap defaultColorMap; + private double defaultMin; + private double defaultMax; + + // for graph persistence only + private boolean used; + private boolean useDefault; + + public DynamicColorContribution(String label, String moduleName, String attributeName, String unit, + double variableGain, double variableBias, DynamicColorMap defaultColorMap, double defaultMin, double defaultMax) { + this.label = label; + this.moduleName = moduleName; + this.attributeName = attributeName; + this.unit = unit; + this.variableGain = variableGain; + this.variableBias = variableBias; + this.defaultColorMap = defaultColorMap; + this.defaultMin = defaultMin; + this.defaultMax = defaultMax; + } + + public String getLabel() { + return label; + } + + public String getModuleName() { + return moduleName; + } + + public String getAttributeName() { + return attributeName; + } + + public String getUnit() { + return unit; + } + + public double getVariableGain() { + return variableGain; + } + + public double getVariableBias() { + return variableBias; + } + + public DynamicColorMap getDefaultColorMap() { + return defaultColorMap; + } + + public double getDefaultMin() { + return defaultMin; + } + + public double getDefaultMax() { + return defaultMax; + } + + public void setUsed(boolean used) { + this.used = used; + } + + public boolean isUsed() { + return used; + } + + public void setUseDefault(boolean useDefault) { + this.useDefault = useDefault; + } + + public boolean isUseDefault() { + return useDefault; + } +} \ No newline at end of file diff --git a/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicColorMap.java b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicColorMap.java new file mode 100644 index 00000000..09cb5794 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicColorMap.java @@ -0,0 +1,70 @@ +package org.simantics.district.network.visualisations.model; + +import java.awt.Color; +import java.util.Arrays; +import java.util.List; + +public class DynamicColorMap { + + static List blues = Arrays.asList(new RGBIntensity(0, 0, 0.1), new RGBIntensity(0, 0, 0.5), new RGBIntensity(0, 0, 0.9)); + + public static final DynamicColorMap DEFAULT = new DynamicColorMap("default", blues); + + private String label; + private List intensities; + + public DynamicColorMap(String label, List intensities) { + this.label = label; + this.intensities = intensities; + } + + public String getLabel() { + return label; + } + + public List getIntensities() { + return intensities; + } + + public static class RGBIntensity { + + private double red; + private double green; + private double blue; + + public RGBIntensity(double red, double green, double blue) { + this.red = red; + this.green = green; + this.blue = blue; + } + + public double getRed() { + return red; + } + + public double getGreen() { + return green; + } + + public double getBlue() { + return blue; + } + } + + public Color getColor(double value, double defaultMin, double defaultMax) { + + double gap = defaultMax - defaultMin; + double singleGap = gap / getIntensities().size(); + + int i = 0; + while (i < getIntensities().size() - 1) { + if (value <= defaultMin + (i * singleGap)) { + break; + } + i++; + } + + RGBIntensity intensity = getIntensities().get(i); + return new Color((float)intensity.getRed(), (float)intensity.getGreen(), (float)intensity.getBlue()); + } +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicSizeContribution.java b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicSizeContribution.java new file mode 100644 index 00000000..009c8c43 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicSizeContribution.java @@ -0,0 +1,83 @@ +package org.simantics.district.network.visualisations.model; + +public class DynamicSizeContribution { + + private String label; + private String moduleName; + private String attributeName; + private String unit; + private double variableGain; + private double variableBias; + private DynamicSizeMap defaultSizeMap; + private double defaultMin; + private double defaultMax; + + // for graph persistence only + private boolean used; + private boolean useDefault; + + public DynamicSizeContribution(String label, String moduleName, String attributeName, String unit, + double variableGain, double variableBias, DynamicSizeMap defaultSizeMap, double defaultMin, double defaultMax) { + this.label = label; + this.moduleName = moduleName; + this.attributeName = attributeName; + this.unit = unit; + this.variableGain = variableGain; + this.variableBias = variableBias; + this.defaultSizeMap = defaultSizeMap; + this.defaultMin = defaultMin; + this.defaultMax = defaultMax; + } + + public String getLabel() { + return label; + } + + public String getModuleName() { + return moduleName; + } + + public String getAttributeName() { + return attributeName; + } + + public String getUnit() { + return unit; + } + + public double getVariableGain() { + return variableGain; + } + + public double getVariableBias() { + return variableBias; + } + + public DynamicSizeMap getDefaultSizeMap() { + return defaultSizeMap; + } + + public double getDefaultMin() { + return defaultMin; + } + + public double getDefaultMax() { + return defaultMax; + } + + public boolean isUsed() { + return used; + } + + public void setUsed(boolean used) { + this.used = used; + } + + public boolean isUseDefault() { + return useDefault; + } + + public void setUseDefault(boolean useDefault) { + this.useDefault = useDefault; + } +} \ No newline at end of file diff --git a/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicSizeMap.java b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicSizeMap.java new file mode 100644 index 00000000..7edb12b9 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicSizeMap.java @@ -0,0 +1,38 @@ +package org.simantics.district.network.visualisations.model; + +import java.util.List; + +public class DynamicSizeMap { + + private String label; + private List sizes; + + public DynamicSizeMap(String label, List sizes) { + this.label = label; + this.sizes = sizes; + } + + public String getLabel() { + return label; + } + + public List getSizes() { + return sizes; + } + + public double getSize(double value, double defaultMin, double defaultMax) { + + double gap = defaultMax - defaultMin; + double singleGap = gap / getSizes().size(); + + int i = 0; + while (i < getSizes().size() - 1) { + if (value <= defaultMin + (i * singleGap)) { + break; + } + i++; + } + + return getSizes().get(i); + } +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicVisualisation.java b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicVisualisation.java new file mode 100644 index 00000000..24cf9bed --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicVisualisation.java @@ -0,0 +1,49 @@ +package org.simantics.district.network.visualisations.model; + +import java.util.Map; + +import org.simantics.db.Resource; + +public class DynamicVisualisation { + + private String name; + private Resource visualisationResource; + + private Map colorContributions; + private ColorBarOptions colorBarOptions; + private Map sizeContributions; + private SizeBarOptions sizeBarOptions; + + public DynamicVisualisation(String name, Resource visualisationResource, Map colorContributions, ColorBarOptions colorBarOptions, Map sizeContributions, SizeBarOptions sizeBarOptions) { + this.name = name; + this.visualisationResource = visualisationResource; + this.colorContributions = colorContributions; + this.colorBarOptions = colorBarOptions; + this.sizeContributions = sizeContributions; + this.sizeBarOptions = sizeBarOptions; + } + + public String getName() { + return name; + } + + public Resource getVisualisationResource() { + return visualisationResource; + } + + public Map getColorContributions() { + return colorContributions; + } + + public ColorBarOptions getColorBarOptions() { + return colorBarOptions; + } + + public Map getSizeContributions() { + return sizeContributions; + } + + public SizeBarOptions getSizeBarOptions() { + return sizeBarOptions; + } +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/SizeBarOptions.java b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/SizeBarOptions.java new file mode 100644 index 00000000..14e9f47f --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/visualisations/model/SizeBarOptions.java @@ -0,0 +1,66 @@ +package org.simantics.district.network.visualisations.model; + +public class SizeBarOptions { + + private boolean showColorBars; + private boolean showColorBarsTicks; + private SizeBarsLocation location; + private SizeBarsSize size; + + public boolean isShowSizeBars() { + return showColorBars; + } + + public SizeBarOptions showSizeBars(boolean show) { + this.showColorBars = show; + return this; + } + + public boolean isShowSizeBarsTicks() { + return showColorBarsTicks; + } + + public SizeBarOptions showSizeBarsTicks(boolean show) { + this.showColorBarsTicks = show; + return this; + } + + public SizeBarsLocation getLocation() { + return location; + } + + public SizeBarOptions withLocation(SizeBarsLocation location) { + this.location = location; + return this; + } + + public SizeBarsSize getSize() { + return size; + } + + public SizeBarOptions withSize(SizeBarsSize size) { + this.size = size; + return this; + } + public enum SizeBarsLocation { + NORTH, EAST, SOUTH, WEST + } + + public enum SizeBarsSize { + SMALL(1), MEDIUM(2), LARGE(3); + + int size; + + SizeBarsSize(int size) { + this.size = size; + } + + public double getSize() { + return size; + } + } + + public static SizeBarOptions useDefault() { + return new SizeBarOptions().showSizeBars(true).withLocation(SizeBarsLocation.EAST).withSize(SizeBarsSize.SMALL); + } +} diff --git a/org.simantics.district.region.ui/src/org/simantics/district/region/ui/DiagramRegionsTableUI.java b/org.simantics.district.region.ui/src/org/simantics/district/region/ui/DiagramRegionsTableUI.java index 562dd24b..bedea060 100644 --- a/org.simantics.district.region.ui/src/org/simantics/district/region/ui/DiagramRegionsTableUI.java +++ b/org.simantics.district.region.ui/src/org/simantics/district/region/ui/DiagramRegionsTableUI.java @@ -101,7 +101,8 @@ public class DiagramRegionsTableUI extends Composite { public Object[] getElements(Object inputElement) { if (inputElement == null && (!(inputElement instanceof Collection))) return new Object[0]; - Set set = (Set) inputElement; + @SuppressWarnings("unchecked") + Set set = (Set) inputElement; return set.toArray(); } diff --git a/org.simantics.district.region.ui/src/org/simantics/district/region/ui/handlers/RemoveRegionHandler.java b/org.simantics.district.region.ui/src/org/simantics/district/region/ui/handlers/RemoveRegionHandler.java index 46b11a2f..0fe76c36 100644 --- a/org.simantics.district.region.ui/src/org/simantics/district/region/ui/handlers/RemoveRegionHandler.java +++ b/org.simantics.district.region.ui/src/org/simantics/district/region/ui/handlers/RemoveRegionHandler.java @@ -18,7 +18,8 @@ import org.slf4j.LoggerFactory; public class RemoveRegionHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(RemoveRegionHandler.class); + @SuppressWarnings("unused") + private static final Logger LOGGER = LoggerFactory.getLogger(RemoveRegionHandler.class); public static final String COMMAND_ID = "org.simantics.district.region.ui.command.removeRegion"; public static final String LABEL = "Remove Region"; diff --git a/org.simantics.district.route.ontology/META-INF/MANIFEST.MF b/org.simantics.district.route.ontology/META-INF/MANIFEST.MF index ee4c1e64..61ec5737 100644 --- a/org.simantics.district.route.ontology/META-INF/MANIFEST.MF +++ b/org.simantics.district.route.ontology/META-INF/MANIFEST.MF @@ -6,7 +6,10 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: Semantum Oy Require-Bundle: org.simantics.layer0, org.simantics.district.network.ontology;bundle-version="1.0.0", - org.simantics.diagram.ontology;bundle-version="2.2.0" + org.simantics.diagram.ontology;bundle-version="2.2.0", + org.simantics.action.ontology;bundle-version="1.1.0", + org.simantics.silk.ontology;bundle-version="1.1.0", + org.simantics.viewpoint.ontology;bundle-version="1.2.0" Automatic-Module-Name: fi.vtt.apros.district.route.ontology Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: org.simantics.district.route.ontology diff --git a/org.simantics.district.route.ontology/graph/DistrictNetworkRoutesViewpoint.pgraph b/org.simantics.district.route.ontology/graph/DistrictNetworkRoutesViewpoint.pgraph new file mode 100644 index 00000000..1b5c1ecd --- /dev/null +++ b/org.simantics.district.route.ontology/graph/DistrictNetworkRoutesViewpoint.pgraph @@ -0,0 +1,15 @@ +L0 = +DN = +DNR = +VP = +ACT = +SILK = + +RAC = DNR.RouteActionContext : VP.BrowseContext + @VP.actionContribution "Select Route" DNR.Route SILK.wand VP.EditActionCategory DNR.Actions.SelectRoute + @VP.actionContribution "Validate Route" DNR.Route SILK.accept VP.EditActionCategory DNR.Actions.ValidateRoute + +DNR.Actions : L0.Library + +DNR.Actions.SelectRoute : ACT.Action +DNR.Actions.ValidateRoute : ACT.Action \ No newline at end of file diff --git a/org.simantics.district.route.ontology/src/org/simantics/district/route/ontology/RouteResource.java b/org.simantics.district.route.ontology/src/org/simantics/district/route/ontology/RouteResource.java index def31255..b9641270 100644 --- a/org.simantics.district.route.ontology/src/org/simantics/district/route/ontology/RouteResource.java +++ b/org.simantics.district.route.ontology/src/org/simantics/district/route/ontology/RouteResource.java @@ -10,11 +10,19 @@ import org.simantics.db.service.QueryControl; public class RouteResource { + public final Resource Actions; + public final Resource Actions_SelectRoute; + public final Resource Actions_ValidateRoute; public final Resource Route; + public final Resource RouteActionContext; public final Resource RouteFolder; public static class URIs { + public static final String Actions = "http://www.simantics.org/DistrictNetworkRoutes-1.0/Actions"; + public static final String Actions_SelectRoute = "http://www.simantics.org/DistrictNetworkRoutes-1.0/Actions/SelectRoute"; + public static final String Actions_ValidateRoute = "http://www.simantics.org/DistrictNetworkRoutes-1.0/Actions/ValidateRoute"; public static final String Route = "http://www.simantics.org/DistrictNetworkRoutes-1.0/Route"; + public static final String RouteActionContext = "http://www.simantics.org/DistrictNetworkRoutes-1.0/RouteActionContext"; public static final String RouteFolder = "http://www.simantics.org/DistrictNetworkRoutes-1.0/RouteFolder"; } @@ -28,7 +36,11 @@ public class RouteResource { } public RouteResource(ReadGraph graph) { + Actions = getResourceOrNull(graph, URIs.Actions); + Actions_SelectRoute = getResourceOrNull(graph, URIs.Actions_SelectRoute); + Actions_ValidateRoute = getResourceOrNull(graph, URIs.Actions_ValidateRoute); Route = getResourceOrNull(graph, URIs.Route); + RouteActionContext = getResourceOrNull(graph, URIs.RouteActionContext); RouteFolder = getResourceOrNull(graph, URIs.RouteFolder); } diff --git a/org.simantics.district.route.ui/META-INF/MANIFEST.MF b/org.simantics.district.route.ui/META-INF/MANIFEST.MF index 3b531e70..70045519 100644 --- a/org.simantics.district.route.ui/META-INF/MANIFEST.MF +++ b/org.simantics.district.route.ui/META-INF/MANIFEST.MF @@ -15,7 +15,8 @@ Require-Bundle: org.eclipse.e4.core.di, org.simantics.ui, org.simantics.district.route, org.simantics.district.network.ui;bundle-version="1.0.0", - org.slf4j.api + org.slf4j.api, + org.simantics.modeling.ui;bundle-version="1.1.1" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Automatic-Module-Name: fi.vtt.apros.district.route.ui Import-Package: javax.annotation;version="1.0.0";resolution:=optional, diff --git a/org.simantics.district.route.ui/adapters.xml b/org.simantics.district.route.ui/adapters.xml new file mode 100644 index 00000000..94d23ae7 --- /dev/null +++ b/org.simantics.district.route.ui/adapters.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/org.simantics.district.route.ui/build.properties b/org.simantics.district.route.ui/build.properties index e29652c5..34579a4c 100644 --- a/org.simantics.district.route.ui/build.properties +++ b/org.simantics.district.route.ui/build.properties @@ -3,5 +3,6 @@ output.. = bin/ bin.includes = META-INF/,\ .,\ plugin.xml,\ + adapters.xml,\ fragment.e4xmi,\ OSGI-INF/ diff --git a/org.simantics.district.route.ui/fragment.e4xmi b/org.simantics.district.route.ui/fragment.e4xmi index 16c774b3..291140d8 100644 --- a/org.simantics.district.route.ui/fragment.e4xmi +++ b/org.simantics.district.route.ui/fragment.e4xmi @@ -6,12 +6,16 @@ + + + + diff --git a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteView.java b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteView.java index 4499782d..78b6be9e 100644 --- a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteView.java +++ b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteView.java @@ -10,9 +10,11 @@ import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.e4.ui.di.Focus; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.model.application.ui.menu.MHandledToolItem; import org.eclipse.e4.ui.model.application.ui.menu.MMenuContribution; import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory; import org.eclipse.e4.ui.model.application.ui.menu.MPopupMenu; +import org.eclipse.e4.ui.model.application.ui.menu.MToolBar; import org.eclipse.e4.ui.services.EMenuService; import org.eclipse.e4.ui.workbench.modeling.ESelectionService; import org.eclipse.swt.widgets.Composite; @@ -52,6 +54,37 @@ public class RouteView { } } part.getMenus().add(popupMenu); + + /** + * See + * http://www.vogella.com/tutorials/EclipsePlugin/article.html#adding-eclipse-4-x-parts-to-eclipse-3-x-applications-via-the-code-org-eclipse-ui-views-code-extension-point + * + * Cannot contribute toolbar items via fragment at this point + */ + MToolBar toolBar = MMenuFactory.INSTANCE.createToolBar(); + toolBar.setToBeRendered(true); + toolBar.getChildren().add(createActivateRouteToolItem(app)); + toolBar.getChildren().add(createDeactivateRouteToolItem(app)); + part.setToolbar(toolBar); + } + + + private static MHandledToolItem createActivateRouteToolItem(MApplication app) { + MHandledToolItem createHandledToolItem = MMenuFactory.INSTANCE.createHandledToolItem(); + createHandledToolItem.setCommand(app.getCommand("org.simantics.district.route.ui.command.activatecreateroute")); // Command is contributed via fragment //$NON-NLS-1$ + createHandledToolItem.setLabel("Activate create route"); + createHandledToolItem.setIconURI("platform:/plugin/com.famfamfam.silk/icons/table_add.png"); //$NON-NLS-1$ + createHandledToolItem.setEnabled(true); + return createHandledToolItem; + } + + private static MHandledToolItem createDeactivateRouteToolItem(MApplication app) { + MHandledToolItem createHandledToolItem = MMenuFactory.INSTANCE.createHandledToolItem(); + createHandledToolItem.setCommand(app.getCommand("org.simantics.district.route.ui.command.deactivatecreateroute")); // Command is contributed via fragment //$NON-NLS-1$ + createHandledToolItem.setLabel("Deactivate create route"); + createHandledToolItem.setIconURI("platform:/plugin/com.famfamfam.silk/icons/table_edit.png"); //$NON-NLS-1$ + createHandledToolItem.setEnabled(true); + return createHandledToolItem; } @PostConstruct diff --git a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/actions/SelectRouteAction.java b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/actions/SelectRouteAction.java new file mode 100644 index 00000000..11de4cc4 --- /dev/null +++ b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/actions/SelectRouteAction.java @@ -0,0 +1,67 @@ +package org.simantics.district.route.ui.actions; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.swt.widgets.Display; +import org.simantics.Simantics; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.adapter.ActionFactory; +import org.simantics.district.network.ui.DistrictNetworkUIUtil; +import org.simantics.district.route.Route; +import org.simantics.district.route.RouteJob; +import org.simantics.district.route.RouterConfiguration; +import org.simantics.district.route.internal.RoutePersistence; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SelectRouteAction implements ActionFactory { + + protected static final Logger LOGGER = LoggerFactory.getLogger(SelectRouteAction.class); + + @Override + public Runnable create(Object target) { + if (!(target instanceof Resource)) + return null; + + return new Runnable() { + @Override + public void run() { + try { + Route route = Simantics.getSession().syncRequest(new RoutePersistence.RouteRequest((Resource)target)); + if (route == null) { + LOGGER.error("Reading a route object failed"); //$NON-NLS-1$ + return; + } + + CompletableFuture> result = new CompletableFuture<>(); + result + .exceptionally(e -> Collections.emptyList()) + .thenAccept(dnElements -> { + if (!dnElements.isEmpty()) { + try { + Display display = Display.getDefault(); + if (display != null) + DistrictNetworkUIUtil.openDNDiagramWithSelection(display, dnElements); + else + LOGGER.error("No display found"); //$NON-NLS-1$ + } catch (DatabaseException e) { + LOGGER.error("Failed to open district network diagram with selection " + dnElements, e); //$NON-NLS-1$ + + } + } + }); + + RouterConfiguration config = new RouterConfiguration(); + new RouteJob(config, route, result).schedule(); + } + catch (DatabaseException e) { + LOGGER.error("Error in selecting route " + target, e); //$NON-NLS-1$ + } + } + }; + } + +} diff --git a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/actions/ValidateRouteAction.java b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/actions/ValidateRouteAction.java new file mode 100644 index 00000000..5b0e7d05 --- /dev/null +++ b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/actions/ValidateRouteAction.java @@ -0,0 +1,58 @@ +package org.simantics.district.route.ui.actions; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.simantics.Simantics; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.adapter.ActionFactory; +import org.simantics.district.route.Route; +import org.simantics.district.route.RouteJob; +import org.simantics.district.route.RouterConfiguration; +import org.simantics.district.route.internal.RoutePersistence; +import org.simantics.utils.ui.dialogs.ShowMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ValidateRouteAction implements ActionFactory { + + protected static final Logger LOGGER = LoggerFactory.getLogger(ValidateRouteAction.class); + + @Override + public Runnable create(Object target) { + if (!(target instanceof Resource)) + return null; + + return new Runnable() { + @Override + public void run() { + try { + Route route = Simantics.getSession().syncRequest(new RoutePersistence.RouteRequest((Resource)target)); + if (route == null) { + ShowMessage.showInformation("Route Validation", "Reading the route object failed"); + LOGGER.error("Reading a route object failed"); //$NON-NLS-1$ + return; + } + + CompletableFuture> result = new CompletableFuture<>(); + result.exceptionally(e -> null).thenAccept(dnElements -> { + if (dnElements != null) { + if (!dnElements.isEmpty()) + ShowMessage.showInformation("Route Validation", "The route is valid"); + else + ShowMessage.showInformation("Route Validation", "The route produced an empty list of vertices"); + } + }); + + RouterConfiguration config = new RouterConfiguration(); + new RouteJob(config, route, result).schedule(); + } + catch (DatabaseException e) { + LOGGER.error("Error in selecting route " + target, e); //$NON-NLS-1$ + } + } + }; + } +} diff --git a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/ActivateCreateRoute.java b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/ActivateCreateRoute.java new file mode 100644 index 00000000..0dc21353 --- /dev/null +++ b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/ActivateCreateRoute.java @@ -0,0 +1,53 @@ +package org.simantics.district.route.ui.handlers; + +import javax.inject.Named; + +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorPart; +import org.simantics.district.network.ui.DistrictDiagramEditor; +import org.simantics.district.network.ui.participants.RoutingMode; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.modeling.ui.diagramEditor.DiagramViewer; +import org.simantics.utils.ui.workbench.WorkbenchUtils; + +/** + * @author Jani Simomaa + */ +public class ActivateCreateRoute { + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + IEditorPart activeEditor = WorkbenchUtils.getActiveEditor(); + if (activeEditor != null && activeEditor instanceof DistrictDiagramEditor) { + DistrictDiagramEditor editor = (DistrictDiagramEditor) activeEditor; + DiagramViewer viewer = editor.getAdapter(DiagramViewer.class); + ICanvasContext context = viewer.getAdapter(ICanvasContext.class); + RoutingMode routingMode = context.getAtMostOneItemOfClass(RoutingMode.class); + return routingMode == null; + } + return false; + } + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell activeShell, + @Named(IServiceConstants.ACTIVE_PART) MPart part, + @Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + + IEditorPart activeEditor = WorkbenchUtils.getActiveEditor(); + if (activeEditor != null && activeEditor instanceof DistrictDiagramEditor) { + DistrictDiagramEditor editor = (DistrictDiagramEditor) activeEditor; + DiagramViewer viewer = editor.getAdapter(DiagramViewer.class); + ICanvasContext context = viewer.getAdapter(ICanvasContext.class); + RoutingMode routingMode = context.getAtMostOneItemOfClass(RoutingMode.class); + if (routingMode == null || routingMode.isRemoved()) { + routingMode = new RoutingMode(0); + context.add(routingMode); + } + } + } +} \ No newline at end of file diff --git a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/DeactivateCreateRoute.java b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/DeactivateCreateRoute.java new file mode 100644 index 00000000..a60478d8 --- /dev/null +++ b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/DeactivateCreateRoute.java @@ -0,0 +1,56 @@ +package org.simantics.district.route.ui.handlers; + +import javax.inject.Named; + +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorPart; +import org.simantics.district.network.ui.DistrictDiagramEditor; +import org.simantics.district.network.ui.participants.RoutingMode; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.modeling.ui.diagramEditor.DiagramViewer; +import org.simantics.utils.ui.workbench.WorkbenchUtils; + +/** + * @author Jani Simomaa + */ +public class DeactivateCreateRoute { + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + IEditorPart activeEditor = WorkbenchUtils.getActiveEditor(); + if (activeEditor != null && activeEditor instanceof DistrictDiagramEditor) { + DistrictDiagramEditor editor = (DistrictDiagramEditor) activeEditor; + DiagramViewer viewer = editor.getAdapter(DiagramViewer.class); + ICanvasContext context = viewer.getAdapter(ICanvasContext.class); + RoutingMode routingMode = context.getAtMostOneItemOfClass(RoutingMode.class); + return routingMode != null; + } + return false; + } + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell activeShell, + @Named(IServiceConstants.ACTIVE_PART) MPart part, + @Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + + IEditorPart activeEditor = WorkbenchUtils.getActiveEditor(); + if (activeEditor != null && activeEditor instanceof DistrictDiagramEditor) { + DistrictDiagramEditor editor = (DistrictDiagramEditor) activeEditor; + DiagramViewer viewer = editor.getAdapter(DiagramViewer.class); + ICanvasContext context = viewer.getAdapter(ICanvasContext.class); + +// context.getSceneGraph +// +// RoutingMode routingMode = context.getAtMostOneItemOfClass(RoutingMode.class); +// if (routingMode == null || routingMode.isRemoved()) { +// routingMode = new RoutingMode(0); +// context.add(routingMode); +// } + } + } +} \ No newline at end of file diff --git a/org.simantics.district.route/src/org/simantics/district/route/RouteService.java b/org.simantics.district.route/src/org/simantics/district/route/RouteService.java index 5d3f63dc..ce8c2848 100644 --- a/org.simantics.district.route/src/org/simantics/district/route/RouteService.java +++ b/org.simantics.district.route/src/org/simantics/district/route/RouteService.java @@ -30,4 +30,6 @@ public interface RouteService { List routers(); + Route readRoute(Object backendRouteObject); + } diff --git a/org.simantics.district.route/src/org/simantics/district/route/internal/RoutePersistence.java b/org.simantics.district.route/src/org/simantics/district/route/internal/RoutePersistence.java index fceb6612..4bc0c6f0 100644 --- a/org.simantics.district.route/src/org/simantics/district/route/internal/RoutePersistence.java +++ b/org.simantics.district.route/src/org/simantics/district/route/internal/RoutePersistence.java @@ -23,6 +23,7 @@ import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.QueryIndexUtils; import org.simantics.db.layer0.request.PossibleActiveModel; +import org.simantics.db.layer0.request.PossibleModel; import org.simantics.db.layer0.util.RemoverUtil; import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.district.route.Waypoint; @@ -135,16 +136,33 @@ public class RoutePersistence { List routes = new ArrayList<>(); for (Resource route : graph.syncRequest(new ObjectsWithType(rf, L0.ConsistsOf, RR.Route))) { - RouteImpl ri = new RouteImpl(graph.getRelatedValue(route, L0.HasLabel, Bindings.STRING)) - .backend(route) - .modelEntity(model) - .waypoints(toWaypoints(graph, ListUtils.toList(graph, route))); - routes.add(ri); + routes.add(getRoute(graph, model, route)); } return routes; } + public static RouteImpl getRoute(ReadGraph graph, Resource model, Resource route) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + RouteImpl ri = new RouteImpl(graph.getRelatedValue(route, L0.HasLabel, Bindings.STRING)) + .backend(route) + .modelEntity(model) + .waypoints(toWaypoints(graph, ListUtils.toList(graph, route))); + return ri; + } + + public static class RouteRequest extends ResourceRead { + public RouteRequest(Resource resource) { + super(resource); + } + + @Override + public RouteImpl perform(ReadGraph graph) throws DatabaseException { + Resource model = graph.syncRequest(new PossibleModel(resource)); + return model != null ? getRoute(graph, model, resource) : null; + } + } + public static class ModelRoutesRequest extends ResourceRead> { public ModelRoutesRequest(Resource model) { super(model); @@ -154,7 +172,7 @@ public class RoutePersistence { return findRoutes(graph, resource); } } - + public static class ActiveModelRoutesRequest extends UniqueRead> { @Override public List perform(ReadGraph graph) throws DatabaseException { diff --git a/org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java b/org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java index 6817ea32..4690ac46 100644 --- a/org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java +++ b/org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java @@ -13,6 +13,7 @@ import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.common.procedure.adapter.DisposableListener; import org.simantics.db.common.procedure.adapter.DisposableSyncListener; +import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.PossibleActiveModel; import org.simantics.db.management.ISessionContext; import org.simantics.db.management.ISessionContextChangedListener; @@ -201,4 +202,17 @@ public class RouteServiceImpl implements RouteService, ISessionContextChangedLis }); } + @Override + public Route readRoute(Object backendRouteObject) { + if (!(backendRouteObject instanceof Resource)) + return null; + + try { + return Simantics.getSession().syncRequest(new RoutePersistence.RouteRequest((Resource)backendRouteObject)); + } catch (DatabaseException e) { + LOGGER.error("Failed to read district route object for " + backendRouteObject, e); + return null; + } + } + } diff --git a/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/DeleteElementSelector.java b/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/DeleteElementSelector.java index 4382e70c..6a005c41 100644 --- a/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/DeleteElementSelector.java +++ b/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/DeleteElementSelector.java @@ -20,6 +20,7 @@ import org.slf4j.LoggerFactory; public class DeleteElementSelector { + @SuppressWarnings("unused") private static final Logger LOGGER = LoggerFactory.getLogger(DeleteElementSelector.class); @Inject diff --git a/org.simantics.district.selection/.gitignore b/org.simantics.district.selection/.gitignore new file mode 100644 index 00000000..eb92844f --- /dev/null +++ b/org.simantics.district.selection/.gitignore @@ -0,0 +1 @@ +/graph.tg diff --git a/org.simantics.district.selection/graph.tg b/org.simantics.district.selection/graph.tg deleted file mode 100644 index fb07142b..00000000 Binary files a/org.simantics.district.selection/graph.tg and /dev/null differ diff --git a/org.simantics.district.ui.feature/feature.xml b/org.simantics.district.ui.feature/feature.xml index d563d637..213c4426 100644 --- a/org.simantics.district.ui.feature/feature.xml +++ b/org.simantics.district.ui.feature/feature.xml @@ -62,4 +62,11 @@ version="0.0.0" unpack="false"/> + + diff --git a/org.simantics.maps.elevation.server.ui/.classpath b/org.simantics.maps.elevation.server.ui/.classpath new file mode 100644 index 00000000..eca7bdba --- /dev/null +++ b/org.simantics.maps.elevation.server.ui/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.simantics.maps.elevation.server.ui/.project b/org.simantics.maps.elevation.server.ui/.project new file mode 100644 index 00000000..ddcb3811 --- /dev/null +++ b/org.simantics.maps.elevation.server.ui/.project @@ -0,0 +1,28 @@ + + + org.simantics.maps.elevation.server.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.simantics.maps.elevation.server.ui/META-INF/MANIFEST.MF b/org.simantics.maps.elevation.server.ui/META-INF/MANIFEST.MF new file mode 100644 index 00000000..fe2d6d66 --- /dev/null +++ b/org.simantics.maps.elevation.server.ui/META-INF/MANIFEST.MF @@ -0,0 +1,13 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Simantics Maps Elevation Server UI +Bundle-SymbolicName: org.simantics.maps.elevation.server.ui;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: org.simantics.maps.elevation.server.ui.Activator +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.slf4j.api;bundle-version="1.7.25", + org.simantics.maps.elevation.server;bundle-version="1.0.0" +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Automatic-Module-Name: org.simantics.maps.elevation.server.ui +Bundle-ActivationPolicy: lazy diff --git a/org.simantics.maps.elevation.server.ui/build.properties b/org.simantics.maps.elevation.server.ui/build.properties new file mode 100644 index 00000000..e9863e28 --- /dev/null +++ b/org.simantics.maps.elevation.server.ui/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/org.simantics.maps.elevation.server.ui/plugin.xml b/org.simantics.maps.elevation.server.ui/plugin.xml new file mode 100644 index 00000000..1e8eaa1a --- /dev/null +++ b/org.simantics.maps.elevation.server.ui/plugin.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/org.simantics.maps.elevation.server.ui/pom.xml b/org.simantics.maps.elevation.server.ui/pom.xml new file mode 100644 index 00000000..84a32b99 --- /dev/null +++ b/org.simantics.maps.elevation.server.ui/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + + org.simantics.district + org.simantics.district.root + 1.0.0-SNAPSHOT + + + org.simantics.maps.elevation.server.ui + eclipse-plugin + 1.0.0-SNAPSHOT + + diff --git a/org.simantics.maps.elevation.server.ui/src/org/simantics/maps/elevation/server/ui/Activator.java b/org.simantics.maps.elevation.server.ui/src/org/simantics/maps/elevation/server/ui/Activator.java new file mode 100644 index 00000000..980dd165 --- /dev/null +++ b/org.simantics.maps.elevation.server.ui/src/org/simantics/maps/elevation/server/ui/Activator.java @@ -0,0 +1,50 @@ +package org.simantics.maps.elevation.server.ui; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.simantics.maps.elevation.server.ui"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/org.simantics.maps.elevation.server.ui/src/org/simantics/maps/elevation/server/ui/MapsElevationServerPreferencePage.java b/org.simantics.maps.elevation.server.ui/src/org/simantics/maps/elevation/server/ui/MapsElevationServerPreferencePage.java new file mode 100644 index 00000000..366ca031 --- /dev/null +++ b/org.simantics.maps.elevation.server.ui/src/org/simantics/maps/elevation/server/ui/MapsElevationServerPreferencePage.java @@ -0,0 +1,85 @@ +package org.simantics.maps.elevation.server.ui; + +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.IntegerFieldEditor; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Group; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.preferences.ScopedPreferenceStore; +import org.simantics.maps.elevation.server.SingletonTiffTileInterface; +import org.simantics.maps.elevation.server.prefs.MapsElevationServerPreferences; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MapsElevationServerPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + private static final Logger LOGGER = LoggerFactory.getLogger(MapsElevationServerPreferencePage.class); + + public MapsElevationServerPreferencePage() { + super(GRID); + setDescription("Maps elevation server preferences"); + } + + @Override + protected IPreferenceStore doGetPreferenceStore() { + return new ScopedPreferenceStore(InstanceScope.INSTANCE, MapsElevationServerPreferences.P_NODE); + } + + private void createGeneralGroup() { + Group serverGroup = new Group(getFieldEditorParent(), SWT.NONE); + serverGroup.setText("General"); + GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(serverGroup); + + BooleanFieldEditor automatically = new BooleanFieldEditor(MapsElevationServerPreferences.P_USE_ELEVATION_SERVER, "Use elevation server", serverGroup); + addField(automatically); + + StringFieldEditor tifFolderPath = new StringFieldEditor(MapsElevationServerPreferences.P_TIFF_FOLDER_PATH, "Tiff folder path", serverGroup); + addField(tifFolderPath); + + IntegerFieldEditor pipeDepth = new IntegerFieldEditor(MapsElevationServerPreferences.P_PIPE_DEPTH_UNDER_GROUND, "Pipe depth under ground", serverGroup); + pipeDepth.setValidRange(Integer.MIN_VALUE, Integer.MAX_VALUE); + addField(pipeDepth); + + GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false).extendedMargins(12, 12, 12, 12).spacing(5, 4).applyTo(serverGroup); + } + + @Override + protected void createFieldEditors() { + createGeneralGroup(); + + } + + @Override + protected void performApply() { + super.performApply(); + } + + @Override + public boolean performOk() { + boolean ok = super.performOk(); + if (ok) { + try { + SingletonTiffTileInterface.reloadElevationServer(); + } catch (Exception e) { + LOGGER.error("Could not reload elevation server", e); + setErrorMessage(e.getMessage()); + return false; + } + } + return ok; + } + + @Override + public void init(IWorkbench workbench) { + + } + + +} diff --git a/org.simantics.maps.elevation.server/.classpath b/org.simantics.maps.elevation.server/.classpath new file mode 100644 index 00000000..eca7bdba --- /dev/null +++ b/org.simantics.maps.elevation.server/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.simantics.maps.elevation.server/.project b/org.simantics.maps.elevation.server/.project new file mode 100644 index 00000000..fb4071cd --- /dev/null +++ b/org.simantics.maps.elevation.server/.project @@ -0,0 +1,28 @@ + + + org.simantics.maps.elevation.server + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.simantics.maps.elevation.server/META-INF/MANIFEST.MF b/org.simantics.maps.elevation.server/META-INF/MANIFEST.MF new file mode 100644 index 00000000..6ea239a4 --- /dev/null +++ b/org.simantics.maps.elevation.server/META-INF/MANIFEST.MF @@ -0,0 +1,16 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Simantics Maps Elevation Server +Bundle-SymbolicName: org.simantics.maps.elevation.server;singleton:=true +Bundle-Version: 1.0.0.qualifier +Automatic-Module-Name: org.simantics.maps.elevation.server +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: org.simantics.district.geotools;bundle-version="1.0.0", + org.slf4j.api;bundle-version="1.7.25", + com.github.benmanes.caffeine;bundle-version="2.6.2", + org.eclipse.osgi, + org.eclipse.core.runtime;bundle-version="3.13.0", + org.eclipse.equinox.preferences +Export-Package: org.simantics.maps.elevation.server, + org.simantics.maps.elevation.server.prefs +Bundle-ActivationPolicy: lazy diff --git a/org.simantics.maps.elevation.server/build.properties b/org.simantics.maps.elevation.server/build.properties new file mode 100644 index 00000000..a4fd10d4 --- /dev/null +++ b/org.simantics.maps.elevation.server/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + scl/ diff --git a/org.simantics.maps.elevation.server/pom.xml b/org.simantics.maps.elevation.server/pom.xml new file mode 100644 index 00000000..f2db8685 --- /dev/null +++ b/org.simantics.maps.elevation.server/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + + org.simantics.district + org.simantics.district.root + 1.0.0-SNAPSHOT + + + org.simantics.maps.elevation.server + eclipse-plugin + 1.0.0-SNAPSHOT + + diff --git a/org.simantics.maps.elevation.server/scl/Simantics/District/Elevation.scl b/org.simantics.maps.elevation.server/scl/Simantics/District/Elevation.scl new file mode 100644 index 00000000..9edd6dac --- /dev/null +++ b/org.simantics.maps.elevation.server/scl/Simantics/District/Elevation.scl @@ -0,0 +1,4 @@ + +importJava "org.simantics.maps.elevation.server.SingletonTiffTileInterface" where + @JavaName lookupd + lookupElevation :: Double -> Double -> Double \ No newline at end of file diff --git a/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/Activator.java b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/Activator.java new file mode 100644 index 00000000..c003700f --- /dev/null +++ b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/Activator.java @@ -0,0 +1,47 @@ +package org.simantics.maps.elevation.server; + +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.imageio.spi.IIORegistry; +import javax.imageio.spi.ImageInputStreamSpi; +import javax.imageio.spi.ImageOutputStreamSpi; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +import it.geosolutions.imageio.stream.input.spi.FileImageInputStreamExtImplSpi; +import it.geosolutions.imageio.stream.input.spi.StringImageInputStreamSpi; +import it.geosolutions.imageio.stream.input.spi.URLImageInputStreamSpi; +import it.geosolutions.imageio.stream.output.spi.FileImageOutputStreamExtImplSpi; +import it.geosolutions.imageio.stream.output.spi.StringImageOutputStreamSpi; +import it.geosolutions.imageio.stream.output.spi.URLImageOutputStreamSpi; + +public class Activator implements BundleActivator { + + private static AtomicBoolean registered = new AtomicBoolean(false); + + public static final String PLUGIN_ID = "org.simantics.maps.elevation.server"; + + @Override + public void start(BundleContext context) throws Exception { + // register SPI's + registerSpis(); + } + + public static void registerSpis() { + if (!registered.getAndSet(true)) { + IIORegistry.getDefaultInstance().registerServiceProvider(new FileImageInputStreamExtImplSpi(), ImageInputStreamSpi.class); + IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi(), ImageInputStreamSpi.class); + IIORegistry.getDefaultInstance().registerServiceProvider(new StringImageInputStreamSpi(), ImageInputStreamSpi.class); + IIORegistry.getDefaultInstance().registerServiceProvider(new FileImageOutputStreamExtImplSpi(), ImageOutputStreamSpi.class); + IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageOutputStreamSpi(), ImageOutputStreamSpi.class); + IIORegistry.getDefaultInstance().registerServiceProvider(new StringImageOutputStreamSpi(), ImageOutputStreamSpi.class); + } + } + + @Override + public void stop(BundleContext context) throws Exception { + + } + +} diff --git a/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/SingletonTiffTileInterface.java b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/SingletonTiffTileInterface.java new file mode 100644 index 00000000..ced1c16b --- /dev/null +++ b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/SingletonTiffTileInterface.java @@ -0,0 +1,73 @@ +package org.simantics.maps.elevation.server; + +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; + +import org.simantics.maps.elevation.server.prefs.MapsElevationServerPreferences; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SingletonTiffTileInterface { + + private static final Logger LOGGER = LoggerFactory.getLogger(SingletonTiffTileInterface.class); + + private static SingletonTiffTileInterface instance; + private TiffTileInterface tileInterface; + + static { + // register spis + Activator.registerSpis(); + } + + private SingletonTiffTileInterface() { + Path tilesFolder = Paths.get(MapsElevationServerPreferences.tifFolderPath()); + try { + Files.createDirectories(tilesFolder); + } catch (IOException e) { + LOGGER.error("Could not create directories {}", tilesFolder.toAbsolutePath(), e); + } + LOGGER.info("Elevation server looking for tif files at {}", tilesFolder.toAbsolutePath()); + tileInterface = new TiffTileInterface(tilesFolder); + } + + private static SingletonTiffTileInterface getInstance() { + if (instance == null ) { + synchronized (SingletonTiffTileInterface.class) { + if (instance == null) { + instance = new SingletonTiffTileInterface(); + } + } + } + return instance; + } + + public static synchronized void reloadElevationServer() { + if (instance != null) { + try { + instance.tileInterface.close(); + } catch (IOException e) { + LOGGER.error("Could not close current elevation server interface", e); + } finally { + instance = null; + } + } + // let's re-initialize + getInstance(); + } + + public static double lookupd(double x, double y) { + return lookup(x, y).doubleValue(); + } + + public static Number lookup(double x, double y) { + return getInstance().tileInterface.lookup(x, y); + } + + public static Collection getBoundingBoxes() { + return getInstance().tileInterface.getBoundingBoxes(); + } +} diff --git a/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffInterface.java b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffInterface.java new file mode 100644 index 00000000..f5935318 --- /dev/null +++ b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffInterface.java @@ -0,0 +1,107 @@ +package org.simantics.maps.elevation.server; + +import java.awt.image.DataBuffer; +import java.io.Closeable; +import java.nio.file.Path; + +import org.geotools.coverage.grid.GridCoverage2D; +import org.geotools.gce.geotiff.GeoTiffReader; +import org.geotools.geometry.Envelope2D; +import org.geotools.referencing.CRS; +import org.opengis.geometry.DirectPosition; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.operation.MathTransform; +import org.simantics.maps.elevation.server.prefs.MapsElevationServerPreferences; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TiffInterface implements Closeable { + + private static final Logger LOGGER = LoggerFactory.getLogger(TiffInterface.class); + + private final Path tifPath; + private GridCoverage2D coverage; + + private boolean init = false; + + public TiffInterface(Path tifPath) { + this.tifPath = tifPath; + loadMetadata(); + } + + private void loadMetadata() { + GeoTiffReader reader = null; + try { + reader = new GeoTiffReader(this.tifPath.toFile()); + this.coverage = reader.read(null); + this.init = true; + } catch (Exception e) { + LOGGER.error("Could not load {}", tifPath, e); + } finally { + if (reader != null) { + reader.dispose(); + } + } + } + + public boolean contains(DirectPosition pos) { + ensureInit(); + Envelope2D e = coverage.getEnvelope2D(); + try { + MathTransform transform = CRS.findMathTransform(pos.getCoordinateReferenceSystem(), getCRS(), false); + DirectPosition target = transform.transform(pos, null); + boolean contains = e.contains(target); + return contains; + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + } + + public Number lookup(DirectPosition pos) { + ensureInit(); + Object r = coverage.evaluate(pos); + final int dataType = coverage.getRenderedImage().getSampleModel().getDataType(); + int pipeDepthUnderGround = MapsElevationServerPreferences.pipeDepthUnderGround(); + switch (dataType) { + case DataBuffer.TYPE_BYTE: { + // TODO: if the result is byte how does one subtract the pipeDepth form the value? + // Might not be even relevant with this use case + return new Byte(((byte[]) r)[0]); + } + case DataBuffer.TYPE_SHORT: // Fall through + case DataBuffer.TYPE_USHORT: // Fall through + case DataBuffer.TYPE_INT: { + int val = ((int[]) r)[0] - pipeDepthUnderGround; + return new Integer(val); + } + case DataBuffer.TYPE_FLOAT: { + float val = ((float[]) r)[0] - pipeDepthUnderGround; + return new Float(val); + } + case DataBuffer.TYPE_DOUBLE: { + double val = ((double[]) r)[0] - pipeDepthUnderGround; + return new Double(val); + } + default: return null; + } + } + + private void ensureInit() { + if (!init) { + throw new IllegalStateException("Interface is not initialized for " + this.tifPath); + } + } + + public void close() { + coverage.dispose(true); + } + + public Envelope2D getCornerCoords() { + return coverage.getEnvelope2D(); + } + + public CoordinateReferenceSystem getCRS() { + return coverage.getCoordinateReferenceSystem(); + } +} diff --git a/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffTileInterface.java b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffTileInterface.java new file mode 100644 index 00000000..8f7ecc01 --- /dev/null +++ b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffTileInterface.java @@ -0,0 +1,147 @@ +package org.simantics.maps.elevation.server; + +import java.awt.geom.Rectangle2D; +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.geotools.geometry.DirectPosition2D; +import org.geotools.geometry.Envelope2D; +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.geotools.referencing.CRS; +import org.opengis.geometry.DirectPosition; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.index.strtree.STRtree; + +public class TiffTileInterface implements Closeable { + + private static final Logger LOGGER = LoggerFactory.getLogger(TiffTileInterface.class); + + private Path tilesFolder; + private Map envelopes = new ConcurrentHashMap<>(); + private LoadingCache interfaceCache; + private int openInterfacesSize; + private STRtree index; + + public TiffTileInterface(Path tilesFolder) { + this(tilesFolder, 5); + } + + public TiffTileInterface(Path tilesFolder, int openInterfacesSize) { + if (!Files.isDirectory(tilesFolder)) { + throw new IllegalArgumentException("tilesFolder has to be a folder: " + tilesFolder.toAbsolutePath()); + } + this.tilesFolder = tilesFolder; + this.index = new STRtree(); + this.openInterfacesSize = openInterfacesSize; + + this.interfaceCache = Caffeine.newBuilder() + .maximumSize(this.openInterfacesSize) + .removalListener((key, gdalInterface, cause) -> ((TiffInterface) gdalInterface).close()) + .build(key -> new TiffInterface(key)); + + try { + initializeIndex(); + } catch (IOException e) { + LOGGER.error("Could not initialize index for folder {}", tilesFolder, e); + } + } + + private TiffInterface openTifInterface(Path tifFile) { + return interfaceCache.get(tifFile); + } + + private Stream allTiffFiles() throws IOException { + return Files.walk(tilesFolder).filter(Files::isRegularFile).filter(tif -> tif.getFileName().toString().endsWith(".tif")); + } + + public void initializeIndex() throws IOException { + LOGGER.info("Initializing index.."); + AtomicInteger counter = new AtomicInteger(); + allTiffFiles().parallel().forEach(tifFile -> { + TiffInterface tifInterface = openTifInterface(tifFile); + Envelope2D coords = tifInterface.getCornerCoords(); + try { + ReferencedEnvelope refEnv = new ReferencedEnvelope(coords); + ReferencedEnvelope targetEnv = refEnv.transform(c4326, false, 30); + + synchronized(index) { + index.insert(targetEnv, tifFile); + } + envelopes.put(tifFile, targetEnv); + } catch (Exception e) { + LOGGER.error("Could not initialize index for file {}", tifFile, e); + } finally { + tifInterface.close(); + int current = counter.getAndIncrement(); + if (current % 100 == 0) { + LOGGER.info(" {}", current); + } + } + }); + } + + public Collection getBoundingBoxes() { + Collection rects = envelopes.values().stream().map(env -> { + double x = env.getMinX(); + double y = env.getMinY(); + return new Rectangle2D.Double(x, y, env.getMaxX() - x, env.getMaxY() - y); + }).collect(Collectors.toList()); + return rects; + } + + private static CoordinateReferenceSystem c4326; + + static { + try { + c4326 = CRS.decode("EPSG:4326"); + } catch (Exception e) { + LOGGER.error("Could not initialize epsg:4326", e); + } + } + + public Number lookup(double x, double y) { + LOGGER.info("Looking up x={} y={}", x, y); + DirectPosition p = new DirectPosition2D(c4326, x, y); + List tifFiles = (List) index.query(new Envelope(new Coordinate(x, y))); + for (Path tifFile : tifFiles) { + TiffInterface tifInterface = openTifInterface(tifFile); + if (tifInterface.contains(p)) { + try { + return tifInterface.lookup(p); + } finally { + tifInterface.close(); + } + } else { + System.out.println("not found"); + } + } + return new Double(0); // use 0 by default for now + } + + @Override + public void close() throws IOException { + interfaceCache.invalidateAll(); + interfaceCache.cleanUp(); + + envelopes.clear(); + envelopes = null; + index = null; + interfaceCache = null; + } +} \ No newline at end of file diff --git a/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/prefs/MapsElevationServerPreferences.java b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/prefs/MapsElevationServerPreferences.java new file mode 100644 index 00000000..bcb59f2c --- /dev/null +++ b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/prefs/MapsElevationServerPreferences.java @@ -0,0 +1,30 @@ +package org.simantics.maps.elevation.server.prefs; + +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.osgi.service.prefs.Preferences; +import org.simantics.maps.elevation.server.Activator; + +public class MapsElevationServerPreferences { + + public static final String P_NODE = Activator.PLUGIN_ID; + + public static final String P_USE_ELEVATION_SERVER = "org.simantics.maps.elevation.server.useElevationServer"; + public static final String P_TIFF_FOLDER_PATH = "org.simantics.maps.elevation.server.tifsFolderPath"; + public static final String P_PIPE_DEPTH_UNDER_GROUND = "org.simantics.maps.elevation.server.pipeDepthUnderGround"; + + public static Preferences getPreferences() { + return InstanceScope.INSTANCE.getNode(MapsElevationServerPreferences.P_NODE); + } + + public static boolean useElevationServer() { + return getPreferences().getBoolean(P_USE_ELEVATION_SERVER, false); + } + + public static String tifFolderPath() { + return getPreferences().get(P_TIFF_FOLDER_PATH, "tifsFolder"); + } + + public static int pipeDepthUnderGround() { + return getPreferences().getInt(P_PIPE_DEPTH_UNDER_GROUND, -1); + } +} diff --git a/org.simantics.maps.server.feature/feature.xml b/org.simantics.maps.server.feature/feature.xml index 6feb1d77..d983b5ae 100644 --- a/org.simantics.maps.server.feature/feature.xml +++ b/org.simantics.maps.server.feature/feature.xml @@ -23,4 +23,18 @@ version="0.0.0" unpack="false"/> + + + + diff --git a/org.simantics.maps.server.ui/src/org/simantics/maps/server/ui/prefs/MapsServerPreferencePage.java b/org.simantics.maps.server.ui/src/org/simantics/maps/server/ui/prefs/MapsServerPreferencePage.java index 0dc33f13..8a1a6f91 100644 --- a/org.simantics.maps.server.ui/src/org/simantics/maps/server/ui/prefs/MapsServerPreferencePage.java +++ b/org.simantics.maps.server.ui/src/org/simantics/maps/server/ui/prefs/MapsServerPreferencePage.java @@ -89,10 +89,8 @@ public class MapsServerPreferencePage extends FieldEditorPreferencePage implemen String labelText = ""; String buttonText = ""; int labelColor = -1; - boolean running = false; try { if (server.isRunning()) { - running = true; buttonText = "Stop"; labelText = "running"; labelColor = SWT.COLOR_DARK_GREEN; diff --git a/org.simantics.maps.server/src/org/simantics/district/maps/server/TileserverMapnik.java b/org.simantics.maps.server/src/org/simantics/district/maps/server/TileserverMapnik.java index dbe52572..3ba5157d 100644 --- a/org.simantics.maps.server/src/org/simantics/district/maps/server/TileserverMapnik.java +++ b/org.simantics.maps.server/src/org/simantics/district/maps/server/TileserverMapnik.java @@ -1,13 +1,11 @@ package org.simantics.district.maps.server; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.HashMap; @@ -37,7 +35,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class TileserverMapnik { private static final Logger LOGGER = LoggerFactory.getLogger(TileserverMapnik.class); - private static final String[] ADDITIONAL_DEPENDENCIES = new String[] { "tilelive-vector@3.9.4", "tilelive-tmstyle@0.6.0" }; private SystemProcess process; private Path serverRoot; @@ -173,7 +170,8 @@ public class TileserverMapnik { // } - private Path tileserverMapnikRoot() { + @SuppressWarnings("unused") + private Path tileserverMapnikRoot() { return serverRoot.resolve("tileserver-mapnik").toAbsolutePath(); } @@ -185,7 +183,8 @@ public class TileserverMapnik { return serverRoot.resolve("dist/share/icu").toAbsolutePath(); } - private Path getTessera() { + @SuppressWarnings("unused") + private Path getTessera() { return serverRoot.resolve("tileserver-mapnik/bin/tessera.js").toAbsolutePath(); } @@ -244,6 +243,7 @@ public class TileserverMapnik { return getDataDirectory().resolve(MapsServerPreferences.currentMBTiles()); } + @SuppressWarnings("unchecked") public void checkTm2Styles() { Path tm2 = getStyleDirectory(); try (DirectoryStream stream = Files.newDirectoryStream(tm2)) { diff --git a/pom.xml b/pom.xml index f36b9763..5489a908 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,11 @@ p2 ${simantics-download-site}/${branch-spec}/sdk + + external-components + p2 + ${simantics-download-site}/${branch-spec}/external-components/maven + @@ -105,7 +110,10 @@ org.simantics.district.route.feature org.simantics.district.ui.feature org.simantics.maps.server.feature - + + org.simantics.maps.elevation.server + org.simantics.maps.elevation.server.ui + org.simantics.district.repository