Author: Adam Pioterek <adam.pioterek@protonmail.ch>
raw gtfs but still too slow
%!v(PANIC=String method: strings: negative Repeat count)
diff --git a/app/build.gradle b/app/build.gradle index 89b94b1a503c82e67b6c323dbb19478ae67285c2..d7b28bff18fb3049628861a5b2e676f896faa9a4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,20 +36,14 @@ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.google.code.gson:gson:2.8.1' implementation 'com.squareup.okhttp3:okhttp:3.8.1' - implementation 'de.siegmar:fastcsv:1.0.2' implementation 'com.github.ghost1372:Mzip-Android:0.4.0' + implementation 'net.sf.supercsv:super-csv:2.4.0' + implementation 'io.requery:sqlite-android:3.22.0' } repositories { maven { url "https://maven.google.com" } maven { url 'https://jitpack.io' } mavenCentral() } - -/*configurations { - all { - exclude module: 'httpclient' - exclude module: 'commons-logging' - } -}*/ apply plugin: 'kotlin-android-extensions' diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt index 6c743b8b0cc19a297ac05a122c8c6343ec37d00c..ad34903559b2740b8cda9f7e594660d7f5aa52ad 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt @@ -40,11 +40,15 @@ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_dash) + + println("view set") AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO) setSupportActionBar(toolbar) supportActionBar?.title = getString(R.string.merge_favourites) + println("getting stops") getStops() + println("stops got") prepareFavourites() diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt index 7e1461e083061f66984121d839e3641294f31de2..04da7ada55bdb13f87789f6fec5dccf0a11e118f 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt @@ -3,6 +3,7 @@ import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.content.Intent +import android.database.sqlite.SQLiteCantOpenDatabaseException import ml.adamsprogs.bimba.models.Timetable import java.io.FileNotFoundException @@ -17,7 +18,7 @@ if (timetable.isEmpty()) startActivity(Intent(this, NoDbActivity::class.java)) else startActivity(Intent(this, DashActivity::class.java)) - } catch(e: FileNotFoundException) { + } catch(e: Exception) { startActivity(Intent(this, NoDbActivity::class.java)) } finish() diff --git a/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableConverter.kt b/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableConverter.kt index c5062388b6ba89e806378e681282d2ea5e554e6f..eba5e37a707473e4c39b73e943c15e5ea10e44b0 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableConverter.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableConverter.kt @@ -1,44 +1,69 @@ package ml.adamsprogs.bimba.datasources +import android.content.ContentValues import android.content.Context -import android.database.sqlite.SQLiteDatabase +import io.requery.android.database.sqlite.SQLiteDatabase import java.io.File -import de.siegmar.fastcsv.reader.CsvRow -import de.siegmar.fastcsv.reader.CsvReader import ir.mahdi.mzip.zip.ZipArchive -import java.nio.charset.StandardCharsets +import org.supercsv.cellprocessor.ift.CellProcessor +import org.supercsv.prefs.CsvPreference +import org.supercsv.io.CsvMapReader +import org.supercsv.io.ICsvMapReader +import java.io.FileReader +import java.util.* - -class TimetableConverter(from: File, to: File, context: Context) { - private val db: SQLiteDatabase = SQLiteDatabase.openOrCreateDatabase(to, null) - +//todo faster csv: http://simpleflatmapper.org/0101-getting-started-csv.html +//todo faster csv: https://github.com/uniVocity/univocity-parsers +class TimetableConverter(from: File, context: Context) { init { + // println(Calendar.getInstance()) val target = File(context.filesDir, "gtfs_files") target.mkdir() ZipArchive.unzip(from.path, target.path, "") + /*println("tables…") createTables() + println(" done") + println("agency…") insertAgency(context) + println(" done") + println("calendar…") insertCalendar(context) + println(" done") + println("dates…") insertCalendarDates(context) + println(" done") + println("feed…") insertFeedInfo(context) + println(" done") + println("routes…") insertRoutes(context) + println(" done") + println("shapes…") insertShapes(context) + println(" done") + println("stops…") insertStops(context) + println(" done") + println("times…") insertStopTimes(context) + println(" done") + println("trips…") insertTrips(context) + println(" done") target.deleteRecursively() - } + println(Calendar.getInstance())*/ + } /* private fun createTables() { - db.rawQuery("create table agency(" + + db.execSQL("create table agency(" + "agency_id TEXT PRIMARY KEY," + "agency_name TEXT," + "agency_url TEXT," + "agency_timezone TEXT," + "agency_phone TEXT," + "agency_lang TEXT" + - ")", null) - db.rawQuery("create table calendar(" + + ")") + db.execSQL("create table calendar(" + "service_id TEXT PRIMARY KEY," + "monday INT," + "tuesday INT," + @@ -49,21 +74,21 @@ "saturday INT," + "sunday INT," + "start_date TEXT," + "end_date TEXT" + - ")", null) - db.rawQuery("create table calendar_dates(" + + ")") + db.execSQL("create table calendar_dates(" + "service_id TEXT," + "date TEXT," + "exception_type INT," + "FOREIGN KEY(service_id) REFERENCES calendar(service_id)" + - ")", null) - db.rawQuery("create table feed_info(" + + ")") + db.execSQL("create table feed_info(" + "feed_publisher_name TEXT PRIMARY KEY," + "feed_publisher_url TEXT," + "feed_lang TEXT," + "feed_start_date TEXT," + "feed_end_date TEXT" + - ")", null) - db.rawQuery("create table routes(" + + ")") + db.execSQL("create table routes(" + "route_id TEXT PRIMARY KEY," + "agency_id TEXT," + "route_short_name TEXT," + @@ -73,23 +98,23 @@ "route_type INT," + "route_color TEXT," + "route_text_color TEXT," + "FOREIGN KEY(agency_id) REFERENCES agency(agency_id)" + - ")", null) - db.rawQuery("create table shapes(" + - "shape_id TEXT PRIMARY KEY," + + ")") + db.execSQL("create table shapes(" + + "shape_id TEXT," + "shape_pt_lat DOUBLE," + "shape_pt_lon DOUBLE," + "shape_pt_sequence INT" + - ")", null) - db.rawQuery("create table stops" + + ")") + db.execSQL("create table stops(" + "stop_id TEXT PRIMARY KEY," + "stop_code TEXT," + "stop_name TEXT," + "stop_lat DOUBLE," + "stop_lon DOUBLE," + "zone_id TEXT" + - ")", null) - db.rawQuery("create table stop_times(" + - "trip_id TEXT PRIMARY KEY," + + ")") + db.execSQL("create table stop_times(" + + "trip_id TEXT," + "arrival_time TEXT," + "departure_time TEXT," + "stop_id TEXT," + @@ -98,8 +123,8 @@ "stop_headsign TEXT," + "pickup_type INT," + "drop_off_type INT," + "FOREIGN KEY(stop_id) REFERENCES stops(stop_id)" + - ")", null) - db.rawQuery("create table trips(" + + ")") + db.execSQL("create table trips(" + "route_id TEXT," + "service_id TEXT," + "trip_id TEXT PRIMARY KEY," + @@ -109,191 +134,286 @@ "shape_id TEXT," + "wheelchair_accessible INT," + "FOREIGN KEY(route_id) REFERENCES routes(route_id)," + "FOREIGN KEY(service_id) REFERENCES calendar(service_id)," + - "FOREIGN KEY(shape_id) REFERENCE shapes(shape_id)" + - ")", null) + "FOREIGN KEY(shape_id) REFERENCES shapes(shape_id)" + + ")") } private fun insertAgency(context: Context) { val file = File(context.filesDir, "gtfs_files/agency.txt") - val csvReader = CsvReader() - csvReader.setContainsHeader(true) + var mapReader: ICsvMapReader? = null + try { + db.beginTransaction() + mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) - csvReader.parse(file, StandardCharsets.UTF_8).use { - var row: CsvRow? = null - while ({ row = it.nextRow(); row }() != null) { - val id = row!!.getField("agency_id") - val name = row!!.getField("agency_name") - val url = row!!.getField("agency_url") - val timezone = row!!.getField("agency_timezone") - val phone = row!!.getField("agency_phone") - val lang = row!!.getField("agency_lang") - db.rawQuery("insert into agency values(?, ?, ?, ?, ?, ?)", - arrayOf(id, name, url, timezone, phone, lang)) + var customerMap: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ customerMap = mapReader.read(header, processors); customerMap }() != null) { + val values = ContentValues().apply { + put("agency_id", customerMap!!["agency_id"] as String) + put("agency_name", customerMap!!["agency_name"] as String) + put("agency_url", customerMap!!["agency_url"] as String) + put("agency_timezone", customerMap!!["agency_timezone"] as String) + put("agency_phone", customerMap!!["agency_phone"] as String) + put("agency_lang", customerMap!!["agency_lang"] as String) + } + db.insert("agency", null, values) + } + db.setTransactionSuccessful() + db.endTransaction() + } finally { + if (mapReader != null) { + mapReader.close() } } } private fun insertCalendar(context: Context) { val file = File(context.filesDir, "gtfs_files/calendar.txt") - val csvReader = CsvReader() - csvReader.setContainsHeader(true) + var mapReader: ICsvMapReader? = null + try { + db.beginTransaction() + mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) - csvReader.parse(file, StandardCharsets.UTF_8).use { - var row: CsvRow? = null - while ({ row = it.nextRow(); row }() != null) { - val serviceId = row!!.getField("service_id") - val monday = row!!.getField("monday") - val tuesday = row!!.getField("tuesday") - val wednesday = row!!.getField("wednesday") - val thursday = row!!.getField("thursday") - val friday = row!!.getField("friday") - val saturday = row!!.getField("saturday") - val sunday = row!!.getField("sunday") - val startDate = row!!.getField("start_date") - val endDate = row!!.getField("end_date") - db.rawQuery("insert into calendar values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - arrayOf(serviceId, monday, tuesday, wednesday, thursday, friday, saturday, - sunday, startDate, endDate)) + var customerMap: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ customerMap = mapReader.read(header, processors); customerMap }() != null) { + val values = ContentValues().apply { + put("service_id", customerMap!!["service_id"] as String) + put("monday", customerMap!!["monday"] as String) + put("tuesday", customerMap!!["tuesday"] as String) + put("wednesday", customerMap!!["wednesday"] as String) + put("thursday", customerMap!!["thursday"] as String) + put("friday", customerMap!!["friday"] as String) + put("saturday", customerMap!!["saturday"] as String) + put("sunday", customerMap!!["sunday"] as String) + put("start_date", customerMap!!["start_date"] as String) + put("end_date", customerMap!!["end_date"] as String) + } + + db.insert("calendar", null, values) + } + db.setTransactionSuccessful() + db.endTransaction() + } finally { + if (mapReader != null) { + mapReader.close() } } } private fun insertCalendarDates(context: Context) { val file = File(context.filesDir, "gtfs_files/calendar_dates.txt") - val csvReader = CsvReader() - csvReader.setContainsHeader(true) + + var mapReader: ICsvMapReader? = null + try { + db.beginTransaction() + mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) - csvReader.parse(file, StandardCharsets.UTF_8).use { - var row: CsvRow? = null - while ({ row = it.nextRow(); row }() != null) { - val serviceId = row!!.getField("service_id") - val date = row!!.getField("date") - val exceptionType = row!!.getField("exceptionType") - db.rawQuery("insert into calendar_dates values(?, ?, ?)", - arrayOf(serviceId, date, exceptionType)) + var customerMap: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ customerMap = mapReader.read(header, processors); customerMap }() != null) { + val values = ContentValues().apply { + put("service_id", customerMap!!["service_id"] as String) + put("date", customerMap!!["date"] as String) + put("exceptionType", customerMap!!["exceptionType"] as String) + } + db.insert("calendar_dates", null, values) + } + db.setTransactionSuccessful() + db.endTransaction() + } finally { + if (mapReader != null) { + mapReader.close() } } } private fun insertFeedInfo(context: Context) { val file = File(context.filesDir, "gtfs_files/feed_info.txt") - val csvReader = CsvReader() - csvReader.setContainsHeader(true) + var mapReader: ICsvMapReader? = null + try { + db.beginTransaction() + mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) - csvReader.parse(file, StandardCharsets.UTF_8).use { - var row: CsvRow? = null - while ({ row = it.nextRow(); row }() != null) { - val name = row!!.getField("feed_publisher_name") - val url = row!!.getField("feed_publisher_url") - val lang = row!!.getField("feed_lang") - val startDate = row!!.getField("feed_start_date") - val endDate = row!!.getField("feed_end_date") - db.rawQuery("insert into feed_info values(?, ?, ?, ?, ?)", - arrayOf(name, url, lang, startDate, endDate)) + var customerMap: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ customerMap = mapReader.read(header, processors); customerMap }() != null) { + val values = ContentValues().apply { + put("feed_publisher_name", customerMap!!["feed_publisher_name"] as String) + put("feed_publisher_url", customerMap!!["feed_publisher_url"] as String) + put("feed_lang", customerMap!!["feed_lang"] as String) + put("feed_start_date", customerMap!!["feed_start_date"] as String) + put("feed_end_date", customerMap!!["feed_end_date"] as String) + } + db.insert("feed_info", null, values) + } + db.setTransactionSuccessful() + db.endTransaction() + } finally { + if (mapReader != null) { + mapReader.close() } } } private fun insertRoutes(context: Context) { val file = File(context.filesDir, "gtfs_files/routes.txt") - val csvReader = CsvReader() - csvReader.setContainsHeader(true) + var mapReader: ICsvMapReader? = null + try { + db.beginTransaction() + mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) - csvReader.parse(file, StandardCharsets.UTF_8).use { - var row: CsvRow? = null - while ({ row = it.nextRow(); row }() != null) { - val id = row!!.getField("route_id") - val agencyId = row!!.getField("agency_id") - val shortName = row!!.getField("route_short_name") - val longName = row!!.getField("route_long_name") - val description = row!!.getField("route_desc") - val type = row!!.getField("route_type") - val colour = row!!.getField("route_color") - val textColour = row!!.getField("route_text_color") - db.rawQuery("insert into routes values(?, ?, ?, ?, ?, ?, ?, ?)", - arrayOf(id, agencyId, shortName, longName, description, type, colour, - textColour)) + var customerMap: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ customerMap = mapReader.read(header, processors); customerMap }() != null) { + val values = ContentValues().apply { + put("route_id", customerMap!!["route_id"] as String) + put("agency_id", customerMap!!["agency_id"] as String) + put("route_short_name", customerMap!!["route_short_name"] as String) + put("route_long_name", customerMap!!["route_long_name"] as String) + put("route_desc", customerMap!!["route_desc"] as String) + put("route_type", customerMap!!["route_type"] as String) + put("route_color", customerMap!!["route_color"] as String) + put("route_text_color", customerMap!!["route_text_color"] as String) + } + db.insert("routes", null, values) + } + db.setTransactionSuccessful() + db.endTransaction() + } finally { + if (mapReader != null) { + mapReader.close() } } } private fun insertShapes(context: Context) { val file = File(context.filesDir, "gtfs_files/shapes.txt") - val csvReader = CsvReader() - csvReader.setContainsHeader(true) + var mapReader: ICsvMapReader? = null + try { + db.beginTransaction() + mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) - csvReader.parse(file, StandardCharsets.UTF_8).use { - var row: CsvRow? = null - while ({ row = it.nextRow(); row }() != null) { - val id = row!!.getField("shape_id") - val latitude = row!!.getField("shape_pt_lat") - val longitude = row!!.getField("shape_pt_lon") - val sequence = row!!.getField("shape_pt_sequence") - db.rawQuery("insert into shapes values(?, ?, ?, ?, ?)", - arrayOf(id, latitude, longitude, sequence)) + var customerMap: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ customerMap = mapReader.read(header, processors); customerMap }() != null) { + val values = ContentValues().apply { + put("shape_id", customerMap!!["shape_id"] as String) + put("shape_pt_lat", customerMap!!["shape_pt_lat"] as String) + put("shape_pt_lon", customerMap!!["shape_pt_lon"] as String) + put("shape_pt_sequence", customerMap!!["shape_pt_sequence"] as String) + } + db.insert("shapes", null, values) + if (mapReader.rowNumber % 1000 == 0) + println(mapReader.rowNumber) + } + db.setTransactionSuccessful() + db.endTransaction() + } finally { + if (mapReader != null) { + mapReader.close() } } } private fun insertStops(context: Context) { val file = File(context.filesDir, "gtfs_files/stops.txt") - val csvReader = CsvReader() - csvReader.setContainsHeader(true) + var mapReader: ICsvMapReader? = null + try { + db.beginTransaction() + mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) - csvReader.parse(file, StandardCharsets.UTF_8).use { - var row: CsvRow? = null - while ({ row = it.nextRow(); row }() != null) { - val id = row!!.getField("stop_id") - val code = row!!.getField("stop_code") - val name = row!!.getField("stop_name") - val latitude = row!!.getField("stop_lat") - val longitude = row!!.getField("stop_lon") - val zone = row!!.getField("zone_id") - db.rawQuery("insert into stops values(?, ?, ?, ?, ?, ?)", - arrayOf(id, code, name, latitude, longitude, zone)) + var customerMap: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ customerMap = mapReader.read(header, processors); customerMap }() != null) { + val values = ContentValues().apply { + put("stop_id", customerMap!!["stop_id"] as String) + put("stop_code", customerMap!!["stop_code"] as String) + put("stop_name", customerMap!!["stop_name"] as String) + put("stop_lat", customerMap!!["stop_lat"] as String) + put("stop_lon", customerMap!!["stop_lon"] as String) + put("zone_id", customerMap!!["zone_id"] as String) + } + db.insert("stops", null, values) + } + db.setTransactionSuccessful() + db.endTransaction() + } finally { + if (mapReader != null) { + mapReader.close() } } } private fun insertStopTimes(context: Context) { val file = File(context.filesDir, "gtfs_files/stop_times.txt") - val csvReader = CsvReader() - csvReader.setContainsHeader(true) + var mapReader: ICsvMapReader? = null + try { + db.beginTransaction() + mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) - csvReader.parse(file, StandardCharsets.UTF_8).use { - var row: CsvRow? = null - while ({ row = it.nextRow(); row }() != null) { - val id = row!!.getField("trip_id") - val arrival = row!!.getField("arrival_time") - val departure = row!!.getField("departure_time") - val stop = row!!.getField("stop_id") - val sequence = row!!.getField("stop_sequence") - val headsign = row!!.getField("stop_headsign") - val pickup = row!!.getField("pickup_type") - val dropOff = row!!.getField("drop_off_type") - db.rawQuery("insert into stop_times values(?, ?, ?, ?, ?, ?, ?, ?)", - arrayOf(id, arrival, departure, stop, sequence, headsign, pickup, dropOff)) + var customerMap: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ customerMap = mapReader.read(header, processors); customerMap }() != null) { + val values = ContentValues().apply { + put("trip_id", customerMap!!["trip_id"] as String) + put("arrival_time", customerMap!!["arrival_time"] as String) + put("departure_time", customerMap!!["departure_time"] as String) + put("stop_id", customerMap!!["stop_id"] as String) + put("stop_sequence", customerMap!!["stop_sequence"] as String) + put("stop_headsign", customerMap!!["stop_headsign"] as String) + put("pickup_type", customerMap!!["pickup_type"] as String) + put("drop_off_type", customerMap!!["drop_off_type"] as String) + } + db.insert("stop_times", null, values) + if (mapReader.rowNumber % 10000 == 0) + println(mapReader.rowNumber) + } + db.setTransactionSuccessful() + db.endTransaction() + } finally { + if (mapReader != null) { + mapReader.close() } } } private fun insertTrips(context: Context) { val file = File(context.filesDir, "gtfs_files/trips.txt") - val csvReader = CsvReader() - csvReader.setContainsHeader(true) + var mapReader: ICsvMapReader? = null + try { + db.beginTransaction() + mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) - csvReader.parse(file, StandardCharsets.UTF_8).use { - var row: CsvRow? = null - while ({ row = it.nextRow(); row }() != null) { - val route = row!!.getField("route_id") - val service = row!!.getField("service_id") - val id = row!!.getField("trip_id") - val headsign = row!!.getField("headsign") - val direction = row!!.getField("direction") - val shape = row!!.getField("shape") - db.rawQuery("insert into trpis values(?, ?, ?, ?, ?, ?)", - arrayOf(route, service, id, headsign, direction, shape)) + var customerMap: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ customerMap = mapReader.read(header, processors); customerMap }() != null) { + val values = ContentValues().apply { + put("route_id", customerMap!!["route_id"] as String) + put("service_id", customerMap!!["service_id"] as String) + put("trip_id", customerMap!!["trip_id"] as String) + put("trip_headsign", customerMap!!["trip_headsign"] as String) + put("direction_id", customerMap!!["direction_id"] as String) + put("shape_id", customerMap!!["shape_id"] as String) + put("wheelchair_accessible", customerMap!!["wheelchair_accessible"] as String) + } + db.insert("trips", null, values) + } + db.setTransactionSuccessful() + db.endTransaction() + } finally { + if (mapReader != null) { + mapReader.close() } } - } + }*/ } \ No newline at end of file diff --git a/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt b/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt index e8b3232b47b94589d1a1a18fb68666998e7c695c..fc3101e6e7c0b3cc699dbc5f9715b08e3025afb9 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt @@ -12,6 +12,7 @@ import java.io.* import java.security.MessageDigest import android.app.NotificationManager import android.os.Build +import ir.mahdi.mzip.zip.ZipArchive import ml.adamsprogs.bimba.NetworkStateReceiver import ml.adamsprogs.bimba.NotificationChannels import ml.adamsprogs.bimba.R @@ -48,9 +49,11 @@ sendResult(RESULT_NO_CONNECTIVITY) return } val lastModified = httpCon.getHeaderField("Content-Disposition").split("=")[1].trim('\"').split("_")[0] - //todo size + size = httpCon.getHeaderField("Content-Length").toInt() / 1024 + + val force = intent.getBooleanExtra(EXTRA_FORCE, false) val currentLastModified = prefs.getString("timetableLastModified", "19791012") - if (lastModified <= currentLastModified && lastModified <= today()) { + if (lastModified <= currentLastModified && lastModified <= today() && !force) { sendResult(RESULT_UP_TO_DATE) return } @@ -58,19 +61,25 @@ notifyDownloading(0) val gtfs = File(this.filesDir, "timetable.zip") - val db = File(this.filesDir, "timetable.db") + //val db = File(this.filesDir, "timetable.db") copyInputStreamToFile(httpCon.inputStream, gtfs) val prefsEditor = prefs.edit() prefsEditor.putString("timetableLastModified", lastModified) prefsEditor.apply() sendResult(RESULT_DOWNLOADED) - notifyConverting() + //notifyConverting() //fixme - db.delete() - TimetableConverter(gtfs, File(this.filesDir, "timetable.db"), this) + //db.delete() + val target = File(this.filesDir, "gtfs_files") + target.deleteRecursively() + println("deleted") + target.mkdir() + ZipArchive.unzip(gtfs.path, target.path, "") + println("unzipped") gtfs.delete() - Timetable.getTimetable().refresh(this) + Timetable.getTimetable().refresh() + println("refreshed") cancelNotification() @@ -81,7 +90,7 @@ private fun today(): String { val cal = Calendar.getInstance() val d = cal[Calendar.DAY_OF_MONTH] - val m = cal[Calendar.MONTH]+1 + val m = cal[Calendar.MONTH] + 1 val y = cal[Calendar.YEAR] return "%d%02d%02d".format(y, m, d) diff --git a/app/src/main/java/ml/adamsprogs/bimba/gtfs/AgencyAndId.kt b/app/src/main/java/ml/adamsprogs/bimba/gtfs/AgencyAndId.kt index 88fba9e0999f624b6522e9f8d306e5c8aa556b86..f54736463c26017c29e5aff321a69dbd3b0ae99a 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/gtfs/AgencyAndId.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/gtfs/AgencyAndId.kt @@ -2,7 +2,11 @@ package ml.adamsprogs.bimba.gtfs import java.io.Serializable -data class AgencyAndId(val id: String):Serializable { +data class AgencyAndId(val id: String) : Serializable, Comparable<AgencyAndId> { + override fun compareTo(other: AgencyAndId): Int { + return this.toString().compareTo(other.toString()) + } + companion object { fun convertFromString(str: String): AgencyAndId { return AgencyAndId(str) diff --git a/app/src/main/java/ml/adamsprogs/bimba/gtfs/Calendar.kt b/app/src/main/java/ml/adamsprogs/bimba/gtfs/Calendar.kt index 4f9db75a45aefaf1944c5a6c4c1c00af3a543431..f1a7cd31e0c9b143e3d887a60718e5df0aec5ce8 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/gtfs/Calendar.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/gtfs/Calendar.kt @@ -1,4 +1,4 @@ package ml.adamsprogs.bimba.gtfs -data class Calendar(val monday: Int, val tuesday: Int, val wednesday: Int, val thursday: Int, - val friday: Int, val saturday: Int, val sunday: Int) \ No newline at end of file +data class Calendar(val monday: Boolean, val tuesday: Boolean, val wednesday: Boolean, val thursday: Boolean, + val friday: Boolean, val saturday: Boolean, val sunday: Boolean) \ No newline at end of file diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/StopSuggestion.kt b/app/src/main/java/ml/adamsprogs/bimba/models/StopSuggestion.kt index 1d2a383aa7b762814fe0db07a1f63373aee78389..3746e8ea1a2f6acc7c16f517a21f0eace3eb419a 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/StopSuggestion.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/StopSuggestion.kt @@ -5,7 +5,7 @@ import android.os.Parcelable import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion import ml.adamsprogs.bimba.gtfs.AgencyAndId -class StopSuggestion(private val directions: HashSet<String>, val id: AgencyAndId) : SearchSuggestion { +class StopSuggestion(private val directions: Set<String>, val id: AgencyAndId) : SearchSuggestion { @Suppress("UNCHECKED_CAST") constructor(parcel: Parcel) : this(parcel.readSerializable() as HashSet<String>, parcel.readSerializable() as AgencyAndId) @@ -15,7 +15,7 @@ return Parcelable.CONTENTS_FILE_DESCRIPTOR } override fun writeToParcel(dest: Parcel?, flags: Int) { - dest?.writeSerializable(directions) + dest?.writeSerializable(directions as HashSet) dest?.writeSerializable(id) } diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt index 130b24089a12bbd0c001049115e4a197a97ddfcb..8627e2102c2f0ee359421cf9229e9d366deab2c7 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt @@ -1,32 +1,35 @@ package ml.adamsprogs.bimba.models import android.content.Context -import android.database.sqlite.SQLiteDatabase import ml.adamsprogs.bimba.datasources.CacheManager import ml.adamsprogs.bimba.gtfs.AgencyAndId import ml.adamsprogs.bimba.gtfs.Route import ml.adamsprogs.bimba.gtfs.Trip import ml.adamsprogs.bimba.gtfs.Calendar import ml.adamsprogs.bimba.secondsAfterMidnight -import ml.adamsprogs.bimba.toPascalCase +import org.supercsv.cellprocessor.ift.CellProcessor +import org.supercsv.io.CsvMapReader +import org.supercsv.prefs.CsvPreference import java.io.File +import java.io.FileReader import kotlin.collections.ArrayList import kotlin.collections.HashMap import kotlin.collections.HashSet import java.util.Calendar as JCalendar +//todo faster csv: http://simpleflatmapper.org/0101-getting-started-csv.html +//todo faster csv: https://github.com/uniVocity/univocity-parsers +//todo prolly need to write own simple and fast parser class Timetable private constructor() { companion object { - const val MODE_WORKDAYS = "workdays" - const val MODE_SATURDAYS = "saturdays" - const val MODE_SUNDAYS = "sundays" private var timetable: Timetable? = null fun getTimetable(context: Context? = null, force: Boolean = false): Timetable { return if (timetable == null || force) if (context != null) { timetable = Timetable() - timetable!!.store = read(context) + //timetable!!.store = read(context) + timetable!!.filesDir = context.filesDir timetable!!.cacheManager = CacheManager.getCacheManager(context) timetable as Timetable } else @@ -35,18 +38,19 @@ else timetable as Timetable } - private fun read(context: Context): SQLiteDatabase { + /*private fun read(context: Context): SQLiteDatabase { return SQLiteDatabase.openDatabase(File(context.filesDir, "timetable.db").path, null, SQLiteDatabase.OPEN_READONLY) - } + }*/ } - lateinit var store: SQLiteDatabase + //lateinit var store: SQLiteDatabase private lateinit var cacheManager: CacheManager private var _stops: ArrayList<StopSuggestion>? = null + private lateinit var filesDir: File - fun refresh(context: Context) { - this.store = read(context) + fun refresh() { + //this.store = read(context) cacheManager.recreate(getStopDeparturesByPlates(cacheManager.keys().toSet())) @@ -54,6 +58,7 @@ getStops(true) } fun getStops(force: Boolean = false): List<StopSuggestion> { + println("STOPS!") if (_stops != null && !force) return _stops!! @@ -74,55 +79,129 @@ AWF 10 → Franowo, 29 → Franowo, 6 → Miłostowo, 5 → Stomil, 18 → Franowo, 15 → Franowo, 12 → Starołęka, 74 → Os. Orła Białego|8:4586|AWF73 */ - //trip_id, stop_id from stop_times if drop_off_type in {0,3} + //trip_id, stop_id from stop_times if pickup_type in {0,3} //route_id as line, trip_id, headsign from trips - //stop_id, stop_code from stops + + println(JCalendar.getInstance()) + val stopTripMap = HashMap<String, Set<String>>() + val stopTimesFile = File(filesDir, "gtfs_files/stop_times.txt") + var mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE) + var header = mapReader.getHeader(true) + + var stopTimesRow: Map<String, Any>? = null + var processors = Array<CellProcessor?>(header.size, { null }) + while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) { + if ((stopTimesRow!!["pickup_type"] as String) in arrayOf("0", "3")) { + val stopId = stopTimesRow!!["stop_id"] as String + val tripId = stopTimesRow!!["trip_id"] as String + if (stopId !in stopTripMap) + stopTripMap[stopId] = HashSet() + (stopTripMap[stopId]!! as HashSet).add(tripId) + } + } + mapReader.close() + println(JCalendar.getInstance()) - val map = HashMap<AgencyAndId, HashSet<String>>() + val tripIds = stopTripMap.flatMap { it.value } - val cursor = store.rawQuery("select route_short_name, trip_headsign, stop_id " + - "from stop_times join trips using trip_id join routes using route_id " + - "where drop_off_type = 0 or drop_off_type = 3", null) + val trips = HashMap<String, String>() + val tripsFile = File(filesDir, "gtfs_files/trips.txt") + mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE) + header = mapReader.getHeader(true) - while(cursor.moveToNext()) { - val line = cursor.getString(0) - val headsign = cursor.getString(1).toPascalCase() - val stopId = AgencyAndId(cursor.getString(2)) - if (map[stopId] == null) - map[stopId] = HashSet() - map[stopId]!!.add("$line → $headsign") + var tripsRow: Map<String, Any>? = null + processors = Array(header.size, { null }) + while ({ tripsRow = mapReader.read(header, processors); tripsRow }() != null) { //fixme takes 16 min, 21 times more than a file 28 times bigger + val tripId = tripsRow!!["trip_id"] as String + if (tripId in tripIds) { + trips[tripId] = tripsRow!!["trip_headsign"] as String //todo save route_id + } } + mapReader.close() + println(JCalendar.getInstance()) - cursor.close() + val routes = HashMap<String, String>() + val routesFile = File(filesDir, "gtfs_files/routes.txt") + mapReader = CsvMapReader(FileReader(routesFile), CsvPreference.STANDARD_PREFERENCE) + header = mapReader.getHeader(true) + + var routesRow: Map<String, Any>? = null + processors = Array(header.size, { null }) + while ({ routesRow = mapReader.read(header, processors); routesRow }() != null) { + val tripId = routesRow!!["route_id"] as String + if (tripId in tripIds) {//fixme + routes[tripId] = routesRow!!["route_short_name"] as String + } + } + mapReader.close() + println(JCalendar.getInstance()) + + val map = HashMap<AgencyAndId, Set<String>>() + + stopTripMap.forEach { + val directions = HashSet<String>() + it.value.forEach { + val route = routes[it] + val headsign = trips[it] + directions.add("$route → $headsign") + } + map[AgencyAndId(it.key)] = directions + } - val stops = map.entries.map { StopSuggestion(it.value, it.key) }.toSet() + _stops = map.map { StopSuggestion(it.value, it.key) }.sortedBy { getStopName(it.id) } as ArrayList<StopSuggestion> - _stops = stops.sortedBy { this.getStopCode(it.id) } as ArrayList<StopSuggestion> return _stops!! } fun getStopName(stopId: AgencyAndId): String { - val cursor = store.rawQuery("select stop_name from stops where stop_id = ?", arrayOf(stopId.id)) - cursor.moveToNext() - val name = cursor.getString(0) - cursor.close() - return name + val file = File(filesDir, "gtfs_files/stops.txt") + val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) + + var row: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ row = mapReader.read(header, processors); row }() != null) { + if ((row!!["stop_id"] as String) == stopId.id) { + mapReader.close() + return row!!["stop_name"] as String + } + } + mapReader.close() + throw IllegalArgumentException("Stop $stopId not in store") } fun getStopCode(stopId: AgencyAndId): String { - val cursor = store.rawQuery("select stop_code from stops where stop_id = ?", arrayOf(stopId.id)) - cursor.moveToNext() - val name = cursor.getString(0) - cursor.close() - return name + val file = File(filesDir, "gtfs_files/stops.txt") + val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) + + var row: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ row = mapReader.read(header, processors); row }() != null) { + if ((row!!["stop_id"] as String) == stopId.id) { + mapReader.close() + return row!!["stop_code"] as String + } + } + mapReader.close() + throw IllegalArgumentException("Stop $stopId not in store") } fun getLineNumber(lineId: AgencyAndId): String { - val cursor = store.rawQuery("select route_short_name from routes where route_id = ?", arrayOf(lineId.id)) - cursor.moveToNext() - val name = cursor.getString(0) - cursor.close() - return name + val file = File(filesDir, "gtfs_files/routes.txt") + val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) + + var row: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ row = mapReader.read(header, processors); row }() != null) { + if ((row!!["route_id"] as String) == lineId.id) { + mapReader.close() + return row!!["route_short_name"] as String + } + } + mapReader.close() + throw IllegalArgumentException("Route $lineId not in store") } fun getStopDepartures(stopId: AgencyAndId): Map<AgencyAndId, List<Departure>> { @@ -171,51 +250,82 @@ } private fun getStopDeparturesByPlate(plate: Plate): Plate { val resultPlate = Plate(Plate.ID(plate.id), HashMap()) - val cursor = store.rawQuery("select departure_time, trip_headsign, route_id, " + - "service_id, wheelchair_accessible, stop_sequence, trip_id, direction_id, shape_id from stop_times join trips using trip_id " + - "where stop_id = ? and route_id = ? and trip_headsign = ?", - arrayOf(plate.id.stop.toString(), plate.id.line.toString(), plate.id.headsign)) // fixme headsign toLower // test if needed - while (cursor.moveToNext()) { - val cal = JCalendar.getInstance() - val (h, m, s) = cursor.getString(0).split(":") - cal.set(JCalendar.HOUR_OF_DAY, h.toInt()) - cal.set(JCalendar.MINUTE, m.toInt()) - cal.set(JCalendar.SECOND, s.toInt()) - val time = cal.secondsAfterMidnight() - val serviceId = AgencyAndId(cursor.getString(3)) - val mode = calendarToMode(serviceId) - val lowFloor = cursor.getInt(4) == 1 - val mod = explainModification(Trip(AgencyAndId(cursor.getString(2)), - serviceId, createTripId(cursor.getString(6)), - cursor.getString(1), cursor.getInt(7), - AgencyAndId(cursor.getString(8))), cursor.getInt(5)) + val trips = HashMap<String, Map<String, Any>>() + val stopTimesFile = File(filesDir, "gtfs_files/stop_times.txt") + var mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE) + var header = mapReader.getHeader(true) - val dep = Departure(plate.id.line, mode, time, lowFloor, mod, plate.id.headsign) - if (resultPlate.departures!![serviceId] == null) - resultPlate.departures[serviceId] = HashSet() - resultPlate.departures[serviceId]!!.add(dep) + var stopTimesRow: Map<String, Any>? = null + var processors = Array<CellProcessor?>(header.size, { null }) + while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) { + if ((stopTimesRow!!["stop_id"] as String) == plate.id.stop.id) { + val tripId = stopTimesRow!!["trip_id"] as String + trips[tripId] = stopTimesRow!! + } } - cursor.close() + mapReader.close() + + val tripsFile = File(filesDir, "gtfs_files/trips.txt") + mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE) + header = mapReader.getHeader(true) + + var tripsRow: Map<String, Any>? = null + processors = Array(header.size, { null }) + while ({ tripsRow = mapReader.read(header, processors); tripsRow }() != null) { + val tripId = tripsRow!!["trip_id"] as String + if (tripId in trips && tripsRow!!["route_id"] as String == plate.id.line.id + && tripsRow!!["trip_headsign"] as String == plate.id.headsign) { //check if toLower is needed + val cal = JCalendar.getInstance() + val (h, m, s) = (trips[tripId]!!["departure_time"] as String).split(":") + cal.set(JCalendar.HOUR_OF_DAY, h.toInt()) + cal.set(JCalendar.MINUTE, m.toInt()) + cal.set(JCalendar.SECOND, s.toInt()) + val time = cal.secondsAfterMidnight() + val serviceId = AgencyAndId(tripsRow!!["service_id"] as String) + val mode = calendarToMode(serviceId) + val lowFloor = trips[tripId]!!["wheelchair_accessible"] as String == "1" + val mod = explainModification(Trip(AgencyAndId(tripsRow!!["route_id"] as String), + serviceId, createTripId(tripsRow!!["trip_id"] as String), + tripsRow!!["trip_headsign"] as String, Integer.parseInt(tripsRow!!["direction_id"] as String), + AgencyAndId(tripsRow!!["shape_id"] as String)), Integer.parseInt(trips[tripId]!!["stop_sequence"] as String)) + + val dep = Departure(plate.id.line, mode, time, lowFloor, mod, plate.id.headsign) + if (resultPlate.departures!![serviceId] == null) + resultPlate.departures[serviceId] = HashSet() + resultPlate.departures[serviceId]!!.add(dep) + } + } + mapReader.close() return resultPlate } fun calendarToMode(serviceId: AgencyAndId): List<Int> { - val cursor = store.rawQuery("select monday, tuesday, wednesday, thursday, friday, " + - "saturday, sunday from calendar where service_id = ?", arrayOf(serviceId.id)) - cursor.moveToNext() - val calendar = Calendar(cursor.getInt(0), cursor.getInt(1), - cursor.getInt(2), cursor.getInt(3), cursor.getInt(4), - cursor.getInt(5), cursor.getInt(6)) - val days = ArrayList<Int>() - if (calendar.monday == 1) days.add(0) - if (calendar.tuesday == 1) days.add(1) - if (calendar.wednesday == 1) days.add(2) - if (calendar.thursday == 1) days.add(3) - if (calendar.friday == 1) days.add(4) - if (calendar.saturday == 1) days.add(5) - if (calendar.sunday == 1) days.add(6) - cursor.close() - return days + val file = File(filesDir, "gtfs_files/calendar.txt") + val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) + + var row: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ row = mapReader.read(header, processors); row }() != null) { + if ((row!!["service_id"] as String) == serviceId.id) { + mapReader.close() + val calendar = Calendar(row!!["monday"] as String == "1", row!!["tuesday"] as String == "1", + row!!["wednesday"] as String == "1", row!!["thursday"] as String == "1", row!!["friday"] as String == "1", + row!!["saturday"] as String == "1", row!!["sunday"] as String == "1") + val days = ArrayList<Int>() + if (calendar.monday) days.add(0) + if (calendar.tuesday) days.add(1) + if (calendar.wednesday) days.add(2) + if (calendar.thursday) days.add(3) + if (calendar.friday) days.add(4) + if (calendar.saturday) days.add(5) + if (calendar.sunday) days.add(6) + + return days + } + } + mapReader.close() + throw IllegalArgumentException("Service $serviceId not in store") } private fun explainModification(trip: Trip, stopSequence: Int): List<String> { @@ -235,31 +345,57 @@ return explanations } private fun getRouteForTrip(trip: Trip): Route { - val cursor = store.rawQuery("select route_id, agency_id, route_short_name, " + - "route_long_name route_desc, route_type, route_color, route_text_color " + - "from routes join trips using route_id where trip_id = ?", arrayOf(trip.rawId)) - cursor.moveToNext() - val id = cursor.getString(0) - val agency = cursor.getString(1) - val shortName = cursor.getString(2) - val longName = cursor.getString(3) - val desc = cursor.getString(4) - val type = cursor.getInt(5) - val colour = Integer.parseInt(cursor.getString(6), 16) - val textColour = Integer.parseInt(cursor.getString(7), 16) - val (to, from) = desc.split("|") - val toSplit = to.split("^") - val fromSplit = from.split("^") - val description = "${toSplit[0]}|${fromSplit[0]}" - val modifications = HashMap<String, String>() - toSplit.slice(1 until toSplit.size).forEach { - val (k, v) = it.split(" - ") - modifications[k] = v + val routesFile = File(filesDir, "gtfs_files/routes.txt") + var mapReader = CsvMapReader(FileReader(routesFile), CsvPreference.STANDARD_PREFERENCE) + var header = mapReader.getHeader(true) + + var routeId = "" + var row: Map<String, Any>? = null + var processors = Array<CellProcessor?>(header.size, { null }) + while ({ row = mapReader.read(header, processors); row }() != null) { + if ((row!!["trip_id"] as String) == trip.rawId) { + mapReader.close() + routeId = row!!["route_id"] as String + break + } + } + if (routeId == "") { + mapReader.close() + throw IllegalArgumentException("Trip ${trip.rawId} not in store") } - val route = Route(AgencyAndId(id), AgencyAndId(agency), shortName, longName, description, - type, colour, textColour, modifications) - cursor.close() - return route + + val tripsFile = File(filesDir, "gtfs_files/trips.txt") + mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE) + header = mapReader.getHeader(true) + + var routeRow: Map<String, Any>? = null + processors = Array(header.size, { null }) + while ({ routeRow = mapReader.read(header, processors); routeRow }() != null) { + if ((routeRow!!["route_id"] as String) == routeId) { + mapReader.close() + val id = routeRow!!["route_id"] as String + val agency = routeRow!!["agency_id"] as String + val shortName = routeRow!!["route_short_name"] as String + val longName = routeRow!!["route_long_name"] as String + val desc = routeRow!!["route_desc"] as String + val type = Integer.parseInt(routeRow!!["route_type"] as String) + val colour = Integer.parseInt(routeRow!!["route_color"] as String, 16) + val textColour = Integer.parseInt(routeRow!!["route_text_color"] as String, 16) + val (to, from) = desc.split("|") + val toSplit = to.split("^") + val fromSplit = from.split("^") + val description = "${toSplit[0]}|${fromSplit[0]}" + val modifications = HashMap<String, String>() + toSplit.slice(1 until toSplit.size).forEach { + val (k, v) = it.split(" - ") + modifications[k] = v + } + return Route(AgencyAndId(id), AgencyAndId(agency), shortName, longName, description, + type, colour, textColour, modifications) + } + } + mapReader.close() + throw IllegalArgumentException("Trip ${trip.rawId} not in store") } // fun getLinesForStop(stopId: AgencyAndId): Set<AgencyAndId> { @@ -269,66 +405,102 @@ // return lines // } fun getTripsForStop(stopId: AgencyAndId): Set<Trip> { + val tripIds = HashSet<String>() + val stopTimesFile = File(filesDir, "gtfs_files/stop_times.txt") + var mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE) + var header = mapReader.getHeader(true) + + var stopTimesRow: Map<String, Any>? = null + var processors = Array<CellProcessor?>(header.size, { null }) + while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) { + if ((stopTimesRow!!["stop_id"] as String) == stopId.id) { + val tripId = stopTimesRow!!["trip_id"] as String + tripIds.add(tripId) + } + } + mapReader.close() + + val trips = HashSet<Trip>() - val cursor = store.rawQuery("select route_id, service_id, trip_id, trip_headsign, " + - "direction_id, shape_id from stop_times join trips using trip_id " + - "where stop_id = ?", arrayOf(stopId.id)) - while (cursor.moveToNext()) { - val routeId = AgencyAndId(cursor.getString(0)) - val serviceId = AgencyAndId(cursor.getString(1)) - val tripId = cursor.getString(2) - val headsign = cursor.getString(3) - val direction = cursor.getInt(4) - val shape = AgencyAndId(cursor.getString(5)) - val trip = Trip(routeId, serviceId, createTripId(tripId), headsign, direction, shape) - trips.add(trip) + val tripsFile = File(filesDir, "gtfs_files/trips.txt") + mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE) + header = mapReader.getHeader(true) + + var tripsRow: Map<String, Any>? = null + processors = Array(header.size, { null }) + while ({ tripsRow = mapReader.read(header, processors); tripsRow }() != null) { + val tripId = tripsRow!!["trip_id"] as String + if (tripId in tripIds) { + trips.add(Trip(AgencyAndId(tripsRow!!["route_id"] as String), + AgencyAndId(tripsRow!!["service_id"] as String), + createTripId(tripId), + tripsRow!!["trip_headsign"] as String, + Integer.parseInt(tripsRow!!["direction_id"] as String), + AgencyAndId(tripsRow!!["shape_id"] as String))) + } } - cursor.close() + mapReader.close() return trips } private fun createTripId(rawId: String): Trip.ID { - var modification = rawId.split("^")[1] - modification = modification.subSequence(0, modification.length - 1) as String - val isMain = modification[modification.length - 1] == '+' - val modifications = HashSet<Trip.ID.Modification>() - modification.split(",").forEach { - try { - val (id, start, end) = it.split(":") - modifications.add(Trip.ID.Modification(AgencyAndId(id), IntRange(start.toInt(), end.toInt()))) - } catch (e: Exception) { - modifications.add(Trip.ID.Modification(AgencyAndId(it), null)) + if (rawId.contains('^')) { + var modification = rawId.split("^")[1] + val isMain = modification[modification.length - 1] == '+' + if (isMain) + modification = modification.subSequence(0, modification.length - 1) as String + val modifications = HashSet<Trip.ID.Modification>() + modification.split(",").forEach { + try { + val (id, start, end) = it.split(":") + modifications.add(Trip.ID.Modification(AgencyAndId(id), IntRange(start.toInt(), end.toInt()))) + } catch (e: Exception) { + modifications.add(Trip.ID.Modification(AgencyAndId(it), null)) + } } - } - return Trip.ID(AgencyAndId(rawId.split("^")[0]), modifications, isMain) + return Trip.ID(AgencyAndId(rawId.split("^")[0]), modifications, isMain) + } else + return Trip.ID(AgencyAndId(rawId), HashSet(), false) } fun isEmpty(): Boolean { - val cursor = store.rawQuery("select * from feed_info", null) - return try { - cursor.moveToNext() - cursor.close() - true + try { + val file = File(filesDir, "gtfs_files/feed_info.txt") + val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) + + val processors = Array<CellProcessor?>(header.size, { null }) + if (mapReader.read(header, processors) == null) { + mapReader.close() + return true + } + mapReader.close() + return false } catch (e: Exception) { - cursor.close() - false + return true } } fun getValidSince(): String { - val cursor = store.rawQuery("select feed_start_date from feed_info", null) - cursor.moveToNext() - val validSince = cursor.getString(0) - cursor.close() - return validSince + val file = File(filesDir, "gtfs_files/feed_info.txt") + val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) + + val processors = Array<CellProcessor?>(header.size, { null }) + val row = mapReader.read(header, processors) + mapReader.close() + return row["feed_start_date"] as String } fun getValidTill(): String { - val cursor = store.rawQuery("select feed_end_date from feed_info", null) - cursor.moveToNext() - val validTill = cursor.getString(0) - cursor.close() - return validTill + val file = File(filesDir, "gtfs_files/feed_info.txt") + val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) + + val processors = Array<CellProcessor?>(header.size, { null }) + val row = mapReader.read(header, processors) + mapReader.close() + return row["feed_end_date"] as String } fun getServiceForToday(): AgencyAndId { @@ -345,28 +517,70 @@ } private fun getServiceFor(day: Int): AgencyAndId { val dow = arrayOf("sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday") - val cursor = store.rawQuery("select service_id from calendar where ? = 1", arrayOf(dow[day - 1])) - cursor.moveToNext() - val service = AgencyAndId(cursor.getString(0)) - cursor.close() - return service + val file = File(filesDir, "gtfs_files/calendar.txt") + val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) + + var row: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ row = mapReader.read(header, processors); row }() != null) { + if ((row!![dow[day - 1]] as String) == "1") { + mapReader.close() + return AgencyAndId(row!!["service_id"] as String) + } + } + mapReader.close() + throw IllegalArgumentException("Day $day not in calendar") } fun getLineForNumber(number: String): AgencyAndId { - val cursor = store.rawQuery("select route_id from routes where route_short_name = ?", arrayOf(number)) - cursor.moveToNext() - val id = AgencyAndId(cursor.getString(0)) - cursor.close() - return id + val file = File(filesDir, "gtfs_files/routes.txt") + val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) + val header = mapReader.getHeader(true) + + var row: Map<String, Any>? = null + val processors = Array<CellProcessor?>(header.size, { null }) + while ({ row = mapReader.read(header, processors); row }() != null) { + if ((row!!["route_short_name"] as String) == number) { + mapReader.close() + return AgencyAndId(row!!["route_id"] as String) + } + } + mapReader.close() + throw IllegalArgumentException("Route $number not in store") } fun getPlatesForStop(stop: AgencyAndId): Set<Plate.ID> { val plates = HashSet<Plate.ID>() - val cursor = store.rawQuery("select trip_headsign, route_id from stop_times join trips using trip_id where stop_id = ?", arrayOf(stop.id)) - while (cursor.moveToNext()) { - plates.add(Plate.ID(AgencyAndId(cursor.getString(1)), stop, cursor.getString(0))) + val tripIds = HashSet<String>() + val stopTimesFile = File(filesDir, "gtfs_files/stop_times.txt") + var mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE) + var header = mapReader.getHeader(true) + + var stopTimesRow: Map<String, Any>? = null + var processors = Array<CellProcessor?>(header.size, { null }) + while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) { + if ((stopTimesRow!!["stop_id"] as String) == stop.id) { + val tripId = stopTimesRow!!["trip_id"] as String + tripIds.add(tripId) + } + } + mapReader.close() + + + val tripsFile = File(filesDir, "gtfs_files/trips.txt") + mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE) + header = mapReader.getHeader(true) + + var tripsRow: Map<String, Any>? = null + processors = Array(header.size, { null }) + while ({ tripsRow = mapReader.read(header, processors); tripsRow }() != null) { + if (tripsRow!!["trip_id"] as String in tripIds) { + plates.add(Plate.ID(AgencyAndId(tripsRow!!["route_id"] as String), stop, tripsRow!!["trip_headsign"] as String)) + } } - cursor.close() + mapReader.close() + return plates } } diff --git a/app/src/main/res/layout/activity_nodb.xml b/app/src/main/res/layout/activity_nodb.xml index 63b8cd16a003e2fccabadb5fbc343f558b5a51ff..e34a2552f1412a7a26afa1e361629cc5e0ab92a6 100644 --- a/app/src/main/res/layout/activity_nodb.xml +++ b/app/src/main/res/layout/activity_nodb.xml @@ -28,6 +28,7 @@ android:layout_marginTop="8dp" android:text="" android:textAlignment="center" android:textAppearance="@style/TextAppearance.AppCompat.Headline" + android:textColor="@color/colorPrimary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"