diff --git a/vta-android/app/build.gradle b/vta-android/app/build.gradle index b81292a..b259894 100644 --- a/vta-android/app/build.gradle +++ b/vta-android/app/build.gradle @@ -29,6 +29,7 @@ dependencies { implementation 'com.android.support:design:27.0.2' implementation 'com.android.support.constraint:constraint-layout:1.0.2' implementation 'com.android.volley:volley:1.1.0' + implementation 'com.google.code.gson:gson:2.8.2' implementation 'com.google.android.gms:play-services-maps:11.8.0' implementation 'com.google.android.gms:play-services-location:11.8.0' implementation 'com.google.android.gms:play-services-places:11.8.0' @@ -37,6 +38,7 @@ dependencies { implementation 'com.google.maps.android:android-maps-utils:0.5+' implementation 'org.tensorflow:tensorflow-android:+' implementation 'ch.hsr:geohash:1.3.0' + implementation 'org.jetbrains.anko:anko-common:0.9' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' diff --git a/vta-android/app/libs/wekaSTRIPPED.jar b/vta-android/app/libs/wekaSTRIPPED.jar deleted file mode 100644 index f151cc0..0000000 Binary files a/vta-android/app/libs/wekaSTRIPPED.jar and /dev/null differ diff --git a/vta-android/app/src/main/AndroidManifest.xml b/vta-android/app/src/main/AndroidManifest.xml index c2cea2d..21c7449 100644 --- a/vta-android/app/src/main/AndroidManifest.xml +++ b/vta-android/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ { spe.putString("email", emailStr) spe.putString("password", passwordStr) spe.apply() - wekaSync(sp) + sklearnSync(sp) // maybeSync(sp) } @@ -206,33 +208,33 @@ class LoginActivity : AppCompatActivity(), LoaderCallbacks { } } - private fun wekaSync(sp: SharedPreferences) { + private fun sklearnSync(sp: SharedPreferences) { val accessToken = sp.getString("id", null) - val wmtime = sp.getString("wmtime", null) + val smtime = sp.getString("smtime", null) val intent = Intent(this@LoginActivity, MapsActivity::class.java) val tz = TimeZone.getTimeZone("UTC") val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) sdf.timeZone = tz - apiController.getFileInfo("${WekaPredictor.model_name}.zip", accessToken){ response -> + apiController.getFileInfo("${ExtraTreesClassifier.model_name}.zip", accessToken){ response -> if(response == null) { Toast.makeText(applicationContext, getString(R.string.offline_alert), Toast.LENGTH_SHORT).show() this@LoginActivity.startActivity(intent) this@LoginActivity.finish() } else { - val newwmtime = response.getString("mtime") - if(wmtime == null) { + val newsmtime = response.getString("mtime") + if(smtime == null) { val spe = sp.edit() - spe.putString("wmtime", newwmtime) + spe.putString("smtime", newsmtime) spe.apply() - syncWekaAndLaunchApp(intent, accessToken) + syncSklearnAndLaunchApp(intent, accessToken) } else { - val date1 = sdf.parse(wmtime) - val date2 = sdf.parse(newwmtime) + val date1 = sdf.parse(smtime) + val date2 = sdf.parse(newsmtime) if (date1.before(date2)) { val spe = sp.edit() - spe.putString("wmtime", newwmtime) + spe.putString("smtime", newsmtime) spe.apply() - syncWekaAndLaunchApp(intent, accessToken) + syncSklearnAndLaunchApp(intent, accessToken) } else { this@LoginActivity.startActivity(intent) this@LoginActivity.finish() @@ -242,25 +244,53 @@ class LoginActivity : AppCompatActivity(), LoaderCallbacks { } } - private fun syncWekaAndLaunchApp(intent: Intent, accessToken: String) { + private fun syncSklearnAndLaunchApp(intent: Intent, accessToken: String) { sync_message.visibility = View.VISIBLE - apiController.downloadFile("${WekaPredictor.model_name}.zip", accessToken, Response.Listener { response -> + apiController.downloadFile("${ExtraTreesClassifier.model_name}.zip", accessToken, Response.Listener { response -> try { if (response != null) { val outputStream: FileOutputStream - val name = "${WekaPredictor.model_name}.zip" + val name = "${ExtraTreesClassifier.model_name}.zip" outputStream = openFileOutput(name, Context.MODE_PRIVATE) outputStream.write(response) outputStream.close() - Toast.makeText(this, "WEKA Download complete.", Toast.LENGTH_LONG).show() - val b = unpackZip("${filesDir.absolutePath}/", name) - Toast.makeText(this, "WEKA unzip: $b.", Toast.LENGTH_LONG).show() - sync_message.visibility = View.GONE - this@LoginActivity.startActivity(intent) - this@LoginActivity.finish() + Toast.makeText(this, "Model Download complete.", Toast.LENGTH_LONG).show() + doAsync { + val b = unpackZip("${filesDir.absolutePath}/", name) + uiThread { + Toast.makeText(applicationContext, "Model unzip: $b.", Toast.LENGTH_LONG).show() + } + } + apiController.downloadFile("${ExtraTreesClassifier.map_prefix}-map.zip", accessToken, Response.Listener {res -> + try { + if (res != null) { + val os: FileOutputStream + val zname = "${ExtraTreesClassifier.map_prefix}-map.zip" + os = openFileOutput(zname, Context.MODE_PRIVATE) + os.write(res) + os.close() + Toast.makeText(this, "Mapping Download complete.", Toast.LENGTH_LONG).show() + doAsync { + val zb = unpackZip("${filesDir.absolutePath}/", zname) + uiThread { + Toast.makeText(applicationContext, "Mapping unzip: $zb.", Toast.LENGTH_LONG).show() + } + } + sync_message.visibility = View.GONE + this@LoginActivity.startActivity(intent) + this@LoginActivity.finish() + } + } catch (e: Exception) { + Log.d("SYNC_ERROR", "UNABLE TO DOWNLOAD MAP FILE") + e.printStackTrace() + } + }, Response.ErrorListener { err -> + err.printStackTrace() + }) + } } catch (e: Exception) { - Log.d("SYNC_ERROR", "UNABLE TO DOWNLOAD WEKA FILE") + Log.d("SYNC_ERROR", "UNABLE TO DOWNLOAD MODEL FILE") e.printStackTrace() } }, Response.ErrorListener { error -> @@ -305,6 +335,12 @@ class LoginActivity : AppCompatActivity(), LoaderCallbacks { } zis.close() + val f = File(path+zipname) + if(f.delete()) { + Log.d("ZIP", "deleted zip") + } else { + Log.d("ZIP", "failed to delete zip") + } } catch (e: IOException) { e.printStackTrace() return false diff --git a/vta-android/app/src/main/java/com/beproject/group1/vta/activities/MapsActivity.kt b/vta-android/app/src/main/java/com/beproject/group1/vta/activities/MapsActivity.kt index 37a7367..7fe84bf 100644 --- a/vta-android/app/src/main/java/com/beproject/group1/vta/activities/MapsActivity.kt +++ b/vta-android/app/src/main/java/com/beproject/group1/vta/activities/MapsActivity.kt @@ -5,7 +5,6 @@ import android.Manifest.permission.ACCESS_FINE_LOCATION import android.content.Context import android.content.Intent import android.content.IntentSender -import android.content.SharedPreferences import android.content.pm.PackageManager import android.content.res.Resources import android.graphics.Color @@ -32,8 +31,8 @@ import com.beproject.group1.vta.R import com.beproject.group1.vta.VTAApplication import com.beproject.group1.vta.helpers.ETA import com.beproject.group1.vta.helpers.Geofence +import com.beproject.group1.vta.helpers.SklearnPredictor import com.beproject.group1.vta.helpers.TFPredictor -import com.beproject.group1.vta.helpers.WekaPredictor import com.google.android.gms.common.api.ApiException import com.google.android.gms.common.api.ResolvableApiException import com.google.android.gms.common.api.Status @@ -53,6 +52,8 @@ import com.google.maps.model.DirectionsResult import com.google.maps.model.DirectionsRoute import com.google.maps.model.GeocodingResult import com.google.maps.model.TravelMode +import org.jetbrains.anko.doAsync +import org.jetbrains.anko.uiThread import org.joda.time.DateTime import java.io.IOException import java.util.* @@ -93,6 +94,8 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene private var timeBar: Snackbar? = null + private lateinit var sklearnPredictor: SklearnPredictor + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_maps) @@ -173,6 +176,8 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene matrixI = FloatArray(9) matrixValues = FloatArray(3) + sklearnPredictor = SklearnPredictor(this) + my_location.setOnClickListener({_ -> if (mylocation != null) { @@ -353,7 +358,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene initMarker() } Handler().postDelayed({ - gmap.setPadding(0, map_bar_layout.height + 60, 0, 0) + gmap.setPadding(0,map_bar_layout.height + 60,0,logout.height+40) }, 100) } @@ -399,8 +404,9 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene private fun addPolyline(results: DirectionsResult, mMap: GoogleMap) { val c = Calendar.getInstance() - val tfPredictor = TFPredictor(this) +// val tfPredictor = TFPredictor(this) // val wekaPredictor = WekaPredictor(this) + Log.d("Total routes", ""+results.routes.size) for(i in 0 until route.size) { @@ -410,6 +416,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene } route.clear() val route_time = ArrayList() + val route_lengths = ArrayList() val routeInFence = ArrayList() for(i in 0 until results.routes.size) { val decodedPath = PolyUtil.decode(results.routes[i].overviewPolyline.encodedPath) @@ -423,60 +430,66 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene val t = ArrayList() routeInFence.add(true) var time: Double = 0.toDouble() - + var dist = 0.toDouble() for(j in 0 until decodedPath.size) { c.time = Date() c.add(Calendar.SECOND, time.toInt()) //mMap.addMarker(MarkerOptions().position(LatLng(decodedPath[i].latitude.toDouble(), decodedPath[i].longitude.toDouble()))) - val traffic = tfPredictor.predict( - decodedPath[j].latitude.toFloat(), - decodedPath[j].longitude.toFloat(), + val traffic = sklearnPredictor.predict( + decodedPath[j].latitude, + decodedPath[j].longitude, c.get(Calendar.DAY_OF_WEEK) - 1, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE)) - /*val traffic = wekaPredictor.predict( - decodedPath[j].latitude, - decodedPath[j].longitude, - (c.get(Calendar.DAY_OF_WEEK) - 1).toDouble(), - c.get(Calendar.HOUR_OF_DAY).toDouble(), - c.get(Calendar.MINUTE).toDouble())*/ + /*val traffic = tfPredictor.predict( + decodedPath[j].latitude.toFloat(), + decodedPath[j].longitude.toFloat(), + c.get(Calendar.DAY_OF_WEEK) - 1, + c.get(Calendar.HOUR_OF_DAY), + c.get(Calendar.MINUTE))*/ when (j) { 0 -> { - val location0 = midPoint(decodedPath[j].latitude, decodedPath[j].longitude, decodedPath[j+1].latitude, decodedPath[j+1].longitude) + val location0 = midPoint(decodedPath[j].latitude, decodedPath[j].longitude, decodedPath[j + 1].latitude, decodedPath[j + 1].longitude) val distance0 = ETA.distance(decodedPath[j].latitude, decodedPath[j].longitude, location0.latitude, location0.longitude) val speed0 = ETA.speed(traffic!!.toInt()) //Log.d("Distance 0 : " ,"" + distance0) time += distance0 / speed0 + dist += distance0 t.add(addSegment(traffic, decodedPath[j], location0)) } - decodedPath.size-1 -> { - val location1 = midPoint(decodedPath[j].latitude, decodedPath[j].longitude, decodedPath[j-1].latitude, decodedPath[j-1].longitude) + decodedPath.size - 1 -> { + val location1 = midPoint(decodedPath[j].latitude, decodedPath[j].longitude, decodedPath[j - 1].latitude, decodedPath[j - 1].longitude) val distance1 = ETA.distance(decodedPath[j].latitude, decodedPath[j].longitude, location1.latitude, location1.longitude) val speed1 = ETA.speed(traffic!!.toInt()) //Log.d("Distance 1 : " ,"" + distance1) time += distance1 / speed1 + dist += distance1 t.add(addSegment(traffic, location1, decodedPath[j])) } else -> { - val location2 = midPoint(decodedPath[j].latitude, decodedPath[j].longitude, decodedPath[j-1].latitude, decodedPath[j-1].longitude) + val location2 = midPoint(decodedPath[j].latitude, decodedPath[j].longitude, decodedPath[j - 1].latitude, decodedPath[j - 1].longitude) val distance2 = ETA.distance(decodedPath[j].latitude, decodedPath[j].longitude, location2.latitude, location2.longitude) val speed2 = ETA.speed(traffic!!.toInt()) //Log.d("Distance 2 : " ,"" + distance2) time += distance2 / speed2 + dist += distance2 t.add(addSegment(traffic, location2, decodedPath[j])) - val location3 = midPoint(decodedPath[j].latitude, decodedPath[j].longitude, decodedPath[j+1].latitude, decodedPath[j+1].longitude) + val location3 = midPoint(decodedPath[j].latitude, decodedPath[j].longitude, decodedPath[j + 1].latitude, decodedPath[j + 1].longitude) val distance3 = ETA.distance(decodedPath[j].latitude, decodedPath[j].longitude, location3.latitude, location3.longitude) val speed3 = ETA.speed(traffic.toInt()) //Log.d("Distance 3 : " ,"" + distance3) time += distance3 / speed3 + dist += distance3 t.add(addSegment(traffic, decodedPath[j], location3)) } } + } route.add(t) route_time.add(time) + route_lengths.add(dist) Log.d("Estimated Time",""+ timeConversion((time).toInt())) } else { @@ -487,7 +500,8 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene if(!route_time.isEmpty()) { x = route_time.indexOf(Collections.min(route_time)) Log.d("Best Route", "" + x) - timeBar = Snackbar.make(root_view, timeConversion(Collections.min(route_time).toInt()),Snackbar.LENGTH_INDEFINITE) + val barMsg = "${timeConversion(Collections.min(route_time).toInt())} (${"%.2f".toString().format(route_lengths[x]/1000)} km)" + timeBar = Snackbar.make(root_view, barMsg, Snackbar.LENGTH_INDEFINITE) timeBar!!.show() } for(j in 0 until route[x].size) { @@ -498,6 +512,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene val decodedPath = PolyUtil.decode(results.routes[i].overviewPolyline.encodedPath) val defaultPolylineOptions = PolylineOptions() .color(ContextCompat.getColor(applicationContext, R.color.routeInactive)) + .width(15f) val t = ArrayList() t.add(mMap.addPolyline(defaultPolylineOptions.addAll(decodedPath))) route.add(t) @@ -505,7 +520,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene } private fun addSegment(traffic: Long, source: LatLng, destination: LatLng): Polyline { - val polyLineOptions = PolylineOptions() + val polyLineOptions = PolylineOptions().width(15f) when (traffic) { 0L -> { polyLineOptions.add(LatLng(source.latitude, source.longitude)).color(ContextCompat.getColor(applicationContext, R.color.green)) @@ -536,7 +551,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene override fun onResult(result: DirectionsResult?) { runOnUiThread { addPolyline(result!!, gmap) - gmap.setPadding(0,map_bar_layout.height + 60,0,0) + gmap.setPadding(0,map_bar_layout.height + 60,0,logout.height+40) positionCamera(result.routes[0], gmap) } @@ -568,7 +583,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback, SensorEventListene val minutes:Int = seconds / 60 seconds %= 60 return when(hours) { - 0-> "$minutes mins" + 0-> if(minutes <= 1) "2 min" else "$minutes mins" 1 -> "$hours hr $minutes mins" else -> "$hours hrs $minutes mins" } diff --git a/vta-android/app/src/main/java/com/beproject/group1/vta/activities/SplashScreen.kt b/vta-android/app/src/main/java/com/beproject/group1/vta/activities/SplashScreen.kt index 7663875..895764e 100644 --- a/vta-android/app/src/main/java/com/beproject/group1/vta/activities/SplashScreen.kt +++ b/vta-android/app/src/main/java/com/beproject/group1/vta/activities/SplashScreen.kt @@ -13,15 +13,16 @@ import android.widget.Toast import com.android.volley.Response import com.beproject.group1.vta.VTAApplication import com.beproject.group1.vta.helpers.APIController +import com.beproject.group1.vta.helpers.ExtraTreesClassifier import com.beproject.group1.vta.helpers.TFPredictor.Companion.model_name import com.beproject.group1.vta.helpers.VolleyService -import com.beproject.group1.vta.helpers.WekaPredictor import kotlinx.android.synthetic.main.activity_splash_screen.* +import org.jetbrains.anko.doAsync +import org.jetbrains.anko.uiThread import org.json.JSONObject import java.io.* import java.text.SimpleDateFormat import java.util.* -import java.nio.file.Files.isDirectory import java.util.zip.ZipEntry import java.util.zip.ZipInputStream @@ -74,12 +75,12 @@ class SplashScreen : AppCompatActivity() { } spe.apply() // maybeSync() - wekaSync() + sklearnSync() } } } else { // maybeSync() - wekaSync() + sklearnSync() } } else { val intent = Intent(this@SplashScreen, LoginActivity::class.java) @@ -89,33 +90,33 @@ class SplashScreen : AppCompatActivity() { } } - private fun wekaSync() { + private fun sklearnSync() { val accessToken = sp.getString("id", null) - val wmtime = sp.getString("wmtime", null) + val smtime = sp.getString("smtime", null) val intent = Intent(this@SplashScreen, MapsActivity::class.java) val tz = TimeZone.getTimeZone("UTC") val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) sdf.timeZone = tz - apiController.getFileInfo("${WekaPredictor.model_name}.zip", accessToken){response -> + apiController.getFileInfo("${ExtraTreesClassifier.model_name}.zip", accessToken){response -> if(response == null) { Toast.makeText(applicationContext, getString(R.string.offline_alert), Toast.LENGTH_SHORT).show() this@SplashScreen.startActivity(intent) this@SplashScreen.finish() } else { - val newwmtime = response.getString("mtime") - if(wmtime == null) { + val newsmtime = response.getString("mtime") + if(smtime == null) { val spe = sp.edit() - spe.putString("wmtime", newwmtime) + spe.putString("smtime", newsmtime) spe.apply() - syncWekaAndLaunchApp(intent, accessToken) + syncSklearnAndLaunchApp(intent, accessToken) } else { - val date1 = sdf.parse(wmtime) - val date2 = sdf.parse(newwmtime) + val date1 = sdf.parse(smtime) + val date2 = sdf.parse(newsmtime) if (date1.before(date2)) { val spe = sp.edit() - spe.putString("wmtime", newwmtime) + spe.putString("smtime", newsmtime) spe.apply() - syncWekaAndLaunchApp(intent, accessToken) + syncSklearnAndLaunchApp(intent, accessToken) } else { this@SplashScreen.startActivity(intent) this@SplashScreen.finish() @@ -125,25 +126,53 @@ class SplashScreen : AppCompatActivity() { } } - private fun syncWekaAndLaunchApp(intent: Intent, accessToken: String) { + private fun syncSklearnAndLaunchApp(intent: Intent, accessToken: String) { splash_message.visibility = View.VISIBLE - apiController.downloadFile("${WekaPredictor.model_name}.zip", accessToken, Response.Listener {response -> + apiController.downloadFile("${ExtraTreesClassifier.model_name}.zip", accessToken, Response.Listener { response -> try { if (response != null) { val outputStream: FileOutputStream - val name = "${WekaPredictor.model_name}.zip" + val name = "${ExtraTreesClassifier.model_name}.zip" outputStream = openFileOutput(name, Context.MODE_PRIVATE) outputStream.write(response) outputStream.close() - Toast.makeText(this, "WEKA Download complete.", Toast.LENGTH_LONG).show() - val b = unpackZip("${filesDir.absolutePath}/", name) - Toast.makeText(this, "WEKA unzip: $b.", Toast.LENGTH_LONG).show() - splash_message.visibility = View.GONE - this@SplashScreen.startActivity(intent) - this@SplashScreen.finish() + Toast.makeText(this, "Model Download complete.", Toast.LENGTH_LONG).show() + doAsync { + val b = unpackZip("${filesDir.absolutePath}/", name) + uiThread { + Toast.makeText(applicationContext, "Model unzip: $b.", Toast.LENGTH_LONG).show() + } + } + apiController.downloadFile("${ExtraTreesClassifier.map_prefix}-map.zip", accessToken, Response.Listener {res -> + try { + if (res != null) { + val os: FileOutputStream + val zname = "${ExtraTreesClassifier.map_prefix}-map.zip" + os = openFileOutput(zname, Context.MODE_PRIVATE) + os.write(res) + os.close() + Toast.makeText(this, "Mapping Download complete.", Toast.LENGTH_LONG).show() + doAsync { + val zb = unpackZip("${filesDir.absolutePath}/", zname) + uiThread { + Toast.makeText(applicationContext, "Mapping unzip: $zb.", Toast.LENGTH_LONG).show() + } + } + splash_message.visibility = View.GONE + this@SplashScreen.startActivity(intent) + this@SplashScreen.finish() + } + } catch (e: Exception) { + Log.d("SYNC_ERROR", "UNABLE TO DOWNLOAD MAP FILE") + e.printStackTrace() + } + }, Response.ErrorListener { err -> + err.printStackTrace() + }) + } } catch (e: Exception) { - Log.d("SYNC_ERROR", "UNABLE TO DOWNLOAD WEKA FILE") + Log.d("SYNC_ERROR", "UNABLE TO DOWNLOAD MODEL FILE") e.printStackTrace() } }, Response.ErrorListener { error -> @@ -188,6 +217,12 @@ class SplashScreen : AppCompatActivity() { } zis.close() + val f = File(path+zipname) + if(f.delete()) { + Log.d("ZIP", "deleted zip") + } else { + Log.d("ZIP", "failed to delete zip") + } } catch (e: IOException) { e.printStackTrace() return false diff --git a/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/ETA.java b/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/ETA.java index 066868f..5c8ceaa 100644 --- a/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/ETA.java +++ b/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/ETA.java @@ -21,7 +21,7 @@ public static double speed(int mode) switch(mode) { case 0: - return 11.1111; //40kmph + return 8.3333; //30kmph case 1: return 5.5556; //20kmph case 2: diff --git a/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/ExtraTreesClassifier.java b/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/ExtraTreesClassifier.java new file mode 100644 index 0000000..6901714 --- /dev/null +++ b/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/ExtraTreesClassifier.java @@ -0,0 +1,69 @@ +package com.beproject.group1.vta.helpers; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.io.File; +import java.io.FileNotFoundException; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Scanner; + +/** + * Created by pavan on 8/3/18. + */ + +public class ExtraTreesClassifier { + public static String model_name = "v1-1-geohash-1"; + public static String map_prefix = "v1-1-geohash-1"; + private class Tree { + private int[] childrenLeft; + private int[] childrenRight; + private double[] thresholds; + private int[] indices; + private double[][] classes; + + private int predict (double[] features, int node) { + if (this.thresholds[node] != -2) { + if (features[this.indices[node]] <= this.thresholds[node]) { + return this.predict(features, this.childrenLeft[node]); + } else { + return this.predict(features, this.childrenRight[node]); + } + } + return ExtraTreesClassifier.findMax(this.classes[node]); + } + private int predict (double[] features) { + return this.predict(features, 0); + } + } + + private List forest; + private int nClasses; + private int nEstimators; + + public ExtraTreesClassifier (String file) throws FileNotFoundException { + String jsonStr = new Scanner(new File(file)).useDelimiter("\\Z").next(); + Gson gson = new Gson(); + Type listType = new TypeToken>(){}.getType(); + this.forest = gson.fromJson(jsonStr, listType); + this.nEstimators = this.forest.size(); + this.nClasses = this.forest.get(0).classes[0].length; + } + + private static int findMax(double[] nums) { + int index = 0; + for (int i = 0; i < nums.length; i++) { + index = nums[i] > nums[index] ? i : index; + } + return index; + } + + public int predict(double[] features) { + double[] classes = new double[this.nClasses]; + for (int i = 0; i < this.nEstimators; i++) { + classes[this.forest.get(i).predict(features, 0)]++; + } + return ExtraTreesClassifier.findMax(classes); + } +} diff --git a/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/SklearnPredictor.kt b/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/SklearnPredictor.kt new file mode 100644 index 0000000..6a861bb --- /dev/null +++ b/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/SklearnPredictor.kt @@ -0,0 +1,78 @@ +package com.beproject.group1.vta.helpers + +import android.app.Activity +import android.util.Log +import ch.hsr.geohash.GeoHash +import java.io.BufferedReader +import java.io.FileNotFoundException +import java.io.InputStreamReader +import java.util.* +import kotlin.collections.ArrayList + +/** + * Created by pavan on 8/3/18. + */ +class SklearnPredictor internal constructor(private val act: Activity) { + private val ranges: IntArray = intArrayOf(0,3,6,9,12,15,18,21,23) + private var cachedModel:String? = null + private var extraTreesClassifier: ExtraTreesClassifier? = null +// private val extraTreesClassifiers: ArrayList = ArrayList() + init { +// for (i in 1 until ranges.size) { +// val modelName = "${ExtraTreesClassifier.model_name}-${ranges[i-1]}${ranges[i]}.json" +// extraTreesClassifiers.add(ExtraTreesClassifier("${act.filesDir.absolutePath}/$modelName")) +// } + } + + fun predict(latitude: Double, longitude: Double, weekday: Int, hour: Int, minutes: Int): Long { + val geohash = GeoHash.geoHashStringWithCharacterPrecision(latitude, longitude, 7) + val mins = findNearestMin(minutes) + val sq_week = weekday*weekday + val sq_hour = hour*hour + val sq_mins = mins*mins + var geohash_label:Double = (0).toDouble() + var i:Int = 1 + for(r in ranges) { + if(r >= hour) break + i++ + } + if(i==9) i-- + val model = "${ExtraTreesClassifier.model_name}-${ranges[i-1]}${ranges[i]}.json" + val mapping = "${ExtraTreesClassifier.map_prefix}-${ranges[i-1]}${ranges[i]}-map.csv" + Log.d("SKLEARN", "Using $model model") + val fis = act.openFileInput(mapping) + val isr = InputStreamReader(fis) + val bufferedReader = BufferedReader(isr) + var line = bufferedReader.readLine() + var found = false + while (line != null) { + val coefs = line.split(",") + if(coefs[0].equals(geohash)) { + geohash_label = coefs[1].toDouble() + found = true + break + } + line = bufferedReader.readLine() + } + if(!found) { + Log.d("SKLEARN", "Default prediction 0") + return (0).toLong() + } + if(extraTreesClassifier == null) { + extraTreesClassifier = ExtraTreesClassifier("${act.filesDir.absolutePath}/$model") + cachedModel = String(model.toCharArray()) + } else if(cachedModel!! != model) { + extraTreesClassifier = ExtraTreesClassifier("${act.filesDir.absolutePath}/$model") + } + val features = doubleArrayOf(geohash_label, weekday.toDouble(), hour.toDouble(), mins.toDouble(), + sq_week.toDouble(), sq_hour.toDouble(), sq_mins.toDouble()) + val pred = extraTreesClassifier!!.predict(features).toLong() + Log.d("SKLEARN", "$pred") + return pred + } + + private fun findNearestMin(min: Int): Int { + val t:Int = min/15 + return t*15 + } +} \ No newline at end of file diff --git a/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/WekaPredictor.kt b/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/WekaPredictor.kt deleted file mode 100644 index 81adaa9..0000000 --- a/vta-android/app/src/main/java/com/beproject/group1/vta/helpers/WekaPredictor.kt +++ /dev/null @@ -1,73 +0,0 @@ -package com.beproject.group1.vta.helpers - -import android.app.Activity -import android.util.Log -import ch.hsr.geohash.GeoHash -import weka.classifiers.Classifier -import android.widget.Toast -import android.R.attr.label -import weka.core.* - - -/** - * Created by pavan on 5/3/18. - */ -class WekaPredictor internal constructor(act: Activity) { - private val classifiers: ArrayList = ArrayList() - companion object { - val model_name = "v1-1-geohash" - val ranges = intArrayOf(3,6,9,12,15,18,21,23) - } - init { - classifiers.add(SerializationHelper.read("${act.filesDir.absolutePath}/$model_name-03.model") as Classifier) - classifiers.add(SerializationHelper.read("${act.filesDir.absolutePath}/$model_name-36.model") as Classifier) - classifiers.add(SerializationHelper.read("${act.filesDir.absolutePath}/$model_name-69.model") as Classifier) - classifiers.add(SerializationHelper.read("${act.filesDir.absolutePath}/$model_name-912.model") as Classifier) - classifiers.add(SerializationHelper.read("${act.filesDir.absolutePath}/$model_name-1215.model") as Classifier) - classifiers.add(SerializationHelper.read("${act.filesDir.absolutePath}/$model_name-1518.model") as Classifier) - classifiers.add(SerializationHelper.read("${act.filesDir.absolutePath}/$model_name-1821.model") as Classifier) - classifiers.add(SerializationHelper.read("${act.filesDir.absolutePath}/$model_name-2123.model") as Classifier) - } - - fun predict(latitude: Double, longitude: Double, weekday: Double, hour: Double, minutes: Double): Long? { - val geohash = GeoHash.geoHashStringWithCharacterPrecision(latitude, longitude, 12) - val geohashAttr = Attribute("geohash", null as ArrayList?) - val weekdayAttr = Attribute("weekday") - val hourAttr = Attribute("hour") - val minutesAttr = Attribute("min") - val trafficAttr = Attribute("traffic") - val attrList: ArrayList = ArrayList() - attrList.add(geohashAttr) - attrList.add(weekdayAttr) - attrList.add(hourAttr) - attrList.add(minutesAttr) - attrList.add(trafficAttr) - val dataUnpredicted = Instances("TestInstances", attrList, 1) - // last feature is target variable - dataUnpredicted.setClassIndex(dataUnpredicted.numAttributes() - 1) - val newInstance = object : DenseInstance(dataUnpredicted.numAttributes()) { - init { - setValue(geohashAttr, geohash) - setValue(weekdayAttr, weekday) - setValue(hourAttr, hour) - setValue(minutesAttr, minutes) - } - } - // reference to dataset - newInstance.setDataset(dataUnpredicted) - var index = 0 - for(i in 0..ranges.size) { - if(hour.toInt() <= ranges[i]) { - index = i - break - } - } - try { - val result = classifiers[index].classifyInstance(newInstance) - return result.toLong() - } catch (e: Exception) { - e.printStackTrace() - } - return null - } -} \ No newline at end of file