diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser new file mode 100644 index 0000000..f76db7a Binary files /dev/null and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/caches/gradle_models.ser b/.idea/caches/gradle_models.ser new file mode 100644 index 0000000..0ccc18d Binary files /dev/null and b/.idea/caches/gradle_models.ser differ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..0d15693 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 3c42e02..d225031 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,11 +1,10 @@ + - + diff --git a/.idea/modules.xml b/.idea/modules.xml index 6e3a539..c0f6a73 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,11 @@ - + + + + + diff --git a/apk/final.apk b/apk/final.apk new file mode 100644 index 0000000..6f612af Binary files /dev/null and b/apk/final.apk differ diff --git a/app/build.gradle b/app/build.gradle index 7fdc74c..213a96a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,15 @@ apply plugin: 'com.android.application' +apply plugin: 'com.google.gms.google-services' +apply plugin: 'com.google.ar.sceneform.plugin' android { compileSdkVersion 27 - buildToolsVersion '28.0.3' + defaultConfig { applicationId "fr.geolabs.dev.mapmint4me" - minSdkVersion 16 + minSdkVersion 24 targetSdkVersion 27 - versionCode 23 + versionCode 22 versionName "1.0" multiDexEnabled true } @@ -25,16 +27,67 @@ android { // but continue the build even when errors are found: abortOnError false } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { //implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:26.1.0' - implementation 'com.android.support:design:26.1.0' - implementation 'com.android.support:support-compat:26.1.0' + //noinspection GradleCompatible + implementation 'com.android.support:appcompat-v7:27.1.0' + implementation 'com.android.support:design:27.1.0' + implementation 'com.android.support:support-compat:27.1.0' api 'com.google.android.gms:play-services:12.0.1' - api 'com.google.android.gms:play-services-gcm:9.0.2' + api 'com.google.android.gms:play-services-gcm:10.2.1' api 'com.google.android.gms:play-services-location:12.0.1' api 'com.readystatesoftware.sqliteasset:sqliteassethelper:+' //implementation 'com.android.support:leanback-v17:26.1.0' + + implementation 'com.google.ar:core:1.8.0' + implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.8.0' + implementation 'com.google.ar.sceneform:core:1.8.0' + + implementation 'com.google.ar:core:1.0.0' + implementation 'javax.vecmath:vecmath:1.5.2' + implementation 'com.android.support:appcompat-v7:27.1.0' + implementation 'com.android.support:design:27.1.0' + implementation 'com.google.firebase:firebase-database:11.0.0' + implementation 'org.greenrobot:eventbus:3.1.1' + implementation 'com.android.support:preference-v7:27.0.0' + implementation 'com.android.support:preference-v14:27.0.0' + + + + + + implementation 'de.javagl:obj:0.2.1' + implementation('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') { + transitive = true; + } + + } + + +sceneform.asset('sampledata/model.obj', + 'default', + 'sampledata/model.sfa', + 'src/main/assets/model') +sceneform.asset('sampledata/wheelchair.fbx', + 'default', + 'sampledata/wheelchair.sfa', + 'src/main/assets/wheelchair') + + +sceneform.asset('sampledata/stroller.fbx', + 'default', + 'sampledata/stroller.sfa', + 'src/main/assets/stroller') + + +sceneform.asset('sampledata/cart.fbx', + 'default', + 'sampledata/cart.sfa', + 'src/main/assets/cart') diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..bc6bde3 --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,48 @@ +{ + "project_info": { + "project_number": "330195530896", + "firebase_url": "https://mapmint4meo.firebaseio.com", + "project_id": "mapmint4meo", + "storage_bucket": "mapmint4meo.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:330195530896:android:7b763fa19e5f1685", + "android_client_info": { + "package_name": "fr.geolabs.dev.mapmint4me" + } + }, + "oauth_client": [ + { + "client_id": "330195530896-a960pga83drjqsmd1p5n0l84pradsa1l.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "fr.geolabs.dev.mapmint4me", + "certificate_hash": "ff2b0d70f42941f42a93c27e07470d25cbca0587" + } + }, + { + "client_id": "330195530896-3k58vvvf50j35dvqba4n9o9nag25ej56.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCq3VvJ7SEZUgut7y9_btAWI9G8qSUNRxs" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "330195530896-3k58vvvf50j35dvqba4n9o9nag25ej56.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/sampledata/Andy.mtl b/app/sampledata/Andy.mtl new file mode 100644 index 0000000..a53ffd1 --- /dev/null +++ b/app/sampledata/Andy.mtl @@ -0,0 +1,7 @@ +newmtl initialShadingGroup +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.00 0.00 0.00 +Tf 1.00 1.00 1.00 +map_Kd Andy_Diffuse.png +Ni 1.00 diff --git a/app/sampledata/Andy.obj b/app/sampledata/Andy.obj new file mode 100644 index 0000000..6c4cafd --- /dev/null +++ b/app/sampledata/Andy.obj @@ -0,0 +1,8619 @@ +# This file uses centimeters as units for non-parametric coordinates. + +mtllib Andy.mtl +g default +v 0.036531 0.203676 -0.001768 +v 0.035000 0.204560 -0.002500 +v 0.033469 0.205443 -0.001768 +v 0.032835 0.205810 0.000000 +v 0.033469 0.205443 0.001768 +v 0.035000 0.204560 0.002500 +v 0.036531 0.203676 0.001768 +v 0.037165 0.203310 0.000000 +v 0.036951 0.204877 -0.001531 +v 0.035625 0.205642 -0.002165 +v 0.034299 0.206408 -0.001531 +v 0.033750 0.206725 0.000000 +v 0.034299 0.206408 0.001531 +v 0.035625 0.205642 0.002165 +v 0.036951 0.204877 0.001531 +v 0.037500 0.204560 0.000000 +v 0.036848 0.205993 -0.000884 +v 0.036083 0.206435 -0.001250 +v 0.035317 0.206877 -0.000884 +v 0.035000 0.207060 0.000000 +v 0.035317 0.206877 0.000884 +v 0.036083 0.206435 0.001250 +v 0.036848 0.205993 0.000884 +v 0.037165 0.205810 0.000000 +v 0.036250 0.206725 0.000000 +v 0.027420 0.185957 0.006258 +v 0.020986 0.188283 0.004790 +v 0.020986 0.188283 -0.004790 +v 0.027420 0.185957 -0.006258 +v 0.028125 0.185957 0.000000 +v 0.021526 0.188283 0.000000 +v 0.024935 0.187127 0.002956 +v 0.026910 0.186366 0.002090 +v 0.022982 0.187924 0.002090 +v 0.024935 0.187127 -0.002956 +v 0.022982 0.187924 -0.002090 +v 0.026910 0.186366 -0.002090 +v 0.027766 0.186119 0.000000 +v 0.022169 0.188170 0.000000 +v -0.027420 0.185957 0.006258 +v -0.020986 0.188283 0.004790 +v -0.020986 0.188283 -0.004790 +v -0.027420 0.185957 -0.006258 +v -0.028125 0.185957 0.000000 +v -0.021526 0.188283 0.000000 +v -0.024935 0.187127 0.002956 +v -0.026910 0.186366 0.002090 +v -0.022982 0.187924 0.002090 +v -0.024935 0.187127 -0.002956 +v -0.022982 0.187924 -0.002090 +v -0.026910 0.186366 -0.002090 +v -0.027766 0.186119 0.000000 +v -0.022169 0.188170 0.000000 +v 0.000000 0.050001 -0.056250 +v -0.012517 0.050001 -0.054840 +v -0.024406 0.050001 -0.050679 +v -0.035071 0.050001 -0.043978 +v -0.043978 0.050001 -0.035071 +v -0.050679 0.050001 -0.024406 +v -0.054840 0.050001 -0.012517 +v -0.056250 0.050001 0.000000 +v -0.054840 0.050001 0.012517 +v -0.050679 0.050001 0.024406 +v -0.043978 0.050001 0.035071 +v -0.035071 0.050001 0.043978 +v -0.024406 0.050001 0.050679 +v -0.012517 0.050001 0.054840 +v 0.000000 0.050001 0.056250 +v 0.000000 0.040000 -0.046217 +v -0.010284 0.040000 -0.045058 +v -0.020053 0.040000 -0.041640 +v -0.028816 0.040000 -0.036134 +v -0.036134 0.040000 -0.028816 +v -0.041640 0.040000 -0.020053 +v -0.045058 0.040000 -0.010284 +v -0.046217 0.040000 0.000000 +v -0.045058 0.040000 0.010284 +v -0.041640 0.040000 0.020053 +v -0.036134 0.040000 0.028816 +v -0.028816 0.040000 0.036134 +v -0.020053 0.040000 0.041640 +v -0.010284 0.040000 0.045058 +v 0.000000 0.040000 0.046217 +v -0.034725 0.046910 0.043544 +v -0.024165 0.046910 0.050180 +v -0.012393 0.046910 0.054299 +v 0.000000 0.046910 0.055695 +v 0.000000 0.046910 -0.055695 +v -0.012393 0.046910 -0.054299 +v -0.024165 0.046910 -0.050180 +v -0.034725 0.046910 -0.043544 +v -0.043544 0.046910 -0.034725 +v -0.050180 0.046910 -0.024165 +v -0.054299 0.046910 -0.012393 +v -0.055695 0.046910 0.000000 +v -0.054299 0.046910 0.012393 +v -0.050180 0.046910 0.024165 +v -0.043544 0.046910 0.034725 +v -0.040731 0.041910 0.032482 +v -0.032482 0.041910 0.040731 +v -0.022604 0.041910 0.046938 +v -0.011593 0.041910 0.050791 +v 0.000000 0.041910 0.052098 +v 0.000000 0.041910 -0.052098 +v -0.011593 0.041910 -0.050791 +v -0.022604 0.041910 -0.046938 +v -0.032482 0.041910 -0.040731 +v -0.040731 0.041910 -0.032482 +v -0.046938 0.041910 -0.022604 +v -0.050791 0.041910 -0.011593 +v -0.052098 0.041910 0.000000 +v -0.050791 0.041910 0.011593 +v -0.046938 0.041910 0.022604 +v 0.012517 0.050001 -0.054840 +v 0.024406 0.050001 -0.050679 +v 0.035071 0.050001 -0.043978 +v 0.043978 0.050001 -0.035071 +v 0.050679 0.050001 -0.024406 +v 0.054840 0.050001 -0.012517 +v 0.056250 0.050001 0.000000 +v 0.054840 0.050001 0.012517 +v 0.050679 0.050001 0.024406 +v 0.043978 0.050001 0.035071 +v 0.035071 0.050001 0.043978 +v 0.024406 0.050001 0.050679 +v 0.012517 0.050001 0.054840 +v 0.010284 0.040000 -0.045058 +v 0.020053 0.040000 -0.041640 +v 0.028816 0.040000 -0.036134 +v 0.036134 0.040000 -0.028816 +v 0.041640 0.040000 -0.020053 +v 0.045058 0.040000 -0.010284 +v 0.046217 0.040000 0.000000 +v 0.045058 0.040000 0.010284 +v 0.041640 0.040000 0.020053 +v 0.036134 0.040000 0.028816 +v 0.028816 0.040000 0.036134 +v 0.020053 0.040000 0.041640 +v 0.010284 0.040000 0.045058 +v 0.034725 0.046910 0.043544 +v 0.024165 0.046910 0.050180 +v 0.012393 0.046910 0.054299 +v 0.012393 0.046910 -0.054299 +v 0.024165 0.046910 -0.050180 +v 0.034725 0.046910 -0.043544 +v 0.043544 0.046910 -0.034725 +v 0.050180 0.046910 -0.024165 +v 0.054299 0.046910 -0.012393 +v 0.055695 0.046910 0.000000 +v 0.054299 0.046910 0.012393 +v 0.050180 0.046910 0.024165 +v 0.043544 0.046910 0.034725 +v 0.040731 0.041910 0.032482 +v 0.032482 0.041910 0.040731 +v 0.022604 0.041910 0.046938 +v 0.011593 0.041910 0.050791 +v 0.011593 0.041910 -0.050791 +v 0.022604 0.041910 -0.046938 +v 0.032482 0.041910 -0.040731 +v 0.040731 0.041910 -0.032482 +v 0.046938 0.041910 -0.022604 +v 0.050791 0.041910 -0.011593 +v 0.052098 0.041910 0.000000 +v 0.050791 0.041910 0.011593 +v 0.046938 0.041910 0.022604 +v 0.000000 0.137500 0.000000 +v 0.000000 0.137500 0.054803 +v 0.000000 0.136062 0.056250 +v -0.012517 0.136062 0.054840 +v -0.012195 0.137500 0.053429 +v -0.024406 0.136062 0.050679 +v -0.023778 0.137500 0.049376 +v -0.035071 0.136062 0.043978 +v -0.034169 0.137500 0.042847 +v -0.043978 0.136062 0.035071 +v -0.042847 0.137500 0.034169 +v -0.050679 0.136062 0.024406 +v -0.049376 0.137500 0.023778 +v -0.054840 0.136062 0.012517 +v -0.053429 0.137500 0.012195 +v -0.056250 0.136062 0.000000 +v -0.054803 0.137500 0.000000 +v -0.054840 0.136062 -0.012517 +v -0.053429 0.137500 -0.012195 +v -0.050679 0.136062 -0.024406 +v -0.049376 0.137500 -0.023778 +v -0.043978 0.136062 -0.035071 +v -0.042847 0.137500 -0.034169 +v -0.035071 0.136062 -0.043978 +v -0.034169 0.137500 -0.042847 +v -0.024406 0.136062 -0.050679 +v -0.023778 0.137500 -0.049376 +v -0.012517 0.136062 -0.054840 +v -0.012195 0.137500 -0.053429 +v 0.000000 0.136062 -0.056250 +v 0.000000 0.137500 -0.054803 +v 0.012517 0.136062 0.054840 +v 0.012195 0.137500 0.053429 +v 0.024406 0.136062 0.050679 +v 0.023778 0.137500 0.049376 +v 0.035071 0.136062 0.043978 +v 0.034169 0.137500 0.042847 +v 0.043978 0.136062 0.035071 +v 0.042847 0.137500 0.034169 +v 0.050679 0.136062 0.024406 +v 0.049376 0.137500 0.023778 +v 0.054840 0.136062 0.012517 +v 0.053429 0.137500 0.012195 +v 0.056250 0.136062 0.000000 +v 0.054803 0.137500 0.000000 +v 0.054840 0.136062 -0.012517 +v 0.053429 0.137500 -0.012195 +v 0.050679 0.136062 -0.024406 +v 0.049376 0.137500 -0.023778 +v 0.043978 0.136062 -0.035071 +v 0.042847 0.137500 -0.034169 +v 0.035071 0.136062 -0.043978 +v 0.034169 0.137500 -0.042847 +v 0.024406 0.136062 -0.050679 +v 0.023778 0.137500 -0.049376 +v 0.012517 0.136062 -0.054840 +v 0.012195 0.137500 -0.053429 +v 0.000000 0.153477 -0.055769 +v -0.012410 0.153477 -0.054370 +v -0.024197 0.153477 -0.050246 +v -0.034771 0.153477 -0.043602 +v -0.043602 0.153477 -0.034771 +v -0.050246 0.153477 -0.024197 +v -0.054370 0.153477 -0.012410 +v -0.055769 0.153477 0.000000 +v -0.054370 0.153477 0.012410 +v -0.050246 0.153477 0.024197 +v -0.043602 0.153477 0.034771 +v -0.034771 0.153477 0.043602 +v -0.024197 0.153477 0.050246 +v -0.012410 0.153477 0.054370 +v 0.000000 0.153477 0.055769 +v 0.000000 0.159345 -0.054333 +v -0.012090 0.159345 -0.052971 +v -0.023574 0.159345 -0.048953 +v -0.033876 0.159345 -0.042479 +v -0.042480 0.159345 -0.033876 +v -0.048953 0.159345 -0.023574 +v -0.052971 0.159345 -0.012090 +v -0.054333 0.159345 0.000000 +v -0.052971 0.159345 0.012090 +v -0.048953 0.159345 0.023574 +v -0.042480 0.159345 0.033876 +v -0.033876 0.159345 0.042480 +v -0.023574 0.159345 0.048953 +v -0.012090 0.159345 0.052971 +v 0.000000 0.159345 0.054333 +v 0.000000 0.164974 -0.051968 +v -0.011564 0.164974 -0.050665 +v -0.022548 0.164974 -0.046822 +v -0.032402 0.164974 -0.040630 +v -0.040630 0.164974 -0.032402 +v -0.046822 0.164974 -0.022548 +v -0.050665 0.164974 -0.011564 +v -0.051968 0.164974 0.000000 +v -0.050665 0.164974 0.011564 +v -0.046822 0.164974 0.022548 +v -0.040630 0.164974 0.032402 +v -0.032402 0.164974 0.040630 +v -0.022548 0.164974 0.046822 +v -0.011564 0.164974 0.050665 +v 0.000000 0.164974 0.051968 +v 0.000000 0.170245 -0.048714 +v -0.010840 0.170245 -0.047493 +v -0.021136 0.170245 -0.043890 +v -0.030373 0.170245 -0.038086 +v -0.038086 0.170245 -0.030373 +v -0.043890 0.170245 -0.021136 +v -0.047493 0.170245 -0.010840 +v -0.048714 0.170245 0.000000 +v -0.047493 0.170245 0.010840 +v -0.043890 0.170245 0.021136 +v -0.038086 0.170245 0.030373 +v -0.030373 0.170245 0.038086 +v -0.021136 0.170245 0.043890 +v -0.010840 0.170245 0.047493 +v 0.000000 0.170245 0.048714 +v 0.000000 0.175052 -0.044626 +v -0.009930 0.175052 -0.043507 +v -0.019363 0.175052 -0.040207 +v -0.027824 0.175052 -0.034890 +v -0.034890 0.175052 -0.027824 +v -0.040207 0.175052 -0.019363 +v -0.043507 0.175052 -0.009930 +v -0.044626 0.175052 0.000000 +v -0.043507 0.175052 0.009930 +v -0.040207 0.175052 0.019363 +v -0.034890 0.175052 0.027824 +v -0.027824 0.175052 0.034890 +v -0.019363 0.175052 0.040207 +v -0.009930 0.175052 0.043507 +v 0.000000 0.175052 0.044626 +v 0.000000 0.179312 -0.039775 +v -0.008851 0.179312 -0.038777 +v -0.017258 0.179312 -0.035836 +v -0.024799 0.179312 -0.031097 +v -0.031097 0.179312 -0.024799 +v -0.035836 0.179312 -0.017258 +v -0.038777 0.179312 -0.008851 +v -0.039775 0.179312 0.000000 +v -0.038777 0.179312 0.008851 +v -0.035836 0.179312 0.017258 +v -0.031097 0.179312 0.024799 +v -0.024799 0.179312 0.031097 +v -0.017258 0.179312 0.035836 +v -0.008851 0.179312 0.038778 +v 0.000000 0.179312 0.039775 +v 0.000000 0.182961 -0.034243 +v -0.007620 0.182961 -0.033384 +v -0.014857 0.182961 -0.030852 +v -0.021350 0.182961 -0.026772 +v -0.026772 0.182961 -0.021350 +v -0.030852 0.182961 -0.014857 +v -0.033384 0.182961 -0.007620 +v -0.034243 0.182961 0.000000 +v -0.033384 0.182961 0.007620 +v -0.030852 0.182961 0.014857 +v -0.026772 0.182961 0.021350 +v -0.021350 0.182961 0.026772 +v -0.014857 0.182961 0.030852 +v -0.007620 0.182961 0.033384 +v 0.000000 0.182961 0.034243 +v 0.000000 0.185957 -0.028125 +v -0.006258 0.185957 -0.027420 +v -0.012203 0.185957 -0.025340 +v -0.017536 0.185957 -0.021989 +v -0.021989 0.185957 -0.017536 +v -0.025340 0.185957 -0.012203 +v -0.025340 0.185957 0.012203 +v -0.021989 0.185957 0.017536 +v -0.017536 0.185957 0.021989 +v -0.012203 0.185957 0.025340 +v -0.006258 0.185957 0.027420 +v 0.000000 0.185957 0.028125 +v 0.000000 0.188283 -0.021526 +v -0.004790 0.188283 -0.020986 +v -0.009340 0.188283 -0.019394 +v -0.013421 0.188283 -0.016830 +v -0.016830 0.188283 -0.013421 +v -0.019394 0.188283 -0.009340 +v -0.019394 0.188283 0.009340 +v -0.016830 0.188283 0.013421 +v -0.013421 0.188283 0.016830 +v -0.009340 0.188283 0.019394 +v -0.004790 0.188283 0.020986 +v 0.000000 0.188283 0.021526 +v 0.000000 0.189936 -0.014559 +v -0.003240 0.189936 -0.014194 +v -0.006317 0.189936 -0.013117 +v -0.009077 0.189936 -0.011382 +v -0.011382 0.189936 -0.009077 +v -0.013117 0.189936 -0.006317 +v -0.014194 0.189936 -0.003240 +v -0.014559 0.189936 0.000000 +v -0.014194 0.189936 0.003240 +v -0.013117 0.189936 0.006317 +v -0.011382 0.189936 0.009077 +v -0.009077 0.189936 0.011382 +v -0.006317 0.189936 0.013117 +v -0.003240 0.189936 0.014194 +v 0.000000 0.189936 0.014559 +v 0.000000 0.190922 -0.007342 +v -0.001634 0.190922 -0.007158 +v -0.003186 0.190922 -0.006615 +v -0.004578 0.190922 -0.005740 +v -0.005740 0.190922 -0.004578 +v -0.006615 0.190922 -0.003186 +v -0.007158 0.190922 -0.001634 +v -0.007342 0.190922 0.000000 +v -0.007158 0.190922 0.001634 +v -0.006615 0.190922 0.003186 +v -0.005740 0.190922 0.004578 +v -0.004578 0.190922 0.005740 +v -0.003186 0.190922 0.006615 +v -0.001634 0.190922 0.007158 +v 0.000000 0.190922 0.007342 +v 0.000000 0.191250 0.000000 +v 0.012410 0.153477 -0.054370 +v 0.024197 0.153477 -0.050246 +v 0.034771 0.153477 -0.043602 +v 0.043602 0.153477 -0.034771 +v 0.050246 0.153477 -0.024197 +v 0.054370 0.153477 -0.012410 +v 0.055769 0.153477 0.000000 +v 0.054370 0.153477 0.012410 +v 0.050246 0.153477 0.024197 +v 0.043602 0.153477 0.034771 +v 0.034771 0.153477 0.043602 +v 0.024197 0.153477 0.050246 +v 0.012410 0.153477 0.054370 +v 0.012090 0.159345 -0.052971 +v 0.023574 0.159345 -0.048953 +v 0.033876 0.159345 -0.042479 +v 0.042479 0.159345 -0.033876 +v 0.048953 0.159345 -0.023574 +v 0.052971 0.159345 -0.012090 +v 0.054333 0.159345 0.000000 +v 0.052971 0.159345 0.012090 +v 0.048953 0.159345 0.023574 +v 0.042480 0.159345 0.033876 +v 0.033876 0.159345 0.042480 +v 0.023574 0.159345 0.048953 +v 0.012090 0.159345 0.052971 +v 0.011564 0.164974 -0.050665 +v 0.022548 0.164974 -0.046822 +v 0.032402 0.164974 -0.040630 +v 0.040630 0.164974 -0.032402 +v 0.046822 0.164974 -0.022548 +v 0.050665 0.164974 -0.011564 +v 0.051968 0.164974 0.000000 +v 0.050665 0.164974 0.011564 +v 0.046822 0.164974 0.022548 +v 0.040630 0.164974 0.032402 +v 0.032402 0.164974 0.040630 +v 0.022548 0.164974 0.046822 +v 0.011564 0.164974 0.050665 +v 0.010840 0.170245 -0.047493 +v 0.021136 0.170245 -0.043890 +v 0.030373 0.170245 -0.038086 +v 0.038086 0.170245 -0.030373 +v 0.043890 0.170245 -0.021136 +v 0.047493 0.170245 -0.010840 +v 0.048714 0.170245 0.000000 +v 0.047493 0.170245 0.010840 +v 0.043890 0.170245 0.021136 +v 0.038086 0.170245 0.030373 +v 0.030373 0.170245 0.038086 +v 0.021136 0.170245 0.043890 +v 0.010840 0.170245 0.047493 +v 0.009930 0.175052 -0.043507 +v 0.019363 0.175052 -0.040207 +v 0.027824 0.175052 -0.034890 +v 0.034890 0.175052 -0.027824 +v 0.040207 0.175052 -0.019363 +v 0.043507 0.175052 -0.009930 +v 0.044626 0.175052 0.000000 +v 0.043507 0.175052 0.009930 +v 0.040207 0.175052 0.019363 +v 0.034890 0.175052 0.027824 +v 0.027824 0.175052 0.034890 +v 0.019363 0.175052 0.040207 +v 0.009930 0.175052 0.043507 +v 0.008851 0.179312 -0.038777 +v 0.017258 0.179312 -0.035836 +v 0.024799 0.179312 -0.031097 +v 0.031097 0.179312 -0.024799 +v 0.035836 0.179312 -0.017258 +v 0.038777 0.179312 -0.008851 +v 0.039775 0.179312 0.000000 +v 0.038777 0.179312 0.008851 +v 0.035836 0.179312 0.017258 +v 0.031097 0.179312 0.024799 +v 0.024799 0.179312 0.031097 +v 0.017258 0.179312 0.035836 +v 0.008851 0.179312 0.038777 +v 0.007620 0.182961 -0.033384 +v 0.014857 0.182961 -0.030852 +v 0.021350 0.182961 -0.026772 +v 0.026772 0.182961 -0.021350 +v 0.030852 0.182961 -0.014857 +v 0.033384 0.182961 -0.007620 +v 0.034243 0.182961 0.000000 +v 0.033384 0.182961 0.007620 +v 0.030852 0.182961 0.014857 +v 0.026772 0.182961 0.021350 +v 0.021350 0.182961 0.026772 +v 0.014857 0.182961 0.030852 +v 0.007620 0.182961 0.033384 +v 0.006258 0.185957 -0.027420 +v 0.012203 0.185957 -0.025340 +v 0.017536 0.185957 -0.021989 +v 0.021989 0.185957 -0.017536 +v 0.025340 0.185957 -0.012203 +v 0.025340 0.185957 0.012203 +v 0.021989 0.185957 0.017536 +v 0.017536 0.185957 0.021989 +v 0.012203 0.185957 0.025340 +v 0.006258 0.185957 0.027420 +v 0.004790 0.188283 -0.020986 +v 0.009340 0.188283 -0.019394 +v 0.013421 0.188283 -0.016830 +v 0.016830 0.188283 -0.013421 +v 0.019394 0.188283 -0.009340 +v 0.019394 0.188283 0.009340 +v 0.016830 0.188283 0.013421 +v 0.013421 0.188283 0.016830 +v 0.009340 0.188283 0.019394 +v 0.004790 0.188283 0.020986 +v 0.003240 0.189936 -0.014194 +v 0.006317 0.189936 -0.013117 +v 0.009077 0.189936 -0.011382 +v 0.011382 0.189936 -0.009077 +v 0.013117 0.189936 -0.006317 +v 0.014194 0.189936 -0.003240 +v 0.014559 0.189936 0.000000 +v 0.014194 0.189936 0.003240 +v 0.013117 0.189936 0.006317 +v 0.011382 0.189936 0.009077 +v 0.009077 0.189936 0.011382 +v 0.006317 0.189936 0.013117 +v 0.003240 0.189936 0.014194 +v 0.001634 0.190922 -0.007158 +v 0.003186 0.190922 -0.006615 +v 0.004578 0.190922 -0.005740 +v 0.005740 0.190922 -0.004578 +v 0.006615 0.190922 -0.003186 +v 0.007158 0.190922 -0.001634 +v 0.007342 0.190922 0.000000 +v 0.007158 0.190922 0.001634 +v 0.006615 0.190922 0.003186 +v 0.005740 0.190922 0.004578 +v 0.004578 0.190922 0.005740 +v 0.003186 0.190922 0.006615 +v 0.001634 0.190922 0.007158 +v 0.000000 0.147500 -0.054742 +v 0.000000 0.148994 -0.056130 +v -0.012490 0.148994 -0.054723 +v -0.012181 0.147500 -0.053369 +v -0.024354 0.148994 -0.050571 +v -0.023751 0.147500 -0.049320 +v -0.034996 0.148994 -0.043884 +v -0.034131 0.147500 -0.042799 +v -0.043884 0.148994 -0.034996 +v -0.042799 0.147500 -0.034131 +v -0.050571 0.148994 -0.024354 +v -0.049320 0.147500 -0.023751 +v -0.054723 0.148994 -0.012490 +v -0.053369 0.147500 -0.012181 +v -0.056130 0.148994 0.000000 +v -0.054742 0.147500 0.000000 +v -0.054723 0.148994 0.012490 +v -0.053369 0.147500 0.012181 +v -0.050571 0.148994 0.024354 +v -0.049320 0.147500 0.023751 +v -0.043884 0.148994 0.034996 +v -0.042799 0.147500 0.034131 +v -0.034996 0.148994 0.043884 +v -0.034131 0.147500 0.042799 +v -0.024354 0.148994 0.050571 +v -0.023751 0.147500 0.049320 +v -0.012490 0.148994 0.054723 +v -0.012181 0.147500 0.053369 +v 0.000000 0.148994 0.056130 +v 0.000000 0.147500 0.054742 +v 0.012490 0.148994 -0.054723 +v 0.012181 0.147500 -0.053369 +v 0.024354 0.148994 -0.050571 +v 0.023751 0.147500 -0.049320 +v 0.034996 0.148994 -0.043884 +v 0.034131 0.147500 -0.042799 +v 0.043884 0.148994 -0.034996 +v 0.042799 0.147500 -0.034131 +v 0.050571 0.148994 -0.024354 +v 0.049320 0.147500 -0.023751 +v 0.054723 0.148994 -0.012490 +v 0.053369 0.147500 -0.012181 +v 0.056130 0.148994 0.000000 +v 0.054742 0.147500 0.000000 +v 0.054723 0.148994 0.012490 +v 0.053369 0.147500 0.012181 +v 0.050571 0.148994 0.024354 +v 0.049320 0.147500 0.023751 +v 0.043884 0.148994 0.034996 +v 0.042799 0.147500 0.034131 +v 0.034996 0.148994 0.043884 +v 0.034131 0.147500 0.042799 +v 0.024354 0.148994 0.050571 +v 0.023751 0.147500 0.049320 +v 0.012490 0.148994 0.054723 +v 0.012181 0.147500 0.053369 +v 0.000000 0.147500 0.000000 +v -0.054402 0.044123 0.000000 +v -0.053038 0.044123 0.012106 +v -0.049014 0.044123 0.023604 +v -0.042533 0.044123 0.033919 +v -0.033919 0.044123 0.042533 +v -0.023604 0.044123 0.049014 +v -0.012106 0.044123 0.053038 +v 0.000000 0.044123 0.054402 +v 0.012106 0.044123 0.053038 +v 0.023604 0.044123 0.049014 +v 0.033919 0.044123 0.042533 +v 0.042533 0.044123 0.033919 +v 0.049014 0.044123 0.023604 +v 0.053038 0.044123 0.012106 +v 0.054402 0.044123 0.000000 +v 0.053038 0.044123 -0.012106 +v 0.049014 0.044123 -0.023604 +v 0.042533 0.044123 -0.033919 +v 0.033919 0.044123 -0.042533 +v 0.023604 0.044123 -0.049014 +v 0.012105 0.044123 -0.053038 +v 0.000000 0.044123 -0.054402 +v -0.012105 0.044123 -0.053038 +v -0.023604 0.044123 -0.049014 +v -0.033919 0.044123 -0.042533 +v -0.042533 0.044123 -0.033919 +v -0.049014 0.044123 -0.023604 +v -0.053038 0.044123 -0.012106 +v -0.049345 0.040490 0.000000 +v -0.048108 0.040490 0.010980 +v -0.044458 0.040490 0.021410 +v -0.038579 0.040490 0.030766 +v -0.030766 0.040490 0.038579 +v -0.021410 0.040490 0.044458 +v -0.010980 0.040490 0.048108 +v 0.000000 0.040490 0.049345 +v 0.010980 0.040490 0.048108 +v 0.021410 0.040490 0.044458 +v 0.030766 0.040490 0.038579 +v 0.038579 0.040490 0.030766 +v 0.044458 0.040490 0.021410 +v 0.048108 0.040490 0.010980 +v 0.049345 0.040490 0.000000 +v 0.048108 0.040490 -0.010980 +v 0.044458 0.040490 -0.021410 +v 0.038579 0.040490 -0.030766 +v 0.030766 0.040490 -0.038579 +v 0.021410 0.040490 -0.044458 +v 0.010980 0.040490 -0.048108 +v 0.000000 0.040490 -0.049345 +v -0.010980 0.040490 -0.048108 +v -0.021410 0.040490 -0.044458 +v -0.030766 0.040490 -0.038579 +v -0.038579 0.040490 -0.030766 +v -0.044458 0.040490 -0.021410 +v -0.048108 0.040490 -0.010980 +v -0.032286 0.040000 0.000000 +v -0.030472 0.040000 0.006768 +v -0.025518 0.040000 0.011722 +v -0.018750 0.040000 0.013536 +v -0.011982 0.040000 0.011722 +v -0.007028 0.040000 0.006768 +v -0.005214 0.040000 0.000000 +v -0.007028 0.040000 -0.006768 +v -0.011982 0.040000 -0.011723 +v -0.018750 0.040000 -0.013536 +v -0.025518 0.040000 -0.011723 +v -0.030472 0.040000 -0.006768 +v 0.000000 0.040000 0.000000 +v 0.032286 0.040000 0.000000 +v 0.030472 0.040000 0.006768 +v 0.025518 0.040000 0.011722 +v 0.018750 0.040000 0.013536 +v 0.011982 0.040000 0.011722 +v 0.007028 0.040000 0.006768 +v 0.005214 0.040000 0.000000 +v 0.007028 0.040000 -0.006768 +v 0.011982 0.040000 -0.011723 +v 0.018750 0.040000 -0.013536 +v 0.025518 0.040000 -0.011723 +v 0.030472 0.040000 -0.006768 +v 0.031250 0.012500 0.000000 +v 0.029575 0.012500 0.006250 +v 0.025000 0.012500 0.010825 +v 0.018750 0.012500 0.012500 +v 0.012500 0.012500 0.010825 +v 0.007925 0.012500 0.006250 +v 0.006250 0.012500 0.000000 +v 0.007925 0.012500 -0.006250 +v 0.012500 0.012500 -0.010826 +v 0.018750 0.012500 -0.012500 +v 0.025000 0.012500 -0.010826 +v 0.029575 0.012500 -0.006250 +v 0.062925 0.125000 -0.006250 +v 0.067500 0.125000 -0.010825 +v 0.073750 0.125000 -0.012500 +v 0.080000 0.125000 -0.010825 +v 0.084575 0.125000 -0.006250 +v 0.086250 0.125000 0.000000 +v 0.084575 0.125000 0.006250 +v 0.080000 0.125000 0.010825 +v 0.073750 0.125000 0.012500 +v 0.067500 0.125000 0.010825 +v 0.062925 0.125000 0.006250 +v 0.061250 0.125000 0.000000 +v 0.062925 0.075000 -0.006250 +v 0.067500 0.075000 -0.010825 +v 0.073750 0.075000 -0.012500 +v 0.080000 0.075000 -0.010825 +v 0.084575 0.075000 -0.006250 +v 0.086250 0.075000 0.000000 +v 0.084575 0.075000 0.006250 +v 0.080000 0.075000 0.010825 +v 0.073750 0.075000 0.012500 +v 0.067500 0.075000 0.010825 +v 0.062925 0.075000 0.006250 +v 0.061250 0.075000 0.000000 +v 0.025097 0.187407 0.002500 +v 0.023447 0.188085 0.001768 +v 0.022760 0.188360 0.000000 +v 0.023447 0.188085 -0.001768 +v 0.025097 0.187407 -0.002500 +v 0.026765 0.186760 -0.001768 +v 0.027488 0.186548 0.000000 +v 0.026765 0.186760 0.001768 +v 0.025000 0.039141 0.010825 +v 0.018750 0.039141 0.012500 +v 0.012500 0.039141 0.010825 +v 0.007925 0.039141 0.006250 +v 0.006250 0.039141 0.000000 +v 0.007925 0.039141 -0.006250 +v 0.012500 0.039141 -0.010826 +v 0.018750 0.039141 -0.012500 +v 0.025000 0.039141 -0.010826 +v 0.029575 0.039141 -0.006250 +v 0.031250 0.039141 0.000000 +v 0.029575 0.039141 0.006250 +v -0.033469 0.205443 -0.001768 +v -0.035000 0.204560 -0.002500 +v -0.036531 0.203676 -0.001768 +v -0.037165 0.203310 0.000000 +v -0.036531 0.203676 0.001768 +v -0.035000 0.204560 0.002500 +v -0.033469 0.205443 0.001768 +v -0.032835 0.205810 0.000000 +v -0.031250 0.012500 0.000000 +v -0.029575 0.012500 0.006250 +v -0.025000 0.012500 0.010825 +v -0.018750 0.012500 0.012500 +v -0.012500 0.012500 0.010825 +v -0.007925 0.012500 0.006250 +v -0.006250 0.012500 0.000000 +v -0.007925 0.012500 -0.006250 +v -0.012500 0.012500 -0.010826 +v -0.018750 0.012500 -0.012500 +v -0.025000 0.012500 -0.010826 +v -0.029575 0.012500 -0.006250 +v -0.025097 0.187407 0.002500 +v -0.023447 0.188085 0.001768 +v -0.022760 0.188360 0.000000 +v -0.023447 0.188085 -0.001768 +v -0.025097 0.187407 -0.002500 +v -0.026765 0.186760 -0.001768 +v -0.027488 0.186548 0.000000 +v -0.026765 0.186760 0.001768 +v -0.025000 0.039141 0.010825 +v -0.018750 0.039141 0.012500 +v -0.012500 0.039141 0.010825 +v -0.007925 0.039141 0.006250 +v -0.006250 0.039141 0.000000 +v -0.007925 0.039141 -0.006250 +v -0.012500 0.039141 -0.010826 +v -0.018750 0.039141 -0.012500 +v -0.025000 0.039141 -0.010826 +v -0.029575 0.039141 -0.006250 +v -0.031250 0.039141 0.000000 +v -0.029575 0.039141 0.006250 +v -0.036951 0.204877 -0.001531 +v -0.035625 0.205642 -0.002165 +v -0.034299 0.206408 -0.001531 +v -0.033750 0.206725 0.000000 +v -0.034299 0.206408 0.001531 +v -0.035625 0.205642 0.002165 +v -0.036951 0.204877 0.001531 +v -0.037500 0.204560 0.000000 +v -0.036848 0.205993 -0.000884 +v -0.036083 0.206435 -0.001250 +v -0.035317 0.206877 -0.000884 +v -0.035000 0.207060 0.000000 +v -0.035317 0.206877 0.000884 +v -0.036083 0.206435 0.001250 +v -0.036848 0.205993 0.000884 +v -0.037165 0.205810 0.000000 +v -0.036250 0.206725 0.000000 +v -0.062925 0.125000 -0.006250 +v -0.067500 0.125000 -0.010825 +v -0.073750 0.125000 -0.012500 +v -0.080000 0.125000 -0.010825 +v -0.084575 0.125000 -0.006250 +v -0.086250 0.125000 0.000000 +v -0.084575 0.125000 0.006250 +v -0.080000 0.125000 0.010825 +v -0.073750 0.125000 0.012500 +v -0.067500 0.125000 0.010825 +v -0.062925 0.125000 0.006250 +v -0.061250 0.125000 0.000000 +v -0.062925 0.075000 -0.006250 +v -0.067500 0.075000 -0.010825 +v -0.073750 0.075000 -0.012500 +v -0.080000 0.075000 -0.010825 +v -0.084575 0.075000 -0.006250 +v -0.086250 0.075000 0.000000 +v -0.084575 0.075000 0.006250 +v -0.080000 0.075000 0.010825 +v -0.073750 0.075000 0.012500 +v -0.067500 0.075000 0.010825 +v -0.062925 0.075000 0.006250 +v -0.061250 0.075000 0.000000 +v -0.021552 0.000426 -0.001618 +v -0.020368 0.000426 -0.002802 +v -0.018750 0.000426 -0.003235 +v -0.017132 0.000426 -0.002802 +v -0.015948 0.000426 -0.001618 +v -0.015515 0.000426 0.000000 +v -0.015948 0.000426 0.001617 +v -0.017132 0.000426 0.002802 +v -0.018750 0.000426 0.003235 +v -0.020368 0.000426 0.002802 +v -0.021552 0.000426 0.001617 +v -0.021985 0.000426 0.000000 +v -0.024163 0.001675 -0.003125 +v -0.021875 0.001675 -0.005413 +v -0.018750 0.001675 -0.006250 +v -0.015625 0.001675 -0.005413 +v -0.013337 0.001675 -0.003125 +v -0.012500 0.001675 0.000000 +v -0.013337 0.001675 0.003125 +v -0.015625 0.001675 0.005412 +v -0.018750 0.001675 0.006250 +v -0.021875 0.001675 0.005412 +v -0.024163 0.001675 0.003125 +v -0.025000 0.001675 0.000000 +v -0.026405 0.003661 -0.004420 +v -0.023169 0.003661 -0.007655 +v -0.018750 0.003661 -0.008839 +v -0.014331 0.003661 -0.007655 +v -0.011095 0.003661 -0.004420 +v -0.009911 0.003661 0.000000 +v -0.011095 0.003661 0.004419 +v -0.014331 0.003661 0.007654 +v -0.018750 0.003661 0.008839 +v -0.023169 0.003661 0.007654 +v -0.026405 0.003661 0.004419 +v -0.027589 0.003661 0.000000 +v -0.028125 0.006250 -0.005413 +v -0.024163 0.006250 -0.009375 +v -0.018750 0.006250 -0.010825 +v -0.013337 0.006250 -0.009375 +v -0.009375 0.006250 -0.005413 +v -0.007925 0.006250 0.000000 +v -0.009375 0.006250 0.005412 +v -0.013337 0.006250 0.009375 +v -0.018750 0.006250 0.010825 +v -0.024163 0.006250 0.009375 +v -0.028125 0.006250 0.005412 +v -0.029575 0.006250 0.000000 +v -0.029207 0.009265 -0.006037 +v -0.024787 0.009265 -0.010457 +v -0.018750 0.009265 -0.012074 +v -0.012713 0.009265 -0.010457 +v -0.008294 0.009265 -0.006037 +v -0.006676 0.009265 0.000000 +v -0.008294 0.009265 0.006037 +v -0.012713 0.009265 0.010456 +v -0.018750 0.009265 0.012074 +v -0.024787 0.009265 0.010456 +v -0.029207 0.009265 0.006037 +v -0.030824 0.009265 0.000000 +v -0.018750 0.000000 0.000000 +v -0.076552 0.062926 -0.001618 +v -0.075368 0.062926 -0.002802 +v -0.073750 0.062926 -0.003235 +v -0.072132 0.062926 -0.002802 +v -0.070948 0.062926 -0.001618 +v -0.070515 0.062926 0.000000 +v -0.070948 0.062926 0.001618 +v -0.072132 0.062926 0.002802 +v -0.073750 0.062926 0.003235 +v -0.075368 0.062926 0.002802 +v -0.076552 0.062926 0.001618 +v -0.076985 0.062926 0.000000 +v -0.079163 0.064174 -0.003125 +v -0.076875 0.064174 -0.005413 +v -0.073750 0.064174 -0.006250 +v -0.070625 0.064174 -0.005413 +v -0.068337 0.064174 -0.003125 +v -0.067500 0.064174 0.000000 +v -0.068337 0.064174 0.003125 +v -0.070625 0.064174 0.005413 +v -0.073750 0.064174 0.006250 +v -0.076875 0.064174 0.005413 +v -0.079163 0.064174 0.003125 +v -0.080000 0.064174 0.000000 +v -0.081405 0.066161 -0.004419 +v -0.078170 0.066161 -0.007655 +v -0.073750 0.066161 -0.008839 +v -0.069331 0.066161 -0.007655 +v -0.066095 0.066161 -0.004419 +v -0.064911 0.066161 0.000000 +v -0.066095 0.066161 0.004419 +v -0.069331 0.066161 0.007655 +v -0.073750 0.066161 0.008839 +v -0.078170 0.066161 0.007655 +v -0.081405 0.066161 0.004419 +v -0.082589 0.066161 0.000000 +v -0.083125 0.068750 -0.005413 +v -0.079163 0.068750 -0.009375 +v -0.073750 0.068750 -0.010825 +v -0.068337 0.068750 -0.009375 +v -0.064375 0.068750 -0.005413 +v -0.062925 0.068750 0.000000 +v -0.064375 0.068750 0.005413 +v -0.068337 0.068750 0.009375 +v -0.073750 0.068750 0.010825 +v -0.079163 0.068750 0.009375 +v -0.083125 0.068750 0.005413 +v -0.084575 0.068750 0.000000 +v -0.084207 0.071765 -0.006037 +v -0.079787 0.071765 -0.010456 +v -0.073750 0.071765 -0.012074 +v -0.067713 0.071765 -0.010456 +v -0.063294 0.071765 -0.006037 +v -0.061676 0.071765 0.000000 +v -0.063294 0.071765 0.006037 +v -0.067713 0.071765 0.010456 +v -0.073750 0.071765 0.012074 +v -0.079787 0.071765 0.010456 +v -0.084207 0.071765 0.006037 +v -0.085824 0.071765 0.000000 +v -0.073750 0.062500 0.000000 +v -0.076552 0.137074 -0.001618 +v -0.075368 0.137074 -0.002802 +v -0.073750 0.137074 -0.003235 +v -0.072132 0.137074 -0.002802 +v -0.070948 0.137074 -0.001618 +v -0.070515 0.137074 0.000000 +v -0.070948 0.137074 0.001618 +v -0.072132 0.137074 0.002802 +v -0.073750 0.137074 0.003235 +v -0.075368 0.137074 0.002802 +v -0.076552 0.137074 0.001618 +v -0.076985 0.137074 0.000000 +v -0.079163 0.135825 -0.003125 +v -0.076875 0.135825 -0.005413 +v -0.073750 0.135825 -0.006250 +v -0.070625 0.135825 -0.005413 +v -0.068337 0.135825 -0.003125 +v -0.067500 0.135825 0.000000 +v -0.068337 0.135825 0.003125 +v -0.070625 0.135825 0.005413 +v -0.073750 0.135825 0.006250 +v -0.076875 0.135825 0.005413 +v -0.079163 0.135825 0.003125 +v -0.080000 0.135825 0.000000 +v -0.081405 0.133839 -0.004419 +v -0.078170 0.133839 -0.007655 +v -0.073750 0.133839 -0.008839 +v -0.069331 0.133839 -0.007655 +v -0.066095 0.133839 -0.004419 +v -0.064911 0.133839 0.000000 +v -0.066095 0.133839 0.004419 +v -0.069331 0.133839 0.007655 +v -0.073750 0.133839 0.008839 +v -0.078170 0.133839 0.007655 +v -0.081405 0.133839 0.004419 +v -0.082589 0.133839 0.000000 +v -0.083125 0.131250 -0.005413 +v -0.079163 0.131250 -0.009375 +v -0.073750 0.131250 -0.010825 +v -0.068337 0.131250 -0.009375 +v -0.064375 0.131250 -0.005413 +v -0.062925 0.131250 0.000000 +v -0.064375 0.131250 0.005413 +v -0.068337 0.131250 0.009375 +v -0.073750 0.131250 0.010825 +v -0.079163 0.131250 0.009375 +v -0.083125 0.131250 0.005413 +v -0.084575 0.131250 0.000000 +v -0.084207 0.128235 -0.006037 +v -0.079787 0.128235 -0.010456 +v -0.073750 0.128235 -0.012074 +v -0.067713 0.128235 -0.010456 +v -0.063294 0.128235 -0.006037 +v -0.061676 0.128235 0.000000 +v -0.063294 0.128235 0.006037 +v -0.067713 0.128235 0.010456 +v -0.073750 0.128235 0.012074 +v -0.079787 0.128235 0.010456 +v -0.084207 0.128235 0.006037 +v -0.085824 0.128235 0.000000 +v -0.073750 0.137500 0.000000 +v 0.021552 0.000426 -0.001618 +v 0.020368 0.000426 -0.002802 +v 0.018750 0.000426 -0.003235 +v 0.017132 0.000426 -0.002802 +v 0.015948 0.000426 -0.001618 +v 0.015515 0.000426 0.000000 +v 0.015948 0.000426 0.001617 +v 0.017132 0.000426 0.002802 +v 0.018750 0.000426 0.003235 +v 0.020368 0.000426 0.002802 +v 0.021552 0.000426 0.001617 +v 0.021985 0.000426 0.000000 +v 0.024163 0.001675 -0.003125 +v 0.021875 0.001675 -0.005413 +v 0.018750 0.001675 -0.006250 +v 0.015625 0.001675 -0.005413 +v 0.013337 0.001675 -0.003125 +v 0.012500 0.001675 0.000000 +v 0.013337 0.001675 0.003125 +v 0.015625 0.001675 0.005412 +v 0.018750 0.001675 0.006250 +v 0.021875 0.001675 0.005412 +v 0.024163 0.001675 0.003125 +v 0.025000 0.001675 0.000000 +v 0.026405 0.003661 -0.004420 +v 0.023169 0.003661 -0.007655 +v 0.018750 0.003661 -0.008839 +v 0.014331 0.003661 -0.007655 +v 0.011095 0.003661 -0.004420 +v 0.009911 0.003661 0.000000 +v 0.011095 0.003661 0.004419 +v 0.014331 0.003661 0.007654 +v 0.018750 0.003661 0.008839 +v 0.023169 0.003661 0.007654 +v 0.026405 0.003661 0.004419 +v 0.027589 0.003661 0.000000 +v 0.028125 0.006250 -0.005413 +v 0.024163 0.006250 -0.009375 +v 0.018750 0.006250 -0.010825 +v 0.013337 0.006250 -0.009375 +v 0.009375 0.006250 -0.005413 +v 0.007925 0.006250 0.000000 +v 0.009375 0.006250 0.005412 +v 0.013337 0.006250 0.009375 +v 0.018750 0.006250 0.010825 +v 0.024163 0.006250 0.009375 +v 0.028125 0.006250 0.005412 +v 0.029575 0.006250 0.000000 +v 0.029207 0.009265 -0.006037 +v 0.024787 0.009265 -0.010457 +v 0.018750 0.009265 -0.012074 +v 0.012713 0.009265 -0.010457 +v 0.008294 0.009265 -0.006037 +v 0.006676 0.009265 0.000000 +v 0.008294 0.009265 0.006037 +v 0.012713 0.009265 0.010456 +v 0.018750 0.009265 0.012074 +v 0.024787 0.009265 0.010456 +v 0.029207 0.009265 0.006037 +v 0.030824 0.009265 0.000000 +v 0.018750 0.000000 0.000000 +v 0.076552 0.062926 -0.001618 +v 0.075368 0.062926 -0.002802 +v 0.073750 0.062926 -0.003235 +v 0.072132 0.062926 -0.002802 +v 0.070948 0.062926 -0.001618 +v 0.070515 0.062926 0.000000 +v 0.070948 0.062926 0.001618 +v 0.072132 0.062926 0.002802 +v 0.073750 0.062926 0.003235 +v 0.075368 0.062926 0.002802 +v 0.076552 0.062926 0.001618 +v 0.076985 0.062926 0.000000 +v 0.079163 0.064174 -0.003125 +v 0.076875 0.064174 -0.005413 +v 0.073750 0.064174 -0.006250 +v 0.070625 0.064174 -0.005413 +v 0.068337 0.064174 -0.003125 +v 0.067500 0.064174 0.000000 +v 0.068337 0.064174 0.003125 +v 0.070625 0.064174 0.005413 +v 0.073750 0.064174 0.006250 +v 0.076875 0.064174 0.005413 +v 0.079163 0.064174 0.003125 +v 0.080000 0.064174 0.000000 +v 0.081405 0.066161 -0.004419 +v 0.078170 0.066161 -0.007655 +v 0.073750 0.066161 -0.008839 +v 0.069331 0.066161 -0.007655 +v 0.066095 0.066161 -0.004419 +v 0.064911 0.066161 0.000000 +v 0.066095 0.066161 0.004419 +v 0.069331 0.066161 0.007655 +v 0.073750 0.066161 0.008839 +v 0.078170 0.066161 0.007655 +v 0.081405 0.066161 0.004419 +v 0.082589 0.066161 0.000000 +v 0.083125 0.068750 -0.005413 +v 0.079163 0.068750 -0.009375 +v 0.073750 0.068750 -0.010825 +v 0.068337 0.068750 -0.009375 +v 0.064375 0.068750 -0.005413 +v 0.062925 0.068750 0.000000 +v 0.064375 0.068750 0.005413 +v 0.068337 0.068750 0.009375 +v 0.073750 0.068750 0.010825 +v 0.079163 0.068750 0.009375 +v 0.083125 0.068750 0.005413 +v 0.084575 0.068750 0.000000 +v 0.084207 0.071765 -0.006037 +v 0.079787 0.071765 -0.010456 +v 0.073750 0.071765 -0.012074 +v 0.067713 0.071765 -0.010456 +v 0.063294 0.071765 -0.006037 +v 0.061676 0.071765 0.000000 +v 0.063294 0.071765 0.006037 +v 0.067713 0.071765 0.010456 +v 0.073750 0.071765 0.012074 +v 0.079787 0.071765 0.010456 +v 0.084207 0.071765 0.006037 +v 0.085824 0.071765 0.000000 +v 0.073750 0.062500 0.000000 +v 0.076552 0.137074 -0.001618 +v 0.075368 0.137074 -0.002802 +v 0.073750 0.137074 -0.003235 +v 0.072132 0.137074 -0.002802 +v 0.070948 0.137074 -0.001618 +v 0.070515 0.137074 0.000000 +v 0.070948 0.137074 0.001618 +v 0.072132 0.137074 0.002802 +v 0.073750 0.137074 0.003235 +v 0.075368 0.137074 0.002802 +v 0.076552 0.137074 0.001618 +v 0.076985 0.137074 0.000000 +v 0.079163 0.135825 -0.003125 +v 0.076875 0.135825 -0.005413 +v 0.073750 0.135825 -0.006250 +v 0.070625 0.135825 -0.005413 +v 0.068337 0.135825 -0.003125 +v 0.067500 0.135825 0.000000 +v 0.068337 0.135825 0.003125 +v 0.070625 0.135825 0.005413 +v 0.073750 0.135825 0.006250 +v 0.076875 0.135825 0.005413 +v 0.079163 0.135825 0.003125 +v 0.080000 0.135825 0.000000 +v 0.081405 0.133839 -0.004419 +v 0.078170 0.133839 -0.007655 +v 0.073750 0.133839 -0.008839 +v 0.069331 0.133839 -0.007655 +v 0.066095 0.133839 -0.004419 +v 0.064911 0.133839 0.000000 +v 0.066095 0.133839 0.004419 +v 0.069331 0.133839 0.007655 +v 0.073750 0.133839 0.008839 +v 0.078170 0.133839 0.007655 +v 0.081405 0.133839 0.004419 +v 0.082589 0.133839 0.000000 +v 0.083125 0.131250 -0.005413 +v 0.079163 0.131250 -0.009375 +v 0.073750 0.131250 -0.010825 +v 0.068337 0.131250 -0.009375 +v 0.064375 0.131250 -0.005413 +v 0.062925 0.131250 0.000000 +v 0.064375 0.131250 0.005413 +v 0.068337 0.131250 0.009375 +v 0.073750 0.131250 0.010825 +v 0.079163 0.131250 0.009375 +v 0.083125 0.131250 0.005413 +v 0.084575 0.131250 0.000000 +v 0.084207 0.128235 -0.006037 +v 0.079787 0.128235 -0.010456 +v 0.073750 0.128235 -0.012074 +v 0.067713 0.128235 -0.010456 +v 0.063294 0.128235 -0.006037 +v 0.061676 0.128235 0.000000 +v 0.063294 0.128235 0.006037 +v 0.067713 0.128235 0.010456 +v 0.073750 0.128235 0.012074 +v 0.079787 0.128235 0.010456 +v 0.084207 0.128235 0.006037 +v 0.085824 0.128235 0.000000 +v 0.073750 0.137500 0.000000 +vt 0.055846 0.659882 +vt 0.055846 0.669764 +vt 0.042671 0.669764 +vt 0.042671 0.659882 +vt 0.055848 0.679645 +vt 0.042671 0.679646 +vt 0.055848 0.689527 +vt 0.042671 0.689527 +vt 0.055847 0.610473 +vt 0.055847 0.620355 +vt 0.042671 0.620355 +vt 0.042671 0.610473 +vt 0.055846 0.630237 +vt 0.042671 0.630237 +vt 0.055846 0.640118 +vt 0.042671 0.640119 +vt 0.055846 0.650000 +vt 0.042671 0.650000 +vt 0.029495 0.669764 +vt 0.029495 0.659882 +vt 0.029496 0.679646 +vt 0.029496 0.689527 +vt 0.029495 0.620355 +vt 0.029495 0.610473 +vt 0.029495 0.630237 +vt 0.029495 0.640118 +vt 0.029495 0.650000 +vt 0.016319 0.664823 +vt 0.016319 0.674705 +vt 0.016320 0.684587 +vt 0.016320 0.615414 +vt 0.016319 0.625296 +vt 0.016319 0.635178 +vt 0.016320 0.645059 +vt 0.016319 0.654941 +vt 0.851173 0.793896 +vt 0.856730 0.787051 +vt 0.855527 0.795680 +vt 0.846863 0.795712 +vt 0.842730 0.790247 +vt 0.851181 0.806034 +vt 0.856732 0.812949 +vt 0.842730 0.809753 +vt 0.846869 0.804239 +vt 0.855530 0.804296 +vt 0.857385 0.800000 +vt 0.858189 0.800000 +vt 0.845151 0.800000 +vt 0.843829 0.800000 +vt 0.748827 0.793896 +vt 0.744473 0.795680 +vt 0.743270 0.787051 +vt 0.753137 0.795712 +vt 0.757270 0.790247 +vt 0.748819 0.806034 +vt 0.753131 0.804239 +vt 0.757270 0.809753 +vt 0.743270 0.812949 +vt 0.744470 0.804296 +vt 0.742615 0.800000 +vt 0.741810 0.800000 +vt 0.754849 0.800000 +vt 0.756171 0.800000 +vt 0.418946 0.313941 +vt 0.450000 0.310442 +vt 0.450000 0.318024 +vt 0.420633 0.321332 +vt 0.389448 0.324263 +vt 0.392738 0.331094 +vt 0.362987 0.340889 +vt 0.367715 0.346817 +vt 0.340889 0.362987 +vt 0.346817 0.367715 +vt 0.324263 0.389448 +vt 0.331093 0.392737 +vt 0.313941 0.418945 +vt 0.321333 0.420632 +vt 0.310442 0.450000 +vt 0.318024 0.450000 +vt 0.313941 0.481055 +vt 0.321333 0.479367 +vt 0.324263 0.510552 +vt 0.331094 0.507263 +vt 0.340889 0.537013 +vt 0.346817 0.532286 +vt 0.362987 0.559111 +vt 0.367715 0.553183 +vt 0.389448 0.575737 +vt 0.392737 0.568906 +vt 0.418946 0.586059 +vt 0.420633 0.578667 +vt 0.450000 0.589558 +vt 0.450000 0.581976 +vt 0.372173 0.547592 +vt 0.395841 0.562464 +vt 0.422223 0.571696 +vt 0.450000 0.574825 +vt 0.450000 0.325175 +vt 0.422223 0.328306 +vt 0.395841 0.337537 +vt 0.372173 0.352408 +vt 0.352408 0.372172 +vt 0.337536 0.395841 +vt 0.328304 0.422223 +vt 0.325175 0.450000 +vt 0.328304 0.477775 +vt 0.337536 0.504159 +vt 0.352408 0.527827 +vt 0.376703 0.541912 +vt 0.358088 0.523298 +vt 0.363535 0.518954 +vt 0.381045 0.536466 +vt 0.398992 0.555918 +vt 0.402015 0.549642 +vt 0.423841 0.564613 +vt 0.425390 0.557822 +vt 0.450000 0.567560 +vt 0.450000 0.560594 +vt 0.423840 0.335387 +vt 0.450000 0.332441 +vt 0.450000 0.339404 +vt 0.425391 0.342179 +vt 0.398993 0.344083 +vt 0.402015 0.350358 +vt 0.376703 0.358087 +vt 0.381046 0.363535 +vt 0.358088 0.376703 +vt 0.363533 0.381045 +vt 0.344082 0.398992 +vt 0.350358 0.402015 +vt 0.335387 0.423841 +vt 0.342178 0.425390 +vt 0.332440 0.450000 +vt 0.339406 0.450000 +vt 0.335387 0.476159 +vt 0.342178 0.474609 +vt 0.344082 0.501008 +vt 0.350358 0.497985 +vt 0.481055 0.313941 +vt 0.479367 0.321332 +vt 0.510552 0.324263 +vt 0.507262 0.331094 +vt 0.537013 0.340889 +vt 0.532286 0.346817 +vt 0.559111 0.362987 +vt 0.553183 0.367714 +vt 0.575738 0.389448 +vt 0.568907 0.392737 +vt 0.586059 0.418945 +vt 0.578667 0.420632 +vt 0.589558 0.450000 +vt 0.581977 0.450000 +vt 0.586059 0.481055 +vt 0.578667 0.479367 +vt 0.575738 0.510552 +vt 0.568907 0.507263 +vt 0.559111 0.537013 +vt 0.553183 0.532286 +vt 0.537013 0.559111 +vt 0.532286 0.553183 +vt 0.510552 0.575737 +vt 0.507262 0.568906 +vt 0.481055 0.586059 +vt 0.479368 0.578667 +vt 0.504159 0.562464 +vt 0.527827 0.547592 +vt 0.477777 0.571696 +vt 0.477777 0.328306 +vt 0.504159 0.337537 +vt 0.527827 0.352408 +vt 0.547592 0.372172 +vt 0.562464 0.395841 +vt 0.571696 0.422223 +vt 0.574824 0.450000 +vt 0.571696 0.477775 +vt 0.562464 0.504159 +vt 0.547592 0.527827 +vt 0.518954 0.536466 +vt 0.536467 0.518954 +vt 0.541912 0.523298 +vt 0.523298 0.541912 +vt 0.497985 0.549642 +vt 0.501007 0.555918 +vt 0.474609 0.557822 +vt 0.476160 0.564613 +vt 0.474609 0.342179 +vt 0.476160 0.335387 +vt 0.497986 0.350358 +vt 0.501007 0.344083 +vt 0.518954 0.363535 +vt 0.523298 0.358087 +vt 0.536467 0.381045 +vt 0.541912 0.376703 +vt 0.549641 0.402015 +vt 0.555919 0.398992 +vt 0.557823 0.425390 +vt 0.564612 0.423841 +vt 0.560594 0.450000 +vt 0.567561 0.450000 +vt 0.557823 0.474609 +vt 0.564612 0.476159 +vt 0.549641 0.497985 +vt 0.555918 0.501008 +vt 0.150000 0.450000 +vt 0.150000 0.584317 +vt 0.120111 0.580950 +vt 0.091721 0.571016 +vt 0.066254 0.555014 +vt 0.044987 0.533746 +vt 0.028984 0.508278 +vt 0.019050 0.479889 +vt 0.015683 0.450000 +vt 0.019050 0.420111 +vt 0.028984 0.391721 +vt 0.044987 0.366255 +vt 0.066254 0.344987 +vt 0.091721 0.328984 +vt 0.120111 0.319050 +vt 0.150000 0.315683 +vt 0.118950 0.313961 +vt 0.150000 0.310462 +vt 0.038795 0.032928 +vt 0.071738 0.032928 +vt 0.071738 0.267072 +vt 0.038795 0.267072 +vt 0.104681 0.032928 +vt 0.104681 0.267072 +vt 0.137625 0.032928 +vt 0.137625 0.267072 +vt 0.170567 0.032928 +vt 0.170567 0.267072 +vt 0.203511 0.032928 +vt 0.203511 0.267072 +vt 0.236454 0.032928 +vt 0.236454 0.267072 +vt 0.269397 0.032928 +vt 0.269397 0.267072 +vt 0.302340 0.032928 +vt 0.302340 0.267072 +vt 0.335284 0.032928 +vt 0.335284 0.267072 +vt 0.368226 0.032928 +vt 0.368226 0.267072 +vt 0.401170 0.032928 +vt 0.401170 0.267072 +vt 0.434114 0.032928 +vt 0.434114 0.267072 +vt 0.467056 0.032928 +vt 0.467056 0.267072 +vt 0.500000 0.032928 +vt 0.500000 0.267072 +vt 0.928263 0.032928 +vt 0.961205 0.032928 +vt 0.961205 0.267072 +vt 0.928263 0.267072 +vt 0.895319 0.032928 +vt 0.895319 0.267072 +vt 0.862375 0.032928 +vt 0.862375 0.267072 +vt 0.829432 0.032928 +vt 0.829432 0.267072 +vt 0.796489 0.032928 +vt 0.796489 0.267072 +vt 0.763546 0.032928 +vt 0.763546 0.267072 +vt 0.730603 0.032928 +vt 0.730603 0.267072 +vt 0.697661 0.032928 +vt 0.697661 0.267072 +vt 0.664716 0.032928 +vt 0.664716 0.267072 +vt 0.631774 0.032928 +vt 0.631774 0.267072 +vt 0.598830 0.032928 +vt 0.598830 0.267072 +vt 0.565886 0.032928 +vt 0.565886 0.267072 +vt 0.532944 0.032928 +vt 0.532944 0.267072 +vt 0.179889 0.319050 +vt 0.208279 0.328984 +vt 0.233746 0.344987 +vt 0.255013 0.366255 +vt 0.271016 0.391721 +vt 0.280950 0.420111 +vt 0.284317 0.450000 +vt 0.280950 0.479889 +vt 0.271016 0.508278 +vt 0.255013 0.533746 +vt 0.233746 0.555014 +vt 0.208279 0.571016 +vt 0.179889 0.580950 +vt 0.089456 0.324280 +vt 0.062999 0.340905 +vt 0.040905 0.363000 +vt 0.024280 0.389456 +vt 0.013961 0.418950 +vt 0.010462 0.450000 +vt 0.013961 0.481050 +vt 0.024280 0.510543 +vt 0.040905 0.537001 +vt 0.062999 0.559096 +vt 0.089456 0.575720 +vt 0.118950 0.586039 +vt 0.150000 0.589538 +vt 0.181050 0.586039 +vt 0.181050 0.313961 +vt 0.210544 0.324280 +vt 0.237000 0.340905 +vt 0.259095 0.363000 +vt 0.275720 0.389456 +vt 0.286039 0.418950 +vt 0.289538 0.450000 +vt 0.286039 0.481050 +vt 0.275720 0.510543 +vt 0.259095 0.537001 +vt 0.237000 0.559096 +vt 0.210544 0.575720 +vt 0.800000 0.963545 +vt 0.763608 0.959445 +vt 0.767378 0.942927 +vt 0.800000 0.946603 +vt 0.729040 0.947350 +vt 0.736391 0.932084 +vt 0.698031 0.927865 +vt 0.708595 0.914618 +vt 0.672135 0.901969 +vt 0.685382 0.891405 +vt 0.652651 0.870960 +vt 0.667916 0.863609 +vt 0.640555 0.836393 +vt 0.657073 0.832622 +vt 0.636455 0.800000 +vt 0.653397 0.800000 +vt 0.640555 0.763607 +vt 0.657073 0.767378 +vt 0.652651 0.729040 +vt 0.667916 0.736391 +vt 0.672135 0.698032 +vt 0.685382 0.708595 +vt 0.698031 0.672135 +vt 0.708595 0.685382 +vt 0.729040 0.652650 +vt 0.736391 0.667916 +vt 0.763607 0.640555 +vt 0.767378 0.657073 +vt 0.800000 0.636455 +vt 0.800000 0.653397 +vt 0.770911 0.927450 +vt 0.800000 0.930727 +vt 0.743280 0.917782 +vt 0.718493 0.902207 +vt 0.697793 0.881507 +vt 0.682218 0.856721 +vt 0.672550 0.829089 +vt 0.669273 0.800000 +vt 0.672550 0.770911 +vt 0.682218 0.743280 +vt 0.697793 0.718493 +vt 0.718493 0.697793 +vt 0.743280 0.682218 +vt 0.770911 0.672550 +vt 0.800000 0.669273 +vt 0.774274 0.912713 +vt 0.800000 0.915612 +vt 0.749837 0.904163 +vt 0.727917 0.890389 +vt 0.709611 0.872084 +vt 0.695837 0.850163 +vt 0.687287 0.825726 +vt 0.684388 0.800000 +vt 0.687287 0.774274 +vt 0.695837 0.749838 +vt 0.709611 0.727918 +vt 0.727917 0.709611 +vt 0.749837 0.695837 +vt 0.774274 0.687287 +vt 0.800000 0.684388 +vt 0.777526 0.898465 +vt 0.800000 0.900997 +vt 0.756179 0.890995 +vt 0.737029 0.878962 +vt 0.721038 0.862971 +vt 0.709005 0.843822 +vt 0.701536 0.822474 +vt 0.699003 0.800000 +vt 0.701536 0.777526 +vt 0.709005 0.756179 +vt 0.721038 0.737029 +vt 0.737029 0.721038 +vt 0.756179 0.709005 +vt 0.777526 0.701536 +vt 0.800000 0.699003 +vt 0.780715 0.884493 +vt 0.800000 0.886665 +vt 0.762397 0.878083 +vt 0.745964 0.867758 +vt 0.732242 0.854036 +vt 0.721917 0.837603 +vt 0.715507 0.819285 +vt 0.713335 0.800000 +vt 0.715507 0.780715 +vt 0.721917 0.762398 +vt 0.732242 0.745964 +vt 0.745964 0.732242 +vt 0.762397 0.721917 +vt 0.780715 0.715507 +vt 0.800000 0.713335 +vt 0.783880 0.870625 +vt 0.800000 0.872441 +vt 0.768569 0.865268 +vt 0.754834 0.856637 +vt 0.743363 0.845167 +vt 0.734732 0.831431 +vt 0.729375 0.816120 +vt 0.727560 0.800000 +vt 0.729375 0.783880 +vt 0.734732 0.768569 +vt 0.743363 0.754835 +vt 0.754834 0.743363 +vt 0.768569 0.734732 +vt 0.783880 0.729375 +vt 0.800000 0.727558 +vt 0.787051 0.856731 +vt 0.800000 0.858189 +vt 0.774753 0.852427 +vt 0.763719 0.845495 +vt 0.754505 0.836281 +vt 0.747573 0.825248 +vt 0.747573 0.774753 +vt 0.754505 0.763719 +vt 0.763719 0.754505 +vt 0.774753 0.747573 +vt 0.787051 0.743268 +vt 0.800000 0.741810 +vt 0.790247 0.842730 +vt 0.800000 0.843829 +vt 0.780983 0.839489 +vt 0.772673 0.834267 +vt 0.765733 0.827327 +vt 0.760511 0.819017 +vt 0.760511 0.780983 +vt 0.765733 0.772673 +vt 0.772673 0.765733 +vt 0.780983 0.760511 +vt 0.790247 0.757270 +vt 0.800000 0.756171 +vt 0.793474 0.828592 +vt 0.800000 0.829327 +vt 0.787275 0.826423 +vt 0.781715 0.822929 +vt 0.777071 0.818285 +vt 0.773577 0.812725 +vt 0.771408 0.806526 +vt 0.770673 0.800000 +vt 0.771408 0.793474 +vt 0.773577 0.787275 +vt 0.777071 0.781715 +vt 0.781715 0.777071 +vt 0.787275 0.773577 +vt 0.793474 0.771408 +vt 0.800000 0.770673 +vt 0.796729 0.814331 +vt 0.800000 0.814700 +vt 0.793621 0.813245 +vt 0.790834 0.811493 +vt 0.788507 0.809166 +vt 0.786755 0.806379 +vt 0.785669 0.803271 +vt 0.785300 0.800000 +vt 0.785668 0.796729 +vt 0.786755 0.793622 +vt 0.788507 0.790835 +vt 0.790834 0.788508 +vt 0.793621 0.786755 +vt 0.796729 0.785669 +vt 0.800000 0.785300 +vt 0.800000 0.800000 +vt 0.832622 0.942927 +vt 0.836392 0.959445 +vt 0.863609 0.932084 +vt 0.870960 0.947350 +vt 0.891405 0.914618 +vt 0.901968 0.927865 +vt 0.914618 0.891405 +vt 0.927865 0.901969 +vt 0.932084 0.863608 +vt 0.947350 0.870960 +vt 0.942927 0.832622 +vt 0.959445 0.836393 +vt 0.946603 0.800000 +vt 0.963545 0.800000 +vt 0.942927 0.767378 +vt 0.959445 0.763608 +vt 0.932084 0.736391 +vt 0.947350 0.729040 +vt 0.914618 0.708595 +vt 0.927865 0.698032 +vt 0.891405 0.685382 +vt 0.901968 0.672135 +vt 0.863609 0.667916 +vt 0.870960 0.652650 +vt 0.832622 0.657073 +vt 0.836392 0.640555 +vt 0.829089 0.927450 +vt 0.856721 0.917782 +vt 0.881507 0.902207 +vt 0.902206 0.881507 +vt 0.917782 0.856721 +vt 0.927450 0.829090 +vt 0.930727 0.800000 +vt 0.927450 0.770910 +vt 0.917782 0.743280 +vt 0.902206 0.718493 +vt 0.881507 0.697793 +vt 0.856721 0.682218 +vt 0.829089 0.672550 +vt 0.825726 0.912713 +vt 0.850162 0.904163 +vt 0.872083 0.890389 +vt 0.890389 0.872083 +vt 0.904163 0.850163 +vt 0.912713 0.825726 +vt 0.915612 0.800000 +vt 0.912713 0.774274 +vt 0.904163 0.749838 +vt 0.890389 0.727918 +vt 0.872083 0.709611 +vt 0.850162 0.695837 +vt 0.825726 0.687286 +vt 0.822474 0.898465 +vt 0.843821 0.890995 +vt 0.862971 0.878962 +vt 0.878962 0.862971 +vt 0.890995 0.843822 +vt 0.898464 0.822474 +vt 0.900997 0.800000 +vt 0.898464 0.777526 +vt 0.890995 0.756179 +vt 0.878962 0.737029 +vt 0.862971 0.721038 +vt 0.843821 0.709005 +vt 0.822474 0.701536 +vt 0.819285 0.884493 +vt 0.837603 0.878083 +vt 0.854036 0.867758 +vt 0.867758 0.854036 +vt 0.878083 0.837603 +vt 0.884493 0.819285 +vt 0.886665 0.800000 +vt 0.884493 0.780715 +vt 0.878083 0.762398 +vt 0.867758 0.745964 +vt 0.854036 0.732242 +vt 0.837603 0.721917 +vt 0.819285 0.715507 +vt 0.816120 0.870625 +vt 0.831431 0.865268 +vt 0.845166 0.856637 +vt 0.856637 0.845166 +vt 0.865268 0.831431 +vt 0.870625 0.816120 +vt 0.872442 0.800000 +vt 0.870625 0.783880 +vt 0.865268 0.768569 +vt 0.856637 0.754835 +vt 0.845167 0.743363 +vt 0.831431 0.734732 +vt 0.816120 0.729375 +vt 0.812949 0.856731 +vt 0.825247 0.852427 +vt 0.836281 0.845495 +vt 0.845495 0.836281 +vt 0.852427 0.825247 +vt 0.852427 0.774753 +vt 0.845495 0.763719 +vt 0.836281 0.754505 +vt 0.825248 0.747573 +vt 0.812949 0.743270 +vt 0.809753 0.842730 +vt 0.819017 0.839488 +vt 0.827327 0.834267 +vt 0.834267 0.827327 +vt 0.839489 0.819017 +vt 0.839489 0.780983 +vt 0.834267 0.772673 +vt 0.827327 0.765733 +vt 0.819017 0.760511 +vt 0.809753 0.757270 +vt 0.806526 0.828592 +vt 0.812725 0.826423 +vt 0.818285 0.822929 +vt 0.822929 0.818285 +vt 0.826423 0.812725 +vt 0.828592 0.806526 +vt 0.829327 0.800000 +vt 0.828592 0.793474 +vt 0.826423 0.787275 +vt 0.822929 0.781715 +vt 0.818285 0.777071 +vt 0.812725 0.773577 +vt 0.806526 0.771408 +vt 0.803271 0.814331 +vt 0.806379 0.813245 +vt 0.809166 0.811493 +vt 0.811493 0.809166 +vt 0.813245 0.806379 +vt 0.814332 0.803271 +vt 0.814700 0.800000 +vt 0.814331 0.796729 +vt 0.813245 0.793622 +vt 0.811493 0.790835 +vt 0.809166 0.788508 +vt 0.806377 0.786755 +vt 0.803271 0.785668 +vt 0.760574 0.972734 +vt 0.800000 0.977177 +vt 0.800000 0.983714 +vt 0.759120 0.979109 +vt 0.723126 0.959631 +vt 0.689532 0.938522 +vt 0.661478 0.910468 +vt 0.640369 0.876875 +vt 0.627266 0.839426 +vt 0.622823 0.800000 +vt 0.627266 0.760574 +vt 0.640369 0.723127 +vt 0.661478 0.689532 +vt 0.689532 0.661478 +vt 0.723125 0.640370 +vt 0.760574 0.627266 +vt 0.800000 0.622823 +vt 0.719984 0.318489 +vt 0.750000 0.315109 +vt 0.750000 0.450000 +vt 0.691473 0.328466 +vt 0.665897 0.344538 +vt 0.644538 0.365896 +vt 0.628466 0.391473 +vt 0.618491 0.419984 +vt 0.615109 0.450000 +vt 0.618491 0.480016 +vt 0.628466 0.508527 +vt 0.644538 0.534103 +vt 0.665897 0.555462 +vt 0.691473 0.571534 +vt 0.719984 0.581509 +vt 0.750000 0.584891 +vt 0.839426 0.972735 +vt 0.876875 0.959631 +vt 0.910468 0.938522 +vt 0.938522 0.910468 +vt 0.959631 0.876875 +vt 0.972734 0.839426 +vt 0.977178 0.800000 +vt 0.972736 0.760574 +vt 0.959631 0.723127 +vt 0.938522 0.689532 +vt 0.910468 0.661478 +vt 0.876875 0.640370 +vt 0.839426 0.627265 +vt 0.720289 0.965521 +vt 0.685457 0.943634 +vt 0.656366 0.914545 +vt 0.634480 0.879710 +vt 0.620892 0.840881 +vt 0.616286 0.800000 +vt 0.620892 0.759120 +vt 0.634480 0.720289 +vt 0.656366 0.685456 +vt 0.685457 0.656366 +vt 0.720289 0.634479 +vt 0.759120 0.620892 +vt 0.800000 0.616286 +vt 0.840880 0.620892 +vt 0.840880 0.979108 +vt 0.879711 0.965521 +vt 0.914544 0.943634 +vt 0.943634 0.914545 +vt 0.965520 0.879710 +vt 0.979108 0.840881 +vt 0.983714 0.800000 +vt 0.979108 0.759120 +vt 0.965522 0.720289 +vt 0.943634 0.685456 +vt 0.914544 0.656366 +vt 0.879711 0.634479 +vt 0.780016 0.318491 +vt 0.808527 0.328466 +vt 0.834103 0.344538 +vt 0.855462 0.365896 +vt 0.871533 0.391473 +vt 0.881509 0.419984 +vt 0.884891 0.450000 +vt 0.881509 0.480016 +vt 0.871533 0.508527 +vt 0.855462 0.534103 +vt 0.834103 0.555462 +vt 0.808527 0.571534 +vt 0.780016 0.581509 +vt 0.346499 0.450000 +vt 0.349094 0.473031 +vt 0.356748 0.494908 +vt 0.369079 0.514533 +vt 0.385469 0.530921 +vt 0.405092 0.543252 +vt 0.426970 0.550906 +vt 0.450000 0.553501 +vt 0.473031 0.550906 +vt 0.494908 0.543252 +vt 0.514531 0.530921 +vt 0.530921 0.514533 +vt 0.543252 0.494908 +vt 0.550907 0.473031 +vt 0.553501 0.450000 +vt 0.550907 0.426969 +vt 0.543252 0.405092 +vt 0.530920 0.385468 +vt 0.514533 0.369079 +vt 0.494908 0.356749 +vt 0.473031 0.349092 +vt 0.450000 0.346500 +vt 0.426969 0.349092 +vt 0.405092 0.356749 +vt 0.385467 0.369079 +vt 0.369080 0.385468 +vt 0.356748 0.405092 +vt 0.349094 0.426969 +vt 0.377697 0.450000 +vt 0.381758 0.465156 +vt 0.392853 0.476252 +vt 0.408010 0.480313 +vt 0.423166 0.476252 +vt 0.434261 0.465156 +vt 0.438322 0.450000 +vt 0.450000 0.450000 +vt 0.434261 0.434844 +vt 0.423166 0.423748 +vt 0.408010 0.419687 +vt 0.392853 0.423747 +vt 0.381758 0.434844 +vt 0.522303 0.450000 +vt 0.518242 0.465156 +vt 0.507147 0.476252 +vt 0.491990 0.480313 +vt 0.476834 0.476252 +vt 0.465739 0.465156 +vt 0.461678 0.450000 +vt 0.465739 0.434844 +vt 0.476834 0.423748 +vt 0.491990 0.419687 +vt 0.507147 0.423747 +vt 0.518242 0.434844 +vt 0.176121 0.669763 +vt 0.176121 0.679645 +vt 0.176121 0.659881 +vt 0.176120 0.650000 +vt 0.176120 0.640118 +vt 0.176120 0.630236 +vt 0.176120 0.620354 +vt 0.176120 0.610473 +vt 0.176121 0.689527 +vt 0.473387 0.972703 +vt 0.473387 0.962787 +vt 0.486693 0.962787 +vt 0.486693 0.972703 +vt 0.460080 0.972703 +vt 0.460080 0.962787 +vt 0.446774 0.972702 +vt 0.446774 0.962787 +vt 0.433468 0.972703 +vt 0.433468 0.962787 +vt 0.420161 0.972702 +vt 0.420161 0.962787 +vt 0.500000 0.962787 +vt 0.500000 0.972703 +vt 0.566532 0.972703 +vt 0.566532 0.962787 +vt 0.579839 0.962787 +vt 0.579839 0.972703 +vt 0.553226 0.972703 +vt 0.553226 0.962787 +vt 0.539919 0.972703 +vt 0.539919 0.962787 +vt 0.526613 0.972703 +vt 0.526613 0.962787 +vt 0.513306 0.972703 +vt 0.513306 0.962787 +vt 0.153226 0.898485 +vt 0.153226 0.801515 +vt 0.166532 0.801515 +vt 0.166533 0.898485 +vt 0.139920 0.898485 +vt 0.139919 0.801515 +vt 0.126613 0.898485 +vt 0.126613 0.801515 +vt 0.113307 0.898485 +vt 0.113306 0.801515 +vt 0.100000 0.898485 +vt 0.100000 0.801515 +vt 0.086694 0.898485 +vt 0.086693 0.801515 +vt 0.073387 0.898485 +vt 0.073387 0.801515 +vt 0.060081 0.898485 +vt 0.060080 0.801515 +vt 0.046775 0.898485 +vt 0.046774 0.801516 +vt 0.033468 0.898485 +vt 0.033468 0.801516 +vt 0.020162 0.898485 +vt 0.020161 0.801516 +vt 0.179838 0.801515 +vt 0.179839 0.898485 +vt 0.183680 0.620354 +vt 0.183681 0.630236 +vt 0.183680 0.610473 +vt 0.183681 0.679645 +vt 0.183681 0.689527 +vt 0.183681 0.669763 +vt 0.183681 0.659881 +vt 0.183681 0.650000 +vt 0.183681 0.640118 +vt 0.460081 0.907136 +vt 0.473387 0.907136 +vt 0.446774 0.907136 +vt 0.433468 0.907136 +vt 0.420161 0.907136 +vt 0.566532 0.907136 +vt 0.579839 0.907136 +vt 0.553226 0.907136 +vt 0.539919 0.907136 +vt 0.526613 0.907136 +vt 0.513306 0.907136 +vt 0.500000 0.907136 +vt 0.486694 0.907136 +vt 0.344153 0.669764 +vt 0.344154 0.679646 +vt 0.223879 0.679645 +vt 0.223879 0.669763 +vt 0.344153 0.659882 +vt 0.223879 0.659881 +vt 0.344153 0.650000 +vt 0.223879 0.650000 +vt 0.344153 0.640119 +vt 0.223879 0.640118 +vt 0.223879 0.630236 +vt 0.344153 0.630237 +vt 0.344153 0.620355 +vt 0.223879 0.620354 +vt 0.344152 0.610473 +vt 0.223879 0.610473 +vt 0.344154 0.689528 +vt 0.223879 0.689527 +vt 0.526613 0.772703 +vt 0.513306 0.772703 +vt 0.513306 0.762787 +vt 0.526613 0.762787 +vt 0.539919 0.772702 +vt 0.539919 0.762787 +vt 0.553226 0.772703 +vt 0.553226 0.762787 +vt 0.566532 0.772702 +vt 0.566532 0.762787 +vt 0.579839 0.772702 +vt 0.579839 0.762787 +vt 0.500000 0.772703 +vt 0.500000 0.762787 +vt 0.433468 0.772703 +vt 0.420161 0.772703 +vt 0.420161 0.762787 +vt 0.433468 0.762787 +vt 0.446774 0.772703 +vt 0.446774 0.762787 +vt 0.460081 0.772703 +vt 0.460081 0.762787 +vt 0.473387 0.772703 +vt 0.473387 0.762787 +vt 0.486694 0.772703 +vt 0.486694 0.762787 +vt 0.216319 0.630236 +vt 0.216319 0.620354 +vt 0.216319 0.610472 +vt 0.216319 0.689527 +vt 0.216319 0.679645 +vt 0.216319 0.669763 +vt 0.216319 0.659881 +vt 0.216319 0.650000 +vt 0.216319 0.640118 +vt 0.539919 0.707136 +vt 0.526613 0.707136 +vt 0.553226 0.707136 +vt 0.566532 0.707136 +vt 0.579839 0.707136 +vt 0.433468 0.707136 +vt 0.420161 0.707136 +vt 0.446774 0.707136 +vt 0.460081 0.707136 +vt 0.473387 0.707136 +vt 0.486694 0.707136 +vt 0.500000 0.707136 +vt 0.513306 0.707136 +vt 0.357329 0.659882 +vt 0.357329 0.669764 +vt 0.357329 0.679646 +vt 0.357330 0.689528 +vt 0.357329 0.610473 +vt 0.357329 0.620355 +vt 0.357329 0.630237 +vt 0.357329 0.640119 +vt 0.357329 0.650000 +vt 0.370504 0.659882 +vt 0.370505 0.669763 +vt 0.370505 0.679645 +vt 0.370505 0.689527 +vt 0.370504 0.610473 +vt 0.370504 0.620355 +vt 0.370504 0.630237 +vt 0.370504 0.640119 +vt 0.370504 0.650000 +vt 0.383681 0.664822 +vt 0.383681 0.674704 +vt 0.383681 0.684586 +vt 0.383680 0.615414 +vt 0.383680 0.625296 +vt 0.383680 0.635178 +vt 0.383680 0.645059 +vt 0.383680 0.654941 +vt 0.246774 0.898485 +vt 0.233467 0.898485 +vt 0.233468 0.801515 +vt 0.246774 0.801515 +vt 0.260080 0.898485 +vt 0.260081 0.801515 +vt 0.273387 0.898485 +vt 0.273387 0.801515 +vt 0.286693 0.898485 +vt 0.286694 0.801515 +vt 0.300000 0.898485 +vt 0.300000 0.801515 +vt 0.313306 0.898485 +vt 0.313306 0.801515 +vt 0.326612 0.898485 +vt 0.326613 0.801515 +vt 0.339919 0.898485 +vt 0.339919 0.801516 +vt 0.353225 0.898485 +vt 0.353226 0.801516 +vt 0.366532 0.898485 +vt 0.366532 0.801516 +vt 0.379838 0.898485 +vt 0.379839 0.801516 +vt 0.220161 0.898485 +vt 0.220161 0.801515 +vt 0.486693 0.640604 +vt 0.486694 0.653910 +vt 0.473387 0.653910 +vt 0.473387 0.640604 +vt 0.460081 0.653910 +vt 0.460081 0.640604 +vt 0.446774 0.653910 +vt 0.446774 0.640604 +vt 0.433468 0.653910 +vt 0.433468 0.640604 +vt 0.420161 0.653910 +vt 0.420161 0.640604 +vt 0.579839 0.640604 +vt 0.579839 0.653910 +vt 0.566532 0.653910 +vt 0.566532 0.640604 +vt 0.553226 0.653910 +vt 0.553226 0.640604 +vt 0.539919 0.653910 +vt 0.539919 0.640604 +vt 0.526613 0.653910 +vt 0.526613 0.640604 +vt 0.513306 0.653910 +vt 0.513306 0.640604 +vt 0.500000 0.653910 +vt 0.500000 0.640604 +vt 0.486694 0.667217 +vt 0.473387 0.667217 +vt 0.460081 0.667217 +vt 0.446774 0.667217 +vt 0.433468 0.667217 +vt 0.420161 0.667217 +vt 0.579839 0.667217 +vt 0.566532 0.667217 +vt 0.553226 0.667217 +vt 0.539919 0.667217 +vt 0.526613 0.667217 +vt 0.513306 0.667217 +vt 0.500000 0.667217 +vt 0.486694 0.680523 +vt 0.473387 0.680523 +vt 0.460081 0.680523 +vt 0.446774 0.680523 +vt 0.433468 0.680523 +vt 0.420161 0.680523 +vt 0.579839 0.680523 +vt 0.566532 0.680523 +vt 0.553226 0.680523 +vt 0.539919 0.680523 +vt 0.526613 0.680523 +vt 0.513306 0.680523 +vt 0.500000 0.680523 +vt 0.486694 0.693830 +vt 0.473387 0.693830 +vt 0.460081 0.693830 +vt 0.446774 0.693830 +vt 0.433468 0.693830 +vt 0.420161 0.693830 +vt 0.579839 0.693830 +vt 0.566532 0.693830 +vt 0.553226 0.693830 +vt 0.539919 0.693830 +vt 0.526613 0.693830 +vt 0.513306 0.693830 +vt 0.500000 0.693830 +vt 0.480040 0.627297 +vt 0.466734 0.627297 +vt 0.453427 0.627298 +vt 0.440121 0.627297 +vt 0.426814 0.627298 +vt 0.573185 0.627297 +vt 0.559879 0.627297 +vt 0.546573 0.627297 +vt 0.533266 0.627297 +vt 0.519960 0.627297 +vt 0.506653 0.627297 +vt 0.493347 0.627297 +vt 0.286694 0.734983 +vt 0.286694 0.748289 +vt 0.273387 0.748289 +vt 0.273387 0.734983 +vt 0.260081 0.748289 +vt 0.260081 0.734983 +vt 0.246775 0.748289 +vt 0.246775 0.734983 +vt 0.233468 0.748289 +vt 0.233468 0.734983 +vt 0.220162 0.748289 +vt 0.220162 0.734983 +vt 0.379839 0.734984 +vt 0.379839 0.748290 +vt 0.366533 0.748290 +vt 0.366533 0.734984 +vt 0.353226 0.748290 +vt 0.353226 0.734983 +vt 0.339920 0.748290 +vt 0.339920 0.734983 +vt 0.326613 0.748290 +vt 0.326614 0.734983 +vt 0.313307 0.748289 +vt 0.313307 0.734983 +vt 0.300000 0.748289 +vt 0.300001 0.734983 +vt 0.286694 0.761596 +vt 0.273387 0.761596 +vt 0.260081 0.761596 +vt 0.246774 0.761596 +vt 0.233468 0.761596 +vt 0.220162 0.761595 +vt 0.379839 0.761597 +vt 0.366533 0.761596 +vt 0.353226 0.761596 +vt 0.339920 0.761596 +vt 0.326613 0.761596 +vt 0.313307 0.761596 +vt 0.300000 0.761596 +vt 0.286694 0.774902 +vt 0.273387 0.774902 +vt 0.260081 0.774902 +vt 0.246775 0.774902 +vt 0.233468 0.774902 +vt 0.220162 0.774902 +vt 0.379839 0.774903 +vt 0.366533 0.774903 +vt 0.353226 0.774903 +vt 0.339920 0.774903 +vt 0.326613 0.774903 +vt 0.313307 0.774902 +vt 0.300000 0.774902 +vt 0.286694 0.788209 +vt 0.273387 0.788209 +vt 0.260081 0.788209 +vt 0.246774 0.788208 +vt 0.233468 0.788208 +vt 0.220162 0.788208 +vt 0.379839 0.788209 +vt 0.366533 0.788209 +vt 0.353226 0.788209 +vt 0.339920 0.788209 +vt 0.326613 0.788209 +vt 0.313307 0.788209 +vt 0.300000 0.788209 +vt 0.280041 0.721676 +vt 0.266734 0.721676 +vt 0.253428 0.721676 +vt 0.240121 0.721676 +vt 0.226815 0.721676 +vt 0.373186 0.721677 +vt 0.359880 0.721677 +vt 0.346573 0.721677 +vt 0.333267 0.721677 +vt 0.319960 0.721677 +vt 0.306654 0.721677 +vt 0.293347 0.721676 +vt 0.286693 0.965017 +vt 0.273386 0.965017 +vt 0.273386 0.951711 +vt 0.286693 0.951711 +vt 0.260080 0.965017 +vt 0.260080 0.951711 +vt 0.246773 0.965017 +vt 0.246774 0.951711 +vt 0.233467 0.965017 +vt 0.233467 0.951711 +vt 0.220161 0.965017 +vt 0.220161 0.951711 +vt 0.379838 0.965017 +vt 0.366532 0.965017 +vt 0.366532 0.951711 +vt 0.379838 0.951711 +vt 0.353225 0.965017 +vt 0.353225 0.951711 +vt 0.339919 0.965017 +vt 0.339919 0.951711 +vt 0.326613 0.965017 +vt 0.326613 0.951711 +vt 0.313306 0.965017 +vt 0.313306 0.951711 +vt 0.300000 0.965017 +vt 0.300000 0.951711 +vt 0.273387 0.938405 +vt 0.286693 0.938405 +vt 0.260080 0.938404 +vt 0.246774 0.938404 +vt 0.233467 0.938404 +vt 0.220161 0.938404 +vt 0.366532 0.938405 +vt 0.379838 0.938405 +vt 0.353225 0.938405 +vt 0.339919 0.938405 +vt 0.326612 0.938405 +vt 0.313306 0.938405 +vt 0.300000 0.938405 +vt 0.273387 0.925098 +vt 0.286693 0.925098 +vt 0.260080 0.925098 +vt 0.246774 0.925098 +vt 0.233467 0.925098 +vt 0.220161 0.925098 +vt 0.366532 0.925098 +vt 0.379838 0.925098 +vt 0.353225 0.925098 +vt 0.339919 0.925098 +vt 0.326613 0.925098 +vt 0.313306 0.925098 +vt 0.300000 0.925098 +vt 0.273387 0.911792 +vt 0.286693 0.911792 +vt 0.260080 0.911792 +vt 0.246774 0.911791 +vt 0.233467 0.911791 +vt 0.220161 0.911791 +vt 0.366532 0.911792 +vt 0.379838 0.911792 +vt 0.353225 0.911792 +vt 0.339919 0.911792 +vt 0.326612 0.911792 +vt 0.313306 0.911792 +vt 0.300000 0.911792 +vt 0.280039 0.978324 +vt 0.266733 0.978324 +vt 0.253427 0.978324 +vt 0.240120 0.978324 +vt 0.226814 0.978324 +vt 0.373185 0.978324 +vt 0.359879 0.978324 +vt 0.346572 0.978324 +vt 0.333266 0.978324 +vt 0.319959 0.978324 +vt 0.306653 0.978324 +vt 0.293346 0.978324 +vt 0.513307 0.840604 +vt 0.526613 0.840604 +vt 0.526613 0.853910 +vt 0.513307 0.853910 +vt 0.539920 0.840604 +vt 0.539919 0.853910 +vt 0.553226 0.840604 +vt 0.553226 0.853910 +vt 0.566533 0.840604 +vt 0.566532 0.853911 +vt 0.579839 0.840604 +vt 0.579839 0.853911 +vt 0.420161 0.840604 +vt 0.433468 0.840604 +vt 0.433468 0.853910 +vt 0.420161 0.853910 +vt 0.446774 0.840604 +vt 0.446774 0.853910 +vt 0.460081 0.840604 +vt 0.460081 0.853910 +vt 0.473387 0.840604 +vt 0.473387 0.853910 +vt 0.486694 0.840604 +vt 0.486694 0.853910 +vt 0.500000 0.840604 +vt 0.500000 0.853910 +vt 0.526613 0.867217 +vt 0.513307 0.867217 +vt 0.539919 0.867217 +vt 0.553226 0.867217 +vt 0.566532 0.867217 +vt 0.579839 0.867217 +vt 0.433468 0.867217 +vt 0.420161 0.867217 +vt 0.446774 0.867217 +vt 0.460081 0.867217 +vt 0.473387 0.867217 +vt 0.486694 0.867217 +vt 0.500000 0.867217 +vt 0.526613 0.880523 +vt 0.513307 0.880523 +vt 0.539919 0.880523 +vt 0.553226 0.880523 +vt 0.566532 0.880523 +vt 0.579839 0.880523 +vt 0.433468 0.880523 +vt 0.420161 0.880523 +vt 0.446774 0.880523 +vt 0.460081 0.880523 +vt 0.473387 0.880523 +vt 0.486694 0.880523 +vt 0.500000 0.880523 +vt 0.526613 0.893830 +vt 0.513307 0.893830 +vt 0.539919 0.893830 +vt 0.553226 0.893830 +vt 0.566532 0.893830 +vt 0.579839 0.893830 +vt 0.433468 0.893830 +vt 0.420161 0.893830 +vt 0.446774 0.893830 +vt 0.460081 0.893830 +vt 0.473387 0.893830 +vt 0.486694 0.893830 +vt 0.500000 0.893830 +vt 0.519960 0.827297 +vt 0.533266 0.827297 +vt 0.546573 0.827297 +vt 0.559879 0.827298 +vt 0.573186 0.827298 +vt 0.426814 0.827297 +vt 0.440121 0.827297 +vt 0.453427 0.827297 +vt 0.466734 0.827297 +vt 0.480040 0.827297 +vt 0.493347 0.827297 +vt 0.506653 0.827297 +vt 0.113306 0.734983 +vt 0.126613 0.734983 +vt 0.126613 0.748289 +vt 0.113306 0.748289 +vt 0.139919 0.734983 +vt 0.139919 0.748289 +vt 0.153225 0.734983 +vt 0.153225 0.748289 +vt 0.166532 0.734983 +vt 0.166532 0.748289 +vt 0.179838 0.734983 +vt 0.179838 0.748289 +vt 0.020161 0.734983 +vt 0.033467 0.734983 +vt 0.033467 0.748290 +vt 0.020161 0.748290 +vt 0.046774 0.734983 +vt 0.046774 0.748290 +vt 0.060080 0.734983 +vt 0.060080 0.748290 +vt 0.073387 0.734983 +vt 0.073387 0.748289 +vt 0.086693 0.734983 +vt 0.086693 0.748289 +vt 0.100000 0.734983 +vt 0.100000 0.748289 +vt 0.126613 0.761596 +vt 0.113306 0.761596 +vt 0.139919 0.761596 +vt 0.153225 0.761596 +vt 0.166532 0.761596 +vt 0.179838 0.761596 +vt 0.033467 0.761596 +vt 0.020161 0.761596 +vt 0.046774 0.761596 +vt 0.060080 0.761596 +vt 0.073387 0.761596 +vt 0.086693 0.761596 +vt 0.100000 0.761596 +vt 0.126613 0.774902 +vt 0.113306 0.774902 +vt 0.139919 0.774902 +vt 0.153226 0.774902 +vt 0.166532 0.774902 +vt 0.179838 0.774902 +vt 0.033467 0.774903 +vt 0.020161 0.774903 +vt 0.046774 0.774903 +vt 0.060080 0.774903 +vt 0.073387 0.774902 +vt 0.086693 0.774902 +vt 0.100000 0.774902 +vt 0.126613 0.788209 +vt 0.113306 0.788209 +vt 0.139919 0.788209 +vt 0.153225 0.788209 +vt 0.166532 0.788209 +vt 0.179838 0.788209 +vt 0.033467 0.788209 +vt 0.020161 0.788209 +vt 0.046774 0.788209 +vt 0.060080 0.788209 +vt 0.073387 0.788209 +vt 0.086693 0.788209 +vt 0.100000 0.788209 +vt 0.119959 0.721676 +vt 0.133266 0.721676 +vt 0.146572 0.721676 +vt 0.159879 0.721676 +vt 0.173185 0.721676 +vt 0.026814 0.721677 +vt 0.040120 0.721677 +vt 0.053427 0.721677 +vt 0.066733 0.721677 +vt 0.080040 0.721677 +vt 0.093346 0.721676 +vt 0.106653 0.721676 +vt 0.113307 0.965017 +vt 0.113307 0.951711 +vt 0.126613 0.951711 +vt 0.126614 0.965017 +vt 0.139920 0.951711 +vt 0.139920 0.965017 +vt 0.153226 0.951711 +vt 0.153226 0.965017 +vt 0.166533 0.951711 +vt 0.166533 0.965017 +vt 0.179839 0.951711 +vt 0.179839 0.965017 +vt 0.020162 0.965017 +vt 0.020162 0.951711 +vt 0.033468 0.951711 +vt 0.033468 0.965017 +vt 0.046774 0.951711 +vt 0.046775 0.965017 +vt 0.060081 0.951711 +vt 0.060081 0.965017 +vt 0.073387 0.951711 +vt 0.073387 0.965017 +vt 0.086694 0.951711 +vt 0.086694 0.965017 +vt 0.100000 0.951711 +vt 0.100000 0.965017 +vt 0.113307 0.938404 +vt 0.126613 0.938404 +vt 0.139920 0.938404 +vt 0.153226 0.938404 +vt 0.166533 0.938404 +vt 0.179839 0.938404 +vt 0.020162 0.938404 +vt 0.033468 0.938404 +vt 0.046774 0.938404 +vt 0.060081 0.938404 +vt 0.073387 0.938404 +vt 0.086694 0.938404 +vt 0.100000 0.938404 +vt 0.113307 0.925098 +vt 0.126613 0.925098 +vt 0.139920 0.925098 +vt 0.153226 0.925098 +vt 0.166533 0.925098 +vt 0.179839 0.925098 +vt 0.020162 0.925098 +vt 0.033468 0.925098 +vt 0.046775 0.925098 +vt 0.060081 0.925098 +vt 0.073387 0.925098 +vt 0.086694 0.925098 +vt 0.100000 0.925098 +vt 0.113307 0.911791 +vt 0.126613 0.911792 +vt 0.139920 0.911791 +vt 0.153226 0.911791 +vt 0.166533 0.911791 +vt 0.179839 0.911791 +vt 0.020162 0.911792 +vt 0.033468 0.911791 +vt 0.046775 0.911792 +vt 0.060081 0.911791 +vt 0.073387 0.911792 +vt 0.086694 0.911791 +vt 0.100000 0.911791 +vt 0.119960 0.978324 +vt 0.133267 0.978324 +vt 0.146573 0.978324 +vt 0.159880 0.978324 +vt 0.173186 0.978324 +vt 0.026815 0.978324 +vt 0.040121 0.978324 +vt 0.053428 0.978324 +vt 0.066734 0.978324 +vt 0.080041 0.978324 +vt 0.093347 0.978324 +vt 0.106654 0.978324 +vn 0.618018 -0.340438 -0.708629 +vn 0.004816 0.013405 -0.999899 +vn 0.217515 0.376643 -0.900459 +vn 0.768905 0.058373 -0.636693 +vn 0.004816 0.013405 -0.999899 +vn -0.606408 0.366126 -0.705848 +vn -0.333953 0.694994 -0.636757 +vn 0.217515 0.376643 -0.900459 +vn -0.606408 0.366126 -0.705848 +vn -0.859035 0.511917 -0.000001 +vn -0.562362 0.826891 0.000000 +vn -0.333953 0.694994 -0.636757 +vn -0.859035 0.511917 -0.000001 +vn -0.606408 0.366126 0.705848 +vn -0.333953 0.694991 0.636759 +vn -0.562362 0.826891 0.000000 +vn -0.606408 0.366126 0.705848 +vn 0.004812 0.013407 0.999899 +vn 0.217514 0.376640 0.900461 +vn -0.333953 0.694991 0.636759 +vn 0.004812 0.013407 0.999899 +vn 0.618018 -0.340438 0.708629 +vn 0.768903 0.058373 0.636695 +vn 0.217514 0.376640 0.900461 +vn 0.618018 -0.340438 0.708629 +vn 0.873073 -0.487589 0.000000 +vn 0.997294 -0.073511 0.000000 +vn 0.768903 0.058373 0.636695 +vn 0.873073 -0.487589 0.000000 +vn 0.618018 -0.340438 -0.708629 +vn 0.768905 0.058373 -0.636693 +vn 0.997294 -0.073511 0.000000 +vn 0.768905 0.058373 -0.636693 +vn 0.217515 0.376643 -0.900459 +vn 0.386277 0.669203 -0.634790 +vn 0.775055 0.444810 -0.448815 +vn 0.217515 0.376643 -0.900459 +vn -0.333953 0.694994 -0.636757 +vn -0.002361 0.893546 -0.448966 +vn 0.386277 0.669203 -0.634790 +vn -0.333953 0.694994 -0.636757 +vn -0.562362 0.826891 0.000000 +vn -0.163222 0.986589 -0.000002 +vn -0.002361 0.893546 -0.448966 +vn -0.562362 0.826891 0.000000 +vn -0.333953 0.694991 0.636759 +vn -0.002361 0.893546 0.448965 +vn -0.163222 0.986589 -0.000002 +vn -0.333953 0.694991 0.636759 +vn 0.217514 0.376640 0.900461 +vn 0.386277 0.669201 0.634791 +vn -0.002361 0.893546 0.448965 +vn 0.217514 0.376640 0.900461 +vn 0.768903 0.058373 0.636695 +vn 0.775054 0.444809 0.448817 +vn 0.386277 0.669201 0.634791 +vn 0.768903 0.058373 0.636695 +vn 0.997294 -0.073511 0.000000 +vn 0.936026 0.351932 0.000001 +vn 0.775054 0.444809 0.448817 +vn 0.997294 -0.073511 0.000000 +vn 0.768905 0.058373 -0.636693 +vn 0.775055 0.444810 -0.448815 +vn 0.936026 0.351932 0.000001 +vn 0.775055 0.444810 -0.448815 +vn 0.386277 0.669203 -0.634790 +vn 0.500086 0.865976 -0.000003 +vn 0.386277 0.669203 -0.634790 +vn -0.002361 0.893546 -0.448966 +vn 0.500086 0.865976 -0.000003 +vn -0.002361 0.893546 -0.448966 +vn -0.163222 0.986589 -0.000002 +vn 0.500086 0.865976 -0.000003 +vn -0.163222 0.986589 -0.000002 +vn -0.002361 0.893546 0.448965 +vn 0.500086 0.865976 -0.000003 +vn -0.002361 0.893546 0.448965 +vn 0.386277 0.669201 0.634791 +vn 0.500086 0.865976 -0.000003 +vn 0.386277 0.669201 0.634791 +vn 0.775054 0.444809 0.448817 +vn 0.500086 0.865976 -0.000003 +vn 0.775054 0.444809 0.448817 +vn 0.936026 0.351932 0.000001 +vn 0.500086 0.865976 -0.000003 +vn 0.936026 0.351932 0.000001 +vn 0.775055 0.444810 -0.448815 +vn 0.500086 0.865976 -0.000003 +vn 0.330416 0.939272 0.092697 +vn 0.396301 0.912607 0.100466 +vn 0.383211 0.922561 0.045064 +vn 0.287051 0.954669 0.078798 +vn 0.269162 0.959884 0.078574 +vn 0.396301 0.912607 0.100466 +vn 0.330416 0.939272 0.092697 +vn 0.330417 0.939272 -0.092697 +vn 0.396301 0.912607 -0.100466 +vn 0.269162 0.959884 -0.078575 +vn 0.287051 0.954669 -0.078798 +vn 0.383211 0.922561 -0.045061 +vn 0.396301 0.912607 -0.100466 +vn 0.330417 0.939272 -0.092697 +vn 0.388183 0.921582 0.000002 +vn 0.436573 0.899669 0.000001 +vn 0.396301 0.912607 -0.100466 +vn 0.383211 0.922561 -0.045061 +vn 0.383211 0.922561 0.045064 +vn 0.396301 0.912607 0.100466 +vn 0.436573 0.899669 0.000001 +vn 0.388183 0.921582 0.000002 +vn 0.202745 0.979232 0.000000 +vn 0.226299 0.974058 -0.000001 +vn 0.269162 0.959884 0.078574 +vn 0.287051 0.954669 0.078798 +vn 0.287051 0.954669 -0.078798 +vn 0.269162 0.959884 -0.078575 +vn 0.226299 0.974058 -0.000001 +vn 0.202745 0.979232 0.000000 +vn -0.330416 0.939272 0.092697 +vn -0.383210 0.922561 0.045064 +vn -0.396301 0.912607 0.100466 +vn -0.287051 0.954669 0.078798 +vn -0.330416 0.939272 0.092697 +vn -0.396301 0.912607 0.100466 +vn -0.269162 0.959884 0.078574 +vn -0.330416 0.939272 -0.092702 +vn -0.287051 0.954669 -0.078800 +vn -0.269162 0.959884 -0.078576 +vn -0.396301 0.912607 -0.100467 +vn -0.383212 0.922560 -0.045065 +vn -0.330416 0.939272 -0.092702 +vn -0.396301 0.912607 -0.100467 +vn -0.388183 0.921582 0.000000 +vn -0.383212 0.922560 -0.045065 +vn -0.396301 0.912607 -0.100467 +vn -0.436572 0.899669 0.000001 +vn -0.383210 0.922561 0.045064 +vn -0.388183 0.921582 0.000000 +vn -0.436572 0.899669 0.000001 +vn -0.396301 0.912607 0.100466 +vn -0.202745 0.979232 0.000004 +vn -0.287051 0.954669 0.078798 +vn -0.269162 0.959884 0.078574 +vn -0.226299 0.974058 0.000001 +vn -0.287051 0.954669 -0.078800 +vn -0.202745 0.979232 0.000004 +vn -0.226299 0.974058 0.000001 +vn -0.269162 0.959884 -0.078576 +vn -0.222517 -0.006117 -0.974910 +vn 0.000000 -0.006117 -0.999981 +vn 0.000000 -0.296295 -0.955097 +vn -0.212529 -0.296295 -0.931150 +vn -0.433876 -0.006117 -0.900952 +vn -0.222517 -0.006117 -0.974910 +vn -0.212529 -0.296295 -0.931150 +vn -0.414401 -0.296294 -0.860513 +vn -0.623478 -0.006117 -0.781817 +vn -0.433876 -0.006117 -0.900952 +vn -0.414401 -0.296294 -0.860513 +vn -0.595493 -0.296294 -0.746725 +vn -0.781817 -0.006117 -0.623478 +vn -0.623478 -0.006117 -0.781817 +vn -0.595493 -0.296294 -0.746725 +vn -0.746725 -0.296294 -0.595493 +vn -0.900952 -0.006117 -0.433876 +vn -0.781817 -0.006117 -0.623478 +vn -0.746725 -0.296294 -0.595493 +vn -0.860512 -0.296295 -0.414401 +vn -0.974910 -0.006117 -0.222517 +vn -0.900952 -0.006117 -0.433876 +vn -0.860512 -0.296295 -0.414401 +vn -0.931150 -0.296294 -0.212530 +vn -0.999981 -0.006117 0.000000 +vn -0.974910 -0.006117 -0.222517 +vn -0.931150 -0.296294 -0.212530 +vn -0.955097 -0.296294 0.000000 +vn -0.974910 -0.006117 0.222517 +vn -0.999981 -0.006117 0.000000 +vn -0.955097 -0.296294 0.000000 +vn -0.931150 -0.296295 0.212529 +vn -0.900952 -0.006117 0.433876 +vn -0.974910 -0.006117 0.222517 +vn -0.931150 -0.296295 0.212529 +vn -0.860513 -0.296294 0.414401 +vn -0.781817 -0.006117 0.623478 +vn -0.900952 -0.006117 0.433876 +vn -0.860513 -0.296294 0.414401 +vn -0.746725 -0.296294 0.595493 +vn -0.623478 -0.006117 0.781817 +vn -0.781817 -0.006117 0.623478 +vn -0.746725 -0.296294 0.595493 +vn -0.595493 -0.296294 0.746725 +vn -0.433875 -0.006117 0.900952 +vn -0.623478 -0.006117 0.781817 +vn -0.595493 -0.296294 0.746725 +vn -0.414401 -0.296294 0.860513 +vn -0.222517 -0.006117 0.974910 +vn -0.433875 -0.006117 0.900952 +vn -0.414401 -0.296294 0.860513 +vn -0.212529 -0.296294 0.931150 +vn 0.000000 -0.006117 0.999981 +vn -0.222517 -0.006117 0.974910 +vn -0.212529 -0.296294 0.931150 +vn 0.000000 -0.296294 0.955097 +vn -0.414401 -0.296294 0.860513 +vn -0.595493 -0.296294 0.746725 +vn -0.510013 -0.575221 0.639536 +vn -0.354916 -0.575221 0.736991 +vn -0.212529 -0.296294 0.931150 +vn -0.414401 -0.296294 0.860513 +vn -0.354916 -0.575221 0.736991 +vn -0.182022 -0.575221 0.797489 +vn 0.000000 -0.296294 0.955097 +vn -0.212529 -0.296294 0.931150 +vn -0.182022 -0.575221 0.797489 +vn 0.000000 -0.575221 0.817998 +vn -0.212529 -0.296295 -0.931150 +vn 0.000000 -0.296295 -0.955097 +vn 0.000000 -0.575221 -0.817998 +vn -0.182022 -0.575221 -0.797489 +vn -0.414401 -0.296294 -0.860513 +vn -0.212529 -0.296295 -0.931150 +vn -0.182022 -0.575221 -0.797489 +vn -0.354916 -0.575221 -0.736991 +vn -0.595493 -0.296294 -0.746725 +vn -0.414401 -0.296294 -0.860513 +vn -0.354916 -0.575221 -0.736991 +vn -0.510013 -0.575221 -0.639537 +vn -0.746725 -0.296294 -0.595493 +vn -0.595493 -0.296294 -0.746725 +vn -0.510013 -0.575221 -0.639537 +vn -0.639537 -0.575221 -0.510013 +vn -0.860512 -0.296295 -0.414401 +vn -0.746725 -0.296294 -0.595493 +vn -0.639537 -0.575221 -0.510013 +vn -0.736991 -0.575221 -0.354916 +vn -0.931150 -0.296294 -0.212530 +vn -0.860512 -0.296295 -0.414401 +vn -0.736991 -0.575221 -0.354916 +vn -0.797490 -0.575220 -0.182022 +vn -0.955097 -0.296294 0.000000 +vn -0.931150 -0.296294 -0.212530 +vn -0.797490 -0.575220 -0.182022 +vn -0.817999 -0.575220 0.000000 +vn -0.931150 -0.296295 0.212529 +vn -0.955097 -0.296294 0.000000 +vn -0.817999 -0.575220 0.000000 +vn -0.797489 -0.575221 0.182022 +vn -0.860513 -0.296294 0.414401 +vn -0.931150 -0.296295 0.212529 +vn -0.797489 -0.575221 0.182022 +vn -0.736991 -0.575221 0.354916 +vn -0.746725 -0.296294 0.595493 +vn -0.860513 -0.296294 0.414401 +vn -0.736991 -0.575221 0.354916 +vn -0.639537 -0.575221 0.510013 +vn -0.595493 -0.296294 0.746725 +vn -0.746725 -0.296294 0.595493 +vn -0.639537 -0.575221 0.510013 +vn -0.510013 -0.575221 0.639536 +vn -0.369762 -0.805164 0.463668 +vn -0.463668 -0.805164 0.369762 +vn -0.253177 -0.946117 0.201902 +vn -0.201902 -0.946117 0.253177 +vn -0.257316 -0.805164 0.534322 +vn -0.369762 -0.805164 0.463668 +vn -0.201902 -0.946117 0.253177 +vn -0.140502 -0.946117 0.291757 +vn -0.131967 -0.805164 0.578184 +vn -0.257316 -0.805164 0.534322 +vn -0.140502 -0.946117 0.291757 +vn -0.072058 -0.946117 0.315706 +vn 0.000000 -0.805163 0.593053 +vn -0.131967 -0.805164 0.578184 +vn -0.072058 -0.946117 0.315706 +vn 0.000000 -0.946117 0.323825 +vn -0.131967 -0.805163 -0.578184 +vn 0.000000 -0.805163 -0.593053 +vn 0.000000 -0.946117 -0.323826 +vn -0.072058 -0.946117 -0.315707 +vn -0.257316 -0.805164 -0.534322 +vn -0.131967 -0.805163 -0.578184 +vn -0.072058 -0.946117 -0.315707 +vn -0.140503 -0.946117 -0.291757 +vn -0.369763 -0.805164 -0.463667 +vn -0.257316 -0.805164 -0.534322 +vn -0.140503 -0.946117 -0.291757 +vn -0.201902 -0.946117 -0.253178 +vn -0.463667 -0.805164 -0.369762 +vn -0.369763 -0.805164 -0.463667 +vn -0.201902 -0.946117 -0.253178 +vn -0.253177 -0.946117 -0.201902 +vn -0.534322 -0.805164 -0.257316 +vn -0.463667 -0.805164 -0.369762 +vn -0.253177 -0.946117 -0.201902 +vn -0.291757 -0.946117 -0.140503 +vn -0.578184 -0.805163 -0.131967 +vn -0.534322 -0.805164 -0.257316 +vn -0.291757 -0.946117 -0.140503 +vn -0.315706 -0.946117 -0.072058 +vn -0.593054 -0.805163 0.000000 +vn -0.578184 -0.805163 -0.131967 +vn -0.315706 -0.946117 -0.072058 +vn -0.323827 -0.946116 0.000000 +vn -0.578184 -0.805163 0.131967 +vn -0.593054 -0.805163 0.000000 +vn -0.323827 -0.946116 0.000000 +vn -0.315710 -0.946116 0.072059 +vn -0.534323 -0.805163 0.257316 +vn -0.578184 -0.805163 0.131967 +vn -0.315710 -0.946116 0.072059 +vn -0.291758 -0.946116 0.140504 +vn -0.253177 -0.946117 0.201902 +vn -0.463668 -0.805164 0.369762 +vn -0.534323 -0.805163 0.257316 +vn -0.291758 -0.946116 0.140504 +vn 0.222517 -0.006117 -0.974910 +vn 0.212529 -0.296295 -0.931150 +vn 0.000000 -0.296295 -0.955097 +vn 0.000000 -0.006117 -0.999981 +vn 0.433876 -0.006117 -0.900952 +vn 0.414401 -0.296294 -0.860513 +vn 0.212529 -0.296295 -0.931150 +vn 0.222517 -0.006117 -0.974910 +vn 0.623478 -0.006117 -0.781817 +vn 0.595493 -0.296294 -0.746725 +vn 0.414401 -0.296294 -0.860513 +vn 0.433876 -0.006117 -0.900952 +vn 0.781817 -0.006117 -0.623478 +vn 0.746725 -0.296294 -0.595493 +vn 0.595493 -0.296294 -0.746725 +vn 0.623478 -0.006117 -0.781817 +vn 0.900952 -0.006117 -0.433876 +vn 0.860512 -0.296294 -0.414401 +vn 0.746725 -0.296294 -0.595493 +vn 0.781817 -0.006117 -0.623478 +vn 0.974910 -0.006117 -0.222517 +vn 0.931150 -0.296294 -0.212530 +vn 0.860512 -0.296294 -0.414401 +vn 0.900952 -0.006117 -0.433876 +vn 0.999981 -0.006117 0.000000 +vn 0.955097 -0.296294 0.000000 +vn 0.931150 -0.296294 -0.212530 +vn 0.974910 -0.006117 -0.222517 +vn 0.974910 -0.006117 0.222517 +vn 0.931150 -0.296294 0.212529 +vn 0.955097 -0.296294 0.000000 +vn 0.999981 -0.006117 0.000000 +vn 0.900952 -0.006117 0.433876 +vn 0.860513 -0.296294 0.414400 +vn 0.931150 -0.296294 0.212529 +vn 0.974910 -0.006117 0.222517 +vn 0.781817 -0.006117 0.623478 +vn 0.746725 -0.296294 0.595493 +vn 0.860513 -0.296294 0.414400 +vn 0.900952 -0.006117 0.433876 +vn 0.623478 -0.006117 0.781817 +vn 0.595493 -0.296294 0.746725 +vn 0.746725 -0.296294 0.595493 +vn 0.781817 -0.006117 0.623478 +vn 0.433875 -0.006117 0.900952 +vn 0.414401 -0.296294 0.860513 +vn 0.595493 -0.296294 0.746725 +vn 0.623478 -0.006117 0.781817 +vn 0.222517 -0.006117 0.974910 +vn 0.212529 -0.296294 0.931150 +vn 0.414401 -0.296294 0.860513 +vn 0.433875 -0.006117 0.900952 +vn 0.000000 -0.006117 0.999981 +vn 0.000000 -0.296294 0.955097 +vn 0.212529 -0.296294 0.931150 +vn 0.222517 -0.006117 0.974910 +vn 0.414401 -0.296294 0.860513 +vn 0.354916 -0.575221 0.736991 +vn 0.510014 -0.575221 0.639537 +vn 0.595493 -0.296294 0.746725 +vn 0.212529 -0.296294 0.931150 +vn 0.182022 -0.575221 0.797489 +vn 0.354916 -0.575221 0.736991 +vn 0.414401 -0.296294 0.860513 +vn 0.000000 -0.296294 0.955097 +vn 0.000000 -0.575221 0.817998 +vn 0.182022 -0.575221 0.797489 +vn 0.212529 -0.296294 0.931150 +vn 0.212529 -0.296295 -0.931150 +vn 0.182022 -0.575221 -0.797489 +vn 0.000000 -0.575221 -0.817998 +vn 0.000000 -0.296295 -0.955097 +vn 0.414401 -0.296294 -0.860513 +vn 0.354916 -0.575221 -0.736991 +vn 0.182022 -0.575221 -0.797489 +vn 0.212529 -0.296295 -0.931150 +vn 0.595493 -0.296294 -0.746725 +vn 0.510013 -0.575221 -0.639537 +vn 0.354916 -0.575221 -0.736991 +vn 0.414401 -0.296294 -0.860513 +vn 0.746725 -0.296294 -0.595493 +vn 0.639537 -0.575221 -0.510013 +vn 0.510013 -0.575221 -0.639537 +vn 0.595493 -0.296294 -0.746725 +vn 0.860512 -0.296294 -0.414401 +vn 0.736991 -0.575221 -0.354916 +vn 0.639537 -0.575221 -0.510013 +vn 0.746725 -0.296294 -0.595493 +vn 0.931150 -0.296294 -0.212530 +vn 0.797490 -0.575220 -0.182022 +vn 0.736991 -0.575221 -0.354916 +vn 0.860512 -0.296294 -0.414401 +vn 0.955097 -0.296294 0.000000 +vn 0.817999 -0.575220 0.000000 +vn 0.797490 -0.575220 -0.182022 +vn 0.931150 -0.296294 -0.212530 +vn 0.931150 -0.296294 0.212529 +vn 0.797489 -0.575221 0.182022 +vn 0.817999 -0.575220 0.000000 +vn 0.955097 -0.296294 0.000000 +vn 0.860513 -0.296294 0.414400 +vn 0.736991 -0.575221 0.354916 +vn 0.797489 -0.575221 0.182022 +vn 0.931150 -0.296294 0.212529 +vn 0.746725 -0.296294 0.595493 +vn 0.639537 -0.575221 0.510013 +vn 0.736991 -0.575221 0.354916 +vn 0.860513 -0.296294 0.414400 +vn 0.595493 -0.296294 0.746725 +vn 0.510014 -0.575221 0.639537 +vn 0.639537 -0.575221 0.510013 +vn 0.746725 -0.296294 0.595493 +vn 0.201902 -0.946117 0.253177 +vn 0.253177 -0.946117 0.201902 +vn 0.463668 -0.805163 0.369762 +vn 0.369763 -0.805163 0.463668 +vn 0.140502 -0.946117 0.291757 +vn 0.201902 -0.946117 0.253177 +vn 0.369763 -0.805163 0.463668 +vn 0.257316 -0.805164 0.534322 +vn 0.072058 -0.946117 0.315707 +vn 0.140502 -0.946117 0.291757 +vn 0.257316 -0.805164 0.534322 +vn 0.131967 -0.805163 0.578184 +vn 0.000000 -0.805163 0.593053 +vn 0.000000 -0.946117 0.323825 +vn 0.072058 -0.946117 0.315707 +vn 0.131967 -0.805163 0.578184 +vn 0.072058 -0.946117 -0.315707 +vn 0.000000 -0.946117 -0.323826 +vn 0.000000 -0.805163 -0.593053 +vn 0.131967 -0.805164 -0.578184 +vn 0.140503 -0.946117 -0.291757 +vn 0.072058 -0.946117 -0.315707 +vn 0.131967 -0.805164 -0.578184 +vn 0.257316 -0.805164 -0.534322 +vn 0.201902 -0.946117 -0.253178 +vn 0.140503 -0.946117 -0.291757 +vn 0.257316 -0.805164 -0.534322 +vn 0.369762 -0.805164 -0.463667 +vn 0.253177 -0.946117 -0.201902 +vn 0.201902 -0.946117 -0.253178 +vn 0.369762 -0.805164 -0.463667 +vn 0.463667 -0.805164 -0.369762 +vn 0.291757 -0.946117 -0.140503 +vn 0.253177 -0.946117 -0.201902 +vn 0.463667 -0.805164 -0.369762 +vn 0.534322 -0.805164 -0.257316 +vn 0.315706 -0.946117 -0.072058 +vn 0.291757 -0.946117 -0.140503 +vn 0.534322 -0.805164 -0.257316 +vn 0.578184 -0.805163 -0.131967 +vn 0.323827 -0.946116 0.000000 +vn 0.315706 -0.946117 -0.072058 +vn 0.578184 -0.805163 -0.131967 +vn 0.593054 -0.805163 0.000000 +vn 0.315710 -0.946116 0.072059 +vn 0.323827 -0.946116 0.000000 +vn 0.593054 -0.805163 0.000000 +vn 0.578184 -0.805163 0.131967 +vn 0.291758 -0.946116 0.140504 +vn 0.315710 -0.946116 0.072059 +vn 0.578184 -0.805163 0.131967 +vn 0.534323 -0.805163 0.257316 +vn 0.463668 -0.805163 0.369762 +vn 0.253177 -0.946117 0.201902 +vn 0.291758 -0.946116 0.140504 +vn 0.534323 -0.805163 0.257316 +vn 0.000000 1.000000 0.000000 +vn 0.000000 0.998395 -0.056639 +vn -0.012603 0.998395 -0.055220 +vn 0.000000 1.000000 0.000000 +vn -0.012603 0.998395 -0.055220 +vn -0.024575 0.998395 -0.051030 +vn 0.000000 1.000000 0.000000 +vn -0.024575 0.998395 -0.051030 +vn -0.035314 0.998395 -0.044283 +vn 0.000000 1.000000 0.000000 +vn -0.035314 0.998395 -0.044283 +vn -0.044282 0.998395 -0.035314 +vn 0.000000 1.000000 0.000000 +vn -0.044282 0.998395 -0.035314 +vn -0.051030 0.998395 -0.024576 +vn 0.000000 1.000000 0.000000 +vn -0.051030 0.998395 -0.024576 +vn -0.055219 0.998395 -0.012605 +vn 0.000000 1.000000 0.000000 +vn -0.055219 0.998395 -0.012605 +vn -0.056639 0.998395 0.000000 +vn 0.000000 1.000000 0.000000 +vn -0.056639 0.998395 0.000000 +vn -0.055219 0.998395 0.012604 +vn 0.000000 1.000000 0.000000 +vn -0.055219 0.998395 0.012604 +vn -0.051030 0.998395 0.024576 +vn 0.000000 1.000000 0.000000 +vn -0.051030 0.998395 0.024576 +vn -0.044282 0.998395 0.035314 +vn 0.000000 1.000000 0.000000 +vn -0.044282 0.998395 0.035314 +vn -0.035314 0.998395 0.044281 +vn 0.000000 1.000000 0.000000 +vn -0.035314 0.998395 0.044281 +vn -0.024575 0.998395 0.051030 +vn 0.000000 1.000000 0.000000 +vn -0.024575 0.998395 0.051030 +vn -0.012603 0.998395 0.055219 +vn 0.000000 1.000000 0.000000 +vn -0.012603 0.998395 0.055219 +vn 0.000000 0.998395 0.056639 +vn 0.000000 0.998395 0.056639 +vn -0.012603 0.998395 0.055219 +vn -0.222494 0.015512 0.974811 +vn 0.000000 0.015512 0.999880 +vn 0.000000 -0.006117 -0.999981 +vn -0.222517 -0.006117 -0.974910 +vn -0.222494 0.015512 -0.974811 +vn 0.000000 0.015512 -0.999880 +vn -0.222494 0.015512 -0.974811 +vn -0.222517 -0.006117 -0.974910 +vn -0.433876 -0.006117 -0.900952 +vn -0.433832 0.015512 -0.900860 +vn -0.433876 -0.006117 -0.900952 +vn -0.623478 -0.006117 -0.781817 +vn -0.623415 0.015512 -0.781738 +vn -0.433832 0.015512 -0.900860 +vn -0.623478 -0.006117 -0.781817 +vn -0.781817 -0.006117 -0.623478 +vn -0.781737 0.015512 -0.623415 +vn -0.623415 0.015512 -0.781738 +vn -0.781817 -0.006117 -0.623478 +vn -0.900952 -0.006117 -0.433876 +vn -0.900860 0.015512 -0.433832 +vn -0.781737 0.015512 -0.623415 +vn -0.900952 -0.006117 -0.433876 +vn -0.974910 -0.006117 -0.222517 +vn -0.974811 0.015512 -0.222494 +vn -0.900860 0.015512 -0.433832 +vn -0.974910 -0.006117 -0.222517 +vn -0.999981 -0.006117 0.000000 +vn -0.999880 0.015512 0.000000 +vn -0.974811 0.015512 -0.222494 +vn -0.999981 -0.006117 0.000000 +vn -0.974910 -0.006117 0.222517 +vn -0.974811 0.015512 0.222494 +vn -0.999880 0.015512 0.000000 +vn -0.974910 -0.006117 0.222517 +vn -0.900952 -0.006117 0.433876 +vn -0.900861 0.015512 0.433831 +vn -0.974811 0.015512 0.222494 +vn -0.900952 -0.006117 0.433876 +vn -0.781817 -0.006117 0.623478 +vn -0.781738 0.015512 0.623414 +vn -0.900861 0.015512 0.433831 +vn -0.781817 -0.006117 0.623478 +vn -0.623478 -0.006117 0.781817 +vn -0.623415 0.015512 0.781737 +vn -0.781738 0.015512 0.623414 +vn -0.623478 -0.006117 0.781817 +vn -0.433875 -0.006117 0.900952 +vn -0.433831 0.015512 0.900861 +vn -0.623415 0.015512 0.781737 +vn -0.433875 -0.006117 0.900952 +vn -0.222517 -0.006117 0.974910 +vn -0.222494 0.015512 0.974811 +vn -0.433831 0.015512 0.900861 +vn -0.222517 -0.006117 0.974910 +vn 0.000000 -0.006117 0.999981 +vn 0.000000 0.015512 0.999880 +vn -0.222494 0.015512 0.974811 +vn 0.222517 -0.006117 -0.974910 +vn 0.000000 -0.006117 -0.999981 +vn 0.000000 0.015512 -0.999880 +vn 0.222494 0.015512 -0.974811 +vn 0.433876 -0.006117 -0.900952 +vn 0.222517 -0.006117 -0.974910 +vn 0.222494 0.015512 -0.974811 +vn 0.433832 0.015512 -0.900860 +vn 0.623478 -0.006117 -0.781817 +vn 0.433876 -0.006117 -0.900952 +vn 0.433832 0.015512 -0.900860 +vn 0.623415 0.015512 -0.781738 +vn 0.781817 -0.006117 -0.623478 +vn 0.623478 -0.006117 -0.781817 +vn 0.623415 0.015512 -0.781738 +vn 0.781737 0.015512 -0.623415 +vn 0.900952 -0.006117 -0.433876 +vn 0.781817 -0.006117 -0.623478 +vn 0.781737 0.015512 -0.623415 +vn 0.900860 0.015512 -0.433832 +vn 0.974910 -0.006117 -0.222517 +vn 0.900952 -0.006117 -0.433876 +vn 0.900860 0.015512 -0.433832 +vn 0.974811 0.015512 -0.222494 +vn 0.999981 -0.006117 0.000000 +vn 0.974910 -0.006117 -0.222517 +vn 0.974811 0.015512 -0.222494 +vn 0.999880 0.015512 0.000000 +vn 0.974910 -0.006117 0.222517 +vn 0.999981 -0.006117 0.000000 +vn 0.999880 0.015512 0.000000 +vn 0.974811 0.015512 0.222494 +vn 0.900952 -0.006117 0.433876 +vn 0.974910 -0.006117 0.222517 +vn 0.974811 0.015512 0.222494 +vn 0.900861 0.015512 0.433831 +vn 0.781817 -0.006117 0.623478 +vn 0.900952 -0.006117 0.433876 +vn 0.900861 0.015512 0.433831 +vn 0.781738 0.015512 0.623415 +vn 0.623478 -0.006117 0.781817 +vn 0.781817 -0.006117 0.623478 +vn 0.781738 0.015512 0.623415 +vn 0.623415 0.015512 0.781737 +vn 0.433875 -0.006117 0.900952 +vn 0.623478 -0.006117 0.781817 +vn 0.623415 0.015512 0.781737 +vn 0.433831 0.015512 0.900861 +vn 0.222517 -0.006117 0.974910 +vn 0.433875 -0.006117 0.900952 +vn 0.433831 0.015512 0.900861 +vn 0.222494 0.015512 0.974811 +vn 0.000000 -0.006117 0.999981 +vn 0.222517 -0.006117 0.974910 +vn 0.222494 0.015512 0.974811 +vn 0.000000 0.015512 0.999880 +vn 0.000000 1.000000 0.000000 +vn 0.000000 0.998395 0.056639 +vn 0.012603 0.998395 0.055219 +vn 0.000000 1.000000 0.000000 +vn 0.012603 0.998395 0.055219 +vn 0.024575 0.998395 0.051030 +vn 0.000000 1.000000 0.000000 +vn 0.024575 0.998395 0.051030 +vn 0.035314 0.998395 0.044282 +vn 0.000000 1.000000 0.000000 +vn 0.035314 0.998395 0.044282 +vn 0.044282 0.998395 0.035314 +vn 0.000000 1.000000 0.000000 +vn 0.044282 0.998395 0.035314 +vn 0.051030 0.998395 0.024575 +vn 0.000000 1.000000 0.000000 +vn 0.051030 0.998395 0.024575 +vn 0.055219 0.998395 0.012604 +vn 0.000000 1.000000 0.000000 +vn 0.055219 0.998395 0.012604 +vn 0.056639 0.998395 0.000000 +vn 0.000000 1.000000 0.000000 +vn 0.056639 0.998395 0.000000 +vn 0.055219 0.998395 -0.012605 +vn 0.000000 1.000000 0.000000 +vn 0.055219 0.998395 -0.012605 +vn 0.051030 0.998395 -0.024576 +vn 0.000000 1.000000 0.000000 +vn 0.051030 0.998395 -0.024576 +vn 0.044282 0.998395 -0.035314 +vn 0.000000 1.000000 0.000000 +vn 0.044282 0.998395 -0.035314 +vn 0.035314 0.998395 -0.044282 +vn 0.000000 1.000000 0.000000 +vn 0.035314 0.998395 -0.044282 +vn 0.024575 0.998395 -0.051030 +vn 0.000000 1.000000 0.000000 +vn 0.024575 0.998395 -0.051030 +vn 0.012603 0.998395 -0.055219 +vn 0.000000 1.000000 0.000000 +vn 0.012603 0.998395 -0.055219 +vn 0.000000 0.998395 -0.056639 +vn -0.012603 0.998395 0.055219 +vn -0.024575 0.998395 0.051030 +vn -0.433831 0.015512 0.900861 +vn -0.222494 0.015512 0.974811 +vn -0.024575 0.998395 0.051030 +vn -0.035314 0.998395 0.044281 +vn -0.623415 0.015512 0.781737 +vn -0.433831 0.015512 0.900861 +vn -0.035314 0.998395 0.044281 +vn -0.044282 0.998395 0.035314 +vn -0.781738 0.015512 0.623414 +vn -0.623415 0.015512 0.781737 +vn -0.044282 0.998395 0.035314 +vn -0.051030 0.998395 0.024576 +vn -0.900861 0.015512 0.433831 +vn -0.781738 0.015512 0.623414 +vn -0.051030 0.998395 0.024576 +vn -0.055219 0.998395 0.012604 +vn -0.974811 0.015512 0.222494 +vn -0.900861 0.015512 0.433831 +vn -0.055219 0.998395 0.012604 +vn -0.056639 0.998395 0.000000 +vn -0.999880 0.015512 0.000000 +vn -0.974811 0.015512 0.222494 +vn -0.056639 0.998395 0.000000 +vn -0.055219 0.998395 -0.012605 +vn -0.974811 0.015512 -0.222494 +vn -0.999880 0.015512 0.000000 +vn -0.055219 0.998395 -0.012605 +vn -0.051030 0.998395 -0.024576 +vn -0.900860 0.015512 -0.433832 +vn -0.974811 0.015512 -0.222494 +vn -0.051030 0.998395 -0.024576 +vn -0.044282 0.998395 -0.035314 +vn -0.781737 0.015512 -0.623415 +vn -0.900860 0.015512 -0.433832 +vn -0.044282 0.998395 -0.035314 +vn -0.035314 0.998395 -0.044283 +vn -0.623415 0.015512 -0.781738 +vn -0.781737 0.015512 -0.623415 +vn -0.035314 0.998395 -0.044283 +vn -0.024575 0.998395 -0.051030 +vn -0.433832 0.015512 -0.900860 +vn -0.623415 0.015512 -0.781738 +vn -0.024575 0.998395 -0.051030 +vn -0.012603 0.998395 -0.055220 +vn -0.222494 0.015512 -0.974811 +vn -0.433832 0.015512 -0.900860 +vn -0.012603 0.998395 -0.055220 +vn 0.000000 0.998395 -0.056639 +vn 0.000000 0.015512 -0.999880 +vn -0.222494 0.015512 -0.974811 +vn 0.000000 0.015512 -0.999880 +vn 0.000000 0.998395 -0.056639 +vn 0.012603 0.998395 -0.055219 +vn 0.222494 0.015512 -0.974811 +vn 0.012603 0.998395 0.055219 +vn 0.000000 0.998395 0.056639 +vn 0.000000 0.015512 0.999880 +vn 0.222494 0.015512 0.974811 +vn 0.024575 0.998395 0.051030 +vn 0.012603 0.998395 0.055219 +vn 0.222494 0.015512 0.974811 +vn 0.433831 0.015512 0.900861 +vn 0.035314 0.998395 0.044282 +vn 0.024575 0.998395 0.051030 +vn 0.433831 0.015512 0.900861 +vn 0.623415 0.015512 0.781737 +vn 0.044282 0.998395 0.035314 +vn 0.035314 0.998395 0.044282 +vn 0.623415 0.015512 0.781737 +vn 0.781738 0.015512 0.623415 +vn 0.051030 0.998395 0.024575 +vn 0.044282 0.998395 0.035314 +vn 0.781738 0.015512 0.623415 +vn 0.900861 0.015512 0.433831 +vn 0.055219 0.998395 0.012604 +vn 0.051030 0.998395 0.024575 +vn 0.900861 0.015512 0.433831 +vn 0.974811 0.015512 0.222494 +vn 0.056639 0.998395 0.000000 +vn 0.055219 0.998395 0.012604 +vn 0.974811 0.015512 0.222494 +vn 0.999880 0.015512 0.000000 +vn 0.055219 0.998395 -0.012605 +vn 0.056639 0.998395 0.000000 +vn 0.999880 0.015512 0.000000 +vn 0.974811 0.015512 -0.222494 +vn 0.051030 0.998395 -0.024576 +vn 0.055219 0.998395 -0.012605 +vn 0.974811 0.015512 -0.222494 +vn 0.900860 0.015512 -0.433832 +vn 0.044282 0.998395 -0.035314 +vn 0.051030 0.998395 -0.024576 +vn 0.900860 0.015512 -0.433832 +vn 0.781737 0.015512 -0.623415 +vn 0.035314 0.998395 -0.044282 +vn 0.044282 0.998395 -0.035314 +vn 0.781737 0.015512 -0.623415 +vn 0.623415 0.015512 -0.781738 +vn 0.024575 0.998395 -0.051030 +vn 0.035314 0.998395 -0.044282 +vn 0.623415 0.015512 -0.781738 +vn 0.433832 0.015512 -0.900860 +vn 0.012603 0.998395 -0.055219 +vn 0.024575 0.998395 -0.051030 +vn 0.433832 0.015512 -0.900860 +vn 0.222494 0.015512 -0.974811 +vn 0.000000 0.169480 -0.985534 +vn -0.219302 0.169481 -0.960824 +vn -0.211496 0.310860 -0.926626 +vn -0.000001 0.310860 -0.950456 +vn -0.219302 0.169481 -0.960824 +vn -0.427607 0.169481 -0.887935 +vn -0.412387 0.310860 -0.856331 +vn -0.211496 0.310860 -0.926626 +vn -0.427607 0.169481 -0.887935 +vn -0.614470 0.169480 -0.770521 +vn -0.592599 0.310860 -0.743096 +vn -0.412387 0.310860 -0.856331 +vn -0.614470 0.169480 -0.770521 +vn -0.770521 0.169480 -0.614471 +vn -0.743096 0.310860 -0.592600 +vn -0.592599 0.310860 -0.743096 +vn -0.770521 0.169480 -0.614471 +vn -0.887935 0.169480 -0.427607 +vn -0.856331 0.310860 -0.412387 +vn -0.743096 0.310860 -0.592600 +vn -0.887935 0.169480 -0.427607 +vn -0.960824 0.169480 -0.219302 +vn -0.926626 0.310859 -0.211496 +vn -0.856331 0.310860 -0.412387 +vn -0.960824 0.169480 -0.219302 +vn -0.985533 0.169481 0.000000 +vn -0.950456 0.310859 0.000000 +vn -0.926626 0.310859 -0.211496 +vn -0.985533 0.169481 0.000000 +vn -0.960824 0.169481 0.219302 +vn -0.926626 0.310860 0.211497 +vn -0.950456 0.310859 0.000000 +vn -0.960824 0.169481 0.219302 +vn -0.887935 0.169481 0.427607 +vn -0.856331 0.310860 0.412387 +vn -0.926626 0.310860 0.211497 +vn -0.887935 0.169481 0.427607 +vn -0.770521 0.169480 0.614470 +vn -0.743096 0.310860 0.592599 +vn -0.856331 0.310860 0.412387 +vn -0.770521 0.169480 0.614470 +vn -0.614471 0.169480 0.770521 +vn -0.592599 0.310860 0.743096 +vn -0.743096 0.310860 0.592599 +vn -0.614471 0.169480 0.770521 +vn -0.427607 0.169480 0.887935 +vn -0.412387 0.310860 0.856331 +vn -0.592599 0.310860 0.743096 +vn -0.427607 0.169480 0.887935 +vn -0.219302 0.169480 0.960824 +vn -0.211496 0.310860 0.926626 +vn -0.412387 0.310860 0.856331 +vn -0.219302 0.169480 0.960824 +vn 0.000000 0.169480 0.985534 +vn 0.000000 0.310860 0.950456 +vn -0.211496 0.310860 0.926626 +vn -0.000001 0.310860 -0.950456 +vn -0.211496 0.310860 -0.926626 +vn -0.198254 0.454112 -0.868607 +vn 0.000000 0.454112 -0.890945 +vn -0.211496 0.310860 -0.926626 +vn -0.412387 0.310860 -0.856331 +vn -0.386567 0.454112 -0.802713 +vn -0.198254 0.454112 -0.868607 +vn -0.412387 0.310860 -0.856331 +vn -0.592599 0.310860 -0.743096 +vn -0.555495 0.454112 -0.696568 +vn -0.386567 0.454112 -0.802713 +vn -0.592599 0.310860 -0.743096 +vn -0.743096 0.310860 -0.592600 +vn -0.696569 0.454112 -0.555495 +vn -0.555495 0.454112 -0.696568 +vn -0.743096 0.310860 -0.592600 +vn -0.856331 0.310860 -0.412387 +vn -0.802713 0.454112 -0.386567 +vn -0.696569 0.454112 -0.555495 +vn -0.856331 0.310860 -0.412387 +vn -0.926626 0.310859 -0.211496 +vn -0.868607 0.454112 -0.198254 +vn -0.802713 0.454112 -0.386567 +vn -0.926626 0.310859 -0.211496 +vn -0.950456 0.310859 0.000000 +vn -0.890945 0.454112 0.000000 +vn -0.868607 0.454112 -0.198254 +vn -0.950456 0.310859 0.000000 +vn -0.926626 0.310860 0.211497 +vn -0.868607 0.454112 0.198254 +vn -0.890945 0.454112 0.000000 +vn -0.926626 0.310860 0.211497 +vn -0.856331 0.310860 0.412387 +vn -0.802713 0.454112 0.386567 +vn -0.868607 0.454112 0.198254 +vn -0.856331 0.310860 0.412387 +vn -0.743096 0.310860 0.592599 +vn -0.696569 0.454112 0.555495 +vn -0.802713 0.454112 0.386567 +vn -0.743096 0.310860 0.592599 +vn -0.592599 0.310860 0.743096 +vn -0.555495 0.454112 0.696569 +vn -0.696569 0.454112 0.555495 +vn -0.592599 0.310860 0.743096 +vn -0.412387 0.310860 0.856331 +vn -0.386566 0.454112 0.802713 +vn -0.555495 0.454112 0.696569 +vn -0.412387 0.310860 0.856331 +vn -0.211496 0.310860 0.926626 +vn -0.198254 0.454112 0.868607 +vn -0.386566 0.454112 0.802713 +vn -0.211496 0.310860 0.926626 +vn 0.000000 0.310860 0.950456 +vn 0.000000 0.454112 0.890945 +vn -0.198254 0.454112 0.868607 +vn 0.000000 0.454112 -0.890945 +vn -0.198254 0.454112 -0.868607 +vn -0.180644 0.583925 -0.791454 +vn 0.000000 0.583925 -0.811808 +vn -0.198254 0.454112 -0.868607 +vn -0.386567 0.454112 -0.802713 +vn -0.352230 0.583925 -0.731413 +vn -0.180644 0.583925 -0.791454 +vn -0.386567 0.454112 -0.802713 +vn -0.555495 0.454112 -0.696568 +vn -0.506154 0.583925 -0.634697 +vn -0.352230 0.583925 -0.731413 +vn -0.555495 0.454112 -0.696568 +vn -0.696569 0.454112 -0.555495 +vn -0.634697 0.583925 -0.506154 +vn -0.506154 0.583925 -0.634697 +vn -0.696569 0.454112 -0.555495 +vn -0.802713 0.454112 -0.386567 +vn -0.731414 0.583925 -0.352230 +vn -0.634697 0.583925 -0.506154 +vn -0.802713 0.454112 -0.386567 +vn -0.868607 0.454112 -0.198254 +vn -0.791454 0.583925 -0.180644 +vn -0.731414 0.583925 -0.352230 +vn -0.868607 0.454112 -0.198254 +vn -0.890945 0.454112 0.000000 +vn -0.811808 0.583925 0.000000 +vn -0.791454 0.583925 -0.180644 +vn -0.890945 0.454112 0.000000 +vn -0.868607 0.454112 0.198254 +vn -0.791454 0.583925 0.180644 +vn -0.811808 0.583925 0.000000 +vn -0.868607 0.454112 0.198254 +vn -0.802713 0.454112 0.386567 +vn -0.731413 0.583925 0.352231 +vn -0.791454 0.583925 0.180644 +vn -0.802713 0.454112 0.386567 +vn -0.696569 0.454112 0.555495 +vn -0.634697 0.583925 0.506153 +vn -0.731413 0.583925 0.352231 +vn -0.696569 0.454112 0.555495 +vn -0.555495 0.454112 0.696569 +vn -0.506154 0.583925 0.634696 +vn -0.634697 0.583925 0.506153 +vn -0.555495 0.454112 0.696569 +vn -0.386566 0.454112 0.802713 +vn -0.352230 0.583925 0.731414 +vn -0.506154 0.583925 0.634696 +vn -0.386566 0.454112 0.802713 +vn -0.198254 0.454112 0.868607 +vn -0.180644 0.583925 0.791454 +vn -0.352230 0.583925 0.731414 +vn -0.198254 0.454112 0.868607 +vn 0.000000 0.454112 0.890945 +vn 0.000000 0.583925 0.811808 +vn -0.180644 0.583925 0.791454 +vn 0.000000 0.583925 -0.811808 +vn -0.180644 0.583925 -0.791454 +vn -0.159601 0.696823 -0.699260 +vn 0.000000 0.696823 -0.717243 +vn -0.180644 0.583925 -0.791454 +vn -0.352230 0.583925 -0.731413 +vn -0.311200 0.696823 -0.646213 +vn -0.159601 0.696823 -0.699260 +vn -0.352230 0.583925 -0.731413 +vn -0.506154 0.583925 -0.634697 +vn -0.447193 0.696823 -0.560763 +vn -0.311200 0.696823 -0.646213 +vn -0.506154 0.583925 -0.634697 +vn -0.634697 0.583925 -0.506154 +vn -0.560762 0.696824 -0.447194 +vn -0.447193 0.696823 -0.560763 +vn -0.634697 0.583925 -0.506154 +vn -0.731414 0.583925 -0.352230 +vn -0.646213 0.696824 -0.311200 +vn -0.560762 0.696824 -0.447194 +vn -0.731414 0.583925 -0.352230 +vn -0.791454 0.583925 -0.180644 +vn -0.699260 0.696824 -0.159601 +vn -0.646213 0.696824 -0.311200 +vn -0.791454 0.583925 -0.180644 +vn -0.811808 0.583925 0.000000 +vn -0.717243 0.696823 -0.000001 +vn -0.699260 0.696824 -0.159601 +vn -0.811808 0.583925 0.000000 +vn -0.791454 0.583925 0.180644 +vn -0.699260 0.696823 0.159601 +vn -0.717243 0.696823 -0.000001 +vn -0.791454 0.583925 0.180644 +vn -0.731413 0.583925 0.352231 +vn -0.646213 0.696823 0.311201 +vn -0.699260 0.696823 0.159601 +vn -0.731413 0.583925 0.352231 +vn -0.634697 0.583925 0.506153 +vn -0.560763 0.696824 0.447193 +vn -0.646213 0.696823 0.311201 +vn -0.634697 0.583925 0.506153 +vn -0.506154 0.583925 0.634696 +vn -0.447194 0.696824 0.560762 +vn -0.560763 0.696824 0.447193 +vn -0.506154 0.583925 0.634696 +vn -0.352230 0.583925 0.731414 +vn -0.311200 0.696824 0.646213 +vn -0.447194 0.696824 0.560762 +vn -0.352230 0.583925 0.731414 +vn -0.180644 0.583925 0.791454 +vn -0.159601 0.696823 0.699260 +vn -0.311200 0.696824 0.646213 +vn -0.180644 0.583925 0.791454 +vn 0.000000 0.583925 0.811808 +vn 0.000000 0.696824 0.717243 +vn -0.159601 0.696823 0.699260 +vn 0.000000 0.696823 -0.717243 +vn -0.159601 0.696823 -0.699260 +vn -0.136308 0.790421 -0.597205 +vn 0.000000 0.790421 -0.612564 +vn -0.159601 0.696823 -0.699260 +vn -0.311200 0.696823 -0.646213 +vn -0.265781 0.790422 -0.551901 +vn -0.136308 0.790421 -0.597205 +vn -0.311200 0.696823 -0.646213 +vn -0.447193 0.696823 -0.560763 +vn -0.381927 0.790422 -0.478921 +vn -0.265781 0.790422 -0.551901 +vn -0.447193 0.696823 -0.560763 +vn -0.560762 0.696824 -0.447194 +vn -0.478921 0.790422 -0.381927 +vn -0.381927 0.790422 -0.478921 +vn -0.560762 0.696824 -0.447194 +vn -0.646213 0.696824 -0.311200 +vn -0.551900 0.790422 -0.265781 +vn -0.478921 0.790422 -0.381927 +vn -0.646213 0.696824 -0.311200 +vn -0.699260 0.696824 -0.159601 +vn -0.597205 0.790422 -0.136308 +vn -0.551900 0.790422 -0.265781 +vn -0.699260 0.696824 -0.159601 +vn -0.717243 0.696823 -0.000001 +vn -0.612563 0.790422 0.000000 +vn -0.597205 0.790422 -0.136308 +vn -0.717243 0.696823 -0.000001 +vn -0.699260 0.696823 0.159601 +vn -0.597205 0.790422 0.136308 +vn -0.612563 0.790422 0.000000 +vn -0.699260 0.696823 0.159601 +vn -0.646213 0.696823 0.311201 +vn -0.551900 0.790421 0.265782 +vn -0.597205 0.790422 0.136308 +vn -0.646213 0.696823 0.311201 +vn -0.560763 0.696824 0.447193 +vn -0.478921 0.790421 0.381927 +vn -0.551900 0.790421 0.265782 +vn -0.560763 0.696824 0.447193 +vn -0.447194 0.696824 0.560762 +vn -0.381927 0.790422 0.478921 +vn -0.478921 0.790421 0.381927 +vn -0.447194 0.696824 0.560762 +vn -0.311200 0.696824 0.646213 +vn -0.265781 0.790422 0.551900 +vn -0.381927 0.790422 0.478921 +vn -0.311200 0.696824 0.646213 +vn -0.159601 0.696823 0.699260 +vn -0.136308 0.790421 0.597206 +vn -0.265781 0.790422 0.551900 +vn -0.159601 0.696823 0.699260 +vn 0.000000 0.696824 0.717243 +vn 0.000000 0.790422 0.612563 +vn -0.136308 0.790421 0.597206 +vn 0.000000 0.790421 -0.612564 +vn -0.136308 0.790421 -0.597205 +vn -0.112059 0.863943 -0.490964 +vn 0.000000 0.863942 -0.503591 +vn -0.136308 0.790421 -0.597205 +vn -0.265781 0.790422 -0.551901 +vn -0.218499 0.863943 -0.453718 +vn -0.112059 0.863943 -0.490964 +vn -0.265781 0.790422 -0.551901 +vn -0.381927 0.790422 -0.478921 +vn -0.313983 0.863943 -0.393722 +vn -0.218499 0.863943 -0.453718 +vn -0.381927 0.790422 -0.478921 +vn -0.478921 0.790422 -0.381927 +vn -0.393723 0.863943 -0.313983 +vn -0.313983 0.863943 -0.393722 +vn -0.478921 0.790422 -0.381927 +vn -0.551900 0.790422 -0.265781 +vn -0.453719 0.863943 -0.218500 +vn -0.393723 0.863943 -0.313983 +vn -0.551900 0.790422 -0.265781 +vn -0.597205 0.790422 -0.136308 +vn -0.490964 0.863943 -0.112059 +vn -0.453719 0.863943 -0.218500 +vn -0.597205 0.790422 -0.136308 +vn -0.612563 0.790422 0.000000 +vn -0.503590 0.863943 0.000001 +vn -0.490964 0.863943 -0.112059 +vn -0.612563 0.790422 0.000000 +vn -0.597205 0.790422 0.136308 +vn -0.490964 0.863943 0.112060 +vn -0.503590 0.863943 0.000001 +vn -0.597205 0.790422 0.136308 +vn -0.551900 0.790421 0.265782 +vn -0.453719 0.863943 0.218500 +vn -0.490964 0.863943 0.112060 +vn -0.551900 0.790421 0.265782 +vn -0.478921 0.790421 0.381927 +vn -0.393722 0.863943 0.313983 +vn -0.453719 0.863943 0.218500 +vn -0.478921 0.790421 0.381927 +vn -0.381927 0.790422 0.478921 +vn -0.313983 0.863943 0.393723 +vn -0.393722 0.863943 0.313983 +vn -0.381927 0.790422 0.478921 +vn -0.265781 0.790422 0.551900 +vn -0.218500 0.863943 0.453719 +vn -0.313983 0.863943 0.393723 +vn -0.265781 0.790422 0.551900 +vn -0.136308 0.790421 0.597206 +vn -0.112059 0.863943 0.490964 +vn -0.218500 0.863943 0.453719 +vn -0.136308 0.790421 0.597206 +vn 0.000000 0.790422 0.612563 +vn 0.000000 0.863943 0.503590 +vn -0.112059 0.863943 0.490964 +vn 0.000000 0.863942 -0.503591 +vn -0.112059 0.863943 -0.490964 +vn -0.088047 0.918388 -0.385761 +vn 0.000000 0.918388 -0.395681 +vn -0.112059 0.863943 -0.490964 +vn -0.218499 0.863943 -0.453718 +vn -0.171679 0.918388 -0.356496 +vn -0.088047 0.918388 -0.385761 +vn -0.218499 0.863943 -0.453718 +vn -0.313983 0.863943 -0.393722 +vn -0.246703 0.918388 -0.309356 +vn -0.171679 0.918388 -0.356496 +vn -0.313983 0.863943 -0.393722 +vn -0.393723 0.863943 -0.313983 +vn -0.309356 0.918388 -0.246703 +vn -0.246703 0.918388 -0.309356 +vn -0.393723 0.863943 -0.313983 +vn -0.453719 0.863943 -0.218500 +vn -0.356496 0.918388 -0.171680 +vn -0.309356 0.918388 -0.246703 +vn -0.453719 0.863943 -0.218500 +vn -0.490964 0.863943 -0.112059 +vn -0.396301 0.912607 -0.100467 +vn -0.356496 0.918388 -0.171680 +vn -0.490964 0.863943 -0.112059 +vn -0.503590 0.863943 0.000001 +vn -0.436572 0.899669 0.000001 +vn -0.396301 0.912607 -0.100467 +vn -0.503590 0.863943 0.000001 +vn -0.490964 0.863943 0.112060 +vn -0.396301 0.912607 0.100466 +vn -0.436572 0.899669 0.000001 +vn -0.490964 0.863943 0.112060 +vn -0.453719 0.863943 0.218500 +vn -0.356496 0.918388 0.171679 +vn -0.396301 0.912607 0.100466 +vn -0.453719 0.863943 0.218500 +vn -0.393722 0.863943 0.313983 +vn -0.309355 0.918388 0.246704 +vn -0.356496 0.918388 0.171679 +vn -0.393722 0.863943 0.313983 +vn -0.313983 0.863943 0.393723 +vn -0.246703 0.918388 0.309356 +vn -0.309355 0.918388 0.246704 +vn -0.313983 0.863943 0.393723 +vn -0.218500 0.863943 0.453719 +vn -0.171679 0.918388 0.356496 +vn -0.246703 0.918388 0.309356 +vn -0.218500 0.863943 0.453719 +vn -0.112059 0.863943 0.490964 +vn -0.088047 0.918388 0.385760 +vn -0.171679 0.918388 0.356496 +vn -0.112059 0.863943 0.490964 +vn 0.000000 0.863943 0.503590 +vn 0.000000 0.918388 0.395680 +vn -0.088047 0.918388 0.385760 +vn 0.000000 0.918388 -0.395681 +vn -0.088047 0.918388 -0.385761 +vn -0.065167 0.956156 -0.285516 +vn 0.000000 0.956156 -0.292859 +vn -0.088047 0.918388 -0.385761 +vn -0.171679 0.918388 -0.356496 +vn -0.127067 0.956156 -0.263856 +vn -0.065167 0.956156 -0.285516 +vn -0.171679 0.918388 -0.356496 +vn -0.246703 0.918388 -0.309356 +vn -0.182594 0.956156 -0.228967 +vn -0.127067 0.956156 -0.263856 +vn -0.246703 0.918388 -0.309356 +vn -0.309356 0.918388 -0.246703 +vn -0.228966 0.956155 -0.182596 +vn -0.182594 0.956156 -0.228967 +vn -0.309356 0.918388 -0.246703 +vn -0.356496 0.918388 -0.171680 +vn -0.263857 0.956156 -0.127067 +vn -0.228966 0.956155 -0.182596 +vn -0.356496 0.918388 -0.171680 +vn -0.396301 0.912607 -0.100467 +vn -0.269162 0.959884 -0.078576 +vn -0.263857 0.956156 -0.127067 +vn -0.396301 0.912607 0.100466 +vn -0.356496 0.918388 0.171679 +vn -0.263857 0.956156 0.127065 +vn -0.269162 0.959884 0.078574 +vn -0.356496 0.918388 0.171679 +vn -0.309355 0.918388 0.246704 +vn -0.228966 0.956156 0.182595 +vn -0.263857 0.956156 0.127065 +vn -0.309355 0.918388 0.246704 +vn -0.246703 0.918388 0.309356 +vn -0.182594 0.956156 0.228967 +vn -0.228966 0.956156 0.182595 +vn -0.246703 0.918388 0.309356 +vn -0.171679 0.918388 0.356496 +vn -0.127067 0.956156 0.263857 +vn -0.182594 0.956156 0.228967 +vn -0.171679 0.918388 0.356496 +vn -0.088047 0.918388 0.385760 +vn -0.065167 0.956156 0.285516 +vn -0.127067 0.956156 0.263857 +vn -0.088047 0.918388 0.385760 +vn 0.000000 0.918388 0.395680 +vn 0.000000 0.956156 0.292859 +vn -0.065167 0.956156 0.285516 +vn 0.000000 0.956156 -0.292859 +vn -0.065167 0.956156 -0.285516 +vn -0.043999 0.980256 -0.192773 +vn 0.000000 0.980256 -0.197731 +vn -0.065167 0.956156 -0.285516 +vn -0.127067 0.956156 -0.263856 +vn -0.085793 0.980256 -0.178150 +vn -0.043999 0.980256 -0.192773 +vn -0.127067 0.956156 -0.263856 +vn -0.182594 0.956156 -0.228967 +vn -0.123284 0.980256 -0.154593 +vn -0.085793 0.980256 -0.178150 +vn -0.182594 0.956156 -0.228967 +vn -0.228966 0.956155 -0.182596 +vn -0.154593 0.980256 -0.123284 +vn -0.123284 0.980256 -0.154593 +vn -0.228966 0.956155 -0.182596 +vn -0.263857 0.956156 -0.127067 +vn -0.178150 0.980256 -0.085792 +vn -0.154593 0.980256 -0.123284 +vn -0.263857 0.956156 -0.127067 +vn -0.269162 0.959884 -0.078576 +vn -0.192774 0.980256 -0.044000 +vn -0.178150 0.980256 -0.085792 +vn -0.269162 0.959884 -0.078576 +vn -0.226299 0.974058 0.000001 +vn -0.197732 0.980256 0.000000 +vn -0.192774 0.980256 -0.044000 +vn -0.226299 0.974058 0.000001 +vn -0.269162 0.959884 0.078574 +vn -0.192774 0.980256 0.044000 +vn -0.197732 0.980256 0.000000 +vn -0.269162 0.959884 0.078574 +vn -0.263857 0.956156 0.127065 +vn -0.178150 0.980256 0.085793 +vn -0.192774 0.980256 0.044000 +vn -0.263857 0.956156 0.127065 +vn -0.228966 0.956156 0.182595 +vn -0.154593 0.980256 0.123284 +vn -0.178150 0.980256 0.085793 +vn -0.228966 0.956156 0.182595 +vn -0.182594 0.956156 0.228967 +vn -0.123284 0.980256 0.154595 +vn -0.154593 0.980256 0.123284 +vn -0.182594 0.956156 0.228967 +vn -0.127067 0.956156 0.263857 +vn -0.085792 0.980256 0.178152 +vn -0.123284 0.980256 0.154595 +vn -0.127067 0.956156 0.263857 +vn -0.065167 0.956156 0.285516 +vn -0.043999 0.980256 0.192775 +vn -0.085792 0.980256 0.178152 +vn -0.065167 0.956156 0.285516 +vn 0.000000 0.956156 0.292859 +vn 0.000000 0.980256 0.197732 +vn -0.043999 0.980256 0.192775 +vn 0.000000 0.980256 -0.197731 +vn -0.043999 0.980256 -0.192773 +vn -0.025567 0.993377 -0.112018 +vn 0.000000 0.993377 -0.114899 +vn -0.043999 0.980256 -0.192773 +vn -0.085793 0.980256 -0.178150 +vn -0.049853 0.993377 -0.103521 +vn -0.025567 0.993377 -0.112018 +vn -0.085793 0.980256 -0.178150 +vn -0.123284 0.980256 -0.154593 +vn -0.071638 0.993377 -0.089830 +vn -0.049853 0.993377 -0.103521 +vn -0.123284 0.980256 -0.154593 +vn -0.154593 0.980256 -0.123284 +vn -0.089831 0.993378 -0.071635 +vn -0.071638 0.993377 -0.089830 +vn -0.154593 0.980256 -0.123284 +vn -0.178150 0.980256 -0.085792 +vn -0.103520 0.993377 -0.049855 +vn -0.089831 0.993378 -0.071635 +vn -0.178150 0.980256 -0.085792 +vn -0.192774 0.980256 -0.044000 +vn -0.112018 0.993377 -0.025569 +vn -0.103520 0.993377 -0.049855 +vn -0.192774 0.980256 -0.044000 +vn -0.197732 0.980256 0.000000 +vn -0.114899 0.993377 0.000000 +vn -0.112018 0.993377 -0.025569 +vn -0.197732 0.980256 0.000000 +vn -0.192774 0.980256 0.044000 +vn -0.112018 0.993377 0.025570 +vn -0.114899 0.993377 0.000000 +vn -0.192774 0.980256 0.044000 +vn -0.178150 0.980256 0.085793 +vn -0.103520 0.993377 0.049855 +vn -0.112018 0.993377 0.025570 +vn -0.178150 0.980256 0.085793 +vn -0.154593 0.980256 0.123284 +vn -0.089831 0.993377 0.071638 +vn -0.103520 0.993377 0.049855 +vn -0.154593 0.980256 0.123284 +vn -0.123284 0.980256 0.154595 +vn -0.071638 0.993377 0.089834 +vn -0.089831 0.993377 0.071638 +vn -0.123284 0.980256 0.154595 +vn -0.085792 0.980256 0.178152 +vn -0.049853 0.993377 0.103522 +vn -0.071638 0.993377 0.089834 +vn -0.085792 0.980256 0.178152 +vn -0.043999 0.980256 0.192775 +vn -0.025567 0.993377 0.112019 +vn -0.049853 0.993377 0.103522 +vn -0.043999 0.980256 0.192775 +vn 0.000000 0.980256 0.197732 +vn 0.000000 0.993377 0.114900 +vn -0.025567 0.993377 0.112019 +vn 0.000000 0.993377 -0.114899 +vn -0.025567 0.993377 -0.112018 +vn 0.000000 1.000000 -0.000001 +vn -0.025567 0.993377 -0.112018 +vn -0.049853 0.993377 -0.103521 +vn 0.000000 1.000000 -0.000001 +vn -0.049853 0.993377 -0.103521 +vn -0.071638 0.993377 -0.089830 +vn 0.000000 1.000000 -0.000001 +vn -0.071638 0.993377 -0.089830 +vn -0.089831 0.993378 -0.071635 +vn 0.000000 1.000000 -0.000001 +vn -0.089831 0.993378 -0.071635 +vn -0.103520 0.993377 -0.049855 +vn 0.000000 1.000000 -0.000001 +vn -0.103520 0.993377 -0.049855 +vn -0.112018 0.993377 -0.025569 +vn 0.000000 1.000000 -0.000001 +vn -0.112018 0.993377 -0.025569 +vn -0.114899 0.993377 0.000000 +vn 0.000000 1.000000 -0.000001 +vn -0.114899 0.993377 0.000000 +vn -0.112018 0.993377 0.025570 +vn 0.000000 1.000000 -0.000001 +vn -0.112018 0.993377 0.025570 +vn -0.103520 0.993377 0.049855 +vn 0.000000 1.000000 -0.000001 +vn -0.103520 0.993377 0.049855 +vn -0.089831 0.993377 0.071638 +vn 0.000000 1.000000 -0.000001 +vn -0.089831 0.993377 0.071638 +vn -0.071638 0.993377 0.089834 +vn 0.000000 1.000000 -0.000001 +vn -0.071638 0.993377 0.089834 +vn -0.049853 0.993377 0.103522 +vn 0.000000 1.000000 -0.000001 +vn -0.049853 0.993377 0.103522 +vn -0.025567 0.993377 0.112019 +vn 0.000000 1.000000 -0.000001 +vn -0.025567 0.993377 0.112019 +vn 0.000000 0.993377 0.114900 +vn 0.000000 1.000000 -0.000001 +vn 0.000000 0.169480 -0.985534 +vn -0.000001 0.310860 -0.950456 +vn 0.211496 0.310861 -0.926626 +vn 0.219302 0.169480 -0.960824 +vn 0.219302 0.169480 -0.960824 +vn 0.211496 0.310861 -0.926626 +vn 0.412388 0.310861 -0.856331 +vn 0.427607 0.169480 -0.887935 +vn 0.427607 0.169480 -0.887935 +vn 0.412388 0.310861 -0.856331 +vn 0.592599 0.310860 -0.743096 +vn 0.614470 0.169480 -0.770521 +vn 0.614470 0.169480 -0.770521 +vn 0.592599 0.310860 -0.743096 +vn 0.743096 0.310860 -0.592600 +vn 0.770521 0.169480 -0.614471 +vn 0.770521 0.169480 -0.614471 +vn 0.743096 0.310860 -0.592600 +vn 0.856331 0.310860 -0.412387 +vn 0.887935 0.169481 -0.427607 +vn 0.887935 0.169481 -0.427607 +vn 0.856331 0.310860 -0.412387 +vn 0.926626 0.310860 -0.211497 +vn 0.960824 0.169480 -0.219302 +vn 0.960824 0.169480 -0.219302 +vn 0.926626 0.310860 -0.211497 +vn 0.950456 0.310860 -0.000001 +vn 0.985534 0.169480 0.000000 +vn 0.985534 0.169480 0.000000 +vn 0.950456 0.310860 -0.000001 +vn 0.926625 0.310861 0.211497 +vn 0.960824 0.169481 0.219302 +vn 0.960824 0.169481 0.219302 +vn 0.926625 0.310861 0.211497 +vn 0.856331 0.310860 0.412388 +vn 0.887935 0.169481 0.427607 +vn 0.887935 0.169481 0.427607 +vn 0.856331 0.310860 0.412388 +vn 0.743096 0.310860 0.592599 +vn 0.770521 0.169480 0.614470 +vn 0.770521 0.169480 0.614470 +vn 0.743096 0.310860 0.592599 +vn 0.592600 0.310860 0.743096 +vn 0.614471 0.169480 0.770521 +vn 0.614471 0.169480 0.770521 +vn 0.592600 0.310860 0.743096 +vn 0.412388 0.310860 0.856331 +vn 0.427607 0.169481 0.887935 +vn 0.427607 0.169481 0.887935 +vn 0.412388 0.310860 0.856331 +vn 0.211497 0.310860 0.926626 +vn 0.219302 0.169480 0.960824 +vn 0.219302 0.169480 0.960824 +vn 0.211497 0.310860 0.926626 +vn 0.000000 0.310860 0.950456 +vn 0.000000 0.169480 0.985534 +vn -0.000001 0.310860 -0.950456 +vn 0.000000 0.454112 -0.890945 +vn 0.198254 0.454112 -0.868607 +vn 0.211496 0.310861 -0.926626 +vn 0.211496 0.310861 -0.926626 +vn 0.198254 0.454112 -0.868607 +vn 0.386567 0.454112 -0.802713 +vn 0.412388 0.310861 -0.856331 +vn 0.412388 0.310861 -0.856331 +vn 0.386567 0.454112 -0.802713 +vn 0.555495 0.454112 -0.696569 +vn 0.592599 0.310860 -0.743096 +vn 0.592599 0.310860 -0.743096 +vn 0.555495 0.454112 -0.696569 +vn 0.696568 0.454111 -0.555496 +vn 0.743096 0.310860 -0.592600 +vn 0.743096 0.310860 -0.592600 +vn 0.696568 0.454111 -0.555496 +vn 0.802713 0.454112 -0.386567 +vn 0.856331 0.310860 -0.412387 +vn 0.856331 0.310860 -0.412387 +vn 0.802713 0.454112 -0.386567 +vn 0.868607 0.454112 -0.198254 +vn 0.926626 0.310860 -0.211497 +vn 0.926626 0.310860 -0.211497 +vn 0.868607 0.454112 -0.198254 +vn 0.890945 0.454112 0.000000 +vn 0.950456 0.310860 -0.000001 +vn 0.950456 0.310860 -0.000001 +vn 0.890945 0.454112 0.000000 +vn 0.868607 0.454112 0.198255 +vn 0.926625 0.310861 0.211497 +vn 0.926625 0.310861 0.211497 +vn 0.868607 0.454112 0.198255 +vn 0.802713 0.454112 0.386567 +vn 0.856331 0.310860 0.412388 +vn 0.856331 0.310860 0.412388 +vn 0.802713 0.454112 0.386567 +vn 0.696569 0.454112 0.555495 +vn 0.743096 0.310860 0.592599 +vn 0.743096 0.310860 0.592599 +vn 0.696569 0.454112 0.555495 +vn 0.555495 0.454112 0.696569 +vn 0.592600 0.310860 0.743096 +vn 0.592600 0.310860 0.743096 +vn 0.555495 0.454112 0.696569 +vn 0.386566 0.454112 0.802714 +vn 0.412388 0.310860 0.856331 +vn 0.412388 0.310860 0.856331 +vn 0.386566 0.454112 0.802714 +vn 0.198254 0.454112 0.868607 +vn 0.211497 0.310860 0.926626 +vn 0.211497 0.310860 0.926626 +vn 0.198254 0.454112 0.868607 +vn 0.000000 0.454112 0.890945 +vn 0.000000 0.310860 0.950456 +vn 0.000000 0.454112 -0.890945 +vn 0.000000 0.583925 -0.811808 +vn 0.180644 0.583925 -0.791454 +vn 0.198254 0.454112 -0.868607 +vn 0.198254 0.454112 -0.868607 +vn 0.180644 0.583925 -0.791454 +vn 0.352230 0.583925 -0.731414 +vn 0.386567 0.454112 -0.802713 +vn 0.386567 0.454112 -0.802713 +vn 0.352230 0.583925 -0.731414 +vn 0.506154 0.583925 -0.634697 +vn 0.555495 0.454112 -0.696569 +vn 0.555495 0.454112 -0.696569 +vn 0.506154 0.583925 -0.634697 +vn 0.634697 0.583924 -0.506154 +vn 0.696568 0.454111 -0.555496 +vn 0.696568 0.454111 -0.555496 +vn 0.634697 0.583924 -0.506154 +vn 0.731413 0.583925 -0.352231 +vn 0.802713 0.454112 -0.386567 +vn 0.802713 0.454112 -0.386567 +vn 0.731413 0.583925 -0.352231 +vn 0.791454 0.583925 -0.180644 +vn 0.868607 0.454112 -0.198254 +vn 0.868607 0.454112 -0.198254 +vn 0.791454 0.583925 -0.180644 +vn 0.811808 0.583925 0.000000 +vn 0.890945 0.454112 0.000000 +vn 0.890945 0.454112 0.000000 +vn 0.811808 0.583925 0.000000 +vn 0.791454 0.583925 0.180645 +vn 0.868607 0.454112 0.198255 +vn 0.868607 0.454112 0.198255 +vn 0.791454 0.583925 0.180645 +vn 0.731414 0.583925 0.352230 +vn 0.802713 0.454112 0.386567 +vn 0.802713 0.454112 0.386567 +vn 0.731414 0.583925 0.352230 +vn 0.634697 0.583925 0.506154 +vn 0.696569 0.454112 0.555495 +vn 0.696569 0.454112 0.555495 +vn 0.634697 0.583925 0.506154 +vn 0.506154 0.583925 0.634697 +vn 0.555495 0.454112 0.696569 +vn 0.555495 0.454112 0.696569 +vn 0.506154 0.583925 0.634697 +vn 0.352230 0.583925 0.731414 +vn 0.386566 0.454112 0.802714 +vn 0.386566 0.454112 0.802714 +vn 0.352230 0.583925 0.731414 +vn 0.180644 0.583925 0.791454 +vn 0.198254 0.454112 0.868607 +vn 0.198254 0.454112 0.868607 +vn 0.180644 0.583925 0.791454 +vn 0.000000 0.583925 0.811808 +vn 0.000000 0.454112 0.890945 +vn 0.000000 0.583925 -0.811808 +vn 0.000000 0.696823 -0.717243 +vn 0.159601 0.696824 -0.699260 +vn 0.180644 0.583925 -0.791454 +vn 0.180644 0.583925 -0.791454 +vn 0.159601 0.696824 -0.699260 +vn 0.311200 0.696824 -0.646213 +vn 0.352230 0.583925 -0.731414 +vn 0.352230 0.583925 -0.731414 +vn 0.311200 0.696824 -0.646213 +vn 0.447194 0.696824 -0.560762 +vn 0.506154 0.583925 -0.634697 +vn 0.506154 0.583925 -0.634697 +vn 0.447194 0.696824 -0.560762 +vn 0.560763 0.696824 -0.447193 +vn 0.634697 0.583924 -0.506154 +vn 0.634697 0.583924 -0.506154 +vn 0.560763 0.696824 -0.447193 +vn 0.646213 0.696824 -0.311200 +vn 0.731413 0.583925 -0.352231 +vn 0.731413 0.583925 -0.352231 +vn 0.646213 0.696824 -0.311200 +vn 0.699260 0.696824 -0.159601 +vn 0.791454 0.583925 -0.180644 +vn 0.791454 0.583925 -0.180644 +vn 0.699260 0.696824 -0.159601 +vn 0.717242 0.696824 0.000000 +vn 0.811808 0.583925 0.000000 +vn 0.811808 0.583925 0.000000 +vn 0.717242 0.696824 0.000000 +vn 0.699260 0.696824 0.159602 +vn 0.791454 0.583925 0.180645 +vn 0.791454 0.583925 0.180645 +vn 0.699260 0.696824 0.159602 +vn 0.646213 0.696824 0.311199 +vn 0.731414 0.583925 0.352230 +vn 0.731414 0.583925 0.352230 +vn 0.646213 0.696824 0.311199 +vn 0.560763 0.696824 0.447193 +vn 0.634697 0.583925 0.506154 +vn 0.634697 0.583925 0.506154 +vn 0.560763 0.696824 0.447193 +vn 0.447193 0.696824 0.560762 +vn 0.506154 0.583925 0.634697 +vn 0.506154 0.583925 0.634697 +vn 0.447193 0.696824 0.560762 +vn 0.311200 0.696824 0.646213 +vn 0.352230 0.583925 0.731414 +vn 0.352230 0.583925 0.731414 +vn 0.311200 0.696824 0.646213 +vn 0.159601 0.696823 0.699260 +vn 0.180644 0.583925 0.791454 +vn 0.180644 0.583925 0.791454 +vn 0.159601 0.696823 0.699260 +vn 0.000000 0.696824 0.717243 +vn 0.000000 0.583925 0.811808 +vn 0.000000 0.696823 -0.717243 +vn 0.000000 0.790421 -0.612564 +vn 0.136308 0.790421 -0.597206 +vn 0.159601 0.696824 -0.699260 +vn 0.159601 0.696824 -0.699260 +vn 0.136308 0.790421 -0.597206 +vn 0.265781 0.790422 -0.551900 +vn 0.311200 0.696824 -0.646213 +vn 0.311200 0.696824 -0.646213 +vn 0.265781 0.790422 -0.551900 +vn 0.381927 0.790422 -0.478921 +vn 0.447194 0.696824 -0.560762 +vn 0.447194 0.696824 -0.560762 +vn 0.381927 0.790422 -0.478921 +vn 0.478921 0.790422 -0.381927 +vn 0.560763 0.696824 -0.447193 +vn 0.560763 0.696824 -0.447193 +vn 0.478921 0.790422 -0.381927 +vn 0.551900 0.790422 -0.265781 +vn 0.646213 0.696824 -0.311200 +vn 0.646213 0.696824 -0.311200 +vn 0.551900 0.790422 -0.265781 +vn 0.597205 0.790422 -0.136308 +vn 0.699260 0.696824 -0.159601 +vn 0.699260 0.696824 -0.159601 +vn 0.597205 0.790422 -0.136308 +vn 0.612563 0.790422 0.000000 +vn 0.717242 0.696824 0.000000 +vn 0.717242 0.696824 0.000000 +vn 0.612563 0.790422 0.000000 +vn 0.597205 0.790422 0.136308 +vn 0.699260 0.696824 0.159602 +vn 0.699260 0.696824 0.159602 +vn 0.597205 0.790422 0.136308 +vn 0.551900 0.790422 0.265781 +vn 0.646213 0.696824 0.311199 +vn 0.646213 0.696824 0.311199 +vn 0.551900 0.790422 0.265781 +vn 0.478921 0.790422 0.381926 +vn 0.560763 0.696824 0.447193 +vn 0.560763 0.696824 0.447193 +vn 0.478921 0.790422 0.381926 +vn 0.381927 0.790422 0.478921 +vn 0.447193 0.696824 0.560762 +vn 0.447193 0.696824 0.560762 +vn 0.381927 0.790422 0.478921 +vn 0.265781 0.790421 0.551901 +vn 0.311200 0.696824 0.646213 +vn 0.311200 0.696824 0.646213 +vn 0.265781 0.790421 0.551901 +vn 0.136308 0.790422 0.597205 +vn 0.159601 0.696823 0.699260 +vn 0.159601 0.696823 0.699260 +vn 0.136308 0.790422 0.597205 +vn 0.000000 0.790422 0.612563 +vn 0.000000 0.696824 0.717243 +vn 0.000000 0.790421 -0.612564 +vn 0.000000 0.863942 -0.503591 +vn 0.112059 0.863942 -0.490965 +vn 0.136308 0.790421 -0.597206 +vn 0.136308 0.790421 -0.597206 +vn 0.112059 0.863942 -0.490965 +vn 0.218500 0.863943 -0.453719 +vn 0.265781 0.790422 -0.551900 +vn 0.265781 0.790422 -0.551900 +vn 0.218500 0.863943 -0.453719 +vn 0.313983 0.863943 -0.393722 +vn 0.381927 0.790422 -0.478921 +vn 0.381927 0.790422 -0.478921 +vn 0.313983 0.863943 -0.393722 +vn 0.393723 0.863943 -0.313983 +vn 0.478921 0.790422 -0.381927 +vn 0.478921 0.790422 -0.381927 +vn 0.393723 0.863943 -0.313983 +vn 0.453719 0.863943 -0.218500 +vn 0.551900 0.790422 -0.265781 +vn 0.551900 0.790422 -0.265781 +vn 0.453719 0.863943 -0.218500 +vn 0.490964 0.863943 -0.112060 +vn 0.597205 0.790422 -0.136308 +vn 0.597205 0.790422 -0.136308 +vn 0.490964 0.863943 -0.112060 +vn 0.503590 0.863943 0.000000 +vn 0.612563 0.790422 0.000000 +vn 0.612563 0.790422 0.000000 +vn 0.503590 0.863943 0.000000 +vn 0.490964 0.863943 0.112060 +vn 0.597205 0.790422 0.136308 +vn 0.597205 0.790422 0.136308 +vn 0.490964 0.863943 0.112060 +vn 0.453719 0.863943 0.218500 +vn 0.551900 0.790422 0.265781 +vn 0.551900 0.790422 0.265781 +vn 0.453719 0.863943 0.218500 +vn 0.393723 0.863942 0.313984 +vn 0.478921 0.790422 0.381926 +vn 0.478921 0.790422 0.381926 +vn 0.393723 0.863942 0.313984 +vn 0.313983 0.863943 0.393723 +vn 0.381927 0.790422 0.478921 +vn 0.381927 0.790422 0.478921 +vn 0.313983 0.863943 0.393723 +vn 0.218499 0.863943 0.453719 +vn 0.265781 0.790421 0.551901 +vn 0.265781 0.790421 0.551901 +vn 0.218499 0.863943 0.453719 +vn 0.112059 0.863943 0.490964 +vn 0.136308 0.790422 0.597205 +vn 0.136308 0.790422 0.597205 +vn 0.112059 0.863943 0.490964 +vn 0.000000 0.863943 0.503590 +vn 0.000000 0.790422 0.612563 +vn 0.000000 0.863942 -0.503591 +vn 0.000000 0.918388 -0.395681 +vn 0.088047 0.918388 -0.385761 +vn 0.112059 0.863942 -0.490965 +vn 0.112059 0.863942 -0.490965 +vn 0.088047 0.918388 -0.385761 +vn 0.171679 0.918388 -0.356497 +vn 0.218500 0.863943 -0.453719 +vn 0.218500 0.863943 -0.453719 +vn 0.171679 0.918388 -0.356497 +vn 0.246703 0.918388 -0.309355 +vn 0.313983 0.863943 -0.393722 +vn 0.313983 0.863943 -0.393722 +vn 0.246703 0.918388 -0.309355 +vn 0.309355 0.918388 -0.246703 +vn 0.393723 0.863943 -0.313983 +vn 0.393723 0.863943 -0.313983 +vn 0.309355 0.918388 -0.246703 +vn 0.356496 0.918388 -0.171680 +vn 0.453719 0.863943 -0.218500 +vn 0.453719 0.863943 -0.218500 +vn 0.356496 0.918388 -0.171680 +vn 0.396301 0.912607 -0.100466 +vn 0.490964 0.863943 -0.112060 +vn 0.490964 0.863943 -0.112060 +vn 0.396301 0.912607 -0.100466 +vn 0.436573 0.899669 0.000001 +vn 0.503590 0.863943 0.000000 +vn 0.503590 0.863943 0.000000 +vn 0.436573 0.899669 0.000001 +vn 0.396301 0.912607 0.100466 +vn 0.490964 0.863943 0.112060 +vn 0.490964 0.863943 0.112060 +vn 0.396301 0.912607 0.100466 +vn 0.356496 0.918388 0.171679 +vn 0.453719 0.863943 0.218500 +vn 0.453719 0.863943 0.218500 +vn 0.356496 0.918388 0.171679 +vn 0.309356 0.918388 0.246703 +vn 0.393723 0.863942 0.313984 +vn 0.393723 0.863942 0.313984 +vn 0.309356 0.918388 0.246703 +vn 0.246703 0.918388 0.309356 +vn 0.313983 0.863943 0.393723 +vn 0.313983 0.863943 0.393723 +vn 0.246703 0.918388 0.309356 +vn 0.171679 0.918388 0.356496 +vn 0.218499 0.863943 0.453719 +vn 0.218499 0.863943 0.453719 +vn 0.171679 0.918388 0.356496 +vn 0.088047 0.918388 0.385760 +vn 0.112059 0.863943 0.490964 +vn 0.112059 0.863943 0.490964 +vn 0.088047 0.918388 0.385760 +vn 0.000000 0.918388 0.395680 +vn 0.000000 0.863943 0.503590 +vn 0.000000 0.918388 -0.395681 +vn 0.000000 0.956156 -0.292859 +vn 0.065167 0.956156 -0.285517 +vn 0.088047 0.918388 -0.385761 +vn 0.088047 0.918388 -0.385761 +vn 0.065167 0.956156 -0.285517 +vn 0.127067 0.956156 -0.263857 +vn 0.171679 0.918388 -0.356497 +vn 0.171679 0.918388 -0.356497 +vn 0.127067 0.956156 -0.263857 +vn 0.182594 0.956156 -0.228966 +vn 0.246703 0.918388 -0.309355 +vn 0.246703 0.918388 -0.309355 +vn 0.182594 0.956156 -0.228966 +vn 0.228966 0.956156 -0.182595 +vn 0.309355 0.918388 -0.246703 +vn 0.309355 0.918388 -0.246703 +vn 0.228966 0.956156 -0.182595 +vn 0.263857 0.956156 -0.127067 +vn 0.356496 0.918388 -0.171680 +vn 0.356496 0.918388 -0.171680 +vn 0.263857 0.956156 -0.127067 +vn 0.269162 0.959884 -0.078575 +vn 0.396301 0.912607 -0.100466 +vn 0.396301 0.912607 0.100466 +vn 0.269162 0.959884 0.078574 +vn 0.263857 0.956156 0.127066 +vn 0.356496 0.918388 0.171679 +vn 0.356496 0.918388 0.171679 +vn 0.263857 0.956156 0.127066 +vn 0.228966 0.956156 0.182594 +vn 0.309356 0.918388 0.246703 +vn 0.309356 0.918388 0.246703 +vn 0.228966 0.956156 0.182594 +vn 0.182595 0.956156 0.228965 +vn 0.246703 0.918388 0.309356 +vn 0.246703 0.918388 0.309356 +vn 0.182595 0.956156 0.228965 +vn 0.127067 0.956156 0.263854 +vn 0.171679 0.918388 0.356496 +vn 0.171679 0.918388 0.356496 +vn 0.127067 0.956156 0.263854 +vn 0.065167 0.956156 0.285515 +vn 0.088047 0.918388 0.385760 +vn 0.088047 0.918388 0.385760 +vn 0.065167 0.956156 0.285515 +vn 0.000000 0.956156 0.292859 +vn 0.000000 0.918388 0.395680 +vn 0.000000 0.956156 -0.292859 +vn 0.000000 0.980256 -0.197731 +vn 0.043999 0.980256 -0.192774 +vn 0.065167 0.956156 -0.285517 +vn 0.065167 0.956156 -0.285517 +vn 0.043999 0.980256 -0.192774 +vn 0.085793 0.980256 -0.178151 +vn 0.127067 0.956156 -0.263857 +vn 0.127067 0.956156 -0.263857 +vn 0.085793 0.980256 -0.178151 +vn 0.123284 0.980256 -0.154592 +vn 0.182594 0.956156 -0.228966 +vn 0.182594 0.956156 -0.228966 +vn 0.123284 0.980256 -0.154592 +vn 0.154593 0.980256 -0.123283 +vn 0.228966 0.956156 -0.182595 +vn 0.228966 0.956156 -0.182595 +vn 0.154593 0.980256 -0.123283 +vn 0.178150 0.980256 -0.085793 +vn 0.263857 0.956156 -0.127067 +vn 0.263857 0.956156 -0.127067 +vn 0.178150 0.980256 -0.085793 +vn 0.192774 0.980256 -0.043999 +vn 0.269162 0.959884 -0.078575 +vn 0.269162 0.959884 -0.078575 +vn 0.192774 0.980256 -0.043999 +vn 0.197732 0.980256 -0.000001 +vn 0.226299 0.974058 -0.000001 +vn 0.226299 0.974058 -0.000001 +vn 0.197732 0.980256 -0.000001 +vn 0.192774 0.980256 0.043999 +vn 0.269162 0.959884 0.078574 +vn 0.269162 0.959884 0.078574 +vn 0.192774 0.980256 0.043999 +vn 0.178150 0.980256 0.085793 +vn 0.263857 0.956156 0.127066 +vn 0.263857 0.956156 0.127066 +vn 0.178150 0.980256 0.085793 +vn 0.154593 0.980256 0.123284 +vn 0.228966 0.956156 0.182594 +vn 0.228966 0.956156 0.182594 +vn 0.154593 0.980256 0.123284 +vn 0.123284 0.980256 0.154593 +vn 0.182595 0.956156 0.228965 +vn 0.182595 0.956156 0.228965 +vn 0.123284 0.980256 0.154593 +vn 0.085793 0.980256 0.178149 +vn 0.127067 0.956156 0.263854 +vn 0.127067 0.956156 0.263854 +vn 0.085793 0.980256 0.178149 +vn 0.043999 0.980256 0.192773 +vn 0.065167 0.956156 0.285515 +vn 0.065167 0.956156 0.285515 +vn 0.043999 0.980256 0.192773 +vn 0.000000 0.980256 0.197732 +vn 0.000000 0.956156 0.292859 +vn 0.000000 0.980256 -0.197731 +vn 0.000000 0.993377 -0.114899 +vn 0.025567 0.993377 -0.112018 +vn 0.043999 0.980256 -0.192774 +vn 0.043999 0.980256 -0.192774 +vn 0.025567 0.993377 -0.112018 +vn 0.049853 0.993377 -0.103521 +vn 0.085793 0.980256 -0.178151 +vn 0.085793 0.980256 -0.178151 +vn 0.049853 0.993377 -0.103521 +vn 0.071638 0.993377 -0.089830 +vn 0.123284 0.980256 -0.154592 +vn 0.123284 0.980256 -0.154592 +vn 0.071638 0.993377 -0.089830 +vn 0.089831 0.993377 -0.071637 +vn 0.154593 0.980256 -0.123283 +vn 0.154593 0.980256 -0.123283 +vn 0.089831 0.993377 -0.071637 +vn 0.103520 0.993377 -0.049855 +vn 0.178150 0.980256 -0.085793 +vn 0.178150 0.980256 -0.085793 +vn 0.103520 0.993377 -0.049855 +vn 0.112018 0.993377 -0.025566 +vn 0.192774 0.980256 -0.043999 +vn 0.192774 0.980256 -0.043999 +vn 0.112018 0.993377 -0.025566 +vn 0.114899 0.993377 0.000000 +vn 0.197732 0.980256 -0.000001 +vn 0.197732 0.980256 -0.000001 +vn 0.114899 0.993377 0.000000 +vn 0.112018 0.993377 0.025569 +vn 0.192774 0.980256 0.043999 +vn 0.192774 0.980256 0.043999 +vn 0.112018 0.993377 0.025569 +vn 0.103520 0.993377 0.049857 +vn 0.178150 0.980256 0.085793 +vn 0.178150 0.980256 0.085793 +vn 0.103520 0.993377 0.049857 +vn 0.089831 0.993377 0.071640 +vn 0.154593 0.980256 0.123284 +vn 0.154593 0.980256 0.123284 +vn 0.089831 0.993377 0.071640 +vn 0.071638 0.993377 0.089833 +vn 0.123284 0.980256 0.154593 +vn 0.123284 0.980256 0.154593 +vn 0.071638 0.993377 0.089833 +vn 0.049853 0.993377 0.103520 +vn 0.085793 0.980256 0.178149 +vn 0.085793 0.980256 0.178149 +vn 0.049853 0.993377 0.103520 +vn 0.025567 0.993377 0.112017 +vn 0.043999 0.980256 0.192773 +vn 0.043999 0.980256 0.192773 +vn 0.025567 0.993377 0.112017 +vn 0.000000 0.993377 0.114900 +vn 0.000000 0.980256 0.197732 +vn 0.000000 0.993377 -0.114899 +vn 0.000000 1.000000 -0.000001 +vn 0.025567 0.993377 -0.112018 +vn 0.025567 0.993377 -0.112018 +vn 0.000000 1.000000 -0.000001 +vn 0.049853 0.993377 -0.103521 +vn 0.049853 0.993377 -0.103521 +vn 0.000000 1.000000 -0.000001 +vn 0.071638 0.993377 -0.089830 +vn 0.071638 0.993377 -0.089830 +vn 0.000000 1.000000 -0.000001 +vn 0.089831 0.993377 -0.071637 +vn 0.089831 0.993377 -0.071637 +vn 0.000000 1.000000 -0.000001 +vn 0.103520 0.993377 -0.049855 +vn 0.103520 0.993377 -0.049855 +vn 0.000000 1.000000 -0.000001 +vn 0.112018 0.993377 -0.025566 +vn 0.112018 0.993377 -0.025566 +vn 0.000000 1.000000 -0.000001 +vn 0.114899 0.993377 0.000000 +vn 0.114899 0.993377 0.000000 +vn 0.000000 1.000000 -0.000001 +vn 0.112018 0.993377 0.025569 +vn 0.112018 0.993377 0.025569 +vn 0.000000 1.000000 -0.000001 +vn 0.103520 0.993377 0.049857 +vn 0.103520 0.993377 0.049857 +vn 0.000000 1.000000 -0.000001 +vn 0.089831 0.993377 0.071640 +vn 0.089831 0.993377 0.071640 +vn 0.000000 1.000000 -0.000001 +vn 0.071638 0.993377 0.089833 +vn 0.071638 0.993377 0.089833 +vn 0.000000 1.000000 -0.000001 +vn 0.049853 0.993377 0.103520 +vn 0.049853 0.993377 0.103520 +vn 0.000000 1.000000 -0.000001 +vn 0.025567 0.993377 0.112017 +vn 0.025567 0.993377 0.112017 +vn 0.000000 1.000000 -0.000001 +vn 0.000000 0.993377 0.114900 +vn -0.219660 -0.159819 -0.962397 +vn -0.000001 -0.159820 -0.987146 +vn 0.000000 -0.998264 -0.058901 +vn -0.013107 -0.998264 -0.057424 +vn -0.000001 -0.159820 -0.987146 +vn -0.219660 -0.159819 -0.962397 +vn -0.219302 0.169481 -0.960824 +vn 0.000000 0.169480 -0.985534 +vn -0.219660 -0.159819 -0.962397 +vn -0.428306 -0.159820 -0.889388 +vn -0.427607 0.169481 -0.887935 +vn -0.219302 0.169481 -0.960824 +vn -0.428306 -0.159820 -0.889388 +vn -0.615476 -0.159821 -0.771782 +vn -0.614470 0.169480 -0.770521 +vn -0.427607 0.169481 -0.887935 +vn -0.615476 -0.159821 -0.771782 +vn -0.771782 -0.159821 -0.615476 +vn -0.770521 0.169480 -0.614471 +vn -0.614470 0.169480 -0.770521 +vn -0.771782 -0.159821 -0.615476 +vn -0.889388 -0.159820 -0.428307 +vn -0.887935 0.169480 -0.427607 +vn -0.770521 0.169480 -0.614471 +vn -0.889388 -0.159820 -0.428307 +vn -0.962396 -0.159820 -0.219661 +vn -0.960824 0.169480 -0.219302 +vn -0.887935 0.169480 -0.427607 +vn -0.962396 -0.159820 -0.219661 +vn -0.987146 -0.159819 0.000000 +vn -0.985533 0.169481 0.000000 +vn -0.960824 0.169480 -0.219302 +vn -0.987146 -0.159819 0.000000 +vn -0.962397 -0.159819 0.219660 +vn -0.960824 0.169481 0.219302 +vn -0.985533 0.169481 0.000000 +vn -0.962397 -0.159819 0.219660 +vn -0.889388 -0.159820 0.428306 +vn -0.887935 0.169481 0.427607 +vn -0.960824 0.169481 0.219302 +vn -0.889388 -0.159820 0.428306 +vn -0.771782 -0.159820 0.615475 +vn -0.770521 0.169480 0.614470 +vn -0.887935 0.169481 0.427607 +vn -0.771782 -0.159820 0.615475 +vn -0.615476 -0.159820 0.771781 +vn -0.614471 0.169480 0.770521 +vn -0.770521 0.169480 0.614470 +vn -0.615476 -0.159820 0.771781 +vn -0.428307 -0.159820 0.889388 +vn -0.427607 0.169480 0.887935 +vn -0.614471 0.169480 0.770521 +vn -0.428307 -0.159820 0.889388 +vn -0.219661 -0.159821 0.962396 +vn -0.219302 0.169480 0.960824 +vn -0.427607 0.169480 0.887935 +vn -0.219661 -0.159821 0.962396 +vn 0.000001 -0.159820 0.987146 +vn 0.000000 0.169480 0.985534 +vn -0.219302 0.169480 0.960824 +vn -0.013107 -0.998264 -0.057424 +vn 0.000000 -0.998264 -0.058901 +vn 0.000000 -1.000000 0.000000 +vn -0.025556 -0.998264 -0.053067 +vn -0.013107 -0.998264 -0.057424 +vn 0.000000 -1.000000 0.000000 +vn -0.036724 -0.998264 -0.046051 +vn -0.025556 -0.998264 -0.053067 +vn 0.000000 -1.000000 0.000000 +vn -0.046050 -0.998264 -0.036724 +vn -0.036724 -0.998264 -0.046051 +vn 0.000000 -1.000000 0.000000 +vn -0.053067 -0.998264 -0.025555 +vn -0.046050 -0.998264 -0.036724 +vn 0.000000 -1.000000 0.000000 +vn -0.057424 -0.998264 -0.013107 +vn -0.053067 -0.998264 -0.025555 +vn 0.000000 -1.000000 0.000000 +vn -0.058900 -0.998264 0.000000 +vn -0.057424 -0.998264 -0.013107 +vn 0.000000 -1.000000 0.000000 +vn -0.057424 -0.998264 0.013107 +vn -0.058900 -0.998264 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.053067 -0.998264 0.025556 +vn -0.057424 -0.998264 0.013107 +vn 0.000000 -1.000000 0.000000 +vn -0.046050 -0.998264 0.036725 +vn -0.053067 -0.998264 0.025556 +vn 0.000000 -1.000000 0.000000 +vn -0.036724 -0.998264 0.046051 +vn -0.046050 -0.998264 0.036725 +vn 0.000000 -1.000000 0.000000 +vn -0.025556 -0.998264 0.053067 +vn -0.036724 -0.998264 0.046051 +vn 0.000000 -1.000000 0.000000 +vn -0.013107 -0.998264 0.057424 +vn -0.025556 -0.998264 0.053067 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -0.998264 0.058900 +vn -0.013107 -0.998264 0.057424 +vn 0.000000 -1.000000 0.000000 +vn -0.000001 -0.159820 -0.987146 +vn 0.000000 0.169480 -0.985534 +vn 0.219302 0.169480 -0.960824 +vn 0.219661 -0.159822 -0.962396 +vn 0.219661 -0.159822 -0.962396 +vn 0.219302 0.169480 -0.960824 +vn 0.427607 0.169480 -0.887935 +vn 0.428307 -0.159821 -0.889388 +vn 0.428307 -0.159821 -0.889388 +vn 0.427607 0.169480 -0.887935 +vn 0.614470 0.169480 -0.770521 +vn 0.615476 -0.159821 -0.771782 +vn 0.615476 -0.159821 -0.771782 +vn 0.614470 0.169480 -0.770521 +vn 0.770521 0.169480 -0.614471 +vn 0.771782 -0.159821 -0.615475 +vn 0.771782 -0.159821 -0.615475 +vn 0.770521 0.169480 -0.614471 +vn 0.887935 0.169481 -0.427607 +vn 0.889388 -0.159821 -0.428306 +vn 0.889388 -0.159821 -0.428306 +vn 0.887935 0.169481 -0.427607 +vn 0.960824 0.169480 -0.219302 +vn 0.962396 -0.159820 -0.219661 +vn 0.962396 -0.159820 -0.219661 +vn 0.960824 0.169480 -0.219302 +vn 0.985534 0.169480 0.000000 +vn 0.987146 -0.159820 0.000000 +vn 0.987146 -0.159820 0.000000 +vn 0.985534 0.169480 0.000000 +vn 0.960824 0.169481 0.219302 +vn 0.962396 -0.159820 0.219661 +vn 0.962396 -0.159820 0.219661 +vn 0.960824 0.169481 0.219302 +vn 0.887935 0.169481 0.427607 +vn 0.889387 -0.159821 0.428308 +vn 0.889387 -0.159821 0.428308 +vn 0.887935 0.169481 0.427607 +vn 0.770521 0.169480 0.614470 +vn 0.771782 -0.159821 0.615475 +vn 0.771782 -0.159821 0.615475 +vn 0.770521 0.169480 0.614470 +vn 0.614471 0.169480 0.770521 +vn 0.615476 -0.159820 0.771781 +vn 0.615476 -0.159820 0.771781 +vn 0.614471 0.169480 0.770521 +vn 0.427607 0.169481 0.887935 +vn 0.428306 -0.159820 0.889388 +vn 0.428306 -0.159820 0.889388 +vn 0.427607 0.169481 0.887935 +vn 0.219302 0.169480 0.960824 +vn 0.219660 -0.159820 0.962397 +vn 0.219660 -0.159820 0.962397 +vn 0.219302 0.169480 0.960824 +vn 0.000000 0.169480 0.985534 +vn 0.000001 -0.159820 0.987146 +vn -0.428306 -0.159820 -0.889388 +vn -0.219660 -0.159819 -0.962397 +vn -0.013107 -0.998264 -0.057424 +vn -0.025556 -0.998264 -0.053067 +vn -0.615476 -0.159821 -0.771782 +vn -0.428306 -0.159820 -0.889388 +vn -0.025556 -0.998264 -0.053067 +vn -0.036724 -0.998264 -0.046051 +vn -0.771782 -0.159821 -0.615476 +vn -0.615476 -0.159821 -0.771782 +vn -0.036724 -0.998264 -0.046051 +vn -0.046050 -0.998264 -0.036724 +vn -0.889388 -0.159820 -0.428307 +vn -0.771782 -0.159821 -0.615476 +vn -0.046050 -0.998264 -0.036724 +vn -0.053067 -0.998264 -0.025555 +vn -0.962396 -0.159820 -0.219661 +vn -0.889388 -0.159820 -0.428307 +vn -0.053067 -0.998264 -0.025555 +vn -0.057424 -0.998264 -0.013107 +vn -0.987146 -0.159819 0.000000 +vn -0.962396 -0.159820 -0.219661 +vn -0.057424 -0.998264 -0.013107 +vn -0.058900 -0.998264 0.000000 +vn -0.962397 -0.159819 0.219660 +vn -0.987146 -0.159819 0.000000 +vn -0.058900 -0.998264 0.000000 +vn -0.057424 -0.998264 0.013107 +vn -0.889388 -0.159820 0.428306 +vn -0.962397 -0.159819 0.219660 +vn -0.057424 -0.998264 0.013107 +vn -0.053067 -0.998264 0.025556 +vn -0.771782 -0.159820 0.615475 +vn -0.889388 -0.159820 0.428306 +vn -0.053067 -0.998264 0.025556 +vn -0.046050 -0.998264 0.036725 +vn -0.615476 -0.159820 0.771781 +vn -0.771782 -0.159820 0.615475 +vn -0.046050 -0.998264 0.036725 +vn -0.036724 -0.998264 0.046051 +vn -0.428307 -0.159820 0.889388 +vn -0.615476 -0.159820 0.771781 +vn -0.036724 -0.998264 0.046051 +vn -0.025556 -0.998264 0.053067 +vn -0.219661 -0.159821 0.962396 +vn -0.428307 -0.159820 0.889388 +vn -0.025556 -0.998264 0.053067 +vn -0.013107 -0.998264 0.057424 +vn 0.000001 -0.159820 0.987146 +vn -0.219661 -0.159821 0.962396 +vn -0.013107 -0.998264 0.057424 +vn 0.000000 -0.998264 0.058900 +vn 0.219660 -0.159820 0.962397 +vn 0.000001 -0.159820 0.987146 +vn 0.000000 -0.998264 0.058900 +vn 0.013107 -0.998264 0.057424 +vn 0.000000 -0.998264 -0.058901 +vn -0.000001 -0.159820 -0.987146 +vn 0.219661 -0.159822 -0.962396 +vn 0.013107 -0.998264 -0.057424 +vn 0.219661 -0.159822 -0.962396 +vn 0.428307 -0.159821 -0.889388 +vn 0.025556 -0.998264 -0.053067 +vn 0.013107 -0.998264 -0.057424 +vn 0.428307 -0.159821 -0.889388 +vn 0.615476 -0.159821 -0.771782 +vn 0.036724 -0.998264 -0.046050 +vn 0.025556 -0.998264 -0.053067 +vn 0.615476 -0.159821 -0.771782 +vn 0.771782 -0.159821 -0.615475 +vn 0.046050 -0.998264 -0.036724 +vn 0.036724 -0.998264 -0.046050 +vn 0.771782 -0.159821 -0.615475 +vn 0.889388 -0.159821 -0.428306 +vn 0.053067 -0.998264 -0.025556 +vn 0.046050 -0.998264 -0.036724 +vn 0.889388 -0.159821 -0.428306 +vn 0.962396 -0.159820 -0.219661 +vn 0.057424 -0.998264 -0.013107 +vn 0.053067 -0.998264 -0.025556 +vn 0.962396 -0.159820 -0.219661 +vn 0.987146 -0.159820 0.000000 +vn 0.058900 -0.998264 0.000000 +vn 0.057424 -0.998264 -0.013107 +vn 0.987146 -0.159820 0.000000 +vn 0.962396 -0.159820 0.219661 +vn 0.057424 -0.998264 0.013106 +vn 0.058900 -0.998264 0.000000 +vn 0.962396 -0.159820 0.219661 +vn 0.889387 -0.159821 0.428308 +vn 0.053067 -0.998264 0.025556 +vn 0.057424 -0.998264 0.013106 +vn 0.889387 -0.159821 0.428308 +vn 0.771782 -0.159821 0.615475 +vn 0.046050 -0.998264 0.036724 +vn 0.053067 -0.998264 0.025556 +vn 0.771782 -0.159821 0.615475 +vn 0.615476 -0.159820 0.771781 +vn 0.036724 -0.998264 0.046050 +vn 0.046050 -0.998264 0.036724 +vn 0.615476 -0.159820 0.771781 +vn 0.428306 -0.159820 0.889388 +vn 0.025556 -0.998264 0.053067 +vn 0.036724 -0.998264 0.046050 +vn 0.428306 -0.159820 0.889388 +vn 0.219660 -0.159820 0.962397 +vn 0.013107 -0.998264 0.057424 +vn 0.025556 -0.998264 0.053067 +vn 0.013107 -0.998264 -0.057424 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -0.998264 -0.058901 +vn 0.025556 -0.998264 -0.053067 +vn 0.000000 -1.000000 0.000000 +vn 0.013107 -0.998264 -0.057424 +vn 0.036724 -0.998264 -0.046050 +vn 0.000000 -1.000000 0.000000 +vn 0.025556 -0.998264 -0.053067 +vn 0.046050 -0.998264 -0.036724 +vn 0.000000 -1.000000 0.000000 +vn 0.036724 -0.998264 -0.046050 +vn 0.053067 -0.998264 -0.025556 +vn 0.000000 -1.000000 0.000000 +vn 0.046050 -0.998264 -0.036724 +vn 0.057424 -0.998264 -0.013107 +vn 0.000000 -1.000000 0.000000 +vn 0.053067 -0.998264 -0.025556 +vn 0.058900 -0.998264 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.057424 -0.998264 -0.013107 +vn 0.057424 -0.998264 0.013106 +vn 0.000000 -1.000000 0.000000 +vn 0.058900 -0.998264 0.000000 +vn 0.053067 -0.998264 0.025556 +vn 0.000000 -1.000000 0.000000 +vn 0.057424 -0.998264 0.013106 +vn 0.046050 -0.998264 0.036724 +vn 0.000000 -1.000000 0.000000 +vn 0.053067 -0.998264 0.025556 +vn 0.036724 -0.998264 0.046050 +vn 0.000000 -1.000000 0.000000 +vn 0.046050 -0.998264 0.036724 +vn 0.025556 -0.998264 0.053067 +vn 0.000000 -1.000000 0.000000 +vn 0.036724 -0.998264 0.046050 +vn 0.013107 -0.998264 0.057424 +vn 0.000000 -1.000000 0.000000 +vn 0.025556 -0.998264 0.053067 +vn 0.000000 -0.998264 0.058900 +vn 0.000000 -1.000000 0.000000 +vn 0.013107 -0.998264 0.057424 +vn -0.797489 -0.575221 0.182022 +vn -0.817999 -0.575220 0.000000 +vn -0.593054 -0.805163 0.000000 +vn -0.578184 -0.805163 0.131967 +vn -0.736991 -0.575221 0.354916 +vn -0.797489 -0.575221 0.182022 +vn -0.578184 -0.805163 0.131967 +vn -0.534323 -0.805163 0.257316 +vn -0.639537 -0.575221 0.510013 +vn -0.736991 -0.575221 0.354916 +vn -0.534323 -0.805163 0.257316 +vn -0.463668 -0.805164 0.369762 +vn -0.510013 -0.575221 0.639536 +vn -0.639537 -0.575221 0.510013 +vn -0.463668 -0.805164 0.369762 +vn -0.369762 -0.805164 0.463668 +vn -0.354916 -0.575221 0.736991 +vn -0.510013 -0.575221 0.639536 +vn -0.369762 -0.805164 0.463668 +vn -0.257316 -0.805164 0.534322 +vn -0.182022 -0.575221 0.797489 +vn -0.354916 -0.575221 0.736991 +vn -0.257316 -0.805164 0.534322 +vn -0.131967 -0.805164 0.578184 +vn 0.000000 -0.575221 0.817998 +vn -0.182022 -0.575221 0.797489 +vn -0.131967 -0.805164 0.578184 +vn 0.000000 -0.805163 0.593053 +vn 0.182022 -0.575221 0.797489 +vn 0.000000 -0.575221 0.817998 +vn 0.000000 -0.805163 0.593053 +vn 0.131967 -0.805163 0.578184 +vn 0.354916 -0.575221 0.736991 +vn 0.182022 -0.575221 0.797489 +vn 0.131967 -0.805163 0.578184 +vn 0.257316 -0.805164 0.534322 +vn 0.510014 -0.575221 0.639537 +vn 0.354916 -0.575221 0.736991 +vn 0.257316 -0.805164 0.534322 +vn 0.369763 -0.805163 0.463668 +vn 0.639537 -0.575221 0.510013 +vn 0.510014 -0.575221 0.639537 +vn 0.369763 -0.805163 0.463668 +vn 0.463668 -0.805163 0.369762 +vn 0.736991 -0.575221 0.354916 +vn 0.639537 -0.575221 0.510013 +vn 0.463668 -0.805163 0.369762 +vn 0.534323 -0.805163 0.257316 +vn 0.797489 -0.575221 0.182022 +vn 0.736991 -0.575221 0.354916 +vn 0.534323 -0.805163 0.257316 +vn 0.578184 -0.805163 0.131967 +vn 0.817999 -0.575220 0.000000 +vn 0.797489 -0.575221 0.182022 +vn 0.578184 -0.805163 0.131967 +vn 0.593054 -0.805163 0.000000 +vn 0.797490 -0.575220 -0.182022 +vn 0.817999 -0.575220 0.000000 +vn 0.593054 -0.805163 0.000000 +vn 0.578184 -0.805163 -0.131967 +vn 0.736991 -0.575221 -0.354916 +vn 0.797490 -0.575220 -0.182022 +vn 0.578184 -0.805163 -0.131967 +vn 0.534322 -0.805164 -0.257316 +vn 0.639537 -0.575221 -0.510013 +vn 0.736991 -0.575221 -0.354916 +vn 0.534322 -0.805164 -0.257316 +vn 0.463667 -0.805164 -0.369762 +vn 0.510013 -0.575221 -0.639537 +vn 0.639537 -0.575221 -0.510013 +vn 0.463667 -0.805164 -0.369762 +vn 0.369762 -0.805164 -0.463667 +vn 0.354916 -0.575221 -0.736991 +vn 0.510013 -0.575221 -0.639537 +vn 0.369762 -0.805164 -0.463667 +vn 0.257316 -0.805164 -0.534322 +vn 0.182022 -0.575221 -0.797489 +vn 0.354916 -0.575221 -0.736991 +vn 0.257316 -0.805164 -0.534322 +vn 0.131967 -0.805164 -0.578184 +vn 0.000000 -0.575221 -0.817998 +vn 0.182022 -0.575221 -0.797489 +vn 0.131967 -0.805164 -0.578184 +vn 0.000000 -0.805163 -0.593053 +vn -0.182022 -0.575221 -0.797489 +vn 0.000000 -0.575221 -0.817998 +vn 0.000000 -0.805163 -0.593053 +vn -0.131967 -0.805163 -0.578184 +vn -0.354916 -0.575221 -0.736991 +vn -0.182022 -0.575221 -0.797489 +vn -0.131967 -0.805163 -0.578184 +vn -0.257316 -0.805164 -0.534322 +vn -0.510013 -0.575221 -0.639537 +vn -0.354916 -0.575221 -0.736991 +vn -0.257316 -0.805164 -0.534322 +vn -0.369763 -0.805164 -0.463667 +vn -0.639537 -0.575221 -0.510013 +vn -0.510013 -0.575221 -0.639537 +vn -0.369763 -0.805164 -0.463667 +vn -0.463667 -0.805164 -0.369762 +vn -0.736991 -0.575221 -0.354916 +vn -0.639537 -0.575221 -0.510013 +vn -0.463667 -0.805164 -0.369762 +vn -0.534322 -0.805164 -0.257316 +vn -0.797490 -0.575220 -0.182022 +vn -0.736991 -0.575221 -0.354916 +vn -0.534322 -0.805164 -0.257316 +vn -0.578184 -0.805163 -0.131967 +vn -0.817999 -0.575220 0.000000 +vn -0.797490 -0.575220 -0.182022 +vn -0.578184 -0.805163 -0.131967 +vn -0.593054 -0.805163 0.000000 +vn -0.315710 -0.946116 0.072059 +vn -0.323827 -0.946116 0.000000 +vn -0.036912 -0.999319 -0.000002 +vn -0.034527 -0.999373 0.007881 +vn -0.291758 -0.946116 0.140504 +vn -0.315710 -0.946116 0.072059 +vn -0.034527 -0.999373 0.007881 +vn -0.035182 -0.999237 0.016944 +vn -0.024746 -0.999499 0.019734 +vn -0.253177 -0.946117 0.201902 +vn -0.291758 -0.946116 0.140504 +vn -0.035182 -0.999237 0.016944 +vn -0.201902 -0.946117 0.253177 +vn -0.253177 -0.946117 0.201902 +vn -0.024746 -0.999499 0.019734 +vn -0.019694 -0.999501 0.024697 +vn -0.140502 -0.946117 0.291757 +vn -0.201902 -0.946117 0.253177 +vn -0.019694 -0.999501 0.024697 +vn -0.010578 -0.999703 0.021965 +vn -0.072058 -0.946117 0.315706 +vn -0.140502 -0.946117 0.291757 +vn -0.010578 -0.999703 0.021965 +vn -0.004293 -0.999814 0.018813 +vn 0.000000 -0.946117 0.323825 +vn -0.072058 -0.946117 0.315706 +vn -0.004293 -0.999814 0.018813 +vn 0.000000 -0.999814 0.019289 +vn 0.072058 -0.946117 0.315707 +vn 0.000000 -0.946117 0.323825 +vn 0.000000 -0.999814 0.019289 +vn 0.004293 -0.999814 0.018813 +vn 0.004293 -0.999814 0.018813 +vn 0.010578 -0.999703 0.021965 +vn 0.140502 -0.946117 0.291757 +vn 0.072058 -0.946117 0.315707 +vn 0.010578 -0.999703 0.021965 +vn 0.019694 -0.999501 0.024697 +vn 0.201902 -0.946117 0.253177 +vn 0.140502 -0.946117 0.291757 +vn 0.019694 -0.999501 0.024697 +vn 0.024746 -0.999499 0.019734 +vn 0.253177 -0.946117 0.201902 +vn 0.201902 -0.946117 0.253177 +vn 0.291758 -0.946116 0.140504 +vn 0.253177 -0.946117 0.201902 +vn 0.024746 -0.999499 0.019734 +vn 0.035182 -0.999237 0.016944 +vn 0.035182 -0.999237 0.016944 +vn 0.034527 -0.999373 0.007881 +vn 0.315710 -0.946116 0.072059 +vn 0.291758 -0.946116 0.140504 +vn 0.034527 -0.999373 0.007881 +vn 0.036912 -0.999319 -0.000001 +vn 0.323827 -0.946116 0.000000 +vn 0.315710 -0.946116 0.072059 +vn 0.036912 -0.999319 -0.000001 +vn 0.034527 -0.999373 -0.007881 +vn 0.315706 -0.946117 -0.072058 +vn 0.323827 -0.946116 0.000000 +vn 0.034527 -0.999373 -0.007881 +vn 0.035182 -0.999237 -0.016943 +vn 0.291757 -0.946117 -0.140503 +vn 0.315706 -0.946117 -0.072058 +vn 0.035182 -0.999237 -0.016943 +vn 0.024746 -0.999499 -0.019735 +vn 0.253177 -0.946117 -0.201902 +vn 0.291757 -0.946117 -0.140503 +vn 0.024746 -0.999499 -0.019735 +vn 0.019695 -0.999501 -0.024697 +vn 0.201902 -0.946117 -0.253178 +vn 0.253177 -0.946117 -0.201902 +vn 0.019695 -0.999501 -0.024697 +vn 0.010578 -0.999703 -0.021965 +vn 0.140503 -0.946117 -0.291757 +vn 0.201902 -0.946117 -0.253178 +vn 0.010578 -0.999703 -0.021965 +vn 0.004294 -0.999814 -0.018813 +vn 0.072058 -0.946117 -0.315707 +vn 0.140503 -0.946117 -0.291757 +vn 0.004294 -0.999814 -0.018813 +vn 0.000000 -0.999814 -0.019289 +vn 0.000000 -0.946117 -0.323826 +vn 0.072058 -0.946117 -0.315707 +vn -0.072058 -0.946117 -0.315707 +vn 0.000000 -0.946117 -0.323826 +vn 0.000000 -0.999814 -0.019289 +vn -0.004294 -0.999814 -0.018813 +vn -0.140503 -0.946117 -0.291757 +vn -0.072058 -0.946117 -0.315707 +vn -0.004294 -0.999814 -0.018813 +vn -0.010578 -0.999703 -0.021965 +vn -0.201902 -0.946117 -0.253178 +vn -0.140503 -0.946117 -0.291757 +vn -0.010578 -0.999703 -0.021965 +vn -0.019695 -0.999501 -0.024697 +vn -0.253177 -0.946117 -0.201902 +vn -0.201902 -0.946117 -0.253178 +vn -0.019695 -0.999501 -0.024697 +vn -0.024746 -0.999499 -0.019735 +vn -0.291757 -0.946117 -0.140503 +vn -0.253177 -0.946117 -0.201902 +vn -0.024746 -0.999499 -0.019735 +vn -0.035182 -0.999237 -0.016943 +vn -0.315706 -0.946117 -0.072058 +vn -0.291757 -0.946117 -0.140503 +vn -0.035182 -0.999237 -0.016943 +vn -0.034527 -0.999373 -0.007881 +vn -0.323827 -0.946116 0.000000 +vn -0.315706 -0.946117 -0.072058 +vn -0.034527 -0.999373 -0.007881 +vn -0.036912 -0.999319 -0.000002 +vn -0.000001 -1.000000 -0.000002 +vn 0.000001 -1.000000 0.000001 +vn -0.034527 -0.999373 0.007881 +vn -0.036912 -0.999319 -0.000002 +vn 0.000001 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn -0.035182 -0.999237 0.016944 +vn -0.034527 -0.999373 0.007881 +vn -0.019694 -0.999501 0.024697 +vn 0.000000 -1.000000 0.000001 +vn -0.010578 -0.999703 0.021965 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn -0.004293 -0.999814 0.018813 +vn -0.010578 -0.999703 0.021965 +vn 0.000000 -1.000000 0.000001 +vn 0.000002 -1.000000 0.000000 +vn 0.000000 -0.999814 0.019289 +vn -0.004293 -0.999814 0.018813 +vn 0.000002 -1.000000 0.000000 +vn 0.000003 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -0.999814 0.019289 +vn 0.000002 -1.000000 0.000000 +vn 0.000000 -1.000000 -0.000001 +vn -0.004294 -0.999814 -0.018813 +vn 0.000000 -0.999814 -0.019289 +vn 0.000000 -1.000000 -0.000001 +vn 0.000000 -1.000000 -0.000001 +vn -0.010578 -0.999703 -0.021965 +vn -0.004294 -0.999814 -0.018813 +vn -0.024746 -0.999499 -0.019735 +vn -0.000001 -1.000000 -0.000001 +vn -0.035182 -0.999237 -0.016943 +vn -0.000001 -1.000000 -0.000001 +vn -0.000001 -1.000000 -0.000001 +vn -0.034527 -0.999373 -0.007881 +vn -0.035182 -0.999237 -0.016943 +vn -0.000001 -1.000000 -0.000001 +vn -0.000001 -1.000000 -0.000002 +vn -0.036912 -0.999319 -0.000002 +vn -0.034527 -0.999373 -0.007881 +vn 0.000003 -1.000000 0.000000 +vn 0.000002 -1.000000 0.000000 +vn 0.000000 -0.999814 -0.019289 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000001 +vn -0.024746 -0.999499 0.019734 +vn -0.035182 -0.999237 0.016944 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn -0.019694 -0.999501 0.024697 +vn -0.024746 -0.999499 0.019734 +vn 0.000000 -1.000000 -0.000001 +vn -0.019695 -0.999501 -0.024697 +vn -0.010578 -0.999703 -0.021965 +vn 0.000000 -1.000000 -0.000001 +vn -0.000001 -1.000000 -0.000001 +vn -0.024746 -0.999499 -0.019735 +vn -0.019695 -0.999501 -0.024697 +vn 0.000001 -1.000000 -0.000002 +vn 0.036912 -0.999319 -0.000001 +vn 0.034527 -0.999373 0.007881 +vn -0.000001 -1.000000 0.000001 +vn -0.000001 -1.000000 0.000001 +vn 0.034527 -0.999373 0.007881 +vn 0.035182 -0.999237 0.016944 +vn 0.000000 -1.000000 0.000001 +vn 0.019694 -0.999501 0.024697 +vn 0.010578 -0.999703 0.021965 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn 0.010578 -0.999703 0.021965 +vn 0.004293 -0.999814 0.018813 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn 0.004293 -0.999814 0.018813 +vn 0.000000 -0.999814 0.019289 +vn -0.000002 -1.000000 0.000000 +vn -0.000002 -1.000000 0.000000 +vn 0.000000 -0.999814 0.019289 +vn 0.000000 -1.000000 0.000000 +vn -0.000003 -1.000000 0.000000 +vn -0.000002 -1.000000 0.000000 +vn 0.000000 -0.999814 -0.019289 +vn 0.004294 -0.999814 -0.018813 +vn 0.000000 -1.000000 -0.000001 +vn 0.000000 -1.000000 -0.000001 +vn 0.004294 -0.999814 -0.018813 +vn 0.010578 -0.999703 -0.021965 +vn 0.000000 -1.000000 -0.000001 +vn 0.024746 -0.999499 -0.019735 +vn 0.035182 -0.999237 -0.016943 +vn 0.000001 -1.000000 -0.000001 +vn 0.000001 -1.000000 -0.000001 +vn 0.035182 -0.999237 -0.016943 +vn 0.034527 -0.999373 -0.007881 +vn 0.000001 -1.000000 0.000000 +vn 0.000001 -1.000000 0.000000 +vn 0.034527 -0.999373 -0.007881 +vn 0.036912 -0.999319 -0.000001 +vn 0.000001 -1.000000 -0.000002 +vn -0.000003 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -0.999814 -0.019289 +vn -0.000002 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000001 +vn 0.035182 -0.999237 0.016944 +vn 0.024746 -0.999499 0.019734 +vn 0.000000 -1.000000 0.000001 +vn 0.024746 -0.999499 0.019734 +vn 0.019694 -0.999501 0.024697 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 -0.000001 +vn 0.010578 -0.999703 -0.021965 +vn 0.019695 -0.999501 -0.024697 +vn 0.000000 -1.000000 -0.000001 +vn 0.019695 -0.999501 -0.024697 +vn 0.024746 -0.999499 -0.019735 +vn 0.000001 -1.000000 -0.000001 +vn 0.004816 0.013405 -0.999899 +vn -0.020734 0.046316 -0.998712 +vn -0.614803 0.388649 -0.686272 +vn -0.606408 0.366126 -0.705848 +vn 0.618018 -0.340438 -0.708629 +vn 0.604569 -0.314879 -0.731674 +vn -0.020734 0.046316 -0.998712 +vn 0.004816 0.013405 -0.999899 +vn 0.873073 -0.487589 0.000000 +vn 0.879862 -0.475229 0.000000 +vn 0.604569 -0.314879 -0.731674 +vn 0.618018 -0.340438 -0.708629 +vn 0.618018 -0.340438 0.708629 +vn 0.604569 -0.314878 0.731675 +vn 0.879862 -0.475229 0.000000 +vn 0.873073 -0.487589 0.000000 +vn -0.020736 0.046317 0.998712 +vn 0.604569 -0.314878 0.731675 +vn 0.618018 -0.340438 0.708629 +vn 0.004812 0.013407 0.999899 +vn -0.606408 0.366126 0.705848 +vn -0.614803 0.388649 0.686272 +vn -0.020736 0.046317 0.998712 +vn 0.004812 0.013407 0.999899 +vn -0.859035 0.511917 -0.000001 +vn -0.851492 0.524367 0.000000 +vn -0.614803 0.388649 0.686272 +vn -0.606408 0.366126 0.705848 +vn -0.606408 0.366126 -0.705848 +vn -0.614803 0.388649 -0.686272 +vn -0.851492 0.524367 0.000000 +vn -0.859035 0.511917 -0.000001 +vn 0.000000 -1.000000 0.000001 +vn 0.499517 -0.043951 0.865189 +vn 0.865193 -0.043951 0.499509 +vn -0.000001 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -0.043951 0.999034 +vn 0.499517 -0.043951 0.865189 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn -0.499517 -0.043951 0.865189 +vn 0.000000 -0.043951 0.999034 +vn 0.000000 -1.000000 0.000001 +vn -0.000002 -1.000000 0.000000 +vn -0.865193 -0.043950 0.499509 +vn -0.499517 -0.043951 0.865189 +vn 0.000000 -1.000000 0.000001 +vn -0.000003 -1.000000 0.000000 +vn -0.999034 -0.043950 0.000000 +vn -0.865193 -0.043950 0.499509 +vn -0.000002 -1.000000 0.000000 +vn 0.865193 -0.043951 0.499509 +vn 0.999034 -0.043950 0.000000 +vn 0.000001 -1.000000 -0.000002 +vn -0.000001 -1.000000 0.000001 +vn -0.000002 -1.000000 0.000000 +vn -0.865189 -0.043950 -0.499517 +vn -0.999034 -0.043950 0.000000 +vn -0.000003 -1.000000 0.000000 +vn 0.000000 -1.000000 -0.000001 +vn -0.499517 -0.043951 -0.865189 +vn -0.865189 -0.043950 -0.499517 +vn -0.000002 -1.000000 0.000000 +vn 0.000000 -1.000000 -0.000001 +vn 0.000000 -0.043951 -0.999034 +vn -0.499517 -0.043951 -0.865189 +vn 0.000000 -1.000000 -0.000001 +vn 0.000001 -1.000000 -0.000001 +vn 0.499517 -0.043951 -0.865189 +vn 0.000000 -0.043951 -0.999034 +vn 0.000000 -1.000000 -0.000001 +vn 0.000001 -1.000000 0.000000 +vn 0.865189 -0.043950 -0.499517 +vn 0.499517 -0.043951 -0.865189 +vn 0.000001 -1.000000 -0.000001 +vn 0.000001 -1.000000 -0.000002 +vn 0.999034 -0.043950 0.000000 +vn 0.865189 -0.043950 -0.499517 +vn 0.000001 -1.000000 0.000000 +vn -0.499985 0.007713 -0.866000 +vn -0.499985 -0.007713 -0.866000 +vn -0.866000 -0.007713 -0.499985 +vn -0.866000 0.007713 -0.499985 +vn 0.000000 0.007713 -0.999970 +vn 0.000000 -0.007713 -0.999970 +vn -0.499985 -0.007713 -0.866000 +vn -0.499985 0.007713 -0.866000 +vn 0.499985 0.007713 -0.866000 +vn 0.499985 -0.007713 -0.866000 +vn 0.000000 -0.007713 -0.999970 +vn 0.000000 0.007713 -0.999970 +vn 0.866000 0.007713 -0.499985 +vn 0.866000 -0.007713 -0.499985 +vn 0.499985 -0.007713 -0.866000 +vn 0.499985 0.007713 -0.866000 +vn 0.999970 0.007713 0.000000 +vn 0.999970 -0.007713 0.000000 +vn 0.866000 -0.007713 -0.499985 +vn 0.866000 0.007713 -0.499985 +vn 0.866000 0.007713 0.499985 +vn 0.866000 -0.007713 0.499985 +vn 0.999970 -0.007713 0.000000 +vn 0.999970 0.007713 0.000000 +vn 0.499985 0.007713 0.866000 +vn 0.499985 -0.007713 0.866000 +vn 0.866000 -0.007713 0.499985 +vn 0.866000 0.007713 0.499985 +vn 0.000000 0.007713 0.999970 +vn 0.000000 -0.007713 0.999970 +vn 0.499985 -0.007713 0.866000 +vn 0.499985 0.007713 0.866000 +vn -0.499985 0.007713 0.866000 +vn -0.499985 -0.007713 0.866000 +vn 0.000000 -0.007713 0.999970 +vn 0.000000 0.007713 0.999970 +vn -0.866000 0.007713 0.499985 +vn -0.866000 -0.007713 0.499985 +vn -0.499985 -0.007713 0.866000 +vn -0.499985 0.007713 0.866000 +vn -0.999970 0.007713 0.000000 +vn -0.999970 -0.007713 0.000000 +vn -0.866000 -0.007713 0.499985 +vn -0.866000 0.007713 0.499985 +vn -0.866000 0.007713 -0.499985 +vn -0.866000 -0.007713 -0.499985 +vn -0.999970 -0.007713 0.000000 +vn -0.999970 0.007713 0.000000 +vn -0.614803 0.388649 0.686272 +vn 0.287051 0.954669 0.078798 +vn 0.330416 0.939272 0.092697 +vn -0.020736 0.046317 0.998712 +vn -0.851492 0.524367 0.000000 +vn 0.202745 0.979232 0.000000 +vn 0.287051 0.954669 0.078798 +vn -0.614803 0.388649 0.686272 +vn -0.614803 0.388649 -0.686272 +vn 0.287051 0.954669 -0.078798 +vn 0.202745 0.979232 0.000000 +vn -0.851492 0.524367 0.000000 +vn -0.020734 0.046316 -0.998712 +vn 0.330417 0.939272 -0.092697 +vn 0.287051 0.954669 -0.078798 +vn -0.614803 0.388649 -0.686272 +vn 0.604569 -0.314879 -0.731674 +vn 0.383211 0.922561 -0.045061 +vn 0.330417 0.939272 -0.092697 +vn -0.020734 0.046316 -0.998712 +vn 0.879862 -0.475229 0.000000 +vn 0.388183 0.921582 0.000002 +vn 0.383211 0.922561 -0.045061 +vn 0.604569 -0.314879 -0.731674 +vn 0.604569 -0.314878 0.731675 +vn 0.383211 0.922561 0.045064 +vn 0.388183 0.921582 0.000002 +vn 0.879862 -0.475229 0.000000 +vn 0.330416 0.939272 0.092697 +vn 0.383211 0.922561 0.045064 +vn 0.604569 -0.314878 0.731675 +vn -0.020736 0.046317 0.998712 +vn 0.000000 -0.013768 0.999905 +vn 0.499953 -0.013768 0.865943 +vn 0.499517 -0.043951 0.865189 +vn 0.000000 -0.043951 0.999034 +vn -0.499953 -0.013768 0.865943 +vn 0.000000 -0.013768 0.999905 +vn 0.000000 -0.043951 0.999034 +vn -0.499517 -0.043951 0.865189 +vn -0.865948 -0.013768 0.499945 +vn -0.499953 -0.013768 0.865943 +vn -0.499517 -0.043951 0.865189 +vn -0.865193 -0.043950 0.499509 +vn -0.999905 -0.013768 0.000000 +vn -0.865948 -0.013768 0.499945 +vn -0.865193 -0.043950 0.499509 +vn -0.999034 -0.043950 0.000000 +vn -0.865943 -0.013768 -0.499953 +vn -0.999905 -0.013768 0.000000 +vn -0.999034 -0.043950 0.000000 +vn -0.865189 -0.043950 -0.499517 +vn -0.499953 -0.013768 -0.865943 +vn -0.865943 -0.013768 -0.499953 +vn -0.865189 -0.043950 -0.499517 +vn -0.499517 -0.043951 -0.865189 +vn 0.000000 -0.013768 -0.999905 +vn -0.499953 -0.013768 -0.865943 +vn -0.499517 -0.043951 -0.865189 +vn 0.000000 -0.043951 -0.999034 +vn 0.499953 -0.013768 -0.865943 +vn 0.000000 -0.013768 -0.999905 +vn 0.000000 -0.043951 -0.999034 +vn 0.499517 -0.043951 -0.865189 +vn 0.865943 -0.013768 -0.499953 +vn 0.499953 -0.013768 -0.865943 +vn 0.499517 -0.043951 -0.865189 +vn 0.865189 -0.043950 -0.499517 +vn 0.999905 -0.013768 0.000000 +vn 0.865943 -0.013768 -0.499953 +vn 0.865189 -0.043950 -0.499517 +vn 0.999034 -0.043950 0.000000 +vn 0.865948 -0.013768 0.499945 +vn 0.999905 -0.013768 0.000000 +vn 0.999034 -0.043950 0.000000 +vn 0.865193 -0.043951 0.499509 +vn 0.499953 -0.013768 0.865943 +vn 0.865948 -0.013768 0.499945 +vn 0.865193 -0.043951 0.499509 +vn 0.499517 -0.043951 0.865189 +vn -0.001996 0.008585 -0.999961 +vn 0.609220 0.361371 -0.705876 +vn 0.614802 0.388648 -0.686273 +vn 0.020735 0.046316 -0.998712 +vn -0.615214 -0.345341 -0.708697 +vn -0.001996 0.008585 -0.999961 +vn 0.020735 0.046316 -0.998712 +vn -0.604568 -0.314878 -0.731675 +vn -0.870284 -0.492551 0.000000 +vn -0.615214 -0.345341 -0.708697 +vn -0.604568 -0.314878 -0.731675 +vn -0.879863 -0.475228 0.000000 +vn -0.615213 -0.345341 0.708697 +vn -0.870284 -0.492551 0.000000 +vn -0.879863 -0.475228 0.000000 +vn -0.604568 -0.314878 0.731676 +vn 0.020736 0.046317 0.998712 +vn -0.001995 0.008586 0.999961 +vn -0.615213 -0.345341 0.708697 +vn -0.604568 -0.314878 0.731676 +vn 0.609221 0.361371 0.705876 +vn -0.001995 0.008586 0.999961 +vn 0.020736 0.046317 0.998712 +vn 0.614803 0.388649 0.686272 +vn 0.861840 0.507181 0.000000 +vn 0.609221 0.361371 0.705876 +vn 0.614803 0.388649 0.686272 +vn 0.851492 0.524367 0.000000 +vn 0.609220 0.361371 -0.705876 +vn 0.861840 0.507181 0.000000 +vn 0.851492 0.524367 0.000000 +vn 0.614802 0.388648 -0.686273 +vn 0.000000 -1.000000 0.000001 +vn 0.000001 -1.000000 0.000001 +vn -0.865193 -0.043950 0.499509 +vn -0.499517 -0.043951 0.865189 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn -0.499517 -0.043951 0.865189 +vn 0.000000 -0.043951 0.999034 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +vn 0.000000 -0.043951 0.999034 +vn 0.499517 -0.043951 0.865189 +vn 0.000002 -1.000000 0.000000 +vn 0.000000 -1.000000 0.000001 +vn 0.499517 -0.043951 0.865189 +vn 0.865193 -0.043950 0.499509 +vn 0.000003 -1.000000 0.000000 +vn 0.000002 -1.000000 0.000000 +vn 0.865193 -0.043950 0.499509 +vn 0.999034 -0.043950 0.000000 +vn -0.865193 -0.043950 0.499509 +vn 0.000001 -1.000000 0.000001 +vn -0.000001 -1.000000 -0.000002 +vn -0.999034 -0.043950 0.000000 +vn 0.000002 -1.000000 0.000000 +vn 0.000003 -1.000000 0.000000 +vn 0.999034 -0.043950 0.000000 +vn 0.865189 -0.043950 -0.499517 +vn 0.000000 -1.000000 -0.000001 +vn 0.000002 -1.000000 0.000000 +vn 0.865189 -0.043950 -0.499517 +vn 0.499517 -0.043951 -0.865189 +vn 0.000000 -1.000000 -0.000001 +vn 0.000000 -1.000000 -0.000001 +vn 0.499517 -0.043951 -0.865189 +vn 0.000000 -0.043951 -0.999034 +vn -0.000001 -1.000000 -0.000001 +vn 0.000000 -1.000000 -0.000001 +vn 0.000000 -0.043951 -0.999034 +vn -0.499517 -0.043951 -0.865189 +vn -0.000001 -1.000000 -0.000001 +vn -0.000001 -1.000000 -0.000001 +vn -0.499517 -0.043951 -0.865189 +vn -0.865189 -0.043950 -0.499517 +vn -0.000001 -1.000000 -0.000002 +vn -0.000001 -1.000000 -0.000001 +vn -0.865189 -0.043950 -0.499517 +vn -0.999034 -0.043950 0.000000 +vn 0.614803 0.388649 0.686272 +vn 0.020736 0.046317 0.998712 +vn -0.330416 0.939272 0.092697 +vn -0.287051 0.954669 0.078798 +vn 0.851492 0.524367 0.000000 +vn 0.614803 0.388649 0.686272 +vn -0.287051 0.954669 0.078798 +vn -0.202745 0.979232 0.000004 +vn 0.614802 0.388648 -0.686273 +vn 0.851492 0.524367 0.000000 +vn -0.202745 0.979232 0.000004 +vn -0.287051 0.954669 -0.078800 +vn 0.020735 0.046316 -0.998712 +vn 0.614802 0.388648 -0.686273 +vn -0.287051 0.954669 -0.078800 +vn -0.330416 0.939272 -0.092702 +vn -0.604568 -0.314878 -0.731675 +vn 0.020735 0.046316 -0.998712 +vn -0.330416 0.939272 -0.092702 +vn -0.383212 0.922560 -0.045065 +vn -0.879863 -0.475228 0.000000 +vn -0.604568 -0.314878 -0.731675 +vn -0.383212 0.922560 -0.045065 +vn -0.388183 0.921582 0.000000 +vn -0.604568 -0.314878 0.731676 +vn -0.879863 -0.475228 0.000000 +vn -0.388183 0.921582 0.000000 +vn -0.383210 0.922561 0.045064 +vn -0.330416 0.939272 0.092697 +vn 0.020736 0.046317 0.998712 +vn -0.604568 -0.314878 0.731676 +vn -0.383210 0.922561 0.045064 +vn 0.000000 -0.013768 0.999905 +vn 0.000000 -0.043951 0.999034 +vn -0.499517 -0.043951 0.865189 +vn -0.499953 -0.013768 0.865943 +vn 0.499953 -0.013768 0.865943 +vn 0.499517 -0.043951 0.865189 +vn 0.000000 -0.043951 0.999034 +vn 0.000000 -0.013768 0.999905 +vn 0.865948 -0.013768 0.499945 +vn 0.865193 -0.043950 0.499509 +vn 0.499517 -0.043951 0.865189 +vn 0.499953 -0.013768 0.865943 +vn 0.999905 -0.013768 0.000000 +vn 0.999034 -0.043950 0.000000 +vn 0.865193 -0.043950 0.499509 +vn 0.865948 -0.013768 0.499945 +vn 0.865943 -0.013768 -0.499953 +vn 0.865189 -0.043950 -0.499517 +vn 0.999034 -0.043950 0.000000 +vn 0.999905 -0.013768 0.000000 +vn 0.499953 -0.013768 -0.865943 +vn 0.499517 -0.043951 -0.865189 +vn 0.865189 -0.043950 -0.499517 +vn 0.865943 -0.013768 -0.499953 +vn 0.000000 -0.013768 -0.999905 +vn 0.000000 -0.043951 -0.999034 +vn 0.499517 -0.043951 -0.865189 +vn 0.499953 -0.013768 -0.865943 +vn -0.499953 -0.013768 -0.865943 +vn -0.499517 -0.043951 -0.865189 +vn 0.000000 -0.043951 -0.999034 +vn 0.000000 -0.013768 -0.999905 +vn -0.865943 -0.013768 -0.499953 +vn -0.865189 -0.043950 -0.499517 +vn -0.499517 -0.043951 -0.865189 +vn -0.499953 -0.013768 -0.865943 +vn -0.999905 -0.013768 0.000000 +vn -0.999034 -0.043950 0.000000 +vn -0.865189 -0.043950 -0.499517 +vn -0.865943 -0.013768 -0.499953 +vn -0.865948 -0.013768 0.499945 +vn -0.865193 -0.043950 0.499509 +vn -0.999034 -0.043950 0.000000 +vn -0.999905 -0.013768 0.000000 +vn -0.499953 -0.013768 0.865943 +vn -0.499517 -0.043951 0.865189 +vn -0.865193 -0.043950 0.499509 +vn -0.865948 -0.013768 0.499945 +vn -0.615214 -0.345341 -0.708697 +vn -0.768903 0.058373 -0.636696 +vn -0.217514 0.376642 -0.900460 +vn -0.001996 0.008585 -0.999961 +vn -0.001996 0.008585 -0.999961 +vn -0.217514 0.376642 -0.900460 +vn 0.333953 0.694994 -0.636757 +vn 0.609220 0.361371 -0.705876 +vn 0.609220 0.361371 -0.705876 +vn 0.333953 0.694994 -0.636757 +vn 0.562362 0.826891 0.000002 +vn 0.861840 0.507181 0.000000 +vn 0.861840 0.507181 0.000000 +vn 0.562362 0.826891 0.000002 +vn 0.333954 0.694992 0.636759 +vn 0.609221 0.361371 0.705876 +vn 0.609221 0.361371 0.705876 +vn 0.333954 0.694992 0.636759 +vn -0.217515 0.376641 0.900460 +vn -0.001995 0.008586 0.999961 +vn -0.001995 0.008586 0.999961 +vn -0.217515 0.376641 0.900460 +vn -0.768903 0.058373 0.636695 +vn -0.615213 -0.345341 0.708697 +vn -0.615213 -0.345341 0.708697 +vn -0.768903 0.058373 0.636695 +vn -0.997294 -0.073511 0.000000 +vn -0.870284 -0.492551 0.000000 +vn -0.870284 -0.492551 0.000000 +vn -0.997294 -0.073511 0.000000 +vn -0.768903 0.058373 -0.636696 +vn -0.615214 -0.345341 -0.708697 +vn -0.768903 0.058373 -0.636696 +vn -0.775054 0.444809 -0.448817 +vn -0.386276 0.669201 -0.634792 +vn -0.217514 0.376642 -0.900460 +vn -0.217514 0.376642 -0.900460 +vn -0.386276 0.669201 -0.634792 +vn 0.002361 0.893545 -0.448967 +vn 0.333953 0.694994 -0.636757 +vn 0.333953 0.694994 -0.636757 +vn 0.002361 0.893545 -0.448967 +vn 0.163221 0.986590 0.000000 +vn 0.562362 0.826891 0.000002 +vn 0.562362 0.826891 0.000002 +vn 0.163221 0.986590 0.000000 +vn 0.002361 0.893546 0.448966 +vn 0.333954 0.694992 0.636759 +vn 0.333954 0.694992 0.636759 +vn 0.002361 0.893546 0.448966 +vn -0.386276 0.669201 0.634792 +vn -0.217515 0.376641 0.900460 +vn -0.217515 0.376641 0.900460 +vn -0.386276 0.669201 0.634792 +vn -0.775053 0.444810 0.448817 +vn -0.768903 0.058373 0.636695 +vn -0.768903 0.058373 0.636695 +vn -0.775053 0.444810 0.448817 +vn -0.936026 0.351932 -0.000000 +vn -0.997294 -0.073511 0.000000 +vn -0.997294 -0.073511 0.000000 +vn -0.936026 0.351932 -0.000000 +vn -0.775054 0.444809 -0.448817 +vn -0.768903 0.058373 -0.636696 +vn -0.775054 0.444809 -0.448817 +vn -0.500086 0.865976 0.000000 +vn -0.386276 0.669201 -0.634792 +vn -0.386276 0.669201 -0.634792 +vn -0.500086 0.865976 0.000000 +vn 0.002361 0.893545 -0.448967 +vn 0.002361 0.893545 -0.448967 +vn -0.500086 0.865976 0.000000 +vn 0.163221 0.986590 0.000000 +vn 0.163221 0.986590 0.000000 +vn -0.500086 0.865976 0.000000 +vn 0.002361 0.893546 0.448966 +vn 0.002361 0.893546 0.448966 +vn -0.500086 0.865976 0.000000 +vn -0.386276 0.669201 0.634792 +vn -0.386276 0.669201 0.634792 +vn -0.500086 0.865976 0.000000 +vn -0.775053 0.444810 0.448817 +vn -0.775053 0.444810 0.448817 +vn -0.500086 0.865976 0.000000 +vn -0.936026 0.351932 -0.000000 +vn -0.936026 0.351932 -0.000000 +vn -0.500086 0.865976 0.000000 +vn -0.775054 0.444809 -0.448817 +vn 0.499985 0.007713 -0.866000 +vn 0.866000 0.007713 -0.499985 +vn 0.866000 -0.007713 -0.499985 +vn 0.499985 -0.007713 -0.866000 +vn 0.000000 0.007713 -0.999970 +vn 0.499985 0.007713 -0.866000 +vn 0.499985 -0.007713 -0.866000 +vn 0.000000 -0.007713 -0.999970 +vn -0.499985 0.007713 -0.866000 +vn 0.000000 0.007713 -0.999970 +vn 0.000000 -0.007713 -0.999970 +vn -0.499985 -0.007713 -0.866000 +vn -0.866000 0.007713 -0.499985 +vn -0.499985 0.007713 -0.866000 +vn -0.499985 -0.007713 -0.866000 +vn -0.866000 -0.007713 -0.499985 +vn -0.999970 0.007713 0.000000 +vn -0.866000 0.007713 -0.499985 +vn -0.866000 -0.007713 -0.499985 +vn -0.999970 -0.007713 0.000000 +vn -0.866000 0.007713 0.499985 +vn -0.999970 0.007713 0.000000 +vn -0.999970 -0.007713 0.000000 +vn -0.866000 -0.007713 0.499985 +vn -0.499985 0.007713 0.866000 +vn -0.866000 0.007713 0.499985 +vn -0.866000 -0.007713 0.499985 +vn -0.499985 -0.007713 0.866000 +vn 0.000000 0.007713 0.999970 +vn -0.499985 0.007713 0.866000 +vn -0.499985 -0.007713 0.866000 +vn 0.000000 -0.007713 0.999970 +vn 0.499985 0.007713 0.866000 +vn 0.000000 0.007713 0.999970 +vn 0.000000 -0.007713 0.999970 +vn 0.499985 -0.007713 0.866000 +vn 0.866000 0.007713 0.499985 +vn 0.499985 0.007713 0.866000 +vn 0.499985 -0.007713 0.866000 +vn 0.866000 -0.007713 0.499985 +vn 0.999970 0.007713 0.000000 +vn 0.866000 0.007713 0.499985 +vn 0.866000 -0.007713 0.499985 +vn 0.999970 -0.007713 0.000000 +vn 0.866000 0.007713 -0.499985 +vn 0.999970 0.007713 0.000000 +vn 0.999970 -0.007713 0.000000 +vn 0.866000 -0.007713 -0.499985 +vn -0.289759 -0.942366 -0.167292 +vn -0.468078 -0.841351 -0.270244 +vn -0.270243 -0.841351 -0.468077 +vn -0.167291 -0.942366 -0.289760 +vn -0.167291 -0.942366 -0.289760 +vn -0.270243 -0.841351 -0.468077 +vn 0.000000 -0.841351 -0.540489 +vn 0.000000 -0.942365 -0.334586 +vn 0.000000 -0.942365 -0.334586 +vn 0.000000 -0.841351 -0.540489 +vn 0.270246 -0.841352 -0.468075 +vn 0.167296 -0.942365 -0.289759 +vn 0.167296 -0.942365 -0.289759 +vn 0.270246 -0.841352 -0.468075 +vn 0.468075 -0.841353 -0.270244 +vn 0.289761 -0.942365 -0.167293 +vn 0.289761 -0.942365 -0.167293 +vn 0.468075 -0.841353 -0.270244 +vn 0.540486 -0.841353 0.000000 +vn 0.334585 -0.942366 0.000000 +vn 0.334585 -0.942366 0.000000 +vn 0.540486 -0.841353 0.000000 +vn 0.468075 -0.841353 0.270244 +vn 0.289760 -0.942365 0.167294 +vn 0.289760 -0.942365 0.167294 +vn 0.468075 -0.841353 0.270244 +vn 0.270246 -0.841352 0.468075 +vn 0.167296 -0.942365 0.289759 +vn 0.167296 -0.942365 0.289759 +vn 0.270246 -0.841352 0.468075 +vn 0.000000 -0.841351 0.540489 +vn 0.000000 -0.942365 0.334587 +vn 0.000000 -0.942365 0.334587 +vn 0.000000 -0.841351 0.540489 +vn -0.270243 -0.841351 0.468078 +vn -0.167291 -0.942366 0.289760 +vn -0.167291 -0.942366 0.289760 +vn -0.270243 -0.841351 0.468078 +vn -0.468077 -0.841351 0.270245 +vn -0.289759 -0.942366 0.167293 +vn -0.289759 -0.942366 0.167293 +vn -0.468077 -0.841351 0.270245 +vn -0.540491 -0.841350 0.000000 +vn -0.334583 -0.942366 0.000000 +vn -0.334583 -0.942366 0.000000 +vn -0.540491 -0.841350 0.000000 +vn -0.468078 -0.841351 -0.270244 +vn -0.289759 -0.942366 -0.167292 +vn -0.468078 -0.841351 -0.270244 +vn -0.631864 -0.683860 -0.364805 +vn -0.364803 -0.683861 -0.631865 +vn -0.270243 -0.841351 -0.468077 +vn -0.270243 -0.841351 -0.468077 +vn -0.364803 -0.683861 -0.631865 +vn 0.000000 -0.683861 -0.729612 +vn 0.000000 -0.841351 -0.540489 +vn 0.000000 -0.841351 -0.540489 +vn 0.000000 -0.683861 -0.729612 +vn 0.364808 -0.683861 -0.631862 +vn 0.270246 -0.841352 -0.468075 +vn 0.270246 -0.841352 -0.468075 +vn 0.364808 -0.683861 -0.631862 +vn 0.631860 -0.683863 -0.364806 +vn 0.468075 -0.841353 -0.270244 +vn 0.468075 -0.841353 -0.270244 +vn 0.631860 -0.683863 -0.364806 +vn 0.729609 -0.683865 0.000000 +vn 0.540486 -0.841353 0.000000 +vn 0.540486 -0.841353 0.000000 +vn 0.729609 -0.683865 0.000000 +vn 0.631860 -0.683864 0.364806 +vn 0.468075 -0.841353 0.270244 +vn 0.468075 -0.841353 0.270244 +vn 0.631860 -0.683864 0.364806 +vn 0.364807 -0.683863 0.631860 +vn 0.270246 -0.841352 0.468075 +vn 0.270246 -0.841352 0.468075 +vn 0.364807 -0.683863 0.631860 +vn 0.000000 -0.683864 0.729610 +vn 0.000000 -0.841351 0.540489 +vn 0.000000 -0.841351 0.540489 +vn 0.000000 -0.683864 0.729610 +vn -0.364802 -0.683863 0.631863 +vn -0.270243 -0.841351 0.468078 +vn -0.270243 -0.841351 0.468078 +vn -0.364802 -0.683863 0.631863 +vn -0.631864 -0.683861 0.364804 +vn -0.468077 -0.841351 0.270245 +vn -0.468077 -0.841351 0.270245 +vn -0.631864 -0.683861 0.364804 +vn -0.729613 -0.683860 0.000000 +vn -0.540491 -0.841350 0.000000 +vn -0.540491 -0.841350 0.000000 +vn -0.729613 -0.683860 0.000000 +vn -0.631864 -0.683860 -0.364805 +vn -0.468078 -0.841351 -0.270244 +vn -0.631864 -0.683860 -0.364805 +vn -0.758869 -0.481825 -0.438135 +vn -0.438134 -0.481821 -0.758872 +vn -0.364803 -0.683861 -0.631865 +vn -0.364803 -0.683861 -0.631865 +vn -0.438134 -0.481821 -0.758872 +vn 0.000000 -0.481821 -0.876270 +vn 0.000000 -0.683861 -0.729612 +vn 0.000000 -0.683861 -0.729612 +vn 0.000000 -0.481821 -0.876270 +vn 0.438134 -0.481822 -0.758872 +vn 0.364808 -0.683861 -0.631862 +vn 0.364808 -0.683861 -0.631862 +vn 0.438134 -0.481822 -0.758872 +vn 0.758870 -0.481823 -0.438136 +vn 0.631860 -0.683863 -0.364806 +vn 0.631860 -0.683863 -0.364806 +vn 0.758870 -0.481823 -0.438136 +vn 0.876268 -0.481824 0.000000 +vn 0.729609 -0.683865 0.000000 +vn 0.729609 -0.683865 0.000000 +vn 0.876268 -0.481824 0.000000 +vn 0.758870 -0.481823 0.438136 +vn 0.631860 -0.683864 0.364806 +vn 0.631860 -0.683864 0.364806 +vn 0.758870 -0.481823 0.438136 +vn 0.438134 -0.481822 0.758872 +vn 0.364807 -0.683863 0.631860 +vn 0.364807 -0.683863 0.631860 +vn 0.438134 -0.481822 0.758872 +vn 0.000000 -0.481822 0.876269 +vn 0.000000 -0.683864 0.729610 +vn 0.000000 -0.683864 0.729610 +vn 0.000000 -0.481822 0.876269 +vn -0.438134 -0.481821 0.758872 +vn -0.364802 -0.683863 0.631863 +vn -0.364802 -0.683863 0.631863 +vn -0.438134 -0.481821 0.758872 +vn -0.758870 -0.481825 0.438135 +vn -0.631864 -0.683861 0.364804 +vn -0.631864 -0.683861 0.364804 +vn -0.758870 -0.481825 0.438135 +vn -0.876266 -0.481828 0.000000 +vn -0.729613 -0.683860 0.000000 +vn -0.729613 -0.683860 0.000000 +vn -0.876266 -0.481828 0.000000 +vn -0.758869 -0.481825 -0.438135 +vn -0.631864 -0.683860 -0.364805 +vn -0.758869 -0.481825 -0.438135 +vn -0.838785 -0.248836 -0.484273 +vn -0.484270 -0.248841 -0.838785 +vn -0.438134 -0.481821 -0.758872 +vn -0.438134 -0.481821 -0.758872 +vn -0.484270 -0.248841 -0.838785 +vn 0.000002 -0.248841 -0.968544 +vn 0.000000 -0.481821 -0.876270 +vn 0.000000 -0.481821 -0.876270 +vn 0.000002 -0.248841 -0.968544 +vn 0.484273 -0.248839 -0.838784 +vn 0.438134 -0.481822 -0.758872 +vn 0.438134 -0.481822 -0.758872 +vn 0.484273 -0.248839 -0.838784 +vn 0.838784 -0.248838 -0.484274 +vn 0.758870 -0.481823 -0.438136 +vn 0.758870 -0.481823 -0.438136 +vn 0.838784 -0.248838 -0.484274 +vn 0.968546 -0.248836 0.000000 +vn 0.876268 -0.481824 0.000000 +vn 0.876268 -0.481824 0.000000 +vn 0.968546 -0.248836 0.000000 +vn 0.838785 -0.248844 0.484270 +vn 0.758870 -0.481823 0.438136 +vn 0.758870 -0.481823 0.438136 +vn 0.838785 -0.248844 0.484270 +vn 0.484275 -0.248853 0.838779 +vn 0.438134 -0.481822 0.758872 +vn 0.438134 -0.481822 0.758872 +vn 0.484275 -0.248853 0.838779 +vn 0.000002 -0.248857 0.968540 +vn 0.000000 -0.481822 0.876269 +vn 0.000000 -0.481822 0.876269 +vn 0.000002 -0.248857 0.968540 +vn -0.484271 -0.248855 0.838781 +vn -0.438134 -0.481821 0.758872 +vn -0.438134 -0.481821 0.758872 +vn -0.484271 -0.248855 0.838781 +vn -0.838786 -0.248843 0.484269 +vn -0.758870 -0.481825 0.438135 +vn -0.758870 -0.481825 0.438135 +vn -0.838786 -0.248843 0.484269 +vn -0.968547 -0.248833 0.000000 +vn -0.876266 -0.481828 0.000000 +vn -0.876266 -0.481828 0.000000 +vn -0.968547 -0.248833 0.000000 +vn -0.838785 -0.248836 -0.484273 +vn -0.758869 -0.481825 -0.438135 +vn -0.838785 -0.248836 -0.484273 +vn -0.865943 -0.013768 -0.499953 +vn -0.499953 -0.013768 -0.865943 +vn -0.484270 -0.248841 -0.838785 +vn -0.484270 -0.248841 -0.838785 +vn -0.499953 -0.013768 -0.865943 +vn 0.000000 -0.013768 -0.999905 +vn 0.000002 -0.248841 -0.968544 +vn 0.000002 -0.248841 -0.968544 +vn 0.000000 -0.013768 -0.999905 +vn 0.499953 -0.013768 -0.865943 +vn 0.484273 -0.248839 -0.838784 +vn 0.484273 -0.248839 -0.838784 +vn 0.499953 -0.013768 -0.865943 +vn 0.865943 -0.013768 -0.499953 +vn 0.838784 -0.248838 -0.484274 +vn 0.838784 -0.248838 -0.484274 +vn 0.865943 -0.013768 -0.499953 +vn 0.999905 -0.013768 0.000000 +vn 0.968546 -0.248836 0.000000 +vn 0.968546 -0.248836 0.000000 +vn 0.999905 -0.013768 0.000000 +vn 0.865948 -0.013768 0.499945 +vn 0.838785 -0.248844 0.484270 +vn 0.838785 -0.248844 0.484270 +vn 0.865948 -0.013768 0.499945 +vn 0.499953 -0.013768 0.865943 +vn 0.484275 -0.248853 0.838779 +vn 0.484275 -0.248853 0.838779 +vn 0.499953 -0.013768 0.865943 +vn 0.000000 -0.013768 0.999905 +vn 0.000002 -0.248857 0.968540 +vn 0.000002 -0.248857 0.968540 +vn 0.000000 -0.013768 0.999905 +vn -0.499953 -0.013768 0.865943 +vn -0.484271 -0.248855 0.838781 +vn -0.484271 -0.248855 0.838781 +vn -0.499953 -0.013768 0.865943 +vn -0.865948 -0.013768 0.499945 +vn -0.838786 -0.248843 0.484269 +vn -0.838786 -0.248843 0.484269 +vn -0.865948 -0.013768 0.499945 +vn -0.999905 -0.013768 0.000000 +vn -0.968547 -0.248833 0.000000 +vn -0.968547 -0.248833 0.000000 +vn -0.999905 -0.013768 0.000000 +vn -0.865943 -0.013768 -0.499953 +vn -0.838785 -0.248836 -0.484273 +vn -0.167291 -0.942366 -0.289760 +vn 0.000001 -1.000000 0.000000 +vn -0.289759 -0.942366 -0.167292 +vn 0.000000 -0.942365 -0.334586 +vn 0.000001 -1.000000 0.000000 +vn -0.167291 -0.942366 -0.289760 +vn 0.167296 -0.942365 -0.289759 +vn 0.000001 -1.000000 0.000000 +vn 0.000000 -0.942365 -0.334586 +vn 0.289761 -0.942365 -0.167293 +vn 0.000001 -1.000000 0.000000 +vn 0.167296 -0.942365 -0.289759 +vn 0.334585 -0.942366 0.000000 +vn 0.000001 -1.000000 0.000000 +vn 0.289761 -0.942365 -0.167293 +vn 0.289760 -0.942365 0.167294 +vn 0.000001 -1.000000 0.000000 +vn 0.334585 -0.942366 0.000000 +vn 0.167296 -0.942365 0.289759 +vn 0.000001 -1.000000 0.000000 +vn 0.289760 -0.942365 0.167294 +vn 0.000000 -0.942365 0.334587 +vn 0.000001 -1.000000 0.000000 +vn 0.167296 -0.942365 0.289759 +vn -0.167291 -0.942366 0.289760 +vn 0.000001 -1.000000 0.000000 +vn 0.000000 -0.942365 0.334587 +vn -0.289759 -0.942366 0.167293 +vn 0.000001 -1.000000 0.000000 +vn -0.167291 -0.942366 0.289760 +vn -0.334583 -0.942366 0.000000 +vn 0.000001 -1.000000 0.000000 +vn -0.289759 -0.942366 0.167293 +vn -0.289759 -0.942366 -0.167292 +vn 0.000001 -1.000000 0.000000 +vn -0.334583 -0.942366 0.000000 +vn -0.289764 -0.942364 -0.167296 +vn -0.468092 -0.841342 -0.270247 +vn -0.270246 -0.841346 -0.468085 +vn -0.167290 -0.942363 -0.289770 +vn -0.167290 -0.942363 -0.289770 +vn -0.270246 -0.841346 -0.468085 +vn 0.000000 -0.841347 -0.540496 +vn 0.000000 -0.942361 -0.334597 +vn 0.000000 -0.942361 -0.334597 +vn 0.000000 -0.841347 -0.540496 +vn 0.270251 -0.841346 -0.468082 +vn 0.167306 -0.942360 -0.289769 +vn 0.167306 -0.942360 -0.289769 +vn 0.270251 -0.841346 -0.468082 +vn 0.468085 -0.841348 -0.270242 +vn 0.289775 -0.942361 -0.167291 +vn 0.289775 -0.942361 -0.167291 +vn 0.468085 -0.841348 -0.270242 +vn 0.540496 -0.841347 0.000000 +vn 0.334596 -0.942362 0.000000 +vn 0.334596 -0.942362 0.000000 +vn 0.540496 -0.841347 0.000000 +vn 0.468085 -0.841348 0.270243 +vn 0.289774 -0.942361 0.167291 +vn 0.289774 -0.942361 0.167291 +vn 0.468085 -0.841348 0.270243 +vn 0.270251 -0.841346 0.468083 +vn 0.167306 -0.942360 0.289769 +vn 0.167306 -0.942360 0.289769 +vn 0.270251 -0.841346 0.468083 +vn 0.000000 -0.841347 0.540496 +vn 0.000000 -0.942361 0.334597 +vn 0.000000 -0.942361 0.334597 +vn 0.000000 -0.841347 0.540496 +vn -0.270246 -0.841346 0.468085 +vn -0.167291 -0.942363 0.289770 +vn -0.167291 -0.942363 0.289770 +vn -0.270246 -0.841346 0.468085 +vn -0.468092 -0.841342 0.270247 +vn -0.289764 -0.942364 0.167296 +vn -0.289764 -0.942364 0.167296 +vn -0.468092 -0.841342 0.270247 +vn -0.540505 -0.841341 0.000000 +vn -0.334592 -0.942363 0.000000 +vn -0.334592 -0.942363 0.000000 +vn -0.540505 -0.841341 0.000000 +vn -0.468092 -0.841342 -0.270247 +vn -0.289764 -0.942364 -0.167296 +vn -0.468092 -0.841342 -0.270247 +vn -0.631879 -0.683841 -0.364817 +vn -0.364817 -0.683841 -0.631878 +vn -0.270246 -0.841346 -0.468085 +vn -0.270246 -0.841346 -0.468085 +vn -0.364817 -0.683841 -0.631878 +vn 0.000000 -0.683847 -0.729626 +vn 0.000000 -0.841347 -0.540496 +vn 0.000000 -0.841347 -0.540496 +vn 0.000000 -0.683847 -0.729626 +vn 0.364815 -0.683845 -0.631875 +vn 0.270251 -0.841346 -0.468082 +vn 0.270251 -0.841346 -0.468082 +vn 0.364815 -0.683845 -0.631875 +vn 0.631876 -0.683847 -0.364810 +vn 0.468085 -0.841348 -0.270242 +vn 0.468085 -0.841348 -0.270242 +vn 0.631876 -0.683847 -0.364810 +vn 0.729620 -0.683852 0.000000 +vn 0.540496 -0.841347 0.000000 +vn 0.540496 -0.841347 0.000000 +vn 0.729620 -0.683852 0.000000 +vn 0.631876 -0.683847 0.364810 +vn 0.468085 -0.841348 0.270243 +vn 0.468085 -0.841348 0.270243 +vn 0.631876 -0.683847 0.364810 +vn 0.364815 -0.683845 0.631875 +vn 0.270251 -0.841346 0.468083 +vn 0.270251 -0.841346 0.468083 +vn 0.364815 -0.683845 0.631875 +vn 0.000000 -0.683847 0.729626 +vn 0.000000 -0.841347 0.540496 +vn 0.000000 -0.841347 0.540496 +vn 0.000000 -0.683847 0.729626 +vn -0.364817 -0.683841 0.631878 +vn -0.270246 -0.841346 0.468085 +vn -0.270246 -0.841346 0.468085 +vn -0.364817 -0.683841 0.631878 +vn -0.631879 -0.683841 0.364816 +vn -0.468092 -0.841342 0.270247 +vn -0.468092 -0.841342 0.270247 +vn -0.631879 -0.683841 0.364816 +vn -0.729628 -0.683844 0.000000 +vn -0.540505 -0.841341 0.000000 +vn -0.540505 -0.841341 0.000000 +vn -0.729628 -0.683844 0.000000 +vn -0.631879 -0.683841 -0.364817 +vn -0.468092 -0.841342 -0.270247 +vn -0.631879 -0.683841 -0.364817 +vn -0.758871 -0.481814 -0.438144 +vn -0.438132 -0.481807 -0.758883 +vn -0.364817 -0.683841 -0.631878 +vn -0.364817 -0.683841 -0.631878 +vn -0.438132 -0.481807 -0.758883 +vn 0.000000 -0.481808 -0.876277 +vn 0.000000 -0.683847 -0.729626 +vn 0.000000 -0.683847 -0.729626 +vn 0.000000 -0.481808 -0.876277 +vn 0.438141 -0.481808 -0.758876 +vn 0.364815 -0.683845 -0.631875 +vn 0.364815 -0.683845 -0.631875 +vn 0.438141 -0.481808 -0.758876 +vn 0.758876 -0.481807 -0.438144 +vn 0.631876 -0.683847 -0.364810 +vn 0.631876 -0.683847 -0.364810 +vn 0.758876 -0.481807 -0.438144 +vn 0.876278 -0.481806 0.000000 +vn 0.729620 -0.683852 0.000000 +vn 0.729620 -0.683852 0.000000 +vn 0.876278 -0.481806 0.000000 +vn 0.758876 -0.481807 0.438144 +vn 0.631876 -0.683847 0.364810 +vn 0.631876 -0.683847 0.364810 +vn 0.758876 -0.481807 0.438144 +vn 0.438141 -0.481808 0.758876 +vn 0.364815 -0.683845 0.631875 +vn 0.364815 -0.683845 0.631875 +vn 0.438141 -0.481808 0.758876 +vn 0.000000 -0.481808 0.876277 +vn 0.000000 -0.683847 0.729626 +vn 0.000000 -0.683847 0.729626 +vn 0.000000 -0.481808 0.876277 +vn -0.438132 -0.481807 0.758883 +vn -0.364817 -0.683841 0.631878 +vn -0.364817 -0.683841 0.631878 +vn -0.438132 -0.481807 0.758883 +vn -0.758871 -0.481814 0.438144 +vn -0.631879 -0.683841 0.364816 +vn -0.631879 -0.683841 0.364816 +vn -0.758871 -0.481814 0.438144 +vn -0.876269 -0.481823 0.000000 +vn -0.729628 -0.683844 0.000000 +vn -0.729628 -0.683844 0.000000 +vn -0.876269 -0.481823 0.000000 +vn -0.758871 -0.481814 -0.438144 +vn -0.631879 -0.683841 -0.364817 +vn -0.758871 -0.481814 -0.438144 +vn -0.838782 -0.248835 -0.484278 +vn -0.484267 -0.248839 -0.838788 +vn -0.438132 -0.481807 -0.758883 +vn -0.438132 -0.481807 -0.758883 +vn -0.484267 -0.248839 -0.838788 +vn 0.000000 -0.248833 -0.968546 +vn 0.000000 -0.481808 -0.876277 +vn 0.000000 -0.481808 -0.876277 +vn 0.000000 -0.248833 -0.968546 +vn 0.484281 -0.248830 -0.838782 +vn 0.438141 -0.481808 -0.758876 +vn 0.438141 -0.481808 -0.758876 +vn 0.484281 -0.248830 -0.838782 +vn 0.838790 -0.248820 -0.484273 +vn 0.758876 -0.481807 -0.438144 +vn 0.758876 -0.481807 -0.438144 +vn 0.838790 -0.248820 -0.484273 +vn 0.968552 -0.248812 0.000000 +vn 0.876278 -0.481806 0.000000 +vn 0.876278 -0.481806 0.000000 +vn 0.968552 -0.248812 0.000000 +vn 0.838790 -0.248820 0.484273 +vn 0.758876 -0.481807 0.438144 +vn 0.758876 -0.481807 0.438144 +vn 0.838790 -0.248820 0.484273 +vn 0.484281 -0.248830 0.838782 +vn 0.438141 -0.481808 0.758876 +vn 0.438141 -0.481808 0.758876 +vn 0.484281 -0.248830 0.838782 +vn 0.000000 -0.248833 0.968547 +vn 0.000000 -0.481808 0.876277 +vn 0.000000 -0.481808 0.876277 +vn 0.000000 -0.248833 0.968547 +vn -0.484267 -0.248839 0.838788 +vn -0.438132 -0.481807 0.758883 +vn -0.438132 -0.481807 0.758883 +vn -0.484267 -0.248839 0.838788 +vn -0.838782 -0.248835 0.484279 +vn -0.758871 -0.481814 0.438144 +vn -0.758871 -0.481814 0.438144 +vn -0.838782 -0.248835 0.484279 +vn -0.968548 -0.248826 0.000000 +vn -0.876269 -0.481823 0.000000 +vn -0.876269 -0.481823 0.000000 +vn -0.968548 -0.248826 0.000000 +vn -0.838782 -0.248835 -0.484278 +vn -0.758871 -0.481814 -0.438144 +vn -0.838782 -0.248835 -0.484278 +vn -0.866000 -0.007713 -0.499985 +vn -0.499985 -0.007713 -0.866000 +vn -0.484267 -0.248839 -0.838788 +vn -0.484267 -0.248839 -0.838788 +vn -0.499985 -0.007713 -0.866000 +vn 0.000000 -0.007713 -0.999970 +vn 0.000000 -0.248833 -0.968546 +vn 0.000000 -0.248833 -0.968546 +vn 0.000000 -0.007713 -0.999970 +vn 0.499985 -0.007713 -0.866000 +vn 0.484281 -0.248830 -0.838782 +vn 0.484281 -0.248830 -0.838782 +vn 0.499985 -0.007713 -0.866000 +vn 0.866000 -0.007713 -0.499985 +vn 0.838790 -0.248820 -0.484273 +vn 0.838790 -0.248820 -0.484273 +vn 0.866000 -0.007713 -0.499985 +vn 0.999970 -0.007713 0.000000 +vn 0.968552 -0.248812 0.000000 +vn 0.968552 -0.248812 0.000000 +vn 0.999970 -0.007713 0.000000 +vn 0.866000 -0.007713 0.499985 +vn 0.838790 -0.248820 0.484273 +vn 0.838790 -0.248820 0.484273 +vn 0.866000 -0.007713 0.499985 +vn 0.499985 -0.007713 0.866000 +vn 0.484281 -0.248830 0.838782 +vn 0.484281 -0.248830 0.838782 +vn 0.499985 -0.007713 0.866000 +vn 0.000000 -0.007713 0.999970 +vn 0.000000 -0.248833 0.968547 +vn 0.000000 -0.248833 0.968547 +vn 0.000000 -0.007713 0.999970 +vn -0.499985 -0.007713 0.866000 +vn -0.484267 -0.248839 0.838788 +vn -0.484267 -0.248839 0.838788 +vn -0.499985 -0.007713 0.866000 +vn -0.866000 -0.007713 0.499985 +vn -0.838782 -0.248835 0.484279 +vn -0.838782 -0.248835 0.484279 +vn -0.866000 -0.007713 0.499985 +vn -0.999970 -0.007713 0.000000 +vn -0.968548 -0.248826 0.000000 +vn -0.968548 -0.248826 0.000000 +vn -0.999970 -0.007713 0.000000 +vn -0.866000 -0.007713 -0.499985 +vn -0.838782 -0.248835 -0.484278 +vn -0.167290 -0.942363 -0.289770 +vn 0.000000 -1.000000 0.000000 +vn -0.289764 -0.942364 -0.167296 +vn 0.000000 -0.942361 -0.334597 +vn 0.000000 -1.000000 0.000000 +vn -0.167290 -0.942363 -0.289770 +vn 0.167306 -0.942360 -0.289769 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -0.942361 -0.334597 +vn 0.289775 -0.942361 -0.167291 +vn 0.000000 -1.000000 0.000000 +vn 0.167306 -0.942360 -0.289769 +vn 0.334596 -0.942362 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.289775 -0.942361 -0.167291 +vn 0.289774 -0.942361 0.167291 +vn 0.000000 -1.000000 0.000000 +vn 0.334596 -0.942362 0.000000 +vn 0.167306 -0.942360 0.289769 +vn 0.000000 -1.000000 0.000000 +vn 0.289774 -0.942361 0.167291 +vn 0.000000 -0.942361 0.334597 +vn 0.000000 -1.000000 0.000000 +vn 0.167306 -0.942360 0.289769 +vn -0.167291 -0.942363 0.289770 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -0.942361 0.334597 +vn -0.289764 -0.942364 0.167296 +vn 0.000000 -1.000000 0.000000 +vn -0.167291 -0.942363 0.289770 +vn -0.334592 -0.942363 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.289764 -0.942364 0.167296 +vn -0.289764 -0.942364 -0.167296 +vn 0.000000 -1.000000 0.000000 +vn -0.334592 -0.942363 0.000000 +vn -0.289764 0.942364 -0.167296 +vn -0.167290 0.942363 -0.289769 +vn -0.270246 0.841346 -0.468086 +vn -0.468092 0.841342 -0.270247 +vn -0.167290 0.942363 -0.289769 +vn -0.000000 0.942361 -0.334598 +vn 0.000000 0.841346 -0.540497 +vn -0.270246 0.841346 -0.468086 +vn -0.000000 0.942361 -0.334598 +vn 0.167306 0.942361 -0.289767 +vn 0.270251 0.841346 -0.468082 +vn 0.000000 0.841346 -0.540497 +vn 0.167306 0.942361 -0.289767 +vn 0.289775 0.942361 -0.167291 +vn 0.468085 0.841347 -0.270243 +vn 0.270251 0.841346 -0.468082 +vn 0.289775 0.942361 -0.167291 +vn 0.334596 0.942362 0.000000 +vn 0.540495 0.841347 0.000000 +vn 0.468085 0.841347 -0.270243 +vn 0.334596 0.942362 0.000000 +vn 0.289774 0.942361 0.167290 +vn 0.468085 0.841347 0.270243 +vn 0.540495 0.841347 0.000000 +vn 0.289774 0.942361 0.167290 +vn 0.167306 0.942361 0.289767 +vn 0.270251 0.841346 0.468082 +vn 0.468085 0.841347 0.270243 +vn 0.167306 0.942361 0.289767 +vn 0.000000 0.942361 0.334598 +vn 0.000000 0.841346 0.540496 +vn 0.270251 0.841346 0.468082 +vn 0.000000 0.942361 0.334598 +vn -0.167291 0.942363 0.289768 +vn -0.270246 0.841346 0.468085 +vn 0.000000 0.841346 0.540496 +vn -0.167291 0.942363 0.289768 +vn -0.289763 0.942364 0.167296 +vn -0.468091 0.841342 0.270247 +vn -0.270246 0.841346 0.468085 +vn -0.289763 0.942364 0.167296 +vn -0.334592 0.942363 -0.000000 +vn -0.540505 0.841341 -0.000000 +vn -0.468091 0.841342 0.270247 +vn -0.334592 0.942363 -0.000000 +vn -0.289764 0.942364 -0.167296 +vn -0.468092 0.841342 -0.270247 +vn -0.540505 0.841341 -0.000000 +vn -0.468092 0.841342 -0.270247 +vn -0.270246 0.841346 -0.468086 +vn -0.364806 0.683864 -0.631860 +vn -0.631860 0.683864 -0.364805 +vn -0.270246 0.841346 -0.468086 +vn 0.000000 0.841346 -0.540497 +vn 0.000000 0.683869 -0.729605 +vn -0.364806 0.683864 -0.631860 +vn 0.000000 0.841346 -0.540497 +vn 0.270251 0.841346 -0.468082 +vn 0.364804 0.683868 -0.631857 +vn 0.000000 0.683869 -0.729605 +vn 0.270251 0.841346 -0.468082 +vn 0.468085 0.841347 -0.270243 +vn 0.631858 0.683869 -0.364800 +vn 0.364804 0.683868 -0.631857 +vn 0.468085 0.841347 -0.270243 +vn 0.540495 0.841347 0.000000 +vn 0.729600 0.683875 0.000000 +vn 0.631858 0.683869 -0.364800 +vn 0.540495 0.841347 0.000000 +vn 0.468085 0.841347 0.270243 +vn 0.631858 0.683869 0.364800 +vn 0.729600 0.683875 0.000000 +vn 0.468085 0.841347 0.270243 +vn 0.270251 0.841346 0.468082 +vn 0.364804 0.683868 0.631857 +vn 0.631858 0.683869 0.364800 +vn 0.270251 0.841346 0.468082 +vn 0.000000 0.841346 0.540496 +vn 0.000000 0.683869 0.729604 +vn 0.364804 0.683868 0.631857 +vn 0.000000 0.841346 0.540496 +vn -0.270246 0.841346 0.468085 +vn -0.364806 0.683864 0.631859 +vn 0.000000 0.683869 0.729604 +vn -0.270246 0.841346 0.468085 +vn -0.468091 0.841342 0.270247 +vn -0.631860 0.683864 0.364805 +vn -0.364806 0.683864 0.631859 +vn -0.468091 0.841342 0.270247 +vn -0.540505 0.841341 -0.000000 +vn -0.729607 0.683867 0.000000 +vn -0.631860 0.683864 0.364805 +vn -0.540505 0.841341 -0.000000 +vn -0.468092 0.841342 -0.270247 +vn -0.631860 0.683864 -0.364805 +vn -0.729607 0.683867 0.000000 +vn -0.631860 0.683864 -0.364805 +vn -0.364806 0.683864 -0.631860 +vn -0.438124 0.481837 -0.758868 +vn -0.758856 0.481845 -0.438135 +vn -0.364806 0.683864 -0.631860 +vn 0.000000 0.683869 -0.729605 +vn 0.000000 0.481839 -0.876260 +vn -0.438124 0.481837 -0.758868 +vn 0.000000 0.683869 -0.729605 +vn 0.364804 0.683868 -0.631857 +vn 0.438133 0.481839 -0.758861 +vn 0.000000 0.481839 -0.876260 +vn 0.364804 0.683868 -0.631857 +vn 0.631858 0.683869 -0.364800 +vn 0.758861 0.481837 -0.438136 +vn 0.438133 0.481839 -0.758861 +vn 0.631858 0.683869 -0.364800 +vn 0.729600 0.683875 0.000000 +vn 0.876261 0.481836 0.000000 +vn 0.758861 0.481837 -0.438136 +vn 0.729600 0.683875 0.000000 +vn 0.631858 0.683869 0.364800 +vn 0.758861 0.481837 0.438135 +vn 0.876261 0.481836 0.000000 +vn 0.631858 0.683869 0.364800 +vn 0.364804 0.683868 0.631857 +vn 0.438133 0.481839 0.758862 +vn 0.758861 0.481837 0.438135 +vn 0.364804 0.683868 0.631857 +vn 0.000000 0.683869 0.729604 +vn 0.000000 0.481839 0.876260 +vn 0.438133 0.481839 0.758862 +vn 0.000000 0.683869 0.729604 +vn -0.364806 0.683864 0.631859 +vn -0.438124 0.481838 0.758868 +vn 0.000000 0.481839 0.876260 +vn -0.364806 0.683864 0.631859 +vn -0.631860 0.683864 0.364805 +vn -0.758856 0.481845 0.438135 +vn -0.438124 0.481838 0.758868 +vn -0.631860 0.683864 0.364805 +vn -0.729607 0.683867 0.000000 +vn -0.876252 0.481854 0.000000 +vn -0.758856 0.481845 0.438135 +vn -0.729607 0.683867 0.000000 +vn -0.631860 0.683864 -0.364805 +vn -0.758856 0.481845 -0.438135 +vn -0.876252 0.481854 0.000000 +vn -0.758856 0.481845 -0.438135 +vn -0.438124 0.481837 -0.758868 +vn -0.484268 0.248828 -0.838790 +vn -0.838785 0.248824 -0.484280 +vn -0.438124 0.481837 -0.758868 +vn 0.000000 0.481839 -0.876260 +vn -0.000000 0.248822 -0.968549 +vn -0.484268 0.248828 -0.838790 +vn 0.000000 0.481839 -0.876260 +vn 0.438133 0.481839 -0.758861 +vn 0.484282 0.248819 -0.838785 +vn -0.000000 0.248822 -0.968549 +vn 0.438133 0.481839 -0.758861 +vn 0.758861 0.481837 -0.438136 +vn 0.838792 0.248809 -0.484275 +vn 0.484282 0.248819 -0.838785 +vn 0.758861 0.481837 -0.438136 +vn 0.876261 0.481836 0.000000 +vn 0.968555 0.248801 0.000000 +vn 0.838792 0.248809 -0.484275 +vn 0.876261 0.481836 0.000000 +vn 0.758861 0.481837 0.438135 +vn 0.838792 0.248809 0.484275 +vn 0.968555 0.248801 0.000000 +vn 0.758861 0.481837 0.438135 +vn 0.438133 0.481839 0.758862 +vn 0.484282 0.248819 0.838785 +vn 0.838792 0.248809 0.484275 +vn 0.438133 0.481839 0.758862 +vn 0.000000 0.481839 0.876260 +vn 0.000000 0.248821 0.968549 +vn 0.484282 0.248819 0.838785 +vn 0.000000 0.481839 0.876260 +vn -0.438124 0.481838 0.758868 +vn -0.484268 0.248828 0.838790 +vn 0.000000 0.248821 0.968549 +vn -0.438124 0.481838 0.758868 +vn -0.758856 0.481845 0.438135 +vn -0.838784 0.248824 0.484280 +vn -0.484268 0.248828 0.838790 +vn -0.758856 0.481845 0.438135 +vn -0.876252 0.481854 0.000000 +vn -0.968551 0.248815 0.000000 +vn -0.838784 0.248824 0.484280 +vn -0.876252 0.481854 0.000000 +vn -0.758856 0.481845 -0.438135 +vn -0.838785 0.248824 -0.484280 +vn -0.968551 0.248815 0.000000 +vn -0.838785 0.248824 -0.484280 +vn -0.484268 0.248828 -0.838790 +vn -0.499985 0.007713 -0.866000 +vn -0.866000 0.007713 -0.499985 +vn -0.484268 0.248828 -0.838790 +vn -0.000000 0.248822 -0.968549 +vn 0.000000 0.007713 -0.999970 +vn -0.499985 0.007713 -0.866000 +vn -0.000000 0.248822 -0.968549 +vn 0.484282 0.248819 -0.838785 +vn 0.499985 0.007713 -0.866000 +vn 0.000000 0.007713 -0.999970 +vn 0.484282 0.248819 -0.838785 +vn 0.838792 0.248809 -0.484275 +vn 0.866000 0.007713 -0.499985 +vn 0.499985 0.007713 -0.866000 +vn 0.838792 0.248809 -0.484275 +vn 0.968555 0.248801 0.000000 +vn 0.999970 0.007713 0.000000 +vn 0.866000 0.007713 -0.499985 +vn 0.968555 0.248801 0.000000 +vn 0.838792 0.248809 0.484275 +vn 0.866000 0.007713 0.499985 +vn 0.999970 0.007713 0.000000 +vn 0.838792 0.248809 0.484275 +vn 0.484282 0.248819 0.838785 +vn 0.499985 0.007713 0.866000 +vn 0.866000 0.007713 0.499985 +vn 0.484282 0.248819 0.838785 +vn 0.000000 0.248821 0.968549 +vn 0.000000 0.007713 0.999970 +vn 0.499985 0.007713 0.866000 +vn 0.000000 0.248821 0.968549 +vn -0.484268 0.248828 0.838790 +vn -0.499985 0.007713 0.866000 +vn 0.000000 0.007713 0.999970 +vn -0.484268 0.248828 0.838790 +vn -0.838784 0.248824 0.484280 +vn -0.866000 0.007713 0.499985 +vn -0.499985 0.007713 0.866000 +vn -0.838784 0.248824 0.484280 +vn -0.968551 0.248815 0.000000 +vn -0.999970 0.007713 0.000000 +vn -0.866000 0.007713 0.499985 +vn -0.968551 0.248815 0.000000 +vn -0.838785 0.248824 -0.484280 +vn -0.866000 0.007713 -0.499985 +vn -0.999970 0.007713 0.000000 +vn -0.167290 0.942363 -0.289769 +vn -0.289764 0.942364 -0.167296 +vn 0.000000 1.000000 0.000001 +vn -0.000000 0.942361 -0.334598 +vn -0.167290 0.942363 -0.289769 +vn 0.000000 1.000000 0.000001 +vn 0.167306 0.942361 -0.289767 +vn -0.000000 0.942361 -0.334598 +vn 0.000000 1.000000 0.000001 +vn 0.289775 0.942361 -0.167291 +vn 0.167306 0.942361 -0.289767 +vn 0.000000 1.000000 0.000001 +vn 0.334596 0.942362 0.000000 +vn 0.289775 0.942361 -0.167291 +vn 0.000000 1.000000 0.000001 +vn 0.289774 0.942361 0.167290 +vn 0.334596 0.942362 0.000000 +vn 0.000000 1.000000 0.000001 +vn 0.167306 0.942361 0.289767 +vn 0.289774 0.942361 0.167290 +vn 0.000000 1.000000 0.000001 +vn 0.000000 0.942361 0.334598 +vn 0.167306 0.942361 0.289767 +vn 0.000000 1.000000 0.000001 +vn -0.167291 0.942363 0.289768 +vn 0.000000 0.942361 0.334598 +vn 0.000000 1.000000 0.000001 +vn -0.289763 0.942364 0.167296 +vn -0.167291 0.942363 0.289768 +vn 0.000000 1.000000 0.000001 +vn -0.334592 0.942363 -0.000000 +vn -0.289763 0.942364 0.167296 +vn 0.000000 1.000000 0.000001 +vn -0.289764 0.942364 -0.167296 +vn -0.334592 0.942363 -0.000000 +vn 0.000000 1.000000 0.000001 +vn 0.289759 -0.942366 -0.167292 +vn 0.167291 -0.942366 -0.289760 +vn 0.270243 -0.841351 -0.468078 +vn 0.468078 -0.841351 -0.270244 +vn 0.167291 -0.942366 -0.289760 +vn 0.000000 -0.942365 -0.334586 +vn 0.000000 -0.841351 -0.540489 +vn 0.270243 -0.841351 -0.468078 +vn 0.000000 -0.942365 -0.334586 +vn -0.167296 -0.942365 -0.289759 +vn -0.270246 -0.841352 -0.468075 +vn 0.000000 -0.841351 -0.540489 +vn -0.167296 -0.942365 -0.289759 +vn -0.289761 -0.942365 -0.167293 +vn -0.468075 -0.841353 -0.270244 +vn -0.270246 -0.841352 -0.468075 +vn -0.289761 -0.942365 -0.167293 +vn -0.334585 -0.942366 0.000000 +vn -0.540486 -0.841353 0.000000 +vn -0.468075 -0.841353 -0.270244 +vn -0.334585 -0.942366 0.000000 +vn -0.289760 -0.942365 0.167294 +vn -0.468075 -0.841353 0.270244 +vn -0.540486 -0.841353 0.000000 +vn -0.289760 -0.942365 0.167294 +vn -0.167296 -0.942365 0.289759 +vn -0.270246 -0.841352 0.468075 +vn -0.468075 -0.841353 0.270244 +vn -0.167296 -0.942365 0.289759 +vn 0.000000 -0.942365 0.334587 +vn 0.000000 -0.841351 0.540489 +vn -0.270246 -0.841352 0.468075 +vn 0.000000 -0.942365 0.334587 +vn 0.167291 -0.942366 0.289760 +vn 0.270243 -0.841351 0.468078 +vn 0.000000 -0.841351 0.540489 +vn 0.167291 -0.942366 0.289760 +vn 0.289759 -0.942366 0.167293 +vn 0.468077 -0.841351 0.270245 +vn 0.270243 -0.841351 0.468078 +vn 0.289759 -0.942366 0.167293 +vn 0.334583 -0.942366 0.000000 +vn 0.540491 -0.841350 0.000000 +vn 0.468077 -0.841351 0.270245 +vn 0.334583 -0.942366 0.000000 +vn 0.289759 -0.942366 -0.167292 +vn 0.468078 -0.841351 -0.270244 +vn 0.540491 -0.841350 0.000000 +vn 0.468078 -0.841351 -0.270244 +vn 0.270243 -0.841351 -0.468078 +vn 0.364803 -0.683861 -0.631865 +vn 0.631864 -0.683861 -0.364805 +vn 0.270243 -0.841351 -0.468078 +vn 0.000000 -0.841351 -0.540489 +vn 0.000000 -0.683861 -0.729612 +vn 0.364803 -0.683861 -0.631865 +vn 0.000000 -0.841351 -0.540489 +vn -0.270246 -0.841352 -0.468075 +vn -0.364808 -0.683861 -0.631862 +vn 0.000000 -0.683861 -0.729612 +vn -0.270246 -0.841352 -0.468075 +vn -0.468075 -0.841353 -0.270244 +vn -0.631860 -0.683863 -0.364806 +vn -0.364808 -0.683861 -0.631862 +vn -0.468075 -0.841353 -0.270244 +vn -0.540486 -0.841353 0.000000 +vn -0.729609 -0.683865 0.000000 +vn -0.631860 -0.683863 -0.364806 +vn -0.540486 -0.841353 0.000000 +vn -0.468075 -0.841353 0.270244 +vn -0.631860 -0.683864 0.364806 +vn -0.729609 -0.683865 0.000000 +vn -0.468075 -0.841353 0.270244 +vn -0.270246 -0.841352 0.468075 +vn -0.364807 -0.683863 0.631860 +vn -0.631860 -0.683864 0.364806 +vn -0.270246 -0.841352 0.468075 +vn 0.000000 -0.841351 0.540489 +vn 0.000000 -0.683864 0.729609 +vn -0.364807 -0.683863 0.631860 +vn 0.000000 -0.841351 0.540489 +vn 0.270243 -0.841351 0.468078 +vn 0.364802 -0.683863 0.631863 +vn 0.000000 -0.683864 0.729609 +vn 0.270243 -0.841351 0.468078 +vn 0.468077 -0.841351 0.270245 +vn 0.631864 -0.683861 0.364804 +vn 0.364802 -0.683863 0.631863 +vn 0.468077 -0.841351 0.270245 +vn 0.540491 -0.841350 0.000000 +vn 0.729613 -0.683861 0.000000 +vn 0.631864 -0.683861 0.364804 +vn 0.540491 -0.841350 0.000000 +vn 0.468078 -0.841351 -0.270244 +vn 0.631864 -0.683861 -0.364805 +vn 0.729613 -0.683861 0.000000 +vn 0.631864 -0.683861 -0.364805 +vn 0.364803 -0.683861 -0.631865 +vn 0.438134 -0.481821 -0.758872 +vn 0.758869 -0.481825 -0.438135 +vn 0.364803 -0.683861 -0.631865 +vn 0.000000 -0.683861 -0.729612 +vn 0.000000 -0.481821 -0.876270 +vn 0.438134 -0.481821 -0.758872 +vn 0.000000 -0.683861 -0.729612 +vn -0.364808 -0.683861 -0.631862 +vn -0.438134 -0.481822 -0.758872 +vn 0.000000 -0.481821 -0.876270 +vn -0.364808 -0.683861 -0.631862 +vn -0.631860 -0.683863 -0.364806 +vn -0.758870 -0.481823 -0.438136 +vn -0.438134 -0.481822 -0.758872 +vn -0.631860 -0.683863 -0.364806 +vn -0.729609 -0.683865 0.000000 +vn -0.876268 -0.481824 0.000000 +vn -0.758870 -0.481823 -0.438136 +vn -0.729609 -0.683865 0.000000 +vn -0.631860 -0.683864 0.364806 +vn -0.758870 -0.481823 0.438136 +vn -0.876268 -0.481824 0.000000 +vn -0.631860 -0.683864 0.364806 +vn -0.364807 -0.683863 0.631860 +vn -0.438134 -0.481822 0.758872 +vn -0.758870 -0.481823 0.438136 +vn -0.364807 -0.683863 0.631860 +vn 0.000000 -0.683864 0.729609 +vn 0.000000 -0.481822 0.876269 +vn -0.438134 -0.481822 0.758872 +vn 0.000000 -0.683864 0.729609 +vn 0.364802 -0.683863 0.631863 +vn 0.438134 -0.481821 0.758872 +vn 0.000000 -0.481822 0.876269 +vn 0.364802 -0.683863 0.631863 +vn 0.631864 -0.683861 0.364804 +vn 0.758870 -0.481825 0.438135 +vn 0.438134 -0.481821 0.758872 +vn 0.631864 -0.683861 0.364804 +vn 0.729613 -0.683861 0.000000 +vn 0.876266 -0.481828 0.000000 +vn 0.758870 -0.481825 0.438135 +vn 0.729613 -0.683861 0.000000 +vn 0.631864 -0.683861 -0.364805 +vn 0.758869 -0.481825 -0.438135 +vn 0.876266 -0.481828 0.000000 +vn 0.758869 -0.481825 -0.438135 +vn 0.438134 -0.481821 -0.758872 +vn 0.484270 -0.248841 -0.838785 +vn 0.838785 -0.248836 -0.484273 +vn 0.438134 -0.481821 -0.758872 +vn 0.000000 -0.481821 -0.876270 +vn -0.000002 -0.248841 -0.968544 +vn 0.484270 -0.248841 -0.838785 +vn 0.000000 -0.481821 -0.876270 +vn -0.438134 -0.481822 -0.758872 +vn -0.484274 -0.248839 -0.838784 +vn -0.000002 -0.248841 -0.968544 +vn -0.438134 -0.481822 -0.758872 +vn -0.758870 -0.481823 -0.438136 +vn -0.838784 -0.248838 -0.484274 +vn -0.484274 -0.248839 -0.838784 +vn -0.758870 -0.481823 -0.438136 +vn -0.876268 -0.481824 0.000000 +vn -0.968546 -0.248836 0.000000 +vn -0.838784 -0.248838 -0.484274 +vn -0.876268 -0.481824 0.000000 +vn -0.758870 -0.481823 0.438136 +vn -0.838785 -0.248844 0.484270 +vn -0.968546 -0.248836 0.000000 +vn -0.758870 -0.481823 0.438136 +vn -0.438134 -0.481822 0.758872 +vn -0.484275 -0.248853 0.838779 +vn -0.838785 -0.248844 0.484270 +vn -0.438134 -0.481822 0.758872 +vn 0.000000 -0.481822 0.876269 +vn -0.000002 -0.248857 0.968540 +vn -0.484275 -0.248853 0.838779 +vn 0.000000 -0.481822 0.876269 +vn 0.438134 -0.481821 0.758872 +vn 0.484271 -0.248855 0.838781 +vn -0.000002 -0.248857 0.968540 +vn 0.438134 -0.481821 0.758872 +vn 0.758870 -0.481825 0.438135 +vn 0.838786 -0.248843 0.484269 +vn 0.484271 -0.248855 0.838781 +vn 0.758870 -0.481825 0.438135 +vn 0.876266 -0.481828 0.000000 +vn 0.968547 -0.248832 0.000000 +vn 0.838786 -0.248843 0.484269 +vn 0.876266 -0.481828 0.000000 +vn 0.758869 -0.481825 -0.438135 +vn 0.838785 -0.248836 -0.484273 +vn 0.968547 -0.248832 0.000000 +vn 0.838785 -0.248836 -0.484273 +vn 0.484270 -0.248841 -0.838785 +vn 0.499953 -0.013768 -0.865943 +vn 0.865943 -0.013768 -0.499953 +vn 0.484270 -0.248841 -0.838785 +vn -0.000002 -0.248841 -0.968544 +vn 0.000000 -0.013768 -0.999905 +vn 0.499953 -0.013768 -0.865943 +vn -0.000002 -0.248841 -0.968544 +vn -0.484274 -0.248839 -0.838784 +vn -0.499953 -0.013768 -0.865943 +vn 0.000000 -0.013768 -0.999905 +vn -0.484274 -0.248839 -0.838784 +vn -0.838784 -0.248838 -0.484274 +vn -0.865943 -0.013768 -0.499953 +vn -0.499953 -0.013768 -0.865943 +vn -0.838784 -0.248838 -0.484274 +vn -0.968546 -0.248836 0.000000 +vn -0.999905 -0.013768 0.000000 +vn -0.865943 -0.013768 -0.499953 +vn -0.968546 -0.248836 0.000000 +vn -0.838785 -0.248844 0.484270 +vn -0.865948 -0.013768 0.499945 +vn -0.999905 -0.013768 0.000000 +vn -0.838785 -0.248844 0.484270 +vn -0.484275 -0.248853 0.838779 +vn -0.499953 -0.013768 0.865943 +vn -0.865948 -0.013768 0.499945 +vn -0.484275 -0.248853 0.838779 +vn -0.000002 -0.248857 0.968540 +vn 0.000000 -0.013768 0.999905 +vn -0.499953 -0.013768 0.865943 +vn -0.000002 -0.248857 0.968540 +vn 0.484271 -0.248855 0.838781 +vn 0.499953 -0.013768 0.865943 +vn 0.000000 -0.013768 0.999905 +vn 0.484271 -0.248855 0.838781 +vn 0.838786 -0.248843 0.484269 +vn 0.865948 -0.013768 0.499945 +vn 0.499953 -0.013768 0.865943 +vn 0.838786 -0.248843 0.484269 +vn 0.968547 -0.248832 0.000000 +vn 0.999905 -0.013768 0.000000 +vn 0.865948 -0.013768 0.499945 +vn 0.968547 -0.248832 0.000000 +vn 0.838785 -0.248836 -0.484273 +vn 0.865943 -0.013768 -0.499953 +vn 0.999905 -0.013768 0.000000 +vn 0.167291 -0.942366 -0.289760 +vn 0.289759 -0.942366 -0.167292 +vn -0.000001 -1.000000 0.000000 +vn 0.000000 -0.942365 -0.334586 +vn 0.167291 -0.942366 -0.289760 +vn -0.000001 -1.000000 0.000000 +vn -0.167296 -0.942365 -0.289759 +vn 0.000000 -0.942365 -0.334586 +vn -0.000001 -1.000000 0.000000 +vn -0.289761 -0.942365 -0.167293 +vn -0.167296 -0.942365 -0.289759 +vn -0.000001 -1.000000 0.000000 +vn -0.334585 -0.942366 0.000000 +vn -0.289761 -0.942365 -0.167293 +vn -0.000001 -1.000000 0.000000 +vn -0.289760 -0.942365 0.167294 +vn -0.334585 -0.942366 0.000000 +vn -0.000001 -1.000000 0.000000 +vn -0.167296 -0.942365 0.289759 +vn -0.289760 -0.942365 0.167294 +vn -0.000001 -1.000000 0.000000 +vn 0.000000 -0.942365 0.334587 +vn -0.167296 -0.942365 0.289759 +vn -0.000001 -1.000000 0.000000 +vn 0.167291 -0.942366 0.289760 +vn 0.000000 -0.942365 0.334587 +vn -0.000001 -1.000000 0.000000 +vn 0.289759 -0.942366 0.167293 +vn 0.167291 -0.942366 0.289760 +vn -0.000001 -1.000000 0.000000 +vn 0.334583 -0.942366 0.000000 +vn 0.289759 -0.942366 0.167293 +vn -0.000001 -1.000000 0.000000 +vn 0.289759 -0.942366 -0.167292 +vn 0.334583 -0.942366 0.000000 +vn -0.000001 -1.000000 0.000000 +vn 0.289764 -0.942363 -0.167297 +vn 0.167290 -0.942363 -0.289770 +vn 0.270246 -0.841346 -0.468085 +vn 0.468091 -0.841342 -0.270247 +vn 0.167290 -0.942363 -0.289770 +vn 0.000000 -0.942361 -0.334597 +vn 0.000000 -0.841347 -0.540496 +vn 0.270246 -0.841346 -0.468085 +vn 0.000000 -0.942361 -0.334597 +vn -0.167306 -0.942360 -0.289769 +vn -0.270251 -0.841346 -0.468082 +vn 0.000000 -0.841347 -0.540496 +vn -0.167306 -0.942360 -0.289769 +vn -0.289775 -0.942361 -0.167291 +vn -0.468085 -0.841347 -0.270243 +vn -0.270251 -0.841346 -0.468082 +vn -0.289775 -0.942361 -0.167291 +vn -0.334596 -0.942362 0.000000 +vn -0.540496 -0.841347 0.000000 +vn -0.468085 -0.841347 -0.270243 +vn -0.334596 -0.942362 0.000000 +vn -0.289774 -0.942361 0.167291 +vn -0.468085 -0.841348 0.270243 +vn -0.540496 -0.841347 0.000000 +vn -0.289774 -0.942361 0.167291 +vn -0.167306 -0.942360 0.289769 +vn -0.270251 -0.841346 0.468083 +vn -0.468085 -0.841348 0.270243 +vn -0.167306 -0.942360 0.289769 +vn 0.000000 -0.942361 0.334598 +vn 0.000000 -0.841347 0.540496 +vn -0.270251 -0.841346 0.468083 +vn 0.000000 -0.942361 0.334598 +vn 0.167291 -0.942363 0.289770 +vn 0.270246 -0.841346 0.468085 +vn 0.000000 -0.841347 0.540496 +vn 0.167291 -0.942363 0.289770 +vn 0.289763 -0.942364 0.167296 +vn 0.468091 -0.841342 0.270247 +vn 0.270246 -0.841346 0.468085 +vn 0.289763 -0.942364 0.167296 +vn 0.334592 -0.942363 0.000000 +vn 0.540505 -0.841341 0.000000 +vn 0.468091 -0.841342 0.270247 +vn 0.334592 -0.942363 0.000000 +vn 0.289764 -0.942363 -0.167297 +vn 0.468091 -0.841342 -0.270247 +vn 0.540505 -0.841341 0.000000 +vn 0.468091 -0.841342 -0.270247 +vn 0.270246 -0.841346 -0.468085 +vn 0.364817 -0.683841 -0.631879 +vn 0.631879 -0.683841 -0.364817 +vn 0.270246 -0.841346 -0.468085 +vn 0.000000 -0.841347 -0.540496 +vn 0.000000 -0.683847 -0.729626 +vn 0.364817 -0.683841 -0.631879 +vn 0.000000 -0.841347 -0.540496 +vn -0.270251 -0.841346 -0.468082 +vn -0.364815 -0.683845 -0.631875 +vn 0.000000 -0.683847 -0.729626 +vn -0.270251 -0.841346 -0.468082 +vn -0.468085 -0.841347 -0.270243 +vn -0.631876 -0.683846 -0.364810 +vn -0.364815 -0.683845 -0.631875 +vn -0.468085 -0.841347 -0.270243 +vn -0.540496 -0.841347 0.000000 +vn -0.729621 -0.683852 0.000000 +vn -0.631876 -0.683846 -0.364810 +vn -0.540496 -0.841347 0.000000 +vn -0.468085 -0.841348 0.270243 +vn -0.631876 -0.683846 0.364810 +vn -0.729621 -0.683852 0.000000 +vn -0.468085 -0.841348 0.270243 +vn -0.270251 -0.841346 0.468083 +vn -0.364815 -0.683845 0.631875 +vn -0.631876 -0.683846 0.364810 +vn -0.270251 -0.841346 0.468083 +vn 0.000000 -0.841347 0.540496 +vn 0.000000 -0.683847 0.729626 +vn -0.364815 -0.683845 0.631875 +vn 0.000000 -0.841347 0.540496 +vn 0.270246 -0.841346 0.468085 +vn 0.364817 -0.683841 0.631878 +vn 0.000000 -0.683847 0.729626 +vn 0.270246 -0.841346 0.468085 +vn 0.468091 -0.841342 0.270247 +vn 0.631879 -0.683841 0.364816 +vn 0.364817 -0.683841 0.631878 +vn 0.468091 -0.841342 0.270247 +vn 0.540505 -0.841341 0.000000 +vn 0.729628 -0.683844 0.000000 +vn 0.631879 -0.683841 0.364816 +vn 0.540505 -0.841341 0.000000 +vn 0.468091 -0.841342 -0.270247 +vn 0.631879 -0.683841 -0.364817 +vn 0.729628 -0.683844 0.000000 +vn 0.631879 -0.683841 -0.364817 +vn 0.364817 -0.683841 -0.631879 +vn 0.438132 -0.481806 -0.758883 +vn 0.758871 -0.481815 -0.438144 +vn 0.364817 -0.683841 -0.631879 +vn 0.000000 -0.683847 -0.729626 +vn 0.000000 -0.481808 -0.876277 +vn 0.438132 -0.481806 -0.758883 +vn 0.000000 -0.683847 -0.729626 +vn -0.364815 -0.683845 -0.631875 +vn -0.438141 -0.481808 -0.758876 +vn 0.000000 -0.481808 -0.876277 +vn -0.364815 -0.683845 -0.631875 +vn -0.631876 -0.683846 -0.364810 +vn -0.758876 -0.481807 -0.438144 +vn -0.438141 -0.481808 -0.758876 +vn -0.631876 -0.683846 -0.364810 +vn -0.729621 -0.683852 0.000000 +vn -0.876278 -0.481805 0.000000 +vn -0.758876 -0.481807 -0.438144 +vn -0.729621 -0.683852 0.000000 +vn -0.631876 -0.683846 0.364810 +vn -0.758876 -0.481807 0.438144 +vn -0.876278 -0.481805 0.000000 +vn -0.631876 -0.683846 0.364810 +vn -0.364815 -0.683845 0.631875 +vn -0.438142 -0.481808 0.758876 +vn -0.758876 -0.481807 0.438144 +vn -0.364815 -0.683845 0.631875 +vn 0.000000 -0.683847 0.729626 +vn 0.000000 -0.481808 0.876277 +vn -0.438142 -0.481808 0.758876 +vn 0.000000 -0.683847 0.729626 +vn 0.364817 -0.683841 0.631878 +vn 0.438132 -0.481807 0.758883 +vn 0.000000 -0.481808 0.876277 +vn 0.364817 -0.683841 0.631878 +vn 0.631879 -0.683841 0.364816 +vn 0.758871 -0.481814 0.438144 +vn 0.438132 -0.481807 0.758883 +vn 0.631879 -0.683841 0.364816 +vn 0.729628 -0.683844 0.000000 +vn 0.876269 -0.481823 0.000000 +vn 0.758871 -0.481814 0.438144 +vn 0.729628 -0.683844 0.000000 +vn 0.631879 -0.683841 -0.364817 +vn 0.758871 -0.481815 -0.438144 +vn 0.876269 -0.481823 0.000000 +vn 0.758871 -0.481815 -0.438144 +vn 0.438132 -0.481806 -0.758883 +vn 0.484267 -0.248839 -0.838788 +vn 0.838782 -0.248835 -0.484279 +vn 0.438132 -0.481806 -0.758883 +vn 0.000000 -0.481808 -0.876277 +vn 0.000000 -0.248833 -0.968547 +vn 0.484267 -0.248839 -0.838788 +vn 0.000000 -0.481808 -0.876277 +vn -0.438141 -0.481808 -0.758876 +vn -0.484281 -0.248830 -0.838782 +vn 0.000000 -0.248833 -0.968547 +vn -0.438141 -0.481808 -0.758876 +vn -0.758876 -0.481807 -0.438144 +vn -0.838790 -0.248820 -0.484273 +vn -0.484281 -0.248830 -0.838782 +vn -0.758876 -0.481807 -0.438144 +vn -0.876278 -0.481805 0.000000 +vn -0.968552 -0.248813 0.000000 +vn -0.838790 -0.248820 -0.484273 +vn -0.876278 -0.481805 0.000000 +vn -0.758876 -0.481807 0.438144 +vn -0.838789 -0.248820 0.484274 +vn -0.968552 -0.248813 0.000000 +vn -0.758876 -0.481807 0.438144 +vn -0.438142 -0.481808 0.758876 +vn -0.484281 -0.248830 0.838782 +vn -0.838789 -0.248820 0.484274 +vn -0.438142 -0.481808 0.758876 +vn 0.000000 -0.481808 0.876277 +vn 0.000000 -0.248833 0.968547 +vn -0.484281 -0.248830 0.838782 +vn 0.000000 -0.481808 0.876277 +vn 0.438132 -0.481807 0.758883 +vn 0.484267 -0.248839 0.838788 +vn 0.000000 -0.248833 0.968547 +vn 0.438132 -0.481807 0.758883 +vn 0.758871 -0.481814 0.438144 +vn 0.838782 -0.248835 0.484278 +vn 0.484267 -0.248839 0.838788 +vn 0.758871 -0.481814 0.438144 +vn 0.876269 -0.481823 0.000000 +vn 0.968548 -0.248826 0.000000 +vn 0.838782 -0.248835 0.484278 +vn 0.876269 -0.481823 0.000000 +vn 0.758871 -0.481815 -0.438144 +vn 0.838782 -0.248835 -0.484279 +vn 0.968548 -0.248826 0.000000 +vn 0.838782 -0.248835 -0.484279 +vn 0.484267 -0.248839 -0.838788 +vn 0.499985 -0.007713 -0.866000 +vn 0.866000 -0.007713 -0.499985 +vn 0.484267 -0.248839 -0.838788 +vn 0.000000 -0.248833 -0.968547 +vn 0.000000 -0.007713 -0.999970 +vn 0.499985 -0.007713 -0.866000 +vn 0.000000 -0.248833 -0.968547 +vn -0.484281 -0.248830 -0.838782 +vn -0.499985 -0.007713 -0.866000 +vn 0.000000 -0.007713 -0.999970 +vn -0.484281 -0.248830 -0.838782 +vn -0.838790 -0.248820 -0.484273 +vn -0.866000 -0.007713 -0.499985 +vn -0.499985 -0.007713 -0.866000 +vn -0.838790 -0.248820 -0.484273 +vn -0.968552 -0.248813 0.000000 +vn -0.999970 -0.007713 0.000000 +vn -0.866000 -0.007713 -0.499985 +vn -0.968552 -0.248813 0.000000 +vn -0.838789 -0.248820 0.484274 +vn -0.866000 -0.007713 0.499985 +vn -0.999970 -0.007713 0.000000 +vn -0.838789 -0.248820 0.484274 +vn -0.484281 -0.248830 0.838782 +vn -0.499985 -0.007713 0.866000 +vn -0.866000 -0.007713 0.499985 +vn -0.484281 -0.248830 0.838782 +vn 0.000000 -0.248833 0.968547 +vn 0.000000 -0.007713 0.999970 +vn -0.499985 -0.007713 0.866000 +vn 0.000000 -0.248833 0.968547 +vn 0.484267 -0.248839 0.838788 +vn 0.499985 -0.007713 0.866000 +vn 0.000000 -0.007713 0.999970 +vn 0.484267 -0.248839 0.838788 +vn 0.838782 -0.248835 0.484278 +vn 0.866000 -0.007713 0.499985 +vn 0.499985 -0.007713 0.866000 +vn 0.838782 -0.248835 0.484278 +vn 0.968548 -0.248826 0.000000 +vn 0.999970 -0.007713 0.000000 +vn 0.866000 -0.007713 0.499985 +vn 0.968548 -0.248826 0.000000 +vn 0.838782 -0.248835 -0.484279 +vn 0.866000 -0.007713 -0.499985 +vn 0.999970 -0.007713 0.000000 +vn 0.167290 -0.942363 -0.289770 +vn 0.289764 -0.942363 -0.167297 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -0.942361 -0.334597 +vn 0.167290 -0.942363 -0.289770 +vn 0.000000 -1.000000 0.000000 +vn -0.167306 -0.942360 -0.289769 +vn 0.000000 -0.942361 -0.334597 +vn 0.000000 -1.000000 0.000000 +vn -0.289775 -0.942361 -0.167291 +vn -0.167306 -0.942360 -0.289769 +vn 0.000000 -1.000000 0.000000 +vn -0.334596 -0.942362 0.000000 +vn -0.289775 -0.942361 -0.167291 +vn 0.000000 -1.000000 0.000000 +vn -0.289774 -0.942361 0.167291 +vn -0.334596 -0.942362 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.167306 -0.942360 0.289769 +vn -0.289774 -0.942361 0.167291 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 -0.942361 0.334598 +vn -0.167306 -0.942360 0.289769 +vn 0.000000 -1.000000 0.000000 +vn 0.167291 -0.942363 0.289770 +vn 0.000000 -0.942361 0.334598 +vn 0.000000 -1.000000 0.000000 +vn 0.289763 -0.942364 0.167296 +vn 0.167291 -0.942363 0.289770 +vn 0.000000 -1.000000 0.000000 +vn 0.334592 -0.942363 0.000000 +vn 0.289763 -0.942364 0.167296 +vn 0.000000 -1.000000 0.000000 +vn 0.289764 -0.942363 -0.167297 +vn 0.334592 -0.942363 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.289764 0.942364 -0.167296 +vn 0.468092 0.841342 -0.270247 +vn 0.270246 0.841346 -0.468086 +vn 0.167290 0.942363 -0.289769 +vn 0.167290 0.942363 -0.289769 +vn 0.270246 0.841346 -0.468086 +vn -0.000000 0.841346 -0.540497 +vn 0.000000 0.942361 -0.334598 +vn 0.000000 0.942361 -0.334598 +vn -0.000000 0.841346 -0.540497 +vn -0.270250 0.841346 -0.468083 +vn -0.167306 0.942361 -0.289767 +vn -0.167306 0.942361 -0.289767 +vn -0.270250 0.841346 -0.468083 +vn -0.468085 0.841348 -0.270243 +vn -0.289775 0.942361 -0.167290 +vn -0.289775 0.942361 -0.167290 +vn -0.468085 0.841348 -0.270243 +vn -0.540496 0.841347 0.000000 +vn -0.334596 0.942362 -0.000000 +vn -0.334596 0.942362 -0.000000 +vn -0.540496 0.841347 0.000000 +vn -0.468085 0.841347 0.270243 +vn -0.289775 0.942361 0.167291 +vn -0.289775 0.942361 0.167291 +vn -0.468085 0.841347 0.270243 +vn -0.270251 0.841346 0.468083 +vn -0.167306 0.942361 0.289768 +vn -0.167306 0.942361 0.289768 +vn -0.270251 0.841346 0.468083 +vn 0.000000 0.841346 0.540497 +vn 0.000000 0.942361 0.334598 +vn 0.000000 0.942361 0.334598 +vn 0.000000 0.841346 0.540497 +vn 0.270246 0.841346 0.468086 +vn 0.167291 0.942363 0.289768 +vn 0.167291 0.942363 0.289768 +vn 0.270246 0.841346 0.468086 +vn 0.468092 0.841342 0.270247 +vn 0.289764 0.942364 0.167295 +vn 0.289764 0.942364 0.167295 +vn 0.468092 0.841342 0.270247 +vn 0.540505 0.841341 0.000000 +vn 0.334592 0.942363 0.000000 +vn 0.334592 0.942363 0.000000 +vn 0.540505 0.841341 0.000000 +vn 0.468092 0.841342 -0.270247 +vn 0.289764 0.942364 -0.167296 +vn 0.468092 0.841342 -0.270247 +vn 0.631860 0.683864 -0.364805 +vn 0.364806 0.683864 -0.631860 +vn 0.270246 0.841346 -0.468086 +vn 0.270246 0.841346 -0.468086 +vn 0.364806 0.683864 -0.631860 +vn -0.000000 0.683869 -0.729605 +vn -0.000000 0.841346 -0.540497 +vn -0.000000 0.841346 -0.540497 +vn -0.000000 0.683869 -0.729605 +vn -0.364804 0.683868 -0.631857 +vn -0.270250 0.841346 -0.468083 +vn -0.270250 0.841346 -0.468083 +vn -0.364804 0.683868 -0.631857 +vn -0.631858 0.683869 -0.364800 +vn -0.468085 0.841348 -0.270243 +vn -0.468085 0.841348 -0.270243 +vn -0.631858 0.683869 -0.364800 +vn -0.729599 0.683875 -0.000000 +vn -0.540496 0.841347 0.000000 +vn -0.540496 0.841347 0.000000 +vn -0.729599 0.683875 -0.000000 +vn -0.631858 0.683869 0.364799 +vn -0.468085 0.841347 0.270243 +vn -0.468085 0.841347 0.270243 +vn -0.631858 0.683869 0.364799 +vn -0.364804 0.683868 0.631857 +vn -0.270251 0.841346 0.468083 +vn -0.270251 0.841346 0.468083 +vn -0.364804 0.683868 0.631857 +vn 0.000000 0.683869 0.729605 +vn 0.000000 0.841346 0.540497 +vn 0.000000 0.841346 0.540497 +vn 0.000000 0.683869 0.729605 +vn 0.364806 0.683864 0.631860 +vn 0.270246 0.841346 0.468086 +vn 0.270246 0.841346 0.468086 +vn 0.364806 0.683864 0.631860 +vn 0.631860 0.683864 0.364805 +vn 0.468092 0.841342 0.270247 +vn 0.468092 0.841342 0.270247 +vn 0.631860 0.683864 0.364805 +vn 0.729607 0.683867 0.000000 +vn 0.540505 0.841341 0.000000 +vn 0.540505 0.841341 0.000000 +vn 0.729607 0.683867 0.000000 +vn 0.631860 0.683864 -0.364805 +vn 0.468092 0.841342 -0.270247 +vn 0.631860 0.683864 -0.364805 +vn 0.758856 0.481845 -0.438135 +vn 0.438124 0.481837 -0.758868 +vn 0.364806 0.683864 -0.631860 +vn 0.364806 0.683864 -0.631860 +vn 0.438124 0.481837 -0.758868 +vn -0.000000 0.481839 -0.876260 +vn -0.000000 0.683869 -0.729605 +vn -0.000000 0.683869 -0.729605 +vn -0.000000 0.481839 -0.876260 +vn -0.438133 0.481839 -0.758861 +vn -0.364804 0.683868 -0.631857 +vn -0.364804 0.683868 -0.631857 +vn -0.438133 0.481839 -0.758861 +vn -0.758861 0.481837 -0.438135 +vn -0.631858 0.683869 -0.364800 +vn -0.631858 0.683869 -0.364800 +vn -0.758861 0.481837 -0.438135 +vn -0.876261 0.481836 0.000000 +vn -0.729599 0.683875 -0.000000 +vn -0.729599 0.683875 -0.000000 +vn -0.876261 0.481836 0.000000 +vn -0.758861 0.481838 0.438135 +vn -0.631858 0.683869 0.364799 +vn -0.631858 0.683869 0.364799 +vn -0.758861 0.481838 0.438135 +vn -0.438133 0.481839 0.758861 +vn -0.364804 0.683868 0.631857 +vn -0.364804 0.683868 0.631857 +vn -0.438133 0.481839 0.758861 +vn 0.000000 0.481839 0.876260 +vn 0.000000 0.683869 0.729605 +vn 0.000000 0.683869 0.729605 +vn 0.000000 0.481839 0.876260 +vn 0.438124 0.481838 0.758868 +vn 0.364806 0.683864 0.631860 +vn 0.364806 0.683864 0.631860 +vn 0.438124 0.481838 0.758868 +vn 0.758857 0.481845 0.438135 +vn 0.631860 0.683864 0.364805 +vn 0.631860 0.683864 0.364805 +vn 0.758857 0.481845 0.438135 +vn 0.876252 0.481853 0.000000 +vn 0.729607 0.683867 0.000000 +vn 0.729607 0.683867 0.000000 +vn 0.876252 0.481853 0.000000 +vn 0.758856 0.481845 -0.438135 +vn 0.631860 0.683864 -0.364805 +vn 0.758856 0.481845 -0.438135 +vn 0.838785 0.248824 -0.484280 +vn 0.484268 0.248828 -0.838790 +vn 0.438124 0.481837 -0.758868 +vn 0.438124 0.481837 -0.758868 +vn 0.484268 0.248828 -0.838790 +vn -0.000000 0.248822 -0.968549 +vn -0.000000 0.481839 -0.876260 +vn -0.000000 0.481839 -0.876260 +vn -0.000000 0.248822 -0.968549 +vn -0.484282 0.248819 -0.838785 +vn -0.438133 0.481839 -0.758861 +vn -0.438133 0.481839 -0.758861 +vn -0.484282 0.248819 -0.838785 +vn -0.838792 0.248809 -0.484275 +vn -0.758861 0.481837 -0.438135 +vn -0.758861 0.481837 -0.438135 +vn -0.838792 0.248809 -0.484275 +vn -0.968555 0.248801 0.000000 +vn -0.876261 0.481836 0.000000 +vn -0.876261 0.481836 0.000000 +vn -0.968555 0.248801 0.000000 +vn -0.838792 0.248809 0.484275 +vn -0.758861 0.481838 0.438135 +vn -0.758861 0.481838 0.438135 +vn -0.838792 0.248809 0.484275 +vn -0.484282 0.248819 0.838785 +vn -0.438133 0.481839 0.758861 +vn -0.438133 0.481839 0.758861 +vn -0.484282 0.248819 0.838785 +vn 0.000000 0.248822 0.968549 +vn 0.000000 0.481839 0.876260 +vn 0.000000 0.481839 0.876260 +vn 0.000000 0.248822 0.968549 +vn 0.484268 0.248828 0.838790 +vn 0.438124 0.481838 0.758868 +vn 0.438124 0.481838 0.758868 +vn 0.484268 0.248828 0.838790 +vn 0.838785 0.248824 0.484280 +vn 0.758857 0.481845 0.438135 +vn 0.758857 0.481845 0.438135 +vn 0.838785 0.248824 0.484280 +vn 0.968551 0.248815 0.000000 +vn 0.876252 0.481853 0.000000 +vn 0.876252 0.481853 0.000000 +vn 0.968551 0.248815 0.000000 +vn 0.838785 0.248824 -0.484280 +vn 0.758856 0.481845 -0.438135 +vn 0.838785 0.248824 -0.484280 +vn 0.866000 0.007713 -0.499985 +vn 0.499985 0.007713 -0.866000 +vn 0.484268 0.248828 -0.838790 +vn 0.484268 0.248828 -0.838790 +vn 0.499985 0.007713 -0.866000 +vn 0.000000 0.007713 -0.999970 +vn -0.000000 0.248822 -0.968549 +vn -0.000000 0.248822 -0.968549 +vn 0.000000 0.007713 -0.999970 +vn -0.499985 0.007713 -0.866000 +vn -0.484282 0.248819 -0.838785 +vn -0.484282 0.248819 -0.838785 +vn -0.499985 0.007713 -0.866000 +vn -0.866000 0.007713 -0.499985 +vn -0.838792 0.248809 -0.484275 +vn -0.838792 0.248809 -0.484275 +vn -0.866000 0.007713 -0.499985 +vn -0.999970 0.007713 0.000000 +vn -0.968555 0.248801 0.000000 +vn -0.968555 0.248801 0.000000 +vn -0.999970 0.007713 0.000000 +vn -0.866000 0.007713 0.499985 +vn -0.838792 0.248809 0.484275 +vn -0.838792 0.248809 0.484275 +vn -0.866000 0.007713 0.499985 +vn -0.499985 0.007713 0.866000 +vn -0.484282 0.248819 0.838785 +vn -0.484282 0.248819 0.838785 +vn -0.499985 0.007713 0.866000 +vn 0.000000 0.007713 0.999970 +vn 0.000000 0.248822 0.968549 +vn 0.000000 0.248822 0.968549 +vn 0.000000 0.007713 0.999970 +vn 0.499985 0.007713 0.866000 +vn 0.484268 0.248828 0.838790 +vn 0.484268 0.248828 0.838790 +vn 0.499985 0.007713 0.866000 +vn 0.866000 0.007713 0.499985 +vn 0.838785 0.248824 0.484280 +vn 0.838785 0.248824 0.484280 +vn 0.866000 0.007713 0.499985 +vn 0.999970 0.007713 0.000000 +vn 0.968551 0.248815 0.000000 +vn 0.968551 0.248815 0.000000 +vn 0.999970 0.007713 0.000000 +vn 0.866000 0.007713 -0.499985 +vn 0.838785 0.248824 -0.484280 +vn 0.167290 0.942363 -0.289769 +vn 0.000000 1.000000 0.000000 +vn 0.289764 0.942364 -0.167296 +vn 0.000000 0.942361 -0.334598 +vn 0.000000 1.000000 0.000000 +vn 0.167290 0.942363 -0.289769 +vn -0.167306 0.942361 -0.289767 +vn 0.000000 1.000000 0.000000 +vn 0.000000 0.942361 -0.334598 +vn -0.289775 0.942361 -0.167290 +vn 0.000000 1.000000 0.000000 +vn -0.167306 0.942361 -0.289767 +vn -0.334596 0.942362 -0.000000 +vn 0.000000 1.000000 0.000000 +vn -0.289775 0.942361 -0.167290 +vn -0.289775 0.942361 0.167291 +vn 0.000000 1.000000 0.000000 +vn -0.334596 0.942362 -0.000000 +vn -0.167306 0.942361 0.289768 +vn 0.000000 1.000000 0.000000 +vn -0.289775 0.942361 0.167291 +vn 0.000000 0.942361 0.334598 +vn 0.000000 1.000000 0.000000 +vn -0.167306 0.942361 0.289768 +vn 0.167291 0.942363 0.289768 +vn 0.000000 1.000000 0.000000 +vn 0.000000 0.942361 0.334598 +vn 0.289764 0.942364 0.167295 +vn 0.000000 1.000000 0.000000 +vn 0.167291 0.942363 0.289768 +vn 0.334592 0.942363 0.000000 +vn 0.000000 1.000000 0.000000 +vn 0.289764 0.942364 0.167295 +vn 0.289764 0.942364 -0.167296 +vn 0.000000 1.000000 0.000000 +vn 0.334592 0.942363 0.000000 +s off +g Andy_GEO +usemtl initialShadingGroup +f 1/1/1 2/2/2 10/3/3 9/4/4 +f 2/2/5 3/5/6 11/6/7 10/3/8 +f 3/5/9 4/7/10 12/8/11 11/6/12 +f 4/9/13 5/10/14 13/11/15 12/12/16 +f 5/10/17 6/13/18 14/14/19 13/11/20 +f 6/13/21 7/15/22 15/16/23 14/14/24 +f 7/15/25 8/17/26 16/18/27 15/16/28 +f 8/17/29 1/1/30 9/4/31 16/18/32 +f 9/4/33 10/3/34 18/19/35 17/20/36 +f 10/3/37 11/6/38 19/21/39 18/19/40 +f 11/6/41 12/8/42 20/22/43 19/21/44 +f 12/12/45 13/11/46 21/23/47 20/24/48 +f 13/11/49 14/14/50 22/25/51 21/23/52 +f 14/14/53 15/16/54 23/26/55 22/25/56 +f 15/16/57 16/18/58 24/27/59 23/26/60 +f 16/18/61 9/4/62 17/20/63 24/27/64 +f 17/20/65 18/19/66 25/28/67 +f 18/19/68 19/21/69 25/29/70 +f 19/21/71 20/22/72 25/30/73 +f 20/24/74 21/23/75 25/31/76 +f 21/23/77 22/25/78 25/32/79 +f 22/25/80 23/26/81 25/33/82 +f 23/26/83 24/27/84 25/34/85 +f 24/27/86 17/20/87 25/35/88 +f 32/36/89 26/37/90 33/38/91 +f 34/39/92 27/40/93 26/37/94 32/36/95 +f 35/41/96 29/42/97 28/43/98 36/44/99 +f 37/45/100 29/42/101 35/41/102 +f 38/46/103 30/47/104 29/42/105 37/45/106 +f 33/38/107 26/37/108 30/47/109 38/46/110 +f 39/48/111 31/49/112 27/40/113 34/39/114 +f 36/44/115 28/43/116 31/49/117 39/48/118 +f 46/50/119 47/51/120 40/52/121 +f 48/53/122 46/50/123 40/52/124 41/54/125 +f 49/55/126 50/56/127 42/57/128 43/58/129 +f 51/59/130 49/55/131 43/58/132 +f 52/60/133 51/59/134 43/58/135 44/61/136 +f 47/51/137 52/60/138 44/61/139 40/52/140 +f 53/62/141 48/53/142 41/54/143 45/63/144 +f 50/56/145 53/62/146 45/63/147 42/57/148 +f 55/64/149 54/65/150 88/66/151 89/67/152 +f 56/68/153 55/64/154 89/67/155 90/69/156 +f 57/70/157 56/68/158 90/69/159 91/71/160 +f 58/72/161 57/70/162 91/71/163 92/73/164 +f 59/74/165 58/72/166 92/73/167 93/75/168 +f 60/76/169 59/74/170 93/75/171 94/77/172 +f 61/78/173 60/76/174 94/77/175 95/79/176 +f 62/80/177 61/78/178 95/79/179 96/81/180 +f 63/82/181 62/80/182 96/81/183 97/83/184 +f 64/84/185 63/82/186 97/83/187 98/85/188 +f 65/86/189 64/84/190 98/85/191 84/87/192 +f 66/88/193 65/86/194 84/87/195 85/89/196 +f 67/90/197 66/88/198 85/89/199 86/91/200 +f 68/92/201 67/90/202 86/91/203 87/93/204 +f 85/89/205 84/87/206 581/94/207 582/95/208 +f 86/91/209 85/89/210 582/95/211 583/96/212 +f 87/93/213 86/91/214 583/96/215 584/97/216 +f 89/67/217 88/66/218 598/98/219 599/99/220 +f 90/69/221 89/67/222 599/99/223 600/100/224 +f 91/71/225 90/69/226 600/100/227 601/101/228 +f 92/73/229 91/71/230 601/101/231 602/102/232 +f 93/75/233 92/73/234 602/102/235 603/103/236 +f 94/77/237 93/75/238 603/103/239 604/104/240 +f 95/79/241 94/77/242 604/104/243 577/105/244 +f 96/81/245 95/79/246 577/105/247 578/106/248 +f 97/83/249 96/81/250 578/106/251 579/107/252 +f 98/85/253 97/83/254 579/107/255 580/108/256 +f 84/87/257 98/85/258 580/108/259 581/94/260 +f 100/109/261 99/110/262 608/111/263 609/112/264 +f 101/113/265 100/109/266 609/112/267 610/114/268 +f 102/115/269 101/113/270 610/114/271 611/116/272 +f 103/117/273 102/115/274 611/116/275 612/118/276 +f 105/119/277 104/120/278 626/121/279 627/122/280 +f 106/123/281 105/119/282 627/122/283 628/124/284 +f 107/125/285 106/123/286 628/124/287 629/126/288 +f 108/127/289 107/125/290 629/126/291 630/128/292 +f 109/129/293 108/127/294 630/128/295 631/130/296 +f 110/131/297 109/129/298 631/130/299 632/132/300 +f 111/133/301 110/131/302 632/132/303 605/134/304 +f 112/135/305 111/133/306 605/134/307 606/136/308 +f 113/137/309 112/135/310 606/136/311 607/138/312 +f 608/111/313 99/110/314 113/137/315 607/138/316 +f 114/139/317 143/140/318 88/66/319 54/65/320 +f 115/141/321 144/142/322 143/140/323 114/139/324 +f 116/143/325 145/144/326 144/142/327 115/141/328 +f 117/145/329 146/146/330 145/144/331 116/143/332 +f 118/147/333 147/148/334 146/146/335 117/145/336 +f 119/149/337 148/150/338 147/148/339 118/147/340 +f 120/151/341 149/152/342 148/150/343 119/149/344 +f 121/153/345 150/154/346 149/152/347 120/151/348 +f 122/155/349 151/156/350 150/154/351 121/153/352 +f 123/157/353 152/158/354 151/156/355 122/155/356 +f 124/159/357 140/160/358 152/158/359 123/157/360 +f 125/161/361 141/162/362 140/160/363 124/159/364 +f 126/163/365 142/164/366 141/162/367 125/161/368 +f 68/92/369 87/93/370 142/164/371 126/163/372 +f 141/162/373 586/165/374 587/166/375 140/160/376 +f 142/164/377 585/167/378 586/165/379 141/162/380 +f 87/93/381 584/97/382 585/167/383 142/164/384 +f 143/140/385 597/168/386 598/98/387 88/66/388 +f 144/142/389 596/169/390 597/168/391 143/140/392 +f 145/144/393 595/170/394 596/169/395 144/142/396 +f 146/146/397 594/171/398 595/170/399 145/144/400 +f 147/148/401 593/172/402 594/171/403 146/146/404 +f 148/150/405 592/173/406 593/172/407 147/148/408 +f 149/152/409 591/174/410 592/173/411 148/150/412 +f 150/154/413 590/175/414 591/174/415 149/152/416 +f 151/156/417 589/176/418 590/175/419 150/154/420 +f 152/158/421 588/177/422 589/176/423 151/156/424 +f 140/160/425 587/166/426 588/177/427 152/158/428 +f 615/178/429 616/179/430 153/180/431 154/181/432 +f 614/182/433 615/178/434 154/181/435 155/183/436 +f 613/184/437 614/182/438 155/183/439 156/185/440 +f 103/117/441 612/118/442 613/184/443 156/185/444 +f 625/186/445 626/121/446 104/120/447 157/187/448 +f 624/188/449 625/186/450 157/187/451 158/189/452 +f 623/190/453 624/188/454 158/189/455 159/191/456 +f 622/192/457 623/190/458 159/191/459 160/193/460 +f 621/194/461 622/192/462 160/193/463 161/195/464 +f 620/196/465 621/194/466 161/195/467 162/197/468 +f 619/198/469 620/196/470 162/197/471 163/199/472 +f 618/200/473 619/198/474 163/199/475 164/201/476 +f 617/202/477 618/200/478 164/201/479 165/203/480 +f 153/180/481 616/179/482 617/202/483 165/203/484 +f 166/204/485 196/205/486 194/206/487 +f 166/204/488 194/206/489 192/207/490 +f 166/204/491 192/207/492 190/208/493 +f 166/204/494 190/208/495 188/209/496 +f 166/204/497 188/209/498 186/210/499 +f 166/204/500 186/210/501 184/211/502 +f 166/204/503 184/211/504 182/212/505 +f 166/204/506 182/212/507 180/213/508 +f 166/204/509 180/213/510 178/214/511 +f 166/204/512 178/214/513 176/215/514 +f 166/204/515 176/215/516 174/216/517 +f 166/204/518 174/216/519 172/217/520 +f 166/204/521 172/217/522 170/218/523 +f 166/204/524 170/218/525 167/219/526 +f 167/219/527 170/218/528 169/220/529 168/221/530 +f 54/222/531 55/223/532 193/224/533 195/225/534 +f 193/224/535 55/223/536 56/226/537 191/227/538 +f 56/226/539 57/228/540 189/229/541 191/227/542 +f 57/228/543 58/230/544 187/231/545 189/229/546 +f 58/230/547 59/232/548 185/233/549 187/231/550 +f 59/232/551 60/234/552 183/235/553 185/233/554 +f 60/234/555 61/236/556 181/237/557 183/235/558 +f 61/236/559 62/238/560 179/239/561 181/237/562 +f 62/238/563 63/240/564 177/241/565 179/239/566 +f 63/240/567 64/242/568 175/243/569 177/241/570 +f 64/242/571 65/244/572 173/245/573 175/243/574 +f 65/244/575 66/246/576 171/247/577 173/245/578 +f 66/246/579 67/248/580 169/249/581 171/247/582 +f 67/248/583 68/250/584 168/251/585 169/249/586 +f 114/252/587 54/253/588 195/254/589 221/255/590 +f 115/256/591 114/252/592 221/255/593 219/257/594 +f 116/258/595 115/256/596 219/257/597 217/259/598 +f 117/260/599 116/258/600 217/259/601 215/261/602 +f 118/262/603 117/260/604 215/261/605 213/263/606 +f 119/264/607 118/262/608 213/263/609 211/265/610 +f 120/266/611 119/264/612 211/265/613 209/267/614 +f 121/268/615 120/266/616 209/267/617 207/269/618 +f 122/270/619 121/268/620 207/269/621 205/271/622 +f 123/272/623 122/270/624 205/271/625 203/273/626 +f 124/274/627 123/272/628 203/273/629 201/275/630 +f 125/276/631 124/274/632 201/275/633 199/277/634 +f 126/278/635 125/276/636 199/277/637 197/279/638 +f 68/250/639 126/278/640 197/279/641 168/251/642 +f 166/204/643 167/219/644 198/280/645 +f 166/204/646 198/280/647 200/281/648 +f 166/204/649 200/281/650 202/282/651 +f 166/204/652 202/282/653 204/283/654 +f 166/204/655 204/283/656 206/284/657 +f 166/204/658 206/284/659 208/285/660 +f 166/204/661 208/285/662 210/286/663 +f 166/204/664 210/286/665 212/287/666 +f 166/204/667 212/287/668 214/288/669 +f 166/204/670 214/288/671 216/289/672 +f 166/204/673 216/289/674 218/290/675 +f 166/204/676 218/290/677 220/291/678 +f 166/204/679 220/291/680 222/292/681 +f 166/204/682 222/292/683 196/205/684 +f 170/218/685 172/217/686 171/293/687 169/220/688 +f 172/217/689 174/216/690 173/294/691 171/293/692 +f 174/216/693 176/215/694 175/295/695 173/294/696 +f 176/215/697 178/214/698 177/296/699 175/295/700 +f 178/214/701 180/213/702 179/297/703 177/296/704 +f 180/213/705 182/212/706 181/298/707 179/297/708 +f 182/212/709 184/211/710 183/299/711 181/298/712 +f 184/211/713 186/210/714 185/300/715 183/299/716 +f 186/210/717 188/209/718 187/301/719 185/300/720 +f 188/209/721 190/208/722 189/302/723 187/301/724 +f 190/208/725 192/207/726 191/303/727 189/302/728 +f 192/207/729 194/206/730 193/304/731 191/303/732 +f 194/206/733 196/205/734 195/305/735 193/304/736 +f 195/305/737 196/205/738 222/292/739 221/306/740 +f 198/280/741 167/219/742 168/221/743 197/307/744 +f 200/281/745 198/280/746 197/307/747 199/308/748 +f 202/282/749 200/281/750 199/308/751 201/309/752 +f 204/283/753 202/282/754 201/309/755 203/310/756 +f 206/284/757 204/283/758 203/310/759 205/311/760 +f 208/285/761 206/284/762 205/311/763 207/312/764 +f 210/286/765 208/285/766 207/312/767 209/313/768 +f 212/287/769 210/286/770 209/313/771 211/314/772 +f 214/288/773 212/287/774 211/314/775 213/315/776 +f 216/289/777 214/288/778 213/315/779 215/316/780 +f 218/290/781 216/289/782 215/316/783 217/317/784 +f 220/291/785 218/290/786 217/317/787 219/318/788 +f 222/292/789 220/291/790 219/318/791 221/306/792 +f 223/319/793 224/320/794 239/321/795 238/322/796 +f 224/320/797 225/323/798 240/324/799 239/321/800 +f 225/323/801 226/325/802 241/326/803 240/324/804 +f 226/325/805 227/327/806 242/328/807 241/326/808 +f 227/327/809 228/329/810 243/330/811 242/328/812 +f 228/329/813 229/331/814 244/332/815 243/330/816 +f 229/331/817 230/333/818 245/334/819 244/332/820 +f 230/333/821 231/335/822 246/336/823 245/334/824 +f 231/335/825 232/337/826 247/338/827 246/336/828 +f 232/337/829 233/339/830 248/340/831 247/338/832 +f 233/339/833 234/341/834 249/342/835 248/340/836 +f 234/341/837 235/343/838 250/344/839 249/342/840 +f 235/343/841 236/345/842 251/346/843 250/344/844 +f 236/345/845 237/347/846 252/348/847 251/346/848 +f 238/322/849 239/321/850 254/349/851 253/350/852 +f 239/321/853 240/324/854 255/351/855 254/349/856 +f 240/324/857 241/326/858 256/352/859 255/351/860 +f 241/326/861 242/328/862 257/353/863 256/352/864 +f 242/328/865 243/330/866 258/354/867 257/353/868 +f 243/330/869 244/332/870 259/355/871 258/354/872 +f 244/332/873 245/334/874 260/356/875 259/355/876 +f 245/334/877 246/336/878 261/357/879 260/356/880 +f 246/336/881 247/338/882 262/358/883 261/357/884 +f 247/338/885 248/340/886 263/359/887 262/358/888 +f 248/340/889 249/342/890 264/360/891 263/359/892 +f 249/342/893 250/344/894 265/361/895 264/360/896 +f 250/344/897 251/346/898 266/362/899 265/361/900 +f 251/346/901 252/348/902 267/363/903 266/362/904 +f 253/350/905 254/349/906 269/364/907 268/365/908 +f 254/349/909 255/351/910 270/366/911 269/364/912 +f 255/351/913 256/352/914 271/367/915 270/366/916 +f 256/352/917 257/353/918 272/368/919 271/367/920 +f 257/353/921 258/354/922 273/369/923 272/368/924 +f 258/354/925 259/355/926 274/370/927 273/369/928 +f 259/355/929 260/356/930 275/371/931 274/370/932 +f 260/356/933 261/357/934 276/372/935 275/371/936 +f 261/357/937 262/358/938 277/373/939 276/372/940 +f 262/358/941 263/359/942 278/374/943 277/373/944 +f 263/359/945 264/360/946 279/375/947 278/374/948 +f 264/360/949 265/361/950 280/376/951 279/375/952 +f 265/361/953 266/362/954 281/377/955 280/376/956 +f 266/362/957 267/363/958 282/378/959 281/377/960 +f 268/365/961 269/364/962 284/379/963 283/380/964 +f 269/364/965 270/366/966 285/381/967 284/379/968 +f 270/366/969 271/367/970 286/382/971 285/381/972 +f 271/367/973 272/368/974 287/383/975 286/382/976 +f 272/368/977 273/369/978 288/384/979 287/383/980 +f 273/369/981 274/370/982 289/385/983 288/384/984 +f 274/370/985 275/371/986 290/386/987 289/385/988 +f 275/371/989 276/372/990 291/387/991 290/386/992 +f 276/372/993 277/373/994 292/388/995 291/387/996 +f 277/373/997 278/374/998 293/389/999 292/388/1000 +f 278/374/1001 279/375/1002 294/390/1003 293/389/1004 +f 279/375/1005 280/376/1006 295/391/1007 294/390/1008 +f 280/376/1009 281/377/1010 296/392/1011 295/391/1012 +f 281/377/1013 282/378/1014 297/393/1015 296/392/1016 +f 283/380/1017 284/379/1018 299/394/1019 298/395/1020 +f 284/379/1021 285/381/1022 300/396/1023 299/394/1024 +f 285/381/1025 286/382/1026 301/397/1027 300/396/1028 +f 286/382/1029 287/383/1030 302/398/1031 301/397/1032 +f 287/383/1033 288/384/1034 303/399/1035 302/398/1036 +f 288/384/1037 289/385/1038 304/400/1039 303/399/1040 +f 289/385/1041 290/386/1042 305/401/1043 304/400/1044 +f 290/386/1045 291/387/1046 306/402/1047 305/401/1048 +f 291/387/1049 292/388/1050 307/403/1051 306/402/1052 +f 292/388/1053 293/389/1054 308/404/1055 307/403/1056 +f 293/389/1057 294/390/1058 309/405/1059 308/404/1060 +f 294/390/1061 295/391/1062 310/406/1063 309/405/1064 +f 295/391/1065 296/392/1066 311/407/1067 310/406/1068 +f 296/392/1069 297/393/1070 312/408/1071 311/407/1072 +f 298/395/1073 299/394/1074 314/409/1075 313/410/1076 +f 299/394/1077 300/396/1078 315/411/1079 314/409/1080 +f 300/396/1081 301/397/1082 316/412/1083 315/411/1084 +f 301/397/1085 302/398/1086 317/413/1087 316/412/1088 +f 302/398/1089 303/399/1090 318/414/1091 317/413/1092 +f 303/399/1093 304/400/1094 319/415/1095 318/414/1096 +f 304/400/1097 305/401/1098 320/416/1099 319/415/1100 +f 305/401/1101 306/402/1102 321/417/1103 320/416/1104 +f 306/402/1105 307/403/1106 322/418/1107 321/417/1108 +f 307/403/1109 308/404/1110 323/419/1111 322/418/1112 +f 308/404/1113 309/405/1114 324/420/1115 323/419/1116 +f 309/405/1117 310/406/1118 325/421/1119 324/420/1120 +f 310/406/1121 311/407/1122 326/422/1123 325/421/1124 +f 311/407/1125 312/408/1126 327/423/1127 326/422/1128 +f 313/410/1129 314/409/1130 329/424/1131 328/425/1132 +f 314/409/1133 315/411/1134 330/426/1135 329/424/1136 +f 315/411/1137 316/412/1138 331/427/1139 330/426/1140 +f 316/412/1141 317/413/1142 332/428/1143 331/427/1144 +f 317/413/1145 318/414/1146 333/429/1147 332/428/1148 +f 318/414/1149 319/415/1150 43/58/1151 333/429/1152 +f 319/415/1153 320/416/1154 44/61/1155 43/58/1156 +f 320/416/1157 321/417/1158 40/52/1159 44/61/1160 +f 321/417/1161 322/418/1162 334/430/1163 40/52/1164 +f 322/418/1165 323/419/1166 335/431/1167 334/430/1168 +f 323/419/1169 324/420/1170 336/432/1171 335/431/1172 +f 324/420/1173 325/421/1174 337/433/1175 336/432/1176 +f 325/421/1177 326/422/1178 338/434/1179 337/433/1180 +f 326/422/1181 327/423/1182 339/435/1183 338/434/1184 +f 328/425/1185 329/424/1186 341/436/1187 340/437/1188 +f 329/424/1189 330/426/1190 342/438/1191 341/436/1192 +f 330/426/1193 331/427/1194 343/439/1195 342/438/1196 +f 331/427/1197 332/428/1198 344/440/1199 343/439/1200 +f 332/428/1201 333/429/1202 345/441/1203 344/440/1204 +f 333/429/1205 43/58/1206 42/57/1207 345/441/1208 +f 40/52/1209 334/430/1210 346/442/1211 41/54/1212 +f 334/430/1213 335/431/1214 347/443/1215 346/442/1216 +f 335/431/1217 336/432/1218 348/444/1219 347/443/1220 +f 336/432/1221 337/433/1222 349/445/1223 348/444/1224 +f 337/433/1225 338/434/1226 350/446/1227 349/445/1228 +f 338/434/1229 339/435/1230 351/447/1231 350/446/1232 +f 340/437/1233 341/436/1234 353/448/1235 352/449/1236 +f 341/436/1237 342/438/1238 354/450/1239 353/448/1240 +f 342/438/1241 343/439/1242 355/451/1243 354/450/1244 +f 343/439/1245 344/440/1246 356/452/1247 355/451/1248 +f 344/440/1249 345/441/1250 357/453/1251 356/452/1252 +f 345/441/1253 42/57/1254 358/454/1255 357/453/1256 +f 42/57/1257 45/63/1258 359/455/1259 358/454/1260 +f 45/63/1261 41/54/1262 360/456/1263 359/455/1264 +f 41/54/1265 346/442/1266 361/457/1267 360/456/1268 +f 346/442/1269 347/443/1270 362/458/1271 361/457/1272 +f 347/443/1273 348/444/1274 363/459/1275 362/458/1276 +f 348/444/1277 349/445/1278 364/460/1279 363/459/1280 +f 349/445/1281 350/446/1282 365/461/1283 364/460/1284 +f 350/446/1285 351/447/1286 366/462/1287 365/461/1288 +f 352/449/1289 353/448/1290 368/463/1291 367/464/1292 +f 353/448/1293 354/450/1294 369/465/1295 368/463/1296 +f 354/450/1297 355/451/1298 370/466/1299 369/465/1300 +f 355/451/1301 356/452/1302 371/467/1303 370/466/1304 +f 356/452/1305 357/453/1306 372/468/1307 371/467/1308 +f 357/453/1309 358/454/1310 373/469/1311 372/468/1312 +f 358/454/1313 359/455/1314 374/470/1315 373/469/1316 +f 359/455/1317 360/456/1318 375/471/1319 374/470/1320 +f 360/456/1321 361/457/1322 376/472/1323 375/471/1324 +f 361/457/1325 362/458/1326 377/473/1327 376/472/1328 +f 362/458/1329 363/459/1330 378/474/1331 377/473/1332 +f 363/459/1333 364/460/1334 379/475/1335 378/474/1336 +f 364/460/1337 365/461/1338 380/476/1339 379/475/1340 +f 365/461/1341 366/462/1342 381/477/1343 380/476/1344 +f 367/464/1345 368/463/1346 382/478/1347 +f 368/463/1348 369/465/1349 382/478/1350 +f 369/465/1351 370/466/1352 382/478/1353 +f 370/466/1354 371/467/1355 382/478/1356 +f 371/467/1357 372/468/1358 382/478/1359 +f 372/468/1360 373/469/1361 382/478/1362 +f 373/469/1363 374/470/1364 382/478/1365 +f 374/470/1366 375/471/1367 382/478/1368 +f 375/471/1369 376/472/1370 382/478/1371 +f 376/472/1372 377/473/1373 382/478/1374 +f 377/473/1375 378/474/1376 382/478/1377 +f 378/474/1378 379/475/1379 382/478/1380 +f 379/475/1381 380/476/1382 382/478/1383 +f 380/476/1384 381/477/1385 382/478/1386 +f 223/319/1387 238/322/1388 396/479/1389 383/480/1390 +f 383/480/1391 396/479/1392 397/481/1393 384/482/1394 +f 384/482/1395 397/481/1396 398/483/1397 385/484/1398 +f 385/484/1399 398/483/1400 399/485/1401 386/486/1402 +f 386/486/1403 399/485/1404 400/487/1405 387/488/1406 +f 387/488/1407 400/487/1408 401/489/1409 388/490/1410 +f 388/490/1411 401/489/1412 402/491/1413 389/492/1414 +f 389/492/1415 402/491/1416 403/493/1417 390/494/1418 +f 390/494/1419 403/493/1420 404/495/1421 391/496/1422 +f 391/496/1423 404/495/1424 405/497/1425 392/498/1426 +f 392/498/1427 405/497/1428 406/499/1429 393/500/1430 +f 393/500/1431 406/499/1432 407/501/1433 394/502/1434 +f 394/502/1435 407/501/1436 408/503/1437 395/504/1438 +f 395/504/1439 408/503/1440 252/348/1441 237/347/1442 +f 238/322/1443 253/350/1444 409/505/1445 396/479/1446 +f 396/479/1447 409/505/1448 410/506/1449 397/481/1450 +f 397/481/1451 410/506/1452 411/507/1453 398/483/1454 +f 398/483/1455 411/507/1456 412/508/1457 399/485/1458 +f 399/485/1459 412/508/1460 413/509/1461 400/487/1462 +f 400/487/1463 413/509/1464 414/510/1465 401/489/1466 +f 401/489/1467 414/510/1468 415/511/1469 402/491/1470 +f 402/491/1471 415/511/1472 416/512/1473 403/493/1474 +f 403/493/1475 416/512/1476 417/513/1477 404/495/1478 +f 404/495/1479 417/513/1480 418/514/1481 405/497/1482 +f 405/497/1483 418/514/1484 419/515/1485 406/499/1486 +f 406/499/1487 419/515/1488 420/516/1489 407/501/1490 +f 407/501/1491 420/516/1492 421/517/1493 408/503/1494 +f 408/503/1495 421/517/1496 267/363/1497 252/348/1498 +f 253/350/1499 268/365/1500 422/518/1501 409/505/1502 +f 409/505/1503 422/518/1504 423/519/1505 410/506/1506 +f 410/506/1507 423/519/1508 424/520/1509 411/507/1510 +f 411/507/1511 424/520/1512 425/521/1513 412/508/1514 +f 412/508/1515 425/521/1516 426/522/1517 413/509/1518 +f 413/509/1519 426/522/1520 427/523/1521 414/510/1522 +f 414/510/1523 427/523/1524 428/524/1525 415/511/1526 +f 415/511/1527 428/524/1528 429/525/1529 416/512/1530 +f 416/512/1531 429/525/1532 430/526/1533 417/513/1534 +f 417/513/1535 430/526/1536 431/527/1537 418/514/1538 +f 418/514/1539 431/527/1540 432/528/1541 419/515/1542 +f 419/515/1543 432/528/1544 433/529/1545 420/516/1546 +f 420/516/1547 433/529/1548 434/530/1549 421/517/1550 +f 421/517/1551 434/530/1552 282/378/1553 267/363/1554 +f 268/365/1555 283/380/1556 435/531/1557 422/518/1558 +f 422/518/1559 435/531/1560 436/532/1561 423/519/1562 +f 423/519/1563 436/532/1564 437/533/1565 424/520/1566 +f 424/520/1567 437/533/1568 438/534/1569 425/521/1570 +f 425/521/1571 438/534/1572 439/535/1573 426/522/1574 +f 426/522/1575 439/535/1576 440/536/1577 427/523/1578 +f 427/523/1579 440/536/1580 441/537/1581 428/524/1582 +f 428/524/1583 441/537/1584 442/538/1585 429/525/1586 +f 429/525/1587 442/538/1588 443/539/1589 430/526/1590 +f 430/526/1591 443/539/1592 444/540/1593 431/527/1594 +f 431/527/1595 444/540/1596 445/541/1597 432/528/1598 +f 432/528/1599 445/541/1600 446/542/1601 433/529/1602 +f 433/529/1603 446/542/1604 447/543/1605 434/530/1606 +f 434/530/1607 447/543/1608 297/393/1609 282/378/1610 +f 283/380/1611 298/395/1612 448/544/1613 435/531/1614 +f 435/531/1615 448/544/1616 449/545/1617 436/532/1618 +f 436/532/1619 449/545/1620 450/546/1621 437/533/1622 +f 437/533/1623 450/546/1624 451/547/1625 438/534/1626 +f 438/534/1627 451/547/1628 452/548/1629 439/535/1630 +f 439/535/1631 452/548/1632 453/549/1633 440/536/1634 +f 440/536/1635 453/549/1636 454/550/1637 441/537/1638 +f 441/537/1639 454/550/1640 455/551/1641 442/538/1642 +f 442/538/1643 455/551/1644 456/552/1645 443/539/1646 +f 443/539/1647 456/552/1648 457/553/1649 444/540/1650 +f 444/540/1651 457/553/1652 458/554/1653 445/541/1654 +f 445/541/1655 458/554/1656 459/555/1657 446/542/1658 +f 446/542/1659 459/555/1660 460/556/1661 447/543/1662 +f 447/543/1663 460/556/1664 312/408/1665 297/393/1666 +f 298/395/1667 313/410/1668 461/557/1669 448/544/1670 +f 448/544/1671 461/557/1672 462/558/1673 449/545/1674 +f 449/545/1675 462/558/1676 463/559/1677 450/546/1678 +f 450/546/1679 463/559/1680 464/560/1681 451/547/1682 +f 451/547/1683 464/560/1684 465/561/1685 452/548/1686 +f 452/548/1687 465/561/1688 466/562/1689 453/549/1690 +f 453/549/1691 466/562/1692 467/563/1693 454/550/1694 +f 454/550/1695 467/563/1696 468/564/1697 455/551/1698 +f 455/551/1699 468/564/1700 469/565/1701 456/552/1702 +f 456/552/1703 469/565/1704 470/566/1705 457/553/1706 +f 457/553/1707 470/566/1708 471/567/1709 458/554/1710 +f 458/554/1711 471/567/1712 472/568/1713 459/555/1714 +f 459/555/1715 472/568/1716 473/569/1717 460/556/1718 +f 460/556/1719 473/569/1720 327/423/1721 312/408/1722 +f 313/410/1723 328/425/1724 474/570/1725 461/557/1726 +f 461/557/1727 474/570/1728 475/571/1729 462/558/1730 +f 462/558/1731 475/571/1732 476/572/1733 463/559/1734 +f 463/559/1735 476/572/1736 477/573/1737 464/560/1738 +f 464/560/1739 477/573/1740 478/574/1741 465/561/1742 +f 465/561/1743 478/574/1744 29/42/1745 466/562/1746 +f 466/562/1747 29/42/1748 30/47/1749 467/563/1750 +f 467/563/1751 30/47/1752 26/37/1753 468/564/1754 +f 468/564/1755 26/37/1756 479/575/1757 469/565/1758 +f 469/565/1759 479/575/1760 480/576/1761 470/566/1762 +f 470/566/1763 480/576/1764 481/577/1765 471/567/1766 +f 471/567/1767 481/577/1768 482/578/1769 472/568/1770 +f 472/568/1771 482/578/1772 483/579/1773 473/569/1774 +f 473/569/1775 483/579/1776 339/435/1777 327/423/1778 +f 328/425/1779 340/437/1780 484/580/1781 474/570/1782 +f 474/570/1783 484/580/1784 485/581/1785 475/571/1786 +f 475/571/1787 485/581/1788 486/582/1789 476/572/1790 +f 476/572/1791 486/582/1792 487/583/1793 477/573/1794 +f 477/573/1795 487/583/1796 488/584/1797 478/574/1798 +f 478/574/1799 488/584/1800 28/43/1801 29/42/1802 +f 26/37/1803 27/40/1804 489/585/1805 479/575/1806 +f 479/575/1807 489/585/1808 490/586/1809 480/576/1810 +f 480/576/1811 490/586/1812 491/587/1813 481/577/1814 +f 481/577/1815 491/587/1816 492/588/1817 482/578/1818 +f 482/578/1819 492/588/1820 493/589/1821 483/579/1822 +f 483/579/1823 493/589/1824 351/447/1825 339/435/1826 +f 340/437/1827 352/449/1828 494/590/1829 484/580/1830 +f 484/580/1831 494/590/1832 495/591/1833 485/581/1834 +f 485/581/1835 495/591/1836 496/592/1837 486/582/1838 +f 486/582/1839 496/592/1840 497/593/1841 487/583/1842 +f 487/583/1843 497/593/1844 498/594/1845 488/584/1846 +f 488/584/1847 498/594/1848 499/595/1849 28/43/1850 +f 28/43/1851 499/595/1852 500/596/1853 31/49/1854 +f 31/49/1855 500/596/1856 501/597/1857 27/40/1858 +f 27/40/1859 501/597/1860 502/598/1861 489/585/1862 +f 489/585/1863 502/598/1864 503/599/1865 490/586/1866 +f 490/586/1867 503/599/1868 504/600/1869 491/587/1870 +f 491/587/1871 504/600/1872 505/601/1873 492/588/1874 +f 492/588/1875 505/601/1876 506/602/1877 493/589/1878 +f 493/589/1879 506/602/1880 366/462/1881 351/447/1882 +f 352/449/1883 367/464/1884 507/603/1885 494/590/1886 +f 494/590/1887 507/603/1888 508/604/1889 495/591/1890 +f 495/591/1891 508/604/1892 509/605/1893 496/592/1894 +f 496/592/1895 509/605/1896 510/606/1897 497/593/1898 +f 497/593/1899 510/606/1900 511/607/1901 498/594/1902 +f 498/594/1903 511/607/1904 512/608/1905 499/595/1906 +f 499/595/1907 512/608/1908 513/609/1909 500/596/1910 +f 500/596/1911 513/609/1912 514/610/1913 501/597/1914 +f 501/597/1915 514/610/1916 515/611/1917 502/598/1918 +f 502/598/1919 515/611/1920 516/612/1921 503/599/1922 +f 503/599/1923 516/612/1924 517/613/1925 504/600/1926 +f 504/600/1927 517/613/1928 518/614/1929 505/601/1930 +f 505/601/1931 518/614/1932 519/615/1933 506/602/1934 +f 506/602/1935 519/615/1936 381/477/1937 366/462/1938 +f 367/464/1939 382/478/1940 507/603/1941 +f 507/603/1942 382/478/1943 508/604/1944 +f 508/604/1945 382/478/1946 509/605/1947 +f 509/605/1948 382/478/1949 510/606/1950 +f 510/606/1951 382/478/1952 511/607/1953 +f 511/607/1954 382/478/1955 512/608/1956 +f 512/608/1957 382/478/1958 513/609/1959 +f 513/609/1960 382/478/1961 514/610/1962 +f 514/610/1963 382/478/1964 515/611/1965 +f 515/611/1966 382/478/1967 516/612/1968 +f 516/612/1969 382/478/1970 517/613/1971 +f 517/613/1972 382/478/1973 518/614/1974 +f 518/614/1975 382/478/1976 519/615/1977 +f 519/615/1978 382/478/1979 381/477/1980 +f 522/616/1981 521/617/1982 520/618/1983 523/619/1984 +f 521/617/1985 522/616/1986 224/320/1987 223/319/1988 +f 522/616/1989 524/620/1990 225/323/1991 224/320/1992 +f 524/620/1993 526/621/1994 226/325/1995 225/323/1996 +f 526/621/1997 528/622/1998 227/327/1999 226/325/2000 +f 528/622/2001 530/623/2002 228/329/2003 227/327/2004 +f 530/623/2005 532/624/2006 229/331/2007 228/329/2008 +f 532/624/2009 534/625/2010 230/333/2011 229/331/2012 +f 534/625/2013 536/626/2014 231/335/2015 230/333/2016 +f 536/626/2017 538/627/2018 232/337/2019 231/335/2020 +f 538/627/2021 540/628/2022 233/339/2023 232/337/2024 +f 540/628/2025 542/629/2026 234/341/2027 233/339/2028 +f 542/629/2029 544/630/2030 235/343/2031 234/341/2032 +f 544/630/2033 546/631/2034 236/345/2035 235/343/2036 +f 546/631/2037 548/632/2038 237/347/2039 236/345/2040 +f 523/633/2041 520/634/2042 576/635/2043 +f 525/636/2044 523/633/2045 576/635/2046 +f 527/637/2047 525/636/2048 576/635/2049 +f 529/638/2050 527/637/2051 576/635/2052 +f 531/639/2053 529/638/2054 576/635/2055 +f 533/640/2056 531/639/2057 576/635/2058 +f 535/641/2059 533/640/2060 576/635/2061 +f 537/642/2062 535/641/2063 576/635/2064 +f 539/643/2065 537/642/2066 576/635/2067 +f 541/644/2068 539/643/2069 576/635/2070 +f 543/645/2071 541/644/2072 576/635/2073 +f 545/646/2074 543/645/2075 576/635/2076 +f 547/647/2077 545/646/2078 576/635/2079 +f 549/648/2080 547/647/2081 576/635/2082 +f 521/617/2083 223/319/2084 383/480/2085 550/649/2086 +f 550/649/2087 383/480/2088 384/482/2089 552/650/2090 +f 552/650/2091 384/482/2092 385/484/2093 554/651/2094 +f 554/651/2095 385/484/2096 386/486/2097 556/652/2098 +f 556/652/2099 386/486/2100 387/488/2101 558/653/2102 +f 558/653/2103 387/488/2104 388/490/2105 560/654/2106 +f 560/654/2107 388/490/2108 389/492/2109 562/655/2110 +f 562/655/2111 389/492/2112 390/494/2113 564/656/2114 +f 564/656/2115 390/494/2116 391/496/2117 566/657/2118 +f 566/657/2119 391/496/2120 392/498/2121 568/658/2122 +f 568/658/2123 392/498/2124 393/500/2125 570/659/2126 +f 570/659/2127 393/500/2128 394/502/2129 572/660/2130 +f 572/660/2131 394/502/2132 395/504/2133 574/661/2134 +f 574/661/2135 395/504/2136 237/347/2137 548/632/2138 +f 524/620/2139 522/616/2140 523/619/2141 525/662/2142 +f 526/621/2143 524/620/2144 525/662/2145 527/663/2146 +f 528/622/2147 526/621/2148 527/663/2149 529/664/2150 +f 530/623/2151 528/622/2152 529/664/2153 531/665/2154 +f 532/624/2155 530/623/2156 531/665/2157 533/666/2158 +f 534/625/2159 532/624/2160 533/666/2161 535/667/2162 +f 536/626/2163 534/625/2164 535/667/2165 537/668/2166 +f 538/627/2167 536/626/2168 537/668/2169 539/669/2170 +f 540/628/2171 538/627/2172 539/669/2173 541/670/2174 +f 542/629/2175 540/628/2176 541/670/2177 543/671/2178 +f 544/630/2179 542/629/2180 543/671/2181 545/672/2182 +f 546/631/2183 544/630/2184 545/672/2185 547/673/2186 +f 548/632/2187 546/631/2188 547/673/2189 549/674/2190 +f 574/661/2191 548/632/2192 549/674/2193 575/675/2194 +f 520/618/2195 521/617/2196 550/649/2197 551/676/2198 +f 550/649/2199 552/650/2200 553/677/2201 551/676/2202 +f 552/650/2203 554/651/2204 555/678/2205 553/677/2206 +f 554/651/2207 556/652/2208 557/679/2209 555/678/2210 +f 556/652/2211 558/653/2212 559/680/2213 557/679/2214 +f 558/653/2215 560/654/2216 561/681/2217 559/680/2218 +f 560/654/2219 562/655/2220 563/682/2221 561/681/2222 +f 562/655/2223 564/656/2224 565/683/2225 563/682/2226 +f 564/656/2227 566/657/2228 567/684/2229 565/683/2230 +f 566/657/2231 568/658/2232 569/685/2233 567/684/2234 +f 568/658/2235 570/659/2236 571/686/2237 569/685/2238 +f 570/659/2239 572/660/2240 573/687/2241 571/686/2242 +f 572/660/2243 574/661/2244 575/675/2245 573/687/2246 +f 551/688/2247 576/635/2248 520/634/2249 +f 553/689/2250 576/635/2251 551/688/2252 +f 555/690/2253 576/635/2254 553/689/2255 +f 557/691/2256 576/635/2257 555/690/2258 +f 559/692/2259 576/635/2260 557/691/2261 +f 561/693/2262 576/635/2263 559/692/2264 +f 563/694/2265 576/635/2266 561/693/2267 +f 565/695/2268 576/635/2269 563/694/2270 +f 567/696/2271 576/635/2272 565/695/2273 +f 569/697/2274 576/635/2275 567/696/2276 +f 571/698/2277 576/635/2278 569/697/2279 +f 573/699/2280 576/635/2281 571/698/2282 +f 575/700/2283 576/635/2284 573/699/2285 +f 549/648/2286 576/635/2287 575/700/2288 +f 578/106/2289 577/105/2290 111/133/2291 112/135/2292 +f 579/107/2293 578/106/2294 112/135/2295 113/137/2296 +f 580/108/2297 579/107/2298 113/137/2299 99/110/2300 +f 581/94/2301 580/108/2302 99/110/2303 100/109/2304 +f 582/95/2305 581/94/2306 100/109/2307 101/113/2308 +f 583/96/2309 582/95/2310 101/113/2311 102/115/2312 +f 584/97/2313 583/96/2314 102/115/2315 103/117/2316 +f 585/167/2317 584/97/2318 103/117/2319 156/185/2320 +f 586/165/2321 585/167/2322 156/185/2323 155/183/2324 +f 587/166/2325 586/165/2326 155/183/2327 154/181/2328 +f 588/177/2329 587/166/2330 154/181/2331 153/180/2332 +f 589/176/2333 588/177/2334 153/180/2335 165/203/2336 +f 590/175/2337 589/176/2338 165/203/2339 164/201/2340 +f 591/174/2341 590/175/2342 164/201/2343 163/199/2344 +f 592/173/2345 591/174/2346 163/199/2347 162/197/2348 +f 593/172/2349 592/173/2350 162/197/2351 161/195/2352 +f 594/171/2353 593/172/2354 161/195/2355 160/193/2356 +f 595/170/2357 594/171/2358 160/193/2359 159/191/2360 +f 596/169/2361 595/170/2362 159/191/2363 158/189/2364 +f 597/168/2365 596/169/2366 158/189/2367 157/187/2368 +f 598/98/2369 597/168/2370 157/187/2371 104/120/2372 +f 599/99/2373 598/98/2374 104/120/2375 105/119/2376 +f 600/100/2377 599/99/2378 105/119/2379 106/123/2380 +f 601/101/2381 600/100/2382 106/123/2383 107/125/2384 +f 602/102/2385 601/101/2386 107/125/2387 108/127/2388 +f 603/103/2389 602/102/2390 108/127/2391 109/129/2392 +f 604/104/2393 603/103/2394 109/129/2395 110/131/2396 +f 577/105/2397 604/104/2398 110/131/2399 111/133/2400 +f 606/136/2401 605/134/2402 76/701/2403 77/702/2404 +f 607/138/2405 606/136/2406 77/702/2407 78/703/2408 +f 79/704/2409 608/111/2410 607/138/2411 78/703/2412 +f 609/112/2413 608/111/2414 79/704/2415 80/705/2416 +f 610/114/2417 609/112/2418 80/705/2419 81/706/2420 +f 611/116/2421 610/114/2422 81/706/2423 82/707/2424 +f 612/118/2425 611/116/2426 82/707/2427 83/708/2428 +f 613/184/2429 612/118/2430 83/708/2431 139/709/2432 +f 139/709/2433 138/710/2434 614/182/2435 613/184/2436 +f 138/710/2437 137/711/2438 615/178/2439 614/182/2440 +f 137/711/2441 136/712/2442 616/179/2443 615/178/2444 +f 617/202/2445 616/179/2446 136/712/2447 135/713/2448 +f 135/713/2449 134/714/2450 618/200/2451 617/202/2452 +f 134/714/2453 133/715/2454 619/198/2455 618/200/2456 +f 133/715/2457 132/716/2458 620/196/2459 619/198/2460 +f 132/716/2461 131/717/2462 621/194/2463 620/196/2464 +f 131/717/2465 130/718/2466 622/192/2467 621/194/2468 +f 130/718/2469 129/719/2470 623/190/2471 622/192/2472 +f 129/719/2473 128/720/2474 624/188/2475 623/190/2476 +f 128/720/2477 127/721/2478 625/186/2479 624/188/2480 +f 127/721/2481 69/722/2482 626/121/2483 625/186/2484 +f 627/122/2485 626/121/2486 69/722/2487 70/723/2488 +f 628/124/2489 627/122/2490 70/723/2491 71/724/2492 +f 629/126/2493 628/124/2494 71/724/2495 72/725/2496 +f 630/128/2497 629/126/2498 72/725/2499 73/726/2500 +f 631/130/2501 630/128/2502 73/726/2503 74/727/2504 +f 632/132/2505 631/130/2506 74/727/2507 75/728/2508 +f 605/134/2509 632/132/2510 75/728/2511 76/701/2512 +f 633/729/2513 634/730/2514 77/702/2515 76/701/2516 +f 634/730/2517 635/731/2518 78/703/2519 77/702/2520 +f 80/705/2521 636/732/2522 81/706/2523 +f 636/732/2524 637/733/2525 82/707/2526 81/706/2527 +f 637/733/2528 638/734/2529 83/708/2530 82/707/2531 +f 638/734/2532 639/735/2533 645/736/2534 83/708/2535 +f 640/737/2536 641/738/2537 70/723/2538 69/722/2539 +f 641/738/2540 642/739/2541 71/724/2542 70/723/2543 +f 73/726/2544 643/740/2545 74/727/2546 +f 643/740/2547 644/741/2548 75/728/2549 74/727/2550 +f 644/741/2551 633/729/2552 76/701/2553 75/728/2554 +f 639/735/2555 640/737/2556 69/722/2557 645/736/2558 +f 635/731/2559 79/704/2560 78/703/2561 +f 635/731/2562 636/732/2563 80/705/2564 79/704/2565 +f 642/739/2566 72/725/2567 71/724/2568 +f 642/739/2569 643/740/2570 73/726/2571 72/725/2572 +f 646/742/2573 133/715/2574 134/714/2575 647/743/2576 +f 647/743/2577 134/714/2578 135/713/2579 648/744/2580 +f 137/711/2581 138/710/2582 649/745/2583 +f 649/745/2584 138/710/2585 139/709/2586 650/746/2587 +f 650/746/2588 139/709/2589 83/708/2590 651/747/2591 +f 651/747/2592 83/708/2593 645/736/2594 652/748/2595 +f 653/749/2596 69/722/2597 127/721/2598 654/750/2599 +f 654/750/2600 127/721/2601 128/720/2602 655/751/2603 +f 130/718/2604 131/717/2605 656/752/2606 +f 656/752/2607 131/717/2608 132/716/2609 657/753/2610 +f 657/753/2611 132/716/2612 133/715/2613 646/742/2614 +f 652/748/2615 645/736/2616 69/722/2617 653/749/2618 +f 648/744/2619 135/713/2620 136/712/2621 +f 648/744/2622 136/712/2623 137/711/2624 649/745/2625 +f 655/751/2626 128/720/2627 129/719/2628 +f 655/751/2629 129/719/2630 130/718/2631 656/752/2632 +f 2/2/2633 698/754/2634 697/755/2635 3/5/2636 +f 1/1/2637 699/756/2638 698/754/2639 2/2/2640 +f 8/17/2641 700/757/2642 699/756/2643 1/1/2644 +f 7/15/2645 701/758/2646 700/757/2647 8/17/2648 +f 694/759/2649 701/758/2650 7/15/2651 6/13/2652 +f 5/10/2653 695/760/2654 694/759/2655 6/13/2656 +f 4/9/2657 696/761/2658 695/760/2659 5/10/2660 +f 3/5/2661 697/755/2662 696/762/2663 4/7/2664 +f 648/763/2665 702/764/2666 713/765/2667 647/766/2668 +f 649/767/2669 703/768/2670 702/764/2671 648/763/2672 +f 650/769/2673 704/770/2674 703/768/2675 649/767/2676 +f 651/771/2677 705/772/2678 704/770/2679 650/769/2680 +f 652/773/2681 706/774/2682 705/772/2683 651/771/2684 +f 713/765/2685 712/775/2686 646/776/2687 647/766/2688 +f 653/777/2689 707/778/2690 706/779/2691 652/780/2692 +f 654/781/2693 708/782/2694 707/778/2695 653/777/2696 +f 655/783/2697 709/784/2698 708/782/2699 654/781/2700 +f 656/785/2701 710/786/2702 709/784/2703 655/783/2704 +f 657/787/2705 711/788/2706 710/786/2707 656/785/2708 +f 646/776/2709 712/775/2710 711/788/2711 657/787/2712 +f 671/789/2713 683/790/2714 682/791/2715 670/792/2716 +f 672/793/2717 684/794/2718 683/790/2719 671/789/2720 +f 673/795/2721 685/796/2722 684/794/2723 672/793/2724 +f 674/797/2725 686/798/2726 685/796/2727 673/795/2728 +f 675/799/2729 687/800/2730 686/798/2731 674/797/2732 +f 676/801/2733 688/802/2734 687/800/2735 675/799/2736 +f 677/803/2737 689/804/2738 688/802/2739 676/801/2740 +f 678/805/2741 690/806/2742 689/804/2743 677/803/2744 +f 679/807/2745 691/808/2746 690/806/2747 678/805/2748 +f 680/809/2749 692/810/2750 691/808/2751 679/807/2752 +f 681/811/2753 693/812/2754 692/810/2755 680/809/2756 +f 670/792/2757 682/791/2758 693/813/2759 681/814/2760 +f 695/760/2761 34/815/2762 32/816/2763 694/759/2764 +f 696/761/2765 39/817/2766 34/815/2767 695/760/2768 +f 697/755/2769 36/818/2770 39/819/2771 696/762/2772 +f 698/754/2773 35/820/2774 36/818/2775 697/755/2776 +f 699/756/2777 37/821/2778 35/820/2779 698/754/2780 +f 700/757/2781 38/822/2782 37/821/2783 699/756/2784 +f 701/758/2785 33/823/2786 38/822/2787 700/757/2788 +f 32/816/2789 33/823/2790 701/758/2791 694/759/2792 +f 661/824/2793 660/825/2794 702/764/2795 703/768/2796 +f 662/826/2797 661/824/2798 703/768/2799 704/770/2800 +f 663/827/2801 662/826/2802 704/770/2803 705/772/2804 +f 664/828/2805 663/827/2806 705/772/2807 706/774/2808 +f 665/829/2809 664/830/2810 706/779/2811 707/778/2812 +f 666/831/2813 665/829/2814 707/778/2815 708/782/2816 +f 667/832/2817 666/831/2818 708/782/2819 709/784/2820 +f 668/833/2821 667/832/2822 709/784/2823 710/786/2824 +f 669/834/2825 668/833/2826 710/786/2827 711/788/2828 +f 658/835/2829 669/834/2830 711/788/2831 712/775/2832 +f 659/836/2833 658/835/2834 712/775/2835 713/765/2836 +f 660/825/2837 659/836/2838 713/765/2839 702/764/2840 +f 715/837/2841 714/838/2842 737/839/2843 738/840/2844 +f 716/841/2845 715/837/2846 738/840/2847 739/842/2848 +f 717/843/2849 716/841/2850 739/842/2851 740/844/2852 +f 718/845/2853 717/843/2854 740/844/2855 741/846/2856 +f 734/847/2857 719/848/2858 718/845/2859 741/846/2860 +f 720/849/2861 719/848/2862 734/847/2863 735/850/2864 +f 721/851/2865 720/849/2866 735/850/2867 736/852/2868 +f 714/838/2869 721/853/2870 736/854/2871 737/839/2872 +f 635/855/2873 634/856/2874 753/857/2875 742/858/2876 +f 636/859/2877 635/855/2878 742/858/2879 743/860/2880 +f 637/861/2881 636/859/2882 743/860/2883 744/862/2884 +f 638/863/2885 637/861/2886 744/862/2887 745/864/2888 +f 639/865/2889 638/863/2890 745/864/2891 746/866/2892 +f 753/857/2893 634/856/2894 633/867/2895 752/868/2896 +f 640/869/2897 639/870/2898 746/871/2899 747/872/2900 +f 641/873/2901 640/869/2902 747/872/2903 748/874/2904 +f 642/875/2905 641/873/2906 748/874/2907 749/876/2908 +f 643/877/2909 642/875/2910 749/876/2911 750/878/2912 +f 644/879/2913 643/877/2914 750/878/2915 751/880/2916 +f 633/867/2917 644/879/2918 751/880/2919 752/868/2920 +f 735/850/2921 734/847/2922 46/881/2923 48/882/2924 +f 736/852/2925 735/850/2926 48/882/2927 53/883/2928 +f 737/839/2929 736/854/2930 53/884/2931 50/885/2932 +f 738/840/2933 737/839/2934 50/885/2935 49/886/2936 +f 739/842/2937 738/840/2938 49/886/2939 51/887/2940 +f 740/844/2941 739/842/2942 51/887/2943 52/888/2944 +f 741/846/2945 740/844/2946 52/888/2947 47/889/2948 +f 46/881/2949 734/847/2950 741/846/2951 47/889/2952 +f 725/890/2953 743/860/2954 742/858/2955 724/891/2956 +f 726/892/2957 744/862/2958 743/860/2959 725/890/2960 +f 727/893/2961 745/864/2962 744/862/2963 726/892/2964 +f 728/894/2965 746/866/2966 745/864/2967 727/893/2968 +f 729/895/2969 747/872/2970 746/871/2971 728/896/2972 +f 730/897/2973 748/874/2974 747/872/2975 729/895/2976 +f 731/898/2977 749/876/2978 748/874/2979 730/897/2980 +f 732/899/2981 750/878/2982 749/876/2983 731/898/2984 +f 733/900/2985 751/880/2986 750/878/2987 732/899/2988 +f 722/901/2989 752/868/2990 751/880/2991 733/900/2992 +f 723/902/2993 753/857/2994 752/868/2995 722/901/2996 +f 724/891/2997 742/858/2998 753/857/2999 723/902/3000 +f 716/841/3001 754/903/3002 755/904/3003 715/837/3004 +f 715/837/3005 755/904/3006 756/905/3007 714/838/3008 +f 714/838/3009 756/905/3010 757/906/3011 721/853/3012 +f 721/851/3013 757/907/3014 758/908/3015 720/849/3016 +f 720/849/3017 758/908/3018 759/909/3019 719/848/3020 +f 719/848/3021 759/909/3022 760/910/3023 718/845/3024 +f 718/845/3025 760/910/3026 761/911/3027 717/843/3028 +f 717/843/3029 761/911/3030 754/903/3031 716/841/3032 +f 754/903/3033 762/912/3034 763/913/3035 755/904/3036 +f 755/904/3037 763/913/3038 764/914/3039 756/905/3040 +f 756/905/3041 764/914/3042 765/915/3043 757/906/3044 +f 757/907/3045 765/916/3046 766/917/3047 758/908/3048 +f 758/908/3049 766/917/3050 767/918/3051 759/909/3052 +f 759/909/3053 767/918/3054 768/919/3055 760/910/3056 +f 760/910/3057 768/919/3058 769/920/3059 761/911/3060 +f 761/911/3061 769/920/3062 762/912/3063 754/903/3064 +f 762/912/3065 770/921/3066 763/913/3067 +f 763/913/3068 770/922/3069 764/914/3070 +f 764/914/3071 770/923/3072 765/915/3073 +f 765/916/3074 770/924/3075 766/917/3076 +f 766/917/3077 770/925/3078 767/918/3079 +f 767/918/3080 770/926/3081 768/919/3082 +f 768/919/3083 770/927/3084 769/920/3085 +f 769/920/3086 770/928/3087 762/912/3088 +f 772/929/3089 771/930/3090 783/931/3091 784/932/3092 +f 773/933/3093 772/929/3094 784/932/3095 785/934/3096 +f 774/935/3097 773/933/3098 785/934/3099 786/936/3100 +f 775/937/3101 774/935/3102 786/936/3103 787/938/3104 +f 776/939/3105 775/937/3106 787/938/3107 788/940/3108 +f 777/941/3109 776/939/3110 788/940/3111 789/942/3112 +f 778/943/3113 777/941/3114 789/942/3115 790/944/3116 +f 779/945/3117 778/943/3118 790/944/3119 791/946/3120 +f 780/947/3121 779/945/3122 791/946/3123 792/948/3124 +f 781/949/3125 780/947/3126 792/948/3127 793/950/3128 +f 782/951/3129 781/949/3130 793/950/3131 794/952/3132 +f 771/930/3133 782/953/3134 794/954/3135 783/931/3136 +f 795/955/3137 807/956/3138 808/957/3139 796/958/3140 +f 796/958/3141 808/957/3142 809/959/3143 797/960/3144 +f 797/960/3145 809/959/3146 810/961/3147 798/962/3148 +f 798/962/3149 810/961/3150 811/963/3151 799/964/3152 +f 799/964/3153 811/963/3154 812/965/3155 800/966/3156 +f 800/967/3157 812/968/3158 813/969/3159 801/970/3160 +f 801/970/3161 813/969/3162 814/971/3163 802/972/3164 +f 802/972/3165 814/971/3166 815/973/3167 803/974/3168 +f 803/974/3169 815/973/3170 816/975/3171 804/976/3172 +f 804/976/3173 816/975/3174 817/977/3175 805/978/3176 +f 805/978/3177 817/977/3178 818/979/3179 806/980/3180 +f 806/980/3181 818/979/3182 807/956/3183 795/955/3184 +f 807/956/3185 819/981/3186 820/982/3187 808/957/3188 +f 808/957/3189 820/982/3190 821/983/3191 809/959/3192 +f 809/959/3193 821/983/3194 822/984/3195 810/961/3196 +f 810/961/3197 822/984/3198 823/985/3199 811/963/3200 +f 811/963/3201 823/985/3202 824/986/3203 812/965/3204 +f 812/968/3205 824/987/3206 825/988/3207 813/969/3208 +f 813/969/3209 825/988/3210 826/989/3211 814/971/3212 +f 814/971/3213 826/989/3214 827/990/3215 815/973/3216 +f 815/973/3217 827/990/3218 828/991/3219 816/975/3220 +f 816/975/3221 828/991/3222 829/992/3223 817/977/3224 +f 817/977/3225 829/992/3226 830/993/3227 818/979/3228 +f 818/979/3229 830/993/3230 819/981/3231 807/956/3232 +f 819/981/3233 831/994/3234 832/995/3235 820/982/3236 +f 820/982/3237 832/995/3238 833/996/3239 821/983/3240 +f 821/983/3241 833/996/3242 834/997/3243 822/984/3244 +f 822/984/3245 834/997/3246 835/998/3247 823/985/3248 +f 823/985/3249 835/998/3250 836/999/3251 824/986/3252 +f 824/987/3253 836/1000/3254 837/1001/3255 825/988/3256 +f 825/988/3257 837/1001/3258 838/1002/3259 826/989/3260 +f 826/989/3261 838/1002/3262 839/1003/3263 827/990/3264 +f 827/990/3265 839/1003/3266 840/1004/3267 828/991/3268 +f 828/991/3269 840/1004/3270 841/1005/3271 829/992/3272 +f 829/992/3273 841/1005/3274 842/1006/3275 830/993/3276 +f 830/993/3277 842/1006/3278 831/994/3279 819/981/3280 +f 831/994/3281 843/1007/3282 844/1008/3283 832/995/3284 +f 832/995/3285 844/1008/3286 845/1009/3287 833/996/3288 +f 833/996/3289 845/1009/3290 846/1010/3291 834/997/3292 +f 834/997/3293 846/1010/3294 847/1011/3295 835/998/3296 +f 835/998/3297 847/1011/3298 848/1012/3299 836/999/3300 +f 836/1000/3301 848/1013/3302 849/1014/3303 837/1001/3304 +f 837/1001/3305 849/1014/3306 850/1015/3307 838/1002/3308 +f 838/1002/3309 850/1015/3310 851/1016/3311 839/1003/3312 +f 839/1003/3313 851/1016/3314 852/1017/3315 840/1004/3316 +f 840/1004/3317 852/1017/3318 853/1018/3319 841/1005/3320 +f 841/1005/3321 853/1018/3322 854/1019/3323 842/1006/3324 +f 842/1006/3325 854/1019/3326 843/1007/3327 831/994/3328 +f 843/1007/3329 733/900/3330 732/899/3331 844/1008/3332 +f 844/1008/3333 732/899/3334 731/898/3335 845/1009/3336 +f 845/1009/3337 731/898/3338 730/897/3339 846/1010/3340 +f 846/1010/3341 730/897/3342 729/895/3343 847/1011/3344 +f 847/1011/3345 729/895/3346 728/896/3347 848/1012/3348 +f 848/1013/3349 728/894/3350 727/893/3351 849/1014/3352 +f 849/1014/3353 727/893/3354 726/892/3355 850/1015/3356 +f 850/1015/3357 726/892/3358 725/890/3359 851/1016/3360 +f 851/1016/3361 725/890/3362 724/891/3363 852/1017/3364 +f 852/1017/3365 724/891/3366 723/902/3367 853/1018/3368 +f 853/1018/3369 723/902/3370 722/901/3371 854/1019/3372 +f 854/1019/3373 722/901/3374 733/900/3375 843/1007/3376 +f 796/958/3377 855/1020/3378 795/955/3379 +f 797/960/3380 855/1021/3381 796/958/3382 +f 798/962/3383 855/1022/3384 797/960/3385 +f 799/964/3386 855/1023/3387 798/962/3388 +f 800/966/3389 855/1024/3390 799/964/3391 +f 801/970/3392 855/1025/3393 800/967/3394 +f 802/972/3395 855/1026/3396 801/970/3397 +f 803/974/3398 855/1027/3399 802/972/3400 +f 804/976/3401 855/1028/3402 803/974/3403 +f 805/978/3404 855/1029/3405 804/976/3406 +f 806/980/3407 855/1030/3408 805/978/3409 +f 795/955/3410 855/1031/3411 806/980/3412 +f 856/1032/3413 868/1033/3414 869/1034/3415 857/1035/3416 +f 857/1035/3417 869/1034/3418 870/1036/3419 858/1037/3420 +f 858/1037/3421 870/1036/3422 871/1038/3423 859/1039/3424 +f 859/1039/3425 871/1038/3426 872/1040/3427 860/1041/3428 +f 860/1041/3429 872/1040/3430 873/1042/3431 861/1043/3432 +f 861/1044/3433 873/1045/3434 874/1046/3435 862/1047/3436 +f 862/1047/3437 874/1046/3438 875/1048/3439 863/1049/3440 +f 863/1049/3441 875/1048/3442 876/1050/3443 864/1051/3444 +f 864/1051/3445 876/1050/3446 877/1052/3447 865/1053/3448 +f 865/1053/3449 877/1052/3450 878/1054/3451 866/1055/3452 +f 866/1055/3453 878/1054/3454 879/1056/3455 867/1057/3456 +f 867/1057/3457 879/1056/3458 868/1033/3459 856/1032/3460 +f 868/1033/3461 880/1058/3462 881/1059/3463 869/1034/3464 +f 869/1034/3465 881/1059/3466 882/1060/3467 870/1036/3468 +f 870/1036/3469 882/1060/3470 883/1061/3471 871/1038/3472 +f 871/1038/3473 883/1061/3474 884/1062/3475 872/1040/3476 +f 872/1040/3477 884/1062/3478 885/1063/3479 873/1042/3480 +f 873/1045/3481 885/1064/3482 886/1065/3483 874/1046/3484 +f 874/1046/3485 886/1065/3486 887/1066/3487 875/1048/3488 +f 875/1048/3489 887/1066/3490 888/1067/3491 876/1050/3492 +f 876/1050/3493 888/1067/3494 889/1068/3495 877/1052/3496 +f 877/1052/3497 889/1068/3498 890/1069/3499 878/1054/3500 +f 878/1054/3501 890/1069/3502 891/1070/3503 879/1056/3504 +f 879/1056/3505 891/1070/3506 880/1058/3507 868/1033/3508 +f 880/1058/3509 892/1071/3510 893/1072/3511 881/1059/3512 +f 881/1059/3513 893/1072/3514 894/1073/3515 882/1060/3516 +f 882/1060/3517 894/1073/3518 895/1074/3519 883/1061/3520 +f 883/1061/3521 895/1074/3522 896/1075/3523 884/1062/3524 +f 884/1062/3525 896/1075/3526 897/1076/3527 885/1063/3528 +f 885/1064/3529 897/1077/3530 898/1078/3531 886/1065/3532 +f 886/1065/3533 898/1078/3534 899/1079/3535 887/1066/3536 +f 887/1066/3537 899/1079/3538 900/1080/3539 888/1067/3540 +f 888/1067/3541 900/1080/3542 901/1081/3543 889/1068/3544 +f 889/1068/3545 901/1081/3546 902/1082/3547 890/1069/3548 +f 890/1069/3549 902/1082/3550 903/1083/3551 891/1070/3552 +f 891/1070/3553 903/1083/3554 892/1071/3555 880/1058/3556 +f 892/1071/3557 904/1084/3558 905/1085/3559 893/1072/3560 +f 893/1072/3561 905/1085/3562 906/1086/3563 894/1073/3564 +f 894/1073/3565 906/1086/3566 907/1087/3567 895/1074/3568 +f 895/1074/3569 907/1087/3570 908/1088/3571 896/1075/3572 +f 896/1075/3573 908/1088/3574 909/1089/3575 897/1076/3576 +f 897/1077/3577 909/1090/3578 910/1091/3579 898/1078/3580 +f 898/1078/3581 910/1091/3582 911/1092/3583 899/1079/3584 +f 899/1079/3585 911/1092/3586 912/1093/3587 900/1080/3588 +f 900/1080/3589 912/1093/3590 913/1094/3591 901/1081/3592 +f 901/1081/3593 913/1094/3594 914/1095/3595 902/1082/3596 +f 902/1082/3597 914/1095/3598 915/1096/3599 903/1083/3600 +f 903/1083/3601 915/1096/3602 904/1084/3603 892/1071/3604 +f 904/1084/3605 787/938/3606 786/936/3607 905/1085/3608 +f 905/1085/3609 786/936/3610 785/934/3611 906/1086/3612 +f 906/1086/3613 785/934/3614 784/932/3615 907/1087/3616 +f 907/1087/3617 784/932/3618 783/931/3619 908/1088/3620 +f 908/1088/3621 783/931/3622 794/954/3623 909/1089/3624 +f 909/1090/3625 794/952/3626 793/950/3627 910/1091/3628 +f 910/1091/3629 793/950/3630 792/948/3631 911/1092/3632 +f 911/1092/3633 792/948/3634 791/946/3635 912/1093/3636 +f 912/1093/3637 791/946/3638 790/944/3639 913/1094/3640 +f 913/1094/3641 790/944/3642 789/942/3643 914/1095/3644 +f 914/1095/3645 789/942/3646 788/940/3647 915/1096/3648 +f 915/1096/3649 788/940/3650 787/938/3651 904/1084/3652 +f 857/1035/3653 916/1097/3654 856/1032/3655 +f 858/1037/3656 916/1098/3657 857/1035/3658 +f 859/1039/3659 916/1099/3660 858/1037/3661 +f 860/1041/3662 916/1100/3663 859/1039/3664 +f 861/1043/3665 916/1101/3666 860/1041/3667 +f 862/1047/3668 916/1102/3669 861/1044/3670 +f 863/1049/3671 916/1103/3672 862/1047/3673 +f 864/1051/3674 916/1104/3675 863/1049/3676 +f 865/1053/3677 916/1105/3678 864/1051/3679 +f 866/1055/3680 916/1106/3681 865/1053/3682 +f 867/1057/3683 916/1107/3684 866/1055/3685 +f 856/1032/3686 916/1108/3687 867/1057/3688 +f 917/1109/3689 918/1110/3690 930/1111/3691 929/1112/3692 +f 918/1110/3693 919/1113/3694 931/1114/3695 930/1111/3696 +f 919/1113/3697 920/1115/3698 932/1116/3699 931/1114/3700 +f 920/1115/3701 921/1117/3702 933/1118/3703 932/1116/3704 +f 921/1117/3705 922/1119/3706 934/1120/3707 933/1118/3708 +f 922/1121/3709 923/1122/3710 935/1123/3711 934/1124/3712 +f 923/1122/3713 924/1125/3714 936/1126/3715 935/1123/3716 +f 924/1125/3717 925/1127/3718 937/1128/3719 936/1126/3720 +f 925/1127/3721 926/1129/3722 938/1130/3723 937/1128/3724 +f 926/1129/3725 927/1131/3726 939/1132/3727 938/1130/3728 +f 927/1131/3729 928/1133/3730 940/1134/3731 939/1132/3732 +f 928/1133/3733 917/1109/3734 929/1112/3735 940/1134/3736 +f 929/1112/3737 930/1111/3738 942/1135/3739 941/1136/3740 +f 930/1111/3741 931/1114/3742 943/1137/3743 942/1135/3744 +f 931/1114/3745 932/1116/3746 944/1138/3747 943/1137/3748 +f 932/1116/3749 933/1118/3750 945/1139/3751 944/1138/3752 +f 933/1118/3753 934/1120/3754 946/1140/3755 945/1139/3756 +f 934/1124/3757 935/1123/3758 947/1141/3759 946/1142/3760 +f 935/1123/3761 936/1126/3762 948/1143/3763 947/1141/3764 +f 936/1126/3765 937/1128/3766 949/1144/3767 948/1143/3768 +f 937/1128/3769 938/1130/3770 950/1145/3771 949/1144/3772 +f 938/1130/3773 939/1132/3774 951/1146/3775 950/1145/3776 +f 939/1132/3777 940/1134/3778 952/1147/3779 951/1146/3780 +f 940/1134/3781 929/1112/3782 941/1136/3783 952/1147/3784 +f 941/1136/3785 942/1135/3786 954/1148/3787 953/1149/3788 +f 942/1135/3789 943/1137/3790 955/1150/3791 954/1148/3792 +f 943/1137/3793 944/1138/3794 956/1151/3795 955/1150/3796 +f 944/1138/3797 945/1139/3798 957/1152/3799 956/1151/3800 +f 945/1139/3801 946/1140/3802 958/1153/3803 957/1152/3804 +f 946/1142/3805 947/1141/3806 959/1154/3807 958/1155/3808 +f 947/1141/3809 948/1143/3810 960/1156/3811 959/1154/3812 +f 948/1143/3813 949/1144/3814 961/1157/3815 960/1156/3816 +f 949/1144/3817 950/1145/3818 962/1158/3819 961/1157/3820 +f 950/1145/3821 951/1146/3822 963/1159/3823 962/1158/3824 +f 951/1146/3825 952/1147/3826 964/1160/3827 963/1159/3828 +f 952/1147/3829 941/1136/3830 953/1149/3831 964/1160/3832 +f 953/1149/3833 954/1148/3834 966/1161/3835 965/1162/3836 +f 954/1148/3837 955/1150/3838 967/1163/3839 966/1161/3840 +f 955/1150/3841 956/1151/3842 968/1164/3843 967/1163/3844 +f 956/1151/3845 957/1152/3846 969/1165/3847 968/1164/3848 +f 957/1152/3849 958/1153/3850 970/1166/3851 969/1165/3852 +f 958/1155/3853 959/1154/3854 971/1167/3855 970/1168/3856 +f 959/1154/3857 960/1156/3858 972/1169/3859 971/1167/3860 +f 960/1156/3861 961/1157/3862 973/1170/3863 972/1169/3864 +f 961/1157/3865 962/1158/3866 974/1171/3867 973/1170/3868 +f 962/1158/3869 963/1159/3870 975/1172/3871 974/1171/3872 +f 963/1159/3873 964/1160/3874 976/1173/3875 975/1172/3876 +f 964/1160/3877 953/1149/3878 965/1162/3879 976/1173/3880 +f 965/1162/3881 966/1161/3882 774/935/3883 775/937/3884 +f 966/1161/3885 967/1163/3886 773/933/3887 774/935/3888 +f 967/1163/3889 968/1164/3890 772/929/3891 773/933/3892 +f 968/1164/3893 969/1165/3894 771/930/3895 772/929/3896 +f 969/1165/3897 970/1166/3898 782/953/3899 771/930/3900 +f 970/1168/3901 971/1167/3902 781/949/3903 782/951/3904 +f 971/1167/3905 972/1169/3906 780/947/3907 781/949/3908 +f 972/1169/3909 973/1170/3910 779/945/3911 780/947/3912 +f 973/1170/3913 974/1171/3914 778/943/3915 779/945/3916 +f 974/1171/3917 975/1172/3918 777/941/3919 778/943/3920 +f 975/1172/3921 976/1173/3922 776/939/3923 777/941/3924 +f 976/1173/3925 965/1162/3926 775/937/3927 776/939/3928 +f 918/1110/3929 917/1109/3930 977/1174/3931 +f 919/1113/3932 918/1110/3933 977/1175/3934 +f 920/1115/3935 919/1113/3936 977/1176/3937 +f 921/1117/3938 920/1115/3939 977/1177/3940 +f 922/1119/3941 921/1117/3942 977/1178/3943 +f 923/1122/3944 922/1121/3945 977/1179/3946 +f 924/1125/3947 923/1122/3948 977/1180/3949 +f 925/1127/3950 924/1125/3951 977/1181/3952 +f 926/1129/3953 925/1127/3954 977/1182/3955 +f 927/1131/3956 926/1129/3957 977/1183/3958 +f 928/1133/3959 927/1131/3960 977/1184/3961 +f 917/1109/3962 928/1133/3963 977/1185/3964 +f 978/1186/3965 979/1187/3966 991/1188/3967 990/1189/3968 +f 979/1187/3969 980/1190/3970 992/1191/3971 991/1188/3972 +f 980/1190/3973 981/1192/3974 993/1193/3975 992/1191/3976 +f 981/1192/3977 982/1194/3978 994/1195/3979 993/1193/3980 +f 982/1194/3981 983/1196/3982 995/1197/3983 994/1195/3984 +f 983/1198/3985 984/1199/3986 996/1200/3987 995/1201/3988 +f 984/1199/3989 985/1202/3990 997/1203/3991 996/1200/3992 +f 985/1202/3993 986/1204/3994 998/1205/3995 997/1203/3996 +f 986/1204/3997 987/1206/3998 999/1207/3999 998/1205/4000 +f 987/1206/4001 988/1208/4002 1000/1209/4003 999/1207/4004 +f 988/1208/4005 989/1210/4006 1001/1211/4007 1000/1209/4008 +f 989/1210/4009 978/1186/4010 990/1189/4011 1001/1211/4012 +f 990/1189/4013 991/1188/4014 1003/1212/4015 1002/1213/4016 +f 991/1188/4017 992/1191/4018 1004/1214/4019 1003/1212/4020 +f 992/1191/4021 993/1193/4022 1005/1215/4023 1004/1214/4024 +f 993/1193/4025 994/1195/4026 1006/1216/4027 1005/1215/4028 +f 994/1195/4029 995/1197/4030 1007/1217/4031 1006/1216/4032 +f 995/1201/4033 996/1200/4034 1008/1218/4035 1007/1219/4036 +f 996/1200/4037 997/1203/4038 1009/1220/4039 1008/1218/4040 +f 997/1203/4041 998/1205/4042 1010/1221/4043 1009/1220/4044 +f 998/1205/4045 999/1207/4046 1011/1222/4047 1010/1221/4048 +f 999/1207/4049 1000/1209/4050 1012/1223/4051 1011/1222/4052 +f 1000/1209/4053 1001/1211/4054 1013/1224/4055 1012/1223/4056 +f 1001/1211/4057 990/1189/4058 1002/1213/4059 1013/1224/4060 +f 1002/1213/4061 1003/1212/4062 1015/1225/4063 1014/1226/4064 +f 1003/1212/4065 1004/1214/4066 1016/1227/4067 1015/1225/4068 +f 1004/1214/4069 1005/1215/4070 1017/1228/4071 1016/1227/4072 +f 1005/1215/4073 1006/1216/4074 1018/1229/4075 1017/1228/4076 +f 1006/1216/4077 1007/1217/4078 1019/1230/4079 1018/1229/4080 +f 1007/1219/4081 1008/1218/4082 1020/1231/4083 1019/1232/4084 +f 1008/1218/4085 1009/1220/4086 1021/1233/4087 1020/1231/4088 +f 1009/1220/4089 1010/1221/4090 1022/1234/4091 1021/1233/4092 +f 1010/1221/4093 1011/1222/4094 1023/1235/4095 1022/1234/4096 +f 1011/1222/4097 1012/1223/4098 1024/1236/4099 1023/1235/4100 +f 1012/1223/4101 1013/1224/4102 1025/1237/4103 1024/1236/4104 +f 1013/1224/4105 1002/1213/4106 1014/1226/4107 1025/1237/4108 +f 1014/1226/4109 1015/1225/4110 1027/1238/4111 1026/1239/4112 +f 1015/1225/4113 1016/1227/4114 1028/1240/4115 1027/1238/4116 +f 1016/1227/4117 1017/1228/4118 1029/1241/4119 1028/1240/4120 +f 1017/1228/4121 1018/1229/4122 1030/1242/4123 1029/1241/4124 +f 1018/1229/4125 1019/1230/4126 1031/1243/4127 1030/1242/4128 +f 1019/1232/4129 1020/1231/4130 1032/1244/4131 1031/1245/4132 +f 1020/1231/4133 1021/1233/4134 1033/1246/4135 1032/1244/4136 +f 1021/1233/4137 1022/1234/4138 1034/1247/4139 1033/1246/4140 +f 1022/1234/4141 1023/1235/4142 1035/1248/4143 1034/1247/4144 +f 1023/1235/4145 1024/1236/4146 1036/1249/4147 1035/1248/4148 +f 1024/1236/4149 1025/1237/4150 1037/1250/4151 1036/1249/4152 +f 1025/1237/4153 1014/1226/4154 1026/1239/4155 1037/1250/4156 +f 1026/1239/4157 1027/1238/4158 668/833/4159 669/834/4160 +f 1027/1238/4161 1028/1240/4162 667/832/4163 668/833/4164 +f 1028/1240/4165 1029/1241/4166 666/831/4167 667/832/4168 +f 1029/1241/4169 1030/1242/4170 665/829/4171 666/831/4172 +f 1030/1242/4173 1031/1243/4174 664/830/4175 665/829/4176 +f 1031/1245/4177 1032/1244/4178 663/827/4179 664/828/4180 +f 1032/1244/4181 1033/1246/4182 662/826/4183 663/827/4184 +f 1033/1246/4185 1034/1247/4186 661/824/4187 662/826/4188 +f 1034/1247/4189 1035/1248/4190 660/825/4191 661/824/4192 +f 1035/1248/4193 1036/1249/4194 659/836/4195 660/825/4196 +f 1036/1249/4197 1037/1250/4198 658/835/4199 659/836/4200 +f 1037/1250/4201 1026/1239/4202 669/834/4203 658/835/4204 +f 979/1187/4205 978/1186/4206 1038/1251/4207 +f 980/1190/4208 979/1187/4209 1038/1252/4210 +f 981/1192/4211 980/1190/4212 1038/1253/4213 +f 982/1194/4214 981/1192/4215 1038/1254/4216 +f 983/1196/4217 982/1194/4218 1038/1255/4219 +f 984/1199/4220 983/1198/4221 1038/1256/4222 +f 985/1202/4223 984/1199/4224 1038/1257/4225 +f 986/1204/4226 985/1202/4227 1038/1258/4228 +f 987/1206/4229 986/1204/4230 1038/1259/4231 +f 988/1208/4232 987/1206/4233 1038/1260/4234 +f 989/1210/4235 988/1208/4236 1038/1261/4237 +f 978/1186/4238 989/1210/4239 1038/1262/4240 +f 1039/1263/4241 1040/1264/4242 1052/1265/4243 1051/1266/4244 +f 1040/1264/4245 1041/1267/4246 1053/1268/4247 1052/1265/4248 +f 1041/1267/4249 1042/1269/4250 1054/1270/4251 1053/1268/4252 +f 1042/1269/4253 1043/1271/4254 1055/1272/4255 1054/1270/4256 +f 1043/1271/4257 1044/1273/4258 1056/1274/4259 1055/1272/4260 +f 1044/1275/4261 1045/1276/4262 1057/1277/4263 1056/1278/4264 +f 1045/1276/4265 1046/1279/4266 1058/1280/4267 1057/1277/4268 +f 1046/1279/4269 1047/1281/4270 1059/1282/4271 1058/1280/4272 +f 1047/1281/4273 1048/1283/4274 1060/1284/4275 1059/1282/4276 +f 1048/1283/4277 1049/1285/4278 1061/1286/4279 1060/1284/4280 +f 1049/1285/4281 1050/1287/4282 1062/1288/4283 1061/1286/4284 +f 1050/1287/4285 1039/1263/4286 1051/1266/4287 1062/1288/4288 +f 1051/1266/4289 1052/1265/4290 1064/1289/4291 1063/1290/4292 +f 1052/1265/4293 1053/1268/4294 1065/1291/4295 1064/1289/4296 +f 1053/1268/4297 1054/1270/4298 1066/1292/4299 1065/1291/4300 +f 1054/1270/4301 1055/1272/4302 1067/1293/4303 1066/1292/4304 +f 1055/1272/4305 1056/1274/4306 1068/1294/4307 1067/1293/4308 +f 1056/1278/4309 1057/1277/4310 1069/1295/4311 1068/1296/4312 +f 1057/1277/4313 1058/1280/4314 1070/1297/4315 1069/1295/4316 +f 1058/1280/4317 1059/1282/4318 1071/1298/4319 1070/1297/4320 +f 1059/1282/4321 1060/1284/4322 1072/1299/4323 1071/1298/4324 +f 1060/1284/4325 1061/1286/4326 1073/1300/4327 1072/1299/4328 +f 1061/1286/4329 1062/1288/4330 1074/1301/4331 1073/1300/4332 +f 1062/1288/4333 1051/1266/4334 1063/1290/4335 1074/1301/4336 +f 1063/1290/4337 1064/1289/4338 1076/1302/4339 1075/1303/4340 +f 1064/1289/4341 1065/1291/4342 1077/1304/4343 1076/1302/4344 +f 1065/1291/4345 1066/1292/4346 1078/1305/4347 1077/1304/4348 +f 1066/1292/4349 1067/1293/4350 1079/1306/4351 1078/1305/4352 +f 1067/1293/4353 1068/1294/4354 1080/1307/4355 1079/1306/4356 +f 1068/1296/4357 1069/1295/4358 1081/1308/4359 1080/1309/4360 +f 1069/1295/4361 1070/1297/4362 1082/1310/4363 1081/1308/4364 +f 1070/1297/4365 1071/1298/4366 1083/1311/4367 1082/1310/4368 +f 1071/1298/4369 1072/1299/4370 1084/1312/4371 1083/1311/4372 +f 1072/1299/4373 1073/1300/4374 1085/1313/4375 1084/1312/4376 +f 1073/1300/4377 1074/1301/4378 1086/1314/4379 1085/1313/4380 +f 1074/1301/4381 1063/1290/4382 1075/1303/4383 1086/1314/4384 +f 1075/1303/4385 1076/1302/4386 1088/1315/4387 1087/1316/4388 +f 1076/1302/4389 1077/1304/4390 1089/1317/4391 1088/1315/4392 +f 1077/1304/4393 1078/1305/4394 1090/1318/4395 1089/1317/4396 +f 1078/1305/4397 1079/1306/4398 1091/1319/4399 1090/1318/4400 +f 1079/1306/4401 1080/1307/4402 1092/1320/4403 1091/1319/4404 +f 1080/1309/4405 1081/1308/4406 1093/1321/4407 1092/1322/4408 +f 1081/1308/4409 1082/1310/4410 1094/1323/4411 1093/1321/4412 +f 1082/1310/4413 1083/1311/4414 1095/1324/4415 1094/1323/4416 +f 1083/1311/4417 1084/1312/4418 1096/1325/4419 1095/1324/4420 +f 1084/1312/4421 1085/1313/4422 1097/1326/4423 1096/1325/4424 +f 1085/1313/4425 1086/1314/4426 1098/1327/4427 1097/1326/4428 +f 1086/1314/4429 1075/1303/4430 1087/1316/4431 1098/1327/4432 +f 1087/1316/4433 1088/1315/4434 685/796/4435 686/798/4436 +f 1088/1315/4437 1089/1317/4438 684/794/4439 685/796/4440 +f 1089/1317/4441 1090/1318/4442 683/790/4443 684/794/4444 +f 1090/1318/4445 1091/1319/4446 682/791/4447 683/790/4448 +f 1091/1319/4449 1092/1320/4450 693/813/4451 682/791/4452 +f 1092/1322/4453 1093/1321/4454 692/810/4455 693/812/4456 +f 1093/1321/4457 1094/1323/4458 691/808/4459 692/810/4460 +f 1094/1323/4461 1095/1324/4462 690/806/4463 691/808/4464 +f 1095/1324/4465 1096/1325/4466 689/804/4467 690/806/4468 +f 1096/1325/4469 1097/1326/4470 688/802/4471 689/804/4472 +f 1097/1326/4473 1098/1327/4474 687/800/4475 688/802/4476 +f 1098/1327/4477 1087/1316/4478 686/798/4479 687/800/4480 +f 1040/1264/4481 1039/1263/4482 1099/1328/4483 +f 1041/1267/4484 1040/1264/4485 1099/1329/4486 +f 1042/1269/4487 1041/1267/4488 1099/1330/4489 +f 1043/1271/4490 1042/1269/4491 1099/1331/4492 +f 1044/1273/4493 1043/1271/4494 1099/1332/4495 +f 1045/1276/4496 1044/1275/4497 1099/1333/4498 +f 1046/1279/4499 1045/1276/4500 1099/1334/4501 +f 1047/1281/4502 1046/1279/4503 1099/1335/4504 +f 1048/1283/4505 1047/1281/4506 1099/1336/4507 +f 1049/1285/4508 1048/1283/4509 1099/1337/4510 +f 1050/1287/4511 1049/1285/4512 1099/1338/4513 +f 1039/1263/4514 1050/1287/4515 1099/1339/4516 +f 1100/1340/4517 1112/1341/4518 1113/1342/4519 1101/1343/4520 +f 1101/1343/4521 1113/1342/4522 1114/1344/4523 1102/1345/4524 +f 1102/1345/4525 1114/1344/4526 1115/1346/4527 1103/1347/4528 +f 1103/1347/4529 1115/1346/4530 1116/1348/4531 1104/1349/4532 +f 1104/1349/4533 1116/1348/4534 1117/1350/4535 1105/1351/4536 +f 1105/1352/4537 1117/1353/4538 1118/1354/4539 1106/1355/4540 +f 1106/1355/4541 1118/1354/4542 1119/1356/4543 1107/1357/4544 +f 1107/1357/4545 1119/1356/4546 1120/1358/4547 1108/1359/4548 +f 1108/1359/4549 1120/1358/4550 1121/1360/4551 1109/1361/4552 +f 1109/1361/4553 1121/1360/4554 1122/1362/4555 1110/1363/4556 +f 1110/1363/4557 1122/1362/4558 1123/1364/4559 1111/1365/4560 +f 1111/1365/4561 1123/1364/4562 1112/1341/4563 1100/1340/4564 +f 1112/1341/4565 1124/1366/4566 1125/1367/4567 1113/1342/4568 +f 1113/1342/4569 1125/1367/4570 1126/1368/4571 1114/1344/4572 +f 1114/1344/4573 1126/1368/4574 1127/1369/4575 1115/1346/4576 +f 1115/1346/4577 1127/1369/4578 1128/1370/4579 1116/1348/4580 +f 1116/1348/4581 1128/1370/4582 1129/1371/4583 1117/1350/4584 +f 1117/1353/4585 1129/1372/4586 1130/1373/4587 1118/1354/4588 +f 1118/1354/4589 1130/1373/4590 1131/1374/4591 1119/1356/4592 +f 1119/1356/4593 1131/1374/4594 1132/1375/4595 1120/1358/4596 +f 1120/1358/4597 1132/1375/4598 1133/1376/4599 1121/1360/4600 +f 1121/1360/4601 1133/1376/4602 1134/1377/4603 1122/1362/4604 +f 1122/1362/4605 1134/1377/4606 1135/1378/4607 1123/1364/4608 +f 1123/1364/4609 1135/1378/4610 1124/1366/4611 1112/1341/4612 +f 1124/1366/4613 1136/1379/4614 1137/1380/4615 1125/1367/4616 +f 1125/1367/4617 1137/1380/4618 1138/1381/4619 1126/1368/4620 +f 1126/1368/4621 1138/1381/4622 1139/1382/4623 1127/1369/4624 +f 1127/1369/4625 1139/1382/4626 1140/1383/4627 1128/1370/4628 +f 1128/1370/4629 1140/1383/4630 1141/1384/4631 1129/1371/4632 +f 1129/1372/4633 1141/1385/4634 1142/1386/4635 1130/1373/4636 +f 1130/1373/4637 1142/1386/4638 1143/1387/4639 1131/1374/4640 +f 1131/1374/4641 1143/1387/4642 1144/1388/4643 1132/1375/4644 +f 1132/1375/4645 1144/1388/4646 1145/1389/4647 1133/1376/4648 +f 1133/1376/4649 1145/1389/4650 1146/1390/4651 1134/1377/4652 +f 1134/1377/4653 1146/1390/4654 1147/1391/4655 1135/1378/4656 +f 1135/1378/4657 1147/1391/4658 1136/1379/4659 1124/1366/4660 +f 1136/1379/4661 1148/1392/4662 1149/1393/4663 1137/1380/4664 +f 1137/1380/4665 1149/1393/4666 1150/1394/4667 1138/1381/4668 +f 1138/1381/4669 1150/1394/4670 1151/1395/4671 1139/1382/4672 +f 1139/1382/4673 1151/1395/4674 1152/1396/4675 1140/1383/4676 +f 1140/1383/4677 1152/1396/4678 1153/1397/4679 1141/1384/4680 +f 1141/1385/4681 1153/1398/4682 1154/1399/4683 1142/1386/4684 +f 1142/1386/4685 1154/1399/4686 1155/1400/4687 1143/1387/4688 +f 1143/1387/4689 1155/1400/4690 1156/1401/4691 1144/1388/4692 +f 1144/1388/4693 1156/1401/4694 1157/1402/4695 1145/1389/4696 +f 1145/1389/4697 1157/1402/4698 1158/1403/4699 1146/1390/4700 +f 1146/1390/4701 1158/1403/4702 1159/1404/4703 1147/1391/4704 +f 1147/1391/4705 1159/1404/4706 1148/1392/4707 1136/1379/4708 +f 1148/1392/4709 674/797/4710 673/795/4711 1149/1393/4712 +f 1149/1393/4713 673/795/4714 672/793/4715 1150/1394/4716 +f 1150/1394/4717 672/793/4718 671/789/4719 1151/1395/4720 +f 1151/1395/4721 671/789/4722 670/792/4723 1152/1396/4724 +f 1152/1396/4725 670/792/4726 681/814/4727 1153/1397/4728 +f 1153/1398/4729 681/811/4730 680/809/4731 1154/1399/4732 +f 1154/1399/4733 680/809/4734 679/807/4735 1155/1400/4736 +f 1155/1400/4737 679/807/4738 678/805/4739 1156/1401/4740 +f 1156/1401/4741 678/805/4742 677/803/4743 1157/1402/4744 +f 1157/1402/4745 677/803/4746 676/801/4747 1158/1403/4748 +f 1158/1403/4749 676/801/4750 675/799/4751 1159/1404/4752 +f 1159/1404/4753 675/799/4754 674/797/4755 1148/1392/4756 +f 1101/1343/4757 1160/1405/4758 1100/1340/4759 +f 1102/1345/4760 1160/1406/4761 1101/1343/4762 +f 1103/1347/4763 1160/1407/4764 1102/1345/4765 +f 1104/1349/4766 1160/1408/4767 1103/1347/4768 +f 1105/1351/4769 1160/1409/4770 1104/1349/4771 +f 1106/1355/4772 1160/1410/4773 1105/1352/4774 +f 1107/1357/4775 1160/1411/4776 1106/1355/4777 +f 1108/1359/4778 1160/1412/4779 1107/1357/4780 +f 1109/1361/4781 1160/1413/4782 1108/1359/4783 +f 1110/1363/4784 1160/1414/4785 1109/1361/4786 +f 1111/1365/4787 1160/1415/4788 1110/1363/4789 +f 1100/1340/4790 1160/1416/4791 1111/1365/4792 diff --git a/app/sampledata/cart.fbx b/app/sampledata/cart.fbx new file mode 100644 index 0000000..6f2d4d7 Binary files /dev/null and b/app/sampledata/cart.fbx differ diff --git a/app/sampledata/cart.sfa b/app/sampledata/cart.sfa new file mode 100644 index 0000000..95b2400 --- /dev/null +++ b/app/sampledata/cart.sfa @@ -0,0 +1,99 @@ +{ + animations: [ + { + path: 'sampledata/cart.fbx', + }, + ], + materials: [ + { + name: 'Shopping_Cart2', + parameters: [ + { + baseColor: [ + 0.80000000000000004, + 0.80000000000000004, + 0.80000000000000004, + 1, + ], + }, + { + baseColorMap: null, + }, + { + normalMap: null, + }, + { + interpolatedColor: null, + }, + { + metallic: 0, + }, + { + metallicMap: null, + }, + { + roughness: 1, + }, + { + roughnessMap: null, + }, + { + opacity: null, + }, + ], + source: 'build/sceneform_sdk/default_materials/fbx_material.sfm', + }, + { + name: 'Shopping_Cart', + parameters: [ + { + baseColor: [ + 0.80000000000000004, + 0.80000000000000004, + 0.80000000000000004, + 1, + ], + }, + { + baseColorMap: null, + }, + { + normalMap: null, + }, + { + interpolatedColor: null, + }, + { + metallic: 0, + }, + { + metallicMap: null, + }, + { + roughness: 1, + }, + { + roughnessMap: null, + }, + { + opacity: null, + }, + ], + source: 'build/sceneform_sdk/default_materials/fbx_material.sfm', + }, + ], + model: { + attributes: [ + 'Position', + 'TexCoord', + 'Orientation', + 'BoneIndices', + 'BoneWeights', + ], + collision: {}, + file: 'sampledata/cart.fbx', + name: 'cart', + recenter: 'root', + }, + version: '0.54:2', +} diff --git a/app/sampledata/cone.fbx b/app/sampledata/cone.fbx new file mode 100644 index 0000000..163aa47 Binary files /dev/null and b/app/sampledata/cone.fbx differ diff --git a/app/sampledata/cone.sfa b/app/sampledata/cone.sfa new file mode 100644 index 0000000..fcedce1 --- /dev/null +++ b/app/sampledata/cone.sfa @@ -0,0 +1,60 @@ +{ + animations: [ + { + path: 'sampledata/models/cone.fbx', + }, + ], + materials: [ + { + name: 'unnamed material', + parameters: [ + { + baseColor: [ + 1, + 1, + 1, + 1, + ], + }, + { + baseColorMap: null, + }, + { + normalMap: null, + }, + { + interpolatedColor: null, + }, + { + metallic: 0, + }, + { + metallicMap: null, + }, + { + roughness: 1, + }, + { + roughnessMap: null, + }, + { + opacity: null, + }, + ], + source: 'build/sceneform_sdk/default_materials/fbx_material.sfm', + }, + ], + model: { + attributes: [ + 'Position', + 'Orientation', + 'BoneIndices', + 'BoneWeights', + ], + collision: {}, + file: 'sampledata/models/cone.fbx', + name: 'cone', + recenter: 'root', + }, + version: '0.54:2', +} diff --git a/app/sampledata/cubito.fbx b/app/sampledata/cubito.fbx new file mode 100644 index 0000000..cd96a51 Binary files /dev/null and b/app/sampledata/cubito.fbx differ diff --git a/app/sampledata/cubito.sfa b/app/sampledata/cubito.sfa new file mode 100644 index 0000000..8673687 --- /dev/null +++ b/app/sampledata/cubito.sfa @@ -0,0 +1,61 @@ +{ + animations: [ + { + path: 'sampledata/cubito.fbx', + }, + ], + materials: [ + { + name: 'Material.002', + parameters: [ + { + baseColor: [ + 0.80000000000000004, + 0.066117999999999996, + 0.40535599999999999, + 1, + ], + }, + { + baseColorMap: null, + }, + { + normalMap: null, + }, + { + interpolatedColor: null, + }, + { + metallic: 0, + }, + { + metallicMap: null, + }, + { + roughness: 1, + }, + { + roughnessMap: null, + }, + { + opacity: null, + }, + ], + source: 'build/sceneform_sdk/default_materials/fbx_material.sfm', + }, + ], + model: { + attributes: [ + 'Position', + 'TexCoord', + 'Orientation', + 'BoneIndices', + 'BoneWeights', + ], + collision: {}, + file: 'sampledata/cubito.fbx', + name: 'cubito', + recenter: 'root', + }, + version: '0.54:2', +} diff --git a/app/sampledata/materials.mtl b/app/sampledata/materials.mtl new file mode 100644 index 0000000..894edb6 --- /dev/null +++ b/app/sampledata/materials.mtl @@ -0,0 +1,90 @@ +newmtl mat0 + Kd 0.73 0.41 0.78 + +newmtl mat1 + Kd 0.61 0.15 0.69 + +newmtl mat2 + Kd 0.40 0.23 0.72 + +newmtl mat3 + Kd 0.50 0.87 0.92 + +newmtl mat4 + Kd 0.00 0.74 0.83 + +newmtl mat5 + Kd 0.01 0.61 0.90 + +newmtl mat6 + Kd 0.97 0.73 0.82 + +newmtl mat7 + Kd 0.94 0.38 0.57 + +newmtl mat8 + Kd 0.96 0.26 0.21 + +newmtl mat9 + Kd 0.55 0.76 0.29 + +newmtl mat10 + Kd 0.30 0.69 0.31 + +newmtl mat11 + Kd 0.00 0.59 0.53 + +newmtl mat12 + Kd 1.00 0.92 0.23 + +newmtl mat13 + Kd 1.00 0.60 0.00 + +newmtl mat14 + Kd 1.00 0.34 0.13 + +newmtl mat15 + Kd 0.81 0.85 0.86 + +newmtl mat16 + Kd 0.47 0.56 0.61 + +newmtl mat17 + Kd 0.27 0.35 0.39 + +newmtl mat18 + Kd 1.00 0.80 0.53 + +newmtl mat19 + Kd 0.87 0.60 0.27 + +newmtl mat20 + Kd 0.47 0.33 0.28 + +newmtl mat21 + Kd 1.00 1.00 1.00 + +newmtl mat22 + Kd 0.62 0.62 0.62 + +newmtl mat23 + Kd 0.10 0.10 0.10 + +newmtl mat24 + Ka 0.58 0.65 1.00 + Kd 0.92 0.95 0.94 + Ks 1 1 1 + illum 9 + Ns 300 + d 0.4 + Ni 1.5 + +newmtl mat25 + Ka 1.00 0.65 0.67 + Kd 0.92 0.95 0.94 + Ks 1 1 1 + illum 9 + Ns 300 + d 0.4 + Ni 1.5 + diff --git a/app/sampledata/model.obj b/app/sampledata/model.obj new file mode 100644 index 0000000..b0bff0e --- /dev/null +++ b/app/sampledata/model.obj @@ -0,0 +1,85 @@ +mtllib materials.mtl +v 0.01999998 -0.00999999 -0.01999998 +v 0.01999998 0.00999999 -0.01999998 +v 0.01999998 0.00999999 0.02000004 +v 0.01999998 -0.00999999 0.02000004 +v 0.05999997 -0.00999999 2.980232E-08 +v 0.05999997 0.00999999 2.980232E-08 +v 0.01999998 -0.00999999 -0.00999999 +v 0.01999998 -0.00999999 0.00999999 +v 0.01999998 0.00999999 0.00999999 +v 0.01999998 0.00999999 -0.00999999 +v -0.1 -0.00999999 -0.00999999 +v -0.1 0.00999999 -0.00999999 +v -0.1 0.00999999 0.00999999 +v -0.1 -0.00999999 0.00999999 +vn -1 0 0 +vn 0 -1 0 +vn 0 1 1.084999E-08 +vn 0.4472139 3.019758E-09 -0.8944271 +vn 0.4472139 -3.019758E-09 0.8944271 +vn 1 0 0 +vn -1 0 0 +vn 0 -1 0 +vn 0 1 0 +vn 0 0 -1 +vn 0 0 1 +vt -0.113574 0.1056691 +vt -0.1147364 0.1072966 +vt -0.1179914 0.1049716 +vt -0.1168289 0.1033442 +vt 0.01493334 -0.05630272 +vt 0.01170451 -0.059397 +vt 0.01547148 -0.06074236 +vt -0.01493334 -0.05630272 +vt -0.01547148 -0.06074236 +vt -0.01170451 -0.059397 +vt -0.1415125 0.06222544 +vt -0.143166 0.06335066 +vt -0.1456821 0.05965347 +vt -0.1440286 0.05852824 +vt 0.162058 0.01301579 +vt 0.1639745 0.008975138 +vt 0.1657816 0.009832244 +vt 0.163865 0.01387289 +vt 0.1143877 0.1050879 +vt 0.1160152 0.1039254 +vt 0.1171776 0.1055529 +vt 0.1155502 0.1067153 +vt -0.1143877 0.1050879 +vt -0.1155502 0.1067153 +vt -0.1171776 0.1055529 +vt -0.1160152 0.1039254 +vt 0.01264625 -0.05973334 +vt 0.008610212 -0.07103425 +vt 0.01049369 -0.07170692 +vt 0.01452973 -0.06040602 +vt -0.01264625 -0.05973334 +vt -0.01452973 -0.06040602 +vt -0.01049369 -0.07170692 +vt -0.008610212 -0.07103425 +vt -0.159208 0.02146626 +vt -0.1609969 0.02236068 +vt -0.1663635 0.01162755 +vt -0.1645746 0.01073313 +vt 0.159208 0.02146626 +vt 0.1645746 0.01073313 +vt 0.1663635 0.01162755 +vt 0.1609969 0.02236068 +o group1696024066 +g mesh1696024066 +usemtl mat2 +f 4/4/1 3/3/1 2/2/1 1/1/1 +f 4/7/2 1/6/2 5/5/2 +f 2/10/3 3/9/3 6/8/3 +f 1/14/4 2/13/4 6/12/4 5/11/4 +f 6/18/5 3/17/5 4/16/5 5/15/5 +o group1772553426 +g mesh1772553426 +usemtl mat4 +f 10/22/6 9/21/6 8/20/6 7/19/6 +f 14/26/7 13/25/7 12/24/7 11/23/7 +f 8/30/8 14/29/8 11/28/8 7/27/8 +f 12/34/9 13/33/9 9/32/9 10/31/9 +f 11/38/10 12/37/10 10/36/10 7/35/10 +f 9/42/11 13/41/11 14/40/11 8/39/11 diff --git a/app/sampledata/model.sfa b/app/sampledata/model.sfa new file mode 100644 index 0000000..4ea8675 --- /dev/null +++ b/app/sampledata/model.sfa @@ -0,0 +1,68 @@ +{ + materials: [ + { + name: 'mat2', + parameters: [ + { + baseColor: null, + }, + { + baseColorTint: [ + 0.40000000000000002, + 0.23000000000000001, + 0.71999999999999997, + 1, + ], + }, + { + metallic: 0, + }, + { + roughness: 1, + }, + { + opacity: null, + }, + ], + source: 'build/sceneform_sdk/default_materials/obj_material.sfm', + }, + { + name: 'mat4', + parameters: [ + { + baseColor: null, + }, + { + baseColorTint: [ + 0, + 0.73999999999999999, + 0.82999999999999996, + 1, + ], + }, + { + metallic: 0, + }, + { + roughness: 1, + }, + { + opacity: null, + }, + ], + source: 'build/sceneform_sdk/default_materials/obj_material.sfm', + }, + ], + model: { + attributes: [ + 'Position', + 'TexCoord', + 'Orientation', + ], + collision: {}, + file: 'sampledata/model.obj', + name: 'model', + recenter: 'root', + }, + version: '0.54:2', +} diff --git a/app/sampledata/pyramid.fbx b/app/sampledata/pyramid.fbx new file mode 100644 index 0000000..733de42 Binary files /dev/null and b/app/sampledata/pyramid.fbx differ diff --git a/app/sampledata/pyramid.sfa b/app/sampledata/pyramid.sfa new file mode 100644 index 0000000..8d79f37 --- /dev/null +++ b/app/sampledata/pyramid.sfa @@ -0,0 +1,60 @@ +{ + animations: [ + { + path: 'sampledata/models/pyramid.fbx', + }, + ], + materials: [ + { + name: 'unnamed material', + parameters: [ + { + baseColor: [ + 1, + 1, + 1, + 1, + ], + }, + { + baseColorMap: null, + }, + { + normalMap: null, + }, + { + interpolatedColor: null, + }, + { + metallic: 0, + }, + { + metallicMap: null, + }, + { + roughness: 1, + }, + { + roughnessMap: null, + }, + { + opacity: null, + }, + ], + source: 'build/sceneform_sdk/default_materials/fbx_material.sfm', + }, + ], + model: { + attributes: [ + 'Position', + 'Orientation', + 'BoneIndices', + 'BoneWeights', + ], + collision: {}, + file: 'sampledata/models/pyramid.fbx', + name: 'pyramid', + recenter: 'root', + }, + version: '0.54:2', +} diff --git a/app/sampledata/ring.fbx b/app/sampledata/ring.fbx new file mode 100644 index 0000000..22572bf Binary files /dev/null and b/app/sampledata/ring.fbx differ diff --git a/app/sampledata/ring.sfa b/app/sampledata/ring.sfa new file mode 100644 index 0000000..c0daaa6 --- /dev/null +++ b/app/sampledata/ring.sfa @@ -0,0 +1,60 @@ +{ + animations: [ + { + path: 'sampledata/models/ring.fbx', + }, + ], + materials: [ + { + name: 'unnamed material', + parameters: [ + { + baseColor: [ + 1, + 1, + 1, + 1, + ], + }, + { + baseColorMap: null, + }, + { + normalMap: null, + }, + { + interpolatedColor: null, + }, + { + metallic: 0, + }, + { + metallicMap: null, + }, + { + roughness: 1, + }, + { + roughnessMap: null, + }, + { + opacity: null, + }, + ], + source: 'build/sceneform_sdk/default_materials/fbx_material.sfm', + }, + ], + model: { + attributes: [ + 'Position', + 'Orientation', + 'BoneIndices', + 'BoneWeights', + ], + collision: {}, + file: 'sampledata/models/ring.fbx', + name: 'ring', + recenter: 'root', + }, + version: '0.54:2', +} diff --git a/app/sampledata/stroller.fbx b/app/sampledata/stroller.fbx new file mode 100644 index 0000000..3e46df8 Binary files /dev/null and b/app/sampledata/stroller.fbx differ diff --git a/app/sampledata/stroller.sfa b/app/sampledata/stroller.sfa new file mode 100644 index 0000000..4401b74 --- /dev/null +++ b/app/sampledata/stroller.sfa @@ -0,0 +1,60 @@ +{ + animations: [ + { + path: 'sampledata/stroller.fbx', + }, + ], + materials: [ + { + name: '_16956_Stroller_v1default', + parameters: [ + { + baseColor: [ + 0.80000000000000004, + 0.80000000000000004, + 0.80000000000000004, + 1, + ], + }, + { + baseColorMap: null, + }, + { + normalMap: null, + }, + { + interpolatedColor: null, + }, + { + metallic: 0, + }, + { + metallicMap: null, + }, + { + roughness: 1, + }, + { + roughnessMap: null, + }, + { + opacity: null, + }, + ], + source: 'build/sceneform_sdk/default_materials/fbx_material.sfm', + }, + ], + model: { + attributes: [ + 'Position', + 'Orientation', + 'BoneIndices', + 'BoneWeights', + ], + collision: {}, + file: 'sampledata/stroller.fbx', + name: 'stroller', + recenter: 'root', + }, + version: '0.54:2', +} diff --git a/app/sampledata/wheelchair.fbx b/app/sampledata/wheelchair.fbx new file mode 100644 index 0000000..7ffddc4 Binary files /dev/null and b/app/sampledata/wheelchair.fbx differ diff --git a/app/sampledata/wheelchair.sfa b/app/sampledata/wheelchair.sfa new file mode 100644 index 0000000..5c7752c --- /dev/null +++ b/app/sampledata/wheelchair.sfa @@ -0,0 +1,61 @@ +{ + animations: [ + { + path: 'sampledata/wheelchair.fbx', + }, + ], + materials: [ + { + name: 'Material.001', + parameters: [ + { + baseColor: [ + 1, + 1, + 1, + 1, + ], + }, + { + baseColorMap: null, + }, + { + normalMap: null, + }, + { + interpolatedColor: null, + }, + { + metallic: 0, + }, + { + metallicMap: null, + }, + { + roughness: 1, + }, + { + roughnessMap: null, + }, + { + opacity: null, + }, + ], + source: 'build/sceneform_sdk/default_materials/fbx_material.sfm', + }, + ], + model: { + attributes: [ + 'Position', + 'TexCoord', + 'Orientation', + 'BoneIndices', + 'BoneWeights', + ], + collision: {}, + file: 'sampledata/wheelchair.fbx', + name: 'wheelchair', + recenter: 'root', + }, + version: '0.54:2', +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ade7eb8..1f4dd11 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,9 +1,12 @@ + + @@ -12,7 +15,8 @@ - + @@ -32,14 +36,21 @@ android:protectionLevel="signature" /> + + + android:theme="@style/AppTheme" + android:name="Satellite.GPSApplication" + + android:fullBackupContent="@xml/my_backup_rules" + + android:networkSecurityConfig="@xml/network_security_config" + android:supportsRtl="true"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/cart.sfb b/app/src/main/assets/cart.sfb new file mode 100644 index 0000000..c5d012d Binary files /dev/null and b/app/src/main/assets/cart.sfb differ diff --git a/app/src/main/assets/cone.sfb b/app/src/main/assets/cone.sfb new file mode 100644 index 0000000..515527c Binary files /dev/null and b/app/src/main/assets/cone.sfb differ diff --git a/app/src/main/assets/content/edit.html b/app/src/main/assets/content/edit.html index d71f5ba..4ceb067 100644 --- a/app/src/main/assets/content/edit.html +++ b/app/src/main/assets/content/edit.html @@ -15,6 +15,67 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/cube.obj b/app/src/main/assets/cube.obj new file mode 100644 index 0000000..198b6a0 --- /dev/null +++ b/app/src/main/assets/cube.obj @@ -0,0 +1,40 @@ +# Blender v2.78 (sub 0) OBJ File: 'cube.blend' +# www.blender.org +mtllib cube.mtl +o Cube +v 0.010000 -0.010000 -0.010000 +v 0.010000 -0.010000 0.010000 +v -0.010000 -0.010000 0.010000 +v -0.010000 -0.010000 -0.010000 +v 0.010000 0.010000 -0.010000 +v 0.010000 0.010000 0.010000 +v -0.010000 0.010000 0.010000 +v -0.010000 0.010000 -0.010000 +vt 0.3756 0.5006 +vt 0.6244 0.5006 +vt 0.6244 0.7494 +vt 0.3756 0.7494 +vt 0.3756 0.2519 +vt 0.3756 0.0031 +vt 0.6244 0.0031 +vt 0.6244 0.2519 +vt 0.8731 0.7494 +vt 0.8731 0.9981 +vt 0.6244 0.9981 +vt 0.3756 0.9981 +vt 0.1269 0.9981 +vt 0.1269 0.7494 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn 1.0000 0.0000 0.0000 +vn -0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn 0.0000 0.0000 -1.0000 +usemtl Material +s off +f 1/1/1 2/2/1 3/3/1 4/4/1 +f 5/5/2 8/6/2 7/7/2 6/8/2 +f 1/1/3 5/5/3 6/8/3 2/2/3 +f 2/9/4 6/10/4 7/11/4 3/3/4 +f 3/3/5 7/11/5 8/12/5 4/4/5 +f 5/13/6 1/14/6 4/4/6 8/12/6 diff --git a/app/src/main/assets/cube_cyan.png b/app/src/main/assets/cube_cyan.png new file mode 100644 index 0000000..8b4f55f Binary files /dev/null and b/app/src/main/assets/cube_cyan.png differ diff --git a/app/src/main/assets/cube_green.png b/app/src/main/assets/cube_green.png new file mode 100644 index 0000000..d29cf0b Binary files /dev/null and b/app/src/main/assets/cube_green.png differ diff --git a/app/src/main/assets/cubito.sfb b/app/src/main/assets/cubito.sfb new file mode 100644 index 0000000..dbb2c1f Binary files /dev/null and b/app/src/main/assets/cubito.sfb differ diff --git a/app/src/main/assets/index.html b/app/src/main/assets/index.html index dcde1b2..a217011 100644 --- a/app/src/main/assets/index.html +++ b/app/src/main/assets/index.html @@ -142,6 +142,7 @@ + '; + console.log(tmpStr); + return tmpStr; + } + if(definedSqlTypes[i]["code"]=="geometry"){ + console.log("CID: "+cid+" select type from mm4me_gc where f_table_schema||'_'||f_table_name = (select name from mm4me_tables where id="+cid+") ") + var geoType=getGeometryType("(select replace(name,'.','_') from mm4me_tables where id="+cid+")"); + var viewb=''; + var res=' '; + if(geoType=='POINT' || geoType=='MULTIPOINT' ) + return res+''+ + '

GPS Informations

Type
Coords
'+ + ''; + else if(geoType=='LINESTRING' || geoType=='MULTILINESTRING' ) + return res+''+ + ''+ + '
'+viewb+'
'; + else if(geoType=='POLYGON' || geoType=='MULTIPOLYGON' ) + return res+''+ + ''+ + '
'+viewb+'
'; + + } + if(definedSqlTypes[i]["code"]=="tbl_linked"){ + var tmp=obj["value"].split(';'); + //console.log(cleanupTableName('SELECT '+tmp[1]+' FROM '+tmp[2]+' WHERE '+tmp[0]+'='+tblName+'.id')); + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(tmp[3]),[])); + var tmpStr='"; + + } + if(definedSqlTypes[i]["code"]=="link"){ + var strReturn; + if(!View_template) + $.ajax({ + async: false, + method: "GET", + url: './content/view_template.html', + error: function(){ + console.log("Nothing to run after"); + }, + success: function(data){ + console.log("**** \n\n Load View Template"); + View_template=data; + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + if(refs!=""){ + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=data.replace(reg,refs["0"]["tid"]); + } + } + }); + else{ + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=View_template.replace(reg,refs["0"]["tid"]); + + } + //console.log(strReturn); + return strReturn; + } + if(definedSqlTypes[i]["code"]=="ref"){ + var req=obj["value"];//.replace(/^\((\w+)\)$/g,"$1"); + if(req[0]=="(") + req="SELECT * FROM "+req; + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(req),[])); + var tmpStr='"; + + if(obj["dependencies"]) + try{ + var lobj={}; + lobj[obj["id"]]={"dep":JSON.parse(obj["dependencies"])}; + for(var jj=0;jj=0){ + return ''; + } + if(definedSqlTypes[i]["code"]=="html") + return ''; + if(definedSqlTypes[i]["code"]=="text") + return ''; + if(definedSqlTypes[i]["code"]=="boolean") + return ''; + if(definedSqlTypes[i]["code"]=="date" || definedSqlTypes[i]["code"]=="datetime") + return ''; + if(definedSqlTypes[i]["code"]=="float") + return ''; + return definedSqlTypes[i]["code"]; + } + } + return null; +} + +function printOptionalCheckbox(obj,cid){ + if(definedSqlTypes.length==0){ + definedSqlTypes=JSON.parse(window.Android.displayTable("select id,code from mm4me_ftypes where ftype='e' order by name",[])); + } + + var res=' '; + for(var i in definedSqlTypes){ + if(definedSqlTypes[i]["id"]==obj["ftype"]){ + if(definedSqlTypes[i]["code"]=="bytea"){ + var tmpStr=""; + return res; + } + else if(definedSqlTypes[i]["code"]=="geometry"){ + return res + } + } + } + return ''; +} + +var refTypeId=null; +var editPrintedOnce=[]; +/***************************************************************************** + * Create HTML part to display the line containing both the title and the + * corresponding input for a given table's field. + *****************************************************************************/ +function printEditionFields(obj,myRoot,cid,mid){ + var list1=window.Android.displayTable("select * from mm4me_edition_fields where mm4me_edition_fields.edition>0 and eid="+obj["id"]+" order by mm4me_edition_fields.id asc",[]); + if(!editSchema[mid]) + editSchema[mid]={}; + editSchema[mid][obj["id"]]=JSON.parse(list1); + list1=JSON.parse(list1); + myRoot.find(".tab-content").first().append('
'+obj["description"]+'
'); + for(var j in list1) + if(list1[j]["edition"]>0) { + myRoot.find(".tab-content").first().children().last().append( + '
'+ + '
'+ + ''+ + '
'+ + '
'+ + printCurrentType(list1[j],mid)+ + '
'+ + '
'); + if(list1[j]["dependencies"]){ + try{ + editPrintedOnce.push(list1[j]["name"]); + console.log("JSON PARSE") + console.log(list1[j]["dependencies"]); + var objJson=JSON.parse(list1[j]["dependencies"]); + console.log("JSON PARSE OK"); + if(!refTypeId) + refTypeId=JSON.parse(window.Android.displayTable("select id from mm4me_ftypes where ftype='e' and code='ref'",[]))[0]["id"]; + console.log(refTypeId); + for(i in objJson){ + if(objJson[i]["myself"]){ + console.log("IS MYSELF!!"); + for(k in objJson[i]["myself"]){ + for(l in objJson[i]["myself"][k]){ + + + if(objJson[i]["myself"][k][l]["dependents"]){ + for(m in objJson[i]["myself"][k][l]["dependents"]){ + for(n in objJson[i]["myself"][k][l]["dependents"][m]){ + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]); + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["label"]); + var lObj={"id": n, "ftype":refTypeId,"value":objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]}; + //if(!myRoot.find('select[name="field_'+list1[j]["id"]+'"]').parent().find('select[name="field_'+n+'"]').length) + myRoot.find('select[name="field_'+list1[j]["id"]+'"]').last().parent().prepend( + '
'+ + '
'+ + ''+ + '
'+ + '
'+ + printCurrentType(lObj,mid)+ + '
'+ + '
'); + console.log(myRoot.find('select[name="field_'+n+'"]')); + console.log(printCurrentType(lObj,mid)); + (function(a,b){ + myRoot.find('select[name="field_'+n+'"]').off('change'); + myRoot.find('select[name="field_'+n+'"]').on('change',function(){ + console.log('select[name="field_'+n+'"]'); + var req=cleanupTableName(a["value"]); + if(a["value"].indexOf("WHERE")<0){ + req=req.replace(/order by/g,"where "+b["tfieldf"]+" "+b["operator"]+" "+(b["operator"]=="like"?"'":"")+$(this).val()+(b["operator"]=="like"?"'":"")+" order by") + } + console.log(req); + var res=JSON.parse(window.Android.displayTable(req,[])); + myRoot.find('select[name="field_'+a["id"]+'"]').html(""); + for(ij in res){ + var tmpStr=' '; + cnt+=1; + } + cnt0+=1; + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + $("select[name=field_"+changingField[j][ckey]["id"]+"]").change(); + }else{ + console.log("DISPLAY ELEMENT IF CINDEX >=0 "); + //console.log(JSON.stringify(changingField[j][ckey])); + console.log('input[name="field_'+ckey+'"],select[name="field_'+ckey+'"],textarea[name="field_'+ckey+'"]'); + console.log($('input[name="field_'+changingField[j][ckey]["id"]+'"],select[name="field_'+changingField[j][ckey]["id"]+'"],textarea[name="field_'+changingField[j][ckey]["id"]+'"]').parent().parent().html()); + var mycKey=changingField[j][ckey]["id"]; + if(cIndex<0) + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().hide(); + else + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().show(); + } + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + } + }catch(e){ + console.log(e); + setTimeout(function() { updateChangingFields(changingFields) }, 500); + } +} + +/***************************************************************************** + * Display the content of a table referencing the current edited table. + *****************************************************************************/ +function listInnerTable(id,vid,name,title,init,prefix,clause,ref){ + var list=JSON.parse(window.Android.displayTable("select mm4me_tables.id as tid,mm4me_tables.name as tname,mm4me_editions.id,mm4me_editions.name from mm4me_editions,mm4me_tables where mm4me_editions.ptid=mm4me_tables.id and mm4me_tables.id="+id+" order by mm4me_editions.step asc",[])); + var cnt=0; + var detectInit=true; + var tid=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + tid=list[i]["tid"]; + if(!allTables[list[i]["tid"]]){ + allTables[list[i]["tid"]]={"id":list[i]["id"],"name":list[i]["tname"],"title":list[i]["name"]}; + detectInit=false; + $("#sub_tableContent_"+id+"").find(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#sub_tableContent_"+id+"").find(".mm4me_edition"),list[i]["id"],list[i]["tid"]); + if(cnt==0){ + try{ + var cid=list[i]["id"]+"_0"; + $("#sub_tableContent_"+id+"").find("ul").first().append('
  • '+window.Android.translate('table')+'
  • '); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+" .require-select").hide(); + printEditionFields(list[i],$("#sub_tableContent_"+id+""),cid,list[i]["tid"]); + }catch(e){ + console.log("**** ERROR ***> "+e); + } + } + var myRoot=$("#sub_tableContent_"+id+""); + myRoot.find('ul').first().find('a').click(function (e) { + e.preventDefault(); + if($(this).parent().hasClass('require-select')) + myRoot.find('.mm4me_edition').show(); + else + myRoot.find('.mm4me_edition').hide(); + $(this).tab('show'); + }) + myRoot.find('ul').first().find('a').first().click(); + myRoot.find('.mm4me_edition').find('ul').find('a').first().click(); + myRoot.find('.swagEditor').summernote(); + myRoot.find(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),tid,editTableReact); + }); + myRoot.find(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),tid,editTableReact); + }); + + } + cnt+=1; + } + + var list=JSON.parse(window.Android.displayTable("SELECT id,name,value,alias,width,class from mm4me_view_fields where vid="+vid+" order by id",[])); + var columns=[]; + var columnNames=[]; + var sqlColumns=""; + var orderColumn="id"; + var orderType="asc"; + for(var i=0;i0){ + orderColumn=list[i]["name"]; + if(list[i]["class"]==1) + orderType="desc"; + } + } + var ccol=getPKey(cleanupTableName(name)); + var req="SELECT "+ccol+" as ogc_fid FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list1=JSON.parse(window.Android.displayTable(req,[])); + req="SELECT "+sqlColumns+" FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list=JSON.parse(window.Android.displayTable(req,[])); + var dataSet = []; + for(var i=0;i'; + cnt+=1; + } + dataSet.push(tmpData); + } + var selected = []; + + //$("#edition_form_table").html('
    '); + var localName="exampleTable_"+id; + ((function(localName,sid){ + + if(!detectInit){ + + var options={ + data: dataSet, + columns: columns, + "scrollX": true, + scrollY: '50vh', + scrollCollapse: true, + select: true, + "rowCallback": function( row, data ) { + if ( $.inArray(data.DT_RowId, selected) !== -1 ) { + $(row).addClass('selected'); + } + } + }; + if(langUrl!=null) + options["language"]={ + url: langUrl + }; + + $('#'+localName).DataTable( options ); + $('#'+localName+' tbody').on('click', 'tr', function () { + var id = this.id; + var index = $.inArray(id, selected); + + if ( index === -1 ) { + selected.push( id ); + } else { + selected.splice( index, 1 ); + } + displayEditForm(sid,$(this).find("input[name=id]").first().val(),true); + var tmp=$(this).hasClass('selected'); + $('#'+localName+' tbody tr').removeClass('selected'); + $(this).toggleClass('selected'); + if(!tmp) + $(this).toggleClass('selected'); + } ); + + }else{ + $('#'+localName).dataTable().fnClearTable(); + if(dataSet.length>0) + $('#'+localName).dataTable().fnAddData(dataSet); + } + $("#sub_tableContent_"+id+"").find("ul").first().find('a').first().click(); + })(localName,tid)); + +} + +var onFormFirstLoad=null; +/***************************************************************************** + * Show the edit form + *****************************************************************************/ +function displayEditForm(cid,selectedId,basic){ + if(basic && !$("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val()){ + if(cid==mtable){ + $("#main_tableContent").find(".require-select").hide(); + $("#main_tableContent").find("ul").first().find("a").first().click(); + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + + }else{ + $("#sub_tableContent_"+cid).find(".require-select").hide(); + $("#sub_tableContent_"+cid).find("ul").first().find("a").first().click(); + } + return; + } + var fields=[] + var sizedFields=[]; + var sizedFieldsAlias=[]; + var notSizedFields=[]; + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + //console.log(JSON.stringify(editSchema[cid][i][j])); + if(editSchema[cid][i][j]["ftype"]=="5"){ + sizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")); + sizedFieldsAlias.push(editSchema[cid][i][j]["id"]); + } + else{ + notSizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + try{ + var tmp=JSON.parse(editSchema[cid][i][j]["dependencies"]); + var sqlReq=""; + var sqlClause=""; + var sqlParams=""; + var sqlParam=0; + var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); + var hasDep=false; + var previousElements=[]; + for(k in tmp) + for(l in tmp[k]){ + //console.log(JSON.stringify(tmp[k][l])); + if(l=="myself"){ + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + //console.log(JSON.stringify(tmp[k][l][m][n])); + if(sqlReq!=""){ + sqlReq+=", "; + sqlParams+=", "; + //sqlClause+=" WHERE "+alphabet[sqlParam-1]+"."+tmp[k][l][m][n]["tfield"]+"="+alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + + } + sqlReq+="("+cleanupTableName(tmp[k][l][m][n]["sql_query"])+") as "+alphabet[sqlParam]; + sqlParams+=alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + sqlParam+=1; + if(tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"])); + for(var o in tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"][o])); + for(var p in tmp[k][l][m][n]["dependents"][o]){ + console.log(tmp[k][l][m][n]["dependents"][o][p]["sql_query"].replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")); + sqlReq+=", ("+cleanupTableName(tmp[k][l][m][n]["dependents"][o][p]["sql_query"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")+") as b"; + sqlParams+=", b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlParam+=2; + sqlReq+=", ("+cleanupTableName(editSchema[cid][i][j]["value"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+" from")+") as c"; + + sqlClause+=" WHERE c."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+"=b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlClause+=" AND b."+tmp[k][l][m][n]["tfield"]+"=a."+tmp[k][l][m][n]["tfield"]; + sqlClause+=" AND c.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + hasDep=true; + } + } + } + } + if(!hasDep){ + var tmpReq=cleanupTableName(editSchema[cid][i][j]["value"]); + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + tmpReq=tmpReq.replace(/from/,","+tmp[k][l][m][n]["tfield"]+" from"); + if(sqlClause=="") + sqlClause+=" WHERE "; + else + sqlClause+=" "+tmp[k][l][m][n]["cond_join"]+" "; + sqlClause+=alphabet[m]+"."+tmp[k][l][m][n]["tfield"]+"=a1."+tmp[k][l][m][n]["tfield"]; + sqlClause+=""; + } + sqlClause+=" AND a1.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + //sqlReq=(tmpReq); + console.log(tmpReq); + sqlReq+=", ("+tmpReq+") as a1"; + } + //console.log(JSON.stringify(tmp[k][l])); + console.log("SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause); + var localQuery="SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause; + var res0=JSON.parse(window.Android.displayTable(localQuery,[])); + console.log(JSON.stringify(res0)); + for(m in res0) + for(n in res0[m]){ + try{ + console.log("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]"); + /**/ + if($('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().val(res0[m][n]).change(); + else{ + for(m0 in tmp[k][l]) + for(n0 in tmp[k][l][m0]){ + /*console.log(n0.indexOf(n)<0); + console.log(n); + console.log(n0); + console.log(res0[m][n]);*/ + if(n0.indexOf(n)>=0){ + console.log("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]"); + if(!$('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]").first().val(res0[m][n]).change(); + } + } + } + }catch(e){ + console.log(e); + } + + } + //if(tmp[k][l]["dependents"]) + }else{ + console.log(JSON.stringify(tmp[k][l])); + if(tmp[k][l]["tfield"]=="none"){ + var isNull=JSON.parse(window.Android.displayTable("SELECT CASE WHEN "+l+" is null THEN 1 ELSE 0 END as p FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId,[])); + console.log(JSON.stringify(isNull)); + if(isNull[0]["p"]=="0"){ + console.log(JSON.stringify(isNull)); + console.log("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]"); + console.log(k+""); + $('.mm4me_edition').find("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]").first().val(k+"").change(); + } + } + + } + } + + //if(sqlReq) + }catch(e){ + console.log(e); + } + } + if(editSchema[cid][i][j]["name"].indexOf("unamed")<0) + fields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + } + } + var ccol=getPKey(cleanupTableName(allTables[cid].name)); + /* $("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val() */ + var editValues; + if(sizedFields.length>0){ + for(var i=0;i 1000000 and "+ccol+"="+selectedId,[])); + //console.log(JSON.stringify(hasElement[0])); + if(hasElement[0]["cnt"]!="0"){ + var nbIteration=parseInt(hasElement[0]["len"])/1000000; + var zfields=[] + var len=0; + var query=""; + var len1=parseInt(hasElement[0]["len"]); + for(var j=0;j0){ + $("#field_"+j+"_map").show(); + $("#field_"+j+"_map").off('click'); + $("#field_"+j+"_map").on('click',function(){ + console.log("Display table with a selected feature"); + console.log($(this).prev().val()); + showElementOnMap($(this).prev().val()); + }); + } + if($(".btn_field_"+j+"_lat").length>0){ + //alert(editValues[i][j]); + $(".btn_field_"+j+"_lat").html(editValues[i][j]); + $(".btn_field_"+j+"_long").html(""); + $(".btn_field_"+j+"_source").html(""); + $("input[name='field_"+j+"']").val("POINT"+editValues[i][j]); + + } + } + //$(".swagEditor").summernote(); + } + } + + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + if(editSchema[cid][i][j]["name"].indexOf("unamed")>=0){ + if(editSchema[cid][i][j]["ftype"]==6){ + var tmp=editSchema[cid][i][j]["value"].split(';'); + var list=JSON.parse(window.Android.displayTable("select "+tmp[1]+" from "+cleanupTableName(tmp[2])+" where "+tmp[0]+"=(SELECT id from "+cleanupTableName(tblName)+" WHERE ogc_fid="+selectedId+")",[])); + editValues["0"][editSchema[cid][i][j]["id"]]=list; + for(var k in list){ + for(var l in list[k]){ + $('.mm4me_edition').find("select[name=field_"+editSchema[cid][i][j]["id"]+"]").find('option').each(function(){ + if($(this).val()==list[k][l]) + $(this).prop("selected",true); + }); + } + } + } + } + } + } + + if(cid==mtable){ + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+editValues["0"]["local_id"]+'
  • '); + } + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().show(); + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().find("a").first().click(); + + if(toRunOnLoad[cid]) + for(var i=0;i=0 order by mm4me_editions.step asc",[]); + if(MM4ME_DEBUG) + console.log(list); + list=JSON.parse(list); + if(MM4ME_DEBUG) + console.log((!allTables[tblId])); + if(!allTables[tblId]){ + allTables[tblId]={"id":id,"name":name,"title":title}; + } + mtable=tblId; + + $(".mm4me_edition").find("ul").first().html(""); + $(".mm4me_edition").find(".well").first().html(""); + var cnt=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + var cid=(i==0?list[i]["id"]+"_0":list[i]["id"]); + $(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#edition_form_edit"),cid,mainTable[id]); + } + if(list.length==1) + $(".mm4me_edition").find("ul").first().hide(); + + var aCnt=0; + $('.mm4me_edition').find('ul').first().find('a').each(function () { + if(aCnt>0) + $(this).parent().addClass('require-select'); + aCnt+=1; + }); + $(".require-select").hide(); + $('.mm4me_listing').find('ul').first().find('a').first().click(); + $('.mm4me_edition').find('ul').find('a').first().click(); + $('.swagEditor').summernote(); + $(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+window.Android.translate('edit')+'
  • '); + $(".breadcrumb").append('
  • '+tblTitle+'
  • '); + + setTimeout(function() { updateChangingFields(changingFields) }, 1500); + + /*for(var i=0;i'; + else + cStr+=changingField[j][ckey]["values"][cIndex][i][lkey]+''; + cnt+=1; + } + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + }*/ + $('.mm4me_listing').show(); + $('.mm4me_content').hide(); + +} + +/***************************************************************************** + * In case no server is configured + *****************************************************************************/ +function displayNoListing(){ + $.ajax({ + method: "GET", + url: 'content/nolisting.html', + success: function(data){ + if(MM4ME_DEBUG) + console.log('Display warning message on the UI !'); + $(".mm4me_content").html(data); + $(".mm4me_content").find(".pannel-body").find("p").first().html(window.Android.translate("mm_no_db_found")); + }, + error: function(){ + window.Android.showToast("error !"); + } + }); +} + + +/***************************************************************************** + * Authenticate a user + *****************************************************************************/ +function authenticate(url,login,passwd,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogIn&DataInputs=login="+login+";password="+passwd+"&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG) + console.log(data); + if(func){ + console.log("Call func!") + func(); + } + }, + error: function(){ + if(func1){ + func1(); + } + else{ + disconnect(url); + if(MM4ME_DEBUG) + console.log("unable to login!"); + var hasBeenShown=false; + var xml=arguments[0].responseText; + $(xml).find("ows\\:ExceptionText").each(function(){ + window.Android.showToast($(this).text()); + hasBeenShown=true; + }); + if(!hasBeenShown){ + window.Android.showToast(JSON.stringify(arguments)); + } + } + + } + }); +} + +/***************************************************************************** + * Disconnect a user + *****************************************************************************/ +function disconnect(url,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogOut&DataInputs=&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG){ + console.log(data); + console.log("** Your are no more connected!"); + } + if(func){ + func(); + } + }, + error: function(){ + if(MM4ME_DEBUG){ + console.log(curl); + console.log("unable to disconnect!"); + } + if(func1){ + func1(); + } + } + }); +} + +var geometries={"line":{"geom":null,"constructor": "ol.geom.Linestring"},"polygon":{"geom":null,"constructor":"ol.geom.Polygon","cline":null}}; +var stopTracking=false; +var currentGeometry="line"; +var currentGeometryField="none"; +var map=null; +var vectorLayer,vectorLayer1; +var position; + +/***************************************************************************** + * Track modification of the GPS location + *****************************************************************************/ +function trackCurrentGPSPosition(){ + console.log("## trackCurrentGPSPosition"); + updateCurrentMapLocation(); + + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(tmp0[0]==tmp[0] && tmp0[1]==tmp[1]){ + console.log(" !!!!!!!! Same position!!!!!!!! "); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + return; + } + + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); +} + +var modalCallback=null; + +function trackStepCurrentGPSPosition(){ + console.log("## trackStepCurrentGPSPosition"); + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!stopTracking) + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); +} + +function addCurrentLocation(){ + console.log("addCurrentLocation!!!!"); + if(geometries["origin"]==null) + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + else{ + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + + } +} + +function trackStepByStepPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + } +} +/***************************************************************************** + * Start tracking GPS location + *****************************************************************************/ +function trackGPSPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + } +} + +var hasAddedElement=0; +var myVectorLayer=null; +function addSelectedElement(wktString){ + var features=[]; + try{ + var format = new ol.format.WKT(); + features.push( + format.readFeature(wktString, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }) + ); + }catch(e){ + console.log("Unable to parse WKT ?!"+e) + } + if(!vectorLayer1){ + myVectorLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: features + }), + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: "#3333aa", + width: 1.4 + }) + }) + }); + map.addLayer(myVectorLayer); + } + else{ + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures(features); + } + console.log(vectorLayer1.getSource().getExtent()); + map.updateSize(); + map.getView().fit(vectorLayer1.getSource().getExtent(),map.getSize()); + hasAddedElement=1; +} + +/***************************************************************************** + * Show selected feature on map + *****************************************************************************/ + var addSelectedIndex=0; + var mySelectedElement=null; +function showElementOnMap(wktString){ + mySelectedElement=wktString; + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().hide(); + if(addSelectedIndex==0){ + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + } + addSelectedElement(mySelectedElement); + $("#myModal").find("h4").html(' '+window.Android.translate("view_feature")); + addSelectedIndex++; + }; + //$("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + console.log("OK "); + $('#myModal').modal(); + console.log("OK "); + vectorLayer1.getSource().clear(); + console.log("OK "); + updateCurrentMapLocation(); + console.log("OK "); + addSelectedElement(wktString); + console.log("OK "); + } + + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); +} + + +/***************************************************************************** + * Store the current GPS location in the edit form + *****************************************************************************/ +function requireGPSPosition(elem){ + var position=JSON.parse(window.Android.getGPS()); + //elem=$("#"+elem); + if(position.lat){ + $(".btn_"+elem+"_lat").html(position.lat); + $(".btn_"+elem+"_long").html(position.lon); + $(".btn_"+elem+"_source").html(position.source); + $("input[name='"+elem+"']").val("POINT("+position.lon+" "+position.lat+")"); + }else{ + + } + console.log(JSON.stringify(position)); +} + +/***************************************************************************** + * Ajax setup to ensure seting "withCredentials" to true + *****************************************************************************/ +function ajaxSetup(){ + $.ajaxSetup({ + xhrFields: { + withCredentials: true + } + }); +} + +/***************************************************************************** + * Get the current Network and GPS availability + *****************************************************************************/ +function getCurrentStatus(){ + var tmp=JSON.parse(window.Android.getGNStatus()); + tmp["gps"]=JSON.parse(tmp["gps"]); + updateStatus(tmp['gps'],tmp['net']); +} + +/***************************************************************************** + * Display the status icons + *****************************************************************************/ +function addStatusControl(){ + $('.breadcrumb').append(' /'+ + ''+ + ''+ + ''+ + ''+ + ''); +} + +/***************************************************************************** + * Initialize the map and show the current GPS location + *****************************************************************************/ +var localTiles,localTiles0; +var tileLayers=[]; +function initMapToLocation(){ + if(map) + return; + var osmSource = new ol.source.OSM(); + + var otherLayers=[]; + var otherLayersSwitcher=""; + try{ + var BasesLayersStr=window.Android.getBaseLayers(); + console.log(BasesLayersStr); + var BasesLayers=JSON.parse(BasesLayersStr); + for(var i=0;i=4) + map.getLayers().item(otherLayers[i]["index"]).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity_"+i).show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(!otherLayers[i]["index"]){ + var myTileLayer=new ol.layer.Tile({source: otherLayers[i]["olLayer"]}); + var layers = map.getLayers(); + layers.insertAt(tileLayers.length+1,myTileLayer); + otherLayers[i]["index"]=tileLayers.length+1; + tileLayers.push(myTileLayer); + if(localTileIndex>0) + localTileIndex+=1; + } + else + map.getLayers().item(otherLayers[i]["index"]).setVisible(true); + } + }); + $(".map").parent().find("input[type=range]").last().on('change',function(){ + console.log("change to "+$(this).val()); + map.getLayers().item(otherLayers[i]["index"]).setOpacity($(this).val()/100); + }); + + })(i); + $(".map").parent().find("input[type=checkbox]").parent().off('click'); + $(".map").parent().find("input[type=checkbox]").parent().on('click',function(){ + var tmp=$(this).find("input[type=checkbox]"); + if(tmp.is(":checked")){ + $(this).find("input[type=checkbox]").prop("checked",false).change(); + $(this).addClass("select"); + } + else{ + $(this).find("input[type=checkbox]").prop("checked",true).change(); + $(this).removeClass("select"); + } + }); + + },1); + })(i) + } + } + map = new ol.Map({ + layers: layers, + target: 'map', + controls: ol.control.defaults({ + attributionOptions: /** @type {olx.control.AttributionOptions} */ ({ + collapsible: true + }) + }), + view: new ol.View({ + center: ol.proj.transform([0,0], 'EPSG:4326', 'EPSG:3857'), + zoom: 14, + maxZoom: 22, + minZoom: 1 + }) + }); + + var bstyle = function (feature, resolution) { + + /*var iconFont = 'glyphicon'; + var iconFontText = '\e062';*/ + //var iconFont = 'glyphicon'; + var iconFont = 'Glyphicons Halflings'; + var iconFontText = '\ue062'; + var iconSize = 24; + var opacity=0.4; + var col = 'rgba(0,255,0,0.6)'; + if(feature.get("source")=="GPS") + col = 'rgba(41,136,54,0.5)';//#298836 + else if(feature.get("source")=="Network"){ + col = 'rgba(91,176,75,0.4)';//#5bb04b + iconSize = 32; + opacity=0.2; + } + else if(feature.get("source")=="other"){ + col='rgba(129,208,113,0.5)';//#81d071 + iconSize = 36; + opacity=0.2; + } + else + col='rgba(166,63,39,0.5)'; //#a63f27 //"#EE0000"; + var styles = []; + + var styleIcon = new ol.style.Style({ + text: new ol.style.Text({ + font: 'Normal ' + iconSize + 'px ' + iconFont, + text: iconFontText, + fill: new ol.style.Fill({ color: col }) + }) + }); + styles.push(styleIcon); + + //console.log(feature.get("type")); + return styles; + /*return function (feature, resolution) { + styles.styleIcon.getText().setText(feature.get("iconCode")); + return styles; + };*/ + }; + position=JSON.parse(window.Android.getFullGPS()); + //console.log(JSON.stringify(position)); + if(position.length==0){ + position=[{lon:3.5,lat:43.5,source:"none"}]; + } + var iconFeatures = []; + for(var i=0;i=4) + map.getLayers().item(localTileIndex).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity").show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(map.getLayers().getLength()0.05){ + map.getView().setRotation(-direction); + oldBearer=direction; + } + }else{ + window.Android.stopReportDirection(); + } +} \ No newline at end of file diff --git a/app/src/main/assets/scripts/gene2.js b/app/src/main/assets/scripts/gene2.js new file mode 100644 index 0000000..7387afa --- /dev/null +++ b/app/src/main/assets/scripts/gene2.js @@ -0,0 +1,2461 @@ +var MM4ME_DEBUG=true; +var EDITION_TYPE_FILE=5; +var mainTable={}; +var mtable=null; +var refTables={}; +var tblId=null; +var allTables={}; +var editSchema={}; +var referenceIds={}; +var lastEdition={}; +var tblName=null; +var toRunOnLoad={}; +var valuesOnLoad=[]; +var definedSqlTypes=[]; +var changingFields=[]; +var lang=window.Android.getLang(); +var langUrl=null; +if(lang=="fr"){ + langUrl="file:///android_asset/localisation/French.json"; +} + + + + +function loadWelcome3(){ + window.Android.startWelcomeScreen("draw"); +} + +/***************************************************************************** + * Update status icon color depending on network and GPS availability + *****************************************************************************/ +function updateStatus(gps,internet){ + if(internet){ + $('.glyphicon-signal').css('color','#00EE00'); + }else + $('.glyphicon-signal').css('color','#EE0000'); + $('#gpsMenu1').next().html(""); + if(gps.length==0){ + $('#gpsMenu1').css('color','#EE0000'); + $('#gpsMenu1').next().html('
  • '+window.Android.translate("no_gps")+'
  • '); + }else{ + $('#gpsMenu1').css('color','#00EE00'); + for(var i=0;i '+gps[i].source+''); + } +} + +/***************************************************************************** + * List all available table for a given theme + *****************************************************************************/ +function fetchTableForTheme(obj,id){ + var hasTable=false; + var tables=JSON.parse(window.Android.displayTable( + "SELECT mm4me_tables.id as tid,mm4me_views.id as id,"+ + "mm4me_tables.name as name, "+ + "mm4me_tables.description, "+ + "mm4me_views.name as title "+ + " from mm4me_tables,mm4me_views"+ + " where mm4me_tables.id=mm4me_views.ptid "+ + "and mm4me_views.visible "+ + "and mm4me_views.id in (select vid from mm4me_views_themes where tid="+id+")",[])); + for(var i=0;i0; +} + +/***************************************************************************** + * Create a JSON Object containing the themes hierarchy + *****************************************************************************/ +function fetchThemes(obj,id){ + var cthemes=JSON.parse(window.Android.displayTable("SELECT id,name FROM mm4me_themes where pid = "+id,[])); + for(var i=0;i'); + $('#tree').treeview({ + data: allThemes, + onNodeSelected: function(event, data) { + if(data["myId"]){ + try{ + func(data["myId"],data["myName"],data["myTitle"],true); + }catch(e){ + console.log(JSON.stringify(data)); + console.log(e); + window.Android.showToast(e); + //exit(); + } + } + else{ + $('#tree').treeview('toggleNodeExpanded',[$("#tree").treeview('getSelected')[0], { silent: true }]); + $('#tree').treeview('toggleNodeSelected',[$("#tree").treeview('getSelected')[0], { silent: true }]); + //console.log(JSON.stringify(data)); + //$(this).open(); + } + }, + showTags: true + }); + var list=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.id=mm4me_views.ptid and mm4me_views.visible",[])); + var tableList=list; + var total=0; + contents=[]; + for(var i in list){ + mainTable[list[i]["id"]]=list[i]["tid"]; + } + } + catch(e){ + console.log(e); + displayNoListing(); + } +} + +/***************************************************************************** + * Update the breadcrumbs text to translated string + *****************************************************************************/ +function updateBreadcrumbs(breadcrumbs){ + //var breadcrumbs=["home","view"]; + var lcnt0=0; + $('.breadcrumb').find("a").each(function(){ + $(this).append(window.Android.translate(breadcrumbs[lcnt0])); + lcnt0+=1; + }); + $('.breadcrumb').find("li").last().each(function(){ + $(this).append(window.Android.translate(breadcrumbs[lcnt0])); + lcnt0+=1; + }); +} + +/***************************************************************************** + * Convert table names in a string from the PostgreSQL to our SQLite format. + * So, convert "AAAXXX.YYYBBB" to "AAAXXX_YYYBBB" + *****************************************************************************/ +function cleanupTableName(name){ + return name.replace(/(\w+)(\d*)\.(\d*)(\w+)/g,"$1$2_$3$4"); +} + +/***************************************************************************** + * Display an HTML part containing the image produced by Camera or picked from + * the photo library. + *****************************************************************************/ +function loadNewPicture(cid,id,picture){ + $(".tab-pane").each(function(){ + if($(this).is(":visible")) + $(this).find("#value_"+id) + .html('
    '+picture+'
    '); + }); +} + + +/***************************************************************************** + * Create a JSON Object representing the dependency values. + *****************************************************************************/ +function fetchDependencies(obj,cid,changingField){ + var list1=null; + //console.log(cid+" "+JSON.stringify(obj)); + + for(var key in changingField){ + for(var i=0;i'; + tmpStr+=res+ + '
    '; + console.log(tmpStr); + return tmpStr; + } + if(definedSqlTypes[i]["code"]=="geometry"){ + console.log("CID: "+cid+" select type from mm4me_gc where f_table_schema||'_'||f_table_name = (select name from mm4me_tables where id="+cid+") ") + var geoType=getGeometryType("(select replace(name,'.','_') from mm4me_tables where id="+cid+")"); + var viewb=''; + var res=' '; + if(geoType=='POINT' || geoType=='MULTIPOINT' ) + return res+''+ + '

    GPS Informations

    Type
    Coords
    '+ + ''; + else if(geoType=='LINESTRING' || geoType=='MULTILINESTRING' ) + return res+''+ + ''+ + '
    '+viewb+'
    '; + else if(geoType=='POLYGON' || geoType=='MULTIPOLYGON' ) + return res+''+ + ''+ + '
    '+viewb+'
    '; + + } + if(definedSqlTypes[i]["code"]=="tbl_linked"){ + var tmp=obj["value"].split(';'); + //console.log(cleanupTableName('SELECT '+tmp[1]+' FROM '+tmp[2]+' WHERE '+tmp[0]+'='+tblName+'.id')); + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(tmp[3]),[])); + var tmpStr='"; + + } + if(definedSqlTypes[i]["code"]=="link"){ + var strReturn; + if(!View_template) + $.ajax({ + async: false, + method: "GET", + url: './content/view_template.html', + error: function(){ + console.log("Nothing to run after"); + }, + success: function(data){ + console.log("**** \n\n Load View Template"); + View_template=data; + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + if(refs!=""){ + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=data.replace(reg,refs["0"]["tid"]); + } + } + }); + else{ + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=View_template.replace(reg,refs["0"]["tid"]); + + } + //console.log(strReturn); + return strReturn; + } + if(definedSqlTypes[i]["code"]=="ref"){ + var req=obj["value"];//.replace(/^\((\w+)\)$/g,"$1"); + if(req[0]=="(") + req="SELECT * FROM "+req; + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(req),[])); + var tmpStr='"; + + if(obj["dependencies"]) + try{ + var lobj={}; + lobj[obj["id"]]={"dep":JSON.parse(obj["dependencies"])}; + for(var jj=0;jj=0){ + return ''; + } + if(definedSqlTypes[i]["code"]=="html") + return ''; + if(definedSqlTypes[i]["code"]=="text") + return ''; + if(definedSqlTypes[i]["code"]=="boolean") + return ''; + if(definedSqlTypes[i]["code"]=="date" || definedSqlTypes[i]["code"]=="datetime") + return ''; + if(definedSqlTypes[i]["code"]=="float") + return ''; + return definedSqlTypes[i]["code"]; + } + } + return null; +} + +function printOptionalCheckbox(obj,cid){ + if(definedSqlTypes.length==0){ + definedSqlTypes=JSON.parse(window.Android.displayTable("select id,code from mm4me_ftypes where ftype='e' order by name",[])); + } + + var res=' '; + for(var i in definedSqlTypes){ + if(definedSqlTypes[i]["id"]==obj["ftype"]){ + if(definedSqlTypes[i]["code"]=="bytea"){ + var tmpStr=""; + return res; + } + else if(definedSqlTypes[i]["code"]=="geometry"){ + return res + } + } + } + return ''; +} + +var refTypeId=null; +var editPrintedOnce=[]; +/***************************************************************************** + * Create HTML part to display the line containing both the title and the + * corresponding input for a given table's field. + *****************************************************************************/ +function printEditionFields(obj,myRoot,cid,mid){ + var list1=window.Android.displayTable("select * from mm4me_edition_fields where mm4me_edition_fields.edition>0 and eid="+obj["id"]+" order by mm4me_edition_fields.id asc",[]); + if(!editSchema[mid]) + editSchema[mid]={}; + editSchema[mid][obj["id"]]=JSON.parse(list1); + list1=JSON.parse(list1); + myRoot.find(".tab-content").first().append('
    '+obj["description"]+'
    '); + for(var j in list1) + if(list1[j]["edition"]>0) { + myRoot.find(".tab-content").first().children().last().append( + '
    '+ + '
    '+ + ''+ + '
    '+ + '
    '+ + printCurrentType(list1[j],mid)+ + '
    '+ + '
    '); + if(list1[j]["dependencies"]){ + try{ + editPrintedOnce.push(list1[j]["name"]); + console.log("JSON PARSE") + console.log(list1[j]["dependencies"]); + var objJson=JSON.parse(list1[j]["dependencies"]); + console.log("JSON PARSE OK"); + if(!refTypeId) + refTypeId=JSON.parse(window.Android.displayTable("select id from mm4me_ftypes where ftype='e' and code='ref'",[]))[0]["id"]; + console.log(refTypeId); + for(i in objJson){ + if(objJson[i]["myself"]){ + console.log("IS MYSELF!!"); + for(k in objJson[i]["myself"]){ + for(l in objJson[i]["myself"][k]){ + + + if(objJson[i]["myself"][k][l]["dependents"]){ + for(m in objJson[i]["myself"][k][l]["dependents"]){ + for(n in objJson[i]["myself"][k][l]["dependents"][m]){ + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]); + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["label"]); + var lObj={"id": n, "ftype":refTypeId,"value":objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]}; + //if(!myRoot.find('select[name="field_'+list1[j]["id"]+'"]').parent().find('select[name="field_'+n+'"]').length) + myRoot.find('select[name="field_'+list1[j]["id"]+'"]').last().parent().prepend( + '
    '+ + '
    '+ + ''+ + '
    '+ + '
    '+ + printCurrentType(lObj,mid)+ + '
    '+ + '
    '); + console.log(myRoot.find('select[name="field_'+n+'"]')); + console.log(printCurrentType(lObj,mid)); + (function(a,b){ + myRoot.find('select[name="field_'+n+'"]').off('change'); + myRoot.find('select[name="field_'+n+'"]').on('change',function(){ + console.log('select[name="field_'+n+'"]'); + var req=cleanupTableName(a["value"]); + if(a["value"].indexOf("WHERE")<0){ + req=req.replace(/order by/g,"where "+b["tfieldf"]+" "+b["operator"]+" "+(b["operator"]=="like"?"'":"")+$(this).val()+(b["operator"]=="like"?"'":"")+" order by") + } + console.log(req); + var res=JSON.parse(window.Android.displayTable(req,[])); + myRoot.find('select[name="field_'+a["id"]+'"]').html(""); + for(ij in res){ + var tmpStr=' '; + cnt+=1; + } + cnt0+=1; + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + $("select[name=field_"+changingField[j][ckey]["id"]+"]").change(); + }else{ + console.log("DISPLAY ELEMENT IF CINDEX >=0 "); + //console.log(JSON.stringify(changingField[j][ckey])); + console.log('input[name="field_'+ckey+'"],select[name="field_'+ckey+'"],textarea[name="field_'+ckey+'"]'); + console.log($('input[name="field_'+changingField[j][ckey]["id"]+'"],select[name="field_'+changingField[j][ckey]["id"]+'"],textarea[name="field_'+changingField[j][ckey]["id"]+'"]').parent().parent().html()); + var mycKey=changingField[j][ckey]["id"]; + if(cIndex<0) + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().hide(); + else + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().show(); + } + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + } + }catch(e){ + console.log(e); + setTimeout(function() { updateChangingFields(changingFields) }, 500); + } +} + +/***************************************************************************** + * Display the content of a table referencing the current edited table. + *****************************************************************************/ +function listInnerTable(id,vid,name,title,init,prefix,clause,ref){ + var list=JSON.parse(window.Android.displayTable("select mm4me_tables.id as tid,mm4me_tables.name as tname,mm4me_editions.id,mm4me_editions.name from mm4me_editions,mm4me_tables where mm4me_editions.ptid=mm4me_tables.id and mm4me_tables.id="+id+" order by mm4me_editions.step asc",[])); + var cnt=0; + var detectInit=true; + var tid=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + tid=list[i]["tid"]; + if(!allTables[list[i]["tid"]]){ + allTables[list[i]["tid"]]={"id":list[i]["id"],"name":list[i]["tname"],"title":list[i]["name"]}; + detectInit=false; + $("#sub_tableContent_"+id+"").find(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#sub_tableContent_"+id+"").find(".mm4me_edition"),list[i]["id"],list[i]["tid"]); + if(cnt==0){ + try{ + var cid=list[i]["id"]+"_0"; + $("#sub_tableContent_"+id+"").find("ul").first().append('
  • '+window.Android.translate('table')+'
  • '); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+" .require-select").hide(); + printEditionFields(list[i],$("#sub_tableContent_"+id+""),cid,list[i]["tid"]); + }catch(e){ + console.log("**** ERROR ***> "+e); + } + } + var myRoot=$("#sub_tableContent_"+id+""); + myRoot.find('ul').first().find('a').click(function (e) { + e.preventDefault(); + if($(this).parent().hasClass('require-select')) + myRoot.find('.mm4me_edition').show(); + else + myRoot.find('.mm4me_edition').hide(); + $(this).tab('show'); + }) + myRoot.find('ul').first().find('a').first().click(); + myRoot.find('.mm4me_edition').find('ul').find('a').first().click(); + myRoot.find('.swagEditor').summernote(); + myRoot.find(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),tid,editTableReact); + }); + myRoot.find(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),tid,editTableReact); + }); + + } + cnt+=1; + } + + var list=JSON.parse(window.Android.displayTable("SELECT id,name,value,alias,width,class from mm4me_view_fields where vid="+vid+" order by id",[])); + var columns=[]; + var columnNames=[]; + var sqlColumns=""; + var orderColumn="id"; + var orderType="asc"; + for(var i=0;i0){ + orderColumn=list[i]["name"]; + if(list[i]["class"]==1) + orderType="desc"; + } + } + var ccol=getPKey(cleanupTableName(name)); + var req="SELECT "+ccol+" as ogc_fid FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list1=JSON.parse(window.Android.displayTable(req,[])); + req="SELECT "+sqlColumns+" FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list=JSON.parse(window.Android.displayTable(req,[])); + var dataSet = []; + for(var i=0;i'; + cnt+=1; + } + dataSet.push(tmpData); + } + var selected = []; + + //$("#edition_form_table").html('
    '); + var localName="exampleTable_"+id; + ((function(localName,sid){ + + if(!detectInit){ + + var options={ + data: dataSet, + columns: columns, + "scrollX": true, + scrollY: '50vh', + scrollCollapse: true, + select: true, + "rowCallback": function( row, data ) { + if ( $.inArray(data.DT_RowId, selected) !== -1 ) { + $(row).addClass('selected'); + } + } + }; + if(langUrl!=null) + options["language"]={ + url: langUrl + }; + + $('#'+localName).DataTable( options ); + $('#'+localName+' tbody').on('click', 'tr', function () { + var id = this.id; + var index = $.inArray(id, selected); + + if ( index === -1 ) { + selected.push( id ); + } else { + selected.splice( index, 1 ); + } + displayEditForm(sid,$(this).find("input[name=id]").first().val(),true); + var tmp=$(this).hasClass('selected'); + $('#'+localName+' tbody tr').removeClass('selected'); + $(this).toggleClass('selected'); + if(!tmp) + $(this).toggleClass('selected'); + } ); + + }else{ + $('#'+localName).dataTable().fnClearTable(); + if(dataSet.length>0) + $('#'+localName).dataTable().fnAddData(dataSet); + } + $("#sub_tableContent_"+id+"").find("ul").first().find('a').first().click(); + })(localName,tid)); + +} + +var onFormFirstLoad=null; +/***************************************************************************** + * Show the edit form + *****************************************************************************/ +function displayEditForm(cid,selectedId,basic){ + if(basic && !$("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val()){ + if(cid==mtable){ + $("#main_tableContent").find(".require-select").hide(); + $("#main_tableContent").find("ul").first().find("a").first().click(); + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + + }else{ + $("#sub_tableContent_"+cid).find(".require-select").hide(); + $("#sub_tableContent_"+cid).find("ul").first().find("a").first().click(); + } + return; + } + var fields=[] + var sizedFields=[]; + var sizedFieldsAlias=[]; + var notSizedFields=[]; + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + //console.log(JSON.stringify(editSchema[cid][i][j])); + if(editSchema[cid][i][j]["ftype"]=="5"){ + sizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")); + sizedFieldsAlias.push(editSchema[cid][i][j]["id"]); + } + else{ + notSizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + try{ + var tmp=JSON.parse(editSchema[cid][i][j]["dependencies"]); + var sqlReq=""; + var sqlClause=""; + var sqlParams=""; + var sqlParam=0; + var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); + var hasDep=false; + var previousElements=[]; + for(k in tmp) + for(l in tmp[k]){ + //console.log(JSON.stringify(tmp[k][l])); + if(l=="myself"){ + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + //console.log(JSON.stringify(tmp[k][l][m][n])); + if(sqlReq!=""){ + sqlReq+=", "; + sqlParams+=", "; + //sqlClause+=" WHERE "+alphabet[sqlParam-1]+"."+tmp[k][l][m][n]["tfield"]+"="+alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + + } + sqlReq+="("+cleanupTableName(tmp[k][l][m][n]["sql_query"])+") as "+alphabet[sqlParam]; + sqlParams+=alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + sqlParam+=1; + if(tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"])); + for(var o in tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"][o])); + for(var p in tmp[k][l][m][n]["dependents"][o]){ + console.log(tmp[k][l][m][n]["dependents"][o][p]["sql_query"].replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")); + sqlReq+=", ("+cleanupTableName(tmp[k][l][m][n]["dependents"][o][p]["sql_query"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")+") as b"; + sqlParams+=", b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlParam+=2; + sqlReq+=", ("+cleanupTableName(editSchema[cid][i][j]["value"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+" from")+") as c"; + + sqlClause+=" WHERE c."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+"=b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlClause+=" AND b."+tmp[k][l][m][n]["tfield"]+"=a."+tmp[k][l][m][n]["tfield"]; + sqlClause+=" AND c.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + hasDep=true; + } + } + } + } + if(!hasDep){ + var tmpReq=cleanupTableName(editSchema[cid][i][j]["value"]); + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + tmpReq=tmpReq.replace(/from/,","+tmp[k][l][m][n]["tfield"]+" from"); + if(sqlClause=="") + sqlClause+=" WHERE "; + else + sqlClause+=" "+tmp[k][l][m][n]["cond_join"]+" "; + sqlClause+=alphabet[m]+"."+tmp[k][l][m][n]["tfield"]+"=a1."+tmp[k][l][m][n]["tfield"]; + sqlClause+=""; + } + sqlClause+=" AND a1.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + //sqlReq=(tmpReq); + console.log(tmpReq); + sqlReq+=", ("+tmpReq+") as a1"; + } + //console.log(JSON.stringify(tmp[k][l])); + console.log("SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause); + var localQuery="SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause; + var res0=JSON.parse(window.Android.displayTable(localQuery,[])); + console.log(JSON.stringify(res0)); + for(m in res0) + for(n in res0[m]){ + try{ + console.log("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]"); + /**/ + if($('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().val(res0[m][n]).change(); + else{ + for(m0 in tmp[k][l]) + for(n0 in tmp[k][l][m0]){ + /*console.log(n0.indexOf(n)<0); + console.log(n); + console.log(n0); + console.log(res0[m][n]);*/ + if(n0.indexOf(n)>=0){ + console.log("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]"); + if(!$('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]").first().val(res0[m][n]).change(); + } + } + } + }catch(e){ + console.log(e); + } + + } + //if(tmp[k][l]["dependents"]) + }else{ + console.log(JSON.stringify(tmp[k][l])); + if(tmp[k][l]["tfield"]=="none"){ + var isNull=JSON.parse(window.Android.displayTable("SELECT CASE WHEN "+l+" is null THEN 1 ELSE 0 END as p FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId,[])); + console.log(JSON.stringify(isNull)); + if(isNull[0]["p"]=="0"){ + console.log(JSON.stringify(isNull)); + console.log("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]"); + console.log(k+""); + $('.mm4me_edition').find("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]").first().val(k+"").change(); + } + } + + } + } + + //if(sqlReq) + }catch(e){ + console.log(e); + } + } + if(editSchema[cid][i][j]["name"].indexOf("unamed")<0) + fields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + } + } + var ccol=getPKey(cleanupTableName(allTables[cid].name)); + /* $("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val() */ + var editValues; + if(sizedFields.length>0){ + for(var i=0;i 1000000 and "+ccol+"="+selectedId,[])); + //console.log(JSON.stringify(hasElement[0])); + if(hasElement[0]["cnt"]!="0"){ + var nbIteration=parseInt(hasElement[0]["len"])/1000000; + var zfields=[] + var len=0; + var query=""; + var len1=parseInt(hasElement[0]["len"]); + for(var j=0;j0){ + $("#field_"+j+"_map").show(); + $("#field_"+j+"_map").off('click'); + $("#field_"+j+"_map").on('click',function(){ + console.log("Display table with a selected feature"); + console.log($(this).prev().val()); + showElementOnMap($(this).prev().val()); + }); + } + if($(".btn_field_"+j+"_lat").length>0){ + //alert(editValues[i][j]); + $(".btn_field_"+j+"_lat").html(editValues[i][j]); + $(".btn_field_"+j+"_long").html(""); + $(".btn_field_"+j+"_source").html(""); + $("input[name='field_"+j+"']").val("POINT"+editValues[i][j]); + + } + } + //$(".swagEditor").summernote(); + } + } + + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + if(editSchema[cid][i][j]["name"].indexOf("unamed")>=0){ + if(editSchema[cid][i][j]["ftype"]==6){ + var tmp=editSchema[cid][i][j]["value"].split(';'); + var list=JSON.parse(window.Android.displayTable("select "+tmp[1]+" from "+cleanupTableName(tmp[2])+" where "+tmp[0]+"=(SELECT id from "+cleanupTableName(tblName)+" WHERE ogc_fid="+selectedId+")",[])); + editValues["0"][editSchema[cid][i][j]["id"]]=list; + for(var k in list){ + for(var l in list[k]){ + $('.mm4me_edition').find("select[name=field_"+editSchema[cid][i][j]["id"]+"]").find('option').each(function(){ + if($(this).val()==list[k][l]) + $(this).prop("selected",true); + }); + } + } + } + } + } + } + + if(cid==mtable){ + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+editValues["0"]["local_id"]+'
  • '); + } + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().show(); + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().find("a").first().click(); + + if(toRunOnLoad[cid]) + for(var i=0;i=0 order by mm4me_editions.step asc",[]); + if(MM4ME_DEBUG) + console.log(list); + list=JSON.parse(list); + if(MM4ME_DEBUG) + console.log((!allTables[tblId])); + if(!allTables[tblId]){ + allTables[tblId]={"id":id,"name":name,"title":title}; + } + mtable=tblId; + + $(".mm4me_edition").find("ul").first().html(""); + $(".mm4me_edition").find(".well").first().html(""); + var cnt=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + var cid=(i==0?list[i]["id"]+"_0":list[i]["id"]); + $(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#edition_form_edit"),cid,mainTable[id]); + } + if(list.length==1) + $(".mm4me_edition").find("ul").first().hide(); + + var aCnt=0; + $('.mm4me_edition').find('ul').first().find('a').each(function () { + if(aCnt>0) + $(this).parent().addClass('require-select'); + aCnt+=1; + }); + $(".require-select").hide(); + $('.mm4me_listing').find('ul').first().find('a').first().click(); + $('.mm4me_edition').find('ul').find('a').first().click(); + $('.swagEditor').summernote(); + $(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+window.Android.translate('edit')+'
  • '); + $(".breadcrumb").append('
  • '+tblTitle+'
  • '); + + setTimeout(function() { updateChangingFields(changingFields) }, 1500); + + /*for(var i=0;i'; + else + cStr+=changingField[j][ckey]["values"][cIndex][i][lkey]+''; + cnt+=1; + } + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + }*/ + $('.mm4me_listing').show(); + $('.mm4me_content').hide(); + +} + +/***************************************************************************** + * In case no server is configured + *****************************************************************************/ +function displayNoListing(){ + $.ajax({ + method: "GET", + url: 'content/nolisting.html', + success: function(data){ + if(MM4ME_DEBUG) + console.log('Display warning message on the UI !'); + $(".mm4me_content").html(data); + $(".mm4me_content").find(".pannel-body").find("p").first().html(window.Android.translate("mm_no_db_found")); + }, + error: function(){ + window.Android.showToast("error !"); + } + }); +} + + +/***************************************************************************** + * Authenticate a user + *****************************************************************************/ +function authenticate(url,login,passwd,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogIn&DataInputs=login="+login+";password="+passwd+"&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG) + console.log(data); + if(func){ + console.log("Call func!") + func(); + } + }, + error: function(){ + if(func1){ + func1(); + } + else{ + disconnect(url); + if(MM4ME_DEBUG) + console.log("unable to login!"); + var hasBeenShown=false; + var xml=arguments[0].responseText; + $(xml).find("ows\\:ExceptionText").each(function(){ + window.Android.showToast($(this).text()); + hasBeenShown=true; + }); + if(!hasBeenShown){ + window.Android.showToast(JSON.stringify(arguments)); + } + } + + } + }); +} + +/***************************************************************************** + * Disconnect a user + *****************************************************************************/ +function disconnect(url,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogOut&DataInputs=&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG){ + console.log(data); + console.log("** Your are no more connected!"); + } + if(func){ + func(); + } + }, + error: function(){ + if(MM4ME_DEBUG){ + console.log(curl); + console.log("unable to disconnect!"); + } + if(func1){ + func1(); + } + } + }); +} + +var geometries={"line":{"geom":null,"constructor": "ol.geom.Linestring"},"polygon":{"geom":null,"constructor":"ol.geom.Polygon","cline":null}}; +var stopTracking=false; +var currentGeometry="line"; +var currentGeometryField="none"; +var map=null; +var vectorLayer,vectorLayer1; +var position; + +/***************************************************************************** + * Track modification of the GPS location + *****************************************************************************/ +function trackCurrentGPSPosition(){ + console.log("## trackCurrentGPSPosition"); + updateCurrentMapLocation(); + + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(tmp0[0]==tmp[0] && tmp0[1]==tmp[1]){ + console.log(" !!!!!!!! Same position!!!!!!!! "); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + return; + } + + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); +} + +var modalCallback=null; + +function trackStepCurrentGPSPosition(){ + console.log("## trackStepCurrentGPSPosition"); + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!stopTracking) + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); +} + +function addCurrentLocation(){ + console.log("addCurrentLocation!!!!"); + if(geometries["origin"]==null) + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + else{ + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + + } +} + +function trackStepByStepPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + } +} +/***************************************************************************** + * Start tracking GPS location + *****************************************************************************/ +function trackGPSPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + } +} + +var hasAddedElement=0; +var myVectorLayer=null; +function addSelectedElement(wktString){ + var features=[]; + try{ + var format = new ol.format.WKT(); + features.push( + format.readFeature(wktString, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }) + ); + }catch(e){ + console.log("Unable to parse WKT ?!"+e) + } + if(!vectorLayer1){ + myVectorLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: features + }), + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: "#3333aa", + width: 1.4 + }) + }) + }); + map.addLayer(myVectorLayer); + } + else{ + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures(features); + } + console.log(vectorLayer1.getSource().getExtent()); + map.updateSize(); + map.getView().fit(vectorLayer1.getSource().getExtent(),map.getSize()); + hasAddedElement=1; +} + +/***************************************************************************** + * Show selected feature on map + *****************************************************************************/ + var addSelectedIndex=0; + var mySelectedElement=null; +function showElementOnMap(wktString){ + mySelectedElement=wktString; + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().hide(); + if(addSelectedIndex==0){ + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + } + addSelectedElement(mySelectedElement); + $("#myModal").find("h4").html(' '+window.Android.translate("view_feature")); + addSelectedIndex++; + }; + //$("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + console.log("OK "); + $('#myModal').modal(); + console.log("OK "); + vectorLayer1.getSource().clear(); + console.log("OK "); + updateCurrentMapLocation(); + console.log("OK "); + addSelectedElement(wktString); + console.log("OK "); + } + + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); +} + + +/***************************************************************************** + * Store the current GPS location in the edit form + *****************************************************************************/ +function requireGPSPosition(elem){ + var position=JSON.parse(window.Android.getGPS()); + //elem=$("#"+elem); + if(position.lat){ + $(".btn_"+elem+"_lat").html(position.lat); + $(".btn_"+elem+"_long").html(position.lon); + $(".btn_"+elem+"_source").html(position.source); + $("input[name='"+elem+"']").val("POINT("+position.lon+" "+position.lat+")"); + }else{ + + } + console.log(JSON.stringify(position)); +} + +/***************************************************************************** + * Ajax setup to ensure seting "withCredentials" to true + *****************************************************************************/ +function ajaxSetup(){ + $.ajaxSetup({ + xhrFields: { + withCredentials: true + } + }); +} + +/***************************************************************************** + * Get the current Network and GPS availability + *****************************************************************************/ +function getCurrentStatus(){ + var tmp=JSON.parse(window.Android.getGNStatus()); + tmp["gps"]=JSON.parse(tmp["gps"]); + updateStatus(tmp['gps'],tmp['net']); +} + +/***************************************************************************** + * Display the status icons + *****************************************************************************/ +function addStatusControl(){ + $('.breadcrumb').append(' /'+ + ''+ + ''+ + ''+ + ''+ + ''); +} + +/***************************************************************************** + * Initialize the map and show the current GPS location + *****************************************************************************/ +var localTiles,localTiles0; +var tileLayers=[]; +function initMapToLocation(){ + if(map) + return; + var osmSource = new ol.source.OSM(); + + var otherLayers=[]; + var otherLayersSwitcher=""; + try{ + var BasesLayersStr=window.Android.getBaseLayers(); + console.log(BasesLayersStr); + var BasesLayers=JSON.parse(BasesLayersStr); + for(var i=0;i=4) + map.getLayers().item(otherLayers[i]["index"]).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity_"+i).show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(!otherLayers[i]["index"]){ + var myTileLayer=new ol.layer.Tile({source: otherLayers[i]["olLayer"]}); + var layers = map.getLayers(); + layers.insertAt(tileLayers.length+1,myTileLayer); + otherLayers[i]["index"]=tileLayers.length+1; + tileLayers.push(myTileLayer); + if(localTileIndex>0) + localTileIndex+=1; + } + else + map.getLayers().item(otherLayers[i]["index"]).setVisible(true); + } + }); + $(".map").parent().find("input[type=range]").last().on('change',function(){ + console.log("change to "+$(this).val()); + map.getLayers().item(otherLayers[i]["index"]).setOpacity($(this).val()/100); + }); + + })(i); + $(".map").parent().find("input[type=checkbox]").parent().off('click'); + $(".map").parent().find("input[type=checkbox]").parent().on('click',function(){ + var tmp=$(this).find("input[type=checkbox]"); + if(tmp.is(":checked")){ + $(this).find("input[type=checkbox]").prop("checked",false).change(); + $(this).addClass("select"); + } + else{ + $(this).find("input[type=checkbox]").prop("checked",true).change(); + $(this).removeClass("select"); + } + }); + + },1); + })(i) + } + } + map = new ol.Map({ + layers: layers, + target: 'map', + controls: ol.control.defaults({ + attributionOptions: /** @type {olx.control.AttributionOptions} */ ({ + collapsible: true + }) + }), + view: new ol.View({ + center: ol.proj.transform([0,0], 'EPSG:4326', 'EPSG:3857'), + zoom: 14, + maxZoom: 22, + minZoom: 1 + }) + }); + + var bstyle = function (feature, resolution) { + + /*var iconFont = 'glyphicon'; + var iconFontText = '\e062';*/ + //var iconFont = 'glyphicon'; + var iconFont = 'Glyphicons Halflings'; + var iconFontText = '\ue062'; + var iconSize = 24; + var opacity=0.4; + var col = 'rgba(0,255,0,0.6)'; + if(feature.get("source")=="GPS") + col = 'rgba(41,136,54,0.5)';//#298836 + else if(feature.get("source")=="Network"){ + col = 'rgba(91,176,75,0.4)';//#5bb04b + iconSize = 32; + opacity=0.2; + } + else if(feature.get("source")=="other"){ + col='rgba(129,208,113,0.5)';//#81d071 + iconSize = 36; + opacity=0.2; + } + else + col='rgba(166,63,39,0.5)'; //#a63f27 //"#EE0000"; + var styles = []; + + var styleIcon = new ol.style.Style({ + text: new ol.style.Text({ + font: 'Normal ' + iconSize + 'px ' + iconFont, + text: iconFontText, + fill: new ol.style.Fill({ color: col }) + }) + }); + styles.push(styleIcon); + + //console.log(feature.get("type")); + return styles; + /*return function (feature, resolution) { + styles.styleIcon.getText().setText(feature.get("iconCode")); + return styles; + };*/ + }; + position=JSON.parse(window.Android.getFullGPS()); + //console.log(JSON.stringify(position)); + if(position.length==0){ + position=[{lon:3.5,lat:43.5,source:"none"}]; + } + var iconFeatures = []; + for(var i=0;i=4) + map.getLayers().item(localTileIndex).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity").show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(map.getLayers().getLength()0.05){ + map.getView().setRotation(-direction); + oldBearer=direction; + } + }else{ + window.Android.stopReportDirection(); + } +} \ No newline at end of file diff --git a/app/src/main/assets/scripts/gene3.js b/app/src/main/assets/scripts/gene3.js new file mode 100644 index 0000000..323dc08 --- /dev/null +++ b/app/src/main/assets/scripts/gene3.js @@ -0,0 +1,2461 @@ +var MM4ME_DEBUG=true; +var EDITION_TYPE_FILE=5; +var mainTable={}; +var mtable=null; +var refTables={}; +var tblId=null; +var allTables={}; +var editSchema={}; +var referenceIds={}; +var lastEdition={}; +var tblName=null; +var toRunOnLoad={}; +var valuesOnLoad=[]; +var definedSqlTypes=[]; +var changingFields=[]; +var lang=window.Android.getLang(); +var langUrl=null; +if(lang=="fr"){ + langUrl="file:///android_asset/localisation/French.json"; +} + + + + +function loadWelcome4(){ + window.Android.startWelcomeScreen("cloud"); +} + +/***************************************************************************** + * Update status icon color depending on network and GPS availability + *****************************************************************************/ +function updateStatus(gps,internet){ + if(internet){ + $('.glyphicon-signal').css('color','#00EE00'); + }else + $('.glyphicon-signal').css('color','#EE0000'); + $('#gpsMenu1').next().html(""); + if(gps.length==0){ + $('#gpsMenu1').css('color','#EE0000'); + $('#gpsMenu1').next().html('
  • '+window.Android.translate("no_gps")+'
  • '); + }else{ + $('#gpsMenu1').css('color','#00EE00'); + for(var i=0;i '+gps[i].source+''); + } +} + +/***************************************************************************** + * List all available table for a given theme + *****************************************************************************/ +function fetchTableForTheme(obj,id){ + var hasTable=false; + var tables=JSON.parse(window.Android.displayTable( + "SELECT mm4me_tables.id as tid,mm4me_views.id as id,"+ + "mm4me_tables.name as name, "+ + "mm4me_tables.description, "+ + "mm4me_views.name as title "+ + " from mm4me_tables,mm4me_views"+ + " where mm4me_tables.id=mm4me_views.ptid "+ + "and mm4me_views.visible "+ + "and mm4me_views.id in (select vid from mm4me_views_themes where tid="+id+")",[])); + for(var i=0;i0; +} + +/***************************************************************************** + * Create a JSON Object containing the themes hierarchy + *****************************************************************************/ +function fetchThemes(obj,id){ + var cthemes=JSON.parse(window.Android.displayTable("SELECT id,name FROM mm4me_themes where pid = "+id,[])); + for(var i=0;i'); + $('#tree').treeview({ + data: allThemes, + onNodeSelected: function(event, data) { + if(data["myId"]){ + try{ + func(data["myId"],data["myName"],data["myTitle"],true); + }catch(e){ + console.log(JSON.stringify(data)); + console.log(e); + window.Android.showToast(e); + //exit(); + } + } + else{ + $('#tree').treeview('toggleNodeExpanded',[$("#tree").treeview('getSelected')[0], { silent: true }]); + $('#tree').treeview('toggleNodeSelected',[$("#tree").treeview('getSelected')[0], { silent: true }]); + //console.log(JSON.stringify(data)); + //$(this).open(); + } + }, + showTags: true + }); + var list=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.id=mm4me_views.ptid and mm4me_views.visible",[])); + var tableList=list; + var total=0; + contents=[]; + for(var i in list){ + mainTable[list[i]["id"]]=list[i]["tid"]; + } + } + catch(e){ + console.log(e); + displayNoListing(); + } +} + +/***************************************************************************** + * Update the breadcrumbs text to translated string + *****************************************************************************/ +function updateBreadcrumbs(breadcrumbs){ + //var breadcrumbs=["home","view"]; + var lcnt0=0; + $('.breadcrumb').find("a").each(function(){ + $(this).append(window.Android.translate(breadcrumbs[lcnt0])); + lcnt0+=1; + }); + $('.breadcrumb').find("li").last().each(function(){ + $(this).append(window.Android.translate(breadcrumbs[lcnt0])); + lcnt0+=1; + }); +} + +/***************************************************************************** + * Convert table names in a string from the PostgreSQL to our SQLite format. + * So, convert "AAAXXX.YYYBBB" to "AAAXXX_YYYBBB" + *****************************************************************************/ +function cleanupTableName(name){ + return name.replace(/(\w+)(\d*)\.(\d*)(\w+)/g,"$1$2_$3$4"); +} + +/***************************************************************************** + * Display an HTML part containing the image produced by Camera or picked from + * the photo library. + *****************************************************************************/ +function loadNewPicture(cid,id,picture){ + $(".tab-pane").each(function(){ + if($(this).is(":visible")) + $(this).find("#value_"+id) + .html('
    '+picture+'
    '); + }); +} + + +/***************************************************************************** + * Create a JSON Object representing the dependency values. + *****************************************************************************/ +function fetchDependencies(obj,cid,changingField){ + var list1=null; + //console.log(cid+" "+JSON.stringify(obj)); + + for(var key in changingField){ + for(var i=0;i'; + tmpStr+=res+ + '
    '; + console.log(tmpStr); + return tmpStr; + } + if(definedSqlTypes[i]["code"]=="geometry"){ + console.log("CID: "+cid+" select type from mm4me_gc where f_table_schema||'_'||f_table_name = (select name from mm4me_tables where id="+cid+") ") + var geoType=getGeometryType("(select replace(name,'.','_') from mm4me_tables where id="+cid+")"); + var viewb=''; + var res=' '; + if(geoType=='POINT' || geoType=='MULTIPOINT' ) + return res+''+ + '

    GPS Informations

    Type
    Coords
    '+ + ''; + else if(geoType=='LINESTRING' || geoType=='MULTILINESTRING' ) + return res+''+ + ''+ + '
    '+viewb+'
    '; + else if(geoType=='POLYGON' || geoType=='MULTIPOLYGON' ) + return res+''+ + ''+ + '
    '+viewb+'
    '; + + } + if(definedSqlTypes[i]["code"]=="tbl_linked"){ + var tmp=obj["value"].split(';'); + //console.log(cleanupTableName('SELECT '+tmp[1]+' FROM '+tmp[2]+' WHERE '+tmp[0]+'='+tblName+'.id')); + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(tmp[3]),[])); + var tmpStr='"; + + } + if(definedSqlTypes[i]["code"]=="link"){ + var strReturn; + if(!View_template) + $.ajax({ + async: false, + method: "GET", + url: './content/view_template.html', + error: function(){ + console.log("Nothing to run after"); + }, + success: function(data){ + console.log("**** \n\n Load View Template"); + View_template=data; + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + if(refs!=""){ + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=data.replace(reg,refs["0"]["tid"]); + } + } + }); + else{ + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=View_template.replace(reg,refs["0"]["tid"]); + + } + //console.log(strReturn); + return strReturn; + } + if(definedSqlTypes[i]["code"]=="ref"){ + var req=obj["value"];//.replace(/^\((\w+)\)$/g,"$1"); + if(req[0]=="(") + req="SELECT * FROM "+req; + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(req),[])); + var tmpStr='"; + + if(obj["dependencies"]) + try{ + var lobj={}; + lobj[obj["id"]]={"dep":JSON.parse(obj["dependencies"])}; + for(var jj=0;jj=0){ + return ''; + } + if(definedSqlTypes[i]["code"]=="html") + return ''; + if(definedSqlTypes[i]["code"]=="text") + return ''; + if(definedSqlTypes[i]["code"]=="boolean") + return ''; + if(definedSqlTypes[i]["code"]=="date" || definedSqlTypes[i]["code"]=="datetime") + return ''; + if(definedSqlTypes[i]["code"]=="float") + return ''; + return definedSqlTypes[i]["code"]; + } + } + return null; +} + +function printOptionalCheckbox(obj,cid){ + if(definedSqlTypes.length==0){ + definedSqlTypes=JSON.parse(window.Android.displayTable("select id,code from mm4me_ftypes where ftype='e' order by name",[])); + } + + var res=' '; + for(var i in definedSqlTypes){ + if(definedSqlTypes[i]["id"]==obj["ftype"]){ + if(definedSqlTypes[i]["code"]=="bytea"){ + var tmpStr=""; + return res; + } + else if(definedSqlTypes[i]["code"]=="geometry"){ + return res + } + } + } + return ''; +} + +var refTypeId=null; +var editPrintedOnce=[]; +/***************************************************************************** + * Create HTML part to display the line containing both the title and the + * corresponding input for a given table's field. + *****************************************************************************/ +function printEditionFields(obj,myRoot,cid,mid){ + var list1=window.Android.displayTable("select * from mm4me_edition_fields where mm4me_edition_fields.edition>0 and eid="+obj["id"]+" order by mm4me_edition_fields.id asc",[]); + if(!editSchema[mid]) + editSchema[mid]={}; + editSchema[mid][obj["id"]]=JSON.parse(list1); + list1=JSON.parse(list1); + myRoot.find(".tab-content").first().append('
    '+obj["description"]+'
    '); + for(var j in list1) + if(list1[j]["edition"]>0) { + myRoot.find(".tab-content").first().children().last().append( + '
    '+ + '
    '+ + ''+ + '
    '+ + '
    '+ + printCurrentType(list1[j],mid)+ + '
    '+ + '
    '); + if(list1[j]["dependencies"]){ + try{ + editPrintedOnce.push(list1[j]["name"]); + console.log("JSON PARSE") + console.log(list1[j]["dependencies"]); + var objJson=JSON.parse(list1[j]["dependencies"]); + console.log("JSON PARSE OK"); + if(!refTypeId) + refTypeId=JSON.parse(window.Android.displayTable("select id from mm4me_ftypes where ftype='e' and code='ref'",[]))[0]["id"]; + console.log(refTypeId); + for(i in objJson){ + if(objJson[i]["myself"]){ + console.log("IS MYSELF!!"); + for(k in objJson[i]["myself"]){ + for(l in objJson[i]["myself"][k]){ + + + if(objJson[i]["myself"][k][l]["dependents"]){ + for(m in objJson[i]["myself"][k][l]["dependents"]){ + for(n in objJson[i]["myself"][k][l]["dependents"][m]){ + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]); + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["label"]); + var lObj={"id": n, "ftype":refTypeId,"value":objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]}; + //if(!myRoot.find('select[name="field_'+list1[j]["id"]+'"]').parent().find('select[name="field_'+n+'"]').length) + myRoot.find('select[name="field_'+list1[j]["id"]+'"]').last().parent().prepend( + '
    '+ + '
    '+ + ''+ + '
    '+ + '
    '+ + printCurrentType(lObj,mid)+ + '
    '+ + '
    '); + console.log(myRoot.find('select[name="field_'+n+'"]')); + console.log(printCurrentType(lObj,mid)); + (function(a,b){ + myRoot.find('select[name="field_'+n+'"]').off('change'); + myRoot.find('select[name="field_'+n+'"]').on('change',function(){ + console.log('select[name="field_'+n+'"]'); + var req=cleanupTableName(a["value"]); + if(a["value"].indexOf("WHERE")<0){ + req=req.replace(/order by/g,"where "+b["tfieldf"]+" "+b["operator"]+" "+(b["operator"]=="like"?"'":"")+$(this).val()+(b["operator"]=="like"?"'":"")+" order by") + } + console.log(req); + var res=JSON.parse(window.Android.displayTable(req,[])); + myRoot.find('select[name="field_'+a["id"]+'"]').html(""); + for(ij in res){ + var tmpStr=' '; + cnt+=1; + } + cnt0+=1; + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + $("select[name=field_"+changingField[j][ckey]["id"]+"]").change(); + }else{ + console.log("DISPLAY ELEMENT IF CINDEX >=0 "); + //console.log(JSON.stringify(changingField[j][ckey])); + console.log('input[name="field_'+ckey+'"],select[name="field_'+ckey+'"],textarea[name="field_'+ckey+'"]'); + console.log($('input[name="field_'+changingField[j][ckey]["id"]+'"],select[name="field_'+changingField[j][ckey]["id"]+'"],textarea[name="field_'+changingField[j][ckey]["id"]+'"]').parent().parent().html()); + var mycKey=changingField[j][ckey]["id"]; + if(cIndex<0) + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().hide(); + else + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().show(); + } + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + } + }catch(e){ + console.log(e); + setTimeout(function() { updateChangingFields(changingFields) }, 500); + } +} + +/***************************************************************************** + * Display the content of a table referencing the current edited table. + *****************************************************************************/ +function listInnerTable(id,vid,name,title,init,prefix,clause,ref){ + var list=JSON.parse(window.Android.displayTable("select mm4me_tables.id as tid,mm4me_tables.name as tname,mm4me_editions.id,mm4me_editions.name from mm4me_editions,mm4me_tables where mm4me_editions.ptid=mm4me_tables.id and mm4me_tables.id="+id+" order by mm4me_editions.step asc",[])); + var cnt=0; + var detectInit=true; + var tid=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + tid=list[i]["tid"]; + if(!allTables[list[i]["tid"]]){ + allTables[list[i]["tid"]]={"id":list[i]["id"],"name":list[i]["tname"],"title":list[i]["name"]}; + detectInit=false; + $("#sub_tableContent_"+id+"").find(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#sub_tableContent_"+id+"").find(".mm4me_edition"),list[i]["id"],list[i]["tid"]); + if(cnt==0){ + try{ + var cid=list[i]["id"]+"_0"; + $("#sub_tableContent_"+id+"").find("ul").first().append('
  • '+window.Android.translate('table')+'
  • '); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+" .require-select").hide(); + printEditionFields(list[i],$("#sub_tableContent_"+id+""),cid,list[i]["tid"]); + }catch(e){ + console.log("**** ERROR ***> "+e); + } + } + var myRoot=$("#sub_tableContent_"+id+""); + myRoot.find('ul').first().find('a').click(function (e) { + e.preventDefault(); + if($(this).parent().hasClass('require-select')) + myRoot.find('.mm4me_edition').show(); + else + myRoot.find('.mm4me_edition').hide(); + $(this).tab('show'); + }) + myRoot.find('ul').first().find('a').first().click(); + myRoot.find('.mm4me_edition').find('ul').find('a').first().click(); + myRoot.find('.swagEditor').summernote(); + myRoot.find(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),tid,editTableReact); + }); + myRoot.find(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),tid,editTableReact); + }); + + } + cnt+=1; + } + + var list=JSON.parse(window.Android.displayTable("SELECT id,name,value,alias,width,class from mm4me_view_fields where vid="+vid+" order by id",[])); + var columns=[]; + var columnNames=[]; + var sqlColumns=""; + var orderColumn="id"; + var orderType="asc"; + for(var i=0;i0){ + orderColumn=list[i]["name"]; + if(list[i]["class"]==1) + orderType="desc"; + } + } + var ccol=getPKey(cleanupTableName(name)); + var req="SELECT "+ccol+" as ogc_fid FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list1=JSON.parse(window.Android.displayTable(req,[])); + req="SELECT "+sqlColumns+" FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list=JSON.parse(window.Android.displayTable(req,[])); + var dataSet = []; + for(var i=0;i'; + cnt+=1; + } + dataSet.push(tmpData); + } + var selected = []; + + //$("#edition_form_table").html('
    '); + var localName="exampleTable_"+id; + ((function(localName,sid){ + + if(!detectInit){ + + var options={ + data: dataSet, + columns: columns, + "scrollX": true, + scrollY: '50vh', + scrollCollapse: true, + select: true, + "rowCallback": function( row, data ) { + if ( $.inArray(data.DT_RowId, selected) !== -1 ) { + $(row).addClass('selected'); + } + } + }; + if(langUrl!=null) + options["language"]={ + url: langUrl + }; + + $('#'+localName).DataTable( options ); + $('#'+localName+' tbody').on('click', 'tr', function () { + var id = this.id; + var index = $.inArray(id, selected); + + if ( index === -1 ) { + selected.push( id ); + } else { + selected.splice( index, 1 ); + } + displayEditForm(sid,$(this).find("input[name=id]").first().val(),true); + var tmp=$(this).hasClass('selected'); + $('#'+localName+' tbody tr').removeClass('selected'); + $(this).toggleClass('selected'); + if(!tmp) + $(this).toggleClass('selected'); + } ); + + }else{ + $('#'+localName).dataTable().fnClearTable(); + if(dataSet.length>0) + $('#'+localName).dataTable().fnAddData(dataSet); + } + $("#sub_tableContent_"+id+"").find("ul").first().find('a').first().click(); + })(localName,tid)); + +} + +var onFormFirstLoad=null; +/***************************************************************************** + * Show the edit form + *****************************************************************************/ +function displayEditForm(cid,selectedId,basic){ + if(basic && !$("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val()){ + if(cid==mtable){ + $("#main_tableContent").find(".require-select").hide(); + $("#main_tableContent").find("ul").first().find("a").first().click(); + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + + }else{ + $("#sub_tableContent_"+cid).find(".require-select").hide(); + $("#sub_tableContent_"+cid).find("ul").first().find("a").first().click(); + } + return; + } + var fields=[] + var sizedFields=[]; + var sizedFieldsAlias=[]; + var notSizedFields=[]; + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + //console.log(JSON.stringify(editSchema[cid][i][j])); + if(editSchema[cid][i][j]["ftype"]=="5"){ + sizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")); + sizedFieldsAlias.push(editSchema[cid][i][j]["id"]); + } + else{ + notSizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + try{ + var tmp=JSON.parse(editSchema[cid][i][j]["dependencies"]); + var sqlReq=""; + var sqlClause=""; + var sqlParams=""; + var sqlParam=0; + var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); + var hasDep=false; + var previousElements=[]; + for(k in tmp) + for(l in tmp[k]){ + //console.log(JSON.stringify(tmp[k][l])); + if(l=="myself"){ + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + //console.log(JSON.stringify(tmp[k][l][m][n])); + if(sqlReq!=""){ + sqlReq+=", "; + sqlParams+=", "; + //sqlClause+=" WHERE "+alphabet[sqlParam-1]+"."+tmp[k][l][m][n]["tfield"]+"="+alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + + } + sqlReq+="("+cleanupTableName(tmp[k][l][m][n]["sql_query"])+") as "+alphabet[sqlParam]; + sqlParams+=alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + sqlParam+=1; + if(tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"])); + for(var o in tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"][o])); + for(var p in tmp[k][l][m][n]["dependents"][o]){ + console.log(tmp[k][l][m][n]["dependents"][o][p]["sql_query"].replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")); + sqlReq+=", ("+cleanupTableName(tmp[k][l][m][n]["dependents"][o][p]["sql_query"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")+") as b"; + sqlParams+=", b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlParam+=2; + sqlReq+=", ("+cleanupTableName(editSchema[cid][i][j]["value"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+" from")+") as c"; + + sqlClause+=" WHERE c."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+"=b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlClause+=" AND b."+tmp[k][l][m][n]["tfield"]+"=a."+tmp[k][l][m][n]["tfield"]; + sqlClause+=" AND c.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + hasDep=true; + } + } + } + } + if(!hasDep){ + var tmpReq=cleanupTableName(editSchema[cid][i][j]["value"]); + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + tmpReq=tmpReq.replace(/from/,","+tmp[k][l][m][n]["tfield"]+" from"); + if(sqlClause=="") + sqlClause+=" WHERE "; + else + sqlClause+=" "+tmp[k][l][m][n]["cond_join"]+" "; + sqlClause+=alphabet[m]+"."+tmp[k][l][m][n]["tfield"]+"=a1."+tmp[k][l][m][n]["tfield"]; + sqlClause+=""; + } + sqlClause+=" AND a1.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + //sqlReq=(tmpReq); + console.log(tmpReq); + sqlReq+=", ("+tmpReq+") as a1"; + } + //console.log(JSON.stringify(tmp[k][l])); + console.log("SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause); + var localQuery="SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause; + var res0=JSON.parse(window.Android.displayTable(localQuery,[])); + console.log(JSON.stringify(res0)); + for(m in res0) + for(n in res0[m]){ + try{ + console.log("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]"); + /**/ + if($('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().val(res0[m][n]).change(); + else{ + for(m0 in tmp[k][l]) + for(n0 in tmp[k][l][m0]){ + /*console.log(n0.indexOf(n)<0); + console.log(n); + console.log(n0); + console.log(res0[m][n]);*/ + if(n0.indexOf(n)>=0){ + console.log("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]"); + if(!$('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]").first().val(res0[m][n]).change(); + } + } + } + }catch(e){ + console.log(e); + } + + } + //if(tmp[k][l]["dependents"]) + }else{ + console.log(JSON.stringify(tmp[k][l])); + if(tmp[k][l]["tfield"]=="none"){ + var isNull=JSON.parse(window.Android.displayTable("SELECT CASE WHEN "+l+" is null THEN 1 ELSE 0 END as p FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId,[])); + console.log(JSON.stringify(isNull)); + if(isNull[0]["p"]=="0"){ + console.log(JSON.stringify(isNull)); + console.log("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]"); + console.log(k+""); + $('.mm4me_edition').find("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]").first().val(k+"").change(); + } + } + + } + } + + //if(sqlReq) + }catch(e){ + console.log(e); + } + } + if(editSchema[cid][i][j]["name"].indexOf("unamed")<0) + fields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + } + } + var ccol=getPKey(cleanupTableName(allTables[cid].name)); + /* $("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val() */ + var editValues; + if(sizedFields.length>0){ + for(var i=0;i 1000000 and "+ccol+"="+selectedId,[])); + //console.log(JSON.stringify(hasElement[0])); + if(hasElement[0]["cnt"]!="0"){ + var nbIteration=parseInt(hasElement[0]["len"])/1000000; + var zfields=[] + var len=0; + var query=""; + var len1=parseInt(hasElement[0]["len"]); + for(var j=0;j0){ + $("#field_"+j+"_map").show(); + $("#field_"+j+"_map").off('click'); + $("#field_"+j+"_map").on('click',function(){ + console.log("Display table with a selected feature"); + console.log($(this).prev().val()); + showElementOnMap($(this).prev().val()); + }); + } + if($(".btn_field_"+j+"_lat").length>0){ + //alert(editValues[i][j]); + $(".btn_field_"+j+"_lat").html(editValues[i][j]); + $(".btn_field_"+j+"_long").html(""); + $(".btn_field_"+j+"_source").html(""); + $("input[name='field_"+j+"']").val("POINT"+editValues[i][j]); + + } + } + //$(".swagEditor").summernote(); + } + } + + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + if(editSchema[cid][i][j]["name"].indexOf("unamed")>=0){ + if(editSchema[cid][i][j]["ftype"]==6){ + var tmp=editSchema[cid][i][j]["value"].split(';'); + var list=JSON.parse(window.Android.displayTable("select "+tmp[1]+" from "+cleanupTableName(tmp[2])+" where "+tmp[0]+"=(SELECT id from "+cleanupTableName(tblName)+" WHERE ogc_fid="+selectedId+")",[])); + editValues["0"][editSchema[cid][i][j]["id"]]=list; + for(var k in list){ + for(var l in list[k]){ + $('.mm4me_edition').find("select[name=field_"+editSchema[cid][i][j]["id"]+"]").find('option').each(function(){ + if($(this).val()==list[k][l]) + $(this).prop("selected",true); + }); + } + } + } + } + } + } + + if(cid==mtable){ + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+editValues["0"]["local_id"]+'
  • '); + } + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().show(); + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().find("a").first().click(); + + if(toRunOnLoad[cid]) + for(var i=0;i=0 order by mm4me_editions.step asc",[]); + if(MM4ME_DEBUG) + console.log(list); + list=JSON.parse(list); + if(MM4ME_DEBUG) + console.log((!allTables[tblId])); + if(!allTables[tblId]){ + allTables[tblId]={"id":id,"name":name,"title":title}; + } + mtable=tblId; + + $(".mm4me_edition").find("ul").first().html(""); + $(".mm4me_edition").find(".well").first().html(""); + var cnt=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + var cid=(i==0?list[i]["id"]+"_0":list[i]["id"]); + $(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#edition_form_edit"),cid,mainTable[id]); + } + if(list.length==1) + $(".mm4me_edition").find("ul").first().hide(); + + var aCnt=0; + $('.mm4me_edition').find('ul').first().find('a').each(function () { + if(aCnt>0) + $(this).parent().addClass('require-select'); + aCnt+=1; + }); + $(".require-select").hide(); + $('.mm4me_listing').find('ul').first().find('a').first().click(); + $('.mm4me_edition').find('ul').find('a').first().click(); + $('.swagEditor').summernote(); + $(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+window.Android.translate('edit')+'
  • '); + $(".breadcrumb").append('
  • '+tblTitle+'
  • '); + + setTimeout(function() { updateChangingFields(changingFields) }, 1500); + + /*for(var i=0;i'; + else + cStr+=changingField[j][ckey]["values"][cIndex][i][lkey]+''; + cnt+=1; + } + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + }*/ + $('.mm4me_listing').show(); + $('.mm4me_content').hide(); + +} + +/***************************************************************************** + * In case no server is configured + *****************************************************************************/ +function displayNoListing(){ + $.ajax({ + method: "GET", + url: 'content/nolisting.html', + success: function(data){ + if(MM4ME_DEBUG) + console.log('Display warning message on the UI !'); + $(".mm4me_content").html(data); + $(".mm4me_content").find(".pannel-body").find("p").first().html(window.Android.translate("mm_no_db_found")); + }, + error: function(){ + window.Android.showToast("error !"); + } + }); +} + + +/***************************************************************************** + * Authenticate a user + *****************************************************************************/ +function authenticate(url,login,passwd,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogIn&DataInputs=login="+login+";password="+passwd+"&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG) + console.log(data); + if(func){ + console.log("Call func!") + func(); + } + }, + error: function(){ + if(func1){ + func1(); + } + else{ + disconnect(url); + if(MM4ME_DEBUG) + console.log("unable to login!"); + var hasBeenShown=false; + var xml=arguments[0].responseText; + $(xml).find("ows\\:ExceptionText").each(function(){ + window.Android.showToast($(this).text()); + hasBeenShown=true; + }); + if(!hasBeenShown){ + window.Android.showToast(JSON.stringify(arguments)); + } + } + + } + }); +} + +/***************************************************************************** + * Disconnect a user + *****************************************************************************/ +function disconnect(url,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogOut&DataInputs=&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG){ + console.log(data); + console.log("** Your are no more connected!"); + } + if(func){ + func(); + } + }, + error: function(){ + if(MM4ME_DEBUG){ + console.log(curl); + console.log("unable to disconnect!"); + } + if(func1){ + func1(); + } + } + }); +} + +var geometries={"line":{"geom":null,"constructor": "ol.geom.Linestring"},"polygon":{"geom":null,"constructor":"ol.geom.Polygon","cline":null}}; +var stopTracking=false; +var currentGeometry="line"; +var currentGeometryField="none"; +var map=null; +var vectorLayer,vectorLayer1; +var position; + +/***************************************************************************** + * Track modification of the GPS location + *****************************************************************************/ +function trackCurrentGPSPosition(){ + console.log("## trackCurrentGPSPosition"); + updateCurrentMapLocation(); + + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(tmp0[0]==tmp[0] && tmp0[1]==tmp[1]){ + console.log(" !!!!!!!! Same position!!!!!!!! "); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + return; + } + + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); +} + +var modalCallback=null; + +function trackStepCurrentGPSPosition(){ + console.log("## trackStepCurrentGPSPosition"); + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!stopTracking) + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); +} + +function addCurrentLocation(){ + console.log("addCurrentLocation!!!!"); + if(geometries["origin"]==null) + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + else{ + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + + } +} + +function trackStepByStepPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + } +} +/***************************************************************************** + * Start tracking GPS location + *****************************************************************************/ +function trackGPSPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + } +} + +var hasAddedElement=0; +var myVectorLayer=null; +function addSelectedElement(wktString){ + var features=[]; + try{ + var format = new ol.format.WKT(); + features.push( + format.readFeature(wktString, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }) + ); + }catch(e){ + console.log("Unable to parse WKT ?!"+e) + } + if(!vectorLayer1){ + myVectorLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: features + }), + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: "#3333aa", + width: 1.4 + }) + }) + }); + map.addLayer(myVectorLayer); + } + else{ + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures(features); + } + console.log(vectorLayer1.getSource().getExtent()); + map.updateSize(); + map.getView().fit(vectorLayer1.getSource().getExtent(),map.getSize()); + hasAddedElement=1; +} + +/***************************************************************************** + * Show selected feature on map + *****************************************************************************/ + var addSelectedIndex=0; + var mySelectedElement=null; +function showElementOnMap(wktString){ + mySelectedElement=wktString; + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().hide(); + if(addSelectedIndex==0){ + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + } + addSelectedElement(mySelectedElement); + $("#myModal").find("h4").html(' '+window.Android.translate("view_feature")); + addSelectedIndex++; + }; + //$("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + console.log("OK "); + $('#myModal').modal(); + console.log("OK "); + vectorLayer1.getSource().clear(); + console.log("OK "); + updateCurrentMapLocation(); + console.log("OK "); + addSelectedElement(wktString); + console.log("OK "); + } + + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); +} + + +/***************************************************************************** + * Store the current GPS location in the edit form + *****************************************************************************/ +function requireGPSPosition(elem){ + var position=JSON.parse(window.Android.getGPS()); + //elem=$("#"+elem); + if(position.lat){ + $(".btn_"+elem+"_lat").html(position.lat); + $(".btn_"+elem+"_long").html(position.lon); + $(".btn_"+elem+"_source").html(position.source); + $("input[name='"+elem+"']").val("POINT("+position.lon+" "+position.lat+")"); + }else{ + + } + console.log(JSON.stringify(position)); +} + +/***************************************************************************** + * Ajax setup to ensure seting "withCredentials" to true + *****************************************************************************/ +function ajaxSetup(){ + $.ajaxSetup({ + xhrFields: { + withCredentials: true + } + }); +} + +/***************************************************************************** + * Get the current Network and GPS availability + *****************************************************************************/ +function getCurrentStatus(){ + var tmp=JSON.parse(window.Android.getGNStatus()); + tmp["gps"]=JSON.parse(tmp["gps"]); + updateStatus(tmp['gps'],tmp['net']); +} + +/***************************************************************************** + * Display the status icons + *****************************************************************************/ +function addStatusControl(){ + $('.breadcrumb').append(' /'+ + ''+ + ''+ + ''+ + ''+ + ''); +} + +/***************************************************************************** + * Initialize the map and show the current GPS location + *****************************************************************************/ +var localTiles,localTiles0; +var tileLayers=[]; +function initMapToLocation(){ + if(map) + return; + var osmSource = new ol.source.OSM(); + + var otherLayers=[]; + var otherLayersSwitcher=""; + try{ + var BasesLayersStr=window.Android.getBaseLayers(); + console.log(BasesLayersStr); + var BasesLayers=JSON.parse(BasesLayersStr); + for(var i=0;i=4) + map.getLayers().item(otherLayers[i]["index"]).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity_"+i).show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(!otherLayers[i]["index"]){ + var myTileLayer=new ol.layer.Tile({source: otherLayers[i]["olLayer"]}); + var layers = map.getLayers(); + layers.insertAt(tileLayers.length+1,myTileLayer); + otherLayers[i]["index"]=tileLayers.length+1; + tileLayers.push(myTileLayer); + if(localTileIndex>0) + localTileIndex+=1; + } + else + map.getLayers().item(otherLayers[i]["index"]).setVisible(true); + } + }); + $(".map").parent().find("input[type=range]").last().on('change',function(){ + console.log("change to "+$(this).val()); + map.getLayers().item(otherLayers[i]["index"]).setOpacity($(this).val()/100); + }); + + })(i); + $(".map").parent().find("input[type=checkbox]").parent().off('click'); + $(".map").parent().find("input[type=checkbox]").parent().on('click',function(){ + var tmp=$(this).find("input[type=checkbox]"); + if(tmp.is(":checked")){ + $(this).find("input[type=checkbox]").prop("checked",false).change(); + $(this).addClass("select"); + } + else{ + $(this).find("input[type=checkbox]").prop("checked",true).change(); + $(this).removeClass("select"); + } + }); + + },1); + })(i) + } + } + map = new ol.Map({ + layers: layers, + target: 'map', + controls: ol.control.defaults({ + attributionOptions: /** @type {olx.control.AttributionOptions} */ ({ + collapsible: true + }) + }), + view: new ol.View({ + center: ol.proj.transform([0,0], 'EPSG:4326', 'EPSG:3857'), + zoom: 14, + maxZoom: 22, + minZoom: 1 + }) + }); + + var bstyle = function (feature, resolution) { + + /*var iconFont = 'glyphicon'; + var iconFontText = '\e062';*/ + //var iconFont = 'glyphicon'; + var iconFont = 'Glyphicons Halflings'; + var iconFontText = '\ue062'; + var iconSize = 24; + var opacity=0.4; + var col = 'rgba(0,255,0,0.6)'; + if(feature.get("source")=="GPS") + col = 'rgba(41,136,54,0.5)';//#298836 + else if(feature.get("source")=="Network"){ + col = 'rgba(91,176,75,0.4)';//#5bb04b + iconSize = 32; + opacity=0.2; + } + else if(feature.get("source")=="other"){ + col='rgba(129,208,113,0.5)';//#81d071 + iconSize = 36; + opacity=0.2; + } + else + col='rgba(166,63,39,0.5)'; //#a63f27 //"#EE0000"; + var styles = []; + + var styleIcon = new ol.style.Style({ + text: new ol.style.Text({ + font: 'Normal ' + iconSize + 'px ' + iconFont, + text: iconFontText, + fill: new ol.style.Fill({ color: col }) + }) + }); + styles.push(styleIcon); + + //console.log(feature.get("type")); + return styles; + /*return function (feature, resolution) { + styles.styleIcon.getText().setText(feature.get("iconCode")); + return styles; + };*/ + }; + position=JSON.parse(window.Android.getFullGPS()); + //console.log(JSON.stringify(position)); + if(position.length==0){ + position=[{lon:3.5,lat:43.5,source:"none"}]; + } + var iconFeatures = []; + for(var i=0;i=4) + map.getLayers().item(localTileIndex).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity").show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(map.getLayers().getLength()0.05){ + map.getView().setRotation(-direction); + oldBearer=direction; + } + }else{ + window.Android.stopReportDirection(); + } +} \ No newline at end of file diff --git a/app/src/main/assets/scripts/gene4.js b/app/src/main/assets/scripts/gene4.js new file mode 100644 index 0000000..fd624f1 --- /dev/null +++ b/app/src/main/assets/scripts/gene4.js @@ -0,0 +1,2461 @@ +var MM4ME_DEBUG=true; +var EDITION_TYPE_FILE=5; +var mainTable={}; +var mtable=null; +var refTables={}; +var tblId=null; +var allTables={}; +var editSchema={}; +var referenceIds={}; +var lastEdition={}; +var tblName=null; +var toRunOnLoad={}; +var valuesOnLoad=[]; +var definedSqlTypes=[]; +var changingFields=[]; +var lang=window.Android.getLang(); +var langUrl=null; +if(lang=="fr"){ + langUrl="file:///android_asset/localisation/French.json"; +} + + + + +function loadWelcome5(){ + window.Android.startWelcomeScreen("sat_finder"); +} + +/***************************************************************************** + * Update status icon color depending on network and GPS availability + *****************************************************************************/ +function updateStatus(gps,internet){ + if(internet){ + $('.glyphicon-signal').css('color','#00EE00'); + }else + $('.glyphicon-signal').css('color','#EE0000'); + $('#gpsMenu1').next().html(""); + if(gps.length==0){ + $('#gpsMenu1').css('color','#EE0000'); + $('#gpsMenu1').next().html('
  • '+window.Android.translate("no_gps")+'
  • '); + }else{ + $('#gpsMenu1').css('color','#00EE00'); + for(var i=0;i '+gps[i].source+''); + } +} + +/***************************************************************************** + * List all available table for a given theme + *****************************************************************************/ +function fetchTableForTheme(obj,id){ + var hasTable=false; + var tables=JSON.parse(window.Android.displayTable( + "SELECT mm4me_tables.id as tid,mm4me_views.id as id,"+ + "mm4me_tables.name as name, "+ + "mm4me_tables.description, "+ + "mm4me_views.name as title "+ + " from mm4me_tables,mm4me_views"+ + " where mm4me_tables.id=mm4me_views.ptid "+ + "and mm4me_views.visible "+ + "and mm4me_views.id in (select vid from mm4me_views_themes where tid="+id+")",[])); + for(var i=0;i0; +} + +/***************************************************************************** + * Create a JSON Object containing the themes hierarchy + *****************************************************************************/ +function fetchThemes(obj,id){ + var cthemes=JSON.parse(window.Android.displayTable("SELECT id,name FROM mm4me_themes where pid = "+id,[])); + for(var i=0;i'); + $('#tree').treeview({ + data: allThemes, + onNodeSelected: function(event, data) { + if(data["myId"]){ + try{ + func(data["myId"],data["myName"],data["myTitle"],true); + }catch(e){ + console.log(JSON.stringify(data)); + console.log(e); + window.Android.showToast(e); + //exit(); + } + } + else{ + $('#tree').treeview('toggleNodeExpanded',[$("#tree").treeview('getSelected')[0], { silent: true }]); + $('#tree').treeview('toggleNodeSelected',[$("#tree").treeview('getSelected')[0], { silent: true }]); + //console.log(JSON.stringify(data)); + //$(this).open(); + } + }, + showTags: true + }); + var list=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.id=mm4me_views.ptid and mm4me_views.visible",[])); + var tableList=list; + var total=0; + contents=[]; + for(var i in list){ + mainTable[list[i]["id"]]=list[i]["tid"]; + } + } + catch(e){ + console.log(e); + displayNoListing(); + } +} + +/***************************************************************************** + * Update the breadcrumbs text to translated string + *****************************************************************************/ +function updateBreadcrumbs(breadcrumbs){ + //var breadcrumbs=["home","view"]; + var lcnt0=0; + $('.breadcrumb').find("a").each(function(){ + $(this).append(window.Android.translate(breadcrumbs[lcnt0])); + lcnt0+=1; + }); + $('.breadcrumb').find("li").last().each(function(){ + $(this).append(window.Android.translate(breadcrumbs[lcnt0])); + lcnt0+=1; + }); +} + +/***************************************************************************** + * Convert table names in a string from the PostgreSQL to our SQLite format. + * So, convert "AAAXXX.YYYBBB" to "AAAXXX_YYYBBB" + *****************************************************************************/ +function cleanupTableName(name){ + return name.replace(/(\w+)(\d*)\.(\d*)(\w+)/g,"$1$2_$3$4"); +} + +/***************************************************************************** + * Display an HTML part containing the image produced by Camera or picked from + * the photo library. + *****************************************************************************/ +function loadNewPicture(cid,id,picture){ + $(".tab-pane").each(function(){ + if($(this).is(":visible")) + $(this).find("#value_"+id) + .html('
    '+picture+'
    '); + }); +} + + +/***************************************************************************** + * Create a JSON Object representing the dependency values. + *****************************************************************************/ +function fetchDependencies(obj,cid,changingField){ + var list1=null; + //console.log(cid+" "+JSON.stringify(obj)); + + for(var key in changingField){ + for(var i=0;i'; + tmpStr+=res+ + '
    '; + console.log(tmpStr); + return tmpStr; + } + if(definedSqlTypes[i]["code"]=="geometry"){ + console.log("CID: "+cid+" select type from mm4me_gc where f_table_schema||'_'||f_table_name = (select name from mm4me_tables where id="+cid+") ") + var geoType=getGeometryType("(select replace(name,'.','_') from mm4me_tables where id="+cid+")"); + var viewb=''; + var res=' '; + if(geoType=='POINT' || geoType=='MULTIPOINT' ) + return res+''+ + '

    GPS Informations

    Type
    Coords
    '+ + ''; + else if(geoType=='LINESTRING' || geoType=='MULTILINESTRING' ) + return res+''+ + ''+ + '
    '+viewb+'
    '; + else if(geoType=='POLYGON' || geoType=='MULTIPOLYGON' ) + return res+''+ + ''+ + '
    '+viewb+'
    '; + + } + if(definedSqlTypes[i]["code"]=="tbl_linked"){ + var tmp=obj["value"].split(';'); + //console.log(cleanupTableName('SELECT '+tmp[1]+' FROM '+tmp[2]+' WHERE '+tmp[0]+'='+tblName+'.id')); + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(tmp[3]),[])); + var tmpStr='"; + + } + if(definedSqlTypes[i]["code"]=="link"){ + var strReturn; + if(!View_template) + $.ajax({ + async: false, + method: "GET", + url: './content/view_template.html', + error: function(){ + console.log("Nothing to run after"); + }, + success: function(data){ + console.log("**** \n\n Load View Template"); + View_template=data; + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + if(refs!=""){ + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=data.replace(reg,refs["0"]["tid"]); + } + } + }); + else{ + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=View_template.replace(reg,refs["0"]["tid"]); + + } + //console.log(strReturn); + return strReturn; + } + if(definedSqlTypes[i]["code"]=="ref"){ + var req=obj["value"];//.replace(/^\((\w+)\)$/g,"$1"); + if(req[0]=="(") + req="SELECT * FROM "+req; + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(req),[])); + var tmpStr='"; + + if(obj["dependencies"]) + try{ + var lobj={}; + lobj[obj["id"]]={"dep":JSON.parse(obj["dependencies"])}; + for(var jj=0;jj=0){ + return ''; + } + if(definedSqlTypes[i]["code"]=="html") + return ''; + if(definedSqlTypes[i]["code"]=="text") + return ''; + if(definedSqlTypes[i]["code"]=="boolean") + return ''; + if(definedSqlTypes[i]["code"]=="date" || definedSqlTypes[i]["code"]=="datetime") + return ''; + if(definedSqlTypes[i]["code"]=="float") + return ''; + return definedSqlTypes[i]["code"]; + } + } + return null; +} + +function printOptionalCheckbox(obj,cid){ + if(definedSqlTypes.length==0){ + definedSqlTypes=JSON.parse(window.Android.displayTable("select id,code from mm4me_ftypes where ftype='e' order by name",[])); + } + + var res=' '; + for(var i in definedSqlTypes){ + if(definedSqlTypes[i]["id"]==obj["ftype"]){ + if(definedSqlTypes[i]["code"]=="bytea"){ + var tmpStr=""; + return res; + } + else if(definedSqlTypes[i]["code"]=="geometry"){ + return res + } + } + } + return ''; +} + +var refTypeId=null; +var editPrintedOnce=[]; +/***************************************************************************** + * Create HTML part to display the line containing both the title and the + * corresponding input for a given table's field. + *****************************************************************************/ +function printEditionFields(obj,myRoot,cid,mid){ + var list1=window.Android.displayTable("select * from mm4me_edition_fields where mm4me_edition_fields.edition>0 and eid="+obj["id"]+" order by mm4me_edition_fields.id asc",[]); + if(!editSchema[mid]) + editSchema[mid]={}; + editSchema[mid][obj["id"]]=JSON.parse(list1); + list1=JSON.parse(list1); + myRoot.find(".tab-content").first().append('
    '+obj["description"]+'
    '); + for(var j in list1) + if(list1[j]["edition"]>0) { + myRoot.find(".tab-content").first().children().last().append( + '
    '+ + '
    '+ + ''+ + '
    '+ + '
    '+ + printCurrentType(list1[j],mid)+ + '
    '+ + '
    '); + if(list1[j]["dependencies"]){ + try{ + editPrintedOnce.push(list1[j]["name"]); + console.log("JSON PARSE") + console.log(list1[j]["dependencies"]); + var objJson=JSON.parse(list1[j]["dependencies"]); + console.log("JSON PARSE OK"); + if(!refTypeId) + refTypeId=JSON.parse(window.Android.displayTable("select id from mm4me_ftypes where ftype='e' and code='ref'",[]))[0]["id"]; + console.log(refTypeId); + for(i in objJson){ + if(objJson[i]["myself"]){ + console.log("IS MYSELF!!"); + for(k in objJson[i]["myself"]){ + for(l in objJson[i]["myself"][k]){ + + + if(objJson[i]["myself"][k][l]["dependents"]){ + for(m in objJson[i]["myself"][k][l]["dependents"]){ + for(n in objJson[i]["myself"][k][l]["dependents"][m]){ + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]); + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["label"]); + var lObj={"id": n, "ftype":refTypeId,"value":objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]}; + //if(!myRoot.find('select[name="field_'+list1[j]["id"]+'"]').parent().find('select[name="field_'+n+'"]').length) + myRoot.find('select[name="field_'+list1[j]["id"]+'"]').last().parent().prepend( + '
    '+ + '
    '+ + ''+ + '
    '+ + '
    '+ + printCurrentType(lObj,mid)+ + '
    '+ + '
    '); + console.log(myRoot.find('select[name="field_'+n+'"]')); + console.log(printCurrentType(lObj,mid)); + (function(a,b){ + myRoot.find('select[name="field_'+n+'"]').off('change'); + myRoot.find('select[name="field_'+n+'"]').on('change',function(){ + console.log('select[name="field_'+n+'"]'); + var req=cleanupTableName(a["value"]); + if(a["value"].indexOf("WHERE")<0){ + req=req.replace(/order by/g,"where "+b["tfieldf"]+" "+b["operator"]+" "+(b["operator"]=="like"?"'":"")+$(this).val()+(b["operator"]=="like"?"'":"")+" order by") + } + console.log(req); + var res=JSON.parse(window.Android.displayTable(req,[])); + myRoot.find('select[name="field_'+a["id"]+'"]').html(""); + for(ij in res){ + var tmpStr=' '; + cnt+=1; + } + cnt0+=1; + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + $("select[name=field_"+changingField[j][ckey]["id"]+"]").change(); + }else{ + console.log("DISPLAY ELEMENT IF CINDEX >=0 "); + //console.log(JSON.stringify(changingField[j][ckey])); + console.log('input[name="field_'+ckey+'"],select[name="field_'+ckey+'"],textarea[name="field_'+ckey+'"]'); + console.log($('input[name="field_'+changingField[j][ckey]["id"]+'"],select[name="field_'+changingField[j][ckey]["id"]+'"],textarea[name="field_'+changingField[j][ckey]["id"]+'"]').parent().parent().html()); + var mycKey=changingField[j][ckey]["id"]; + if(cIndex<0) + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().hide(); + else + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().show(); + } + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + } + }catch(e){ + console.log(e); + setTimeout(function() { updateChangingFields(changingFields) }, 500); + } +} + +/***************************************************************************** + * Display the content of a table referencing the current edited table. + *****************************************************************************/ +function listInnerTable(id,vid,name,title,init,prefix,clause,ref){ + var list=JSON.parse(window.Android.displayTable("select mm4me_tables.id as tid,mm4me_tables.name as tname,mm4me_editions.id,mm4me_editions.name from mm4me_editions,mm4me_tables where mm4me_editions.ptid=mm4me_tables.id and mm4me_tables.id="+id+" order by mm4me_editions.step asc",[])); + var cnt=0; + var detectInit=true; + var tid=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + tid=list[i]["tid"]; + if(!allTables[list[i]["tid"]]){ + allTables[list[i]["tid"]]={"id":list[i]["id"],"name":list[i]["tname"],"title":list[i]["name"]}; + detectInit=false; + $("#sub_tableContent_"+id+"").find(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#sub_tableContent_"+id+"").find(".mm4me_edition"),list[i]["id"],list[i]["tid"]); + if(cnt==0){ + try{ + var cid=list[i]["id"]+"_0"; + $("#sub_tableContent_"+id+"").find("ul").first().append('
  • '+window.Android.translate('table')+'
  • '); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+" .require-select").hide(); + printEditionFields(list[i],$("#sub_tableContent_"+id+""),cid,list[i]["tid"]); + }catch(e){ + console.log("**** ERROR ***> "+e); + } + } + var myRoot=$("#sub_tableContent_"+id+""); + myRoot.find('ul').first().find('a').click(function (e) { + e.preventDefault(); + if($(this).parent().hasClass('require-select')) + myRoot.find('.mm4me_edition').show(); + else + myRoot.find('.mm4me_edition').hide(); + $(this).tab('show'); + }) + myRoot.find('ul').first().find('a').first().click(); + myRoot.find('.mm4me_edition').find('ul').find('a').first().click(); + myRoot.find('.swagEditor').summernote(); + myRoot.find(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),tid,editTableReact); + }); + myRoot.find(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),tid,editTableReact); + }); + + } + cnt+=1; + } + + var list=JSON.parse(window.Android.displayTable("SELECT id,name,value,alias,width,class from mm4me_view_fields where vid="+vid+" order by id",[])); + var columns=[]; + var columnNames=[]; + var sqlColumns=""; + var orderColumn="id"; + var orderType="asc"; + for(var i=0;i0){ + orderColumn=list[i]["name"]; + if(list[i]["class"]==1) + orderType="desc"; + } + } + var ccol=getPKey(cleanupTableName(name)); + var req="SELECT "+ccol+" as ogc_fid FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list1=JSON.parse(window.Android.displayTable(req,[])); + req="SELECT "+sqlColumns+" FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list=JSON.parse(window.Android.displayTable(req,[])); + var dataSet = []; + for(var i=0;i'; + cnt+=1; + } + dataSet.push(tmpData); + } + var selected = []; + + //$("#edition_form_table").html('
    '); + var localName="exampleTable_"+id; + ((function(localName,sid){ + + if(!detectInit){ + + var options={ + data: dataSet, + columns: columns, + "scrollX": true, + scrollY: '50vh', + scrollCollapse: true, + select: true, + "rowCallback": function( row, data ) { + if ( $.inArray(data.DT_RowId, selected) !== -1 ) { + $(row).addClass('selected'); + } + } + }; + if(langUrl!=null) + options["language"]={ + url: langUrl + }; + + $('#'+localName).DataTable( options ); + $('#'+localName+' tbody').on('click', 'tr', function () { + var id = this.id; + var index = $.inArray(id, selected); + + if ( index === -1 ) { + selected.push( id ); + } else { + selected.splice( index, 1 ); + } + displayEditForm(sid,$(this).find("input[name=id]").first().val(),true); + var tmp=$(this).hasClass('selected'); + $('#'+localName+' tbody tr').removeClass('selected'); + $(this).toggleClass('selected'); + if(!tmp) + $(this).toggleClass('selected'); + } ); + + }else{ + $('#'+localName).dataTable().fnClearTable(); + if(dataSet.length>0) + $('#'+localName).dataTable().fnAddData(dataSet); + } + $("#sub_tableContent_"+id+"").find("ul").first().find('a').first().click(); + })(localName,tid)); + +} + +var onFormFirstLoad=null; +/***************************************************************************** + * Show the edit form + *****************************************************************************/ +function displayEditForm(cid,selectedId,basic){ + if(basic && !$("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val()){ + if(cid==mtable){ + $("#main_tableContent").find(".require-select").hide(); + $("#main_tableContent").find("ul").first().find("a").first().click(); + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + + }else{ + $("#sub_tableContent_"+cid).find(".require-select").hide(); + $("#sub_tableContent_"+cid).find("ul").first().find("a").first().click(); + } + return; + } + var fields=[] + var sizedFields=[]; + var sizedFieldsAlias=[]; + var notSizedFields=[]; + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + //console.log(JSON.stringify(editSchema[cid][i][j])); + if(editSchema[cid][i][j]["ftype"]=="5"){ + sizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")); + sizedFieldsAlias.push(editSchema[cid][i][j]["id"]); + } + else{ + notSizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + try{ + var tmp=JSON.parse(editSchema[cid][i][j]["dependencies"]); + var sqlReq=""; + var sqlClause=""; + var sqlParams=""; + var sqlParam=0; + var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); + var hasDep=false; + var previousElements=[]; + for(k in tmp) + for(l in tmp[k]){ + //console.log(JSON.stringify(tmp[k][l])); + if(l=="myself"){ + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + //console.log(JSON.stringify(tmp[k][l][m][n])); + if(sqlReq!=""){ + sqlReq+=", "; + sqlParams+=", "; + //sqlClause+=" WHERE "+alphabet[sqlParam-1]+"."+tmp[k][l][m][n]["tfield"]+"="+alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + + } + sqlReq+="("+cleanupTableName(tmp[k][l][m][n]["sql_query"])+") as "+alphabet[sqlParam]; + sqlParams+=alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + sqlParam+=1; + if(tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"])); + for(var o in tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"][o])); + for(var p in tmp[k][l][m][n]["dependents"][o]){ + console.log(tmp[k][l][m][n]["dependents"][o][p]["sql_query"].replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")); + sqlReq+=", ("+cleanupTableName(tmp[k][l][m][n]["dependents"][o][p]["sql_query"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")+") as b"; + sqlParams+=", b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlParam+=2; + sqlReq+=", ("+cleanupTableName(editSchema[cid][i][j]["value"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+" from")+") as c"; + + sqlClause+=" WHERE c."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+"=b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlClause+=" AND b."+tmp[k][l][m][n]["tfield"]+"=a."+tmp[k][l][m][n]["tfield"]; + sqlClause+=" AND c.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + hasDep=true; + } + } + } + } + if(!hasDep){ + var tmpReq=cleanupTableName(editSchema[cid][i][j]["value"]); + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + tmpReq=tmpReq.replace(/from/,","+tmp[k][l][m][n]["tfield"]+" from"); + if(sqlClause=="") + sqlClause+=" WHERE "; + else + sqlClause+=" "+tmp[k][l][m][n]["cond_join"]+" "; + sqlClause+=alphabet[m]+"."+tmp[k][l][m][n]["tfield"]+"=a1."+tmp[k][l][m][n]["tfield"]; + sqlClause+=""; + } + sqlClause+=" AND a1.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + //sqlReq=(tmpReq); + console.log(tmpReq); + sqlReq+=", ("+tmpReq+") as a1"; + } + //console.log(JSON.stringify(tmp[k][l])); + console.log("SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause); + var localQuery="SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause; + var res0=JSON.parse(window.Android.displayTable(localQuery,[])); + console.log(JSON.stringify(res0)); + for(m in res0) + for(n in res0[m]){ + try{ + console.log("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]"); + /**/ + if($('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().val(res0[m][n]).change(); + else{ + for(m0 in tmp[k][l]) + for(n0 in tmp[k][l][m0]){ + /*console.log(n0.indexOf(n)<0); + console.log(n); + console.log(n0); + console.log(res0[m][n]);*/ + if(n0.indexOf(n)>=0){ + console.log("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]"); + if(!$('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]").first().val(res0[m][n]).change(); + } + } + } + }catch(e){ + console.log(e); + } + + } + //if(tmp[k][l]["dependents"]) + }else{ + console.log(JSON.stringify(tmp[k][l])); + if(tmp[k][l]["tfield"]=="none"){ + var isNull=JSON.parse(window.Android.displayTable("SELECT CASE WHEN "+l+" is null THEN 1 ELSE 0 END as p FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId,[])); + console.log(JSON.stringify(isNull)); + if(isNull[0]["p"]=="0"){ + console.log(JSON.stringify(isNull)); + console.log("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]"); + console.log(k+""); + $('.mm4me_edition').find("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]").first().val(k+"").change(); + } + } + + } + } + + //if(sqlReq) + }catch(e){ + console.log(e); + } + } + if(editSchema[cid][i][j]["name"].indexOf("unamed")<0) + fields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + } + } + var ccol=getPKey(cleanupTableName(allTables[cid].name)); + /* $("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val() */ + var editValues; + if(sizedFields.length>0){ + for(var i=0;i 1000000 and "+ccol+"="+selectedId,[])); + //console.log(JSON.stringify(hasElement[0])); + if(hasElement[0]["cnt"]!="0"){ + var nbIteration=parseInt(hasElement[0]["len"])/1000000; + var zfields=[] + var len=0; + var query=""; + var len1=parseInt(hasElement[0]["len"]); + for(var j=0;j0){ + $("#field_"+j+"_map").show(); + $("#field_"+j+"_map").off('click'); + $("#field_"+j+"_map").on('click',function(){ + console.log("Display table with a selected feature"); + console.log($(this).prev().val()); + showElementOnMap($(this).prev().val()); + }); + } + if($(".btn_field_"+j+"_lat").length>0){ + //alert(editValues[i][j]); + $(".btn_field_"+j+"_lat").html(editValues[i][j]); + $(".btn_field_"+j+"_long").html(""); + $(".btn_field_"+j+"_source").html(""); + $("input[name='field_"+j+"']").val("POINT"+editValues[i][j]); + + } + } + //$(".swagEditor").summernote(); + } + } + + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + if(editSchema[cid][i][j]["name"].indexOf("unamed")>=0){ + if(editSchema[cid][i][j]["ftype"]==6){ + var tmp=editSchema[cid][i][j]["value"].split(';'); + var list=JSON.parse(window.Android.displayTable("select "+tmp[1]+" from "+cleanupTableName(tmp[2])+" where "+tmp[0]+"=(SELECT id from "+cleanupTableName(tblName)+" WHERE ogc_fid="+selectedId+")",[])); + editValues["0"][editSchema[cid][i][j]["id"]]=list; + for(var k in list){ + for(var l in list[k]){ + $('.mm4me_edition').find("select[name=field_"+editSchema[cid][i][j]["id"]+"]").find('option').each(function(){ + if($(this).val()==list[k][l]) + $(this).prop("selected",true); + }); + } + } + } + } + } + } + + if(cid==mtable){ + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+editValues["0"]["local_id"]+'
  • '); + } + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().show(); + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().find("a").first().click(); + + if(toRunOnLoad[cid]) + for(var i=0;i=0 order by mm4me_editions.step asc",[]); + if(MM4ME_DEBUG) + console.log(list); + list=JSON.parse(list); + if(MM4ME_DEBUG) + console.log((!allTables[tblId])); + if(!allTables[tblId]){ + allTables[tblId]={"id":id,"name":name,"title":title}; + } + mtable=tblId; + + $(".mm4me_edition").find("ul").first().html(""); + $(".mm4me_edition").find(".well").first().html(""); + var cnt=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + var cid=(i==0?list[i]["id"]+"_0":list[i]["id"]); + $(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#edition_form_edit"),cid,mainTable[id]); + } + if(list.length==1) + $(".mm4me_edition").find("ul").first().hide(); + + var aCnt=0; + $('.mm4me_edition').find('ul').first().find('a').each(function () { + if(aCnt>0) + $(this).parent().addClass('require-select'); + aCnt+=1; + }); + $(".require-select").hide(); + $('.mm4me_listing').find('ul').first().find('a').first().click(); + $('.mm4me_edition').find('ul').find('a').first().click(); + $('.swagEditor').summernote(); + $(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+window.Android.translate('edit')+'
  • '); + $(".breadcrumb").append('
  • '+tblTitle+'
  • '); + + setTimeout(function() { updateChangingFields(changingFields) }, 1500); + + /*for(var i=0;i'; + else + cStr+=changingField[j][ckey]["values"][cIndex][i][lkey]+''; + cnt+=1; + } + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + }*/ + $('.mm4me_listing').show(); + $('.mm4me_content').hide(); + +} + +/***************************************************************************** + * In case no server is configured + *****************************************************************************/ +function displayNoListing(){ + $.ajax({ + method: "GET", + url: 'content/nolisting.html', + success: function(data){ + if(MM4ME_DEBUG) + console.log('Display warning message on the UI !'); + $(".mm4me_content").html(data); + $(".mm4me_content").find(".pannel-body").find("p").first().html(window.Android.translate("mm_no_db_found")); + }, + error: function(){ + window.Android.showToast("error !"); + } + }); +} + + +/***************************************************************************** + * Authenticate a user + *****************************************************************************/ +function authenticate(url,login,passwd,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogIn&DataInputs=login="+login+";password="+passwd+"&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG) + console.log(data); + if(func){ + console.log("Call func!") + func(); + } + }, + error: function(){ + if(func1){ + func1(); + } + else{ + disconnect(url); + if(MM4ME_DEBUG) + console.log("unable to login!"); + var hasBeenShown=false; + var xml=arguments[0].responseText; + $(xml).find("ows\\:ExceptionText").each(function(){ + window.Android.showToast($(this).text()); + hasBeenShown=true; + }); + if(!hasBeenShown){ + window.Android.showToast(JSON.stringify(arguments)); + } + } + + } + }); +} + +/***************************************************************************** + * Disconnect a user + *****************************************************************************/ +function disconnect(url,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogOut&DataInputs=&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG){ + console.log(data); + console.log("** Your are no more connected!"); + } + if(func){ + func(); + } + }, + error: function(){ + if(MM4ME_DEBUG){ + console.log(curl); + console.log("unable to disconnect!"); + } + if(func1){ + func1(); + } + } + }); +} + +var geometries={"line":{"geom":null,"constructor": "ol.geom.Linestring"},"polygon":{"geom":null,"constructor":"ol.geom.Polygon","cline":null}}; +var stopTracking=false; +var currentGeometry="line"; +var currentGeometryField="none"; +var map=null; +var vectorLayer,vectorLayer1; +var position; + +/***************************************************************************** + * Track modification of the GPS location + *****************************************************************************/ +function trackCurrentGPSPosition(){ + console.log("## trackCurrentGPSPosition"); + updateCurrentMapLocation(); + + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(tmp0[0]==tmp[0] && tmp0[1]==tmp[1]){ + console.log(" !!!!!!!! Same position!!!!!!!! "); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + return; + } + + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); +} + +var modalCallback=null; + +function trackStepCurrentGPSPosition(){ + console.log("## trackStepCurrentGPSPosition"); + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!stopTracking) + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); +} + +function addCurrentLocation(){ + console.log("addCurrentLocation!!!!"); + if(geometries["origin"]==null) + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + else{ + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + + } +} + +function trackStepByStepPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + } +} +/***************************************************************************** + * Start tracking GPS location + *****************************************************************************/ +function trackGPSPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + } +} + +var hasAddedElement=0; +var myVectorLayer=null; +function addSelectedElement(wktString){ + var features=[]; + try{ + var format = new ol.format.WKT(); + features.push( + format.readFeature(wktString, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }) + ); + }catch(e){ + console.log("Unable to parse WKT ?!"+e) + } + if(!vectorLayer1){ + myVectorLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: features + }), + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: "#3333aa", + width: 1.4 + }) + }) + }); + map.addLayer(myVectorLayer); + } + else{ + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures(features); + } + console.log(vectorLayer1.getSource().getExtent()); + map.updateSize(); + map.getView().fit(vectorLayer1.getSource().getExtent(),map.getSize()); + hasAddedElement=1; +} + +/***************************************************************************** + * Show selected feature on map + *****************************************************************************/ + var addSelectedIndex=0; + var mySelectedElement=null; +function showElementOnMap(wktString){ + mySelectedElement=wktString; + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().hide(); + if(addSelectedIndex==0){ + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + } + addSelectedElement(mySelectedElement); + $("#myModal").find("h4").html(' '+window.Android.translate("view_feature")); + addSelectedIndex++; + }; + //$("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + console.log("OK "); + $('#myModal').modal(); + console.log("OK "); + vectorLayer1.getSource().clear(); + console.log("OK "); + updateCurrentMapLocation(); + console.log("OK "); + addSelectedElement(wktString); + console.log("OK "); + } + + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); +} + + +/***************************************************************************** + * Store the current GPS location in the edit form + *****************************************************************************/ +function requireGPSPosition(elem){ + var position=JSON.parse(window.Android.getGPS()); + //elem=$("#"+elem); + if(position.lat){ + $(".btn_"+elem+"_lat").html(position.lat); + $(".btn_"+elem+"_long").html(position.lon); + $(".btn_"+elem+"_source").html(position.source); + $("input[name='"+elem+"']").val("POINT("+position.lon+" "+position.lat+")"); + }else{ + + } + console.log(JSON.stringify(position)); +} + +/***************************************************************************** + * Ajax setup to ensure seting "withCredentials" to true + *****************************************************************************/ +function ajaxSetup(){ + $.ajaxSetup({ + xhrFields: { + withCredentials: true + } + }); +} + +/***************************************************************************** + * Get the current Network and GPS availability + *****************************************************************************/ +function getCurrentStatus(){ + var tmp=JSON.parse(window.Android.getGNStatus()); + tmp["gps"]=JSON.parse(tmp["gps"]); + updateStatus(tmp['gps'],tmp['net']); +} + +/***************************************************************************** + * Display the status icons + *****************************************************************************/ +function addStatusControl(){ + $('.breadcrumb').append(' /'+ + ''+ + ''+ + ''+ + ''+ + ''); +} + +/***************************************************************************** + * Initialize the map and show the current GPS location + *****************************************************************************/ +var localTiles,localTiles0; +var tileLayers=[]; +function initMapToLocation(){ + if(map) + return; + var osmSource = new ol.source.OSM(); + + var otherLayers=[]; + var otherLayersSwitcher=""; + try{ + var BasesLayersStr=window.Android.getBaseLayers(); + console.log(BasesLayersStr); + var BasesLayers=JSON.parse(BasesLayersStr); + for(var i=0;i=4) + map.getLayers().item(otherLayers[i]["index"]).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity_"+i).show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(!otherLayers[i]["index"]){ + var myTileLayer=new ol.layer.Tile({source: otherLayers[i]["olLayer"]}); + var layers = map.getLayers(); + layers.insertAt(tileLayers.length+1,myTileLayer); + otherLayers[i]["index"]=tileLayers.length+1; + tileLayers.push(myTileLayer); + if(localTileIndex>0) + localTileIndex+=1; + } + else + map.getLayers().item(otherLayers[i]["index"]).setVisible(true); + } + }); + $(".map").parent().find("input[type=range]").last().on('change',function(){ + console.log("change to "+$(this).val()); + map.getLayers().item(otherLayers[i]["index"]).setOpacity($(this).val()/100); + }); + + })(i); + $(".map").parent().find("input[type=checkbox]").parent().off('click'); + $(".map").parent().find("input[type=checkbox]").parent().on('click',function(){ + var tmp=$(this).find("input[type=checkbox]"); + if(tmp.is(":checked")){ + $(this).find("input[type=checkbox]").prop("checked",false).change(); + $(this).addClass("select"); + } + else{ + $(this).find("input[type=checkbox]").prop("checked",true).change(); + $(this).removeClass("select"); + } + }); + + },1); + })(i) + } + } + map = new ol.Map({ + layers: layers, + target: 'map', + controls: ol.control.defaults({ + attributionOptions: /** @type {olx.control.AttributionOptions} */ ({ + collapsible: true + }) + }), + view: new ol.View({ + center: ol.proj.transform([0,0], 'EPSG:4326', 'EPSG:3857'), + zoom: 14, + maxZoom: 22, + minZoom: 1 + }) + }); + + var bstyle = function (feature, resolution) { + + /*var iconFont = 'glyphicon'; + var iconFontText = '\e062';*/ + //var iconFont = 'glyphicon'; + var iconFont = 'Glyphicons Halflings'; + var iconFontText = '\ue062'; + var iconSize = 24; + var opacity=0.4; + var col = 'rgba(0,255,0,0.6)'; + if(feature.get("source")=="GPS") + col = 'rgba(41,136,54,0.5)';//#298836 + else if(feature.get("source")=="Network"){ + col = 'rgba(91,176,75,0.4)';//#5bb04b + iconSize = 32; + opacity=0.2; + } + else if(feature.get("source")=="other"){ + col='rgba(129,208,113,0.5)';//#81d071 + iconSize = 36; + opacity=0.2; + } + else + col='rgba(166,63,39,0.5)'; //#a63f27 //"#EE0000"; + var styles = []; + + var styleIcon = new ol.style.Style({ + text: new ol.style.Text({ + font: 'Normal ' + iconSize + 'px ' + iconFont, + text: iconFontText, + fill: new ol.style.Fill({ color: col }) + }) + }); + styles.push(styleIcon); + + //console.log(feature.get("type")); + return styles; + /*return function (feature, resolution) { + styles.styleIcon.getText().setText(feature.get("iconCode")); + return styles; + };*/ + }; + position=JSON.parse(window.Android.getFullGPS()); + //console.log(JSON.stringify(position)); + if(position.length==0){ + position=[{lon:3.5,lat:43.5,source:"none"}]; + } + var iconFeatures = []; + for(var i=0;i=4) + map.getLayers().item(localTileIndex).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity").show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(map.getLayers().getLength()0.05){ + map.getView().setRotation(-direction); + oldBearer=direction; + } + }else{ + window.Android.stopReportDirection(); + } +} \ No newline at end of file diff --git a/app/src/main/assets/scripts/gene5.js b/app/src/main/assets/scripts/gene5.js new file mode 100644 index 0000000..c777907 --- /dev/null +++ b/app/src/main/assets/scripts/gene5.js @@ -0,0 +1,2461 @@ +var MM4ME_DEBUG=true; +var EDITION_TYPE_FILE=5; +var mainTable={}; +var mtable=null; +var refTables={}; +var tblId=null; +var allTables={}; +var editSchema={}; +var referenceIds={}; +var lastEdition={}; +var tblName=null; +var toRunOnLoad={}; +var valuesOnLoad=[]; +var definedSqlTypes=[]; +var changingFields=[]; +var lang=window.Android.getLang(); +var langUrl=null; +if(lang=="fr"){ + langUrl="file:///android_asset/localisation/French.json"; +} + + + + +function loadWelcome6(){ + window.Android.startWelcomeScreen("ARSimulation"); +} + +/***************************************************************************** + * Update status icon color depending on network and GPS availability + *****************************************************************************/ +function updateStatus(gps,internet){ + if(internet){ + $('.glyphicon-signal').css('color','#00EE00'); + }else + $('.glyphicon-signal').css('color','#EE0000'); + $('#gpsMenu1').next().html(""); + if(gps.length==0){ + $('#gpsMenu1').css('color','#EE0000'); + $('#gpsMenu1').next().html('
  • '+window.Android.translate("no_gps")+'
  • '); + }else{ + $('#gpsMenu1').css('color','#00EE00'); + for(var i=0;i '+gps[i].source+''); + } +} + +/***************************************************************************** + * List all available table for a given theme + *****************************************************************************/ +function fetchTableForTheme(obj,id){ + var hasTable=false; + var tables=JSON.parse(window.Android.displayTable( + "SELECT mm4me_tables.id as tid,mm4me_views.id as id,"+ + "mm4me_tables.name as name, "+ + "mm4me_tables.description, "+ + "mm4me_views.name as title "+ + " from mm4me_tables,mm4me_views"+ + " where mm4me_tables.id=mm4me_views.ptid "+ + "and mm4me_views.visible "+ + "and mm4me_views.id in (select vid from mm4me_views_themes where tid="+id+")",[])); + for(var i=0;i0; +} + +/***************************************************************************** + * Create a JSON Object containing the themes hierarchy + *****************************************************************************/ +function fetchThemes(obj,id){ + var cthemes=JSON.parse(window.Android.displayTable("SELECT id,name FROM mm4me_themes where pid = "+id,[])); + for(var i=0;i'); + $('#tree').treeview({ + data: allThemes, + onNodeSelected: function(event, data) { + if(data["myId"]){ + try{ + func(data["myId"],data["myName"],data["myTitle"],true); + }catch(e){ + console.log(JSON.stringify(data)); + console.log(e); + window.Android.showToast(e); + //exit(); + } + } + else{ + $('#tree').treeview('toggleNodeExpanded',[$("#tree").treeview('getSelected')[0], { silent: true }]); + $('#tree').treeview('toggleNodeSelected',[$("#tree").treeview('getSelected')[0], { silent: true }]); + //console.log(JSON.stringify(data)); + //$(this).open(); + } + }, + showTags: true + }); + var list=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.id=mm4me_views.ptid and mm4me_views.visible",[])); + var tableList=list; + var total=0; + contents=[]; + for(var i in list){ + mainTable[list[i]["id"]]=list[i]["tid"]; + } + } + catch(e){ + console.log(e); + displayNoListing(); + } +} + +/***************************************************************************** + * Update the breadcrumbs text to translated string + *****************************************************************************/ +function updateBreadcrumbs(breadcrumbs){ + //var breadcrumbs=["home","view"]; + var lcnt0=0; + $('.breadcrumb').find("a").each(function(){ + $(this).append(window.Android.translate(breadcrumbs[lcnt0])); + lcnt0+=1; + }); + $('.breadcrumb').find("li").last().each(function(){ + $(this).append(window.Android.translate(breadcrumbs[lcnt0])); + lcnt0+=1; + }); +} + +/***************************************************************************** + * Convert table names in a string from the PostgreSQL to our SQLite format. + * So, convert "AAAXXX.YYYBBB" to "AAAXXX_YYYBBB" + *****************************************************************************/ +function cleanupTableName(name){ + return name.replace(/(\w+)(\d*)\.(\d*)(\w+)/g,"$1$2_$3$4"); +} + +/***************************************************************************** + * Display an HTML part containing the image produced by Camera or picked from + * the photo library. + *****************************************************************************/ +function loadNewPicture(cid,id,picture){ + $(".tab-pane").each(function(){ + if($(this).is(":visible")) + $(this).find("#value_"+id) + .html('
    '+picture+'
    '); + }); +} + + +/***************************************************************************** + * Create a JSON Object representing the dependency values. + *****************************************************************************/ +function fetchDependencies(obj,cid,changingField){ + var list1=null; + //console.log(cid+" "+JSON.stringify(obj)); + + for(var key in changingField){ + for(var i=0;i'; + tmpStr+=res+ + '
    '; + console.log(tmpStr); + return tmpStr; + } + if(definedSqlTypes[i]["code"]=="geometry"){ + console.log("CID: "+cid+" select type from mm4me_gc where f_table_schema||'_'||f_table_name = (select name from mm4me_tables where id="+cid+") ") + var geoType=getGeometryType("(select replace(name,'.','_') from mm4me_tables where id="+cid+")"); + var viewb=''; + var res=' '; + if(geoType=='POINT' || geoType=='MULTIPOINT' ) + return res+''+ + '

    GPS Informations

    Type
    Coords
    '+ + ''; + else if(geoType=='LINESTRING' || geoType=='MULTILINESTRING' ) + return res+''+ + ''+ + '
    '+viewb+'
    '; + else if(geoType=='POLYGON' || geoType=='MULTIPOLYGON' ) + return res+''+ + ''+ + '
    '+viewb+'
    '; + + } + if(definedSqlTypes[i]["code"]=="tbl_linked"){ + var tmp=obj["value"].split(';'); + //console.log(cleanupTableName('SELECT '+tmp[1]+' FROM '+tmp[2]+' WHERE '+tmp[0]+'='+tblName+'.id')); + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(tmp[3]),[])); + var tmpStr='"; + + } + if(definedSqlTypes[i]["code"]=="link"){ + var strReturn; + if(!View_template) + $.ajax({ + async: false, + method: "GET", + url: './content/view_template.html', + error: function(){ + console.log("Nothing to run after"); + }, + success: function(data){ + console.log("**** \n\n Load View Template"); + View_template=data; + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + if(refs!=""){ + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=data.replace(reg,refs["0"]["tid"]); + } + } + }); + else{ + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=View_template.replace(reg,refs["0"]["tid"]); + + } + //console.log(strReturn); + return strReturn; + } + if(definedSqlTypes[i]["code"]=="ref"){ + var req=obj["value"];//.replace(/^\((\w+)\)$/g,"$1"); + if(req[0]=="(") + req="SELECT * FROM "+req; + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(req),[])); + var tmpStr='"; + + if(obj["dependencies"]) + try{ + var lobj={}; + lobj[obj["id"]]={"dep":JSON.parse(obj["dependencies"])}; + for(var jj=0;jj=0){ + return ''; + } + if(definedSqlTypes[i]["code"]=="html") + return ''; + if(definedSqlTypes[i]["code"]=="text") + return ''; + if(definedSqlTypes[i]["code"]=="boolean") + return ''; + if(definedSqlTypes[i]["code"]=="date" || definedSqlTypes[i]["code"]=="datetime") + return ''; + if(definedSqlTypes[i]["code"]=="float") + return ''; + return definedSqlTypes[i]["code"]; + } + } + return null; +} + +function printOptionalCheckbox(obj,cid){ + if(definedSqlTypes.length==0){ + definedSqlTypes=JSON.parse(window.Android.displayTable("select id,code from mm4me_ftypes where ftype='e' order by name",[])); + } + + var res=' '; + for(var i in definedSqlTypes){ + if(definedSqlTypes[i]["id"]==obj["ftype"]){ + if(definedSqlTypes[i]["code"]=="bytea"){ + var tmpStr=""; + return res; + } + else if(definedSqlTypes[i]["code"]=="geometry"){ + return res + } + } + } + return ''; +} + +var refTypeId=null; +var editPrintedOnce=[]; +/***************************************************************************** + * Create HTML part to display the line containing both the title and the + * corresponding input for a given table's field. + *****************************************************************************/ +function printEditionFields(obj,myRoot,cid,mid){ + var list1=window.Android.displayTable("select * from mm4me_edition_fields where mm4me_edition_fields.edition>0 and eid="+obj["id"]+" order by mm4me_edition_fields.id asc",[]); + if(!editSchema[mid]) + editSchema[mid]={}; + editSchema[mid][obj["id"]]=JSON.parse(list1); + list1=JSON.parse(list1); + myRoot.find(".tab-content").first().append('
    '+obj["description"]+'
    '); + for(var j in list1) + if(list1[j]["edition"]>0) { + myRoot.find(".tab-content").first().children().last().append( + '
    '+ + '
    '+ + ''+ + '
    '+ + '
    '+ + printCurrentType(list1[j],mid)+ + '
    '+ + '
    '); + if(list1[j]["dependencies"]){ + try{ + editPrintedOnce.push(list1[j]["name"]); + console.log("JSON PARSE") + console.log(list1[j]["dependencies"]); + var objJson=JSON.parse(list1[j]["dependencies"]); + console.log("JSON PARSE OK"); + if(!refTypeId) + refTypeId=JSON.parse(window.Android.displayTable("select id from mm4me_ftypes where ftype='e' and code='ref'",[]))[0]["id"]; + console.log(refTypeId); + for(i in objJson){ + if(objJson[i]["myself"]){ + console.log("IS MYSELF!!"); + for(k in objJson[i]["myself"]){ + for(l in objJson[i]["myself"][k]){ + + + if(objJson[i]["myself"][k][l]["dependents"]){ + for(m in objJson[i]["myself"][k][l]["dependents"]){ + for(n in objJson[i]["myself"][k][l]["dependents"][m]){ + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]); + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["label"]); + var lObj={"id": n, "ftype":refTypeId,"value":objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]}; + //if(!myRoot.find('select[name="field_'+list1[j]["id"]+'"]').parent().find('select[name="field_'+n+'"]').length) + myRoot.find('select[name="field_'+list1[j]["id"]+'"]').last().parent().prepend( + '
    '+ + '
    '+ + ''+ + '
    '+ + '
    '+ + printCurrentType(lObj,mid)+ + '
    '+ + '
    '); + console.log(myRoot.find('select[name="field_'+n+'"]')); + console.log(printCurrentType(lObj,mid)); + (function(a,b){ + myRoot.find('select[name="field_'+n+'"]').off('change'); + myRoot.find('select[name="field_'+n+'"]').on('change',function(){ + console.log('select[name="field_'+n+'"]'); + var req=cleanupTableName(a["value"]); + if(a["value"].indexOf("WHERE")<0){ + req=req.replace(/order by/g,"where "+b["tfieldf"]+" "+b["operator"]+" "+(b["operator"]=="like"?"'":"")+$(this).val()+(b["operator"]=="like"?"'":"")+" order by") + } + console.log(req); + var res=JSON.parse(window.Android.displayTable(req,[])); + myRoot.find('select[name="field_'+a["id"]+'"]').html(""); + for(ij in res){ + var tmpStr=' '; + cnt+=1; + } + cnt0+=1; + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + $("select[name=field_"+changingField[j][ckey]["id"]+"]").change(); + }else{ + console.log("DISPLAY ELEMENT IF CINDEX >=0 "); + //console.log(JSON.stringify(changingField[j][ckey])); + console.log('input[name="field_'+ckey+'"],select[name="field_'+ckey+'"],textarea[name="field_'+ckey+'"]'); + console.log($('input[name="field_'+changingField[j][ckey]["id"]+'"],select[name="field_'+changingField[j][ckey]["id"]+'"],textarea[name="field_'+changingField[j][ckey]["id"]+'"]').parent().parent().html()); + var mycKey=changingField[j][ckey]["id"]; + if(cIndex<0) + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().hide(); + else + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().show(); + } + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + } + }catch(e){ + console.log(e); + setTimeout(function() { updateChangingFields(changingFields) }, 500); + } +} + +/***************************************************************************** + * Display the content of a table referencing the current edited table. + *****************************************************************************/ +function listInnerTable(id,vid,name,title,init,prefix,clause,ref){ + var list=JSON.parse(window.Android.displayTable("select mm4me_tables.id as tid,mm4me_tables.name as tname,mm4me_editions.id,mm4me_editions.name from mm4me_editions,mm4me_tables where mm4me_editions.ptid=mm4me_tables.id and mm4me_tables.id="+id+" order by mm4me_editions.step asc",[])); + var cnt=0; + var detectInit=true; + var tid=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + tid=list[i]["tid"]; + if(!allTables[list[i]["tid"]]){ + allTables[list[i]["tid"]]={"id":list[i]["id"],"name":list[i]["tname"],"title":list[i]["name"]}; + detectInit=false; + $("#sub_tableContent_"+id+"").find(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#sub_tableContent_"+id+"").find(".mm4me_edition"),list[i]["id"],list[i]["tid"]); + if(cnt==0){ + try{ + var cid=list[i]["id"]+"_0"; + $("#sub_tableContent_"+id+"").find("ul").first().append('
  • '+window.Android.translate('table')+'
  • '); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+" .require-select").hide(); + printEditionFields(list[i],$("#sub_tableContent_"+id+""),cid,list[i]["tid"]); + }catch(e){ + console.log("**** ERROR ***> "+e); + } + } + var myRoot=$("#sub_tableContent_"+id+""); + myRoot.find('ul').first().find('a').click(function (e) { + e.preventDefault(); + if($(this).parent().hasClass('require-select')) + myRoot.find('.mm4me_edition').show(); + else + myRoot.find('.mm4me_edition').hide(); + $(this).tab('show'); + }) + myRoot.find('ul').first().find('a').first().click(); + myRoot.find('.mm4me_edition').find('ul').find('a').first().click(); + myRoot.find('.swagEditor').summernote(); + myRoot.find(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),tid,editTableReact); + }); + myRoot.find(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),tid,editTableReact); + }); + + } + cnt+=1; + } + + var list=JSON.parse(window.Android.displayTable("SELECT id,name,value,alias,width,class from mm4me_view_fields where vid="+vid+" order by id",[])); + var columns=[]; + var columnNames=[]; + var sqlColumns=""; + var orderColumn="id"; + var orderType="asc"; + for(var i=0;i0){ + orderColumn=list[i]["name"]; + if(list[i]["class"]==1) + orderType="desc"; + } + } + var ccol=getPKey(cleanupTableName(name)); + var req="SELECT "+ccol+" as ogc_fid FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list1=JSON.parse(window.Android.displayTable(req,[])); + req="SELECT "+sqlColumns+" FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list=JSON.parse(window.Android.displayTable(req,[])); + var dataSet = []; + for(var i=0;i'; + cnt+=1; + } + dataSet.push(tmpData); + } + var selected = []; + + //$("#edition_form_table").html('
    '); + var localName="exampleTable_"+id; + ((function(localName,sid){ + + if(!detectInit){ + + var options={ + data: dataSet, + columns: columns, + "scrollX": true, + scrollY: '50vh', + scrollCollapse: true, + select: true, + "rowCallback": function( row, data ) { + if ( $.inArray(data.DT_RowId, selected) !== -1 ) { + $(row).addClass('selected'); + } + } + }; + if(langUrl!=null) + options["language"]={ + url: langUrl + }; + + $('#'+localName).DataTable( options ); + $('#'+localName+' tbody').on('click', 'tr', function () { + var id = this.id; + var index = $.inArray(id, selected); + + if ( index === -1 ) { + selected.push( id ); + } else { + selected.splice( index, 1 ); + } + displayEditForm(sid,$(this).find("input[name=id]").first().val(),true); + var tmp=$(this).hasClass('selected'); + $('#'+localName+' tbody tr').removeClass('selected'); + $(this).toggleClass('selected'); + if(!tmp) + $(this).toggleClass('selected'); + } ); + + }else{ + $('#'+localName).dataTable().fnClearTable(); + if(dataSet.length>0) + $('#'+localName).dataTable().fnAddData(dataSet); + } + $("#sub_tableContent_"+id+"").find("ul").first().find('a').first().click(); + })(localName,tid)); + +} + +var onFormFirstLoad=null; +/***************************************************************************** + * Show the edit form + *****************************************************************************/ +function displayEditForm(cid,selectedId,basic){ + if(basic && !$("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val()){ + if(cid==mtable){ + $("#main_tableContent").find(".require-select").hide(); + $("#main_tableContent").find("ul").first().find("a").first().click(); + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + + }else{ + $("#sub_tableContent_"+cid).find(".require-select").hide(); + $("#sub_tableContent_"+cid).find("ul").first().find("a").first().click(); + } + return; + } + var fields=[] + var sizedFields=[]; + var sizedFieldsAlias=[]; + var notSizedFields=[]; + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + //console.log(JSON.stringify(editSchema[cid][i][j])); + if(editSchema[cid][i][j]["ftype"]=="5"){ + sizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")); + sizedFieldsAlias.push(editSchema[cid][i][j]["id"]); + } + else{ + notSizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + try{ + var tmp=JSON.parse(editSchema[cid][i][j]["dependencies"]); + var sqlReq=""; + var sqlClause=""; + var sqlParams=""; + var sqlParam=0; + var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); + var hasDep=false; + var previousElements=[]; + for(k in tmp) + for(l in tmp[k]){ + //console.log(JSON.stringify(tmp[k][l])); + if(l=="myself"){ + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + //console.log(JSON.stringify(tmp[k][l][m][n])); + if(sqlReq!=""){ + sqlReq+=", "; + sqlParams+=", "; + //sqlClause+=" WHERE "+alphabet[sqlParam-1]+"."+tmp[k][l][m][n]["tfield"]+"="+alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + + } + sqlReq+="("+cleanupTableName(tmp[k][l][m][n]["sql_query"])+") as "+alphabet[sqlParam]; + sqlParams+=alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + sqlParam+=1; + if(tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"])); + for(var o in tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"][o])); + for(var p in tmp[k][l][m][n]["dependents"][o]){ + console.log(tmp[k][l][m][n]["dependents"][o][p]["sql_query"].replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")); + sqlReq+=", ("+cleanupTableName(tmp[k][l][m][n]["dependents"][o][p]["sql_query"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")+") as b"; + sqlParams+=", b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlParam+=2; + sqlReq+=", ("+cleanupTableName(editSchema[cid][i][j]["value"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+" from")+") as c"; + + sqlClause+=" WHERE c."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+"=b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlClause+=" AND b."+tmp[k][l][m][n]["tfield"]+"=a."+tmp[k][l][m][n]["tfield"]; + sqlClause+=" AND c.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + hasDep=true; + } + } + } + } + if(!hasDep){ + var tmpReq=cleanupTableName(editSchema[cid][i][j]["value"]); + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + tmpReq=tmpReq.replace(/from/,","+tmp[k][l][m][n]["tfield"]+" from"); + if(sqlClause=="") + sqlClause+=" WHERE "; + else + sqlClause+=" "+tmp[k][l][m][n]["cond_join"]+" "; + sqlClause+=alphabet[m]+"."+tmp[k][l][m][n]["tfield"]+"=a1."+tmp[k][l][m][n]["tfield"]; + sqlClause+=""; + } + sqlClause+=" AND a1.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + //sqlReq=(tmpReq); + console.log(tmpReq); + sqlReq+=", ("+tmpReq+") as a1"; + } + //console.log(JSON.stringify(tmp[k][l])); + console.log("SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause); + var localQuery="SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause; + var res0=JSON.parse(window.Android.displayTable(localQuery,[])); + console.log(JSON.stringify(res0)); + for(m in res0) + for(n in res0[m]){ + try{ + console.log("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]"); + /**/ + if($('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().val(res0[m][n]).change(); + else{ + for(m0 in tmp[k][l]) + for(n0 in tmp[k][l][m0]){ + /*console.log(n0.indexOf(n)<0); + console.log(n); + console.log(n0); + console.log(res0[m][n]);*/ + if(n0.indexOf(n)>=0){ + console.log("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]"); + if(!$('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]").first().val(res0[m][n]).change(); + } + } + } + }catch(e){ + console.log(e); + } + + } + //if(tmp[k][l]["dependents"]) + }else{ + console.log(JSON.stringify(tmp[k][l])); + if(tmp[k][l]["tfield"]=="none"){ + var isNull=JSON.parse(window.Android.displayTable("SELECT CASE WHEN "+l+" is null THEN 1 ELSE 0 END as p FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId,[])); + console.log(JSON.stringify(isNull)); + if(isNull[0]["p"]=="0"){ + console.log(JSON.stringify(isNull)); + console.log("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]"); + console.log(k+""); + $('.mm4me_edition').find("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]").first().val(k+"").change(); + } + } + + } + } + + //if(sqlReq) + }catch(e){ + console.log(e); + } + } + if(editSchema[cid][i][j]["name"].indexOf("unamed")<0) + fields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + } + } + var ccol=getPKey(cleanupTableName(allTables[cid].name)); + /* $("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val() */ + var editValues; + if(sizedFields.length>0){ + for(var i=0;i 1000000 and "+ccol+"="+selectedId,[])); + //console.log(JSON.stringify(hasElement[0])); + if(hasElement[0]["cnt"]!="0"){ + var nbIteration=parseInt(hasElement[0]["len"])/1000000; + var zfields=[] + var len=0; + var query=""; + var len1=parseInt(hasElement[0]["len"]); + for(var j=0;j0){ + $("#field_"+j+"_map").show(); + $("#field_"+j+"_map").off('click'); + $("#field_"+j+"_map").on('click',function(){ + console.log("Display table with a selected feature"); + console.log($(this).prev().val()); + showElementOnMap($(this).prev().val()); + }); + } + if($(".btn_field_"+j+"_lat").length>0){ + //alert(editValues[i][j]); + $(".btn_field_"+j+"_lat").html(editValues[i][j]); + $(".btn_field_"+j+"_long").html(""); + $(".btn_field_"+j+"_source").html(""); + $("input[name='field_"+j+"']").val("POINT"+editValues[i][j]); + + } + } + //$(".swagEditor").summernote(); + } + } + + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + if(editSchema[cid][i][j]["name"].indexOf("unamed")>=0){ + if(editSchema[cid][i][j]["ftype"]==6){ + var tmp=editSchema[cid][i][j]["value"].split(';'); + var list=JSON.parse(window.Android.displayTable("select "+tmp[1]+" from "+cleanupTableName(tmp[2])+" where "+tmp[0]+"=(SELECT id from "+cleanupTableName(tblName)+" WHERE ogc_fid="+selectedId+")",[])); + editValues["0"][editSchema[cid][i][j]["id"]]=list; + for(var k in list){ + for(var l in list[k]){ + $('.mm4me_edition').find("select[name=field_"+editSchema[cid][i][j]["id"]+"]").find('option').each(function(){ + if($(this).val()==list[k][l]) + $(this).prop("selected",true); + }); + } + } + } + } + } + } + + if(cid==mtable){ + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+editValues["0"]["local_id"]+'
  • '); + } + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().show(); + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().find("a").first().click(); + + if(toRunOnLoad[cid]) + for(var i=0;i=0 order by mm4me_editions.step asc",[]); + if(MM4ME_DEBUG) + console.log(list); + list=JSON.parse(list); + if(MM4ME_DEBUG) + console.log((!allTables[tblId])); + if(!allTables[tblId]){ + allTables[tblId]={"id":id,"name":name,"title":title}; + } + mtable=tblId; + + $(".mm4me_edition").find("ul").first().html(""); + $(".mm4me_edition").find(".well").first().html(""); + var cnt=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + var cid=(i==0?list[i]["id"]+"_0":list[i]["id"]); + $(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#edition_form_edit"),cid,mainTable[id]); + } + if(list.length==1) + $(".mm4me_edition").find("ul").first().hide(); + + var aCnt=0; + $('.mm4me_edition').find('ul').first().find('a').each(function () { + if(aCnt>0) + $(this).parent().addClass('require-select'); + aCnt+=1; + }); + $(".require-select").hide(); + $('.mm4me_listing').find('ul').first().find('a').first().click(); + $('.mm4me_edition').find('ul').find('a').first().click(); + $('.swagEditor').summernote(); + $(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+window.Android.translate('edit')+'
  • '); + $(".breadcrumb").append('
  • '+tblTitle+'
  • '); + + setTimeout(function() { updateChangingFields(changingFields) }, 1500); + + /*for(var i=0;i'; + else + cStr+=changingField[j][ckey]["values"][cIndex][i][lkey]+''; + cnt+=1; + } + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + }*/ + $('.mm4me_listing').show(); + $('.mm4me_content').hide(); + +} + +/***************************************************************************** + * In case no server is configured + *****************************************************************************/ +function displayNoListing(){ + $.ajax({ + method: "GET", + url: 'content/nolisting.html', + success: function(data){ + if(MM4ME_DEBUG) + console.log('Display warning message on the UI !'); + $(".mm4me_content").html(data); + $(".mm4me_content").find(".pannel-body").find("p").first().html(window.Android.translate("mm_no_db_found")); + }, + error: function(){ + window.Android.showToast("error !"); + } + }); +} + + +/***************************************************************************** + * Authenticate a user + *****************************************************************************/ +function authenticate(url,login,passwd,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogIn&DataInputs=login="+login+";password="+passwd+"&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG) + console.log(data); + if(func){ + console.log("Call func!") + func(); + } + }, + error: function(){ + if(func1){ + func1(); + } + else{ + disconnect(url); + if(MM4ME_DEBUG) + console.log("unable to login!"); + var hasBeenShown=false; + var xml=arguments[0].responseText; + $(xml).find("ows\\:ExceptionText").each(function(){ + window.Android.showToast($(this).text()); + hasBeenShown=true; + }); + if(!hasBeenShown){ + window.Android.showToast(JSON.stringify(arguments)); + } + } + + } + }); +} + +/***************************************************************************** + * Disconnect a user + *****************************************************************************/ +function disconnect(url,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogOut&DataInputs=&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG){ + console.log(data); + console.log("** Your are no more connected!"); + } + if(func){ + func(); + } + }, + error: function(){ + if(MM4ME_DEBUG){ + console.log(curl); + console.log("unable to disconnect!"); + } + if(func1){ + func1(); + } + } + }); +} + +var geometries={"line":{"geom":null,"constructor": "ol.geom.Linestring"},"polygon":{"geom":null,"constructor":"ol.geom.Polygon","cline":null}}; +var stopTracking=false; +var currentGeometry="line"; +var currentGeometryField="none"; +var map=null; +var vectorLayer,vectorLayer1; +var position; + +/***************************************************************************** + * Track modification of the GPS location + *****************************************************************************/ +function trackCurrentGPSPosition(){ + console.log("## trackCurrentGPSPosition"); + updateCurrentMapLocation(); + + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(tmp0[0]==tmp[0] && tmp0[1]==tmp[1]){ + console.log(" !!!!!!!! Same position!!!!!!!! "); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + return; + } + + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); +} + +var modalCallback=null; + +function trackStepCurrentGPSPosition(){ + console.log("## trackStepCurrentGPSPosition"); + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!stopTracking) + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); +} + +function addCurrentLocation(){ + console.log("addCurrentLocation!!!!"); + if(geometries["origin"]==null) + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + else{ + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + + } +} + +function trackStepByStepPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + } +} +/***************************************************************************** + * Start tracking GPS location + *****************************************************************************/ +function trackGPSPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + } +} + +var hasAddedElement=0; +var myVectorLayer=null; +function addSelectedElement(wktString){ + var features=[]; + try{ + var format = new ol.format.WKT(); + features.push( + format.readFeature(wktString, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }) + ); + }catch(e){ + console.log("Unable to parse WKT ?!"+e) + } + if(!vectorLayer1){ + myVectorLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: features + }), + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: "#3333aa", + width: 1.4 + }) + }) + }); + map.addLayer(myVectorLayer); + } + else{ + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures(features); + } + console.log(vectorLayer1.getSource().getExtent()); + map.updateSize(); + map.getView().fit(vectorLayer1.getSource().getExtent(),map.getSize()); + hasAddedElement=1; +} + +/***************************************************************************** + * Show selected feature on map + *****************************************************************************/ + var addSelectedIndex=0; + var mySelectedElement=null; +function showElementOnMap(wktString){ + mySelectedElement=wktString; + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().hide(); + if(addSelectedIndex==0){ + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + } + addSelectedElement(mySelectedElement); + $("#myModal").find("h4").html(' '+window.Android.translate("view_feature")); + addSelectedIndex++; + }; + //$("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + console.log("OK "); + $('#myModal').modal(); + console.log("OK "); + vectorLayer1.getSource().clear(); + console.log("OK "); + updateCurrentMapLocation(); + console.log("OK "); + addSelectedElement(wktString); + console.log("OK "); + } + + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); +} + + +/***************************************************************************** + * Store the current GPS location in the edit form + *****************************************************************************/ +function requireGPSPosition(elem){ + var position=JSON.parse(window.Android.getGPS()); + //elem=$("#"+elem); + if(position.lat){ + $(".btn_"+elem+"_lat").html(position.lat); + $(".btn_"+elem+"_long").html(position.lon); + $(".btn_"+elem+"_source").html(position.source); + $("input[name='"+elem+"']").val("POINT("+position.lon+" "+position.lat+")"); + }else{ + + } + console.log(JSON.stringify(position)); +} + +/***************************************************************************** + * Ajax setup to ensure seting "withCredentials" to true + *****************************************************************************/ +function ajaxSetup(){ + $.ajaxSetup({ + xhrFields: { + withCredentials: true + } + }); +} + +/***************************************************************************** + * Get the current Network and GPS availability + *****************************************************************************/ +function getCurrentStatus(){ + var tmp=JSON.parse(window.Android.getGNStatus()); + tmp["gps"]=JSON.parse(tmp["gps"]); + updateStatus(tmp['gps'],tmp['net']); +} + +/***************************************************************************** + * Display the status icons + *****************************************************************************/ +function addStatusControl(){ + $('.breadcrumb').append(' /'+ + ''+ + ''+ + ''+ + ''+ + ''); +} + +/***************************************************************************** + * Initialize the map and show the current GPS location + *****************************************************************************/ +var localTiles,localTiles0; +var tileLayers=[]; +function initMapToLocation(){ + if(map) + return; + var osmSource = new ol.source.OSM(); + + var otherLayers=[]; + var otherLayersSwitcher=""; + try{ + var BasesLayersStr=window.Android.getBaseLayers(); + console.log(BasesLayersStr); + var BasesLayers=JSON.parse(BasesLayersStr); + for(var i=0;i=4) + map.getLayers().item(otherLayers[i]["index"]).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity_"+i).show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(!otherLayers[i]["index"]){ + var myTileLayer=new ol.layer.Tile({source: otherLayers[i]["olLayer"]}); + var layers = map.getLayers(); + layers.insertAt(tileLayers.length+1,myTileLayer); + otherLayers[i]["index"]=tileLayers.length+1; + tileLayers.push(myTileLayer); + if(localTileIndex>0) + localTileIndex+=1; + } + else + map.getLayers().item(otherLayers[i]["index"]).setVisible(true); + } + }); + $(".map").parent().find("input[type=range]").last().on('change',function(){ + console.log("change to "+$(this).val()); + map.getLayers().item(otherLayers[i]["index"]).setOpacity($(this).val()/100); + }); + + })(i); + $(".map").parent().find("input[type=checkbox]").parent().off('click'); + $(".map").parent().find("input[type=checkbox]").parent().on('click',function(){ + var tmp=$(this).find("input[type=checkbox]"); + if(tmp.is(":checked")){ + $(this).find("input[type=checkbox]").prop("checked",false).change(); + $(this).addClass("select"); + } + else{ + $(this).find("input[type=checkbox]").prop("checked",true).change(); + $(this).removeClass("select"); + } + }); + + },1); + })(i) + } + } + map = new ol.Map({ + layers: layers, + target: 'map', + controls: ol.control.defaults({ + attributionOptions: /** @type {olx.control.AttributionOptions} */ ({ + collapsible: true + }) + }), + view: new ol.View({ + center: ol.proj.transform([0,0], 'EPSG:4326', 'EPSG:3857'), + zoom: 14, + maxZoom: 22, + minZoom: 1 + }) + }); + + var bstyle = function (feature, resolution) { + + /*var iconFont = 'glyphicon'; + var iconFontText = '\e062';*/ + //var iconFont = 'glyphicon'; + var iconFont = 'Glyphicons Halflings'; + var iconFontText = '\ue062'; + var iconSize = 24; + var opacity=0.4; + var col = 'rgba(0,255,0,0.6)'; + if(feature.get("source")=="GPS") + col = 'rgba(41,136,54,0.5)';//#298836 + else if(feature.get("source")=="Network"){ + col = 'rgba(91,176,75,0.4)';//#5bb04b + iconSize = 32; + opacity=0.2; + } + else if(feature.get("source")=="other"){ + col='rgba(129,208,113,0.5)';//#81d071 + iconSize = 36; + opacity=0.2; + } + else + col='rgba(166,63,39,0.5)'; //#a63f27 //"#EE0000"; + var styles = []; + + var styleIcon = new ol.style.Style({ + text: new ol.style.Text({ + font: 'Normal ' + iconSize + 'px ' + iconFont, + text: iconFontText, + fill: new ol.style.Fill({ color: col }) + }) + }); + styles.push(styleIcon); + + //console.log(feature.get("type")); + return styles; + /*return function (feature, resolution) { + styles.styleIcon.getText().setText(feature.get("iconCode")); + return styles; + };*/ + }; + position=JSON.parse(window.Android.getFullGPS()); + //console.log(JSON.stringify(position)); + if(position.length==0){ + position=[{lon:3.5,lat:43.5,source:"none"}]; + } + var iconFeatures = []; + for(var i=0;i=4) + map.getLayers().item(localTileIndex).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity").show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(map.getLayers().getLength()0.05){ + map.getView().setRotation(-direction); + oldBearer=direction; + } + }else{ + window.Android.stopReportDirection(); + } +} diff --git a/app/src/main/assets/scripts/gene6.js b/app/src/main/assets/scripts/gene6.js new file mode 100644 index 0000000..6500fa0 --- /dev/null +++ b/app/src/main/assets/scripts/gene6.js @@ -0,0 +1,2461 @@ +var MM4ME_DEBUG=true; +var EDITION_TYPE_FILE=5; +var mainTable={}; +var mtable=null; +var refTables={}; +var tblId=null; +var allTables={}; +var editSchema={}; +var referenceIds={}; +var lastEdition={}; +var tblName=null; +var toRunOnLoad={}; +var valuesOnLoad=[]; +var definedSqlTypes=[]; +var changingFields=[]; +var lang=window.Android.getLang(); +var langUrl=null; +if(lang=="fr"){ + langUrl="file:///android_asset/localisation/French.json"; +} + + + + +function loadWelcome7(){ + window.Android.startWelcomeScreen("ARArea"); +} + +/***************************************************************************** + * Update status icon color depending on network and GPS availability + *****************************************************************************/ +function updateStatus(gps,internet){ + if(internet){ + $('.glyphicon-signal').css('color','#00EE00'); + }else + $('.glyphicon-signal').css('color','#EE0000'); + $('#gpsMenu1').next().html(""); + if(gps.length==0){ + $('#gpsMenu1').css('color','#EE0000'); + $('#gpsMenu1').next().html('
  • '+window.Android.translate("no_gps")+'
  • '); + }else{ + $('#gpsMenu1').css('color','#00EE00'); + for(var i=0;i '+gps[i].source+''); + } +} + +/***************************************************************************** + * List all available table for a given theme + *****************************************************************************/ +function fetchTableForTheme(obj,id){ + var hasTable=false; + var tables=JSON.parse(window.Android.displayTable( + "SELECT mm4me_tables.id as tid,mm4me_views.id as id,"+ + "mm4me_tables.name as name, "+ + "mm4me_tables.description, "+ + "mm4me_views.name as title "+ + " from mm4me_tables,mm4me_views"+ + " where mm4me_tables.id=mm4me_views.ptid "+ + "and mm4me_views.visible "+ + "and mm4me_views.id in (select vid from mm4me_views_themes where tid="+id+")",[])); + for(var i=0;i0; +} + +/***************************************************************************** + * Create a JSON Object containing the themes hierarchy + *****************************************************************************/ +function fetchThemes(obj,id){ + var cthemes=JSON.parse(window.Android.displayTable("SELECT id,name FROM mm4me_themes where pid = "+id,[])); + for(var i=0;i'); + $('#tree').treeview({ + data: allThemes, + onNodeSelected: function(event, data) { + if(data["myId"]){ + try{ + func(data["myId"],data["myName"],data["myTitle"],true); + }catch(e){ + console.log(JSON.stringify(data)); + console.log(e); + window.Android.showToast(e); + //exit(); + } + } + else{ + $('#tree').treeview('toggleNodeExpanded',[$("#tree").treeview('getSelected')[0], { silent: true }]); + $('#tree').treeview('toggleNodeSelected',[$("#tree").treeview('getSelected')[0], { silent: true }]); + //console.log(JSON.stringify(data)); + //$(this).open(); + } + }, + showTags: true + }); + var list=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.id=mm4me_views.ptid and mm4me_views.visible",[])); + var tableList=list; + var total=0; + contents=[]; + for(var i in list){ + mainTable[list[i]["id"]]=list[i]["tid"]; + } + } + catch(e){ + console.log(e); + displayNoListing(); + } +} + +/***************************************************************************** + * Update the breadcrumbs text to translated string + *****************************************************************************/ +function updateBreadcrumbs(breadcrumbs){ + //var breadcrumbs=["home","view"]; + var lcnt0=0; + $('.breadcrumb').find("a").each(function(){ + $(this).append(window.Android.translate(breadcrumbs[lcnt0])); + lcnt0+=1; + }); + $('.breadcrumb').find("li").last().each(function(){ + $(this).append(window.Android.translate(breadcrumbs[lcnt0])); + lcnt0+=1; + }); +} + +/***************************************************************************** + * Convert table names in a string from the PostgreSQL to our SQLite format. + * So, convert "AAAXXX.YYYBBB" to "AAAXXX_YYYBBB" + *****************************************************************************/ +function cleanupTableName(name){ + return name.replace(/(\w+)(\d*)\.(\d*)(\w+)/g,"$1$2_$3$4"); +} + +/***************************************************************************** + * Display an HTML part containing the image produced by Camera or picked from + * the photo library. + *****************************************************************************/ +function loadNewPicture(cid,id,picture){ + $(".tab-pane").each(function(){ + if($(this).is(":visible")) + $(this).find("#value_"+id) + .html('
    '+picture+'
    '); + }); +} + + +/***************************************************************************** + * Create a JSON Object representing the dependency values. + *****************************************************************************/ +function fetchDependencies(obj,cid,changingField){ + var list1=null; + //console.log(cid+" "+JSON.stringify(obj)); + + for(var key in changingField){ + for(var i=0;i'; + tmpStr+=res+ + '
    '; + console.log(tmpStr); + return tmpStr; + } + if(definedSqlTypes[i]["code"]=="geometry"){ + console.log("CID: "+cid+" select type from mm4me_gc where f_table_schema||'_'||f_table_name = (select name from mm4me_tables where id="+cid+") ") + var geoType=getGeometryType("(select replace(name,'.','_') from mm4me_tables where id="+cid+")"); + var viewb=''; + var res=' '; + if(geoType=='POINT' || geoType=='MULTIPOINT' ) + return res+''+ + '

    GPS Informations

    Type
    Coords
    '+ + ''; + else if(geoType=='LINESTRING' || geoType=='MULTILINESTRING' ) + return res+''+ + ''+ + '
    '+viewb+'
    '; + else if(geoType=='POLYGON' || geoType=='MULTIPOLYGON' ) + return res+''+ + ''+ + '
    '+viewb+'
    '; + + } + if(definedSqlTypes[i]["code"]=="tbl_linked"){ + var tmp=obj["value"].split(';'); + //console.log(cleanupTableName('SELECT '+tmp[1]+' FROM '+tmp[2]+' WHERE '+tmp[0]+'='+tblName+'.id')); + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(tmp[3]),[])); + var tmpStr='"; + + } + if(definedSqlTypes[i]["code"]=="link"){ + var strReturn; + if(!View_template) + $.ajax({ + async: false, + method: "GET", + url: './content/view_template.html', + error: function(){ + console.log("Nothing to run after"); + }, + success: function(data){ + console.log("**** \n\n Load View Template"); + View_template=data; + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + if(refs!=""){ + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=data.replace(reg,refs["0"]["tid"]); + } + } + }); + else{ + var tmp=obj["value"].split(";"); + var refs=JSON.parse(window.Android.displayTable("SELECT mm4me_tables.id as tid,mm4me_views.id as id,mm4me_tables.name as name,mm4me_tables.description,mm4me_views.name as title from mm4me_tables,mm4me_views where mm4me_tables.name=\""+tmp[1]+"\" and mm4me_tables.id=mm4me_views.ptid",[])); + var reg=new RegExp("\\[id\\]","g"); + if(!valuesOnLoad[cid]) + valuesOnLoad[cid]=[]; + valuesOnLoad[cid].push(refs); + if(!toRunOnLoad[cid]) + toRunOnLoad[cid]=[]; + toRunOnLoad[cid].push(function(){ + prefix="_"+arguments[0]["0"]["id"]; + //if(!mainTable[arguments[0]["0"]["id"]]) + //mainTable[arguments[0]["0"]["id"]]=arguments[0]["0"]["tid"]; + //console.log(arguments[0]["0"]["tid"]+" "+arguments[0]["0"]["name"]+" "+arguments[0]["0"]["title"]); + listInnerTable(arguments[0]["0"]["tid"],arguments[0]["0"]["id"],arguments[0]["0"]["name"],arguments[0]["0"]["title"],false,prefix,tmp[0]+"="+arguments[1]["local_id"]); + }); + refTables[refs["0"]["tid"]]={"oid":cid,"col":tmp[0],"vid":refs["0"]["id"],"name":refs["0"]["table"],"name":refs["0"]["title"]}; + strReturn=View_template.replace(reg,refs["0"]["tid"]); + + } + //console.log(strReturn); + return strReturn; + } + if(definedSqlTypes[i]["code"]=="ref"){ + var req=obj["value"];//.replace(/^\((\w+)\)$/g,"$1"); + if(req[0]=="(") + req="SELECT * FROM "+req; + var refs=JSON.parse(window.Android.displayTable(cleanupTableName(req),[])); + var tmpStr='"; + + if(obj["dependencies"]) + try{ + var lobj={}; + lobj[obj["id"]]={"dep":JSON.parse(obj["dependencies"])}; + for(var jj=0;jj=0){ + return ''; + } + if(definedSqlTypes[i]["code"]=="html") + return ''; + if(definedSqlTypes[i]["code"]=="text") + return ''; + if(definedSqlTypes[i]["code"]=="boolean") + return ''; + if(definedSqlTypes[i]["code"]=="date" || definedSqlTypes[i]["code"]=="datetime") + return ''; + if(definedSqlTypes[i]["code"]=="float") + return ''; + return definedSqlTypes[i]["code"]; + } + } + return null; +} + +function printOptionalCheckbox(obj,cid){ + if(definedSqlTypes.length==0){ + definedSqlTypes=JSON.parse(window.Android.displayTable("select id,code from mm4me_ftypes where ftype='e' order by name",[])); + } + + var res=' '; + for(var i in definedSqlTypes){ + if(definedSqlTypes[i]["id"]==obj["ftype"]){ + if(definedSqlTypes[i]["code"]=="bytea"){ + var tmpStr=""; + return res; + } + else if(definedSqlTypes[i]["code"]=="geometry"){ + return res + } + } + } + return ''; +} + +var refTypeId=null; +var editPrintedOnce=[]; +/***************************************************************************** + * Create HTML part to display the line containing both the title and the + * corresponding input for a given table's field. + *****************************************************************************/ +function printEditionFields(obj,myRoot,cid,mid){ + var list1=window.Android.displayTable("select * from mm4me_edition_fields where mm4me_edition_fields.edition>0 and eid="+obj["id"]+" order by mm4me_edition_fields.id asc",[]); + if(!editSchema[mid]) + editSchema[mid]={}; + editSchema[mid][obj["id"]]=JSON.parse(list1); + list1=JSON.parse(list1); + myRoot.find(".tab-content").first().append('
    '+obj["description"]+'
    '); + for(var j in list1) + if(list1[j]["edition"]>0) { + myRoot.find(".tab-content").first().children().last().append( + '
    '+ + '
    '+ + ''+ + '
    '+ + '
    '+ + printCurrentType(list1[j],mid)+ + '
    '+ + '
    '); + if(list1[j]["dependencies"]){ + try{ + editPrintedOnce.push(list1[j]["name"]); + console.log("JSON PARSE") + console.log(list1[j]["dependencies"]); + var objJson=JSON.parse(list1[j]["dependencies"]); + console.log("JSON PARSE OK"); + if(!refTypeId) + refTypeId=JSON.parse(window.Android.displayTable("select id from mm4me_ftypes where ftype='e' and code='ref'",[]))[0]["id"]; + console.log(refTypeId); + for(i in objJson){ + if(objJson[i]["myself"]){ + console.log("IS MYSELF!!"); + for(k in objJson[i]["myself"]){ + for(l in objJson[i]["myself"][k]){ + + + if(objJson[i]["myself"][k][l]["dependents"]){ + for(m in objJson[i]["myself"][k][l]["dependents"]){ + for(n in objJson[i]["myself"][k][l]["dependents"][m]){ + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]); + console.log(objJson[i]["myself"][k][l]["dependents"][m][n]["label"]); + var lObj={"id": n, "ftype":refTypeId,"value":objJson[i]["myself"][k][l]["dependents"][m][n]["sql_query"]}; + //if(!myRoot.find('select[name="field_'+list1[j]["id"]+'"]').parent().find('select[name="field_'+n+'"]').length) + myRoot.find('select[name="field_'+list1[j]["id"]+'"]').last().parent().prepend( + '
    '+ + '
    '+ + ''+ + '
    '+ + '
    '+ + printCurrentType(lObj,mid)+ + '
    '+ + '
    '); + console.log(myRoot.find('select[name="field_'+n+'"]')); + console.log(printCurrentType(lObj,mid)); + (function(a,b){ + myRoot.find('select[name="field_'+n+'"]').off('change'); + myRoot.find('select[name="field_'+n+'"]').on('change',function(){ + console.log('select[name="field_'+n+'"]'); + var req=cleanupTableName(a["value"]); + if(a["value"].indexOf("WHERE")<0){ + req=req.replace(/order by/g,"where "+b["tfieldf"]+" "+b["operator"]+" "+(b["operator"]=="like"?"'":"")+$(this).val()+(b["operator"]=="like"?"'":"")+" order by") + } + console.log(req); + var res=JSON.parse(window.Android.displayTable(req,[])); + myRoot.find('select[name="field_'+a["id"]+'"]').html(""); + for(ij in res){ + var tmpStr=' '; + cnt+=1; + } + cnt0+=1; + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + $("select[name=field_"+changingField[j][ckey]["id"]+"]").change(); + }else{ + console.log("DISPLAY ELEMENT IF CINDEX >=0 "); + //console.log(JSON.stringify(changingField[j][ckey])); + console.log('input[name="field_'+ckey+'"],select[name="field_'+ckey+'"],textarea[name="field_'+ckey+'"]'); + console.log($('input[name="field_'+changingField[j][ckey]["id"]+'"],select[name="field_'+changingField[j][ckey]["id"]+'"],textarea[name="field_'+changingField[j][ckey]["id"]+'"]').parent().parent().html()); + var mycKey=changingField[j][ckey]["id"]; + if(cIndex<0) + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().hide(); + else + $('input[name="field_'+mycKey+'"],select[name="field_'+mycKey+'"],textarea[name="field_'+mycKey+'"]').parent().parent().show(); + } + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + } + }catch(e){ + console.log(e); + setTimeout(function() { updateChangingFields(changingFields) }, 500); + } +} + +/***************************************************************************** + * Display the content of a table referencing the current edited table. + *****************************************************************************/ +function listInnerTable(id,vid,name,title,init,prefix,clause,ref){ + var list=JSON.parse(window.Android.displayTable("select mm4me_tables.id as tid,mm4me_tables.name as tname,mm4me_editions.id,mm4me_editions.name from mm4me_editions,mm4me_tables where mm4me_editions.ptid=mm4me_tables.id and mm4me_tables.id="+id+" order by mm4me_editions.step asc",[])); + var cnt=0; + var detectInit=true; + var tid=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + tid=list[i]["tid"]; + if(!allTables[list[i]["tid"]]){ + allTables[list[i]["tid"]]={"id":list[i]["id"],"name":list[i]["tname"],"title":list[i]["name"]}; + detectInit=false; + $("#sub_tableContent_"+id+"").find(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#sub_tableContent_"+id+"").find(".mm4me_edition"),list[i]["id"],list[i]["tid"]); + if(cnt==0){ + try{ + var cid=list[i]["id"]+"_0"; + $("#sub_tableContent_"+id+"").find("ul").first().append('
  • '+window.Android.translate('table')+'
  • '); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+"").find("ul").first().append(''); + $("#sub_tableContent_"+id+" .require-select").hide(); + printEditionFields(list[i],$("#sub_tableContent_"+id+""),cid,list[i]["tid"]); + }catch(e){ + console.log("**** ERROR ***> "+e); + } + } + var myRoot=$("#sub_tableContent_"+id+""); + myRoot.find('ul').first().find('a').click(function (e) { + e.preventDefault(); + if($(this).parent().hasClass('require-select')) + myRoot.find('.mm4me_edition').show(); + else + myRoot.find('.mm4me_edition').hide(); + $(this).tab('show'); + }) + myRoot.find('ul').first().find('a').first().click(); + myRoot.find('.mm4me_edition').find('ul').find('a').first().click(); + myRoot.find('.swagEditor').summernote(); + myRoot.find(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),tid,editTableReact); + }); + myRoot.find(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),tid,editTableReact); + }); + + } + cnt+=1; + } + + var list=JSON.parse(window.Android.displayTable("SELECT id,name,value,alias,width,class from mm4me_view_fields where vid="+vid+" order by id",[])); + var columns=[]; + var columnNames=[]; + var sqlColumns=""; + var orderColumn="id"; + var orderType="asc"; + for(var i=0;i0){ + orderColumn=list[i]["name"]; + if(list[i]["class"]==1) + orderType="desc"; + } + } + var ccol=getPKey(cleanupTableName(name)); + var req="SELECT "+ccol+" as ogc_fid FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list1=JSON.parse(window.Android.displayTable(req,[])); + req="SELECT "+sqlColumns+" FROM "+cleanupTableName(name)+" WHERE "+clause+" ORDER BY "+cleanupTableName(name)+"."+orderColumn+" "+orderType; + var list=JSON.parse(window.Android.displayTable(req,[])); + var dataSet = []; + for(var i=0;i'; + cnt+=1; + } + dataSet.push(tmpData); + } + var selected = []; + + //$("#edition_form_table").html('
    '); + var localName="exampleTable_"+id; + ((function(localName,sid){ + + if(!detectInit){ + + var options={ + data: dataSet, + columns: columns, + "scrollX": true, + scrollY: '50vh', + scrollCollapse: true, + select: true, + "rowCallback": function( row, data ) { + if ( $.inArray(data.DT_RowId, selected) !== -1 ) { + $(row).addClass('selected'); + } + } + }; + if(langUrl!=null) + options["language"]={ + url: langUrl + }; + + $('#'+localName).DataTable( options ); + $('#'+localName+' tbody').on('click', 'tr', function () { + var id = this.id; + var index = $.inArray(id, selected); + + if ( index === -1 ) { + selected.push( id ); + } else { + selected.splice( index, 1 ); + } + displayEditForm(sid,$(this).find("input[name=id]").first().val(),true); + var tmp=$(this).hasClass('selected'); + $('#'+localName+' tbody tr').removeClass('selected'); + $(this).toggleClass('selected'); + if(!tmp) + $(this).toggleClass('selected'); + } ); + + }else{ + $('#'+localName).dataTable().fnClearTable(); + if(dataSet.length>0) + $('#'+localName).dataTable().fnAddData(dataSet); + } + $("#sub_tableContent_"+id+"").find("ul").first().find('a').first().click(); + })(localName,tid)); + +} + +var onFormFirstLoad=null; +/***************************************************************************** + * Show the edit form + *****************************************************************************/ +function displayEditForm(cid,selectedId,basic){ + if(basic && !$("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val()){ + if(cid==mtable){ + $("#main_tableContent").find(".require-select").hide(); + $("#main_tableContent").find("ul").first().find("a").first().click(); + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + + }else{ + $("#sub_tableContent_"+cid).find(".require-select").hide(); + $("#sub_tableContent_"+cid).find("ul").first().find("a").first().click(); + } + return; + } + var fields=[] + var sizedFields=[]; + var sizedFieldsAlias=[]; + var notSizedFields=[]; + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + //console.log(JSON.stringify(editSchema[cid][i][j])); + if(editSchema[cid][i][j]["ftype"]=="5"){ + sizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")); + sizedFieldsAlias.push(editSchema[cid][i][j]["id"]); + } + else{ + notSizedFields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + try{ + var tmp=JSON.parse(editSchema[cid][i][j]["dependencies"]); + var sqlReq=""; + var sqlClause=""; + var sqlParams=""; + var sqlParam=0; + var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); + var hasDep=false; + var previousElements=[]; + for(k in tmp) + for(l in tmp[k]){ + //console.log(JSON.stringify(tmp[k][l])); + if(l=="myself"){ + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + //console.log(JSON.stringify(tmp[k][l][m][n])); + if(sqlReq!=""){ + sqlReq+=", "; + sqlParams+=", "; + //sqlClause+=" WHERE "+alphabet[sqlParam-1]+"."+tmp[k][l][m][n]["tfield"]+"="+alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + + } + sqlReq+="("+cleanupTableName(tmp[k][l][m][n]["sql_query"])+") as "+alphabet[sqlParam]; + sqlParams+=alphabet[sqlParam]+"."+tmp[k][l][m][n]["tfield"]; + sqlParam+=1; + if(tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"])); + for(var o in tmp[k][l][m][n]["dependents"]){ + //console.log(JSON.stringify(tmp[k][l][m][n]["dependents"][o])); + for(var p in tmp[k][l][m][n]["dependents"][o]){ + console.log(tmp[k][l][m][n]["dependents"][o][p]["sql_query"].replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")); + sqlReq+=", ("+cleanupTableName(tmp[k][l][m][n]["dependents"][o][p]["sql_query"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfield"]+" from")+") as b"; + sqlParams+=", b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlParam+=2; + sqlReq+=", ("+cleanupTableName(editSchema[cid][i][j]["value"]).replace(/from/,","+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+" from")+") as c"; + + sqlClause+=" WHERE c."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]+"=b."+tmp[k][l][m][n]["dependents"][o][p]["tfieldf"]; + sqlClause+=" AND b."+tmp[k][l][m][n]["tfield"]+"=a."+tmp[k][l][m][n]["tfield"]; + sqlClause+=" AND c.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + hasDep=true; + } + } + } + } + if(!hasDep){ + var tmpReq=cleanupTableName(editSchema[cid][i][j]["value"]); + for(m in tmp[k][l]) + for(n in tmp[k][l][m]){ + tmpReq=tmpReq.replace(/from/,","+tmp[k][l][m][n]["tfield"]+" from"); + if(sqlClause=="") + sqlClause+=" WHERE "; + else + sqlClause+=" "+tmp[k][l][m][n]["cond_join"]+" "; + sqlClause+=alphabet[m]+"."+tmp[k][l][m][n]["tfield"]+"=a1."+tmp[k][l][m][n]["tfield"]; + sqlClause+=""; + } + sqlClause+=" AND a1.id=(SELECT "+editSchema[cid][i][j]["name"]+" FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId+")"; + //sqlReq=(tmpReq); + console.log(tmpReq); + sqlReq+=", ("+tmpReq+") as a1"; + } + //console.log(JSON.stringify(tmp[k][l])); + console.log("SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause); + var localQuery="SELECT "+sqlParams+" FROM "+sqlReq+" "+sqlClause; + var res0=JSON.parse(window.Android.displayTable(localQuery,[])); + console.log(JSON.stringify(res0)); + for(m in res0) + for(n in res0[m]){ + try{ + console.log("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]"); + /**/ + if($('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().val(res0[m][n]).change(); + else{ + for(m0 in tmp[k][l]) + for(n0 in tmp[k][l][m0]){ + /*console.log(n0.indexOf(n)<0); + console.log(n); + console.log(n0); + console.log(res0[m][n]);*/ + if(n0.indexOf(n)>=0){ + console.log("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]"); + if(!$('.mm4me_edition').find("input[name=field_"+n+"],select[name=field_"+n+"],textarea[name=field_"+n+"]").first().length) + $('.mm4me_edition').find("input[name=field_"+n0+"],select[name=field_"+n0+"],textarea[name=field_"+n0+"]").first().val(res0[m][n]).change(); + } + } + } + }catch(e){ + console.log(e); + } + + } + //if(tmp[k][l]["dependents"]) + }else{ + console.log(JSON.stringify(tmp[k][l])); + if(tmp[k][l]["tfield"]=="none"){ + var isNull=JSON.parse(window.Android.displayTable("SELECT CASE WHEN "+l+" is null THEN 1 ELSE 0 END as p FROM "+cleanupTableName(allTables[cid].name)+" where id="+selectedId,[])); + console.log(JSON.stringify(isNull)); + if(isNull[0]["p"]=="0"){ + console.log(JSON.stringify(isNull)); + console.log("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]"); + console.log(k+""); + $('.mm4me_edition').find("input[name=field_"+editSchema[cid][i][j]["id"]+"],select[name=field_"+editSchema[cid][i][j]["id"]+"],textarea[name=field_"+editSchema[cid][i][j]["id"]+"]").first().val(k+"").change(); + } + } + + } + } + + //if(sqlReq) + }catch(e){ + console.log(e); + } + } + if(editSchema[cid][i][j]["name"].indexOf("unamed")<0) + fields.push(editSchema[cid][i][j]["name"].replace(/wkb_geometry/g,"geometry")+" AS \""+editSchema[cid][i][j]["id"]+"\""); + } + } + var ccol=getPKey(cleanupTableName(allTables[cid].name)); + /* $("#exampleTable"+(cid==mtable?"":"_"+cid)).find(".selected").find('input[type=hidden]').first().val() */ + var editValues; + if(sizedFields.length>0){ + for(var i=0;i 1000000 and "+ccol+"="+selectedId,[])); + //console.log(JSON.stringify(hasElement[0])); + if(hasElement[0]["cnt"]!="0"){ + var nbIteration=parseInt(hasElement[0]["len"])/1000000; + var zfields=[] + var len=0; + var query=""; + var len1=parseInt(hasElement[0]["len"]); + for(var j=0;j0){ + $("#field_"+j+"_map").show(); + $("#field_"+j+"_map").off('click'); + $("#field_"+j+"_map").on('click',function(){ + console.log("Display table with a selected feature"); + console.log($(this).prev().val()); + showElementOnMap($(this).prev().val()); + }); + } + if($(".btn_field_"+j+"_lat").length>0){ + //alert(editValues[i][j]); + $(".btn_field_"+j+"_lat").html(editValues[i][j]); + $(".btn_field_"+j+"_long").html(""); + $(".btn_field_"+j+"_source").html(""); + $("input[name='field_"+j+"']").val("POINT"+editValues[i][j]); + + } + } + //$(".swagEditor").summernote(); + } + } + + for(var i in editSchema[cid]){ + for(var j in editSchema[cid][i]){ + if(editSchema[cid][i][j]["name"].indexOf("unamed")>=0){ + if(editSchema[cid][i][j]["ftype"]==6){ + var tmp=editSchema[cid][i][j]["value"].split(';'); + var list=JSON.parse(window.Android.displayTable("select "+tmp[1]+" from "+cleanupTableName(tmp[2])+" where "+tmp[0]+"=(SELECT id from "+cleanupTableName(tblName)+" WHERE ogc_fid="+selectedId+")",[])); + editValues["0"][editSchema[cid][i][j]["id"]]=list; + for(var k in list){ + for(var l in list[k]){ + $('.mm4me_edition').find("select[name=field_"+editSchema[cid][i][j]["id"]+"]").find('option').each(function(){ + if($(this).val()==list[k][l]) + $(this).prop("selected",true); + }); + } + } + } + } + } + } + + if(cid==mtable){ + if($(".breadcrumb").children().length==4) + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+editValues["0"]["local_id"]+'
  • '); + } + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().show(); + ((cid==mtable)?$(".require-select"):$("#sub_tableContent_"+cid).find(".require-select")).first().find("a").first().click(); + + if(toRunOnLoad[cid]) + for(var i=0;i=0 order by mm4me_editions.step asc",[]); + if(MM4ME_DEBUG) + console.log(list); + list=JSON.parse(list); + if(MM4ME_DEBUG) + console.log((!allTables[tblId])); + if(!allTables[tblId]){ + allTables[tblId]={"id":id,"name":name,"title":title}; + } + mtable=tblId; + + $(".mm4me_edition").find("ul").first().html(""); + $(".mm4me_edition").find(".well").first().html(""); + var cnt=0; + for(var i in list){ + lastEdition[list[i]["id"]]=list[i]; + var cid=(i==0?list[i]["id"]+"_0":list[i]["id"]); + $(".mm4me_edition").find("ul").first().append(''); + printEditionFields(list[i],$("#edition_form_edit"),cid,mainTable[id]); + } + if(list.length==1) + $(".mm4me_edition").find("ul").first().hide(); + + var aCnt=0; + $('.mm4me_edition').find('ul').first().find('a').each(function () { + if(aCnt>0) + $(this).parent().addClass('require-select'); + aCnt+=1; + }); + $(".require-select").hide(); + $('.mm4me_listing').find('ul').first().find('a').first().click(); + $('.mm4me_edition').find('ul').find('a').first().click(); + $('.swagEditor').summernote(); + $(".mm-act-add").click(function(){ + runInsertQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".mm-act-save").click(function(){ + runUpdateQuery($(this).parent().parent(),mainTable[id],editOnlyTableReact); + }); + $(".breadcrumb").children().last().remove(); + $(".breadcrumb").append('
  • '+window.Android.translate('edit')+'
  • '); + $(".breadcrumb").append('
  • '+tblTitle+'
  • '); + + setTimeout(function() { updateChangingFields(changingFields) }, 1500); + + /*for(var i=0;i'; + else + cStr+=changingField[j][ckey]["values"][cIndex][i][lkey]+''; + cnt+=1; + } + $("select[name=field_"+changingField[j][ckey]["id"]+"]").append(cStr); + } + if(i==0) + $("select[name=field_"+changingField[j][ckey]["id"]+"]").html(''); + } + } + }; + }; + $("select[name=field_"+key+"]").off('change'); + $("select[name=field_"+key+"]").change(localFunc(changingFields[i][key]["dep"])); + $("select[name=field_"+key+"]").change(); + } + }*/ + $('.mm4me_listing').show(); + $('.mm4me_content').hide(); + +} + +/***************************************************************************** + * In case no server is configured + *****************************************************************************/ +function displayNoListing(){ + $.ajax({ + method: "GET", + url: 'content/nolisting.html', + success: function(data){ + if(MM4ME_DEBUG) + console.log('Display warning message on the UI !'); + $(".mm4me_content").html(data); + $(".mm4me_content").find(".pannel-body").find("p").first().html(window.Android.translate("mm_no_db_found")); + }, + error: function(){ + window.Android.showToast("error !"); + } + }); +} + + +/***************************************************************************** + * Authenticate a user + *****************************************************************************/ +function authenticate(url,login,passwd,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogIn&DataInputs=login="+login+";password="+passwd+"&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG) + console.log(data); + if(func){ + console.log("Call func!") + func(); + } + }, + error: function(){ + if(func1){ + func1(); + } + else{ + disconnect(url); + if(MM4ME_DEBUG) + console.log("unable to login!"); + var hasBeenShown=false; + var xml=arguments[0].responseText; + $(xml).find("ows\\:ExceptionText").each(function(){ + window.Android.showToast($(this).text()); + hasBeenShown=true; + }); + if(!hasBeenShown){ + window.Android.showToast(JSON.stringify(arguments)); + } + } + + } + }); +} + +/***************************************************************************** + * Disconnect a user + *****************************************************************************/ +function disconnect(url,func,func1){ + var curl=url+"?service=WPS&request=Execute&version=1.0.0&Identifier=authenticate.clogOut&DataInputs=&RawDataOutput=Result"; + if(MM4ME_DEBUG) + console.log(curl); + $.ajax({ + method: "GET", + url: curl, + success: function(data){ + if(MM4ME_DEBUG){ + console.log(data); + console.log("** Your are no more connected!"); + } + if(func){ + func(); + } + }, + error: function(){ + if(MM4ME_DEBUG){ + console.log(curl); + console.log("unable to disconnect!"); + } + if(func1){ + func1(); + } + } + }); +} + +var geometries={"line":{"geom":null,"constructor": "ol.geom.Linestring"},"polygon":{"geom":null,"constructor":"ol.geom.Polygon","cline":null}}; +var stopTracking=false; +var currentGeometry="line"; +var currentGeometryField="none"; +var map=null; +var vectorLayer,vectorLayer1; +var position; + +/***************************************************************************** + * Track modification of the GPS location + *****************************************************************************/ +function trackCurrentGPSPosition(){ + console.log("## trackCurrentGPSPosition"); + updateCurrentMapLocation(); + + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(tmp0[0]==tmp[0] && tmp0[1]==tmp[1]){ + console.log(" !!!!!!!! Same position!!!!!!!! "); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + return; + } + + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + if(!stopTracking) + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); +} + +var modalCallback=null; + +function trackStepCurrentGPSPosition(){ + console.log("## trackStepCurrentGPSPosition"); + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!stopTracking) + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); +} + +function addCurrentLocation(){ + console.log("addCurrentLocation!!!!"); + if(geometries["origin"]==null) + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + else{ + var tmp0=geometries["origin"].getCoordinates(); + if(geometries[currentGeometry]["geom"]!=null){ + var tmp1=geometries[currentGeometry]["geom"].getCoordinates(); + tmp0=tmp1[tmp1.length-1]; + } + tmp=ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857'); + console.log(JSON.stringify(tmp0)); + console.log(JSON.stringify(tmp)); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(geometries[currentGeometry]["geom"]==null){ + //geometries[currentGeometry]=new geometries[currentGeometry]["constructor"]([tmp0,tmp]); + if(currentGeometry=="line") + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + else + geometries[currentGeometry]["geom"]=new ol.geom.LineString([tmp0,tmp]); + }else{ + if(currentGeometry=="line"){ + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + tmpCoordinates.push(tmp); + geometries[currentGeometry]["geom"]=new ol.geom.LineString(tmpCoordinates); + } + else{ + if(geometries[currentGeometry]["cline"]==null) + tmpCoordinates=geometries[currentGeometry]["geom"].getCoordinates(); + else + tmpCoordinates=geometries[currentGeometry]["cline"].getCoordinates(); + console.log(JSON.stringify(tmpCoordinates)); + tmpCoordinates.push(tmp); + console.log(JSON.stringify(tmpCoordinates)); + if(tmpCoordinates.length>2){ + geometries[currentGeometry]["cline"]=new ol.geom.LineString(tmpCoordinates); + tmpCoordinates.push(tmpCoordinates[0]); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"]=new ol.geom.Polygon(); + console.log(JSON.stringify(tmpCoordinates)); + geometries[currentGeometry]["geom"].appendLinearRing(new ol.geom.LinearRing(tmpCoordinates)); + console.log(JSON.stringify(tmpCoordinates)); + } + } + + } + try{ + console.log(JSON.stringify(geometries[currentGeometry]["geom"].getCoordinates())); + var feature=new ol.Feature({geometry: geometries[currentGeometry]["geom"],"name":"myFeature"}); + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures([feature]); + var wkt=new ol.format.WKT(); + var tmpGeometry=geometries[currentGeometry]["geom"].clone().transform('EPSG:3857', 'EPSG:4326'); + console.log(wkt.writeGeometry(tmpGeometry)); + $("input[name='"+currentGeometryField+"']").val(wkt.writeGeometry(tmpGeometry)); + }catch(e){ + console.log(e); + } + + } +} + +function trackStepByStepPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=null; + setTimeout(function() { trackStepCurrentGPSPosition();}, 1000); + } +} +/***************************************************************************** + * Start tracking GPS location + *****************************************************************************/ +function trackGPSPosition(elem,ltype){ + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().show(); + if(myVectorLayer) + myVectorLayer.getSource().clear(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + $("#myModal").find("h4").html(' '+window.Android.translate("gps_track")); + $("#myModal").find(".modal-footer").html( + ''+ + '' + ); + }; + currentGeometry=ltype; + currentGeometryField=elem; + geometries[ltype]["geom"]=null; + geometries[ltype]["cline"]=null; + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + /*geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000);*/ + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + stopTracking=false; + $('#myModal').modal(); + vectorLayer1.getSource().clear(); + updateCurrentMapLocation(); + geometries["origin"]=new ol.geom.Point(ol.proj.transform([position[bestIndex].lon,position[bestIndex].lat], 'EPSG:4326','EPSG:3857')); + setTimeout(function() { trackCurrentGPSPosition();}, 1000); + } +} + +var hasAddedElement=0; +var myVectorLayer=null; +function addSelectedElement(wktString){ + var features=[]; + try{ + var format = new ol.format.WKT(); + features.push( + format.readFeature(wktString, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }) + ); + }catch(e){ + console.log("Unable to parse WKT ?!"+e) + } + if(!vectorLayer1){ + myVectorLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: features + }), + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: "#3333aa", + width: 1.4 + }) + }) + }); + map.addLayer(myVectorLayer); + } + else{ + vectorLayer1.getSource().clear(); + vectorLayer1.getSource().addFeatures(features); + } + console.log(vectorLayer1.getSource().getExtent()); + map.updateSize(); + map.getView().fit(vectorLayer1.getSource().getExtent(),map.getSize()); + hasAddedElement=1; +} + +/***************************************************************************** + * Show selected feature on map + *****************************************************************************/ + var addSelectedIndex=0; + var mySelectedElement=null; +function showElementOnMap(wktString){ + mySelectedElement=wktString; + modalCallback=function(){ + //$("#myModal").find(".glyphicon-ok").parent().hide(); + if(addSelectedIndex==0){ + updateCurrentMapLocation(); + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + } + addSelectedElement(mySelectedElement); + $("#myModal").find("h4").html(' '+window.Android.translate("view_feature")); + addSelectedIndex++; + }; + //$("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); + if(!$("#map").length) + $.ajax({ + method: "GET", + url: './map-modal.html', + error: function(){ + }, + success: function(data){ + console.log(data); + $("body").append(data); + $("#map").css("height",($(window).height()-220)+"px"); + $('#myModal').modal(); + addOptionalLocalTiles(true); + $('#myModal').on('shown.bs.modal', function () { + console.log($("#mmm4me_ls").length); + initMapToLocation(); + localTileIndex=map.getLayers().getLength(); + modalCallback(); + }); + $('#myModal').on('hide.bs.modal', function () { + console.log("HIDE"); + stopTracking=true; + }); + } + }); + else{ + console.log("OK "); + $('#myModal').modal(); + console.log("OK "); + vectorLayer1.getSource().clear(); + console.log("OK "); + updateCurrentMapLocation(); + console.log("OK "); + addSelectedElement(wktString); + console.log("OK "); + } + + $("#currentPosition").html("Position: "+position[bestIndex].lon.toFixed(6)+","+position[bestIndex].lat.toFixed(6)+""); +} + + +/***************************************************************************** + * Store the current GPS location in the edit form + *****************************************************************************/ +function requireGPSPosition(elem){ + var position=JSON.parse(window.Android.getGPS()); + //elem=$("#"+elem); + if(position.lat){ + $(".btn_"+elem+"_lat").html(position.lat); + $(".btn_"+elem+"_long").html(position.lon); + $(".btn_"+elem+"_source").html(position.source); + $("input[name='"+elem+"']").val("POINT("+position.lon+" "+position.lat+")"); + }else{ + + } + console.log(JSON.stringify(position)); +} + +/***************************************************************************** + * Ajax setup to ensure seting "withCredentials" to true + *****************************************************************************/ +function ajaxSetup(){ + $.ajaxSetup({ + xhrFields: { + withCredentials: true + } + }); +} + +/***************************************************************************** + * Get the current Network and GPS availability + *****************************************************************************/ +function getCurrentStatus(){ + var tmp=JSON.parse(window.Android.getGNStatus()); + tmp["gps"]=JSON.parse(tmp["gps"]); + updateStatus(tmp['gps'],tmp['net']); +} + +/***************************************************************************** + * Display the status icons + *****************************************************************************/ +function addStatusControl(){ + $('.breadcrumb').append(' /'+ + ''+ + ''+ + ''+ + ''+ + ''); +} + +/***************************************************************************** + * Initialize the map and show the current GPS location + *****************************************************************************/ +var localTiles,localTiles0; +var tileLayers=[]; +function initMapToLocation(){ + if(map) + return; + var osmSource = new ol.source.OSM(); + + var otherLayers=[]; + var otherLayersSwitcher=""; + try{ + var BasesLayersStr=window.Android.getBaseLayers(); + console.log(BasesLayersStr); + var BasesLayers=JSON.parse(BasesLayersStr); + for(var i=0;i=4) + map.getLayers().item(otherLayers[i]["index"]).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity_"+i).show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(!otherLayers[i]["index"]){ + var myTileLayer=new ol.layer.Tile({source: otherLayers[i]["olLayer"]}); + var layers = map.getLayers(); + layers.insertAt(tileLayers.length+1,myTileLayer); + otherLayers[i]["index"]=tileLayers.length+1; + tileLayers.push(myTileLayer); + if(localTileIndex>0) + localTileIndex+=1; + } + else + map.getLayers().item(otherLayers[i]["index"]).setVisible(true); + } + }); + $(".map").parent().find("input[type=range]").last().on('change',function(){ + console.log("change to "+$(this).val()); + map.getLayers().item(otherLayers[i]["index"]).setOpacity($(this).val()/100); + }); + + })(i); + $(".map").parent().find("input[type=checkbox]").parent().off('click'); + $(".map").parent().find("input[type=checkbox]").parent().on('click',function(){ + var tmp=$(this).find("input[type=checkbox]"); + if(tmp.is(":checked")){ + $(this).find("input[type=checkbox]").prop("checked",false).change(); + $(this).addClass("select"); + } + else{ + $(this).find("input[type=checkbox]").prop("checked",true).change(); + $(this).removeClass("select"); + } + }); + + },1); + })(i) + } + } + map = new ol.Map({ + layers: layers, + target: 'map', + controls: ol.control.defaults({ + attributionOptions: /** @type {olx.control.AttributionOptions} */ ({ + collapsible: true + }) + }), + view: new ol.View({ + center: ol.proj.transform([0,0], 'EPSG:4326', 'EPSG:3857'), + zoom: 14, + maxZoom: 22, + minZoom: 1 + }) + }); + + var bstyle = function (feature, resolution) { + + /*var iconFont = 'glyphicon'; + var iconFontText = '\e062';*/ + //var iconFont = 'glyphicon'; + var iconFont = 'Glyphicons Halflings'; + var iconFontText = '\ue062'; + var iconSize = 24; + var opacity=0.4; + var col = 'rgba(0,255,0,0.6)'; + if(feature.get("source")=="GPS") + col = 'rgba(41,136,54,0.5)';//#298836 + else if(feature.get("source")=="Network"){ + col = 'rgba(91,176,75,0.4)';//#5bb04b + iconSize = 32; + opacity=0.2; + } + else if(feature.get("source")=="other"){ + col='rgba(129,208,113,0.5)';//#81d071 + iconSize = 36; + opacity=0.2; + } + else + col='rgba(166,63,39,0.5)'; //#a63f27 //"#EE0000"; + var styles = []; + + var styleIcon = new ol.style.Style({ + text: new ol.style.Text({ + font: 'Normal ' + iconSize + 'px ' + iconFont, + text: iconFontText, + fill: new ol.style.Fill({ color: col }) + }) + }); + styles.push(styleIcon); + + //console.log(feature.get("type")); + return styles; + /*return function (feature, resolution) { + styles.styleIcon.getText().setText(feature.get("iconCode")); + return styles; + };*/ + }; + position=JSON.parse(window.Android.getFullGPS()); + //console.log(JSON.stringify(position)); + if(position.length==0){ + position=[{lon:3.5,lat:43.5,source:"none"}]; + } + var iconFeatures = []; + for(var i=0;i=4) + map.getLayers().item(localTileIndex).setVisible(false); + }else{ + $(this).parent().find("i").removeClass("glyphicon-eye-close").addClass("glyphicon-eye-open"); + $("#dmopacity").show(); + console.log(JSON.stringify(map.getLayers().getLength())); + if(map.getLayers().getLength()0.05){ + map.getView().setRotation(-direction); + oldBearer=direction; + } + }else{ + window.Android.stopReportDirection(); + } +} diff --git a/app/src/main/assets/stroller.sfb b/app/src/main/assets/stroller.sfb new file mode 100644 index 0000000..535e46c Binary files /dev/null and b/app/src/main/assets/stroller.sfb differ diff --git a/app/src/main/assets/trigrid.png b/app/src/main/assets/trigrid.png new file mode 100755 index 0000000..05cbe6e Binary files /dev/null and b/app/src/main/assets/trigrid.png differ diff --git a/app/src/main/assets/wheelchair.sfb b/app/src/main/assets/wheelchair.sfb new file mode 100644 index 0000000..fc2ecbd Binary files /dev/null and b/app/src/main/assets/wheelchair.sfb differ diff --git a/app/src/main/java/Ar_Simulation/ARSimulation.java b/app/src/main/java/Ar_Simulation/ARSimulation.java new file mode 100644 index 0000000..e7e6ee5 --- /dev/null +++ b/app/src/main/java/Ar_Simulation/ARSimulation.java @@ -0,0 +1,326 @@ +package Ar_Simulation; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.Context; +import android.os.Build; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ListPopupWindow; +import android.widget.SeekBar; +import android.widget.Spinner; +import android.widget.Toast; + +import android.support.v7.app.AppCompatActivity; + +import com.google.ar.core.Anchor; +import com.google.ar.core.HitResult; +import com.google.ar.core.Plane; +import com.google.ar.core.Pose; +import com.google.ar.sceneform.AnchorNode; +import com.google.ar.sceneform.math.Quaternion; +import com.google.ar.sceneform.math.Vector3; +import com.google.ar.sceneform.rendering.ModelRenderable; +import com.google.ar.sceneform.ux.ArFragment; +import com.google.ar.sceneform.ux.TransformableNode; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import fr.geolabs.dev.mapmint4me.R; + +import static java.lang.Math.atan2; + +public class ARSimulation extends AppCompatActivity { + private static final String TAG = ARSimulation.class.getSimpleName(); + private static final double MIN_OPENGL_VERSION = 3.0; + private float x=0f, y=0f, z=0f; + private ArFragment arFragment; + private AnchorNode myanchornode; + TransformableNode mytranode = null; + + private SeekBar sb_size; + private Spinner spn_model; + + private HitResult myhit; + private float mySize = 70f; + private float mytravel=0.01f, distance_x=0f, distance_z=0f, myangle=0f; + + + int[] sfb_source = {R.raw.wheelchair, R.raw.model, R.raw.stroller, R.raw.cart}; + String[] arr_models = {"Wheelchair", "Arrow", "Stroller", "Shopping cart"}; + private ModelRenderable[] renderable_models = new ModelRenderable[sfb_source.length]; + + @Override + @SuppressWarnings({"AndroidApiChecker", "FutureReturnValueIgnored"}) + // CompletableFuture requires api level 24 + + // FutureReturnValueIgnored is not valid + protected void onCreate(Bundle savedInstanceState) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + super.onCreate(savedInstanceState); + + if (!checkIsSupportedDeviceOrFinish(this)) { + return; + } + try + { + this.getSupportActionBar().hide(); + } + catch (NullPointerException e){} + setContentView(R.layout.ar_simulation); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment); + + Button r_left = (Button)findViewById(R.id.r_left); + Button r_right = (Button)findViewById(R.id.r_right); + Button accelerate = (Button)findViewById(R.id.accelerate); + spn_model = (Spinner) findViewById(R.id.spn_model); + sb_size = (SeekBar) findViewById(R.id.sb_size); + List anchorNodes = new ArrayList<>(); + + sb_size.setEnabled(false); + + // Keep immersive view on spinner opening + Field popup = null; + try { + popup = Spinner.class.getDeclaredField("mPopup"); + popup.setAccessible(true); + ListPopupWindow popupWindow = (ListPopupWindow) popup.get(spn_model); + popupWindow.setModal(false); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + + + sb_size.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + mySize = progress; + myanchornode.setLocalScale(new Vector3(progress/70f, progress/70f, progress/70f)); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + + accelerate.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mytranode != null) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + myangle = set(mytranode.getLocalRotation()); + } + if (event.getAction() == MotionEvent.ACTION_UP) { + } + forward(myanchornode); + } + return true; + + } + }); + + r_left.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if(mytranode != null){ + Quaternion q1 = mytranode.getLocalRotation(); + Quaternion q2 = Quaternion.axisAngle(new Vector3(0, 1f, 0f), .5f); + mytranode.setLocalRotation(Quaternion.multiply(q1, q2)); + myangle = set(mytranode.getLocalRotation()); + } + + return true; + } + + }); + + r_right.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if(mytranode != null){ + myangle+=0.01f; + Quaternion q1 = mytranode.getLocalRotation(); + Quaternion q2 = Quaternion.axisAngle(new Vector3(0, 1f, 0f), -.5f); + mytranode.setLocalRotation(Quaternion.multiply(q1, q2)); + myangle = set(mytranode.getLocalRotation()); + } + + return true; + } + + }); + + for(int i = 0 ; i < sfb_source.length ; i++) { + int finalI = i; + ModelRenderable.builder() + .setSource(this, sfb_source[i]) + .build() + .thenAccept(renderable -> renderable_models[finalI] = renderable) + .exceptionally( + throwable -> { + Toast toast = + Toast.makeText(this, "Unable to load andy renderable", Toast.LENGTH_LONG); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.show(); + return null; + }); + } + + + arFragment.setOnTapArPlaneListener( + (HitResult hitResult, Plane plane, MotionEvent motionEvent) -> { + if (renderable_models[spn_model.getSelectedItemPosition()] == null) { + return; + } + + distance_x=0f; + distance_z=0f; + myangle=0f; + + myhit = hitResult; + + // Create the Anchor. + Anchor anchor = hitResult.createAnchor(); + + AnchorNode anchorNode = new AnchorNode(anchor); + + + anchorNode.setParent(arFragment.getArSceneView().getScene()); + anchorNodes.add(anchorNode); + sb_size.setEnabled(true); + + myanchornode = anchorNode; + + // Create the transformable andy and add it to the anchor. + TransformableNode andy; + if(mytranode == null) + andy = new TransformableNode(arFragment.getTransformationSystem()); + else andy = mytranode; + + andy.setParent(anchorNode); + andy.setRenderable(renderable_models[spn_model.getSelectedItemPosition()]); + andy.select(); + + mytranode = andy; + mytranode.setLocalRotation(new Quaternion(0f, 0f, 0f, 1f)); + myanchornode.setLocalScale(new Vector3(mySize/70f, mySize/70f, mySize/70f)); + }); + + ArrayAdapter adapter = new ArrayAdapter(ARSimulation.this, + android.R.layout.simple_spinner_dropdown_item,arr_models); + + spn_model.setAdapter(adapter); + spn_model.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + if(mytranode != null) + mytranode.setRenderable(renderable_models[i]); + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + } + }); + + } + + void ascend(AnchorNode an, float x, float y, float z){ + Anchor anchor = myhit.getTrackable().createAnchor( + myhit.getHitPose().compose(Pose.makeTranslation(x/100f, z/100f, y/100f))); + + an.setAnchor(anchor); + } + + Quaternion rotate(AnchorNode an, float angle) { + //mytranode.setLocalRotation(Quaternion.axisAngle(new Vector3(0, 1f, 0), angle)); + + return mytranode.getLocalRotation(); + } + + + public float set(Quaternion q1) { + Vector3 angles = new Vector3(); + double sqw = q1.w*q1.w; + double sqx = q1.x*q1.x; + double sqy = q1.y*q1.y; + double sqz = q1.z*q1.z; + double unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor + double test = q1.x*q1.y + q1.z*q1.w; + if (test > 0.499*unit) { // singularity at north pole + angles.x = (float) (2 * atan2(q1.x,q1.w)); + angles.y = (float) Math.PI/2; + angles.z = 0; + return angles.x; + } + if (test < -0.499*unit) { // singularity at south pole + angles.x = (float) (-2 * atan2(q1.x,q1.w)); + angles.y = (float) (-Math.PI/2); + angles.z = 0; + return angles.x; + } + angles.x = (float) atan2(2*q1.y*q1.w-2*q1.x*q1.z , sqx - sqy - sqz + sqw); + angles.y = (float) Math.asin(2*test/unit); + angles.z = (float) atan2(2*q1.x*q1.w-2*q1.y*q1.z , -sqx + sqy - sqz + sqw); + return angles.x; + } + + + void forward(AnchorNode an){ + distance_x+= Math.sin(myangle)*mytravel; + distance_z+= Math.cos(myangle)*mytravel; + + Anchor anchor = myhit.getTrackable().createAnchor( + myhit.getHitPose().compose(Pose.makeTranslation(-distance_x, 0f, -distance_z))); + + an.setAnchor(anchor); + } + + float getMetersBetweenAnchors(Anchor anchor1, Anchor anchor2) { + float[] distance_vector = anchor1.getPose().inverse() + .compose(anchor2.getPose()).getTranslation(); + float totalDistanceSquared = 0; + for(int i=0; i<3; ++i) + totalDistanceSquared += distance_vector[i]*distance_vector[i]; + return (float) Math.sqrt(totalDistanceSquared); + } + + public static boolean checkIsSupportedDeviceOrFinish(final Activity activity) { + if (Build.VERSION.SDK_INT < VERSION_CODES.N) { + Log.e(TAG, "Sceneform requires Android N or later"); + Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show(); + activity.finish(); + return false; + } + String openGlVersionString = + ((ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE)) + .getDeviceConfigurationInfo() + .getGlEsVersion(); + if (Double.parseDouble(openGlVersionString) < MIN_OPENGL_VERSION) { + Log.e(TAG, "Sceneform requires OpenGL ES 3.0 later"); + Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG) + .show(); + activity.finish(); + return false; + } + return true; + } +} diff --git a/app/src/main/java/Satellite/DatabaseHandler.java b/app/src/main/java/Satellite/DatabaseHandler.java new file mode 100644 index 0000000..fb05987 --- /dev/null +++ b/app/src/main/java/Satellite/DatabaseHandler.java @@ -0,0 +1,1087 @@ + +package Satellite; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.location.Location; +import android.util.Log; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + + +class DatabaseHandler extends SQLiteOpenHelper { + + // All Static variables + // Database Version + private static final int DATABASE_VERSION = 2; // Updated to 2 in v2.1.3 (code 14) + private static final int LOCATION_TYPE_LOCATION = 1; + private static final int LOCATION_TYPE_PLACEMARK = 2; + + // Database Name + private static final String DATABASE_NAME = "GPS"; + + // -------------------------------------------------------------------------------- Table names + private static final String TABLE_LOCATIONS = "locations"; + private static final String TABLE_TRACKS = "tracks"; + private static final String TABLE_PLACEMARKS = "placemarks"; + + // ----------------------------------------------------------------------- Common Columns names + private static final String KEY_ID = "id"; + private static final String KEY_TRACK_ID = "track_id"; + + // --------------------------------------------------------------- Location Table Columns names + private static final String KEY_LOCATION_NUMBER = "nr"; + private static final String KEY_LOCATION_LATITUDE = "latitude"; + private static final String KEY_LOCATION_LONGITUDE = "longitude"; + private static final String KEY_LOCATION_ALTITUDE = "altitude"; + private static final String KEY_LOCATION_SPEED = "speed"; + private static final String KEY_LOCATION_ACCURACY = "accuracy"; + private static final String KEY_LOCATION_BEARING = "bearing"; + private static final String KEY_LOCATION_TIME = "time"; + private static final String KEY_LOCATION_NUMBEROFSATELLITES = "number_of_satellites"; + private static final String KEY_LOCATION_TYPE = "type"; + private static final String KEY_LOCATION_NUMBEROFSATELLITESUSEDINFIX = "number_of_satellites_used_in_fix"; + + // ---------------------------------------------------------------------------- Placemarks adds + private static final String KEY_LOCATION_NAME = "name"; + + // ------------------------------------------------------------------ Track Table Columns names + private static final String KEY_TRACK_NAME = "name"; + private static final String KEY_TRACK_FROM = "location_from"; + private static final String KEY_TRACK_TO = "location_to"; + + + private static final String KEY_TRACK_START_LATITUDE = "start_latitude"; + private static final String KEY_TRACK_START_LONGITUDE = "start_longitude"; + private static final String KEY_TRACK_START_ALTITUDE = "start_altitude"; + private static final String KEY_TRACK_START_ACCURACY = "start_accuracy"; + private static final String KEY_TRACK_START_SPEED = "start_speed"; + private static final String KEY_TRACK_START_TIME = "start_time"; + + private static final String KEY_TRACK_LASTFIX_TIME = "lastfix_time"; + + private static final String KEY_TRACK_END_LATITUDE = "end_latitude"; + private static final String KEY_TRACK_END_LONGITUDE = "end_longitude"; + private static final String KEY_TRACK_END_ALTITUDE = "end_altitude"; + private static final String KEY_TRACK_END_ACCURACY = "end_accuracy"; + private static final String KEY_TRACK_END_SPEED = "end_speed"; + private static final String KEY_TRACK_END_TIME = "end_time"; + + private static final String KEY_TRACK_LASTSTEPDST_LATITUDE = "laststepdst_latitude"; + private static final String KEY_TRACK_LASTSTEPDST_LONGITUDE = "laststepdst_longitude"; + private static final String KEY_TRACK_LASTSTEPDST_ACCURACY = "laststepdst_accuracy"; + + private static final String KEY_TRACK_LASTSTEPALT_ALTITUDE = "laststepalt_altitude"; + private static final String KEY_TRACK_LASTSTEPALT_ACCURACY = "laststepalt_accuracy"; + + private static final String KEY_TRACK_MIN_LATITUDE = "min_latitude"; + private static final String KEY_TRACK_MIN_LONGITUDE = "min_longitude"; + + private static final String KEY_TRACK_MAX_LATITUDE = "max_latitude"; + private static final String KEY_TRACK_MAX_LONGITUDE = "max_longitude"; + + private static final String KEY_TRACK_DURATION = "duration"; + private static final String KEY_TRACK_DURATION_MOVING = "duration_moving"; + + private static final String KEY_TRACK_DISTANCE = "distance"; + private static final String KEY_TRACK_DISTANCE_INPROGRESS = "distance_in_progress"; + private static final String KEY_TRACK_DISTANCE_LASTALTITUDE = "distance_last_altitude"; + + private static final String KEY_TRACK_ALTITUDE_UP = "altitude_up"; + private static final String KEY_TRACK_ALTITUDE_DOWN = "altitude_down"; + private static final String KEY_TRACK_ALTITUDE_INPROGRESS = "altitude_in_progress"; + + private static final String KEY_TRACK_SPEED_MAX = "speed_max"; + private static final String KEY_TRACK_SPEED_AVERAGE = "speed_average"; + private static final String KEY_TRACK_SPEED_AVERAGEMOVING = "speed_average_moving"; + + private static final String KEY_TRACK_NUMBEROFLOCATIONS = "number_of_locations"; + private static final String KEY_TRACK_NUMBEROFPLACEMARKS = "number_of_placemarks"; + private static final String KEY_TRACK_TYPE = "type"; + + private static final String KEY_TRACK_VALIDMAP = "validmap"; + + + + public DatabaseHandler(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + // Creating Tables + @Override + public void onCreate(SQLiteDatabase db) { + String CREATE_TRACKS_TABLE = "CREATE TABLE " + TABLE_TRACKS + "(" + + KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," // 0 + + KEY_TRACK_NAME + " TEXT," // 1 + + KEY_TRACK_FROM + " TEXT," // 2 + + KEY_TRACK_TO + " TEXT," // 3 + + KEY_TRACK_START_LATITUDE + " REAL," // 4 + + KEY_TRACK_START_LONGITUDE + " REAL," // 5 + + KEY_TRACK_START_ALTITUDE + " REAL," // 6 + + KEY_TRACK_START_ACCURACY + " REAL," // 7 + + KEY_TRACK_START_SPEED + " REAL," // 8 + + KEY_TRACK_START_TIME + " REAL," // 9 + + KEY_TRACK_LASTFIX_TIME + " REAL," // 10 + + KEY_TRACK_END_LATITUDE + " REAL," // 11 + + KEY_TRACK_END_LONGITUDE + " REAL," // 12 + + KEY_TRACK_END_ALTITUDE + " REAL," // 13 + + KEY_TRACK_END_ACCURACY + " REAL," // 14 + + KEY_TRACK_END_SPEED + " REAL," // 15 + + KEY_TRACK_END_TIME + " REAL," // 16 + + KEY_TRACK_LASTSTEPDST_LATITUDE + " REAL," // 17 + + KEY_TRACK_LASTSTEPDST_LONGITUDE + " REAL," // 18 + + KEY_TRACK_LASTSTEPDST_ACCURACY + " REAL," // 19 + + KEY_TRACK_LASTSTEPALT_ALTITUDE + " REAL," // 20 + + KEY_TRACK_LASTSTEPALT_ACCURACY + " REAL," // 21 + + KEY_TRACK_MIN_LATITUDE + " REAL," // 22 + + KEY_TRACK_MIN_LONGITUDE + " REAL," // 23 + + KEY_TRACK_MAX_LATITUDE + " REAL," // 24 + + KEY_TRACK_MAX_LONGITUDE + " REAL," // 25 + + KEY_TRACK_DURATION + " REAL," // 26 + + KEY_TRACK_DURATION_MOVING + " REAL," // 27 + + KEY_TRACK_DISTANCE + " REAL," // 28 + + KEY_TRACK_DISTANCE_INPROGRESS + " REAL," // 29 + + KEY_TRACK_DISTANCE_LASTALTITUDE + " REAL," // 30 + + KEY_TRACK_ALTITUDE_UP + " REAL," // 31 + + KEY_TRACK_ALTITUDE_DOWN + " REAL," // 32 + + KEY_TRACK_ALTITUDE_INPROGRESS + " REAL," // 33 + + KEY_TRACK_SPEED_MAX + " REAL," // 34 + + KEY_TRACK_SPEED_AVERAGE + " REAL," // 35 + + KEY_TRACK_SPEED_AVERAGEMOVING + " REAL," // 36 + + KEY_TRACK_NUMBEROFLOCATIONS + " INTEGER," // 37 + + KEY_TRACK_NUMBEROFPLACEMARKS + " INTEGER," // 38 + + KEY_TRACK_VALIDMAP + " INTEGER," // 39 + + KEY_TRACK_TYPE + " INTEGER " + ")"; // 40 + db.execSQL(CREATE_TRACKS_TABLE); + + String CREATE_LOCATIONS_TABLE = "CREATE TABLE " + TABLE_LOCATIONS + "(" + + KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," // 0 + + KEY_TRACK_ID + " INTEGER," // 1 + + KEY_LOCATION_NUMBER + " INTEGER," // 2 + + KEY_LOCATION_LATITUDE + " REAL," // 3 + + KEY_LOCATION_LONGITUDE + " REAL," // 4 + + KEY_LOCATION_ALTITUDE + " REAL," // 5 + + KEY_LOCATION_SPEED + " REAL," // 6 + + KEY_LOCATION_ACCURACY + " REAL," // 7 + + KEY_LOCATION_BEARING + " REAL," // 8 + + KEY_LOCATION_TIME + " REAL," // 9 + + KEY_LOCATION_NUMBEROFSATELLITES + " INTEGER," // 10 + + KEY_LOCATION_TYPE + " INTEGER," // 11 + + KEY_LOCATION_NUMBEROFSATELLITESUSEDINFIX + " INTEGER" + ")"; // 12 + db.execSQL(CREATE_LOCATIONS_TABLE); + + String CREATE_PLACEMARKS_TABLE = "CREATE TABLE " + TABLE_PLACEMARKS + "(" + + KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," // 0 + + KEY_TRACK_ID + " INTEGER," // 1 + + KEY_LOCATION_NUMBER + " INTEGER," // 2 + + KEY_LOCATION_LATITUDE + " REAL," // 3 + + KEY_LOCATION_LONGITUDE + " REAL," // 4 + + KEY_LOCATION_ALTITUDE + " REAL," // 5 + + KEY_LOCATION_SPEED + " REAL," // 6 + + KEY_LOCATION_ACCURACY + " REAL," // 7 + + KEY_LOCATION_BEARING + " REAL," // 8 + + KEY_LOCATION_TIME + " REAL," // 9 + + KEY_LOCATION_NUMBEROFSATELLITES + " INTEGER," // 10 + + KEY_LOCATION_TYPE + " INTEGER," // 11 + + KEY_LOCATION_NAME + " TEXT," // 12 + + KEY_LOCATION_NUMBEROFSATELLITESUSEDINFIX + " INTEGER" + ")"; // 13 + db.execSQL(CREATE_PLACEMARKS_TABLE); + } + + + private static final int NOT_AVAILABLE = -100000; + + private static final String DATABASE_ALTER_TABLE_LOCATIONS_TO_V2 = "ALTER TABLE " + + TABLE_LOCATIONS + " ADD COLUMN " + KEY_LOCATION_NUMBEROFSATELLITESUSEDINFIX + " INTEGER DEFAULT " + NOT_AVAILABLE + ";"; + private static final String DATABASE_ALTER_TABLE_PLACEMARKS_TO_V2 = "ALTER TABLE " + + TABLE_PLACEMARKS + " ADD COLUMN " + KEY_LOCATION_NUMBEROFSATELLITESUSEDINFIX + " INTEGER DEFAULT " + NOT_AVAILABLE + ";"; + + // Upgrading database + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + + // Use this function in case of DB version upgrade. + + // Drop older table if existed + //db.execSQL("DROP TABLE IF EXISTS " + TABLE_LOCATIONS); + //db.execSQL("DROP TABLE IF EXISTS " + TABLE_PLACEMARKS); + //db.execSQL("DROP TABLE IF EXISTS " + TABLE_TRACKS); + + // Create tables again + //onCreate(db); + + switch (oldVersion) + { + case 1: + //upgrade from version 1 to 2 + //Log.w("myApp", "[#] DatabaseHandler.java - onUpgrade: from version 1 to 2 ..."); + db.execSQL(DATABASE_ALTER_TABLE_LOCATIONS_TO_V2); + db.execSQL(DATABASE_ALTER_TABLE_PLACEMARKS_TO_V2); + //case 2: + //upgrade from version 2 to 3 + // db.execSQL(DATABASE_ALTER_TEAM_TO_V3); + + //and so on.. do not add breaks so that switch will + //start at oldVersion, and run straight through to the latest + } + //Log.w("myApp", "[#] DatabaseHandler.java - onUpgrade: DB upgraded to version " + newVersion); + } + +// ----------------------------------------------------------------------- LOCATIONS AND PLACEMARKS + + // Add new Location and update the corresponding track + public void addLocationToTrack(LocationExtended location, Track track) { + SQLiteDatabase db = this.getWritableDatabase(); + + Location loc = location.getLocation(); + + ContentValues locvalues = new ContentValues(); + locvalues.put(KEY_TRACK_ID, track.getId()); + locvalues.put(KEY_LOCATION_NUMBER, track.getNumberOfLocations()); + locvalues.put(KEY_LOCATION_LATITUDE, loc.getLatitude()); + locvalues.put(KEY_LOCATION_LONGITUDE, loc.getLongitude()); + locvalues.put(KEY_LOCATION_ALTITUDE, loc.hasAltitude() ? loc.getAltitude() : NOT_AVAILABLE); + locvalues.put(KEY_LOCATION_SPEED, loc.hasSpeed() ? loc.getSpeed() : NOT_AVAILABLE); + locvalues.put(KEY_LOCATION_ACCURACY, loc.hasAccuracy() ? loc.getAccuracy() : NOT_AVAILABLE); + locvalues.put(KEY_LOCATION_BEARING, loc.hasBearing() ? loc.getBearing() : NOT_AVAILABLE); + locvalues.put(KEY_LOCATION_TIME, loc.getTime()); + locvalues.put(KEY_LOCATION_NUMBEROFSATELLITES, location.getNumberOfSatellites()); + locvalues.put(KEY_LOCATION_TYPE, LOCATION_TYPE_LOCATION); + locvalues.put(KEY_LOCATION_NUMBEROFSATELLITESUSEDINFIX, location.getNumberOfSatellitesUsedInFix()); + + ContentValues trkvalues = new ContentValues(); + trkvalues.put(KEY_TRACK_NAME, track.getName()); + trkvalues.put(KEY_TRACK_FROM, ""); + trkvalues.put(KEY_TRACK_TO, ""); + + trkvalues.put(KEY_TRACK_START_LATITUDE, track.getStart_Latitude()); + trkvalues.put(KEY_TRACK_START_LONGITUDE, track.getStart_Longitude()); + trkvalues.put(KEY_TRACK_START_ALTITUDE, track.getStart_Altitude()); + trkvalues.put(KEY_TRACK_START_ACCURACY, track.getStart_Accuracy()); + trkvalues.put(KEY_TRACK_START_SPEED, track.getStart_Speed()); + trkvalues.put(KEY_TRACK_START_TIME, track.getStart_Time()); + + trkvalues.put(KEY_TRACK_LASTFIX_TIME, track.getLastFix_Time()); + + trkvalues.put(KEY_TRACK_END_LATITUDE, track.getEnd_Latitude()); + trkvalues.put(KEY_TRACK_END_LONGITUDE, track.getEnd_Longitude()); + trkvalues.put(KEY_TRACK_END_ALTITUDE, track.getEnd_Altitude()); + trkvalues.put(KEY_TRACK_END_ACCURACY, track.getEnd_Accuracy()); + trkvalues.put(KEY_TRACK_END_SPEED, track.getEnd_Speed()); + trkvalues.put(KEY_TRACK_END_TIME, track.getEnd_Time()); + + trkvalues.put(KEY_TRACK_LASTSTEPDST_LATITUDE, track.getLastStepDistance_Latitude()); + trkvalues.put(KEY_TRACK_LASTSTEPDST_LONGITUDE, track.getLastStepDistance_Longitude()); + trkvalues.put(KEY_TRACK_LASTSTEPDST_ACCURACY, track.getLastStepDistance_Accuracy()); + + trkvalues.put(KEY_TRACK_LASTSTEPALT_ALTITUDE, track.getLastStepAltitude_Altitude()); + trkvalues.put(KEY_TRACK_LASTSTEPALT_ACCURACY, track.getLastStepAltitude_Accuracy()); + + trkvalues.put(KEY_TRACK_MIN_LATITUDE, track.getMin_Latitude()); + trkvalues.put(KEY_TRACK_MIN_LONGITUDE, track.getMin_Longitude()); + + trkvalues.put(KEY_TRACK_MAX_LATITUDE, track.getMax_Latitude()); + trkvalues.put(KEY_TRACK_MAX_LONGITUDE, track.getMax_Longitude()); + + trkvalues.put(KEY_TRACK_DURATION, track.getDuration()); + trkvalues.put(KEY_TRACK_DURATION_MOVING, track.getDuration_Moving()); + + trkvalues.put(KEY_TRACK_DISTANCE, track.getDistance()); + trkvalues.put(KEY_TRACK_DISTANCE_INPROGRESS, track.getDistanceInProgress()); + trkvalues.put(KEY_TRACK_DISTANCE_LASTALTITUDE, track.getDistanceLastAltitude()); + + trkvalues.put(KEY_TRACK_ALTITUDE_UP, track.getAltitude_Up()); + trkvalues.put(KEY_TRACK_ALTITUDE_DOWN, track.getAltitude_Down()); + trkvalues.put(KEY_TRACK_ALTITUDE_INPROGRESS, track.getAltitude_InProgress()); + + trkvalues.put(KEY_TRACK_SPEED_MAX, track.getSpeedMax()); + trkvalues.put(KEY_TRACK_SPEED_AVERAGE, track.getSpeedAverage()); + trkvalues.put(KEY_TRACK_SPEED_AVERAGEMOVING, track.getSpeedAverageMoving()); + + trkvalues.put(KEY_TRACK_NUMBEROFLOCATIONS, track.getNumberOfLocations()); + trkvalues.put(KEY_TRACK_NUMBEROFPLACEMARKS, track.getNumberOfPlacemarks()); + trkvalues.put(KEY_TRACK_TYPE, track.getType()); + + trkvalues.put(KEY_TRACK_VALIDMAP, track.getValidMap()); + + db.beginTransaction(); + db.insert(TABLE_LOCATIONS, null, locvalues); // Insert the new Location + db.update(TABLE_TRACKS, trkvalues, KEY_ID + " = ?", + new String[] { String.valueOf(track.getId()) }); // Update the corresponding Track + db.setTransactionSuccessful(); + db.endTransaction(); + + //Log.w("myApp", "[#] DatabaseHandler.java - addLocation: Location " + track.getNumberOfLocations() + " added into track " + track.getID()); + } + + + // Add new Placemark and update the corresponding track + public void addPlacemarkToTrack(LocationExtended placemark, Track track) { + SQLiteDatabase db = this.getWritableDatabase(); + + Location loc = placemark.getLocation(); + + ContentValues locvalues = new ContentValues(); + locvalues.put(KEY_TRACK_ID, track.getId()); + locvalues.put(KEY_LOCATION_NUMBER, track.getNumberOfPlacemarks()); + locvalues.put(KEY_LOCATION_LATITUDE, loc.getLatitude()); + locvalues.put(KEY_LOCATION_LONGITUDE, loc.getLongitude()); + locvalues.put(KEY_LOCATION_ALTITUDE, loc.hasAltitude() ? loc.getAltitude() : NOT_AVAILABLE); + locvalues.put(KEY_LOCATION_SPEED, loc.hasSpeed() ? loc.getSpeed() : NOT_AVAILABLE); + locvalues.put(KEY_LOCATION_ACCURACY, loc.hasAccuracy() ? loc.getAccuracy() : NOT_AVAILABLE); + locvalues.put(KEY_LOCATION_BEARING, loc.hasBearing() ? loc.getBearing() : NOT_AVAILABLE); + locvalues.put(KEY_LOCATION_TIME, loc.getTime()); + locvalues.put(KEY_LOCATION_NUMBEROFSATELLITES, placemark.getNumberOfSatellites()); + locvalues.put(KEY_LOCATION_TYPE, LOCATION_TYPE_PLACEMARK); + locvalues.put(KEY_LOCATION_NAME, placemark.getDescription()); + locvalues.put(KEY_LOCATION_NUMBEROFSATELLITESUSEDINFIX, placemark.getNumberOfSatellitesUsedInFix()); + + ContentValues trkvalues = new ContentValues(); + trkvalues.put(KEY_TRACK_NAME, track.getName()); + trkvalues.put(KEY_TRACK_FROM, ""); + trkvalues.put(KEY_TRACK_TO, ""); + + trkvalues.put(KEY_TRACK_START_LATITUDE, track.getStart_Latitude()); + trkvalues.put(KEY_TRACK_START_LONGITUDE, track.getStart_Longitude()); + trkvalues.put(KEY_TRACK_START_ALTITUDE, track.getStart_Altitude()); + trkvalues.put(KEY_TRACK_START_ACCURACY, track.getStart_Accuracy()); + trkvalues.put(KEY_TRACK_START_SPEED, track.getStart_Speed()); + trkvalues.put(KEY_TRACK_START_TIME, track.getStart_Time()); + + trkvalues.put(KEY_TRACK_LASTFIX_TIME, track.getLastFix_Time()); + + trkvalues.put(KEY_TRACK_END_LATITUDE, track.getEnd_Latitude()); + trkvalues.put(KEY_TRACK_END_LONGITUDE, track.getEnd_Longitude()); + trkvalues.put(KEY_TRACK_END_ALTITUDE, track.getEnd_Altitude()); + trkvalues.put(KEY_TRACK_END_ACCURACY, track.getEnd_Accuracy()); + trkvalues.put(KEY_TRACK_END_SPEED, track.getEnd_Speed()); + trkvalues.put(KEY_TRACK_END_TIME, track.getEnd_Time()); + + trkvalues.put(KEY_TRACK_LASTSTEPDST_LATITUDE, track.getLastStepDistance_Latitude()); + trkvalues.put(KEY_TRACK_LASTSTEPDST_LONGITUDE, track.getLastStepDistance_Longitude()); + trkvalues.put(KEY_TRACK_LASTSTEPDST_ACCURACY, track.getLastStepDistance_Accuracy()); + + trkvalues.put(KEY_TRACK_LASTSTEPALT_ALTITUDE, track.getLastStepAltitude_Altitude()); + trkvalues.put(KEY_TRACK_LASTSTEPALT_ACCURACY, track.getLastStepAltitude_Accuracy()); + + trkvalues.put(KEY_TRACK_MIN_LATITUDE, track.getMin_Latitude()); + trkvalues.put(KEY_TRACK_MIN_LONGITUDE, track.getMin_Longitude()); + + trkvalues.put(KEY_TRACK_MAX_LATITUDE, track.getMax_Latitude()); + trkvalues.put(KEY_TRACK_MAX_LONGITUDE, track.getMax_Longitude()); + + trkvalues.put(KEY_TRACK_DURATION, track.getDuration()); + trkvalues.put(KEY_TRACK_DURATION_MOVING, track.getDuration_Moving()); + + trkvalues.put(KEY_TRACK_DISTANCE, track.getDistance()); + trkvalues.put(KEY_TRACK_DISTANCE_INPROGRESS, track.getDistanceInProgress()); + trkvalues.put(KEY_TRACK_DISTANCE_LASTALTITUDE, track.getDistanceLastAltitude()); + + trkvalues.put(KEY_TRACK_ALTITUDE_UP, track.getAltitude_Up()); + trkvalues.put(KEY_TRACK_ALTITUDE_DOWN, track.getAltitude_Down()); + trkvalues.put(KEY_TRACK_ALTITUDE_INPROGRESS, track.getAltitude_InProgress()); + + trkvalues.put(KEY_TRACK_SPEED_MAX, track.getSpeedMax()); + trkvalues.put(KEY_TRACK_SPEED_AVERAGE, track.getSpeedAverage()); + trkvalues.put(KEY_TRACK_SPEED_AVERAGEMOVING, track.getSpeedAverageMoving()); + + trkvalues.put(KEY_TRACK_NUMBEROFLOCATIONS, track.getNumberOfLocations()); + trkvalues.put(KEY_TRACK_NUMBEROFPLACEMARKS, track.getNumberOfPlacemarks()); + trkvalues.put(KEY_TRACK_TYPE, track.getType()); + + trkvalues.put(KEY_TRACK_VALIDMAP, track.getValidMap()); + + db.beginTransaction(); + db.insert(TABLE_PLACEMARKS, null, locvalues); // Insert the new Location + db.update(TABLE_TRACKS, trkvalues, KEY_ID + " = ?", + new String[] { String.valueOf(track.getId()) }); // Update the corresponding Track + db.setTransactionSuccessful(); + db.endTransaction(); + + //Log.w("myApp", "[#] DatabaseHandler.java - addLocation: Location " + track.getNumberOfLocations() + " added into track " + track.getID()); + } + + + // Get single Location + public LocationExtended getLocation(long id) { + SQLiteDatabase db = this.getWritableDatabase(); + LocationExtended extdloc = null; + double lcdata_double; + float lcdata_float; + + Cursor cursor = db.query(TABLE_LOCATIONS, new String[] {KEY_ID, + KEY_LOCATION_LATITUDE, + KEY_LOCATION_LONGITUDE, + KEY_LOCATION_ALTITUDE, + KEY_LOCATION_SPEED, + KEY_LOCATION_ACCURACY, + KEY_LOCATION_BEARING, + KEY_LOCATION_TIME, + KEY_LOCATION_NUMBEROFSATELLITES, + KEY_LOCATION_NUMBEROFSATELLITESUSEDINFIX}, KEY_ID + "=?", + new String[] { String.valueOf(id) }, null, null, null, null); + if (cursor != null) { + cursor.moveToFirst(); + + Location lc = new Location("DB"); + lc.setLatitude(cursor.getDouble(1)); + lc.setLongitude(cursor.getDouble(2)); + + lcdata_double = cursor.getDouble(3); + if (lcdata_double != NOT_AVAILABLE) lc.setAltitude(lcdata_double); + //else lc.removeAltitude(); + + lcdata_float = cursor.getFloat(4); + if (lcdata_float != NOT_AVAILABLE) lc.setSpeed(lcdata_float); + //else lc.removeSpeed(); + + lcdata_float = cursor.getFloat(5); + if (lcdata_float != NOT_AVAILABLE) lc.setAccuracy(lcdata_float); + //else lc.removeAccuracy(); + + lcdata_float = cursor.getFloat(6); + if (lcdata_float != NOT_AVAILABLE) lc.setBearing(lcdata_float); + //else lc.removeBearing(); + + lc.setTime(cursor.getLong(7)); + + + extdloc = new LocationExtended(lc); + extdloc.setNumberOfSatellites(cursor.getInt(8)); + extdloc.setNumberOfSatellitesUsedInFix(cursor.getInt(9)); + + cursor.close(); + } + return extdloc != null ? extdloc : null; + } + + + + // Getting a list of Locations associated to a specified track, with number between startNumber and endNumber + // Please note that limits both are inclusive! + public List getLocationsList(long TrackID, long startNumber, long endNumber) { + + List locationList = new ArrayList<>(); + + String selectQuery = "SELECT * FROM " + TABLE_LOCATIONS + " WHERE " + + KEY_TRACK_ID + " = " + TrackID + " AND " + + KEY_LOCATION_NUMBER + " BETWEEN " + startNumber + " AND " + endNumber + + " ORDER BY " + KEY_LOCATION_NUMBER; + + //Log.w("myApp", "[#] DatabaseHandler.java - getLocationList(" + TrackID + ", " + startNumber + ", " +endNumber + ") ==> " + selectQuery); + + SQLiteDatabase db = this.getWritableDatabase(); + Cursor cursor = db.rawQuery(selectQuery, null); + double lcdata_double; + float lcdata_float; + + if (cursor != null) { + // looping through all rows and adding to list + if (cursor.moveToFirst()) { + do { + Location lc = new Location("DB"); + lc.setLatitude(cursor.getDouble(3)); + lc.setLongitude(cursor.getDouble(4)); + + lcdata_double = cursor.getDouble(5); + if (lcdata_double != NOT_AVAILABLE) lc.setAltitude(lcdata_double); + //else lc.removeAltitude(); + + lcdata_float = cursor.getFloat(6); + if (lcdata_float != NOT_AVAILABLE) lc.setSpeed(lcdata_float); + //else lc.removeSpeed(); + + lcdata_float = cursor.getFloat(7); + if (lcdata_float != NOT_AVAILABLE) lc.setAccuracy(lcdata_float); + //else lc.removeAccuracy(); + + lcdata_float = cursor.getFloat(8); + if (lcdata_float != NOT_AVAILABLE) lc.setBearing(lcdata_float); + //else lc.removeBearing(); + + lc.setTime(cursor.getLong(9)); + + LocationExtended extdloc = new LocationExtended(lc); + extdloc.setNumberOfSatellites(cursor.getInt(10)); + extdloc.setNumberOfSatellitesUsedInFix(cursor.getInt(12)); + + locationList.add(extdloc); // Add Location to list + } while (cursor.moveToNext()); + } + cursor.close(); + } + return locationList; + } + + // Getting a list of Locations associated to a specified track, with number between startNumber and endNumber + // Please note that limits both are inclusive! + public List getPlacemarksList(long TrackID, long startNumber, long endNumber) { + + List placemarkList = new ArrayList<>(); + + String selectQuery = "SELECT * FROM " + TABLE_PLACEMARKS + " WHERE " + + KEY_TRACK_ID + " = " + TrackID + " AND " + + KEY_LOCATION_NUMBER + " BETWEEN " + startNumber + " AND " + endNumber + + " ORDER BY " + KEY_LOCATION_NUMBER; + + //Log.w("myApp", "[#] DatabaseHandler.java - getLocationList(" + TrackID + ", " + startNumber + ", " +endNumber + ") ==> " + selectQuery); + + SQLiteDatabase db = this.getWritableDatabase(); + Cursor cursor = db.rawQuery(selectQuery, null); + double lcdata_double; + float lcdata_float; + + if (cursor != null) { + // looping through all rows and adding to list + if (cursor.moveToFirst()) { + do { + Location lc = new Location("DB"); + lc.setLatitude(cursor.getDouble(3)); + lc.setLongitude(cursor.getDouble(4)); + + lcdata_double = cursor.getDouble(5); + if (lcdata_double != NOT_AVAILABLE) lc.setAltitude(lcdata_double); + //else lc.removeAltitude(); + + lcdata_float = cursor.getFloat(6); + if (lcdata_float != NOT_AVAILABLE) lc.setSpeed(lcdata_float); + //else lc.removeSpeed(); + + lcdata_float = cursor.getFloat(7); + if (lcdata_float != NOT_AVAILABLE) lc.setAccuracy(lcdata_float); + //else lc.removeAccuracy(); + + lcdata_float = cursor.getFloat(8); + if (lcdata_float != NOT_AVAILABLE) lc.setBearing(lcdata_float); + //else lc.removeBearing(); + + lc.setTime(cursor.getLong(9)); + + LocationExtended extdloc = new LocationExtended(lc); + extdloc.setNumberOfSatellites(cursor.getInt(10)); + extdloc.setNumberOfSatellitesUsedInFix(cursor.getInt(13)); + extdloc.setDescription(cursor.getString(12)); + + placemarkList.add(extdloc); // Add Location to list + } while (cursor.moveToNext()); + } + cursor.close(); + } + return placemarkList; + } + + + // Getting a list of Locations associated to a specified track, with number between startNumber and endNumber + // Please note that limits both are inclusive! + public List getLatLngList(long TrackID, long startNumber, long endNumber) { + + List latlngList = new ArrayList<>(); + + String selectQuery = "SELECT " + KEY_TRACK_ID + "," + KEY_LOCATION_LATITUDE + "," + KEY_LOCATION_LONGITUDE + "," + KEY_LOCATION_NUMBER + + " FROM " + TABLE_LOCATIONS + " WHERE " + + KEY_TRACK_ID + " = " + TrackID + " AND " + + KEY_LOCATION_NUMBER + " BETWEEN " + startNumber + " AND " + endNumber + + " ORDER BY " + KEY_LOCATION_NUMBER; + + //Log.w("myApp", "[#] DatabaseHandler.java - getLocationList(" + TrackID + ", " + startNumber + ", " +endNumber + ") ==> " + selectQuery); + + SQLiteDatabase db = this.getWritableDatabase(); + Cursor cursor = db.rawQuery(selectQuery, null); + + if (cursor != null) { + // looping through all rows and adding to list + if (cursor.moveToFirst()) { + do { + LatLng latlng = new LatLng(); + latlng.Latitude = cursor.getDouble(1); + latlng.Longitude = cursor.getDouble(2); + + latlngList.add(latlng); // Add Location to list + } while (cursor.moveToNext()); + } + cursor.close(); + } + return latlngList; + } + + + // Get the total amount of Locations stored in the DB + public long getLocationsTotalCount() { + String countQuery = "SELECT * FROM " + TABLE_LOCATIONS; + SQLiteDatabase db = this.getWritableDatabase(); + Cursor cursor = db.rawQuery(countQuery, null); + long result = cursor.getCount(); + cursor.close(); + return result; + } + + + // Get the number of Locations of a Track + public long getLocationsCount(long TrackID) { + String countQuery = "SELECT * FROM " + TABLE_LOCATIONS + " WHERE " + KEY_TRACK_ID + " = " + TrackID; + SQLiteDatabase db = this.getWritableDatabase(); + Cursor cursor = db.rawQuery(countQuery, null); + long result = cursor.getCount(); + cursor.close(); + return result; + } + + + // Get last Location ID + public long getLastLocationID(long TrackID) { + SQLiteDatabase db = this.getWritableDatabase(); + long result = 0; + + String query = "SELECT " + KEY_ID + " FROM " + TABLE_LOCATIONS + + " WHERE " + KEY_TRACK_ID + " = " + TrackID + " ORDER BY " + KEY_ID + " DESC LIMIT 1"; + + //Log.w("myApp", "[#] DatabaseHandler.java - getLastLocationID(): " + query); + + Cursor cursor = db.rawQuery(query, null); + + if (cursor != null) { + //Log.w("myApp", "[#] !null"); + if (cursor.moveToFirst()) { + result = cursor.getLong(0); + } //else Log.w("myApp", "[#] DatabaseHandler.java - Location table is empty"); + cursor.close(); + } + //Log.w("myApp", "[#] RETURN getLastTrack()"); + return result; + } + + + + +// ----------------------------------------------------------------------------------------- TRACKS + + // Delete the track with the specified ID; + // The method deletes also Placemarks and Locations associated to the specified track + public void DeleteTrack(long TrackID) { + SQLiteDatabase db = this.getWritableDatabase(); + db.beginTransaction(); + db.delete(TABLE_PLACEMARKS, KEY_TRACK_ID + " = ?", + new String[] { String.valueOf(TrackID) }); // Delete track's Placemarks + db.delete(TABLE_LOCATIONS, KEY_TRACK_ID + " = ?", + new String[] { String.valueOf(TrackID) }); // Delete track's Locations + db.delete(TABLE_TRACKS, KEY_ID + " = ?", + new String[] { String.valueOf(TrackID) }); // Delete track + db.setTransactionSuccessful(); + db.endTransaction(); + + //Log.w("myApp", "[#] DatabaseHandler.java - addLocation: Location " + track.getNumberOfLocations() + " added into track " + track.getID()); + } + + + // Add a new Track, returns the TrackID + public long addTrack(Track track) { + + SQLiteDatabase db = this.getWritableDatabase(); + + ContentValues trkvalues = new ContentValues(); + trkvalues.put(KEY_TRACK_NAME, track.getName()); + trkvalues.put(KEY_TRACK_FROM, ""); + trkvalues.put(KEY_TRACK_TO, ""); + + trkvalues.put(KEY_TRACK_START_LATITUDE, track.getStart_Latitude()); + trkvalues.put(KEY_TRACK_START_LONGITUDE, track.getStart_Longitude()); + trkvalues.put(KEY_TRACK_START_ALTITUDE, track.getStart_Altitude()); + trkvalues.put(KEY_TRACK_START_ACCURACY, track.getStart_Accuracy()); + trkvalues.put(KEY_TRACK_START_SPEED, track.getStart_Speed()); + trkvalues.put(KEY_TRACK_START_TIME, track.getStart_Time()); + + trkvalues.put(KEY_TRACK_LASTFIX_TIME, track.getLastFix_Time()); + + trkvalues.put(KEY_TRACK_END_LATITUDE, track.getEnd_Latitude()); + trkvalues.put(KEY_TRACK_END_LONGITUDE, track.getEnd_Longitude()); + trkvalues.put(KEY_TRACK_END_ALTITUDE, track.getEnd_Altitude()); + trkvalues.put(KEY_TRACK_END_ACCURACY, track.getEnd_Accuracy()); + trkvalues.put(KEY_TRACK_END_SPEED, track.getEnd_Speed()); + trkvalues.put(KEY_TRACK_END_TIME, track.getEnd_Time()); + + trkvalues.put(KEY_TRACK_LASTSTEPDST_LATITUDE, track.getLastStepDistance_Latitude()); + trkvalues.put(KEY_TRACK_LASTSTEPDST_LONGITUDE, track.getLastStepDistance_Longitude()); + trkvalues.put(KEY_TRACK_LASTSTEPDST_ACCURACY, track.getLastStepDistance_Accuracy()); + + trkvalues.put(KEY_TRACK_LASTSTEPALT_ALTITUDE, track.getLastStepAltitude_Altitude()); + trkvalues.put(KEY_TRACK_LASTSTEPALT_ACCURACY, track.getLastStepAltitude_Accuracy()); + + trkvalues.put(KEY_TRACK_MIN_LATITUDE, track.getMin_Latitude()); + trkvalues.put(KEY_TRACK_MIN_LONGITUDE, track.getMin_Longitude()); + + trkvalues.put(KEY_TRACK_MAX_LATITUDE, track.getMax_Latitude()); + trkvalues.put(KEY_TRACK_MAX_LONGITUDE, track.getMax_Longitude()); + + trkvalues.put(KEY_TRACK_DURATION, track.getDuration()); + trkvalues.put(KEY_TRACK_DURATION_MOVING, track.getDuration_Moving()); + + trkvalues.put(KEY_TRACK_DISTANCE, track.getDistance()); + trkvalues.put(KEY_TRACK_DISTANCE_INPROGRESS, track.getDistanceInProgress()); + trkvalues.put(KEY_TRACK_DISTANCE_LASTALTITUDE, track.getDistanceLastAltitude()); + + trkvalues.put(KEY_TRACK_ALTITUDE_UP, track.getAltitude_Up()); + trkvalues.put(KEY_TRACK_ALTITUDE_DOWN, track.getAltitude_Down()); + trkvalues.put(KEY_TRACK_ALTITUDE_INPROGRESS, track.getAltitude_InProgress()); + + trkvalues.put(KEY_TRACK_SPEED_MAX, track.getSpeedMax()); + trkvalues.put(KEY_TRACK_SPEED_AVERAGE, track.getSpeedAverage()); + trkvalues.put(KEY_TRACK_SPEED_AVERAGEMOVING, track.getSpeedAverageMoving()); + + trkvalues.put(KEY_TRACK_NUMBEROFLOCATIONS, track.getNumberOfLocations()); + trkvalues.put(KEY_TRACK_NUMBEROFPLACEMARKS, track.getNumberOfPlacemarks()); + trkvalues.put(KEY_TRACK_TYPE, track.getType()); + + trkvalues.put(KEY_TRACK_VALIDMAP, track.getValidMap()); + + long TrackID; + // Inserting Row + TrackID = (db.insert(TABLE_TRACKS, null, trkvalues)); + + //Log.w("myApp", "[#] DatabaseHandler.java - addTrack " + TrackID); + + return TrackID; // Insert this in the track ID !!! + } + + + // Get Track + public Track getTrack(long TrackID) { + + Track track = null; + + String selectQuery = "SELECT * FROM " + TABLE_TRACKS + " WHERE " + + KEY_ID + " = " + TrackID; + + //Log.w("myApp", "[#] DatabaseHandler.java - getTrackList(" + startNumber + ", " +endNumber + ") ==> " + selectQuery); + + SQLiteDatabase db = this.getWritableDatabase(); + Cursor cursor = db.rawQuery(selectQuery, null); + + //Log.w("myApp", "[#] DatabaseHandler.java - getTrack(" + TrackID + ")"); + + if (cursor != null) { + if (cursor.moveToFirst()) { + track = new Track(); + track.FromDB(cursor.getLong(0), + cursor.getString(1), + cursor.getString(2), + cursor.getString(3), + + cursor.getDouble(4), + cursor.getDouble(5), + cursor.getDouble(6), + cursor.getFloat(7), + cursor.getFloat(8), + cursor.getLong(9), + + cursor.getLong(10), + + cursor.getDouble(11), + cursor.getDouble(12), + cursor.getDouble(13), + cursor.getFloat(14), + cursor.getFloat(15), + cursor.getLong(16), + + cursor.getDouble(17), + cursor.getDouble(18), + cursor.getFloat(19), + + cursor.getDouble(20), + cursor.getFloat(21), + + cursor.getDouble(22), + cursor.getDouble(23), + cursor.getDouble(24), + cursor.getDouble(25), + + cursor.getLong(26), + cursor.getLong(27), + + cursor.getFloat(28), + cursor.getFloat(29), + cursor.getLong(30), + + cursor.getDouble(31), + cursor.getDouble(32), + cursor.getDouble(33), + + cursor.getFloat(34), + cursor.getFloat(35), + cursor.getFloat(36), + + cursor.getLong(37), + cursor.getLong(38), + + cursor.getInt(39), + cursor.getInt(40)); + } + cursor.close(); + } + return track; + } + + + // Get last TrackID + public long getLastTrackID() { + SQLiteDatabase db = this.getWritableDatabase(); + long result = 0; + + String query = "SELECT " + KEY_ID + " FROM " + TABLE_TRACKS + " ORDER BY " + KEY_ID + " DESC LIMIT 1"; + + //Log.w("myApp", "[#] DatabaseHandler.java - getLastTrackID(): " + query); + + Cursor cursor = db.rawQuery(query, null); + + if (cursor != null) { + //Log.w("myApp", "[#] !null"); + if (cursor.moveToFirst()) { + result = cursor.getLong(0); + } //else Log.w("myApp", "[#] Tracks table is empty"); + cursor.close(); + } + //Log.w("myApp", "[#] RETURN getLastTrack()"); + //Log.w("myApp", "[#] DatabaseHandler.java - LastTrackID = " + result); + return result; + } + + + // Get last TrackID + public Track getLastTrack() { + return getTrack(getLastTrackID()); + } + + + // Getting a list of Tracks, with number between startNumber and endNumber + // Please note that limits both are inclusive! + public List getTracksList(long startNumber, long endNumber) { + + List trackList = new ArrayList<>(); + + String selectQuery = "SELECT * FROM " + TABLE_TRACKS + " WHERE " + + KEY_ID + " BETWEEN " + startNumber + " AND " + endNumber + + " ORDER BY " + KEY_ID + " DESC"; + + //Log.w("myApp", "[#] DatabaseHandler.java - getTrackList(" + startNumber + ", " +endNumber + ") ==> " + selectQuery); + + SQLiteDatabase db = this.getWritableDatabase(); + Cursor cursor = db.rawQuery(selectQuery, null); + + if (cursor != null) { + // looping through all rows and adding to list + if (cursor.moveToFirst()) { + do { + Track trk = new Track(); + trk.FromDB(cursor.getLong(0), + cursor.getString(1), + cursor.getString(2), + cursor.getString(3), + + cursor.getDouble(4), + cursor.getDouble(5), + cursor.getDouble(6), + cursor.getFloat(7), + cursor.getFloat(8), + cursor.getLong(9), + + cursor.getLong(10), + + cursor.getDouble(11), + cursor.getDouble(12), + cursor.getDouble(13), + cursor.getFloat(14), + cursor.getFloat(15), + cursor.getLong(16), + + cursor.getDouble(17), + cursor.getDouble(18), + cursor.getFloat(19), + + cursor.getDouble(20), + cursor.getFloat(21), + + cursor.getDouble(22), + cursor.getDouble(23), + cursor.getDouble(24), + cursor.getDouble(25), + + cursor.getLong(26), + cursor.getLong(27), + + cursor.getFloat(28), + cursor.getFloat(29), + cursor.getLong(30), + + cursor.getDouble(31), + cursor.getDouble(32), + cursor.getDouble(33), + + cursor.getFloat(34), + cursor.getFloat(35), + cursor.getFloat(36), + + cursor.getLong(37), + cursor.getLong(38), + + cursor.getInt(39), + cursor.getInt(40)); + + trackList.add(trk); // Add Track to list + } while (cursor.moveToNext()); + } + cursor.close(); + } + return trackList; + } + + + public void CorrectGPSWeekRollover() { + String CorrectLocationsQuery = "UPDATE " + TABLE_LOCATIONS + " SET " + KEY_LOCATION_TIME + " = " + KEY_LOCATION_TIME + " + 619315200000 WHERE " + + KEY_LOCATION_TIME + " <= 1388534400000 "; // 01/01/2014 00:00:00.000 + String CorrectPlacemarksQuery = "UPDATE " + TABLE_PLACEMARKS + " SET " + KEY_LOCATION_TIME + " = " + KEY_LOCATION_TIME + " + 619315200000 WHERE " + + KEY_LOCATION_TIME + " <= 1388534400000 "; // 01/01/2014 00:00:00.000 + String CorrectNamesQuery = "SELECT " + KEY_ID + "," + KEY_TRACK_NAME + " FROM " + TABLE_TRACKS + " WHERE " + + KEY_TRACK_NAME + " LIKE '199%'"; + + class IdAndName { + long id; + String Name; + } + + ArrayList Names = new ArrayList<>(); + + //Log.w("myApp", "[#] DatabaseHandler.java - getTrackList(" + startNumber + ", " +endNumber + ") ==> " + selectQuery); + + SQLiteDatabase db = this.getWritableDatabase(); + + // Correction of Locations + db.execSQL(CorrectLocationsQuery); + + // Correction of Placemarks + db.execSQL(CorrectPlacemarksQuery); + + + // Correction of Track Names + Cursor cursor = db.rawQuery(CorrectNamesQuery, null); + + if (cursor != null) { + int i = 0; + // looping through all rows and adding to list + if (cursor.moveToFirst()) { + do { + SimpleDateFormat SDF = new SimpleDateFormat("yyyyMMdd-HHmmss"); // date and time formatter + SDF.setTimeZone(TimeZone.getTimeZone("GMT")); + try { + Date d = SDF.parse(cursor.getString(1)); + long timeInMilliseconds = d.getTime(); + long timeInMilliseconds_Corrected = timeInMilliseconds + 619315200000L; + String Name_Corrected = SDF.format(timeInMilliseconds_Corrected); + //Log.w("myApp", "[#] GPSApplication.java - NAME CORRECTED FROM " + cursor.getString(0) + " TO " + Name_Corrected); + IdAndName IN = new IdAndName(); + IN.id = cursor.getLong(0); + IN.Name = Name_Corrected; + Names.add(IN); + } catch (ParseException ex) { + Log.v("Exception", ex.getLocalizedMessage()); + } + i++; + } while (cursor.moveToNext()); + } + Log.w("myApp", "[#] DatabaseHandler.java - CorrectGPSWeekRollover NAMES = " + i); + cursor.close(); + } + + for (IdAndName N : Names) { + Log.w("myApp", "[#] GPSApplication.java - CORRECTING TRACK " + N.id + " = " + N.Name); + db.execSQL("UPDATE " + TABLE_TRACKS + " SET " + KEY_TRACK_NAME + " = \"" + N.Name + "\" WHERE " + KEY_ID + " = " + N.id); + } + } + + + // Getting the list of all Tracks in the DB + /* NOT USED, COMMENTED OUT !! + public List getTracksList() { + + List trackList = new ArrayList(); + + String selectQuery = "SELECT * FROM " + TABLE_TRACKS + + " ORDER BY " + KEY_ID + " DESC"; + + //Log.w("myApp", "[#] DatabaseHandler.java - getTrackList() ==> " + selectQuery); + + SQLiteDatabase db = this.getWritableDatabase(); + Cursor cursor = db.rawQuery(selectQuery, null); + + if (cursor != null) { + // looping through all rows and adding to list + if (cursor.moveToFirst()) { + do { + Track trk = new Track(); + trk.FromDB(cursor.getLong(0), + cursor.getString(1), + cursor.getString(2), + cursor.getString(3), + + cursor.getDouble(4), + cursor.getDouble(5), + cursor.getDouble(6), + cursor.getFloat(7), + cursor.getFloat(8), + cursor.getLong(9), + + cursor.getLong(10), + + cursor.getDouble(11), + cursor.getDouble(12), + cursor.getDouble(13), + cursor.getFloat(14), + cursor.getFloat(15), + cursor.getLong(16), + + cursor.getDouble(17), + cursor.getDouble(18), + cursor.getFloat(19), + + cursor.getDouble(20), + cursor.getFloat(21), + + cursor.getDouble(22), + cursor.getDouble(23), + cursor.getDouble(24), + cursor.getDouble(25), + + cursor.getLong(26), + cursor.getLong(27), + + cursor.getFloat(28), + cursor.getFloat(29), + cursor.getLong(30), + + cursor.getDouble(31), + cursor.getDouble(32), + cursor.getDouble(33), + + cursor.getFloat(34), + cursor.getFloat(35), + cursor.getFloat(36), + + cursor.getLong(37), + cursor.getLong(38), + + cursor.getInt(39), + cursor.getInt(40)); + trackList.add(trk); // Add Track to list + } while (cursor.moveToNext()); + } + cursor.close(); + } + return trackList; + } */ +} \ No newline at end of file diff --git a/app/src/main/java/Satellite/EGM96.java b/app/src/main/java/Satellite/EGM96.java new file mode 100644 index 0000000..e1d9964 --- /dev/null +++ b/app/src/main/java/Satellite/EGM96.java @@ -0,0 +1,276 @@ + +package Satellite; + + +import android.util.Log; + +import org.greenrobot.eventbus.EventBus; + +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +class EGM96 { + + // ---------------------------------------------------------------------------- Singleton Class + private static EGM96 instance = new EGM96(); + + private EGM96(){} + + public static EGM96 getInstance(){ + return instance; + } + + + + private int BOUNDARY = 3; // The grid extensions (in each of the 4 sides) of the real 721 x 1440 grid + private short[][] EGMGrid = new short[BOUNDARY + 1440 + BOUNDARY][BOUNDARY + 721 + BOUNDARY]; + private boolean isEGMGridLoaded = false; + private boolean isEGMGridLoading = false; + private boolean isEGMFileCopying = false; + private String EGMFileName; + private String EGMFileNameLocalCopy; + + public double EGM96_VALUE_INVALID = -100000; + + public void LoadGridFromFile(String FileName, String FileNameLocalCopy) { + if (!isEGMGridLoaded && !isEGMGridLoading) { + isEGMGridLoading = true; + EGMFileName = FileName; + EGMFileNameLocalCopy = FileNameLocalCopy; + new Thread(new LoadEGM96Grid()).start(); + } else { + if (isEGMGridLoading) Log.w("myApp", "[#] EGM96.java - Grid is already loading, please wait"); + if (isEGMGridLoaded) Log.w("myApp", "[#] EGM96.java - Grid already loaded"); + } + } + + /* public void UnloadGrid() { + isEGMGridLoaded = false; + isEGMGridLoading = false; + //listener.onEGMGridLoaded(isEGMGridLoaded); + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACKLIST); + } +*/ + public boolean isEGMGridLoaded() { + return isEGMGridLoaded; + } + + public boolean isEGMGridLoading() { + return isEGMGridLoading; + } + + public double getEGMCorrection(double Latitude, double Longitude) { + // This function calculates and return the EGM96 altitude correction value of the input coordinates, in m; + // Input coordinates are: -90 < Latitude < 90; -180 < Longitude < 360 (android range -180 < Longitude < 180); + + if (isEGMGridLoaded) { + double Lat = 90.0 - Latitude; + double Lon = Longitude; + if (Lon < 0) Lon += 360.0; + + int ilon = (int) (Lon / 0.25) + BOUNDARY; + int ilat = (int) (Lat / 0.25) + BOUNDARY; + + try { + // Creating points for interpolation + short hc11 = EGMGrid[ilon][ilat]; + short hc12 = EGMGrid[ilon][ilat + 1]; + short hc21 = EGMGrid[ilon + 1][ilat]; + short hc22 = EGMGrid[ilon + 1][ilat + 1]; + + // Interpolation: + // Latitude + double hc1 = hc11 + (hc12 - hc11) * (Lat % 0.25) / 0.25; + double hc2 = hc21 + (hc22 - hc21) * (Lat % 0.25) / 0.25; + // Longitude + //double hc = (hc1 + (hc2 - hc1) * (Lon % 0.25) / 0.25) / 100; + //Log.w("myApp", "[#] EGM96.java - getEGMCorrection(" + Latitude + ", " + Longitude + ") = " + hc); + + return ((hc1 + (hc2 - hc1) * (Lon % 0.25) / 0.25) / 100); + } catch (ArrayIndexOutOfBoundsException e) { + return EGM96_VALUE_INVALID; + } + } + else return EGM96_VALUE_INVALID; + } + + private void copyFile(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[1024]; + int read; + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + } + + private void DeleteFile(String filename) { + File file = new File(filename); + if (file.exists ()) file.delete(); + } + + + // The Thread that loads the grid in background ------------------------------------------------ + + private class LoadEGM96Grid implements Runnable { + // Thread: Load EGM grid + + @Override + public void run() { + Thread.currentThread().setPriority(Thread.MIN_PRIORITY); + Log.w("myApp", "[#] EGM96.java - Start loading grid"); + + boolean islocalcopypresent = false; + boolean issharedcopypresent = false; + + File localfile = new File(EGMFileNameLocalCopy); + if (localfile.exists() && (localfile.length() == 2076480)) islocalcopypresent = true; + + File sharedfile = new File(EGMFileName); + if (sharedfile.exists() && (sharedfile.length() == 2076480)) issharedcopypresent = true; + + File file = new File(islocalcopypresent ? EGMFileNameLocalCopy : EGMFileName); + if (islocalcopypresent || issharedcopypresent) { + Log.w("myApp", "[#] EGM96.java - From file: " + file.getAbsolutePath()); + + FileInputStream fin; + try { + fin = new FileInputStream(file); + } catch (FileNotFoundException e) { + isEGMGridLoaded = false; + isEGMGridLoading = false; + Log.w("myApp", "[#] EGM96.java - FileNotFoundException"); + //Toast.makeText(getApplicationContext(), "Oops", Toast.LENGTH_SHORT).show(); + //e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + return; + } + BufferedInputStream bin = new BufferedInputStream(fin); + DataInputStream din = new DataInputStream(bin); + int i; + int i_lon = BOUNDARY; + int i_lat = BOUNDARY; + int count = (int) ((file.length() / 2)); + + for (i = 0; (i < count); i++) { + try { + EGMGrid[i_lon][i_lat] = din.readShort(); + i_lon++; + if (i_lon >= (1440 + BOUNDARY)) { + i_lat++; + i_lon = BOUNDARY; + } + } catch (IOException e) { + isEGMGridLoaded = false; + isEGMGridLoading = false; + Log.w("myApp", "[#] EGM96.java - IOException"); + return; + //Toast.makeText(getApplicationContext(), "Oops", Toast.LENGTH_SHORT).show(); + //e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + if (BOUNDARY > 0) { + // Fill boundaries with correct data, in order to speed up retrieving for interpolation; + // fill left + right boundaries + //Log.w("myApp", "[#] EGM96.java - LR BOUNDARIES"); + for (int ix = 0; (ix < BOUNDARY); ix++) { + for (int iy = BOUNDARY; (iy < BOUNDARY + 721); iy++) { + EGMGrid[ix][iy] = EGMGrid[ix + 1440][iy]; + EGMGrid[BOUNDARY + ix + 1440][iy] = EGMGrid[BOUNDARY + ix][iy]; + } + } + // fill top + bottom boundaries + //Log.w("myApp", "[#] EGM96.java - TOP DOWN BOUNDARIES"); + for (int iy = 0; (iy < BOUNDARY); iy++) { + for (int ix = 0; (ix < BOUNDARY + 1440 + BOUNDARY); ix++) { + if (ix > 720) { + EGMGrid[ix][iy] = EGMGrid[ix - 720][BOUNDARY + BOUNDARY - iy]; + EGMGrid[ix][BOUNDARY + iy + 721] = EGMGrid[ix - 720][BOUNDARY + 721-2 - iy]; + } + else { + EGMGrid[ix][iy] = EGMGrid[ix + 720][BOUNDARY + BOUNDARY - iy]; + EGMGrid[ix][BOUNDARY + iy + 721] = EGMGrid[ix + 720][BOUNDARY + 721-2 - iy]; + } + } + } + } + + isEGMGridLoading = false; + isEGMGridLoaded = true; + Log.w("myApp", "[#] EGM96.java - Grid Successfully Loaded: " + file.getAbsolutePath()); + //Toast.makeText(getApplicationContext(), "EGM96 correction grid loaded", Toast.LENGTH_SHORT).show(); + + if (issharedcopypresent) { + if (!islocalcopypresent) new Thread(new CopyEGM96Grid()).start(); + else { + DeleteFile(EGMFileName); // Delete the EGM file from the shared folder + Log.w("myApp", "[#] EGM96.java - EGM File already present into FilesDir. File deleted from shared folder"); + } + } + + } else { + isEGMGridLoading = false; + isEGMGridLoaded = false; + if (!file.exists()) { Log.w("myApp", "[#] EGM96.java - File not found"); } + if (!file.canRead()) { Log.w("myApp", "[#] EGM96.java - Cannot read file"); } + if (file.length() != 2076480) { + Log.w("myApp", "[#] EGM96.java - File has invalid length: " + file.length());} + //Toast.makeText(getApplicationContext(), "EGM96 correction not available", Toast.LENGTH_SHORT).show(); + } + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACKLIST); + //listener.onEGMGridLoaded(isEGMGridLoaded); + } + } + + // The Thread that copies the EGM grid in FilesDir (in background) ----------------------------- + + private class CopyEGM96Grid implements Runnable { + // Thread: Copy the EGM grid in FilesDir + + @Override + public void run() { + Thread.currentThread().setPriority(Thread.MIN_PRIORITY); + Log.w("myApp", "[#] EGM96.java - Copy EGM96 Grid into FilesDir"); + + if (isEGMFileCopying) return; + + isEGMFileCopying = true; + + File sd_cpy = new File(EGMFileNameLocalCopy); + if (sd_cpy.exists()) sd_cpy.delete(); + + File sd_old = new File(EGMFileName); + if (sd_old.exists()) { + InputStream in = null; + OutputStream out = null; + try { + in = new FileInputStream(EGMFileName); + out = new FileOutputStream(EGMFileNameLocalCopy); + copyFile(in, out); + in.close(); + in = null; + out.flush(); + out.close(); + out = null; + Log.w("myApp", "[#] EGM96.java - EGM File copy completed"); + DeleteFile(EGMFileName); // Delete the EGM file from the shared folder + Log.w("myApp", "[#] EGM96.java - EGM File deleted from shared folder"); + + } catch(Exception e) { + Log.w("MyApp", "[#] EGM96.java - Unable to make local copy of EGM file: " + e.getMessage()); + } + } + + isEGMFileCopying = false; + } + } +} diff --git a/app/src/main/java/Satellite/EventBusMSG.java b/app/src/main/java/Satellite/EventBusMSG.java new file mode 100644 index 0000000..7eeacfd --- /dev/null +++ b/app/src/main/java/Satellite/EventBusMSG.java @@ -0,0 +1,32 @@ + +package Satellite; + +public class EventBusMSG { + + static final short APP_RESUME = 1; // Sent to components on app resume + static final short APP_PAUSE = 2; // Sent to components on app pause + static final short NEW_TRACK = 3; // Request to create a new track + static final short UPDATE_FIX = 4; // Notify that a new fix is available + static final short UPDATE_TRACK = 5; // Notify that the current track stats are updated + static final short UPDATE_TRACKLIST = 6; // Notify that the tracklist is changed + static final short UPDATE_SETTINGS = 7; // Tell that settings are changed + static final short REQUEST_ADD_PLACEMARK = 8; // The user ask to add a placemark + static final short ADD_PLACEMARK = 9; // The placemark is available + static final short APPLY_SETTINGS = 10; // The new settings must be applied + static final short TOAST_TRACK_EXPORTED = 11; // The exporter has finished to export the track, shows toast + static final short TOAST_STORAGE_PERMISSION_REQUIRED= 12; // The Storage permission is required + static final short UPDATE_JOB_PROGRESS = 13; // Update the progress of the current Job + static final short NOTIFY_TRACKS_DELETED = 14; // Notify that some tracks are deleted + static final short UPDATE_ACTIONBAR = 15; // Notify that the actionbar must be updated + static final short REFRESH_TRACKLIST = 16; // Refresh the tracklist, without update it from DB + + static final short TRACKLIST_DESELECT = 24; // The user deselect (into the tracklist) the track with a given id + static final short TRACKLIST_SELECT = 25; // The user select (into the tracklist) the track with a given id + static final short INTENT_SEND = 26; // Request to share + static final short TOAST_UNABLE_TO_WRITE_THE_FILE = 27; // Exporter fails to export the Track (given id) + + static final short ACTION_BULK_DELETE_TRACKS = 40; // Delete the selected tracks + static final short ACTION_BULK_EXPORT_TRACKS = 41; // Export the selected tracks + static final short ACTION_BULK_VIEW_TRACKS = 42; // View the selected tracks + static final short ACTION_BULK_SHARE_TRACKS = 43; // Share the selected tracks +} diff --git a/app/src/main/java/Satellite/ExportingTask.java b/app/src/main/java/Satellite/ExportingTask.java new file mode 100644 index 0000000..ace345f --- /dev/null +++ b/app/src/main/java/Satellite/ExportingTask.java @@ -0,0 +1,58 @@ + +package Satellite; + +public class ExportingTask { + + static final short STATUS_PENDING = 0; // Task not yet started + static final short STATUS_RUNNING = 1; // Task is running... + static final short STATUS_ENDED_SUCCESS = 2; // Task ended with success + static final short STATUS_ENDED_FAILED = 3; // Task failed to export + + private long id = 0; + private long NumberOfPoints_Total = 0; + private long NumberOfPoints_Processed = 0; + private short Status = STATUS_PENDING; + private String Name = ""; + + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getNumberOfPoints_Total() { + return NumberOfPoints_Total; + } + + public void setNumberOfPoints_Total(long numberOfPoints_Total) { + NumberOfPoints_Total = numberOfPoints_Total; + } + + public long getNumberOfPoints_Processed() { + return NumberOfPoints_Processed; + } + + public void setNumberOfPoints_Processed(long numberOfPoints_Processed) { + NumberOfPoints_Processed = numberOfPoints_Processed; + } + + public short getStatus() { + return Status; + } + + public void setStatus(short status) { + Status = status; + } + + public String getName() { + return Name; + } + + public void setName(String name) { + Name = name; + } +} + diff --git a/app/src/main/java/Satellite/FragmentGPSFix.java b/app/src/main/java/Satellite/FragmentGPSFix.java new file mode 100644 index 0000000..82f3445 --- /dev/null +++ b/app/src/main/java/Satellite/FragmentGPSFix.java @@ -0,0 +1,290 @@ + +package Satellite; + +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.method.ScrollingMovementMethod; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TableLayout; +import android.widget.TextView; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import fr.geolabs.dev.mapmint4me.R; + +public class FragmentGPSFix extends Fragment { + + private static final int NOT_AVAILABLE = -100000; + + private final int GPS_DISABLED = 0; + private final int GPS_OUTOFSERVICE = 1; + private final int GPS_TEMPORARYUNAVAILABLE = 2; + private final int GPS_SEARCHING = 3; + private final int GPS_STABILIZING = 4; + private final int GPS_OK = 5; + + private PhysicalDataFormatter phdformatter = new PhysicalDataFormatter(); + + private FrameLayout FLGPSFix; + + private TextView TVLatitude; + private TextView TVLongitude; + private TextView TVLatitudeUM; + private TextView TVLongitudeUM; + private TextView TVAltitude; + private TextView TVAltitudeUM; + private TextView TVSpeed; + private TextView TVSpeedUM; + private TextView TVBearing; + private TextView TVAccuracy; + private TextView TVAccuracyUM; + private TextView TVGPSFixStatus; + private TextView TVDirectionUM; + private TextView TVTime; + public TextView TVSatellites; + public TextView TVSatellites1; + private TableLayout TLCoordinates; + private TableLayout TLAltitude; + private TableLayout TLSpeed; + private TableLayout TLBearing; + private TableLayout TLAccuracy; + private TableLayout TLTime; + public TableLayout TLSatellites; + + private LinearLayout LLTimeSatellites; + + private PhysicalData phdLatitude; + private PhysicalData phdLongitude; + private PhysicalData phdAltitude; + private PhysicalData phdSpeed; + private PhysicalData phdBearing; + private PhysicalData phdAccuracy; + private PhysicalData phdTime; + + final GPSApplication gpsApplication = GPSApplication.getInstance(); + + public FragmentGPSFix() { + // Required empty public constructor + } + + @Subscribe (threadMode = ThreadMode.MAIN) + public void onEvent(Short msg) { + if (msg == EventBusMSG.UPDATE_FIX) { + Update(); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + View view = inflater.inflate(R.layout.fragment_gpsfix, container, false); + + // FrameLayouts + FLGPSFix = view.findViewById(R.id.id_fragmentgpsfixFrameLayout); + + // TextViews + TVLatitude = view.findViewById(R.id.id_textView_Latitude); + TVLongitude = view.findViewById(R.id.id_textView_Longitude); + TVLatitudeUM = view.findViewById(R.id.id_textView_LatitudeUM); + TVLongitudeUM = view.findViewById(R.id.id_textView_LongitudeUM); + TVAltitude = view.findViewById(R.id.id_textView_Altitude); + TVAltitudeUM = view.findViewById(R.id.id_textView_AltitudeUM); + TVSpeed = view.findViewById(R.id.id_textView_Speed); + TVSpeedUM = view.findViewById(R.id.id_textView_SpeedUM); + TVBearing = view.findViewById(R.id.id_textView_Bearing); + TVAccuracy = view.findViewById(R.id.id_textView_Accuracy); + TVAccuracyUM = view.findViewById(R.id.id_textView_AccuracyUM); + TVGPSFixStatus = view.findViewById(R.id.id_textView_GPSFixStatus); + TVDirectionUM = view.findViewById(R.id.id_textView_BearingUM); + TVTime = view.findViewById(R.id.id_textView_Time); + TVSatellites = view.findViewById(R.id.id_textView_satellite1); + TVSatellites1 = view.findViewById(R.id.id_textView_satellite2); + TVSatellites1.setMovementMethod(new ScrollingMovementMethod()); + + + // TableLayouts + TLCoordinates = view.findViewById(R.id.id_TableLayout_Coordinates) ; + TLAltitude = view.findViewById(R.id.id_TableLayout_Altitude); + TLSpeed = view.findViewById(R.id.id_TableLayout_Speed); + TLBearing = view.findViewById(R.id.id_TableLayout_Bearing); + TLAccuracy = view.findViewById(R.id.id_TableLayout_Accuracy); + TLTime = view.findViewById(R.id.id_TableLayout_Time); + TLSatellites = view.findViewById(R.id.id_TableLayout_Satellites); + + // LinearLayouts + LLTimeSatellites = view.findViewById(R.id.id_linearLayout_Time_Satellites); + + TVGPSFixStatus.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (GPSStatus == GPS_DISABLED) { + if (GPSApplication.getInstance().getLocationSettingsFlag()) { + // This is the second click + GPSApplication.getInstance().setLocationSettingsFlag(false); + // Go to Settings screen + Intent callGPSSettingIntent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); + if (callGPSSettingIntent != null) startActivityForResult(callGPSSettingIntent, 0); + } else { + GPSApplication.getInstance().setLocationSettingsFlag(true); // Start the timer + } + } + } + }); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + + // Workaround for Nokia Devices, Android 9 + // https://github.com/BasicAirData/GPSLogger/issues/77 + if (EventBus.getDefault().isRegistered(this)) { + //Log.w("myApp", "[#] FragmentGPSFix.java - EventBus: FragmentGPSFix already registered"); + EventBus.getDefault().unregister(this); + } + + EventBus.getDefault().register(this); + Update(); + } + + @Override + public void onPause() { + EventBus.getDefault().unregister(this); + super.onPause(); + } + + + private LocationExtended location; + private double AltitudeManualCorrection; + private int prefDirections; + private int GPSStatus = GPS_DISABLED; + private boolean EGMAltitudeCorrection; + private boolean isValidAltitude; + + public void Update() { + //Log.w("myApp", "[#] FragmentGPSFix.java - Update(Location location)"); + location = gpsApplication.getCurrentLocationExtended(); + AltitudeManualCorrection = gpsApplication.getPrefAltitudeCorrection(); + prefDirections = gpsApplication.getPrefShowDirections(); + GPSStatus = gpsApplication.getGPSStatus(); + EGMAltitudeCorrection = gpsApplication.getPrefEGM96AltitudeCorrection(); + if (isAdded()) { + if ((location != null) && (GPSStatus == GPS_OK)) { + + phdLatitude = phdformatter.format(location.getLatitude(), PhysicalDataFormatter.FORMAT_LATITUDE); + phdLongitude = phdformatter.format(location.getLongitude(), PhysicalDataFormatter.FORMAT_LONGITUDE); + phdAltitude = phdformatter.format(location.getAltitudeCorrected(AltitudeManualCorrection, EGMAltitudeCorrection), PhysicalDataFormatter.FORMAT_ALTITUDE); + phdSpeed = phdformatter.format(location.getSpeed(), PhysicalDataFormatter.FORMAT_SPEED); + phdBearing = phdformatter.format(location.getBearing(), PhysicalDataFormatter.FORMAT_BEARING); + phdAccuracy = phdformatter.format(location.getAccuracy(), PhysicalDataFormatter.FORMAT_ACCURACY); + phdTime = phdformatter.format(location.getTime(), PhysicalDataFormatter.FORMAT_TIME); + + TVLatitude.setText(phdLatitude.Value); + TVLongitude.setText(phdLongitude.Value); + TVLatitudeUM.setText(phdLatitude.UM); + TVLongitudeUM.setText(phdLongitude.UM); + //TVAltitude.setText(phdAltitude.Value);TVAltitudeUM.setText(phdAltitude.UM); + //TVSpeed.setText(phdSpeed.Value); + //TVSpeedUM.setText(phdSpeed.UM); + //TVBearing.setText(phdBearing.Value); + //TVAccuracy.setText(phdAccuracy.Value); + //TVAccuracyUM.setText(phdAccuracy.UM); + // TVTime.setText(phdTime.Value); + TVSatellites.setText(location.getNumberOfSatellitesUsedInFix() != NOT_AVAILABLE ? location.getNumberOfSatellitesUsedInFix() + "/" + location.getNumberOfSatellites() : ""); + int i =0; + while(i<10) { + + TVSatellites1.setText(location.getSatellite_info()); + i = i + 1; + } + // Colorize the Altitude textview depending on the altitude EGM Correction + isValidAltitude = EGMAltitudeCorrection && (location.getAltitudeEGM96Correction() != NOT_AVAILABLE); + TVAltitude.setTextColor(isValidAltitude ? getResources().getColor(R.color.textColorPrimary) : getResources().getColor(R.color.textColorSecondary)); + TVAltitudeUM.setTextColor(isValidAltitude ? getResources().getColor(R.color.textColorPrimary) : getResources().getColor(R.color.textColorSecondary)); + + TVGPSFixStatus.setVisibility(View.GONE); + + TVDirectionUM.setVisibility(prefDirections == 0 ? View.GONE : View.VISIBLE); + + TLCoordinates.setVisibility(phdLatitude.Value.equals("") ? View.INVISIBLE : View.VISIBLE); + TLAltitude.setVisibility(phdAltitude.Value.equals("") ? View.INVISIBLE : View.VISIBLE); + TLSpeed.setVisibility(phdSpeed.Value.equals("") ? View.INVISIBLE : View.VISIBLE); + TLBearing.setVisibility(phdBearing.Value.equals("") ? View.INVISIBLE : View.VISIBLE); + TLAccuracy.setVisibility(phdAccuracy.Value.equals("") ? View.INVISIBLE : View.VISIBLE); + TLTime.setVisibility(View.VISIBLE); + TLSatellites.setVisibility(location.getNumberOfSatellitesUsedInFix() == NOT_AVAILABLE ? View.INVISIBLE : View.VISIBLE); + + FLGPSFix.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + FLGPSFix.removeOnLayoutChangeListener(this); + + int ViewHeight = TLTime.getMeasuredHeight() + (int)(6*getResources().getDisplayMetrics().density); + int LayoutHeight = FLGPSFix.getHeight() - (int)(6*getResources().getDisplayMetrics().density); + //Log.w("myApp", "[#]"); + //Log.w("myApp", "[#] -----------------------------------"); + boolean isTimeAndSatellitesVisible; + if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ + isTimeAndSatellitesVisible = LayoutHeight >= 6*ViewHeight; + //Log.w("myApp", "[#] " + LayoutHeight + " / " + 6*ViewHeight + " -> " + isTimeAndSatellitesVisible); + } else { + isTimeAndSatellitesVisible = LayoutHeight >= 4*ViewHeight; + //Log.w("myApp", "[#] " + LayoutHeight + " / " + 4*ViewHeight + " -> " + isTimeAndSatellitesVisible); + } + LLTimeSatellites.setVisibility(isTimeAndSatellitesVisible ? View.VISIBLE : View.GONE); + + //Log.w("myApp", "[#] -----------------------------------"); + //Log.w("myApp", "[#] Available Height = " + LayoutHeight + " px"); + //Log.w("myApp", "[#] Density = " + getResources().getDisplayMetrics().density); + //Log.w("myApp", "[#] Tile Height = " + ViewHeight + " px"); + } + }); + + } else { + TLCoordinates.setVisibility(View.INVISIBLE); + TLAltitude.setVisibility(View.INVISIBLE); + TLSpeed.setVisibility(View.INVISIBLE); + TLBearing.setVisibility(View.INVISIBLE); + TLAccuracy.setVisibility(View.INVISIBLE); + TLTime.setVisibility(View.INVISIBLE); + TLSatellites.setVisibility(View.INVISIBLE); + + TVGPSFixStatus.setVisibility(View.VISIBLE); + switch (GPSStatus) { + case GPS_DISABLED: + TVGPSFixStatus.setText(R.string.gps_disabled_with_hint); + break; + case GPS_OUTOFSERVICE: + TVGPSFixStatus.setText(R.string.gps_out_of_service); + break; + case GPS_TEMPORARYUNAVAILABLE: + //TVGPSFixStatus.setText(R.string.gps_temporary_unavailable); + //break; + case GPS_SEARCHING: + TVGPSFixStatus.setText(R.string.gps_searching); + break; + case GPS_STABILIZING: + TVGPSFixStatus.setText(R.string.gps_stabilizing); + break; + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/Satellite/GPSActivity.java b/app/src/main/java/Satellite/GPSActivity.java new file mode 100644 index 0000000..f778f23 --- /dev/null +++ b/app/src/main/java/Satellite/GPSActivity.java @@ -0,0 +1,583 @@ + +package Satellite; + +import android.Manifest; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.provider.Settings; +import android.support.annotation.NonNull; +import android.support.design.widget.BottomSheetBehavior; +import android.support.design.widget.TabLayout; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewPager; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.preference.PreferenceManager; +import android.support.v7.view.ActionMode; +import android.support.v7.view.ContextThemeWrapper; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.WindowManager; +import android.widget.Toast; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import fr.geolabs.dev.mapmint4me.R; + +public class GPSActivity extends AppCompatActivity { + + private static final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1; + + private final GPSApplication GPSApp = GPSApplication.getInstance(); + + private Toolbar toolbar; + private TabLayout tabLayout; + private ViewPager viewPager; + private ActionMode actionMode; + private View bottomSheet; + private MenuItem menutrackfinished = null; + private int activeTab = 1; + final Context context = this; + + private boolean prefKeepScreenOn = true; + private boolean show_toast_grant_storage_permission = false; + private int theme; + + private BottomSheetBehavior mBottomSheetBehavior; + + Toast ToastClickAgain; + + + @Override + public void onRestart(){ + Log.w("myApp", "[#] " + this + " - onRestart()"); + + if (Integer.valueOf(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("prefColorTheme", "2")) != theme) { + Log.w("myApp", "[#] GPSActivity.java - it needs to be recreated (Theme changed)"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // Normal behaviour for Android 5 + + this.recreate(); + } else { + // Workaround to a bug on Android 4.4.X platform (google won't fix because Android 4.4 is obsolete) + // Android 4.4.X: taskAffinity & launchmode='singleTask' violating Activity's lifecycle + // https://issuetracker.google.com/issues/36998700 + finish(); + startActivity(new Intent(this, getClass())); + } + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + } + super.onRestart(); + } + + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.w("myApp", "[#] " + this + " - onCreate()"); + + setTheme(R.style.MyMaterialTheme); + theme = Integer.valueOf(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("prefColorTheme", "2")); + + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_gps); + toolbar = findViewById(R.id.id_toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(false); + viewPager = findViewById(R.id.id_viewpager); + viewPager.setOffscreenPageLimit(3); + + setupViewPager(viewPager); + + tabLayout = findViewById(R.id.id_tablayout); + tabLayout.setTabMode(TabLayout.MODE_FIXED); + tabLayout.setupWithViewPager(viewPager); + + tabLayout.addOnTabSelectedListener( + new TabLayout.ViewPagerOnTabSelectedListener(viewPager) { + @Override + public void onTabSelected(TabLayout.Tab tab) { + super.onTabSelected(tab); + activeTab = tab.getPosition(); + GPSApp.setGPSActivity_activeTab(activeTab); + updateBottomSheetPosition(); + ActivateActionModeIfNeeded(); + } + }); + + bottomSheet = findViewById( R.id.id_bottomsheet ); + + mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet); + mBottomSheetBehavior.setHideable (false); + + ToastClickAgain = Toast.makeText(this, getString(R.string.toast_track_finished_click_again), Toast.LENGTH_SHORT); + } + + + @Override + public void onStart() { + Log.w("myApp", "[#] " + this + " - onStart()"); + super.onStart(); + activeTab = tabLayout.getSelectedTabPosition(); + System.out.println(activeTab); + GPSApp.setGPSActivity_activeTab(activeTab); + } + + + @Override + public void onStop() { + Log.w("myApp", "[#] " + this + " - onStop()"); + super.onStop(); + } + + + @Override + public void onResume() { + Log.w("myApp", "[#] " + this + " - onResume()"); + super.onResume(); + + // Workaround for Nokia Devices, Android 9 + // https://github.com/BasicAirData/GPSLogger/issues/77 + if (EventBus.getDefault().isRegistered(this)) { + //Log.w("myApp", "[#] GPSActivity.java - EventBus: GPSActivity already registered"); + EventBus.getDefault().unregister(this); + } + + EventBus.getDefault().register(this); + LoadPreferences(); + EventBus.getDefault().post(EventBusMSG.APP_RESUME); + if (menutrackfinished != null) menutrackfinished.setVisible(!GPSApp.getCurrentTrack().getName().equals("")); + + // Check for Location runtime Permissions (for Android 23+) + if (!GPSApp.isLocationPermissionChecked()) { + CheckLocationPermission(); + GPSApp.setLocationPermissionChecked(true); + } + + ActivateActionModeIfNeeded(); + + if (GPSApp.FlagExists(GPSApp.FLAG_RECORDING) && !GPSApp.getRecording()) { + // The app is crashed in background + Log.w("myApp", "[#] GPSActivity.java - THE APP HAS BEEN KILLED IN BACKGROUND DURING A RECORDING !!!"); + GPSApp.FlagRemove(GPSApp.FLAG_RECORDING); + + AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.StyledDialog)); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + builder.setMessage(getResources().getString(R.string.dlg_app_killed) + "\n\n" + getResources().getString(R.string.dlg_app_killed_description)); + builder.setNeutralButton(R.string.open_android_app_settings, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", getPackageName(), null); + intent.setData(uri); + try { + startActivity(intent); + } catch (Exception e) { + //Log.w("myApp", "[#] GPSActivity.java - Unable to open the settings screen"); + } + dialog.dismiss(); + } + }); + } + else builder.setMessage(getResources().getString(R.string.dlg_app_killed)); + builder.setIcon(android.R.drawable.ic_menu_info_details); + builder.setPositiveButton(R.string.about_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + } + + if (GPSApp.isJustStarted() && (GPSApp.getCurrentTrack().getNumberOfLocations() + GPSApp.getCurrentTrack().getNumberOfPlacemarks() > 0)) { + Toast.makeText(this.context, getString(R.string.toast_active_track_not_empty), Toast.LENGTH_LONG).show(); + GPSApp.setJustStarted(false); + } else GPSApp.setJustStarted(false); + + if (show_toast_grant_storage_permission) { + Toast.makeText(this.context, getString(R.string.please_grant_storage_permission), Toast.LENGTH_LONG).show(); + show_toast_grant_storage_permission = false; + } + } + + + @Override + public void onPause() { + Log.w("myApp", "[#] " + this + " - onPause()"); + EventBus.getDefault().post(EventBusMSG.APP_PAUSE); + EventBus.getDefault().unregister(this); + super.onPause(); + } + + + @Override + public void onBackPressed() { + ShutdownApp(); + } + + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + updateBottomSheetPosition(); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main_menu, menu); + return true; + } + + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menutrackfinished = menu.findItem(R.id.action_track_finished); + menutrackfinished.setVisible(!GPSApp.getCurrentTrack().getName().equals("")); + return true; + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + + /* if (id == R.id.action_settings) { + GPSApp.setHandlerTimer(60000); + // Intent intent = new Intent(this, SettingsActivity.class); + // startActivity(intent); + return true; + } */ + if (id == R.id.action_track_finished) { + if (GPSApp.getNewTrackFlag()) { + // This is the second click + GPSApp.setNewTrackFlag(false); + GPSApp.setRecording(false); + EventBus.getDefault().post(EventBusMSG.NEW_TRACK); + ToastClickAgain.cancel(); + Toast.makeText(this, getString(R.string.toast_track_saved_into_tracklist), Toast.LENGTH_SHORT).show(); + } else { + // This is the first click + GPSApp.setNewTrackFlag(true); // Start the timer + ToastClickAgain.show(); + } + return true; + } + /* if (id == R.id.action_about) { + // Show About Dialog + FragmentManager fm = getSupportFragmentManager(); + FragmentAboutDialog aboutDialog = new FragmentAboutDialog(); + aboutDialog.show(fm, ""); + return true; + } */ + /* if (id == R.id.action_shutdown) { + ShutdownApp(); + return true; + }*/ + return super.onOptionsItemSelected(item); + } + + + private void updateBottomSheetPosition() { + activeTab = tabLayout.getSelectedTabPosition(); + if (activeTab != 2) { + mBottomSheetBehavior.setPeekHeight(1); + mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); + //Log.w("myApp", "[#] GPSActivity.java - mBottomSheetBehavior.setPeekHeight(" + bottomSheet.getHeight() +");"); + mBottomSheetBehavior.setPeekHeight(bottomSheet.getHeight()); + } else { + mBottomSheetBehavior.setPeekHeight(1); + mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED) ; + } + } + + + private void setupViewPager(ViewPager viewPager) { + ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); + adapter.addFragment(new FragmentGPSFix(), getString(R.string.tab_gpsfix1)); + //adapter.addFragment(new FragmentTrack(), getString(R.string.tab_track)); + //adapter.addFragment(new FragmentTracklist(), getString(R.string.tab_tracklist)); + viewPager.setAdapter(adapter); + } + + class ViewPagerAdapter extends FragmentPagerAdapter { + private final List mFragmentList = new ArrayList<>(); + private final List mFragmentTitleList = new ArrayList<>(); + + public ViewPagerAdapter(FragmentManager manager) { + super(manager); + } + + @Override + public Fragment getItem(int position) { + return mFragmentList.get(position); + } + + @Override + public int getCount() { + return mFragmentList.size(); + } + + public void addFragment(Fragment fragment, String title) { + mFragmentList.add(fragment); + mFragmentTitleList.add(title); + } + + @Override + public CharSequence getPageTitle(int position) { + return mFragmentTitleList.get(position); + } + } + /* + @Subscribe + public void onEvent(EventBusMSGNormal msg) { + switch (msg.MSGType) { + case EventBusMSG.TRACKLIST_SELECT: + case EventBusMSG.TRACKLIST_DESELECT: + ActivateActionModeIfNeeded(); + } + } + */ + @Subscribe + public void onEvent(Short msg) { + switch (msg) { + case EventBusMSG.REQUEST_ADD_PLACEMARK: + // Show Placemark Dialog + FragmentManager fm = getSupportFragmentManager(); + //FragmentPlacemarkDialog placemarkDialog = new FragmentPlacemarkDialog(); + //placemarkDialog.show(fm, ""); + break; + case EventBusMSG.UPDATE_TRACKLIST: + case EventBusMSG.NOTIFY_TRACKS_DELETED: + ActivateActionModeIfNeeded(); + break; + case EventBusMSG.APPLY_SETTINGS: + LoadPreferences(); + break; + + case EventBusMSG.UPDATE_TRACK: + runOnUiThread(new Runnable() { + @Override + public void run() { + if (menutrackfinished != null) + menutrackfinished.setVisible(!GPSApp.getCurrentTrack().getName().equals("")); + } + }); + break; + case EventBusMSG.TOAST_TRACK_EXPORTED: + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(context, getString(R.string.toast_track_exported), Toast.LENGTH_LONG).show(); + } + }); + break; + case EventBusMSG.TOAST_STORAGE_PERMISSION_REQUIRED: + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(context, getString(R.string.please_grant_storage_permission), Toast.LENGTH_LONG).show(); + } + }); + break; + case EventBusMSG.TOAST_UNABLE_TO_WRITE_THE_FILE: + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(context, getString(R.string.export_unable_to_write_file), Toast.LENGTH_LONG).show(); + } + }); + } + } + + private void LoadPreferences() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + prefKeepScreenOn = preferences.getBoolean("prefKeepScreenOn", true); + if (prefKeepScreenOn) getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + else getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + private void ShutdownApp() + { + if ((GPSApp.getCurrentTrack().getNumberOfLocations() > 0) + || (GPSApp.getCurrentTrack().getNumberOfPlacemarks() > 0) + || (GPSApp.getRecording()) + || (GPSApp.getPlacemarkRequest())) { + + AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.StyledDialog)); + builder.setMessage(getResources().getString(R.string.message_exit_finalizing)); + builder.setIcon(android.R.drawable.ic_menu_info_details); + builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + GPSApp.setRecording(false); + GPSApp.setPlacemarkRequest(false); + EventBus.getDefault().post(EventBusMSG.NEW_TRACK); + GPSApp.StopAndUnbindGPSService(); + GPSApp.setLocationPermissionChecked(false); + + dialog.dismiss(); + GPSApp.setJustStarted(true); + finish(); + } + }); + builder.setNeutralButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }); + builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + GPSApp.setRecording(false); + GPSApp.setPlacemarkRequest(false); + GPSApp.StopAndUnbindGPSService(); + GPSApp.setLocationPermissionChecked(false); + + dialog.dismiss(); + GPSApp.setJustStarted(true); + finish(); + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + } else { + GPSApp.setRecording(false); + GPSApp.setPlacemarkRequest(false); + GPSApp.StopAndUnbindGPSService(); + GPSApp.setLocationPermissionChecked(false); + + finish(); + } + } + + private void ActivateActionModeIfNeeded() { + runOnUiThread(new Runnable() { + @Override + public void run() { + if ((GPSApp.getNumberOfSelectedTracks() > 0) && (activeTab == 2)) { + if (actionMode == null) + // actionMode = (startSupportActionMode(new ToolbarActionMode())); + actionMode.setTitle(GPSApp.getNumberOfSelectedTracks() > 1 ? String.valueOf(GPSApp.getNumberOfSelectedTracks()) : ""); + } else if (actionMode != null) { + actionMode.finish(); + actionMode = null; + } + } + }); + } + + + public boolean CheckLocationPermission() { + Log.w("myApp", "[#] GPSActivity.java - Check Location Permission..."); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + Log.w("myApp", "[#] GPSActivity.java - Location Permission granted"); + return true; // Permission Granted + } else { + Log.w("myApp", "[#] GPSActivity.java - Location Permission denied"); + boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION); + if (showRationale || !GPSApp.isLocationPermissionChecked()) { + Log.w("myApp", "[#] GPSActivity.java - Location Permission denied, need new check"); + List listPermissionsNeeded = new ArrayList<>(); + listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION); + ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]) , REQUEST_ID_MULTIPLE_PERMISSIONS); + } + return false; + } + } + + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + switch (requestCode) { + case REQUEST_ID_MULTIPLE_PERMISSIONS: { + Map perms = new HashMap<>(); + + if (grantResults.length > 0) { + // Fill with actual results from user + for (int i = 0; i < permissions.length; i++) perms.put(permissions[i], grantResults[i]); + // Check for permissions + if (perms.containsKey(Manifest.permission.ACCESS_FINE_LOCATION)) { + if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + Log.w("myApp", "[#] GPSActivity.java - ACCESS_FINE_LOCATION = PERMISSION_GRANTED; setGPSLocationUpdates!"); + GPSApp.setGPSLocationUpdates(false); + GPSApp.setGPSLocationUpdates(true); + GPSApp.updateGPSLocationFrequency(); + } else { + Log.w("myApp", "[#] GPSActivity.java - ACCESS_FINE_LOCATION = PERMISSION_DENIED"); + } + } + + if (perms.containsKey(Manifest.permission.INTERNET)) { + if (perms.get(Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED) { + Log.w("myApp", "[#] GPSActivity.java - INTERNET = PERMISSION_GRANTED"); + } else { + Log.w("myApp", "[#] GPSActivity.java - INTERNET = PERMISSION_DENIED"); + } + } + + if (perms.containsKey(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + if (perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + Log.w("myApp", "[#] GPSActivity.java - WRITE_EXTERNAL_STORAGE = PERMISSION_GRANTED"); + // ---------------------------------------------------- Create the Directories if not exist + File sd = new File(Environment.getExternalStorageDirectory() + "/GPSLogger"); + if (!sd.exists()) { + sd.mkdir(); + } + sd = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); + if (!sd.exists()) { + sd.mkdir(); + } + sd = new File(getApplicationContext().getFilesDir() + "/Thumbnails"); + if (!sd.exists()) { + sd.mkdir(); + } + EGM96 egm96 = EGM96.getInstance(); + if (egm96 != null) { + if (!egm96.isEGMGridLoaded()) { + //Log.w("myApp", "[#] GPSApplication.java - Loading EGM Grid..."); + egm96.LoadGridFromFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/WW15MGH.DAC", getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); + } + } + + if (GPSApp.getJobsPending() > 0) GPSApp.ExecuteJob(); + + } else { + Log.w("myApp", "[#] GPSActivity.java - WRITE_EXTERNAL_STORAGE = PERMISSION_DENIED"); + if (GPSApp.getJobsPending() > 0) { + // Shows toast "Unable to write the file" + show_toast_grant_storage_permission = true; + EventBus.getDefault().post(EventBusMSG.TOAST_STORAGE_PERMISSION_REQUIRED); + GPSApp.setJobsPending(0); + } + } + } + } + break; + } + default: { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/Satellite/GPSActivity_sat.java b/app/src/main/java/Satellite/GPSActivity_sat.java new file mode 100644 index 0000000..6a29428 --- /dev/null +++ b/app/src/main/java/Satellite/GPSActivity_sat.java @@ -0,0 +1,133 @@ +package Satellite; + + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.location.Address; +import android.location.Geocoder; +import android.location.Location; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AppCompatActivity; +import android.text.Html; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import com.google.android.gms.location.FusedLocationProviderClient; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.Task; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +import fr.geolabs.dev.mapmint4me.R; + +public class GPSActivity_sat extends AppCompatActivity { + + Button btLocation,btLocation1; + TextView textView1, textView2, textView3, textView4, textView5; + FusedLocationProviderClient fusedLocationProviderClient; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.layout); + + btLocation = findViewById(R.id.bt_location); + btLocation1 = findViewById(R.id.bt_location1); + textView1 = findViewById(R.id.text_view1); + textView2 = findViewById(R.id.text_view2); + textView3 = findViewById(R.id.text_view3); + textView4 = findViewById(R.id.text_view4); + textView5 = findViewById(R.id.text_view5); + + fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this); + + btLocation.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + if (ActivityCompat.checkSelfPermission(GPSActivity_sat.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + getLocation(); + } else { + ActivityCompat.requestPermissions(GPSActivity_sat.this, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 44); + } + + } + }); + + btLocation1.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(GPSActivity_sat.this, GPSActivity.class); + startActivity(intent); + } + }); + + } + + private void getLocation() { + + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + // TODO: Consider calling + // ActivityCompat#requestPermissions + // here to request the missing permissions, and then overriding + // public void onRequestPermissionsResult(int requestCode, String[] permissions, + // int[] grantResults) + // to handle the case where the user grants the permission. See the documentation + // for ActivityCompat#requestPermissions for more details. + return; + } + fusedLocationProviderClient.getLastLocation().addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + Location location = task.getResult(); + if (location != null) { + try { + Geocoder geocoder = new Geocoder(GPSActivity_sat.this, + Locale.getDefault()); + + List
    addresses = geocoder.getFromLocation( + location.getLatitude(), location.getLongitude(), 1 + ); + textView1.setText(Html.fromHtml( + "lattitude:
    " + + addresses.get(0).getLatitude() + )); + + textView2.setText(Html.fromHtml( + "longitude:
    " + + addresses.get(0).getLongitude() + )); + + textView3.setText(Html.fromHtml( + "Country Name:
    " + + addresses.get(0).getCountryName() + )); + + textView4.setText(Html.fromHtml( + "Locality:
    " + + addresses.get(0).getLocality() + )); + + textView5.setText(Html.fromHtml( + "Address:
    " + + addresses.get(0).getAddressLine(0) + )); + + + } catch (IOException e) { + } + } + } + }); + + } +} \ No newline at end of file diff --git a/app/src/main/java/Satellite/GPSApplication.java b/app/src/main/java/Satellite/GPSApplication.java new file mode 100644 index 0000000..75bd16e --- /dev/null +++ b/app/src/main/java/Satellite/GPSApplication.java @@ -0,0 +1,1592 @@ + +package Satellite; + +import android.Manifest; +import android.app.Application; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.location.GpsSatellite; +import android.location.GpsStatus; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.location.LocationProvider; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.IBinder; +import android.os.StrictMode; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatDelegate; +import android.support.v7.preference.PreferenceManager; +import android.util.Log; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.StringTokenizer; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import fr.geolabs.dev.mapmint4me.R; + + + +public class GPSApplication extends Application implements GpsStatus.Listener, LocationListener { + + //private static final float M_TO_FT = 3.280839895f; + private static final int NOT_AVAILABLE = -100000; + + //private static final int UM_METRIC_MS = 0; + private static final int UM_METRIC_KMH = 1; + //private static final int UM_IMPERIAL_FPS = 8; + //private static final int UM_IMPERIAL_MPH = 9; + + private static final int STABILIZERVALUE = 3000; // The application discards fixes for 3000 ms (minimum) + private static final int DEFAULTHANDLERTIMER = 5000; // The timer for turning off GPS on exit + private static final int GPSUNAVAILABLEHANDLERTIMER = 7000; // The "GPS temporary unavailable" timer + private int StabilizingSamples = 3; + + private static final int GPS_DISABLED = 0; + private static final int GPS_OUTOFSERVICE = 1; + private static final int GPS_TEMPORARYUNAVAILABLE = 2; + private static final int GPS_SEARCHING = 3; + private static final int GPS_STABILIZING = 4; + private static final int GPS_OK = 5; + + public static final int APP_ORIGIN_NOT_SPECIFIED = 0; + public static final int APP_ORIGIN_GOOGLE_PLAY_STORE = 1; // The app is installed via the Google Play Store + + public static final int JOB_TYPE_NONE = 0; // No operation + public static final int JOB_TYPE_EXPORT = 1; // Bulk Exportation + public static final int JOB_TYPE_VIEW = 2; // Bulk View + public static final int JOB_TYPE_SHARE = 3; // Bulk Share + public static final int JOB_TYPE_DELETE = 4; // Bulk Delete + + public static final String FLAG_RECORDING = "flagRecording"; // The persistent Flag is set when the app is recording, in order to detect Background Crashes + + + // Preferences Variables + // private boolean prefKeepScreenOn = true; // DONE in GPSActivity + private boolean prefShowDecimalCoordinates = false; + private int prefViewTracksWith = 0; + private int prefUM = UM_METRIC_KMH; + private float prefGPSdistance = 0f; + private long prefGPSupdatefrequency = 1000L; + private boolean prefEGM96AltitudeCorrection = false; + private double prefAltitudeCorrection = 0d; + private boolean prefExportKML = true; + private boolean prefExportGPX = true; + private int prefGPXVersion = 100; // the version of the GPX schema + private boolean prefExportTXT = false; + private int prefKMLAltitudeMode = 0; + private int prefShowTrackStatsType = 0; + private int prefShowDirections = 0; + private boolean prefGPSWeekRolloverCorrected= false; + + private boolean LocationPermissionChecked = false; // If the flag is false the GPSActivity will check for Location Permission + private boolean isFirstRun = false; // True if it is the first run of the app (the DB is empty) + private boolean isJustStarted = true; // True if the application has just been started + private boolean isMockProvider = false; // True if the location is from mock provider + + private LocationExtended PrevFix = null; + private boolean isPrevFixRecorded = false; + + private LocationExtended PrevRecordedFix = null; + + private boolean MustUpdatePrefs = true; // True if preferences needs to be updated + + private boolean isCurrentTrackVisible = false; + private boolean isContextMenuShareVisible = false; // True if "Share with ..." menu is visible + private boolean isContextMenuViewVisible = false; // True if "View in *" menu is visible + private String ViewInApp = ""; // The string of default app name for "View" + private String _satelliteList = ""; + // "" in case of selector + + // Singleton instance + private static GPSApplication singleton; + public static GPSApplication getInstance(){ + return singleton; + } + + + DatabaseHandler GPSDataBase; + private String PlacemarkDescription = ""; + private boolean Recording = false; + private boolean PlacemarkRequest = false; + private boolean isGPSLocationUpdatesActive = false; + private int GPSStatus = GPS_SEARCHING; + + private int AppOrigin = APP_ORIGIN_NOT_SPECIFIED; // Which package manager is used to install this app + + private boolean NewTrackFlag = false; // The variable that handle the double-click on "Track Finished" + final Handler newtrackhandler = new Handler(); + Runnable newtrackr = new Runnable() { + @Override + public void run() { + NewTrackFlag = false; + } + }; + + private boolean LocationSettingsFlag = false; // The variable that handle the double-click on "Open Location Settings" + final Handler locationsettingshandler = new Handler(); + Runnable locationsettingsr = new Runnable() { + @Override + public void run() { + LocationSettingsFlag = false; + } + }; + + private LocationManager mlocManager = null; // GPS LocationManager + private int _NumberOfSatellites = 0; + private int _NumberOfSatellitesUsedInFix = 0; + + private int GPSActivity_activeTab = 0; // The active tab on GPSActivity + private int JobProgress = 0; + private int JobsPending = 0; // The number of jobs to be done + public int JobType = JOB_TYPE_NONE; // The type of job that is pending + private boolean DeleteAlsoExportedFiles = false; // When true, the deletion of some tracks will delete also the exported files of the tracks + + public int GPSActivityColorTheme; + + private int _Stabilizer = StabilizingSamples; + private int HandlerTimer = DEFAULTHANDLERTIMER; + + private LocationExtended _currentLocationExtended = null; + private LocationExtended _currentPlacemark = null; + private Track _currentTrack = null; + private List _ArrayListTracks = Collections.synchronizedList(new ArrayList()); + + Thumbnailer Th; + //Exporter Ex; + private AsyncUpdateThreadClass asyncUpdateThread = new AsyncUpdateThreadClass(); + + // The handler that switches off the location updates after a time delay: + final Handler handler = new Handler(); + Runnable r = new Runnable() { + + @Override + public void run() { + setGPSLocationUpdates(false); + } + }; + + final Handler gpsunavailablehandler = new Handler(); + Runnable unavailr = new Runnable() { + + @Override + public void run() { + if ((GPSStatus == GPS_OK) || (GPSStatus == GPS_STABILIZING)) { + GPSStatus = GPS_TEMPORARYUNAVAILABLE; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + } + } + }; + + private final int MAX_ACTIVE_EXPORTER_THREADS = 3; // The maximum number of Exporter threads to run simultaneously + + private List ExportingTaskList = new ArrayList<>(); + + + // The handler that checks the progress of an exportation: + private final int ExportingStatusCheckInterval = 16; // The app updates the progress of exportation every 16 milliseconds + final Handler ExportingStatusCheckHandler = new Handler(); + + Runnable ExportingStatusChecker = new Runnable() { + @Override + public void run() { + long Total = 0; + long Progress = 0; + int Exporters_Total = ExportingTaskList.size(); // The total amount of exportation into the current job + int Exporters_Pending = 0; + int Exporters_Running = 0; // The amount of exportation in progress + int Exporters_Success = 0; // The amount of exportation finished with success + int Exporters_Failed = 0; // The amount of exportation failed + + + // Check Progress + for (ExportingTask ET : ExportingTaskList) { + Total += ET.getNumberOfPoints_Total(); + Progress += ET.getNumberOfPoints_Processed(); + if (ET.getStatus() == ExportingTask.STATUS_PENDING) Exporters_Pending++; + if (ET.getStatus() == ExportingTask.STATUS_RUNNING) Exporters_Running++; + if (ET.getStatus() == ExportingTask.STATUS_ENDED_SUCCESS) Exporters_Success++; + if (ET.getStatus() == ExportingTask.STATUS_ENDED_FAILED) Exporters_Failed++; + } + + // Update job progress + if (Total != 0) { + if (JobProgress != (int) Math.round(1000L * Progress / Total)) { // The ProgressBar on FragmentJobProgress has android:max="1000" + JobProgress = (int) Math.round(1000L * Progress / Total); + EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); + } + } else { + if (JobProgress != 0) { + JobProgress = 0; + EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); + } + } + + //Log.w("myApp", "[#] GPSApplication.java - ExportingStatusChecker running: " + 100*Progress/Total + "% - P " + // + Exporters_Pending + " - R " + Exporters_Running + " - S " + Exporters_Success + " - F " + Exporters_Failed); + + // Exportation Failed + if (Exporters_Failed != 0) { + EventBus.getDefault().post(EventBusMSG.TOAST_UNABLE_TO_WRITE_THE_FILE); + JobProgress = 0; + JobsPending = 0; + EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); + return; + } + + // Exportation Finished + if (Exporters_Success == Exporters_Total) { + if (JobType == JOB_TYPE_VIEW) { + if (!ExportingTaskList.isEmpty()) ViewTrack(ExportingTaskList.get(0)); + } else if (JobType == JOB_TYPE_SHARE) { + EventBus.getDefault().post(EventBusMSG.INTENT_SEND); + } else { + EventBus.getDefault().post(EventBusMSG.TOAST_TRACK_EXPORTED); + } + JobProgress = 0; + JobsPending = 0; + EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); + return; + } + + // If needed, run another Exportation Thread + if ((Exporters_Running < MAX_ACTIVE_EXPORTER_THREADS) && (Exporters_Pending > 0)) { + for (ExportingTask ET : ExportingTaskList) { + if (ET.getStatus() == ExportingTask.STATUS_PENDING) { + //Log.w("myApp", "[#] GPSApplication.java - Run the export thread nr." + Exporters_Running + ": " + ET.getId()); + ET.setStatus(ExportingTask.STATUS_RUNNING); + //ExecuteExportingTask(ET); + break; + } + } + } + + ExportingStatusCheckHandler.postDelayed(ExportingStatusChecker, ExportingStatusCheckInterval); + } + }; + + void startExportingStatusChecker() { + ExportingStatusChecker.run(); + } + + void stopExportingStatusChecker() { + ExportingStatusCheckHandler.removeCallbacks(ExportingStatusChecker); + } + + + // ------------------------------------------------------------------------------------ Service + Intent GPSServiceIntent; + GPSService GPSLoggerService; + boolean isGPSServiceBound = false; + + private ServiceConnection GPSServiceConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + GPSService.LocalBinder binder = (GPSService.LocalBinder) service; + GPSLoggerService = binder.getServiceInstance(); //Get instance of your service! + Log.w("myApp", "[#] GPSApplication.java - GPSSERVICE CONNECTED - onServiceConnected event"); + isGPSServiceBound = true; + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + Log.w("myApp", "[#] GPSApplication.java - GPSSERVICE DISCONNECTED - onServiceDisconnected event"); + isGPSServiceBound = false; + } + }; + + private void StartAndBindGPSService() { + GPSServiceIntent = new Intent(GPSApplication.this, GPSService.class); + //Start the service + startService(GPSServiceIntent); + //Bind to the service + if (Build.VERSION.SDK_INT >= 14) + bindService(GPSServiceIntent, GPSServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT); + else + bindService(GPSServiceIntent, GPSServiceConnection, Context.BIND_AUTO_CREATE); + Log.w("myApp", "[#] GPSApplication.java - StartAndBindGPSService"); + } + + + /* private void UnbindGPSService() { //UNUSED + try { + unbindService(GPSServiceConnection); //Unbind to the service + Log.w("myApp", "[#] GPSApplication.java - Service unbound"); + } catch (Exception e) { + Log.w("myApp", "[#] GPSApplication.java - Unable to unbind the GPSService"); + } + } */ + + public void StopAndUnbindGPSService() { + try { + unbindService(GPSServiceConnection); //Unbind to the service + Log.w("myApp", "[#] GPSApplication.java - Service unbound"); + } catch (Exception e) { + Log.w("myApp", "[#] GPSApplication.java - Unable to unbind the GPSService"); + } + try { + stopService(GPSServiceIntent); //Stop the service + Log.w("myApp", "[#] GPSApplication.java - Service stopped"); + } catch (Exception e) { + Log.w("myApp", "[#] GPSApplication.java - Unable to stop GPSService"); + } + } + + + // ------------------------------------------------------------------------ Getters and Setters + public boolean getNewTrackFlag() { + return NewTrackFlag; + } + + public void setNewTrackFlag(boolean newTrackFlag) { + if (newTrackFlag) { + NewTrackFlag = true; + newtrackhandler.removeCallbacks(newtrackr); // Cancel the previous newtrackr handler + newtrackhandler.postDelayed(newtrackr, 1500); // starts the new handler + } else { + NewTrackFlag = false; + newtrackhandler.removeCallbacks(newtrackr); // Cancel the previous newtrackr handler + } + } + + public boolean getLocationSettingsFlag() { + return LocationSettingsFlag; + } + + public void setLocationSettingsFlag(boolean locationSettingsFlag) { + if (locationSettingsFlag) { + LocationSettingsFlag = true; + locationsettingshandler.removeCallbacks(locationsettingsr); // Cancel the previous locationsettingsr handler + locationsettingshandler.postDelayed(locationsettingsr, 1000); // starts the new handler + } else { + LocationSettingsFlag = false; + locationsettingshandler.removeCallbacks(locationsettingsr); // Cancel the previous locationsettingsr handler + } + } + + public boolean isLocationPermissionChecked() { + return LocationPermissionChecked; + } + + public void setLocationPermissionChecked(boolean locationPermissionChecked) { + LocationPermissionChecked = locationPermissionChecked; + } + + public void setHandlerTimer(int handlerTimer) { + HandlerTimer = handlerTimer; + } + + public int getHandlerTimer() { + return HandlerTimer; + } + + public int getGPSStatus() { + return GPSStatus; + } + + + + public double getPrefAltitudeCorrection() { + return prefAltitudeCorrection; + } + + public boolean getPrefEGM96AltitudeCorrection() { + return prefEGM96AltitudeCorrection; + } + + public boolean getPrefShowDecimalCoordinates() { + return prefShowDecimalCoordinates; + } + + + public int getPrefUM() { + return prefUM; + } + + public int getPrefShowTrackStatsType() { + return prefShowTrackStatsType; + } + + public int getPrefShowDirections() { + return prefShowDirections; + } + + public LocationExtended getCurrentLocationExtended() { + return _currentLocationExtended; + } + + public void setPlacemarkDescription(String Description) { + this.PlacemarkDescription = Description; + } + + public Track getCurrentTrack() { + return _currentTrack; + } + + public int getNumberOfSatellites() { + return _NumberOfSatellites; + } + + public String get_satelliteList() { + return _satelliteList; + } + + public int getNumberOfSatellitesUsedInFix() { + return _NumberOfSatellitesUsedInFix; + } + + public boolean getRecording() { + return Recording; + } + + public void setRecording(boolean recordingState) { + PrevRecordedFix = null; + Recording = recordingState; + if (Recording) FlagAdd(FLAG_RECORDING); + else FlagRemove(FLAG_RECORDING); + } + + public boolean getPlacemarkRequest() { return PlacemarkRequest; } + + public void setPlacemarkRequest(boolean placemarkRequest) { PlacemarkRequest = placemarkRequest; } + + public List getTrackList() { + return _ArrayListTracks; + } + + public boolean isCurrentTrackVisible() { + return isCurrentTrackVisible; + } + + public void setisCurrentTrackVisible(boolean currentTrackVisible) { + isCurrentTrackVisible = currentTrackVisible; + } + + public int getAppOrigin() { + return AppOrigin; + } + + public int getJobProgress() { + return JobProgress; + } + + public int getJobsPending() { + return JobsPending; + } + + public void setJobsPending(int jobsPending) { + JobsPending = jobsPending; + } + + public int getGPSActivity_activeTab() { + return GPSActivity_activeTab; + } + + public void setGPSActivity_activeTab(int GPSActivity_activeTab) { + System.out.println(GPSActivity_activeTab); + this.GPSActivity_activeTab = 1; + } + + public List getExportingTaskList() { + return ExportingTaskList; + } + + public void setDeleteAlsoExportedFiles(boolean deleteAlsoExportedFiles) { + DeleteAlsoExportedFiles = deleteAlsoExportedFiles; + } + + public boolean isJustStarted() { + return isJustStarted; + } + + public void setJustStarted(boolean justStarted) { + isJustStarted = justStarted; + } + + // ------------------------------------------------------------------------ Utility + + private void DeleteFile(String filename) { + File file = new File(filename); + boolean deleted; + if (file.exists ()) { + deleted = file.delete(); + if (deleted) Log.w("myApp", "[#] GPSApplication.java - DeleteFile: " + filename + " deleted"); + else Log.w("myApp", "[#] GPSApplication.java - DeleteFile: " + filename + " unable to delete the File"); + } + else Log.w("myApp", "[#] GPSApplication.java - DeleteFile: " + filename + " doesn't exists"); + } + + + /* NOT USED, Commented out + private boolean FileExists(String filename) { + File file = new File(filename); + return file.exists (); + } */ + + + // Flags are Boolean SharedPreferences that are excluded by automatic Backups + + public void FlagAdd (String flag) { + SharedPreferences preferences_nobackup = getSharedPreferences("prefs_nobackup",Context.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences_nobackup.edit(); + editor.putBoolean(flag, true); + editor.commit(); + } + + + public void FlagRemove (String flag) { + SharedPreferences preferences_nobackup = getSharedPreferences("prefs_nobackup",Context.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences_nobackup.edit(); + editor.remove(flag); + editor.commit(); + } + + + public boolean FlagExists (String flag) { + SharedPreferences preferences_nobackup = getSharedPreferences("prefs_nobackup",Context.MODE_PRIVATE); + return preferences_nobackup.getBoolean(flag, false); + } + + + // -------------------------------------------------------------------------------------------- + + @Override + public void onCreate() { + + AppCompatDelegate.setDefaultNightMode(Integer.valueOf(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("prefColorTheme", "2"))); + + super.onCreate(); + + singleton = this; + + // work around the android.os.FileUriExposedException + StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); + StrictMode.setVmPolicy(builder.build()); + + final String CHANNEL_ID = "GPSLoggerServiceChannel"; + + // Create notification channel for Android >= O + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + getString(R.string.app_name), + NotificationManager.IMPORTANCE_LOW + ); + channel.setSound(null, null); + channel.enableLights(false); + channel.enableVibration(false); + channel.setSound(null,null); + + NotificationManager manager = getSystemService(NotificationManager.class); + manager.createNotificationChannel(channel); + } + + // ----------------------- + // TODO: Uncomment it to run the Week Rollover Tests (For Test Purpose) + // SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit(); + // editor.putBoolean("prefGPSWeekRolloverCorrected", false); + // editor.commit(); + // ----------------------- + + // ----------------------- + // TODO: Uncomment it to reload the EGM Grid File (For Test Purpose) + //File file = new File(getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); + //if (file.exists ()) file.delete(); + // ----------------------- + + EventBus.getDefault().register(this); + + mlocManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); // Location Manager + + File sd = new File(Environment.getExternalStorageDirectory() + "/GPSLogger"); // Create the Directories if not exist + if (!sd.exists()) { + sd.mkdir(); + Log.w("myApp", "[#] GPSApplication.java - Folder created: " + sd.getAbsolutePath()); + } + sd = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); + if (!sd.exists()) { + sd.mkdir(); + Log.w("myApp", "[#] GPSApplication.java - Folder created: " + sd.getAbsolutePath()); + } + + sd = new File(getApplicationContext().getFilesDir() + "/Thumbnails"); + if (!sd.exists()) { + sd.mkdir(); + Log.w("myApp", "[#] GPSApplication.java - Folder created: " + sd.getAbsolutePath()); + } + + EGM96 egm96 = EGM96.getInstance(); // Load EGM Grid + if (egm96 != null) { + if (!egm96.isEGMGridLoaded()) { + egm96.LoadGridFromFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/WW15MGH.DAC", getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); + } + } + + try { // Determine the app installation source + String installer; + installer = getApplicationContext().getPackageManager().getInstallerPackageName(getApplicationContext().getPackageName()); + if (installer.equals("com.android.vending") || installer.equals("com.google.android.feedback")) + AppOrigin = APP_ORIGIN_GOOGLE_PLAY_STORE; // App installed from Google Play Store + else AppOrigin = APP_ORIGIN_NOT_SPECIFIED; // Otherwise + } catch (Throwable e) { + Log.w("myApp", "[#] GPSApplication.java - Exception trying to determine the package installer"); + AppOrigin = APP_ORIGIN_NOT_SPECIFIED; + } + + GPSDataBase = new DatabaseHandler(this); // Initialize the Database + + // Prepare the current track + if (GPSDataBase.getLastTrackID() == 0) { + GPSDataBase.addTrack(new Track()); // Creation of the first track if the DB is empty + isFirstRun = true; + } + _currentTrack = GPSDataBase.getLastTrack(); // Get the last track + + LoadPreferences(); // Load Settings + + // ---------------------------------------------------------------------------------------- + + asyncUpdateThread.start(); + + // Get max available VM memory, exceeding this amount will throw an + // OutOfMemory exception. Stored in kilobytes as LruCache takes an + // int in its constructor. + //Log.w("myApp", "[#] GPSApplication.java - Max available VM memory = " + (int) (Runtime.getRuntime().maxMemory() / 1024) + " kbytes"); + } + + + @Override + public void onTerminate() { + Log.w("myApp", "[#] GPSApplication.java - onTerminate"); + EventBus.getDefault().unregister(this); + StopAndUnbindGPSService(); + super.onTerminate(); + } + + + @Subscribe + public void onEvent(Short msg) { + if (msg == EventBusMSG.NEW_TRACK) { + AsyncTODO ast = new AsyncTODO(); + ast.TaskType = "TASK_NEWTRACK"; + ast.location = null; + AsyncTODOQueue.add(ast); + return; + } + if (msg == EventBusMSG.ADD_PLACEMARK) { + AsyncTODO ast = new AsyncTODO(); + ast.TaskType = "TASK_ADDPLACEMARK"; + ast.location = _currentPlacemark; + _currentPlacemark.setDescription(PlacemarkDescription); + AsyncTODOQueue.add(ast); + return; + } + if (msg == EventBusMSG.APP_PAUSE) { + handler.postDelayed(r, getHandlerTimer()); // Starts the switch-off handler (delayed by HandlerTimer) + if ((_currentTrack.getNumberOfLocations() == 0) && (_currentTrack.getNumberOfPlacemarks() == 0) + && (!Recording) && (!PlacemarkRequest)) StopAndUnbindGPSService(); + System.gc(); // Clear mem from released objects with Garbage Collector + return; + } + if (msg == EventBusMSG.APP_RESUME) { + //Log.w("myApp", "[#] GPSApplication.java - Received EventBusMSG.APP_RESUME"); + AsyncPrepareTracklistContextMenu asyncPrepareTracklistContextMenu = new AsyncPrepareTracklistContextMenu(); + asyncPrepareTracklistContextMenu.start(); + handler.removeCallbacks(r); // Cancel the switch-off handler + setHandlerTimer(DEFAULTHANDLERTIMER); + setGPSLocationUpdates(true); + if (MustUpdatePrefs) { + MustUpdatePrefs = false; + LoadPreferences(); + } + StartAndBindGPSService(); + return; + } + if (msg == EventBusMSG.UPDATE_SETTINGS) { + MustUpdatePrefs = true; + return; + } + } + + + public void setGPSLocationUpdates (boolean state) { + // Request permissions = https://developer.android.com/training/permissions/requesting.html + if (!state && !getRecording() && isGPSLocationUpdatesActive + && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { + GPSStatus = GPS_SEARCHING; + mlocManager.removeGpsStatusListener(this); + mlocManager.removeUpdates(this); + isGPSLocationUpdatesActive = false; + //Log.w("myApp", "[#] GPSApplication.java - setGPSLocationUpdates = false"); + } + if (state && !isGPSLocationUpdatesActive + && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { + mlocManager.addGpsStatusListener(this); + mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, prefGPSupdatefrequency, 0, this); // Requires Location update + isGPSLocationUpdatesActive = true; + //Log.w("myApp", "[#] GPSApplication.java - setGPSLocationUpdates = true"); + if (prefGPSupdatefrequency >= 1000) StabilizingSamples = (int) Math.ceil(STABILIZERVALUE / prefGPSupdatefrequency); + else StabilizingSamples = (int) Math.ceil(STABILIZERVALUE / 1000); + } + } + + public void updateGPSLocationFrequency () { + if (isGPSLocationUpdatesActive + && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { + //Log.w("myApp", "[#] GPSApplication.java - updateGPSLocationFrequency"); + mlocManager.removeGpsStatusListener(this); + mlocManager.removeUpdates(this); + if (prefGPSupdatefrequency >= 1000) StabilizingSamples = (int) Math.ceil(STABILIZERVALUE / prefGPSupdatefrequency); + else StabilizingSamples = (int) Math.ceil(STABILIZERVALUE / 1000); + mlocManager.addGpsStatusListener(this); + mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, prefGPSupdatefrequency, 0, this); + } + } + + public void updateSats() { + try { + if ((mlocManager != null) && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) { + GpsStatus gs = mlocManager.getGpsStatus(null); + int sats_inview = 0; // Satellites in view; + int sats_used = 0; // Satellites used in fix; + + if (gs != null) { + Iterable sats = gs.getSatellites(); + for (GpsSatellite sat : sats) { + sats_inview++; + + if (sat.usedInFix()) { + sats_used++; + int i =0; + if(i<5){ + + _satelliteList = _satelliteList+ "PRN No:" +sat.getPrn() + ", SNR: " + sat.getSnr() + ", Azimuth " +sat.getAzimuth()+", Elevation:"+sat.getElevation()+"\n"; + Log.d("Satellite_info",_satelliteList); + i = i+1; + + + } + } + //Log.w("myApp", "[#] GPSApplication.java - updateSats: i=" + i); + } + _NumberOfSatellites = sats_inview; + _NumberOfSatellitesUsedInFix = sats_used; + } else { + _NumberOfSatellites = NOT_AVAILABLE; + _NumberOfSatellitesUsedInFix = NOT_AVAILABLE; + } + } else { + _NumberOfSatellites = NOT_AVAILABLE; + _NumberOfSatellitesUsedInFix = NOT_AVAILABLE; + } + } catch (NullPointerException e) { + _NumberOfSatellites = NOT_AVAILABLE; + _NumberOfSatellitesUsedInFix = NOT_AVAILABLE; + //Log.w("myApp", "[#] GPSApplication.java - updateSats: Caught NullPointerException: " + e); + } + //Log.w("myApp", "[#] GPSApplication.java - updateSats: Total=" + _NumberOfSatellites + " Used=" + _NumberOfSatellitesUsedInFix); + } + + + private void ViewTrack(ExportingTask exportingTask) { + if (prefViewTracksWith == 0) { // KML Viewer + File file = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/", exportingTask.getName() + ".kml"); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setDataAndType(Uri.fromFile(file), "application/vnd.google-earth.kml+xml"); + try { + startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w("myApp", "[#] GPSApplication.java - ViewTrack: Unable to view the track: " + e); + AsyncPrepareTracklistContextMenu asyncPrepareTracklistContextMenu = new AsyncPrepareTracklistContextMenu(); + asyncPrepareTracklistContextMenu.start(); + } + } + if (prefViewTracksWith == 1) { // GPX Viewer + File file = new File(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/", exportingTask.getName() + ".gpx"); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setDataAndType(Uri.fromFile(file), "gpx+xml"); + try { + startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w("myApp", "[#] GPSApplication.java - ViewTrack: Unable to view the track: " + e); + AsyncPrepareTracklistContextMenu asyncPrepareTracklistContextMenu = new AsyncPrepareTracklistContextMenu(); + asyncPrepareTracklistContextMenu.start(); + } + } + } + + + public ArrayList getSelectedTracks() { + ArrayList selTracks = new ArrayList(); + synchronized(_ArrayListTracks) { + for (Track T : _ArrayListTracks) { + if (T.isSelected()) { + selTracks.add(T); + } + } + } + return (selTracks); + } + + + public int getNumberOfSelectedTracks() { + int nsel = 0; + synchronized(_ArrayListTracks) { + for (Track T : _ArrayListTracks) { + if (T.isSelected()) nsel++; + } + } + return nsel; + } + + + public void DeselectAllTracks() { + synchronized(_ArrayListTracks) { + for (Track T : _ArrayListTracks) { + if (T.isSelected()) { + T.setSelected(false); + // EventBus.getDefault().post(new EventBusMSGNormal(EventBusMSG.TRACKLIST_DESELECT, T.getId())); + } + } + } + EventBus.getDefault().post(EventBusMSG.REFRESH_TRACKLIST); + } + + + public void LoadJob (int jobType) { + ExportingTaskList.clear(); + synchronized(_ArrayListTracks) { + for (Track T : _ArrayListTracks) { + if (T.isSelected()) { + ExportingTask ET = new ExportingTask(); + ET.setId(T.getId()); + ET.setName(T.getName()); + ET.setNumberOfPoints_Total(T.getNumberOfLocations() + T.getNumberOfPlacemarks()); + ET.setNumberOfPoints_Processed(0); + ExportingTaskList.add(ET); + } + } + } + JobsPending = ExportingTaskList.size(); + JobType = jobType; + } + + /* + public void ExecuteExportingTask (ExportingTask exportingTask) { + switch (JobType) { + case JOB_TYPE_NONE: + case JOB_TYPE_DELETE: + break; + case JOB_TYPE_EXPORT: + Ex = new Exporter(exportingTask, prefExportKML, prefExportGPX, prefExportTXT, Environment.getExternalStorageDirectory() + "/GPSLogger"); + Ex.start(); + break; + case JOB_TYPE_VIEW: + if (prefViewTracksWith == 0) Ex = new Exporter(exportingTask, true, false, false, Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); + if (prefViewTracksWith == 1) Ex = new Exporter(exportingTask, false, true, false, Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); + Ex.start(); + break; + case JOB_TYPE_SHARE: + Ex = new Exporter(exportingTask, prefExportKML, prefExportGPX, prefExportTXT, Environment.getExternalStorageDirectory() + "/GPSLogger/AppData"); + Ex.start(); + break; + default: + break; + } + } + */ + public void ExecuteJob () { + if (!ExportingTaskList.isEmpty()) { + switch (JobType) { + case JOB_TYPE_NONE: + break; + case JOB_TYPE_DELETE: + String S = "TASK_DELETE_TRACKS"; + for (ExportingTask ET : ExportingTaskList) { + S = S + " " + ET.getId(); + } + AsyncTODO ast = new AsyncTODO(); + ast.TaskType = S; + ast.location = null; + AsyncTODOQueue.add(ast); + break; + case JOB_TYPE_EXPORT: + case JOB_TYPE_VIEW: + case JOB_TYPE_SHARE: + startExportingStatusChecker(); + break; + default: + break; + } + } else { + Log.w("myApp", "[#] GPSApplication.java - Empty Job, nothing processed"); + JobProgress = 0; + JobsPending = 0; + } + } + + + private class AsyncPrepareTracklistContextMenu extends Thread { + + public AsyncPrepareTracklistContextMenu() { + } + + public void run() { + isContextMenuShareVisible = false; + isContextMenuViewVisible = false; + ViewInApp = ""; + + final PackageManager pm = getPackageManager(); + + // ----- menu share + Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setType("text/xml"); + // Verify the intent will resolve to at least one activity + if ((intent.resolveActivity(pm) != null)) isContextMenuShareVisible = true; + + // ----- menu view + intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setType("application/vnd.google-earth.kml+xml"); + + if (prefViewTracksWith == 0) { // KML Viewer + intent.setType("application/vnd.google-earth.kml+xml"); + } + if (prefViewTracksWith == 1) { // GPX Viewer + intent.setType("application/gpx+xml"); + } + ResolveInfo ri = pm.resolveActivity(intent, 0); // Find default app + if (ri != null) { + //Log.w("myApp", "[#] GPSApplication.java - Open with: " + ri.activityInfo.applicationInfo.loadLabel(getContext().getPackageManager())); + List lri = pm.queryIntentActivities(intent, 0); + //Log.w("myApp", "[#] GPSApplication.java - Found " + lri.size() + " viewers:"); + for (ResolveInfo tmpri : lri) { + //Log.w("myApp", "[#] " + ri.activityInfo.applicationInfo.packageName + " - " + tmpri.activityInfo.applicationInfo.packageName); + if (ri.activityInfo.applicationInfo.packageName.equals(tmpri.activityInfo.applicationInfo.packageName)) { + ViewInApp = ri.activityInfo.applicationInfo.loadLabel(pm).toString(); + //Log.w("myApp", "[#] DEFAULT --> " + tmpri.activityInfo.applicationInfo.loadLabel(getPackageManager())); + } //else Log.w("myApp", "[#] " + tmpri.activityInfo.applicationInfo.loadLabel(getContext().getPackageManager())); + } + isContextMenuViewVisible = true; + } + Log.w("myApp", "[#] GPSApplication.java - Tracklist ContextMenu prepared"); + EventBus.getDefault().post(EventBusMSG.UPDATE_ACTIONBAR); + } + } + + + // ------------------------------------------------------------------------- GpsStatus.Listener + @Override + public void onGpsStatusChanged(final int event) { + switch (event) { + case GpsStatus.GPS_EVENT_SATELLITE_STATUS: + // TODO: get here the status of the GPS, and save into a GpsStatus to be used for satellites visualization; + // Use GpsStatus getGpsStatus (GpsStatus status) + // https://developer.android.com/reference/android/location/LocationManager.html#getGpsStatus(android.location.GpsStatus) + updateSats(); + break; + } + } + + + // --------------------------------------------------------------------------- LocationListener + @Override + public void onLocationChanged(Location loc) { + //if ((loc != null) && (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) { + if (loc != null) { // Location data is valid + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { // For API >= 18 + if ((PrevFix == null) || (loc.isFromMockProvider()!=isMockProvider)) { // Reset the number of satellites when the provider changes between GPS and MOCK + isMockProvider = loc.isFromMockProvider(); + _NumberOfSatellites = NOT_AVAILABLE; + _NumberOfSatellitesUsedInFix = NOT_AVAILABLE; + if (isMockProvider) Log.w("myApp", "[#] GPSApplication.java - Provider Type = MOCK PROVIDER"); + else Log.w("myApp", "[#] GPSApplication.java - Provider Type = GPS PROVIDER"); + } + } + + //Log.w("myApp", "[#] GPSApplication.java - onLocationChanged: provider=" + loc.getProvider()); + if (loc.hasSpeed() && (loc.getSpeed() == 0)) loc.removeBearing(); // Removes bearing if the speed is zero + // --------- Workaround for old GPS that are affected to Week Rollover + //loc.setTime(loc.getTime() - 619315200000L); // Commented out, it simulate the old GPS hardware Timestamp + if (loc.getTime() <= 1388534400000L) // if the Location Time is <= 01/01/2014 00:00:00.000 + loc.setTime(loc.getTime() + 619315200000L); // Timestamp incremented by 1024×7×24×60×60×1000 = 619315200000 ms + // This value must be doubled every 1024 weeks !!! + LocationExtended eloc = new LocationExtended(loc); + eloc.setNumberOfSatellites(getNumberOfSatellites()); + eloc.setNumberOfSatellitesUsedInFix(getNumberOfSatellitesUsedInFix()); + eloc.setSatellite_info(get_satelliteList()); + + // eloc.setSatelliteDescription(get_satelliteList()); + boolean ForceRecord = false; + + gpsunavailablehandler.removeCallbacks(unavailr); // Cancel the previous unavail countdown handler + gpsunavailablehandler.postDelayed(unavailr, GPSUNAVAILABLEHANDLERTIMER); // starts the unavailability timeout (in 7 sec.) + + if (GPSStatus != GPS_OK) { + if (GPSStatus != GPS_STABILIZING) { + GPSStatus = GPS_STABILIZING; + _Stabilizer = StabilizingSamples; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + } + else _Stabilizer--; + if (_Stabilizer == 0) GPSStatus = GPS_OK; + PrevFix = eloc; + PrevRecordedFix = eloc; + isPrevFixRecorded = true; + } + + // Save fix in case this is a STOP or a START (the speed is "old>0 and new=0" or "old=0 and new>0") + if ((PrevFix != null) && (PrevFix.getLocation().hasSpeed()) && (eloc.getLocation().hasSpeed()) && (GPSStatus == GPS_OK) && (Recording) + && (((eloc.getLocation().getSpeed() == 0) && (PrevFix.getLocation().getSpeed() != 0)) || ((eloc.getLocation().getSpeed() != 0) && (PrevFix.getLocation().getSpeed() == 0)))) { + if (!isPrevFixRecorded) { // Record the old sample if not already recorded + AsyncTODO ast = new AsyncTODO(); + ast.TaskType = "TASK_ADDLOCATION"; + ast.location = PrevFix; + AsyncTODOQueue.add(ast); + PrevRecordedFix = PrevFix; + isPrevFixRecorded = true; + } + + ForceRecord = true; // + Force to record the new + } + + if (GPSStatus == GPS_OK) { + AsyncTODO ast = new AsyncTODO(); + if ((Recording) && ((prefGPSdistance == 0) || (PrevRecordedFix == null) || (ForceRecord) || (loc.distanceTo(PrevRecordedFix.getLocation()) >= prefGPSdistance))) { + PrevRecordedFix = eloc; + ast.TaskType = "TASK_ADDLOCATION"; + ast.location = eloc; + AsyncTODOQueue.add(ast); + isPrevFixRecorded = true; + } else { + ast.TaskType = "TASK_UPDATEFIX"; + ast.location = eloc; + AsyncTODOQueue.add(ast); + isPrevFixRecorded = false; + } + + if (PlacemarkRequest) { + _currentPlacemark = new LocationExtended(loc); + _currentPlacemark.setNumberOfSatellites(getNumberOfSatellites()); + _currentPlacemark.setNumberOfSatellitesUsedInFix(getNumberOfSatellitesUsedInFix()); + _currentPlacemark.setSatellite_info(get_satelliteList()); + PlacemarkRequest = false; + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + EventBus.getDefault().post(EventBusMSG.REQUEST_ADD_PLACEMARK); + + } + PrevFix = eloc; + } + } + } + + @Override + public void onProviderDisabled(String provider) { + GPSStatus = GPS_DISABLED; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + } + + @Override + public void onProviderEnabled(String provider) { + GPSStatus = GPS_SEARCHING; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + } + + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + // This is called when the GPS status changes + switch (status) { + case LocationProvider.OUT_OF_SERVICE: + //Log.w("myApp", "[#] GPSApplication.java - GPS Out of Service"); + gpsunavailablehandler.removeCallbacks(unavailr); // Cancel the previous unavail countdown handler + GPSStatus = GPS_OUTOFSERVICE; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + //Toast.makeText( getApplicationContext(), "GPS Out of Service", Toast.LENGTH_SHORT).show(); + break; + case LocationProvider.TEMPORARILY_UNAVAILABLE: + //Log.w("myApp", "[#] GPSApplication.java - GPS Temporarily Unavailable"); + gpsunavailablehandler.removeCallbacks(unavailr); // Cancel the previous unavail countdown handler + GPSStatus = GPS_TEMPORARYUNAVAILABLE; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + //Toast.makeText( getApplicationContext(), "GPS Temporarily Unavailable", Toast.LENGTH_SHORT).show(); + break; + case LocationProvider.AVAILABLE: + gpsunavailablehandler.removeCallbacks(unavailr); // Cancel the previous unavail countdown handler + //Log.w("myApp", "[#] GPSApplication.java - GPS Available: " + _NumberOfSatellites + " satellites"); + break; + } + } + + + public void UpdateTrackList() { + long ID = GPSDataBase.getLastTrackID(); + List _OldArrayListTracks = new ArrayList(); + _OldArrayListTracks.addAll(_ArrayListTracks); + + if (ID > 0) { + synchronized(_ArrayListTracks) { + // Save Selections + ArrayList SelectedT = new ArrayList<>(); + for (Track T : _ArrayListTracks) { + if (T.isSelected()) SelectedT.add(T.getId()); + } + + // Update the List + _ArrayListTracks.clear(); + _ArrayListTracks.addAll(GPSDataBase.getTracksList(0, ID - 1)); + if ((ID > 1) && (GPSDataBase.getTrack(ID - 1) != null)) { + String fname = (ID - 1) + ".png"; + File file = new File(getApplicationContext().getFilesDir() + "/Thumbnails/", fname); + if (!file.exists()) Th = new Thumbnailer(ID - 1); + } + if (_currentTrack.getNumberOfLocations() + _currentTrack.getNumberOfPlacemarks() > 0) { + Log.w("myApp", "[#] GPSApplication.java - Update Tracklist: current track (" + _currentTrack.getId() + ") visible into the tracklist"); + _ArrayListTracks.add(0, _currentTrack); + } else + Log.w("myApp", "[#] GPSApplication.java - Update Tracklist: current track not visible into the tracklist"); + + // Restore the selection state + for (Track T : _ArrayListTracks) { + for (Long SelT : SelectedT) { + if (SelT == T.getId()) { + T.setSelected(true); + break; + } + } + } + } + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACKLIST); + //Log.w("myApp", "[#] GPSApplication.java - Update Tracklist: Added " + _ArrayListTracks.size() + " tracks"); + } + } + + +// PREFERENCES LOADER ------------------------------------------------------------------------------ + + private void LoadPreferences() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + + // ---------Conversion from the previous versions of GPS Logger preferences + if (preferences.contains("prefShowImperialUnits")) { // The old boolean setting for imperial units in v.1.1.5 + Log.w("myApp", "[#] GPSApplication.java - Old setting prefShowImperialUnits present. Converting to new preference PrefUM."); + boolean imperialUM = preferences.getBoolean("prefShowImperialUnits", false); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString("prefUM", (imperialUM ? "8" : "0")); + editor.remove("prefShowImperialUnits"); + editor.commit(); + } + + // ---------Remove the prefIsStoragePermissionChecked in preferences if present + + if (preferences.contains("prefIsStoragePermissionChecked")) { + SharedPreferences.Editor editor = preferences.edit(); + editor.remove("prefIsStoragePermissionChecked"); + editor.commit(); + } + + // ----------------------------------------------------------------------- + + //prefKeepScreenOn = preferences.getBoolean("prefKeepScreenOn", true); + prefGPSWeekRolloverCorrected = preferences.getBoolean("prefGPSWeekRolloverCorrected", false); + prefShowDecimalCoordinates = preferences.getBoolean("prefShowDecimalCoordinates", false); + prefViewTracksWith = Integer.valueOf(preferences.getString("prefViewTracksWith", "0")); + prefUM = Integer.valueOf(preferences.getString("prefUM", "0")) + Integer.valueOf(preferences.getString("prefUMSpeed", "1")); + prefGPSdistance = Float.valueOf(preferences.getString("prefGPSdistance", "0")); + prefEGM96AltitudeCorrection = preferences.getBoolean("prefEGM96AltitudeCorrection", false); + prefAltitudeCorrection = Double.valueOf(preferences.getString("prefAltitudeCorrection", "0")); + Log.w("myApp", "[#] GPSApplication.java - Manual Correction set to " + prefAltitudeCorrection + " m"); + prefExportKML = preferences.getBoolean("prefExportKML", true); + prefExportGPX = preferences.getBoolean("prefExportGPX", true); + prefExportTXT = preferences.getBoolean("prefExportTXT", false); + prefKMLAltitudeMode = Integer.valueOf(preferences.getString("prefKMLAltitudeMode", "1")); + prefGPXVersion = Integer.valueOf(preferences.getString("prefGPXVersion", "100")); // Default value = v.1.0 + prefShowTrackStatsType = Integer.valueOf(preferences.getString("prefShowTrackStatsType", "0")); + prefShowDirections = Integer.valueOf(preferences.getString("prefShowDirections", "0")); + + long oldGPSupdatefrequency = prefGPSupdatefrequency; + prefGPSupdatefrequency = Long.valueOf(preferences.getString("prefGPSupdatefrequency", "1000")); + + // ---------------------------------------------- Update the GPS Update Frequency if needed + if (oldGPSupdatefrequency != prefGPSupdatefrequency) updateGPSLocationFrequency(); + + // ---------------------------------------------------------------- If no Exportation formats are enabled, enable the GPX one + if (!prefExportKML && !prefExportGPX && !prefExportTXT) { + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("prefExportGPX", true); + editor.commit(); + prefExportGPX = true; + } + + // ---------------------------------------------------------------- Load EGM Grid if needed + EGM96 egm96 = EGM96.getInstance(); + if (egm96 != null) { + if (!egm96.isEGMGridLoaded()) { + egm96.LoadGridFromFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/WW15MGH.DAC", getApplicationContext().getFilesDir() + "/WW15MGH.DAC"); + } + } + + // ------------------------------------------------------------------- Request of UI Update + EventBus.getDefault().post(EventBusMSG.APPLY_SETTINGS); + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACKLIST); + } + + +// THE THREAD THAT DOES ASYNCHRONOUS OPERATIONS --------------------------------------------------- + + + class AsyncTODO { + String TaskType; + LocationExtended location; + } + + private BlockingQueue AsyncTODOQueue = new LinkedBlockingQueue<>(); + + private class AsyncUpdateThreadClass extends Thread { + + Track track; + LocationExtended locationExtended; + + public AsyncUpdateThreadClass() {} + + public void run() { + + track = _currentTrack; + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + UpdateTrackList(); + + // ---------------------------------------------------------------------------------------- + // Apply the GPS Week Rollover Correction, for data already stored into the DB + // ---------------------------------------------------------------------------------------- + + if (!prefGPSWeekRolloverCorrected) { + if (!isFirstRun) { + Log.w("myApp", "[#] GPSApplication.java - CORRECTING DATA FOR GPS WEEK ROLLOVER"); + GPSDataBase.CorrectGPSWeekRollover(); + Log.w("myApp", "[#] GPSApplication.java - DATA FOR GPS WEEK ROLLOVER CORRECTED"); + UpdateTrackList(); + Log.w("myApp", "[#] GPSApplication.java - TRACKLIST UPDATED WITH THE CORRECTED NAMES"); + } + prefGPSWeekRolloverCorrected = true; + SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit(); + editor.putBoolean("prefGPSWeekRolloverCorrected", true); + editor.commit(); + } + // ---------------------------------------------------------------------------------------- + + + while (true) { + AsyncTODO asyncTODO; + try { + asyncTODO = AsyncTODOQueue.take(); + } catch (InterruptedException e) { + Log.w("myApp", "[!] Buffer not available: " + e.getMessage()); + break; + } + + // Task: Create new track (if needed) + if (asyncTODO.TaskType.equals("TASK_NEWTRACK")) { + if ((track.getNumberOfLocations() != 0) || (track.getNumberOfPlacemarks() != 0)) { + // ---- Delete 2 thumbs files forward - in case of user deleted DB in App manager (pngs could be already presents for the new IDS) + String fname = (track.getId() + 1) +".png"; + File file = new File(getApplicationContext().getFilesDir() + "/Thumbnails/", fname); + if (file.exists ()) file.delete (); + fname = (track.getId() + 2) +".png"; + file = new File(getApplicationContext().getFilesDir() + "/Thumbnails/", fname); + if (file.exists ()) file.delete (); + track = new Track(); + // ---- + track.setId(GPSDataBase.addTrack(track)); + Log.w("myApp", "[#] GPSApplication.java - TASK_NEWTRACK: " + track.getId()); + _currentTrack = track; + UpdateTrackList(); + } else Log.w("myApp", "[#] GPSApplication.java - TASK_NEWTRACK: Track " + track.getId() + " already empty (New track not created)"); + _currentTrack = track; + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + } + + // Task: Add location to current track + if (asyncTODO.TaskType.equals("TASK_ADDLOCATION")) { + locationExtended = new LocationExtended(asyncTODO.location.getLocation()); + locationExtended.setNumberOfSatellites(asyncTODO.location.getNumberOfSatellites()); + locationExtended.setNumberOfSatellitesUsedInFix(asyncTODO.location.getNumberOfSatellitesUsedInFix()); + locationExtended.setSatellite_info(asyncTODO.location.getSatellite_info()); + _currentLocationExtended = locationExtended; + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + track.add(locationExtended); + GPSDataBase.addLocationToTrack(locationExtended, track); + //Log.w("myApp", "[#] GPSApplication.java - TASK_ADDLOCATION: Added new Location in " + track.getId()); + _currentTrack = track; + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + if (_currentTrack.getNumberOfLocations() + _currentTrack.getNumberOfPlacemarks() == 1) UpdateTrackList(); + } + + // Task: Add a placemark to current track + if (asyncTODO.TaskType.equals("TASK_ADDPLACEMARK")) { + locationExtended = new LocationExtended(asyncTODO.location.getLocation()); + locationExtended.setDescription(asyncTODO.location.getDescription()); + locationExtended.setNumberOfSatellites(asyncTODO.location.getNumberOfSatellites()); + locationExtended.setNumberOfSatellitesUsedInFix(asyncTODO.location.getNumberOfSatellitesUsedInFix()); + locationExtended.setSatellite_info(asyncTODO.location.getSatellite_info()); + // locationExtended.setSatelliteinfo(asyncTODO.location.get_satellit) + track.addPlacemark(locationExtended); + GPSDataBase.addPlacemarkToTrack(locationExtended, track); + _currentTrack = track; + EventBus.getDefault().post(EventBusMSG.UPDATE_TRACK); + if (_currentTrack.getNumberOfLocations() + _currentTrack.getNumberOfPlacemarks() == 1) UpdateTrackList(); + } + + // Task: Update current Fix + if (asyncTODO.TaskType.equals("TASK_UPDATEFIX")) { + _currentLocationExtended = new LocationExtended(asyncTODO.location.getLocation()); + _currentLocationExtended.setNumberOfSatellites(asyncTODO.location.getNumberOfSatellites()); + _currentLocationExtended.setNumberOfSatellitesUsedInFix(asyncTODO.location.getNumberOfSatellitesUsedInFix()); + _currentLocationExtended.setSatellite_info(asyncTODO.location.getSatellite_info()); + EventBus.getDefault().post(EventBusMSG.UPDATE_FIX); + } + + // Task: Delete some tracks + if (asyncTODO.TaskType.startsWith("TASK_DELETE_TRACKS")) { + + String STokens = asyncTODO.TaskType.substring(19); + List tokens = new ArrayList<>(); + StringTokenizer tokenizer = new StringTokenizer(STokens, " "); + while (tokenizer.hasMoreElements()) { + tokens.add(tokenizer.nextToken()); + } + if (!tokens.isEmpty()) { + JobProgress = 0; + int TracksToBeDeleted = tokens.size(); + int TracksDeleted = 0; + for (String s : tokens) { + Track track = null; // The track found in the _ArrayListTracks + int i = Integer.valueOf(s); + if (i != _currentTrack.getId()) { // Prevent the deletion of the current track + synchronized (_ArrayListTracks) { + for (Track T : _ArrayListTracks) { + if (T.getId() == i) { + track = T; + GPSDataBase.DeleteTrack(i); + Log.w("myApp", "[#] GPSApplication.java - TASK_DELETE_TRACKS: Track " + i + " deleted."); + _ArrayListTracks.remove(T); + break; + } + } + } + if (track != null) { + // Delete track files + DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/" + track.getName() + ".txt"); + DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/" + track.getName() + ".kml"); + DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/" + track.getName() + ".gpx"); + DeleteFile(getApplicationContext().getFilesDir() + "/Thumbnails/" + track.getId() + ".png"); + if (DeleteAlsoExportedFiles) { + // Delete exported files + DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/" + track.getName() + ".txt"); + DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/" + track.getName() + ".kml"); + DeleteFile(Environment.getExternalStorageDirectory() + "/GPSLogger/" + track.getName() + ".gpx"); + } + + TracksDeleted++; + JobProgress = (int) Math.round(1000L * TracksDeleted / TracksToBeDeleted); + EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); + if (JobsPending > 0) JobsPending--; + } + } else { + Log.w("myApp", "[#] GPSApplication.java - TASK_DELETE_TRACKS: Unable to delete the current track!"); + TracksDeleted++; + JobProgress = (int) Math.round(1000L * TracksDeleted / TracksToBeDeleted); + EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); + if (JobsPending > 0) JobsPending--; + } + } + } + JobProgress = 0; + EventBus.getDefault().post(EventBusMSG.UPDATE_JOB_PROGRESS); + EventBus.getDefault().post(EventBusMSG.NOTIFY_TRACKS_DELETED); + } + } + } + } + + + + + +// THE THREAD THAT GENERATES A TRACK THUMBNAIL ----------------------------------------------------- + + public class Thumbnailer { + + long Id; + long NumberOfLocations; + + private Paint drawPaint = new Paint(); + private Paint BGPaint = new Paint(); + private Paint EndDotdrawPaint = new Paint(); + private Paint EndDotBGPaint = new Paint(); + private int Size = (int)(getResources().getDimension(R.dimen.thumbSize)); + + private int Margin = (int) Math.ceil(getResources().getDimension(R.dimen.thumbLineWidth) * 3); + private int Size_Minus_Margins = Size - 2 * Margin; + + private double MinLatitude; + private double MinLongitude; + + double Distance_Proportion; + double DrawScale; + double Lat_Offset; + double Lon_Offset; + + private AsyncThumbnailThreadClass asyncThumbnailThreadClass = new AsyncThumbnailThreadClass(); + + public Thumbnailer(long ID) { + + Track track = GPSDataBase.getTrack(ID); + //Log.w("myApp", "[#] GPSApplication.java - Bitmap Size = " + Size); + + if ((track.getNumberOfLocations() > 2) && (track.getDistance() >= 15) && (track.getValidMap() != 0)) { + Id = track.getId(); + NumberOfLocations = track.getNumberOfLocations(); + + // Setup Paints + //drawPaint.setColor(getResources().getColor(R.color.colorThumbnailLineColor)); + drawPaint.setAntiAlias(true); + drawPaint.setStrokeWidth(getResources().getDimension(R.dimen.thumbLineWidth)); + //drawPaint.setStrokeWidth(2); + drawPaint.setStyle(Paint.Style.STROKE); + drawPaint.setStrokeJoin(Paint.Join.ROUND); + drawPaint.setStrokeCap(Paint.Cap.ROUND); + + BGPaint.setColor(Color.BLACK); + BGPaint.setAntiAlias(true); + BGPaint.setStrokeWidth(getResources().getDimension(R.dimen.thumbLineWidth) * 3); + //BGPaint.setStrokeWidth(6); + BGPaint.setStyle(Paint.Style.STROKE); + BGPaint.setStrokeJoin(Paint.Join.ROUND); + BGPaint.setStrokeCap(Paint.Cap.ROUND); + + // EndDotdrawPaint.setColor(getResources().getColor(R.color.colorThumbnailLineColor)); + EndDotdrawPaint.setAntiAlias(true); + EndDotdrawPaint.setStrokeWidth(getResources().getDimension(R.dimen.thumbLineWidth) * 2.5f); + EndDotdrawPaint.setStyle(Paint.Style.STROKE); + EndDotdrawPaint.setStrokeJoin(Paint.Join.ROUND); + EndDotdrawPaint.setStrokeCap(Paint.Cap.ROUND); + + EndDotBGPaint.setColor(Color.BLACK); + EndDotBGPaint.setAntiAlias(true); + EndDotBGPaint.setStrokeWidth(getResources().getDimension(R.dimen.thumbLineWidth) * 4.5f); + EndDotBGPaint.setStyle(Paint.Style.STROKE); + EndDotBGPaint.setStrokeJoin(Paint.Join.ROUND); + EndDotBGPaint.setStrokeCap(Paint.Cap.ROUND); + + // Calculate the drawing scale + double Mid_Latitude = (track.getMax_Latitude() + track.getMin_Latitude()) / 2; + double Angle_From_Equator = Math.abs(Mid_Latitude); + + Distance_Proportion = Math.cos(Math.toRadians(Angle_From_Equator)); + //Log.w("myApp", "[#] GPSApplication.java - Distance_Proportion = " + Distance_Proportion); + + DrawScale = Math.max(track.getMax_Latitude() - track.getMin_Latitude(), Distance_Proportion * (track.getMax_Longitude() - track.getMin_Longitude())); + Lat_Offset = Size_Minus_Margins * (1 - (track.getMax_Latitude() - track.getMin_Latitude()) / DrawScale) / 2; + Lon_Offset = Size_Minus_Margins * (1 - (Distance_Proportion * (track.getMax_Longitude() - track.getMin_Longitude()) / DrawScale)) / 2; + + MinLatitude = track.getMin_Latitude(); + MinLongitude = track.getMin_Longitude(); + + asyncThumbnailThreadClass.start(); + } + } + + private class AsyncThumbnailThreadClass extends Thread { + + public AsyncThumbnailThreadClass() {} + + public void run() { + Thread.currentThread().setPriority(Thread.MIN_PRIORITY); + + String fname = Id + ".png"; + File file = new File(getApplicationContext().getFilesDir() + "/Thumbnails/", fname); + if (file.exists()) file.delete(); + + if (DrawScale > 0) { + int GroupOfLocations = 200; + Path path = new Path(); + List latlngList = new ArrayList<>(); + + //Log.w("myApp", "[#] GPSApplication.java - Thumbnailer Thread started"); + for (int i = 0; i < NumberOfLocations; i += GroupOfLocations) { + latlngList.addAll(GPSDataBase.getLatLngList(Id, i, i + GroupOfLocations - 1)); + } + //Log.w("myApp", "[#] GPSApplication.java - Added " + latlngList.size() + " items to Path"); + if (!latlngList.isEmpty()) { + Bitmap ThumbBitmap = Bitmap.createBitmap(Size, Size, Bitmap.Config.ARGB_8888); + Canvas ThumbCanvas = new Canvas(ThumbBitmap); + + for (int i = 0; i < latlngList.size(); i++) { + if (i == 0) + path.moveTo((float) (Lon_Offset + Margin + Size_Minus_Margins * ((latlngList.get(i).Longitude - MinLongitude) * Distance_Proportion / DrawScale)), + (float) (-Lat_Offset + Size - (Margin + Size_Minus_Margins * ((latlngList.get(i).Latitude - MinLatitude) / DrawScale)))); + else + path.lineTo((float) (Lon_Offset + Margin + Size_Minus_Margins * ((latlngList.get(i).Longitude - MinLongitude) * Distance_Proportion / DrawScale)), + (float) (-Lat_Offset + Size - (Margin + Size_Minus_Margins * ((latlngList.get(i).Latitude - MinLatitude) / DrawScale)))); + } + ThumbCanvas.drawPath(path, BGPaint); + ThumbCanvas.drawPoint((float) (Lon_Offset + Margin + Size_Minus_Margins * ((latlngList.get(latlngList.size()-1).Longitude - MinLongitude) * Distance_Proportion / DrawScale)), + (float) (-Lat_Offset + Size - (Margin + Size_Minus_Margins * ((latlngList.get(latlngList.size()-1).Latitude - MinLatitude) / DrawScale))), EndDotBGPaint); + ThumbCanvas.drawPath(path, drawPaint); + ThumbCanvas.drawPoint((float) (Lon_Offset + Margin + Size_Minus_Margins * ((latlngList.get(latlngList.size()-1).Longitude - MinLongitude) * Distance_Proportion / DrawScale)), + (float) (-Lat_Offset + Size - (Margin + Size_Minus_Margins * ((latlngList.get(latlngList.size()-1).Latitude - MinLatitude) / DrawScale))), EndDotdrawPaint); + + try { + FileOutputStream out = new FileOutputStream(file); + //Log.w("myApp", "[#] GPSApplication.java - FileOutputStream out = new FileOutputStream(file)"); + //boolean res = ThumbBitmap.compress(Bitmap.CompressFormat.PNG, 60, out); + ThumbBitmap.compress(Bitmap.CompressFormat.PNG, 60, out); + //Log.w("myApp", "[#] GPSApplication.java - ThumbBitmap.compress(Bitmap.CompressFormat.PNG, 60, out): " + res); + out.flush(); + //Log.w("myApp", "[#] GPSApplication.java - out.flush();"); + out.close(); + //Log.w("myApp", "[#] GPSApplication.java - out.close();"); + } catch (Exception e) { + e.printStackTrace(); + //Log.w("myApp", "[#] GPSApplication.java - Unable to save: " + Environment.getExternalStorageDirectory() + "/GPSLogger/AppData/" + fname); + } + + EventBus.getDefault().post(EventBusMSG.REFRESH_TRACKLIST); + } + } + } + } + } +} diff --git a/app/src/main/java/Satellite/GPSService.java b/app/src/main/java/Satellite/GPSService.java new file mode 100644 index 0000000..2cd2787 --- /dev/null +++ b/app/src/main/java/Satellite/GPSService.java @@ -0,0 +1,121 @@ + +package Satellite; + +import android.app.Notification; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.os.PowerManager; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +public class GPSService extends Service { + // Singleton instance + private static GPSService singleton; + public static GPSService getInstance(){ + return singleton; + } + // IBinder + private final IBinder mBinder = new LocalBinder(); + public class LocalBinder extends Binder { //returns the instance of the service + public GPSService getServiceInstance(){ + return GPSService.this; + } + } + + // PARTIAL_WAKELOCK + private PowerManager powerManager; + PowerManager.WakeLock wakeLock; + + private Notification getNotification() { + final String CHANNEL_ID = "GPSLoggerServiceChannel"; + + NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID); + //builder.setSmallIcon(R.drawable.ic_notification_24dp) + /* builder.setSmallIcon(R.mipmap.ic_notify_24dp) + .setColor(getResources().getColor(R.color.colorPrimaryLight)) + .setContentTitle(getString(R.string.app_name)) + .setShowWhen(false) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .setOngoing(true) + .setContentText(getString(R.string.notification_contenttext)); +*/ + //if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ + // builder.setPriority(NotificationCompat.PRIORITY_LOW); + //} + + final Intent startIntent = new Intent(getApplicationContext(), GPSActivity.class); + startIntent.setAction(Intent.ACTION_MAIN); + startIntent.addCategory(Intent.CATEGORY_LAUNCHER); + //startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 1, startIntent, 0); + builder.setContentIntent(contentIntent); + return builder.build(); + } + + /* THREAD FOR DEBUG PURPOSE + Thread t = new Thread() { + public void run() { + boolean i = true; + while (i) { + try { + sleep(1000); + Log.w("myApp", "[#] GPSService.java - ** RUNNING **"); + } catch (InterruptedException e) { + i = false; + } + } + } + }; */ + + @Override + public void onCreate() { + super.onCreate(); + singleton = this; + // THREAD FOR DEBUG PURPOSE + //if (!t.isAlive()) { + // t.start(); + //} + + // PARTIAL_WAKELOCK + powerManager = (PowerManager) getSystemService(POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"GPSLogger:wakelock"); + Log.w("myApp", "[#] GPSService.java - CREATE = onCreate"); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + startForeground(1, getNotification()); + Log.w("myApp", "[#] GPSService.java - START = onStartCommand"); + return START_NOT_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + if (wakeLock != null && !wakeLock.isHeld()) { + wakeLock.acquire(); + Log.w("myApp", "[#] GPSService.java - WAKELOCK acquired"); + } + Log.w("myApp", "[#] GPSService.java - BIND = onBind"); + return mBinder; + //return null; + } + + @Override + public void onDestroy() { + // PARTIAL_WAKELOCK + if (wakeLock != null && wakeLock.isHeld()) { + wakeLock.release(); + Log.w("myApp", "[#] GPSService.java - WAKELOCK released"); + } + + Log.w("myApp", "[#] GPSService.java - DESTROY = onDestroy"); + // THREAD FOR DEBUG PURPOSE + //if (t.isAlive()) t.interrupt(); + super.onDestroy(); + } +} diff --git a/app/src/main/java/Satellite/LatLng.java b/app/src/main/java/Satellite/LatLng.java new file mode 100644 index 0000000..63de7fc --- /dev/null +++ b/app/src/main/java/Satellite/LatLng.java @@ -0,0 +1,7 @@ + +package Satellite; + +class LatLng { + double Latitude; + double Longitude; +} diff --git a/app/src/main/java/Satellite/LocationExtended.java b/app/src/main/java/Satellite/LocationExtended.java new file mode 100644 index 0000000..c8686c0 --- /dev/null +++ b/app/src/main/java/Satellite/LocationExtended.java @@ -0,0 +1,97 @@ + +package Satellite; + +import android.location.Location; + +public class LocationExtended { + + private final int NOT_AVAILABLE = -100000; + + private Location _Location; + private String _Description = ""; + private double _AltitudeEGM96Correction = NOT_AVAILABLE; + private int _NumberOfSatellites = NOT_AVAILABLE; + private int _NumberOfSatellitesUsedInFix = NOT_AVAILABLE; + private String Satellite_info = ""; + + + // Constructor + public LocationExtended(Location location) { + _Location = location; + EGM96 egm96 = EGM96.getInstance(); + if (egm96 != null) { + if (egm96.isEGMGridLoaded()) _AltitudeEGM96Correction = egm96.getEGMCorrection(_Location.getLatitude(), _Location.getLongitude()); + } + } + + // Getters and Setters ------------------------------------------------------------------------- + + public Location getLocation() { + return _Location; + } + + public double getLatitude() { return _Location.getLatitude(); } + public double getLongitude() { return _Location.getLongitude(); } + public double getAltitude() { return _Location.hasAltitude() ? _Location.getAltitude() : NOT_AVAILABLE; } + public float getSpeed() { return _Location.hasSpeed() ? _Location.getSpeed() : NOT_AVAILABLE; } + public float getAccuracy() { return _Location.hasAccuracy() ? _Location.getAccuracy() : NOT_AVAILABLE; } + public float getBearing() { return _Location.hasBearing() ? _Location.getBearing() : NOT_AVAILABLE; } + public long getTime() { return _Location.getTime(); } + public String getSatellite_info(){return Satellite_info; } + + public String getDescription() { + return _Description; + } + + public void setDescription(String Description) { + this._Description = Description; + } + + public void setNumberOfSatellites(int numberOfSatellites) { + _NumberOfSatellites = numberOfSatellites; + } + + public int getNumberOfSatellites() { + return _NumberOfSatellites; + } + + public void setNumberOfSatellitesUsedInFix(int numberOfSatellites) { + _NumberOfSatellitesUsedInFix = numberOfSatellites; + } + + public void setSatellite_info(String satellite_info){ + this.Satellite_info = satellite_info; + + + } + + public int getNumberOfSatellitesUsedInFix() { + return _NumberOfSatellitesUsedInFix; + } + + + + public double getAltitudeEGM96Correction(){ + if (_AltitudeEGM96Correction == NOT_AVAILABLE) { + //Log.w("myApp", "[#] LocationExtended.java - _AltitudeEGM96Correction == NOT_AVAILABLE"); + EGM96 egm96 = EGM96.getInstance(); + if (egm96 != null) { + if (egm96.isEGMGridLoaded()) _AltitudeEGM96Correction = egm96.getEGMCorrection(_Location.getLatitude(), _Location.getLongitude()); + } + } + return _AltitudeEGM96Correction; + } + + public double getAltitudeCorrected(double AltitudeManualCorrection, boolean EGMCorrection) { + if (_Location != null) { + if (!_Location.hasAltitude()) return NOT_AVAILABLE; + if ((EGMCorrection) && (getAltitudeEGM96Correction() != NOT_AVAILABLE)) return _Location.getAltitude() - getAltitudeEGM96Correction() + AltitudeManualCorrection; + else return _Location.getAltitude() + AltitudeManualCorrection; + } + return NOT_AVAILABLE; + } + + + +} + diff --git a/app/src/main/java/Satellite/PhysicalData.java b/app/src/main/java/Satellite/PhysicalData.java new file mode 100644 index 0000000..74e18f1 --- /dev/null +++ b/app/src/main/java/Satellite/PhysicalData.java @@ -0,0 +1,7 @@ + +package Satellite; + +class PhysicalData { + String Value; //The string of the Numerical value of the Physical Quantity + String UM; //The string of the Unit of Measurement +} diff --git a/app/src/main/java/Satellite/PhysicalDataFormatter.java b/app/src/main/java/Satellite/PhysicalDataFormatter.java new file mode 100644 index 0000000..64b0c6d --- /dev/null +++ b/app/src/main/java/Satellite/PhysicalDataFormatter.java @@ -0,0 +1,258 @@ + +package Satellite; + +import android.location.Location; + +import java.text.SimpleDateFormat; +import java.util.TimeZone; + +import fr.geolabs.dev.mapmint4me.R; + +class PhysicalDataFormatter { + + private final int NOT_AVAILABLE = -100000; + + private final int UM_METRIC_MS = 0; + private final int UM_METRIC_KMH = 1; + private final int UM_IMPERIAL_FPS = 8; + private final int UM_IMPERIAL_MPH = 9; + private final int UM_NAUTICAL_KN = 16; + private final int UM_NAUTICAL_MPH = 17; + + static final byte FORMAT_LATITUDE = 1; + static final byte FORMAT_LONGITUDE = 2; + static final byte FORMAT_ALTITUDE = 3; + static final byte FORMAT_SPEED = 4; + static final byte FORMAT_ACCURACY = 5; + static final byte FORMAT_BEARING = 6; + static final byte FORMAT_DURATION = 7; + static final byte FORMAT_SPEED_AVG = 8; + static final byte FORMAT_DISTANCE = 9; + static final byte FORMAT_TIME = 10; + + private final float M_TO_FT = 3.280839895f; + private final float M_TO_NM = 0.000539957f; + private final float MS_TO_MPH = 2.2369363f; + private final float MS_TO_KMH = 3.6f; + private final float MS_TO_KN = 1.943844491f; + private final float KM_TO_MI = 0.621371192237f; + + //private PhysicalData _PhysicalData = new PhysicalData(); + private GPSApplication gpsApplication = GPSApplication.getInstance(); + + + public PhysicalData format(float Number, byte Format) { + PhysicalData _PhysicalData = new PhysicalData(); + _PhysicalData.Value = ""; + _PhysicalData.UM = ""; + + if (Number == NOT_AVAILABLE) return(_PhysicalData); // Returns empty fields if the data is not available + + switch (Format) { + case FORMAT_SPEED: // Speed + switch (gpsApplication.getPrefUM()) { + case UM_METRIC_KMH: + _PhysicalData.Value = String.valueOf(Math.round(Number * MS_TO_KMH)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_km_h); + return(_PhysicalData); + case UM_METRIC_MS: + _PhysicalData.Value = String.valueOf(Math.round(Number)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_m_s); + return(_PhysicalData); + case UM_IMPERIAL_MPH: + case UM_NAUTICAL_MPH: + _PhysicalData.Value = String.valueOf(Math.round(Number * MS_TO_MPH)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_mph); + return(_PhysicalData); + case UM_IMPERIAL_FPS: + _PhysicalData.Value = String.valueOf(Math.round(Number * M_TO_FT)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_fps); + return(_PhysicalData); + case UM_NAUTICAL_KN: + _PhysicalData.Value = String.valueOf(Math.round(Number * MS_TO_KN)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_kn); + return(_PhysicalData); + } + + case FORMAT_SPEED_AVG: // Average Speed, formatted with 1 decimal + switch (gpsApplication.getPrefUM()) { + case UM_METRIC_KMH: + _PhysicalData.Value = String.format("%.1f", (Number * MS_TO_KMH)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_km_h); + return(_PhysicalData); + case UM_METRIC_MS: + _PhysicalData.Value = String.format("%.1f", (Number)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_m_s); + return(_PhysicalData); + case UM_IMPERIAL_MPH: + case UM_NAUTICAL_MPH: + _PhysicalData.Value = String.format("%.1f", (Number * MS_TO_MPH)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_mph); + return(_PhysicalData); + case UM_IMPERIAL_FPS: + _PhysicalData.Value = String.format("%.1f", (Number * M_TO_FT)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_fps); + return(_PhysicalData); + case UM_NAUTICAL_KN: + _PhysicalData.Value = String.format("%.1f", (Number * MS_TO_KN)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_kn); + return(_PhysicalData); + } + + case FORMAT_ACCURACY: // Accuracy + switch (gpsApplication.getPrefUM()) { + case UM_METRIC_KMH: + case UM_METRIC_MS: + _PhysicalData.Value = String.valueOf(Math.round(Number)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_m); + return(_PhysicalData); + case UM_IMPERIAL_MPH: + case UM_IMPERIAL_FPS: + case UM_NAUTICAL_MPH: + case UM_NAUTICAL_KN: + _PhysicalData.Value = String.valueOf(Math.round(Number * M_TO_FT)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_ft); + return(_PhysicalData); + } + + case FORMAT_BEARING: // Bearing (Direction) + switch (gpsApplication.getPrefShowDirections()) { + case 0: // NSWE + int dr = (int) Math.round(Number / 22.5); + switch (dr) { + case 0: _PhysicalData.Value = gpsApplication.getString(R.string.north); return(_PhysicalData); + case 1: _PhysicalData.Value = gpsApplication.getString(R.string.north_northeast); return(_PhysicalData); + case 2: _PhysicalData.Value = gpsApplication.getString(R.string.northeast); return(_PhysicalData); + case 3: _PhysicalData.Value = gpsApplication.getString(R.string.east_northeast); return(_PhysicalData); + case 4: _PhysicalData.Value = gpsApplication.getString(R.string.east); return(_PhysicalData); + case 5: _PhysicalData.Value = gpsApplication.getString(R.string.east_southeast); return(_PhysicalData); + case 6: _PhysicalData.Value = gpsApplication.getString(R.string.southeast); return(_PhysicalData); + case 7: _PhysicalData.Value = gpsApplication.getString(R.string.south_southeast); return(_PhysicalData); + case 8: _PhysicalData.Value = gpsApplication.getString(R.string.south); return(_PhysicalData); + case 9: _PhysicalData.Value = gpsApplication.getString(R.string.south_southwest); return(_PhysicalData); + case 10: _PhysicalData.Value = gpsApplication.getString(R.string.southwest); return(_PhysicalData); + case 11: _PhysicalData.Value = gpsApplication.getString(R.string.west_southwest); return(_PhysicalData); + case 12: _PhysicalData.Value = gpsApplication.getString(R.string.west); return(_PhysicalData); + case 13: _PhysicalData.Value = gpsApplication.getString(R.string.west_northwest); return(_PhysicalData); + case 14: _PhysicalData.Value = gpsApplication.getString(R.string.northwest); return(_PhysicalData); + case 15: _PhysicalData.Value = gpsApplication.getString(R.string.north_northwest); return(_PhysicalData); + case 16: _PhysicalData.Value = gpsApplication.getString(R.string.north); return(_PhysicalData); + } + case 1: // Angle + _PhysicalData.Value = String.valueOf(Math.round(Number)); + return(_PhysicalData); + } + + case FORMAT_DISTANCE: // Distance + switch (gpsApplication.getPrefUM()) { + case UM_METRIC_KMH: + case UM_METRIC_MS: + if (Number < 1000) { + _PhysicalData.Value = String.format("%.0f", (Math.floor(Number))); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_m); + } + else { + if (Number < 10000) _PhysicalData.Value = String.format("%.2f" , ((Math.floor(Number / 10.0)))/100.0); + else _PhysicalData.Value = String.format("%.1f" , ((Math.floor(Number / 100.0)))/10.0); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_km); + } + return(_PhysicalData); + case UM_IMPERIAL_MPH: + case UM_IMPERIAL_FPS: + if ((Number * M_TO_FT) < 1000) { + _PhysicalData.Value = String.format("%.0f", (Math.floor(Number * M_TO_FT))); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_ft); + } + else { + if ((Number * KM_TO_MI) < 10000) _PhysicalData.Value = String.format("%.2f", ((Math.floor((Number * KM_TO_MI) / 10.0)))/100.0); + else _PhysicalData.Value = String.format("%.1f", ((Math.floor((Number * KM_TO_MI) / 100.0)))/10.0); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_mi); + } + return(_PhysicalData); + case UM_NAUTICAL_KN: + case UM_NAUTICAL_MPH: + if ((Number * M_TO_NM) < 100) _PhysicalData.Value = String.format("%.2f", ((Math.floor((Number * M_TO_NM) * 100.0))) / 100.0); + else _PhysicalData.Value = String.format("%.1f", ((Math.floor((Number * M_TO_NM) * 10.0))) / 10.0); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_nm); + return(_PhysicalData); + } + } + return(_PhysicalData); + } + + public PhysicalData format(double Number, byte Format) { + PhysicalData _PhysicalData = new PhysicalData(); + _PhysicalData.Value = ""; + _PhysicalData.UM = ""; + + if (Number == NOT_AVAILABLE) return(_PhysicalData); // Returns empty fields if the data is not available + + switch (Format) { + case FORMAT_LATITUDE: // Latitude + _PhysicalData.Value = gpsApplication.getPrefShowDecimalCoordinates() ? + String.format("%.9f", Math.abs(Number)) : Location.convert(Math.abs(Number), Location.FORMAT_SECONDS); + _PhysicalData.UM = Number >= 0 ? gpsApplication.getString(R.string.north) : gpsApplication.getString(R.string.south); + return(_PhysicalData); + + case FORMAT_LONGITUDE: // Longitude + _PhysicalData.Value = gpsApplication.getPrefShowDecimalCoordinates() ? + String.format("%.9f", Math.abs(Number)) : Location.convert(Math.abs(Number), Location.FORMAT_SECONDS); + _PhysicalData.UM = Number >= 0 ? + gpsApplication.getString(R.string.east) : gpsApplication.getString(R.string.west); + return(_PhysicalData); + + case FORMAT_ALTITUDE: // Altitude + switch (gpsApplication.getPrefUM()) { + case UM_METRIC_KMH: + case UM_METRIC_MS: + _PhysicalData.Value = String.valueOf(Math.round(Number)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_m); + return(_PhysicalData); + case UM_IMPERIAL_MPH: + case UM_IMPERIAL_FPS: + case UM_NAUTICAL_KN: + case UM_NAUTICAL_MPH: + _PhysicalData.Value = String.valueOf(Math.round(Number * M_TO_FT)); + _PhysicalData.UM = gpsApplication.getString(R.string.UM_ft); + return(_PhysicalData); + } + } + return(_PhysicalData); + } + + public PhysicalData format(long Number, byte Format) { + PhysicalData _PhysicalData = new PhysicalData(); + _PhysicalData.Value = ""; + _PhysicalData.UM = ""; + + if (Number == NOT_AVAILABLE) return(_PhysicalData); // Returns empty fields if the data is not available + + switch (Format) { + case FORMAT_DURATION: // Durations + long time = Number / 1000; + String seconds = Integer.toString((int) (time % 60)); + String minutes = Integer.toString((int) ((time % 3600) / 60)); + String hours = Integer.toString((int) (time / 3600)); + for (int i = 0; i < 2; i++) { + if (seconds.length() < 2) { + seconds = "0" + seconds; + } + if (minutes.length() < 2) { + minutes = "0" + minutes; + } + if (hours.length() < 2) { + hours = "0" + hours; + } + } + _PhysicalData.Value = hours.equals("00") ? minutes + ":" + seconds : hours + ":" + minutes + ":" + seconds; + return(_PhysicalData); + + case FORMAT_TIME: // Timestamps + SimpleDateFormat dfdTime = new SimpleDateFormat("HH:mm:ss"); // date and time formatter + dfdTime.setTimeZone(TimeZone.getTimeZone("GMT")); + _PhysicalData.Value = dfdTime.format(Number); + return(_PhysicalData); + } + return(_PhysicalData); + } +} diff --git a/app/src/main/java/Satellite/SpikesChecker.java b/app/src/main/java/Satellite/SpikesChecker.java new file mode 100644 index 0000000..5ad63a9 --- /dev/null +++ b/app/src/main/java/Satellite/SpikesChecker.java @@ -0,0 +1,76 @@ +/* + * SpikesChecker - Java Class for Android + * Created by G.Capelli (BasicAirData) on 15/9/2016 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package Satellite; + + +class SpikesChecker { + private static final int NOT_AVAILABLE = -100000; + + private long Good_Time = NOT_AVAILABLE; // The time of the last good value + + private double Prev_Altitude = NOT_AVAILABLE; // the previous data loaded + private long Prev_Time = NOT_AVAILABLE; + private float Prev_VerticalSpeed = NOT_AVAILABLE; + + private double New_Altitude = NOT_AVAILABLE; // the new (current) data loaded + private long New_Time = NOT_AVAILABLE; + private float New_VerticalSpeed = NOT_AVAILABLE; + + private long Time_Interval = NOT_AVAILABLE; // Interval between fixes (in seconds) + private float VerticalAcceleration; + + private float MAX_ACCELERATION; // The maximum vertical acceleration allowed + private int STABILIZATION_TIME = 4; // Stabilization window, in seconds. It must be > 0 + + // Constructor + SpikesChecker(float max_acceleration, int Stabilization_Time) { + MAX_ACCELERATION = max_acceleration; + STABILIZATION_TIME = Stabilization_Time; + } + + void load(long Time, double Altitude) { + if (Time > New_Time) { + Prev_Time = New_Time; + New_Time = Time; + Prev_Altitude = New_Altitude; + Prev_VerticalSpeed = New_VerticalSpeed; + } + + Time_Interval = Prev_Time != NOT_AVAILABLE ? (New_Time - Prev_Time) / 1000 : NOT_AVAILABLE; + New_Altitude = Altitude; + + if ((Time_Interval > 0) && (Prev_Altitude != NOT_AVAILABLE)) { + New_VerticalSpeed = (float) (New_Altitude - Prev_Altitude) / Time_Interval; + + if (Prev_VerticalSpeed != NOT_AVAILABLE) { + if (Time_Interval > 1000) VerticalAcceleration = NOT_AVAILABLE; // Prevent Vertical Acceleration value from exploding + else VerticalAcceleration = 2 * (-Prev_VerticalSpeed * Time_Interval + (float)(New_Altitude - Prev_Altitude)) / (Time_Interval * Time_Interval); + } + } + + if (Math.abs(VerticalAcceleration) >= MAX_ACCELERATION) Good_Time = New_Time ; + + //Log.w("myApp", "[#] SpikesChecker.java - Vertical Acceleration = " + VerticalAcceleration); + //Log.w("myApp", "[#] SpikesChecker.java - Validation window = " + (New_Time - Good_Time) / 1000); + } + + boolean isValid() { + return (New_Time - Good_Time) / 1000 >= STABILIZATION_TIME; + } +} diff --git a/app/src/main/java/Satellite/Track.java b/app/src/main/java/Satellite/Track.java new file mode 100644 index 0000000..5d6caf4 --- /dev/null +++ b/app/src/main/java/Satellite/Track.java @@ -0,0 +1,724 @@ + +package Satellite; + +import android.location.Location; + +import java.text.SimpleDateFormat; + +public class Track { + + // Constants + private static final int NOT_AVAILABLE = -100000; + private static final double MIN_ALTITUDE_STEP = 8.0; + private static final float MOVEMENT_SPEED_THRESHOLD = 0.5f; // The minimum speed (in m/s) to consider the user in movement + private static final float STANDARD_ACCURACY = 10.0f; + private static final float SECURITY_COEFF = 1.7f; + + private final int TRACK_TYPE_STEADY = 0; + private final int TRACK_TYPE_WALK = 1; + private final int TRACK_TYPE_MOUNTAIN = 2; + private final int TRACK_TYPE_RUN = 3; + private final int TRACK_TYPE_BICYCLE = 4; + private final int TRACK_TYPE_CAR = 5; + private final int TRACK_TYPE_FLIGHT = 6; + private final int TRACK_TYPE_ND = NOT_AVAILABLE; + + // Variables + private long id; // Saved in DB + private String Name = ""; // Saved in DB + + private double Start_Latitude = NOT_AVAILABLE; // Saved in DB + private double Start_Longitude = NOT_AVAILABLE; // Saved in DB + private double Start_Altitude = NOT_AVAILABLE; // Saved in DB + private double Start_EGMAltitudeCorrection = NOT_AVAILABLE; + private float Start_Accuracy = STANDARD_ACCURACY;// Saved in DB + private float Start_Speed = NOT_AVAILABLE; // Saved in DB + private long Start_Time = NOT_AVAILABLE; // Saved in DB + + private long LastFix_Time = NOT_AVAILABLE; // Saved in DB + + private double End_Latitude = NOT_AVAILABLE; // Saved in DB + private double End_Longitude = NOT_AVAILABLE; // Saved in DB + private double End_Altitude = NOT_AVAILABLE; // Saved in DB + private double End_EGMAltitudeCorrection = NOT_AVAILABLE; + private float End_Accuracy = STANDARD_ACCURACY;// Saved in DB + private float End_Speed = NOT_AVAILABLE; // Saved in DB + private long End_Time = NOT_AVAILABLE; // Saved in DB + + private double LastStepDistance_Latitude = NOT_AVAILABLE; // Saved in DB + private double LastStepDistance_Longitude = NOT_AVAILABLE; // Saved in DB + private float LastStepDistance_Accuracy = STANDARD_ACCURACY;// Saved in DB + + private double LastStepAltitude_Altitude = NOT_AVAILABLE; // Saved in DB + private float LastStepAltitude_Accuracy = STANDARD_ACCURACY;// Saved in DB + + private double Min_Latitude = NOT_AVAILABLE; // Saved in DB + private double Min_Longitude = NOT_AVAILABLE; // Saved in DB + + private double Max_Latitude = NOT_AVAILABLE; // Saved in DB + private double Max_Longitude = NOT_AVAILABLE; // Saved in DB + + private long Duration = NOT_AVAILABLE; // Saved in DB + private long Duration_Moving = NOT_AVAILABLE; // Saved in DB + + private float Distance = NOT_AVAILABLE; // Saved in DB + private float DistanceInProgress = NOT_AVAILABLE; // Saved in DB + private long DistanceLastAltitude = NOT_AVAILABLE; // Saved in DB + + private double Altitude_Up = NOT_AVAILABLE; // Saved in DB + private double Altitude_Down = NOT_AVAILABLE; // Saved in DB + private double Altitude_InProgress = NOT_AVAILABLE; // Saved in DB + + private float SpeedMax = NOT_AVAILABLE; // Saved in DB + private float SpeedAverage = NOT_AVAILABLE; // Saved in DB + private float SpeedAverageMoving = NOT_AVAILABLE; // Saved in DB + + private long NumberOfLocations = 0; // Saved in DB + private long NumberOfPlacemarks = 0; // Saved in DB + + private int ValidMap = 1; // Saved in DB + // 1 = Map extents valid, OK generation of Thumb + // 0 = Do not generate thumb (track crosses antimeridian) + + private int Type = TRACK_TYPE_ND; // Saved in DB + + // True if the card view is selected + private boolean Selected = false; + + // The altitude validator (the anti spikes filter): + // - Max Acceleration = 12 m/s^2 + // - Stabilization time = 4 s + private SpikesChecker AltitudeFilter = new SpikesChecker(12, 4); + + public void add(LocationExtended location) { + if (NumberOfLocations == 0) { + // Init "Start" variables + Start_Latitude = location.getLocation().getLatitude(); + Start_Longitude = location.getLocation().getLongitude(); + if (location.getLocation().hasAltitude()) { + Start_Altitude = location.getLocation().getAltitude(); + } else { + Start_Altitude = NOT_AVAILABLE; + } + Start_EGMAltitudeCorrection = location.getAltitudeEGM96Correction(); + Start_Speed = location.getLocation().hasSpeed() ? location.getLocation().getSpeed() : NOT_AVAILABLE; + Start_Accuracy = location.getLocation().hasAccuracy() ? location.getLocation().getAccuracy() : STANDARD_ACCURACY; + Start_Time = location.getLocation().getTime(); + + LastStepDistance_Latitude = Start_Latitude; + LastStepDistance_Longitude = Start_Longitude; + LastStepDistance_Accuracy = Start_Accuracy; + + Max_Latitude = Start_Latitude; + Max_Longitude = Start_Longitude; + Min_Latitude = Start_Latitude; + Min_Longitude = Start_Longitude; + + if (Name.equals("")) { + SimpleDateFormat df2 = new SimpleDateFormat("yyyyMMdd-HHmmss"); + Name = df2.format(Start_Time); + } + + LastFix_Time = Start_Time; + End_Time = Start_Time; + + Duration_Moving = 0; + Duration = 0; + Distance = 0; + } + + LastFix_Time = End_Time; + + End_Latitude = location.getLocation().getLatitude(); + End_Longitude = location.getLocation().getLongitude(); + if (location.getLocation().hasAltitude()) { + End_Altitude = location.getLocation().getAltitude(); + } else { + End_Altitude = NOT_AVAILABLE; + } + End_EGMAltitudeCorrection = location.getAltitudeEGM96Correction(); + + End_Speed = location.getLocation().hasSpeed() ? location.getLocation().getSpeed() : NOT_AVAILABLE; + End_Accuracy = location.getLocation().hasAccuracy() ? location.getLocation().getAccuracy() : STANDARD_ACCURACY; + End_Time = location.getLocation().getTime(); + + if (End_EGMAltitudeCorrection == NOT_AVAILABLE) getEnd_EGMAltitudeCorrection(); + if (Start_EGMAltitudeCorrection == NOT_AVAILABLE) getStart_EGMAltitudeCorrection(); + + // ---------------------------------------------- Load the new value into antispikes filter + if (End_Altitude != NOT_AVAILABLE) AltitudeFilter.load(End_Time, End_Altitude); + + // ------------------------------------------------------------- Coords for thumb and stats + + if (ValidMap != 0) { + if (End_Latitude > Max_Latitude) Max_Latitude = End_Latitude; + if (End_Longitude > Max_Longitude) Max_Longitude = End_Longitude; + if (End_Latitude < Min_Latitude) Min_Latitude = End_Latitude; + if (End_Longitude < Min_Longitude) Min_Longitude = End_Longitude; + + if (Math.abs(LastStepDistance_Longitude - End_Longitude) > 90) ValidMap = 0; + // YOU PASS FROM -180 TO +180, OR REVERSE. iN THE PACIFIC OCEAN. + // in that case the app doesn't generate the thumb map. + } + + // ---------------------------------------------------------------------------------- Times + + Duration = End_Time - Start_Time; + if (End_Speed >= MOVEMENT_SPEED_THRESHOLD) Duration_Moving += End_Time - LastFix_Time; + + // --------------------------- Spaces (Distances) increment if distance > sum of accuracies + + // -- Temp locations for "DistanceTo" + Location LastStepDistanceLoc = new Location("TEMP"); + LastStepDistanceLoc.setLatitude(LastStepDistance_Latitude); + LastStepDistanceLoc.setLongitude(LastStepDistance_Longitude); + + Location EndLoc = new Location("TEMP"); + EndLoc.setLatitude(End_Latitude); + EndLoc.setLongitude(End_Longitude); + // ----------------------------------- + + DistanceInProgress = LastStepDistanceLoc.distanceTo(EndLoc); + float DeltaDistancePlusAccuracy = DistanceInProgress + End_Accuracy; + + if (DeltaDistancePlusAccuracy < DistanceInProgress + End_Accuracy) { + LastStepDistance_Accuracy = DeltaDistancePlusAccuracy; + //Log.w("myApp", "[#] Track.java - LastStepDistance_Accuracy updated to " + LastStepDistance_Accuracy ); + } + + if (DistanceInProgress > End_Accuracy + LastStepDistance_Accuracy) { + Distance += DistanceInProgress; + if (DistanceLastAltitude != NOT_AVAILABLE) DistanceLastAltitude += DistanceInProgress; + DistanceInProgress = 0; + + LastStepDistance_Latitude = End_Latitude; + LastStepDistance_Longitude = End_Longitude; + LastStepDistance_Accuracy = End_Accuracy; + } + + // Found a first fix with altitude!! + if ((End_Altitude != NOT_AVAILABLE) && (DistanceLastAltitude == NOT_AVAILABLE)) { + DistanceLastAltitude = 0; + Altitude_Up = 0; + Altitude_Down = 0; + if (Start_Altitude == NOT_AVAILABLE) Start_Altitude = End_Altitude; + LastStepAltitude_Altitude = End_Altitude; + LastStepAltitude_Accuracy = End_Accuracy; + } + + if ((LastStepAltitude_Altitude != NOT_AVAILABLE) && (End_Altitude != NOT_AVAILABLE)) { + Altitude_InProgress = End_Altitude - LastStepAltitude_Altitude; + // Improve last step accuracy in case of new data elements: + float DeltaAltitudePlusAccuracy = (float) Math.abs(Altitude_InProgress) + End_Accuracy; + if (DeltaAltitudePlusAccuracy <= LastStepAltitude_Accuracy) { + LastStepAltitude_Accuracy = DeltaAltitudePlusAccuracy; + DistanceLastAltitude = 0; + //Log.w("myApp", "[#] Track.java - LastStepAltitude_Accuracy updated to " + LastStepAltitude_Accuracy ); + } + // Evaluate the altitude step convalidation: + if ((Math.abs(Altitude_InProgress) > MIN_ALTITUDE_STEP) && AltitudeFilter.isValid() + && ((float) Math.abs(Altitude_InProgress) > (SECURITY_COEFF * (LastStepAltitude_Accuracy + End_Accuracy)))) { + // Altitude step: + // increment distance only if the inclination is relevant (assume deltah=20m in max 5000m) + if (DistanceLastAltitude < 5000) { + float hypotenuse = (float) Math.sqrt((double) (DistanceLastAltitude * DistanceLastAltitude) + (Altitude_InProgress * Altitude_InProgress)); + Distance = Distance + hypotenuse - DistanceLastAltitude; + //Log.w("myApp", "[#] Track.java - Distance += " + (hypotenuse - DistanceLastAltitude)); + } + //Reset variables + LastStepAltitude_Altitude = End_Altitude; + LastStepAltitude_Accuracy = End_Accuracy; + DistanceLastAltitude = 0; + + if (Altitude_InProgress > 0) Altitude_Up += Altitude_InProgress; // Increment the correct value of Altitude UP/DOWN + else Altitude_Down -= Altitude_InProgress; + Altitude_InProgress = 0; + } + + } + + // --------------------------------------------------------------------------------- Speeds + + if ((End_Speed != NOT_AVAILABLE) && (End_Speed > SpeedMax)) SpeedMax = End_Speed; + if (Duration > 0) SpeedAverage = (Distance + DistanceInProgress) / (((float) Duration) / 1000f); + if (Duration_Moving > 0) SpeedAverageMoving = (Distance + DistanceInProgress) / (((float) Duration_Moving) / 1000f); + NumberOfLocations++; + } + + // Empty constructor + public Track(){ + } + + // constructor + public Track(String Name){ + this.Name = Name; + } + + public void FromDB(long id, String Name, String From, String To, + double Start_Latitude, double Start_Longitude, double Start_Altitude, + float Start_Accuracy, float Start_Speed, long Start_Time, long LastFix_Time, + double End_Latitude, double End_Longitude, double End_Altitude, + float End_Accuracy, float End_Speed, long End_Time, + double LastStepDistance_Latitude, double LastStepDistance_Longitude, float LastStepDistance_Accuracy, + double LastStepAltitude_Altitude, float LastStepAltitude_Accuracy, + double Min_Latitude, double Min_Longitude, + double Max_Latitude, double Max_Longitude, + long Duration, long Duration_Moving, float Distance, float DistanceInProgress, + long DistanceLastAltitude, double Altitude_Up, double Altitude_Down, + double Altitude_InProgress, float SpeedMax, float SpeedAverage, + float SpeedAverageMoving, long NumberOfLocations, long NumberOfPlacemarks, + int ValidMap, int Type) { + this.id = id; + this.Name = Name; + + this.Start_Latitude = Start_Latitude; + this.Start_Longitude = Start_Longitude; + this.Start_Altitude = Start_Altitude; + this.Start_Accuracy = Start_Accuracy; + this.Start_Speed = Start_Speed; + this.Start_Time = Start_Time; + + this.LastFix_Time = LastFix_Time; + + this.End_Latitude = End_Latitude; + this.End_Longitude = End_Longitude; + this.End_Altitude = End_Altitude; + this.End_Accuracy = End_Accuracy; + this.End_Speed = End_Speed; + this.End_Time = End_Time; + + this.LastStepDistance_Latitude = LastStepDistance_Latitude; + this.LastStepDistance_Longitude = LastStepDistance_Longitude; + this.LastStepDistance_Accuracy = LastStepDistance_Accuracy; + + this.LastStepAltitude_Altitude = LastStepAltitude_Altitude; + this.LastStepAltitude_Accuracy = LastStepAltitude_Accuracy; + + this.Min_Latitude = Min_Latitude; + this.Min_Longitude = Min_Longitude; + + this.Max_Latitude = Max_Latitude; + this.Max_Longitude = Max_Longitude; + + this.Duration = Duration; + this.Duration_Moving = Duration_Moving; + + this.Distance = Distance; + this.DistanceInProgress = DistanceInProgress; + this.DistanceLastAltitude = DistanceLastAltitude; + + this.Altitude_Up = Altitude_Up; + this.Altitude_Down = Altitude_Down; + this.Altitude_InProgress = Altitude_InProgress; + + this.SpeedMax = SpeedMax; + this.SpeedAverage = SpeedAverage; + this.SpeedAverageMoving = SpeedAverageMoving; + + this.NumberOfLocations = NumberOfLocations; + this.NumberOfPlacemarks = NumberOfPlacemarks; + + this.ValidMap = ValidMap; + this.Type = Type; + + EGM96 egm96 = EGM96.getInstance(); + if (egm96 != null) { + if (egm96.isEGMGridLoaded()) { + if (Start_Latitude != NOT_AVAILABLE) Start_EGMAltitudeCorrection = egm96.getEGMCorrection(Start_Latitude, Start_Longitude); + if (End_Latitude != NOT_AVAILABLE) End_EGMAltitudeCorrection = egm96.getEGMCorrection(End_Latitude, End_Longitude); + } + } + } + + + // ------------------------------------------------------------------------ Getters and Setters + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return Name; + } + + public void setName(String name) { + Name = name; + } + + public double getStart_Latitude() { + return Start_Latitude; + } + + public double getStart_Longitude() { + return Start_Longitude; + } + + public double getStart_Altitude() { + return Start_Altitude; + } + + public double getStart_EGMAltitudeCorrection() { + + if (Start_EGMAltitudeCorrection == NOT_AVAILABLE) { + EGM96 egm96 = EGM96.getInstance(); + if (egm96 != null) { + if (egm96.isEGMGridLoaded()) { + if (Start_Latitude != NOT_AVAILABLE) + Start_EGMAltitudeCorrection = egm96.getEGMCorrection(Start_Latitude, Start_Longitude); + } + } + } + return Start_EGMAltitudeCorrection; + } + + public float getStart_Accuracy() { + return Start_Accuracy; + } + + public float getStart_Speed() { + return Start_Speed; + } + + public long getStart_Time() { + return Start_Time; + } + + public long getLastFix_Time() { + return LastFix_Time; + } + + public double getEnd_Latitude() { + return End_Latitude; + } + + public double getEnd_Longitude() { + return End_Longitude; + } + + public double getEnd_Altitude() { + return End_Altitude; + } + + public double getEnd_EGMAltitudeCorrection() { + if (End_EGMAltitudeCorrection == NOT_AVAILABLE) { + EGM96 egm96 = EGM96.getInstance(); + if (egm96 != null) { + if (egm96.isEGMGridLoaded()) { + if (End_Latitude != NOT_AVAILABLE) + End_EGMAltitudeCorrection = egm96.getEGMCorrection(End_Latitude, End_Longitude); + } + } + } + return End_EGMAltitudeCorrection; + } + + public float getEnd_Accuracy() { + return End_Accuracy; + } + + public float getEnd_Speed() { + return End_Speed; + } + + public long getEnd_Time() { + return End_Time; + } + + public double getLastStepDistance_Latitude() { + return LastStepDistance_Latitude; + } + + public double getLastStepDistance_Longitude() { + return LastStepDistance_Longitude; + } + + public float getLastStepDistance_Accuracy() { + return LastStepDistance_Accuracy; + } + + public double getLastStepAltitude_Altitude() { + return LastStepAltitude_Altitude; + } + + public float getLastStepAltitude_Accuracy() { + return LastStepAltitude_Accuracy; + } + + public double getMin_Latitude() { + return Min_Latitude; + } + + public double getMin_Longitude() { + return Min_Longitude; + } + + public double getMax_Latitude() { + return Max_Latitude; + } + + public double getMax_Longitude() { + return Max_Longitude; + } + + public long getDuration() { + return Duration; + } + + public long getDuration_Moving() { + return Duration_Moving; + } + + public float getDistance() { + return Distance; + } + + public float getDistanceInProgress() { + return DistanceInProgress; + } + + public long getDistanceLastAltitude() { + return DistanceLastAltitude; + } + + public double getAltitude_Up() { + return Altitude_Up; + } + + public double getAltitude_Down() { + return Altitude_Down; + } + + public double getAltitude_InProgress() { + return Altitude_InProgress; + } + + public float getSpeedMax() { + return SpeedMax; + } + + public float getSpeedAverage() { + return SpeedAverage; + } + + public float getSpeedAverageMoving() { + return SpeedAverageMoving; + } + + public long getNumberOfLocations() { + return NumberOfLocations; + } + + public long getNumberOfPlacemarks() { + return NumberOfPlacemarks; + } + + public int getValidMap() { + return ValidMap; + } + + public int getType() { + return Type; + } + + public boolean isSelected() { + return Selected; + } + + public void setSelected(boolean selected) { + Selected = selected; + } + + // -------------------------------------------------------------------------------------------- + + public boolean isValidAltitude() { + return AltitudeFilter.isValid(); + } + + public long addPlacemark(LocationExtended location) { + this.NumberOfPlacemarks++ ; + + if (Name.equals("")) { + SimpleDateFormat df2 = new SimpleDateFormat("yyyyMMdd-HHmmss"); + Name = df2.format(location.getLocation().getTime()); + } + + return NumberOfPlacemarks; + } + + public float getEstimatedDistance(){ + if (NumberOfLocations == 0) return NOT_AVAILABLE; + if (NumberOfLocations == 1) return 0; + return Distance + DistanceInProgress; + } + + + public double getEstimatedAltitudeUp(boolean EGMCorrection){ + // Retrieve EGM Corrections if available + if ((Start_EGMAltitudeCorrection == NOT_AVAILABLE) || (End_EGMAltitudeCorrection == NOT_AVAILABLE)) { + EGM96 egm96 = EGM96.getInstance(); + if (egm96 != null) { + if (egm96.isEGMGridLoaded()) { + if (Start_Latitude != NOT_AVAILABLE) Start_EGMAltitudeCorrection = egm96.getEGMCorrection(Start_Latitude, Start_Longitude); + if (End_Latitude != NOT_AVAILABLE) End_EGMAltitudeCorrection = egm96.getEGMCorrection(End_Latitude, End_Longitude); + } + } + } + double egmcorr = 0; + if ((EGMCorrection) && ((Start_EGMAltitudeCorrection != NOT_AVAILABLE) && (End_EGMAltitudeCorrection != NOT_AVAILABLE))) { + egmcorr = Start_EGMAltitudeCorrection - End_EGMAltitudeCorrection; + } + double dresultUp = Altitude_InProgress > 0 ? Altitude_Up + Altitude_InProgress : Altitude_Up; + dresultUp -= egmcorr < 0 ? egmcorr : 0; + double dresultDown = Altitude_InProgress < 0 ? Altitude_Down - Altitude_InProgress : Altitude_Down; + dresultDown -= egmcorr > 0 ? egmcorr : 0; + + if (dresultUp < 0) { + dresultDown -= dresultUp; + dresultUp = 0; + } + if (dresultDown < 0) { + dresultUp -= dresultDown; + //dresultDown = 0; + } + return dresultUp; + } + + + public double getEstimatedAltitudeDown(boolean EGMCorrection){ + // Retrieve EGM Corrections if available + if ((Start_EGMAltitudeCorrection == NOT_AVAILABLE) || (End_EGMAltitudeCorrection == NOT_AVAILABLE)) { + EGM96 egm96 = EGM96.getInstance(); + if (egm96 != null) { + if (egm96.isEGMGridLoaded()) { + if (Start_Latitude != NOT_AVAILABLE) Start_EGMAltitudeCorrection = egm96.getEGMCorrection(Start_Latitude, Start_Longitude); + if (End_Latitude != NOT_AVAILABLE) End_EGMAltitudeCorrection = egm96.getEGMCorrection(End_Latitude, End_Longitude); + } + } + } + double egmcorr = 0; + if ((EGMCorrection) && ((Start_EGMAltitudeCorrection != NOT_AVAILABLE) && (End_EGMAltitudeCorrection != NOT_AVAILABLE))) { + egmcorr = Start_EGMAltitudeCorrection - End_EGMAltitudeCorrection; + } + double dresultUp = Altitude_InProgress > 0 ? Altitude_Up + Altitude_InProgress : Altitude_Up; + dresultUp -= egmcorr < 0 ? egmcorr : 0; + double dresultDown = Altitude_InProgress < 0 ? Altitude_Down - Altitude_InProgress : Altitude_Down; + dresultDown -= egmcorr > 0 ? egmcorr : 0; + + if (dresultUp < 0) { + dresultDown -= dresultUp; + dresultUp = 0; + } + if (dresultDown < 0) { + //dresultUp -= dresultDown; + dresultDown = 0; + } + return dresultDown; + } + + public double getEstimatedAltitudeGap(boolean EGMCorrection){ + return getEstimatedAltitudeUp(EGMCorrection) - getEstimatedAltitudeDown(EGMCorrection); + } + + + public float getBearing() { + if (End_Latitude != NOT_AVAILABLE) { + if (((Start_Latitude == End_Latitude) && (Start_Longitude == End_Longitude)) || (Distance == 0)) + return NOT_AVAILABLE; + Location EndLoc = new Location("TEMP"); + EndLoc.setLatitude(End_Latitude); + EndLoc.setLongitude(End_Longitude); + Location StartLoc = new Location("TEMP"); + StartLoc.setLatitude(Start_Latitude); + StartLoc.setLongitude(Start_Longitude); + float BTo = StartLoc.bearingTo(EndLoc); + if (BTo < 0) BTo += 360f; + return BTo; + } + return NOT_AVAILABLE; + } + + + // Returns the time, based on preferences (Total or Moving) + public long getPrefTime() { + GPSApplication gpsApplication = GPSApplication.getInstance(); + int pTime = gpsApplication.getPrefShowTrackStatsType(); + switch (pTime) { + case 0: // Total based + return Duration; + case 1: // Moving based + return Duration_Moving; + default: + return Duration; + } + } + + + // Returns the average speed, based on preferences (Total or Moving) + public float getPrefSpeedAverage() { + if (NumberOfLocations == 0) return NOT_AVAILABLE; + GPSApplication gpsApplication = GPSApplication.getInstance(); + int pTime = gpsApplication.getPrefShowTrackStatsType(); + switch (pTime) { + case 0: // Total based + return SpeedAverage; + case 1: // Moving based + return SpeedAverageMoving; + default: + return SpeedAverage; + } + } + + + public int getTrackType() { + + //if (Type != TRACK_TYPE_ND) return Type; + + if ((Distance == NOT_AVAILABLE) || (SpeedMax == NOT_AVAILABLE)) { + if (NumberOfPlacemarks == 0) return TRACK_TYPE_ND; + else return TRACK_TYPE_STEADY; + } + if ((Distance < 15.0f) || (SpeedMax == 0.0f) || (SpeedAverageMoving == NOT_AVAILABLE)) return TRACK_TYPE_STEADY; + if (SpeedMax < (7.0f / 3.6f)) { + if ((Altitude_Up != NOT_AVAILABLE) && (Altitude_Down != NOT_AVAILABLE)) + if ((Altitude_Down + Altitude_Up > (0.1f * Distance)) && (Distance > 500.0f)) return TRACK_TYPE_MOUNTAIN; + else return TRACK_TYPE_WALK; + } + if (SpeedMax < (15.0f / 3.6f)) { + if (SpeedAverageMoving > 8.0f / 3.6f) return TRACK_TYPE_RUN; + else { + if ((Altitude_Up != NOT_AVAILABLE) && (Altitude_Down != NOT_AVAILABLE)) + if ((Altitude_Down + Altitude_Up > (0.1f * Distance)) && (Distance > 500.0f)) return TRACK_TYPE_MOUNTAIN; + else return TRACK_TYPE_WALK; + } + } + if (SpeedMax < (50.0f / 3.6f)) { + if ((SpeedAverageMoving + SpeedMax) / 2 > 35.0f / 3.6f) return TRACK_TYPE_CAR; + if ((SpeedAverageMoving + SpeedMax) / 2 > 20.0f / 3.6) return TRACK_TYPE_BICYCLE; + else if ((SpeedAverageMoving + SpeedMax) / 2 > 12.0f / 3.6f) return TRACK_TYPE_RUN; + else { + if ((Altitude_Up != NOT_AVAILABLE) && (Altitude_Down != NOT_AVAILABLE)) + if ((Altitude_Down + Altitude_Up > (0.1f * Distance)) && (Distance > 500.0f)) + return TRACK_TYPE_MOUNTAIN; + else return TRACK_TYPE_WALK; + } + /* + if (SpeedAverageMoving > 20.0f / 3.6f) return TRACK_TYPE_CAR; + if (SpeedAverageMoving > 12.0f / 3.6) return TRACK_TYPE_BICYCLE; + else if (SpeedAverageMoving > 8.0f / 3.6f) return TRACK_TYPE_RUN; + else { + if ((Altitude_Up != NOT_AVAILABLE) && (Altitude_Down != NOT_AVAILABLE)) + if ((Altitude_Down + Altitude_Up > (0.1f * Distance)) && (Distance > 500.0f)) + return TRACK_TYPE_MOUNTAIN; + else return TRACK_TYPE_WALK; + }*/ + } + if ((Altitude_Up != NOT_AVAILABLE) && (Altitude_Down != NOT_AVAILABLE)) + if ((Altitude_Down + Altitude_Up > 5000.0) && (SpeedMax > 300.0f / 3.6f)) return TRACK_TYPE_FLIGHT; + + return TRACK_TYPE_CAR; + } +} diff --git a/app/src/main/java/armeasure/Armeasure_save.java b/app/src/main/java/armeasure/Armeasure_save.java new file mode 100644 index 0000000..c4e087c --- /dev/null +++ b/app/src/main/java/armeasure/Armeasure_save.java @@ -0,0 +1,330 @@ +package armeasure; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Build; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.Toast; + +import android.support.v7.app.AppCompatActivity; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AlertDialog.Builder; + + +import com.google.ar.core.Anchor; +import com.google.ar.core.HitResult; +import com.google.ar.core.Plane; +import com.google.ar.core.Pose; +import com.google.ar.sceneform.AnchorNode; +import com.google.ar.sceneform.math.Vector3; +import com.google.ar.sceneform.rendering.ModelRenderable; +import com.google.ar.sceneform.ux.ArFragment; +import com.google.ar.sceneform.ux.TransformableNode; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +import fr.geolabs.dev.mapmint4me.R; + +public class Armeasure_save extends AppCompatActivity { + + private static final String TAG = Armeasure_save.class.getSimpleName(); + private static final double MIN_OPENGL_VERSION = 3.0; + private float upDistance = 0f; + private ArFragment arFragment; + private ModelRenderable andyRenderable; + private AnchorNode myanchornode; + private DecimalFormat form_numbers = new DecimalFormat("#0.00 m"); + + private Anchor anchor1 = null, anchor2 = null; + + private HitResult myhit; + + private TextView text; + private SeekBar sk_height_control; + private Button btn_save, btn_width, btn_height; + private Button btn_share; + + List anchorNodes = new ArrayList<>(); + + private boolean measure_height = false; + private ArrayList arl_saved = new ArrayList(); + + private float fl_measurement = 0.0f; + + private String message; + + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (!checkIsSupportedDeviceOrFinish(this)) { + return; + } + try + { + this.getSupportActionBar().hide(); + } + catch (NullPointerException e){} + + setContentView(R.layout.armeasure_save); + + + arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment); + text = (TextView) findViewById(R.id.text); + + sk_height_control = (SeekBar) findViewById(R.id.sk_height_control); + btn_height = (Button) findViewById(R.id.btn_height); + btn_save = (Button) findViewById(R.id.btn_save); + btn_width = (Button) findViewById(R.id.btn_width); + btn_share = (Button) findViewById(R.id.btn_share); + + sk_height_control.setEnabled(false); + + btn_width.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + resetLayout(); + measure_height = false; + // text.setText("Click the extremes you want to measure"); + } + }); + + btn_height.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + resetLayout(); + measure_height = true; + + text.setText("Click the base of the object you want to measure"); + } + }); + + btn_save.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if(fl_measurement != 0.0f) + saveDialog(); + else + Toast.makeText(Armeasure_save.this, "Make a measurement before saving", Toast.LENGTH_SHORT).show(); + } + }); + + btn_share.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if(arl_saved.size() > 0){ + Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); + sharingIntent.setType("text/plain"); + String shareBody = ""; + for(String measurement : arl_saved) + shareBody += measurement+"\n"; + + System.out.println(shareBody); + shareBody = shareBody.trim(); + sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "AR Measurements"); + sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody); + startActivity(Intent.createChooser(sharingIntent, "Share via")); + } + else + Toast.makeText(Armeasure_save.this, "Save measurements before sharing", Toast.LENGTH_SHORT).show(); + } + }); + + + sk_height_control.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + upDistance = progress; + fl_measurement = progress/100f; + text.setText("Height: "+form_numbers.format(fl_measurement)); + myanchornode.setLocalScale(new Vector3(1f, progress/10f, 1f)); + //ascend(myanchornode, upDistance); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + + ModelRenderable.builder() + .setSource(this, R.raw.cubito) + .build() + .thenAccept(renderable -> andyRenderable = renderable) + .exceptionally( + throwable -> { + Toast toast = + Toast.makeText(this, "Unable to load andy renderable", Toast.LENGTH_LONG); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.show(); + return null; + }); + + arFragment.setOnTapArPlaneListener( + (HitResult hitResult, Plane plane, MotionEvent motionEvent) -> { + if (andyRenderable == null) { + return; + } + myhit = hitResult; + + // Create the Anchor. + Anchor anchor = hitResult.createAnchor(); + + AnchorNode anchorNode = new AnchorNode(anchor); + + + anchorNode.setParent(arFragment.getArSceneView().getScene()); + + if(!measure_height) { + if(anchor2 != null){ + emptyAnchors(); + } + if (anchor1 == null) { + anchor1 = anchor; + } else { + anchor2 = anchor; + fl_measurement = getMetersBetweenAnchors(anchor1, anchor2); + // text.setText("Width: " + + // form_numbers.format(fl_measurement)); + + } + } + else{ + emptyAnchors(); + anchor1 = anchor; + //text.setText("Move the slider till the cube reaches the upper base"); + sk_height_control.setEnabled(true); + } + + myanchornode = anchorNode; + anchorNodes.add(anchorNode); + + // Create the transformable andy and add it to the anchor. + TransformableNode andy = new TransformableNode(arFragment.getTransformationSystem()); + andy.setParent(anchorNode); + andy.setRenderable(andyRenderable); + andy.select(); + andy.getScaleController().setEnabled(false); + }); + } + + /** + * Function to raise an object perpendicular to the ArPlane a specific distance + * @param an anchor belonging to the object that should be raised + * @param up distance in centimeters the object should be raised vertically + */ + private void ascend(AnchorNode an, float up) { + Anchor anchor = myhit.getTrackable().createAnchor( + myhit.getHitPose().compose(Pose.makeTranslation(0, up / 100f, 0))); + + an.setAnchor(anchor); + } + + /** + * Function to return the distance in meters between two objects placed in ArPlane + * @param anchor1 first object's anchor + * @param anchor2 second object's anchor + * @return the distance between the two anchors in meters + */ + private float getMetersBetweenAnchors(Anchor anchor1, Anchor anchor2) { + float[] distance_vector = anchor1.getPose().inverse() + .compose(anchor2.getPose()).getTranslation(); + float totalDistanceSquared = 0; + for (int i = 0; i < 3; ++i) + totalDistanceSquared += distance_vector[i] * distance_vector[i]; + return (float) Math.sqrt(totalDistanceSquared); + } + + + /** + * Check whether the device supports the tools required to use the measurement tools + * @param activity + * @return boolean determining whether the device is supported or not + */ + private boolean checkIsSupportedDeviceOrFinish(final Activity activity) { + if (Build.VERSION.SDK_INT < VERSION_CODES.N) { + Log.e(TAG, "Sceneform requires Android N or later"); + Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show(); + activity.finish(); + return false; + } + String openGlVersionString = + ((ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE)) + .getDeviceConfigurationInfo() + .getGlEsVersion(); + if (Double.parseDouble(openGlVersionString) < MIN_OPENGL_VERSION) { + Log.e(TAG, "Sceneform requires OpenGL ES 3.0 later"); + Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG) + .show(); + activity.finish(); + return false; + } + return true; + } + + private void saveDialog() { + Builder mBuilder = new Builder(Armeasure_save.this); + View mView = getLayoutInflater().inflate(R.layout.dialog_save, null); + + EditText et_measure = (EditText) mView.findViewById(R.id.et_measure); + mBuilder.setTitle("Measurement title"); + + mBuilder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + if(et_measure.length() != 0){ + arl_saved.add(et_measure.getText()+": "+form_numbers.format(fl_measurement)); + System.out.println(arl_saved); + dialogInterface.dismiss(); + } + else + Toast.makeText(Armeasure_save.this, "Title can't be empty", Toast.LENGTH_SHORT).show(); + } + }); + + mBuilder.setView(mView); + AlertDialog dialog = mBuilder.create(); + + dialog.show(); + } + + /** + * Set layout to its initial state + */ + private void resetLayout(){ + sk_height_control.setProgress(10); + sk_height_control.setEnabled(false); + measure_height = false; + emptyAnchors(); + } + + private void emptyAnchors(){ + anchor1 = null; + anchor2 = null; + for (AnchorNode n : anchorNodes) { + arFragment.getArSceneView().getScene().removeChild(n); + n.getAnchor().detach(); + n.setParent(null); + n = null; + } + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/helloar/CameraPermissionHelper.java b/app/src/main/java/com/google/ar/core/examples/java/helloar/CameraPermissionHelper.java new file mode 100755 index 0000000..093550a --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/helloar/CameraPermissionHelper.java @@ -0,0 +1,52 @@ + +package com.google.ar.core.examples.java.helloar; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.provider.Settings; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; + +/** + * Helper to ask camera permission. + */ +public class CameraPermissionHelper { + private static final int CAMERA_PERMISSION_CODE = 0; + private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA; + + /** + * Check to see we have the necessary permissions for this app. + */ + public static boolean hasCameraPermission(Activity activity) { + return ContextCompat.checkSelfPermission(activity, CAMERA_PERMISSION) + == PackageManager.PERMISSION_GRANTED; + } + + /** + * Check to see we have the necessary permissions for this app, and ask for them if we don't. + */ + public static void requestCameraPermission(Activity activity) { + ActivityCompat.requestPermissions( + activity, new String[]{CAMERA_PERMISSION}, CAMERA_PERMISSION_CODE); + } + + /** + * Check to see if we need to show the rationale for this permission. + */ + public static boolean shouldShowRequestPermissionRationale(Activity activity) { + return ActivityCompat.shouldShowRequestPermissionRationale(activity, CAMERA_PERMISSION); + } + + /** + * Launch Application Setting to grant permission. + */ + public static void launchPermissionSettings(Activity activity) { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", activity.getPackageName(), null)); + activity.startActivity(intent); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/helloar/DisplayRotationHelper.java b/app/src/main/java/com/google/ar/core/examples/java/helloar/DisplayRotationHelper.java new file mode 100644 index 0000000..7befd34 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/helloar/DisplayRotationHelper.java @@ -0,0 +1,91 @@ + +package com.google.ar.core.examples.java.helloar; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManager.DisplayListener; +import android.view.Display; +import android.view.WindowManager; + +import com.google.ar.core.Session; + +/** + * Helper to track the display rotations. In particular, the 180 degree rotations are not notified + * by the onSurfaceChanged() callback, and thus they require listening to the android display + * events. + */ +public class DisplayRotationHelper implements DisplayListener { + private boolean viewportChanged; + private int viewportWidth; + private int viewportHeight; + private final Context context; + private final Display display; + + /** + * Constructs the DisplayRotationHelper but does not register the listener yet. + * + * @param context the Android {@link Context}. + */ + public DisplayRotationHelper(Context context) { + this.context = context; + display = context.getSystemService(WindowManager.class).getDefaultDisplay(); + } + + public void onResume() { + context.getSystemService(DisplayManager.class).registerDisplayListener(this, null); + } + + + public void onPause() { + context.getSystemService(DisplayManager.class).unregisterDisplayListener(this); + } + + /** + * Records a change in surface dimensions. This will be later used by {@link + * #updateSessionIfNeeded(Session)}. Should be called from {@link + * android.opengl.GLSurfaceView.Renderer + * #onSurfaceChanged(javax.microedition.khronos.opengles.GL10, int, int)}. + * + * @param width the updated width of the surface. + * @param height the updated height of the surface. + */ + public void onSurfaceChanged(int width, int height) { + viewportWidth = width; + viewportHeight = height; + viewportChanged = true; + } + + /** + * Updates the session display geometry if a change was posted either by {@link + * #onSurfaceChanged(int, int)} call or by {@link #onDisplayChanged(int)} system callback. This + * function should be called explicitly before each call to {@link Session#update()}. This + * function will also clear the 'pending update' (viewportChanged) flag. + * + * @param session the {@link Session} object to update if display geometry changed. + */ + public void updateSessionIfNeeded(Session session) { + if (viewportChanged) { + int displayRotation = display.getRotation(); + session.setDisplayGeometry(displayRotation, viewportWidth, viewportHeight); + viewportChanged = false; + } + } + + /** + * Returns the current rotation state of android display. Same as {@link Display#getRotation()}. + */ + public int getRotation() { + return display.getRotation(); + } + + @Override + public void onDisplayAdded(int displayId) {} + + @Override + public void onDisplayRemoved(int displayId) {} + + @Override + public void onDisplayChanged(int displayId) { + viewportChanged = true; + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/BackgroundRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/BackgroundRenderer.java new file mode 100755 index 0000000..ff67b7f --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/BackgroundRenderer.java @@ -0,0 +1,178 @@ + +package com.google.ar.core.examples.java.helloar.rendering; + +import android.content.Context; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; + +import com.google.ar.core.Frame; +import com.google.ar.core.Session; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import fr.geolabs.dev.mapmint4me.R; + +/** + * This class renders the AR background from camera feed. It creates and hosts the texture given to + * ARCore to be filled with the camera image. + */ +public class BackgroundRenderer { + private static final String TAG = BackgroundRenderer.class.getSimpleName(); + + private static final int COORDS_PER_VERTEX = 3; + private static final int TEXCOORDS_PER_VERTEX = 2; + private static final int FLOAT_SIZE = 4; + + private FloatBuffer quadVertices; + private FloatBuffer quadTexCoord; + private FloatBuffer quadTexCoordTransformed; + + private int quadProgram; + + private int quadPositionParam; + private int quadTexCoordParam; + private int textureId = -1; + + public BackgroundRenderer() { + } + + public int getTextureId() { + return textureId; + } + + /** + * Allocates and initializes OpenGL resources needed by the background renderer. Must be called on + * the OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, + * EGLConfig)}. + * + * @param context Needed to access shader source. + */ + public void createOnGlThread(Context context) { + // Generate the background texture. + int[] textures = new int[1]; + GLES20.glGenTextures(1, textures, 0); + textureId = textures[0]; + int textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; + GLES20.glBindTexture(textureTarget, textureId); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); + + int numVertices = 4; + if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) { + throw new RuntimeException("Unexpected number of vertices in BackgroundRenderer."); + } + + ByteBuffer bbVertices = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE); + bbVertices.order(ByteOrder.nativeOrder()); + quadVertices = bbVertices.asFloatBuffer(); + quadVertices.put(QUAD_COORDS); + quadVertices.position(0); + + ByteBuffer bbTexCoords = + ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE); + bbTexCoords.order(ByteOrder.nativeOrder()); + quadTexCoord = bbTexCoords.asFloatBuffer(); + quadTexCoord.put(QUAD_TEXCOORDS); + quadTexCoord.position(0); + + ByteBuffer bbTexCoordsTransformed = + ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE); + bbTexCoordsTransformed.order(ByteOrder.nativeOrder()); + quadTexCoordTransformed = bbTexCoordsTransformed.asFloatBuffer(); + + int vertexShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, R.raw.screenquad_vertex); + int fragmentShader = + ShaderUtil.loadGLShader( + TAG, context, GLES20.GL_FRAGMENT_SHADER, R.raw.screenquad_fragment_oes); + + quadProgram = GLES20.glCreateProgram(); + GLES20.glAttachShader(quadProgram, vertexShader); + GLES20.glAttachShader(quadProgram, fragmentShader); + GLES20.glLinkProgram(quadProgram); + GLES20.glUseProgram(quadProgram); + + ShaderUtil.checkGLError(TAG, "Program creation"); + + quadPositionParam = GLES20.glGetAttribLocation(quadProgram, "a_Position"); + quadTexCoordParam = GLES20.glGetAttribLocation(quadProgram, "a_TexCoord"); + + ShaderUtil.checkGLError(TAG, "Program parameters"); + } + + /** + * Draws the AR background image. The image will be drawn such that virtual content rendered with + * the matrices provided by {@link com.google.ar.core.Camera#getViewMatrix(float[], int)} and + * {@link com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)} will + * accurately follow static physical objects. This must be called before drawing virtual + * content. + * + * @param frame The last {@code Frame} returned by {@link Session#update()}. + */ + public void draw(Frame frame) { + // If display rotation changed (also includes view size change), we need to re-query the uv + // coordinates for the screen rect, as they may have changed as well. + if (frame.hasDisplayGeometryChanged()) { + frame.transformDisplayUvCoords(quadTexCoord, quadTexCoordTransformed); + } + + // No need to test or write depth, the screen quad has arbitrary depth, and is expected + // to be drawn first. + GLES20.glDisable(GLES20.GL_DEPTH_TEST); + GLES20.glDepthMask(false); + + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); + + GLES20.glUseProgram(quadProgram); + + // Set the vertex positions. + GLES20.glVertexAttribPointer( + quadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadVertices); + + // Set the texture coordinates. + GLES20.glVertexAttribPointer( + quadTexCoordParam, + TEXCOORDS_PER_VERTEX, + GLES20.GL_FLOAT, + false, + 0, + quadTexCoordTransformed); + + // Enable vertex arrays + GLES20.glEnableVertexAttribArray(quadPositionParam); + GLES20.glEnableVertexAttribArray(quadTexCoordParam); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + // Disable vertex arrays + GLES20.glDisableVertexAttribArray(quadPositionParam); + GLES20.glDisableVertexAttribArray(quadTexCoordParam); + + // Restore the depth state for further drawing. + GLES20.glDepthMask(true); + GLES20.glEnable(GLES20.GL_DEPTH_TEST); + + ShaderUtil.checkGLError(TAG, "Draw"); + } + + private static final float[] QUAD_COORDS = + new float[]{ + -1.0f, -1.0f, 0.0f, -1.0f, +1.0f, 0.0f, +1.0f, -1.0f, 0.0f, +1.0f, +1.0f, 0.0f, + }; + + private static final float[] QUAD_TEXCOORDS = + new float[]{ + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 1.0f, + 1.0f, 0.0f, + }; +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/ObjectRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/ObjectRenderer.java new file mode 100755 index 0000000..1693f65 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/ObjectRenderer.java @@ -0,0 +1,373 @@ + +package com.google.ar.core.examples.java.helloar.rendering; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLES20; +import android.opengl.GLUtils; +import android.opengl.Matrix; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import de.javagl.obj.Obj; +import de.javagl.obj.ObjData; +import de.javagl.obj.ObjReader; +import de.javagl.obj.ObjUtils; +import fr.geolabs.dev.mapmint4me.R; + +/** + * Renders an object loaded from an OBJ file in OpenGL. + */ +public class ObjectRenderer { + private static final String TAG = ObjectRenderer.class.getSimpleName(); + + /** + * Blend mode. + * + * @see #setBlendMode(BlendMode) + */ + public enum BlendMode { + /** + * Multiplies the destination color by the source alpha. + */ + Shadow, + /** + * Normal alpha blending. + */ + Grid + } + + private static final int COORDS_PER_VERTEX = 3; + + // Note: the last component must be zero to avoid applying the translational part of the matrix. + private static final float[] LIGHT_DIRECTION = new float[]{0.250f, 0.866f, 0.433f, 0.0f}; + private final float[] viewLightDirection = new float[4]; + + // Object vertex buffer variables. + private int vertexBufferId; + private int verticesBaseAddress; + private int texCoordsBaseAddress; + private int normalsBaseAddress; + private int indexBufferId; + private int indexCount; + + private int program; + private final int[] textures = new int[1]; + + // Shader location: model view projection matrix. + private int modelViewUniform; + private int modelViewProjectionUniform; + + // Shader location: object attributes. + private int positionAttribute; + private int normalAttribute; + private int texCoordAttribute; + + // Shader location: texture sampler. + private int textureUniform; + + // Shader location: environment properties. + private int lightingParametersUniform; + + // Shader location: material properties. + private int materialParametersUniform; + + private BlendMode blendMode = null; + + // Temporary matrices allocated here to reduce number of allocations for each frame. + private final float[] modelMatrix = new float[16]; + private final float[] modelViewMatrix = new float[16]; + private final float[] modelViewProjectionMatrix = new float[16]; + + // Set some default material properties to use for lighting. + private float ambient = 0.3f; + private float diffuse = 1.0f; + private float specular = 1.0f; + private float specularPower = 6.0f; + + public ObjectRenderer() { + } + + /** + * Creates and initializes OpenGL resources needed for rendering the model. + * + * @param context Context for loading the shader and below-named model and texture assets. + * @param objAssetName Name of the OBJ file containing the model geometry. + * @param diffuseTextureAssetName Name of the PNG file containing the diffuse texture map. + */ + public void createOnGlThread(Context context, String objAssetName, String diffuseTextureAssetName) + throws IOException { + // Read the texture. + Bitmap textureBitmap = + BitmapFactory.decodeStream(context.getAssets().open(diffuseTextureAssetName)); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glGenTextures(textures.length, textures, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + + GLES20.glTexParameteri( + GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, textureBitmap, 0); + GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + + textureBitmap.recycle(); + + ShaderUtil.checkGLError(TAG, "Texture loading"); + + // Read the obj file. + InputStream objInputStream = context.getAssets().open(objAssetName); + Obj obj = ObjReader.read(objInputStream); + + // Prepare the Obj so that its structure is suitable for + // rendering with OpenGL: + // 1. Triangulate it + // 2. Make sure that texture coordinates are not ambiguous + // 3. Make sure that normals are not ambiguous + // 4. Convert it to single-indexed data + obj = ObjUtils.convertToRenderable(obj); + + // OpenGL does not use Java arrays. ByteBuffers are used instead to provide data in a format + // that OpenGL understands. + + // Obtain the data from the OBJ, as direct buffers: + IntBuffer wideIndices = ObjData.getFaceVertexIndices(obj, 3); + FloatBuffer vertices = ObjData.getVertices(obj); + FloatBuffer texCoords = ObjData.getTexCoords(obj, 2); + FloatBuffer normals = ObjData.getNormals(obj); + + // Convert int indices to shorts for GL ES 2.0 compatibility + ShortBuffer indices = + ByteBuffer.allocateDirect(2 * wideIndices.limit()) + .order(ByteOrder.nativeOrder()) + .asShortBuffer(); + while (wideIndices.hasRemaining()) { + indices.put((short) wideIndices.get()); + } + indices.rewind(); + + int[] buffers = new int[2]; + GLES20.glGenBuffers(2, buffers, 0); + vertexBufferId = buffers[0]; + indexBufferId = buffers[1]; + + // Load vertex buffer + verticesBaseAddress = 0; + texCoordsBaseAddress = verticesBaseAddress + 4 * vertices.limit(); + normalsBaseAddress = texCoordsBaseAddress + 4 * texCoords.limit(); + final int totalBytes = normalsBaseAddress + 4 * normals.limit(); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId); + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, totalBytes, null, GLES20.GL_STATIC_DRAW); + GLES20.glBufferSubData( + GLES20.GL_ARRAY_BUFFER, verticesBaseAddress, 4 * vertices.limit(), vertices); + GLES20.glBufferSubData( + GLES20.GL_ARRAY_BUFFER, texCoordsBaseAddress, 4 * texCoords.limit(), texCoords); + GLES20.glBufferSubData( + GLES20.GL_ARRAY_BUFFER, normalsBaseAddress, 4 * normals.limit(), normals); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + // Load index buffer + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBufferId); + indexCount = indices.limit(); + GLES20.glBufferData( + GLES20.GL_ELEMENT_ARRAY_BUFFER, 2 * indexCount, indices, GLES20.GL_STATIC_DRAW); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); + + ShaderUtil.checkGLError(TAG, "OBJ buffer load"); + + final int vertexShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, R.raw.object_vertex); + final int fragmentShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, R.raw.object_fragment); + + program = GLES20.glCreateProgram(); + GLES20.glAttachShader(program, vertexShader); + GLES20.glAttachShader(program, fragmentShader); + GLES20.glLinkProgram(program); + GLES20.glUseProgram(program); + + ShaderUtil.checkGLError(TAG, "Program creation"); + + modelViewUniform = GLES20.glGetUniformLocation(program, "u_ModelView"); + modelViewProjectionUniform = GLES20.glGetUniformLocation(program, "u_ModelViewProjection"); + + positionAttribute = GLES20.glGetAttribLocation(program, "a_Position"); + normalAttribute = GLES20.glGetAttribLocation(program, "a_Normal"); + texCoordAttribute = GLES20.glGetAttribLocation(program, "a_TexCoord"); + + textureUniform = GLES20.glGetUniformLocation(program, "u_Texture"); + + lightingParametersUniform = GLES20.glGetUniformLocation(program, "u_LightingParameters"); + materialParametersUniform = GLES20.glGetUniformLocation(program, "u_MaterialParameters"); + + ShaderUtil.checkGLError(TAG, "Program parameters"); + + Matrix.setIdentityM(modelMatrix, 0); + } + + /** + * Selects the blending mode for rendering. + * + * @param blendMode The blending mode. Null indicates no blending (opaque rendering). + */ + public void setBlendMode(BlendMode blendMode) { + this.blendMode = blendMode; + } + + /** + * Updates the object model matrix and applies scaling. + * + * @param modelMatrix A 4x4 model-to-world transformation matrix, stored in column-major order. + * @param scaleFactor A separate scaling factor to apply before the {@code modelMatrix}. + * @see Matrix + */ + public void updateModelMatrix(float[] modelMatrix, float scaleFactor) { + float[] scaleMatrix = new float[16]; + Matrix.setIdentityM(scaleMatrix, 0); + scaleMatrix[0] = scaleFactor; + scaleMatrix[5] = scaleFactor; + scaleMatrix[10] = scaleFactor; + Matrix.multiplyMM(this.modelMatrix, 0, modelMatrix, 0, scaleMatrix, 0); + } + + /** + * Sets the surface characteristics of the rendered model. + * + * @param ambient Intensity of non-directional surface illumination. + * @param diffuse Diffuse (matte) surface reflectivity. + * @param specular Specular (shiny) surface reflectivity. + * @param specularPower Surface shininess. Larger values result in a smaller, sharper specular + * highlight. + */ + public void setMaterialProperties( + float ambient, float diffuse, float specular, float specularPower) { + this.ambient = ambient; + this.diffuse = diffuse; + this.specular = specular; + this.specularPower = specularPower; + } + + /** + * Draws the model. + * + * @param cameraView A 4x4 view matrix, in column-major order. + * @param cameraPerspective A 4x4 projection matrix, in column-major order. + * @param lightIntensity Illumination intensity. Combined with diffuse and specular material + * properties. + * @see #setBlendMode(BlendMode) + * @see #updateModelMatrix(float[], float) + * @see #setMaterialProperties(float, float, float, float) + * @see Matrix + */ + public void draw(float[] cameraView, float[] cameraPerspective, float lightIntensity) { + + ShaderUtil.checkGLError(TAG, "Before draw"); + + // Build the ModelView and ModelViewProjection matrices + // for calculating object position and light. + Matrix.multiplyMM(modelViewMatrix, 0, cameraView, 0, modelMatrix, 0); + Matrix.multiplyMM(modelViewProjectionMatrix, 0, cameraPerspective, 0, modelViewMatrix, 0); + + GLES20.glUseProgram(program); + + // Set the lighting environment properties. + Matrix.multiplyMV(viewLightDirection, 0, modelViewMatrix, 0, LIGHT_DIRECTION, 0); + normalizeVec3(viewLightDirection); + GLES20.glUniform4f( + lightingParametersUniform, + viewLightDirection[0], + viewLightDirection[1], + viewLightDirection[2], + lightIntensity); + + // Set the object material properties. + GLES20.glUniform4f(materialParametersUniform, ambient, diffuse, specular, specularPower); + + // Attach the object texture. + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + GLES20.glUniform1i(textureUniform, 0); + + // Set the vertex attributes. + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId); + + GLES20.glVertexAttribPointer( + positionAttribute, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, verticesBaseAddress); + GLES20.glVertexAttribPointer(normalAttribute, 3, GLES20.GL_FLOAT, false, 0, normalsBaseAddress); + GLES20.glVertexAttribPointer( + texCoordAttribute, 2, GLES20.GL_FLOAT, false, 0, texCoordsBaseAddress); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + // Set the ModelViewProjection matrix in the shader. + GLES20.glUniformMatrix4fv(modelViewUniform, 1, false, modelViewMatrix, 0); + GLES20.glUniformMatrix4fv(modelViewProjectionUniform, 1, false, modelViewProjectionMatrix, 0); + + // Enable vertex arrays + GLES20.glEnableVertexAttribArray(positionAttribute); + GLES20.glEnableVertexAttribArray(normalAttribute); + GLES20.glEnableVertexAttribArray(texCoordAttribute); + + if (blendMode != null) { + GLES20.glDepthMask(false); + GLES20.glEnable(GLES20.GL_BLEND); + switch (blendMode) { + case Shadow: + // Multiplicative blending function for Shadow. + GLES20.glBlendFunc(GLES20.GL_ZERO, GLES20.GL_ONE_MINUS_SRC_ALPHA); + break; + case Grid: + // Grid, additive blending function. + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); + break; + } + } + + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBufferId); + GLES20.glDrawElements(GLES20.GL_TRIANGLES, indexCount, GLES20.GL_UNSIGNED_SHORT, 0); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); + + if (blendMode != null) { + GLES20.glDisable(GLES20.GL_BLEND); + GLES20.glDepthMask(true); + } + + // Disable vertex arrays + GLES20.glDisableVertexAttribArray(positionAttribute); + GLES20.glDisableVertexAttribArray(normalAttribute); + GLES20.glDisableVertexAttribArray(texCoordAttribute); + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + + ShaderUtil.checkGLError(TAG, "After draw"); + } + + private static void normalizeVec3(float[] v) { + float reciprocalLength = 1.0f / (float) Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + v[0] *= reciprocalLength; + v[1] *= reciprocalLength; + v[2] *= reciprocalLength; + } + + public float[] getModelViewProjectionMatrix(){ + return modelViewProjectionMatrix; + } + + public float[] getModelViewMatrix(){ + return modelViewMatrix; + } + + public float[] getModelMatrix(){ + return modelMatrix; + } + +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/PlaneRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/PlaneRenderer.java new file mode 100644 index 0000000..6e825c7 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/PlaneRenderer.java @@ -0,0 +1,429 @@ + +package com.google.ar.core.examples.java.helloar.rendering; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.opengl.GLUtils; +import android.opengl.Matrix; + +import com.google.ar.core.Camera; +import com.google.ar.core.Plane; +import com.google.ar.core.Pose; +import com.google.ar.core.TrackingState; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import fr.geolabs.dev.mapmint4me.R; + +/** + * Renders the detected AR planes. + */ +public class PlaneRenderer { + private static final String TAG = PlaneRenderer.class.getSimpleName(); + + private static final int BYTES_PER_FLOAT = Float.SIZE / 8; + private static final int BYTES_PER_SHORT = Short.SIZE / 8; + private static final int COORDS_PER_VERTEX = 3; // x, z, alpha + + private static final int VERTS_PER_BOUNDARY_VERT = 2; + private static final int INDICES_PER_BOUNDARY_VERT = 3; + private static final int INITIAL_BUFFER_BOUNDARY_VERTS = 64; + + private static final int INITIAL_VERTEX_BUFFER_SIZE_BYTES = + BYTES_PER_FLOAT * COORDS_PER_VERTEX * VERTS_PER_BOUNDARY_VERT * INITIAL_BUFFER_BOUNDARY_VERTS; + + private static final int INITIAL_INDEX_BUFFER_SIZE_BYTES = + BYTES_PER_SHORT + * INDICES_PER_BOUNDARY_VERT + * INDICES_PER_BOUNDARY_VERT + * INITIAL_BUFFER_BOUNDARY_VERTS; + + private static final float FADE_RADIUS_M = 0.25f; + private static final float DOTS_PER_METER = 10.0f; + private static final float EQUILATERAL_TRIANGLE_SCALE = (float) (1 / Math.sqrt(3)); + + // Using the "signed distance field" approach to render sharp lines and circles. + // {dotThreshold, lineThreshold, lineFadeSpeed, occlusionScale} + // dotThreshold/lineThreshold: red/green intensity above which dots/lines are present + // lineFadeShrink: lines will fade in between alpha = 1-(1/lineFadeShrink) and 1.0 + // occlusionShrink: occluded planes will fade out between alpha = 0 and 1/occlusionShrink + private static final float[] GRID_CONTROL = {0.2f, 0.4f, 2.0f, 1.5f}; + + private int planeProgram; + private final int[] textures = new int[1]; + + private int planeXZPositionAlphaAttribute; + + private int planeModelUniform; + private int planeModelViewProjectionUniform; + private int textureUniform; + private int lineColorUniform; + private int dotColorUniform; + private int gridControlUniform; + private int planeUvMatrixUniform; + + private FloatBuffer vertexBuffer = + ByteBuffer.allocateDirect(INITIAL_VERTEX_BUFFER_SIZE_BYTES) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + private ShortBuffer indexBuffer = + ByteBuffer.allocateDirect(INITIAL_INDEX_BUFFER_SIZE_BYTES) + .order(ByteOrder.nativeOrder()) + .asShortBuffer(); + + // Temporary lists/matrices allocated here to reduce number of allocations for each frame. + private final float[] modelMatrix = new float[16]; + private final float[] modelViewMatrix = new float[16]; + private final float[] modelViewProjectionMatrix = new float[16]; + private final float[] planeColor = new float[4]; + private final float[] planeAngleUvMatrix = + new float[4]; // 2x2 rotation matrix applied to uv coords. + + private final Map planeIndexMap = new HashMap<>(); + + public PlaneRenderer() { + } + + /** + * Allocates and initializes OpenGL resources needed by the plane renderer. Must be called on the + * OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)}. + * + * @param context Needed to access shader source and texture PNG. + * @param gridDistanceTextureName Name of the PNG file containing the grid texture. + */ + public void createOnGlThread(Context context, String gridDistanceTextureName) throws IOException { + int vertexShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, R.raw.plane_vertex); + int passthroughShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_FRAGMENT_SHADER, R.raw.plane_fragment); + + planeProgram = GLES20.glCreateProgram(); + GLES20.glAttachShader(planeProgram, vertexShader); + GLES20.glAttachShader(planeProgram, passthroughShader); + GLES20.glLinkProgram(planeProgram); + GLES20.glUseProgram(planeProgram); + + ShaderUtil.checkGLError(TAG, "Program creation"); + + // Read the texture. + Bitmap textureBitmap = + BitmapFactory.decodeStream(context.getAssets().open(gridDistanceTextureName)); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glGenTextures(textures.length, textures, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + + GLES20.glTexParameteri( + GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, textureBitmap, 0); + GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + + ShaderUtil.checkGLError(TAG, "Texture loading"); + + planeXZPositionAlphaAttribute = GLES20.glGetAttribLocation(planeProgram, "a_XZPositionAlpha"); + + planeModelUniform = GLES20.glGetUniformLocation(planeProgram, "u_Model"); + planeModelViewProjectionUniform = + GLES20.glGetUniformLocation(planeProgram, "u_ModelViewProjection"); + textureUniform = GLES20.glGetUniformLocation(planeProgram, "u_Texture"); + lineColorUniform = GLES20.glGetUniformLocation(planeProgram, "u_lineColor"); + dotColorUniform = GLES20.glGetUniformLocation(planeProgram, "u_dotColor"); + gridControlUniform = GLES20.glGetUniformLocation(planeProgram, "u_gridControl"); + planeUvMatrixUniform = GLES20.glGetUniformLocation(planeProgram, "u_PlaneUvMatrix"); + + ShaderUtil.checkGLError(TAG, "Program parameters"); + } + + /** + * Updates the plane model transform matrix and extents. + */ + private void updatePlaneParameters( + float[] planeMatrix, float extentX, float extentZ, FloatBuffer boundary) { + System.arraycopy(planeMatrix, 0, modelMatrix, 0, 16); + if (boundary == null) { + vertexBuffer.limit(0); + indexBuffer.limit(0); + return; + } + + // Generate a new set of vertices and a corresponding triangle strip index set so that + // the plane boundary polygon has a fading edge. This is done by making a copy of the + // boundary polygon vertices and scaling it down around center to push it inwards. Then + // the index buffer is setup accordingly. + boundary.rewind(); + int boundaryVertices = boundary.limit() / 2; + int numVertices; + int numIndices; + + numVertices = boundaryVertices * VERTS_PER_BOUNDARY_VERT; + // drawn as GL_TRIANGLE_STRIP with 3n-2 triangles (n-2 for fill, 2n for perimeter). + numIndices = boundaryVertices * INDICES_PER_BOUNDARY_VERT; + + if (vertexBuffer.capacity() < numVertices * COORDS_PER_VERTEX) { + int size = vertexBuffer.capacity(); + while (size < numVertices * COORDS_PER_VERTEX) { + size *= 2; + } + vertexBuffer = + ByteBuffer.allocateDirect(BYTES_PER_FLOAT * size) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + } + vertexBuffer.rewind(); + vertexBuffer.limit(numVertices * COORDS_PER_VERTEX); + + if (indexBuffer.capacity() < numIndices) { + int size = indexBuffer.capacity(); + while (size < numIndices) { + size *= 2; + } + indexBuffer = + ByteBuffer.allocateDirect(BYTES_PER_SHORT * size) + .order(ByteOrder.nativeOrder()) + .asShortBuffer(); + } + indexBuffer.rewind(); + indexBuffer.limit(numIndices); + + // Note: when either dimension of the bounding box is smaller than 2*FADE_RADIUS_M we + // generate a bunch of 0-area triangles. These don't get rendered though so it works + // out ok. + float xScale = Math.max((extentX - 2 * FADE_RADIUS_M) / extentX, 0.0f); + float zScale = Math.max((extentZ - 2 * FADE_RADIUS_M) / extentZ, 0.0f); + + while (boundary.hasRemaining()) { + float x = boundary.get(); + float z = boundary.get(); + vertexBuffer.put(x); + vertexBuffer.put(z); + vertexBuffer.put(0.0f); + vertexBuffer.put(x * xScale); + vertexBuffer.put(z * zScale); + vertexBuffer.put(1.0f); + } + + // step 1, perimeter + indexBuffer.put((short) ((boundaryVertices - 1) * 2)); + for (int i = 0; i < boundaryVertices; ++i) { + indexBuffer.put((short) (i * 2)); + indexBuffer.put((short) (i * 2 + 1)); + } + indexBuffer.put((short) 1); + // This leaves us on the interior edge of the perimeter between the inset vertices + // for boundary verts n-1 and 0. + + // step 2, interior: + for (int i = 1; i < boundaryVertices / 2; ++i) { + indexBuffer.put((short) ((boundaryVertices - 1 - i) * 2 + 1)); + indexBuffer.put((short) (i * 2 + 1)); + } + if (boundaryVertices % 2 != 0) { + indexBuffer.put((short) ((boundaryVertices / 2) * 2 + 1)); + } + } + + private void draw(float[] cameraView, float[] cameraPerspective) { + // Build the ModelView and ModelViewProjection matrices + // for calculating cube position and light. + Matrix.multiplyMM(modelViewMatrix, 0, cameraView, 0, modelMatrix, 0); + Matrix.multiplyMM(modelViewProjectionMatrix, 0, cameraPerspective, 0, modelViewMatrix, 0); + + // Set the position of the plane + vertexBuffer.rewind(); + GLES20.glVertexAttribPointer( + planeXZPositionAlphaAttribute, + COORDS_PER_VERTEX, + GLES20.GL_FLOAT, + false, + BYTES_PER_FLOAT * COORDS_PER_VERTEX, + vertexBuffer); + + // Set the Model and ModelViewProjection matrices in the shader. + GLES20.glUniformMatrix4fv(planeModelUniform, 1, false, modelMatrix, 0); + GLES20.glUniformMatrix4fv( + planeModelViewProjectionUniform, 1, false, modelViewProjectionMatrix, 0); + + indexBuffer.rewind(); + GLES20.glDrawElements( + GLES20.GL_TRIANGLE_STRIP, indexBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, indexBuffer); + ShaderUtil.checkGLError(TAG, "Drawing plane"); + } + + static class SortablePlane { + final float distance; + final Plane plane; + + SortablePlane(float distance, Plane plane) { + this.distance = distance; + this.plane = plane; + } + } + + /** + * Draws the collection of tracked planes, with closer planes hiding more distant ones. + * + * @param allPlanes The collection of planes to draw. + * @param cameraPose The pose of the camera, as returned by {@link Camera#getPose()} + * @param cameraPerspective The projection matrix, as returned by {@link + * Camera#getProjectionMatrix(float[], int, float, float)} + */ + public void drawPlanes(Collection allPlanes, Pose cameraPose, float[] cameraPerspective) { + // Planes must be sorted by distance from camera so that we draw closer planes first, and + // they occlude the farther planes. + List sortedPlanes = new ArrayList<>(); + float[] normal = new float[3]; + float cameraX = cameraPose.tx(); + float cameraY = cameraPose.ty(); + float cameraZ = cameraPose.tz(); + for (Plane plane : allPlanes) { + if (plane.getTrackingState() != TrackingState.TRACKING || plane.getSubsumedBy() != null) { + continue; + } + + Pose center = plane.getCenterPose(); + // Get transformed Y axis of plane's coordinate system. + center.getTransformedAxis(1, 1.0f, normal, 0); + // Compute dot product of plane's normal with vector from camera to plane center. + float distance = + (cameraX - center.tx()) * normal[0] + + (cameraY - center.ty()) * normal[1] + + (cameraZ - center.tz()) * normal[2]; + if (distance < 0) { // Plane is back-facing. + continue; + } + sortedPlanes.add(new SortablePlane(distance, plane)); + } + Collections.sort( + sortedPlanes, + new Comparator() { + @Override + public int compare(SortablePlane a, SortablePlane b) { + return Float.compare(a.distance, b.distance); + } + }); + + float[] cameraView = new float[16]; + cameraPose.inverse().toMatrix(cameraView, 0); + + // Planes are drawn with additive blending, masked by the alpha channel for occlusion. + + // Start by clearing the alpha channel of the color buffer to 1.0. + GLES20.glClearColor(1, 1, 1, 1); + GLES20.glColorMask(false, false, false, true); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + GLES20.glColorMask(true, true, true, true); + + // Disable depth write. + GLES20.glDepthMask(false); + + // Additive blending, masked by alpha channel, clearing alpha channel. + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFuncSeparate( + GLES20.GL_DST_ALPHA, GLES20.GL_ONE, // RGB (src, dest) + GLES20.GL_ZERO, GLES20.GL_ONE_MINUS_SRC_ALPHA); // ALPHA (src, dest) + + // Set up the shader. + GLES20.glUseProgram(planeProgram); + + // Attach the texture. + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + GLES20.glUniform1i(textureUniform, 0); + + // Shared fragment uniforms. + GLES20.glUniform4fv(gridControlUniform, 1, GRID_CONTROL, 0); + + // Enable vertex arrays + GLES20.glEnableVertexAttribArray(planeXZPositionAlphaAttribute); + + ShaderUtil.checkGLError(TAG, "Setting up to draw planes"); + + for (SortablePlane sortedPlane : sortedPlanes) { + Plane plane = sortedPlane.plane; + float[] planeMatrix = new float[16]; + plane.getCenterPose().toMatrix(planeMatrix, 0); + + updatePlaneParameters( + planeMatrix, plane.getExtentX(), plane.getExtentZ(), plane.getPolygon()); + + // Get plane index. Keep a map to assign same indices to same planes. + Integer planeIndex = planeIndexMap.get(plane); + if (planeIndex == null) { + planeIndex = planeIndexMap.size(); + planeIndexMap.put(plane, planeIndex); + } + + // Set plane color. Computed deterministically from the Plane index. + int colorIndex = planeIndex % PLANE_COLORS_RGBA.length; + colorRgbaToFloat(planeColor, PLANE_COLORS_RGBA[colorIndex]); + GLES20.glUniform4fv(lineColorUniform, 1, planeColor, 0); + GLES20.glUniform4fv(dotColorUniform, 1, planeColor, 0); + + // Each plane will have its own angle offset from others, to make them easier to + // distinguish. Compute a 2x2 rotation matrix from the angle. + float angleRadians = planeIndex * 0.144f; + float uScale = DOTS_PER_METER; + float vScale = DOTS_PER_METER * EQUILATERAL_TRIANGLE_SCALE; + planeAngleUvMatrix[0] = +(float) Math.cos(angleRadians) * uScale; + planeAngleUvMatrix[1] = -(float) Math.sin(angleRadians) * vScale; + planeAngleUvMatrix[2] = +(float) Math.sin(angleRadians) * uScale; + planeAngleUvMatrix[3] = +(float) Math.cos(angleRadians) * vScale; + GLES20.glUniformMatrix2fv(planeUvMatrixUniform, 1, false, planeAngleUvMatrix, 0); + + draw(cameraView, cameraPerspective); + } + + // Clean up the state we set + GLES20.glDisableVertexAttribArray(planeXZPositionAlphaAttribute); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + GLES20.glDisable(GLES20.GL_BLEND); + GLES20.glDepthMask(true); + + ShaderUtil.checkGLError(TAG, "Cleaning up after drawing planes"); + } + + private static void colorRgbaToFloat(float[] planeColor, int colorRgba) { + planeColor[0] = ((float) ((colorRgba >> 24) & 0xff)) / 255.0f; + planeColor[1] = ((float) ((colorRgba >> 16) & 0xff)) / 255.0f; + planeColor[2] = ((float) ((colorRgba >> 8) & 0xff)) / 255.0f; + planeColor[3] = ((float) ((colorRgba >> 0) & 0xff)) / 255.0f; + } + + private static final int[] PLANE_COLORS_RGBA = { + 0xFFFFFFFF, + 0xF44336FF, + 0xE91E63FF, + 0x9C27B0FF, + 0x673AB7FF, + 0x3F51B5FF, + 0x2196F3FF, + 0x03A9F4FF, + 0x00BCD4FF, + 0x009688FF, + 0x4CAF50FF, + 0x8BC34AFF, + 0xCDDC39FF, + 0xFFEB3BFF, + 0xFFC107FF, + 0xFF9800FF, + }; +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/PointCloudRenderer.java b/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/PointCloudRenderer.java new file mode 100644 index 0000000..2b96b15 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/PointCloudRenderer.java @@ -0,0 +1,144 @@ + +package com.google.ar.core.examples.java.helloar.rendering; + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.opengl.Matrix; + +import com.google.ar.core.PointCloud; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import fr.geolabs.dev.mapmint4me.R; + +/** + * Renders a point cloud. + */ +public class PointCloudRenderer { + private static final String TAG = PointCloud.class.getSimpleName(); + + private static final int BYTES_PER_FLOAT = Float.SIZE / 8; + private static final int FLOATS_PER_POINT = 4; // X,Y,Z,confidence. + private static final int BYTES_PER_POINT = BYTES_PER_FLOAT * FLOATS_PER_POINT; + private static final int INITIAL_BUFFER_POINTS = 1000; + + private int vbo; + private int vboSize; + + private int programName; + private int positionAttribute; + private int modelViewProjectionUniform; + private int colorUniform; + private int pointSizeUniform; + + private int numPoints = 0; + + // Keep track of the last point cloud rendered to avoid updating the VBO if point cloud + // was not changed. + private PointCloud lastPointCloud = null; + + public PointCloudRenderer() {} + + /** + * Allocates and initializes OpenGL resources needed by the plane renderer. Must be called on the + * OpenGL thread, typically in {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)}. + * + * @param context Needed to access shader source. + */ + public void createOnGlThread(Context context) { + ShaderUtil.checkGLError(TAG, "before create"); + + int[] buffers = new int[1]; + GLES20.glGenBuffers(1, buffers, 0); + vbo = buffers[0]; + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo); + + vboSize = INITIAL_BUFFER_POINTS * BYTES_PER_POINT; + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vboSize, null, GLES20.GL_DYNAMIC_DRAW); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + ShaderUtil.checkGLError(TAG, "buffer alloc"); + + int vertexShader = + ShaderUtil.loadGLShader(TAG, context, GLES20.GL_VERTEX_SHADER, R.raw.point_cloud_vertex); + int passthroughShader = + ShaderUtil.loadGLShader( + TAG, context, GLES20.GL_FRAGMENT_SHADER, R.raw.passthrough_fragment); + + programName = GLES20.glCreateProgram(); + GLES20.glAttachShader(programName, vertexShader); + GLES20.glAttachShader(programName, passthroughShader); + GLES20.glLinkProgram(programName); + GLES20.glUseProgram(programName); + + ShaderUtil.checkGLError(TAG, "program"); + + positionAttribute = GLES20.glGetAttribLocation(programName, "a_Position"); + colorUniform = GLES20.glGetUniformLocation(programName, "u_Color"); + modelViewProjectionUniform = GLES20.glGetUniformLocation(programName, "u_ModelViewProjection"); + pointSizeUniform = GLES20.glGetUniformLocation(programName, "u_PointSize"); + + ShaderUtil.checkGLError(TAG, "program params"); + } + + /** + * Updates the OpenGL buffer contents to the provided point. Repeated calls with the same point + * cloud will be ignored. + */ + public void update(PointCloud cloud) { + if (lastPointCloud == cloud) { + // Redundant call. + return; + } + + ShaderUtil.checkGLError(TAG, "before update"); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo); + lastPointCloud = cloud; + + // If the VBO is not large enough to fit the new point cloud, resize it. + numPoints = lastPointCloud.getPoints().remaining() / FLOATS_PER_POINT; + if (numPoints * BYTES_PER_POINT > vboSize) { + while (numPoints * BYTES_PER_POINT > vboSize) { + vboSize *= 2; + } + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vboSize, null, GLES20.GL_DYNAMIC_DRAW); + } + GLES20.glBufferSubData( + GLES20.GL_ARRAY_BUFFER, 0, numPoints * BYTES_PER_POINT, lastPointCloud.getPoints()); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + ShaderUtil.checkGLError(TAG, "after update"); + } + + /** + * Renders the point cloud. ArCore point cloud is given in world space. + * + * @param cameraView the camera view matrix for this frame, typically from {@link + * com.google.ar.core.Camera#getViewMatrix(float[], int)}. + * @param cameraPerspective the camera projection matrix for this frame, typically from {@link + * com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)}. + */ + public void draw(float[] cameraView, float[] cameraPerspective) { + float[] modelViewProjection = new float[16]; + Matrix.multiplyMM(modelViewProjection, 0, cameraPerspective, 0, cameraView, 0); + + ShaderUtil.checkGLError(TAG, "Before draw"); + + GLES20.glUseProgram(programName); + GLES20.glEnableVertexAttribArray(positionAttribute); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo); + GLES20.glVertexAttribPointer(positionAttribute, 4, GLES20.GL_FLOAT, false, BYTES_PER_POINT, 0); + GLES20.glUniform4f(colorUniform, 31.0f / 255.0f, 188.0f / 255.0f, 210.0f / 255.0f, 1.0f); + GLES20.glUniformMatrix4fv(modelViewProjectionUniform, 1, false, modelViewProjection, 0); + GLES20.glUniform1f(pointSizeUniform, 5.0f); + + GLES20.glDrawArrays(GLES20.GL_POINTS, 0, numPoints); + GLES20.glDisableVertexAttribArray(positionAttribute); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + ShaderUtil.checkGLError(TAG, "Draw"); + } +} diff --git a/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/ShaderUtil.java b/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/ShaderUtil.java new file mode 100755 index 0000000..93dc727 --- /dev/null +++ b/app/src/main/java/com/google/ar/core/examples/java/helloar/rendering/ShaderUtil.java @@ -0,0 +1,89 @@ + +package com.google.ar.core.examples.java.helloar.rendering; + +import android.content.Context; +import android.opengl.GLES20; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * Shader helper functions. + */ +public class ShaderUtil { + /** + * Converts a raw text file, saved as a resource, into an OpenGL ES shader. + * + * @param type The type of shader we will be creating. + * @param resId The resource ID of the raw text file about to be turned into a shader. + * @return The shader object handler. + */ + public static int loadGLShader(String tag, Context context, int type, int resId) { + String code = readRawTextFile(context, resId); + int shader = GLES20.glCreateShader(type); + GLES20.glShaderSource(shader, code); + GLES20.glCompileShader(shader); + + // Get the compilation status. + final int[] compileStatus = new int[1]; + GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); + + // If the compilation failed, delete the shader. + if (compileStatus[0] == 0) { + Log.e(tag, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader)); + GLES20.glDeleteShader(shader); + shader = 0; + } + + if (shader == 0) { + throw new RuntimeException("Error creating shader."); + } + + return shader; + } + + /** + * Checks if we've had an error inside of OpenGL ES, and if so what that error is. + * + * @param label Label to report in case of error. + * @throws RuntimeException If an OpenGL error is detected. + */ + public static void checkGLError(String tag, String label) { + int lastError = GLES20.GL_NO_ERROR; + // Drain the queue of all errors. + int error; + while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { + Log.e(tag, label + ": glError " + error); + lastError = error; + } + if (lastError != GLES20.GL_NO_ERROR) { + throw new RuntimeException(label + ": glError " + lastError); + } + } + + /** + * Converts a raw text file into a string. + * + * @param resId The resource ID of the raw text file about to be turned into a shader. + * @return The context of the text file, or null in case of error. + */ + private static String readRawTextFile(Context context, int resId) { + InputStream inputStream = context.getResources().openRawResource(resId); + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + reader.close(); + return sb.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/app/src/main/java/com/hl3hl3/arcoremeasure/ArMeasureActivity.java b/app/src/main/java/com/hl3hl3/arcoremeasure/ArMeasureActivity.java new file mode 100644 index 0000000..0bcc394 --- /dev/null +++ b/app/src/main/java/com/hl3hl3/arcoremeasure/ArMeasureActivity.java @@ -0,0 +1,967 @@ +package com.hl3hl3.arcoremeasure; + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.opengl.Matrix; +import android.os.Build; +import android.os.Bundle; +import android.support.design.widget.BaseTransientBottomBar; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.PopupWindow; +import android.widget.TextView; +import android.widget.Toast; + +import com.crashlytics.android.BuildConfig; +import com.crashlytics.android.Crashlytics; +import com.google.ar.core.Anchor; +import com.google.ar.core.ArCoreApk; +import com.google.ar.core.Camera; +import com.google.ar.core.Config; +import com.google.ar.core.Frame; +import com.google.ar.core.HitResult; +import com.google.ar.core.Plane; +import com.google.ar.core.Point; +import com.google.ar.core.PointCloud; +import com.google.ar.core.Pose; +import com.google.ar.core.Session; +import com.google.ar.core.Trackable; +import com.google.ar.core.TrackingState; +import com.google.ar.core.examples.java.helloar.CameraPermissionHelper; +import com.google.ar.core.examples.java.helloar.DisplayRotationHelper; +import com.google.ar.core.examples.java.helloar.rendering.BackgroundRenderer; +import com.google.ar.core.examples.java.helloar.rendering.ObjectRenderer; +import com.google.ar.core.examples.java.helloar.rendering.PlaneRenderer; +import com.google.ar.core.examples.java.helloar.rendering.PointCloudRenderer; +import com.google.ar.core.exceptions.CameraNotAvailableException; +import com.google.ar.core.exceptions.NotTrackingException; +import com.google.ar.core.exceptions.UnavailableApkTooOldException; +import com.google.ar.core.exceptions.UnavailableArcoreNotInstalledException; +import com.google.ar.core.exceptions.UnavailableSdkTooOldException; +import com.google.ar.core.exceptions.UnavailableUserDeclinedInstallationException; +import com.hl3hl3.arcoremeasure.renderer.RectanglePolygonRenderer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.concurrent.ArrayBlockingQueue; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import fr.geolabs.dev.mapmint4me.R; +import io.fabric.sdk.android.Fabric; + + +public class ArMeasureActivity extends AppCompatActivity { + private static final String TAG = ArMeasureActivity.class.getSimpleName(); + private static final String ASSET_NAME_CUBE_OBJ = "cube.obj"; + private static final String ASSET_NAME_CUBE = "cube_green.png"; + private static final String ASSET_NAME_CUBE_SELECTED = "cube_cyan.png"; + + private static final int MAX_CUBE_COUNT = 16; + + // Rendering. The Renderers are created here, and initialized when the GL surface is created. + private GLSurfaceView surfaceView = null; + + private boolean installRequested; + + private Session session = null; + private GestureDetector gestureDetector; + private Snackbar messageSnackbar = null; + private DisplayRotationHelper displayRotationHelper; + + private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer(); + private final PlaneRenderer planeRenderer = new PlaneRenderer(); + private final PointCloudRenderer pointCloud = new PointCloudRenderer(); + + private final ObjectRenderer cube = new ObjectRenderer(); + private final ObjectRenderer cubeSelected = new ObjectRenderer(); + private RectanglePolygonRenderer rectRenderer = null; + + // Temporary matrix allocated here to reduce number of allocations for each frame. + private final float[] anchorMatrix = new float[MAX_CUBE_COUNT]; + private final ImageView[] ivCubeIconList = new ImageView[MAX_CUBE_COUNT]; + private final int[] cubeIconIdArray = { + R.id.iv_cube1, + R.id.iv_cube2, + R.id.iv_cube3, + R.id.iv_cube4, + R.id.iv_cube5, + R.id.iv_cube6, + R.id.iv_cube7, + R.id.iv_cube8, + R.id.iv_cube9, + R.id.iv_cube10, + R.id.iv_cube11, + R.id.iv_cube12, + R.id.iv_cube13, + R.id.iv_cube14, + R.id.iv_cube15, + R.id.iv_cube16 + }; + + // Tap handling and UI. + private ArrayBlockingQueue queuedSingleTaps = new ArrayBlockingQueue<>(MAX_CUBE_COUNT); + private ArrayBlockingQueue queuedLongPress = new ArrayBlockingQueue<>(MAX_CUBE_COUNT); + private final ArrayList anchors = new ArrayList<>(); + private ArrayList showingTapPointX = new ArrayList<>(); + private ArrayList showingTapPointY = new ArrayList<>(); + + private ArrayBlockingQueue queuedScrollDx = new ArrayBlockingQueue<>(MAX_CUBE_COUNT); + private ArrayBlockingQueue queuedScrollDy = new ArrayBlockingQueue<>(MAX_CUBE_COUNT); + + private void log(String tag, String log){ + if(BuildConfig.DEBUG) { + Log.d(tag, log); + } + } + + private void log(Exception e){ + try { + Crashlytics.logException(e); + if (BuildConfig.DEBUG) { + e.printStackTrace(); + } + }catch (Exception ex){ + if (BuildConfig.DEBUG) { + ex.printStackTrace(); + } + } + } + + private void logStatus(String msg){ + try { + Crashlytics.log(msg); + }catch (Exception e){ + log(e); + } + } + + // OverlayView overlayViewForTest; + private TextView tv_result; + private FloatingActionButton fab; + + private GLSurfaceRenderer glSerfaceRenderer = null; + private GestureDetector.SimpleOnGestureListener gestureDetectorListener = new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onSingleTapUp(MotionEvent e) { + // Queue tap if there is space. Tap is lost if queue is full. + queuedSingleTaps.offer(e); +// log(TAG, "onSingleTapUp, e=" + e.getRawX() + ", " + e.getRawY()); + return true; + } + + @Override + public void onLongPress(MotionEvent e) { + queuedLongPress.offer(e); + } + + @Override + public boolean onDown(MotionEvent e) { + return true; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, + float distanceX, float distanceY) { +// log(TAG, "onScroll, dx=" + distanceX + " dy=" + distanceY); + queuedScrollDx.offer(distanceX); + queuedScrollDy.offer(distanceY); + return true; + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Fabric.with(this, new Crashlytics()); + setContentView(R.layout.activity_main); + +// overlayViewForTest = (OverlayView)findViewById(R.id.overlay_for_test); + tv_result = findViewById(R.id.tv_result); + fab = findViewById(R.id.fab); + + for(int i=0; i= Build.VERSION_CODES.N) { + float screenWidth = getResources().getDisplayMetrics().widthPixels; + float screenHeight = getResources().getDisplayMetrics().heightPixels; + popUp.showAtLocation(v, Gravity.NO_GRAVITY, (int)screenWidth/2, (int)screenHeight/2); + } else { + popUp.showAsDropDown(v); + } + } + }); + fab.hide(); + + + displayRotationHelper = new DisplayRotationHelper(/*context=*/ this); + + if(CameraPermissionHelper.hasCameraPermission(this)){ + setupRenderer(); + } + + installRequested = false; + } + + private void setupRenderer(){ + if(surfaceView != null){ + return; + } + surfaceView = findViewById(R.id.surfaceview); + + // Set up tap listener. + gestureDetector = new GestureDetector(this, gestureDetectorListener); + surfaceView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return gestureDetector.onTouchEvent(event); + } + }); + glSerfaceRenderer = new GLSurfaceRenderer(this); + surfaceView.setPreserveEGLContextOnPause(true); + surfaceView.setEGLContextClientVersion(2); + surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending. + surfaceView.setRenderer(glSerfaceRenderer); + surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + } + + @Override + protected void onResume() { + super.onResume(); + logStatus("onResume()"); + if (session == null) { + Exception exception = null; + String message = null; + try { + switch (ArCoreApk.getInstance().requestInstall(this, !installRequested)) { + case INSTALL_REQUESTED: + installRequested = true; + return; + case INSTALLED: + break; + } + + // ARCore requires camera permissions to operate. If we did not yet obtain runtime + // permission on Android M and above, now is a good time to ask the user for it. + if (!CameraPermissionHelper.hasCameraPermission(this)) { + CameraPermissionHelper.requestCameraPermission(this); + return; + } + + session = new Session(/* context= */ this); + } catch (UnavailableArcoreNotInstalledException + | UnavailableUserDeclinedInstallationException e) { + message = "Please install ARCore"; + exception = e; + } catch (UnavailableApkTooOldException e) { + message = "Please update ARCore"; + exception = e; + } catch (UnavailableSdkTooOldException e) { + message = "Please update this app"; + exception = e; + } catch (Exception e) { + message = "This device does not support AR"; + exception = e; + } + + if (message != null) { + showSnackbarMessage(message, true); + Log.e(TAG, "Exception creating session", exception); + return; + } + + // Create default config and check if supported. + Config config = new Config(session); + if (!session.isSupported(config)) { + showSnackbarMessage("This device does not support AR", true); + } + session.configure(config); + + setupRenderer(); + } + + showLoadingMessage(); + // Note that order matters - see the note in onPause(), the reverse applies here. + try { + session.resume(); + } catch (CameraNotAvailableException e) { + e.printStackTrace(); + } + surfaceView.onResume(); + displayRotationHelper.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + logStatus("onPause()"); + if (session != null) { + // Note that the order matters - GLSurfaceView is paused first so that it does not try + // to query the session. If Session is paused before GLSurfaceView, GLSurfaceView may + // still call session.update() and get a SessionPausedException. + displayRotationHelper.onPause(); + surfaceView.onPause(); + session.pause(); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) { + logStatus("onRequestPermissionsResult()"); + Toast.makeText(this, R.string.need_permission, Toast.LENGTH_LONG) + .show(); + if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(this)) { + // Permission denied with checking "Do not ask again". + CameraPermissionHelper.launchPermissionSettings(this); + } + finish(); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + logStatus("onWindowFocusChanged()"); + if (hasFocus) { + // Standard Android full-screen functionality. + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } + + private void showLoadingMessage() { + runOnUiThread(new Runnable() { + @Override + public void run() { + messageSnackbar = Snackbar.make( + ArMeasureActivity.this.findViewById(android.R.id.content), + "Searching for surfaces...", Snackbar.LENGTH_INDEFINITE); + messageSnackbar.getView().setBackgroundColor(0xbf323232); + messageSnackbar.show(); + } + }); + } + + private void hideLoadingMessage() { + runOnUiThread(new Runnable() { + @Override + public void run() { + if(messageSnackbar != null) { + messageSnackbar.dismiss(); + } + messageSnackbar = null; + } + }); + } + + private void showSnackbarMessage(String message, boolean finishOnDismiss) { + messageSnackbar = + Snackbar.make( + ArMeasureActivity.this.findViewById(android.R.id.content), + message, + Snackbar.LENGTH_INDEFINITE); + messageSnackbar.getView().setBackgroundColor(0xbf323232); + if (finishOnDismiss) { + messageSnackbar.setAction( + "Dismiss", + new View.OnClickListener() { + @Override + public void onClick(View v) { + messageSnackbar.dismiss(); + } + }); + messageSnackbar.addCallback( + new BaseTransientBottomBar.BaseCallback() { + @Override + public void onDismissed(Snackbar transientBottomBar, int event) { + super.onDismissed(transientBottomBar, event); + finish(); + } + }); + } + messageSnackbar.show(); + } + + private void toast(int stringResId){ + Toast.makeText(this, stringResId, Toast.LENGTH_SHORT).show(); + } + private boolean isVerticalMode = false; + private PopupWindow popupWindow; + private PopupWindow getPopupWindow() { + + // initialize a pop up window type + popupWindow = new PopupWindow(this); + + ArrayList sortList = new ArrayList<>(); + sortList.add(getString(R.string.action_1)); + sortList.add(getString(R.string.action_2)); + sortList.add(getString(R.string.action_3)); + sortList.add(getString(R.string.action_4)); + + ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, + sortList); + // the drop down list is a list view + ListView listViewSort = new ListView(this); + // set our adapter and pass our pop up window contents + listViewSort.setAdapter(adapter); + listViewSort.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + switch (position){ + case 3:// move vertical axis + toast(R.string.action_4_toast); + break; + case 0:// delete + toast(R.string.action_1_toast); + break; + case 1:// set as first + toast(R.string.action_2_toast); + break; + case 2:// move horizontal axis + default: + toast(R.string.action_3_toast); + break; + } + return true; + } + }); + // set on item selected + listViewSort.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + switch (position){ + case 3:// move vertical axis + isVerticalMode = true; + popupWindow.dismiss(); + break; + case 0:// delete + glSerfaceRenderer.deleteNowSelection(); + popupWindow.dismiss(); + fab.hide(); + break; + case 1:// set as first + glSerfaceRenderer.setNowSelectionAsFirst(); + popupWindow.dismiss(); + fab.hide(); + break; + case 2:// move horizontal axis + default: + isVerticalMode = false; + popupWindow.dismiss(); + break; + } + + } + }); + // some other visual settings for popup window + popupWindow.setFocusable(true); + popupWindow.setWidth((int)(getResources().getDisplayMetrics().widthPixels * 0.4f)); + // popupWindow.setBackgroundDrawable(getResources().getDrawable(R.drawable.white)); + popupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT); + // set the listview as popup content + popupWindow.setContentView(listViewSort); + return popupWindow; + } + + private class GLSurfaceRenderer implements GLSurfaceView.Renderer{ + private static final String TAG = "GLSurfaceRenderer"; + private Context context; + private final int DEFAULT_VALUE = -1; + private int nowTouchingPointIndex = DEFAULT_VALUE; + private int viewWidth = 0; + private int viewHeight = 0; + // according to cube.obj, cube diameter = 0.02f + private final float cubeHitAreaRadius = 0.08f; + private final float[] centerVertexOfCube = {0f, 0f, 0f, 1}; + private final float[] vertexResult = new float[4]; + + private float[] tempTranslation = new float[3]; + private float[] tempRotation = new float[4]; + private float[] projmtx = new float[16]; + private float[] viewmtx = new float[16]; + + public GLSurfaceRenderer(Context context){ + this.context = context; + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + logStatus("onSurfaceCreated()"); + GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + + // Create the texture and pass it to ARCore session to be filled during update(). + backgroundRenderer.createOnGlThread(context); + if (session != null) { + session.setCameraTextureName(backgroundRenderer.getTextureId()); + } + + // Prepare the other rendering objects. + try { + rectRenderer = new RectanglePolygonRenderer(); + cube.createOnGlThread(context, ASSET_NAME_CUBE_OBJ, ASSET_NAME_CUBE); + cube.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f); + cubeSelected.createOnGlThread(context, ASSET_NAME_CUBE_OBJ, ASSET_NAME_CUBE_SELECTED); + cubeSelected.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f); + } catch (IOException e) { + log(TAG, "Failed to read obj file"); + } + try { + planeRenderer.createOnGlThread(context, "trigrid.png"); + } catch (IOException e) { + log(TAG, "Failed to read plane texture"); + } + pointCloud.createOnGlThread(context); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + if(width <= 0 || height <= 0){ + logStatus("onSurfaceChanged(), <= 0"); + return; + } + logStatus("onSurfaceChanged()"); + + displayRotationHelper.onSurfaceChanged(width, height); + GLES20.glViewport(0, 0, width, height); + viewWidth = width; + viewHeight = height; + setNowTouchingPointIndex(DEFAULT_VALUE); + } + + public void deleteNowSelection(){ + logStatus("deleteNowSelection()"); + int index = nowTouchingPointIndex; + if (index > -1){ + if(index < anchors.size()) { + anchors.remove(index).detach(); + } + if(index < showingTapPointX.size()) { + showingTapPointX.remove(index); + } + if(index < showingTapPointY.size()) { + showingTapPointY.remove(index); + } + } + setNowTouchingPointIndex(DEFAULT_VALUE); + } + + public void setNowSelectionAsFirst(){ + logStatus("setNowSelectionAsFirst()"); + int index = nowTouchingPointIndex; + if (index > -1 && index < anchors.size()) { + if(index < anchors.size()){ + for(int i=0; i= 16) { + anchors.get(0).detach(); + anchors.remove(0); + + showingTapPointX.remove(0); + showingTapPointY.remove(0); + } + + // Adding an Anchor tells ARCore that it should track this position in + // space. This anchor will be used in PlaneAttachment to place the 3d model + // in the correct position relative both to the world and to the plane. + anchors.add(hit.createAnchor()); + + showingTapPointX.add(tap.getX()); + showingTapPointY.add(tap.getY()); + nowTouchingPointIndex = anchors.size() - 1; + + showMoreAction(); + showCubeStatus(); + break; + } + } + }else{ + handleMoveEvent(nowTouchingPointIndex); + } + } catch (Throwable t) { + // Avoid crashing the application due to unhandled exceptions. + Log.e(TAG, "Exception on the OpenGL thread", t); + } + } + + private void handleMoveEvent(int nowSelectedIndex){ + try { + if (showingTapPointX.size() < 1 || queuedScrollDx.size() < 2) { + // no action, don't move + return; + } + if (nowTouchingPointIndex == DEFAULT_VALUE) { + // no selected cube, don't move + return; + } + if (nowSelectedIndex >= showingTapPointX.size()) { + // wrong index, don't move. + return; + } + float scrollDx = 0; + float scrollDy = 0; + int scrollQueueSize = queuedScrollDx.size(); + for (int i = 0; i < scrollQueueSize; i++) { + scrollDx += queuedScrollDx.poll(); + scrollDy += queuedScrollDy.poll(); + } + + if (isVerticalMode) { + Anchor anchor = anchors.remove(nowSelectedIndex); + anchor.detach(); + setPoseDataToTempArray(getPose(anchor)); +// log(TAG, "point[" + nowSelectedIndex + "] move vertical "+ (scrollDy / viewHeight) + ", tY=" + tempTranslation[1] +// + ", new tY=" + (tempTranslation[1] += (scrollDy / viewHeight))); + tempTranslation[1] += (scrollDy / viewHeight); + anchors.add(nowSelectedIndex, + session.createAnchor(new Pose(tempTranslation, tempRotation))); + } else { + float toX = showingTapPointX.get(nowSelectedIndex) - scrollDx; + showingTapPointX.remove(nowSelectedIndex); + showingTapPointX.add(nowSelectedIndex, toX); + + float toY = showingTapPointY.get(nowSelectedIndex) - scrollDy; + showingTapPointY.remove(nowSelectedIndex); + showingTapPointY.add(nowSelectedIndex, toY); + + if (anchors.size() > nowSelectedIndex) { + Anchor anchor = anchors.remove(nowSelectedIndex); + anchor.detach(); + // remove duplicated anchor + setPoseDataToTempArray(getPose(anchor)); + tempTranslation[0] -= (scrollDx / viewWidth); + tempTranslation[2] -= (scrollDy / viewHeight); + anchors.add(nowSelectedIndex, + session.createAnchor(new Pose(tempTranslation, tempRotation))); + } + } + } catch (NotTrackingException e) { + e.printStackTrace(); + } + } + + private final float[] mPoseTranslation = new float[3]; + private final float[] mPoseRotation = new float[4]; + private Pose getPose(Anchor anchor){ + Pose pose = anchor.getPose(); + pose.getTranslation(mPoseTranslation, 0); + pose.getRotationQuaternion(mPoseRotation, 0); + return new Pose(mPoseTranslation, mPoseRotation); + } + + private void setPoseDataToTempArray(Pose pose){ + pose.getTranslation(tempTranslation, 0); + pose.getRotationQuaternion(tempRotation, 0); + } + + private void drawLine(Pose pose0, Pose pose1, float[] viewmtx, float[] projmtx){ + float lineWidth = 0.002f; + float lineWidthH = lineWidth / viewHeight * viewWidth; + rectRenderer.setVerts( + pose0.tx() - lineWidth, pose0.ty() + lineWidthH, pose0.tz() - lineWidth, + pose0.tx() + lineWidth, pose0.ty() + lineWidthH, pose0.tz() + lineWidth, + pose1.tx() + lineWidth, pose1.ty() + lineWidthH, pose1.tz() + lineWidth, + pose1.tx() - lineWidth, pose1.ty() + lineWidthH, pose1.tz() - lineWidth + , + pose0.tx() - lineWidth, pose0.ty() - lineWidthH, pose0.tz() - lineWidth, + pose0.tx() + lineWidth, pose0.ty() - lineWidthH, pose0.tz() + lineWidth, + pose1.tx() + lineWidth, pose1.ty() - lineWidthH, pose1.tz() + lineWidth, + pose1.tx() - lineWidth, pose1.ty() - lineWidthH, pose1.tz() - lineWidth + ); + + rectRenderer.draw(viewmtx, projmtx); + } + + private void drawObj(Pose pose, ObjectRenderer renderer, float[] cameraView, float[] cameraPerspective, float lightIntensity){ + pose.toMatrix(anchorMatrix, 0); + renderer.updateModelMatrix(anchorMatrix, 1); + renderer.draw(cameraView, cameraPerspective, lightIntensity); + } + + private void checkIfHit(ObjectRenderer renderer, int cubeIndex){ + if(isMVPMatrixHitMotionEvent(renderer.getModelViewProjectionMatrix(), queuedLongPress.peek())){ + // long press hit a cube, show context menu for the cube + nowTouchingPointIndex = cubeIndex; + queuedLongPress.poll(); + showMoreAction(); + showCubeStatus(); + runOnUiThread(new Runnable() { + @Override + public void run() { + fab.performClick(); + } + }); + }else if(isMVPMatrixHitMotionEvent(renderer.getModelViewProjectionMatrix(), queuedSingleTaps.peek())){ + nowTouchingPointIndex = cubeIndex; + queuedSingleTaps.poll(); + showMoreAction(); + showCubeStatus(); + } + } + + private boolean isMVPMatrixHitMotionEvent(float[] ModelViewProjectionMatrix, MotionEvent event){ + if(event == null){ + return false; + } + Matrix.multiplyMV(vertexResult, 0, ModelViewProjectionMatrix, 0, centerVertexOfCube, 0); + /** + * vertexResult = [x, y, z, w] + * + * coordinates in View + * ┌─────────────────────────────────────────┐╮ + * │[0, 0] [viewWidth, 0]│ + * │ [viewWidth/2, viewHeight/2] │view height + * │[0, viewHeight] [viewWidth, viewHeight]│ + * └─────────────────────────────────────────┘╯ + * ╰ view width ╯ + * + * coordinates in GLSurfaceView frame + * ┌─────────────────────────────────────────┐╮ + * │[-1.0, 1.0] [1.0, 1.0]│ + * │ [0, 0] │view height + * │[-1.0, -1.0] [1.0, -1.0]│ + * └─────────────────────────────────────────┘╯ + * ╰ view width ╯ + */ + // circle hit test + float radius = (viewWidth / 2) * (cubeHitAreaRadius/vertexResult[3]); + float dx = event.getX() - (viewWidth / 2) * (1 + vertexResult[0]/vertexResult[3]); + float dy = event.getY() - (viewHeight / 2) * (1 - vertexResult[1]/vertexResult[3]); + double distance = Math.sqrt(dx * dx + dy * dy); +// // for debug +// overlayViewForTest.setPoint("cubeCenter", screenX, screenY); +// overlayViewForTest.postInvalidate(); + return distance < radius; + } + + private double getDistance(Pose pose0, Pose pose1){ + float dx = pose0.tx() - pose1.tx(); + float dy = pose0.ty() - pose1.ty(); + float dz = pose0.tz() - pose1.tz(); + return Math.sqrt(dx * dx + dz * dz + dy * dy); + } + + private void showResult(final String result){ + runOnUiThread(new Runnable() { + @Override + public void run() { + tv_result.setText(result); + } + }); + } + + private void showMoreAction(){ + runOnUiThread(new Runnable() { + @Override + public void run() { + fab.show(); + } + }); + } + + private void hideMoreAction(){ + runOnUiThread(new Runnable() { + @Override + public void run() { + fab.hide(); + } + }); + } + + private void showCubeStatus(){ + runOnUiThread(new Runnable() { + @Override + public void run() { + int nowSelectIndex = glSerfaceRenderer.getNowTouchingPointIndex(); + for(int i = 0; i pointMap = new HashMap<>(); + + public void setPoint(String tag, float x, float y){ + pointMap.put(tag, new Point((int)x, (int)y)); + } + + @Override + protected void onDraw(Canvas canvas) { + if(pointMap == null){ + return; + } + + for(String key : pointMap.keySet()){ + Point point = pointMap.get(key); + canvas.drawCircle(point.x, point.y, 20, paint); + Log.d("OverlayView", "drawCircle"); + } + + } + +} diff --git a/app/src/main/java/com/hl3hl3/arcoremeasure/renderer/LineRenderer.java b/app/src/main/java/com/hl3hl3/arcoremeasure/renderer/LineRenderer.java new file mode 100644 index 0000000..a82f43f --- /dev/null +++ b/app/src/main/java/com/hl3hl3/arcoremeasure/renderer/LineRenderer.java @@ -0,0 +1,161 @@ +package com.hl3hl3.arcoremeasure.renderer; + +import android.opengl.GLES20; +import android.opengl.Matrix; + +import com.google.ar.core.examples.java.helloar.rendering.ShaderUtil; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + + +public class LineRenderer { + + private final int mProgram; + + private final String vertexShaderCode = + // This matrix member variable provides a hook to manipulate + // the coordinates of the objects that use this vertex shader + "uniform mat4 uMVPMatrix;" + + "attribute vec4 vPosition;" + + "void main() {" + + // the matrix must be included as a modifier of gl_Position + // Note that the uMVPMatrix factor *must be first* in order + // for the matrix multiplication product to be correct. + " gl_Position = uMVPMatrix * vPosition;" + + "}"; + + // Use to access and set the view transformation + private int mMVPMatrixHandle; + + private final String fragmentShaderCode = + "precision mediump float;" + + "uniform vec4 vColor;" + + "void main() {" + + " gl_FragColor = vColor;" + + "}"; + + + private FloatBuffer vertexBuffer; + + static final int COORDS_PER_VERTEX = 3; + static float coordinates[] = { // in counterclockwise order: + 0.0f, 0.0f, 0.0f, // point 1 + 1.0f, 0.0f, 0.0f, // point 2 + }; + + float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f}; + + private int mPositionHandle; + private int mColorHandle; + + private final int vertexCount = coordinates.length / COORDS_PER_VERTEX; + private final int vertexStride = COORDS_PER_VERTEX * 4; + + private int loadShader(int type, String shaderCode){ + int shader = GLES20.glCreateShader(type); + GLES20.glShaderSource(shader, shaderCode); + GLES20.glCompileShader(shader); + return shader; + } + + public LineRenderer() { + // initialize vertex byte buffer for shape coordinates + // number ofr coordinate values * 4 bytes per float + ByteBuffer bb = ByteBuffer.allocateDirect(coordinates.length * 4); + bb.order(ByteOrder.nativeOrder()); // use the device hardware's native byte order + vertexBuffer = bb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer + vertexBuffer.put(coordinates); // add the coordinate to the FloatBuffer + vertexBuffer.position(0); // set the buffer to read the first coordinate + + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); + + mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program + GLES20.glAttachShader(mProgram, vertexShader); // add the shader to program + GLES20.glAttachShader(mProgram, fragmentShader); + GLES20.glLinkProgram(mProgram); // create OpenGL ES program executables + Matrix.setIdentityM(mModelMatrix, 0); + } + + public void setVerts(float v0, float v1, float v2, float v3, float v4, float v5) { + coordinates[0] = v0; + coordinates[1] = v1; + coordinates[2] = v2; + coordinates[3] = v3; + coordinates[4] = v4; + coordinates[5] = v5; + + vertexBuffer.put(coordinates); + // set the buffer to read the first coordinate + vertexBuffer.position(0); + } + + public void setColor(float red, float green, float blue, float alpha) { + color[0] = red; + color[1] = green; + color[2] = blue; + color[3] = alpha; + } + + // Temporary matrices allocated here to reduce number of allocations for each frame. + private float[] mModelMatrix = new float[16]; + private float[] mModelViewMatrix = new float[16]; + private float[] mModelViewProjectionMatrix = new float[16]; + final String TAG = "Line"; + + public void draw(float[] cameraView, float[] cameraPerspective) { + ShaderUtil.checkGLError(TAG, "Before draw"); + + // Build the ModelView and ModelViewProjection matrices + // for calculating object position and light. + Matrix.multiplyMM(mModelViewMatrix, 0, cameraView, 0, mModelMatrix, 0); + Matrix.multiplyMM(mModelViewProjectionMatrix, 0, cameraPerspective, 0, mModelViewMatrix, 0); + + // add program to OpenGL ES environment + GLES20.glUseProgram(mProgram); + ShaderUtil.checkGLError(TAG, "After glBindBuffer"); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + ShaderUtil.checkGLError(TAG, "After glBindBuffer"); + + // get handle to vertex shader's vPosition member + mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); + ShaderUtil.checkGLError(TAG, "After glGetAttribLocation"); + + // enable a handle to the vertices + GLES20.glEnableVertexAttribArray(mPositionHandle); + ShaderUtil.checkGLError(TAG, "After glEnableVertexAttribArray"); + + // prepare the coordinate data + GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, + GLES20.GL_FLOAT, false, + vertexStride, vertexBuffer); + ShaderUtil.checkGLError(TAG, "After glVertexAttribPointer"); + + // get handle to fragment shader's vColor member + mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); + + // set color for drawing the triangle + GLES20.glUniform4fv(mColorHandle, 1, color, 0); + ShaderUtil.checkGLError(TAG, "After glUniform4fv"); + + // get handle to shape's transformation matrix + mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); + + // Pass the projection and view transformation to the shader + GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mModelViewProjectionMatrix, 0); + ShaderUtil.checkGLError(TAG, "After glUniformMatrix4fv"); + + // Draw the line + GLES20.glDrawArrays(GLES20.GL_LINES, 0, vertexCount); + ShaderUtil.checkGLError(TAG, "After glDrawArrays"); + + // Disable vertex array + GLES20.glDisableVertexAttribArray(mPositionHandle); + ShaderUtil.checkGLError(TAG, "After draw"); + } + + +} diff --git a/app/src/main/java/com/hl3hl3/arcoremeasure/renderer/RectanglePolygonRenderer.java b/app/src/main/java/com/hl3hl3/arcoremeasure/renderer/RectanglePolygonRenderer.java new file mode 100644 index 0000000..1abfbf6 --- /dev/null +++ b/app/src/main/java/com/hl3hl3/arcoremeasure/renderer/RectanglePolygonRenderer.java @@ -0,0 +1,206 @@ +package com.hl3hl3.arcoremeasure.renderer; + +import android.opengl.GLES20; +import android.opengl.Matrix; + +import com.google.ar.core.examples.java.helloar.rendering.ShaderUtil; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; + + +public class RectanglePolygonRenderer { + + private FloatBuffer vertexBuffer; + private ShortBuffer drawListBuffer; + + // number of coordinates pervertex in this array + static final int COORDS_PER_VERTEX = 3; + static float coords[] = { + -0.6f, 0.5f, 0.2f, // top left + -0.4f, -0.5f, 0.2f, // bottom left + 0.5f, -0.5f, 0.2f, // bottom right + 0.5f, 0.5f, 0.2f, // top right + + -0.5f, 0.6f, 0.0f, // top left + -0.5f, -0.8f, 0.0f, // bottom left + 0.5f, -0.5f, 0.0f, // bottom right + 0.5f, 0.5f, 0.0f // top right + }; + private short drawOrder[] = { + 0, 1, 2, 0, 2, 3, + 3, 2, 6, 3, 6, 7, + 4, 5, 6, 4, 6, 7, + 0, 1, 5, 0, 5, 4, + 4, 0, 3, 4, 3, 7, + 5, 1, 2, 5, 2, 6 + }; // order to draw vertex + + float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f}; + + public void setVerts(float v0, float v1, float v2, + float v3, float v4, float v5, + float v6, float v7, float v8, + float v9, float v10, float v11, + + float v12, float v13, float v14, + float v15, float v16, float v17, + float v18, float v19, float v20, + float v21, float v22, float v23 + ) { + coords[0] = v0; + coords[1] = v1; + coords[2] = v2; + + coords[3] = v3; + coords[4] = v4; + coords[5] = v5; + + coords[6] = v6; + coords[7] = v7; + coords[8] = v8; + + coords[9] = v9; + coords[10] = v10; + coords[11] = v11; + + coords[12] = v12; + coords[13] = v13; + coords[14] = v14; + + coords[15] = v15; + coords[16] = v16; + coords[17] = v17; + + coords[18] = v18; + coords[19] = v19; + coords[20] = v20; + + coords[21] = v21; + coords[22] = v22; + coords[23] = v23; + + vertexBuffer.put(coords); + // set the buffer to read the first coordinate + vertexBuffer.position(0); + } + + public void setColor(float red, float green, float blue, float alpha) { + color[0] = red; + color[1] = green; + color[2] = blue; + color[3] = alpha; + } + + + private final int mProgram; + + private final String vertexShaderCode = + "uniform mat4 uMVPMatrix;" + + "attribute vec4 vPosition;" + + "void main() {" + + " gl_Position = uMVPMatrix * vPosition;" + + "}"; + + // Use to access and set the view transformation + private int mMVPMatrixHandle; + + private final String fragmentShaderCode = + "precision mediump float;" + + "uniform vec4 vColor;" + + "void main() {" + + " gl_FragColor = vColor;" + + "}"; + + private int mPositionHandle; + private int mColorHandle; + + private final int vertexCount = coords.length / COORDS_PER_VERTEX; + private final int vertexStride = COORDS_PER_VERTEX * 4; + + private int loadShader(int type, String shaderCode){ + int shader = GLES20.glCreateShader(type); + GLES20.glShaderSource(shader, shaderCode); + GLES20.glCompileShader(shader); + return shader; + } + + public RectanglePolygonRenderer(){ + // initialize vertex byte buffer for shape coordinates + ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * 4); + bb.order(ByteOrder.nativeOrder()); + vertexBuffer = bb.asFloatBuffer(); + vertexBuffer.put(coords); + vertexBuffer.position(0); + + // initialize byte buffer for the draw list + ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2); // 2 bytes per short + dlb.order(ByteOrder.nativeOrder()); + drawListBuffer = dlb.asShortBuffer(); + drawListBuffer.put(drawOrder); + drawListBuffer.position(0); + + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); + + // create empty OpenGL ES Program + mProgram = GLES20.glCreateProgram(); + + // add the shader to program + GLES20.glAttachShader(mProgram, vertexShader); + GLES20.glAttachShader(mProgram, fragmentShader); + + // create OpenGL ES program executables + GLES20.glLinkProgram(mProgram); + Matrix.setIdentityM(mModelMatrix, 0); + } + + // Temporary matrices allocated here to reduce number of allocations for each frame. + private float[] mModelMatrix = new float[16]; + private float[] mModelViewMatrix = new float[16]; + private float[] mModelViewProjectionMatrix = new float[16]; + final String TAG = "RectanglePolygon"; + + public void draw(float[] cameraView, float[] cameraPerspective) { + ShaderUtil.checkGLError(TAG, "Before draw"); + + // Build the ModelView and ModelViewProjection matrices + // for calculating object position and light. + Matrix.multiplyMM(mModelViewMatrix, 0, cameraView, 0, mModelMatrix, 0); + Matrix.multiplyMM(mModelViewProjectionMatrix, 0, cameraPerspective, 0, mModelViewMatrix, 0); + + // add program to OpenGL ES environment + GLES20.glUseProgram(mProgram); + + // get handle to vertex shader's vPosition member + mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); + + // enable a handle to the triangle vertices + GLES20.glEnableVertexAttribArray(mPositionHandle); + + // prepare the triangle coordinate data + GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, + GLES20.GL_FLOAT, false, + vertexStride, vertexBuffer); + + // get handle to fragment shader's vColor member + mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); + + // set color for drawing the triangle + GLES20.glUniform4fv(mColorHandle, 1, color, 0); + + // get handle to shape's transformation matrix + mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); + + // Pass the projection and view transformation to the shader + GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mModelViewProjectionMatrix, 0); + + GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, + GLES20.GL_UNSIGNED_SHORT, drawListBuffer); + + // Disable vertex array + GLES20.glDisableVertexAttribArray(mPositionHandle); + } +} diff --git a/app/src/main/java/com/hl3hl3/arcoremeasure/renderer/SquareRenderer.java b/app/src/main/java/com/hl3hl3/arcoremeasure/renderer/SquareRenderer.java new file mode 100644 index 0000000..393711e --- /dev/null +++ b/app/src/main/java/com/hl3hl3/arcoremeasure/renderer/SquareRenderer.java @@ -0,0 +1,175 @@ +package com.hl3hl3.arcoremeasure.renderer; + +import android.opengl.GLES20; +import android.opengl.Matrix; + +import com.google.ar.core.examples.java.helloar.rendering.ShaderUtil; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; + + +public class SquareRenderer { + + private FloatBuffer vertexBuffer; + private ShortBuffer drawListBuffer; + + // number of coordinates pervertex in this array + static final int COORDS_PER_VERTEX = 3; + static float coords[] = { + -0.05f, 0.05f, 0.0f, // top left + -0.05f, -0.05f, 0.0f, // bottom left + 0.05f, -0.05f, 0.0f, // bottom right + 0.05f, 0.05f, 0.0f }; // top right + private short drawOrder[] = {0, 1, 2, 0, 2, 3}; // order to draw vertex + + float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f}; + + public void setVerts(float v0, float v1, float v2, + float v3, float v4, float v5, + float v6, float v7, float v8, + float v9, float v10, float v11) { + coords[0] = v0; + coords[1] = v1; + coords[2] = v2; + + coords[3] = v3; + coords[4] = v4; + coords[5] = v5; + + coords[6] = v6; + coords[7] = v7; + coords[8] = v8; + + coords[9] = v9; + coords[10] = v10; + coords[11] = v11; + + vertexBuffer.put(coords); + // set the buffer to read the first coordinate + vertexBuffer.position(0); + } + + public void setColor(float red, float green, float blue, float alpha) { + color[0] = red; + color[1] = green; + color[2] = blue; + color[3] = alpha; + } + + + + private final int mProgram; + + private final String vertexShaderCode = + "uniform mat4 uMVPMatrix;" + + "attribute vec4 vPosition;" + + "void main() {" + + " gl_Position = uMVPMatrix * vPosition;" + + "}"; + + // Use to access and set the view transformation + private int mMVPMatrixHandle; + + private final String fragmentShaderCode = + "precision mediump float;" + + "uniform vec4 vColor;" + + "void main() {" + + " gl_FragColor = vColor;" + + "}"; + + private int mPositionHandle; + private int mColorHandle; + + private final int vertexCount = coords.length / COORDS_PER_VERTEX; + private final int vertexStride = COORDS_PER_VERTEX * 4; + + private int loadShader(int type, String shaderCode){ + int shader = GLES20.glCreateShader(type); + GLES20.glShaderSource(shader, shaderCode); + GLES20.glCompileShader(shader); + return shader; + } + + public SquareRenderer(){ + // initialize vertex byte buffer for shape coordinates + ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * 4); + bb.order(ByteOrder.nativeOrder()); + vertexBuffer = bb.asFloatBuffer(); + vertexBuffer.put(coords); + vertexBuffer.position(0); + + // initialize byte buffer for the draw list + ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2); // 2 bytes per short + dlb.order(ByteOrder.nativeOrder()); + drawListBuffer = dlb.asShortBuffer(); + drawListBuffer.put(drawOrder); + drawListBuffer.position(0); + + + + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); + + // create empty OpenGL ES Program + mProgram = GLES20.glCreateProgram(); + + // add the shader to program + GLES20.glAttachShader(mProgram, vertexShader); + GLES20.glAttachShader(mProgram, fragmentShader); + + // create OpenGL ES program executables + GLES20.glLinkProgram(mProgram); + Matrix.setIdentityM(mModelMatrix, 0); + } + + // Temporary matrices allocated here to reduce number of allocations for each frame. + private float[] mModelMatrix = new float[16]; + private float[] mModelViewMatrix = new float[16]; + private float[] mModelViewProjectionMatrix = new float[16]; + final String TAG = "Rectangle"; + + public void draw(float[] cameraView, float[] cameraPerspective) { // pass in the calculated transformation matrix + + ShaderUtil.checkGLError(TAG, "Before draw"); + + // Build the ModelView and ModelViewProjection matrices + // for calculating object position and light. + Matrix.multiplyMM(mModelViewMatrix, 0, cameraView, 0, mModelMatrix, 0); + Matrix.multiplyMM(mModelViewProjectionMatrix, 0, cameraPerspective, 0, mModelViewMatrix, 0); + + // add program to OpenGL ES environment + GLES20.glUseProgram(mProgram); + + // get handle to vertex shader's vPosition member + mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); + + // enable a handle to the triangle vertices + GLES20.glEnableVertexAttribArray(mPositionHandle); + + // prepare the triangle coordinate data + GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, + GLES20.GL_FLOAT, false, + vertexStride, vertexBuffer); + + // get handle to fragment shader's vColor member + mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); + + // set color for drawing the triangle + GLES20.glUniform4fv(mColorHandle, 1, color, 0); + + // get handle to shape's transformation matrix + mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); + + // Pass the projection and view transformation to the shader + GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mModelViewProjectionMatrix, 0); + + GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, + GLES20.GL_UNSIGNED_SHORT, drawListBuffer); + + // Disable vertex array + GLES20.glDisableVertexAttribArray(mPositionHandle); + } +} diff --git a/app/src/main/java/drawar/AppSettings.java b/app/src/main/java/drawar/AppSettings.java new file mode 100644 index 0000000..9f91f82 --- /dev/null +++ b/app/src/main/java/drawar/AppSettings.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 drawar; + +import javax.vecmath.Vector3f; + +public class AppSettings { + private static final Vector3f color = new Vector3f(1f, 1f, 1f); + private static final float strokeDrawDistance = 0.125f; + private static final float minDistance = 0.000625f; + private static final float nearClip = 0.001f; + private static final float farClip = 100.0f; + + + public static float getStrokeDrawDistance() { + return strokeDrawDistance; + } + + public static Vector3f getColor() { + return color; + } + + public static float getMinDistance() { + return minDistance; + } + + public static float getNearClip(){ + return nearClip; + } + public static float getFarClip(){ + return farClip; + } + +} diff --git a/app/src/main/java/drawar/BiquadFilter.java b/app/src/main/java/drawar/BiquadFilter.java new file mode 100644 index 0000000..ec39de2 --- /dev/null +++ b/app/src/main/java/drawar/BiquadFilter.java @@ -0,0 +1,72 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 drawar; + +import javax.vecmath.Vector3f; + + +/** + * BiquadFilter is a object for easily lowpass filtering incomming values. + */ +public class BiquadFilter { + private Vector3f val = new Vector3f(); + + private BiquadFilterInstance[] inst = new BiquadFilterInstance[3]; + + BiquadFilter(double Fc){ + for(int i=0;i<3;i++){ + inst[i] = new BiquadFilterInstance(Fc); + } + } + + Vector3f update(Vector3f in){ + val.x = (float) inst[0].process(in.x); + val.y = (float) inst[1].process(in.y); + val.z = (float) inst[2].process(in.z); + return val; + } + + private class BiquadFilterInstance { + double a0, a1, a2, b1, b2; + double Fc=0.5,Q=0.707, peakGain=0.0; + double z1=0.0, z2=0.0; + + BiquadFilterInstance(double fc){ + Fc = fc; + calcBiquad(); + } + + double process(double in){ + double out = in * a0 + z1; + z1 = in * a1 + z2 - b1 * out; + z2 = in * a2 - b2 * out; + return out; + } + + void calcBiquad() { + double norm; + double K = Math.tan(Math.PI * Fc); + norm = 1 / (1 + K / Q + K * K); + a0 = K * K * norm; + a1 = 2 * a0; + a2 = a0; + b1 = 2 * (K * K - 1) * norm; + b2 = (1 - K / Q + K * K) * norm; + } + } +} + + diff --git a/app/src/main/java/drawar/DisplayRotationHelper.java b/app/src/main/java/drawar/DisplayRotationHelper.java new file mode 100755 index 0000000..de5a3b4 --- /dev/null +++ b/app/src/main/java/drawar/DisplayRotationHelper.java @@ -0,0 +1,106 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 drawar; + +import android.app.Activity; +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManager.DisplayListener; +import android.view.Display; +import android.view.WindowManager; + +import com.google.ar.core.Session; + +/** + * Helper to track the display rotations. In particular, the 180 degree rotations are not notified + * by the onSurfaceChanged() callback, and thus they require listening to the android display + * events. + */ +public class DisplayRotationHelper implements DisplayListener { + private boolean viewportChanged; + private int viewportWidth; + private int viewportHeight; + private final Context context; + private final Display display; + + /** + * Constructs the DisplayRotationHelper but does not register the listener yet. + * + * @param context the Android {@link Context}. + */ + public DisplayRotationHelper(Context context) { + this.context = context; + display = context.getSystemService(WindowManager.class).getDefaultDisplay(); + } + + + public void onResume() { + context.getSystemService(DisplayManager.class).registerDisplayListener(this, null); + } + + + public void onPause() { + context.getSystemService(DisplayManager.class).unregisterDisplayListener(this); + } + + /** + * Records a change in surface dimensions. This will be later used by {@link + * #updateSessionIfNeeded(Session)}. Should be called from {@link + * android.opengl.GLSurfaceView.Renderer + * #onSurfaceChanged(javax.microedition.khronos.opengles.GL10, int, int)}. + * + * @param width the updated width of the surface. + * @param height the updated height of the surface. + */ + public void onSurfaceChanged(int width, int height) { + viewportWidth = width; + viewportHeight = height; + viewportChanged = true; + } + + /** + * Updates the session display geometry if a change was posted either by {@link + * #onSurfaceChanged(int, int)} call or by {@link #onDisplayChanged(int)} system callback. This + * function should be called explicitly before each call to {@link Session#update()}. This + * function will also clear the 'pending update' (viewportChanged) flag. + * + * @param session the {@link Session} object to update if display geometry changed. + */ + public void updateSessionIfNeeded(Session session) { + if (viewportChanged) { + int displayRotation = display.getRotation(); + session.setDisplayGeometry(displayRotation, viewportWidth, viewportHeight); + viewportChanged = false; + } + } + + /** + * Returns the current rotation state of android display. Same as {@link Display#getRotation()}. + */ + public int getRotation() { + return display.getRotation(); + } + + @Override + public void onDisplayAdded(int displayId) {} + + @Override + public void onDisplayRemoved(int displayId) {} + + @Override + public void onDisplayChanged(int displayId) { + viewportChanged = true; + } +} diff --git a/app/src/main/java/drawar/DrawAR.java b/app/src/main/java/drawar/DrawAR.java new file mode 100755 index 0000000..22ec4a3 --- /dev/null +++ b/app/src/main/java/drawar/DrawAR.java @@ -0,0 +1,739 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * + * 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 drawar; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.opengl.Matrix; +import android.os.Bundle; +import android.support.design.widget.Snackbar; +import android.support.v4.view.GestureDetectorCompat; +import android.support.v7.app.AppCompatActivity; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.SeekBar; +import android.widget.Toast; + +import com.google.ar.core.ArCoreApk; +import com.google.ar.core.Camera; +import com.google.ar.core.Config; +import com.google.ar.core.Frame; +import com.google.ar.core.Session; +import com.google.ar.core.TrackingState; +import com.google.ar.core.exceptions.CameraNotAvailableException; +import com.google.ar.core.exceptions.UnavailableApkTooOldException; +import com.google.ar.core.exceptions.UnavailableArcoreNotInstalledException; +import com.google.ar.core.exceptions.UnavailableSdkTooOldException; +import com.google.ar.core.exceptions.UnavailableUserDeclinedInstallationException; + +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; +import javax.vecmath.Vector2f; +import javax.vecmath.Vector3f; + +import drawar.rendering.BackgroundRenderer; +import drawar.rendering.LineShaderRenderer; +import drawar.rendering.LineUtils; +import fr.geolabs.dev.mapmint4me.R; + + +/** + * This is a complex example that shows how to create an augmented reality (AR) application using + * the ARCore API. + */ + +public class DrawAR extends AppCompatActivity implements GLSurfaceView.Renderer, GestureDetector.OnGestureListener, + GestureDetector.OnDoubleTapListener{ + private static final String TAG = DrawAR.class.getSimpleName(); + + private GLSurfaceView mSurfaceView; + + private Config mDefaultConfig; + private Session mSession; + private BackgroundRenderer mBackgroundRenderer = new BackgroundRenderer(); + private LineShaderRenderer mLineShaderRenderer = new LineShaderRenderer(); + private Frame mFrame; + + private float[] projmtx = new float[16]; + private float[] viewmtx = new float[16]; + private float[] mZeroMatrix = new float[16]; + + private boolean mPaused = false; + + private float mScreenWidth = 0; + private float mScreenHeight = 0; + + private BiquadFilter biquadFilter; + private Vector3f mLastPoint; + private AtomicReference lastTouch = new AtomicReference<>(); + + private GestureDetectorCompat mDetector; + + private LinearLayout mSettingsUI; + private LinearLayout mButtonBar; + + private SeekBar mLineWidthBar; + private SeekBar mLineDistanceScaleBar; + private SeekBar mSmoothingBar; + + + private float mLineWidthMax = 0.33f; + private float mDistanceScale = 0.0f; + private float mLineSmoothing = 0.1f; + + private float[] mLastFramePosition; + + private AtomicBoolean bIsTracking = new AtomicBoolean(true); + private AtomicBoolean bReCenterView = new AtomicBoolean(false); + private AtomicBoolean bTouchDown = new AtomicBoolean(false); + private AtomicBoolean bClearDrawing = new AtomicBoolean(false); + private AtomicBoolean bLineParameters = new AtomicBoolean(false); + private AtomicBoolean bUndo = new AtomicBoolean(false); + private AtomicBoolean bNewStroke = new AtomicBoolean(false); + + private ArrayList> mStrokes; + + private DisplayRotationHelper mDisplayRotationHelper; + private Snackbar mMessageSnackbar; + + private boolean bInstallRequested; + + private TrackingState mState; + /** + * Setup the app when main activity is created + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final SharedPreferences sharedPref = this.getPreferences(Context.MODE_PRIVATE); + + setContentView(R.layout.act_layout); + + mSurfaceView = findViewById(R.id.surfaceview); + mSettingsUI = findViewById(R.id.strokeUI); + mButtonBar = findViewById(R.id.button_bar); + + // Settings seek bars + mLineDistanceScaleBar = findViewById(R.id.distanceScale); + mLineWidthBar = findViewById(R.id.lineWidth); + mSmoothingBar = findViewById(R.id.smoothingSeekBar); + + mLineDistanceScaleBar.setProgress(sharedPref.getInt("mLineDistanceScale", 1)); + mLineWidthBar.setProgress(sharedPref.getInt("mLineWidth", 10)); + mSmoothingBar.setProgress(sharedPref.getInt("mSmoothing", 50)); + + mDistanceScale = LineUtils.map((float) mLineDistanceScaleBar.getProgress(), 0, 100, 1, 200, true); + mLineWidthMax = LineUtils.map((float) mLineWidthBar.getProgress(), 0f, 100f, 0.1f, 5f, true); + mLineSmoothing = LineUtils.map((float) mSmoothingBar.getProgress(), 0, 100, 0.01f, 0.2f, true); + + SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() { + /** + * Listen for seekbar changes, and update the settings + */ + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + SharedPreferences.Editor editor = sharedPref.edit(); + + if (seekBar == mLineDistanceScaleBar) { + editor.putInt("mLineDistanceScale", progress); + mDistanceScale = LineUtils.map((float) progress, 0f, 100f, 1f, 200f, true); + } else if (seekBar == mLineWidthBar) { + editor.putInt("mLineWidth", progress); + mLineWidthMax = LineUtils.map((float) progress, 0f, 100f, 0.1f, 5f, true); + } else if (seekBar == mSmoothingBar) { + editor.putInt("mSmoothing", progress); + mLineSmoothing = LineUtils.map((float) progress, 0, 100, 0.01f, 0.2f, true); + } + mLineShaderRenderer.bNeedsUpdate.set(true); + + editor.apply(); + + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }; + + mLineDistanceScaleBar.setOnSeekBarChangeListener(seekBarChangeListener); + mLineWidthBar.setOnSeekBarChangeListener(seekBarChangeListener); + mSmoothingBar.setOnSeekBarChangeListener(seekBarChangeListener); + + // Hide the settings ui + mSettingsUI.setVisibility(View.GONE); + + mDisplayRotationHelper = new DisplayRotationHelper(/*context=*/ this); + // Reset the zero matrix + Matrix.setIdentityM(mZeroMatrix, 0); + + mLastPoint = new Vector3f(0, 0, 0); + + bInstallRequested = false; + + // Set up renderer. + mSurfaceView.setPreserveEGLContextOnPause(true); + mSurfaceView.setEGLContextClientVersion(2); + mSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending. + mSurfaceView.setRenderer(this); + mSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + + // Setup touch detector + mDetector = new GestureDetectorCompat(this, this); + mDetector.setOnDoubleTapListener(this); + mStrokes = new ArrayList<>(); + + + } + + + /** + * addStroke adds a new stroke to the scene + * + * @param touchPoint a 2D point in screen space and is projected into 3D world space + */ + private void addStroke(Vector2f touchPoint) { + Vector3f newPoint = LineUtils.GetWorldCoords(touchPoint, mScreenWidth, mScreenHeight, projmtx, viewmtx); + addStroke(newPoint); + } + + + /** + * addPoint adds a point to the current stroke + * + * @param touchPoint a 2D point in screen space and is projected into 3D world space + */ + private void addPoint(Vector2f touchPoint) { + Vector3f newPoint = LineUtils.GetWorldCoords(touchPoint, mScreenWidth, mScreenHeight, projmtx, viewmtx); + addPoint(newPoint); + } + + + /** + * addStroke creates a new stroke + * + * @param newPoint a 3D point in world space + */ + private void addStroke(Vector3f newPoint) { + biquadFilter = new BiquadFilter(mLineSmoothing); + for (int i = 0; i < 1500; i++) { + biquadFilter.update(newPoint); + } + Vector3f p = biquadFilter.update(newPoint); + mLastPoint = new Vector3f(p); + mStrokes.add(new ArrayList()); + mStrokes.get(mStrokes.size() - 1).add(mLastPoint); + } + + /** + * addPoint adds a point to the current stroke + * + * @param newPoint a 3D point in world space + */ + private void addPoint(Vector3f newPoint) { + if (LineUtils.distanceCheck(newPoint, mLastPoint)) { + Vector3f p = biquadFilter.update(newPoint); + mLastPoint = new Vector3f(p); + mStrokes.get(mStrokes.size() - 1).add(mLastPoint); + } + } + + + /** + * onResume part of the Android Activity Lifecycle + */ + @Override + protected void onResume() { + super.onResume(); + + if (mSession == null) { + Exception exception = null; + String message = null; + try { + switch (ArCoreApk.getInstance().requestInstall(this, !bInstallRequested)) { + case INSTALL_REQUESTED: + bInstallRequested = true; + return; + case INSTALLED: + break; + } + + // ARCore requires camera permissions to operate. If we did not yet obtain runtime + // permission on Android M and above, now is a good time to ask the user for it. + if (!PermissionHelper.hasCameraPermission(this)) { + PermissionHelper.requestCameraPermission(this); + return; + } + + mSession = new Session(/* context= */ this); + } catch (UnavailableArcoreNotInstalledException + | UnavailableUserDeclinedInstallationException e) { + message = "Please install ARCore"; + exception = e; + } catch (UnavailableApkTooOldException e) { + message = "Please update ARCore"; + exception = e; + } catch (UnavailableSdkTooOldException e) { + message = "Please update this app"; + exception = e; + } catch (Exception e) { + message = "This device does not support AR"; + exception = e; + } + + if (message != null) { + Log.e(TAG, "Exception creating session", exception); + return; + } + + // Create default config and check if supported. + Config config = new Config(mSession); + if (!mSession.isSupported(config)) { + Log.e(TAG, "Exception creating session Device Does Not Support ARCore", exception); + } + mSession.configure(config); + } + // Note that order matters - see the note in onPause(), the reverse applies here. + try { + mSession.resume(); + } catch (CameraNotAvailableException e) { + e.printStackTrace(); + } + mSurfaceView.onResume(); + mDisplayRotationHelper.onResume(); + mPaused = false; + } + + /** + * onPause part of the Android Activity Lifecycle + */ + @Override + public void onPause() { + super.onPause(); + // Note that the order matters - GLSurfaceView is paused first so that it does not try + // to query the session. If Session is paused before GLSurfaceView, GLSurfaceView may + // still call mSession.update() and get a SessionPausedException. + + if (mSession != null) { + mDisplayRotationHelper.onPause(); + mSurfaceView.onPause(); + mSession.pause(); + } + + mPaused = false; + + + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + mScreenHeight = displayMetrics.heightPixels; + mScreenWidth = displayMetrics.widthPixels; + } + + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) { + if (!PermissionHelper.hasCameraPermission(this)) { + Toast.makeText(this, + "Camera permission is needed to run this application", Toast.LENGTH_LONG).show(); + finish(); + } + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) { + // Standard Android full-screen functionality. + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } + + + /** + * Create renderers after the Surface is Created and on the GL Thread + */ + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + + if (mSession == null) { + return; + } + + GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + + // Create the texture and pass it to ARCore session to be filled during update(). + mBackgroundRenderer.createOnGlThread(/*context=*/this); + + try { + + mSession.setCameraTextureName(mBackgroundRenderer.getTextureId()); + mLineShaderRenderer.createOnGlThread(this); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + GLES20.glViewport(0, 0, width, height); + // Notify ARCore session that the view size changed so that the perspective matrix and + // the video background can be properly adjusted. + mDisplayRotationHelper.onSurfaceChanged(width, height); + mScreenWidth = width; + mScreenHeight = height; + } + + + /** + * update() is executed on the GL Thread. + * The method handles all operations that need to take place before drawing to the screen. + * The method : + * extracts the current projection matrix and view matrix from the AR Pose + * handles adding stroke and points to the data collections + * updates the ZeroMatrix and performs the matrix multiplication needed to re-center the drawing + * updates the Line Renderer with the current strokes, color, distance scale, line width etc + */ + private void update() { + + if (mSession == null) { + return; + } + + mDisplayRotationHelper.updateSessionIfNeeded(mSession); + + try { + + mSession.setCameraTextureName(mBackgroundRenderer.getTextureId()); + + mFrame = mSession.update(); + Camera camera = mFrame.getCamera(); + + mState = camera.getTrackingState(); + + // Update tracking states + if (mState == TrackingState.TRACKING && !bIsTracking.get()) { + bIsTracking.set(true); + } else if (mState== TrackingState.STOPPED && bIsTracking.get()) { + bIsTracking.set(false); + bTouchDown.set(false); + } + + // Get projection matrix. + camera.getProjectionMatrix(projmtx, 0, AppSettings.getNearClip(), AppSettings.getFarClip()); + camera.getViewMatrix(viewmtx, 0); + + float[] position = new float[3]; + camera.getPose().getTranslation(position, 0); + + // Check if camera has moved much, if thats the case, stop touchDown events + // (stop drawing lines abruptly through the air) + if (mLastFramePosition != null) { + Vector3f distance = new Vector3f(position[0], position[1], position[2]); + distance.sub(new Vector3f(mLastFramePosition[0], mLastFramePosition[1], mLastFramePosition[2])); + + if (distance.length() > 0.15) { + bTouchDown.set(false); + } + } + mLastFramePosition = position; + + // Multiply the zero matrix + Matrix.multiplyMM(viewmtx, 0, viewmtx, 0, mZeroMatrix, 0); + + + if (bNewStroke.get()) { + bNewStroke.set(false); + addStroke(lastTouch.get()); + mLineShaderRenderer.bNeedsUpdate.set(true); + } else if (bTouchDown.get()) { + addPoint(lastTouch.get()); + mLineShaderRenderer.bNeedsUpdate.set(true); + } + + if (bReCenterView.get()) { + bReCenterView.set(false); + mZeroMatrix = getCalibrationMatrix(); + } + + if (bClearDrawing.get()) { + bClearDrawing.set(false); + clearDrawing(); + mLineShaderRenderer.bNeedsUpdate.set(true); + } + + if (bUndo.get()) { + bUndo.set(false); + if (mStrokes.size() > 0) { + mStrokes.remove(mStrokes.size() - 1); + mLineShaderRenderer.bNeedsUpdate.set(true); + } + } + mLineShaderRenderer.setDrawDebug(bLineParameters.get()); + if (mLineShaderRenderer.bNeedsUpdate.get()) { + mLineShaderRenderer.setColor(AppSettings.getColor()); + mLineShaderRenderer.mDrawDistance = AppSettings.getStrokeDrawDistance(); + mLineShaderRenderer.setDistanceScale(mDistanceScale); + mLineShaderRenderer.setLineWidth(mLineWidthMax); + mLineShaderRenderer.clear(); + mLineShaderRenderer.updateStrokes(mStrokes); + mLineShaderRenderer.upload(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + + /** + * GL Thread Loop + * clears the Color Buffer and Depth Buffer, draws the current texture from the camera + * and draws the Line Renderer if ARCore is tracking the world around it + */ + @Override + public void onDrawFrame(GL10 gl) { + if (mPaused) return; + + update(); + + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + + if (mFrame == null) { + return; + } + + // Draw background. + mBackgroundRenderer.draw(mFrame); + + // Draw Lines + if (mFrame.getCamera().getTrackingState() == TrackingState.TRACKING) { + mLineShaderRenderer.draw(viewmtx, projmtx, mScreenWidth, mScreenHeight, AppSettings.getNearClip(), AppSettings.getFarClip()); + } + } + + + /** + * Get a matrix usable for zero calibration (only position and compass direction) + */ + public float[] getCalibrationMatrix() { + float[] t = new float[3]; + float[] m = new float[16]; + + mFrame.getCamera().getPose().getTranslation(t, 0); + float[] z = mFrame.getCamera().getPose().getZAxis(); + Vector3f zAxis = new Vector3f(z[0], z[1], z[2]); + zAxis.y = 0; + zAxis.normalize(); + + double rotate = Math.atan2(zAxis.x, zAxis.z); + + Matrix.setIdentityM(m, 0); + Matrix.translateM(m, 0, t[0], t[1], t[2]); + Matrix.rotateM(m, 0, (float) Math.toDegrees(rotate), 0, 1, 0); + return m; + } + + + /** + * Clears the Datacollection of Strokes and sets the Line Renderer to clear and update itself + * Designed to be executed on the GL Thread + */ + public void clearDrawing() { + mStrokes.clear(); + mLineShaderRenderer.clear(); + } + + + /** + * onClickUndo handles the touch input on the GUI and sets the AtomicBoolean bUndo to be true + * the actual undo functionality is executed in the GL Thread + */ + public void onClickUndo(View button) { + bUndo.set(true); + } + + /** + * onClickLineDebug toggles the Line Renderer's Debug View on and off. The line renderer will + * highlight the lines on the same depth plane to allow users to draw things more coherently + */ + public void onClickLineDebug(View button) { + bLineParameters.set(!bLineParameters.get()); + } + + + /** + * onClickSettings toggles showing and hiding the Line Width, Smoothing, and Debug View toggle + */ + public void onClickSettings(View button) { + ImageButton settingsButton = findViewById(R.id.settingsButton); + + if (mSettingsUI.getVisibility() == View.GONE) { + mSettingsUI.setVisibility(View.VISIBLE); + mLineDistanceScaleBar = findViewById(R.id.distanceScale); + mLineWidthBar = findViewById(R.id.lineWidth); + + settingsButton.setColorFilter(getResources().getColor(R.color.active)); + } else { + mSettingsUI.setVisibility(View.GONE); + settingsButton.setColorFilter(getResources().getColor(R.color.gray)); + } + } + + /** + * onClickClear handle showing an AlertDialog to clear the drawing + */ + public void onClickClear(View button) { + + AlertDialog.Builder builder = new AlertDialog.Builder(this) + .setMessage("Sure you want to clear?"); + + // Set up the buttons + builder.setPositiveButton("Clear ", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + bClearDrawing.set(true); + } + }); + builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + builder.show(); + } + + + /** + * onClickRecenter handles the touch input on the GUI and sets the AtomicBoolean bReCEnterView to be true + * the actual recenter functionality is executed on the GL Thread + */ + public void onClickRecenter(View button) { + bReCenterView.set(true); + } + + // ------- Touch events + + /** + * onTouchEvent handles saving the lastTouch screen position and setting bTouchDown and bNewStroke + * AtomicBooleans to trigger addPoint and addStroke on the GL Thread to be called + */ + @Override + public boolean onTouchEvent(MotionEvent tap) { + this.mDetector.onTouchEvent(tap); + + if (tap.getAction() == MotionEvent.ACTION_DOWN ) { + lastTouch.set(new Vector2f(tap.getX(), tap.getY())); + bTouchDown.set(true); + bNewStroke.set(true); + return true; + } else if (tap.getAction() == MotionEvent.ACTION_MOVE || tap.getAction() == MotionEvent.ACTION_POINTER_DOWN) { + lastTouch.set(new Vector2f(tap.getX(), tap.getY())); + bTouchDown.set(true); + return true; + } else if (tap.getAction() == MotionEvent.ACTION_UP || tap.getAction() == MotionEvent.ACTION_CANCEL) { + bTouchDown.set(false); + lastTouch.set(new Vector2f(tap.getX(), tap.getY())); + return true; + } + + return super.onTouchEvent(tap); + } + + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + return false; + } + + /** + * onDoubleTap shows and hides the Button Bar at the Top of the View + */ + @Override + public boolean onDoubleTap(MotionEvent e) { + if (mButtonBar.getVisibility() == View.GONE) { + mButtonBar.setVisibility(View.VISIBLE); + } else { + mButtonBar.setVisibility(View.GONE); + } + return false; + } + + @Override + public boolean onDoubleTapEvent(MotionEvent e) { + return false; + } + + @Override + public boolean onDown(MotionEvent e) { + return false; + } + + @Override + public void onShowPress(MotionEvent tap) { + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + return false; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + return false; + } + + @Override + public void onLongPress(MotionEvent e) { + + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + return false; + } + +} diff --git a/app/src/main/java/drawar/PermissionHelper.java b/app/src/main/java/drawar/PermissionHelper.java new file mode 100755 index 0000000..f234556 --- /dev/null +++ b/app/src/main/java/drawar/PermissionHelper.java @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 drawar; + +import android.Manifest; +import android.app.Activity; +import android.content.pm.PackageManager; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; + +/** + * Helper to ask camera permission. + */ +public class PermissionHelper { + private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA; + private static final int CAMERA_PERMISSION_CODE = 0; + + /** + * Check to see we have the necessary permissions for this app. + */ + public static boolean hasCameraPermission(Activity activity) { + return ContextCompat.checkSelfPermission(activity, CAMERA_PERMISSION) == + PackageManager.PERMISSION_GRANTED; + + } + + /** + * Check to see we have the necessary permissions for this app, and ask for them if we don't. + */ + public static void requestCameraPermission(Activity activity) { + ActivityCompat.requestPermissions(activity, new String[]{CAMERA_PERMISSION}, + CAMERA_PERMISSION_CODE); + } +} diff --git a/app/src/main/java/drawar/package-info.java b/app/src/main/java/drawar/package-info.java new file mode 100755 index 0000000..7fee246 --- /dev/null +++ b/app/src/main/java/drawar/package-info.java @@ -0,0 +1,17 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * + * 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 drawar; diff --git a/app/src/main/java/drawar/rendering/BackgroundRenderer.java b/app/src/main/java/drawar/rendering/BackgroundRenderer.java new file mode 100755 index 0000000..b8e07ba --- /dev/null +++ b/app/src/main/java/drawar/rendering/BackgroundRenderer.java @@ -0,0 +1,184 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 drawar.rendering; + +import android.content.Context; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; + +import com.google.ar.core.Frame; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import fr.geolabs.dev.mapmint4me.R; + +/** + * This class renders the AR background from camera feed. It creates and hosts the texture + * given to ARCore to be filled with the camera image. + */ +public class BackgroundRenderer { + private static final String TAG = BackgroundRenderer.class.getSimpleName(); + + private static final int COORDS_PER_VERTEX = 3; + private static final int TEXCOORDS_PER_VERTEX = 2; + private static final int FLOAT_SIZE = 4; + + private FloatBuffer mQuadVertices; + private FloatBuffer mQuadTexCoord; + private FloatBuffer mQuadTexCoordTransformed; + + private int mQuadProgram; + + private int mQuadPositionParam; + private int mQuadTexCoordParam; + private int mTextureId = -1; + private int mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; + public BackgroundRenderer() { + } + + /** + * @return + */ + public int getTextureId() { + return mTextureId; + } + + /** + * Allocates and initializes OpenGL resources needed by the background renderer. Must be + * called on the OpenGL thread, typically in + * {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)}. + * + * @param context Needed to access shader source. + */ + public void createOnGlThread(Context context) { + // Generate the background texture. + int textures[] = new int[1]; + GLES20.glGenTextures(1, textures, 0); + mTextureId = textures[0]; + GLES20.glBindTexture(mTextureTarget, mTextureId); + GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); + + int numVertices = 4; + if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) { + throw new RuntimeException("Unexpected number of vertices in BackgroundRenderer."); + } + + ByteBuffer bbVertices = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE); + bbVertices.order(ByteOrder.nativeOrder()); + mQuadVertices = bbVertices.asFloatBuffer(); + mQuadVertices.put(QUAD_COORDS); + mQuadVertices.position(0); + + ByteBuffer bbTexCoords = ByteBuffer.allocateDirect( + numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE); + bbTexCoords.order(ByteOrder.nativeOrder()); + mQuadTexCoord = bbTexCoords.asFloatBuffer(); + mQuadTexCoord.put(QUAD_TEXCOORDS); + mQuadTexCoord.position(0); + + ByteBuffer bbTexCoordsTransformed = ByteBuffer.allocateDirect( + numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE); + bbTexCoordsTransformed.order(ByteOrder.nativeOrder()); + mQuadTexCoordTransformed = bbTexCoordsTransformed.asFloatBuffer(); + + int vertexShader = ShaderUtil.loadGLShader(TAG, context, + GLES20.GL_VERTEX_SHADER, R.raw.screenquad_vertex); + int fragmentShader = ShaderUtil.loadGLShader(TAG, context, + GLES20.GL_FRAGMENT_SHADER, R.raw.screenquad_fragment_oes); + + mQuadProgram = GLES20.glCreateProgram(); + GLES20.glAttachShader(mQuadProgram, vertexShader); + GLES20.glAttachShader(mQuadProgram, fragmentShader); + GLES20.glLinkProgram(mQuadProgram); + GLES20.glUseProgram(mQuadProgram); + + ShaderUtil.checkGLError(TAG, "Program creation"); + + mQuadPositionParam = GLES20.glGetAttribLocation(mQuadProgram, "a_Position"); + mQuadTexCoordParam = GLES20.glGetAttribLocation(mQuadProgram, "a_TexCoord"); + + ShaderUtil.checkGLError(TAG, "Program parameters"); + } + + + public void draw(Frame frame) { + + if (frame == null) { + return; + } + + // If display rotation changed (also includes view size change), we need to re-query the uv + // coordinates for the screen rect, as they may have changed as well. + if (frame.hasDisplayGeometryChanged()) { + frame.transformDisplayUvCoords(mQuadTexCoord, mQuadTexCoordTransformed); + } + + // No need to test or write depth, the screen quad has arbitrary depth, and is expected + // to be drawn first. + GLES20.glDisable(GLES20.GL_DEPTH_TEST); + GLES20.glDepthMask(false); + + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId); + + GLES20.glUseProgram(mQuadProgram); + + // Set the vertex positions. + GLES20.glVertexAttribPointer( + mQuadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, mQuadVertices); + + // Set the texture coordinates. + GLES20.glVertexAttribPointer(mQuadTexCoordParam, TEXCOORDS_PER_VERTEX, + GLES20.GL_FLOAT, false, 0, mQuadTexCoordTransformed); + + // Enable vertex arrays + GLES20.glEnableVertexAttribArray(mQuadPositionParam); + GLES20.glEnableVertexAttribArray(mQuadTexCoordParam); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + // Disable vertex arrays + GLES20.glDisableVertexAttribArray(mQuadPositionParam); + GLES20.glDisableVertexAttribArray(mQuadTexCoordParam); + + // Restore the depth state for further drawing. + GLES20.glDepthMask(true); + GLES20.glEnable(GLES20.GL_DEPTH_TEST); + + ShaderUtil.checkGLError(TAG, "Draw"); + } + + public static final float[] QUAD_COORDS = new float[]{ + -1.0f, -1.0f, 0.0f, + -1.0f, +1.0f, 0.0f, + +1.0f, -1.0f, 0.0f, + +1.0f, +1.0f, 0.0f, + }; + + public static final float[] QUAD_TEXCOORDS = new float[]{ + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 1.0f, + 1.0f, 0.0f, + }; +} diff --git a/app/src/main/java/drawar/rendering/LineShaderRenderer.java b/app/src/main/java/drawar/rendering/LineShaderRenderer.java new file mode 100644 index 0000000..f4dddfe --- /dev/null +++ b/app/src/main/java/drawar/rendering/LineShaderRenderer.java @@ -0,0 +1,537 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 drawar.rendering; + + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.opengl.Matrix; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; +import javax.vecmath.Vector3f; + +import fr.geolabs.dev.mapmint4me.R; + + +/** + * Renders a point cloud. + */ +public class LineShaderRenderer { + + + private static final String TAG = LineShaderRenderer.class.getSimpleName(); + private static final int FLOATS_PER_POINT = 3; // X,Y,Z. + private static final int BYTES_PER_FLOAT = 4; + private static final int BYTES_PER_POINT = BYTES_PER_FLOAT * FLOATS_PER_POINT; + + private float[] mModelMatrix = new float[16]; + private float[] mModelViewMatrix = new float[16]; + private float[] mModelViewProjectionMatrix = new float[16]; + + + private int mPositionAttribute = 0; + private int mPreviousAttribute = 0; + private int mNextAttribute = 0; + private int mSideAttribute = 0; + private int mWidthAttribte = 0; + + private int mCountersAttribute = 0; + + private int mProjectionUniform = 0; + private int mModelViewUniform = 0; + private int mResolutionUniform = 0; + private int mLineWidthUniform = 0; + private int mColorUniform = 0; + private int mOpacityUniform = 0; + private int mNearUniform = 0; + private int mFarUniform = 0; + private int mSizeAttenuationUniform = 0; + private int mDrawModeUniform = 0; + private int mNearCutoffUniform = 0; + private int mFarCutoffUniform = 0; + + private boolean mDrawMode = false; + + private int mVisibility = 0; + private int mAlphaTest = 0; + + + private float[] mPositions; + private float[] mCounters; + private float[] mNext; + private float[] mSide; + private float[] mWidth; + private float[] mPrevious; + + private int mPositionAddress; + private int mPreviousAddress; + private int mNextAddress; + private int mSideAddress; + private int mWidthAddress; + private int mCounterAddress; + + + private int mNumPoints = 0; + private int mNumBytes = 0; + + private int mVbo = 0; + private int mVboSize = 0; + + private int mProgramName = 0; + private float lineWidth = 0; + + + private Vector3f mColor; + + + public AtomicBoolean bNeedsUpdate = new AtomicBoolean(); + + private int mLineDepthScaleUniform; + private float mLineDepthScale = 10.0f; + + public float mDrawDistance; + + public LineShaderRenderer() { + + } + + /** + * Allocates and initializes OpenGL resources needed by the Line renderer. Must be + * called on the OpenGL thread, typically in + * {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)}. + * + * @param context Needed to access shader source. + */ + public void createOnGlThread(Context context) { + ShaderUtil.checkGLError(TAG, "before create"); + + int buffers[] = new int[1]; + GLES20.glGenBuffers(1, buffers, 0); + mVbo = buffers[0]; + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVbo); + mVboSize = 0; + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mVboSize, null, GLES20.GL_DYNAMIC_DRAW); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + + ShaderUtil.checkGLError(TAG, "buffer alloc"); + + + /** + * + * The LineShaderRenderer uses an ES2 pipeline. It uses the line_vert.glsl and + * line_frag.glsl shader to render a volumetric line. It uses several techniques detailed in + * the following resources: + * + * Drawing Lines is Hard by Matt DesLauriers + * https://mattdesl.svbtle.com/drawing-lines-is-hard + * + * InkSpace an Android Experiment by Zach Lieberman + * https://experiments.withgoogle.com/android/ink-space + * https://github.com/ofZach/inkSpace + * + * THREEJS.MeshLine by Jaume Sanchez + * https://github.com/spite/THREE.MeshLine/blob/master/src/THREE.MeshLine.js + * + * + * The Renderer batches all of the geometry into a single VBO. This allows us to have a single + * draw call to render the geometry. We also optimize the application to only re-upload the + * geometry data when a new stroke or new points are added to the drawing. The renderer uses + * a technique detailed in the following link to create degenerate faces between the strokes + * to disconnect them from one another. + * https://developer.apple.com/library/content/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/TechniquesforWorkingwithVertexData/TechniquesforWorkingwithVertexData.html + * + */ + + int vertexShader = ShaderUtil.loadGLShader(TAG, context, + GLES20.GL_VERTEX_SHADER, R.raw.line_vert); + int fragmentShader = ShaderUtil.loadGLShader(TAG, context, + GLES20.GL_FRAGMENT_SHADER, R.raw.line_frag); + + + mProgramName = GLES20.glCreateProgram(); + GLES20.glAttachShader(mProgramName, vertexShader); + GLES20.glAttachShader(mProgramName, fragmentShader); + GLES20.glLinkProgram(mProgramName); + GLES20.glUseProgram(mProgramName); + + ShaderUtil.checkGLError(TAG, "program"); + + mPositionAttribute = GLES20.glGetAttribLocation(mProgramName, "position"); + mPreviousAttribute = GLES20.glGetAttribLocation(mProgramName, "previous"); + mNextAttribute = GLES20.glGetAttribLocation(mProgramName, "next"); + mSideAttribute = GLES20.glGetAttribLocation(mProgramName, "side"); + mWidthAttribte = GLES20.glGetAttribLocation(mProgramName, "width"); + mCountersAttribute = GLES20.glGetAttribLocation(mProgramName, "counters"); + mProjectionUniform = GLES20.glGetUniformLocation(mProgramName, "projectionMatrix"); + mModelViewUniform = GLES20.glGetUniformLocation(mProgramName, "modelViewMatrix"); + mResolutionUniform = GLES20.glGetUniformLocation(mProgramName, "resolution"); + mLineWidthUniform = GLES20.glGetUniformLocation(mProgramName, "lineWidth"); + mColorUniform = GLES20.glGetUniformLocation(mProgramName, "color"); + mOpacityUniform = GLES20.glGetUniformLocation(mProgramName, "opacity"); + mNearUniform = GLES20.glGetUniformLocation(mProgramName, "near"); + mFarUniform = GLES20.glGetUniformLocation(mProgramName, "far"); + mSizeAttenuationUniform = GLES20.glGetUniformLocation(mProgramName, "sizeAttenuation"); + mVisibility = GLES20.glGetUniformLocation(mProgramName, "visibility"); + mAlphaTest = GLES20.glGetUniformLocation(mProgramName, "alphaTest"); + mDrawModeUniform = GLES20.glGetUniformLocation(mProgramName, "drawMode"); + mNearCutoffUniform = GLES20.glGetUniformLocation(mProgramName, "nearCutOff"); + mFarCutoffUniform = GLES20.glGetUniformLocation(mProgramName, "farCutOff"); + mLineDepthScaleUniform = GLES20.glGetUniformLocation(mProgramName, "lineDepthScale"); + + ShaderUtil.checkGLError(TAG, "program params"); + + Matrix.setIdentityM(mModelMatrix, 0); + + mColor = new Vector3f(1f, 1f, 1f); + lineWidth = 0.5f; + } + + /** + * Sets the LineWidth of the Line. + * Requires bNeedsUpdate.set(true) to take effect + * @param width + */ + public void setLineWidth(float width){ + lineWidth = width; + } + + /** + * Enables or Disables the Debug View in the Fragment Shader. Debug View highlights the strokes + * at the same depth as the user. It allows the user to position new drawings to intersect or + * bypass the existing strokes + * @param drawDebugMode + */ + public void setDrawDebug(boolean drawDebugMode){ + mDrawMode = drawDebugMode; + } + + + /** + * This sets the color of the line by setting the color uniform in the shader. + * @param color a Vector3f representing R, G, B for the X, Y, Z values + */ + public void setColor(Vector3f color) { + mColor = new Vector3f(color); + } + + + /** + * This sets a feature in the vertex shader to scale the line width based on the distance away + * from the current view. + * @param distanceScale + */ + public void setDistanceScale(float distanceScale) { + this.mLineDepthScale = distanceScale; + } + + /** + * This updates the geometry data to be rendered. It ensures the capacity of the float arrays + * and then calls addLine to generate the geometry. + * @param strokes a ArrayList of ArrayLists of Vector3fs in world space. The outer ArrayList + * contains the strokes, while the inner ArrayList contains the Vertex of each Line + */ + public void updateStrokes(ArrayList> strokes) { + mNumPoints = 0; + for (ArrayList l : strokes) { + mNumPoints += l.size()*2 + 2; + } + + ensureCapacity(mNumPoints); + + int offset = 0; + for (ArrayList l : strokes) { + offset = addLine(l, offset); + } + mNumBytes = offset; + + } + + /** + * This ensures the capacity of the float arrays that hold the information bound to the Vertex + * Attributes needed to render the line with the Vertex and Fragment shader. + * @param numPoints + */ + private void ensureCapacity(int numPoints){ + int count = 1024; + if(mSide != null){ + count = mSide.length; + } + + while(count < numPoints){ + count += 1024; + } + + if(mSide == null || mSide.length < count) { + Log.i(TAG, "alloc "+count); + mPositions = new float[count*3]; + mNext = new float[count*3]; + mPrevious = new float[count*3]; + + mCounters = new float[count]; + mSide = new float[count]; + mWidth = new float[count]; + } + } + + /** + * AddLine takes in the 3D positions adds to the buffers to create the stroke and the degenerate + * faces needed so the lines render properly. + * @param line + * @param offset + * @return + */ + private int addLine(List line, int offset) { + if (line == null || line.size() < 2) + return offset; + + + + + int lineSize = line.size(); + + + int ii = offset; + for (int i = 0; i < lineSize; i++) { + + int iGood = i; + if (iGood < 0) iGood = 0; + if (iGood >= lineSize) iGood = lineSize - 1; + + int i_m_1 = (iGood - 1) < 0 ? iGood : iGood - 1; + int i_p_1 = (iGood + 1) > (lineSize - 1) ? iGood : iGood + 1; + float c = ((float) i / lineSize); + + + Vector3f current = line.get(iGood); + Vector3f previous = line.get(i_m_1); + Vector3f next = line.get(i_p_1); + + + + if (i == 0) { + setMemory(ii++, current, previous, next, c, lineWidth, 1f); + } + + setMemory(ii++, current, previous, next, c, lineWidth, 1f); + setMemory(ii++, current, previous, next, c, lineWidth, -1f); + + if (i == lineSize - 1) { + setMemory(ii++, current, previous, next, c, lineWidth, -1f); + } + } + return ii; + } + + /** + * + * setMemory is a helper method used to add the stroke data to the float[] buffers + * @param index + * @param pos + * @param prev + * @param next + * @param counter + * @param width + * @param side + */ + private void setMemory(int index, Vector3f pos, Vector3f prev, Vector3f next, float counter, float width, float side){ + mPositions[index*3] = pos.x; + mPositions[index*3+1] = pos.y; + mPositions[index*3+2] = pos.z; + + mNext[index*3] = next.x; + mNext[index*3+1] = next.y; + mNext[index*3+2] = next.z; + + mPrevious[index*3] = prev.x; + mPrevious[index*3+1] = prev.y; + mPrevious[index*3+2] = prev.z; + + mCounters[index] = counter; + mSide[index] = side; + mWidth[index] = width; + + } + + /** + * Sets the bNeedsUpdate to true. + */ + public void clear() { + bNeedsUpdate.set(true); + } + + + /** + * This takes the float[] and creates FloatBuffers, Binds the VBO, and upload the Attributes to + * correct locations with the correct offsets so the Vertex and Fragment shader can render the lines + */ + public void upload() { + bNeedsUpdate.set(false); + + FloatBuffer current = toFloatBuffer(mPositions); + FloatBuffer next = toFloatBuffer(mNext); + FloatBuffer previous = toFloatBuffer(mPrevious); + + FloatBuffer side = toFloatBuffer(mSide); + FloatBuffer width = toFloatBuffer(mWidth); + FloatBuffer counter = toFloatBuffer(mCounters); + + +// mNumPoints = mPositions.length; + + mPositionAddress = 0; + mNextAddress = mPositionAddress + mNumBytes * 3 * BYTES_PER_FLOAT; + mPreviousAddress = mNextAddress + mNumBytes * 3 * BYTES_PER_FLOAT; + mSideAddress = mPreviousAddress + mNumBytes *3 * BYTES_PER_FLOAT; + + mWidthAddress = mSideAddress + mNumBytes * BYTES_PER_FLOAT; + mCounterAddress = mWidthAddress + mNumBytes * BYTES_PER_FLOAT; + mVboSize = mCounterAddress + mNumBytes * BYTES_PER_FLOAT; + + ShaderUtil.checkGLError(TAG, "before update"); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVbo); + + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mVboSize, null, GLES20.GL_DYNAMIC_DRAW); + + GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, mPositionAddress, mNumBytes * 3 * BYTES_PER_FLOAT, + current); + GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, mNextAddress, mNumBytes * 3 * BYTES_PER_FLOAT, + next); + GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, mPreviousAddress, mNumBytes * 3 * BYTES_PER_FLOAT, + previous); + GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, mSideAddress, mNumBytes * BYTES_PER_FLOAT, + side); + GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, mWidthAddress, mNumBytes * BYTES_PER_FLOAT, + width); + GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, mCounterAddress, mNumBytes * BYTES_PER_FLOAT, + counter); + + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + ShaderUtil.checkGLError(TAG, "after update"); + } + + + /** + * + * This method takes in the current CameraView Matrix and the Camera's Projection Matrix, the + * current position and pose of the device, uses those to calculate the ModelViewMatrix and + * ModelViewProjectionMatrix. It binds the VBO, enables the custom attribute locations, + * binds and uploads the shader uniforms, calls our single DrawArray call, and finally disables + * and unbinds the shader attributes and VBO. + * + * @param cameraView + * @param cameraPerspective + * @param screenWidth + * @param screenHeight + * @param nearClip + * @param farClip + */ + public void draw(float[] cameraView, float[] cameraPerspective, float screenWidth, float screenHeight, float nearClip, float farClip) { + + + Matrix.multiplyMM(mModelViewMatrix, 0, cameraView, 0, mModelMatrix, 0); + Matrix.multiplyMM(mModelViewProjectionMatrix, 0, cameraPerspective, 0, mModelViewMatrix, 0); + + ShaderUtil.checkGLError(TAG, "Before draw"); + + GLES20.glUseProgram(mProgramName); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVbo); + GLES20.glVertexAttribPointer( + mPositionAttribute, FLOATS_PER_POINT, GLES20.GL_FLOAT, false, BYTES_PER_POINT, mPositionAddress); + GLES20.glVertexAttribPointer( + mPreviousAttribute, FLOATS_PER_POINT, GLES20.GL_FLOAT, false, BYTES_PER_POINT, mPreviousAddress); + GLES20.glVertexAttribPointer( + mNextAttribute, FLOATS_PER_POINT, GLES20.GL_FLOAT, false, BYTES_PER_POINT, mNextAddress); + GLES20.glVertexAttribPointer( + mSideAttribute, 1, GLES20.GL_FLOAT, false, BYTES_PER_FLOAT, mSideAddress); + GLES20.glVertexAttribPointer( + mWidthAttribte, 1, GLES20.GL_FLOAT, false, BYTES_PER_FLOAT, mWidthAddress); + GLES20.glVertexAttribPointer( + mCountersAttribute, 1, GLES20.GL_FLOAT, false, BYTES_PER_FLOAT, mCounterAddress); + GLES20.glUniformMatrix4fv( + mModelViewUniform, 1, false, mModelViewMatrix, 0); + GLES20.glUniformMatrix4fv( + mProjectionUniform, 1, false, cameraPerspective, 0); + + + GLES20.glUniform2f(mResolutionUniform, screenWidth, screenHeight); + GLES20.glUniform1f(mLineWidthUniform, 0.01f); + GLES20.glUniform3f(mColorUniform, mColor.x, mColor.y, mColor.z); + GLES20.glUniform1f(mOpacityUniform, 1.0f); + GLES20.glUniform1f(mNearUniform, nearClip); + GLES20.glUniform1f(mFarUniform, farClip); + GLES20.glUniform1f(mSizeAttenuationUniform, 1.0f); + GLES20.glUniform1f(mVisibility, 1.0f); + GLES20.glUniform1f(mAlphaTest, 1.0f); + GLES20.glUniform1f(mDrawModeUniform, mDrawMode?1.0f:0.0f); + GLES20.glUniform1f(mNearCutoffUniform, mDrawDistance - 0.0075f); + GLES20.glUniform1f(mFarCutoffUniform, mDrawDistance + 0.0075f); + GLES20.glUniform1f(mLineDepthScaleUniform, mLineDepthScale); + + GLES20.glEnableVertexAttribArray(mPositionAttribute); + GLES20.glEnableVertexAttribArray(mPreviousAttribute); + GLES20.glEnableVertexAttribArray(mNextAttribute); + GLES20.glEnableVertexAttribArray(mSideAttribute); + GLES20.glEnableVertexAttribArray(mWidthAttribte); + GLES20.glEnableVertexAttribArray(mCountersAttribute); + + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, mNumBytes); + + + GLES20.glDisableVertexAttribArray(mCountersAttribute); + GLES20.glDisableVertexAttribArray(mWidthAttribte); + GLES20.glDisableVertexAttribArray(mSideAttribute); + GLES20.glDisableVertexAttribArray(mNextAttribute); + GLES20.glDisableVertexAttribArray(mPreviousAttribute); + GLES20.glDisableVertexAttribArray(mPositionAttribute); + + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + ShaderUtil.checkGLError(TAG, "Draw"); + } + + + /** + * A helper function to allocate a FloatBuffer the size of our float[] and copy the float[] into + * the newly created FloatBuffer. + * @param data + * @return + */ + private FloatBuffer toFloatBuffer(float[] data) { + FloatBuffer buff; + ByteBuffer bb = ByteBuffer.allocateDirect(data.length * BYTES_PER_FLOAT); + bb.order(ByteOrder.nativeOrder()); + buff = bb.asFloatBuffer(); + buff.put(data); + buff.position(0); + return buff; + } + +} diff --git a/app/src/main/java/drawar/rendering/LineUtils.java b/app/src/main/java/drawar/rendering/LineUtils.java new file mode 100644 index 0000000..614c763 --- /dev/null +++ b/app/src/main/java/drawar/rendering/LineUtils.java @@ -0,0 +1,130 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 drawar.rendering; + +import android.opengl.Matrix; + +import javax.vecmath.Vector2f; +import javax.vecmath.Vector3f; + +import drawar.AppSettings; + +public class LineUtils { + + /** + * @param value + * @param inputMin + * @param inputMax + * @param outputMin + * @param outputMax + * @param clamp + * @return + */ + public static float map(float value, float inputMin, float inputMax, float outputMin, float outputMax, boolean clamp) { + float outVal = ((value - inputMin) / (inputMax - inputMin) * (outputMax - outputMin) + outputMin); + + if (clamp) { + if (outputMax < outputMin) { + if (outVal < outputMax) outVal = outputMax; + else if (outVal > outputMin) outVal = outputMin; + } else { + if (outVal > outputMax) outVal = outputMax; + else if (outVal < outputMin) outVal = outputMin; + } + } + return outVal; + } + + /** + * @param start + * @param stop + * @param amt + * @return + */ + public static float lerp(float start, float stop, float amt) { + return start + (stop - start) * amt; + } + + /** + * @param touchPoint + * @param screenWidth + * @param screenHeight + * @param projectionMatrix + * @param viewMatrix + * @return + */ + public static Vector3f GetWorldCoords(Vector2f touchPoint, float screenWidth, float screenHeight, float[] projectionMatrix, float[] viewMatrix) { + Ray touchRay = projectRay(touchPoint, screenWidth, screenHeight, projectionMatrix, viewMatrix); + touchRay.direction.scale(AppSettings.getStrokeDrawDistance()); + touchRay.origin.add(touchRay.direction); + return touchRay.origin; + } + + /** + * @param point + * @param viewportSize + * @param viewProjMtx + * @return + */ + public static Ray screenPointToRay(Vector2f point, Vector2f viewportSize, float[] viewProjMtx) { + point.y = viewportSize.y - point.y; + float x = point.x * 2.0F / viewportSize.x - 1.0F; + float y = point.y * 2.0F / viewportSize.y - 1.0F; + float[] farScreenPoint = new float[]{x, y, 1.0F, 1.0F}; + float[] nearScreenPoint = new float[]{x, y, -1.0F, 1.0F}; + float[] nearPlanePoint = new float[4]; + float[] farPlanePoint = new float[4]; + float[] invertedProjectionMatrix = new float[16]; + Matrix.setIdentityM(invertedProjectionMatrix, 0); + Matrix.invertM(invertedProjectionMatrix, 0, viewProjMtx, 0); + Matrix.multiplyMV(nearPlanePoint, 0, invertedProjectionMatrix, 0, nearScreenPoint, 0); + Matrix.multiplyMV(farPlanePoint, 0, invertedProjectionMatrix, 0, farScreenPoint, 0); + Vector3f direction = new Vector3f(farPlanePoint[0] / farPlanePoint[3], farPlanePoint[1] / farPlanePoint[3], farPlanePoint[2] / farPlanePoint[3]); + Vector3f origin = new Vector3f(new Vector3f(nearPlanePoint[0] / nearPlanePoint[3], nearPlanePoint[1] / nearPlanePoint[3], nearPlanePoint[2] / nearPlanePoint[3])); + direction.sub(origin); + direction.normalize(); + return new Ray(origin, direction); + } + + /** + * @param touchPoint + * @param screenWidth + * @param screenHeight + * @param projectionMatrix + * @param viewMatrix + * @return + */ + public static Ray projectRay(Vector2f touchPoint, float screenWidth, float screenHeight, float[] projectionMatrix, float[] viewMatrix) { + float[] viewProjMtx = new float[16]; + Matrix.multiplyMM(viewProjMtx, 0, projectionMatrix, 0, viewMatrix, 0); + return screenPointToRay(touchPoint, new Vector2f(screenWidth, screenHeight), viewProjMtx); + } + + + /** + * @param newPoint + * @param lastPoint + * @return + */ + public static boolean distanceCheck(Vector3f newPoint, Vector3f lastPoint) { + Vector3f temp = new Vector3f(); + temp.sub(newPoint, lastPoint); + if (temp.length() > AppSettings.getMinDistance()) { + return true; + } + return false; + } +} diff --git a/app/src/main/java/drawar/rendering/Ray.java b/app/src/main/java/drawar/rendering/Ray.java new file mode 100644 index 0000000..223c584 --- /dev/null +++ b/app/src/main/java/drawar/rendering/Ray.java @@ -0,0 +1,27 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 drawar.rendering; + +import javax.vecmath.Vector3f; + +public class Ray { + public final Vector3f origin; + public final Vector3f direction; + public Ray(Vector3f origin, Vector3f direction) { + this.origin = origin; + this.direction = direction; + } +} \ No newline at end of file diff --git a/app/src/main/java/drawar/rendering/ShaderUtil.java b/app/src/main/java/drawar/rendering/ShaderUtil.java new file mode 100755 index 0000000..0192731 --- /dev/null +++ b/app/src/main/java/drawar/rendering/ShaderUtil.java @@ -0,0 +1,97 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 drawar.rendering; + +import android.content.Context; +import android.opengl.GLES20; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * Shader helper functions. + */ +public class ShaderUtil { + /** + * Converts a raw text file, saved as a resource, into an OpenGL ES shader. + * + * @param type The type of shader we will be creating. + * @param resId The resource ID of the raw text file about to be turned into a shader. + * @return The shader object handler. + */ + public static int loadGLShader(String tag, Context context, int type, int resId) { + String code = readRawTextFile(context, resId); + int shader = GLES20.glCreateShader(type); + GLES20.glShaderSource(shader, code); + GLES20.glCompileShader(shader); + + // Get the compilation status. + final int[] compileStatus = new int[1]; + GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); + + // If the compilation failed, delete the shader. + if (compileStatus[0] == 0) { + Log.e(tag, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader)); + GLES20.glDeleteShader(shader); + shader = 0; + } + + if (shader == 0) { + throw new RuntimeException("Error creating shader."); + } + + return shader; + } + + /** + * Checks if we've had an error inside of OpenGL ES, and if so what that error is. + * + * @param label Label to report in case of error. + * @throws RuntimeException If an OpenGL error is detected. + */ + public static void checkGLError(String tag, String label) { + int error; + while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { + Log.e(tag, label + ": glError " + error); + throw new RuntimeException(label + ": glError " + error); + } + } + + /** + * Converts a raw text file into a string. + * + * @param resId The resource ID of the raw text file about to be turned into a shader. + * @return The context of the text file, or null in case of error. + */ + private static String readRawTextFile(Context context, int resId) { + InputStream inputStream = context.getResources().openRawResource(resId); + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + reader.close(); + return sb.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/app/src/main/java/drawar/rendering/package-info.java b/app/src/main/java/drawar/rendering/package-info.java new file mode 100755 index 0000000..08169a6 --- /dev/null +++ b/app/src/main/java/drawar/rendering/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * + * 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. + */ +/** + * This package contains classes that do the rendering for this example. + */ +package drawar.rendering; diff --git a/app/src/main/java/fr/geolabs/dev/mapmint4me/ARSimulation.java b/app/src/main/java/fr/geolabs/dev/mapmint4me/ARSimulation.java new file mode 100644 index 0000000..f9b1640 --- /dev/null +++ b/app/src/main/java/fr/geolabs/dev/mapmint4me/ARSimulation.java @@ -0,0 +1,6 @@ +package fr.geolabs.dev.mapmint4me; + +import android.app.Activity; + +public class ARSimulation extends Activity { +} diff --git a/app/src/main/java/fr/geolabs/dev/mapmint4me/CloudAnchor.java b/app/src/main/java/fr/geolabs/dev/mapmint4me/CloudAnchor.java new file mode 100644 index 0000000..6c73007 --- /dev/null +++ b/app/src/main/java/fr/geolabs/dev/mapmint4me/CloudAnchor.java @@ -0,0 +1,219 @@ +package fr.geolabs.dev.mapmint4me; + + +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.google.ar.core.Anchor; +import com.google.ar.sceneform.AnchorNode; +import com.google.ar.sceneform.rendering.ModelRenderable; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.database.ValueEventListener; + +public class CloudAnchor extends AppCompatActivity { + + private DatabaseReference mDatabase; + private DatabaseReference mref; + private DatabaseReference mload; + + EditText anchorid; + Button resolve; + private CustomArFragment arFragment; + + private enum AppAnchorState { + NONE, + HOSTING, + HOSTED + } + + private AppAnchorState appAnchorState = AppAnchorState.NONE; + + private Anchor anchor; + + private boolean isPlaced = false; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.cloudanchor); + mDatabase = FirebaseDatabase.getInstance().getReference(); + + mload=mDatabase.child("lastcode"); + + anchorid =(EditText)findViewById(R.id.editText); + resolve=(Button)findViewById(R.id.resolve); + + + arFragment = (CustomArFragment) getSupportFragmentManager().findFragmentById(R.id.fragment); + + arFragment.setOnTapArPlaneListener((hitResult, plane, motionEvent) -> { + + if (!isPlaced) { + + anchor = arFragment.getArSceneView().getSession().hostCloudAnchor(hitResult.createAnchor()); + + appAnchorState = AppAnchorState.HOSTING; + + + createModel(anchor); + + isPlaced = true; + + } + + + }); + + + arFragment.getArSceneView().getScene().addOnUpdateListener(frameTime -> { + + if (appAnchorState != AppAnchorState.HOSTING) + return; + + Anchor.CloudAnchorState cloudAnchorState = anchor.getCloudAnchorState(); + + if (cloudAnchorState.isError()) { + showToast(cloudAnchorState.toString()); + } else if (cloudAnchorState == Anchor.CloudAnchorState.SUCCESS) { + showToast("Hosting Anchor..... " ); + + appAnchorState = AppAnchorState.HOSTED; + String anchorId = anchor.getCloudAnchorId(); + + + + + + mload.addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + + String value = (String) dataSnapshot.getValue(); + + + mDatabase.child(value).setValue(anchorId); + + showToast("Anchor Hosted On Room Code : "+ value); + + int i = Integer.parseInt(value); + i=i+1; + mDatabase.child("lastcode").setValue(""+i); + + resolve.setEnabled(false); + anchorid.setEnabled(false); + + + + + + + + } + + @Override + public void onCancelled(DatabaseError databaseError) { + showToast("Firebase Error"); + + } + + + }); + + + + + + } + + }); + + } + + private void showToast(String toString) { + Toast.makeText(CloudAnchor.this, toString, Toast.LENGTH_SHORT).show(); + } + + private void createModel(Anchor anchor) { + + ModelRenderable.builder().setSource(this, Uri.parse("model.sfb")).build() + .thenAccept(modelRenderable -> placeModel(anchor, modelRenderable)); + + } + + private void placeModel(Anchor anchor, ModelRenderable modelRenderable) { + + AnchorNode anchorNode = new AnchorNode(anchor); + anchorNode.setRenderable(modelRenderable); + arFragment.getArSceneView().getScene().addChild(anchorNode); + } + + public void OnButtonClick(View v) + { + String code=anchorid.getText().toString(); + + mref = mDatabase.child(code); + + + mref.addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + + try { + String value = (String) dataSnapshot.getValue(); + + if(value.equals("")) + showToast("Room Code Unavalible"); + else + { + + + + showToast("Cloud Anchor Loaded:" + value); + Anchor resolvedAnchor = arFragment.getArSceneView().getSession().resolveCloudAnchor(value); + createModel(resolvedAnchor); + + resolve.setEnabled(false); + anchorid.setEnabled(false); + + } + } + catch (NullPointerException e) + { + + showToast("Enter Valid Anchor Room Code"); + } + + + + + } + + @Override + public void onCancelled(DatabaseError databaseError) { + showToast("Firebase Error"); + + } + + + }); + + + + + } + + + +} + + + diff --git a/app/src/main/java/fr/geolabs/dev/mapmint4me/CustomArFragment.java b/app/src/main/java/fr/geolabs/dev/mapmint4me/CustomArFragment.java new file mode 100644 index 0000000..234b76f --- /dev/null +++ b/app/src/main/java/fr/geolabs/dev/mapmint4me/CustomArFragment.java @@ -0,0 +1,24 @@ +package fr.geolabs.dev.mapmint4me; + +import com.google.ar.core.Config; +import com.google.ar.core.Session; +import com.google.ar.sceneform.ux.ArFragment; + +public class CustomArFragment extends ArFragment { + @Override + protected Config getSessionConfiguration(Session session) { + + Config config= new Config(session); + + config.setCloudAnchorMode(Config.CloudAnchorMode.ENABLED); + config.setFocusMode(Config.FocusMode.AUTO); + + session.configure(config); + + this.getArSceneView().setupSession(session); + + return config; + + + } +} diff --git a/app/src/main/java/fr/geolabs/dev/mapmint4me/MapMint4ME.java b/app/src/main/java/fr/geolabs/dev/mapmint4me/MapMint4ME.java index c81766d..1bc19fe 100644 --- a/app/src/main/java/fr/geolabs/dev/mapmint4me/MapMint4ME.java +++ b/app/src/main/java/fr/geolabs/dev/mapmint4me/MapMint4ME.java @@ -1,12 +1,10 @@ package fr.geolabs.dev.mapmint4me; -import android.*; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.app.NotificationChannel; import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.ContentValues; import android.content.Context; import android.content.Intent; @@ -15,8 +13,6 @@ import android.content.pm.ResolveInfo; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.hardware.GeomagneticField; import android.hardware.Sensor; import android.hardware.SensorEvent; @@ -24,42 +20,24 @@ import android.hardware.SensorManager; import android.location.Location; import android.location.LocationManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; import android.net.Uri; -import android.net.http.SslError; import android.os.Build; -import android.os.Environment; -import android.os.ParcelFileDescriptor; +import android.os.Bundle; +import android.os.Handler; import android.provider.MediaStore; -import android.provider.SyncStateContract; import android.support.v4.app.ActivityCompat; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.NotificationManagerCompat; import android.support.v4.content.ContextCompat; import android.support.v4.content.FileProvider; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; -import android.os.Handler; -import android.support.v7.widget.AlertDialogLayout; -import android.text.format.DateFormat; import android.util.Log; -import android.view.Gravity; import android.view.MotionEvent; import android.view.View; -import android.view.Window; import android.webkit.CookieManager; -import android.webkit.CookieSyncManager; import android.webkit.GeolocationPermissions; import android.webkit.JavascriptInterface; -import android.webkit.SslErrorHandler; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.Toast; import com.google.android.gms.appindexing.Action; @@ -73,7 +51,6 @@ import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -84,12 +61,8 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE; -import static java.lang.Thread.sleep; - +import armeasure.Armeasure_save; /** @@ -293,6 +266,16 @@ public boolean shouldOverrideUrlLoading(WebView webView, String webResourceReque } } + + + + + + + + + + private String channel_name="MapMint4ME"; private String channel_description="MapMint4ME channel used for notification"; public String CHANNEL_ID="MapMint4ME-11223344"; @@ -314,10 +297,27 @@ public void createNotificationChannel() { } } + public void launchWelcomeScreen() { startActivity(new Intent(getApplicationContext(), WelcomeScreen.class)); finish(); } + public void launchWelcomeScreen2() { + startActivity(new Intent(getApplicationContext(), com.hl3hl3.arcoremeasure.ArMeasureActivity.class)); + finish(); + } + public void launchWelcomeScreen3() { + startActivity(new Intent(getApplicationContext(), drawar.DrawAR.class)); + finish(); + } public void launchWelcomeScreen4() { + startActivity(new Intent(getApplicationContext(), CloudAnchor.class)); + finish(); + } + + // public void launchWelcomeScreen5() { + // startActivity(new Intent(getApplicationContext(), GPSActivity_sat.class)); + // finish(); + // } public boolean mInternetActivated=false; @@ -505,6 +505,23 @@ public void onAccuracyChanged(Sensor sensor, int accuracy) { // not in use } + public void launchWelcomeScreen7() { + startActivity(new Intent(getApplicationContext(), Satellite.GPSActivity.class)); + finish(); + + + } + + public void launchWelcomeScreen8() { + startActivity(new Intent(getApplicationContext(), Ar_Simulation.ARSimulation.class)); + finish(); + + } + public void launchWelcomeScreen9() { + startActivity(new Intent(getApplicationContext(), armeasure.Armeasure_save.class)); + finish(); + + } /** diff --git a/app/src/main/java/fr/geolabs/dev/mapmint4me/WebAppInterface.java b/app/src/main/java/fr/geolabs/dev/mapmint4me/WebAppInterface.java index 398d765..c168e9f 100644 --- a/app/src/main/java/fr/geolabs/dev/mapmint4me/WebAppInterface.java +++ b/app/src/main/java/fr/geolabs/dev/mapmint4me/WebAppInterface.java @@ -1,10 +1,8 @@ package fr.geolabs.dev.mapmint4me; -import android.*; import android.accounts.Account; import android.accounts.AccountManager; import android.annotation.TargetApi; -import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; @@ -41,25 +39,19 @@ import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; -import java.net.HttpCookie; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; -import java.util.Arrays; -import java.util.List; import java.util.Locale; import java.util.regex.Pattern; @@ -114,9 +106,9 @@ public void notify(String msg) { .setContentText(msg) .setStyle(new NotificationCompat.BigTextStyle().bigText(msg)) .setPriority(NotificationCompat.PRIORITY_DEFAULT); - // Set the intent that will fire when the user taps the notification - //.setContentIntent(pendingIntent); - //.setAutoCancel(true); + // Set the intent that will fire when the user taps the notification + //.setContentIntent(pendingIntent); + //.setAutoCancel(true); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mContext); currentId++; @@ -441,25 +433,25 @@ public String translate(String text) { /** * Set mTop to true/false from the web page - @JavascriptInterface - public String getOrientation() throws JSONException{ - try { - ((MapMint4ME)mContext).updateOrientationAngles(); - float[] res=((MapMint4ME)mContext).getOrientationAngles(); - Log.e("Error", "" + res.toString()); - JSONArray jsonArray = new JSONArray(); - for(int i=0;i<3;i++) - jsonArray.put(i,res[i]); - float[] res1=((MapMint4ME)mContext).getRotationMatrix(); - Log.e("Error", "" + res.toString()); - for(int i=0;i<3;i++) - jsonArray.put(i+3,res1[i]); - return jsonArray.toString(); - } catch (Exception e) { - Log.e("Error", "" + e.toString()); - return e.toString(); - } - }*/ + @JavascriptInterface + public String getOrientation() throws JSONException{ + try { + ((MapMint4ME)mContext).updateOrientationAngles(); + float[] res=((MapMint4ME)mContext).getOrientationAngles(); + Log.e("Error", "" + res.toString()); + JSONArray jsonArray = new JSONArray(); + for(int i=0;i<3;i++) + jsonArray.put(i,res[i]); + float[] res1=((MapMint4ME)mContext).getRotationMatrix(); + Log.e("Error", "" + res.toString()); + for(int i=0;i<3;i++) + jsonArray.put(i+3,res1[i]); + return jsonArray.toString(); + } catch (Exception e) { + Log.e("Error", "" + e.toString()); + return e.toString(); + } + }*/ private LocalDB db = null; @@ -570,11 +562,53 @@ public String downloadedFile() { } @JavascriptInterface - public void startWelcomeScreen(){ - ((MapMint4ME) mContext).launchWelcomeScreen(); - ((MapMint4ME) mContext).finish(); + + public void startWelcomeScreen(String s) { + + if (s.equals("scale")) { + + ((MapMint4ME) mContext).launchWelcomeScreen2(); + ((MapMint4ME) mContext).finish(); + } + if (s.equals("draw")) { + + ((MapMint4ME) mContext).launchWelcomeScreen3(); + ((MapMint4ME) mContext).finish(); + } + if (s.equals("cloud")) { + + ((MapMint4ME) mContext).launchWelcomeScreen4(); + ((MapMint4ME) mContext).finish(); + } + if (s.equals("sat_finder")) { + + ((MapMint4ME) mContext).launchWelcomeScreen7(); + ((MapMint4ME) mContext).finish(); + } + + if (s.equals("ARSimulation")) { + + ((MapMint4ME) mContext).launchWelcomeScreen8(); + ((MapMint4ME) mContext).finish(); + } + + if (s.equals("ARArea")) { + + ((MapMint4ME) mContext).launchWelcomeScreen9(); + ((MapMint4ME) mContext).finish(); + } + + + + if (s.equals("help")) { + ((MapMint4ME) mContext).launchWelcomeScreen(); + ((MapMint4ME) mContext).finish(); + } } + + + @JavascriptInterface public void keepScreenOn(){ ((MapMint4ME) mContext).runOnUiThread(new Runnable() { @@ -759,7 +793,7 @@ public String getBaseLayers() throws JSONException, IOException { public String getGNStatus() throws JSONException { JSONObject json = new JSONObject(); ConnectivityManager connectivityManager - = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); json.put("net",(activeNetworkInfo!=null&&activeNetworkInfo.isConnected())); try{ diff --git a/app/src/main/java/fr/geolabs/dev/mapmint4me/WelcomeScreen.java b/app/src/main/java/fr/geolabs/dev/mapmint4me/WelcomeScreen.java index 23e92d3..a382dcb 100644 --- a/app/src/main/java/fr/geolabs/dev/mapmint4me/WelcomeScreen.java +++ b/app/src/main/java/fr/geolabs/dev/mapmint4me/WelcomeScreen.java @@ -2,23 +2,19 @@ import android.annotation.SuppressLint; import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; - import android.content.Context; import android.content.Intent; +import android.os.Bundle; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.text.Html; import android.util.Log; import android.view.LayoutInflater; -import android.view.ViewGroup; import android.view.View; -import android.text.Html; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.Button; -import android.os.Bundle; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; import android.widget.LinearLayout; import android.widget.TextView; diff --git a/app/src/main/res/drawable/bg_btn.xml b/app/src/main/res/drawable/bg_btn.xml new file mode 100644 index 0000000..a72158f --- /dev/null +++ b/app/src/main/res/drawable/bg_btn.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/circle.xml b/app/src/main/res/drawable/circle.xml new file mode 100644 index 0000000..d3ed938 --- /dev/null +++ b/app/src/main/res/drawable/circle.xml @@ -0,0 +1,6 @@ + + + + + /> + diff --git a/app/src/main/res/drawable/circle2.xml b/app/src/main/res/drawable/circle2.xml new file mode 100644 index 0000000..3f51438 --- /dev/null +++ b/app/src/main/res/drawable/circle2.xml @@ -0,0 +1,6 @@ + + + + + /> + \ No newline at end of file diff --git a/app/src/main/res/drawable/draw.png b/app/src/main/res/drawable/draw.png new file mode 100755 index 0000000..374d91c Binary files /dev/null and b/app/src/main/res/drawable/draw.png differ diff --git a/app/src/main/res/drawable/ic_check_empty.png b/app/src/main/res/drawable/ic_check_empty.png new file mode 100644 index 0000000..2b9db14 Binary files /dev/null and b/app/src/main/res/drawable/ic_check_empty.png differ diff --git a/app/src/main/res/drawable/ic_check_pressed.png b/app/src/main/res/drawable/ic_check_pressed.png new file mode 100644 index 0000000..dd38cd8 Binary files /dev/null and b/app/src/main/res/drawable/ic_check_pressed.png differ diff --git a/app/src/main/res/drawable/ic_checked.png b/app/src/main/res/drawable/ic_checked.png new file mode 100644 index 0000000..e3ce43f Binary files /dev/null and b/app/src/main/res/drawable/ic_checked.png differ diff --git a/app/src/main/res/drawable/ic_cube.xml b/app/src/main/res/drawable/ic_cube.xml new file mode 100644 index 0000000..de64e59 --- /dev/null +++ b/app/src/main/res/drawable/ic_cube.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_cube_disable.png b/app/src/main/res/drawable/ic_cube_disable.png new file mode 100644 index 0000000..47ded7e Binary files /dev/null and b/app/src/main/res/drawable/ic_cube_disable.png differ diff --git a/app/src/main/res/drawable/ic_cube_green.png b/app/src/main/res/drawable/ic_cube_green.png new file mode 100644 index 0000000..93c637e Binary files /dev/null and b/app/src/main/res/drawable/ic_cube_green.png differ diff --git a/app/src/main/res/drawable/ic_cube_pink.png b/app/src/main/res/drawable/ic_cube_pink.png new file mode 100644 index 0000000..4f0650e Binary files /dev/null and b/app/src/main/res/drawable/ic_cube_pink.png differ diff --git a/app/src/main/res/drawable/ic_delete_black_24dp.xml b/app/src/main/res/drawable/ic_delete_black_24dp.xml new file mode 100644 index 0000000..39e64d6 --- /dev/null +++ b/app/src/main/res/drawable/ic_delete_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher.xml b/app/src/main/res/drawable/ic_launcher.xml new file mode 100644 index 0000000..9d69828 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_my_location_black_24dp.xml b/app/src/main/res/drawable/ic_my_location_black_24dp.xml new file mode 100644 index 0000000..07d6e46 --- /dev/null +++ b/app/src/main/res/drawable/ic_my_location_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings_black_24dp.xml b/app/src/main/res/drawable/ic_settings_black_24dp.xml new file mode 100644 index 0000000..ace746c --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_undo_black_24dp.xml b/app/src/main/res/drawable/ic_undo_black_24dp.xml new file mode 100644 index 0000000..5558e37 --- /dev/null +++ b/app/src/main/res/drawable/ic_undo_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/launch_screen.xml b/app/src/main/res/drawable/launch_screen.xml new file mode 100644 index 0000000..f77c2eb --- /dev/null +++ b/app/src/main/res/drawable/launch_screen.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/scale.jpg b/app/src/main/res/drawable/scale.jpg new file mode 100644 index 0000000..4a77860 Binary files /dev/null and b/app/src/main/res/drawable/scale.jpg differ diff --git a/app/src/main/res/drawable/scrollbar_vertical_thumb.xml b/app/src/main/res/drawable/scrollbar_vertical_thumb.xml new file mode 100644 index 0000000..aef8cd2 --- /dev/null +++ b/app/src/main/res/drawable/scrollbar_vertical_thumb.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/scrollbar_vertical_track.xml b/app/src/main/res/drawable/scrollbar_vertical_track.xml new file mode 100644 index 0000000..e98305c --- /dev/null +++ b/app/src/main/res/drawable/scrollbar_vertical_track.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_cb.xml b/app/src/main/res/drawable/selector_cb.xml new file mode 100644 index 0000000..f6a7731 --- /dev/null +++ b/app/src/main/res/drawable/selector_cb.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/act_layout.xml b/app/src/main/res/layout/act_layout.xml new file mode 100644 index 0000000..da03977 --- /dev/null +++ b/app/src/main/res/layout/act_layout.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_gps.xml b/app/src/main/res/layout/activity_gps.xml new file mode 100644 index 0000000..22e700d --- /dev/null +++ b/app/src/main/res/layout/activity_gps.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100755 index 0000000..b26d0ea --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/ar_simulation.xml b/app/src/main/res/layout/ar_simulation.xml new file mode 100644 index 0000000..72c5690 --- /dev/null +++ b/app/src/main/res/layout/ar_simulation.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + +