]> gerrit.simantics Code Review - simantics/district.git/commitdiff
Merge remote-tracking branch 'origin/master' into release/1.35.2
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Fri, 6 Sep 2019 08:56:11 +0000 (11:56 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 10 Sep 2019 12:40:26 +0000 (15:40 +0300)
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

196 files changed:
org.simantics.district.feature/build.properties
org.simantics.district.feature/feature.xml
org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV.py [new file with mode: 0644]
org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV2.py [new file with mode: 0644]
org.simantics.district.feature/rootFiles/QGIS scripts/generateCSV3.py [new file with mode: 0644]
org.simantics.district.geotools/.classpath
org.simantics.district.geotools/META-INF/MANIFEST.MF
org.simantics.district.geotools/build.properties
org.simantics.district.geotools/lib/commons-io-2.1.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/gt-api-16.0.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/gt-coverage-16.0.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/gt-geotiff-16.0.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/gt-main-16.0.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/guava-17.0.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/imageio-ext-geocore-1.1.16.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/imageio-ext-streams-1.1.16.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/imageio-ext-tiff-1.1.16.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/imageio-ext-utilities-1.1.16.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jai_codec-1.1.3.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jai_imageio-1.1.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jdom-1.1.3.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-affine-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-algebra-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-bandcombine-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-bandmerge-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-bandselect-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-binarize-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-border-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-buffer-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-classifier-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-colorconvert-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-colorindexer-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-crop-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-errordiffusion-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-format-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-imagefunction-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-iterators-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-lookup-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-mosaic-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-nullop-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-orderdither-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-piecewise-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-rescale-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-rlookup-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-scale-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-stats-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-translate-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-utilities-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-utils-1.4.0.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-vectorbin-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-warp-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-zonal-1.0.11.jar [new file with mode: 0644]
org.simantics.district.geotools/lib/jt-zonalstats-1.4.0.jar [new file with mode: 0644]
org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizard.java
org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizardFirstPage.java
org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportWizardPage.java
org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/ComponentMappingPage.java
org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/controls/DynamicComboFieldEditor.java
org.simantics.district.imports/META-INF/MANIFEST.MF
org.simantics.district.imports/build.properties
org.simantics.district.imports/scl/Simantics/District/Import.scl [new file with mode: 0644]
org.simantics.district.imports/src/org/simantics/district/imports/CSVImportModel.java [moved from org.simantics.district.imports.ui/src/org/simantics/district/imports/ui/CSVImportModel.java with 93% similarity]
org.simantics.district.imports/src/org/simantics/district/imports/DistrictImportUtils.java
org.simantics.district.maps/META-INF/MANIFEST.MF
org.simantics.district.maps/src/org/simantics/maps/MapScalingTransform.java
org.simantics.district.maps/src/org/simantics/maps/sg/MapAttributionNode.java
org.simantics.district.maps/src/org/simantics/maps/sg/MapLocationZoomInfoNode.java
org.simantics.district.maps/src/org/simantics/maps/sg/MapNode.java
org.simantics.district.maps/src/org/simantics/maps/sg/MapScaleNode.java
org.simantics.district.network.ontology/graph/DistrictNetwork.pgraph
org.simantics.district.network.ontology/graph/DistrictNetworkDiagramSettings.pgraph
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/plugin.xml
org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramEditor.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictDiagramViewer.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictPanZoomRotateHandler.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/DistrictTransformUtil.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/NetworkDrawingParticipant.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/OpenDiagramFromConfigurationAdapter.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/adapters/DistrictNetworkEdgeElement.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/adapters/DistrictNetworkVertexElement.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeMappingTypeHandler.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeRoutePointToVertexHandler.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/ChangeVertexToRoutePointHandler.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/contributions/CopyDistrictVertexHandler.java [new file with mode: 0644]
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/contributions/PasteDistrictVertexHandler.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/function/Functions.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/internal/Activator.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DeferredNode.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DeferredRenderingNode.java [new file with mode: 0644]
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/DistrictNetworkHoverInfoNode.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkNodeUtils.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkStaticInfoNode.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkVertexNode.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DynamicVisualisationContributionsNode.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/ElevationServerNode.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/HoverSensitiveNode.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/NetworkDrawingNode.java
org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/DynamicVisualisationContributionsParticipant.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/ElevationServerParticipant.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/ConnectionLineStyle.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/DistrictNetworkHoverInfoStyle.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/DistrictNetworkStaticInfoStyle.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/styles/ElevationRectangleStyle.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ColumnHeaderTableDataProvider.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataAction.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataCommand.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTable.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableUI.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableView.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/EditingSupportConfiguration.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportCSVHandler.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/PasteDataCommandHandler.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/RowHeaderTableDataProvider.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/TableDataProvider.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsUI.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsView.java [new file with mode: 0644]
org.simantics.district.network/META-INF/MANIFEST.MF
org.simantics.district.network/adapters.xml
org.simantics.district.network/scl/Simantics/District.scl
org.simantics.district.network/scl/Simantics/District/Algorithm.scl
org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/ColorContribution.scl [new file with mode: 0644]
org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/ColorMap.scl [new file with mode: 0644]
org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/SizeContribution.scl [new file with mode: 0644]
org.simantics.district.network/scl/Simantics/District/DynamicVisualisations/SizeMap.scl [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/DNEdgeBuilder.java [moved from org.simantics.district.network.ui/src/org/simantics/district/network/ui/DNEdgeBuilder.java with 61% similarity]
org.simantics.district.network/src/org/simantics/district/network/DistrictNetworkUtil.java
org.simantics.district.network/src/org/simantics/district/network/changeset/DistrictChangeListener.java
org.simantics.district.network/src/org/simantics/district/network/profile/ActiveDynamicVisualisationsRequest.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/profile/ArrowLengthStyle.java
org.simantics.district.network/src/org/simantics/district/network/profile/DNElementColorStyle.java
org.simantics.district.network/src/org/simantics/district/network/profile/DNElementSizeStyle.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/profile/DynamicVisualisationsRequest.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/profile/HideStyle.java
org.simantics.district.network/src/org/simantics/district/network/profile/MidBranchEdgeSetRequest.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/profile/RuntimeDynamicVisualisationsRequest.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/profile/ThrottledStyleBase.java
org.simantics.district.network/src/org/simantics/district/network/visualisations/DynamicVisualisationsContributions.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/visualisations/VisualisationColoringObject.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/visualisations/model/ColorBarOptions.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicColorContribution.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicColorMap.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicSizeContribution.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicSizeMap.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/visualisations/model/DynamicVisualisation.java [new file with mode: 0644]
org.simantics.district.network/src/org/simantics/district/network/visualisations/model/SizeBarOptions.java [new file with mode: 0644]
org.simantics.district.region.ui/src/org/simantics/district/region/ui/DiagramRegionsTableUI.java
org.simantics.district.region.ui/src/org/simantics/district/region/ui/handlers/RemoveRegionHandler.java
org.simantics.district.route.ontology/META-INF/MANIFEST.MF
org.simantics.district.route.ontology/graph/DistrictNetworkRoutesViewpoint.pgraph [new file with mode: 0644]
org.simantics.district.route.ontology/src/org/simantics/district/route/ontology/RouteResource.java
org.simantics.district.route.ui/META-INF/MANIFEST.MF
org.simantics.district.route.ui/adapters.xml [new file with mode: 0644]
org.simantics.district.route.ui/build.properties
org.simantics.district.route.ui/fragment.e4xmi
org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteView.java
org.simantics.district.route.ui/src/org/simantics/district/route/ui/actions/SelectRouteAction.java [new file with mode: 0644]
org.simantics.district.route.ui/src/org/simantics/district/route/ui/actions/ValidateRouteAction.java [new file with mode: 0644]
org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/ActivateCreateRoute.java [new file with mode: 0644]
org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/DeactivateCreateRoute.java [new file with mode: 0644]
org.simantics.district.route/src/org/simantics/district/route/RouteService.java
org.simantics.district.route/src/org/simantics/district/route/internal/RoutePersistence.java
org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java
org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/DeleteElementSelector.java
org.simantics.district.selection/.gitignore [new file with mode: 0644]
org.simantics.district.selection/graph.tg [deleted file]
org.simantics.district.ui.feature/feature.xml
org.simantics.maps.elevation.server.ui/.classpath [new file with mode: 0644]
org.simantics.maps.elevation.server.ui/.project [new file with mode: 0644]
org.simantics.maps.elevation.server.ui/META-INF/MANIFEST.MF [new file with mode: 0644]
org.simantics.maps.elevation.server.ui/build.properties [new file with mode: 0644]
org.simantics.maps.elevation.server.ui/plugin.xml [new file with mode: 0644]
org.simantics.maps.elevation.server.ui/pom.xml [new file with mode: 0644]
org.simantics.maps.elevation.server.ui/src/org/simantics/maps/elevation/server/ui/Activator.java [new file with mode: 0644]
org.simantics.maps.elevation.server.ui/src/org/simantics/maps/elevation/server/ui/MapsElevationServerPreferencePage.java [new file with mode: 0644]
org.simantics.maps.elevation.server/.classpath [new file with mode: 0644]
org.simantics.maps.elevation.server/.project [new file with mode: 0644]
org.simantics.maps.elevation.server/META-INF/MANIFEST.MF [new file with mode: 0644]
org.simantics.maps.elevation.server/build.properties [new file with mode: 0644]
org.simantics.maps.elevation.server/pom.xml [new file with mode: 0644]
org.simantics.maps.elevation.server/scl/Simantics/District/Elevation.scl [new file with mode: 0644]
org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/Activator.java [new file with mode: 0644]
org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/SingletonTiffTileInterface.java [new file with mode: 0644]
org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffInterface.java [new file with mode: 0644]
org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffTileInterface.java [new file with mode: 0644]
org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/prefs/MapsElevationServerPreferences.java [new file with mode: 0644]
org.simantics.maps.server.feature/feature.xml
org.simantics.maps.server.ui/src/org/simantics/maps/server/ui/prefs/MapsServerPreferencePage.java
org.simantics.maps.server/src/org/simantics/district/maps/server/TileserverMapnik.java
pom.xml

index 82ab19c62d182db3688b1af6e6f465a74265ac92..191f89077607f97c5d3a485e075b434e74bbaa87 100644 (file)
@@ -1 +1,2 @@
-bin.includes = feature.xml\r
+bin.includes = feature.xml
+root=rootFiles
index f08da0a1749036a23bc61435073de5fe74013d68..c27c5d6d40193fe52a9620e09d2d4e4d488f21c1 100644 (file)
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="org.simantics.maps.elevation.server"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="com.github.benmanes.caffeine"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
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 (file)
index 0000000..e4d5150
--- /dev/null
@@ -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 (file)
index 0000000..bb4bc8b
--- /dev/null
@@ -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 (file)
index 0000000..5b37255
--- /dev/null
@@ -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}
index b7283c772722fda9577c3f0f9de1768045760c9b..29b4b7aaa3f411df5b47b9e8d80f0d3cd3913be8 100644 (file)
@@ -1,5 +1,50 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
+       <classpathentry exported="true" kind="lib" path="lib/commons-io-2.1.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/gt-api-16.0.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/gt-coverage-16.0.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/gt-geotiff-16.0.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/gt-main-16.0.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/guava-17.0.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/imageio-ext-geocore-1.1.16.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/imageio-ext-streams-1.1.16.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/imageio-ext-tiff-1.1.16.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/imageio-ext-utilities-1.1.16.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jai_codec-1.1.3.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jai_imageio-1.1.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jdom-1.1.3.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-affine-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-algebra-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-bandcombine-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-bandmerge-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-bandselect-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-binarize-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-border-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-buffer-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-classifier-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-colorconvert-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-colorindexer-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-crop-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-errordiffusion-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-format-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-imagefunction-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-iterators-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-lookup-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-mosaic-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-nullop-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-orderdither-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-piecewise-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-rescale-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-rlookup-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-scale-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-stats-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-translate-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-utilities-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-utils-1.4.0.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-vectorbin-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-warp-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-zonal-1.0.11.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/jt-zonalstats-1.4.0.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/hsqldb-2.3.4.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/gt-epsg-hsql-16.0.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/commons-pool-1.5.4.jar"/>
index f8eee2ddabf11f7a25d79f7ff21963ef16ebc6be..1cdbfcaa30357ead0ea9d35348981e5e286d357e 100644 (file)
@@ -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,
index efaa0a7ff368b5ad539199b37fb304a1159eb445..8741e47a55d470518e4ccd5448297168028815f9 100644 (file)
@@ -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 (file)
index 0000000..b5c7d69
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 (file)
index 0000000..e38314e
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 (file)
index 0000000..76e2240
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 (file)
index 0000000..5979a2f
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 (file)
index 0000000..fbf2072
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 (file)
index 0000000..661fc74
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 (file)
index 0000000..394e94a
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 (file)
index 0000000..72b8dff
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 (file)
index 0000000..a629aff
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 (file)
index 0000000..dd60adb
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 (file)
index 0000000..056ecd3
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 (file)
index 0000000..571aa19
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 (file)
index 0000000..a287727
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 (file)
index 0000000..6a45086
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 (file)
index 0000000..7aef840
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 (file)
index 0000000..1d38bb0
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 (file)
index 0000000..e88ce44
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 (file)
index 0000000..32b0903
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 (file)
index 0000000..12d8be0
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 (file)
index 0000000..c868ff2
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 (file)
index 0000000..66d6feb
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 (file)
index 0000000..82f0ff8
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 (file)
index 0000000..7d0fb17
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 (file)
index 0000000..b4074b4
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 (file)
index 0000000..073151c
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 (file)
index 0000000..687f37e
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 (file)
index 0000000..a9c748a
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 (file)
index 0000000..136c2d5
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 (file)
index 0000000..5ed3d28
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 (file)
index 0000000..cb13ed3
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 (file)
index 0000000..fd93d10
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 (file)
index 0000000..10402a8
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 (file)
index 0000000..7781b21
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 (file)
index 0000000..ea16e9d
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 (file)
index 0000000..1dd0ecf
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 (file)
index 0000000..4f1bfcb
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 (file)
index 0000000..6ff872a
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 (file)
index 0000000..179021e
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 (file)
index 0000000..2d1b416
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 (file)
index 0000000..dca7dd3
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 (file)
index 0000000..99afd47
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 (file)
index 0000000..afd3194
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 (file)
index 0000000..90ee42a
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 (file)
index 0000000..ba9540c
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 (file)
index 0000000..3c52f32
Binary files /dev/null and b/org.simantics.district.geotools/lib/jt-zonalstats-1.4.0.jar differ
index 8c087210400459bba772a717fc3c2ec2620bfcb1..a4d29fe1ae943e0cc31ba85ef7514c528d862ea9 100644 (file)
@@ -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<CSVRecord> 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<Resource> vertices = graph.syncRequest(new ObjectsWithType(model.getParentDiagram(), Layer0.getInstance(graph).ConsistsOf, DistrictNetworkResource.getInstance(graph).Vertex));
-                                List<ResourceVertex> 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<Double> 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);
-                }
-            }
-        }
-    }
 }
index 9ba7e2da0d277104471268eb0fd41d7cc5f1c120..a0cc023c58633434305c524ec963a547c8bdb476 100644 (file)
@@ -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;
index 552f7e60878e5dfb00905e1281181349f41398c9..268ed310df432885378907646e6976046793c607 100644 (file)
@@ -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<CSVRecord> rows = model.getRows(5);
+            List<CSVRecord> 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);
     }
 
 }
index c74603be90b96949059cb1a659f1cd057f3b1976..7b87b29f9c124063b70b2321725da1569613b3ff 100644 (file)
@@ -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<String, Resource> componentMappings;
-    private Collection<String> distinctMappingIvalues;
     private Collection<String> distinctMappingIndexColumnValues;
 
     public ComponentMappingPage(CSVImportModel model) {
index 5fc8cad30a09cc470c80b6756729d96f5bce7597..f3bb8c72a952a673bd490325889bae8c114db390 100644 (file)
@@ -66,6 +66,7 @@ public class DynamicComboFieldEditor extends FieldEditor {
         *
         * @return <code>true</code> if it is ok, and <code>false</code> otherwise
         */
+       @SuppressWarnings("unused")
        private boolean checkArray(String[][] table) {
                if (table == null) {
                        return false;
index b04e92231529be57325cddb6a37e9eb05891d524..747c33cb2ebf99dafe9b35fa47cce4fe4dccd2a8 100644 (file)
@@ -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,
index 78db44253844415f513f8c5b6b28d66a7d3aba27..4e38199d7eaf39d4213d359d20909eb756433e44 100644 (file)
@@ -1,5 +1,6 @@
-source.. = src/\r
-output.. = bin/\r
-bin.includes = META-INF/,\\r
-               .,\\r
-               lib/commons-csv-1.4.jar\r
+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 (file)
index 0000000..a3d36f4
--- /dev/null
@@ -0,0 +1,47 @@
+import "Files"
+import "Map" as Map
+import "Simantics/DB"
+
+importJava "org.simantics.district.imports.CSVImportModel" where
+    data CSVImportModel
+    
+    @JavaName "<init>"
+    csvImportModel :: () -> CSVImportModel
+    
+    setParentDiagram :: CSVImportModel -> Resource -> <Proc> ()
+    setSource :: CSVImportModel -> Path -> <Proc> ()
+    setDelimiter :: CSVImportModel -> Character -> <Proc> ()
+    
+    // Vertex
+    setXCoordIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setYCoordIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setZCoordIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setAltElevationIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setHeatLoadDsIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setHeatPowerIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setPeakPowerIndex :: CSVImportModel -> Integer -> <Proc> ()
+    
+    // Edge
+    setStartXCoordIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setStartYCoordIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setStartZCoordIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setEndXCoordIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setEndYCoordIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setEndZCoordIndex :: CSVImportModel -> Integer -> <Proc> ()
+    
+    setLengthIndex :: CSVImportModel -> Integer -> <Proc> ()
+    detailedGeometryIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setDiameterIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setOuterDiameterIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setEdgePadding :: CSVImportModel -> Double -> <Proc> ()
+    
+    // Common
+    setSourceCRS :: CSVImportModel -> String -> <Proc> ()
+    setIdIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setAddressIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setComponentMappingIndex :: CSVImportModel -> Integer -> <Proc> ()
+    setComponentMappings :: CSVImportModel -> String -> Resource -> <Proc> ()
+
+importJava "org.simantics.district.imports.DistrictImportUtils" where
+    importVertices :: CSVImportModel -> <Proc> ()
+    importEdges :: CSVImportModel -> <Proc> ()
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 93646ded2022e92c9f213ab59054d3c0a60166e2..9f85225f3302fe51b83f6a65a9563932b22d7e9f 100644 (file)
@@ -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<CSVRecord> getRows(int amount) throws IOException {
+    public List<CSVRecord> 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<Map<String, String>> 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;
+    }
 }
index 6b22746c4f5d3fc082f8cebe693d685e489c7001..7e2a0e1e23c8cbbb4cd258792f7201a02b044dbc 100644 (file)
@@ -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<Map<String, String>> readRows(Path source, char delimiter, boolean firstAsHeader, int amount) throws IOException {
-        return readRows(source, CSVFormat.newFormat(delimiter), firstAsHeader, amount);
-    }
-
     public static List<Map<String, String>> 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<CSVRecord> readRows(Path source, char delim, int rowAmount) throws IOException {
+    public static List<CSVRecord> readRows(Path source, char delim, boolean firstAsHeader, int rowAmount) throws IOException {
         List<CSVRecord> 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<CSVRecord, Boolean> consumer) throws IOException {
         CSVFormat format = CSVFormat.newFormat(delim);
+        if (firstAsHeader) {
+            format = format.withFirstRecordAsHeader();
+        }
         try (CSVParser parser = format.parse(Files.newBufferedReader(source))) {
             Iterator<CSVRecord> 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<CSVRecord, Boolean>) 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<Quadtree>() {
+
+            @Override
+            public Quadtree perform(ReadGraph graph) throws DatabaseException {
+                Collection<Resource> 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<Resource> 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<Double> 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);
+                }
+            }
+        }
+    }
 }
index c7858653d4beb8c168254032804ecdc735beb92c..5a51674830c444fbd19d4997130129f1c4e5d0c4 100644 (file)
@@ -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,
index e29e8e553fc9fadc90c326dcb28f704c3fabf171..7a47777dfec4cfe238e8f51267921494dce93f08 100644 (file)
@@ -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();
index d97d22ebd5eec6dff9388cfefe38b8c02db04160..b2d28c0b7575491bdd0e3d690c19fbf42fc30ccc 100644 (file)
@@ -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
         
index 9e027ecbbf27b535960a3574830b4fa480cb5b41..790ebbe01b7550155a6040042439dde96d30ae64 100644 (file)
@@ -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();
index 7ca092cf74b711939b460ab3b9e2b24edbe4a296..ae418de477f61d55daedf9f9af1bdcef755c7452 100644 (file)
@@ -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;
 
index dc6b393d5a4ea2e8daf334d1095e3f7b46f0cb98..e506e11d4a14ab71e2f4dacb7ed5c8797d4f502f 100644 (file)
@@ -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 };
     
index d9ab6d6e842a8fe53c45ddfb6f977ae4c9c694c0..bb3130eb5307c5d42309c1c4a0b93bfb6395100c 100644 (file)
@@ -39,6 +39,8 @@ DN.Element <T DIA.Element : L0.Type
 DN.Vertex <T 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 <T DN.Element
     >-- 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 <T DN.Element
     >-- 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 <T DN.Element
@@ -112,6 +150,16 @@ DN.EdgeDefaultMapping <R L0.HasProperty : DN.EdgeMappingParameterType
 DN.VertexDefaultMapping <R L0.HasProperty : DN.VertexMappingParameterType
     L0.HasLabel "Default Vertex mapping"
 
+DN.RightClickDefaultMapping <R L0.HasProperty : DN.VertexMappingParameterType
+    L0.HasLabel "Right click Vertex mapping"
+
+DN.LeftClickDefaultMapping <R L0.HasProperty : DN.VertexMappingParameterType
+    L0.HasLabel "Left click Vertex mapping"
+
+DN.Diagram.DefaultPipeTechTypeId <R L0.HasProperty : SEL.GenericParameterType
+    L0.HasLabel "Default Pipe Tech Type ID"
+
+
 // ----------------------------------------------------------------------------
 
 DN.Functions : L0.Library
@@ -164,6 +212,61 @@ DN.Diagram.splitToMultipleEnabled <R L0.HasProperty : SEL.GenericParameterType
     L0.HasRange L0.Boolean
     L0.HasLabel "Split To Multiple Diagrams"
 
+
+
+DN.Diagram.Visualisations <T L0.Entity
+    >-- 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 <T L0.Entity
+DN.Diagram.Visualisations.SizeContribution <T L0.Entity
+
+DN.Diagram.Visualisations.colorContributions <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributions <R L0.HasProperty
+
+DN.Diagram.Visualisations.colorContributionContributorName <R L0.HasProperty
+DN.Diagram.Visualisations.colorContributionLabel <R L0.HasProperty
+DN.Diagram.Visualisations.colorContributionModuleName <R L0.HasProperty
+DN.Diagram.Visualisations.colorContributionModuleAttribute <R L0.HasProperty
+DN.Diagram.Visualisations.colorContributionUnit <R L0.HasProperty
+DN.Diagram.Visualisations.colorContributionVariableGain <R L0.HasProperty
+DN.Diagram.Visualisations.colorContributionVariableBias <R L0.HasProperty
+DN.Diagram.Visualisations.colorContributionDefaultColorMap <R L0.HasProperty
+DN.Diagram.Visualisations.colorContributionDefaultMin <R L0.HasProperty
+DN.Diagram.Visualisations.colorContributionDefaultMax <R L0.HasProperty
+// for graph persistence only
+DN.Diagram.Visualisations.colorContributionUsed <R L0.HasProperty
+DN.Diagram.Visualisations.colorContributionUseDefault <R L0.HasProperty
+
+DN.Diagram.Visualisations.sizeContributionContributorName <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributionLabel <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributionModuleName <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributionModuleAttribute <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributionUnit <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributionVariableGain <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributionVariableBias <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributionDefaultSizeMap <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributionDefaultMin <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributionDefaultMax <R L0.HasProperty
+// for graph persistence only
+DN.Diagram.Visualisations.sizeContributionUsed <R L0.HasProperty
+DN.Diagram.Visualisations.sizeContributionUseDefault <R L0.HasProperty
+
+
 DN.AddLayerToDNDiagramTemplate <T L0.Template
 
 DN.EPSG_4326 : DN.SpatialRefSystem
@@ -226,6 +329,7 @@ DN.Mapping.TerminalPair <T L0.Property
 DN.Mapping.VertexMapping <T DN.Mapping.Base
     // Common
     >-- DN.Mapping.VertexMapping.ElevationAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.ElevationAltAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.VertexMapping.SupplyTemperatureAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.VertexMapping.ReturnTemperatureAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.VertexMapping.SupplyPressureAttribute --> L0.String <R L0.HasProperty
@@ -238,6 +342,7 @@ DN.Mapping.VertexMapping <T DN.Mapping.Base
     >-- DN.Mapping.VertexMapping.dpAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.VertexMapping.dtAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.VertexMapping.HeatPowerAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.PeakPowerAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.VertexMapping.NominalHeadMAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.VertexMapping.NominalHeadBAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.VertexMapping.NominalFlowAttribute --> L0.String <R L0.HasProperty
@@ -253,6 +358,27 @@ DN.Mapping.VertexMapping <T DN.Mapping.Base
     >-- DN.Mapping.VertexMapping.NominalPressureLossAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.VertexMapping.NominalMassFlowAttribute --> L0.String <R L0.HasProperty
 
+    // pumping station
+    >-- DN.Mapping.VertexMapping.HeadPumpMaximumAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.HeadPumpNominalAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.FrequencyConverterControlledAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.InternalValveMeasurementAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.PumpInReturnLineAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.PumpMassFlowNominalAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.PumpMeMaxAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.PumpMeMinAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.PumpSpeedMaxAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.PumpSpeedMinAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.ValveReturnLineAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.ValveMeMaxAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.ValveMeMinAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.ValveMinPositionAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.ValveOutletModeAttribute --> L0.String <R L0.HasProperty
+    >-- DN.Mapping.VertexMapping.ValvePressLossNominalAttribute --> L0.String <R L0.HasProperty
+
+    // valve
+    >-- DN.Mapping.VertexMapping.OpeningTimeAttribute --> L0.String <R L0.HasProperty
+    
 DN.Mapping.EdgeMapping <T DN.Mapping.Base
     >-- DN.Mapping.EdgeMapping.LengthAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.EdgeMapping.DiameterAttribute --> L0.String <R L0.HasProperty
@@ -264,7 +390,6 @@ DN.Mapping.EdgeMapping <T DN.Mapping.Base
     >-- DN.Mapping.EdgeMapping.TGroundAttribute --> L0.String <R L0.HasProperty
     >-- DN.Mapping.EdgeMapping.ElevationAttribute --> L0.String <R L0.HasProperty
 
-
 // Allowed connection types
 DN.SupplyConnectionType <T STR.ConnectionType
 
index 69cb2807cd32e30574c3e0a13910c697bf8054e6..2e259ef7bee17e801e45266cd05cb0fc46f3a0f9 100644 (file)
@@ -106,6 +106,9 @@ DN.Diagram
         L0.HasLabel "Diagram Background Color"
     >-- DN.Diagram.drawMapEnabled ==> "Boolean" <R L0.HasProperty : SEL.GenericParameterType
         L0.HasLabel "Draw Map"
+    >-- DN.Diagram.hasVisualisation --> DN.Diagram.Visualisations <R L0.HasProperty
+        L0.HasLabel "Visualisation"
+    >-- DN.Diagram.hasActiveVisualisation --> DN.Diagram.Visualisations <R L0.HasProperty
     >-- DN.Diagram.profileUpdateInterval ==> "Long" <R L0.HasProperty : SEL.GenericParameterType
         L0.HasLabel "Profile update interval"
     @L0.assert DN.Diagram.elementColoringGradientHue
@@ -127,6 +130,7 @@ DN.Diagram
     @L0.assert DN.Diagram.drawMapEnabled true
     @L0.assert DN.Diagram.profileUpdateInterval
         2000 : L0.Long
+    @L0.assert DN.Diagram.DefaultPipeTechTypeId 0
 
 // ----------------------------------------------------------------------------
 // Built-in enumerated ScaleProperty & ThicknessProperty instances
index a853d85e8058337a52b7d52e2757d4a4193cbc6c..ecb8231180dfafa1ab05f4eaee1782b0c3db0595 100644 (file)
@@ -20,12 +20,20 @@ DN.Groups.EdgeGroup : DIA.TypeGroup
     DIA.TypeGroup.HasType DN.Edge
 
 DN.ElementColoringStyle : DIA.Style
+DN.ElementSizingStyle : DIA.Style
 DN.VertexSizeStyle : DIA.Style
 DN.EdgeThicknessStyle : DIA.Style
 DN.ArrowLengthStyle : DIA.Style
 DN.HideStyle : DIA.Style
 DN.VertexSymbolStyle : DIA.Style
 DN.ConnectionLineStyle : DIA.Style
+DN.ElevationRectangleStyle : DIA.Style
+
+// Style for user component-specified text grid entries
+DN.DistrictNetworkHoverInfoStyle : DIA.Style
+
+// Style for user component-specified static info text for network branches
+DN.DistrictNetworkStaticInfoStyle : DIA.Style
 
 // Function for dynamic selection of symbols for a vertex
 // The input of the function is a DN.Vertex
index a48137cf625bf68cd6ecdf51a6b697d28a4838a1..e27aef7c3a129165f0bb9f1628090666eb03fe8d 100644 (file)
@@ -22,8 +22,81 @@ public class DistrictNetworkResource {
     public final Resource Connection;
     public final Resource ConnectionLineStyle;
     public final Resource Diagram;
+    public final Resource Diagram_DefaultPipeTechTypeId;
+    public final Resource Diagram_DefaultPipeTechTypeId_Inverse;
     public final Resource Diagram_MappedDiagram;
     public final Resource Diagram_MappedFromDiagram;
+    public final Resource Diagram_Visualisations;
+    public final Resource Diagram_Visualisations_ColorBarLocation;
+    public final Resource Diagram_Visualisations_ColorBarLocation_Inverse;
+    public final Resource Diagram_Visualisations_ColorBarSize;
+    public final Resource Diagram_Visualisations_ColorBarSize_Inverse;
+    public final Resource Diagram_Visualisations_ColorContribution;
+    public final Resource Diagram_Visualisations_ShowColorBarTicks;
+    public final Resource Diagram_Visualisations_ShowColorBarTicks_Inverse;
+    public final Resource Diagram_Visualisations_ShowColorBars;
+    public final Resource Diagram_Visualisations_ShowColorBars_Inverse;
+    public final Resource Diagram_Visualisations_ShowSizeBarTicks;
+    public final Resource Diagram_Visualisations_ShowSizeBarTicks_Inverse;
+    public final Resource Diagram_Visualisations_ShowSizeBars;
+    public final Resource Diagram_Visualisations_ShowSizeBars_Inverse;
+    public final Resource Diagram_Visualisations_SizeBarLocation;
+    public final Resource Diagram_Visualisations_SizeBarLocation_Inverse;
+    public final Resource Diagram_Visualisations_SizeBarSize;
+    public final Resource Diagram_Visualisations_SizeBarSize_Inverse;
+    public final Resource Diagram_Visualisations_SizeContribution;
+    public final Resource Diagram_Visualisations_colorContributionContributorName;
+    public final Resource Diagram_Visualisations_colorContributionContributorName_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionDefaultColorMap;
+    public final Resource Diagram_Visualisations_colorContributionDefaultColorMap_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionDefaultMax;
+    public final Resource Diagram_Visualisations_colorContributionDefaultMax_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionDefaultMin;
+    public final Resource Diagram_Visualisations_colorContributionDefaultMin_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionLabel;
+    public final Resource Diagram_Visualisations_colorContributionLabel_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionModuleAttribute;
+    public final Resource Diagram_Visualisations_colorContributionModuleAttribute_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionModuleName;
+    public final Resource Diagram_Visualisations_colorContributionModuleName_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionUnit;
+    public final Resource Diagram_Visualisations_colorContributionUnit_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionUseDefault;
+    public final Resource Diagram_Visualisations_colorContributionUseDefault_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionUsed;
+    public final Resource Diagram_Visualisations_colorContributionUsed_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionVariableBias;
+    public final Resource Diagram_Visualisations_colorContributionVariableBias_Inverse;
+    public final Resource Diagram_Visualisations_colorContributionVariableGain;
+    public final Resource Diagram_Visualisations_colorContributionVariableGain_Inverse;
+    public final Resource Diagram_Visualisations_colorContributions;
+    public final Resource Diagram_Visualisations_colorContributions_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionContributorName;
+    public final Resource Diagram_Visualisations_sizeContributionContributorName_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionDefaultMax;
+    public final Resource Diagram_Visualisations_sizeContributionDefaultMax_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionDefaultMin;
+    public final Resource Diagram_Visualisations_sizeContributionDefaultMin_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionDefaultSizeMap;
+    public final Resource Diagram_Visualisations_sizeContributionDefaultSizeMap_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionLabel;
+    public final Resource Diagram_Visualisations_sizeContributionLabel_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionModuleAttribute;
+    public final Resource Diagram_Visualisations_sizeContributionModuleAttribute_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionModuleName;
+    public final Resource Diagram_Visualisations_sizeContributionModuleName_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionUnit;
+    public final Resource Diagram_Visualisations_sizeContributionUnit_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionUseDefault;
+    public final Resource Diagram_Visualisations_sizeContributionUseDefault_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionUsed;
+    public final Resource Diagram_Visualisations_sizeContributionUsed_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionVariableBias;
+    public final Resource Diagram_Visualisations_sizeContributionVariableBias_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributionVariableGain;
+    public final Resource Diagram_Visualisations_sizeContributionVariableGain_Inverse;
+    public final Resource Diagram_Visualisations_sizeContributions;
+    public final Resource Diagram_Visualisations_sizeContributions_Inverse;
     public final Resource Diagram_arrowLengthBias;
     public final Resource Diagram_arrowLengthBias_Inverse;
     public final Resource Diagram_arrowLengthGain;
@@ -46,6 +119,10 @@ public class DistrictNetworkResource {
     public final Resource Diagram_elementColoringGradientHue_Inverse;
     public final Resource Diagram_elementColoringGradientSaturation;
     public final Resource Diagram_elementColoringGradientSaturation_Inverse;
+    public final Resource Diagram_hasActiveVisualisation;
+    public final Resource Diagram_hasActiveVisualisation_Inverse;
+    public final Resource Diagram_hasVisualisation;
+    public final Resource Diagram_hasVisualisation_Inverse;
     public final Resource Diagram_nodeScaleBias;
     public final Resource Diagram_nodeScaleBias_Inverse;
     public final Resource Diagram_nodeScaleGain;
@@ -58,6 +135,8 @@ public class DistrictNetworkResource {
     public final Resource Diagram_splitToMultipleEnabled_Inverse;
     public final Resource Diagram_trackChangesEnabled;
     public final Resource Diagram_trackChangesEnabled_Inverse;
+    public final Resource DistrictNetworkHoverInfoStyle;
+    public final Resource DistrictNetworkStaticInfoStyle;
     public final Resource DistrictNodeGroup;
     public final Resource DistrictNodeGroup_hasComponentTypeName;
     public final Resource DistrictNodeGroup_hasComponentTypeName_Inverse;
@@ -108,6 +187,8 @@ public class DistrictNetworkResource {
     public final Resource Edge_ThicknessProperty_value_Inverse;
     public final Resource Element;
     public final Resource ElementColoringStyle;
+    public final Resource ElementSizingStyle;
+    public final Resource ElevationRectangleStyle;
     public final Resource Functions;
     public final Resource Functions_arrowLengthPropertyEnumerationValues;
     public final Resource Functions_arrowLengthPropertyModifier;
@@ -155,6 +236,8 @@ public class DistrictNetworkResource {
     public final Resource Images_MapImage;
     public final Resource InLayer;
     public final Resource Layer;
+    public final Resource LeftClickDefaultMapping;
+    public final Resource LeftClickDefaultMapping_Inverse;
     public final Resource MappedComponent;
     public final Resource MappedFromElement;
     public final Resource Mapping;
@@ -198,14 +281,24 @@ public class DistrictNetworkResource {
     public final Resource Mapping_VertexMapping_DeltaPressureAttribute_Inverse;
     public final Resource Mapping_VertexMapping_DeltaTemperatureAttribute;
     public final Resource Mapping_VertexMapping_DeltaTemperatureAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_ElevationAltAttribute;
+    public final Resource Mapping_VertexMapping_ElevationAltAttribute_Inverse;
     public final Resource Mapping_VertexMapping_ElevationAttribute;
     public final Resource Mapping_VertexMapping_ElevationAttribute_Inverse;
     public final Resource Mapping_VertexMapping_FlowAreaAttribute;
     public final Resource Mapping_VertexMapping_FlowAreaAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_FrequencyConverterControlledAttribute;
+    public final Resource Mapping_VertexMapping_FrequencyConverterControlledAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_HeadPumpMaximumAttribute;
+    public final Resource Mapping_VertexMapping_HeadPumpMaximumAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_HeadPumpNominalAttribute;
+    public final Resource Mapping_VertexMapping_HeadPumpNominalAttribute_Inverse;
     public final Resource Mapping_VertexMapping_HeatLoadDsAttribute;
     public final Resource Mapping_VertexMapping_HeatLoadDsAttribute_Inverse;
     public final Resource Mapping_VertexMapping_HeatPowerAttribute;
     public final Resource Mapping_VertexMapping_HeatPowerAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_InternalValveMeasurementAttribute;
+    public final Resource Mapping_VertexMapping_InternalValveMeasurementAttribute_Inverse;
     public final Resource Mapping_VertexMapping_MassFlowAttribute;
     public final Resource Mapping_VertexMapping_MassFlowAttribute_Inverse;
     public final Resource Mapping_VertexMapping_MaximumHeadMAttribute;
@@ -220,6 +313,22 @@ public class DistrictNetworkResource {
     public final Resource Mapping_VertexMapping_NominalMassFlowAttribute_Inverse;
     public final Resource Mapping_VertexMapping_NominalPressureLossAttribute;
     public final Resource Mapping_VertexMapping_NominalPressureLossAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_OpeningTimeAttribute;
+    public final Resource Mapping_VertexMapping_OpeningTimeAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_PeakPowerAttribute;
+    public final Resource Mapping_VertexMapping_PeakPowerAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_PumpInReturnLineAttribute;
+    public final Resource Mapping_VertexMapping_PumpInReturnLineAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_PumpMassFlowNominalAttribute;
+    public final Resource Mapping_VertexMapping_PumpMassFlowNominalAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_PumpMeMaxAttribute;
+    public final Resource Mapping_VertexMapping_PumpMeMaxAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_PumpMeMinAttribute;
+    public final Resource Mapping_VertexMapping_PumpMeMinAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_PumpSpeedMaxAttribute;
+    public final Resource Mapping_VertexMapping_PumpSpeedMaxAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_PumpSpeedMinAttribute;
+    public final Resource Mapping_VertexMapping_PumpSpeedMinAttribute_Inverse;
     public final Resource Mapping_VertexMapping_ReturnPressureAttribute;
     public final Resource Mapping_VertexMapping_ReturnPressureAttribute_Inverse;
     public final Resource Mapping_VertexMapping_ReturnTemperatureAttribute;
@@ -228,8 +337,20 @@ public class DistrictNetworkResource {
     public final Resource Mapping_VertexMapping_SupplyPressureAttribute_Inverse;
     public final Resource Mapping_VertexMapping_SupplyTemperatureAttribute;
     public final Resource Mapping_VertexMapping_SupplyTemperatureAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_ValveMeMaxAttribute;
+    public final Resource Mapping_VertexMapping_ValveMeMaxAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_ValveMeMinAttribute;
+    public final Resource Mapping_VertexMapping_ValveMeMinAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_ValveMinPositionAttribute;
+    public final Resource Mapping_VertexMapping_ValveMinPositionAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_ValveOutletModeAttribute;
+    public final Resource Mapping_VertexMapping_ValveOutletModeAttribute_Inverse;
     public final Resource Mapping_VertexMapping_ValvePositionAttribute;
     public final Resource Mapping_VertexMapping_ValvePositionAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_ValvePressLossNominalAttribute;
+    public final Resource Mapping_VertexMapping_ValvePressLossNominalAttribute_Inverse;
+    public final Resource Mapping_VertexMapping_ValveReturnLineAttribute;
+    public final Resource Mapping_VertexMapping_ValveReturnLineAttribute_Inverse;
     public final Resource Mapping_VertexMapping_VelocityAttribute;
     public final Resource Mapping_VertexMapping_VelocityAttribute_Inverse;
     public final Resource Mapping_VertexMapping_VolFlowAttribute;
@@ -245,6 +366,8 @@ public class DistrictNetworkResource {
     public final Resource ReturnConnectionType;
     public final Resource ReturnInConnectionType;
     public final Resource ReturnOutConnectionType;
+    public final Resource RightClickDefaultMapping;
+    public final Resource RightClickDefaultMapping_Inverse;
     public final Resource SCLMain;
     public final Resource SpatialRefSystem;
     public final Resource SupplyConnectionType;
@@ -259,6 +382,8 @@ public class DistrictNetworkResource {
     public final Resource VertexSymbolStyle;
     public final Resource Vertex_HasAddress;
     public final Resource Vertex_HasAddress_Inverse;
+    public final Resource Vertex_HasAltElevation;
+    public final Resource Vertex_HasAltElevation_Inverse;
     public final Resource Vertex_HasDeltaPressure;
     public final Resource Vertex_HasDeltaPressure_Inverse;
     public final Resource Vertex_HasDeltaTemperature;
@@ -267,10 +392,18 @@ public class DistrictNetworkResource {
     public final Resource Vertex_HasElevation_Inverse;
     public final Resource Vertex_HasFlowArea;
     public final Resource Vertex_HasFlowArea_Inverse;
+    public final Resource Vertex_HasFrequencyConverterControlled;
+    public final Resource Vertex_HasFrequencyConverterControlled_Inverse;
+    public final Resource Vertex_HasHeadPumpMaximum;
+    public final Resource Vertex_HasHeadPumpMaximum_Inverse;
+    public final Resource Vertex_HasHeadPumpNominal;
+    public final Resource Vertex_HasHeadPumpNominal_Inverse;
     public final Resource Vertex_HasHeatLoadDs;
     public final Resource Vertex_HasHeatLoadDs_Inverse;
     public final Resource Vertex_HasHeatPower;
     public final Resource Vertex_HasHeatPower_Inverse;
+    public final Resource Vertex_HasInternalValveMeasurement;
+    public final Resource Vertex_HasInternalValveMeasurement_Inverse;
     public final Resource Vertex_HasMassFlow;
     public final Resource Vertex_HasMassFlow_Inverse;
     public final Resource Vertex_HasMaximumHeadM;
@@ -283,6 +416,22 @@ public class DistrictNetworkResource {
     public final Resource Vertex_HasNominalHeadM_Inverse;
     public final Resource Vertex_HasNominalPressureLoss;
     public final Resource Vertex_HasNominalPressureLoss_Inverse;
+    public final Resource Vertex_HasOpeningTime;
+    public final Resource Vertex_HasOpeningTime_Inverse;
+    public final Resource Vertex_HasPeakPower;
+    public final Resource Vertex_HasPeakPower_Inverse;
+    public final Resource Vertex_HasPumpInReturnLine;
+    public final Resource Vertex_HasPumpInReturnLine_Inverse;
+    public final Resource Vertex_HasPumpMassFlowNominal;
+    public final Resource Vertex_HasPumpMassFlowNominal_Inverse;
+    public final Resource Vertex_HasPumpMeMax;
+    public final Resource Vertex_HasPumpMeMax_Inverse;
+    public final Resource Vertex_HasPumpMeMin;
+    public final Resource Vertex_HasPumpMeMin_Inverse;
+    public final Resource Vertex_HasPumpSpeedMax;
+    public final Resource Vertex_HasPumpSpeedMax_Inverse;
+    public final Resource Vertex_HasPumpSpeedMin;
+    public final Resource Vertex_HasPumpSpeedMin_Inverse;
     public final Resource Vertex_HasReturnPressure;
     public final Resource Vertex_HasReturnPressure_Inverse;
     public final Resource Vertex_HasReturnTemperature;
@@ -291,8 +440,20 @@ public class DistrictNetworkResource {
     public final Resource Vertex_HasSupplyPressure_Inverse;
     public final Resource Vertex_HasSupplyTemperature;
     public final Resource Vertex_HasSupplyTemperature_Inverse;
+    public final Resource Vertex_HasValveMeMax;
+    public final Resource Vertex_HasValveMeMax_Inverse;
+    public final Resource Vertex_HasValveMeMin;
+    public final Resource Vertex_HasValveMeMin_Inverse;
+    public final Resource Vertex_HasValveMinPosition;
+    public final Resource Vertex_HasValveMinPosition_Inverse;
+    public final Resource Vertex_HasValveOutletMode;
+    public final Resource Vertex_HasValveOutletMode_Inverse;
     public final Resource Vertex_HasValvePosition;
     public final Resource Vertex_HasValvePosition_Inverse;
+    public final Resource Vertex_HasValvePressLossNominal;
+    public final Resource Vertex_HasValvePressLossNominal_Inverse;
+    public final Resource Vertex_HasValveReturnLine;
+    public final Resource Vertex_HasValveReturnLine_Inverse;
     public final Resource Vertex_HasVelocity;
     public final Resource Vertex_HasVelocity_Inverse;
     public final Resource Vertex_HasVolFlow;
@@ -321,8 +482,81 @@ public class DistrictNetworkResource {
         public static final String Connection = "http://www.simantics.org/DistrictNetwork-1.0/Connection";
         public static final String ConnectionLineStyle = "http://www.simantics.org/DistrictNetwork-1.0/ConnectionLineStyle";
         public static final String Diagram = "http://www.simantics.org/DistrictNetwork-1.0/Diagram";
+        public static final String Diagram_DefaultPipeTechTypeId = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/DefaultPipeTechTypeId";
+        public static final String Diagram_DefaultPipeTechTypeId_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/DefaultPipeTechTypeId/Inverse";
         public static final String Diagram_MappedDiagram = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/MappedDiagram";
         public static final String Diagram_MappedFromDiagram = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/MappedFromDiagram";
+        public static final String Diagram_Visualisations = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations";
+        public static final String Diagram_Visualisations_ColorBarLocation = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ColorBarLocation";
+        public static final String Diagram_Visualisations_ColorBarLocation_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ColorBarLocation/Inverse";
+        public static final String Diagram_Visualisations_ColorBarSize = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ColorBarSize";
+        public static final String Diagram_Visualisations_ColorBarSize_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ColorBarSize/Inverse";
+        public static final String Diagram_Visualisations_ColorContribution = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ColorContribution";
+        public static final String Diagram_Visualisations_ShowColorBarTicks = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ShowColorBarTicks";
+        public static final String Diagram_Visualisations_ShowColorBarTicks_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ShowColorBarTicks/Inverse";
+        public static final String Diagram_Visualisations_ShowColorBars = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ShowColorBars";
+        public static final String Diagram_Visualisations_ShowColorBars_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ShowColorBars/Inverse";
+        public static final String Diagram_Visualisations_ShowSizeBarTicks = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ShowSizeBarTicks";
+        public static final String Diagram_Visualisations_ShowSizeBarTicks_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ShowSizeBarTicks/Inverse";
+        public static final String Diagram_Visualisations_ShowSizeBars = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ShowSizeBars";
+        public static final String Diagram_Visualisations_ShowSizeBars_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/ShowSizeBars/Inverse";
+        public static final String Diagram_Visualisations_SizeBarLocation = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/SizeBarLocation";
+        public static final String Diagram_Visualisations_SizeBarLocation_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/SizeBarLocation/Inverse";
+        public static final String Diagram_Visualisations_SizeBarSize = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/SizeBarSize";
+        public static final String Diagram_Visualisations_SizeBarSize_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/SizeBarSize/Inverse";
+        public static final String Diagram_Visualisations_SizeContribution = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/SizeContribution";
+        public static final String Diagram_Visualisations_colorContributionContributorName = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionContributorName";
+        public static final String Diagram_Visualisations_colorContributionContributorName_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionContributorName/Inverse";
+        public static final String Diagram_Visualisations_colorContributionDefaultColorMap = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionDefaultColorMap";
+        public static final String Diagram_Visualisations_colorContributionDefaultColorMap_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionDefaultColorMap/Inverse";
+        public static final String Diagram_Visualisations_colorContributionDefaultMax = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionDefaultMax";
+        public static final String Diagram_Visualisations_colorContributionDefaultMax_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionDefaultMax/Inverse";
+        public static final String Diagram_Visualisations_colorContributionDefaultMin = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionDefaultMin";
+        public static final String Diagram_Visualisations_colorContributionDefaultMin_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionDefaultMin/Inverse";
+        public static final String Diagram_Visualisations_colorContributionLabel = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionLabel";
+        public static final String Diagram_Visualisations_colorContributionLabel_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionLabel/Inverse";
+        public static final String Diagram_Visualisations_colorContributionModuleAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionModuleAttribute";
+        public static final String Diagram_Visualisations_colorContributionModuleAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionModuleAttribute/Inverse";
+        public static final String Diagram_Visualisations_colorContributionModuleName = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionModuleName";
+        public static final String Diagram_Visualisations_colorContributionModuleName_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionModuleName/Inverse";
+        public static final String Diagram_Visualisations_colorContributionUnit = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionUnit";
+        public static final String Diagram_Visualisations_colorContributionUnit_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionUnit/Inverse";
+        public static final String Diagram_Visualisations_colorContributionUseDefault = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionUseDefault";
+        public static final String Diagram_Visualisations_colorContributionUseDefault_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionUseDefault/Inverse";
+        public static final String Diagram_Visualisations_colorContributionUsed = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionUsed";
+        public static final String Diagram_Visualisations_colorContributionUsed_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionUsed/Inverse";
+        public static final String Diagram_Visualisations_colorContributionVariableBias = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionVariableBias";
+        public static final String Diagram_Visualisations_colorContributionVariableBias_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionVariableBias/Inverse";
+        public static final String Diagram_Visualisations_colorContributionVariableGain = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionVariableGain";
+        public static final String Diagram_Visualisations_colorContributionVariableGain_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributionVariableGain/Inverse";
+        public static final String Diagram_Visualisations_colorContributions = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributions";
+        public static final String Diagram_Visualisations_colorContributions_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/colorContributions/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionContributorName = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionContributorName";
+        public static final String Diagram_Visualisations_sizeContributionContributorName_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionContributorName/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionDefaultMax = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionDefaultMax";
+        public static final String Diagram_Visualisations_sizeContributionDefaultMax_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionDefaultMax/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionDefaultMin = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionDefaultMin";
+        public static final String Diagram_Visualisations_sizeContributionDefaultMin_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionDefaultMin/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionDefaultSizeMap = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionDefaultSizeMap";
+        public static final String Diagram_Visualisations_sizeContributionDefaultSizeMap_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionDefaultSizeMap/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionLabel = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionLabel";
+        public static final String Diagram_Visualisations_sizeContributionLabel_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionLabel/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionModuleAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionModuleAttribute";
+        public static final String Diagram_Visualisations_sizeContributionModuleAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionModuleAttribute/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionModuleName = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionModuleName";
+        public static final String Diagram_Visualisations_sizeContributionModuleName_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionModuleName/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionUnit = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionUnit";
+        public static final String Diagram_Visualisations_sizeContributionUnit_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionUnit/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionUseDefault = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionUseDefault";
+        public static final String Diagram_Visualisations_sizeContributionUseDefault_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionUseDefault/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionUsed = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionUsed";
+        public static final String Diagram_Visualisations_sizeContributionUsed_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionUsed/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionVariableBias = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionVariableBias";
+        public static final String Diagram_Visualisations_sizeContributionVariableBias_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionVariableBias/Inverse";
+        public static final String Diagram_Visualisations_sizeContributionVariableGain = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionVariableGain";
+        public static final String Diagram_Visualisations_sizeContributionVariableGain_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributionVariableGain/Inverse";
+        public static final String Diagram_Visualisations_sizeContributions = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributions";
+        public static final String Diagram_Visualisations_sizeContributions_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/Visualisations/sizeContributions/Inverse";
         public static final String Diagram_arrowLengthBias = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/arrowLengthBias";
         public static final String Diagram_arrowLengthBias_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/arrowLengthBias/Inverse";
         public static final String Diagram_arrowLengthGain = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/arrowLengthGain";
@@ -345,6 +579,10 @@ public class DistrictNetworkResource {
         public static final String Diagram_elementColoringGradientHue_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/elementColoringGradientHue/Inverse";
         public static final String Diagram_elementColoringGradientSaturation = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/elementColoringGradientSaturation";
         public static final String Diagram_elementColoringGradientSaturation_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/elementColoringGradientSaturation/Inverse";
+        public static final String Diagram_hasActiveVisualisation = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/hasActiveVisualisation";
+        public static final String Diagram_hasActiveVisualisation_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/hasActiveVisualisation/Inverse";
+        public static final String Diagram_hasVisualisation = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/hasVisualisation";
+        public static final String Diagram_hasVisualisation_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/hasVisualisation/Inverse";
         public static final String Diagram_nodeScaleBias = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/nodeScaleBias";
         public static final String Diagram_nodeScaleBias_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/nodeScaleBias/Inverse";
         public static final String Diagram_nodeScaleGain = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/nodeScaleGain";
@@ -357,6 +595,8 @@ public class DistrictNetworkResource {
         public static final String Diagram_splitToMultipleEnabled_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/splitToMultipleEnabled/Inverse";
         public static final String Diagram_trackChangesEnabled = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/trackChangesEnabled";
         public static final String Diagram_trackChangesEnabled_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/trackChangesEnabled/Inverse";
+        public static final String DistrictNetworkHoverInfoStyle = "http://www.simantics.org/DistrictNetwork-1.0/DistrictNetworkHoverInfoStyle";
+        public static final String DistrictNetworkStaticInfoStyle = "http://www.simantics.org/DistrictNetwork-1.0/DistrictNetworkStaticInfoStyle";
         public static final String DistrictNodeGroup = "http://www.simantics.org/DistrictNetwork-1.0/DistrictNodeGroup";
         public static final String DistrictNodeGroup_hasComponentTypeName = "http://www.simantics.org/DistrictNetwork-1.0/DistrictNodeGroup/hasComponentTypeName";
         public static final String DistrictNodeGroup_hasComponentTypeName_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/DistrictNodeGroup/hasComponentTypeName/Inverse";
@@ -407,6 +647,8 @@ public class DistrictNetworkResource {
         public static final String Edge_ThicknessProperty_value_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Edge/ThicknessProperty/value/Inverse";
         public static final String Element = "http://www.simantics.org/DistrictNetwork-1.0/Element";
         public static final String ElementColoringStyle = "http://www.simantics.org/DistrictNetwork-1.0/ElementColoringStyle";
+        public static final String ElementSizingStyle = "http://www.simantics.org/DistrictNetwork-1.0/ElementSizingStyle";
+        public static final String ElevationRectangleStyle = "http://www.simantics.org/DistrictNetwork-1.0/ElevationRectangleStyle";
         public static final String Functions = "http://www.simantics.org/DistrictNetwork-1.0/Functions";
         public static final String Functions_arrowLengthPropertyEnumerationValues = "http://www.simantics.org/DistrictNetwork-1.0/Functions/arrowLengthPropertyEnumerationValues";
         public static final String Functions_arrowLengthPropertyModifier = "http://www.simantics.org/DistrictNetwork-1.0/Functions/arrowLengthPropertyModifier";
@@ -454,6 +696,8 @@ public class DistrictNetworkResource {
         public static final String Images_MapImage = "http://www.simantics.org/DistrictNetwork-1.0/Images/MapImage";
         public static final String InLayer = "http://www.simantics.org/DistrictNetwork-1.0/InLayer";
         public static final String Layer = "http://www.simantics.org/DistrictNetwork-1.0/Layer";
+        public static final String LeftClickDefaultMapping = "http://www.simantics.org/DistrictNetwork-1.0/LeftClickDefaultMapping";
+        public static final String LeftClickDefaultMapping_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/LeftClickDefaultMapping/Inverse";
         public static final String MappedComponent = "http://www.simantics.org/DistrictNetwork-1.0/MappedComponent";
         public static final String MappedFromElement = "http://www.simantics.org/DistrictNetwork-1.0/MappedFromElement";
         public static final String Mapping = "http://www.simantics.org/DistrictNetwork-1.0/Mapping";
@@ -497,14 +741,24 @@ public class DistrictNetworkResource {
         public static final String Mapping_VertexMapping_DeltaPressureAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/DeltaPressureAttribute/Inverse";
         public static final String Mapping_VertexMapping_DeltaTemperatureAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/DeltaTemperatureAttribute";
         public static final String Mapping_VertexMapping_DeltaTemperatureAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/DeltaTemperatureAttribute/Inverse";
+        public static final String Mapping_VertexMapping_ElevationAltAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ElevationAltAttribute";
+        public static final String Mapping_VertexMapping_ElevationAltAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ElevationAltAttribute/Inverse";
         public static final String Mapping_VertexMapping_ElevationAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ElevationAttribute";
         public static final String Mapping_VertexMapping_ElevationAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ElevationAttribute/Inverse";
         public static final String Mapping_VertexMapping_FlowAreaAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/FlowAreaAttribute";
         public static final String Mapping_VertexMapping_FlowAreaAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/FlowAreaAttribute/Inverse";
+        public static final String Mapping_VertexMapping_FrequencyConverterControlledAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/FrequencyConverterControlledAttribute";
+        public static final String Mapping_VertexMapping_FrequencyConverterControlledAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/FrequencyConverterControlledAttribute/Inverse";
+        public static final String Mapping_VertexMapping_HeadPumpMaximumAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/HeadPumpMaximumAttribute";
+        public static final String Mapping_VertexMapping_HeadPumpMaximumAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/HeadPumpMaximumAttribute/Inverse";
+        public static final String Mapping_VertexMapping_HeadPumpNominalAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/HeadPumpNominalAttribute";
+        public static final String Mapping_VertexMapping_HeadPumpNominalAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/HeadPumpNominalAttribute/Inverse";
         public static final String Mapping_VertexMapping_HeatLoadDsAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/HeatLoadDsAttribute";
         public static final String Mapping_VertexMapping_HeatLoadDsAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/HeatLoadDsAttribute/Inverse";
         public static final String Mapping_VertexMapping_HeatPowerAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/HeatPowerAttribute";
         public static final String Mapping_VertexMapping_HeatPowerAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/HeatPowerAttribute/Inverse";
+        public static final String Mapping_VertexMapping_InternalValveMeasurementAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/InternalValveMeasurementAttribute";
+        public static final String Mapping_VertexMapping_InternalValveMeasurementAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/InternalValveMeasurementAttribute/Inverse";
         public static final String Mapping_VertexMapping_MassFlowAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/MassFlowAttribute";
         public static final String Mapping_VertexMapping_MassFlowAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/MassFlowAttribute/Inverse";
         public static final String Mapping_VertexMapping_MaximumHeadMAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/MaximumHeadMAttribute";
@@ -519,6 +773,22 @@ public class DistrictNetworkResource {
         public static final String Mapping_VertexMapping_NominalMassFlowAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/NominalMassFlowAttribute/Inverse";
         public static final String Mapping_VertexMapping_NominalPressureLossAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/NominalPressureLossAttribute";
         public static final String Mapping_VertexMapping_NominalPressureLossAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/NominalPressureLossAttribute/Inverse";
+        public static final String Mapping_VertexMapping_OpeningTimeAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/OpeningTimeAttribute";
+        public static final String Mapping_VertexMapping_OpeningTimeAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/OpeningTimeAttribute/Inverse";
+        public static final String Mapping_VertexMapping_PeakPowerAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PeakPowerAttribute";
+        public static final String Mapping_VertexMapping_PeakPowerAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PeakPowerAttribute/Inverse";
+        public static final String Mapping_VertexMapping_PumpInReturnLineAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpInReturnLineAttribute";
+        public static final String Mapping_VertexMapping_PumpInReturnLineAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpInReturnLineAttribute/Inverse";
+        public static final String Mapping_VertexMapping_PumpMassFlowNominalAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpMassFlowNominalAttribute";
+        public static final String Mapping_VertexMapping_PumpMassFlowNominalAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpMassFlowNominalAttribute/Inverse";
+        public static final String Mapping_VertexMapping_PumpMeMaxAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpMeMaxAttribute";
+        public static final String Mapping_VertexMapping_PumpMeMaxAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpMeMaxAttribute/Inverse";
+        public static final String Mapping_VertexMapping_PumpMeMinAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpMeMinAttribute";
+        public static final String Mapping_VertexMapping_PumpMeMinAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpMeMinAttribute/Inverse";
+        public static final String Mapping_VertexMapping_PumpSpeedMaxAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpSpeedMaxAttribute";
+        public static final String Mapping_VertexMapping_PumpSpeedMaxAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpSpeedMaxAttribute/Inverse";
+        public static final String Mapping_VertexMapping_PumpSpeedMinAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpSpeedMinAttribute";
+        public static final String Mapping_VertexMapping_PumpSpeedMinAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/PumpSpeedMinAttribute/Inverse";
         public static final String Mapping_VertexMapping_ReturnPressureAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ReturnPressureAttribute";
         public static final String Mapping_VertexMapping_ReturnPressureAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ReturnPressureAttribute/Inverse";
         public static final String Mapping_VertexMapping_ReturnTemperatureAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ReturnTemperatureAttribute";
@@ -527,8 +797,20 @@ public class DistrictNetworkResource {
         public static final String Mapping_VertexMapping_SupplyPressureAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/SupplyPressureAttribute/Inverse";
         public static final String Mapping_VertexMapping_SupplyTemperatureAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/SupplyTemperatureAttribute";
         public static final String Mapping_VertexMapping_SupplyTemperatureAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/SupplyTemperatureAttribute/Inverse";
+        public static final String Mapping_VertexMapping_ValveMeMaxAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValveMeMaxAttribute";
+        public static final String Mapping_VertexMapping_ValveMeMaxAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValveMeMaxAttribute/Inverse";
+        public static final String Mapping_VertexMapping_ValveMeMinAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValveMeMinAttribute";
+        public static final String Mapping_VertexMapping_ValveMeMinAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValveMeMinAttribute/Inverse";
+        public static final String Mapping_VertexMapping_ValveMinPositionAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValveMinPositionAttribute";
+        public static final String Mapping_VertexMapping_ValveMinPositionAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValveMinPositionAttribute/Inverse";
+        public static final String Mapping_VertexMapping_ValveOutletModeAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValveOutletModeAttribute";
+        public static final String Mapping_VertexMapping_ValveOutletModeAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValveOutletModeAttribute/Inverse";
         public static final String Mapping_VertexMapping_ValvePositionAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValvePositionAttribute";
         public static final String Mapping_VertexMapping_ValvePositionAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValvePositionAttribute/Inverse";
+        public static final String Mapping_VertexMapping_ValvePressLossNominalAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValvePressLossNominalAttribute";
+        public static final String Mapping_VertexMapping_ValvePressLossNominalAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValvePressLossNominalAttribute/Inverse";
+        public static final String Mapping_VertexMapping_ValveReturnLineAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValveReturnLineAttribute";
+        public static final String Mapping_VertexMapping_ValveReturnLineAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/ValveReturnLineAttribute/Inverse";
         public static final String Mapping_VertexMapping_VelocityAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/VelocityAttribute";
         public static final String Mapping_VertexMapping_VelocityAttribute_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/VelocityAttribute/Inverse";
         public static final String Mapping_VertexMapping_VolFlowAttribute = "http://www.simantics.org/DistrictNetwork-1.0/Mapping/VertexMapping/VolFlowAttribute";
@@ -544,6 +826,8 @@ public class DistrictNetworkResource {
         public static final String ReturnConnectionType = "http://www.simantics.org/DistrictNetwork-1.0/ReturnConnectionType";
         public static final String ReturnInConnectionType = "http://www.simantics.org/DistrictNetwork-1.0/ReturnInConnectionType";
         public static final String ReturnOutConnectionType = "http://www.simantics.org/DistrictNetwork-1.0/ReturnOutConnectionType";
+        public static final String RightClickDefaultMapping = "http://www.simantics.org/DistrictNetwork-1.0/RightClickDefaultMapping";
+        public static final String RightClickDefaultMapping_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/RightClickDefaultMapping/Inverse";
         public static final String SCLMain = "http://www.simantics.org/DistrictNetwork-1.0/SCLMain";
         public static final String SpatialRefSystem = "http://www.simantics.org/DistrictNetwork-1.0/SpatialRefSystem";
         public static final String SupplyConnectionType = "http://www.simantics.org/DistrictNetwork-1.0/SupplyConnectionType";
@@ -558,6 +842,8 @@ public class DistrictNetworkResource {
         public static final String VertexSymbolStyle = "http://www.simantics.org/DistrictNetwork-1.0/VertexSymbolStyle";
         public static final String Vertex_HasAddress = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasAddress";
         public static final String Vertex_HasAddress_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasAddress/Inverse";
+        public static final String Vertex_HasAltElevation = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasAltElevation";
+        public static final String Vertex_HasAltElevation_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasAltElevation/Inverse";
         public static final String Vertex_HasDeltaPressure = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasDeltaPressure";
         public static final String Vertex_HasDeltaPressure_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasDeltaPressure/Inverse";
         public static final String Vertex_HasDeltaTemperature = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasDeltaTemperature";
@@ -566,10 +852,18 @@ public class DistrictNetworkResource {
         public static final String Vertex_HasElevation_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasElevation/Inverse";
         public static final String Vertex_HasFlowArea = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasFlowArea";
         public static final String Vertex_HasFlowArea_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasFlowArea/Inverse";
+        public static final String Vertex_HasFrequencyConverterControlled = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasFrequencyConverterControlled";
+        public static final String Vertex_HasFrequencyConverterControlled_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasFrequencyConverterControlled/Inverse";
+        public static final String Vertex_HasHeadPumpMaximum = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasHeadPumpMaximum";
+        public static final String Vertex_HasHeadPumpMaximum_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasHeadPumpMaximum/Inverse";
+        public static final String Vertex_HasHeadPumpNominal = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasHeadPumpNominal";
+        public static final String Vertex_HasHeadPumpNominal_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasHeadPumpNominal/Inverse";
         public static final String Vertex_HasHeatLoadDs = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasHeatLoadDs";
         public static final String Vertex_HasHeatLoadDs_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasHeatLoadDs/Inverse";
         public static final String Vertex_HasHeatPower = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasHeatPower";
         public static final String Vertex_HasHeatPower_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasHeatPower/Inverse";
+        public static final String Vertex_HasInternalValveMeasurement = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasInternalValveMeasurement";
+        public static final String Vertex_HasInternalValveMeasurement_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasInternalValveMeasurement/Inverse";
         public static final String Vertex_HasMassFlow = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasMassFlow";
         public static final String Vertex_HasMassFlow_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasMassFlow/Inverse";
         public static final String Vertex_HasMaximumHeadM = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasMaximumHeadM";
@@ -582,6 +876,22 @@ public class DistrictNetworkResource {
         public static final String Vertex_HasNominalHeadM_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasNominalHeadM/Inverse";
         public static final String Vertex_HasNominalPressureLoss = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasNominalPressureLoss";
         public static final String Vertex_HasNominalPressureLoss_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasNominalPressureLoss/Inverse";
+        public static final String Vertex_HasOpeningTime = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasOpeningTime";
+        public static final String Vertex_HasOpeningTime_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasOpeningTime/Inverse";
+        public static final String Vertex_HasPeakPower = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPeakPower";
+        public static final String Vertex_HasPeakPower_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPeakPower/Inverse";
+        public static final String Vertex_HasPumpInReturnLine = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpInReturnLine";
+        public static final String Vertex_HasPumpInReturnLine_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpInReturnLine/Inverse";
+        public static final String Vertex_HasPumpMassFlowNominal = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpMassFlowNominal";
+        public static final String Vertex_HasPumpMassFlowNominal_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpMassFlowNominal/Inverse";
+        public static final String Vertex_HasPumpMeMax = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpMeMax";
+        public static final String Vertex_HasPumpMeMax_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpMeMax/Inverse";
+        public static final String Vertex_HasPumpMeMin = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpMeMin";
+        public static final String Vertex_HasPumpMeMin_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpMeMin/Inverse";
+        public static final String Vertex_HasPumpSpeedMax = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpSpeedMax";
+        public static final String Vertex_HasPumpSpeedMax_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpSpeedMax/Inverse";
+        public static final String Vertex_HasPumpSpeedMin = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpSpeedMin";
+        public static final String Vertex_HasPumpSpeedMin_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasPumpSpeedMin/Inverse";
         public static final String Vertex_HasReturnPressure = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasReturnPressure";
         public static final String Vertex_HasReturnPressure_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasReturnPressure/Inverse";
         public static final String Vertex_HasReturnTemperature = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasReturnTemperature";
@@ -590,8 +900,20 @@ public class DistrictNetworkResource {
         public static final String Vertex_HasSupplyPressure_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasSupplyPressure/Inverse";
         public static final String Vertex_HasSupplyTemperature = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasSupplyTemperature";
         public static final String Vertex_HasSupplyTemperature_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasSupplyTemperature/Inverse";
+        public static final String Vertex_HasValveMeMax = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValveMeMax";
+        public static final String Vertex_HasValveMeMax_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValveMeMax/Inverse";
+        public static final String Vertex_HasValveMeMin = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValveMeMin";
+        public static final String Vertex_HasValveMeMin_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValveMeMin/Inverse";
+        public static final String Vertex_HasValveMinPosition = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValveMinPosition";
+        public static final String Vertex_HasValveMinPosition_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValveMinPosition/Inverse";
+        public static final String Vertex_HasValveOutletMode = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValveOutletMode";
+        public static final String Vertex_HasValveOutletMode_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValveOutletMode/Inverse";
         public static final String Vertex_HasValvePosition = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValvePosition";
         public static final String Vertex_HasValvePosition_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValvePosition/Inverse";
+        public static final String Vertex_HasValvePressLossNominal = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValvePressLossNominal";
+        public static final String Vertex_HasValvePressLossNominal_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValvePressLossNominal/Inverse";
+        public static final String Vertex_HasValveReturnLine = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValveReturnLine";
+        public static final String Vertex_HasValveReturnLine_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasValveReturnLine/Inverse";
         public static final String Vertex_HasVelocity = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasVelocity";
         public static final String Vertex_HasVelocity_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasVelocity/Inverse";
         public static final String Vertex_HasVolFlow = "http://www.simantics.org/DistrictNetwork-1.0/Vertex/HasVolFlow";
@@ -630,8 +952,81 @@ public class DistrictNetworkResource {
         Connection = getResourceOrNull(graph, URIs.Connection);
         ConnectionLineStyle = getResourceOrNull(graph, URIs.ConnectionLineStyle);
         Diagram = getResourceOrNull(graph, URIs.Diagram);
+        Diagram_DefaultPipeTechTypeId = getResourceOrNull(graph, URIs.Diagram_DefaultPipeTechTypeId);
+        Diagram_DefaultPipeTechTypeId_Inverse = getResourceOrNull(graph, URIs.Diagram_DefaultPipeTechTypeId_Inverse);
         Diagram_MappedDiagram = getResourceOrNull(graph, URIs.Diagram_MappedDiagram);
         Diagram_MappedFromDiagram = getResourceOrNull(graph, URIs.Diagram_MappedFromDiagram);
+        Diagram_Visualisations = getResourceOrNull(graph, URIs.Diagram_Visualisations);
+        Diagram_Visualisations_ColorBarLocation = getResourceOrNull(graph, URIs.Diagram_Visualisations_ColorBarLocation);
+        Diagram_Visualisations_ColorBarLocation_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_ColorBarLocation_Inverse);
+        Diagram_Visualisations_ColorBarSize = getResourceOrNull(graph, URIs.Diagram_Visualisations_ColorBarSize);
+        Diagram_Visualisations_ColorBarSize_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_ColorBarSize_Inverse);
+        Diagram_Visualisations_ColorContribution = getResourceOrNull(graph, URIs.Diagram_Visualisations_ColorContribution);
+        Diagram_Visualisations_ShowColorBarTicks = getResourceOrNull(graph, URIs.Diagram_Visualisations_ShowColorBarTicks);
+        Diagram_Visualisations_ShowColorBarTicks_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_ShowColorBarTicks_Inverse);
+        Diagram_Visualisations_ShowColorBars = getResourceOrNull(graph, URIs.Diagram_Visualisations_ShowColorBars);
+        Diagram_Visualisations_ShowColorBars_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_ShowColorBars_Inverse);
+        Diagram_Visualisations_ShowSizeBarTicks = getResourceOrNull(graph, URIs.Diagram_Visualisations_ShowSizeBarTicks);
+        Diagram_Visualisations_ShowSizeBarTicks_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_ShowSizeBarTicks_Inverse);
+        Diagram_Visualisations_ShowSizeBars = getResourceOrNull(graph, URIs.Diagram_Visualisations_ShowSizeBars);
+        Diagram_Visualisations_ShowSizeBars_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_ShowSizeBars_Inverse);
+        Diagram_Visualisations_SizeBarLocation = getResourceOrNull(graph, URIs.Diagram_Visualisations_SizeBarLocation);
+        Diagram_Visualisations_SizeBarLocation_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_SizeBarLocation_Inverse);
+        Diagram_Visualisations_SizeBarSize = getResourceOrNull(graph, URIs.Diagram_Visualisations_SizeBarSize);
+        Diagram_Visualisations_SizeBarSize_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_SizeBarSize_Inverse);
+        Diagram_Visualisations_SizeContribution = getResourceOrNull(graph, URIs.Diagram_Visualisations_SizeContribution);
+        Diagram_Visualisations_colorContributionContributorName = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionContributorName);
+        Diagram_Visualisations_colorContributionContributorName_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionContributorName_Inverse);
+        Diagram_Visualisations_colorContributionDefaultColorMap = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionDefaultColorMap);
+        Diagram_Visualisations_colorContributionDefaultColorMap_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionDefaultColorMap_Inverse);
+        Diagram_Visualisations_colorContributionDefaultMax = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionDefaultMax);
+        Diagram_Visualisations_colorContributionDefaultMax_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionDefaultMax_Inverse);
+        Diagram_Visualisations_colorContributionDefaultMin = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionDefaultMin);
+        Diagram_Visualisations_colorContributionDefaultMin_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionDefaultMin_Inverse);
+        Diagram_Visualisations_colorContributionLabel = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionLabel);
+        Diagram_Visualisations_colorContributionLabel_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionLabel_Inverse);
+        Diagram_Visualisations_colorContributionModuleAttribute = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionModuleAttribute);
+        Diagram_Visualisations_colorContributionModuleAttribute_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionModuleAttribute_Inverse);
+        Diagram_Visualisations_colorContributionModuleName = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionModuleName);
+        Diagram_Visualisations_colorContributionModuleName_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionModuleName_Inverse);
+        Diagram_Visualisations_colorContributionUnit = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionUnit);
+        Diagram_Visualisations_colorContributionUnit_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionUnit_Inverse);
+        Diagram_Visualisations_colorContributionUseDefault = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionUseDefault);
+        Diagram_Visualisations_colorContributionUseDefault_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionUseDefault_Inverse);
+        Diagram_Visualisations_colorContributionUsed = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionUsed);
+        Diagram_Visualisations_colorContributionUsed_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionUsed_Inverse);
+        Diagram_Visualisations_colorContributionVariableBias = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionVariableBias);
+        Diagram_Visualisations_colorContributionVariableBias_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionVariableBias_Inverse);
+        Diagram_Visualisations_colorContributionVariableGain = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionVariableGain);
+        Diagram_Visualisations_colorContributionVariableGain_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributionVariableGain_Inverse);
+        Diagram_Visualisations_colorContributions = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributions);
+        Diagram_Visualisations_colorContributions_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_colorContributions_Inverse);
+        Diagram_Visualisations_sizeContributionContributorName = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionContributorName);
+        Diagram_Visualisations_sizeContributionContributorName_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionContributorName_Inverse);
+        Diagram_Visualisations_sizeContributionDefaultMax = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionDefaultMax);
+        Diagram_Visualisations_sizeContributionDefaultMax_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionDefaultMax_Inverse);
+        Diagram_Visualisations_sizeContributionDefaultMin = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionDefaultMin);
+        Diagram_Visualisations_sizeContributionDefaultMin_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionDefaultMin_Inverse);
+        Diagram_Visualisations_sizeContributionDefaultSizeMap = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionDefaultSizeMap);
+        Diagram_Visualisations_sizeContributionDefaultSizeMap_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionDefaultSizeMap_Inverse);
+        Diagram_Visualisations_sizeContributionLabel = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionLabel);
+        Diagram_Visualisations_sizeContributionLabel_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionLabel_Inverse);
+        Diagram_Visualisations_sizeContributionModuleAttribute = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionModuleAttribute);
+        Diagram_Visualisations_sizeContributionModuleAttribute_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionModuleAttribute_Inverse);
+        Diagram_Visualisations_sizeContributionModuleName = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionModuleName);
+        Diagram_Visualisations_sizeContributionModuleName_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionModuleName_Inverse);
+        Diagram_Visualisations_sizeContributionUnit = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionUnit);
+        Diagram_Visualisations_sizeContributionUnit_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionUnit_Inverse);
+        Diagram_Visualisations_sizeContributionUseDefault = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionUseDefault);
+        Diagram_Visualisations_sizeContributionUseDefault_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionUseDefault_Inverse);
+        Diagram_Visualisations_sizeContributionUsed = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionUsed);
+        Diagram_Visualisations_sizeContributionUsed_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionUsed_Inverse);
+        Diagram_Visualisations_sizeContributionVariableBias = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionVariableBias);
+        Diagram_Visualisations_sizeContributionVariableBias_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionVariableBias_Inverse);
+        Diagram_Visualisations_sizeContributionVariableGain = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionVariableGain);
+        Diagram_Visualisations_sizeContributionVariableGain_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributionVariableGain_Inverse);
+        Diagram_Visualisations_sizeContributions = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributions);
+        Diagram_Visualisations_sizeContributions_Inverse = getResourceOrNull(graph, URIs.Diagram_Visualisations_sizeContributions_Inverse);
         Diagram_arrowLengthBias = getResourceOrNull(graph, URIs.Diagram_arrowLengthBias);
         Diagram_arrowLengthBias_Inverse = getResourceOrNull(graph, URIs.Diagram_arrowLengthBias_Inverse);
         Diagram_arrowLengthGain = getResourceOrNull(graph, URIs.Diagram_arrowLengthGain);
@@ -654,6 +1049,10 @@ public class DistrictNetworkResource {
         Diagram_elementColoringGradientHue_Inverse = getResourceOrNull(graph, URIs.Diagram_elementColoringGradientHue_Inverse);
         Diagram_elementColoringGradientSaturation = getResourceOrNull(graph, URIs.Diagram_elementColoringGradientSaturation);
         Diagram_elementColoringGradientSaturation_Inverse = getResourceOrNull(graph, URIs.Diagram_elementColoringGradientSaturation_Inverse);
+        Diagram_hasActiveVisualisation = getResourceOrNull(graph, URIs.Diagram_hasActiveVisualisation);
+        Diagram_hasActiveVisualisation_Inverse = getResourceOrNull(graph, URIs.Diagram_hasActiveVisualisation_Inverse);
+        Diagram_hasVisualisation = getResourceOrNull(graph, URIs.Diagram_hasVisualisation);
+        Diagram_hasVisualisation_Inverse = getResourceOrNull(graph, URIs.Diagram_hasVisualisation_Inverse);
         Diagram_nodeScaleBias = getResourceOrNull(graph, URIs.Diagram_nodeScaleBias);
         Diagram_nodeScaleBias_Inverse = getResourceOrNull(graph, URIs.Diagram_nodeScaleBias_Inverse);
         Diagram_nodeScaleGain = getResourceOrNull(graph, URIs.Diagram_nodeScaleGain);
@@ -666,6 +1065,8 @@ public class DistrictNetworkResource {
         Diagram_splitToMultipleEnabled_Inverse = getResourceOrNull(graph, URIs.Diagram_splitToMultipleEnabled_Inverse);
         Diagram_trackChangesEnabled = getResourceOrNull(graph, URIs.Diagram_trackChangesEnabled);
         Diagram_trackChangesEnabled_Inverse = getResourceOrNull(graph, URIs.Diagram_trackChangesEnabled_Inverse);
+        DistrictNetworkHoverInfoStyle = getResourceOrNull(graph, URIs.DistrictNetworkHoverInfoStyle);
+        DistrictNetworkStaticInfoStyle = getResourceOrNull(graph, URIs.DistrictNetworkStaticInfoStyle);
         DistrictNodeGroup = getResourceOrNull(graph, URIs.DistrictNodeGroup);
         DistrictNodeGroup_hasComponentTypeName = getResourceOrNull(graph, URIs.DistrictNodeGroup_hasComponentTypeName);
         DistrictNodeGroup_hasComponentTypeName_Inverse = getResourceOrNull(graph, URIs.DistrictNodeGroup_hasComponentTypeName_Inverse);
@@ -716,6 +1117,8 @@ public class DistrictNetworkResource {
         Edge_ThicknessProperty_value_Inverse = getResourceOrNull(graph, URIs.Edge_ThicknessProperty_value_Inverse);
         Element = getResourceOrNull(graph, URIs.Element);
         ElementColoringStyle = getResourceOrNull(graph, URIs.ElementColoringStyle);
+        ElementSizingStyle = getResourceOrNull(graph, URIs.ElementSizingStyle);
+        ElevationRectangleStyle = getResourceOrNull(graph, URIs.ElevationRectangleStyle);
         Functions = getResourceOrNull(graph, URIs.Functions);
         Functions_arrowLengthPropertyEnumerationValues = getResourceOrNull(graph, URIs.Functions_arrowLengthPropertyEnumerationValues);
         Functions_arrowLengthPropertyModifier = getResourceOrNull(graph, URIs.Functions_arrowLengthPropertyModifier);
@@ -763,6 +1166,8 @@ public class DistrictNetworkResource {
         Images_MapImage = getResourceOrNull(graph, URIs.Images_MapImage);
         InLayer = getResourceOrNull(graph, URIs.InLayer);
         Layer = getResourceOrNull(graph, URIs.Layer);
+        LeftClickDefaultMapping = getResourceOrNull(graph, URIs.LeftClickDefaultMapping);
+        LeftClickDefaultMapping_Inverse = getResourceOrNull(graph, URIs.LeftClickDefaultMapping_Inverse);
         MappedComponent = getResourceOrNull(graph, URIs.MappedComponent);
         MappedFromElement = getResourceOrNull(graph, URIs.MappedFromElement);
         Mapping = getResourceOrNull(graph, URIs.Mapping);
@@ -806,14 +1211,24 @@ public class DistrictNetworkResource {
         Mapping_VertexMapping_DeltaPressureAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_DeltaPressureAttribute_Inverse);
         Mapping_VertexMapping_DeltaTemperatureAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_DeltaTemperatureAttribute);
         Mapping_VertexMapping_DeltaTemperatureAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_DeltaTemperatureAttribute_Inverse);
+        Mapping_VertexMapping_ElevationAltAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ElevationAltAttribute);
+        Mapping_VertexMapping_ElevationAltAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ElevationAltAttribute_Inverse);
         Mapping_VertexMapping_ElevationAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ElevationAttribute);
         Mapping_VertexMapping_ElevationAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ElevationAttribute_Inverse);
         Mapping_VertexMapping_FlowAreaAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_FlowAreaAttribute);
         Mapping_VertexMapping_FlowAreaAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_FlowAreaAttribute_Inverse);
+        Mapping_VertexMapping_FrequencyConverterControlledAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_FrequencyConverterControlledAttribute);
+        Mapping_VertexMapping_FrequencyConverterControlledAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_FrequencyConverterControlledAttribute_Inverse);
+        Mapping_VertexMapping_HeadPumpMaximumAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_HeadPumpMaximumAttribute);
+        Mapping_VertexMapping_HeadPumpMaximumAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_HeadPumpMaximumAttribute_Inverse);
+        Mapping_VertexMapping_HeadPumpNominalAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_HeadPumpNominalAttribute);
+        Mapping_VertexMapping_HeadPumpNominalAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_HeadPumpNominalAttribute_Inverse);
         Mapping_VertexMapping_HeatLoadDsAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_HeatLoadDsAttribute);
         Mapping_VertexMapping_HeatLoadDsAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_HeatLoadDsAttribute_Inverse);
         Mapping_VertexMapping_HeatPowerAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_HeatPowerAttribute);
         Mapping_VertexMapping_HeatPowerAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_HeatPowerAttribute_Inverse);
+        Mapping_VertexMapping_InternalValveMeasurementAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_InternalValveMeasurementAttribute);
+        Mapping_VertexMapping_InternalValveMeasurementAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_InternalValveMeasurementAttribute_Inverse);
         Mapping_VertexMapping_MassFlowAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_MassFlowAttribute);
         Mapping_VertexMapping_MassFlowAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_MassFlowAttribute_Inverse);
         Mapping_VertexMapping_MaximumHeadMAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_MaximumHeadMAttribute);
@@ -828,6 +1243,22 @@ public class DistrictNetworkResource {
         Mapping_VertexMapping_NominalMassFlowAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_NominalMassFlowAttribute_Inverse);
         Mapping_VertexMapping_NominalPressureLossAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_NominalPressureLossAttribute);
         Mapping_VertexMapping_NominalPressureLossAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_NominalPressureLossAttribute_Inverse);
+        Mapping_VertexMapping_OpeningTimeAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_OpeningTimeAttribute);
+        Mapping_VertexMapping_OpeningTimeAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_OpeningTimeAttribute_Inverse);
+        Mapping_VertexMapping_PeakPowerAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PeakPowerAttribute);
+        Mapping_VertexMapping_PeakPowerAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PeakPowerAttribute_Inverse);
+        Mapping_VertexMapping_PumpInReturnLineAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpInReturnLineAttribute);
+        Mapping_VertexMapping_PumpInReturnLineAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpInReturnLineAttribute_Inverse);
+        Mapping_VertexMapping_PumpMassFlowNominalAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpMassFlowNominalAttribute);
+        Mapping_VertexMapping_PumpMassFlowNominalAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpMassFlowNominalAttribute_Inverse);
+        Mapping_VertexMapping_PumpMeMaxAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpMeMaxAttribute);
+        Mapping_VertexMapping_PumpMeMaxAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpMeMaxAttribute_Inverse);
+        Mapping_VertexMapping_PumpMeMinAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpMeMinAttribute);
+        Mapping_VertexMapping_PumpMeMinAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpMeMinAttribute_Inverse);
+        Mapping_VertexMapping_PumpSpeedMaxAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpSpeedMaxAttribute);
+        Mapping_VertexMapping_PumpSpeedMaxAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpSpeedMaxAttribute_Inverse);
+        Mapping_VertexMapping_PumpSpeedMinAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpSpeedMinAttribute);
+        Mapping_VertexMapping_PumpSpeedMinAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_PumpSpeedMinAttribute_Inverse);
         Mapping_VertexMapping_ReturnPressureAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ReturnPressureAttribute);
         Mapping_VertexMapping_ReturnPressureAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ReturnPressureAttribute_Inverse);
         Mapping_VertexMapping_ReturnTemperatureAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ReturnTemperatureAttribute);
@@ -836,8 +1267,20 @@ public class DistrictNetworkResource {
         Mapping_VertexMapping_SupplyPressureAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_SupplyPressureAttribute_Inverse);
         Mapping_VertexMapping_SupplyTemperatureAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_SupplyTemperatureAttribute);
         Mapping_VertexMapping_SupplyTemperatureAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_SupplyTemperatureAttribute_Inverse);
+        Mapping_VertexMapping_ValveMeMaxAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValveMeMaxAttribute);
+        Mapping_VertexMapping_ValveMeMaxAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValveMeMaxAttribute_Inverse);
+        Mapping_VertexMapping_ValveMeMinAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValveMeMinAttribute);
+        Mapping_VertexMapping_ValveMeMinAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValveMeMinAttribute_Inverse);
+        Mapping_VertexMapping_ValveMinPositionAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValveMinPositionAttribute);
+        Mapping_VertexMapping_ValveMinPositionAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValveMinPositionAttribute_Inverse);
+        Mapping_VertexMapping_ValveOutletModeAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValveOutletModeAttribute);
+        Mapping_VertexMapping_ValveOutletModeAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValveOutletModeAttribute_Inverse);
         Mapping_VertexMapping_ValvePositionAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValvePositionAttribute);
         Mapping_VertexMapping_ValvePositionAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValvePositionAttribute_Inverse);
+        Mapping_VertexMapping_ValvePressLossNominalAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValvePressLossNominalAttribute);
+        Mapping_VertexMapping_ValvePressLossNominalAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValvePressLossNominalAttribute_Inverse);
+        Mapping_VertexMapping_ValveReturnLineAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValveReturnLineAttribute);
+        Mapping_VertexMapping_ValveReturnLineAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_ValveReturnLineAttribute_Inverse);
         Mapping_VertexMapping_VelocityAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_VelocityAttribute);
         Mapping_VertexMapping_VelocityAttribute_Inverse = getResourceOrNull(graph, URIs.Mapping_VertexMapping_VelocityAttribute_Inverse);
         Mapping_VertexMapping_VolFlowAttribute = getResourceOrNull(graph, URIs.Mapping_VertexMapping_VolFlowAttribute);
@@ -853,6 +1296,8 @@ public class DistrictNetworkResource {
         ReturnConnectionType = getResourceOrNull(graph, URIs.ReturnConnectionType);
         ReturnInConnectionType = getResourceOrNull(graph, URIs.ReturnInConnectionType);
         ReturnOutConnectionType = getResourceOrNull(graph, URIs.ReturnOutConnectionType);
+        RightClickDefaultMapping = getResourceOrNull(graph, URIs.RightClickDefaultMapping);
+        RightClickDefaultMapping_Inverse = getResourceOrNull(graph, URIs.RightClickDefaultMapping_Inverse);
         SCLMain = getResourceOrNull(graph, URIs.SCLMain);
         SpatialRefSystem = getResourceOrNull(graph, URIs.SpatialRefSystem);
         SupplyConnectionType = getResourceOrNull(graph, URIs.SupplyConnectionType);
@@ -867,6 +1312,8 @@ public class DistrictNetworkResource {
         VertexSymbolStyle = getResourceOrNull(graph, URIs.VertexSymbolStyle);
         Vertex_HasAddress = getResourceOrNull(graph, URIs.Vertex_HasAddress);
         Vertex_HasAddress_Inverse = getResourceOrNull(graph, URIs.Vertex_HasAddress_Inverse);
+        Vertex_HasAltElevation = getResourceOrNull(graph, URIs.Vertex_HasAltElevation);
+        Vertex_HasAltElevation_Inverse = getResourceOrNull(graph, URIs.Vertex_HasAltElevation_Inverse);
         Vertex_HasDeltaPressure = getResourceOrNull(graph, URIs.Vertex_HasDeltaPressure);
         Vertex_HasDeltaPressure_Inverse = getResourceOrNull(graph, URIs.Vertex_HasDeltaPressure_Inverse);
         Vertex_HasDeltaTemperature = getResourceOrNull(graph, URIs.Vertex_HasDeltaTemperature);
@@ -875,10 +1322,18 @@ public class DistrictNetworkResource {
         Vertex_HasElevation_Inverse = getResourceOrNull(graph, URIs.Vertex_HasElevation_Inverse);
         Vertex_HasFlowArea = getResourceOrNull(graph, URIs.Vertex_HasFlowArea);
         Vertex_HasFlowArea_Inverse = getResourceOrNull(graph, URIs.Vertex_HasFlowArea_Inverse);
+        Vertex_HasFrequencyConverterControlled = getResourceOrNull(graph, URIs.Vertex_HasFrequencyConverterControlled);
+        Vertex_HasFrequencyConverterControlled_Inverse = getResourceOrNull(graph, URIs.Vertex_HasFrequencyConverterControlled_Inverse);
+        Vertex_HasHeadPumpMaximum = getResourceOrNull(graph, URIs.Vertex_HasHeadPumpMaximum);
+        Vertex_HasHeadPumpMaximum_Inverse = getResourceOrNull(graph, URIs.Vertex_HasHeadPumpMaximum_Inverse);
+        Vertex_HasHeadPumpNominal = getResourceOrNull(graph, URIs.Vertex_HasHeadPumpNominal);
+        Vertex_HasHeadPumpNominal_Inverse = getResourceOrNull(graph, URIs.Vertex_HasHeadPumpNominal_Inverse);
         Vertex_HasHeatLoadDs = getResourceOrNull(graph, URIs.Vertex_HasHeatLoadDs);
         Vertex_HasHeatLoadDs_Inverse = getResourceOrNull(graph, URIs.Vertex_HasHeatLoadDs_Inverse);
         Vertex_HasHeatPower = getResourceOrNull(graph, URIs.Vertex_HasHeatPower);
         Vertex_HasHeatPower_Inverse = getResourceOrNull(graph, URIs.Vertex_HasHeatPower_Inverse);
+        Vertex_HasInternalValveMeasurement = getResourceOrNull(graph, URIs.Vertex_HasInternalValveMeasurement);
+        Vertex_HasInternalValveMeasurement_Inverse = getResourceOrNull(graph, URIs.Vertex_HasInternalValveMeasurement_Inverse);
         Vertex_HasMassFlow = getResourceOrNull(graph, URIs.Vertex_HasMassFlow);
         Vertex_HasMassFlow_Inverse = getResourceOrNull(graph, URIs.Vertex_HasMassFlow_Inverse);
         Vertex_HasMaximumHeadM = getResourceOrNull(graph, URIs.Vertex_HasMaximumHeadM);
@@ -891,6 +1346,22 @@ public class DistrictNetworkResource {
         Vertex_HasNominalHeadM_Inverse = getResourceOrNull(graph, URIs.Vertex_HasNominalHeadM_Inverse);
         Vertex_HasNominalPressureLoss = getResourceOrNull(graph, URIs.Vertex_HasNominalPressureLoss);
         Vertex_HasNominalPressureLoss_Inverse = getResourceOrNull(graph, URIs.Vertex_HasNominalPressureLoss_Inverse);
+        Vertex_HasOpeningTime = getResourceOrNull(graph, URIs.Vertex_HasOpeningTime);
+        Vertex_HasOpeningTime_Inverse = getResourceOrNull(graph, URIs.Vertex_HasOpeningTime_Inverse);
+        Vertex_HasPeakPower = getResourceOrNull(graph, URIs.Vertex_HasPeakPower);
+        Vertex_HasPeakPower_Inverse = getResourceOrNull(graph, URIs.Vertex_HasPeakPower_Inverse);
+        Vertex_HasPumpInReturnLine = getResourceOrNull(graph, URIs.Vertex_HasPumpInReturnLine);
+        Vertex_HasPumpInReturnLine_Inverse = getResourceOrNull(graph, URIs.Vertex_HasPumpInReturnLine_Inverse);
+        Vertex_HasPumpMassFlowNominal = getResourceOrNull(graph, URIs.Vertex_HasPumpMassFlowNominal);
+        Vertex_HasPumpMassFlowNominal_Inverse = getResourceOrNull(graph, URIs.Vertex_HasPumpMassFlowNominal_Inverse);
+        Vertex_HasPumpMeMax = getResourceOrNull(graph, URIs.Vertex_HasPumpMeMax);
+        Vertex_HasPumpMeMax_Inverse = getResourceOrNull(graph, URIs.Vertex_HasPumpMeMax_Inverse);
+        Vertex_HasPumpMeMin = getResourceOrNull(graph, URIs.Vertex_HasPumpMeMin);
+        Vertex_HasPumpMeMin_Inverse = getResourceOrNull(graph, URIs.Vertex_HasPumpMeMin_Inverse);
+        Vertex_HasPumpSpeedMax = getResourceOrNull(graph, URIs.Vertex_HasPumpSpeedMax);
+        Vertex_HasPumpSpeedMax_Inverse = getResourceOrNull(graph, URIs.Vertex_HasPumpSpeedMax_Inverse);
+        Vertex_HasPumpSpeedMin = getResourceOrNull(graph, URIs.Vertex_HasPumpSpeedMin);
+        Vertex_HasPumpSpeedMin_Inverse = getResourceOrNull(graph, URIs.Vertex_HasPumpSpeedMin_Inverse);
         Vertex_HasReturnPressure = getResourceOrNull(graph, URIs.Vertex_HasReturnPressure);
         Vertex_HasReturnPressure_Inverse = getResourceOrNull(graph, URIs.Vertex_HasReturnPressure_Inverse);
         Vertex_HasReturnTemperature = getResourceOrNull(graph, URIs.Vertex_HasReturnTemperature);
@@ -899,8 +1370,20 @@ public class DistrictNetworkResource {
         Vertex_HasSupplyPressure_Inverse = getResourceOrNull(graph, URIs.Vertex_HasSupplyPressure_Inverse);
         Vertex_HasSupplyTemperature = getResourceOrNull(graph, URIs.Vertex_HasSupplyTemperature);
         Vertex_HasSupplyTemperature_Inverse = getResourceOrNull(graph, URIs.Vertex_HasSupplyTemperature_Inverse);
+        Vertex_HasValveMeMax = getResourceOrNull(graph, URIs.Vertex_HasValveMeMax);
+        Vertex_HasValveMeMax_Inverse = getResourceOrNull(graph, URIs.Vertex_HasValveMeMax_Inverse);
+        Vertex_HasValveMeMin = getResourceOrNull(graph, URIs.Vertex_HasValveMeMin);
+        Vertex_HasValveMeMin_Inverse = getResourceOrNull(graph, URIs.Vertex_HasValveMeMin_Inverse);
+        Vertex_HasValveMinPosition = getResourceOrNull(graph, URIs.Vertex_HasValveMinPosition);
+        Vertex_HasValveMinPosition_Inverse = getResourceOrNull(graph, URIs.Vertex_HasValveMinPosition_Inverse);
+        Vertex_HasValveOutletMode = getResourceOrNull(graph, URIs.Vertex_HasValveOutletMode);
+        Vertex_HasValveOutletMode_Inverse = getResourceOrNull(graph, URIs.Vertex_HasValveOutletMode_Inverse);
         Vertex_HasValvePosition = getResourceOrNull(graph, URIs.Vertex_HasValvePosition);
         Vertex_HasValvePosition_Inverse = getResourceOrNull(graph, URIs.Vertex_HasValvePosition_Inverse);
+        Vertex_HasValvePressLossNominal = getResourceOrNull(graph, URIs.Vertex_HasValvePressLossNominal);
+        Vertex_HasValvePressLossNominal_Inverse = getResourceOrNull(graph, URIs.Vertex_HasValvePressLossNominal_Inverse);
+        Vertex_HasValveReturnLine = getResourceOrNull(graph, URIs.Vertex_HasValveReturnLine);
+        Vertex_HasValveReturnLine_Inverse = getResourceOrNull(graph, URIs.Vertex_HasValveReturnLine_Inverse);
         Vertex_HasVelocity = getResourceOrNull(graph, URIs.Vertex_HasVelocity);
         Vertex_HasVelocity_Inverse = getResourceOrNull(graph, URIs.Vertex_HasVelocity_Inverse);
         Vertex_HasVolFlow = getResourceOrNull(graph, URIs.Vertex_HasVolFlow);
index d2c3962a99b9fabe8d99efe3a63e5c648e2ccf0e..9399017efb7fe373f6488b058b2098c30fa4bc58 100644 (file)
@@ -8,7 +8,8 @@ Export-Package: org.simantics.district.network.ui,
  org.simantics.district.network.ui.adapters,
  org.simantics.district.network.ui.breakdown,
  org.simantics.district.network.ui.function,
- org.simantics.district.network.ui.nodes
+ org.simantics.district.network.ui.nodes,
+ org.simantics.district.network.ui.participants
 Require-Bundle: org.eclipse.e4.ui.model.workbench;bundle-version="1.1.100.v20150407-1430",
  org.eclipse.swt,
  org.simantics.g2d,
@@ -32,7 +33,13 @@ Require-Bundle: org.eclipse.e4.ui.model.workbench;bundle-version="1.1.100.v20150
  org.eclipse.jface,
  org.simantics.scl.osgi,
  org.simantics.district.route,
- org.simantics.scenegraph.profile
+ org.simantics.scenegraph.profile,
+ org.simantics.district.geotools;bundle-version="1.0.0",
+ org.simantics.maps.elevation.server,
+ org.eclipse.e4.core.services,
+ org.eclipse.osgi.services;bundle-version="3.6.0",
+ org.eclipse.nebula.widgets.nattable.core,
+ org.simantics.district.imports
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: javax.annotation;version="1.0.0";resolution:=optional,
   javax.inject;version="1.0.0"
index 590cb0c693efa1884beb9ea77fe9057f078d4731..f4dea68aa515a0ac4ee597b6c165a8902cbf1851 100644 (file)
     
     <target interface="org.simantics.scenegraph.profile.Style">
         <resource uri="http://www.simantics.org/DistrictNetwork-1.0/ConnectionLineStyle"
-            class="org.simantics.district.network.ui.nodes.ConnectionLineStyle">
+            class="org.simantics.district.network.ui.styles.ConnectionLineStyle">
+        </resource>
+        <resource uri="http://www.simantics.org/DistrictNetwork-1.0/DistrictNetworkHoverInfoStyle"
+            class="org.simantics.district.network.ui.styles.DistrictNetworkHoverInfoStyle">
+            <this />
+        </resource>
+        <resource uri="http://www.simantics.org/DistrictNetwork-1.0/DistrictNetworkStaticInfoStyle"
+            class="org.simantics.district.network.ui.styles.DistrictNetworkStaticInfoStyle">
+            <this />
+        </resource>
+        <resource uri="http://www.simantics.org/DistrictNetwork-1.0/ElevationRectangleStyle"
+            class="org.simantics.district.network.ui.styles.ElevationRectangleStyle">
         </resource>
     </target>
     
index fb9bde4c8db2e9276a0e098753a526ff1e4dd436..89afdc5d9435258dd03c497252f391a7527069c9 100644 (file)
@@ -9,6 +9,14 @@
     <elements xsi:type="commands:Command" xmi:id="_Uv6NEOJdEei2MexsDMErvQ" elementId="org.simantics.district.network.ui.command.changemapbackgroundcolor" commandName="Change Map Background Color"/>
     <elements xsi:type="commands:Command" xmi:id="_eTLe0PhxEeiSu98FclGtqA" elementId="org.simantics.district.network.ui.command.toggletrackchanges" commandName="Toggle Track Changes"/>
     <elements xsi:type="commands:Command" xmi:id="_f4ugUPhxEeiSu98FclGtqA" elementId="org.simantics.district.network.ui.command.splittomultiplediagrams" commandName="Split to multiple diagrams"/>
+    <elements xsi:type="commands:Command" xmi:id="_RJIeMFn_Eem2uLDOwusbsQ" elementId="org.simantics.district.network.ui.command.copydistrictvertex" commandName="Copy District Vertex">
+      <parameters xmi:id="_lH-1cFoDEem2uLDOwusbsQ" elementId="org.simantics.district.network.ui.commandparameter.0" name="cut"/>
+    </elements>
+    <elements xsi:type="commands:Command" xmi:id="_T3or0Fn_Eem2uLDOwusbsQ" elementId="org.simantics.district.network.ui.command.pastedistrictvertex" commandName="Paste District Vertex"/>
+    <elements xsi:type="commands:Command" xmi:id="_CLKOUHVvEemS_rRAHnCQSw" elementId="org.simantics.district.network.ui.command.changeroutepointtovertex" commandName="Change Route Point to Vertex"/>
+    <elements xsi:type="commands:Command" xmi:id="_Ee0TAHVvEemS_rRAHnCQSw" elementId="org.simantics.district.network.ui.command.changevertextoroutepoint" commandName="Change Vertex to Route Point"/>
+    <elements xsi:type="commands:Command" xmi:id="_QcEKQIAGEemKlokjSaREFQ" elementId="org.simantics.district.network.ui.command.importcsv" commandName="Import CSV"/>
+    <elements xsi:type="commands:Command" xmi:id="_VWtY8LqWEemcscVaZzEyWw" elementId="org.simantics.district.network.ui.command.importtechtypecsv" commandName="Import Tech Type CSV"/>
   </fragments>
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_Js7rUMMAEea1mdgpHNVHMA" featurename="menuContributions" parentElementId="xpath:/">
     <elements xsi:type="menu:MenuContribution" xmi:id="_T-jiEN8NEeaigNyzMJBOrg" elementId="org.simantics.district.network.ui.menucontribution.districtDiagramPopup" parentId="#DistrictDiagramPopup">
       <children xsi:type="menu:HandledMenuItem" xmi:id="_ZE-9sOGeEei2MexsDMErvQ" elementId="org.simantics.district.network.ui.handledmenuitem.toggledrawmap" label="Toggle Draw Map" iconURI="platform:/plugin/com.famfamfam.silk/icons/map_add.png" command="_qillYOGQEeiIMuq1qhQJRQ"/>
       <children xsi:type="menu:HandledMenuItem" xmi:id="_bd3S0OJdEei2MexsDMErvQ" elementId="org.simantics.district.network.ui.handledmenuitem.changebackgroundcolor" label="Change Background Color" iconURI="platform:/plugin/com.famfamfam.silk/icons/palette.png" command="_Uv6NEOJdEei2MexsDMErvQ"/>
       <children xsi:type="menu:DynamicMenuContribution" xmi:id="_jK8QQD9EEemjkrTOYZxVwQ" elementId="org.simantics.district.network.ui.dynamicmenucontribution.elementActions" label="Element Actions" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.contributions.NetworkElementActionMenuContribution"/>
+      <children xsi:type="menu:HandledMenuItem" xmi:id="_aqopoFn_Eem2uLDOwusbsQ" elementId="org.simantics.district.network.ui.handledmenuitem.copy" label="Copy" enabled="false" command="_RJIeMFn_Eem2uLDOwusbsQ"/>
+      <children xsi:type="menu:HandledMenuItem" xmi:id="_dGH0oFn_Eem2uLDOwusbsQ" elementId="org.simantics.district.network.ui.handledmenuitem.paste" label="Paste" command="_T3or0Fn_Eem2uLDOwusbsQ"/>
+      <children xsi:type="menu:HandledMenuItem" xmi:id="_ajCQAFoDEem2uLDOwusbsQ" elementId="org.simantics.district.network.ui.handledmenuitem.cut" label="Cut" command="_RJIeMFn_Eem2uLDOwusbsQ">
+        <parameters xmi:id="_qNYB8FoDEem2uLDOwusbsQ" elementId="org.simantics.district.network.ui.parameter.0" name="org.simantics.district.network.ui.commandparameter.0" value="true"/>
+      </children>
+      <children xsi:type="menu:HandledMenuItem" xmi:id="_KOAqcHVvEemS_rRAHnCQSw" elementId="org.simantics.district.network.ui.handledmenuitem.changevertextoroutepoint" label="Change Vertex to Route Point" command="_Ee0TAHVvEemS_rRAHnCQSw"/>
+      <children xsi:type="menu:HandledMenuItem" xmi:id="_NgV94HVvEemS_rRAHnCQSw" elementId="org.simantics.district.network.ui.handledmenuitem.changeroutepointtovertex" label="Change Route Point to Vertex" command="_CLKOUHVvEemS_rRAHnCQSw"/>
     </elements>
   </fragments>
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_dbiHcMMBEea1mdgpHNVHMA" featurename="handlers" parentElementId="xpath:/">
     <elements xsi:type="commands:Handler" xmi:id="_WmQDoOJdEei2MexsDMErvQ" elementId="org.simantics.district.network.ui.handler.2" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.contributions.ChangeMapBackgroundColorHandler" command="_Uv6NEOJdEei2MexsDMErvQ"/>
     <elements xsi:type="commands:Handler" xmi:id="_h3ZzgPhxEeiSu98FclGtqA" elementId="org.simantics.district.network.ui.handler.3" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.contributions.ToggleTrackChanges" command="_eTLe0PhxEeiSu98FclGtqA"/>
     <elements xsi:type="commands:Handler" xmi:id="_kc-uoPhxEeiSu98FclGtqA" elementId="org.simantics.district.network.ui.handler.4" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.contributions.ToggleSplitToMultipleDiagrams" command="_f4ugUPhxEeiSu98FclGtqA"/>
+    <elements xsi:type="commands:Handler" xmi:id="_WEwqsFn_Eem2uLDOwusbsQ" elementId="org.simantics.district.network.ui.handler.5" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.contributions.CopyDistrictVertexHandler" command="_RJIeMFn_Eem2uLDOwusbsQ"/>
+    <elements xsi:type="commands:Handler" xmi:id="_X7QSQFn_Eem2uLDOwusbsQ" elementId="org.simantics.district.network.ui.handler.6" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.contributions.PasteDistrictVertexHandler" command="_T3or0Fn_Eem2uLDOwusbsQ"/>
+    <elements xsi:type="commands:Handler" xmi:id="_F9vvIHVvEemS_rRAHnCQSw" elementId="org.simantics.district.network.ui.handler.7" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.contributions.ChangeRoutePointToVertexHandler" command="_CLKOUHVvEemS_rRAHnCQSw"/>
+    <elements xsi:type="commands:Handler" xmi:id="_H7TA8HVvEemS_rRAHnCQSw" elementId="org.simantics.district.network.ui.handler.8" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.contributions.ChangeVertexToRoutePointHandler" command="_Ee0TAHVvEemS_rRAHnCQSw"/>
+    <elements xsi:type="commands:Handler" xmi:id="_akE8EIAGEemKlokjSaREFQ" elementId="org.simantics.district.network.ui.handler.9" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.table.ImportCSVHandler" command="_QcEKQIAGEemKlokjSaREFQ"/>
+    <elements xsi:type="commands:Handler" xmi:id="_XjbIILqWEemcscVaZzEyWw" elementId="org.simantics.district.network.ui.handler.10" contributionURI="bundleclass://org.simantics.district.network.ui/org.simantics.district.network.ui.table.ImportTechTypeCSVHandler" command="_VWtY8LqWEemcscVaZzEyWw"/>
   </fragments>
 </fragment:ModelFragments>
index f33e345a7186f7f2b833ddc061b2f77fd480d57e..b8a712d8d12f7e06113e75bbcad20c7ce77c99d4 100644 (file)
             name="District Network Breakdown"
             restorable="true">
       </e4view>
+      <e4view
+            class="org.simantics.district.network.ui.table.DistrictCSVTableView"
+            icon="platform:/plugin/com.famfamfam.silk/icons/table_edit.png"
+            id="org.simantics.district.network.ui.table.csvTableView"
+            name="District CSV Import Table"
+            restorable="true"
+            allowMultiple="false">
+      </e4view>
+      <e4view
+            class="org.simantics.district.network.ui.techtype.table.TechTypeTableView"
+            icon="platform:/plugin/com.famfamfam.silk/icons/table_edit.png"
+            id="org.simantics.district.network.ui.techtype.table.techtypeTableView"
+            name="Tech Type Table"
+            restorable="true"
+            allowMultiple="false">
+      </e4view>
+      <e4view
+            class="org.simantics.district.network.ui.visualisations.DynamicVisualisationsView"
+            icon="platform:/plugin/com.famfamfam.silk/icons/table_edit.png"
+            id="org.simantics.district.network.ui.visualisations.dynamicVisualisationsView"
+            name="District Visualisations"
+            restorable="true"
+            allowMultiple="false">
+      </e4view>
    </extension>
   <extension
          point="org.eclipse.ui.preferencePages">
index 9862cde2f143decd67f93349409911b2701af035..7805ea1b016d214c003b5fc8a6407b26b5fea0b3 100644 (file)
@@ -1,5 +1,6 @@
 package org.simantics.district.network.ui;\r
 \r
+import org.eclipse.ui.IWorkbenchPartReference;\r
 import org.eclipse.ui.PartInitException;\r
 import org.simantics.modeling.ui.diagramEditor.DiagramEditor;\r
 import org.simantics.modeling.ui.diagramEditor.DiagramViewer;\r
@@ -7,9 +8,16 @@ import org.simantics.modeling.ui.diagramEditor.DiagramViewer;
 public class DistrictDiagramEditor extends DiagramEditor {\r
 \r
     public static final String ID = "org.simantics.district.network.ui.diagrameditor";\r
-    \r
+\r
     @Override\r
     protected DiagramViewer createViewer() throws PartInitException {\r
         return new DistrictDiagramViewer();\r
     }\r
+\r
+    @Override\r
+    public void partHidden(IWorkbenchPartReference partRef) {\r
+        // we do not want to have disposer for district diagram viewer\r
+        // to add disposer with default settings uncomment the following line\r
+        // super.partHidden(partRef);\r
+    }\r
 }\r
index 6175ace7cb1885d60d3f920ba551ae8fe3d92801..f0305fad2ce9ca663a10feab158abf90d2bb5b87 100644 (file)
@@ -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<String, DynamicColorContribution> 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<String, DynamicSizeContribution> 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<Resource, Boolean> {
 
         public DrawMapEnabledRequest(Resource diagram) {
@@ -178,7 +263,7 @@ public class DistrictDiagramViewer extends DiagramViewer {
 
     private static class MapBackgroundColorListener implements Listener<RGB.Integer> {
 
-        private static final Logger LOGGER = LoggerFactory.getLogger(DrawMapEnabledListener.class);
+        private static final Logger LOGGER = LoggerFactory.getLogger(MapBackgroundColorListener.class);
 
         private Consumer<RGB.Integer> callback;
         private Supplier<Boolean> isDisposed;
@@ -203,4 +288,184 @@ public class DistrictDiagramViewer extends DiagramViewer {
             return isDisposed.get();
         }
     }
+
+    private static class ColorBarOptionsRequest extends UnaryRead<Resource, ColorBarOptions> {
+
+        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<Resource, Map<String,DynamicColorContribution>> {
+
+        public ColoringObjectsRequest(Resource diagram) {
+            super(diagram);
+        }
+
+        @Override
+        public Map<String, DynamicColorContribution> 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<Map<String,DynamicColorContribution>> {
+
+        private static final Logger LOGGER = LoggerFactory.getLogger(ColoringObjectsListener.class);
+
+        private Consumer<Map<String,DynamicColorContribution>> callback;
+        private Supplier<Boolean> isDisposed;
+
+        public ColoringObjectsListener(Consumer<Map<String,DynamicColorContribution>> callback, Supplier<Boolean> isDisposed) {
+            this.callback = callback;
+            this.isDisposed = isDisposed;
+        }
+
+        @Override
+        public void execute(Map<String,DynamicColorContribution> 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<ColorBarOptions> {
+
+        private static final Logger LOGGER = LoggerFactory.getLogger(ColorBarOptionsListener.class);
+
+        private Consumer<ColorBarOptions> callback;
+        private Supplier<Boolean> isDisposed;
+
+        public ColorBarOptionsListener(Consumer<ColorBarOptions> callback, Supplier<Boolean> 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<Resource, SizeBarOptions> {
+
+        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<SizeBarOptions> {
+
+        private static final Logger LOGGER = LoggerFactory.getLogger(SizeBarOptionsListener.class);
+
+        private Consumer<SizeBarOptions> callback;
+        private Supplier<Boolean> isDisposed;
+
+        public SizeBarOptionsListener(Consumer<SizeBarOptions> callback, Supplier<Boolean> 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<Resource, Map<String, DynamicSizeContribution>> {
+
+        public SizingObjectsRequest(Resource diagram) {
+            super(diagram);
+        }
+
+        @Override
+        public Map<String, DynamicSizeContribution> 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<Map<String,DynamicSizeContribution>> {
+
+        private static final Logger LOGGER = LoggerFactory.getLogger(SizingObjectsListener.class);
+
+        private Consumer<Map<String,DynamicSizeContribution>> callback;
+        private Supplier<Boolean> isDisposed;
+
+        public SizingObjectsListener(Consumer<Map<String, DynamicSizeContribution>> callback, Supplier<Boolean> isDisposed) {
+            this.callback = callback;
+            this.isDisposed = isDisposed;
+        }
+
+        @Override
+        public void execute(Map<String, DynamicSizeContribution> result) {
+            callback.accept(result);
+        }
+
+        @Override
+        public void exception(Throwable t) {
+            LOGGER.error("Could not listen ColorBarOptions", t);
+        }
+
+        @Override
+        public boolean isDisposed() {
+            return isDisposed.get();
+        }
+    }
 }
index 05866bdabb3fea2b6bf14f4562267d5309bf11f9..ac0d22e3df34bb79b8ec17f07d44f25fb86d13ae 100644 (file)
@@ -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;
     }
index e71acf0a8ff0e22c2a753ac653b97c3d3e6ca959..4f4583d1a02dc62421d90507022b0f54d26ed652 100644 (file)
@@ -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;
             }
             
index 69d1f724193fcbb797ae1eeb0edc403463758c81..bd81bd01aac6e0683c2ec904c0987143a2578322 100644 (file)
@@ -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<IElement> pickables = new ArrayList<IElement>();
         pick.pick(diagram, req, pickables);
         
         List<IElement> 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<IElement> 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<IElement> 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<IElement> pickables = new ArrayList<IElement>();
+        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() {
index 1c49acbcbc9a505772018badb9c2860f3cd8a4d8..e4e97d59eb6dcadc6ba5dc02254e3e9c9d854a41 100644 (file)
@@ -1,26 +1,26 @@
-package org.simantics.district.network.ui;\r
-\r
-import java.util.Collections;\r
-\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.district.network.ontology.DistrictNetworkResource;\r
-\r
-public class OpenDiagramFromConfigurationAdapter\r
-        extends org.simantics.modeling.ui.diagramEditor.OpenDiagramFromConfigurationAdapter {\r
-\r
-    @Override\r
-    public boolean canHandle(ReadGraph g, Resource r) throws DatabaseException {\r
-        Resource diagram = getDiagram(g, r, Collections.emptySet());\r
-        if (diagram == null)\r
-            return false;\r
-        DistrictNetworkResource DN = DistrictNetworkResource.getInstance(g);\r
-        return g.isInstanceOf(diagram, DN.Diagram) && !isLocked(g, diagram);\r
-    }\r
-\r
-    @Override\r
-    protected String getEditorId() {\r
-        return DistrictDiagramEditor.ID;\r
-    }\r
-}\r
+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;
+    }
+}
index 34424739eb9a0764dbab3d7338a183b86994ff29..ef2e21fed709518ca5c7c21075018526f0474dc6 100644 (file)
@@ -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) {
index a989c56117644025e9b883620f1af29acf57ff5e..1ff5eefa407c7c75a3e978b9531232a23c067bdd 100644 (file)
@@ -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:
index 1047dc13e8b6f953192e487684c90fc9ff02f0c4..3f32f445265a84325082b195f417b40ab2278fc6 100644 (file)
@@ -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<Resource, Collection<NamedResource>> entry : results.entrySet()) {
-                                        Resource newMapping = entry.getKey();
-                                        Collection<NamedResource> elements = entry.getValue();
-                                        for (NamedResource element : elements) {
-                                            graph.deny(element.getResource(), DN.HasMapping);
-                                            graph.claim(element.getResource(), DN.HasMapping, newMapping);
-                                        }
+                                        List<Resource> 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<NamedResource, Map<String, Resource>> possibleMappings = new HashMap<>();
         
-        private Resource defaultVertexMapping;
-
         protected SelectMappingDialog(Shell parentShell, CompletableFuture<Map<NamedResource, Collection<NamedResource>>> 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 (file)
index 0000000..f9e37c0
--- /dev/null
@@ -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<Resource> elements;
+    static boolean cut = true;
+
+    @CanExecute
+    public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) {
+        List<Resource> elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class);
+        if (elements.size() != 1)
+            return false;
+        try {
+            return Simantics.getSession().syncRequest(new Read<Boolean>() {
+
+                @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<Resource> 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<Resource> leftEdge = DNEdgeBuilder.create(graph, diagram, currentStartVertexCoords, Double.MAX_VALUE, midVertexCoords, Double.MAX_VALUE, detailedLeftEdgeGeometryCoords, 0.001);
+                                                    Optional<Resource> 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 (file)
index 0000000..cd71341
--- /dev/null
@@ -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<Resource> elements;
+    static boolean cut = true;
+
+    @CanExecute
+    public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) {
+        List<Resource> elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class);
+        if (elements.size() != 1)
+            return false;
+        try {
+            return Simantics.getSession().syncRequest(new Read<Boolean>() {
+
+                @Override
+                public Boolean perform(ReadGraph graph) throws DatabaseException {
+                    DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+                    for (Resource selection : elements) {
+                        if (graph.isInstanceOf(selection, DN.Vertex)) {
+                            Collection<Resource> startingEdge = graph.getObjects(selection, DN.HasStartVertex_Inverse);
+                            Collection<Resource> 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<Resource> 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 (file)
index 0000000..9365a8c
--- /dev/null
@@ -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<Resource> elements;
+    static boolean cut = true;
+
+    @CanExecute
+    public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) {
+        List<Resource> elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class);
+        if (elements.size() != 1)
+            return false;
+        try {
+            return Simantics.getSession().syncRequest(new Read<Boolean>() {
+
+                @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<Resource> 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;
+    }
+}
index ec03ab87ffe5020c22d6106640ec53a204137025..e29d11a22e2f0ec919d9c5e397b6b78c606344c8 100644 (file)
@@ -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<MMenuElement> items) {
+    public void aboutToShow(@Named(IServiceConstants.ACTIVE_SELECTION) Object selection, Display display, List<MMenuElement> items) {
         final List<Resource> vertices = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class);
         if (vertices.size() != 1)
             return;
@@ -67,7 +70,7 @@ public class NetworkElementActionMenuContribution {
         List<MMenuElement> 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 (file)
index 0000000..7fadac3
--- /dev/null
@@ -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<Resource> elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class);
+        if (elements.size() < 1)
+            return false;
+        try {
+            return Simantics.getSession().syncRequest(new Read<Boolean>() {
+
+                @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<Statement> statements = graph.getStatements(source, L0.HasProperty);
+               Collection<Statement> 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<Resource> sourceStartVertex_inverse = graph.getObjects(sourceElement, DN.HasStartVertex_Inverse);
+               Collection<Resource> sourceEndVertex_inverse = graph.getObjects(sourceElement, DN.HasEndVertex_Inverse);
+               Collection<Resource> targetStartVertex_inverse = graph.getObjects(targetElement, DN.HasStartVertex_Inverse);
+               Collection<Resource> 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<Resource> elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class);
+        final List<Resource> 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<Resource, Object> sourceCopyAttributes = new HashMap<>();
+            Map<Resource, Object> 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<Resource, Object> attrValue : targetCopyAttributes.entrySet()) {
+                                                                       graph.claimLiteral(newSource, attrValue.getKey(), attrValue.getValue());
+                                                               }
+                                                               copyVertexInverses(graph, sourceElement, newSource);
+                                                               deleteExistingTarget(graph, sourceElement);
+                                                       }
+                                                       for (Map.Entry<Resource, Object> 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<Resource, Object> 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<Statement> statements = graph.getStatements(mapping, L0.HasProperty);
+        Collection<Statement> 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<Resource, Object> 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;
+    }
+}
index 60dd3e22131ee17b4ba9890052d5c5d0fca6fe2e..cc05a173b739e2b6d33fe5b823e3f1fa513c236b 100644 (file)
@@ -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<Resource>() {
-                    @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<Resource>() {
+
+            @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<Resource>() {
+
+            @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<Resource> getDistrictDiagrams(ReadGraph graph) throws DatabaseException {
index 9c56d543578213feaaf8b0e4160a804a295045db..72d7cf80a350fb5ae606c909745e598a2b029cab 100644 (file)
@@ -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<Resource> dnElements = Simantics.getSession().syncRequest(new UniqueRead<List<Resource>>() {
+                                               
+                                               @Override
+                                               public List<Resource> 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<SubgraphProvider, SubgraphProvider> subgraphProviderTracker;
     private ServiceTracker<RouteService, RouteService> 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 (file)
index 0000000..c2afab7
--- /dev/null
@@ -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 (file)
index 0000000..f243071
--- /dev/null
@@ -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<Pair<AffineTransform, DeferredNode>> queue = new LinkedList<>();
+       
+       @Override
+       public void render(Graphics2D g2d) {
+               AffineTransform currentTransform = g2d.getTransform();
+               
+               ListIterator<Pair<AffineTransform, DeferredNode>> listIterator = queue.listIterator();
+               while (listIterator.hasNext()) {
+                       Pair<AffineTransform, DeferredNode> 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));
+       }
+}
index 21f3d741e77d81755376faa7047b3762266d0a92..a089ecaf149e61653a6849c6bf536a523c94aab3 100644 (file)
@@ -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 (file)
index 0000000..7a75ad5
--- /dev/null
@@ -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<Tuple3> labels;
+
+       @SuppressWarnings("unused")
+       private Point2D origin;
+
+       private boolean hover = false;
+
+       private Point2D mousePosition;
+       
+       private static AtomicReference<DistrictNetworkHoverInfoNode> 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<Integer> 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<Tuple3> 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);
+       }
+}
index c48693ac4470b94122bf6f47c13a87a7031a9b01..0d300d058bb1df991ae6cbca6726d9d4332fcfeb 100644 (file)
@@ -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 (file)
index 0000000..b86d0ac
--- /dev/null
@@ -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;
+       }
+}
index 221ed714ac562cfcf0aa38f2915f65c760db66ef..2e2b4293871114144b53fb50e73d99065ac7c10e 100644 (file)
@@ -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 (file)
index 0000000..53d7862
--- /dev/null
@@ -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<String, DynamicColorContribution> dynamicColoringContributions;
+    private Map<String, DynamicSizeContribution> 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<String, DynamicColorContribution> 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<RGBIntensity> 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<String, DynamicSizeContribution> 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<Double> 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<String,DynamicColorContribution> dynamicColoringObjects) {
+        this.dynamicColoringContributions = dynamicColoringObjects;
+    }
+
+    public void setColorBarOptions(ColorBarOptions colorBarsOptions) {
+        this.colorBarsOptions = colorBarsOptions;
+    }
+
+    public void setDynamicSizingObjects(Map<String, DynamicSizeContribution> 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 (file)
index 0000000..ea9f986
--- /dev/null
@@ -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<Rectangle2D> 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<Rectangle2D> rectangles) {
+        this.rectangles = rectangles;
+    }
+
+    public Collection<Rectangle2D> 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 (file)
index 0000000..cd6631b
--- /dev/null
@@ -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) {};
+}
index 674702b8476276103d90301e36dd7e80e24143c6..2cd87d69a2b0a65980e85f4972b94a76e8e2691a 100644 (file)
@@ -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<Point2D> routeNodes = new ArrayList<>();
+    }
+
     private static final long serialVersionUID = -3475301184009620573L;
     
     private Point2D currentMousePos = null;
-    
-    private List<Point2D> nodes = new ArrayList<>();
 
-    private Resource diagramResource;
+    private List<DrawingNode> 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<Point2D> 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<DrawingNode> dnodeIterator = nodes.iterator();
+        while (dnodeIterator.hasNext()) {
+            Path2D path = new Path2D.Double();
+            DrawingNode dnode = dnodeIterator.next();
+            Iterator<Point2D> 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<Point2D> 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<DrawingNode> 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 (file)
index 0000000..6908606
--- /dev/null
@@ -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<String,DynamicColorContribution> getDynamicColoringObjects() {
+        Map<String,DynamicColorContribution> 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<String, DynamicSizeContribution> getDynamicSizingObjects() {
+        Map<String, DynamicSizeContribution> 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 (file)
index 0000000..d5f02bb
--- /dev/null
@@ -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 (file)
index 0000000..b74cbba
--- /dev/null
@@ -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<List<Point2D>> {
+       
+       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<Point2D> 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<Point2D> 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<Resource, List<Resource>> fun = getConnectedComponentsFunctionCached(graph, componentType);
+               if (fun == null)
+                       return Collections.emptyList();
+
+               List<Resource> components = Simantics.applySCLRead(graph, fun, component);
+               
+               if (components == null || components.isEmpty())
+                       return Collections.emptyList();
+               
+               List<Point2D> 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<Point2D> 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<Resource, List<Resource>> getConnectedComponentsFunctionCached(ReadGraph graph, Resource componentType)
+                       throws DatabaseException {
+               return graph.syncRequest(new ConnectedComponentsFunctionRequest(componentType), TransientCacheListener.instance());
+       }
+       
+       private static final class ConnectedComponentsFunctionRequest
+                       extends ResourceRead<Function1<Resource, List<Resource>>> {
+               public ConnectedComponentsFunctionRequest(Resource resource) {
+                       super(resource);
+               }
+
+               @SuppressWarnings("unchecked")
+               @Override
+               public Function1<Resource, List<Resource>> 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<Resource, List<Resource>>) 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 (file)
index 0000000..77be9c9
--- /dev/null
@@ -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<DistrictNetworkHoverInfoStyle.StyleResult> {
+
+    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<Tuple3> labels;
+
+               public StyleResult(Point2D origin, List<Tuple3> labels) {
+                       this.origin = origin;
+                       this.labels = labels;
+               }
+
+               public Point2D getOrigin() {
+                       return origin;
+               }
+
+               public List<Tuple3> 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<Variable, List<Tuple3>> function = getUCTextGridFunctionCached(graph, moduleType);
+               if (function == null)
+                       return null;
+               
+               List<Tuple3> 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("<pending>", "", ""));
+               } 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<Variable, List<Tuple3>> getUCTextGridFunctionCached(ReadGraph graph, Resource componentType)
+                       throws DatabaseException {
+               return graph.syncRequest(new UCTextGridFunctionRequest(componentType), TransientCacheListener.instance());
+       }
+       
+       private static final class UCTextGridFunctionRequest extends ResourceRead<Function1<Variable, List<Tuple3>>> {
+               public UCTextGridFunctionRequest(Resource resource) {
+                       super(resource);
+               }
+
+               @SuppressWarnings("unchecked")
+               @Override
+               public Function1<Variable, List<Tuple3>> 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<Variable, List<Tuple3>>) 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 (file)
index 0000000..27c2fc8
--- /dev/null
@@ -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<DistrictNetworkStaticInfoStyle.StyleResult> {
+
+    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<Resource> 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<Variable, String> 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<Variable, String> getUCPipelineInfoFunctionCached(ReadGraph graph, Resource componentType)
+                       throws DatabaseException {
+               return graph.syncRequest(new UCPipelineInfoRequest(componentType), TransientCacheListener.instance());
+       }
+       
+       private static final class UCPipelineInfoRequest extends ResourceRead<Function1<Variable, String>> {
+               public UCPipelineInfoRequest(Resource resource) {
+                       super(resource);
+               }
+
+               @SuppressWarnings("unchecked")
+               @Override
+               public Function1<Variable, String> 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<Variable, String>) 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 (file)
index 0000000..59506e7
--- /dev/null
@@ -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<Rectangle2D> 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 (file)
index 0000000..530870b
--- /dev/null
@@ -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 (file)
index 0000000..dc8c684
--- /dev/null
@@ -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<Range> 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<rows.length;++i)
+            cells[i] = rows[i].split(separator, -1);
+        
+        String[][] fullData = cells; //transpose(cells);
+        
+        return fullData;
+    }
+    
+    private static String[][] transpose(String[][] cells) {
+        int rowCount = 0;
+        for(String[] cols : cells)
+            rowCount = Math.max(rowCount, cols.length);
+        String[][] result = new String[rowCount][cells.length];
+        for(int i=0;i<cells.length;++i) {
+            String[] cols = cells[i];
+            int j;
+            for(j=0;j<cols.length;++j)
+                result[j][i] = cols[j];
+            for(;j<rowCount;++j)
+                result[j][i] = "";
+        }
+        return result;
+
+    }
+}
\ No newline at end of file
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataCommand.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataCommand.java
new file mode 100644 (file)
index 0000000..2ddc0a4
--- /dev/null
@@ -0,0 +1,28 @@
+package org.simantics.district.network.ui.table;
+
+import org.eclipse.nebula.widgets.nattable.command.AbstractMultiColumnCommand;
+import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;
+
+public class CustomPasteDataCommand extends AbstractMultiColumnCommand {
+
+    int pasteRow;
+    String[][] data;
+
+    protected CustomPasteDataCommand(ILayer layer, int[] columnPositions, int pasteRow, String[][] data) {
+        super(layer, columnPositions);
+        this.pasteRow = pasteRow;
+        this.data = data;
+    }
+
+    protected CustomPasteDataCommand(CustomPasteDataCommand pasteDataCommand) {
+        super(pasteDataCommand);
+        this.pasteRow = pasteDataCommand.pasteRow;
+        this.data = pasteDataCommand.data;
+    }
+    @Override
+    public ILayerCommand cloneCommand() {
+        return new CustomPasteDataCommand(this);
+    }
+    
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTable.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTable.java
new file mode 100644 (file)
index 0000000..2f811c7
--- /dev/null
@@ -0,0 +1,160 @@
+package org.simantics.district.network.ui.table;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.nebula.widgets.nattable.NatTable;
+import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.copy.command.CopyDataCommandHandler;
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+import org.eclipse.nebula.widgets.nattable.freeze.CompositeFreezeLayer;
+import org.eclipse.nebula.widgets.nattable.freeze.FreezeLayer;
+import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultColumnHeaderDataLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.group.ColumnGroupHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel;
+import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
+import org.eclipse.nebula.widgets.nattable.hover.HoverLayer;
+import org.eclipse.nebula.widgets.nattable.hover.config.BodyHoverStylingBindings;
+import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;
+import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
+import org.eclipse.nebula.widgets.nattable.reorder.RowReorderLayer;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
+import org.eclipse.nebula.widgets.nattable.ui.matcher.KeyEventMatcher;
+import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.widgets.Composite;
+
+public class DistrictCSVTable extends Composite {
+
+       NatTable table;
+       private TableDataProvider bodyDataProvider;
+       DataLayer bodyDataLayer;
+       private IConfigRegistry summaryConfigRegistry;
+       private IUniqueIndexLayer summaryRowLayer;
+       private ViewportLayer viewportLayer;
+       private CompositeFreezeLayer compositeFreezeLayer;
+       private FreezeLayer freezeLayer;
+       //private TableDataSortModel sortModel;
+       private ColumnHideShowLayer columnHideShowLayer;
+       private ColumnGroupModel columnGroupModel = new ColumnGroupModel();
+       private ColumnHeaderTableDataProvider columnHeaderDataProvider;
+       Clipboard cpb;
+       public SelectionLayer selectionLayer;
+
+       public DistrictCSVTable(Composite parent, int style) {
+               super(parent, style);
+               defaultInitializeUI();
+       }
+
+       private void defaultInitializeUI() {
+               GridDataFactory.fillDefaults().grab(true, true).applyTo(this);
+               GridLayoutFactory.fillDefaults().numColumns(1).applyTo(this);
+               createTable(this);
+       }
+
+       private void createTable(Composite parent) {
+
+               // build the body layer stack 
+               // Usually you would create a new layer stack by extending AbstractIndexLayerTransform and
+               // setting the ViewportLayer as underlying layer. But in this case using the ViewportLayer
+               // directly as body layer is also working.
+               bodyDataProvider = new TableDataProvider();
+               bodyDataLayer = new DataLayer(bodyDataProvider);
+
+               RowReorderLayer rowReorderLayer =
+                               new RowReorderLayer(columnHideShowLayer = new ColumnHideShowLayer(bodyDataLayer));
+
+               HoverLayer hoverLayer = new HoverLayer(rowReorderLayer, false);
+               // we need to ensure that the hover styling is removed when the mouse
+               // cursor moves out of the cell area
+               hoverLayer.addConfiguration(new BodyHoverStylingBindings(hoverLayer));
+
+               selectionLayer = new SelectionLayer(hoverLayer);
+               viewportLayer = new ViewportLayer(selectionLayer);
+               viewportLayer.setRegionName(GridRegion.BODY);
+               freezeLayer = new FreezeLayer(selectionLayer);
+               compositeFreezeLayer = new CompositeFreezeLayer(freezeLayer, viewportLayer, selectionLayer);
+
+               // build the column header layer
+               columnHeaderDataProvider = new ColumnHeaderTableDataProvider();
+               DataLayer columnHeaderDataLayer = new DefaultColumnHeaderDataLayer(columnHeaderDataProvider);
+               columnHeaderDataLayer.setRowsResizableByDefault(false);
+               columnHeaderDataLayer.setColumnsResizableByDefault(true);
+               ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(columnHeaderDataLayer, compositeFreezeLayer, selectionLayer);
+               ColumnGroupHeaderLayer columnGroupHeaderLayer = new ColumnGroupHeaderLayer(columnHeaderLayer, selectionLayer, columnGroupModel);
+               columnGroupHeaderLayer.setCalculateHeight(true);
+
+               // build the row header layer
+               IDataProvider rowHeaderDataProvider = new RowHeaderTableDataProvider(bodyDataProvider);
+               DataLayer rowHeaderDataLayer = new DefaultRowHeaderDataLayer(rowHeaderDataProvider);
+               rowHeaderDataLayer.setRowsResizableByDefault(false);
+               rowHeaderDataLayer.setColumnsResizableByDefault(false);
+               RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, compositeFreezeLayer, selectionLayer);
+
+               // build the corner layer
+               IDataProvider cornerDataProvider = new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider);
+               DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
+               ILayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, columnGroupHeaderLayer);
+
+               // build the grid layer
+               GridLayer gridLayer = new GridLayer(compositeFreezeLayer, columnGroupHeaderLayer, rowHeaderLayer, cornerLayer);
+               
+               table = new NatTable(parent, NatTable.DEFAULT_STYLE_OPTIONS | SWT.BORDER, gridLayer, false);
+               GridDataFactory.fillDefaults().grab(true, true).applyTo(table);
+               
+               // Register a CopyDataCommandHandler that also copies the headers and
+               // uses the configured IDisplayConverters
+               CopyDataCommandHandler copyHandler = new CopyDataCommandHandler(
+                               selectionLayer,
+                               columnHeaderDataLayer,
+                               rowHeaderDataLayer);
+               copyHandler.setCopyFormattedText(true);
+               gridLayer.registerCommandHandler(copyHandler);
+               
+               // initialize paste handler with SWT clipboard
+               cpb = new Clipboard(getDisplay());
+               PasteDataCommandHandler pasteHandler = new PasteDataCommandHandler(bodyDataProvider, bodyDataLayer, selectionLayer, cpb);
+               bodyDataLayer.registerCommandHandler(pasteHandler);
+               
+               table.addConfiguration(new DefaultNatTableStyleConfiguration());
+               table.addConfiguration(new EditingSupportConfiguration(bodyDataProvider));
+               
+               table.addConfiguration(new AbstractRegistryConfiguration() {
+                       
+                       @Override
+                       public void configureRegistry(IConfigRegistry configRegistry) {
+                       }
+
+                       @Override
+                       public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
+                               super.configureUiBindings(uiBindingRegistry);
+                               // ui binding to perform a paste action on pressing CTRL+V
+                               uiBindingRegistry.registerFirstKeyBinding(new KeyEventMatcher(SWT.MOD1, 'v'), new CustomPasteDataAction(DistrictCSVTable.this));
+                       }
+               });
+               
+               table.configure();
+       }
+       
+       @Override
+       public void dispose() {
+               cpb.dispose();
+               super.dispose();
+       }
+
+       public String[][] getCurrentData() {
+               return bodyDataProvider.getCurrentData();
+       }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableUI.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableUI.java
new file mode 100644 (file)
index 0000000..e2d3d6e
--- /dev/null
@@ -0,0 +1,11 @@
+package org.simantics.district.network.ui.table;
+
+import org.eclipse.swt.widgets.Composite;
+
+public class DistrictCSVTableUI extends Composite {
+
+       public DistrictCSVTableUI(Composite parent, int style) {
+               super(parent, style);
+       }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableView.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableView.java
new file mode 100644 (file)
index 0000000..b7d4f5b
--- /dev/null
@@ -0,0 +1,53 @@
+package org.simantics.district.network.ui.table;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+
+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.MMenuFactory;
+import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.swt.widgets.Composite;
+
+public class DistrictCSVTableView {
+
+       @Inject ESelectionService selectionService;
+
+       private DistrictCSVTable table;
+
+       @Inject
+       public void init(MPart part, MApplication app) {
+               MToolBar toolBar = MMenuFactory.INSTANCE.createToolBar();
+               toolBar.setToBeRendered(true);
+               toolBar.getChildren().add(createImportCSVDataToolItem(app));
+               part.setToolbar(toolBar);
+       }
+
+       private MHandledToolItem createImportCSVDataToolItem(MApplication app) {
+               MHandledToolItem createHandledToolItem = MMenuFactory.INSTANCE.createHandledToolItem();
+               // Command is contributed via fragment
+               createHandledToolItem.setCommand(app.getCommand("org.simantics.district.network.ui.command.importcsv")); //$NON-NLS-1$
+               createHandledToolItem.setLabel("Import CSV");
+               createHandledToolItem.setIconURI("platform:/plugin/com.famfamfam.silk/icons/table_edit.png"); //$NON-NLS-1$
+               return createHandledToolItem;
+       }
+
+       @PostConstruct
+       public void postConstruct(Composite parent) {
+               table = new DistrictCSVTable(parent, 0);
+               
+       }
+       
+       @PreDestroy
+       public void dispose() {
+               table.dispose();
+               table = null;
+       }
+
+       public String[][] getCurrentData() {
+               return table.getCurrentData();
+       }
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/EditingSupportConfiguration.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/EditingSupportConfiguration.java
new file mode 100644 (file)
index 0000000..a798bc6
--- /dev/null
@@ -0,0 +1,32 @@
+package org.simantics.district.network.ui.table;
+
+import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
+import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
+
+public class EditingSupportConfiguration extends AbstractRegistryConfiguration {
+
+    private TableDataProvider bodyDataProvider;
+
+    public EditingSupportConfiguration(TableDataProvider bodyDataProvider) {
+        this.bodyDataProvider = bodyDataProvider;
+    }
+
+    @Override
+    public void configureRegistry(IConfigRegistry configRegistry) {
+        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, new IEditableRule() {
+            @Override
+            public boolean isEditable(ILayerCell cell, IConfigRegistry configRegistry) {
+                return bodyDataProvider.isEditable(cell.getColumnIndex(), cell.getRowIndex());
+            }
+
+            @Override
+            public boolean isEditable(int columnIndex, int rowIndex) {
+                return bodyDataProvider.isEditable(columnIndex, rowIndex);
+            }
+        });
+    }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportCSVHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportCSVHandler.java
new file mode 100644 (file)
index 0000000..d484e1b
--- /dev/null
@@ -0,0 +1,286 @@
+package org.simantics.district.network.ui.table;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+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.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.dialogs.SelectionStatusDialog;
+import org.geotools.geometry.DirectPosition2D;
+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.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.IndexRoot;
+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.request.PossibleActiveModel;
+import org.simantics.district.network.DistrictNetworkUtil;
+import org.simantics.district.network.ui.function.Functions;
+import org.simantics.modeling.ModelingResources;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ImportCSVHandler {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ImportCSVHandler.class);
+
+       @Inject
+       EPartService partService;
+       
+       @Execute
+       public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell s) {
+               // here we can import based on the current CSV table data
+               MPart activePart = partService.getActivePart();
+               Object object = activePart.getObject();
+               // this object is the part wrapped by e4part
+               DistrictCSVTableView view = (DistrictCSVTableView) object;
+               String[][] data = view.getCurrentData();
+
+               SelectionDialog d = new SelectionDialog(s);
+               
+               if (d.open() == Dialog.CANCEL)
+                   return;
+               
+               String sourceEPSGCRS =  d.getSourceCRS();//"EPSG:3067";
+               
+               Resource targetDiagram = d.getTargetDiagram();
+               Resource mappingType = d.getMappingType();
+
+               try {
+                       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, false);
+                               doTransform = true;
+                       }
+                       
+                       final MathTransform finalTransform = transform;
+                       
+                       int x = xColumn(data);
+                       int y = yColumn(data);
+                       int z = zColumn(data);
+                       
+                       Simantics.getSession().asyncRequest(new WriteRequest() {
+                
+                @Override
+                public void perform(WriteGraph graph) throws DatabaseException {
+                    for (int i = 1; i < data.length; i++) {
+                        try {
+                            String[] rows = data[i];
+                            String xCoords = rows[x];
+                            String yCoords = rows[y];
+                            double xCoord = Double.parseDouble(xCoords);
+                            double yCoord = Double.parseDouble(yCoords);
+                            DirectPosition2D targetPos = new DirectPosition2D();
+                            DirectPosition2D sourcePos = new DirectPosition2D(xCoord, yCoord);
+                            DirectPosition res = finalTransform.transform(sourcePos, targetPos);
+                            double[] coords = res.getCoordinate();
+                            flipAxes(coords);
+                            Resource vertex = DistrictNetworkUtil.createVertex(graph, targetDiagram, coords, z, mappingType);
+                        } catch (Exception e) {
+                            LOGGER.error("", e);
+                        }
+                    }
+                }
+            });
+               } catch (Exception e) {
+                       LOGGER.error("", e);
+               }
+               
+               
+       }
+
+    private static void flipAxes(double[] coords) {
+        double tmp = coords[0];
+        coords[0] = coords[1];
+        coords[1] = tmp;
+    }
+
+       
+       private int zColumn(String[][] data) {
+               return column("z", data);
+       }
+       
+       private int yColumn(String[][] data) {
+               return column("y", data);
+       }
+
+       private int xColumn(String[][] data) {
+               return column("x", data);
+       }
+
+       private int column(String expected, String[][] data) {
+               String[] columns = data[0]; // first row should be headers
+               for (int i = 0; i < columns.length; i++) {
+                       String col = columns[i];
+                       if (col.equals(expected)) {
+                               return i;
+                       }
+               }
+               return -1;
+       }
+       
+       private static class SelectionDialog extends SelectionStatusDialog {
+
+        private Map<String, Resource> diagrams = new HashMap<>();
+        private Map<String, Resource> 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<Resource> 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<String> 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 (file)
index 0000000..06e9fbd
--- /dev/null
@@ -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<CustomPasteDataCommand> {
+
+       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<CustomPasteDataCommand> getCommandClass() {
+        return CustomPasteDataCommand.class;
+    }
+
+    @Override
+    protected boolean doCommand(CustomPasteDataCommand command) {
+        String[][] fullData = command.data;
+        int pasteRow = command.pasteRow;
+        Collection<Integer> 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 (file)
index 0000000..b85f6e3
--- /dev/null
@@ -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 (file)
index 0000000..a8e2a01
--- /dev/null
@@ -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<Integer> 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 (file)
index 0000000..031e502
--- /dev/null
@@ -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<Supplier<Pair<String, DynamicColorContribution>>> 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<Resource> existing) {
+        
+        List<Pair<String, DynamicColorContribution>> 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<Pair<String, DynamicSizeContribution>> 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<DynamicColoringObject> result = Simantics.getSession().syncRequest(new UniqueRead<Collection<DynamicColoringObject>>() {
+
+                    @Override
+                    public Collection<DynamicColoringObject> 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<Pair<String, DynamicColorContribution>> 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<String, ColoringObjectRow> coloringRows = new HashMap<>();
+    private Map<String, SizingObjectRow> sizingRows = new HashMap<>();
+
+    private VisualisationsListener visualisationsListener;
+
+    private Collection<NamedResource> visualisations;
+
+    private List<Supplier<Pair<String, DynamicSizeContribution>>> 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<Pair<String, DynamicColorContribution>> 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<String, DynamicColorContribution> 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<Pair<String, DynamicColorContribution>>() {
+
+            @Override
+            public Pair<String, DynamicColorContribution> 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<String, DynamicColorMap> colorMaps = Simantics.getSession().syncRequest(new UniqueRead<Map<String, DynamicColorMap>>() {
+        
+                                @Override
+                                public Map<String, DynamicColorMap> 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<Pair<String, DynamicSizeContribution>> 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<String, DynamicSizeContribution> 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<Pair<String, DynamicSizeContribution>>() {
+
+            @Override
+            public Pair<String, DynamicSizeContribution> 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<String, DynamicSizeMap> sizeMaps = Simantics.getSession().syncRequest(new UniqueRead<Map<String, DynamicSizeMap>>() {
+        
+                                @Override
+                                public Map<String, DynamicSizeMap> 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<DynamicSizingObject> resultSizing = Simantics.getSession().syncRequest(new UniqueRead<Collection<DynamicSizingObject>>() {
+
+                @Override
+                public Collection<DynamicSizingObject> 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<Pair<String, DynamicSizeContribution>> 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<Collection<NamedResource>> {
+
+        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<NamedResource> 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<DynamicVisualisation> {
+
+        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<String, DynamicColorContribution> colorContributions = visualisation.getColorContributions();
+                for (Entry<String, DynamicColorContribution> 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<String, DynamicSizeContribution> sizeContributions = visualisation.getSizeContributions();
+                for (Entry<String, DynamicSizeContribution> 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<NamedResource> 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 (file)
index 0000000..d98a378
--- /dev/null
@@ -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);
+        }
+    };
+}
index 58fd3ab49511a1dadf7bfa2d4f4ff463f92a06b9..77e7c464d2f1803028435f4ab298bc509ad6d7f0 100644 (file)
@@ -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
index fddb3b74017fb050ee20173aaa4d28ef8cf5c007..dc464c88bc066c94dd1341cabf796e12e332be8b 100644 (file)
@@ -12,6 +12,9 @@
                <resource uri="http://www.simantics.org/DistrictNetwork-1.0/ElementColoringStyle"
                        class="org.simantics.district.network.profile.DNElementColorStyle">
                </resource>
+               <resource uri="http://www.simantics.org/DistrictNetwork-1.0/ElementSizingStyle"
+                       class="org.simantics.district.network.profile.DNElementSizeStyle">
+               </resource>
                <resource uri="http://www.simantics.org/DistrictNetwork-1.0/VertexSizeStyle"
                        class="org.simantics.district.network.profile.VertexSizeStyle">
                </resource>
index 2f46d361ded6b3eb212bec13d941e1dd6288b11a..0e79ef774402aa6a83a06e093c9023129223c181 100644 (file)
@@ -82,8 +82,8 @@ translateElement elem = do
     ()
 
 importJava "org.simantics.district.network.DistrictNetworkUtil" where
-    createVertex :: Resource -> Vector Double -> Resource -> <WriteGraph, Proc> Resource
-    createEdge :: Resource -> Resource -> <WriteGraph, Proc> Resource
+    createVertex :: Resource -> Vector Double -> Double -> Resource -> <WriteGraph, Proc> Resource
+    createEdge :: Resource -> Resource -> Vector Double -> <WriteGraph, Proc> 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] -> <ReadGraph> [Resource]
 dnElementsMappedToComponents mappedComponents = mapMaybe possibleDNElementMappedToComponent mappedComponents
+
+
+importJava "org.simantics.district.network.DistrictNetworkUtil" where
+    createNetworkDiagram :: Resource -> Resource -> String -> Resource -> Resource -> Resource -> Resource -> Resource -> <WriteGraph, Proc> Resource
+    changeMappingType :: Resource -> [Resource] -> <WriteGraph, Proc> ()
+    findDNElementById :: Resource -> String -> <ReadGraph, Proc> Maybe Resource
+    findDNElementByXYCoordinates :: Resource -> Double -> Double -> Double -> <ReadGraph, Proc> [Resource]
+
index 259ad087620bed708791cb3887a4ba12f6977aef..c03ea7964ebc379a32ebc96a2547f3921a8391cd 100644 (file)
@@ -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 -> <ReadGraph> [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 (file)
index 0000000..800aa2b
--- /dev/null
@@ -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 "<init>"
+    dynamicColorMap :: String -> [RGBIntensity] -> DynamicColorMap
+
+importJava "org.simantics.district.network.visualisations.model.DynamicColorMap$RGBIntensity" where
+    data RGBIntensity
+    
+    @JavaName "<init>"
+    rgbIntensity :: Double -> Double -> Double -> RGBIntensity 
+
+importJava "org.simantics.district.network.visualisations.model.DynamicColorContribution" where
+    data DynamicColorContribution
+    
+    @JavaName "<init>"
+    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 (file)
index 0000000..e69de29
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 (file)
index 0000000..e6b2ed3
--- /dev/null
@@ -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 "<init>"
+    dynamicSizeMap :: String -> [Double] -> DynamicSizeMap
+
+importJava "org.simantics.district.network.visualisations.model.DynamicSizeContribution" where
+    data DynamicSizeContribution
+    
+    @JavaName "<init>"
+    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 (file)
index 0000000..e69de29
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 0df72c517f533f79e89dbee085b64819ec76cb9f..3f86964951184e3300eb789c388d41776942280f 100644 (file)
@@ -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<Resource> create(WriteGraph graph, Resource diagramResource, double[] start, double startElevation, double[] end, double endElevation, double[] detailedGeometryCoords, double padding) throws DatabaseException {
         Collection<Resource> vertices = graph.syncRequest(new ObjectsWithType(diagramResource, Layer0.getInstance(graph).ConsistsOf, DistrictNetworkResource.getInstance(graph).Vertex));
-        List<ResourceVertex> 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<ResourceVertex> vertices, Resource diagramResource, Resource mapping, double[] start, double[] end, double padding, boolean writeElevationToEdgeFromPoints) throws DatabaseException {
-        
+    public static Optional<Resource> 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<Resource> 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<ResourceVertex> 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<ResourceVertex> vertices = (List<ResourceVertex>) 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;
-        }
-    }
 }
index 24a77575d6d6ed6ac9ba88485bb12c8763efa3f3..6a7b453799f725c15e56b5d43d9e0ba5782aafcc 100644 (file)
@@ -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<Resource> {
         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<Resource> 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<Resource> 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<Resource> elements = findDNElementsById(graph, context, idToFind).collect(Collectors.toList());
+        if (elements.size() == 1) {
+            return elements.iterator().next();
+        }
+        return null;
+    }
+    
+    public static List<Resource> findDNElementByXYCoordinates(ReadGraph graph, Resource context, double lat, double lon, double padding) throws DatabaseException {
+        DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+        DiagramResource DIA = DiagramResource.getInstance(graph);
+        List<Resource> results = new ArrayList<>();
+        Collection<Resource> 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<String, DynamicColorContribution> colorContributions(ReadGraph graph, Resource visualisation) throws DatabaseException {
+        DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+        if (visualisation == null) {
+            return Collections.emptyMap();
+        }
+        Map<String, DynamicColorContribution> contributions = new HashMap<>();
+        
+        Map<String, DynamicColorMap> colorMaps = DynamicVisualisationsContributions.dynamicColorMaps(graph);
+        
+        Collection<Resource> 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<Pair<String, DynamicColorContribution>> collect) throws DatabaseException {
+        DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+        Layer0 L0 = Layer0.getInstance(graph);
+        
+        graph.deny(visualisation, DN.Diagram_Visualisations_colorContributions);
+        for (Pair<String, DynamicColorContribution> 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<String, DynamicSizeContribution> sizeContributions(ReadGraph graph, Resource visualisation) throws DatabaseException {
+        DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+        if (visualisation == null) {
+            return Collections.emptyMap();
+        }
+        Map<String, DynamicSizeContribution> contributions = new HashMap<>();
+        
+        Map<String, DynamicSizeMap> sizeMaps = DynamicVisualisationsContributions.dynamicSizeMaps(graph);
+        
+        Collection<Resource> 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<Pair<String, DynamicSizeContribution>> collect) throws DatabaseException {
+        DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+        Layer0 L0 = Layer0.getInstance(graph);
+
+        graph.deny(visualisation, DN.Diagram_Visualisations_sizeContributions);
+        for (Pair<String, DynamicSizeContribution> 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);
+    }
+
 }
index 39227a872777c75c3ee5a781bc7cc68051feb465..7cdb9c7db3d757e7fa7705d36ef288d146ef2c1c 100644 (file)
@@ -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<DependencyChangesRequest, DependencyChanges> {
 
-    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 (file)
index 0000000..52cfd61
--- /dev/null
@@ -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<DynamicVisualisation> {
+
+    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<String, DynamicColorContribution> colorContributions = DistrictNetworkUtil.colorContributions(graph, visualisationResource);
+            ColorBarOptions colorBarOptions = DistrictNetworkUtil.colorBarOptions(graph, visualisationResource);
+            Map<String, DynamicSizeContribution> 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;
+    }
+
+}
index 2e8c3458164770959409e9aa7fce6f87b2276532..d180d77bd75a98328e7437ab48b57633b5e03a9f 100644 (file)
@@ -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<Double> {
 
        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<Double> {
                // 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<Double> {
                for (INode nn : n.getNodes())
                        ProfileVariables.claimNodeProperty(nn, "arrowLength", null, evaluationContext);
        }
-
-       private static final class MidBranchEdgeSetRequest extends ResourceRead<Set<Resource>> {
-               private MidBranchEdgeSetRequest(Resource resource) {
-                       super(resource);
-               }
-       
-               @Override
-               public Set<Resource> perform(ReadGraph graph) throws DatabaseException {
-                       List<Resource> edges = Simantics.applySCL("Simantics/District/Algorithm", "midBranchEdges", graph, resource);
-                       return new HashSet<>(edges);
-               }
-       }
-
 }
index f8f6ba5c5760d8ddd2c1e118382dc3b35269dfa4..fe0a1fe7f2ebce8bbbfdec501fce0a0032b6e544 100644 (file)
@@ -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<Color> {
 
-       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<String, DynamicColorContribution> 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 (file)
index 0000000..990aa4d
--- /dev/null
@@ -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<Double> {
+
+    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<String, DynamicSizeContribution> 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 (file)
index 0000000..3c187a1
--- /dev/null
@@ -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<Collection<NamedResource>> {
+
+    public DynamicVisualisationsRequest(Resource diagram) {
+        super(diagram);
+    }
+
+    @Override
+    public Collection<NamedResource> perform(ReadGraph graph) throws DatabaseException {
+        DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+        Collection<Resource> visualisationResources = graph.getObjects(resource, DN.Diagram_hasVisualisation);
+        List<NamedResource> 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;
+    }
+
+}
index 8e053a065c6d88d923c29d1592586678e2316c46..d5f57c8296efcb157a5665ef9432bf45968643f8 100644 (file)
@@ -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<Boolean> {
     }
 
     @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 (file)
index 0000000..c1a9394
--- /dev/null
@@ -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<Set<Resource>> {
+       public MidBranchEdgeSetRequest(Resource resource) {
+               super(resource);
+       }
+
+       @Override
+       public Set<Resource> perform(ReadGraph graph) throws DatabaseException {
+               List<Resource> 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 (file)
index 0000000..05ed85b
--- /dev/null
@@ -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<DynamicVisualisation> {
+
+    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;
+    }
+
+}
index 67ca25952522f41caa0b7a59a163131fa2a7b878..93af11c4fa633e73dbc8ca4e885fe2e4a45ad599 100644 (file)
@@ -38,7 +38,7 @@ public abstract class ThrottledStyleBase<Result> extends StyleBase<Optional<Resu
     private static final Logger LOGGER = LoggerFactory.getLogger(ThrottledStyleBase.class);
     private static final long DEFAULT_INTERVAL = 2000;
 
-    private long lastCalculateTimestamp = 0;
+    //private long lastCalculateTimestamp = 0;
     private AtomicLong interval = new AtomicLong(DEFAULT_INTERVAL);
     private Listener<Optional<Result>> 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 (file)
index 0000000..45e52fb
--- /dev/null
@@ -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<String, DynamicColorMap> dynamicColorMaps(ReadGraph graph) throws DatabaseException {
+        List<Resource> sharedOntologies = Simantics.applySCL("Simantics/SharedOntologies", "getSharedOntologies", graph, Tuple0.INSTANCE);
+        
+        Map<String, DynamicColorMap> 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<DynamicColorMap> result = (List<DynamicColorMap>) 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<String, DynamicSizeMap> dynamicSizeMaps(ReadGraph graph) throws DatabaseException {
+        List<Resource> sharedOntologies = Simantics.applySCL("Simantics/SharedOntologies", "getSharedOntologies", graph, Tuple0.INSTANCE);
+        
+        Map<String, DynamicSizeMap> 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<DynamicSizeMap> result = (List<DynamicSizeMap>) 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<DynamicColoringObject> dynamicColoringObjects(ReadGraph graph) throws DatabaseException {
+        
+        List<Resource> sharedOntologies = Simantics.applySCL("Simantics/SharedOntologies", "getSharedOntologies", graph, Tuple0.INSTANCE);
+        
+        List<DynamicColoringObject> results = new ArrayList<>();
+        
+        for (Resource sharedOntology : sharedOntologies) {
+            Collection<Resource> findByType = graph.syncRequest(new ObjectsWithSupertype(sharedOntology, Layer0.getInstance(graph).ConsistsOf, StructuralResource2.getInstance(graph).Component));
+            //Collection<Resource> 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<DynamicSizingObject> dynamicSizingObjects(ReadGraph graph) throws DatabaseException {
+        
+        List<Resource> sharedOntologies = Simantics.applySCL("Simantics/SharedOntologies", "getSharedOntologies", graph, Tuple0.INSTANCE);
+        
+        List<DynamicSizingObject> results = new ArrayList<>();
+        
+        for (Resource sharedOntology : sharedOntologies) {
+            Collection<Resource> findByType = graph.syncRequest(new ObjectsWithSupertype(sharedOntology, Layer0.getInstance(graph).ConsistsOf, StructuralResource2.getInstance(graph).Component));
+            //Collection<Resource> 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<Stream<DynamicColorMap>> getDynamicColorMapSupplier(String uri, String expressionText) {
+        return () -> {
+            try {
+                @SuppressWarnings("unchecked")
+                List<DynamicColorMap> result = (List<DynamicColorMap>) 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<Stream<DynamicColorContribution>> getDynamicColorContributionSupplier(String uri, String expressionText) {
+        return () -> {
+            try {
+                @SuppressWarnings("unchecked")
+                List<DynamicColorContribution> result = (List<DynamicColorContribution>) 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<Stream<DynamicSizeContribution>> getDynamicSizeContributionSupplier(String uri, String expressionText) {
+        return () -> {
+            try {
+                @SuppressWarnings("unchecked")
+                List<DynamicSizeContribution> result = (List<DynamicSizeContribution>) 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<Stream<DynamicColorContribution>> colorContributionSupplier;
+        private Map<String, DynamicColorContribution> colorContributions;
+
+        public DynamicColoringObject(NamedResource coloringObject, Supplier<Stream<DynamicColorContribution>> colorContributionSupplier) {
+            this.coloringObject = coloringObject;
+            this.colorContributionSupplier = colorContributionSupplier;
+        }
+
+        public NamedResource getColoringObject() {
+            return coloringObject;
+        }
+
+        public Map<String, DynamicColorContribution> 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<Stream<DynamicColorMap>> colorContributionSupplier;
+        private Map<String, DynamicColorMap> colorContributions;
+
+        public DynamicColoringMap(NamedResource coloringObject, Supplier<Stream<DynamicColorMap>> colorContributionSupplier) {
+            this.coloringObject = coloringObject;
+            this.colorContributionSupplier = colorContributionSupplier;
+        }
+
+        public NamedResource getColoringObject() {
+            return coloringObject;
+        }
+
+        public Map<String, DynamicColorMap> 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<Stream<DynamicSizeContribution>> sizeContributionSupplier;
+        private Map<String, DynamicSizeContribution> sizeContributions;
+        
+        public DynamicSizingObject(NamedResource coloringObject, Supplier<Stream<DynamicSizeContribution>> sizeContributionSupplier) {
+            this.sizingObject = coloringObject;
+            this.sizeContributionSupplier = sizeContributionSupplier;
+        }
+
+        public NamedResource getSizingObject() {
+            return sizingObject;
+        }
+
+        public Map<String, DynamicSizeContribution> 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 (file)
index 0000000..c13bcd4
--- /dev/null
@@ -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 (file)
index 0000000..a57ad91
--- /dev/null
@@ -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 (file)
index 0000000..2839bcc
--- /dev/null
@@ -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 (file)
index 0000000..09cb579
--- /dev/null
@@ -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<RGBIntensity> 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<RGBIntensity> intensities;
+    
+    public DynamicColorMap(String label, List<RGBIntensity> intensities) {
+        this.label = label;
+        this.intensities = intensities;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public List<RGBIntensity> 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 (file)
index 0000000..009c8c4
--- /dev/null
@@ -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 (file)
index 0000000..7edb12b
--- /dev/null
@@ -0,0 +1,38 @@
+package org.simantics.district.network.visualisations.model;
+
+import java.util.List;
+
+public class DynamicSizeMap {
+
+    private String label;
+    private List<Double> sizes;
+
+    public DynamicSizeMap(String label, List<Double> sizes) {
+        this.label = label;
+        this.sizes = sizes;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+    
+    public List<Double> 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 (file)
index 0000000..24cf9be
--- /dev/null
@@ -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<String, DynamicColorContribution> colorContributions;
+    private ColorBarOptions colorBarOptions;
+    private Map<String, DynamicSizeContribution> sizeContributions;
+    private SizeBarOptions sizeBarOptions;
+    
+    public DynamicVisualisation(String name, Resource visualisationResource, Map<String, DynamicColorContribution> colorContributions, ColorBarOptions colorBarOptions, Map<String, DynamicSizeContribution> 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<String, DynamicColorContribution> getColorContributions() {
+        return colorContributions;
+    }
+
+    public ColorBarOptions getColorBarOptions() {
+        return colorBarOptions;
+    }
+
+    public Map<String, DynamicSizeContribution> 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 (file)
index 0000000..14e9f47
--- /dev/null
@@ -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);
+    }
+}
index 562dd24bb7d22108ae4e810bd42174c5ff6fc18b..bedea060f9628b23cdccd39d1ceea5cf5b5348ab 100644 (file)
@@ -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<DiagramRegion> set = (Set) inputElement;
+                @SuppressWarnings("unchecked")
+                Set<DiagramRegion> set = (Set<DiagramRegion>) inputElement;
                 return set.toArray();
             }
             
index 46b11a2febedada000c662607628f96727ece4e9..0fe76c36310a5d63723e76e281601bd5cf775d1c 100644 (file)
@@ -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";
 
index ee4c1e64ad52d578c67a8d0db8fad0d06b26c35f..61ec5737d9fcaaab4bfef73a5e563e1139f7f869 100644 (file)
@@ -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 (file)
index 0000000..1b5c1ec
--- /dev/null
@@ -0,0 +1,15 @@
+L0 = <http://www.simantics.org/Layer0-1.1>
+DN = <http://www.simantics.org/DistrictNetwork-1.0>
+DNR = <http://www.simantics.org/DistrictNetworkRoutes-1.0>
+VP = <http://www.simantics.org/Viewpoint-1.2>
+ACT = <http://www.simantics.org/Action-1.1>
+SILK = <http://www.simantics.org/Silk-1.1>
+
+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
index def312552f17b6e1a1cf0b95d6005762d2a549af..b9641270aaa8a86761f85df745ee6a5d172a6f07 100644 (file)
@@ -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);
     }
     
index 3b531e707da2667c7b3ab10fd3a4a29617ced9c8..700455198b5e54308b0e508da060bbcfe903f082 100644 (file)
@@ -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 (file)
index 0000000..94d23ae
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<adapters>
+       <target interface="org.simantics.db.layer0.adapter.ActionFactory">
+               <resource uri="http://www.simantics.org/DistrictNetworkRoutes-1.0/Actions/SelectRoute"
+                       class="org.simantics.district.route.ui.actions.SelectRouteAction" />
+       </target>
+       <target interface="org.simantics.db.layer0.adapter.ActionFactory">
+               <resource uri="http://www.simantics.org/DistrictNetworkRoutes-1.0/Actions/ValidateRoute"
+                       class="org.simantics.district.route.ui.actions.ValidateRouteAction" />
+       </target>
+</adapters>
index e29652c54b9e2e94d1a281d88f22c2e3b8c8cb7a..34579a4ca2b55910a5e4649f92c1bed09e158d06 100644 (file)
@@ -3,5 +3,6 @@ output.. = bin/
 bin.includes = META-INF/,\
                .,\
                plugin.xml,\
+               adapters.xml,\
                fragment.e4xmi,\
                OSGI-INF/
index 16c774b371847e9122cde24c406815259a984db3..291140d8190e800246a0e52eb9c530812c423543 100644 (file)
@@ -6,12 +6,16 @@
     <elements xsi:type="commands:Command" xmi:id="_k63rkP6zEeiBo8tg-6EPYA" elementId="org.simantics.district.route.ui.command.selectRouteOnDiagram" commandName="Select Route on Diagram"/>
     <elements xsi:type="commands:Command" xmi:id="_bA61IAI7EemGocelWS26pQ" elementId="org.simantics.district.route.ui.command.discardRoute" commandName="Discard Route"/>
     <elements xsi:type="commands:Command" xmi:id="_bfN6sAI7EemGocelWS26pQ" elementId="org.simantics.district.route.ui.command.renameRoute" commandName="Rename Route"/>
+    <elements xsi:type="commands:Command" xmi:id="_Of1BQGp0EemcK4j7CyatJA" elementId="org.simantics.district.route.ui.command.activatecreateroute" commandName="Activate Create Route"/>
+    <elements xsi:type="commands:Command" xmi:id="_QSvwYGp0EemcK4j7CyatJA" elementId="org.simantics.district.route.ui.command.deactivatecreateroute" commandName="Deactivate Create Route"/>
   </fragments>
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_1Ds_gMqVEeeUz6Cs9kKeKg" featurename="handlers" parentElementId="xpath:/">
     <elements xsi:type="commands:Handler" xmi:id="_1G5RYH4WEei74IuRct85qQ" elementId="org.simantics.district.route.ui.handlers.openRouteView" contributionURI="bundleclass://org.simantics.district.route.ui/org.simantics.district.route.ui.OpenRouteView" command="_x1nG0H4WEei74IuRct85qQ"/>
     <elements xsi:type="commands:Handler" xmi:id="_O9tbQP60EeiBo8tg-6EPYA" elementId="org.simantics.district.route.ui.handlers.selectRouteOnDiagram" contributionURI="bundleclass://org.simantics.district.route.ui/org.simantics.district.route.ui.handlers.SelectRouteOnDiagram" command="_k63rkP6zEeiBo8tg-6EPYA"/>
     <elements xsi:type="commands:Handler" xmi:id="_mRg_oAI7EemGocelWS26pQ" elementId="org.simantics.district.route.ui.handler.discardRoute" contributionURI="bundleclass://org.simantics.district.route.ui/org.simantics.district.route.ui.handlers.DiscardRoute" command="_bA61IAI7EemGocelWS26pQ"/>
     <elements xsi:type="commands:Handler" xmi:id="_mh_EkAI7EemGocelWS26pQ" elementId="org.simantics.district.route.ui.handler.renameRoute" contributionURI="bundleclass://org.simantics.district.route.ui/org.simantics.district.route.ui.handlers.RenameRoute" command="_bfN6sAI7EemGocelWS26pQ"/>
+    <elements xsi:type="commands:Handler" xmi:id="_YkoHAGp0EemcK4j7CyatJA" elementId="org.simantics.district.route.ui.handler.0" contributionURI="bundleclass://org.simantics.district.route.ui/org.simantics.district.route.ui.handlers.ActivateCreateRoute" command="_Of1BQGp0EemcK4j7CyatJA"/>
+    <elements xsi:type="commands:Handler" xmi:id="_aQl5kGp0EemcK4j7CyatJA" elementId="org.simantics.district.route.ui.handler.1" contributionURI="bundleclass://org.simantics.district.route.ui/org.simantics.district.route.ui.handlers.DeactivateCreateRoute" command="_QSvwYGp0EemcK4j7CyatJA"/>
   </fragments>
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_Fso08MrIEeeUz6Cs9kKeKg" featurename="toolBarContributions" parentElementId="xpath:/"/>
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_WNB8EH4oEei74IuRct85qQ" featurename="trimContributions" parentElementId="xpath:/">
index 4499782d100860411da25f878a6ee1c9da8074be..78b6be9e2f25f5478b0aa37366f0cd8749b8a5b6 100644 (file)
@@ -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 (file)
index 0000000..11de4cc
--- /dev/null
@@ -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<List<Resource>> 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 (file)
index 0000000..5b0e7d0
--- /dev/null
@@ -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<List<Resource>> 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 (file)
index 0000000..0dc2135
--- /dev/null
@@ -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 (file)
index 0000000..a60478d
--- /dev/null
@@ -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
index 5d3f63dc2499d471bdb40978a7a537b3b053bebc..ce8c2848635adff2a65455289f42ed5de4668766 100644 (file)
@@ -30,4 +30,6 @@ public interface RouteService {
 
     List<Router> routers();
 
+    Route readRoute(Object backendRouteObject);
+
 }
index fceb6612cb521fd2d81e3d5759046c4daee2f120..4bc0c6f0cc4fa3d73d93a5fae9eda5ab25796147 100644 (file)
@@ -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<RouteImpl> 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<RouteImpl> {
+        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<List<RouteImpl>> {
         public ModelRoutesRequest(Resource model) {
             super(model);
@@ -154,7 +172,7 @@ public class RoutePersistence {
             return findRoutes(graph, resource);
         }
     }
-
+    
     public static class ActiveModelRoutesRequest extends UniqueRead<List<RouteImpl>> {
         @Override
         public List<RouteImpl> perform(ReadGraph graph) throws DatabaseException {
index 6817ea3282843de3f70531c69d47889c71786bcc..4690ac46b3dafa87727096273365ef17afc3c646 100644 (file)
@@ -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;
+        }
+    }
+
 }
index 4382e70c1e55c6efca2b284b8d632e6dd6874b79..6a005c4186f95454e77ca34d82534228ba7f39f5 100644 (file)
@@ -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 (file)
index 0000000..eb92844
--- /dev/null
@@ -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 (file)
index fb07142..0000000
Binary files a/org.simantics.district.selection/graph.tg and /dev/null differ
index d563d637eb4eba3ddbe470f3ebff97572a52e73c..213c44268778d156f0c4578ccd57de49420ddc17 100644 (file)
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="org.simantics.maps.elevation.server.ui"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
diff --git a/org.simantics.maps.elevation.server.ui/.classpath b/org.simantics.maps.elevation.server.ui/.classpath
new file mode 100644 (file)
index 0000000..eca7bdb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.simantics.maps.elevation.server.ui/.project b/org.simantics.maps.elevation.server.ui/.project
new file mode 100644 (file)
index 0000000..ddcb381
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.simantics.maps.elevation.server.ui</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
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 (file)
index 0000000..fe2d6d6
--- /dev/null
@@ -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 (file)
index 0000000..e9863e2
--- /dev/null
@@ -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 (file)
index 0000000..1e8eaa1
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+    <extension
+          point="org.eclipse.ui.preferencePages">
+    <page
+          category="org.simantics.district.network.ui.preferences"
+          class="org.simantics.maps.elevation.server.ui.MapsElevationServerPreferencePage"
+          id="org.simantics.maps.elevation.server.ui.preferences"
+          name="Elevation server">
+    </page>
+    </extension>
+</plugin>
diff --git a/org.simantics.maps.elevation.server.ui/pom.xml b/org.simantics.maps.elevation.server.ui/pom.xml
new file mode 100644 (file)
index 0000000..84a32b9
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+       xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+       <modelVersion>4.0.0</modelVersion>
+
+       <parent>
+               <groupId>org.simantics.district</groupId>
+               <artifactId>org.simantics.district.root</artifactId>
+               <version>1.0.0-SNAPSHOT</version>
+       </parent>
+
+       <artifactId>org.simantics.maps.elevation.server.ui</artifactId>
+       <packaging>eclipse-plugin</packaging>
+       <version>1.0.0-SNAPSHOT</version>
+
+</project>
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 (file)
index 0000000..980dd16
--- /dev/null
@@ -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 (file)
index 0000000..366ca03
--- /dev/null
@@ -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 (file)
index 0000000..eca7bdb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.simantics.maps.elevation.server/.project b/org.simantics.maps.elevation.server/.project
new file mode 100644 (file)
index 0000000..fb4071c
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.simantics.maps.elevation.server</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
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 (file)
index 0000000..6ea239a
--- /dev/null
@@ -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 (file)
index 0000000..a4fd10d
--- /dev/null
@@ -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 (file)
index 0000000..f2db868
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+       xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+       <modelVersion>4.0.0</modelVersion>
+
+       <parent>
+               <groupId>org.simantics.district</groupId>
+               <artifactId>org.simantics.district.root</artifactId>
+               <version>1.0.0-SNAPSHOT</version>
+       </parent>
+
+       <artifactId>org.simantics.maps.elevation.server</artifactId>
+       <packaging>eclipse-plugin</packaging>
+       <version>1.0.0-SNAPSHOT</version>
+
+</project>
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 (file)
index 0000000..9edd6da
--- /dev/null
@@ -0,0 +1,4 @@
+
+importJava "org.simantics.maps.elevation.server.SingletonTiffTileInterface" where
+    @JavaName lookupd
+    lookupElevation :: Double -> Double -> <Proc> 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 (file)
index 0000000..c003700
--- /dev/null
@@ -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 (file)
index 0000000..ced1c16
--- /dev/null
@@ -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<Rectangle2D> 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 (file)
index 0000000..f593531
--- /dev/null
@@ -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 (file)
index 0000000..8f7ecc0
--- /dev/null
@@ -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<Path, Envelope> envelopes = new ConcurrentHashMap<>();
+    private LoadingCache<Path, TiffInterface> 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<Path> 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<Rectangle2D> getBoundingBoxes() {
+        Collection<Rectangle2D> 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<Path> tifFiles = (List<Path>) 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 (file)
index 0000000..bcb59f2
--- /dev/null
@@ -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);
+    }
+}
index 6feb1d7771be3ae86765fb03cabaf6b9f83630e3..d983b5ae80a74829fd240586ef3de4eb9afa5220 100644 (file)
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="org.simantics.maps.elevation.server"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="com.github.benmanes.caffeine"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
index 0dc33f13041741b2bbc60ff9ba0d101922183b58..8a1a6f91271f2bd2d54372f5724f5c68d99a59d3 100644 (file)
@@ -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;
index dbe525725dff6d6bc3fa9ef456b3ab8a023011bd..3ba5157da66407bc3f9020e20f8d3d4e9d643307 100644 (file)
@@ -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<Path> stream = Files.newDirectoryStream(tm2)) {
diff --git a/pom.xml b/pom.xml
index f36b9763c05f7bb8e0a3e25d42dd74ce768243c6..5489a9087306724350c083bf849047ec9a21eeec 100644 (file)
--- a/pom.xml
+++ b/pom.xml
                        <layout>p2</layout>
                        <url>${simantics-download-site}/${branch-spec}/sdk</url>
                </repository>
+               <repository>
+                       <id>external-components</id>
+                       <layout>p2</layout>
+                       <url>${simantics-download-site}/${branch-spec}/external-components/maven</url>
+               </repository>
        </repositories>
 
        <pluginRepositories>
                <module>org.simantics.district.route.feature</module>
                <module>org.simantics.district.ui.feature</module>
                <module>org.simantics.maps.server.feature</module>
-
+               
+               <module>org.simantics.maps.elevation.server</module>
+               <module>org.simantics.maps.elevation.server.ui</module>
+               
                <module>org.simantics.district.repository</module>
        </modules>
 </project>