From 21bc60b13076364af0340243995108f3ee5262a4 Mon Sep 17 00:00:00 2001 From: Rushi Date: Fri, 8 Aug 2025 19:14:12 +0200 Subject: [PATCH] Initial commit --- .gitignore | 42 + .idea/.gitignore | 8 + .idea/.name | 1 + .idea/artifacts/energy_ade2_citydb_jar.xml | 109 ++ .idea/gradle.xml | 31 + .idea/inspectionProfiles/Project_Default.xml | 15 + .idea/misc.xml | 11 + .idea/uiDesigner.xml | 124 ++ .idea/vcs.xml | 7 + README.md | 22 + build.gradle | 63 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 234 +++ gradlew.bat | 89 ++ .../3dcitydb/postgreSQL/CREATE_ADE_DB.sql | 1048 ++++++++++++ .../3dcitydb/postgreSQL/DROP_ADE_DB.sql | 149 ++ .../schema-mapping/schema-mapping.xml | 1408 +++++++++++++++++ settings.gradle | 2 + .../de/stuttgart/hft/EnergyADEExtension.java | 59 + .../AbstractQualifiedAttributeExporter.java | 135 ++ .../exporter/BuildingPropertiesExporter.java | 137 ++ .../CityObjectPropertiesExporter.java | 91 ++ .../ade/energy2/exporter/ExportManager.java | 146 ++ .../RefurbishmentMeasureExporter.java | 152 ++ .../energy2/exporter/ResourceExporter.java | 322 ++++ .../energy2/exporter/TimeSeriesExporter.java | 411 +++++ .../exporter/UrbanFunctionAreaExporter.java | 87 + .../energy2/exporter/WeatherDataExporter.java | 184 +++ .../exporter/WeatherStationExporter.java | 131 ++ .../importer/AbstractBuildingImporter.java | 83 + .../importer/AbstractDeviceImporter.java | 114 ++ .../AbstractQualifiedAttributeImporter.java | 138 ++ .../importer/BuildingPropertiesImporter.java | 207 +++ .../CityObjectPropertiesImporter.java | 142 ++ .../importer/CityObjectRelationImporter.java | 68 + .../importer/DeviceOperationImporter.java | 63 + .../EnergyPerformanceCertificateImporter.java | 90 ++ .../ade/energy2/importer/ImportManager.java | 160 ++ .../RefurbishmentMeasureImporter.java | 93 ++ .../energy2/importer/ResourceImporter.java | 268 ++++ .../energy2/importer/TimeSeriesImporter.java | 276 ++++ .../importer/UrbanFunctionAreaImporter.java | 75 + .../UtilityNetworkConnectionImporter.java | 89 ++ .../energy2/importer/WeatherDataImporter.java | 123 ++ .../importer/WeatherStationImporter.java | 94 ++ .../hft/ade/energy2/schema/ADESequence.java | 43 + .../hft/ade/energy2/schema/ADETable.java | 88 ++ .../hft/ade/energy2/schema/ObjectMapper.java | 140 ++ .../hft/ade/energy2/schema/SchemaMapper.java | 64 + src/main/resources/META-INF/MANIFEST.MF | 3 + .../services/org.citydb.core.ade.ADEExtension | 1 + 52 files changed, 7646 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/artifacts/energy_ade2_citydb_jar.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 README.md create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 resources/database/3dcitydb/postgreSQL/CREATE_ADE_DB.sql create mode 100644 resources/database/3dcitydb/postgreSQL/DROP_ADE_DB.sql create mode 100644 resources/database/schema-mapping/schema-mapping.xml create mode 100644 settings.gradle create mode 100644 src/main/java/de/stuttgart/hft/EnergyADEExtension.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/exporter/AbstractQualifiedAttributeExporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/exporter/BuildingPropertiesExporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/exporter/CityObjectPropertiesExporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/exporter/ExportManager.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/exporter/RefurbishmentMeasureExporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/exporter/ResourceExporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/exporter/TimeSeriesExporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/exporter/UrbanFunctionAreaExporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/exporter/WeatherDataExporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/exporter/WeatherStationExporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractBuildingImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractDeviceImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractQualifiedAttributeImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/BuildingPropertiesImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/CityObjectPropertiesImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/CityObjectRelationImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/DeviceOperationImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/EnergyPerformanceCertificateImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/ImportManager.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/RefurbishmentMeasureImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/ResourceImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/TimeSeriesImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/UrbanFunctionAreaImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/UtilityNetworkConnectionImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/WeatherDataImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/importer/WeatherStationImporter.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/schema/ADESequence.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/schema/ADETable.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/schema/ObjectMapper.java create mode 100644 src/main/java/de/stuttgart/hft/ade/energy2/schema/SchemaMapper.java create mode 100644 src/main/resources/META-INF/MANIFEST.MF create mode 100644 src/main/resources/services/org.citydb.core.ade.ADEExtension diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b63da45 --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..4914fb0 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +energy-ade2-citydb \ No newline at end of file diff --git a/.idea/artifacts/energy_ade2_citydb_jar.xml b/.idea/artifacts/energy_ade2_citydb_jar.xml new file mode 100644 index 0000000..0d20439 --- /dev/null +++ b/.idea/artifacts/energy_ade2_citydb_jar.xml @@ -0,0 +1,109 @@ + + + $PROJECT_DIR$/out/artifacts/energy_ade2_citydb_jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..a27b7c2 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,31 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..63758e7 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6bfb2cf --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..ae84807 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e1a8936 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Energy ADE (ver 2 beta 7) module for importer/exporter plugin v5.5.1 for 3DCityDB v4.4.2 - Work in Progress + +[Java 17](https://bell-sw.com/pages/downloads/#jdk-17-lts) is the minimum requirement. + +This is a 3DCityDB importer/exporter plugin module for the **Energy Application Domain Extension** (Energy ADE) v2 beta7 for CityGML. + +There are +currently _no plans to migrate_ the Energy ADE 2 module to citygml4j v3, CityGML 3 or 3DCityDB v5. + +Priority List: + +- Timeseries +- DataType +- Core +- Resource +- UrbanFunctionAreas +- WeatherStation +- Layered Construction +- Schedule +- Devices +- BuildingPhysics +- Occupancy \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..5c7447d --- /dev/null +++ b/build.gradle @@ -0,0 +1,63 @@ +plugins { + id 'java' + id 'java-library' + id 'distribution' + id 'maven-publish' + id 'signing' + id 'io.github.gradle-nexus.publish-plugin' version '2.0.0' +} + +group = 'de.stuttgart.hft' +version = 'beta7' +description 'Energy ADE2 extension for the 3DCityDB' + +repositories { + maven { + url 'https://3dcitydb.org/maven' + } + maven { + url 'https://repo.osgeo.org/repository/release' + } + mavenCentral() + mavenLocal() +} + +configurations { + citygml4j +} + +dependencies { + api 'org.citydb:impexp-client-gui:5.5.1' + api 'org.citygml4j.ade:energy-ade2-citygml4j:2.0.1' + + citygml4j('org.citygml4j.ade:energy-ade2-citygml4j:2.0.1') { + transitive = false + } + + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0' + +} + +test { + useJUnitPlatform() +} + +distributions.main { + distributionBaseName = 'energy-ade2' + + contents { + from 'README.md' + into('lib') { + from jar + from configurations.citygml4j + } + into('schema-mapping') { + from "$rootDir/resources/database/schema-mapping" + } + into('3dcitydb') { + from "$rootDir/resources/database/3dcitydb" + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 zcmb5WV{~QRw(p$^Dz@00IL3?^hro$gg*4VI_WAaTyVM5Foj~O|-84 z$;06hMwt*rV;^8iB z1~&0XWpYJmG?Ts^K9PC62H*`G}xom%S%yq|xvG~FIfP=9*f zZoDRJBm*Y0aId=qJ?7dyb)6)JGWGwe)MHeNSzhi)Ko6J<-m@v=a%NsP537lHe0R* z`If4$aaBA#S=w!2z&m>{lpTy^Lm^mg*3?M&7HFv}7K6x*cukLIGX;bQG|QWdn{%_6 zHnwBKr84#B7Z+AnBXa16a?or^R?+>$4`}{*a_>IhbjvyTtWkHw)|ay)ahWUd-qq$~ zMbh6roVsj;_qnC-R{G+Cy6bApVOinSU-;(DxUEl!i2)1EeQ9`hrfqj(nKI7?Z>Xur zoJz-a`PxkYit1HEbv|jy%~DO^13J-ut986EEG=66S}D3!L}Efp;Bez~7tNq{QsUMm zh9~(HYg1pA*=37C0}n4g&bFbQ+?-h-W}onYeE{q;cIy%eZK9wZjSwGvT+&Cgv z?~{9p(;bY_1+k|wkt_|N!@J~aoY@|U_RGoWX<;p{Nu*D*&_phw`8jYkMNpRTWx1H* z>J-Mi_!`M468#5Aix$$u1M@rJEIOc?k^QBc?T(#=n&*5eS#u*Y)?L8Ha$9wRWdH^3D4|Ps)Y?m0q~SiKiSfEkJ!=^`lJ(%W3o|CZ zSrZL-Xxc{OrmsQD&s~zPfNJOpSZUl%V8tdG%ei}lQkM+z@-4etFPR>GOH9+Y_F<3=~SXln9Kb-o~f>2a6Xz@AS3cn^;c_>lUwlK(n>z?A>NbC z`Ud8^aQy>wy=$)w;JZzA)_*Y$Z5hU=KAG&htLw1Uh00yE!|Nu{EZkch zY9O6x7Y??>!7pUNME*d!=R#s)ghr|R#41l!c?~=3CS8&zr6*aA7n9*)*PWBV2w+&I zpW1-9fr3j{VTcls1>ua}F*bbju_Xq%^v;-W~paSqlf zolj*dt`BBjHI)H9{zrkBo=B%>8}4jeBO~kWqO!~Thi!I1H(in=n^fS%nuL=X2+s!p}HfTU#NBGiwEBF^^tKU zbhhv+0dE-sbK$>J#t-J!B$TMgN@Wh5wTtK2BG}4BGfsZOoRUS#G8Cxv|6EI*n&Xxq zt{&OxCC+BNqz$9b0WM7_PyBJEVObHFh%%`~!@MNZlo*oXDCwDcFwT~Rls!aApL<)^ zbBftGKKBRhB!{?fX@l2_y~%ygNFfF(XJzHh#?`WlSL{1lKT*gJM zs>bd^H9NCxqxn(IOky5k-wALFowQr(gw%|`0991u#9jXQh?4l|l>pd6a&rx|v=fPJ z1mutj{YzpJ_gsClbWFk(G}bSlFi-6@mwoQh-XeD*j@~huW4(8ub%^I|azA)h2t#yG z7e_V_<4jlM3D(I+qX}yEtqj)cpzN*oCdYHa!nm%0t^wHm)EmFP*|FMw!tb@&`G-u~ zK)=Sf6z+BiTAI}}i{*_Ac$ffr*Wrv$F7_0gJkjx;@)XjYSh`RjAgrCck`x!zP>Ifu z&%he4P|S)H*(9oB4uvH67^0}I-_ye_!w)u3v2+EY>eD3#8QR24<;7?*hj8k~rS)~7 zSXs5ww)T(0eHSp$hEIBnW|Iun<_i`}VE0Nc$|-R}wlSIs5pV{g_Dar(Zz<4X3`W?K z6&CAIl4U(Qk-tTcK{|zYF6QG5ArrEB!;5s?tW7 zrE3hcFY&k)+)e{+YOJ0X2uDE_hd2{|m_dC}kgEKqiE9Q^A-+>2UonB+L@v3$9?AYw zVQv?X*pK;X4Ovc6Ev5Gbg{{Eu*7{N3#0@9oMI~}KnObQE#Y{&3mM4`w%wN+xrKYgD zB-ay0Q}m{QI;iY`s1Z^NqIkjrTlf`B)B#MajZ#9u41oRBC1oM1vq0i|F59> z#StM@bHt|#`2)cpl_rWB($DNJ3Lap}QM-+A$3pe}NyP(@+i1>o^fe-oxX#Bt`mcQc zb?pD4W%#ep|3%CHAYnr*^M6Czg>~L4?l16H1OozM{P*en298b+`i4$|w$|4AHbzqB zHpYUsHZET$Z0ztC;U+0*+amF!@PI%^oUIZy{`L{%O^i{Xk}X0&nl)n~tVEpcAJSJ} zverw15zP1P-O8h9nd!&hj$zuwjg?DoxYIw{jWM zW5_pj+wFy8Tsa9g<7Qa21WaV&;ejoYflRKcz?#fSH_)@*QVlN2l4(QNk| z4aPnv&mrS&0|6NHq05XQw$J^RR9T{3SOcMKCXIR1iSf+xJ0E_Wv?jEc*I#ZPzyJN2 zUG0UOXHl+PikM*&g$U@g+KbG-RY>uaIl&DEtw_Q=FYq?etc!;hEC_}UX{eyh%dw2V zTTSlap&5>PY{6I#(6`j-9`D&I#|YPP8a;(sOzgeKDWsLa!i-$frD>zr-oid!Hf&yS z!i^cr&7tN}OOGmX2)`8k?Tn!!4=tz~3hCTq_9CdiV!NIblUDxHh(FJ$zs)B2(t5@u z-`^RA1ShrLCkg0)OhfoM;4Z{&oZmAec$qV@ zGQ(7(!CBk<5;Ar%DLJ0p0!ResC#U<+3i<|vib1?{5gCebG7$F7URKZXuX-2WgF>YJ^i zMhHDBsh9PDU8dlZ$yJKtc6JA#y!y$57%sE>4Nt+wF1lfNIWyA`=hF=9Gj%sRwi@vd z%2eVV3y&dvAgyuJ=eNJR+*080dbO_t@BFJO<@&#yqTK&+xc|FRR;p;KVk@J3$S{p` zGaMj6isho#%m)?pOG^G0mzOAw0z?!AEMsv=0T>WWcE>??WS=fII$t$(^PDPMU(P>o z_*0s^W#|x)%tx8jIgZY~A2yG;US0m2ZOQt6yJqW@XNY_>_R7(Nxb8Ged6BdYW6{prd!|zuX$@Q2o6Ona8zzYC1u!+2!Y$Jc9a;wy+pXt}o6~Bu1oF1c zp7Y|SBTNi@=I(K%A60PMjM#sfH$y*c{xUgeSpi#HB`?|`!Tb&-qJ3;vxS!TIzuTZs-&%#bAkAyw9m4PJgvey zM5?up*b}eDEY+#@tKec)-c(#QF0P?MRlD1+7%Yk*jW;)`f;0a-ZJ6CQA?E%>i2Dt7T9?s|9ZF|KP4;CNWvaVKZ+Qeut;Jith_y{v*Ny6Co6!8MZx;Wgo z=qAi%&S;8J{iyD&>3CLCQdTX*$+Rx1AwA*D_J^0>suTgBMBb=*hefV+Ars#mmr+YsI3#!F@Xc1t4F-gB@6aoyT+5O(qMz*zG<9Qq*f0w^V!03rpr*-WLH}; zfM{xSPJeu6D(%8HU%0GEa%waFHE$G?FH^kMS-&I3)ycx|iv{T6Wx}9$$D&6{%1N_8 z_CLw)_9+O4&u94##vI9b-HHm_95m)fa??q07`DniVjAy`t7;)4NpeyAY(aAk(+T_O z1om+b5K2g_B&b2DCTK<>SE$Ode1DopAi)xaJjU>**AJK3hZrnhEQ9E`2=|HHe<^tv z63e(bn#fMWuz>4erc47}!J>U58%<&N<6AOAewyzNTqi7hJc|X{782&cM zHZYclNbBwU6673=!ClmxMfkC$(CykGR@10F!zN1Se83LR&a~$Ht&>~43OX22mt7tcZUpa;9@q}KDX3O&Ugp6< zLZLfIMO5;pTee1vNyVC$FGxzK2f>0Z-6hM82zKg44nWo|n}$Zk6&;5ry3`(JFEX$q zK&KivAe${e^5ZGc3a9hOt|!UOE&OocpVryE$Y4sPcs4rJ>>Kbi2_subQ9($2VN(3o zb~tEzMsHaBmBtaHAyES+d3A(qURgiskSSwUc9CfJ@99&MKp2sooSYZu+-0t0+L*!I zYagjOlPgx|lep9tiU%ts&McF6b0VE57%E0Ho%2oi?=Ks+5%aj#au^OBwNwhec zta6QAeQI^V!dF1C)>RHAmB`HnxyqWx?td@4sd15zPd*Fc9hpDXP23kbBenBxGeD$k z;%0VBQEJ-C)&dTAw_yW@k0u?IUk*NrkJ)(XEeI z9Y>6Vel>#s_v@=@0<{4A{pl=9cQ&Iah0iD0H`q)7NeCIRz8zx;! z^OO;1+IqoQNak&pV`qKW+K0^Hqp!~gSohcyS)?^P`JNZXw@gc6{A3OLZ?@1Uc^I2v z+X!^R*HCm3{7JPq{8*Tn>5;B|X7n4QQ0Bs79uTU%nbqOJh`nX(BVj!#f;#J+WZxx4 z_yM&1Y`2XzhfqkIMO7tB3raJKQS+H5F%o83bM+hxbQ zeeJm=Dvix$2j|b4?mDacb67v-1^lTp${z=jc1=j~QD>7c*@+1?py>%Kj%Ejp7Y-!? z8iYRUlGVrQPandAaxFfks53@2EC#0)%mrnmGRn&>=$H$S8q|kE_iWko4`^vCS2aWg z#!`RHUGyOt*k?bBYu3*j3u0gB#v(3tsije zgIuNNWNtrOkx@Pzs;A9un+2LX!zw+p3_NX^Sh09HZAf>m8l@O*rXy_82aWT$Q>iyy zqO7Of)D=wcSn!0+467&!Hl))eff=$aneB?R!YykdKW@k^_uR!+Q1tR)+IJb`-6=jj zymzA>Sv4>Z&g&WWu#|~GcP7qP&m*w-S$)7Xr;(duqCTe7p8H3k5>Y-n8438+%^9~K z3r^LIT_K{i7DgEJjIocw_6d0!<;wKT`X;&vv+&msmhAAnIe!OTdybPctzcEzBy88_ zWO{6i4YT%e4^WQZB)KHCvA(0tS zHu_Bg+6Ko%a9~$EjRB90`P(2~6uI@SFibxct{H#o&y40MdiXblu@VFXbhz>Nko;7R z70Ntmm-FePqhb%9gL+7U8@(ch|JfH5Fm)5${8|`Lef>LttM_iww6LW2X61ldBmG0z zax3y)njFe>j*T{i0s8D4=L>X^j0)({R5lMGVS#7(2C9@AxL&C-lZQx~czI7Iv+{%1 z2hEG>RzX4S8x3v#9sgGAnPzptM)g&LB}@%E>fy0vGSa(&q0ch|=ncKjNrK z`jA~jObJhrJ^ri|-)J^HUyeZXz~XkBp$VhcTEcTdc#a2EUOGVX?@mYx#Vy*!qO$Jv zQ4rgOJ~M*o-_Wptam=~krnmG*p^j!JAqoQ%+YsDFW7Cc9M%YPiBOrVcD^RY>m9Pd< zu}#9M?K{+;UIO!D9qOpq9yxUquQRmQNMo0pT`@$pVt=rMvyX)ph(-CCJLvUJy71DI zBk7oc7)-%ngdj~s@76Yse3L^gV0 z2==qfp&Q~L(+%RHP0n}+xH#k(hPRx(!AdBM$JCfJ5*C=K3ts>P?@@SZ_+{U2qFZb>4kZ{Go37{# zSQc+-dq*a-Vy4?taS&{Ht|MLRiS)Sn14JOONyXqPNnpq&2y~)6wEG0oNy>qvod$FF z`9o&?&6uZjhZ4_*5qWVrEfu(>_n2Xi2{@Gz9MZ8!YmjYvIMasE9yVQL10NBrTCczq zcTY1q^PF2l!Eraguf{+PtHV3=2A?Cu&NN&a8V(y;q(^_mFc6)%Yfn&X&~Pq zU1?qCj^LF(EQB1F`8NxNjyV%fde}dEa(Hx=r7$~ts2dzDwyi6ByBAIx$NllB4%K=O z$AHz1<2bTUb>(MCVPpK(E9wlLElo(aSd(Os)^Raum`d(g9Vd_+Bf&V;l=@mM=cC>) z)9b0enb)u_7V!!E_bl>u5nf&Rl|2r=2F3rHMdb7y9E}}F82^$Rf+P8%dKnOeKh1vs zhH^P*4Ydr^$)$h@4KVzxrHyy#cKmWEa9P5DJ|- zG;!Qi35Tp7XNj60=$!S6U#!(${6hyh7d4q=pF{`0t|N^|L^d8pD{O9@tF~W;#Je*P z&ah%W!KOIN;SyAEhAeTafJ4uEL`(RtnovM+cb(O#>xQnk?dzAjG^~4$dFn^<@-Na3 z395;wBnS{t*H;Jef2eE!2}u5Ns{AHj>WYZDgQJt8v%x?9{MXqJsGP|l%OiZqQ1aB! z%E=*Ig`(!tHh>}4_z5IMpg{49UvD*Pp9!pxt_gdAW%sIf3k6CTycOT1McPl=_#0?8 zVjz8Hj*Vy9c5-krd-{BQ{6Xy|P$6LJvMuX$* zA+@I_66_ET5l2&gk9n4$1M3LN8(yEViRx&mtd#LD}AqEs?RW=xKC(OCWH;~>(X6h!uDxXIPH06xh z*`F4cVlbDP`A)-fzf>MuScYsmq&1LUMGaQ3bRm6i7OsJ|%uhTDT zlvZA1M}nz*SalJWNT|`dBm1$xlaA>CCiQ zK`xD-RuEn>-`Z?M{1%@wewf#8?F|(@1e0+T4>nmlSRrNK5f)BJ2H*$q(H>zGD0>eL zQ!tl_Wk)k*e6v^m*{~A;@6+JGeWU-q9>?+L_#UNT%G?4&BnOgvm9@o7l?ov~XL+et zbGT)|G7)KAeqb=wHSPk+J1bdg7N3$vp(ekjI1D9V$G5Cj!=R2w=3*4!z*J-r-cyeb zd(i2KmX!|Lhey!snRw z?#$Gu%S^SQEKt&kep)up#j&9}e+3=JJBS(s>MH+|=R(`8xK{mmndWo_r`-w1#SeRD&YtAJ#GiVI*TkQZ}&aq<+bU2+coU3!jCI6E+Ad_xFW*ghnZ$q zAoF*i&3n1j#?B8x;kjSJD${1jdRB;)R*)Ao!9bd|C7{;iqDo|T&>KSh6*hCD!rwv= zyK#F@2+cv3=|S1Kef(E6Niv8kyLVLX&e=U;{0x{$tDfShqkjUME>f8d(5nzSkY6@! z^-0>DM)wa&%m#UF1F?zR`8Y3X#tA!*7Q$P3lZJ%*KNlrk_uaPkxw~ zxZ1qlE;Zo;nb@!SMazSjM>;34ROOoygo%SF);LL>rRonWwR>bmSd1XD^~sGSu$Gg# zFZ`|yKU0%!v07dz^v(tY%;So(e`o{ZYTX`hm;@b0%8|H>VW`*cr8R%3n|ehw2`(9B+V72`>SY}9^8oh$En80mZK9T4abVG*to;E z1_S6bgDOW?!Oy1LwYy=w3q~KKdbNtyH#d24PFjX)KYMY93{3-mPP-H>@M-_>N~DDu zENh~reh?JBAK=TFN-SfDfT^=+{w4ea2KNWXq2Y<;?(gf(FgVp8Zp-oEjKzB%2Iqj;48GmY3h=bcdYJ}~&4tS`Q1sb=^emaW$IC$|R+r-8V- zf0$gGE(CS_n4s>oicVk)MfvVg#I>iDvf~Ov8bk}sSxluG!6#^Z_zhB&U^`eIi1@j( z^CK$z^stBHtaDDHxn+R;3u+>Lil^}fj?7eaGB z&5nl^STqcaBxI@v>%zG|j))G(rVa4aY=B@^2{TFkW~YP!8!9TG#(-nOf^^X-%m9{Z zCC?iC`G-^RcBSCuk=Z`(FaUUe?hf3{0C>>$?Vs z`2Uud9M+T&KB6o4o9kvdi^Q=Bw!asPdxbe#W-Oaa#_NP(qpyF@bVxv5D5))srkU#m zj_KA+#7sqDn*Ipf!F5Byco4HOSd!Ui$l94|IbW%Ny(s1>f4|Mv^#NfB31N~kya9!k zWCGL-$0ZQztBate^fd>R!hXY_N9ZjYp3V~4_V z#eB)Kjr8yW=+oG)BuNdZG?jaZlw+l_ma8aET(s+-x+=F-t#Qoiuu1i`^x8Sj>b^U} zs^z<()YMFP7CmjUC@M=&lA5W7t&cxTlzJAts*%PBDAPuqcV5o7HEnqjif_7xGt)F% zGx2b4w{@!tE)$p=l3&?Bf#`+!-RLOleeRk3 z7#pF|w@6_sBmn1nECqdunmG^}pr5(ZJQVvAt$6p3H(16~;vO>?sTE`Y+mq5YP&PBo zvq!7#W$Gewy`;%6o^!Dtjz~x)T}Bdk*BS#=EY=ODD&B=V6TD2z^hj1m5^d6s)D*wk zu$z~D7QuZ2b?5`p)E8e2_L38v3WE{V`bVk;6fl#o2`) z99JsWhh?$oVRn@$S#)uK&8DL8>An0&S<%V8hnGD7Z^;Y(%6;^9!7kDQ5bjR_V+~wp zfx4m3z6CWmmZ<8gDGUyg3>t8wgJ5NkkiEm^(sedCicP^&3D%}6LtIUq>mXCAt{9eF zNXL$kGcoUTf_Lhm`t;hD-SE)m=iBnxRU(NyL}f6~1uH)`K!hmYZjLI%H}AmEF5RZt z06$wn63GHnApHXZZJ}s^s)j9(BM6e*7IBK6Bq(!)d~zR#rbxK9NVIlgquoMq z=eGZ9NR!SEqP6=9UQg#@!rtbbSBUM#ynF);zKX+|!Zm}*{H z+j=d?aZ2!?@EL7C~%B?6ouCKLnO$uWn;Y6Xz zX8dSwj732u(o*U3F$F=7xwxm>E-B+SVZH;O-4XPuPkLSt_?S0)lb7EEg)Mglk0#eS z9@jl(OnH4juMxY+*r03VDfPx_IM!Lmc(5hOI;`?d37f>jPP$?9jQQIQU@i4vuG6MagEoJrQ=RD7xt@8E;c zeGV*+Pt+t$@pt!|McETOE$9k=_C!70uhwRS9X#b%ZK z%q(TIUXSS^F0`4Cx?Rk07C6wI4!UVPeI~-fxY6`YH$kABdOuiRtl73MqG|~AzZ@iL&^s?24iS;RK_pdlWkhcF z@Wv-Om(Aealfg)D^adlXh9Nvf~Uf@y;g3Y)i(YP zEXDnb1V}1pJT5ZWyw=1i+0fni9yINurD=EqH^ciOwLUGi)C%Da)tyt=zq2P7pV5-G zR7!oq28-Fgn5pW|nlu^b!S1Z#r7!Wtr{5J5PQ>pd+2P7RSD?>(U7-|Y z7ZQ5lhYIl_IF<9?T9^IPK<(Hp;l5bl5tF9>X-zG14_7PfsA>6<$~A338iYRT{a@r_ zuXBaT=`T5x3=s&3=RYx6NgG>No4?5KFBVjE(swfcivcIpPQFx5l+O;fiGsOrl5teR z_Cm+;PW}O0Dwe_(4Z@XZ)O0W-v2X><&L*<~*q3dg;bQW3g7)a#3KiQP>+qj|qo*Hk z?57>f2?f@`=Fj^nkDKeRkN2d$Z@2eNKpHo}ksj-$`QKb6n?*$^*%Fb3_Kbf1(*W9K>{L$mud2WHJ=j0^=g30Xhg8$#g^?36`p1fm;;1@0Lrx+8t`?vN0ZorM zSW?rhjCE8$C|@p^sXdx z|NOHHg+fL;HIlqyLp~SSdIF`TnSHehNCU9t89yr@)FY<~hu+X`tjg(aSVae$wDG*C zq$nY(Y494R)hD!i1|IIyP*&PD_c2FPgeY)&mX1qujB1VHPG9`yFQpLFVQ0>EKS@Bp zAfP5`C(sWGLI?AC{XEjLKR4FVNw(4+9b?kba95ukgR1H?w<8F7)G+6&(zUhIE5Ef% z=fFkL3QKA~M@h{nzjRq!Y_t!%U66#L8!(2-GgFxkD1=JRRqk=n%G(yHKn%^&$dW>; zSjAcjETMz1%205se$iH_)ZCpfg_LwvnsZQAUCS#^FExp8O4CrJb6>JquNV@qPq~3A zZ<6dOU#6|8+fcgiA#~MDmcpIEaUO02L5#T$HV0$EMD94HT_eXLZ2Zi&(! z&5E>%&|FZ`)CN10tM%tLSPD*~r#--K(H-CZqIOb99_;m|D5wdgJ<1iOJz@h2Zkq?} z%8_KXb&hf=2Wza(Wgc;3v3TN*;HTU*q2?#z&tLn_U0Nt!y>Oo>+2T)He6%XuP;fgn z-G!#h$Y2`9>Jtf}hbVrm6D70|ERzLAU>3zoWhJmjWfgM^))T+2u$~5>HF9jQDkrXR z=IzX36)V75PrFjkQ%TO+iqKGCQ-DDXbaE;C#}!-CoWQx&v*vHfyI>$HNRbpvm<`O( zlx9NBWD6_e&J%Ous4yp~s6)Ghni!I6)0W;9(9$y1wWu`$gs<$9Mcf$L*piP zPR0Av*2%ul`W;?-1_-5Zy0~}?`e@Y5A&0H!^ApyVTT}BiOm4GeFo$_oPlDEyeGBbh z1h3q&Dx~GmUS|3@4V36&$2uO8!Yp&^pD7J5&TN{?xphf*-js1fP?B|`>p_K>lh{ij zP(?H%e}AIP?_i^f&Li=FDSQ`2_NWxL+BB=nQr=$ zHojMlXNGauvvwPU>ZLq!`bX-5F4jBJ&So{kE5+ms9UEYD{66!|k~3vsP+mE}x!>%P za98bAU0!h0&ka4EoiDvBM#CP#dRNdXJcb*(%=<(g+M@<)DZ!@v1V>;54En?igcHR2 zhubQMq}VSOK)onqHfczM7YA@s=9*ow;k;8)&?J3@0JiGcP! zP#00KZ1t)GyZeRJ=f0^gc+58lc4Qh*S7RqPIC6GugG1gXe$LIQMRCo8cHf^qXgAa2 z`}t>u2Cq1CbSEpLr~E=c7~=Qkc9-vLE%(v9N*&HF`(d~(0`iukl5aQ9u4rUvc8%m) zr2GwZN4!s;{SB87lJB;veebPmqE}tSpT>+`t?<457Q9iV$th%i__Z1kOMAswFldD6 ztbOvO337S5o#ZZgN2G99_AVqPv!?Gmt3pzgD+Hp3QPQ`9qJ(g=kjvD+fUSS3upJn! zqoG7acIKEFRX~S}3|{EWT$kdz#zrDlJU(rPkxjws_iyLKU8+v|*oS_W*-guAb&Pj1 z35Z`3z<&Jb@2Mwz=KXucNYdY#SNO$tcVFr9KdKm|%^e-TXzs6M`PBper%ajkrIyUe zp$vVxVs9*>Vp4_1NC~Zg)WOCPmOxI1V34QlG4!aSFOH{QqSVq1^1)- z0P!Z?tT&E-ll(pwf0?=F=yOzik=@nh1Clxr9}Vij89z)ePDSCYAqw?lVI?v?+&*zH z)p$CScFI8rrwId~`}9YWPFu0cW1Sf@vRELs&cbntRU6QfPK-SO*mqu|u~}8AJ!Q$z znzu}50O=YbjwKCuSVBs6&CZR#0FTu)3{}qJJYX(>QPr4$RqWiwX3NT~;>cLn*_&1H zaKpIW)JVJ>b{uo2oq>oQt3y=zJjb%fU@wLqM{SyaC6x2snMx-}ivfU<1- znu1Lh;i$3Tf$Kh5Uk))G!D1UhE8pvx&nO~w^fG)BC&L!_hQk%^p`Kp@F{cz>80W&T ziOK=Sq3fdRu*V0=S53rcIfWFazI}Twj63CG(jOB;$*b`*#B9uEnBM`hDk*EwSRdwP8?5T?xGUKs=5N83XsR*)a4|ijz|c{4tIU+4j^A5C<#5 z*$c_d=5ml~%pGxw#?*q9N7aRwPux5EyqHVkdJO=5J>84!X6P>DS8PTTz>7C#FO?k#edkntG+fJk8ZMn?pmJSO@`x-QHq;7^h6GEXLXo1TCNhH z8ZDH{*NLAjo3WM`xeb=X{((uv3H(8&r8fJJg_uSs_%hOH%JDD?hu*2NvWGYD+j)&` zz#_1%O1wF^o5ryt?O0n;`lHbzp0wQ?rcbW(F1+h7_EZZ9{>rePvLAPVZ_R|n@;b$;UchU=0j<6k8G9QuQf@76oiE*4 zXOLQ&n3$NR#p4<5NJMVC*S);5x2)eRbaAM%VxWu9ohlT;pGEk7;002enCbQ>2r-us z3#bpXP9g|mE`65VrN`+3mC)M(eMj~~eOf)do<@l+fMiTR)XO}422*1SL{wyY(%oMpBgJagtiDf zz>O6(m;};>Hi=t8o{DVC@YigqS(Qh+ix3Rwa9aliH}a}IlOCW1@?%h_bRbq-W{KHF z%Vo?-j@{Xi@=~Lz5uZP27==UGE15|g^0gzD|3x)SCEXrx`*MP^FDLl%pOi~~Il;dc z^hrwp9sYeT7iZ)-ajKy@{a`kr0-5*_!XfBpXwEcFGJ;%kV$0Nx;apKrur zJN2J~CAv{Zjj%FolyurtW8RaFmpn&zKJWL>(0;;+q(%(Hx!GMW4AcfP0YJ*Vz!F4g z!ZhMyj$BdXL@MlF%KeInmPCt~9&A!;cRw)W!Hi@0DY(GD_f?jeV{=s=cJ6e}JktJw zQORnxxj3mBxfrH=x{`_^Z1ddDh}L#V7i}$njUFRVwOX?qOTKjfPMBO4y(WiU<)epb zvB9L=%jW#*SL|Nd_G?E*_h1^M-$PG6Pc_&QqF0O-FIOpa4)PAEPsyvB)GKasmBoEt z?_Q2~QCYGH+hW31x-B=@5_AN870vY#KB~3a*&{I=f);3Kv7q4Q7s)0)gVYx2#Iz9g(F2;=+Iy4 z6KI^8GJ6D@%tpS^8boU}zpi=+(5GfIR)35PzrbuXeL1Y1N%JK7PG|^2k3qIqHfX;G zQ}~JZ-UWx|60P5?d1e;AHx!_;#PG%d=^X(AR%i`l0jSpYOpXoKFW~7ip7|xvN;2^? zsYC9fanpO7rO=V7+KXqVc;Q5z%Bj})xHVrgoR04sA2 zl~DAwv=!(()DvH*=lyhIlU^hBkA0$e*7&fJpB0|oB7)rqGK#5##2T`@_I^|O2x4GO z;xh6ROcV<9>?e0)MI(y++$-ksV;G;Xe`lh76T#Htuia+(UrIXrf9?

L(tZ$0BqX1>24?V$S+&kLZ`AodQ4_)P#Q3*4xg8}lMV-FLwC*cN$< zt65Rf%7z41u^i=P*qO8>JqXPrinQFapR7qHAtp~&RZ85$>ob|Js;GS^y;S{XnGiBc zGa4IGvDl?x%gY`vNhv8wgZnP#UYI-w*^4YCZnxkF85@ldepk$&$#3EAhrJY0U)lR{F6sM3SONV^+$;Zx8BD&Eku3K zKNLZyBni3)pGzU0;n(X@1fX8wYGKYMpLmCu{N5-}epPDxClPFK#A@02WM3!myN%bkF z|GJ4GZ}3sL{3{qXemy+#Uk{4>Kf8v11;f8I&c76+B&AQ8udd<8gU7+BeWC`akUU~U zgXoxie>MS@rBoyY8O8Tc&8id!w+_ooxcr!1?#rc$-|SBBtH6S?)1e#P#S?jFZ8u-Bs&k`yLqW|{j+%c#A4AQ>+tj$Y z^CZajspu$F%73E68Lw5q7IVREED9r1Ijsg#@DzH>wKseye>hjsk^{n0g?3+gs@7`i zHx+-!sjLx^fS;fY!ERBU+Q zVJ!e0hJH%P)z!y%1^ZyG0>PN@5W~SV%f>}c?$H8r;Sy-ui>aruVTY=bHe}$e zi&Q4&XK!qT7-XjCrDaufT@>ieQ&4G(SShUob0Q>Gznep9fR783jGuUynAqc6$pYX; z7*O@@JW>O6lKIk0G00xsm|=*UVTQBB`u1f=6wGAj%nHK_;Aqmfa!eAykDmi-@u%6~ z;*c!pS1@V8r@IX9j&rW&d*}wpNs96O2Ute>%yt{yv>k!6zfT6pru{F1M3P z2WN1JDYqoTB#(`kE{H676QOoX`cnqHl1Yaru)>8Ky~VU{)r#{&s86Vz5X)v15ULHA zAZDb{99+s~qI6;-dQ5DBjHJP@GYTwn;Dv&9kE<0R!d z8tf1oq$kO`_sV(NHOSbMwr=To4r^X$`sBW4$gWUov|WY?xccQJN}1DOL|GEaD_!@& z15p?Pj+>7d`@LvNIu9*^hPN)pwcv|akvYYq)ks%`G>!+!pW{-iXPZsRp8 z35LR;DhseQKWYSD`%gO&k$Dj6_6q#vjWA}rZcWtQr=Xn*)kJ9kacA=esi*I<)1>w^ zO_+E>QvjP)qiSZg9M|GNeLtO2D7xT6vsj`88sd!94j^AqxFLi}@w9!Y*?nwWARE0P znuI_7A-saQ+%?MFA$gttMV-NAR^#tjl_e{R$N8t2NbOlX373>e7Ox=l=;y#;M7asp zRCz*CLnrm$esvSb5{T<$6CjY zmZ(i{Rs_<#pWW>(HPaaYj`%YqBra=Ey3R21O7vUbzOkJJO?V`4-D*u4$Me0Bx$K(lYo`JO}gnC zx`V}a7m-hLU9Xvb@K2ymioF)vj12<*^oAqRuG_4u%(ah?+go%$kOpfb`T96P+L$4> zQ#S+sA%VbH&mD1k5Ak7^^dZoC>`1L%i>ZXmooA!%GI)b+$D&ziKrb)a=-ds9xk#~& z7)3iem6I|r5+ZrTRe_W861x8JpD`DDIYZNm{$baw+$)X^Jtjnl0xlBgdnNY}x%5za zkQ8E6T<^$sKBPtL4(1zi_Rd(tVth*3Xs!ulflX+70?gb&jRTnI8l+*Aj9{|d%qLZ+ z>~V9Z;)`8-lds*Zgs~z1?Fg?Po7|FDl(Ce<*c^2=lFQ~ahwh6rqSjtM5+$GT>3WZW zj;u~w9xwAhOc<kF}~`CJ68 z?(S5vNJa;kriPlim33{N5`C{9?NWhzsna_~^|K2k4xz1`xcui*LXL-1#Y}Hi9`Oo!zQ>x-kgAX4LrPz63uZ+?uG*84@PKq-KgQlMNRwz=6Yes) zY}>YN+qP}nwr$(CZQFjUOI=-6J$2^XGvC~EZ+vrqWaOXB$k?%Suf5k=4>AveC1aJ! ziaW4IS%F$_Babi)kA8Y&u4F7E%99OPtm=vzw$$ zEz#9rvn`Iot_z-r3MtV>k)YvErZ<^Oa${`2>MYYODSr6?QZu+be-~MBjwPGdMvGd!b!elsdi4% z`37W*8+OGulab8YM?`KjJ8e+jM(tqLKSS@=jimq3)Ea2EB%88L8CaM+aG7;27b?5` z4zuUWBr)f)k2o&xg{iZ$IQkJ+SK>lpq4GEacu~eOW4yNFLU!Kgc{w4&D$4ecm0f}~ zTTzquRW@`f0}|IILl`!1P+;69g^upiPA6F{)U8)muWHzexRenBU$E^9X-uIY2%&1w z_=#5*(nmxJ9zF%styBwivi)?#KMG96-H@hD-H_&EZiRNsfk7mjBq{L%!E;Sqn!mVX*}kXhwH6eh;b42eD!*~upVG@ z#smUqz$ICm!Y8wY53gJeS|Iuard0=;k5i5Z_hSIs6tr)R4n*r*rE`>38Pw&lkv{_r!jNN=;#?WbMj|l>cU(9trCq; z%nN~r^y7!kH^GPOf3R}?dDhO=v^3BeP5hF|%4GNQYBSwz;x({21i4OQY->1G=KFyu z&6d`f2tT9Yl_Z8YACZaJ#v#-(gcyeqXMhYGXb=t>)M@fFa8tHp2x;ODX=Ap@a5I=U z0G80^$N0G4=U(>W%mrrThl0DjyQ-_I>+1Tdd_AuB3qpYAqY54upwa3}owa|x5iQ^1 zEf|iTZxKNGRpI>34EwkIQ2zHDEZ=(J@lRaOH>F|2Z%V_t56Km$PUYu^xA5#5Uj4I4RGqHD56xT%H{+P8Ag>e_3pN$4m8n>i%OyJFPNWaEnJ4McUZPa1QmOh?t8~n& z&RulPCors8wUaqMHECG=IhB(-tU2XvHP6#NrLVyKG%Ee*mQ5Ps%wW?mcnriTVRc4J`2YVM>$ixSF2Xi+Wn(RUZnV?mJ?GRdw%lhZ+t&3s7g!~g{%m&i<6 z5{ib-<==DYG93I(yhyv4jp*y3#*WNuDUf6`vTM%c&hiayf(%=x@4$kJ!W4MtYcE#1 zHM?3xw63;L%x3drtd?jot!8u3qeqctceX3m;tWetK+>~q7Be$h>n6riK(5@ujLgRS zvOym)k+VAtyV^mF)$29Y`nw&ijdg~jYpkx%*^ z8dz`C*g=I?;clyi5|!27e2AuSa$&%UyR(J3W!A=ZgHF9OuKA34I-1U~pyD!KuRkjA zbkN!?MfQOeN>DUPBxoy5IX}@vw`EEB->q!)8fRl_mqUVuRu|C@KD-;yl=yKc=ZT0% zB$fMwcC|HE*0f8+PVlWHi>M`zfsA(NQFET?LrM^pPcw`cK+Mo0%8*x8@65=CS_^$cG{GZQ#xv($7J z??R$P)nPLodI;P!IC3eEYEHh7TV@opr#*)6A-;EU2XuogHvC;;k1aI8asq7ovoP!* z?x%UoPrZjj<&&aWpsbr>J$Er-7!E(BmOyEv!-mbGQGeJm-U2J>74>o5x`1l;)+P&~ z>}f^=Rx(ZQ2bm+YE0u=ZYrAV@apyt=v1wb?R@`i_g64YyAwcOUl=C!i>=Lzb$`tjv zOO-P#A+)t-JbbotGMT}arNhJmmGl-lyUpMn=2UacVZxmiG!s!6H39@~&uVokS zG=5qWhfW-WOI9g4!R$n7!|ViL!|v3G?GN6HR0Pt_L5*>D#FEj5wM1DScz4Jv@Sxnl zB@MPPmdI{(2D?;*wd>3#tjAirmUnQoZrVv`xM3hARuJksF(Q)wd4P$88fGYOT1p6U z`AHSN!`St}}UMBT9o7i|G`r$ zrB=s$qV3d6$W9@?L!pl0lf%)xs%1ko^=QY$ty-57=55PvP(^6E7cc zGJ*>m2=;fOj?F~yBf@K@9qwX0hA803Xw+b0m}+#a(>RyR8}*Y<4b+kpp|OS+!whP( zH`v{%s>jsQI9rd$*vm)EkwOm#W_-rLTHcZRek)>AtF+~<(did)*oR1|&~1|e36d-d zgtm5cv1O0oqgWC%Et@P4Vhm}Ndl(Y#C^MD03g#PH-TFy+7!Osv1z^UWS9@%JhswEq~6kSr2DITo59+; ze=ZC}i2Q?CJ~Iyu?vn|=9iKV>4j8KbxhE4&!@SQ^dVa-gK@YfS9xT(0kpW*EDjYUkoj! zE49{7H&E}k%5(>sM4uGY)Q*&3>{aitqdNnRJkbOmD5Mp5rv-hxzOn80QsG=HJ_atI-EaP69cacR)Uvh{G5dTpYG7d zbtmRMq@Sexey)||UpnZ?;g_KMZq4IDCy5}@u!5&B^-=6yyY{}e4Hh3ee!ZWtL*s?G zxG(A!<9o!CL+q?u_utltPMk+hn?N2@?}xU0KlYg?Jco{Yf@|mSGC<(Zj^yHCvhmyx z?OxOYoxbptDK()tsJ42VzXdINAMWL$0Gcw?G(g8TMB)Khw_|v9`_ql#pRd2i*?CZl z7k1b!jQB=9-V@h%;Cnl7EKi;Y^&NhU0mWEcj8B|3L30Ku#-9389Q+(Yet0r$F=+3p z6AKOMAIi|OHyzlHZtOm73}|ntKtFaXF2Fy|M!gOh^L4^62kGUoWS1i{9gsds_GWBc zLw|TaLP64z3z9?=R2|T6Xh2W4_F*$cq>MtXMOy&=IPIJ`;!Tw?PqvI2b*U1)25^<2 zU_ZPoxg_V0tngA0J+mm?3;OYw{i2Zb4x}NedZug!>EoN3DC{1i)Z{Z4m*(y{ov2%- zk(w>+scOO}MN!exSc`TN)!B=NUX`zThWO~M*ohqq;J2hx9h9}|s#?@eR!=F{QTrq~ zTcY|>azkCe$|Q0XFUdpFT=lTcyW##i;-e{}ORB4D?t@SfqGo_cS z->?^rh$<&n9DL!CF+h?LMZRi)qju!meugvxX*&jfD!^1XB3?E?HnwHP8$;uX{Rvp# zh|)hM>XDv$ZGg=$1{+_bA~u-vXqlw6NH=nkpyWE0u}LQjF-3NhATL@9rRxMnpO%f7 z)EhZf{PF|mKIMFxnC?*78(}{Y)}iztV12}_OXffJ;ta!fcFIVjdchyHxH=t%ci`Xd zX2AUB?%?poD6Zv*&BA!6c5S#|xn~DK01#XvjT!w!;&`lDXSJT4_j$}!qSPrb37vc{ z9^NfC%QvPu@vlxaZ;mIbn-VHA6miwi8qJ~V;pTZkKqqOii<1Cs}0i?uUIss;hM4dKq^1O35y?Yp=l4i zf{M!@QHH~rJ&X~8uATV><23zZUbs-J^3}$IvV_ANLS08>k`Td7aU_S1sLsfi*C-m1 z-e#S%UGs4E!;CeBT@9}aaI)qR-6NU@kvS#0r`g&UWg?fC7|b^_HyCE!8}nyh^~o@< zpm7PDFs9yxp+byMS(JWm$NeL?DNrMCNE!I^ko-*csB+dsf4GAq{=6sfyf4wb>?v1v zmb`F*bN1KUx-`ra1+TJ37bXNP%`-Fd`vVQFTwWpX@;s(%nDQa#oWhgk#mYlY*!d>( zE&!|ySF!mIyfING+#%RDY3IBH_fW$}6~1%!G`suHub1kP@&DoAd5~7J55;5_noPI6eLf{t;@9Kf<{aO0`1WNKd?<)C-|?C?)3s z>wEq@8=I$Wc~Mt$o;g++5qR+(6wt9GI~pyrDJ%c?gPZe)owvy^J2S=+M^ z&WhIE`g;;J^xQLVeCtf7b%Dg#Z2gq9hp_%g)-%_`y*zb; zn9`f`mUPN-Ts&fFo(aNTsXPA|J!TJ{0hZp0^;MYHLOcD=r_~~^ymS8KLCSeU3;^QzJNqS z5{5rEAv#l(X?bvwxpU;2%pQftF`YFgrD1jt2^~Mt^~G>T*}A$yZc@(k9orlCGv&|1 zWWvVgiJsCAtamuAYT~nzs?TQFt<1LSEx!@e0~@yd6$b5!Zm(FpBl;(Cn>2vF?k zOm#TTjFwd2D-CyA!mqR^?#Uwm{NBemP>(pHmM}9;;8`c&+_o3#E5m)JzfwN?(f-a4 zyd%xZc^oQx3XT?vcCqCX&Qrk~nu;fxs@JUoyVoi5fqpi&bUhQ2y!Ok2pzsFR(M(|U zw3E+kH_zmTRQ9dUMZWRE%Zakiwc+lgv7Z%|YO9YxAy`y28`Aw;WU6HXBgU7fl@dnt z-fFBV)}H-gqP!1;V@Je$WcbYre|dRdp{xt!7sL3Eoa%IA`5CAA%;Wq8PktwPdULo! z8!sB}Qt8#jH9Sh}QiUtEPZ6H0b*7qEKGJ%ITZ|vH)5Q^2m<7o3#Z>AKc%z7_u`rXA zqrCy{-{8;9>dfllLu$^M5L z-hXs))h*qz%~ActwkIA(qOVBZl2v4lwbM>9l70Y`+T*elINFqt#>OaVWoja8RMsep z6Or3f=oBnA3vDbn*+HNZP?8LsH2MY)x%c13@(XfuGR}R?Nu<|07{$+Lc3$Uv^I!MQ z>6qWgd-=aG2Y^24g4{Bw9ueOR)(9h`scImD=86dD+MnSN4$6 z^U*o_mE-6Rk~Dp!ANp#5RE9n*LG(Vg`1)g6!(XtDzsov$Dvz|Gv1WU68J$CkshQhS zCrc|cdkW~UK}5NeaWj^F4MSgFM+@fJd{|LLM)}_O<{rj z+?*Lm?owq?IzC%U%9EBga~h-cJbIu=#C}XuWN>OLrc%M@Gu~kFEYUi4EC6l#PR2JS zQUkGKrrS#6H7}2l0F@S11DP`@pih0WRkRJl#F;u{c&ZC{^$Z+_*lB)r)-bPgRFE;* zl)@hK4`tEP=P=il02x7-C7p%l=B`vkYjw?YhdJU9!P!jcmY$OtC^12w?vy3<<=tlY zUwHJ_0lgWN9vf>1%WACBD{UT)1qHQSE2%z|JHvP{#INr13jM}oYv_5#xsnv9`)UAO zuwgyV4YZ;O)eSc3(mka6=aRohi!HH@I#xq7kng?Acdg7S4vDJb6cI5fw?2z%3yR+| zU5v@Hm}vy;${cBp&@D=HQ9j7NcFaOYL zj-wV=eYF{|XTkFNM2uz&T8uH~;)^Zo!=KP)EVyH6s9l1~4m}N%XzPpduPg|h-&lL` zAXspR0YMOKd2yO)eMFFJ4?sQ&!`dF&!|niH*!^*Ml##o0M(0*uK9&yzekFi$+mP9s z>W9d%Jb)PtVi&-Ha!o~Iyh@KRuKpQ@)I~L*d`{O8!kRObjO7=n+Gp36fe!66neh+7 zW*l^0tTKjLLzr`x4`_8&on?mjW-PzheTNox8Hg7Nt@*SbE-%kP2hWYmHu#Fn@Q^J(SsPUz*|EgOoZ6byg3ew88UGdZ>9B2Tq=jF72ZaR=4u%1A6Vm{O#?@dD!(#tmR;eP(Fu z{$0O%=Vmua7=Gjr8nY%>ul?w=FJ76O2js&17W_iq2*tb!i{pt#`qZB#im9Rl>?t?0c zicIC}et_4d+CpVPx)i4~$u6N-QX3H77ez z?ZdvXifFk|*F8~L(W$OWM~r`pSk5}#F?j_5u$Obu9lDWIknO^AGu+Blk7!9Sb;NjS zncZA?qtASdNtzQ>z7N871IsPAk^CC?iIL}+{K|F@BuG2>qQ;_RUYV#>hHO(HUPpk@ z(bn~4|F_jiZi}Sad;_7`#4}EmD<1EiIxa48QjUuR?rC}^HRocq`OQPM@aHVKP9E#q zy%6bmHygCpIddPjE}q_DPC`VH_2m;Eey&ZH)E6xGeStOK7H)#+9y!%-Hm|QF6w#A( zIC0Yw%9j$s-#odxG~C*^MZ?M<+&WJ+@?B_QPUyTg9DJGtQN#NIC&-XddRsf3n^AL6 zT@P|H;PvN;ZpL0iv$bRb7|J{0o!Hq+S>_NrH4@coZtBJu#g8#CbR7|#?6uxi8d+$g z87apN>EciJZ`%Zv2**_uiET9Vk{pny&My;+WfGDw4EVL#B!Wiw&M|A8f1A@ z(yFQS6jfbH{b8Z-S7D2?Ixl`j0{+ZnpT=;KzVMLW{B$`N?Gw^Fl0H6lT61%T2AU**!sX0u?|I(yoy&Xveg7XBL&+>n6jd1##6d>TxE*Vj=8lWiG$4=u{1UbAa5QD>5_ z;Te^42v7K6Mmu4IWT6Rnm>oxrl~b<~^e3vbj-GCdHLIB_>59}Ya+~OF68NiH=?}2o zP(X7EN=quQn&)fK>M&kqF|<_*H`}c zk=+x)GU>{Af#vx&s?`UKUsz})g^Pc&?Ka@t5$n$bqf6{r1>#mWx6Ep>9|A}VmWRnowVo`OyCr^fHsf# zQjQ3Ttp7y#iQY8l`zEUW)(@gGQdt(~rkxlkefskT(t%@i8=|p1Y9Dc5bc+z#n$s13 zGJk|V0+&Ekh(F};PJzQKKo+FG@KV8a<$gmNSD;7rd_nRdc%?9)p!|B-@P~kxQG}~B zi|{0}@}zKC(rlFUYp*dO1RuvPC^DQOkX4<+EwvBAC{IZQdYxoq1Za!MW7%p7gGr=j zzWnAq%)^O2$eItftC#TTSArUyL$U54-O7e|)4_7%Q^2tZ^0-d&3J1}qCzR4dWX!)4 zzIEKjgnYgMus^>6uw4Jm8ga6>GBtMjpNRJ6CP~W=37~||gMo_p@GA@#-3)+cVYnU> zE5=Y4kzl+EbEh%dhQokB{gqNDqx%5*qBusWV%!iprn$S!;oN_6E3?0+umADVs4ako z?P+t?m?};gev9JXQ#Q&KBpzkHPde_CGu-y z<{}RRAx=xlv#mVi+Ibrgx~ujW$h{?zPfhz)Kp7kmYS&_|97b&H&1;J-mzrBWAvY} zh8-I8hl_RK2+nnf&}!W0P+>5?#?7>npshe<1~&l_xqKd0_>dl_^RMRq@-Myz&|TKZBj1=Q()) zF{dBjv5)h=&Z)Aevx}+i|7=R9rG^Di!sa)sZCl&ctX4&LScQ-kMncgO(9o6W6)yd< z@Rk!vkja*X_N3H=BavGoR0@u0<}m-7|2v!0+2h~S2Q&a=lTH91OJsvms2MT~ zY=c@LO5i`mLpBd(vh|)I&^A3TQLtr>w=zoyzTd=^f@TPu&+*2MtqE$Avf>l>}V|3-8Fp2hzo3y<)hr_|NO(&oSD z!vEjTWBxbKTiShVl-U{n*B3#)3a8$`{~Pk}J@elZ=>Pqp|MQ}jrGv7KrNcjW%TN_< zZz8kG{#}XoeWf7qY?D)L)8?Q-b@Na&>i=)(@uNo zr;cH98T3$Iau8Hn*@vXi{A@YehxDE2zX~o+RY`)6-X{8~hMpc#C`|8y> zU8Mnv5A0dNCf{Ims*|l-^ z(MRp{qoGohB34|ggDI*p!Aw|MFyJ|v+<+E3brfrI)|+l3W~CQLPbnF@G0)P~Ly!1TJLp}xh8uW`Q+RB-v`MRYZ9Gam3cM%{ zb4Cb*f)0deR~wtNb*8w-LlIF>kc7DAv>T0D(a3@l`k4TFnrO+g9XH7;nYOHxjc4lq zMmaW6qpgAgy)MckYMhl?>sq;-1E)-1llUneeA!ya9KM$)DaNGu57Z5aE>=VST$#vb zFo=uRHr$0M{-ha>h(D_boS4zId;3B|Tpqo|?B?Z@I?G(?&Iei+-{9L_A9=h=Qfn-U z1wIUnQe9!z%_j$F_{rf&`ZFSott09gY~qrf@g3O=Y>vzAnXCyL!@(BqWa)Zqt!#_k zfZHuwS52|&&)aK;CHq9V-t9qt0au{$#6c*R#e5n3rje0hic7c7m{kW$p(_`wB=Gw7 z4k`1Hi;Mc@yA7dp@r~?@rfw)TkjAW++|pkfOG}0N|2guek}j8Zen(!+@7?qt_7ndX zB=BG6WJ31#F3#Vk3=aQr8T)3`{=p9nBHlKzE0I@v`{vJ}h8pd6vby&VgFhzH|q;=aonunAXL6G2y(X^CtAhWr*jI zGjpY@raZDQkg*aMq}Ni6cRF z{oWv}5`nhSAv>usX}m^GHt`f(t8@zHc?K|y5Zi=4G*UG1Sza{$Dpj%X8 zzEXaKT5N6F5j4J|w#qlZP!zS7BT)9b+!ZSJdToqJts1c!)fwih4d31vfb{}W)EgcA zH2pZ^8_k$9+WD2n`6q5XbOy8>3pcYH9 z07eUB+p}YD@AH!}p!iKv><2QF-Y^&xx^PAc1F13A{nUeCDg&{hnix#FiO!fe(^&%Qcux!h znu*S!s$&nnkeotYsDthh1dq(iQrE|#f_=xVgfiiL&-5eAcC-> z5L0l|DVEM$#ulf{bj+Y~7iD)j<~O8CYM8GW)dQGq)!mck)FqoL^X zwNdZb3->hFrbHFm?hLvut-*uK?zXn3q1z|UX{RZ;-WiLoOjnle!xs+W0-8D)kjU#R z+S|A^HkRg$Ij%N4v~k`jyHffKaC~=wg=9)V5h=|kLQ@;^W!o2^K+xG&2n`XCd>OY5Ydi= zgHH=lgy++erK8&+YeTl7VNyVm9-GfONlSlVb3)V9NW5tT!cJ8d7X)!b-$fb!s76{t z@d=Vg-5K_sqHA@Zx-L_}wVnc@L@GL9_K~Zl(h5@AR#FAiKad8~KeWCo@mgXIQ#~u{ zgYFwNz}2b6Vu@CP0XoqJ+dm8px(5W5-Jpis97F`+KM)TuP*X8H@zwiVKDKGVp59pI zifNHZr|B+PG|7|Y<*tqap0CvG7tbR1R>jn70t1X`XJixiMVcHf%Ez*=xm1(CrTSDt z0cle!+{8*Ja&EOZ4@$qhBuKQ$U95Q%rc7tg$VRhk?3=pE&n+T3upZg^ZJc9~c2es% zh7>+|mrmA-p&v}|OtxqmHIBgUxL~^0+cpfkSK2mhh+4b=^F1Xgd2)}U*Yp+H?ls#z zrLxWg_hm}AfK2XYWr!rzW4g;+^^&bW%LmbtRai9f3PjU${r@n`JThy-cphbcwn)rq9{A$Ht`lmYKxOacy z6v2R(?gHhD5@&kB-Eg?4!hAoD7~(h>(R!s1c1Hx#s9vGPePUR|of32bS`J5U5w{F) z>0<^ktO2UHg<0{oxkdOQ;}coZDQph8p6ruj*_?uqURCMTac;>T#v+l1Tc~%^k-Vd@ zkc5y35jVNc49vZpZx;gG$h{%yslDI%Lqga1&&;mN{Ush1c7p>7e-(zp}6E7f-XmJb4nhk zb8zS+{IVbL$QVF8pf8}~kQ|dHJAEATmmnrb_wLG}-yHe>W|A&Y|;muy-d^t^<&)g5SJfaTH@P1%euONny=mxo+C z4N&w#biWY41r8k~468tvuYVh&XN&d#%QtIf9;iVXfWY)#j=l`&B~lqDT@28+Y!0E+MkfC}}H*#(WKKdJJq=O$vNYCb(ZG@p{fJgu;h z21oHQ(14?LeT>n5)s;uD@5&ohU!@wX8w*lB6i@GEH0pM>YTG+RAIWZD;4#F1&F%Jp zXZUml2sH0!lYJT?&sA!qwez6cXzJEd(1ZC~kT5kZSp7(@=H2$Azb_*W&6aA|9iwCL zdX7Q=42;@dspHDwYE?miGX#L^3xD&%BI&fN9^;`v4OjQXPBaBmOF1;#C)8XA(WFlH zycro;DS2?(G&6wkr6rqC>rqDv3nfGw3hmN_9Al>TgvmGsL8_hXx09};l9Ow@)F5@y z#VH5WigLDwZE4nh^7&@g{1FV^UZ%_LJ-s<{HN*2R$OPg@R~Z`c-ET*2}XB@9xvAjrK&hS=f|R8Gr9 zr|0TGOsI7RD+4+2{ZiwdVD@2zmg~g@^D--YL;6UYGSM8i$NbQr4!c7T9rg!8;TM0E zT#@?&S=t>GQm)*ua|?TLT2ktj#`|R<_*FAkOu2Pz$wEc%-=Y9V*$&dg+wIei3b*O8 z2|m$!jJG!J!ZGbbIa!(Af~oSyZV+~M1qGvelMzPNE_%5?c2>;MeeG2^N?JDKjFYCy z7SbPWH-$cWF9~fX%9~v99L!G(wi!PFp>rB!9xj7=Cv|F+7CsGNwY0Q_J%FID%C^CBZQfJ9K(HK%k31j~e#&?hQ zNuD6gRkVckU)v+53-fc} z7ZCzYN-5RG4H7;>>Hg?LU9&5_aua?A0)0dpew1#MMlu)LHe(M;OHjHIUl7|%%)YPo z0cBk;AOY00%Fe6heoN*$(b<)Cd#^8Iu;-2v@>cE-OB$icUF9EEoaC&q8z9}jMTT2I z8`9;jT%z0;dy4!8U;GW{i`)3!c6&oWY`J3669C!tM<5nQFFrFRglU8f)5Op$GtR-3 zn!+SPCw|04sv?%YZ(a7#L?vsdr7ss@WKAw&A*}-1S|9~cL%uA+E~>N6QklFE>8W|% zyX-qAUGTY1hQ-+um`2|&ji0cY*(qN!zp{YpDO-r>jPk*yuVSay<)cUt`t@&FPF_&$ zcHwu1(SQ`I-l8~vYyUxm@D1UEdFJ$f5Sw^HPH7b!9 zzYT3gKMF((N(v0#4f_jPfVZ=ApN^jQJe-X$`A?X+vWjLn_%31KXE*}5_}d8 zw_B1+a#6T1?>M{ronLbHIlEsMf93muJ7AH5h%;i99<~JX^;EAgEB1uHralD*!aJ@F zV2ruuFe9i2Q1C?^^kmVy921eb=tLDD43@-AgL^rQ3IO9%+vi_&R2^dpr}x{bCVPej z7G0-0o64uyWNtr*loIvslyo0%)KSDDKjfThe0hcqs)(C-MH1>bNGBDRTW~scy_{w} zp^aq8Qb!h9Lwielq%C1b8=?Z=&U)ST&PHbS)8Xzjh2DF?d{iAv)Eh)wsUnf>UtXN( zL7=$%YrZ#|^c{MYmhn!zV#t*(jdmYdCpwqpZ{v&L8KIuKn`@IIZfp!uo}c;7J57N` zAxyZ-uA4=Gzl~Ovycz%MW9ZL7N+nRo&1cfNn9(1H5eM;V_4Z_qVann7F>5f>%{rf= zPBZFaV@_Sobl?Fy&KXyzFDV*FIdhS5`Uc~S^Gjo)aiTHgn#<0C=9o-a-}@}xDor;D zZyZ|fvf;+=3MZd>SR1F^F`RJEZo+|MdyJYQAEauKu%WDol~ayrGU3zzbHKsnHKZ*z zFiwUkL@DZ>!*x05ql&EBq@_Vqv83&?@~q5?lVmffQZ+V-=qL+!u4Xs2Z2zdCQ3U7B&QR9_Iggy} z(om{Y9eU;IPe`+p1ifLx-XWh?wI)xU9ik+m#g&pGdB5Bi<`PR*?92lE0+TkRuXI)z z5LP!N2+tTc%cB6B1F-!fj#}>S!vnpgVU~3!*U1ej^)vjUH4s-bd^%B=ItQqDCGbrEzNQi(dJ`J}-U=2{7-d zK8k^Rlq2N#0G?9&1?HSle2vlkj^KWSBYTwx`2?9TU_DX#J+f+qLiZCqY1TXHFxXZqYMuD@RU$TgcnCC{_(vwZ-*uX)~go#%PK z@}2Km_5aQ~(<3cXeJN6|F8X_1@L%@xTzs}$_*E|a^_URF_qcF;Pfhoe?FTFwvjm1o z8onf@OY@jC2tVcMaZS;|T!Ks(wOgPpRzRnFS-^RZ4E!9dsnj9sFt609a|jJbb1Dt@ z<=Gal2jDEupxUSwWu6zp<<&RnAA;d&4gKVG0iu6g(DsST(4)z6R)zDpfaQ}v{5ARt zyhwvMtF%b-YazR5XLz+oh=mn;y-Mf2a8>7?2v8qX;19y?b>Z5laGHvzH;Nu9S`B8} zI)qN$GbXIQ1VL3lnof^6TS~rvPVg4V?Dl2Bb*K2z4E{5vy<(@@K_cN@U>R!>aUIRnb zL*)=787*cs#zb31zBC49x$`=fkQbMAef)L2$dR{)6BAz!t5U_B#1zZG`^neKSS22oJ#5B=gl%U=WeqL9REF2g zZnfCb0?quf?Ztj$VXvDSWoK`0L=Zxem2q}!XWLoT-kYMOx)!7fcgT35uC~0pySEme z`{wGWTkGr7>+Kb^n;W?BZH6ZP(9tQX%-7zF>vc2}LuWDI(9kh1G#7B99r4x6;_-V+k&c{nPUrR zAXJGRiMe~aup{0qzmLNjS_BC4cB#sXjckx{%_c&^xy{M61xEb>KW_AG5VFXUOjAG4 z^>Qlm9A#1N{4snY=(AmWzatb!ngqiqPbBZ7>Uhb3)dTkSGcL#&SH>iMO-IJBPua`u zo)LWZ>=NZLr758j{%(|uQuZ)pXq_4c!!>s|aDM9#`~1bzK3J1^^D#<2bNCccH7~-X}Ggi!pIIF>uFx%aPARGQsnC8ZQc8lrQ5o~smqOg>Ti^GNme94*w z)JZy{_{#$jxGQ&`M z!OMvZMHR>8*^>eS%o*6hJwn!l8VOOjZQJvh)@tnHVW&*GYPuxqXw}%M!(f-SQf`=L z5;=5w2;%82VMH6Xi&-K3W)o&K^+vJCepWZ-rW%+Dc6X3(){z$@4zjYxQ|}8UIojeC zYZpQ1dU{fy=oTr<4VX?$q)LP}IUmpiez^O&N3E_qPpchGTi5ZM6-2ScWlQq%V&R2Euz zO|Q0Hx>lY1Q1cW5xHv5!0OGU~PVEqSuy#fD72d#O`N!C;o=m+YioGu-wH2k6!t<~K zSr`E=W9)!g==~x9VV~-8{4ZN9{~-A9zJpRe%NGg$+MDuI-dH|b@BD)~>pPCGUNNzY zMDg||0@XGQgw`YCt5C&A{_+J}mvV9Wg{6V%2n#YSRN{AP#PY?1FF1#|vO_%e+#`|2*~wGAJaeRX6=IzFNeWhz6gJc8+(03Ph4y6ELAm=AkN7TOgMUEw*N{= z_)EIDQx5q22oUR+_b*tazu9+pX|n1c*IB-}{DqIj z-?E|ks{o3AGRNb;+iKcHkZvYJvFsW&83RAPs1Oh@IWy%l#5x2oUP6ZCtv+b|q>jsf zZ_9XO;V!>n`UxH1LvH8)L4?8raIvasEhkpQoJ`%!5rBs!0Tu(s_D{`4opB;57)pkX z4$A^8CsD3U5*!|bHIEqsn~{q+Ddj$ME@Gq4JXtgVz&7l{Ok!@?EA{B3P~NAqb9)4? zkQo30A^EbHfQ@87G5&EQTd`frrwL)&Yw?%-W@uy^Gn23%j?Y!Iea2xw<-f;esq zf%w5WN@E1}zyXtYv}}`U^B>W`>XPmdLj%4{P298|SisrE;7HvXX;A}Ffi8B#3Lr;1 zHt6zVb`8{#+e$*k?w8|O{Uh|&AG}|DG1PFo1i?Y*cQm$ZwtGcVgMwtBUDa{~L1KT-{jET4w60>{KZ27vXrHJ;fW{6| z=|Y4!&UX020wU1>1iRgB@Q#m~1^Z^9CG1LqDhYBrnx%IEdIty z!46iOoKlKs)c}newDG)rWUikD%j`)p z_w9Ph&e40=(2eBy;T!}*1p1f1SAUDP9iWy^u^Ubdj21Kn{46;GR+hwLO=4D11@c~V zI8x&(D({K~Df2E)Nx_yQvYfh4;MbMJ@Z}=Dt3_>iim~QZ*hZIlEs0mEb z_54+&*?wMD`2#vsQRN3KvoT>hWofI_Vf(^C1ff-Ike@h@saEf7g}<9T`W;HAne-Nd z>RR+&SP35w)xKn8^U$7))PsM!jKwYZ*RzEcG-OlTrX3}9a{q%#Un5E5W{{hp>w~;` zGky+3(vJvQyGwBo`tCpmo0mo((?nM8vf9aXrrY1Ve}~TuVkB(zeds^jEfI}xGBCM2 zL1|#tycSaWCurP+0MiActG3LCas@_@tao@(R1ANlwB$4K53egNE_;!&(%@Qo$>h`^1S_!hN6 z)vZtG$8fN!|BXBJ=SI>e(LAU(y(i*PHvgQ2llulxS8>qsimv7yL}0q_E5WiAz7)(f zC(ahFvG8&HN9+6^jGyLHM~$)7auppeWh_^zKk&C_MQ~8;N??OlyH~azgz5fe^>~7F zl3HnPN3z-kN)I$4@`CLCMQx3sG~V8hPS^}XDXZrQA>}mQPw%7&!sd(Pp^P=tgp-s^ zjl}1-KRPNWXgV_K^HkP__SR`S-|OF0bR-N5>I%ODj&1JUeAQ3$9i;B~$S6}*^tK?= z**%aCiH7y?xdY?{LgVP}S0HOh%0%LI$wRx;$T|~Y8R)Vdwa}kGWv8?SJVm^>r6+%I z#lj1aR94{@MP;t-scEYQWc#xFA30^}?|BeX*W#9OL;Q9#WqaaM546j5j29((^_8Nu z4uq}ESLr~r*O7E7$D{!k9W>`!SLoyA53i9QwRB{!pHe8um|aDE`Cg0O*{jmor)^t)3`>V>SWN-2VJcFmj^1?~tT=JrP`fVh*t zXHarp=8HEcR#vFe+1a%XXuK+)oFs`GDD}#Z+TJ}Ri`FvKO@ek2ayn}yaOi%(8p%2$ zpEu)v0Jym@f}U|-;}CbR=9{#<^z28PzkkTNvyKvJDZe+^VS2bES3N@Jq!-*}{oQlz z@8bgC_KnDnT4}d#&Cpr!%Yb?E!brx0!eVOw~;lLwUoz#Np%d$o%9scc3&zPm`%G((Le|6o1 zM(VhOw)!f84zG^)tZ1?Egv)d8cdNi+T${=5kV+j;Wf%2{3g@FHp^Gf*qO0q!u$=m9 zCaY`4mRqJ;FTH5`a$affE5dJrk~k`HTP_7nGTY@B9o9vvnbytaID;^b=Tzp7Q#DmD zC(XEN)Ktn39z5|G!wsVNnHi) z%^q94!lL|hF`IijA^9NR0F$@h7k5R^ljOW(;Td9grRN0Mb)l_l7##{2nPQ@?;VjXv zaLZG}yuf$r$<79rVPpXg?6iiieX|r#&`p#Con2i%S8*8F}(E) zI5E6c3tG*<;m~6>!&H!GJ6zEuhH7mkAzovdhLy;)q z{H2*8I^Pb}xC4s^6Y}6bJvMu=8>g&I)7!N!5QG$xseeU#CC?ZM-TbjsHwHgDGrsD= z{%f;@Sod+Ch66Ko2WF~;Ty)v>&x^aovCbCbD7>qF*!?BXmOV3(s|nxsb*Lx_2lpB7 zokUnzrk;P=T-&kUHO}td+Zdj!3n&NR?K~cRU zAXU!DCp?51{J4w^`cV#ye}(`SQhGQkkMu}O3M*BWt4UsC^jCFUy;wTINYmhD$AT;4 z?Xd{HaJjP`raZ39qAm;%beDbrLpbRf(mkKbANan7XsL>_pE2oo^$TgdidjRP!5-`% zv0d!|iKN$c0(T|L0C~XD0aS8t{*&#LnhE;1Kb<9&=c2B+9JeLvJr*AyyRh%@jHej=AetOMSlz^=!kxX>>B{2B1uIrQyfd8KjJ+DBy!h)~*(!|&L4^Q_07SQ~E zcemVP`{9CwFvPFu7pyVGCLhH?LhEVb2{7U+Z_>o25#+3<|8%1T^5dh}*4(kfJGry} zm%r#hU+__Z;;*4fMrX=Bkc@7|v^*B;HAl0((IBPPii%X9+u3DDF6%bI&6?Eu$8&aWVqHIM7mK6?Uvq$1|(-T|)IV<>e?!(rY zqkmO1MRaLeTR=)io(0GVtQT@s6rN%C6;nS3@eu;P#ry4q;^O@1ZKCJyp_Jo)Ty^QW z+vweTx_DLm{P-XSBj~Sl<%_b^$=}odJ!S2wAcxenmzFGX1t&Qp8Vxz2VT`uQsQYtdn&_0xVivIcxZ_hnrRtwq4cZSj1c-SG9 z7vHBCA=fd0O1<4*=lu$6pn~_pVKyL@ztw1swbZi0B?spLo56ZKu5;7ZeUml1Ws1?u zqMf1p{5myAzeX$lAi{jIUqo1g4!zWLMm9cfWcnw`k6*BR^?$2(&yW?>w;G$EmTA@a z6?y#K$C~ZT8+v{87n5Dm&H6Pb_EQ@V0IWmG9cG=O;(;5aMWWrIPzz4Q`mhK;qQp~a z+BbQrEQ+w{SeiuG-~Po5f=^EvlouB@_|4xQXH@A~KgpFHrwu%dwuCR)=B&C(y6J4J zvoGk9;lLs9%iA-IJGU#RgnZZR+@{5lYl8(e1h6&>Vc_mvg0d@);X zji4T|n#lB!>pfL|8tQYkw?U2bD`W{na&;*|znjmalA&f;*U++_aBYerq;&C8Kw7mI z7tsG*?7*5j&dU)Lje;^{D_h`%(dK|pB*A*1(Jj)w^mZ9HB|vGLkF1GEFhu&rH=r=8 zMxO42e{Si6$m+Zj`_mXb&w5Q(i|Yxyg?juUrY}78uo@~3v84|8dfgbPd0iQJRdMj< zncCNGdMEcsxu#o#B5+XD{tsg*;j-eF8`mp~K8O1J!Z0+>0=7O=4M}E?)H)ENE;P*F z$Ox?ril_^p0g7xhDUf(q652l|562VFlC8^r8?lQv;TMvn+*8I}&+hIQYh2 z1}uQQaag&!-+DZ@|C+C$bN6W;S-Z@)d1|en+XGvjbOxCa-qAF*LA=6s(Jg+g;82f$ z(Vb)8I)AH@cdjGFAR5Rqd0wiNCu!xtqWbcTx&5kslzTb^7A78~Xzw1($UV6S^VWiP zFd{Rimd-0CZC_Bu(WxBFW7+k{cOW7DxBBkJdJ;VsJ4Z@lERQr%3eVv&$%)b%<~ zCl^Y4NgO}js@u{|o~KTgH}>!* z_iDNqX2(As7T0xivMH|3SC1ivm8Q}6Ffcd7owUKN5lHAtzMM4<0v+ykUT!QiowO;`@%JGv+K$bBx@*S7C8GJVqQ_K>12}M`f_Ys=S zKFh}HM9#6Izb$Y{wYzItTy+l5U2oL%boCJn?R3?jP@n$zSIwlmyGq30Cw4QBO|14` zW5c);AN*J3&eMFAk$SR~2k|&+&Bc$e>s%c{`?d~85S-UWjA>DS5+;UKZ}5oVa5O(N zqqc@>)nee)+4MUjH?FGv%hm2{IlIF-QX}ym-7ok4Z9{V+ZHVZQl$A*x!(q%<2~iVv znUa+BX35&lCb#9VE-~Y^W_f;Xhl%vgjwdjzMy$FsSIj&ok}L+X`4>J=9BkN&nu^E*gbhj3(+D>C4E z@Fwq_=N)^bKFSHTzZk?-gNU$@l}r}dwGyh_fNi=9b|n}J>&;G!lzilbWF4B}BBq4f zYIOl?b)PSh#XTPp4IS5ZR_2C!E)Z`zH0OW%4;&~z7UAyA-X|sh9@~>cQW^COA9hV4 zXcA6qUo9P{bW1_2`eo6%hgbN%(G-F1xTvq!sc?4wN6Q4`e9Hku zFwvlAcRY?6h^Fj$R8zCNEDq8`=uZB8D-xn)tA<^bFFy}4$vA}Xq0jAsv1&5!h!yRA zU()KLJya5MQ`q&LKdH#fwq&(bNFS{sKlEh_{N%{XCGO+po#(+WCLmKW6&5iOHny>g z3*VFN?mx!16V5{zyuMWDVP8U*|BGT$(%IO|)?EF|OI*sq&RovH!N%=>i_c?K*A>>k zyg1+~++zY4Q)J;VWN0axhoIKx;l&G$gvj(#go^pZskEVj8^}is3Jw26LzYYVos0HX zRPvmK$dVxM8(Tc?pHFe0Z3uq){{#OK3i-ra#@+;*=ui8)y6hsRv z4Fxx1c1+fr!VI{L3DFMwXKrfl#Q8hfP@ajgEau&QMCxd{g#!T^;ATXW)nUg&$-n25 zruy3V!!;{?OTobo|0GAxe`Acn3GV@W=&n;~&9 zQM>NWW~R@OYORkJAo+eq1!4vzmf9K%plR4(tB@TR&FSbDoRgJ8qVcH#;7lQub*nq&?Z>7WM=oeEVjkaG zT#f)=o!M2DO5hLR+op>t0CixJCIeXH*+z{-XS|%jx)y(j&}Wo|3!l7{o)HU3m7LYyhv*xF&tq z%IN7N;D4raue&&hm0xM=`qv`+TK@;_xAcGKuK(2|75~ar2Yw)geNLSmVxV@x89bQu zpViVKKnlkwjS&&c|-X6`~xdnh}Ps)Hs z4VbUL^{XNLf7_|Oi>tA%?SG5zax}esF*FH3d(JH^Gvr7Rp*n=t7frH!U;!y1gJB^i zY_M$KL_}mW&XKaDEi9K-wZR|q*L32&m+2n_8lq$xRznJ7p8}V>w+d@?uB!eS3#u<} zIaqi!b!w}a2;_BfUUhGMy#4dPx>)_>yZ`ai?Rk`}d0>~ce-PfY-b?Csd(28yX22L% zI7XI>OjIHYTk_@Xk;Gu^F52^Gn6E1&+?4MxDS2G_#PQ&yXPXP^<-p|2nLTb@AAQEY zI*UQ9Pmm{Kat}wuazpjSyXCdnrD&|C1c5DIb1TnzF}f4KIV6D)CJ!?&l&{T)e4U%3HTSYqsQ zo@zWB1o}ceQSV)<4G<)jM|@@YpL+XHuWsr5AYh^Q{K=wSV99D~4RRU52FufmMBMmd z_H}L#qe(}|I9ZyPRD6kT>Ivj&2Y?qVZq<4bG_co_DP`sE*_Xw8D;+7QR$Uq(rr+u> z8bHUWbV19i#)@@G4bCco@Xb<8u~wVDz9S`#k@ciJtlu@uP1U0X?yov8v9U3VOig2t zL9?n$P3=1U_Emi$#slR>N5wH-=J&T=EdUHA}_Z zZIl3nvMP*AZS9{cDqFanrA~S5BqxtNm9tlu;^`)3X&V4tMAkJ4gEIPl= zoV!Gyx0N{3DpD@)pv^iS*dl2FwANu;1;%EDl}JQ7MbxLMAp>)UwNwe{=V}O-5C*>F zu?Ny+F64jZn<+fKjF01}8h5H_3pey|;%bI;SFg$w8;IC<8l|3#Lz2;mNNik6sVTG3 z+Su^rIE#40C4a-587$U~%KedEEw1%r6wdvoMwpmlXH$xPnNQN#f%Z7|p)nC>WsuO= z4zyqapLS<8(UJ~Qi9d|dQijb_xhA2)v>la)<1md5s^R1N&PiuA$^k|A<+2C?OiHbj z>Bn$~t)>Y(Zb`8hW7q9xQ=s>Rv81V+UiuZJc<23HplI88isqRCId89fb`Kt|CxVIg znWcwprwXnotO>3s&Oypkte^9yJjlUVVxSe%_xlzmje|mYOVPH^vjA=?6xd0vaj0Oz zwJ4OJNiFdnHJX3rw&inskjryukl`*fRQ#SMod5J|KroJRsVXa5_$q7whSQ{gOi*s0 z1LeCy|JBWRsDPn7jCb4s(p|JZiZ8+*ExC@Vj)MF|*Vp{B(ziccSn`G1Br9bV(v!C2 z6#?eqpJBc9o@lJ#^p-`-=`4i&wFe>2)nlPK1p9yPFzJCzBQbpkcR>={YtamIw)3nt z(QEF;+)4`>8^_LU)_Q3 zC5_7lgi_6y>U%m)m@}Ku4C}=l^J=<<7c;99ec3p{aR+v=diuJR7uZi%aQv$oP?dn?@6Yu_+*^>T0ptf(oobdL;6)N-I!TO`zg^Xbv3#L0I~sn@WGk-^SmPh5>W+LB<+1PU}AKa?FCWF|qMNELOgdxR{ zbqE7@jVe+FklzdcD$!(A$&}}H*HQFTJ+AOrJYnhh}Yvta(B zQ_bW4Rr;R~&6PAKwgLWXS{Bnln(vUI+~g#kl{r+_zbngT`Y3`^Qf=!PxN4IYX#iW4 zucW7@LLJA9Zh3(rj~&SyN_pjO8H&)|(v%!BnMWySBJV=eSkB3YSTCyIeJ{i;(oc%_hk{$_l;v>nWSB)oVeg+blh=HB5JSlG_r7@P z3q;aFoZjD_qS@zygYqCn=;Zxjo!?NK!%J$ z52lOP`8G3feEj+HTp@Tnn9X~nG=;tS+z}u{mQX_J0kxtr)O30YD%oo)L@wy`jpQYM z@M>Me=95k1p*FW~rHiV1CIfVc{K8r|#Kt(ApkXKsDG$_>76UGNhHExFCw#Ky9*B-z zNq2ga*xax!HMf_|Vp-86r{;~YgQKqu7%szk8$hpvi_2I`OVbG1doP(`gn}=W<8%Gn z%81#&WjkH4GV;4u43EtSW>K_Ta3Zj!XF?;SO3V#q=<=>Tc^@?A`i;&`-cYj|;^ zEo#Jl5zSr~_V-4}y8pnufXLa80vZY4z2ko7fj>DR)#z=wWuS1$$W!L?(y}YC+yQ|G z@L&`2upy3f>~*IquAjkVNU>}c10(fq#HdbK$~Q3l6|=@-eBbo>B9(6xV`*)sae58*f zym~RRVx;xoCG3`JV`xo z!lFw)=t2Hy)e!IFs?0~7osWk(d%^wxq&>_XD4+U#y&-VF%4z?XH^i4w`TxpF{`XhZ z%G}iEzf!T(l>g;W9<~K+)$g!{UvhW{E0Lis(S^%I8OF&%kr!gJ&fMOpM=&=Aj@wuL zBX?*6i51Qb$uhkwkFYkaD_UDE+)rh1c;(&Y=B$3)J&iJfQSx!1NGgPtK!$c9OtJuu zX(pV$bfuJpRR|K(dp@^j}i&HeJOh@|7lWo8^$*o~Xqo z5Sb+!EtJ&e@6F+h&+_1ETbg7LfP5GZjvIUIN3ibCOldAv z)>YdO|NH$x7AC8dr=<2ekiY1%fN*r~e5h6Yaw<{XIErujKV~tiyrvV_DV0AzEknC- zR^xKM3i<1UkvqBj3C{wDvytOd+YtDSGu!gEMg+!&|8BQrT*|p)(dwQLEy+ zMtMzij3zo40)CA!BKZF~yWg?#lWhqD3@qR)gh~D{uZaJO;{OWV8XZ_)J@r3=)T|kt zUS1pXr6-`!Z}w2QR7nP%d?ecf90;K_7C3d!UZ`N(TZoWNN^Q~RjVhQG{Y<%E1PpV^4 z-m-K+$A~-+VDABs^Q@U*)YvhY4Znn2^w>732H?NRK(5QSS$V@D7yz2BVX4)f5A04~$WbxGOam22>t&uD)JB8-~yiQW6ik;FGblY_I>SvB_z2?PS z*Qm&qbKI{H1V@YGWzpx`!v)WeLT02};JJo*#f$a*FH?IIad-^(;9XC#YTWN6;Z6+S zm4O1KH=#V@FJw7Pha0!9Vb%ZIM$)a`VRMoiN&C|$YA3~ZC*8ayZRY^fyuP6$n%2IU z$#XceYZeqLTXw(m$_z|33I$B4k~NZO>pP6)H_}R{E$i%USGy{l{-jOE;%CloYPEU+ zRFxOn4;7lIOh!7abb23YKD+_-?O z0FP9otcAh+oSj;=f#$&*ExUHpd&e#bSF%#8*&ItcL2H$Sa)?pt0Xtf+t)z$_u^wZi z44oE}r4kIZGy3!Mc8q$B&6JqtnHZ>Znn!Zh@6rgIu|yU+zG8q`q9%B18|T|oN3zMq z`l&D;U!OL~%>vo&q0>Y==~zLiCZk4v%s_7!9DxQ~id1LLE93gf*gg&2$|hB#j8;?3 z5v4S;oM6rT{Y;I+#FdmNw z){d%tNM<<#GN%n9ox7B=3#;u7unZ~tLB_vRZ52a&2=IM)2VkXm=L+Iqq~uk#Dug|x z>S84e+A7EiOY5lj*!q?6HDkNh~0g;0Jy(al!ZHHDtur9T$y-~)94HelX1NHjXWIM7UAe}$?jiz z9?P4`I0JM=G5K{3_%2jPLC^_Mlw?-kYYgb7`qGa3@dn|^1fRMwiyM@Ch z;CB&o7&&?c5e>h`IM;Wnha0QKnEp=$hA8TJgR-07N~U5(>9vJzeoFsSRBkDq=x(YgEMpb=l4TDD`2 zwVJpWGTA_u7}?ecW7s6%rUs&NXD3+n;jB86`X?8(l3MBo6)PdakI6V6a}22{)8ilT zM~T*mU}__xSy|6XSrJ^%lDAR3Lft%+yxC|ZUvSO_nqMX!_ul3;R#*{~4DA=h$bP)%8Yv9X zyp><|e8=_ttI}ZAwOd#dlnSjck#6%273{E$kJuCGu=I@O)&6ID{nWF5@gLb16sj|&Sb~+du4e4O_%_o`Ix4NRrAsyr1_}MuP94s>de8cH-OUkVPk3+K z&jW)It9QiU-ti~AuJkL`XMca8Oh4$SyJ=`-5WU<{cIh+XVH#e4d&zive_UHC!pN>W z3TB;Mn5i)9Qn)#6@lo4QpI3jFYc0~+jS)4AFz8fVC;lD^+idw^S~Qhq>Tg(!3$yLD zzktzoFrU@6s4wwCMz}edpF5i5Q1IMmEJQHzp(LAt)pgN3&O!&d?3W@6U4)I^2V{;- z6A(?zd93hS*uQmnh4T)nHnE{wVhh(=MMD(h(P4+^p83Om6t<*cUW>l(qJzr%5vp@K zN27ka(L{JX=1~e2^)F^i=TYj&;<7jyUUR2Bek^A8+3Up*&Xwc{)1nRR5CT8vG>ExV zHnF3UqXJOAno_?bnhCX-&kwI~Ti8t4`n0%Up>!U`ZvK^w2+0Cs-b9%w%4`$+To|k= zKtgc&l}P`*8IS>8DOe?EB84^kx4BQp3<7P{Pq}&p%xF_81pg!l2|u=&I{AuUgmF5n zJQCTLv}%}xbFGYtKfbba{CBo)lWW%Z>i(_NvLhoQZ*5-@2l&x>e+I~0Nld3UI9tdL zRzu8}i;X!h8LHVvN?C+|M81e>Jr38%&*9LYQec9Ax>?NN+9(_>XSRv&6hlCYB`>Qm z1&ygi{Y()OU4@D_jd_-7vDILR{>o|7-k)Sjdxkjgvi{@S>6GqiF|o`*Otr;P)kLHN zZkpts;0zw_6;?f(@4S1FN=m!4^mv~W+lJA`&7RH%2$)49z0A+8@0BCHtj|yH--AEL z0tW6G%X-+J+5a{5*WKaM0QDznf;V?L5&uQw+yegDNDP`hA;0XPYc6e0;Xv6|i|^F2WB)Z$LR|HR4 zTQsRAby9(^Z@yATyOgcfQw7cKyr^3Tz7lc7+JEwwzA7)|2x+PtEb>nD(tpxJQm)Kn zW9K_*r!L%~N*vS8<5T=iv|o!zTe9k_2jC_j*7ik^M_ zaf%k{WX{-;0*`t`G!&`eW;gChVXnJ-Rn)To8vW-?>>a%QU1v`ZC=U)f8iA@%JG0mZ zDqH;~mgBnrCP~1II<=V9;EBL)J+xzCoiRBaeH&J6rL!{4zIY8tZka?_FBeQeNO3q6 zyG_alW54Ba&wQf{&F1v-r1R6ID)PTsqjIBc+5MHkcW5Fnvi~{-FjKe)t1bl}Y;z@< z=!%zvpRua>>t_x}^}z0<7MI!H2v6|XAyR9!t50q-A)xk0nflgF4*OQlCGK==4S|wc zRMsSscNhRzHMBU8TdcHN!q^I}x0iXJ%uehac|Zs_B$p@CnF)HeXPpB_Za}F{<@6-4 zl%kml@}kHQ(ypD8FsPJ2=14xXJE|b20RUIgs!2|R3>LUMGF6X*B_I|$`Qg=;zm7C z{mEDy9dTmPbued7mlO@phdmAmJ7p@GR1bjCkMw6*G7#4+`k>fk1czdJUB!e@Q(~6# zwo%@p@V5RL0ABU2LH7Asq^quDUho@H>eTZH9f*no9fY0T zD_-9px3e}A!>>kv5wk91%C9R1J_Nh!*&Kk$J3KNxC}c_@zlgpJZ+5L)Nw|^p=2ue}CJtm;uj*Iqr)K})kA$xtNUEvX;4!Px*^&9T_`IN{D z{6~QY=Nau6EzpvufB^hflc#XIsSq0Y9(nf$d~6ZwK}fal92)fr%T3=q{0mP-EyP_G z)UR5h@IX}3Qll2b0oCAcBF>b*@Etu*aTLPU<%C>KoOrk=x?pN!#f_Og-w+;xbFgjQ zXp`et%lDBBh~OcFnMKMUoox0YwBNy`N0q~bSPh@+enQ=4RUw1) zpovN`QoV>vZ#5LvC;cl|6jPr}O5tu!Ipoyib8iXqy}TeJ;4+_7r<1kV0v5?Kv>fYp zg>9L`;XwXa&W7-jf|9~uP2iyF5`5AJ`Q~p4eBU$MCC00`rcSF>`&0fbd^_eqR+}mK z4n*PMMa&FOcc)vTUR zlDUAn-mh`ahi_`f`=39JYTNVjsTa_Y3b1GOIi)6dY)D}xeshB0T8Eov5%UhWd1)u}kjEQ|LDo{tqKKrYIfVz~@dp!! zMOnah@vp)%_-jDTUG09l+;{CkDCH|Q{NqX*uHa1YxFShy*1+;J`gywKaz|2Q{lG8x zP?KBur`}r`!WLKXY_K;C8$EWG>jY3UIh{+BLv0=2)KH%P}6xE2kg)%(-uA6lC?u8}{K(#P*c zE9C8t*u%j2r_{;Rpe1A{9nNXU;b_N0vNgyK!EZVut~}+R2rcbsHilqsOviYh-pYX= zHw@53nlmwYI5W5KP>&`dBZe0Jn?nAdC^HY1wlR6$u^PbpB#AS&5L6zqrXN&7*N2Q` z+Rae1EwS)H=aVSIkr8Ek^1jy2iS2o7mqm~Mr&g5=jjt7VxwglQ^`h#Mx+x2v|9ZAwE$i_9918MjJxTMr?n!bZ6n$}y11u8I9COTU`Z$Fi z!AeAQLMw^gp_{+0QTEJrhL424pVDp%wpku~XRlD3iv{vQ!lAf!_jyqd_h}+Tr1XG| z`*FT*NbPqvHCUsYAkFnM`@l4u_QH&bszpUK#M~XLJt{%?00GXY?u_{gj3Hvs!=N(I z(=AuWPijyoU!r?aFTsa8pLB&cx}$*%;K$e*XqF{~*rA-qn)h^!(-;e}O#B$|S~c+U zN4vyOK0vmtx$5K!?g*+J@G1NmlEI=pyZXZ69tAv=@`t%ag_Hk{LP~OH9iE)I= zaJ69b4kuCkV0V zo(M0#>phpQ_)@j;h%m{-a*LGi(72TP)ws2w*@4|C-3+;=5DmC4s7Lp95%n%@Ko zfdr3-a7m*dys9iIci$A=4NPJ`HfJ;hujLgU)ZRuJI`n;Pw|yksu!#LQnJ#dJysgNb z@@qwR^wrk(jbq4H?d!lNyy72~Dnn87KxsgQ!)|*m(DRM+eC$wh7KnS-mho3|KE)7h zK3k;qZ;K1Lj6uEXLYUYi)1FN}F@-xJ z@@3Hb84sl|j{4$3J}aTY@cbX@pzB_qM~APljrjju6P0tY{C@ zpUCOz_NFmALMv1*blCcwUD3?U6tYs+N%cmJ98D%3)%)Xu^uvzF zS5O!sc#X6?EwsYkvPo6A%O8&y8sCCQH<%f2togVwW&{M;PR!a(ZT_A+jVAbf{@5kL zB@Z(hb$3U{T_}SKA_CoQVU-;j>2J=L#lZ~aQCFg-d<9rzs$_gO&d5N6eFSc z1ml8)P*FSi+k@!^M9nDWR5e@ATD8oxtDu=36Iv2!;dZzidIS(PCtEuXAtlBb1;H%Z zwnC^Ek*D)EX4#Q>R$$WA2sxC_t(!!6Tr?C#@{3}n{<^o;9id1RA&-Pig1e-2B1XpG zliNjgmd3c&%A}s>qf{_j#!Z`fu0xIwm4L0)OF=u(OEmp;bLCIaZX$&J_^Z%4Sq4GZ zPn6sV_#+6pJmDN_lx@1;Zw6Md_p0w9h6mHtzpuIEwNn>OnuRSC2=>fP^Hqgc)xu^4 z<3!s`cORHJh#?!nKI`Et7{3C27+EuH)Gw1f)aoP|B3y?fuVfvpYYmmukx0ya-)TQX zR{ggy5cNf4X|g)nl#jC9p>7|09_S7>1D2GTRBUTW zAkQ=JMRogZqG#v;^=11O6@rPPwvJkr{bW-Qg8`q8GoD#K`&Y+S#%&B>SGRL>;ZunM@49!}Uy zN|bBCJ%sO;@3wl0>0gbl3L@1^O60ONObz8ZI7nder>(udj-jt`;yj^nTQ$L9`OU9W zX4alF#$|GiR47%x@s&LV>2Sz2R6?;2R~5k6V>)nz!o_*1Y!$p>BC5&?hJg_MiE6UBy>RkVZj`9UWbRkN-Hk!S`=BS3t3uyX6)7SF#)71*}`~Ogz z1rap5H6~dhBJ83;q-Y<5V35C2&F^JI-it(=5D#v!fAi9p#UwV~2tZQI+W(Dv?1t9? zfh*xpxxO{-(VGB>!Q&0%^YW_F!@aZS#ucP|YaD#>wd1Fv&Z*SR&mc;asi}1G) z_H>`!akh-Zxq9#io(7%;a$)w+{QH)Y$?UK1Dt^4)up!Szcxnu}kn$0afcfJL#IL+S z5gF_Y30j;{lNrG6m~$Ay?)*V9fZuU@3=kd40=LhazjFrau>(Y>SJNtOz>8x_X-BlA zIpl{i>OarVGj1v(4?^1`R}aQB&WCRQzS~;7R{tDZG=HhgrW@B`W|#cdyj%YBky)P= zpxuOZkW>S6%q7U{VsB#G(^FMsH5QuGXhb(sY+!-R8Bmv6Sx3WzSW<1MPPN1!&PurYky(@`bP9tz z52}LH9Q?+FF5jR6-;|+GVdRA!qtd;}*-h&iIw3Tq3qF9sDIb1FFxGbo&fbG5n8$3F zyY&PWL{ys^dTO}oZ#@sIX^BKW*bon=;te9j5k+T%wJ zNJtoN1~YVj4~YRrlZl)b&kJqp+Z`DqT!la$x&&IxgOQw#yZd-nBP3!7FijBXD|IsU8Zl^ zc6?MKpJQ+7ka|tZQLfchD$PD|;K(9FiLE|eUZX#EZxhG!S-63C$jWX1Yd!6-Yxi-u zjULIr|0-Q%D9jz}IF~S%>0(jOqZ(Ln<$9PxiySr&2Oic7vb<8q=46)Ln%Z|<*z5&> z3f~Zw@m;vR(bESB<=Jqkxn(=#hQw42l(7)h`vMQQTttz9XW6^|^8EK7qhju4r_c*b zJIi`)MB$w@9epwdIfnEBR+?~);yd6C(LeMC& zn&&N*?-g&BBJcV;8&UoZi4Lmxcj16ojlxR~zMrf=O_^i1wGb9X-0@6_rpjPYemIin zmJb+;lHe;Yp=8G)Q(L1bzH*}I>}uAqhj4;g)PlvD9_e_ScR{Ipq|$8NvAvLD8MYr}xl=bU~)f%B3E>r3Bu9_t|ThF3C5~BdOve zEbk^r&r#PT&?^V1cb{72yEWH}TXEE}w>t!cY~rA+hNOTK8FAtIEoszp!qqptS&;r$ zaYV-NX96-h$6aR@1xz6_E0^N49mU)-v#bwtGJm)ibygzJ8!7|WIrcb`$XH~^!a#s& z{Db-0IOTFq#9!^j!n_F}#Z_nX{YzBK8XLPVmc&X`fT7!@$U-@2KM9soGbmOSAmqV z{nr$L^MBo_u^Joyf0E^=eo{Rt0{{e$IFA(#*kP@SQd6lWT2-#>` zP1)7_@IO!9lk>Zt?#CU?cuhiLF&)+XEM9B)cS(gvQT!X3`wL*{fArTS;Ak`J<84du zALKPz4}3nlG8Fo^MH0L|oK2-4xIY!~Oux~1sw!+It)&D3p;+N8AgqKI`ld6v71wy8I!eP0o~=RVcFQR2Gr(eP_JbSytoQ$Yt}l*4r@A8Me94y z8cTDWhqlq^qoAhbOzGBXv^Wa4vUz$(7B!mX`T=x_ueKRRDfg&Uc-e1+z4x$jyW_Pm zp?U;-R#xt^Z8Ev~`m`iL4*c#65Nn)q#=Y0l1AuD&+{|8-Gsij3LUZXpM0Bx0u7WWm zH|%yE@-#XEph2}-$-thl+S;__ciBxSSzHveP%~v}5I%u!z_l_KoW{KRx2=eB33umE zIYFtu^5=wGU`Jab8#}cnYry@9p5UE#U|VVvx_4l49JQ;jQdp(uw=$^A$EA$LM%vmE zvdEOaIcp5qX8wX{mYf0;#51~imYYPn4=k&#DsKTxo{_Mg*;S495?OBY?#gv=edYC* z^O@-sd-qa+U24xvcbL0@C7_6o!$`)sVr-jSJE4XQUQ$?L7}2(}Eixqv;L8AdJAVqc zq}RPgpnDb@E_;?6K58r3h4-!4rT4Ab#rLHLX?eMOfluJk=3i1@Gt1i#iA=O`M0@x! z(HtJP9BMHXEzuD93m|B&woj0g6T?f#^)>J>|I4C5?Gam>n9!8CT%~aT;=oco5d6U8 zMXl(=W;$ND_8+DD*?|5bJ!;8ebESXMUKBAf7YBwNVJibGaJ*(2G`F%wx)grqVPjudiaq^Kl&g$8A2 zWMxMr@_$c}d+;_B`#kUX-t|4VKH&_f^^EP0&=DPLW)H)UzBG%%Tra*5 z%$kyZe3I&S#gfie^z5)!twG={3Cuh)FdeA!Kj<-9** zvT*5%Tb`|QbE!iW-XcOuy39>D3oe6x{>&<#E$o8Ac|j)wq#kQzz|ATd=Z0K!p2$QE zPu?jL8Lb^y3_CQE{*}sTDe!2!dtlFjq&YLY@2#4>XS`}v#PLrpvc4*@q^O{mmnr5D zmyJq~t?8>FWU5vZdE(%4cuZuao0GNjp3~Dt*SLaxI#g_u>hu@k&9Ho*#CZP~lFJHj z(e!SYlLigyc?&5-YxlE{uuk$9b&l6d`uIlpg_z15dPo*iU&|Khx2*A5Fp;8iK_bdP z?T6|^7@lcx2j0T@x>X7|kuuBSB7<^zeY~R~4McconTxA2flHC0_jFxmSTv-~?zVT| zG_|yDqa9lkF*B6_{j=T>=M8r<0s;@z#h)3BQ4NLl@`Xr__o7;~M&dL3J8fP&zLfDfy z);ckcTev{@OUlZ`bCo(-3? z1u1xD`PKgSg?RqeVVsF<1SLF;XYA@Bsa&cY!I48ZJn1V<3d!?s=St?TLo zC0cNr`qD*M#s6f~X>SCNVkva^9A2ZP>CoJ9bvgXe_c}WdX-)pHM5m7O zrHt#g$F0AO+nGA;7dSJ?)|Mo~cf{z2L)Rz!`fpi73Zv)H=a5K)*$5sf_IZypi($P5 zsPwUc4~P-J1@^3C6-r9{V-u0Z&Sl7vNfmuMY4yy*cL>_)BmQF!8Om9Dej%cHxbIzA zhtV0d{=%cr?;bpBPjt@4w=#<>k5ee=TiWAXM2~tUGfm z$s&!Dm0R^V$}fOR*B^kGaipi~rx~A2cS0;t&khV1a4u38*XRUP~f za!rZMtay8bsLt6yFYl@>-y^31(*P!L^^s@mslZy(SMsv9bVoX`O#yBgEcjCmGpyc* zeH$Dw6vB5P*;jor+JOX@;6K#+xc)Z9B8M=x2a@Wx-{snPGpRmOC$zpsqW*JCh@M2Y z#K+M(>=#d^>Of9C`))h<=Bsy)6zaMJ&x-t%&+UcpLjV`jo4R2025 zXaG8EA!0lQa)|dx-@{O)qP6`$rhCkoQqZ`^SW8g-kOwrwsK8 z3ms*AIcyj}-1x&A&vSq{r=QMyp3CHdWH35!sad#!Sm>^|-|afB+Q;|Iq@LFgqIp#Z zD1%H+3I?6RGnk&IFo|u+E0dCxXz4yI^1i!QTu7uvIEH>i3rR{srcST`LIRwdV1P;W z+%AN1NIf@xxvVLiSX`8ILA8MzNqE&7>%jMzGt9wm78bo9<;h*W84i29^w!>V>{N+S zd`5Zmz^G;f=icvoOZfK5#1ctx*~UwD=ab4DGQXehQ!XYnak*dee%YN$_ZPL%KZuz$ zD;$PpT;HM^$KwtQm@7uvT`i6>Hae1CoRVM2)NL<2-k2PiX=eAx+-6j#JI?M}(tuBW zkF%jjLR)O`gI2fcPBxF^HeI|DWwQWHVR!;;{BXXHskxh8F@BMDn`oEi-NHt;CLymW z=KSv5)3dyzec0T5B*`g-MQ<;gz=nIWKUi9ko<|4I(-E0k$QncH>E4l z**1w&#={&zv4Tvhgz#c29`m|;lU-jmaXFMC11 z*dlXDMEOG>VoLMc>!rApwOu2prKSi*!w%`yzGmS+k(zm*CsLK*wv{S_0WX^8A-rKy zbk^Gf_92^7iB_uUF)EE+ET4d|X|>d&mdN?x@vxKAQk`O+r4Qdu>XGy(a(19g;=jU} zFX{O*_NG>!$@jh!U369Lnc+D~qch3uT+_Amyi}*k#LAAwh}k8IPK5a-WZ81ufD>l> z$4cF}GSz>ce`3FAic}6W4Z7m9KGO?(eWqi@L|5Hq0@L|&2flN1PVl}XgQ2q*_n2s3 zt5KtowNkTYB5b;SVuoXA@i5irXO)A&%7?V`1@HGCB&)Wgk+l|^XXChq;u(nyPB}b3 zY>m5jkxpZgi)zfbgv&ec4Zqdvm+D<?Im*mXweS9H+V>)zF#Zp3)bhl$PbISY{5=_z!8&*Jv~NYtI-g!>fDs zmvL5O^U%!^VaKA9gvKw|5?-jk>~%CVGvctKmP$kpnpfN{D8@X*Aazi$txfa%vd-|E z>kYmV66W!lNekJPom29LdZ%(I+ZLZYTXzTg*to~m?7vp%{V<~>H+2}PQ?PPAq`36R z<%wR8v6UkS>Wt#hzGk#44W<%9S=nBfB);6clKwnxY}T*w21Qc3_?IJ@4gYzC7s;WP zVQNI(M=S=JT#xsZy7G`cR(BP9*je0bfeN8JN5~zY(DDs0t{LpHOIbN);?T-69Pf3R zSNe*&p2%AwXHL>__g+xd4Hlc_vu<25H?(`nafS%)3UPP7_4;gk-9ckt8SJRTv5v0M z_Hww`qPudL?ajIR&X*;$y-`<)6dxx1U~5eGS13CB!lX;3w7n&lDDiArbAhSycd}+b zya_3p@A`$kQy;|NJZ~s44Hqo7Hwt}X86NK=(ey>lgWTtGL6k@Gy;PbO!M%1~Wcn2k zUFP|*5d>t-X*RU8g%>|(wwj*~#l4z^Aatf^DWd1Wj#Q*AY0D^V@sC`M zjJc6qXu0I7Y*2;;gGu!plAFzG=J;1%eIOdn zQA>J&e05UN*7I5@yRhK|lbBSfJ+5Uq;!&HV@xfPZrgD}kE*1DSq^=%{o%|LChhl#0 zlMb<^a6ixzpd{kNZr|3jTGeEzuo}-eLT-)Q$#b{!vKx8Tg}swCni>{#%vDY$Ww$84 zew3c9BBovqb}_&BRo#^!G(1Eg((BScRZ}C)Oz?y`T5wOrv);)b^4XR8 zhJo7+<^7)qB>I;46!GySzdneZ>n_E1oWZY;kf94#)s)kWjuJN1c+wbVoNQcmnv}{> zN0pF+Sl3E}UQ$}slSZeLJrwT>Sr}#V(dVaezCQl2|4LN`7L7v&siYR|r7M(*JYfR$ zst3=YaDw$FSc{g}KHO&QiKxuhEzF{f%RJLKe3p*7=oo`WNP)M(9X1zIQPP0XHhY3c znrP{$4#Ol$A0s|4S7Gx2L23dv*Gv2o;h((XVn+9+$qvm}s%zi6nI-_s6?mG! zj{DV;qesJb&owKeEK?=J>UcAlYckA7Sl+I&IN=yasrZOkejir*kE@SN`fk<8Fgx*$ zy&fE6?}G)d_N`){P~U@1jRVA|2*69)KSe_}!~?+`Yb{Y=O~_+@!j<&oVQQMnhoIRU zA0CyF1OFfkK44n*JD~!2!SCPM;PRSk%1XL=0&rz00wxPs&-_eapJy#$h!eqY%nS0{ z!aGg58JIJPF3_ci%n)QSVpa2H`vIe$RD43;#IRfDV&Ibit z+?>HW4{2wOfC6Fw)}4x}i1maDxcE1qi@BS*qcxD2gE@h3#4cgU*D-&3z7D|tVZWt= z-Cy2+*Cm@P4GN_TPUtaVyVesbVDazF@)j8VJ4>XZv!f%}&eO1SvIgr}4`A*3#vat< z_MoByL(qW6L7SFZ#|Gc1fFN)L2PxY+{B8tJp+pxRyz*87)vXR}*=&ahXjBlQKguuf zX6x<<6fQulE^C*KH8~W%ptpaC0l?b=_{~*U4?5Vt;dgM4t_{&UZ1C2j?b>b+5}{IF_CUyvz-@QZPMlJ)r_tS$9kH%RPv#2_nMb zRLj5;chJ72*U`Z@Dqt4$@_+k$%|8m(HqLG!qT4P^DdfvGf&){gKnGCX#H0!;W=AGP zbA&Z`-__a)VTS}kKFjWGk z%|>yE?t*EJ!qeQ%dPk$;xIQ+P0;()PCBDgjJm6Buj{f^awNoVx+9<|lg3%-$G(*f) zll6oOkN|yamn1uyl2*N-lnqRI1cvs_JxLTeahEK=THV$Sz*gQhKNb*p0fNoda#-&F zB-qJgW^g}!TtM|0bS2QZekW7_tKu%GcJ!4?lObt0z_$mZ4rbQ0o=^curCs3bJK6sq z9fu-aW-l#>z~ca(B;4yv;2RZ?tGYAU)^)Kz{L|4oPj zdOf_?de|#yS)p2v8-N||+XL=O*%3+y)oI(HbM)Ds?q8~HPzIP(vs*G`iddbWq}! z(2!VjP&{Z1w+%eUq^ '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/resources/database/3dcitydb/postgreSQL/CREATE_ADE_DB.sql b/resources/database/3dcitydb/postgreSQL/CREATE_ADE_DB.sql new file mode 100644 index 0000000..ee163e5 --- /dev/null +++ b/resources/database/3dcitydb/postgreSQL/CREATE_ADE_DB.sql @@ -0,0 +1,1048 @@ +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- +-- CityGML Energy ADE 2.0 (beta 7) +-- +-- Last update: 2025-06-25 +-- +-- This DDL script installs the 3DCityDB schema for the Energy ADE 2.0. It must be run +-- from within the ADE Manager plugin of the 3DCityDB Importer/Exporter. +-- +-- This script was first automatically generated using the 3DCityDB ADE Manager +-- and successively edited and restructured by: +-- +-- Dr. Giorgio Agugiaro +-- 3D Geoinformation group +-- Delft University of Technology +-- The Netherlands +-- +-- https://3d.bk.tudelft.nl/gagugiaro/ +-- +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- ***************************** CREATE_ADE_DB.SQL execution START ************************ +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- *********************************** Create Sequences *********************************** +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +CREATE SEQUENCE ng2_ctyobj_relation_seq INCREMENT BY 1 MINVALUE 0 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE OWNED BY NONE; +CREATE SEQUENCE ng2_optical_property_seq INCREMENT BY 1 MINVALUE 0 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE OWNED BY NONE; +CREATE SEQUENCE ng2_qualified_attribute_seq INCREMENT BY 1 MINVALUE 0 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE OWNED BY NONE; +CREATE SEQUENCE ng2_suitability_seq INCREMENT BY 1 MINVALUE 0 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE OWNED BY NONE; + +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- *********************************** Create tables ************************************** +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +-- -------------------------------------------------------------------- +-- ng2_address_to_building_unit +-- -------------------------------------------------------------------- +CREATE TABLE ng2_address_to_building_unit ( + address_id BIGINT NOT NULL, + building_unit_id BIGINT NOT NULL, + PRIMARY KEY (address_id, building_unit_id) +); + +-- -------------------------------------------------------------------- +-- ng2_building - extends building table +-- -------------------------------------------------------------------- +CREATE TABLE ng2_building ( + id BIGINT PRIMARY KEY, + type VARCHAR, + type_codespace VARCHAR, + is_protected NUMERIC, + constr_weight VARCHAR, + constr_weight_codespace VARCHAR, + attic_thm_status VARCHAR, + basement_thm_status VARCHAR +); + +-- -------------------------------------------------------------------- +-- ng2_building_partition +-- -------------------------------------------------------------------- +CREATE TABLE ng2_building_partition ( + id BIGINT PRIMARY KEY, + objectclass_id INTEGER, +-- Thermal Zone attributes + heat_capacity NUMERIC, + heat_capacity_uom VARCHAR, + infiltration_rate NUMERIC, + infiltration_rate_uom VARCHAR, + is_cooled NUMERIC, + is_heated NUMERIC, + coincides_with_lod2_hull NUMERIC, + coincides_with_lod3_hull NUMERIC, +-- Usage Zone and BuildingUnit attributes + type VARCHAR, + type_codespace VARCHAR, +-- Usage Zone attributes + is_primary NUMERIC, + num_of_building_units INTEGER, + int_heat_gains NUMERIC, + int_heat_gains_uom VARCHAR, + int_heat_gains_conv NUMERIC, + int_heat_gains_conv_uom VARCHAR, + int_heat_gains_lat NUMERIC, + int_heat_gains_lat_uom VARCHAR, + int_heat_gains_rad NUMERIC, + int_heat_gains_rad_uom VARCHAR, +-- BuildingUnit attributes + num_of_rooms INTEGER, + owner_name VARCHAR, + ownership_type VARCHAR, + ownership_type_codespace VARCHAR, +-- Used to store the FK to the schedule(s) + cooling_schedule_id BIGINT, + heating_schedule_id BIGINT, + ventilation_schedule_id BIGINT, +-- Used to store the association from UsageZone to BuildingUnit + usage_zone_id BIGINT, +-- Used to store the association from ThermalZone to UsageZone + thermal_zone_id BIGINT, +-- Parent id (Building(Part) of a ThermalZone, UsageZone or BuildintUnit + building_id BIGINT, +-- FK to the geometries in the surface_geometry table + lod1_solid_id BIGINT, + lod2_solid_id BIGINT, + lod3_solid_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_cityobject - extends cityobject table +-- -------------------------------------------------------------------- +CREATE TABLE ng2_cityobject ( + id BIGINT PRIMARY KEY, + layered_construction_id BIGINT, + ref_point geometry(POINTZ) +); + +-- -------------------------------------------------------------------- +-- ng2_ctyobj_relation +-- -------------------------------------------------------------------- +CREATE TABLE ng2_ctyobj_relation ( + id BIGINT PRIMARY KEY DEFAULT nextval('ng2_ctyobj_relation_seq'::regclass), +-- this refers to ng2_cityobject (id), i.e. the source + ng2_cityobject_id BIGINT, +-- this refers to cityobject (id), i.e. the target + cityobject_id BIGINT, + relation_type VARCHAR, + relation_type_codespace VARCHAR +); + +-- -------------------------------------------------------------------- +-- ng2_device +-- -------------------------------------------------------------------- +CREATE TABLE ng2_device ( + id BIGINT PRIMARY KEY, + objectclass_id INTEGER, +-- AbstractDevice attributes (also for LightingDevice, GenericDevice, GenericElectricalDevice) + model VARCHAR, + num_of_devices INTEGER, + year_of_manufacture INTEGER, + installed_power NUMERIC, + installed_power_uom VARCHAR, + nominal_efficiency NUMERIC, + nominal_efficiency_uom VARCHAR, + efficiency_indicator VARCHAR, + heat_diss NUMERIC, + heat_diss_uom VARCHAR, + heat_diss_conv NUMERIC, + heat_diss_conv_uom VARCHAR, + heat_diss_lat NUMERIC, + heat_diss_lat_uom VARCHAR, + heat_diss_rad NUMERIC, + heat_diss_rad_uom VARCHAR, +-- HeatPump attributes + heat_source VARCHAR, + cop_source_temp NUMERIC, + cop_source_temp_uom VARCHAR, + cop_operation_temp NUMERIC, + cop_operation_temp_uom VARCHAR, +-- Boiler attributes + has_condensation NUMERIC, +-- MovableShadingDevice attributes + type VARCHAR, + type_codespace VARCHAR, + installation_side VARCHAR, + max_cover_ratio NUMERIC, + max_cover_ratio_uom VARCHAR, +-- FK to table optical_property + transmittance_id BIGINT, + cityobject_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_device_operation +-- -------------------------------------------------------------------- +CREATE TABLE ng2_device_operation ( + id BIGINT PRIMARY KEY, + type VARCHAR, + type_codespace VARCHAR, + yearly_global_efficiency NUMERIC, +-- FK + schedule_id BIGINT, + device_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_energy_perf_cert +-- -------------------------------------------------------------------- +CREATE TABLE ng2_energy_perf_cert ( + id BIGINT PRIMARY KEY, + type VARCHAR, + type_codespace VARCHAR, + label VARCHAR, + value NUMERIC, + value_uom VARCHAR, + issue_date DATE, + expiration_date DATE, + cert_method VARCHAR, + cert_uri VARCHAR, +-- FK + building_id BIGINT, + building_partition_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_layer +-- -------------------------------------------------------------------- +CREATE TABLE ng2_layer ( + id BIGINT PRIMARY KEY, + thickness NUMERIC, + thickness_uom VARCHAR, + material_id BIGINT, +-- FK + layered_construction_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_layered_construction +-- -------------------------------------------------------------------- +CREATE TABLE ng2_layered_construction ( + id BIGINT PRIMARY KEY, + objectclass_id INTEGER, + u_value NUMERIC, + u_value_uom VARCHAR, + g_value NUMERIC, + g_value_uom VARCHAR, + glazing_ratio NUMERIC, + glazing_ratio_uom VARCHAR, + library_code VARCHAR, + library_code_codespace VARCHAR, +-- FKs + layered_construction_id BIGINT, + library_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_library +-- -------------------------------------------------------------------- +CREATE TABLE ng2_library ( + id BIGINT PRIMARY KEY, + objectclass_id INTEGER, + type VARCHAR, + type_codespace VARCHAR, + author VARCHAR, + source VARCHAR +); + +-- -------------------------------------------------------------------- +-- ng2_material +-- -------------------------------------------------------------------- +CREATE TABLE ng2_material ( + id BIGINT PRIMARY KEY, + objectclass_id INTEGER, +-- Abstract library attributes + library_code VARCHAR, + library_code_codespace VARCHAR, +-- SolidMaterial attributes + thm_conductivity NUMERIC, + thm_conductivity_uom VARCHAR, + spec_heat_capacity NUMERIC, + spec_heat_capacity_uom VARCHAR, + density NUMERIC, + density_uom VARCHAR, + permeance NUMERIC, + permeance_uom VARCHAR, + porosity NUMERIC, + porosity_uom VARCHAR, + embodied_carbon NUMERIC, + embodied_carbon_uom VARCHAR, + embodied_energy NUMERIC, + embodied_energy_uom VARCHAR, +-- Gas attributes + is_ventilated NUMERIC, + r_value NUMERIC, + r_value_uom VARCHAR, +-- FK + library_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_occupants +-- -------------------------------------------------------------------- +CREATE TABLE ng2_occupants ( + id BIGINT PRIMARY KEY, + type VARCHAR, + type_codespace VARCHAR, + num_of_occupants INTEGER, + heat_diss NUMERIC, + heat_diss_uom VARCHAR, + heat_diss_conv NUMERIC, + heat_diss_conv_uom VARCHAR, + heat_diss_lat NUMERIC, + heat_diss_lat_uom VARCHAR, + heat_diss_rad NUMERIC, + heat_diss_rad_uom VARCHAR, + avg_diet_type VARCHAR, + avg_diet_type_codespace VARCHAR, + avg_income_level VARCHAR, + avg_income_level_codespace VARCHAR, + avg_instr_level VARCHAR, + avg_instr_level_codespace VARCHAR, +-- FK + schedule_id BIGINT, + building_partition_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_opening - extends (building) opening table +-- -------------------------------------------------------------------- +CREATE TABLE ng2_opening ( + id BIGINT PRIMARY KEY, + area NUMERIC, + area_uom VARCHAR, + azimuth NUMERIC, + azimuth_uom VARCHAR, + inclination NUMERIC, + inclination_uom VARCHAR, + ground_view_factor NUMERIC, + ground_view_factor_uom VARCHAR, + sky_view_factor NUMERIC, + sky_view_factor_uom VARCHAR +); + +-- -------------------------------------------------------------------- +-- ng2_optical_property +-- -------------------------------------------------------------------- +CREATE TABLE ng2_optical_property ( + id BIGINT PRIMARY KEY DEFAULT nextval('ng2_optical_property_seq'::regclass), + objectclass_id INTEGER, + fraction NUMERIC, + fraction_uom VARCHAR, + surface VARCHAR, + wavelength_range VARCHAR, +-- FK + layered_construction_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_qualified_attribute +-- -------------------------------------------------------------------- +CREATE TABLE ng2_qualified_attribute ( + id BIGINT PRIMARY KEY DEFAULT nextval('ng2_qualified_attribute_seq'::regclass), + objectclass_id INTEGER, + type VARCHAR, + type_codespace VARCHAR, + value NUMERIC, + value_uom VARCHAR, + description VARCHAR, + source VARCHAR, +-- FK + building_id BIGINT, + building_partition_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_refurbishment_measure +-- -------------------------------------------------------------------- +CREATE TABLE ng2_refurbishment_measure ( + id BIGINT PRIMARY KEY, + type VARCHAR, + type_codespace VARCHAR, + start_date DATE, + end_date DATE, + library_code VARCHAR, + library_code_codespace VARCHAR, +-- FK + building_id BIGINT, + building_partition_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_resource +-- -------------------------------------------------------------------- +CREATE TABLE ng2_resource ( + id BIGINT PRIMARY KEY, + objectclass_id INTEGER, + type VARCHAR, + type_codespace VARCHAR, + enduse VARCHAR, + enduse_codespace VARCHAR, + status VARCHAR, + operation_type VARCHAR, + operation_type_codespace VARCHAR, + year INTEGER, + amount_type VARCHAR, + amount_type_codespace VARCHAR, + amount NUMERIC, + amount_uom VARCHAR, + time_series_id BIGINT, + is_amount_normalized NUMERIC, + normalization_param VARCHAR, + normalization_value NUMERIC, + normalization_value_uom VARCHAR, + co2_equivalent NUMERIC, + co2_equivalent_uom VARCHAR, + costs_money NUMERIC, + costs_money_uom VARCHAR, + yields_money NUMERIC, + yields_money_uom VARCHAR, +-- Attributes for Energy + energy_carrier VARCHAR, + energy_carrier_codespace VARCHAR, + maximum_load NUMERIC, + maximum_load_uom VARCHAR, + source VARCHAR, + source_codespace VARCHAR, +-- Attributes for Waste + is_dangerous NUMERIC, + is_recyclable NUMERIC, +-- FK + cityobject_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_schedule +-- -------------------------------------------------------------------- +CREATE TABLE ng2_schedule ( + id BIGINT PRIMARY KEY, + objectclass_id INTEGER, + library_code VARCHAR, + library_code_codespace VARCHAR, + type VARCHAR, + type_codespace VARCHAR, + start_time TIME WITHOUT TIME ZONE, + start_day INTEGER, + start_month INTEGER, + start_year INTEGER, + time_interval NUMERIC, + time_interval_unit VARCHAR, +-- time_interval_factor INTEGER, -- used for temporalExtent +-- time_interval_radix INTEGER, -- used for temporalExtent +-- DualValue Schedule + idle_value NUMERIC, + idle_value_uom VARCHAR, + usage_value NUMERIC, + usage_value_uom VARCHAR, + start_usage_time TIME WITHOUT TIME ZONE, + end_usage_time TIME WITHOUT TIME ZONE, +-- Atomic Schedule + constant_value NUMERIC, + constant_value_uom VARCHAR, +-- FK + time_series_id BIGINT, + library_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_schedule_component +-- -------------------------------------------------------------------- +CREATE TABLE ng2_schedule_component ( + id BIGINT PRIMARY KEY, + type VARCHAR, + type_codespace VARCHAR, + repetitions INTEGER NOT NULL DEFAULT 1, + additional_gap NUMERIC, + additional_gap_unit VARCHAR, +-- additional_gap_factor INTEGER, +-- additional_gap_radix INTEGER, +-- FK + parent_schedule_id BIGINT, + schedule_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_solar_collector +-- -------------------------------------------------------------------- +CREATE TABLE ng2_solar_collector ( + id BIGINT PRIMARY KEY, + objectclass_id INTEGER, + type VARCHAR, + type_codespace VARCHAR, + cell_type VARCHAR, + cell_type_codespace VARCHAR, + module_area NUMERIC, + module_area_uom VARCHAR, + azimuth NUMERIC, + azimuth_uom VARCHAR, + inclination NUMERIC, + inclination_uom VARCHAR, + aperture_area NUMERIC, + aperture_area_uom VARCHAR, + opt_efficiency NUMERIC, + opt_efficiency_uom VARCHAR, + lin_heat_loss_coeff NUMERIC, + quad_heat_loss_coeff NUMERIC, +-- FK + lod2_multi_surface_id BIGINT, + lod3_multi_surface_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_storage_device +-- -------------------------------------------------------------------- +CREATE TABLE ng2_storage_device ( + id BIGINT PRIMARY KEY, + objectclass_id INTEGER, + medium VARCHAR, + medium_codespace VARCHAR, + preparation_temp NUMERIC, + preparation_temp_uom VARCHAR, + thm_losses_factor NUMERIC, + thm_losses_factor_uom VARCHAR, + volume NUMERIC, + volume_uom VARCHAR, + batt_techn VARCHAR, + batt_techn_codespace VARCHAR, + power_capacity NUMERIC, + power_capacity_uom VARCHAR +); + +-- -------------------------------------------------------------------- +-- ng2_suitability +-- -------------------------------------------------------------------- +CREATE TABLE ng2_suitability ( + id BIGINT PRIMARY KEY DEFAULT nextval('ng2_suitability_seq'::regclass), + reason VARCHAR, + reason_codespace VARCHAR, + value NUMERIC, + value_uom VARCHAR, + description VARCHAR, + schedule_id BIGINT, +-- FK + cityobject_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_them_surf_to_thermal_zone +-- -------------------------------------------------------------------- +CREATE TABLE ng2_them_surf_to_thermal_zone ( + thematic_surface_id BIGINT NOT NULL, + thermal_zone_id BIGINT NOT NULL, + PRIMARY KEY (thematic_surface_id, thermal_zone_id) +); + +-- -------------------------------------------------------------------- +-- ng2_thematic_surface - extends (building) thematic_surface table +-- -------------------------------------------------------------------- +CREATE TABLE ng2_thematic_surface ( + id BIGINT PRIMARY KEY, + total_surf_area NUMERIC, + total_surf_area_uom VARCHAR, + opaque_surf_area NUMERIC, + opaque_surf_area_uom VARCHAR, + open_to_surf_ratio NUMERIC, + open_to_surf_ratio_uom VARCHAR, + thickness NUMERIC, + thickness_uom VARCHAR, + azimuth NUMERIC, + azimuth_uom VARCHAR, + inclination NUMERIC, + inclination_uom VARCHAR, + ground_view_factor NUMERIC, + ground_view_factor_uom VARCHAR, + sky_view_factor NUMERIC, + sky_view_factor_uom VARCHAR, + is_adiabatic NUMERIC, + heat_capacity NUMERIC, + heat_capacity_uom VARCHAR +); + +-- -------------------------------------------------------------------- +-- ng2_time_series +-- -------------------------------------------------------------------- +CREATE TABLE ng2_time_series ( + id BIGINT PRIMARY KEY, + objectclass_id INTEGER, +-- AbstractTieSeries attributes + acquisition_method VARCHAR, + acquisition_method_codespace VARCHAR, + interpolation_type VARCHAR, + source VARCHAR, +-- Other attributes + period_begin TIMESTAMP WITH TIME ZONE, + period_end TIMESTAMP WITH TIME ZONE, + start_time TIME WITHOUT TIME ZONE, + start_day INTEGER, + start_month INTEGER, + temporal_extent NUMERIC, + temporal_extent_unit VARCHAR, +-- temporal_extent_factor INTEGER, +-- temporal_extent_radix INTEGER, + time_interval NUMERIC, + time_interval_unit VARCHAR, +-- time_interval_factor INTEGER, +-- time_interval_radix INTEGER, + values_list TEXT, + values_list_uom VARCHAR, + uom VARCHAR, + file_uri VARCHAR, + num_of_header_lines INTEGER, + field_separator VARCHAR, + record_separator VARCHAR, + value_column_number INTEGER, + decimal_symbol VARCHAR, + auth_type VARCHAR, + auth_type_codespace VARCHAR, + base_url VARCHAR, + connection_type VARCHAR, + connection_type_codespace VARCHAR, + datastream_id VARCHAR, + link_to_observation VARCHAR, + link_to_sensor_description VARCHAR, + mqtt_server VARCHAR, + mqtt_topic VARCHAR, + observation_id VARCHAR, + observation_property VARCHAR, + sensor_id VARCHAR, + sensor_name VARCHAR +); + +-- -------------------------------------------------------------------- +-- ng2_urban_function_area +-- -------------------------------------------------------------------- +CREATE TABLE ng2_urban_function_area ( + id BIGINT PRIMARY KEY, + type VARCHAR, + type_codespace VARCHAR, + code VARCHAR, + code_codespace VARCHAR +); + +-- -------------------------------------------------------------------- +-- ng2_utl_ntw_connection +-- -------------------------------------------------------------------- +CREATE TABLE ng2_utl_ntw_connection ( + id BIGINT PRIMARY KEY, + network_type VARCHAR, + network_type_codespace VARCHAR, + connection_status VARCHAR, + function_in_network VARCHAR, + function_in_network_codespace VARCHAR, + usage_in_network VARCHAR, + usage_in_network_codespace VARCHAR, + network_id VARCHAR, + network_node_id VARCHAR, +-- FK + cityobject_id BIGINT +); + +-- -------------------------------------------------------------------- +-- ng2_weather_data +-- -------------------------------------------------------------------- +CREATE TABLE ng2_weather_data ( + id BIGINT PRIMARY KEY, + type VARCHAR, + type_codespace VARCHAR, + value_type VARCHAR, + value_type_codespace VARCHAR, + yearly_value NUMERIC, + yearly_value_uom VARCHAR, + library_code VARCHAR, + library_code_codespace VARCHAR, +-- FK + time_series_id BIGINT, + cityobject_id BIGINT, +-- + position geometry(POINTZ) +); + +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- *********************************** Create foreign keys ******************************** +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +-- -------------------------------------------------------------------- +-- ng2_address_to_building_unit +-- -------------------------------------------------------------------- +ALTER TABLE ng2_address_to_building_unit ADD CONSTRAINT ng2_addr_to_bdgu_fk1 FOREIGN KEY (address_id) REFERENCES address (id) ON DELETE CASCADE; +ALTER TABLE ng2_address_to_building_unit ADD CONSTRAINT ng2_addr_to_bdgu_fk2 FOREIGN KEY (building_unit_id) REFERENCES ng2_building_partition (id) ON DELETE CASCADE; + +-- -------------------------------------------------------------------- +-- ng2_building +-- -------------------------------------------------------------------- +ALTER TABLE ng2_building ADD CONSTRAINT ng2_bdg_fk FOREIGN KEY (id) REFERENCES building (id); + +-- -------------------------------------------------------------------- +-- ng2_building_partition +-- -------------------------------------------------------------------- +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_lod1_fk FOREIGN KEY (lod1_solid_id) REFERENCES surface_geometry (id); +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_lod2_fk FOREIGN KEY (lod2_solid_id) REFERENCES surface_geometry (id); +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_lod3_fk FOREIGN KEY (lod3_solid_id) REFERENCES surface_geometry (id); +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_uz_fk FOREIGN KEY (usage_zone_id) REFERENCES ng2_building_partition (id) ON DELETE SET NULL; +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_tz_fk FOREIGN KEY (thermal_zone_id) REFERENCES ng2_building_partition (id) ON DELETE SET NULL; +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_bdg_fk FOREIGN KEY (building_id) REFERENCES ng2_building (id); +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_sched_fk1 FOREIGN KEY (cooling_schedule_id) REFERENCES ng2_schedule (id) ON DELETE SET NULL; +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_sched_fk2 FOREIGN KEY (heating_schedule_id) REFERENCES ng2_schedule (id) ON DELETE SET NULL; +ALTER TABLE ng2_building_partition ADD CONSTRAINT ng2_bdgp_sched_fk3 FOREIGN KEY (ventilation_schedule_id) REFERENCES ng2_schedule (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_cityobject +-- -------------------------------------------------------------------- +ALTER TABLE ng2_cityobject ADD CONSTRAINT ng2_cto_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_cityobject ADD CONSTRAINT ng2_cto_lcns_fk FOREIGN KEY (layered_construction_id) REFERENCES ng2_layered_construction (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_ctyobj_relation +-- -------------------------------------------------------------------- +ALTER TABLE ng2_ctyobj_relation ADD CONSTRAINT ng2_cto_rel_fk2 FOREIGN KEY (cityobject_id) REFERENCES cityobject (id) ON DELETE SET NULL; +ALTER TABLE ng2_ctyobj_relation ADD CONSTRAINT ng2_cto_rel_fk1 FOREIGN KEY (ng2_cityobject_id) REFERENCES ng2_cityobject (id); + +-- -------------------------------------------------------------------- +-- ng2_device +-- -------------------------------------------------------------------- +ALTER TABLE ng2_device ADD CONSTRAINT ng2_dev_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_device ADD CONSTRAINT ng2_dev_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); +ALTER TABLE ng2_device ADD CONSTRAINT ng2_dev_opt_fk FOREIGN KEY (transmittance_id) REFERENCES ng2_optical_property (id) ON DELETE SET NULL; +ALTER TABLE ng2_device ADD CONSTRAINT ng2_dev_ng2_cto_fk FOREIGN KEY (cityobject_id) REFERENCES ng2_cityobject (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_device_operation +-- -------------------------------------------------------------------- +ALTER TABLE ng2_device_operation ADD CONSTRAINT ng2_dev_opr_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_device_operation ADD CONSTRAINT ng2_dev_opr_sched_fk FOREIGN KEY (schedule_id) REFERENCES ng2_schedule (id) ON DELETE SET NULL; +ALTER TABLE ng2_device_operation ADD CONSTRAINT ng2_dev_opr_dev_fk FOREIGN KEY (device_id) REFERENCES ng2_device (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_energy_perf_cert +-- -------------------------------------------------------------------- +ALTER TABLE ng2_energy_perf_cert ADD CONSTRAINT ng2_epc_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_energy_perf_cert ADD CONSTRAINT ng2_epc_bdg_fk FOREIGN KEY (building_id) REFERENCES ng2_building (id) ON DELETE SET NULL; +ALTER TABLE ng2_energy_perf_cert ADD CONSTRAINT ng2_epc_bdg_part_fk FOREIGN KEY (building_partition_id) REFERENCES ng2_building_partition (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_layer +-- -------------------------------------------------------------------- +ALTER TABLE ng2_layer ADD CONSTRAINT ng2_lyr_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_layer ADD CONSTRAINT ng2_lyr_mat_fk FOREIGN KEY (material_id) REFERENCES ng2_material (id) ON DELETE SET NULL; +ALTER TABLE ng2_layer ADD CONSTRAINT ng2_lyr_lcns_fk FOREIGN KEY (layered_construction_id) REFERENCES ng2_layered_construction (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_layered_construction +-- -------------------------------------------------------------------- +ALTER TABLE ng2_layered_construction ADD CONSTRAINT ng2_lcns_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_layered_construction ADD CONSTRAINT ng2_lcns_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); +ALTER TABLE ng2_layered_construction ADD CONSTRAINT ng2_lcns_lcns_fk FOREIGN KEY (layered_construction_id) REFERENCES ng2_layered_construction (id) ON DELETE SET NULL; +ALTER TABLE ng2_layered_construction ADD CONSTRAINT ng2_lcns_lib_fk FOREIGN KEY (library_id) REFERENCES ng2_library (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_library +-- -------------------------------------------------------------------- +ALTER TABLE ng2_library ADD CONSTRAINT ng2_lib_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_library ADD CONSTRAINT ng2_lib_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); + +-- -------------------------------------------------------------------- +-- ng2_material +-- -------------------------------------------------------------------- +ALTER TABLE ng2_material ADD CONSTRAINT ng2_mat_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_material ADD CONSTRAINT ng2_mat_lib_fk FOREIGN KEY (library_id) REFERENCES ng2_library (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_occupants +-- -------------------------------------------------------------------- +ALTER TABLE ng2_occupants ADD CONSTRAINT ng2_occ_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_occupants ADD CONSTRAINT ng2_occ_sched_fk FOREIGN KEY (schedule_id) REFERENCES ng2_schedule (id) ON DELETE SET NULL; +ALTER TABLE ng2_occupants ADD CONSTRAINT ng2_occ_ng2_bdgp_fk FOREIGN KEY (building_partition_id) REFERENCES ng2_building_partition (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_opening +-- -------------------------------------------------------------------- +ALTER TABLE ng2_opening ADD CONSTRAINT ng2_opn_fk FOREIGN KEY (id) REFERENCES opening (id); + +-- -------------------------------------------------------------------- +-- ng2_optical_property +-- -------------------------------------------------------------------- +ALTER TABLE ng2_optical_property ADD CONSTRAINT ng2_optpty_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); +ALTER TABLE ng2_optical_property ADD CONSTRAINT ng2_optpty_lcns_fk FOREIGN KEY (layered_construction_id) REFERENCES ng2_layered_construction (id); + +-- -------------------------------------------------------------------- +-- ng2_qualified_attribute +-- -------------------------------------------------------------------- +ALTER TABLE ng2_qualified_attribute ADD CONSTRAINT ng2_qatt_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); +ALTER TABLE ng2_qualified_attribute ADD CONSTRAINT ng2_qatt_bdg_fk FOREIGN KEY (building_id) REFERENCES ng2_building (id); +ALTER TABLE ng2_qualified_attribute ADD CONSTRAINT ng2_qatt_bdg_part_fk FOREIGN KEY (building_partition_id) REFERENCES ng2_building_partition (id); + +-- -------------------------------------------------------------------- +-- ng2_refurbishment_measure +-- -------------------------------------------------------------------- +ALTER TABLE ng2_refurbishment_measure ADD CONSTRAINT ng2_refurb_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_refurbishment_measure ADD CONSTRAINT ng2_refurb_bdg_fk FOREIGN KEY (building_id) REFERENCES ng2_building (id) ON DELETE SET NULL; +ALTER TABLE ng2_refurbishment_measure ADD CONSTRAINT ng2_refurb_bdg_part_fk FOREIGN KEY (building_partition_id) REFERENCES ng2_building_partition (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_resource +-- -------------------------------------------------------------------- +ALTER TABLE ng2_resource ADD CONSTRAINT ng2_res_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_resource ADD CONSTRAINT ng2_res_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); +ALTER TABLE ng2_resource ADD CONSTRAINT ng2_res_ts_fk FOREIGN KEY (time_series_id) REFERENCES ng2_time_series (id) ON DELETE SET NULL; +ALTER TABLE ng2_resource ADD CONSTRAINT ng2_res_cto_fk FOREIGN KEY (cityobject_id) REFERENCES ng2_cityobject (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_schedule +-- -------------------------------------------------------------------- +ALTER TABLE ng2_schedule ADD CONSTRAINT ng2_sched_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_schedule ADD CONSTRAINT ng2_sched_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); +ALTER TABLE ng2_schedule ADD CONSTRAINT ng2_sched_ts_fk FOREIGN KEY (time_series_id) REFERENCES ng2_time_series (id) ON DELETE SET NULL; +ALTER TABLE ng2_schedule ADD CONSTRAINT ng2_sched_lib_fk FOREIGN KEY (library_id) REFERENCES ng2_library (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_schedule_component +-- -------------------------------------------------------------------- +ALTER TABLE ng2_schedule_component ADD CONSTRAINT ng2_sched_comp_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_schedule_component ADD CONSTRAINT ng2_sched_comp_sched_fk1 FOREIGN KEY (parent_schedule_id) REFERENCES ng2_schedule (id); +ALTER TABLE ng2_schedule_component ADD CONSTRAINT ng2_sched_comp_sched_fk2 FOREIGN KEY (schedule_id) REFERENCES ng2_schedule (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_solar_collector +-- -------------------------------------------------------------------- +ALTER TABLE ng2_solar_collector ADD CONSTRAINT ng2_sol_coll_fk FOREIGN KEY (id) REFERENCES ng2_device (id); +ALTER TABLE ng2_solar_collector ADD CONSTRAINT ng2_sol_coll_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); +ALTER TABLE ng2_solar_collector ADD CONSTRAINT ng2_sol_coll_lod2_fk FOREIGN KEY (lod2_multi_surface_id) REFERENCES surface_geometry (id); +ALTER TABLE ng2_solar_collector ADD CONSTRAINT ng2_sol_coll_lod3_fk FOREIGN KEY (lod3_multi_surface_id) REFERENCES surface_geometry (id); + +-- -------------------------------------------------------------------- +-- ng2_storage_device +-- -------------------------------------------------------------------- +ALTER TABLE ng2_storage_device ADD CONSTRAINT ng2_sto_dev_fk FOREIGN KEY (id) REFERENCES ng2_device (id); +ALTER TABLE ng2_storage_device ADD CONSTRAINT ng2_sto_dev_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); + +-- -------------------------------------------------------------------- +-- ng2_suitability +-- -------------------------------------------------------------------- +ALTER TABLE ng2_suitability ADD CONSTRAINT ng2_suit_cto_fk FOREIGN KEY (cityobject_id) REFERENCES ng2_cityobject (id); +ALTER TABLE ng2_suitability ADD CONSTRAINT ng2_suit_sched_fk FOREIGN KEY (schedule_id) REFERENCES ng2_schedule (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_them_surf_to_thermal_zone +-- -------------------------------------------------------------------- +ALTER TABLE ng2_them_surf_to_thermal_zone ADD CONSTRAINT ng2_thm_surf_to_tz_fk2 FOREIGN KEY (thermal_zone_id) REFERENCES ng2_building_partition (id); +ALTER TABLE ng2_them_surf_to_thermal_zone ADD CONSTRAINT ng2_thm_surf_to_tz_fk1 FOREIGN KEY (thematic_surface_id) REFERENCES thematic_surface (id) ON DELETE CASCADE; + +-- -------------------------------------------------------------------- +-- ng2_thematic_surface +-- -------------------------------------------------------------------- +ALTER TABLE ng2_thematic_surface ADD CONSTRAINT ng2_them_surf_fk FOREIGN KEY (id) REFERENCES thematic_surface (id); + +-- -------------------------------------------------------------------- +-- ng2_time_series +-- -------------------------------------------------------------------- +ALTER TABLE ng2_time_series ADD CONSTRAINT ng2_ts_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_time_series ADD CONSTRAINT ng2_ts_oc_fk FOREIGN KEY (objectclass_id) REFERENCES objectclass (id); + +-- -------------------------------------------------------------------- +-- ng2_urban_function_area +-- -------------------------------------------------------------------- +ALTER TABLE ng2_urban_function_area ADD CONSTRAINT ng2_ufa_fk FOREIGN KEY (id) REFERENCES cityobjectgroup (id); + +-- -------------------------------------------------------------------- +-- ng2_utl_ntw_connection +-- -------------------------------------------------------------------- +ALTER TABLE ng2_utl_ntw_connection ADD CONSTRAINT ng2_utl_ntw_con_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_utl_ntw_connection ADD CONSTRAINT ng2_utl_ntw_con_cto_fk FOREIGN KEY (cityobject_id) REFERENCES ng2_cityobject (id) ON DELETE SET NULL; + +-- -------------------------------------------------------------------- +-- ng2_weather_data +-- -------------------------------------------------------------------- +ALTER TABLE ng2_weather_data ADD CONSTRAINT ng2_wth_data_fk FOREIGN KEY (id) REFERENCES cityobject (id); +ALTER TABLE ng2_weather_data ADD CONSTRAINT ng2_wth_data_ts_fk FOREIGN KEY (time_series_id) REFERENCES ng2_time_series (id) ON DELETE SET NULL; +ALTER TABLE ng2_weather_data ADD CONSTRAINT ng2_wth_data_ng2_cto_fk FOREIGN KEY (cityobject_id) REFERENCES ng2_cityobject (id) ON DELETE SET NULL; + + +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- *********************************** Create Indexes ************************************* +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +-- -------------------------------------------------------------------- +-- ng2_address_to_building_unit +-- -------------------------------------------------------------------- +CREATE INDEX ng2_address_to_bdgu_fk1x ON ng2_address_to_building_unit USING btree (address_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_address_to_bdgu_fk2x ON ng2_address_to_building_unit USING btree (building_unit_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_building +-- -------------------------------------------------------------------- +-- no indices needed + +-- -------------------------------------------------------------------- +-- ng2_building_partition +-- -------------------------------------------------------------------- +CREATE INDEX ng2_bdgp_oc_fkx ON ng2_building_partition USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_bdgp_sched_fk1x ON ng2_building_partition USING btree (cooling_schedule_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_bdgp_sched_fk2x ON ng2_building_partition USING btree (heating_schedule_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_bdgp_sched_fk3x ON ng2_building_partition USING btree (ventilation_schedule_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_bdgp_uz_fkx ON ng2_building_partition USING btree (usage_zone_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_bdgp_tz_fkx ON ng2_building_partition USING btree (thermal_zone_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_bdgp_bdg_fkx ON ng2_building_partition USING btree (building_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_bdgp_solid1_fkx ON ng2_building_partition USING btree (lod1_solid_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_bdgp_solid2_fkx ON ng2_building_partition USING btree (lod2_solid_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_bdgp_solid3_fkx ON ng2_building_partition USING btree (lod3_solid_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_cityobject +-- -------------------------------------------------------------------- +CREATE INDEX ng2_cto_ref_point_spx ON ng2_cityobject USING gist (ref_point); +CREATE INDEX ng2_cto_lcns_fkx ON ng2_cityobject USING btree (layered_construction_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_ctyobj_relation +-- -------------------------------------------------------------------- +CREATE INDEX ng2_cto_rel_fk1x ON ng2_ctyobj_relation USING btree (ng2_cityobject_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_cto_rel_fk2x ON ng2_ctyobj_relation USING btree (cityobject_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_device +-- -------------------------------------------------------------------- +CREATE INDEX ng2_dev_oc_fkx ON ng2_device USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_dev_opt_fkx ON ng2_device USING btree (transmittance_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_dev_cto_fkx ON ng2_device USING btree (cityobject_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_device_operation +-- -------------------------------------------------------------------- +CREATE INDEX ng2_dev_opr_sched_fkx ON ng2_device_operation USING btree (schedule_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_dev_opr_dev_fkx ON ng2_device_operation USING btree (device_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_energy_perf_cert +-- -------------------------------------------------------------------- +CREATE INDEX ng2_epc_bdg_fkx ON ng2_energy_perf_cert USING btree (building_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_epc_bdg_part_fkx ON ng2_energy_perf_cert USING btree (building_partition_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_layer +-- -------------------------------------------------------------------- +CREATE INDEX ng2_lyr_mat_fkx ON ng2_layer USING btree (material_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_lyr_lcns_fkx ON ng2_layer USING btree (layered_construction_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_layered_constr +-- -------------------------------------------------------------------- +CREATE INDEX ng2_lcns_oc_fkx ON ng2_layered_construction USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_lcns_lcns_fkx ON ng2_layered_construction USING btree (layered_construction_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_lcns_lib_fkx ON ng2_layered_construction USING btree (library_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_library +-- -------------------------------------------------------------------- +CREATE INDEX ng2_lib_oc_fkx ON ng2_library USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_material +-- -------------------------------------------------------------------- +CREATE INDEX ng2_mat_oc_fkx ON ng2_material USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_mat_lib_fkx ON ng2_material USING btree (library_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_occupants +-- -------------------------------------------------------------------- +CREATE INDEX ng2_occ_sched_fkx ON ng2_occupants USING btree (schedule_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_occ_ng2_bdgp_fkx ON ng2_occupants USING btree (building_partition_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_opening +-- -------------------------------------------------------------------- +-- no indices needed + +-- -------------------------------------------------------------------- +-- ng2_optical_property +-- -------------------------------------------------------------------- +CREATE INDEX ng2_optpty_oc_fkx ON ng2_optical_property USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_optpty_lcns_fkx ON ng2_optical_property USING btree (layered_construction_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_qualified_attribute +-- -------------------------------------------------------------------- +CREATE INDEX ng2_qual_attr_oc_fkx ON ng2_qualified_attribute USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_qual_attr_bdg_fkx ON ng2_qualified_attribute USING btree (building_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_qual_attr_bdg_part_fkx ON ng2_qualified_attribute USING btree (building_partition_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_qual_attr_type_idx ON ng2_qualified_attribute USING btree (type ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_refurbishment_measure +-- -------------------------------------------------------------------- +CREATE INDEX ng2_ref_meas_bdg_fkx ON ng2_refurbishment_measure USING btree (building_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_ref_meas_bdg_part_fkx ON ng2_refurbishment_measure USING btree (building_partition_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_resource +-- -------------------------------------------------------------------- +CREATE INDEX ng2_res_oc_fkx ON ng2_resource USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_res_ts_fkx ON ng2_resource USING btree (time_series_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_res_cto_fkx ON ng2_resource USING btree (cityobject_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_schedule +-- -------------------------------------------------------------------- +CREATE INDEX ng2_sched_oc_fkx ON ng2_schedule USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_sched_ts_fkx ON ng2_schedule USING btree (time_series_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_sched_lib_fkx ON ng2_schedule USING btree (library_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_schedule_component +-- -------------------------------------------------------------------- +CREATE INDEX ng2_sched_comp_sched_fk1x ON ng2_schedule_component USING btree (parent_schedule_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_sched_comp_sched_fk2x ON ng2_schedule_component USING btree (schedule_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_solar_collector +-- -------------------------------------------------------------------- +CREATE INDEX ng2_sol_col_oc_fkx ON ng2_solar_collector USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_sol_col_lod2_fkx ON ng2_solar_collector USING btree (lod2_multi_surface_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_sol_col_lod3_fkx ON ng2_solar_collector USING btree (lod2_multi_surface_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_storage_device +-- -------------------------------------------------------------------- +CREATE INDEX ng2_sto_dev_oc_fkx ON ng2_storage_device USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_suitability +-- -------------------------------------------------------------------- +CREATE INDEX ng2_suit_cto_fkx ON ng2_suitability USING btree (cityobject_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_suit_sched_fkx ON ng2_suitability USING btree (schedule_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_suit_reas_fkx ON ng2_suitability USING btree (reason ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_them_surf_to_thermal_zone +-- -------------------------------------------------------------------- +CREATE INDEX ng2_thm_surf_to_tz_fk1x ON ng2_them_surf_to_thermal_zone USING btree (thematic_surface_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_thm_surf_to_tz_fk2x ON ng2_them_surf_to_thermal_zone USING btree (thermal_zone_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_thematic_surface +-- -------------------------------------------------------------------- +-- no indices needed + +-- -------------------------------------------------------------------- +-- ng2_time_series +-- -------------------------------------------------------------------- +CREATE INDEX ng2_ts_oc_fkx ON ng2_time_series USING btree (objectclass_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_urban_function_area +-- -------------------------------------------------------------------- +-- no indices needed + +-- -------------------------------------------------------------------- +-- ng2_utl_ntw_connection +-- -------------------------------------------------------------------- +CREATE INDEX ng2_utl_ntw_conn_cto_fkx ON ng2_utl_ntw_connection USING btree (cityobject_id ASC NULLS LAST) WITH (FILLFACTOR = 90); + +-- -------------------------------------------------------------------- +-- ng2_weather_data +-- -------------------------------------------------------------------- +CREATE INDEX ng2_wht_data_ts_fkx ON ng2_weather_data USING btree (time_series_id ASC NULLS LAST) WITH (FILLFACTOR = 90); +CREATE INDEX ng2_wht_data_cto_fkx ON ng2_weather_data USING btree (cityobject_id ASC NULLS LAST) WITH (FILLFACTOR = 90); diff --git a/resources/database/3dcitydb/postgreSQL/DROP_ADE_DB.sql b/resources/database/3dcitydb/postgreSQL/DROP_ADE_DB.sql new file mode 100644 index 0000000..c499046 --- /dev/null +++ b/resources/database/3dcitydb/postgreSQL/DROP_ADE_DB.sql @@ -0,0 +1,149 @@ +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- +-- CityGML Energy ADE 2.0 (beta 7) +-- +-- Last update: 2025-05-09 +-- +-- This DDL script uninstalls the 3DCityDB schema for the Energy ADE 2.0. It must be run +-- from within the ADE Manager plugin of the 3DCityDB Importer/Exporter. +-- +-- This script was first automatically generated using the 3DCityDB ADE Manager +-- and successively edited and restructured by: +-- +-- Dr. Giorgio Agugiaro +-- 3D Geoinformation group +-- Delft University of Technology +-- The Netherlands +-- +-- https://3d.bk.tudelft.nl/gagugiaro/ +-- +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- ******************************* DROP_ADE_DB.SQL execution START ************************ +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- *********************************** Drop foreign keys ********************************** +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +ALTER TABLE ng2_address_to_building_unit DROP CONSTRAINT ng2_addr_to_bdgu_fk1; +ALTER TABLE ng2_address_to_building_unit DROP CONSTRAINT ng2_addr_to_bdgu_fk2; +ALTER TABLE ng2_building DROP CONSTRAINT ng2_bdg_fk; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_bdg_fk; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_fk; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_lod1_fk; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_lod2_fk; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_lod3_fk; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_oc_fk; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_sched_fk1; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_sched_fk2; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_sched_fk3; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_tz_fk; +ALTER TABLE ng2_building_partition DROP CONSTRAINT ng2_bdgp_uz_fk; +ALTER TABLE ng2_cityobject DROP CONSTRAINT ng2_cto_fk; +ALTER TABLE ng2_cityobject DROP CONSTRAINT ng2_cto_lcns_fk; +ALTER TABLE ng2_ctyobj_relation DROP CONSTRAINT ng2_cto_rel_fk1; +ALTER TABLE ng2_ctyobj_relation DROP CONSTRAINT ng2_cto_rel_fk2; +ALTER TABLE ng2_device DROP CONSTRAINT ng2_dev_fk; +ALTER TABLE ng2_device DROP CONSTRAINT ng2_dev_ng2_cto_fk; +ALTER TABLE ng2_device DROP CONSTRAINT ng2_dev_oc_fk; +ALTER TABLE ng2_device DROP CONSTRAINT ng2_dev_opt_fk; +ALTER TABLE ng2_device_operation DROP CONSTRAINT ng2_dev_opr_fk; +ALTER TABLE ng2_device_operation DROP CONSTRAINT ng2_dev_opr_dev_fk; +ALTER TABLE ng2_device_operation DROP CONSTRAINT ng2_dev_opr_sched_fk; +ALTER TABLE ng2_energy_perf_cert DROP CONSTRAINT ng2_epc_fk; +ALTER TABLE ng2_energy_perf_cert DROP CONSTRAINT ng2_epc_bdg_fk; +ALTER TABLE ng2_energy_perf_cert DROP CONSTRAINT ng2_epc_bdg_part_fk; +ALTER TABLE ng2_layer DROP CONSTRAINT ng2_lyr_fk; +ALTER TABLE ng2_layer DROP CONSTRAINT ng2_lyr_lcns_fk; +ALTER TABLE ng2_layer DROP CONSTRAINT ng2_lyr_mat_fk; +ALTER TABLE ng2_layered_construction DROP CONSTRAINT ng2_lcns_fk; +ALTER TABLE ng2_layered_construction DROP CONSTRAINT ng2_lcns_lcns_fk; +ALTER TABLE ng2_layered_construction DROP CONSTRAINT ng2_lcns_oc_fk; +ALTER TABLE ng2_layered_construction DROP CONSTRAINT ng2_lcns_lib_fk; +ALTER TABLE ng2_library DROP CONSTRAINT ng2_lib_fk; +ALTER TABLE ng2_library DROP CONSTRAINT ng2_lib_oc_fk; +ALTER TABLE ng2_material DROP CONSTRAINT ng2_mat_fk; +ALTER TABLE ng2_material DROP CONSTRAINT ng2_mat_lib_fk; +ALTER TABLE ng2_occupants DROP CONSTRAINT ng2_occ_fk; +ALTER TABLE ng2_occupants DROP CONSTRAINT ng2_occ_ng2_bdgp_fk; +ALTER TABLE ng2_occupants DROP CONSTRAINT ng2_occ_sched_fk; +ALTER TABLE ng2_opening DROP CONSTRAINT ng2_opn_fk; +ALTER TABLE ng2_optical_property DROP CONSTRAINT ng2_optpty_lcns_fk; +ALTER TABLE ng2_optical_property DROP CONSTRAINT ng2_optpty_oc_fk; +ALTER TABLE ng2_qualified_attribute DROP CONSTRAINT ng2_qatt_oc_fk; +ALTER TABLE ng2_qualified_attribute DROP CONSTRAINT ng2_qatt_bdg_fk; +ALTER TABLE ng2_qualified_attribute DROP CONSTRAINT ng2_qatt_bdg_part_fk; +ALTER TABLE ng2_refurbishment_measure DROP CONSTRAINT ng2_refurb_fk; +ALTER TABLE ng2_refurbishment_measure DROP CONSTRAINT ng2_refurb_bdg_fk; +ALTER TABLE ng2_refurbishment_measure DROP CONSTRAINT ng2_refurb_bdg_part_fk; +ALTER TABLE ng2_resource DROP CONSTRAINT ng2_res_fk; +ALTER TABLE ng2_resource DROP CONSTRAINT ng2_res_oc_fk; +ALTER TABLE ng2_resource DROP CONSTRAINT ng2_res_ts_fk; +ALTER TABLE ng2_resource DROP CONSTRAINT ng2_res_cto_fk; +ALTER TABLE ng2_schedule DROP CONSTRAINT ng2_sched_fk; +ALTER TABLE ng2_schedule DROP CONSTRAINT ng2_sched_oc_fk; +ALTER TABLE ng2_schedule DROP CONSTRAINT ng2_sched_ts_fk; +ALTER TABLE ng2_schedule DROP CONSTRAINT ng2_sched_lib_fk; +ALTER TABLE ng2_schedule_component DROP CONSTRAINT ng2_sched_comp_fk; +ALTER TABLE ng2_schedule_component DROP CONSTRAINT ng2_sched_comp_sched_fk1; +ALTER TABLE ng2_schedule_component DROP CONSTRAINT ng2_sched_comp_sched_fk2; +ALTER TABLE ng2_solar_collector DROP CONSTRAINT ng2_sol_coll_fk; +ALTER TABLE ng2_solar_collector DROP CONSTRAINT ng2_sol_coll_oc_fk; +ALTER TABLE ng2_solar_collector DROP CONSTRAINT ng2_sol_coll_lod2_fk; +ALTER TABLE ng2_solar_collector DROP CONSTRAINT ng2_sol_coll_lod3_fk; +ALTER TABLE ng2_storage_device DROP CONSTRAINT ng2_sto_dev_fk; +ALTER TABLE ng2_storage_device DROP CONSTRAINT ng2_sto_dev_oc_fk; +ALTER TABLE ng2_suitability DROP CONSTRAINT ng2_suit_cto_fk; +ALTER TABLE ng2_suitability DROP CONSTRAINT ng2_suit_sched_fk; +ALTER TABLE ng2_them_surf_to_thermal_zone DROP CONSTRAINT ng2_thm_surf_to_tz_fk1; +ALTER TABLE ng2_them_surf_to_thermal_zone DROP CONSTRAINT ng2_thm_surf_to_tz_fk2; +ALTER TABLE ng2_thematic_surface DROP CONSTRAINT ng2_them_surf_fk; +ALTER TABLE ng2_time_series DROP CONSTRAINT ng2_ts_fk; +ALTER TABLE ng2_time_series DROP CONSTRAINT ng2_ts_oc_fk; +ALTER TABLE ng2_urban_function_area DROP CONSTRAINT ng2_ufa_fk; +ALTER TABLE ng2_utl_ntw_connection DROP CONSTRAINT ng2_utl_ntw_con_cto_fk; +ALTER TABLE ng2_utl_ntw_connection DROP CONSTRAINT ng2_utl_ntw_con_fk; +ALTER TABLE ng2_weather_data DROP CONSTRAINT ng2_wth_data_fk; +ALTER TABLE ng2_weather_data DROP CONSTRAINT ng2_wth_data_ng2_cto_fk; +ALTER TABLE ng2_weather_data DROP CONSTRAINT ng2_wth_data_ts_fk; + +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- *********************************** Drop tables *************************************** +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +DROP TABLE ng2_address_to_building_unit CASCADE; +DROP TABLE ng2_building CASCADE; +DROP TABLE ng2_building_partition CASCADE; +DROP TABLE ng2_cityobject CASCADE; +DROP TABLE ng2_ctyobj_relation CASCADE; +DROP TABLE ng2_device CASCADE; +DROP TABLE ng2_device_operation CASCADE; +DROP TABLE ng2_energy_perf_cert CASCADE; +DROP TABLE ng2_layer CASCADE; +DROP TABLE ng2_layered_construction CASCADE; +DROP TABLE ng2_library CASCADE; +DROP TABLE ng2_material CASCADE; +DROP TABLE ng2_occupants CASCADE; +DROP TABLE ng2_opening CASCADE; +DROP TABLE ng2_optical_property CASCADE; +DROP TABLE ng2_qualified_attribute CASCADE; +DROP TABLE ng2_refurbishment_measure CASCADE; +DROP TABLE ng2_resource CASCADE; +DROP TABLE ng2_schedule CASCADE; +DROP TABLE ng2_schedule_component CASCADE; +DROP TABLE ng2_solar_collector CASCADE; +DROP TABLE ng2_storage_device CASCADE; +DROP TABLE ng2_suitability CASCADE; +DROP TABLE ng2_them_surf_to_thermal_zone CASCADE; +DROP TABLE ng2_thematic_surface CASCADE; +DROP TABLE ng2_time_series CASCADE; +DROP TABLE ng2_urban_function_area CASCADE; +DROP TABLE ng2_utl_ntw_connection CASCADE; +DROP TABLE ng2_weather_data CASCADE; + +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-- *********************************** Drop Sequences ************************************* +-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +DROP SEQUENCE ng2_ctyobj_relation_seq; +DROP SEQUENCE ng2_optical_property_seq; +DROP SEQUENCE ng2_qualified_attribute_seq; +DROP SEQUENCE ng2_suitability_seq; \ No newline at end of file diff --git a/resources/database/schema-mapping/schema-mapping.xml b/resources/database/schema-mapping/schema-mapping.xml new file mode 100644 index 0000000..deaf3a8 --- /dev/null +++ b/resources/database/schema-mapping/schema-mapping.xml @@ -0,0 +1,1408 @@ + + + + + Energy ADE + 2.0 beta 7 (2025-06-25) + Database schema for the 3DCityDB 4.x + ng2 + + + + http://www.citygml.org/ade/energy/2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..c79eb41 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'energy-ade2-citydb' +includeBuild '../energy-ade2-citygml4j' \ No newline at end of file diff --git a/src/main/java/de/stuttgart/hft/EnergyADEExtension.java b/src/main/java/de/stuttgart/hft/EnergyADEExtension.java new file mode 100644 index 0000000..3d7fcab --- /dev/null +++ b/src/main/java/de/stuttgart/hft/EnergyADEExtension.java @@ -0,0 +1,59 @@ +package de.stuttgart.hft; + +import de.stuttgart.hft.ade.energy2.exporter.ExportManager; +import de.stuttgart.hft.ade.energy2.importer.ImportManager; +import de.stuttgart.hft.ade.energy2.schema.ObjectMapper; +import de.stuttgart.hft.ade.energy2.schema.SchemaMapper; +import org.citydb.core.ade.ADEExtension; +import org.citydb.core.ade.ADEExtensionException; +import org.citydb.core.ade.ADEObjectMapper; +import org.citydb.core.ade.exporter.ADEExportManager; +import org.citydb.core.ade.importer.ADEImportManager; +import org.citydb.core.database.schema.mapping.SchemaMapping; +import org.citydb.gui.ImpExpLauncher; +import org.citygml4j.ade.energy.EnergyADEContext; +import org.citygml4j.model.citygml.ade.binding.ADEContext; + +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; + +public class EnergyADEExtension extends ADEExtension { + private final ObjectMapper objectMapper = new ObjectMapper(); + private final SchemaMapper schemaMapper = new SchemaMapper(); + private final EnergyADEContext context = new EnergyADEContext(); + + public static void main(String[] args) { + EnergyADEExtension adeExtension = new EnergyADEExtension(); + adeExtension.setBasePath(Paths.get("resources/database").toAbsolutePath()); + new ImpExpLauncher().withArgs(args) + .withADEExtension(adeExtension) + .start(); + } + + @Override + public void init(SchemaMapping schemaMapping) throws ADEExtensionException { + objectMapper.populateObjectClassIds(schemaMapping); + schemaMapper.populateSchemaNames(schemaMapping.getMetadata().getDBPrefix().toLowerCase()); + } + + @Override + public List getADEContexts() { + return Collections.singletonList(context); + } + + @Override + public ADEObjectMapper getADEObjectMapper() { + return objectMapper; + } + + @Override + public ADEImportManager createADEImportManager() { + return new ImportManager(this, schemaMapper); + } + + @Override + public ADEExportManager createADEExportManager() { + return new ExportManager(objectMapper, schemaMapper); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/exporter/AbstractQualifiedAttributeExporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/AbstractQualifiedAttributeExporter.java new file mode 100644 index 0000000..db9a778 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/AbstractQualifiedAttributeExporter.java @@ -0,0 +1,135 @@ +package de.stuttgart.hft.ade.energy2.exporter; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.core.ade.exporter.ADEExporter; +import org.citydb.core.ade.exporter.CityGMLExportHelper; +import org.citydb.core.database.schema.mapping.AbstractType; +import org.citydb.core.database.schema.mapping.ComplexType; +import org.citydb.core.database.schema.mapping.SchemaMapping; +import org.citydb.core.operation.exporter.CityGMLExportException; +import org.citydb.core.query.filter.projection.ProjectionFilter; +import org.citydb.core.registry.ObjectRegistry; +import org.citygml4j.ade.energy.model.core.*; +import org.citygml4j.ade.energy.model.module.EnergyADEModule; +import org.citygml4j.model.gml.basicTypes.Code; +import org.citygml4j.model.gml.basicTypes.Measure; +import org.citygml4j.model.gml.base.StringOrRef; + +import java.lang.reflect.InvocationTargetException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class AbstractQualifiedAttributeExporter implements ADEExporter { + + private final PreparedStatement ps; + private final CityGMLExportHelper helper; + private final ExportManager manager; + private final Connection connection; + private WeatherDataExporter weatherDataExporter; + private ResourceExporter resourceExporter; + private String module; + private Map> attributeClassMap; + + public AbstractQualifiedAttributeExporter(Connection connection, CityGMLExportHelper helper, ExportManager manager) throws CityGMLExportException, SQLException { + this.connection = connection; + this.helper = helper; + this.manager = manager; + attributeClassMap = new HashMap<>(); + SchemaMapping schemaMapping = ObjectRegistry.getInstance().getSchemaMapping(); + ComplexType qualifiedHeightType = schemaMapping.getComplexType("QualifiedHeight", "http://www.citygml.org/ade/energy/2.0"); + int objectClassIdOfQualifiedHeight = qualifiedHeightType.getObjectClassId(); + attributeClassMap.put(objectClassIdOfQualifiedHeight, QualifiedHeight.class); + + ComplexType qualifiedAreaType = schemaMapping.getComplexType("QualifiedArea", "http://www.citygml.org/ade/energy/2.0"); + int objectClassIdOfQualifiedArea = qualifiedAreaType.getObjectClassId(); + attributeClassMap.put(objectClassIdOfQualifiedArea, QualifiedArea.class); + + ComplexType qualifiedVolumeType = schemaMapping.getComplexType("QualifiedVolume", "http://www.citygml.org/ade/energy/2.0"); + int objectClassIdOfQualifiedVolume = qualifiedVolumeType.getObjectClassId(); + attributeClassMap.put(objectClassIdOfQualifiedVolume, QualifiedVolume.class); + + String tableName = helper.getTableNameWithSchema( + manager.getSchemaMapper().getTableName(ADETable.QUALIFIED_ATTRIBUTE) + ); + + String select = "SELECT id, objectclass_id, type, type_codespace, \"value\", value_uom, description, source, building_id, building_partition_id " + + "FROM " + tableName + " WHERE building_id = ?"; + + module = EnergyADEModule.v2_0.getNamespaceURI(); + ps = connection.prepareStatement(select); + } + + public Collection doExport(long objectId) throws SQLException { + ps.setLong(1, objectId); + + try (ResultSet rs = ps.executeQuery()) { + Collection result = new ArrayList<>(); + + while (rs.next()) { + long objectIdFromDB = rs.getLong(1); + int objectClassId = rs.getInt(2); + + AbstractQualifiedAttribute attribute = createQualifiedAttribute(objectClassId); + + String descriptionValue = rs.getString(7); + if (descriptionValue != null && !descriptionValue.trim().isEmpty()) { + StringOrRef description = new StringOrRef(); + description.setValue(descriptionValue); + attribute.setDescription(description); + // System.out.println("Set description for QualifiedAttribute id=" + objectIdFromDB + ": " + descriptionValue); + } + + if (rs.getString(8) != null) + attribute.setSource(rs.getString(8)); + + Measure value = new Measure(); + value.setValue(rs.getDouble(5)); + value.setUom(rs.getString(6)); + attribute.setValue(value); + + if (attribute instanceof QualifiedHeight qualifiedHeight) { + Code type = new Code(); + type.setValue(rs.getString(3)); + type.setCodeSpace(rs.getString(4)); + qualifiedHeight.setType(type); + } else if (attribute instanceof QualifiedArea qualifiedArea) { + Code type = new Code(); + type.setValue(rs.getString(3)); + type.setCodeSpace(rs.getString(4)); + qualifiedArea.setType(type); + } else if (attribute instanceof QualifiedVolume qualifiedVolume) { + Code type = new Code(); + type.setValue(rs.getString(3)); + type.setCodeSpace(rs.getString(4)); + qualifiedVolume.setType(type); + } + + result.add(attribute); + } + + return result; + } + } + + private AbstractQualifiedAttribute createQualifiedAttribute(int objectClassId) { + + try { + Class aClass = attributeClassMap.get(objectClassId); + return aClass.getDeclaredConstructor().newInstance(); + + }catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException exception){ + throw new IllegalStateException(exception); + } + } + + @Override + public void close() throws CityGMLExportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/exporter/BuildingPropertiesExporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/BuildingPropertiesExporter.java new file mode 100644 index 0000000..3c33d64 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/BuildingPropertiesExporter.java @@ -0,0 +1,137 @@ +package de.stuttgart.hft.ade.energy2.exporter; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import net.opengis.gml.CodeType; +import org.citydb.core.ade.exporter.ADEExporter; +import org.citydb.core.ade.exporter.CityGMLExportHelper; +import org.citydb.core.database.schema.mapping.AbstractType; +import org.citydb.core.operation.exporter.CityGMLExportException; +import org.citydb.core.query.filter.projection.ProjectionFilter; +import org.citygml4j.ade.energy.model.core.*; +import org.citygml4j.ade.energy.model.module.EnergyADEModule; +import org.citygml4j.model.citygml.building.AbstractBuilding; +import org.sig3d.citygml.energyADE2beta7.ThermalStatusValueType; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +public class BuildingPropertiesExporter implements ADEExporter { + private final PreparedStatement ps; + private final CityGMLExportHelper helper; + private final ExportManager manager; + private final Connection connection; + private final AbstractQualifiedAttributeExporter abstractQualifiedAttributeExporter; + private final RefurbishmentMeasureExporter refurbishmentMeasureExporter; + private final String module; + + public BuildingPropertiesExporter(Connection connection, CityGMLExportHelper helper, ExportManager manager) throws CityGMLExportException, SQLException { + this.connection = connection; + this.helper = helper; + this.manager = manager; + + String select = "select id, type, type_codespace, is_protected, constr_weight, constr_weight_codespace, attic_thm_status, basement_thm_status from " + + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.BUILDING)) + + " where id = ?"; + + module = EnergyADEModule.v2_0.getNamespaceURI(); + ps = connection.prepareStatement(select); + abstractQualifiedAttributeExporter = manager.getExporter(AbstractQualifiedAttributeExporter.class); + refurbishmentMeasureExporter = manager.getExporter(RefurbishmentMeasureExporter.class); + } + + public void doExport(AbstractBuilding building, long objectId, AbstractType objectType, ProjectionFilter projectionFilter) throws SQLException, CityGMLExportException { + ps.setLong(1, objectId); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + // Building type + if (projectionFilter.containsProperty("buildingType", module)) { + String type = rs.getString("type"); + String typeCodespace = rs.getString("type_codespace"); + if (type != null || typeCodespace != null) { + CodeType bdgType = new CodeType(); + if (type != null) + bdgType.setValue(type); + if (typeCodespace != null) + bdgType.setCodeSpace(typeCodespace); + BuildingTypeProperty buildingTypeProperty = new BuildingTypeProperty(); + buildingTypeProperty.setValue(bdgType); + building.addGenericApplicationPropertyOfAbstractBuilding(buildingTypeProperty); + } + } + + // Is protected + if (projectionFilter.containsProperty("isProtected", module)) { + int isProtected = rs.getInt("is_protected"); + boolean wasNull = rs.wasNull(); + Boolean isProtectedValue = wasNull ? false : (isProtected == 1); + BuildingIsProtectedProperty buildingIsProtectedProperty = new BuildingIsProtectedProperty(); + buildingIsProtectedProperty.setValue(isProtectedValue); + building.addGenericApplicationPropertyOfAbstractBuilding(buildingIsProtectedProperty); + } + + // Construction weight + if (projectionFilter.containsProperty("constructionWeight", module)) { + String constrWeightValue = rs.getString("constr_weight"); + String constrWeightCodeSpace = rs.getString("constr_weight_codespace"); + if (constrWeightValue != null || constrWeightCodeSpace != null) { + CodeType constrWeight = new CodeType(); + constrWeight.setValue(constrWeightValue); + constrWeight.setCodeSpace(constrWeightCodeSpace); + BuildingConstructionWeightProperty buildingConstructionWeightProperty = new BuildingConstructionWeightProperty(); + buildingConstructionWeightProperty.setValue(constrWeight); + building.addGenericApplicationPropertyOfAbstractBuilding(buildingConstructionWeightProperty); + } + } + + // Attic thermal status + if (projectionFilter.containsProperty("atticThermalStatus", module)) { + String atticThmStatusValue = rs.getString("attic_thm_status"); + if (atticThmStatusValue != null) { + ThermalStatusValueType thermalStatusValue = ThermalStatusValueType.fromValue(atticThmStatusValue); + BuildingAtticThermalStatusProperty buildingAtticThmStatusProperty = new BuildingAtticThermalStatusProperty(); + buildingAtticThmStatusProperty.setValue(thermalStatusValue); + building.addGenericApplicationPropertyOfAbstractBuilding(buildingAtticThmStatusProperty); + } + } + + // Basement thermal status + if (projectionFilter.containsProperty("basementThermalStatus", module)) { + String basementThmStatusValue = rs.getString("basement_thm_status"); + if (basementThmStatusValue != null) { + ThermalStatusValueType thermalStatusValueType = ThermalStatusValueType.fromValue(basementThmStatusValue); + BuildingBasementThermalStatusProperty buildingBasementThermalStatusProperty = new BuildingBasementThermalStatusProperty(); + buildingBasementThermalStatusProperty.setValue(thermalStatusValueType); + building.addGenericApplicationPropertyOfAbstractBuilding(buildingBasementThermalStatusProperty); + } + } + + // Refurbishment measures + if (projectionFilter.containsProperty("refurbishmentMeasure", module)) { + Collection refurbProps = refurbishmentMeasureExporter.doExport(objectId, objectType, projectionFilter); + for (RefurbishmentMeasureProperty refurbProp : refurbProps) { + building.addGenericApplicationPropertyOfAbstractBuilding(refurbProp); + } + } + + // Qualified attributes + if (projectionFilter.containsProperty("qualifiedAttribute", module)) { + for (AbstractQualifiedAttribute qualifiedAttribute : abstractQualifiedAttributeExporter.doExport(objectId)) { + AbstractQualifiedAttributeProperty property = new AbstractQualifiedAttributeProperty(qualifiedAttribute); + building.addGenericApplicationPropertyOfAbstractBuilding(property); + } + } + } + } + } + + @Override + public void close() throws CityGMLExportException, SQLException { + ps.close(); + abstractQualifiedAttributeExporter.close(); + refurbishmentMeasureExporter.close(); + } +} \ No newline at end of file diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/exporter/CityObjectPropertiesExporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/CityObjectPropertiesExporter.java new file mode 100644 index 0000000..5809304 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/CityObjectPropertiesExporter.java @@ -0,0 +1,91 @@ +package de.stuttgart.hft.ade.energy2.exporter; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.config.geometry.GeometryObject; +import org.citydb.core.ade.exporter.ADEExporter; +import org.citydb.core.ade.exporter.CityGMLExportHelper; +import org.citydb.core.database.schema.mapping.AbstractType; +import org.citydb.core.operation.exporter.CityGMLExportException; +import org.citydb.core.query.filter.projection.ProjectionFilter; +import org.citygml4j.ade.energy.model.core.*; +import org.citygml4j.ade.energy.model.module.EnergyADEModule; +import org.citygml4j.model.citygml.core.AbstractCityObject; +import org.citygml4j.model.gml.geometry.primitives.PointProperty; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; + +public class CityObjectPropertiesExporter implements ADEExporter { + + private final PreparedStatement ps; + private final CityGMLExportHelper helper; + private final ExportManager manager; + private final Connection connection; + private WeatherDataExporter weatherDataExporter; + private ResourceExporter resourceExporter; + private String module; + + public CityObjectPropertiesExporter(Connection connection, CityGMLExportHelper helper, ExportManager manager) throws CityGMLExportException, SQLException { + this.connection = connection; + this.helper = helper; + this.manager = manager; + + String select = "select id, ref_point from " + + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.CITYOBJECT)) + + " where id = ?"; + + module = EnergyADEModule.v2_0.getNamespaceURI(); + ps = connection.prepareStatement(select); + + weatherDataExporter = manager.getExporter(WeatherDataExporter.class); + resourceExporter = manager.getExporter(ResourceExporter.class); + } + + public Collection doExport(AbstractCityObject cityObject, long objectId, AbstractType objectType, ProjectionFilter projectionFilter) throws SQLException { + ps.setLong(1, objectId); + + try (ResultSet rs = ps.executeQuery()) { + Collection result = new ArrayList<>(); + while (rs.next()) { + Object pointObj = rs.getObject(2); + if (projectionFilter.containsProperty("weatherData", module)) { + for (WeatherData weatherData : weatherDataExporter.doExport(objectId)) { + WeatherDataProperty property = new WeatherDataProperty(weatherData); + cityObject.addGenericApplicationPropertyOfCityObject(property); + } + } + + if (projectionFilter.containsProperty("resource", module)) { + for (AbstractResource resource : resourceExporter.doExport(objectId)) { + AbstractResourceProperty property = new AbstractResourceProperty(resource); + cityObject.addGenericApplicationPropertyOfCityObject(property); + } + } + + if (pointObj != null) { + GeometryObject pointObject = helper.getDatabaseAdapter().getGeometryConverter().getPoint(pointObj); + if (pointObject != null) { + PointProperty point = helper.getGMLConverter().getPointProperty(pointObject , false); + ReferencePointProperty referencePointProperty = new ReferencePointProperty(point); + cityObject.getGenericApplicationPropertyOfCityObject().add(referencePointProperty); + } + } + + result.add(cityObject); + } + return result; + } catch (CityGMLExportException e) { + throw new RuntimeException(e); + } + } + + + @Override + public void close() throws CityGMLExportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/exporter/ExportManager.java b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/ExportManager.java new file mode 100644 index 0000000..057d069 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/ExportManager.java @@ -0,0 +1,146 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stuttgart.hft.ade.energy2.exporter; + + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import de.stuttgart.hft.ade.energy2.schema.ObjectMapper; +import de.stuttgart.hft.ade.energy2.schema.SchemaMapper; +import org.citydb.core.ade.exporter.ADEExportManager; +import org.citydb.core.ade.exporter.ADEExporter; +import org.citydb.core.ade.exporter.CityGMLExportHelper; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.database.schema.mapping.FeatureType; +import org.citydb.core.operation.exporter.CityGMLExportException; +import org.citydb.core.query.filter.projection.ProjectionFilter; +import org.citygml4j.ade.energy.model.core.AbstractResource; +import org.citygml4j.ade.energy.model.core.QualifiedHeight; +import org.citygml4j.ade.energy.model.core.WeatherData; +import org.citygml4j.ade.energy.model.supportingClasses.*; +import org.citygml4j.model.citygml.ade.binding.ADEModelObject; +import org.citygml4j.model.citygml.building.AbstractBuilding; +import org.citygml4j.model.citygml.core.AbstractCityObject; +import org.citygml4j.model.gml.feature.AbstractFeature; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +public class ExportManager implements ADEExportManager { + private final Map, ADEExporter> exporters; + private final ObjectMapper objectMapper; + private final SchemaMapper schemaMapper; + + private Connection connection; + private CityGMLExportHelper helper; + + public ExportManager(ObjectMapper objectMapper, SchemaMapper schemaMapper) { + this.objectMapper = objectMapper; + this.schemaMapper = schemaMapper; + exporters = new HashMap<>(); + } + + @Override + public void init(Connection connection, CityGMLExportHelper helper) throws CityGMLExportException, SQLException { + this.connection = connection; + this.helper = helper; + } + + @Override + public void exportObject(ADEModelObject object, long objectId, AbstractObjectType objectType, ProjectionFilter projectionFilter) throws CityGMLExportException, SQLException { + if (object instanceof WeatherStation) + getExporter(WeatherStationExporter.class).doExport((WeatherStation) object, objectId, objectType, projectionFilter); + else if (object instanceof WeatherData) + getExporter(WeatherDataExporter.class).doExport(objectId); + else if (object instanceof UrbanFunctionArea) + getExporter(UrbanFunctionAreaExporter.class).doExport((UrbanFunctionArea) object, objectId, objectType, projectionFilter); + else if (object instanceof AbstractTimeSeries) + getExporter(TimeSeriesExporter.class).doExport(objectId); + else if (object instanceof AbstractResource) + getExporter(ResourceExporter.class).doExport(objectId); + } + + @Override + public void exportGenericApplicationProperties(String adeHookTable, AbstractFeature parent, long parentId, FeatureType parentType, ProjectionFilter projectionFilter) throws CityGMLExportException, SQLException { + if (adeHookTable.equals(schemaMapper.getTableName(ADETable.TIME_SERIES)) && parent instanceof AbstractTimeSeries) + getExporter(TimeSeriesExporter.class).doExport(parentId); + else if (adeHookTable.equals(schemaMapper.getTableName(ADETable.CITYOBJECT)) && parent instanceof AbstractCityObject) + getExporter(CityObjectPropertiesExporter.class).doExport((AbstractCityObject) parent, parentId, parentType, projectionFilter); + else if (adeHookTable.equals(schemaMapper.getTableName(ADETable.RESOURCE)) && parent instanceof AbstractResource) + getExporter(ResourceExporter.class).doExport(parentId); + else if (adeHookTable.equals(schemaMapper.getTableName(ADETable.BUILDING)) && parent instanceof AbstractBuilding) + getExporter(BuildingPropertiesExporter.class).doExport((AbstractBuilding) parent, parentId, parentType, projectionFilter); + } + + @Override + public void close() throws CityGMLExportException, SQLException { + for (ADEExporter exporter : exporters.values()) + exporter.close(); + } + + protected ObjectMapper getObjectMapper() { + return objectMapper; + } + + protected SchemaMapper getSchemaMapper() { + return schemaMapper; + } + + protected T getExporter(Class type) throws CityGMLExportException, SQLException { + ADEExporter exporter = exporters.get(type); + + if (exporter == null) { + if (type == TimeSeriesExporter.class) + exporter = new TimeSeriesExporter(connection, helper, this); + else if (type == UrbanFunctionAreaExporter.class) + exporter = new UrbanFunctionAreaExporter(connection, helper, this); + else if (type == WeatherStationExporter.class) + exporter = new WeatherStationExporter(connection, helper, this); + else if (type == ResourceExporter.class) + exporter = new ResourceExporter(connection, helper, this); + else if (type == WeatherDataExporter.class) + exporter = new WeatherDataExporter(connection, helper, this); + else if (type == CityObjectPropertiesExporter.class) + exporter = new CityObjectPropertiesExporter(connection, helper, this); + else if (type == BuildingPropertiesExporter.class) + exporter = new BuildingPropertiesExporter(connection, helper, this); + else if (type == AbstractQualifiedAttributeExporter.class) + exporter = new AbstractQualifiedAttributeExporter(connection, helper, this); + else if (type == RefurbishmentMeasureExporter.class) + exporter = new RefurbishmentMeasureExporter(connection, helper, this); + if (exporter == null) + throw new SQLException("Failed to build ADE exporter of type " + type.getName() + "."); + + exporters.put(type, exporter); + } + + return type.cast(exporter); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/exporter/RefurbishmentMeasureExporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/RefurbishmentMeasureExporter.java new file mode 100644 index 0000000..b1306ce --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/RefurbishmentMeasureExporter.java @@ -0,0 +1,152 @@ +package de.stuttgart.hft.ade.energy2.exporter; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.core.ade.exporter.ADEExporter; +import org.citydb.core.ade.exporter.CityGMLExportHelper; +import org.citydb.core.database.schema.mapping.AbstractType; +import org.citydb.core.operation.exporter.CityGMLExportException; +import org.citydb.core.query.filter.projection.ProjectionFilter; +import org.citygml4j.ade.energy.model.core.RefurbishmentMeasure; +import org.citygml4j.ade.energy.model.core.RefurbishmentMeasureProperty; +import org.citygml4j.ade.energy.model.module.EnergyADEModule; +import org.citygml4j.model.gml.basicTypes.Code; +import org.citygml4j.model.gml.base.StringOrRef; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.GregorianCalendar; + +public class RefurbishmentMeasureExporter implements ADEExporter { + private final PreparedStatement ps; + private final PreparedStatement cityObjectPs; + private final CityGMLExportHelper helper; + private final ExportManager manager; + private final Connection connection; + private final String module; + + public RefurbishmentMeasureExporter(Connection connection, CityGMLExportHelper helper, ExportManager manager) throws CityGMLExportException, SQLException { + this.connection = connection; + this.helper = helper; + this.manager = manager; + + String select = "SELECT id, type, type_codespace, start_date, end_date, library_code, library_code_codespace, building_id, building_partition_id FROM " + + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.REFURBISHMENT_MEASURE)) + + " WHERE building_id = ?"; + module = EnergyADEModule.v2_0.getNamespaceURI(); + ps = connection.prepareStatement(select); + + // Prepare statement for cityobject table + String cityObjectSelect = "SELECT gmlid, description FROM " + + helper.getTableNameWithSchema("cityobject") + + " WHERE id = ?"; + cityObjectPs = connection.prepareStatement(cityObjectSelect); + } + + public Collection doExport(long objectId, AbstractType objectType, ProjectionFilter projectionFilter) throws SQLException { + ps.setLong(1, objectId); // Set the building_id parameter + Collection result = new ArrayList<>(); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + long refurbId = rs.getLong("id"); + RefurbishmentMeasure refurbishmentMeasure = new RefurbishmentMeasure(); + + // Fetch gmlid and description from cityobject + cityObjectPs.setLong(1, refurbId); + try (ResultSet cityObjectRs = cityObjectPs.executeQuery()) { + if (cityObjectRs.next()) { + String gmlId = cityObjectRs.getString("gmlid"); + if (!cityObjectRs.wasNull()) { + refurbishmentMeasure.setId(gmlId); + } + String description = cityObjectRs.getString("description"); + if (!cityObjectRs.wasNull()) { + StringOrRef descriptionType = new StringOrRef(); + descriptionType.setValue(description); + refurbishmentMeasure.setDescription(descriptionType); + } + } + } + + // Set type + if (projectionFilter.containsProperty("type", module)) { + String type = rs.getString("type"); + String typeCodespace = rs.getString("type_codespace"); + if (type != null || typeCodespace != null) { + Code refMeasureType = new Code(); + if (type != null) { + refMeasureType.setValue(type); + } + if (typeCodespace != null) { + refMeasureType.setCodeSpace(typeCodespace); + } + refurbishmentMeasure.setType(refMeasureType); + } + } + + // Set start date + if (projectionFilter.containsProperty("startDate", module)) { + Date start_date = rs.getDate("start_date"); + if (start_date != null) { + GregorianCalendar cal = new GregorianCalendar(); + cal.setTime(start_date); + XMLGregorianCalendar xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal); + //xmlCal.setTimezone(DatatypeConstants.FIELD_UNDEFINED); + refurbishmentMeasure.setStartDate(xmlCal); + } + } + + // Set end date + if (projectionFilter.containsProperty("endDate", module)) { + Date end_date = rs.getDate("end_date"); + if (end_date != null) { + GregorianCalendar cal = new GregorianCalendar(); + cal.setTime(end_date); + XMLGregorianCalendar xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal); + // xmlCal.setTimezone(DatatypeConstants.FIELD_UNDEFINED); + refurbishmentMeasure.setEndDate(xmlCal); + } + } + + // Set library code + if (projectionFilter.containsProperty("libraryCode", module)) { + String library_code = rs.getString("library_code"); + String library_code_codespace = rs.getString("library_code_codespace"); + if (library_code != null || library_code_codespace != null) { + Code library = new Code(); + if (library_code != null) { + library.setValue(library_code); + } + if (library_code_codespace != null) { + library.setCodeSpace(library_code_codespace); + } + refurbishmentMeasure.setLibraryCode(library); + } + } + + // Wrap in RefurbishmentMeasureProperty + RefurbishmentMeasureProperty refurbProp = new RefurbishmentMeasureProperty(); + refurbProp.setRefurbishmentMeasure(refurbishmentMeasure); + result.add(refurbProp); + } + return result; + } catch (DatatypeConfigurationException e) { + throw new SQLException("Failed to convert date: " + e.getMessage(), e); + } catch (SQLException e) { + throw e; + } + } + + @Override + public void close() throws CityGMLExportException, SQLException { + ps.close(); + cityObjectPs.close(); + } +} \ No newline at end of file diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/exporter/ResourceExporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/ResourceExporter.java new file mode 100644 index 0000000..2a8d838 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/ResourceExporter.java @@ -0,0 +1,322 @@ +package de.stuttgart.hft.ade.energy2.exporter; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.core.ade.exporter.ADEExporter; +import org.citydb.core.ade.exporter.CityGMLExportHelper; +import org.citydb.core.database.schema.mapping.ObjectType; +import org.citydb.core.operation.exporter.CityGMLExportException; +import org.citydb.core.query.filter.projection.ProjectionFilter; +import org.citygml4j.ade.energy.model.core.AbstractResource; +import org.citygml4j.ade.energy.model.supportingClasses.*; +import org.citygml4j.model.gml.basicTypes.Code; +import org.citygml4j.model.gml.basicTypes.Measure; +import org.sig3d.citygml.energyADE2beta7.ResourceStatusValueType; + +import java.sql.*; +import java.util.ArrayList; +import java.util.Collection; + +public class ResourceExporter implements ADEExporter { + private final CityGMLExportHelper helper; + private TimeSeriesExporter timeSeriesExporter; + private PreparedStatement ps; + + public ResourceExporter(Connection connection, CityGMLExportHelper helper, ExportManager manager) + throws CityGMLExportException, SQLException { + this.helper = helper; + String tableName = helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.RESOURCE)); + String sql = "SELECT " + getColumnNames() + " FROM " + tableName + " WHERE cityobject_id = ?"; + ps = connection.prepareStatement(sql); + timeSeriesExporter = manager.getExporter(TimeSeriesExporter.class); + } + + public Collection doExport(long cityObjectId) throws CityGMLExportException, SQLException { + ps.setLong(1, cityObjectId); + try (ResultSet rs = ps.executeQuery()) { + Collection result = new ArrayList<>(); + while (rs.next()) { + long objectId = rs.getLong(1); + int objectClassId = rs.getInt(2); + if (rs.wasNull()) { + continue; + } + + AbstractResource resource = helper.createObject(objectId, objectClassId, AbstractResource.class); + if (resource == null) { + helper.logOrThrowErrorMessage("Failed to instantiate " + helper.getObjectSignature(objectClassId, cityObjectId) + " as resource object."); + continue; + } + + ObjectType objectType = helper.getObjectType(objectClassId); + ProjectionFilter projectionFilter = helper.getProjectionFilter(objectType); + + // Set AbstractResource properties + String statusValue = rs.getString(7); + if (statusValue != null) { + resource.setStatus(ResourceStatusValueType.fromValue(statusValue)); + } + + String operationValue = rs.getString(8); + String operationCodeSpace = rs.getString(9); + if (operationValue != null || operationCodeSpace != null) { + Code operationType = new Code(); + operationType.setValue(operationValue); + operationType.setCodeSpace(operationCodeSpace); + resource.setOperationType(operationType); + } + + String amountValue = rs.getString(11); + String amountCodeSpace = rs.getString(12); + if (amountValue != null || amountCodeSpace != null) { + Code amountType = new Code(); + amountType.setValue(amountValue); + amountType.setCodeSpace(amountCodeSpace); + resource.setAmountType(amountType); + } + + double amountVal = rs.getDouble(13); + if (!rs.wasNull()) { + Measure amount = new Measure(); + amount.setValue(amountVal); + String uom = rs.getString(14); + if (uom != null) { + amount.setUom(uom); + } + amount.setParent(resource); + resource.setAmount(amount); + } + + int normalized = rs.getInt(16); + if (!rs.wasNull()) { + resource.setAmountNormalized(normalized == 1); + } + + String normalizationParam = rs.getString(17); + if (normalizationParam != null) { + resource.setNormalizationParameter(normalizationParam); + } + + double normVal = rs.getDouble(18); + if (!rs.wasNull()) { + Measure normalizationValue = new Measure(); + normalizationValue.setParent(resource); + normalizationValue.setValue(normVal); + String uom = rs.getString(19); + if (uom != null) { + normalizationValue.setUom(uom); + } + resource.setNormalizationValue(normalizationValue); + } + + double co2Val = rs.getDouble(20); + if (!rs.wasNull()) { + Measure co2Equivalent = new Measure(); + co2Equivalent.setValue(co2Val); + String uom = rs.getString(21); + if (uom != null) { + co2Equivalent.setUom(uom); + } + resource.setCo2Equivalent(co2Equivalent); + } + + int year = rs.getInt(10); + if (!rs.wasNull()) { + resource.setYear(year); + } + + double costVal = rs.getDouble(22); + if (!rs.wasNull()) { + Measure costsMoney = new Measure(); + costsMoney.setValue(costVal); + String uom = rs.getString(23); + if (uom != null) { + costsMoney.setUom(uom); + } + resource.setCostsMoney(costsMoney); + } + + double yieldVal = rs.getDouble(24); + if (!rs.wasNull()) { + Measure yieldsMoney = new Measure(); + yieldsMoney.setValue(yieldVal); + String uom = rs.getString(25); + if (uom != null) { + yieldsMoney.setUom(uom); + } + resource.setYieldsMoney(yieldsMoney); + } + + long timeSeriesId = rs.getLong(15); + if (timeSeriesId != 0 && timeSeriesExporter != null) { + Collection timeSeries = timeSeriesExporter.doExport(timeSeriesId); + if (timeSeries != null && !timeSeries.isEmpty()) { + AbstractTimeSeries firstSeries = timeSeries.iterator().next(); + if (firstSeries != null) { + resource.setTimeDependentAmount(new AbstractTimeSeriesProperty(firstSeries)); + } + } + } + + if (resource instanceof Energy) { + Energy energy = (Energy) resource; + String energyType = rs.getString(3); + String energyTypeCodeSpace = rs.getString(4); + if (energyType != null || energyTypeCodeSpace != null) { + Code energyTypeValue = new Code(); + energyTypeValue.setValue(energyType); + energyTypeValue.setCodeSpace(energyTypeCodeSpace); + energy.setEnergyTypeValue(energyTypeValue); + } + String energyEndUse = rs.getString(5); + String energyEndUseCodeSpace = rs.getString(6); + if (energyEndUse != null || energyEndUseCodeSpace != null) { + Code energyEndUseValue = new Code(); + energyEndUseValue.setValue(energyEndUse); + energyEndUseValue.setCodeSpace(energyEndUseCodeSpace); + energy.setEnergyEndUseValue(energyEndUseValue); + } + String energyCarrier = rs.getString(26); + String energyCarrierCodeSpace = rs.getString(27); + if (energyCarrier != null || energyCarrierCodeSpace != null) { + Code energyCarrierValue = new Code(); + energyCarrierValue.setValue(energyCarrier); + energyCarrierValue.setCodeSpace(energyCarrierCodeSpace); + energy.setEnergyCarrierValue(energyCarrierValue); + } + double maxLoadVal = rs.getDouble(28); + if (!rs.wasNull()) { + Measure maximumLoad = new Measure(); + maximumLoad.setValue(maxLoadVal); + String uom = rs.getString(29); + if (uom != null) { + maximumLoad.setUom(uom); + } + energy.setMaximumLoad(maximumLoad); + } + String energySource = rs.getString(30); + String energySourceCodeSpace = rs.getString(31); + if (energySource != null || energySourceCodeSpace != null) { + Code energySourceValue = new Code(); + energySourceValue.setValue(energySource); + energySourceValue.setCodeSpace(energySourceCodeSpace); + energy.setEnergySourceValue(energySourceValue); + } + } else if (resource instanceof Water) { + Water water = (Water) resource; + String waterType = rs.getString(3); + String waterTypeCodeSpace = rs.getString(4); + if (waterType != null || waterTypeCodeSpace != null) { + Code waterTypeValue = new Code(); + waterTypeValue.setValue(waterType); + waterTypeValue.setCodeSpace(waterTypeCodeSpace); + water.setWaterTypeValue(waterTypeValue); + } + String waterEndUse = rs.getString(5); + String waterEndUseCodeSpace = rs.getString(6); + if (waterEndUse != null || waterEndUseCodeSpace != null) { + Code waterEndUseValue = new Code(); + waterEndUseValue.setValue(waterEndUse); + waterEndUseValue.setCodeSpace(waterEndUseCodeSpace); + water.setWaterEndUseValue(waterEndUseValue); + } + } else if (resource instanceof Waste) { + Waste waste = (Waste) resource; + String wasteType = rs.getString(3); + String wasteTypeCodeSpace = rs.getString(4); + if (wasteType != null || wasteTypeCodeSpace != null) { + Code wasteTypeValue = new Code(); + wasteTypeValue.setValue(wasteType); + wasteTypeValue.setCodeSpace(wasteTypeCodeSpace); + waste.setWasteTypeValue(wasteTypeValue); + } + String wasteEndUse = rs.getString(5); + String wasteEndUseCodeSpace = rs.getString(6); + if (wasteEndUse != null || wasteEndUseCodeSpace != null) { + Code wasteEndUseValue = new Code(); + wasteEndUseValue.setValue(wasteEndUse); + wasteEndUseValue.setCodeSpace(wasteEndUseCodeSpace); + waste.setWasteEndUseValue(wasteEndUseValue); + } + int isDangerous = rs.getInt(32); + if (!rs.wasNull()) { + waste.setAmountNormalized(isDangerous == 1); + } + int recyclable = rs.getInt(33); + if (!rs.wasNull()) { + waste.setRecyclable(recyclable == 1); + } + } else if (resource instanceof ConstructionMaterial) { + ConstructionMaterial cm = (ConstructionMaterial) resource; + String type = rs.getString(3); + String typeCodeSpace = rs.getString(4); + if (type != null || typeCodeSpace != null) { + Code typeValue = new Code(); + typeValue.setValue(type); + typeValue.setCodeSpace(typeCodeSpace); + cm.setConstructionMaterialTypeValue(typeValue); + } + String endUse = rs.getString(5); + String endUseCodeSpace = rs.getString(6); + if (endUse != null || endUseCodeSpace != null) { + Code endUseValue = new Code(); + endUseValue.setValue(endUse); + endUseValue.setCodeSpace(endUseCodeSpace); + cm.setConstructionMaterialEndUseValue(endUseValue); + } + } else if (resource instanceof Food) { + Food food = (Food) resource; + String foodType = rs.getString(3); + String foodTypeCodeSpace = rs.getString(4); + if (foodType != null || foodTypeCodeSpace != null) { + Code foodTypeValue = new Code(); + foodTypeValue.setValue(foodType); + foodTypeValue.setCodeSpace(foodTypeCodeSpace); + food.setFoodTypeValue(foodTypeValue); + } + String foodEndUse = rs.getString(5); + String foodEndUseCodeSpace = rs.getString(6); + if (foodEndUse != null || foodEndUseCodeSpace != null) { + Code foodEndUseValue = new Code(); + foodEndUseValue.setValue(foodEndUse); + foodEndUseValue.setCodeSpace(foodEndUseCodeSpace); + food.setFoodEndUseValue(foodEndUseValue); + } + } else if (resource instanceof OtherResource) { + OtherResource other = (OtherResource) resource; + String otherType = rs.getString(3); + String otherTypeCodeSpace = rs.getString(4); + if (otherType != null || otherTypeCodeSpace != null) { + Code otherTypeValue = new Code(); + otherTypeValue.setValue(otherType); + otherTypeValue.setCodeSpace(otherTypeCodeSpace); + other.setOtherResourceTypeValue(otherTypeValue); + } + String otherEndUse = rs.getString(5); + String otherEndUseCS = rs.getString(6); + if (otherEndUse != null || otherEndUseCS != null) { + Code otherEndUseValue = new Code(); + otherEndUseValue.setValue(otherEndUse); + otherEndUseValue.setCodeSpace(otherEndUseCS); + other.setOtherResourceEndUseValue(otherEndUseValue); + } + } + result.add(resource); + } + return result; + } + } + + @Override + public void close() throws CityGMLExportException, SQLException { + ps.close(); + } + + private String getColumnNames() { + return "id, objectclass_id, type, type_codespace, enduse, enduse_codespace, status, operation_type," + + " operation_type_codespace, year, amount_type, amount_type_codespace, amount, " + + "amount_uom, time_series_id, is_amount_normalized, normalization_param, normalization_value, " + + "normalization_value_uom, co2_equivalent, co2_equivalent_uom, costs_money, costs_money_uom, " + + "yields_money, yields_money_uom, energy_carrier, energy_carrier_codespace, maximum_load, " + + "maximum_load_uom, source, source_codespace, is_dangerous, is_recyclable, cityobject_id"; + } +} \ No newline at end of file diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/exporter/TimeSeriesExporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/TimeSeriesExporter.java new file mode 100644 index 0000000..3d79a6c --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/TimeSeriesExporter.java @@ -0,0 +1,411 @@ +package de.stuttgart.hft.ade.energy2.exporter; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.core.ade.exporter.ADEExporter; +import org.citydb.core.ade.exporter.CityGMLExportHelper; +import org.citydb.core.operation.exporter.CityGMLExportException; +import org.citygml4j.ade.energy.model.supportingClasses.*; +import org.citygml4j.model.gml.basicTypes.Code; +import org.citygml4j.model.gml.basicTypes.MeasureList; + +import java.net.URI; +import java.sql.*; +import java.time.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class TimeSeriesExporter implements ADEExporter { + + private final CityGMLExportHelper helper; + private AbstractTimeSeries abstractTimeSeries; + private PreparedStatement ps; + + public TimeSeriesExporter(Connection connection, CityGMLExportHelper helper, ExportManager manager) + throws CityGMLExportException, SQLException { + this.helper = helper; + String tableName = helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.TIME_SERIES)); + String sql = "SELECT " + getColumnNames() + " FROM " + tableName + " WHERE id = ?"; + ps = connection.prepareStatement(sql); + } + + public Collection doExport(long objectId) throws CityGMLExportException, SQLException { + ps.setLong(1, objectId); + try (ResultSet rs = ps.executeQuery()) { + Collection result = new ArrayList<>(); + while (rs.next()) { + long timeSeriesId = rs.getLong(1); + int abstractTimeSeriesObjectClassId = rs.getInt(2); + if (rs.wasNull()) { + continue; + } + + abstractTimeSeries = helper.createObject(timeSeriesId, abstractTimeSeriesObjectClassId, AbstractTimeSeries.class); + if (abstractTimeSeries == null) { + helper.logOrThrowErrorMessage("Failed to instantiate " + helper.getObjectSignature(abstractTimeSeriesObjectClassId, timeSeriesId) + " as time series object."); + continue; + } + + // Set AbstractTimeSeries properties + String acquisition_method = rs.getString(3); + String acquisition_method_codespace = rs.getString(4); + if (acquisition_method != null || acquisition_method_codespace != null) { + Code aqmtd = new Code(); + aqmtd.setParent(abstractTimeSeries); + aqmtd.setValue(acquisition_method); + aqmtd.setCodeSpace(acquisition_method_codespace); + abstractTimeSeries.setAcquisitionMethod(aqmtd); + } + + String interpolation_type = rs.getString(5); + if (interpolation_type != null) { + abstractTimeSeries.setInterpolationTypeValue(InterpolationTypeValue.fromValue(interpolation_type)); + } + + String source = rs.getString(6); + if (source != null) { + abstractTimeSeries.setSource(source); + } + + if (abstractTimeSeries instanceof RegularTimeSeries) { + RegularTimeSeries regularTimeSeries = (RegularTimeSeries) abstractTimeSeries; + Timestamp period_begin = rs.getTimestamp(7); + Timestamp period_end = rs.getTimestamp(8); + + if (period_begin != null && period_end != null) { + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime beginZdt = period_begin.toInstant().atZone(zoneId); + ZonedDateTime endZdt = period_end.toInstant().atZone(zoneId); + + TimePeriod timePeriod = new TimePeriod(); + timePeriod.setBeginPosition(beginZdt); + timePeriod.setEndPosition(endZdt); + + TimePeriodProperty timePeriodProperty = new TimePeriodProperty(); + timePeriodProperty.setTimePeriod(timePeriod); + regularTimeSeries.setTemporalExtent(timePeriodProperty); + } + + Double time_interval = rs.getObject(14) != null ? rs.getDouble(14) : null; + String time_interval_unit = rs.getString(15); + if (time_interval != null) { + TimeIntervalLength timeIntervalLength = new TimeIntervalLength(); + timeIntervalLength.setValue(time_interval); + timeIntervalLength.setUnit(time_interval_unit); + regularTimeSeries.setTimeInterval(timeIntervalLength); + } + + String valuesString = rs.getString(16); + String uom = rs.getString(17); + if (valuesString != null && !valuesString.isBlank()) { + List valuesList = Arrays.stream(valuesString.trim().split("\\s+")) + .map(Double::parseDouble) + .collect(Collectors.toList()); + + MeasureList measureList = new MeasureList(); + measureList.setValue(valuesList); + measureList.setUom(uom != null ? uom : ""); + regularTimeSeries.setValues(measureList); + } + + } else if (abstractTimeSeries instanceof RegularTimeSeriesFile) { + RegularTimeSeriesFile regularTimeSeriesFile = (RegularTimeSeriesFile) abstractTimeSeries; + + ZonedDateTime beginZdt = rs.getObject(7, ZonedDateTime.class); + ZonedDateTime endZdt = rs.getObject(8, ZonedDateTime.class); + + if (beginZdt != null && endZdt != null) { + TimePeriod timePeriod = new TimePeriod(); + timePeriod.setBeginPosition(beginZdt); + timePeriod.setEndPosition(endZdt); + + TimePeriodProperty timePeriodProperty = new TimePeriodProperty(); + timePeriodProperty.setTimePeriod(timePeriod); + regularTimeSeriesFile.setTemporalExtent(timePeriodProperty); + } + + String uom = rs.getString(18); + if (uom != null) { + regularTimeSeriesFile.setUom(uom); + } + + String file_uri = rs.getString(19); + if (file_uri != null && !file_uri.isEmpty()) { + try { + regularTimeSeriesFile.setFileURI(URI.create(file_uri)); + } catch (IllegalArgumentException e) { + System.out.println("Invalid URI for TimeSeries id=" + timeSeriesId + ": " + file_uri); + } + } + + Double time_interval = rs.getObject(14) != null ? rs.getDouble(14) : null; + String time_interval_unit = rs.getString(15); + if (time_interval != null) { + TimeIntervalLength timeIntervalLength = new TimeIntervalLength(); + timeIntervalLength.setValue(time_interval); + timeIntervalLength.setUnit(time_interval_unit); + regularTimeSeriesFile.setTimeInterval(timeIntervalLength); + } + + int num_of_header_lines = rs.getInt(20); + if (!rs.wasNull()) { + regularTimeSeriesFile.setNumberOfHeaderLines(num_of_header_lines); + } + + String field_separator = rs.getString(21); + if (field_separator != null) { + regularTimeSeriesFile.setFieldSeparator(field_separator); + } + + String record_separator = rs.getString(22); + if (record_separator != null) { + regularTimeSeriesFile.setRecordSeparator(record_separator); + } + + String decimal_symbol = rs.getString(24); + if (decimal_symbol != null) { + regularTimeSeriesFile.setDecimalSymbol(decimal_symbol); + } + + Integer value_column_number = rs.getObject(23) != null ? rs.getInt(23) : null; + if (value_column_number != null) { + regularTimeSeriesFile.setValueColumnNumber(value_column_number); + } + } else if (abstractTimeSeries instanceof TypicalValuesTimeSeries) { + TypicalValuesTimeSeries typicalValuesTimeSeries = (TypicalValuesTimeSeries) abstractTimeSeries; + + Time start_time = rs.getTime(9); + if (start_time != null) { + LocalTime startTimestamp = start_time.toLocalTime(); + typicalValuesTimeSeries.setStartTime(startTimestamp); + } + + Integer start_day = rs.getObject(10) != null ? rs.getInt(10) : null; + if (start_day != null) { + typicalValuesTimeSeries.setStartDay(start_day); + } + + Integer start_month = rs.getObject(11) != null ? rs.getInt(11) : null; + if (start_month != null) { + typicalValuesTimeSeries.setStartMonth(start_month); + } + + Double time_interval = rs.getObject(14) != null ? rs.getDouble(14) : null; + String time_interval_unit = rs.getString(15); + if (time_interval != null) { + TimeIntervalLength timeIntervalLength = new TimeIntervalLength(); + timeIntervalLength.setValue(time_interval); + timeIntervalLength.setUnit(time_interval_unit); + typicalValuesTimeSeries.setTimeInterval(timeIntervalLength); + } + + Double temporal_extent = rs.getDouble(12); + String temporal_extent_unit = rs.getString(13); + if (!rs.wasNull()) { + TimeIntervalLength timeIntervalLength = new TimeIntervalLength(); + timeIntervalLength.setValue(temporal_extent); + timeIntervalLength.setUnit(temporal_extent_unit); + typicalValuesTimeSeries.setTemporalExtent(timeIntervalLength); + } + + String valuesString = rs.getString(16); + String uom = rs.getString(17); + if (valuesString != null && !valuesString.isBlank()) { + List valuesList = Arrays.stream(valuesString.trim().split("\\s+")) + .map(Double::parseDouble) + .collect(Collectors.toList()); + + MeasureList measureList = new MeasureList(); + measureList.setValue(valuesList); + measureList.setUom(uom != null ? uom : ""); + typicalValuesTimeSeries.setValues(measureList); + } + } else if (abstractTimeSeries instanceof TypicalValuesTimeSeriesFile) { + TypicalValuesTimeSeriesFile timeSeriesFile = (TypicalValuesTimeSeriesFile) abstractTimeSeries; + + Time start_time = rs.getTime(9); + if (start_time != null) { + LocalTime localTime = start_time.toLocalTime(); + ZonedDateTime zonedDateTime = localTime.atDate(LocalDate.now()) + .atZone(ZoneId.systemDefault()); + timeSeriesFile.setStartTime(zonedDateTime); + } + + Integer start_day = rs.getObject(10) != null ? rs.getInt(10) : null; + if (start_day != null) { + timeSeriesFile.setStartDay(start_day); + } + + Integer start_month = rs.getObject(11) != null ? rs.getInt(11) : null; + if (start_month != null) { + timeSeriesFile.setStartMonth(start_month); + } + + Double time_interval = rs.getObject(14) != null ? rs.getDouble(14) : null; + String time_interval_unit = rs.getString(15); + if (time_interval != null) { + TimeIntervalLength timeIntervalLength = new TimeIntervalLength(); + timeIntervalLength.setValue(time_interval); + timeIntervalLength.setUnit(time_interval_unit); + timeSeriesFile.setTimeInterval(timeIntervalLength); + } + + Double temporal_extent = rs.getDouble(12); + String temporal_extent_unit = rs.getString(13); + if (!rs.wasNull()) { + TimeIntervalLength timeIntervalLength = new TimeIntervalLength(); + timeIntervalLength.setValue(temporal_extent); + timeIntervalLength.setUnit(temporal_extent_unit); + timeSeriesFile.setTemporalExtent(timeIntervalLength); + } + + String uom = rs.getString(18); + if (uom != null) { + timeSeriesFile.setUom(uom); + } + + String file_uri = rs.getString(19); + if (file_uri != null && !file_uri.isEmpty()) { + try { + timeSeriesFile.setFileURI(URI.create(file_uri)); + } catch (IllegalArgumentException e) { + System.out.println("Invalid URI for TimeSeries id=" + timeSeriesId + ": " + file_uri); + } + } + + int num_of_header_lines = rs.getInt(20); + if (!rs.wasNull()) { + timeSeriesFile.setNumberOfHeaderLines(num_of_header_lines); + } + + String field_separator = rs.getString(21); + if (field_separator != null) { + timeSeriesFile.setFieldSeparator(field_separator); + } + + String record_separator = rs.getString(22); + if (record_separator != null) { + timeSeriesFile.setRecordSeparator(record_separator); + } + + String decimal_symbol = rs.getString(24); + if (decimal_symbol != null) { + timeSeriesFile.setDecimalSymbol(decimal_symbol); + } + + Integer value_column_number = rs.getObject(23) != null ? rs.getInt(23) : null; + if (value_column_number != null) { + timeSeriesFile.setValueColumnNumber(value_column_number); + } + + } else if (abstractTimeSeries instanceof SensorConnection) { + SensorConnection sensorConnection = (SensorConnection) abstractTimeSeries; + Double temporal_extent = rs.getDouble(12); + String temporal_extent_unit = rs.getString(13); + if (!rs.wasNull()) { + TimeIntervalLength timeIntervalLength = new TimeIntervalLength(); + timeIntervalLength.setValue(temporal_extent); + timeIntervalLength.setUnit(temporal_extent_unit); + sensorConnection.setTemporalExtent(timeIntervalLength); + } + + String connection_type = rs.getString(28); + String connection_type_codespace = rs.getString(29); + if (connection_type != null) { + Code code = new Code(); + code.setValue(connection_type); + code.setCodeSpace(connection_type_codespace); + sensorConnection.setConnectionType(code); + } + + String observation_property = rs.getString(36); + if (observation_property != null) { + sensorConnection.setObservationProperty(observation_property); + } + + String uom = rs.getString(18); + if (uom != null) { + sensorConnection.setUom(uom); + } + + String sensor_id = rs.getString(37); + if (sensor_id != null) { + sensorConnection.setSensorID(sensor_id); + } + + String sensor_name = rs.getString(38); + if (sensor_name != null) { + sensorConnection.setSensorName(sensor_name); + } + + String observation_id = rs.getString(35); + if (observation_id != null) { + sensorConnection.setObservationID(observation_id); + } + + String datastream_id = rs.getString(30); + if (datastream_id != null) { + sensorConnection.setDataStreamID(datastream_id); + } + + String base_url = rs.getString(27); + if (base_url != null) { + try { + sensorConnection.setBaseURL(URI.create(base_url)); + } catch (IllegalArgumentException e) { + System.out.println("Invalid base_url for TimeSeries id=" + timeSeriesId + ": " + base_url); + } + } + + String auth_type = rs.getString(25); + String auth_type_codespace = rs.getString(26); + if (auth_type != null) { + Code code = new Code(); + code.setValue(auth_type); + code.setCodeSpace(auth_type_codespace); + sensorConnection.setAuthType(code); + } + + String link_to_observation = rs.getString(31); + if (link_to_observation != null) { + sensorConnection.setLinkToObservation(link_to_observation); + } + + String link_to_sensor_description = rs.getString(32); + if (link_to_sensor_description != null) { + sensorConnection.setLinkToSensorDescription(link_to_sensor_description); + } + + String mqtt_server = rs.getString(33); + if (mqtt_server != null) { + sensorConnection.setMqtServer(mqtt_server); + } + + String mqtt_topic = rs.getString(34); + if (mqtt_topic != null) { + sensorConnection.setMqtTopic(mqtt_topic); + } + } + result.add(abstractTimeSeries); + } + return result; + } + } + + @Override + public void close() throws CityGMLExportException, SQLException { + ps.close(); + } + + private String getColumnNames() { + return "id, objectclass_id, acquisition_method, acquisition_method_codespace, interpolation_type, source, " + + "period_begin, period_end, start_time, start_day, start_month, temporal_extent, temporal_extent_unit, " + + "time_interval, time_interval_unit, values_list, values_list_uom, uom, file_uri, num_of_header_lines, " + + "field_separator, record_separator, value_column_number, decimal_symbol, auth_type, auth_type_codespace, " + + "base_url, connection_type, connection_type_codespace, datastream_id, link_to_observation, " + + "link_to_sensor_description, mqtt_server, mqtt_topic, observation_id, observation_property, " + + "sensor_id, sensor_name"; + } +} \ No newline at end of file diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/exporter/UrbanFunctionAreaExporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/UrbanFunctionAreaExporter.java new file mode 100644 index 0000000..3853f7d --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/UrbanFunctionAreaExporter.java @@ -0,0 +1,87 @@ +package de.stuttgart.hft.ade.energy2.exporter; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.core.ade.exporter.ADEExporter; +import org.citydb.core.ade.exporter.CityGMLExportHelper; +import org.citydb.core.database.schema.mapping.AbstractType; +import org.citydb.core.operation.exporter.CityGMLExportException; +import org.citydb.core.query.filter.projection.ProjectionFilter; +import org.citygml4j.ade.energy.model.module.EnergyADEModule; +import org.citygml4j.ade.energy.model.supportingClasses.AbstractTimeSeries; +import org.citygml4j.ade.energy.model.supportingClasses.AbstractTimeSeriesProperty; +import org.citygml4j.ade.energy.model.supportingClasses.UrbanFunctionArea; +import org.citygml4j.model.gml.basicTypes.Code; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class UrbanFunctionAreaExporter implements ADEExporter { + + private PreparedStatement ps; + private String module; + private CityObjectPropertiesExporter cityObjectPropertiesExporter; + private TimeSeriesExporter timeSeriesExporter; + + + public UrbanFunctionAreaExporter(Connection connection, CityGMLExportHelper helper, ExportManager manager) throws CityGMLExportException, SQLException { + String select = "select type, type_codespace, code, code_codespace from " + + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.URBAN_FUNCTION_AREA)) + + " where "; + + module = EnergyADEModule.v2_0.getNamespaceURI(); + ps = connection.prepareStatement(select + "id = ?"); + cityObjectPropertiesExporter = manager.getExporter(CityObjectPropertiesExporter.class); + timeSeriesExporter = manager.getExporter(TimeSeriesExporter.class); + } + + public UrbanFunctionArea doExport(UrbanFunctionArea uaf, long objectId, AbstractType objectType, ProjectionFilter projectionFilter) throws SQLException { + ps.setLong(1, objectId); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + + String type = rs.getString(1); + String typeCodespace = rs.getString(2); + String code = rs.getString(3); + String codeCodespace = rs.getString(4); + + Code typeCode = new Code(); + if (typeCodespace != null) { + typeCode.setCodeSpace(typeCodespace); + } + if (type != null) { + typeCode.setValue(type); + } + uaf.setType(typeCode); + + Code code1 = new Code(); + if (codeCodespace != null) { + code1.setCodeSpace(codeCodespace); + } + if (code != null) { + code1.setValue(code); + } + uaf.setCode(code1); + + if (projectionFilter.containsProperty("timeSeries", module)) { + for (AbstractTimeSeries timeSeries : timeSeriesExporter.doExport(objectId)) { + AbstractTimeSeriesProperty property = new AbstractTimeSeriesProperty(timeSeries); + uaf.addGenericApplicationPropertyOfCityObject(property); + } + } + } + + } catch (CityGMLExportException e) { + throw new RuntimeException(e); + } + return uaf; + } + + + @Override + public void close() throws CityGMLExportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/exporter/WeatherDataExporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/WeatherDataExporter.java new file mode 100644 index 0000000..7e4dbac --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/WeatherDataExporter.java @@ -0,0 +1,184 @@ +package de.stuttgart.hft.ade.energy2.exporter; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.config.geometry.GeometryObject; +import org.citydb.core.ade.exporter.ADEExporter; +import org.citydb.core.ade.exporter.CityGMLExportHelper; +import org.citydb.core.database.schema.mapping.AbstractType; +import org.citydb.core.database.schema.mapping.ObjectType; +import org.citydb.core.operation.exporter.CityGMLExportException; +import org.citydb.core.operation.exporter.database.content.GMLConverter; +import org.citydb.core.query.filter.projection.CombinedProjectionFilter; +import org.citydb.core.query.filter.projection.ProjectionFilter; +import org.citydb.sqlbuilder.expression.PlaceHolder; +import org.citydb.sqlbuilder.schema.Table; +import org.citydb.sqlbuilder.select.Select; +import org.citydb.sqlbuilder.select.operator.comparison.ComparisonFactory; +import org.citygml4j.ade.energy.model.core.ReferencePointProperty; +import org.citygml4j.ade.energy.model.core.WeatherData; +import org.citygml4j.ade.energy.model.module.EnergyADEModule; +import org.citygml4j.ade.energy.model.supportingClasses.AbstractTimeSeries; +import org.citygml4j.ade.energy.model.supportingClasses.AbstractTimeSeriesProperty; +import org.citygml4j.model.gml.basicTypes.Code; +import org.citygml4j.model.gml.basicTypes.Measure; +import org.citygml4j.model.gml.geometry.primitives.Point; +import org.citygml4j.model.gml.geometry.primitives.PointProperty; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; + +public class WeatherDataExporter implements ADEExporter { + + private final CityGMLExportHelper helper; + private final int objectClassId; + + private PreparedStatement ps; + private TimeSeriesExporter timeSeriesExporter; + private GMLConverter gmlConverter; + private String module; + + public WeatherDataExporter(Connection connection, CityGMLExportHelper helper, ExportManager manager) throws CityGMLExportException, SQLException { + this.helper = helper; + objectClassId = manager.getObjectMapper().getObjectClassId(WeatherData.class); + + String tableName = manager.getSchemaMapper().getTableName(ADETable.WEATHER_DATA); + CombinedProjectionFilter projectionFilter = helper.getCombinedProjectionFilter(tableName); + module = EnergyADEModule.v2_0.getNamespaceURI(); + + Table table = new Table(helper.getTableNameWithSchema(tableName)); + + Select select = new Select().addProjection( + table.getColumn("id"), + table.getColumn("type"), + table.getColumn("type_codespace"), + table.getColumn("value_type"), + table.getColumn("value_type_codespace"), + table.getColumn("yearly_value"), + table.getColumn("yearly_value_uom"), + table.getColumn("library_code"), + table.getColumn("library_code_codespace"), + table.getColumn("time_series_id"), + table.getColumn("cityobject_id") + ); + + if (projectionFilter.containsProperty("position", module)) + select.addProjection(table.getColumn("position")); + + select.addSelection(ComparisonFactory.equalTo(table.getColumn("cityobject_id"), new PlaceHolder<>())); + + ps = connection.prepareStatement(select.toString()); + + timeSeriesExporter = manager.getExporter(TimeSeriesExporter.class); + gmlConverter = helper.getGMLConverter(); + } + + public Collection doExport(long parentId) throws CityGMLExportException, SQLException { + ps.setLong(1, parentId); + + try (ResultSet rs = ps.executeQuery()) { + Collection result = new ArrayList<>(); + + while (rs.next()) { + long weatherDataId = rs.getLong(1); + + WeatherData weatherData = helper.createObject(weatherDataId, objectClassId, WeatherData.class); + if (weatherData == null) { + helper.logOrThrowErrorMessage("Failed to instantiate " + helper.getObjectSignature(objectClassId, weatherDataId) + " as weather data object."); + continue; + } + + ObjectType objectType = helper.getObjectType(objectClassId); + ProjectionFilter projectionFilter = helper.getProjectionFilter(objectType); + + weatherData.setId(String.valueOf(weatherDataId)); + + // Set type + String typeValue = rs.getString(2); + String typeCodeSpace = rs.getString(3); + if (typeValue != null || typeCodeSpace != null) { + Code type = new Code(); + type.setValue(typeValue); + type.setCodeSpace(typeCodeSpace); + type.setParent(weatherData); + weatherData.setType(type); + } + + // Set value type + String valueTypeValue = rs.getString(4); + String valueTypeCodeSpace = rs.getString(5); + if (valueTypeValue != null || valueTypeCodeSpace != null) { + Code valueType = new Code(); + valueType.setValue(valueTypeValue); + valueType.setCodeSpace(valueTypeCodeSpace); + valueType.setParent(weatherData); + weatherData.setValueType(valueType); + } + + // Set yearly value and UOM + double yearlyValue = rs.getDouble(6); + if (!rs.wasNull()) { + Measure yearlyMeasure = new Measure(); + yearlyMeasure.setValue(yearlyValue); + String yearlyValueUom = rs.getString(7); + if (yearlyValueUom != null) { + yearlyMeasure.setUom(yearlyValueUom); + } + yearlyMeasure.setParent(weatherData); + weatherData.setYearlyValue(yearlyMeasure); + } + + // Set library code + String libraryCodeValue = rs.getString(8); + String libraryCodeSpace = rs.getString(9); + if (libraryCodeValue != null || libraryCodeSpace != null) { + Code libraryCode = new Code(); + libraryCode.setValue(libraryCodeValue); + libraryCode.setCodeSpace(libraryCodeSpace); + libraryCode.setParent(weatherData); + weatherData.setLibraryCode(libraryCode); + } + + // Set time series + long timeSeriesId = rs.getLong(10); + if (!rs.wasNull()) { + Collection timeSeries = timeSeriesExporter.doExport(timeSeriesId); + if (timeSeries != null && !timeSeries.isEmpty()) { + AbstractTimeSeries firstSeries = timeSeries.iterator().next(); + weatherData.setTimeDependentValues(new AbstractTimeSeriesProperty(firstSeries)); + } + } + + // Set position (if requested) + if (projectionFilter.containsProperty("position", module)) { + Object pointObj = rs.getObject("position"); + if (pointObj != null) { + try { + GeometryObject point = helper.getDatabaseAdapter().getGeometryConverter().getPoint(pointObj); + if (point != null) { + Point gmlPoint = gmlConverter.getPoint(point); + PointProperty pointProperty = new PointProperty(gmlPoint); + ReferencePointProperty refPointProp = new ReferencePointProperty(pointProperty); + weatherData.setPosition(refPointProp); + } + } catch (Exception e) { + // Skip invalid geometry to continue export + } + } + } + + result.add(weatherData); + } + + return result; + } + } + + @Override + public void close() throws CityGMLExportException, SQLException { + ps.close(); + } +} \ No newline at end of file diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/exporter/WeatherStationExporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/WeatherStationExporter.java new file mode 100644 index 0000000..0d9c2c0 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/exporter/WeatherStationExporter.java @@ -0,0 +1,131 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stuttgart.hft.ade.energy2.exporter; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.core.ade.exporter.ADEExporter; +import org.citydb.core.ade.exporter.CityGMLExportHelper; +import org.citydb.core.database.schema.mapping.AbstractType; +import org.citydb.core.operation.exporter.CityGMLExportException; +import org.citydb.core.operation.exporter.database.content.GMLConverter; +import org.citydb.core.query.filter.projection.ProjectionFilter; +import org.citydb.sqlbuilder.expression.PlaceHolder; +import org.citydb.sqlbuilder.schema.Table; +import org.citydb.sqlbuilder.select.Select; +import org.citydb.sqlbuilder.select.operator.comparison.ComparisonFactory; +import org.citygml4j.ade.energy.model.core.*; +import org.citygml4j.ade.energy.model.module.EnergyADEModule; +import org.citygml4j.ade.energy.model.supportingClasses.WeatherStation; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class WeatherStationExporter implements ADEExporter { + private final CityGMLExportHelper helper; + private final int weatherDataObjectClassId; + + private PreparedStatement ps; + private TimeSeriesExporter timeSeriesExporter; + private WeatherDataExporter weatherDataExporter; + private GMLConverter gmlConverter; + private String module; + + public WeatherStationExporter(Connection connection, CityGMLExportHelper helper, ExportManager manager) throws CityGMLExportException, SQLException { + this.helper = helper; + weatherDataObjectClassId = manager.getObjectMapper().getObjectClassId(WeatherData.class); + + String tableName = manager.getSchemaMapper().getTableName(ADETable.CITYOBJECT); + module = EnergyADEModule.v2_0.getNamespaceURI(); + + Table table = new Table(helper.getTableNameWithSchema(tableName)); + Select select = new Select().addProjection(table.getColumn("id")); + select.addSelection(ComparisonFactory.equalTo(table.getColumn("id"), new PlaceHolder<>())); + ps = connection.prepareStatement(select.toString()); + + timeSeriesExporter = manager.getExporter(TimeSeriesExporter.class); + weatherDataExporter = manager.getExporter(WeatherDataExporter.class); + gmlConverter = helper.getGMLConverter(); + } + + public void doExport(WeatherStation weatherStation, long objectId, AbstractType objectType, ProjectionFilter projectionFilter) throws CityGMLExportException, SQLException { + ps.setLong(1, objectId); + +// try (ResultSet rs = ps.executeQuery()) { +// while (rs.next()) { +// +// long weatherDataId = rs.getLong("id"); +// if (!rs.wasNull()) { +// WeatherData weatherData = helper.createObject(weatherDataId, weatherDataObjectClassId, WeatherData.class); +// if (weatherData == null) { +// helper.logOrThrowErrorMessage("Failed to instantiate " + helper.getObjectSignature(weatherDataObjectClassId, weatherDataId) + " as weather data object."); +// continue; +// } +// +// ObjectType weatherDataObjectType = helper.getObjectType(weatherDataObjectClassId); +// ProjectionFilter weatherDataProjectionFilter = helper.getProjectionFilter(weatherDataObjectType); +// +//// weatherData.setType(new Code(rs.getString("weatherdatatype"))); +//// +//// long valuesId = rs.getLong("values_id"); +// if (!rs.wasNull()) { +// AbstractTimeSeries timeSeries = timeSeriesExporter.doExport(objectId); +// if (timeSeries != null) +// weatherData.setTimeDependentValues(new AbstractTimeSeriesProperty(timeSeries)); +// } +// +// if (weatherDataProjectionFilter.containsProperty("position", module)) { +// Object pointObj = rs.getObject("position"); +// if (pointObj != null) { +// GeometryObject point = helper.getDatabaseAdapter().getGeometryConverter().getPoint(pointObj); +// if (point != null) { +// +// PointProperty pointProperty = new PointProperty(); +// pointProperty.setGeometry(point); +// +// +// ReferencePointProperty referencePointProperty = new ReferencePointProperty(pointProperty); +// +// weatherData.setPosition(referencePointProperty); +// } +// } +// } +// +// weatherStation.addParameter(new WeatherDataProperty(weatherData)); +// } +// } +// } + } + + + @Override + public void close() throws CityGMLExportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractBuildingImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractBuildingImporter.java new file mode 100644 index 0000000..5233a49 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractBuildingImporter.java @@ -0,0 +1,83 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import de.stuttgart.hft.ade.energy2.schema.SchemaMapper; +import org.citydb.config.geometry.GeometryObject; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.ADEPropertyCollection; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.database.schema.mapping.FeatureType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citygml4j.ade.energy.model.core.AbstractBuilding; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Collections; + +public class AbstractBuildingImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + private final SchemaMapper schemaMapper; + + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + public AbstractBuildingImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + this.schemaMapper = manager.getSchemaMapper(); + + ps = connection.prepareStatement( + "INSERT INTO " + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.BUILDING)) + + " (id, type, type_codespace, is_protected, constr_weight, constr_weight_codespace, attic_thm_status, basement_thm_status) " + + "VALUES (" + String.join(", ", Collections.nCopies(8, "?")) + ")" + ); + + geometryConverter = helper.getGeometryConverter(); + } + + + public void doImport(ADEPropertyCollection properties, AbstractBuilding building, long parentId, FeatureType parentType) throws CityGMLImportException, SQLException { + + ps.setLong(1, parentId); + + ps.setString(2, building.getBdgType() != null ? building.getBdgType().getValue() : null); + + + ps.setString(3, building.getBdgType() != null ? building.getBdgType().getCodeSpace() : null); + + ps.setInt(4, building.isBdgIsProtected() ? 0 : 1); + + ps.setString(5, building.getBdgConstructionWeight() != null ? building.getBdgConstructionWeight().getValue() : null); + ps.setString(6, building.getBdgConstructionWeight() != null ? building.getBdgConstructionWeight().getCodeSpace() : null); + + ps.setString(7, building.getBdgAtticThermalStatus() != null ? building.getBdgAtticThermalStatus().toString() : null); + ps.setString(8, building.getBdgBasementThermalStatus() != null ? building.getBdgBasementThermalStatus().toString() : null); + + + GeometryObject geometryObject = null; + + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(schemaMapper.getTableName(ADETable.BUILDING)); + } + + + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractDeviceImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractDeviceImporter.java new file mode 100644 index 0000000..0d6af28 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractDeviceImporter.java @@ -0,0 +1,114 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import de.stuttgart.hft.ade.energy2.schema.SchemaMapper; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citygml4j.ade.energy.model.core.AbstractDevice; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; + +public class AbstractDeviceImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + private final SchemaMapper schemaMapper; + + private PreparedStatement ps; + private int batchCounter; + + public AbstractDeviceImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.helper = helper; + this.connection = connection; + + this.schemaMapper = manager.getSchemaMapper(); + + ps = connection.prepareStatement( + "INSERT INTO " + helper.getTableNameWithSchema(schemaMapper.getTableName(ADETable.DEVICE)) + " " + + "(id, objectclass_id, model, num_of_devices, year_of_manufacture, installed_power, installed_power_uom, " + + "nominal_efficiency, nominal_efficiency_uom, efficiency_indicator, heat_diss, heat_diss_uom, heat_diss_conv, " + + "heat_diss_conv_uom, heat_diss_lat, heat_diss_lat_uom, heat_diss_rad, heat_diss_rad_uom, heat_source, " + + "cop_source_temp, cop_source_temp_uom, cop_operation_temp, cop_operation_temp_uom, has_condensation, type, " + + "type_codespace, installation_side, max_cover_ratio, max_cover_ratio_uom, transmittance_id, cityobject_id) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + ); + } + + public void doImport(AbstractDevice abstractDevice, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + ps.setLong(1, objectId); + + ps.setInt(2, objectType != null ? objectType.getObjectClassId() : 0); + ps.setString(3, abstractDevice != null ? abstractDevice.getModel() : null); + ps.setInt(4, abstractDevice != null ? abstractDevice.getNumberOfDevices() : 0); + ps.setInt(5, abstractDevice != null ? abstractDevice.getYearOfManufacture() : 0); + + ps.setDouble(6, (abstractDevice != null && abstractDevice.getInstalledPower() != null) ? + abstractDevice.getInstalledPower().getValue() : 0.0); + ps.setString(7, (abstractDevice != null && abstractDevice.getInstalledPower() != null) ? + abstractDevice.getInstalledPower().getUom() : null); + + ps.setDouble(8, (abstractDevice != null && abstractDevice.getNominalEfficiency() != null) ? + abstractDevice.getNominalEfficiency().getValue() : 0.0); + ps.setString(9, (abstractDevice != null && abstractDevice.getNominalEfficiency() != null) ? + abstractDevice.getNominalEfficiency().getUom() : null); + + ps.setString(10, abstractDevice != null ? abstractDevice.getEfficiencyIndicator() : null); + + ps.setDouble(11, (abstractDevice != null && abstractDevice.getHeatDissipation() != null) ? + abstractDevice.getHeatDissipation().getValue() : 0.0); + ps.setString(12, (abstractDevice != null && abstractDevice.getHeatDissipation() != null) ? + abstractDevice.getHeatDissipation().getUom() : null); + + ps.setDouble(13, (abstractDevice != null && abstractDevice.getHeatDissipationConvectiveFraction() != null) ? + abstractDevice.getHeatDissipationConvectiveFraction().getValue() : 0.0); + ps.setString(14, (abstractDevice != null && abstractDevice.getHeatDissipationConvectiveFraction() != null) ? + abstractDevice.getHeatDissipationConvectiveFraction().getUom() : null); + + ps.setDouble(15, (abstractDevice != null && abstractDevice.getHeatDissipationLatentFraction() != null) ? + abstractDevice.getHeatDissipationLatentFraction().getValue() : 0.0); + ps.setString(16, (abstractDevice != null && abstractDevice.getHeatDissipationLatentFraction() != null) ? + abstractDevice.getHeatDissipationLatentFraction().getUom() : null); + + ps.setDouble(17, (abstractDevice != null && abstractDevice.getHeatDissipationRadiantFraction() != null) ? + abstractDevice.getHeatDissipationRadiantFraction().getValue() : 0.0); + ps.setString(18, (abstractDevice != null && abstractDevice.getHeatDissipationRadiantFraction() != null) ? + abstractDevice.getHeatDissipationRadiantFraction().getUom() : null); + + ps.setNull(19, Types.VARCHAR); + ps.setNull(20, Types.DOUBLE); + ps.setNull(21, Types.VARCHAR); + ps.setNull(22, Types.DOUBLE); + ps.setNull(23, Types.VARCHAR); + ps.setNull(24, Types.DOUBLE); + ps.setNull(25, Types.VARCHAR); + ps.setNull(26, Types.VARCHAR); + ps.setNull(27, Types.VARCHAR); + ps.setNull(28, Types.DOUBLE); + ps.setNull(29, Types.VARCHAR); + ps.setNull(30, Types.BIGINT); + ps.setNull(31, Types.BIGINT); + + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(objectType); + } + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } + +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractQualifiedAttributeImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractQualifiedAttributeImporter.java new file mode 100644 index 0000000..6091a22 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/AbstractQualifiedAttributeImporter.java @@ -0,0 +1,138 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADESequence; +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import de.stuttgart.hft.ade.energy2.schema.SchemaMapper; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.database.schema.mapping.ComplexType; +import org.citydb.core.database.schema.mapping.ObjectType; +import org.citydb.core.database.schema.mapping.SchemaMapping; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citydb.core.registry.ObjectRegistry; +import org.citydb.core.util.Util; +import org.citygml4j.ade.energy.model.core.AbstractQualifiedAttribute; +import org.citygml4j.ade.energy.model.core.QualifiedArea; +import org.citygml4j.ade.energy.model.core.QualifiedHeight; +import org.citygml4j.ade.energy.model.core.QualifiedVolume; +import org.sig3d.citygml.energyADE2beta7.AbstractQualifiedAttributeType; +import org.sig3d.citygml.energyADE2beta7.QualifiedAreaType; +import org.sig3d.citygml.energyADE2beta7.QualifiedHeightType; +import org.sig3d.citygml.energyADE2beta7.QualifiedVolumeType; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Collections; + +public class AbstractQualifiedAttributeImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + private final ImportManager manager; + private SchemaMapper schemaMapper; + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + public AbstractQualifiedAttributeImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + this.manager = manager; + this.schemaMapper = manager.getSchemaMapper(); + + ps = connection.prepareStatement( + "INSERT INTO " + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.QUALIFIED_ATTRIBUTE)) + + "(id, objectclass_id, type, type_codespace, value, value_uom, description, source, building_id, building_partition_id) " + + "VALUES (" + String.join(", ", Collections.nCopies(10, "?")) + ")" + ); + + geometryConverter = helper.getGeometryConverter(); + } + + public void doImport(AbstractQualifiedAttributeType abstractQualifiedAttribute, long parentId) throws CityGMLImportException, SQLException { + + setAbstractQualifiedAttributeToNull(); + long objectId = helper.getNextSequenceValue(schemaMapper.getSequenceName(ADESequence.QUALIFIED_ATTRIBUTE_SEQ)); + + ps.setLong(1, objectId); + + ps.setLong(9, parentId); + + + if (abstractQualifiedAttribute.isSetDescription()) + ps.setString(7, abstractQualifiedAttribute.getDescription()); + if (abstractQualifiedAttribute.isSetSource()) + ps.setString(8, abstractQualifiedAttribute.getSource()); + if (abstractQualifiedAttribute.isSetValue()) { + ps.setDouble(5, abstractQualifiedAttribute.getValue().getValue()); + ps.setString(6, abstractQualifiedAttribute.getValue().getUom()); + } + + if (abstractQualifiedAttribute instanceof QualifiedHeightType qualifiedHeight) { + if (qualifiedHeight.isSetType()) { + SchemaMapping schemaMapping = ObjectRegistry.getInstance().getSchemaMapping(); + ComplexType qualifiedHeightType = schemaMapping.getComplexType("QualifiedHeight", "http://www.citygml.org/ade/energy/2.0"); + int objectClassId = qualifiedHeightType.getObjectClassId(); + ps.setInt(2, objectClassId); + ps.setString(3, qualifiedHeight.getType().getValue()); + ps.setString(4, qualifiedHeight.getType().getCodeSpace()); + } + } else if (abstractQualifiedAttribute instanceof QualifiedAreaType qualifiedArea) { + if (qualifiedArea.isSetType()) { + SchemaMapping schemaMapping = ObjectRegistry.getInstance().getSchemaMapping(); + ComplexType qualifiedAreaType = schemaMapping.getComplexType("QualifiedArea", "http://www.citygml.org/ade/energy/2.0"); + int objectClassId = qualifiedAreaType.getObjectClassId(); + ps.setInt(2, objectClassId); + ps.setString(3, qualifiedArea.getType().getValue()); + ps.setString(4, qualifiedArea.getType().getCodeSpace()); + } + } else if (abstractQualifiedAttribute instanceof QualifiedVolumeType qualifiedVolume) { + if (qualifiedVolume.isSetType()){ + SchemaMapping schemaMapping = ObjectRegistry.getInstance().getSchemaMapping(); + ComplexType qualifiedVolumeType = schemaMapping.getComplexType("QualifiedVolume", "http://www.citygml.org/ade/energy/2.0"); + int objectClassId = qualifiedVolumeType.getObjectClassId(); + ps.setInt(2, objectClassId); + ps.setString(3, qualifiedVolume.getType().getValue()); + ps.setString(4, qualifiedVolume.getType().getCodeSpace()); + } + } + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(schemaMapper.getTableName(ADETable.QUALIFIED_ATTRIBUTE)); + + } + + private void setAbstractQualifiedAttributeToNull() throws SQLException { + + ps.setNull(1, Types.BIGINT); // id + ps.setNull(2, Types.INTEGER); // objectclass_id + ps.setNull(3, Types.VARCHAR); // type + ps.setNull(4, Types.VARCHAR); // type_codespace + ps.setNull(5, Types.DOUBLE); // value + ps.setNull(6, Types.VARCHAR); // value_uom + ps.setNull(7, Types.VARCHAR); // description + ps.setNull(8, Types.VARCHAR); // source + ps.setNull(9, Types.BIGINT); // building_id + ps.setNull(10, Types.BIGINT); // building_partition_id + + } + + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/BuildingPropertiesImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/BuildingPropertiesImporter.java new file mode 100644 index 0000000..3340a83 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/BuildingPropertiesImporter.java @@ -0,0 +1,207 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import de.stuttgart.hft.ade.energy2.schema.SchemaMapper; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.ADEPropertyCollection; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.FeatureType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citygml4j.ade.energy.model.core.*; +import org.citygml4j.model.citygml.building.AbstractBuilding; +import org.sig3d.citygml.energyADE2beta7.QualifiedAreaType; +import org.sig3d.citygml.energyADE2beta7.QualifiedHeightType; +import org.sig3d.citygml.energyADE2beta7.QualifiedVolumeType; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; + +public class BuildingPropertiesImporter implements ADEImporter { + private final Connection connection; + private final CityGMLImportHelper helper; + private final SchemaMapper schemaMapper; + private final ImportManager manager; + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + private AbstractQualifiedAttributeImporter abstractQualifiedAttributeImporter; + private RefurbishmentMeasureImporter refurbishmentMeasureImporter; + + public BuildingPropertiesImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + this.schemaMapper = manager.getSchemaMapper(); + this.manager = manager; + + ps = connection.prepareStatement("insert into " + + helper.getTableNameWithSchema(schemaMapper.getTableName(ADETable.BUILDING)) + " " + + "(id, type, type_codespace, is_protected, constr_weight, constr_weight_codespace, attic_thm_status, basement_thm_status) " + + "values (?, ?, ?, ?, ?, ?, ?, ?)"); + + geometryConverter = helper.getGeometryConverter(); + abstractQualifiedAttributeImporter = manager.getImporter(AbstractQualifiedAttributeImporter.class); + refurbishmentMeasureImporter = manager.getImporter(RefurbishmentMeasureImporter.class); + } + + public void doImport(ADEPropertyCollection properties, AbstractBuilding abstractBuilding, long parentId, FeatureType parentType) throws CityGMLImportException, SQLException { + ps.setLong(1, parentId); + + setBuildingNull(); + + if (properties.contains(BuildingTypeProperty.class)){ + for (BuildingTypeProperty propertyElement : properties.getAll(BuildingTypeProperty.class)) { + String type = propertyElement.getValue().getValue(); + ps.setString(2, type); + String typeCodeSpace = propertyElement.getValue().getCodeSpace(); + ps.setString(3, typeCodeSpace); + } + } + if (properties.contains(BuildingIsProtectedProperty.class)) { + for (BuildingIsProtectedProperty propertyElement : properties.getAll(BuildingIsProtectedProperty.class)) { + Boolean isProtected = propertyElement.getValue(); + ps.setInt(4, (isProtected != null && isProtected) ? 1 : 0); + } + } + if (properties.contains(BuildingConstructionWeightProperty.class)) { + for (BuildingConstructionWeightProperty propertyElement : properties.getAll(BuildingConstructionWeightProperty.class)) { + String weight = propertyElement.getValue().getValue(); + ps.setString(5, weight); + String weightCodeSpace = propertyElement.getValue().getCodeSpace(); + ps.setString(6, weightCodeSpace); + } + } + if (properties.contains(BuildingAtticThermalStatusProperty.class)) { + for (BuildingAtticThermalStatusProperty propertyElement : properties.getAll(BuildingAtticThermalStatusProperty.class)) { + String atticThermalStatus = propertyElement.getValue().value(); + ps.setString(7, atticThermalStatus); + } + } + if (properties.contains(BuildingBasementThermalStatusProperty.class)){ + for (BuildingBasementThermalStatusProperty propertyElement : properties.getAll(BuildingBasementThermalStatusProperty.class)) { + String buildingBasementThermalStatus = propertyElement.getValue().value(); + ps.setString(8, buildingBasementThermalStatus); + } + } + if (properties.contains(QualifiedAreaProperty.class)){ + for (QualifiedAreaProperty propertyElement : properties.getAll(QualifiedAreaProperty.class)) { + QualifiedAreaType qualifiedArea = propertyElement.getValue().getQualifiedArea(); + if (qualifiedArea != null) { + abstractQualifiedAttributeImporter.doImport(qualifiedArea, parentId); + } + } + } + if (properties.contains(QualifiedHeightProperty.class)){ + for (QualifiedHeightProperty propertyElement : properties.getAll(QualifiedHeightProperty.class)) { + QualifiedHeightType qualifiedHeightType = propertyElement.getValue().getQualifiedHeight(); + if (qualifiedHeightType != null) { + abstractQualifiedAttributeImporter.doImport(qualifiedHeightType, parentId); + } + } + } + if (properties.contains(QualifiedVolumeProperty.class)){ + for (QualifiedVolumeProperty propertyElement : properties.getAll(QualifiedVolumeProperty.class)) { + QualifiedVolumeType qualifiedVolumeType = propertyElement.getValue().getQualifiedVolume(); + if (qualifiedVolumeType != null) { + abstractQualifiedAttributeImporter.doImport(qualifiedVolumeType, parentId); + } + } + } + + if (properties.contains(RefurbishmentMeasureProperty.class)){ + for (RefurbishmentMeasureProperty propertyElement : properties.getAll(RefurbishmentMeasureProperty.class)) { + RefurbishmentMeasure refurbishmentMeasure = propertyElement.getRefurbishmentMeasure(); + if (refurbishmentMeasure != null) { + helper.importObject(refurbishmentMeasure, ForeignKeys.create().with("buildingId", parentId)); + } + } + } + +// if (properties.contains(UsageZoneProperty.class)) { +// for (UsageZoneProperty propertyElement : properties.getAll(UsageZoneProperty.class)) { +// AbstractUsageZone usageZone = propertyElement.getValue().getAbstractUsageZone(); +// if (usageZone != null) { +// helper.importObject(usageZone, ForeignKeys.create().with("buildingId", parentId)); +// propertyElement.getValue().unsetAbstractUsageZone(); +// } else { +// String href = propertyElement.getValue().getHref(); +// if (href != null && href.length() != 0) +// helper.logOrThrowUnsupportedXLinkMessage(parent, AbstractUsageZone.class, href); +// } +// } +// } +// +// if (properties.contains(ThermalZonePropertyElement.class)) { +// for (ThermalZonePropertyElement propertyElement : properties.getAll(ThermalZonePropertyElement.class)) { +// AbstractThermalZone thermalZone = propertyElement.getValue().getAbstractThermalZone(); +// if (thermalZone != null) { +// helper.importObject(thermalZone, ForeignKeys.create().with("buildingId", parentId)); +// propertyElement.getValue().unsetAbstractThermalZone(); +// } else { +// String href = propertyElement.getValue().getHref(); +// if (href != null && href.length() != 0) +// helper.logOrThrowUnsupportedXLinkMessage(parent, AbstractThermalZone.class, href); +// } +// } +// } + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(schemaMapper.getTableName(ADETable.BUILDING)); + } + + private void setBuildingNull() throws SQLException { + ps.setNull(2, Types.VARCHAR); + ps.setNull(3, Types.VARCHAR); + ps.setNull(4, Types.INTEGER); + ps.setNull(5, Types.VARCHAR); + ps.setNull(6, Types.VARCHAR); + ps.setNull(7, Types.VARCHAR); + ps.setNull(8, Types.VARCHAR); + } + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } + +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/CityObjectPropertiesImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/CityObjectPropertiesImporter.java new file mode 100644 index 0000000..559aa77 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/CityObjectPropertiesImporter.java @@ -0,0 +1,142 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import de.stuttgart.hft.ade.energy2.schema.SchemaMapper; +import org.citydb.config.geometry.GeometryObject; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.ADEPropertyCollection; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.FeatureType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citygml4j.ade.energy.model.core.*; +import org.citygml4j.ade.energy.model.supportingClasses.*; +import org.citygml4j.model.citygml.core.AbstractCityObject; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class CityObjectPropertiesImporter implements ADEImporter { + private final CityGMLImportHelper helper; + private final SchemaMapper schemaMapper; + private final ImportManager manager; + + private PreparedStatement ps; + private int batchCounter; + private Connection connection; + + public CityObjectPropertiesImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.helper = helper; + this.schemaMapper = manager.getSchemaMapper(); + this.manager = manager; + this.connection=connection; + + ps = connection.prepareStatement("insert into " + + helper.getTableNameWithSchema(schemaMapper.getTableName(ADETable.CITYOBJECT)) + " " + + "(id, ref_point) " + + "values (?,?)"); + } + + public void doImport(ADEPropertyCollection properties, AbstractCityObject parent, long parentId, FeatureType parentType) throws CityGMLImportException, SQLException { + ps.setLong(1, parentId); + GeometryObject geometryObject = null; + ReferencePointProperty referencePoint = properties.getFirst(ReferencePointProperty.class); + if (referencePoint != null && referencePoint.isSetValue()) + geometryObject = helper.getGeometryConverter().getPoint(referencePoint.getValue()); + if (geometryObject != null) + ps.setObject(2, helper.getDatabaseAdapter().getGeometryConverter().getDatabaseObject(geometryObject, connection)); + else + ps.setNull(2, helper.getDatabaseAdapter().getGeometryConverter().getNullGeometryType(), + helper.getDatabaseAdapter().getGeometryConverter().getNullGeometryTypeName()); + + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(schemaMapper.getTableName(ADETable.CITYOBJECT)); + + if (properties.contains(AbstractResourceProperty.class)) { + + for (AbstractResourceProperty propertyElement : properties.getAll(AbstractResourceProperty.class)) { + AbstractResource resource = propertyElement.getAbstractResource(); + if (resource != null) { + helper.importObject(resource, ForeignKeys.create().with("cityObjectId", parentId)); + propertyElement.unsetAbstractResource(); + } else { + String href = propertyElement.getHref(); + if (href != null && href.length() != 0) + helper.logOrThrowUnsupportedXLinkMessage(parent, AbstractResource.class, href); + } + } + } + + if (properties.contains(EnergyProperty.class)) { + for (EnergyProperty propertyElement : properties.getAll(EnergyProperty.class)) { + Energy energyDemand = propertyElement.getEnergy(); + if (energyDemand != null) { + helper.importObject(energyDemand, ForeignKeys.create().with("cityObjectId", parentId)); + propertyElement.unsetEnergy(); + } else { + String href = propertyElement.getHref(); + if (href != null && href.length() != 0) + helper.logOrThrowUnsupportedXLinkMessage(parent, Energy.class, href); + } + } + } + + if (properties.contains(WeatherDataProperty.class)) { + for (WeatherDataProperty propertyElement : properties.getAll(WeatherDataProperty.class)) { + WeatherData weatherData = propertyElement.getWeatherData(); + if (weatherData != null) { + helper.importObject(weatherData, ForeignKeys.create().with("cityObjectId", parentId)); + propertyElement.unsetWeatherData(); + } else { + String href = propertyElement.getHref(); + if (href != null && href.length() != 0) + helper.logOrThrowUnsupportedXLinkMessage(parent, WeatherData.class, href); + } + } + } + } + + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } + +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/CityObjectRelationImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/CityObjectRelationImporter.java new file mode 100644 index 0000000..0d1945b --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/CityObjectRelationImporter.java @@ -0,0 +1,68 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citygml4j.ade.energy.model.core.CityObjectRelation; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class CityObjectRelationImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + public CityObjectRelationImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + + ps = connection.prepareStatement("INSERT INTO " + + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.UTL_NTW_CONNECTION)) + " " + + "(id, ng2_cityobject_id, cityobject_id, relation_type, relation_type_codespace) " + + "VALUES (?, ?, ?, ?, ?)"); + + geometryConverter = helper.getGeometryConverter(); + + } + + public void doImport(CityObjectRelation cityObjectRelation, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + ps.setLong(1, objectId); + + if (cityObjectRelation.isSetRelation()) { + ps.setString(4, cityObjectRelation.getRelationType().getValue()); + ps.setString(5, cityObjectRelation.getRelationType().getCodeSpace()); + } + + + //set correctly + ps.setLong(2, objectType.getObjectClassId()); + ps.setLong(3, objectType.getObjectClassId()); + + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(objectType); + } + + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/DeviceOperationImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/DeviceOperationImporter.java new file mode 100644 index 0000000..cc0aca4 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/DeviceOperationImporter.java @@ -0,0 +1,63 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import de.stuttgart.hft.ade.energy2.schema.SchemaMapper; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citygml4j.ade.energy.model.core.DeviceOperation; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class DeviceOperationImporter implements ADEImporter { + + private final CityGMLImportHelper helper; + private final SchemaMapper schemaMapper; + + private PreparedStatement ps; + private int batchCounter; + + public DeviceOperationImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.helper = helper; + this.schemaMapper = manager.getSchemaMapper(); + + ps = connection.prepareStatement("insert into " + + helper.getTableNameWithSchema(schemaMapper.getTableName(ADETable.DEVICE_OPERATION)) + " " + + "(id, type, type_codespace, yearly_global_efficiency, schedule_id, device_id) " + + "values (?, ?, ?, ?, ?,?)"); + } + + public void doImport(DeviceOperation deviceOperation, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + ps.setLong(1, objectId); + + ps.setString(2, + deviceOperation.getType() != null ? + deviceOperation.getType().getValue() : null); + + ps.setString(3, + deviceOperation.getType() != null ? + deviceOperation.getType().getCodeSpace() : null); + + ps.setDouble(4, + deviceOperation.getYearlyGlobalEfficiency() != null ? + deviceOperation.getYearlyGlobalEfficiency() : 0.0); + + } + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } + +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/EnergyPerformanceCertificateImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/EnergyPerformanceCertificateImporter.java new file mode 100644 index 0000000..015f572 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/EnergyPerformanceCertificateImporter.java @@ -0,0 +1,90 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.config.geometry.GeometryObject; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citygml4j.ade.energy.model.core.EnergyPerformanceCertificate; + +import javax.xml.datatype.XMLGregorianCalendar; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Collections; + +public class EnergyPerformanceCertificateImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + public EnergyPerformanceCertificateImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + + PreparedStatement ps = connection.prepareStatement( + "INSERT INTO " + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.ENERGY_PERF_CERT)) + + " (id, type, type_codespace, label, value, value_uom, issue_date, expiration_date, cert_method, cert_uri, cityobject_id) " + + "VALUES (" + String.join(", ", Collections.nCopies(11, "?")) + ")" + ); + + geometryConverter = helper.getGeometryConverter(); + } + + + public void doImport(EnergyPerformanceCertificate energyPerformanceCertificate, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + + ps.setLong(1, objectId); + if (energyPerformanceCertificate.isSetType()) { + ps.setString(2, energyPerformanceCertificate.getType().getValue()); + ps.setString(3, energyPerformanceCertificate.getType().getCodeSpace()); + } + if (energyPerformanceCertificate.isSetLabel()) + ps.setString(4, energyPerformanceCertificate.getLabel()); + if (energyPerformanceCertificate.isSetValue()){ + ps.setDouble(5, energyPerformanceCertificate.getValue().getValue()); + ps.setString(6, energyPerformanceCertificate.getValue().getUom()); + } + if (energyPerformanceCertificate.isSetIssueDate()){ + XMLGregorianCalendar xmlCal = energyPerformanceCertificate.getIssueDate(); + Timestamp timestamp = new Timestamp(xmlCal.toGregorianCalendar().getTimeInMillis()); + ps.setTimestamp(7, timestamp); + } + if (energyPerformanceCertificate.isSetExpirationDate()){ + XMLGregorianCalendar xmlCal = energyPerformanceCertificate.getExpirationDate(); + Timestamp timestamp = new Timestamp(xmlCal.toGregorianCalendar().getTimeInMillis()); + ps.setTimestamp(8, timestamp); + } + if (energyPerformanceCertificate.isSetCertificationMethod()) + ps.setString(9, energyPerformanceCertificate.getCertificationMethod()); + if (energyPerformanceCertificate.isSetCertificationURI()) + ps.setString(10, energyPerformanceCertificate.getCertificationURI().toString()); + ps.setLong(11, objectId); + + + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(objectType); + } + + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/ImportManager.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/ImportManager.java new file mode 100644 index 0000000..df1b659 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/ImportManager.java @@ -0,0 +1,160 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stuttgart.hft.ade.energy2.importer; + + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import de.stuttgart.hft.ade.energy2.schema.SchemaMapper; +import org.citydb.core.ade.ADEExtension; +import org.citydb.core.ade.importer.*; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.database.schema.mapping.FeatureType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citygml4j.ade.energy.model.core.*; +import org.citygml4j.ade.energy.model.supportingClasses.*; +import org.citygml4j.model.citygml.ade.binding.ADEModelObject; +import org.citygml4j.model.citygml.building.AbstractBuilding; +import org.citygml4j.model.citygml.core.AbstractCityObject; +import org.citygml4j.model.gml.feature.AbstractFeature; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +public class ImportManager implements ADEImportManager { + private final ADEExtension adeExtension; + private final Map, ADEImporter> importers; + private final SchemaMapper schemaMapper; + + private Connection connection; + private CityGMLImportHelper helper; + + public ImportManager(ADEExtension adeExtension, SchemaMapper schemaMapper) { + this.adeExtension = adeExtension; + this.schemaMapper = schemaMapper; + importers = new HashMap<>(); + } + + @Override + public void init(Connection connection, CityGMLImportHelper helper) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + } + + @Override + public void importObject(ADEModelObject object, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + if(object instanceof AbstractTimeSeries) + getImporter(TimeSeriesImporter.class).doImport((AbstractTimeSeries) object, objectId, objectType, foreignKeys); + else if (object instanceof AbstractDevice) + getImporter(AbstractDeviceImporter.class).doImport((AbstractDevice) object, objectId, objectType, foreignKeys); + else if (object instanceof DeviceOperation) + getImporter(DeviceOperationImporter.class).doImport((DeviceOperation) object, objectId, objectType, foreignKeys); + else if (object instanceof AbstractResource) + getImporter(ResourceImporter.class).doImport((AbstractResource) object, objectId, objectType, foreignKeys); + else if(object instanceof UrbanFunctionArea) + getImporter(UrbanFunctionAreaImporter.class).doImport((UrbanFunctionArea) object, objectId, objectType, foreignKeys); + else if(object instanceof RefurbishmentMeasure) + getImporter(RefurbishmentMeasureImporter.class).doImport((RefurbishmentMeasure) object, objectId, objectType, foreignKeys); + else if(object instanceof WeatherStation) + getImporter(WeatherStationImporter.class).doImport((WeatherStation) object, objectId, objectType, foreignKeys); + else if (object instanceof WeatherData) + getImporter(WeatherDataImporter.class).doImport((WeatherData) object, objectId, objectType, foreignKeys); + + + } + + @Override + public void importGenericApplicationProperties(ADEPropertyCollection properties, AbstractFeature parent, long parentId, FeatureType parentType) throws CityGMLImportException, SQLException { + if (parent instanceof AbstractCityObject) + getImporter(CityObjectPropertiesImporter.class).doImport(properties, (AbstractCityObject) parent, parentId, parentType); + if (parent instanceof AbstractBuilding + && properties.containsOneOf(QualifiedHeightProperty.class, + QualifiedAreaProperty.class, QualifiedVolumeProperty.class, RefurbishmentMeasureProperty.class)) + getImporter(BuildingPropertiesImporter.class).doImport(properties,(AbstractBuilding) parent, parentId, parentType); + } + + @Override + public void executeBatch(String tableName) throws CityGMLImportException, SQLException { + ADETable adeTable = schemaMapper.fromTableName(tableName); + if (adeTable != null) { + ADEImporter importer = importers.get(adeTable.getImporterClass()); + if (importer != null) + importer.executeBatch(); + } else + throw new CityGMLImportException("The table " + tableName + " not managed by the ADE extension for '" + adeExtension.getMetadata().getIdentifier() + "'."); + } + + @Override + public void close() throws CityGMLImportException, SQLException { + for (ADEImporter importer : importers.values()) + importer.close(); + } + + protected SchemaMapper getSchemaMapper() { + return schemaMapper; + } + + protected T getImporter(Class type) throws CityGMLImportException, SQLException { + ADEImporter importer = importers.get(type); + + if (importer == null) { + if(type== AbstractDeviceImporter.class) + importer = new AbstractDeviceImporter(connection, helper, this); + else if(type== CityObjectPropertiesImporter.class) + importer = new CityObjectPropertiesImporter(connection, helper, this); + else if(type== DeviceOperationImporter.class) + importer = new DeviceOperationImporter(connection, helper, this); + else if(type== TimeSeriesImporter.class) + importer = new TimeSeriesImporter(connection, helper, this); + else if(type== ResourceImporter.class) + importer = new ResourceImporter(connection, helper, this); +// else if(type== WaterImporter.class) +// importer = new WaterImporter(connection, helper, this); + else if(type == UrbanFunctionAreaImporter.class) + importer = new UrbanFunctionAreaImporter(connection, helper, this); + else if (type == WeatherStationImporter.class) + importer = new WeatherStationImporter(connection, helper, this); + else if (type == WeatherDataImporter.class) + importer = new WeatherDataImporter(connection, helper, this); + else if (type == RefurbishmentMeasureImporter.class) + importer = new RefurbishmentMeasureImporter(connection, helper, this); + else if (type == BuildingPropertiesImporter.class) + importer = new BuildingPropertiesImporter(connection, helper, this); + else if (type == AbstractQualifiedAttributeImporter.class) + importer = new AbstractQualifiedAttributeImporter(connection, helper, this); + else throw new SQLException("Failed to build ADE importer of type " + type.getName() + "."); + + + importers.put(type, importer); + } + + return type.cast(importer); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/RefurbishmentMeasureImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/RefurbishmentMeasureImporter.java new file mode 100644 index 0000000..45564cb --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/RefurbishmentMeasureImporter.java @@ -0,0 +1,93 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADESequence; +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import de.stuttgart.hft.ade.energy2.schema.SchemaMapper; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citygml4j.ade.energy.model.core.RefurbishmentMeasure; + +import javax.xml.datatype.XMLGregorianCalendar; +import java.sql.*; + +public class RefurbishmentMeasureImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + private SchemaMapper schemaMapper; + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + public RefurbishmentMeasureImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + this.schemaMapper = manager.getSchemaMapper(); + + ps = connection.prepareStatement("insert into " + + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.REFURBISHMENT_MEASURE)) + " " + + "(id, type, type_codespace, start_date, end_date, library_code, library_code_codespace, building_id, building_partition_id) " + + "values (?, ?, ?, ?, ?, ?, ?, ?, ?)"); + + geometryConverter = helper.getGeometryConverter(); + } + + public void doImport(RefurbishmentMeasure refurbishmentMeasure, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + ps.setLong(1, objectId); + + setRefurbishmentNull(refurbishmentMeasure); + if (refurbishmentMeasure.isSetType()) { + ps.setString(2, refurbishmentMeasure.getType().getValue()); + ps.setString(3, refurbishmentMeasure.getType().getCodeSpace()); + } + if (refurbishmentMeasure.isSetStartDate()) { + XMLGregorianCalendar xmlCal = refurbishmentMeasure.getStartDate(); + Timestamp timestamp = new Timestamp(xmlCal.toGregorianCalendar().getTimeInMillis()); + ps.setTimestamp(4, timestamp); + } + if (refurbishmentMeasure.isSetEndDate()) { + XMLGregorianCalendar xmlCal = refurbishmentMeasure.getEndDate(); + Timestamp timestamp = new Timestamp(xmlCal.toGregorianCalendar().getTimeInMillis()); + ps.setTimestamp(5, timestamp); + } + if (refurbishmentMeasure.isSetLibraryCode()) { + ps.setString(6, refurbishmentMeasure.getLibraryCode().getValue()); + ps.setString(7, refurbishmentMeasure.getLibraryCode().getCodeSpace()); + } + + ps.setLong(8, foreignKeys.get("buildingId")); //building id + ps.setNull(9, Types.BIGINT); //building partionId is null + + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(schemaMapper.getTableName(ADETable.REFURBISHMENT_MEASURE)); + } + + private void setRefurbishmentNull(RefurbishmentMeasure refurbishmentMeasure) throws SQLException { + ps.setNull(2, Types.VARCHAR); // type + ps.setNull(3, Types.VARCHAR); // type_codespace + ps.setNull(4, Types.DATE); // start_date + ps.setNull(5, Types.DATE); // end_date + ps.setNull(6, Types.VARCHAR); // library_code + ps.setNull(7, Types.VARCHAR); // library_code_codespace + ps.setNull(8, Types.BIGINT); // building_id + ps.setNull(9, Types.BIGINT); // building_partition_id + } + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/ResourceImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/ResourceImporter.java new file mode 100644 index 0000000..4661db3 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/ResourceImporter.java @@ -0,0 +1,268 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citygml4j.ade.energy.model.core.AbstractResource; +import org.citygml4j.ade.energy.model.supportingClasses.*; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Collections; + +public class ResourceImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + public ResourceImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + + ps = connection.prepareStatement( + "INSERT INTO " + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.RESOURCE)) + + "(id, objectclass_id, type, type_codespace, enduse, enduse_codespace, status, " + + "operation_type, operation_type_codespace, year, amount_type, amount_type_codespace, amount, " + + "amount_uom, time_series_id, is_amount_normalized, normalization_param, normalization_value, " + + "normalization_value_uom, co2_equivalent, co2_equivalent_uom, costs_money, costs_money_uom, " + + "yields_money, yields_money_uom, energy_carrier, energy_carrier_codespace, maximum_load, " + + "maximum_load_uom, source, source_codespace, is_dangerous, is_recyclable, cityobject_id) " + + "VALUES (" + String.join(", ", Collections.nCopies(34, "?")) + ")" + ); + + geometryConverter = helper.getGeometryConverter(); + } + + public void doImport(AbstractResource abstractResource, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + + setResourceToNull(); + ps.setLong(1, objectId); + ps.setLong(2, objectType.getObjectClassId()); + ps.setLong(34, foreignKeys.get("cityObjectId")); + setAbstractResourceProperties(abstractResource); + + if (abstractResource instanceof Energy) { + Energy energy = (Energy) abstractResource; + if (energy.getEnergyTypeValue() != null) { + ps.setString(3, energy.getEnergyTypeValue().getValue()); + ps.setString(4, energy.getEnergyTypeValue().getCodeSpace()); + } + if (energy.getEnergyEndUseValue() != null) { + ps.setString(5, energy.getEnergyEndUseValue().getValue()); + ps.setString(6, energy.getEnergyEndUseValue().getCodeSpace()); + } + if (energy.getEnergyCarrierValue() != null) { + ps.setString(26, energy.getEnergyCarrierValue().getValue()); + ps.setString(27, energy.getEnergyCarrierValue().getCodeSpace()); + } + if (energy.getMaximumLoad() != null) { + ps.setDouble(28, energy.getMaximumLoad().getValue()); + ps.setString(29, energy.getMaximumLoad().getUom()); + + } + if (energy.getEnergySourceValue() != null) { + ps.setString(30, energy.getEnergySourceValue().getValue()); + ps.setString(31, energy.getEnergySourceValue().getCodeSpace()); + } + } else if (abstractResource instanceof Water) { + + Water water = (Water) abstractResource; + if (water.getWaterTypeValue() != null) { + ps.setString(3, water.getWaterTypeValue().getValue()); + ps.setString(4, water.getWaterTypeValue().getCodeSpace()); + } + if (water.getWaterEndUseValue() != null) { + ps.setString(5, water.getWaterEndUseValue().getValue()); + ps.setString(6, water.getWaterEndUseValue().getCodeSpace()); + } + + } else if (abstractResource instanceof Waste) { + Waste waste = (Waste) abstractResource; + if (waste.getWasteTypeValue() != null) { + ps.setString(3, waste.getWasteTypeValue().getValue()); + ps.setString(4, waste.getWasteTypeValue().getCodeSpace()); + } + if (waste.getWasteEndUseValue() != null) { + ps.setString(5, waste.getWasteEndUseValue().getValue()); + ps.setString(6, waste.getWasteEndUseValue().getCodeSpace()); + } + if (waste.isSetAmountNormalized()) { + ps.setInt(32, 1); // True -> 1 + } + if (waste.isRecyclable()) { + ps.setInt(33, 1); + } + } else if (abstractResource instanceof Food) { + Food food = (Food) abstractResource; + if (food.getFoodTypeValue() != null) { + ps.setString(3, food.getFoodTypeValue().getValue()); + ps.setString(4, food.getFoodTypeValue().getCodeSpace()); + } + if (food.getFoodEndUseValue() != null) { + ps.setString(5, food.getFoodEndUseValue().getValue()); + ps.setString(6, food.getFoodEndUseValue().getCodeSpace()); + } + } else if (abstractResource instanceof ConstructionMaterial) { + + ConstructionMaterial constructionMaterial = (ConstructionMaterial) abstractResource; + if (constructionMaterial.getConstructionMaterialTypeValue() != null) { + ps.setString(3, constructionMaterial.getConstructionMaterialTypeValue().getValue()); + ps.setString(4, constructionMaterial.getConstructionMaterialTypeValue().getCodeSpace()); + } + if (constructionMaterial.getConstructionMaterialEndUseValue() != null) { + ps.setString(5, constructionMaterial.getConstructionMaterialEndUseValue().getValue()); + ps.setString(6, constructionMaterial.getConstructionMaterialEndUseValue().getCodeSpace()); + } + } else if (abstractResource instanceof OtherResource){ + OtherResource otherResource = (OtherResource) abstractResource; + if (otherResource.getOtherResourceTypeValue() != null) { + ps.setString(3, otherResource.getOtherResourceTypeValue().getValue()); + ps.setString(4, otherResource.getOtherResourceTypeValue().getCodeSpace()); + } + if (otherResource.getOtherResourceEndUseValue() != null) { + ps.setString(5, otherResource.getOtherResourceEndUseValue().getValue()); + ps.setString(6, otherResource.getOtherResourceEndUseValue().getCodeSpace()); + } + } else if(abstractResource instanceof UrbanSpace){ + UrbanSpace urbanSpace = (UrbanSpace) abstractResource; + if (urbanSpace.isSetType()){ + ps.setString(3, urbanSpace.getType().getValue()); + ps.setString(4, urbanSpace.getType().getCodeSpace()); + } + if (urbanSpace.isSetEndUse()) { + ps.setString(5, urbanSpace.getEndUse().getValue()); + ps.setString(6, urbanSpace.getEndUse().getCodeSpace()); + } + + } + + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(objectType); + } + + private void setAbstractResourceProperties(AbstractResource abstractResource) throws SQLException, CityGMLImportException { + + if (abstractResource.getStatus() != null) { + ps.setString(7, abstractResource.getStatus().value()); + } + + if (abstractResource.getOperationType() != null) { + ps.setString(8, abstractResource.getOperationType().getValue()); + ps.setString(9, abstractResource.getOperationType().getCodeSpace()); + } + + if (abstractResource.getAmountType() != null) { + ps.setString(11, abstractResource.getAmountType().getValue()); + ps.setString(12, abstractResource.getAmountType().getCodeSpace()); + } + + if (abstractResource.getAmount() != null) { + ps.setDouble(13, abstractResource.getAmount().getValue()); + ps.setString(14, abstractResource.getAmount().getUom()); + } + + if (abstractResource.isSetTimeDependentAmount()){ + long timeSeriesId = helper.importObject(abstractResource.getTimeDependentAmount().getAbstractTimeSeries(), null); + ps.setLong(15, timeSeriesId); + + } + + // Handling boolean-to-numeric conversion + if (abstractResource.isSetAmountNormalized()) { + ps.setInt(16, 1); // True -> 1 + } else { + ps.setInt(16, 0); + } + + if (abstractResource.getNormalizationValue() != null) { + ps.setDouble(18, abstractResource.getNormalizationValue().getValue()); + ps.setString(19, abstractResource.getNormalizationValue().getUom()); + } + + if (abstractResource.getNormalizationParameter() != null) { + ps.setString(17, abstractResource.getNormalizationParameter()); + } + + if (abstractResource.getCo2Equivalent() != null) { + ps.setDouble(20, abstractResource.getCo2Equivalent().getValue()); + ps.setString(21, abstractResource.getCo2Equivalent().getUom()); + } + + if (abstractResource.getYear() != null) { + ps.setInt(10, abstractResource.getYear()); + } + + if (abstractResource.getCostsMoney() != null) { + ps.setDouble(22, abstractResource.getCostsMoney().getValue()); + ps.setString(23, abstractResource.getCostsMoney().getUom()); + } + + if (abstractResource.getYieldsMoney() != null) { + ps.setDouble(24, abstractResource.getYieldsMoney().getValue()); + ps.setString(25, abstractResource.getYieldsMoney().getUom()); + } + } + + private void setResourceToNull() throws SQLException { + + ps.setNull(1, Types.BIGINT); // id + ps.setNull(2, Types.INTEGER); // objectclass_id + ps.setNull(3, Types.VARCHAR); // type + ps.setNull(4, Types.VARCHAR); // type_codespace + ps.setNull(5, Types.VARCHAR); // enduse + ps.setNull(6, Types.VARCHAR); // enduse_codespace + ps.setNull(7, Types.VARCHAR); // status + ps.setNull(8, Types.VARCHAR); // operation_type + ps.setNull(9, Types.VARCHAR); // operation_type_codespace + ps.setNull(10, Types.INTEGER); // year + ps.setNull(11, Types.VARCHAR); // amount_type + ps.setNull(12, Types.VARCHAR); // amount_type_codespace + ps.setNull(13, Types.NUMERIC); // amount + ps.setNull(14, Types.VARCHAR); // amount_uom + ps.setNull(15, Types.BIGINT); // time_series_id + ps.setNull(16, Types.NUMERIC); // is_amount_normalized + ps.setNull(17, Types.VARCHAR); // normalization_param + ps.setNull(18, Types.NUMERIC); // normalization_value + ps.setNull(19, Types.VARCHAR); // normalization_value_uom + ps.setNull(20, Types.NUMERIC); // co2_equivalent + ps.setNull(21, Types.VARCHAR); // co2_equivalent_uom + ps.setNull(22, Types.NUMERIC); // costs_money + ps.setNull(23, Types.VARCHAR); // costs_money_uom + ps.setNull(24, Types.NUMERIC); // yields_money + ps.setNull(25, Types.NUMERIC); // yields_money_uom + ps.setNull(26, Types.VARCHAR); // energy_carrier + ps.setNull(27, Types.VARCHAR); // energy_carrier_codespace + ps.setNull(28, Types.NUMERIC); // maximum_load + ps.setNull(29, Types.VARCHAR); // maximum_load_uom + ps.setNull(30, Types.VARCHAR); // source + ps.setNull(31, Types.VARCHAR); // source_codespace + ps.setNull(32, Types.NUMERIC); // is_dangerous + ps.setNull(33, Types.NUMERIC); // is_recyclable + ps.setNull(34, Types.BIGINT); // cityobject_id + + } + + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/TimeSeriesImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/TimeSeriesImporter.java new file mode 100644 index 0000000..120ac59 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/TimeSeriesImporter.java @@ -0,0 +1,276 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.AttributeValueJoiner; +import org.citygml4j.ade.energy.model.supportingClasses.*; + +import java.sql.*; +import java.time.LocalTime; +import java.util.Collections; + +public class TimeSeriesImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + + private AttributeValueJoiner valueJoiner; + private PreparedStatement psTimeSeries; + private int batchCounter; + + public TimeSeriesImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + + String tableName = helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.TIME_SERIES)); + String sql = "INSERT INTO " + tableName + " (" + getColumnNames() + ") VALUES (" + getPlaceholders(38) + ") ON CONFLICT (id) DO NOTHING"; + + psTimeSeries = connection.prepareStatement(sql); + valueJoiner = helper.getAttributeValueJoiner(); + } + + public void doImport(AbstractTimeSeries abstractTimeSeries, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + + setTimeSeriesToNull(); + psTimeSeries.setLong(1, objectId); + psTimeSeries.setLong(2, objectType.getObjectClassId()); + setAbstractTimeseriesProperties(abstractTimeSeries); + //contains weatherdata + if (abstractTimeSeries instanceof RegularTimeSeries) { + RegularTimeSeries regularTimeSeries = (RegularTimeSeries) abstractTimeSeries; + + if (regularTimeSeries.isSetValues() && regularTimeSeries.getValues().isSetValue()) { + String values = valueJoiner.join(" ", regularTimeSeries.getValues().getValue()); + psTimeSeries.setString(16, values); + psTimeSeries.setString(17, regularTimeSeries.getValues().getUom()); + } + if (regularTimeSeries.isSetTemporalExtent()){ + Timestamp beginPosition = Timestamp.from(regularTimeSeries.getTemporalExtent().getTimePeriod().getBeginPosition().toInstant()); + Timestamp endPosition = Timestamp.from(regularTimeSeries.getTemporalExtent().getTimePeriod().getEndPosition().toInstant()); + psTimeSeries.setTimestamp(7, beginPosition); + psTimeSeries.setTimestamp(8, endPosition); + } + if (regularTimeSeries.isSetTimeInterval()) { + psTimeSeries.setDouble(14, regularTimeSeries.getTimeInterval().getValue()); + psTimeSeries.setString(15, regularTimeSeries.getTimeInterval().getUnit()); + } + } else if (abstractTimeSeries instanceof RegularTimeSeriesFile) { + + RegularTimeSeriesFile timeSeriesFile = (RegularTimeSeriesFile) abstractTimeSeries; + if (timeSeriesFile.isSetUom()) + psTimeSeries.setString(18, timeSeriesFile.getUom()); + if (timeSeriesFile.isSetFileURI()) + psTimeSeries.setString(19, timeSeriesFile.getFileURI().toString()); + if (timeSeriesFile.isSetNumberOfHeaderLines()) + psTimeSeries.setLong(20, timeSeriesFile.getNumberOfHeaderLines()); + if (timeSeriesFile.isSetFieldSeparator()) + psTimeSeries.setString(21, timeSeriesFile.getFieldSeparator()); + if (timeSeriesFile.isSetRecordSeparator() ) + psTimeSeries.setString(22, timeSeriesFile.getRecordSeparator()); + if (timeSeriesFile.isSetDecimalSymbol() ) + psTimeSeries.setString(24, timeSeriesFile.getDecimalSymbol()); + if (timeSeriesFile.isSetValueColumnNumber()) + psTimeSeries.setLong(23, timeSeriesFile.getValueColumnNumber()); + if (timeSeriesFile.isSetTemporalExtent()){ + Timestamp beginPosition = Timestamp.from(timeSeriesFile.getTemporalExtent().getTimePeriod().getBeginPosition().toInstant()); + Timestamp endPosition = Timestamp.from(timeSeriesFile.getTemporalExtent().getTimePeriod().getEndPosition().toInstant()); + psTimeSeries.setTimestamp(7, beginPosition); + psTimeSeries.setTimestamp(8, endPosition); + } + if (timeSeriesFile.isSetTimeInterval()) { + psTimeSeries.setDouble(14, timeSeriesFile.getTimeInterval().getValue()); + psTimeSeries.setString(15, timeSeriesFile.getTimeInterval().getUnit()); + } + } else if (abstractTimeSeries instanceof TypicalValuesTimeSeries) { + + TypicalValuesTimeSeries timeSeries = (TypicalValuesTimeSeries) abstractTimeSeries; + + if (timeSeries.isSetStartTime()) { + psTimeSeries.setObject(9, timeSeries.getStartTime()); + } + if (timeSeries.isSetStartDay()) { + psTimeSeries.setInt(10, timeSeries.getStartDay()); + } + if (timeSeries.isSetStartMonth()) { + psTimeSeries.setInt(11, timeSeries.getStartMonth()); + } + if (timeSeries.isSetValues() && timeSeries.getValues().isSetValue()) { + String values = valueJoiner.join(" ", timeSeries.getValues().getValue()); + psTimeSeries.setString(16, values); + psTimeSeries.setString(17, timeSeries.getValues().getUom()); + } + if (timeSeries.isSetTemporalExtent()){ + psTimeSeries.setDouble(12, timeSeries.getTemporalExtent().getValue()); + psTimeSeries.setString(13, timeSeries.getTemporalExtent().getUnit()); + } + if (timeSeries.isSetTimeInterval()) { + psTimeSeries.setDouble(14, timeSeries.getTimeInterval().getValue()); + psTimeSeries.setString(15, timeSeries.getTimeInterval().getUnit()); + } + } else if (abstractTimeSeries instanceof TypicalValuesTimeSeriesFile) { + TypicalValuesTimeSeriesFile file = (TypicalValuesTimeSeriesFile) abstractTimeSeries; + if (file.isSetStartTime()) { + LocalTime localTime = file.getStartTime().toLocalTime(); + Time sqlTime = Time.valueOf(localTime); + psTimeSeries.setTime(9, sqlTime); + } + if (file.isSetStartDay()) { + psTimeSeries.setInt(10, file.getStartDay()); + } + if (file.isSetStartMonth()) { + psTimeSeries.setInt(11, file.getStartMonth()); + } + if (file.isSetUom()) + psTimeSeries.setString(18, file.getUom()); + if (file.isSetFileURI()) + psTimeSeries.setString(19, file.getFileURI().toString()); + + if (file.isSetTemporalExtent()){ + psTimeSeries.setDouble(12, file.getTemporalExtent().getValue()); + psTimeSeries.setString(13, file.getTemporalExtent().getUnit()); + } + if (file.isSetTimeInterval()) { + psTimeSeries.setDouble(14, file.getTimeInterval().getValue()); + psTimeSeries.setString(15, file.getTimeInterval().getUnit()); + } + if (file.isSetNumberOfHeaderLines()) + psTimeSeries.setLong(20, file.getNumberOfHeaderLines()); + if (file.isSetFieldSeparator()) + psTimeSeries.setString(21, file.getFieldSeparator()); + if (file.isSetRecordSeparator()) + psTimeSeries.setString(22, file.getRecordSeparator()); + if (file.isSetDecimalSymbol()) + psTimeSeries.setString(24, file.getDecimalSymbol()); + if (file.isSetValueColumnNumber()) + psTimeSeries.setInt(23, file.getValueColumnNumber()); + + } else if (abstractTimeSeries instanceof SensorConnection) { + SensorConnection sensorConnection = (SensorConnection) abstractTimeSeries; + if (sensorConnection.isSetTemporalExtent()) { + psTimeSeries.setDouble(12, sensorConnection.getTemporalExtent().getValue()); + psTimeSeries.setString(13, sensorConnection.getTemporalExtent().getUnit()); + } + if (sensorConnection.isSetConnectionType()) { + psTimeSeries.setString(28, sensorConnection.getConnectionType().getValue()); + psTimeSeries.setString(27, sensorConnection.getConnectionType().getCodeSpace()); + } + if (sensorConnection.isSetObservationProperty()) + psTimeSeries.setString(36, sensorConnection.getObservationProperty()); + if (sensorConnection.isSetUom()) + psTimeSeries.setString(18, sensorConnection.getUom()); + if (sensorConnection.isSetSensorID()) + psTimeSeries.setString(37, sensorConnection.getSensorID()); + if (sensorConnection.isSetSensorName()) + psTimeSeries.setString(38, sensorConnection.getSensorName()); + if (sensorConnection.isSetObservationID()) + psTimeSeries.setString(35, sensorConnection.getObservationID()); + if (sensorConnection.isSetObservationProperty()) + psTimeSeries.setString(36, sensorConnection.getObservationProperty()); + if (sensorConnection.isSetDataStreamID()) + psTimeSeries.setString(30, sensorConnection.getDataStreamID()); + if (sensorConnection.isSetBaseURL()) + psTimeSeries.setString(27, sensorConnection.getBaseURL().toString()); + if (sensorConnection.isSetAuthType()) + psTimeSeries.setString(25, sensorConnection.getAuthType().getValue()); + psTimeSeries.setString(26, sensorConnection.getAuthType().getCodeSpace()); + if (sensorConnection.isSetMqtServer()) + psTimeSeries.setString(33, sensorConnection.getMqtServer()); + if (sensorConnection.isSetMqtTopic()) + psTimeSeries.setString(34, sensorConnection.getMqtTopic()); + if (sensorConnection.isSetLinkToObservation()) + psTimeSeries.setString(31, sensorConnection.getLinkToObservation()); + if (sensorConnection.isSetLinkToSensorDescription()) + psTimeSeries.setString(32, sensorConnection.getLinkToSensorDescription()); + } + psTimeSeries.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(objectType); + } + + private void setTimeSeriesToNull() throws SQLException { + + psTimeSeries.setNull(1, Types.INTEGER); // id + psTimeSeries.setNull(2, Types.INTEGER); // objectclass_id + psTimeSeries.setNull(3, Types.VARCHAR); // acquisition_method + psTimeSeries.setNull(4, Types.VARCHAR); // acquisition_method_codespace + psTimeSeries.setNull(5, Types.VARCHAR); // interpolation_type + psTimeSeries.setNull(6, Types.VARCHAR); // source + psTimeSeries.setNull(7, Types.TIMESTAMP); // period_begin + psTimeSeries.setNull(8, Types.TIMESTAMP); // period_end + psTimeSeries.setNull(9, Types.TIME); // start_time + psTimeSeries.setNull(10, Types.INTEGER); // start_day + psTimeSeries.setNull(11, Types.INTEGER); // start_month + psTimeSeries.setNull(12, Types.NUMERIC); // temporal_extent + psTimeSeries.setNull(13, Types.VARCHAR); // temporal_extent_unit + psTimeSeries.setNull(14, Types.NUMERIC); // time_interval + psTimeSeries.setNull(15, Types.VARCHAR); // time_interval_unit + psTimeSeries.setNull(16, Types.VARCHAR); // values_list + psTimeSeries.setNull(17, Types.VARCHAR); // values_list_uom + psTimeSeries.setNull(18, Types.VARCHAR); // uom + psTimeSeries.setNull(19, Types.VARCHAR); // file_uri + psTimeSeries.setNull(20, Types.INTEGER); // num_of_header_lines + psTimeSeries.setNull(21, Types.VARCHAR); // field_separator + psTimeSeries.setNull(22, Types.VARCHAR); // record_separator + psTimeSeries.setNull(23, Types.INTEGER); // value_column_number + psTimeSeries.setNull(24, Types.VARCHAR); // decimal_symbol + psTimeSeries.setNull(25, Types.VARCHAR); // auth_type + psTimeSeries.setNull(26, Types.VARCHAR); // auth_type_codespace + psTimeSeries.setNull(27, Types.VARCHAR); // base_url + psTimeSeries.setNull(28, Types.VARCHAR); // connection_type + psTimeSeries.setNull(29, Types.VARCHAR); // connection_type_codespace + psTimeSeries.setNull(30, Types.VARCHAR); // datastream_id + psTimeSeries.setNull(31, Types.VARCHAR); // link_to_observation + psTimeSeries.setNull(32, Types.VARCHAR); // link_to_sensor_description + psTimeSeries.setNull(33, Types.VARCHAR); // mqtt_server + psTimeSeries.setNull(34, Types.VARCHAR); // mqtt_topic + psTimeSeries.setNull(35, Types.VARCHAR); // observation_id + psTimeSeries.setNull(36, Types.VARCHAR); // observation_property + psTimeSeries.setNull(37, Types.VARCHAR); // sensor_id + psTimeSeries.setNull(38, Types.VARCHAR); // sensor_name + + } + + private void setAbstractTimeseriesProperties(AbstractTimeSeries abstractTimeSeries) throws SQLException { + + if (abstractTimeSeries.isSetAcquisitionMethod()) { + psTimeSeries.setString(3, abstractTimeSeries.getAcquisitionMethod().getValue()); + psTimeSeries.setString(4, abstractTimeSeries.getAcquisitionMethod().getCodeSpace()); + } + if (abstractTimeSeries.isSetInterpolationTypeValue()) + psTimeSeries.setString(5, abstractTimeSeries.getInterpolationTypeValue().value()); + if (abstractTimeSeries.isSetSource()) + psTimeSeries.setString(6, abstractTimeSeries.getSource()); + } + + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + psTimeSeries.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + psTimeSeries.close(); + } + + private String getColumnNames() { + return "id, objectclass_id, acquisition_method, acquisition_method_codespace, interpolation_type, source, " + + "period_begin, period_end, start_time, start_day, start_month, temporal_extent, temporal_extent_unit, " + + "time_interval, time_interval_unit, values_list, values_list_uom, uom, file_uri, num_of_header_lines, " + + "field_separator, record_separator, value_column_number, decimal_symbol, auth_type, auth_type_codespace, " + + "base_url, connection_type, connection_type_codespace, datastream_id, link_to_observation, " + + "link_to_sensor_description, mqtt_server, mqtt_topic, observation_id, observation_property, " + + "sensor_id, sensor_name"; + } + + private String getPlaceholders(int count) { + return String.join(",", Collections.nCopies(count, "?")); + } + + } diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/UrbanFunctionAreaImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/UrbanFunctionAreaImporter.java new file mode 100644 index 0000000..571a0b7 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/UrbanFunctionAreaImporter.java @@ -0,0 +1,75 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.config.geometry.GeometryObject; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citygml4j.ade.energy.model.supportingClasses.UrbanFunctionArea; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; + +public class UrbanFunctionAreaImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + public UrbanFunctionAreaImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + + ps = connection.prepareStatement("insert into " + + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.URBAN_FUNCTION_AREA)) + " " + + "(id, type, type_codespace, code, code_codespace) " + + "values (?, ?, ?, ?, ?)"); + + geometryConverter = helper.getGeometryConverter(); + } + + public void doImport(UrbanFunctionArea urbanFunctionArea, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + ps.setLong(1, objectId); + setUrbanFunctionAreaNull(); + if (urbanFunctionArea.isSetType()) { + ps.setString(2, urbanFunctionArea.getType().getValue()); + ps.setString(3, urbanFunctionArea.getType().getCodeSpace()); + } + if (urbanFunctionArea.isSetCode()){ + ps.setString(4, urbanFunctionArea.getCode().getValue()); + ps.setString(5, urbanFunctionArea.getCode().getCodeSpace()); + } + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(objectType); + + } + + private void setUrbanFunctionAreaNull() throws SQLException { + ps.setNull(2, Types.VARCHAR); // type + ps.setNull(3, Types.VARCHAR); // type_codespace + ps.setNull(4, Types.VARCHAR); // code + ps.setNull(5, Types.VARCHAR); // code_codespace + } + + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/UtilityNetworkConnectionImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/UtilityNetworkConnectionImporter.java new file mode 100644 index 0000000..aeff048 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/UtilityNetworkConnectionImporter.java @@ -0,0 +1,89 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citygml4j.ade.energy.model.core.UtilityNetworkConnection; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class UtilityNetworkConnectionImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + public UtilityNetworkConnectionImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + + ps = connection.prepareStatement("INSERT INTO " + + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.UTL_NTW_CONNECTION)) + " " + + "(id, network_type, network_type_codespace, connection_status, function_in_network, " + + "function_in_network_codespace, usage_in_network, usage_in_network_codespace, " + + "network_id, network_node_id, cityobject_id) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + + geometryConverter = helper.getGeometryConverter(); + } + + public void doImport(UtilityNetworkConnection networkConnection, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + ps.setLong(1, objectId); + + if (networkConnection.isSetType()) { + ps.setString(2, networkConnection.getNetworkType().getValue()); + ps.setString(3, networkConnection.getNetworkType().getCodeSpace()); + } + + if (networkConnection.isSetConnectionStatus()) { + ps.setString(4, networkConnection.getConnectionStatus().toString()); + } + + if (networkConnection.isSetFunctionInNetwork()) { + ps.setString(5, networkConnection.getFunctionInNetwork().getValue()); + ps.setString(6, networkConnection.getFunctionInNetwork().getCodeSpace()); + } + + if (networkConnection.isSetUsageInNetwork()) { + ps.setString(7, networkConnection.getUsageInNetwork().getValue()); + ps.setString(8, networkConnection.getUsageInNetwork().getCodeSpace()); + } + + if (networkConnection.isSetNetworkID()) { + ps.setString(9, networkConnection.getNetworkID()); + } + + if (networkConnection.isSetNetworkNodeID()) { + ps.setString(10, networkConnection.getNetworkNodeID()); + } + + //set correctly + ps.setLong(11, objectType.getObjectClassId()); + + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(objectType); + } + + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/WeatherDataImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/WeatherDataImporter.java new file mode 100644 index 0000000..ddf69cd --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/WeatherDataImporter.java @@ -0,0 +1,123 @@ +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; + +import org.citydb.config.geometry.GeometryObject; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citygml4j.ade.energy.model.core.ReferencePointProperty; +import org.citygml4j.ade.energy.model.core.WeatherData; +import org.citygml4j.ade.energy.model.supportingClasses.AbstractTimeSeries; +import org.citygml4j.ade.energy.model.supportingClasses.AbstractTimeSeriesProperty; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; + +public class WeatherDataImporter implements ADEImporter { + + private final Connection connection; + private final CityGMLImportHelper helper; + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + public WeatherDataImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws SQLException { + + this.connection = connection; + this.helper = helper; + + ps = connection.prepareStatement("insert into " + + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.WEATHER_DATA)) + " " + + "(id, type, type_codespace, value_type, value_type_codespace, yearly_value, yearly_value_uom, library_code, library_code_codespace, time_series_id, cityObject_id, position) " + + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + + geometryConverter = helper.getGeometryConverter(); + } + + public void doImport(WeatherData weatherData, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + long cityObjectId = foreignKeys.get("cityObjectId"); + setWeatherDataNull(weatherData); + + ps.setLong(1, objectId); + + if(weatherData.isSetType()){ + ps.setString(2, weatherData.getType().getValue()); + ps.setString(3, weatherData.getType().getCodeSpace()); + } + + if (weatherData.isSetValueType()){ + ps.setString(4, weatherData.getValueType().getValue()); + ps.setString(5, weatherData.getValueType().getCodeSpace()); + } + + if (weatherData.isSetYearlyValue()){ + ps.setDouble(6, weatherData.getYearlyValue().getValue()); + ps.setString(7, weatherData.getYearlyValue().getUom()); + } + + if (weatherData.isSetLibraryCode()){ + ps.setString(8, weatherData.getLibraryCode().getValue()); + ps.setString(9, weatherData.getLibraryCode().getCodeSpace()); + } + + if (cityObjectId != 0) { + ps.setLong(11, cityObjectId); + } + + if (weatherData.isSetPosition()){ + + GeometryObject geometryObject = null; + ReferencePointProperty referencePoint = weatherData.getPosition(); + if (referencePoint != null && referencePoint.isSetValue()) + geometryObject = helper.getGeometryConverter().getPoint(referencePoint.getValue()); + if (geometryObject != null) + ps.setObject(12, helper.getDatabaseAdapter().getGeometryConverter().getDatabaseObject(geometryObject, connection)); + else + ps.setNull(12, helper.getDatabaseAdapter().getGeometryConverter().getNullGeometryType(), + helper.getDatabaseAdapter().getGeometryConverter().getNullGeometryTypeName()); + } + + if (weatherData.getTimeDependentValues() != null){ + long timeSeriesId = helper.importObject(weatherData.getTimeDependentValues().getAbstractTimeSeries(), null); + ps.setLong(10, timeSeriesId); + } + + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(objectType); + } + + public void setWeatherDataNull(WeatherData weatherData) throws SQLException { + ps.setNull(1, Types.INTEGER); + ps.setNull(2, Types.VARCHAR); + ps.setNull(3, Types.VARCHAR); + ps.setNull(4, Types.VARCHAR); + ps.setNull(5, Types.VARCHAR); + ps.setNull(6, Types.BIGINT); + ps.setNull(7, Types.VARCHAR); + ps.setNull(8, Types.VARCHAR); + ps.setNull(9, Types.VARCHAR); + ps.setNull(10, Types.BIGINT); + ps.setNull(11, Types.BIGINT); + ps.setNull(12, Types.BINARY); + } + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/importer/WeatherStationImporter.java b/src/main/java/de/stuttgart/hft/ade/energy2/importer/WeatherStationImporter.java new file mode 100644 index 0000000..238edaf --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/importer/WeatherStationImporter.java @@ -0,0 +1,94 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stuttgart.hft.ade.energy2.importer; + +import de.stuttgart.hft.ade.energy2.schema.ADETable; +import org.citydb.config.geometry.GeometryObject; +import org.citydb.core.ade.importer.ADEImporter; +import org.citydb.core.ade.importer.CityGMLImportHelper; +import org.citydb.core.ade.importer.ForeignKeys; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.operation.importer.CityGMLImportException; +import org.citydb.core.operation.importer.util.GeometryConverter; +import org.citygml4j.ade.energy.model.supportingClasses.WeatherStation; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class WeatherStationImporter implements ADEImporter { + private final Connection connection; + private final CityGMLImportHelper helper; + + private GeometryConverter geometryConverter; + private PreparedStatement ps; + private int batchCounter; + + public WeatherStationImporter(Connection connection, CityGMLImportHelper helper, ImportManager manager) throws CityGMLImportException, SQLException { + this.connection = connection; + this.helper = helper; + + ps = connection.prepareStatement("insert into " + + helper.getTableNameWithSchema(manager.getSchemaMapper().getTableName(ADETable.CITYOBJECT)) + " " + + "(id, ref_point) " + + "values (?, ?)"); + + geometryConverter = helper.getGeometryConverter(); + } + + public void doImport(WeatherStation weatherStation, long objectId, AbstractObjectType objectType, ForeignKeys foreignKeys) throws CityGMLImportException, SQLException { + ps.setLong(1, objectId); + + GeometryObject geometryObject = null; + + if (geometryObject != null) + ps.setObject(2, helper.getDatabaseAdapter().getGeometryConverter().getDatabaseObject(geometryObject, connection)); + else + ps.setNull(2, helper.getDatabaseAdapter().getGeometryConverter().getNullGeometryType(), + helper.getDatabaseAdapter().getGeometryConverter().getNullGeometryTypeName()); + + ps.addBatch(); + if (++batchCounter == helper.getDatabaseAdapter().getMaxBatchSize()) + helper.executeBatch(objectType); + + } + + @Override + public void executeBatch() throws CityGMLImportException, SQLException { + if (batchCounter > 0) { + ps.executeBatch(); + batchCounter = 0; + } + } + + @Override + public void close() throws CityGMLImportException, SQLException { + ps.close(); + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/schema/ADESequence.java b/src/main/java/de/stuttgart/hft/ade/energy2/schema/ADESequence.java new file mode 100644 index 0000000..de2a2c2 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/schema/ADESequence.java @@ -0,0 +1,43 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stuttgart.hft.ade.energy2.schema; + +public enum ADESequence { + VOLUMETYPE_SEQ, + FLOORAREA_SEQ, + QUALIFIED_ATTRIBUTE_SEQ, + HEATEXCHANGETYPE_SEQ, + TRANSMITTANCE_SEQ, + OPTICALPROPERTIES_SEQ, + REFLECTANCE_SEQ, + TIMEVALUESPROPERTI_SEQ, + PERIODOFYEAR_SEQ, + DAILYSCHEDULE_SEQ, + REFURBISHMENT_MEASURE_SEQ +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/schema/ADETable.java b/src/main/java/de/stuttgart/hft/ade/energy2/schema/ADETable.java new file mode 100644 index 0000000..43f11d6 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/schema/ADETable.java @@ -0,0 +1,88 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stuttgart.hft.ade.energy2.schema; + +import de.stuttgart.hft.ade.energy2.importer.*; +import org.citydb.core.ade.importer.ADEImporter; +import org.citygml4j.ade.energy.model.core.AbstractQualifiedAttribute; + +public enum ADETable { + ADDRESS_TO_BUILDING_UNIT(null), + BUILDING(BuildingPropertiesImporter.class), + BUILDING_PARTITION(null), + CITYOBJECT(CityObjectPropertiesImporter.class), + CTYOBJ_RELATION(null), + DEVICE(AbstractDeviceImporter.class), + DEVICE_OPERATION(DeviceOperationImporter.class), + ENERGY_PERF_CERT(EnergyPerformanceCertificateImporter.class), + FACILITIES(null), + HEAT_EXCHANGE_TYPE(null), + IMAGE_TEXTURE(null), + LAYERED_CONSTRUCTION(null), + LAYER_COMPONENT(null), + LAYER(null), + LIBRARY(null), + MATERIAL(null), + OCCUPANTS(null), + OPENING(null), + OPTICAL_PROPERTY(null), + PERIOD_OF_YEAR(null), + QUALIFIED_ATTRIBUTE(AbstractQualifiedAttributeImporter.class), + RESOURCE(ResourceImporter.class), + REFURBISHMENT_MEASURE(RefurbishmentMeasureImporter.class), + SCHEDULE(null), + SYSTEM_OPERATION(null), + SOLAR_COLLECTOR(null), + STORAGE_SYSTEM(null), + STORAGE_DEVICE(null), + SENSOR_CONNECTION(null), + SOLAR_ENERGY_SYSTEM(null), + SUITABILITY(null), + SCHEDULE_COMPONENT(null), + THERMAL_BOUNDARY(null), + THERMAL_OPENING(null), + THEM_SURF_TO_THERMAL_ZONE(null), + THEMATIC_SURFACE(null), + THERMAL_ZONE(null), + TIME_SERIES(TimeSeriesImporter.class), + URBAN_FUNCTION_AREA(UrbanFunctionAreaImporter.class), + USAGE_ZONE(null), + UTL_NTW_CONNECTION(UtilityNetworkConnectionImporter.class), + WEATHER_DATA(WeatherDataImporter.class); + + private Class importerClass; + + ADETable(Class importerClass) { + this.importerClass = importerClass; + } + + public Class getImporterClass() { + return importerClass; + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/schema/ObjectMapper.java b/src/main/java/de/stuttgart/hft/ade/energy2/schema/ObjectMapper.java new file mode 100644 index 0000000..4463ee2 --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/schema/ObjectMapper.java @@ -0,0 +1,140 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stuttgart.hft.ade.energy2.schema; + +import org.citydb.core.ade.ADEExtensionException; +import org.citydb.core.ade.ADEObjectMapper; +import org.citydb.core.database.schema.mapping.AbstractObjectType; +import org.citydb.core.database.schema.mapping.SchemaMapping; +import org.citygml4j.ade.energy.model.core.*; +import org.citygml4j.ade.energy.model.supportingClasses.*; +import org.citygml4j.model.gml.base.AbstractGML; +import org.citygml4j.model.module.citygml.CityGMLVersion; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class ObjectMapper implements ADEObjectMapper { + private Map, Integer> objectClassIds = new HashMap<>(); + + public void populateObjectClassIds(SchemaMapping schemaMapping) throws ADEExtensionException { + for (AbstractObjectType type : schemaMapping.getAbstractObjectTypes()) { + int objectClassId = type.getObjectClassId(); + + switch (type.getPath()) { + case "AbstractSchedule": + objectClassIds.put(AbstractSchedule.class, objectClassId); + break; + case "AbstractTimeSeries": + objectClassIds.put(AbstractTimeSeries.class, objectClassId); + break; + case "RegularTimeSeries": + objectClassIds.put(RegularTimeSeries.class, objectClassId); + break; + case "RegularTimeSeriesFile": + objectClassIds.put(RegularTimeSeriesFile.class, objectClassId); + break; + case "TypicalValuesTimeSeries": + objectClassIds.put(TypicalValuesTimeSeries.class, objectClassId); + break; + case "TypicalValuesTimeSeriesFile": + objectClassIds.put(TypicalValuesTimeSeriesFile.class, objectClassId); + break; + case "SensorConnection": + objectClassIds.put(SensorConnection.class, objectClassId); + break; + case "AbstractResource": + objectClassIds.put(AbstractResource.class, objectClassId); + case "UrbanFunctionArea": + objectClassIds.put(UrbanFunctionArea.class, objectClassId); + break; + case "RefurbishmentMeasure": + objectClassIds.put(RefurbishmentMeasure.class, objectClassId); + break; + case "WeatherData": + objectClassIds.put(WeatherData.class, objectClassId); + break; + case "WeatherStation": + objectClassIds.put(WeatherStation.class, objectClassId); + break; + case "Water": + objectClassIds.put(Water.class, objectClassId); + break; + case "Energy": + objectClassIds.put(Energy.class, objectClassId); + break; + case "AbstractDevice": + objectClassIds.put(AbstractDevice.class, objectClassId); + case "DeviceOperation": + objectClassIds.put(DeviceOperation.class, objectClassId); + break; + case "UtilityNetworkConnection": + objectClassIds.put(UtilityNetworkConnection.class, objectClassId); + break; + case "AbstractBuilding": + objectClassIds.put(AbstractBuilding.class, objectClassId); + break; + case "QualifiedVolume": + objectClassIds.put(QualifiedVolume.class, objectClassId); + break; + case "QualifiedHeight": + objectClassIds.put(QualifiedHeight.class, objectClassId); + break; + case "QualifiedArea": + objectClassIds.put(QualifiedArea.class, objectClassId); + break; + } + } + } + + @Override + public AbstractGML createObject(int objectClassId, CityGMLVersion version) { + if (version == CityGMLVersion.v2_0_0) { + for (Entry, Integer> entry : objectClassIds.entrySet()) { + if (entry.getValue() == objectClassId) { + try { + return entry.getKey().getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | + InvocationTargetException e) { + } + } + } + } + + return null; + } + + @Override + public int getObjectClassId(Class adeObjectClass) { + Integer objectClassId = objectClassIds.get(adeObjectClass); + return objectClassId != null ? objectClassId : 0; + } +} diff --git a/src/main/java/de/stuttgart/hft/ade/energy2/schema/SchemaMapper.java b/src/main/java/de/stuttgart/hft/ade/energy2/schema/SchemaMapper.java new file mode 100644 index 0000000..3f7746f --- /dev/null +++ b/src/main/java/de/stuttgart/hft/ade/energy2/schema/SchemaMapper.java @@ -0,0 +1,64 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stuttgart.hft.ade.energy2.schema; + +import java.util.EnumMap; +import java.util.Map; + +public class SchemaMapper { + private EnumMap tableNames = new EnumMap<>(ADETable.class); + private EnumMap sequenceNames = new EnumMap<>(ADESequence.class); + + public void populateSchemaNames(String prefix) { + for (ADETable table : ADETable.values()) + tableNames.put(table, prefix + "_" + table.toString().toLowerCase()); + + for (ADESequence sequence : ADESequence.values()) + sequenceNames.put(sequence, prefix + "_" + sequence.toString().toLowerCase()); + } + + public String getTableName(ADETable table) { + return tableNames.get(table); + } + + public ADETable fromTableName(String tableName) { + tableName = tableName.toLowerCase(); + + for (Map.Entry entry : tableNames.entrySet()) { + if (entry.getValue().equals(tableName)) + return entry.getKey(); + } + + return null; + } + + public String getSequenceName(ADESequence sequence) { + return sequenceNames.get(sequence); + } +} diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..f60b961 --- /dev/null +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: de.stuttgart.hft.EnergyADEExtension + diff --git a/src/main/resources/services/org.citydb.core.ade.ADEExtension b/src/main/resources/services/org.citydb.core.ade.ADEExtension new file mode 100644 index 0000000..70ec670 --- /dev/null +++ b/src/main/resources/services/org.citydb.core.ade.ADEExtension @@ -0,0 +1 @@ +de.stuttgart.hft.EnergyADEExtension \ No newline at end of file -- GitLab