diff --git a/app/build.gradle b/app/build.gradle index f09e43bd..199d51c4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,7 +35,7 @@ android { targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '11' } buildFeatures { viewBinding true @@ -91,7 +91,6 @@ dependencies { // 读取spatialite文件 implementation 'com.github.sevar83:android-spatialite:2.0.1' - } //允许引用生成的代码 kapt { diff --git a/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt b/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt index ae704354..d6d09a80 100644 --- a/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt +++ b/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt @@ -16,7 +16,6 @@ class OMQSApplication : Application() { Util.getInstance().init(applicationContext) NetUtils.getInstance().init(this) TakePhotoManager.getInstance().init(this, 1) - FileManager.initRootDir(this) } private fun getKey(inputString: String): String { diff --git a/app/src/main/java/com/navinfo/omqs/bean/TaskBean.kt b/app/src/main/java/com/navinfo/omqs/bean/TaskBean.kt index d4ae318e..86265253 100644 --- a/app/src/main/java/com/navinfo/omqs/bean/TaskBean.kt +++ b/app/src/main/java/com/navinfo/omqs/bean/TaskBean.kt @@ -5,6 +5,7 @@ import com.navinfo.omqs.Constant import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus import io.realm.RealmList import io.realm.RealmObject +import io.realm.annotations.Ignore import io.realm.annotations.PrimaryKey import io.realm.annotations.RealmClass @@ -55,9 +56,11 @@ open class TaskBean @JvmOverloads constructor( /** * 当前下载状态 */ - var status: Int = FileDownloadStatus.NONE -) : RealmObject(){ - fun getDownLoadUrl():String{ + var status: Int = FileDownloadStatus.NONE, + @Ignore + var message: String = "" +) : RealmObject() { + fun getDownLoadUrl(): String { return "${Constant.SERVER_ADDRESS}devcp/download?fileStr=26" } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/db/ImportOMDBHelper.kt b/app/src/main/java/com/navinfo/omqs/db/ImportOMDBHelper.kt index a3d18999..ea70906b 100644 --- a/app/src/main/java/com/navinfo/omqs/db/ImportOMDBHelper.kt +++ b/app/src/main/java/com/navinfo/omqs/db/ImportOMDBHelper.kt @@ -30,13 +30,24 @@ import kotlin.streams.toList /** * 导入omdb数据的帮助类 * */ -class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val context: Context,@Assisted("omdbFile") val omdbFile: File) { +class ImportOMDBHelper @AssistedInject constructor( + @Assisted("context") val context: Context, + @Assisted("omdbFile") val omdbFile: File +) { @Inject lateinit var omdbHiltFactory: OMDBDataBaseHiltFactory + @Inject lateinit var gson: Gson - private val database by lazy { omdbHiltFactory.obtainOmdbDataBaseHelper(context, omdbFile.absolutePath, 1).writableDatabase } - private val configFile: File = File("${Constant.USER_DATA_PATH}", Constant.OMDB_CONFIG) + private val database by lazy { + omdbHiltFactory.obtainOmdbDataBaseHelper( + context, + omdbFile.absolutePath, + 1 + ).writableDatabase + } + private val configFile: File = + File("${Constant.DATA_PATH}/${Constant.CURRENT_USER_ID}", Constant.OMDB_CONFIG) /** * 读取config的配置文件 @@ -67,8 +78,10 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont } }.toList() - val cursor = database.query(table, finalColumns.toTypedArray(), "1=1", - mutableListOf<String>().toTypedArray(), null, null, null, null) + val cursor = database.query( + table, finalColumns.toTypedArray(), "1=1", + mutableListOf<String>().toTypedArray(), null, null, null, null + ) with(cursor) { if (moveToFirst()) { while (moveToNext()) { @@ -76,13 +89,17 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont for (columnIndex in 0 until columnCount) { var columnName = getColumnName(columnIndex) if (columnName.startsWith("ST_AsText(")) { - columnName = columnName.replace("ST_AsText(", "").substringBeforeLast(")") + columnName = columnName.replace("ST_AsText(", "") + .substringBeforeLast(")") } - when(getType(columnIndex)) { + when (getType(columnIndex)) { FIELD_TYPE_NULL -> rowMap[columnName] = "" - FIELD_TYPE_INTEGER -> rowMap[columnName] = getInt(columnIndex) - FIELD_TYPE_FLOAT -> rowMap[columnName] = getFloat(columnIndex) - FIELD_TYPE_BLOB -> rowMap[columnName] = String(getBlob(columnIndex), Charsets.UTF_8) + FIELD_TYPE_INTEGER -> rowMap[columnName] = + getInt(columnIndex) + FIELD_TYPE_FLOAT -> rowMap[columnName] = + getFloat(columnIndex) + FIELD_TYPE_BLOB -> rowMap[columnName] = + String(getBlob(columnIndex), Charsets.UTF_8) else -> rowMap[columnName] = getString(columnIndex) } } @@ -104,54 +121,60 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont suspend fun importOmdbZipFile(omdbZipFile: File): Flow<String> = withContext(Dispatchers.IO) { val importConfig = openConfigFile() val unZipFolder = File(omdbZipFile.parentFile, "result") - flow<String> { + flow { if (unZipFolder.exists()) { unZipFolder.deleteRecursively() } unZipFolder.mkdirs() // 开始解压zip文件 val unZipFiles = ZipUtils.unzipFile(omdbZipFile, unZipFolder) + // 将listResult数据插入到Realm数据库中 + Realm.getDefaultInstance().beginTransaction() // 遍历解压后的文件,读取该数据返回 for ((index, currentConfig) in importConfig.tables.withIndex()) { val txtFile = unZipFiles.find { it.name == currentConfig.table } - val listResult: MutableList<Map<String, Any>> = mutableListOf() + val listResult = mutableListOf<Map<String, Any?>>() currentConfig?.let { val list = FileIOUtils.readFile2List(txtFile, "UTF-8") - if (list!=null) { + if (list != null) { // 将list数据转换为map for (line in list) { - val map = gson.fromJson<Map<String, Any>>(line, object : TypeToken<MutableMap<String, Any>>() {}.type) + val map = gson.fromJson<Map<String, Any?>>(line, object:TypeToken<Map<String, Any?>>(){}.getType()) .toMutableMap() - map["QItable"] = currentConfig.table - map["QIname"] = currentConfig.name - map["QIcode"] = currentConfig.code + map["qi_table"] = currentConfig.table + map["qi_name"] = currentConfig.name + map["qi_code"] = currentConfig.code listResult.add(map) } } } - // 将listResult数据插入到Realm数据库中 - Realm.getDefaultInstance().beginTransaction() for (map in listResult) { // 每一个map就是Realm的一条数据 // 先查询这个mesh下有没有数据,如果有则跳过即可 // val meshEntity = Realm.getDefaultInstance().where(RenderEntity::class.java).equalTo("properties['mesh']", map["mesh"].toString()).findFirst() val renderEntity = RenderEntity() - renderEntity.code = map["QIcode"].toString().toInt() - renderEntity.name = map["QIname"].toString() - renderEntity.table = map["QItable"].toString() + renderEntity.code = map["qi_code"].toString().toInt() + renderEntity.name = map["qi_name"].toString() + renderEntity.table = map["qi_table"].toString() // 其他数据插入到Properties中 renderEntity.geometry = map["geometry"].toString() - for (entry in map) { - renderEntity.properties[entry.key] = entry.value.toString() + for ((key, value) in map) { + when (value) { + is String -> renderEntity.properties[key.toString()] = value + is Int -> renderEntity.properties[key.toString()] = value.toInt().toString() + is Double -> renderEntity.properties[key.toString()] = value.toDouble().toString() + else -> renderEntity.properties[key.toString()] = value.toString() + } } Realm.getDefaultInstance().insert(renderEntity) } - Realm.getDefaultInstance().commitTransaction() // 1个文件发送一次flow流 - emit("${index+1}/${importConfig.tables.size}") + emit("${index + 1}/${importConfig.tables.size}") } + Realm.getDefaultInstance().commitTransaction() + emit("OK") } } @@ -160,7 +183,15 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont val columns = mutableListOf<String>() // 查询 sqlite_master 表获取指定数据表的元数据信息 - val cursor = db.query("sqlite_master", arrayOf("sql"), "type='table' AND name=?", arrayOf(tableName), null, null, null) + val cursor = db.query( + "sqlite_master", + arrayOf("sql"), + "type='table' AND name=?", + arrayOf(tableName), + null, + null, + null + ) // 从元数据信息中解析出列名 if (cursor.moveToFirst()) { diff --git a/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt b/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt index 15350948..cab1956b 100644 --- a/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt +++ b/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt @@ -1,9 +1,9 @@ package com.navinfo.omqs.db -import com.navinfo.collect.library.data.entity.QsRecordBean import com.navinfo.omqs.bean.HadLinkDvoBean import com.navinfo.omqs.bean.TaskBean +import io.realm.annotations.RealmModule -@io.realm.annotations.RealmModule(classes = [TaskBean::class, HadLinkDvoBean::class]) +@RealmModule(classes = [TaskBean::class, HadLinkDvoBean::class]) class MyRealmModule { } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/hilt/ActivityModule.kt b/app/src/main/java/com/navinfo/omqs/hilt/ActivityModule.kt new file mode 100644 index 00000000..aa4e2a56 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/hilt/ActivityModule.kt @@ -0,0 +1,66 @@ +package com.navinfo.omqs.hilt + +import android.content.Context +import com.navinfo.collect.library.map.NIMapController +import com.navinfo.omqs.db.RealmOperateHelper +import com.navinfo.omqs.db.RoomAppDatabase +import com.navinfo.omqs.http.RetrofitNetworkServiceAPI +import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager +import com.navinfo.omqs.http.taskdownload.TaskDownloadManager +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityRetainedComponent +import dagger.hilt.android.qualifiers.ActivityContext +import dagger.hilt.android.scopes.ActivityRetainedScoped + +@InstallIn(ActivityRetainedComponent::class) +@Module +class ActivityModule { + + /** + * 注入地图控制器,在activity范围内使用,单例 + */ + @ActivityRetainedScoped + @Provides + fun providesMapController(): NIMapController = NIMapController() + + /** + * 注入离线地图下载管理,在activity范围内使用,单例 + */ + @ActivityRetainedScoped + @Provides + fun providesOfflineMapDownloadManager( + networkServiceAPI: RetrofitNetworkServiceAPI, + roomAppDatabase: RoomAppDatabase, + mapController: NIMapController + ): OfflineMapDownloadManager = + OfflineMapDownloadManager(networkServiceAPI, roomAppDatabase, mapController) + + /** + * 注入任务下载 + */ + @ActivityRetainedScoped + @Provides + fun providesTaskListDownloadManager( + networkServiceAPI: RetrofitNetworkServiceAPI, + importFactory: ImportOMDBHiltFactory, + ): TaskDownloadManager = + TaskDownloadManager(importFactory, networkServiceAPI) + + /** + * 实验失败,这样创建,viewmodel不会在activity销毁的时候同时销毁 + * 4-14:因为没有传入activity的 owner,无法检测生命周期, + */ +// @ActivityRetainedScoped +// @Provides +// fun providesMainViewModel(mapController: NIMapController): MainViewModel { +// return MainViewModel(mapController) +// } + + @ActivityRetainedScoped + @Provides + fun providesRealmOperateHelper(): RealmOperateHelper { + return RealmOperateHelper() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt b/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt index 907043db..25cc9516 100644 --- a/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt +++ b/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt @@ -1,20 +1,23 @@ package com.navinfo.omqs.hilt import android.app.Application -import android.content.Context import android.util.Log import androidx.room.Room import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.reflect.TypeToken import com.navinfo.omqs.Constant import com.navinfo.omqs.OMQSApplication import com.navinfo.omqs.db.RoomAppDatabase import com.navinfo.omqs.http.RetrofitNetworkServiceAPI +import com.navinfo.omqs.tools.IntTypeAdapter import com.tencent.wcdb.database.SQLiteCipherSpec import com.tencent.wcdb.room.db.WCDBOpenHelperFactory import dagger.Lazy import dagger.Module import dagger.Provides import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import io.realm.Realm import kotlinx.coroutines.* @@ -34,7 +37,7 @@ class GlobalModule { @Singleton @Provides - fun provideApplication(application: Application): OMQSApplication { + fun provideApplication(@ApplicationContext application: Application): OMQSApplication { return application as OMQSApplication } @@ -86,7 +89,12 @@ class GlobalModule { @Provides @Singleton - fun provideGson(): Gson = Gson() + fun provideGson(): Gson = GsonBuilder() + // 解决解析Json时将int类型自动转换为Double的问题 + .registerTypeAdapter(object : TypeToken<Map<String, Any?>>() {}.getType(), IntTypeAdapter()) + .registerTypeAdapter(object : TypeToken<Map<String, Any>>() {}.getType(), IntTypeAdapter()) + .registerTypeAdapter(object : TypeToken<Map<Any, Any>>() {}.getType(), IntTypeAdapter()) + .create() @Provides @Singleton diff --git a/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt b/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt index 7d353b88..8a855745 100644 --- a/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt +++ b/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt @@ -1,63 +1,21 @@ package com.navinfo.omqs.hilt +import android.content.Context import com.navinfo.collect.library.map.NIMapController -import com.navinfo.omqs.db.RealmOperateHelper -import com.navinfo.omqs.db.RoomAppDatabase -import com.navinfo.omqs.http.RetrofitNetworkServiceAPI -import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager -import com.navinfo.omqs.http.taskdownload.TaskDownloadManager import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityRetainedComponent -import dagger.hilt.android.scopes.ActivityRetainedScoped +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.qualifiers.ActivityContext +import dagger.hilt.android.scopes.ActivityScoped -@InstallIn(ActivityRetainedComponent::class) +@InstallIn(ActivityComponent::class) @Module class MainActivityModule { - /** * 注入地图控制器,在activity范围内使用,单例 */ - @ActivityRetainedScoped + @ActivityScoped @Provides - fun providesMapController(): NIMapController = NIMapController() - - /** - * 注入离线地图下载管理,在activity范围内使用,单例 - */ - @ActivityRetainedScoped - @Provides - fun providesOfflineMapDownloadManager( - networkServiceAPI: RetrofitNetworkServiceAPI, - roomAppDatabase: RoomAppDatabase, - mapController: NIMapController - ): OfflineMapDownloadManager = - OfflineMapDownloadManager(networkServiceAPI, roomAppDatabase, mapController) - - /** - * 注入任务下载 - */ - @ActivityRetainedScoped - @Provides - fun providesTaskListDownloadManager( - networkServiceAPI: RetrofitNetworkServiceAPI, - ): TaskDownloadManager = - TaskDownloadManager(networkServiceAPI) - - /** - * 实验失败,这样创建,viewmodel不会在activity销毁的时候同时销毁 - * 4-14:因为没有传入activity的 owner,无法检测生命周期, - */ -// @ActivityRetainedScoped -// @Provides -// fun providesMainViewModel(mapController: NIMapController): MainViewModel { -// return MainViewModel(mapController) -// } - - @ActivityRetainedScoped - @Provides - fun providesRealmOperateHelper(): RealmOperateHelper { - return RealmOperateHelper() - } + fun providesContext(@ActivityContext context: Context): Context = context } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/hilt/Qualifiers.kt b/app/src/main/java/com/navinfo/omqs/hilt/Qualifiers.kt new file mode 100644 index 00000000..ab9d81cd --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/hilt/Qualifiers.kt @@ -0,0 +1,7 @@ +//package com.navinfo.omqs.hilt +// +//import javax.inject.Qualifier +// +//@Qualifier +//@Retention(AnnotationRetention.RUNTIME) +//annotation class ActivityContext \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadManager.kt b/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadManager.kt index 57da8cd8..8c5c1b5c 100644 --- a/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadManager.kt +++ b/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadManager.kt @@ -1,23 +1,32 @@ package com.navinfo.omqs.http.taskdownload +import android.content.Context import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.Observer import com.navinfo.omqs.bean.TaskBean +import com.navinfo.omqs.hilt.ImportOMDBHiltFactory +import com.navinfo.omqs.hilt.OMDBDataBaseHiltFactory import com.navinfo.omqs.http.RetrofitNetworkServiceAPI +import dagger.hilt.android.qualifiers.ActivityContext import java.util.concurrent.ConcurrentHashMap +import javax.inject.Inject /** * 管理任务数据下载 */ -class TaskDownloadManager( +class TaskDownloadManager constructor( + val importFactory: ImportOMDBHiltFactory, val netApi: RetrofitNetworkServiceAPI, ) { + + lateinit var context: Context + /** * 最多同时下载数量 */ - private val MAX_SCOPE = 3 + private val MAX_SCOPE = 1 /** * 存储有哪些城市需要下载的队列 @@ -33,6 +42,9 @@ class TaskDownloadManager( ConcurrentHashMap<Int, TaskDownloadScope>() } + fun init(context: Context) { + this.context = context + } /** * 启动下载任务 @@ -92,7 +104,7 @@ class TaskDownloadManager( fun addTask(taskBean: TaskBean) { if (!scopeMap.containsKey(taskBean.id)) { - scopeMap[taskBean.id] = TaskDownloadScope(this, taskBean) + scopeMap[taskBean.id] = TaskDownloadScope( this, taskBean) } } diff --git a/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadScope.kt b/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadScope.kt index 15694cb1..f15902d7 100644 --- a/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadScope.kt +++ b/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadScope.kt @@ -1,23 +1,34 @@ package com.navinfo.omqs.http.taskdownload +import android.content.Context import android.util.Log +import androidx.core.content.ContentProviderCompat.requireContext import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer import com.navinfo.omqs.Constant import com.navinfo.omqs.bean.TaskBean +import com.navinfo.omqs.db.ImportOMDBHelper +import com.navinfo.omqs.hilt.ImportOMDBHiltFactory +import com.navinfo.omqs.tools.FileManager import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus +import io.realm.Realm import kotlinx.coroutines.* import java.io.File import java.io.IOException import java.io.InputStream import java.io.RandomAccessFile +import javax.inject.Inject class TaskDownloadScope( private val downloadManager: TaskDownloadManager, val taskBean: TaskBean, ) : CoroutineScope by CoroutineScope(Dispatchers.IO + CoroutineName("OfflineMapDownLoad")) { + + @Inject + lateinit var importOMDBHiltFactory: ImportOMDBHiltFactory + /** *下载任务,用来取消的 */ @@ -59,7 +70,12 @@ class TaskDownloadScope( */ fun launch() { downloadJob = launch() { - download() + FileManager.checkOMDBFileInfo(taskBean) + if (taskBean.status == FileDownloadStatus.IMPORT) { + importData() + } else { + download() + } downloadManager.launchNext(taskBean.id) } } @@ -76,14 +92,17 @@ class TaskDownloadScope( * 更新任务 * @param status [OfflineMapCityBean.Status] */ - private fun change(status: Int) { - if (taskBean.status != status || status == FileDownloadStatus.LOADING) { + private fun change(status: Int, message: String = "") { + if (taskBean.status != status || status == FileDownloadStatus.LOADING || status == FileDownloadStatus.IMPORTING) { taskBean.status = status + taskBean.message = message downloadData.postValue(taskBean) - launch(Dispatchers.IO) { -// downloadManager.roomDatabase.getOfflineMapDao().update(taskBean) + launch { + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + it.copyToRealmOrUpdate(taskBean) + } } - } } @@ -96,36 +115,75 @@ class TaskDownloadScope( downloadData.observe(owner, ob) } + /** + * 导入数据 + */ + private suspend fun importData(file: File? = null) { + try { + Log.e("jingo", "importData SSS") + change(FileDownloadStatus.IMPORTING) + var fileNew = file + ?: File("${Constant.DOWNLOAD_PATH}${taskBean.evaluationTaskName}_${taskBean.dataVersion}.zip") + val importOMDBHelper: ImportOMDBHelper = + downloadManager.importFactory.obtainImportOMDBHelper( + downloadManager.context, + fileNew + ) + importOMDBHelper.importOmdbZipFile(importOMDBHelper.omdbFile).collect { + Log.e("jingo", "数据安装 $it") + if (it == "OK") { + change(FileDownloadStatus.DONE) + } else { + change(FileDownloadStatus.IMPORTING, it) + } + } + } catch (e: Exception) { + Log.e("jingo", "数据安装失败 ${e.toString()}") + change(FileDownloadStatus.ERROR) + } + + Log.e("jingo", "importData EEE") + } + /** * 下载文件 */ private suspend fun download() { + //如果文件下载安装已经完毕 + if (taskBean.status == FileDownloadStatus.DONE) { + return + } var inputStream: InputStream? = null var randomAccessFile: RandomAccessFile? = null try { //创建离线地图 下载文件夹,.map文件夹的下一级 - val fileDir = File("${Constant.DOWNLOAD_PATH}download") + val fileDir = File("${Constant.DOWNLOAD_PATH}") if (!fileDir.exists()) { fileDir.mkdirs() } val fileTemp = - File("${Constant.DOWNLOAD_PATH}${taskBean.id}_${taskBean.dataVersion}") + File("${Constant.DOWNLOAD_PATH}${taskBean.evaluationTaskName}_${taskBean.dataVersion}.zip") val startPosition = taskBean.currentSize - //验证断点有效性 - if (startPosition < 0) throw IOException("jingo Start position less than zero") + + if (fileTemp.length() > 0 && taskBean.fileSize > 0 && fileTemp.length() == taskBean.fileSize) { + importData(fileTemp) + return + } + val response = downloadManager.netApi.retrofitDownLoadFile( start = "bytes=$startPosition-", url = taskBean.getDownLoadUrl() ) val responseBody = response.body() - change(FileDownloadStatus.LOADING) - responseBody ?: throw IOException("jingo ResponseBody is null") + responseBody ?: throw IOException("jingo ResponseBody is null") if (startPosition == 0L) { taskBean.fileSize = responseBody.contentLength() + Log.e("jingo", "当前文件大小 ${taskBean.fileSize}") } + change(FileDownloadStatus.LOADING) //写入文件 randomAccessFile = RandomAccessFile(fileTemp, "rwd") randomAccessFile.seek(startPosition) @@ -146,17 +204,14 @@ class TaskDownloadScope( } } - Log.e("jingo", "文件下载完成 ${taskBean.currentSize} == ${taskBean.fileSize}") if (taskBean.currentSize == taskBean.fileSize) { - val res = - fileTemp.renameTo(File("${Constant.DOWNLOAD_PATH}${taskBean.evaluationTaskName}.zip")) - Log.e("jingo", "文件下载完成 修改文件 $res") - change(FileDownloadStatus.DONE) + importData(fileTemp) } else { change(FileDownloadStatus.PAUSE) } } catch (e: Throwable) { change(FileDownloadStatus.ERROR) + Log.e("jingo","数据下载出错 ${e.message}") } finally { inputStream?.close() randomAccessFile?.close() diff --git a/app/src/main/java/com/navinfo/omqs/tools/FileManager.kt b/app/src/main/java/com/navinfo/omqs/tools/FileManager.kt index 4b0aa70d..754d2a7f 100644 --- a/app/src/main/java/com/navinfo/omqs/tools/FileManager.kt +++ b/app/src/main/java/com/navinfo/omqs/tools/FileManager.kt @@ -16,8 +16,10 @@ class FileManager { const val LOADING = 2 //下载中 const val PAUSE = 3 //暂停 const val ERROR = 4 //错误 - const val DONE = 5 //完成 - const val UPDATE = 6 //有新版本要更新 + const val IMPORT = 5 //安装 + const val IMPORTING = 6 //安装中 + const val UPDATE = 7 //有新版本要更新 + const val DONE = 8 //完成 } //初始化数据文件夹 @@ -109,60 +111,28 @@ class FileManager { * 检查离线地图文件 */ suspend fun checkOMDBFileInfo(taskBean: TaskBean) { + if (taskBean.status == FileDownloadStatus.DONE) + return //访问离线地图文件夹 val fileDir = File("${Constant.DOWNLOAD_PATH}") //如果连本地文件夹还没有,就不用修改任何数据了 if (!fileDir.exists()) { return } - //访问离线地图临时下载文件夹 - val fileTempDir = File(Constant.DOWNLOAD_PATH) - //是否有一份.map文件了 - var mapFile: File? = null //文件夹里文件挨个访问 for (item in fileDir.listFiles()) { //先找到对应的省市文件,例如:540000_西藏自治区_20230401195018.map",以id开头 - if (item.isFile && item.name.startsWith("${taskBean.id}_")) { - //如果本地文件与从网络获取到版本号一致,表示这个文件已经下载完毕,不用处理了 - if (item.name == "${taskBean.id}_${taskBean.dataVersion}") { - taskBean.status = FileDownloadStatus.DONE - return - } - //文件存在,版本号不对应,留给下面流程处理 - mapFile = item - break - } - } - //临时下载文件夹 - if (fileTempDir.exists()) { - for (item in fileTempDir.listFiles()) { - //先找到对应的省市文件,例如:540000_20230401195018",以id开头 - if (item.isFile && item.name.startsWith("${taskBean.id}_")) { - //如果本地文件与从网络获取到版本号一致,表示这个文件已经在下载列表中 - if (item.name == "${taskBean.id}_${taskBean.dataVersion}") { - //如果这个临时文件的大小和下载大小是一致的,说明已经下载完了,但是在下载环节没有更名移动成功,需要重命名和移动文件夹 - if (item.length() == taskBean.fileSize) { - //移动更名文件后删除旧数据,修改状态 - if (item.renameTo(File("${Constant.OFFLINE_MAP_PATH}${taskBean.evaluationTaskName}.zip"))) { - //删除旧版本数据 - mapFile?.delete() - taskBean.status = FileDownloadStatus.DONE - return - } - } else { // 临时文件大小和目标不一致,说明下载了一半 - taskBean.status = FileDownloadStatus.PAUSE - taskBean.currentSize = item.length() - return - } - } else { //虽然省市id开头一致,但是版本号不一致,说明之前版本下载了一部分,现在要更新了,原来下载的文件直接删除 - taskBean.status = FileDownloadStatus.UPDATE - item.delete() - return - } - break + if (item.isFile && item.name == "${taskBean.evaluationTaskName}_${taskBean.dataVersion}.zip") { + taskBean.currentSize = item.length() + if (taskBean.fileSize > 0 && taskBean.fileSize == item.length()) { + taskBean.status = FileDownloadStatus.IMPORT + } else { + taskBean.status = FileDownloadStatus.PAUSE } + return } } + taskBean.status = FileDownloadStatus.NONE } } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/tools/IntTypeAdapter.kt b/app/src/main/java/com/navinfo/omqs/tools/IntTypeAdapter.kt new file mode 100644 index 00000000..894053f4 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/tools/IntTypeAdapter.kt @@ -0,0 +1,69 @@ +package com.navinfo.omqs.tools + +import com.google.gson.Gson +import com.google.gson.TypeAdapter +import com.google.gson.internal.LinkedTreeMap +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonToken +import com.google.gson.stream.JsonWriter +import java.io.IOException + +class IntTypeAdapter : TypeAdapter<Any>() { + private val delegate: TypeAdapter<Any> = Gson().getAdapter(Any::class.java) + + @Throws(IOException::class) + override fun read(`in`: JsonReader): Any? { + val token = `in`.peek() + when (token) { + JsonToken.BEGIN_ARRAY -> { + val list: MutableList<Any?> = ArrayList() + `in`.beginArray() + while (`in`.hasNext()) { + list.add(read(`in`)) + } + `in`.endArray() + return list + } + JsonToken.BEGIN_OBJECT -> { + val map: MutableMap<String, Any?> = LinkedTreeMap() + `in`.beginObject() + while (`in`.hasNext()) { + map[`in`.nextName()] = read(`in`) + } + `in`.endObject() + return map + } + JsonToken.STRING -> return `in`.nextString() + JsonToken.NUMBER -> { + // 改写数字的处理逻辑,将数字值分为整型与浮点型。 + val dbNum = `in`.nextDouble() + // 数字超过long的最大值,返回浮点类型 + if (dbNum > Long.MAX_VALUE) { + return dbNum + } + // 判断数字是否为整数值 + val lngNum = dbNum.toLong() + return if (dbNum == lngNum.toDouble()) { + try { + lngNum.toInt() + } catch (e: Exception) { + lngNum + } + } else { + dbNum + } + } + JsonToken.BOOLEAN -> return `in`.nextBoolean() + JsonToken.NULL -> { + `in`.nextNull() + return null + } + else -> throw IllegalStateException() + } + } + + @Throws(IOException::class) + override fun write(out: JsonWriter, value: Any?) { + delegate.write(out, value) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt index e241cb99..94d4f611 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.viewModelScope import com.blankj.utilcode.util.ResourceUtils import com.navinfo.omqs.Constant import com.navinfo.omqs.bean.LoginUserBean +import com.navinfo.omqs.db.MyRealmModule import com.navinfo.omqs.db.RoomAppDatabase import com.navinfo.omqs.http.NetResult import com.navinfo.omqs.http.NetworkService @@ -159,7 +160,8 @@ class LoginViewModel @Inject constructor( Constant.VERSION_ID = userId Constant.USER_DATA_PATH = Constant.DATA_PATH + Constant.USER_ID + "/" + Constant.VERSION_ID // 在SD卡创建用户目录,解压资源等 - val userFolder = File("${Constant.DATA_PATH}/${userId}") + val userFolder = File(Constant.USER_DATA_PATH) + if (!userFolder.exists()) userFolder.mkdirs() // 初始化Realm Realm.init(context.applicationContext) val password = "encryp".encodeToByteArray().copyInto(ByteArray(64)) @@ -169,7 +171,7 @@ class LoginViewModel @Inject constructor( .directory(userFolder) .name("OMQS.realm") .encryptionKey(password) -// .modules(Realm.getDefaultModule(), MyRealmModule()) + .modules(Realm.getDefaultModule(), MyRealmModule()) .schemaVersion(1) .build() Realm.setDefaultConfiguration(config) diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt index 14d6015c..51273fb5 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt @@ -4,10 +4,8 @@ import android.os.Bundle import androidx.activity.viewModels import androidx.core.view.WindowCompat import androidx.databinding.DataBindingUtil -import androidx.lifecycle.ViewModelProvider -import com.blankj.utilcode.util.ToastUtils -import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController +import com.blankj.utilcode.util.ToastUtils import com.navinfo.collect.library.map.NIMapController import com.navinfo.collect.library.map.handler.NiLocationListener import com.navinfo.omqs.Constant @@ -34,10 +32,10 @@ class MainActivity : BaseActivity() { @Inject lateinit var offlineMapDownloadManager: OfflineMapDownloadManager + override fun onCreate(savedInstanceState: Bundle?) { WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) - binding = DataBindingUtil.setContentView(this, R.layout.activity_main) //初始化地图 mapController.init( diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt index 49a4603f..4f7cd21f 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt @@ -3,38 +3,24 @@ package com.navinfo.omqs.ui.fragment.personalcenter import android.content.Intent import android.net.Uri import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import com.blankj.utilcode.util.UriUtils import com.github.k1rakishou.fsaf.FileChooser import com.github.k1rakishou.fsaf.callback.FSAFActivityCallbacks import com.github.k1rakishou.fsaf.callback.FileChooserCallback -import com.navinfo.collect.library.data.RealmUtils -import com.navinfo.collect.library.data.entity.OMDBEntity import com.navinfo.collect.library.map.NIMapController import com.navinfo.omqs.R import com.navinfo.omqs.databinding.FragmentPersonalCenterBinding import com.navinfo.omqs.db.ImportOMDBHelper import com.navinfo.omqs.hilt.ImportOMDBHiltFactory import com.navinfo.omqs.tools.CoroutineUtils -import com.navinfo.omqs.ui.activity.BaseActivity import com.navinfo.omqs.ui.fragment.BaseFragment import dagger.hilt.android.AndroidEntryPoint -import io.realm.Realm -import io.realm.RealmDictionary -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.FlowCollector -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.oscim.core.GeoPoint -import java.io.File -import java.util.UUID import javax.inject.Inject /** @@ -104,13 +90,6 @@ class PersonalCenterFragment : BaseFragment(), FSAFActivityCallbacks { file ) viewModel.importOMDBData(importOMDBHelper) -// // 开始导入数据 -// CoroutineUtils.launchWithLoading( -// requireContext(), -// loadingMessage = "导入数据..." -// ) { -// -// } } }) } @@ -122,14 +101,13 @@ class PersonalCenterFragment : BaseFragment(), FSAFActivityCallbacks { override fun onResult(uri: Uri) { viewModel.importScProblemData(uri) - } }) } R.id.personal_center_menu_test -> { viewModel.readRealmData() // 定位到指定位置 - niMapController.mMapView.vtmMap.animator().animateTo(GeoPoint(28.608398, 115.67901)) + niMapController.mMapView.vtmMap.animator().animateTo(GeoPoint(30.270367985798032, 113.83513667119433)) } R.id.personal_center_menu_task_list -> { findNavController().navigate(R.id.TaskListFragment) diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListAdapter.kt index 8e3ab8f8..692404ca 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListAdapter.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListAdapter.kt @@ -13,6 +13,8 @@ import com.navinfo.omqs.http.taskdownload.TaskDownloadManager import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter import com.navinfo.omqs.ui.other.BaseViewHolder +import java.io.File +import javax.inject.Inject /** * 离线地图城市列表 RecyclerView 适配器 @@ -31,7 +33,7 @@ class TaskListAdapter( if (it.tag != null) { val taskBean = data[it.tag as Int] when (taskBean.status) { - FileDownloadStatus.NONE, FileDownloadStatus.UPDATE, FileDownloadStatus.PAUSE, FileDownloadStatus.ERROR -> { + FileDownloadStatus.NONE, FileDownloadStatus.UPDATE, FileDownloadStatus.PAUSE, FileDownloadStatus.IMPORT, FileDownloadStatus.ERROR -> { Log.e("jingo", "开始下载 ${taskBean.status}") downloadManager.start(taskBean.id) } @@ -125,6 +127,29 @@ class TaskListAdapter( View.INVISIBLE binding.taskDownloadBtn.text = "更新" } + FileDownloadStatus.IMPORTING -> { + if (binding.taskProgress.visibility != View.VISIBLE) binding.taskProgress.visibility = + View.VISIBLE + binding.taskDownloadBtn.text = "安装中" + val split = taskBean.message.split("/") + if (split.size == 2) { + try { + val index = split[0].toInt() + val count = split[1].toInt() + binding.taskProgress.progress = + index * 100 / count + } catch (e: Exception) { + Log.e("jingo", "更新进度条 $e") + } + } else { + binding.taskProgress.progress = 0 + } + } + FileDownloadStatus.IMPORT -> { + if (binding.taskProgress.visibility != View.VISIBLE) binding.taskProgress.visibility = + View.INVISIBLE + binding.taskDownloadBtn.text = "安装" + } } } diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListFragment.kt index 55e149d9..9ec1ab92 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListFragment.kt @@ -30,6 +30,7 @@ class TaskListFragment : BaseFragment(){ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { + downloadManager.init(requireContext()) _binding = FragmentTaskListBinding.inflate(inflater, container, false) return binding.root diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListViewModel.kt index d8825811..f3175363 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListViewModel.kt @@ -27,12 +27,28 @@ class TaskListViewModel @Inject constructor( fun getTaskList(context: Context) { viewModelScope.launch(Dispatchers.IO) { val realm = Realm.getDefaultInstance() - Log.e("jingo","realm hashCOde ${realm.hashCode()}") + Log.e("jingo", "realm hashCOde ${realm.hashCode()}") + var taskList: List<TaskBean> = mutableListOf() when (val result = networkService.getTaskList("02911")) { is NetResult.Success -> { if (result.data != null) { realm.executeTransaction { - realm.copyToRealmOrUpdate(result.data.obj) + result.data.obj?.let { list -> + for (task in list) { + val item = realm.where(TaskBean::class.java).equalTo( + "id", task.id + ).findFirst() + if (item != null) { + task.fileSize = item.fileSize + Log.e("jingo", "当前文件大小 ${task.fileSize}") + task.status = item.status + task.currentSize = item.currentSize + } + realm.copyToRealmOrUpdate(task) + } + } + val objects = realm.where(TaskBean::class.java).findAll() + taskList = realm.copyFromRealm(objects) } } } @@ -51,9 +67,8 @@ class TaskListViewModel @Inject constructor( is NetResult.Loading -> {} else -> {} } - val objects = realm.where(TaskBean::class.java).findAll() - val taskList = realm.copyFromRealm(objects) - for(item in taskList){ + + for (item in taskList) { FileManager.checkOMDBFileInfo(item) } liveDataTaskList.postValue(taskList) diff --git a/app/src/main/res/layout/adapter_task_list.xml b/app/src/main/res/layout/adapter_task_list.xml index c64c31cf..f83eb5c1 100644 --- a/app/src/main/res/layout/adapter_task_list.xml +++ b/app/src/main/res/layout/adapter_task_list.xml @@ -8,6 +8,7 @@ android:paddingLeft="10dp" android:paddingTop="5dp" android:paddingRight="10dp" + android:paddingBottom="5dp" tools:context="com.navinfo.omqs.ui.fragment.tasklist.TaskListAdapter"> <TextView diff --git a/collect-library/build.gradle b/collect-library/build.gradle index 63bc27c2..d61972fa 100644 --- a/collect-library/build.gradle +++ b/collect-library/build.gradle @@ -38,8 +38,8 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } sourceSets { diff --git a/collect-library/src/main/assets/editormarker.xml b/collect-library/src/main/assets/editormarker.xml index 7ca48369..aa009d1b 100644 --- a/collect-library/src/main/assets/editormarker.xml +++ b/collect-library/src/main/assets/editormarker.xml @@ -1549,4 +1549,104 @@ <symbol src="assets:symbols/dot_blue.svg" /> </m> </m> + + <m k="qi_table"> + <!-- 道路线 --> + <m v="OMDB_RD_LINK"> + <line stroke="#9c9c9c" width="1"/> + </m> + <!--道路方向--> + <m v="OMDB_RD_LINK_DIRECT"> + <m k="direct"> + <m v="2"> + <lineSymbol src="assets:omdb/oneway_left.svg"></lineSymbol> + </m> + <m v="3"> + <lineSymbol src="assets:omdb/oneway_right.svg"></lineSymbol> + </m> + </m> + </m> + <!--道路种别--> + <m v="OMDB_RD_LINK_KIND"> + <m k="kind"> + <m v="1"> + <line stroke="#fcacd4" width="1"/> + </m> + <m v="2"> + <line stroke="#dcacfc" width="1"/> + </m> + <m v="3"> + <line stroke="#fc9c9c" width="1"/> + </m> + <m v="4"> + <line stroke="#fcd484" width="1"/> + </m> + <m v="5"> + <line stroke="#ecfccc" width="1"/> + </m> + <m v="6"> + <line stroke="#acec84" width="1"/> + </m> + <m v="7"> + <line stroke="#806048" width="1"/> + </m> + <m v="8"> + <line stroke="#fcfc7c" width="1"/> + </m> + <m v="9"> + <line stroke="#acc4fc" width="1"/> + </m> + <m v="10"> + <line stroke="#8cc8e0" width="1"/> + </m> + <m v="11"> + <line stroke="#64ecdc" width="1"/> + </m> + <m v="13"> + <line stroke="#585080" width="1"/> + </m> + <m v="15"> + <line stroke="#647430" width="1"/> + </m> + </m> + </m> + + <!--常规点限速--> + <m v="OMDB_SPEEDLIMIT"> + <m k="speedFlag"> + <m v="0"> + <circle fill="#0000ff" radius="30" scale-radius="true" /> + <text k="maxSpeed" use="road"></text> + <text k="minSpeed" dy="30" bg-fill="#00ff00" use="road"></text> + </m> + <m v="1"> +<!-- <circle fill="#0000ff" radius="30" scale-radius="true" />--> + <text k="maxSpeed" use="road"></text> + <text k="minSpeed" dy="30" bg-fill="#00ff00" use="road"></text> + </m> + </m> + </m> + <!--条件点限速--> + <m v="OMDB_SPEEDLIMIT_COND"> + <m k="direct"> + <m v="2"> + <lineSymbol src="assets:omdb/oneway_left.svg"></lineSymbol> + </m> + <m v="3"> + <lineSymbol src="assets:omdb/oneway_right.svg"></lineSymbol> + </m> + </m> + </m> + <!--可变点限速--> + <m v="OMDB_SPEEDLIMIT_VAR"> + <m k="direct"> + <m v="2"> + <lineSymbol src="assets:omdb/oneway_left.svg"></lineSymbol> + </m> + <m v="3"> + <lineSymbol src="assets:omdb/oneway_right.svg"></lineSymbol> + </m> + </m> + </m> + </m> </rendertheme> \ No newline at end of file diff --git a/collect-library/src/main/assets/omdb/oneway_left.svg b/collect-library/src/main/assets/omdb/oneway_left.svg new file mode 100644 index 00000000..9acd8dbe --- /dev/null +++ b/collect-library/src/main/assets/omdb/oneway_left.svg @@ -0,0 +1,11 @@ +<svg width="580" height="580" xmlns="http://www.w3.org/2000/svg" version="1.0"> + <metadata id="metadata10"/> + + <g> + <title>Layer 1</title> + <g transform="rotate(-179.925 290 290)" id="svg_1"> + <path stroke="#ffffff" shape-rendering="auto" filter-blend-mode="normal" stroke-linejoin="round" fill="none" solid-opacity="1" color-interpolation="sRGB" isolation="auto" stroke-width="64" color-rendering="auto" image-rendering="auto" color="#000000" filter-gaussianBlur-deviation="0" color-interpolation-filters="linearRGB" opacity="0.8" solid-color="#000000" mix-blend-mode="normal" d="m30,261l286,0l0,-72.5l234,101.85l-234,101.15l0,-72.5l-286,0l0,-58z" id="path4151"/> + <path fill="#6a6a6a" id="path4136" d="m30,261l286,0l0,-72.5l234,101.85l-234,101.15l0,-72.5l-286,0l0,-58z"/> + </g> + </g> +</svg> \ No newline at end of file diff --git a/collect-library/src/main/assets/omdb/oneway_right.svg b/collect-library/src/main/assets/omdb/oneway_right.svg new file mode 100644 index 00000000..b289a043 --- /dev/null +++ b/collect-library/src/main/assets/omdb/oneway_right.svg @@ -0,0 +1,11 @@ +<svg width="580" height="580" xmlns="http://www.w3.org/2000/svg" version="1.0"> + <metadata id="metadata10"/> + + <g> + <title>Layer 1</title> + <g id="svg_1" transform="rotate(-0.400407 290 290)"> + <path id="path4151" d="m30,261l286,0l0,-72.5l234,101.85l-234,101.15l0,-72.5l-286,0l0,-58z" mix-blend-mode="normal" solid-color="#000000" opacity="0.8" color-interpolation-filters="linearRGB" filter-gaussianBlur-deviation="0" color="#000000" image-rendering="auto" color-rendering="auto" stroke-width="64" isolation="auto" color-interpolation="sRGB" solid-opacity="1" fill="none" stroke-linejoin="round" filter-blend-mode="normal" shape-rendering="auto" stroke="#ffffff"/> + <path d="m30,261l286,0l0,-72.5l234,101.85l-234,101.15l0,-72.5l-286,0l0,-58z" id="path4136" fill="#6a6a6a"/> + </g> + </g> +</svg> \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt index 58d4bb8a..42adfd2a 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt @@ -4,7 +4,6 @@ import com.navinfo.collect.library.utils.GeometryToolsKt import io.realm.RealmObject import io.realm.RealmSet import io.realm.annotations.PrimaryKey -import io.realm.annotations.RealmClass /** diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt index 3b419ed5..f3524706 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt @@ -116,6 +116,18 @@ open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView,tr mMapView.updateMap() // initMapLifeSource() + // 设置矢量图层均在12级以上才显示 + mMapView.vtmMap.events.bind(UpdateListener { e, mapPosition -> + if (e == org.oscim.map.Map.SCALE_EVENT) { + // 测评数据图层在指定Zoom后开始显示 + val isOmdbZoom = mapPosition.zoomLevel>=Constant.OMDB_MIN_ZOOM + baseGroupLayer?.layers?.forEach { + it.isEnabled = !isOmdbZoom + } + omdbVectorTileLayer.isEnabled = isOmdbZoom + omdbLabelLayer.isEnabled = isOmdbZoom + } + }) } private fun initOMDBVectorTileLayer() { diff --git a/collect-library/src/main/java/com/navinfo/collect/library/utils/GeometryToolsKt.kt b/collect-library/src/main/java/com/navinfo/collect/library/utils/GeometryToolsKt.kt index 3d5fd4a5..888e0afd 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/utils/GeometryToolsKt.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/utils/GeometryToolsKt.kt @@ -42,7 +42,7 @@ class GeometryToolsKt { val tileY1 = MercatorProjection.latitudeToTileY(minMaxY[1], Constant.OVER_ZOOM.toByte()) val minTileY = if (tileY0 <= tileY1) tileY0 else tileY1 val maxTileY = if (tileY0 <= tileY1) tileY1 else tileY0 - println("getTileYByGeometry$envelope===$minTileY===$maxTileY") +// println("getTileYByGeometry$envelope===$minTileY===$maxTileY") for (i in minTileY..maxTileY) { tileYSet.add(i) @@ -86,7 +86,7 @@ class GeometryToolsKt { val tileX1 = MercatorProjection.longitudeToTileX(minMaxX[1], Constant.OVER_ZOOM.toByte()) val minTileX = if (tileX0 <= tileX1) tileX0 else tileX1 val maxTileX = if (tileX0 <= tileX1) tileX1 else tileX0 - println("getTileXByGeometry$envelope$minTileX===$maxTileX") +// println("getTileXByGeometry$envelope$minTileX===$maxTileX") for (i in minTileX..maxTileX) { tileXSet.add(i) } diff --git a/collect-library/src/main/res/resources/rendertheme.xsd b/collect-library/src/main/res/resources/rendertheme.xsd new file mode 100644 index 00000000..b28e722a --- /dev/null +++ b/collect-library/src/main/res/resources/rendertheme.xsd @@ -0,0 +1,367 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema xmlns:tns="http://opensciencemap.org/rendertheme" + xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" + targetNamespace="http://opensciencemap.org/rendertheme" xml:lang="en"> + + <!-- attribute types --> + <xs:simpleType name="cap"> + <xs:restriction base="xs:string"> + <xs:enumeration value="butt"/> + <xs:enumeration value="round"/> + <xs:enumeration value="square"/> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="closed"> + <xs:restriction base="xs:string"> + <xs:enumeration value="yes"/> + <xs:enumeration value="no"/> + <xs:enumeration value="any"/> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="color"> + <xs:restriction base="xs:string"> + <xs:pattern value="#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})"/> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="elementList"> + <xs:restriction base="xs:string"> + <xs:enumeration value="node"/> + <xs:enumeration value="way"/> + <xs:enumeration value="any"/> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="selectorList"> + <xs:restriction base="xs:string"> + <xs:enumeration value="first"/> + <xs:enumeration value="any"/> + <xs:enumeration value="when-matched"/> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="fontFamily"> + <xs:restriction base="xs:string"> + <xs:enumeration value="default"/> + <xs:enumeration value="default_bold"/> + <xs:enumeration value="monospace"/> + <xs:enumeration value="sans_serif"/> + <xs:enumeration value="serif"/> + <xs:enumeration value="thin"/> + <xs:enumeration value="light"/> + <xs:enumeration value="medium"/> + <xs:enumeration value="black"/> + <xs:enumeration value="condensed"/> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="fontStyle"> + <xs:restriction base="xs:string"> + <xs:enumeration value="bold"/> + <xs:enumeration value="bold_italic"/> + <xs:enumeration value="italic"/> + <xs:enumeration value="normal"/> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="nonNegativeFloat"> + <xs:restriction base="xs:float"> + <xs:minInclusive value="0"/> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="src"> + <xs:restriction base="xs:string"> + <xs:pattern value="((file|assets):)?.+"/> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="strokeDasharray"> + <xs:restriction base="xs:string"> + <xs:pattern + value="([0-9]+(\.[0-9]+)? *, *[0-9]+(\.[0-9]+)? *, *)*[0-9]+(\.[0-9]+)? *, *[0-9]+(\.[0-9]+)?"/> + </xs:restriction> + </xs:simpleType> + + <xs:simpleType name="textKey"> + <xs:restriction base="xs:string"> + <xs:enumeration value="ele"/> + <xs:enumeration value="addr:housenumber"/> + <xs:enumeration value="addr_housenumber"/> <!-- Mapzen --> + <xs:enumeration value="housenumber"/> <!-- OpenMapTiles --> + <xs:enumeration value="name"/> + <xs:enumeration value="maxSpeed"/><!--最高速度--> + <xs:enumeration value="minSpeed"/><!--最低速度--> + <xs:enumeration value="ref"/> + </xs:restriction> + </xs:simpleType> + + <!-- style menu cat element --> + <xs:complexType name="cat"> + <xs:attribute name="id" type="xs:string" use="required"/> + </xs:complexType> + + <!-- style menu name element --> + <xs:complexType name="name"> + <xs:attribute name="lang" type="xs:string" use="required"/> + <xs:attribute name="value" type="xs:string" use="required"/> + </xs:complexType> + + <!-- style menu overlay element --> + <xs:complexType name="overlay"> + <xs:attribute name="id" type="xs:string" use="required"/> + </xs:complexType> + + <!-- style menu layer element --> + <xs:complexType name="layer"> + <xs:sequence maxOccurs="1" minOccurs="0"> + <xs:element name="name" maxOccurs="unbounded" minOccurs="0" type="tns:name"/> + <xs:element name="cat" maxOccurs="unbounded" minOccurs="0" type="tns:cat"/> + <xs:element name="overlay" maxOccurs="unbounded" minOccurs="0" type="tns:overlay"/> + </xs:sequence> + <xs:attribute name="id" type="xs:string" use="required"/> + <xs:attribute name="parent" type="xs:string" use="optional"/> + <xs:attribute name="visible" default="false" type="xs:boolean" use="optional"/> + <xs:attribute name="enabled" default="false" type="xs:boolean" use="optional"/> + </xs:complexType> + + <!-- rendering instructions --> + <xs:complexType name="area"> + <xs:attribute name="cat" type="xs:string" use="optional"/> + <xs:attribute name="id" default="0" type="xs:string" use="optional"/> + <xs:attribute name="use" default="0" type="xs:string" use="optional"/> + <xs:attribute name="src" type="tns:src" use="optional"/> + <xs:attribute name="symbol-width" type="xs:positiveInteger" use="optional"/> + <xs:attribute name="symbol-height" type="xs:positiveInteger" use="optional"/> + <xs:attribute name="symbol-percent" type="xs:positiveInteger" use="optional"/> + <xs:attribute name="fill" default="#000000" type="tns:color" use="optional"/> + <xs:attribute name="stroke" default="#00000000" type="tns:color" use="optional"/> + <xs:attribute name="stroke-width" default="0" type="tns:nonNegativeFloat" use="optional"/> + <xs:attribute name="fade" default="-1" type="xs:integer" use="optional"/> + <xs:attribute name="blend" default="-1" type="xs:integer" use="optional"/> + <xs:attribute name="blend-fill" default="#000000" type="tns:color" use="optional"/> + <xs:attribute name="mesh" default="false" type="xs:boolean" use="optional"/> + </xs:complexType> + + <xs:complexType name="caption"> + <xs:attribute name="cat" type="xs:string" use="optional"/> + <xs:attribute name="k" type="tns:textKey" use="required"/> + <xs:attribute name="dy" default="0" type="xs:float" use="optional"/> + <xs:attribute name="font-family" default="default" type="tns:fontFamily" use="optional"/> + <xs:attribute name="style" default="normal" type="tns:fontStyle" use="optional"/> + <xs:attribute name="size" default="0" type="tns:nonNegativeFloat" use="optional"/> + <xs:attribute name="bg-fill" default="#00000000" type="tns:color" use="optional"/> + <xs:attribute name="fill" default="#000000" type="tns:color" use="optional"/> + <xs:attribute name="stroke" default="#000000" type="tns:color" use="optional"/> + <xs:attribute name="stroke-width" default="0" type="tns:nonNegativeFloat" use="optional"/> + <!-- polygon area expressed as a ratio to tile area, e.g. 0.1 for 10% of tile area --> + <xs:attribute name="area-size" default="0" type="tns:nonNegativeFloat" use="optional"/> + <!-- priority for label placement, 0 = highest priority --> + <xs:attribute name="priority" default="0" type="xs:integer" use="optional"/> + <!-- symbol src name --> + <xs:attribute name="symbol" type="tns:src" use="optional"/> + <xs:attribute name="symbol-width" type="xs:positiveInteger" use="optional"/> + <xs:attribute name="symbol-height" type="xs:positiveInteger" use="optional"/> + <xs:attribute name="symbol-percent" type="xs:positiveInteger" use="optional"/> + </xs:complexType> + + <xs:complexType name="circle"> + <xs:attribute name="cat" type="xs:string" use="optional"/> + <xs:attribute name="radius" type="tns:nonNegativeFloat" use="required"/> + <xs:attribute name="scale-radius" default="false" type="xs:boolean" use="optional"/> + <xs:attribute name="fill" default="#00000000" type="tns:color" use="optional"/> + <xs:attribute name="stroke" default="#00000000" type="tns:color" use="optional"/> + <xs:attribute name="stroke-width" default="0" type="tns:nonNegativeFloat" use="optional"/> + </xs:complexType> + + <xs:complexType name="line"> + <xs:attribute name="cat" type="xs:string" use="optional"/> + + <!-- style: TODO only in style-line--> + <xs:attribute name="id" default="0" type="xs:string" use="optional"/> + + <!-- inherited style --> + <xs:attribute name="use" default="0" type="xs:string" use="optional"/> + + <xs:attribute name="src" type="tns:src" use="optional"/> + <xs:attribute name="symbol-width" type="xs:positiveInteger" use="optional"/> + <xs:attribute name="symbol-height" type="xs:positiveInteger" use="optional"/> + <xs:attribute name="symbol-percent" type="xs:positiveInteger" use="optional"/> + <xs:attribute name="stroke" default="#000000" type="tns:color" use="optional"/> + <xs:attribute name="width" default="0" type="xs:float" use="optional"/> + <!-- minimum scaled width to draw outline --> + <xs:attribute name="min" default="0" type="xs:float" use="optional"/> + <xs:attribute name="dasharray" type="tns:strokeDasharray" use="optional"/> + <xs:attribute name="cap" default="round" type="tns:cap" use="optional"/> + <xs:attribute name="outline" default="" type="xs:string" use="optional"/> + <xs:attribute name="fade" default="-1" type="xs:integer" use="optional"/> + <xs:attribute name="blur" default="-1" type="xs:float" use="optional"/> + <xs:attribute name="fix" default="false" type="xs:boolean" use="optional"/> + <xs:attribute name="repeat-gap" default="200" type="xs:float" use="optional"/> + <xs:attribute name="repeat-start" default="30" type="xs:float" use="optional"/> + + <!-- stipple repeat in 'pixel' --> + <xs:attribute name="stipple" default="0" type="xs:integer" use="optional"/> + <!-- stipple color --> + <xs:attribute name="stipple-stroke" default="#000000" type="tns:color" use="optional"/> + <!-- stipple width relative to line width, i.e 0.0-1.0 --> + <xs:attribute name="stipple-width" default="0" type="xs:float" use="optional"/> + + </xs:complexType> + + <xs:complexType name="text"> + <xs:attribute name="cat" type="xs:string" use="optional"/> + + <xs:attribute name="id" default="0" type="xs:string" use="optional"/> + <xs:attribute name="use" default="0" type="xs:string" use="optional"/> + + <xs:attribute name="k" default="name" type="tns:textKey" use="optional"/> + <xs:attribute name="dy" default="0" type="xs:float" use="optional"/> + <xs:attribute name="font-family" default="default" type="tns:fontFamily" use="optional"/> + <xs:attribute name="style" default="normal" type="tns:fontStyle" use="optional"/> + <xs:attribute name="size" default="0" type="tns:nonNegativeFloat" use="optional"/> + <xs:attribute name="bg-fill" default="#00000000" type="tns:color" use="optional"/> + <xs:attribute name="fill" default="#000000" type="tns:color" use="optional"/> + <xs:attribute name="stroke" default="#000000" type="tns:color" use="optional"/> + <xs:attribute name="stroke-width" default="0" type="tns:nonNegativeFloat" use="optional"/> + <xs:attribute name="caption" default="false" type="xs:boolean" use="optional"/> + <!-- polygon area expressed as a ratio to tile area, e.g. 0.1 for 10% of tile area --> + <xs:attribute name="area-size" default="0" type="tns:nonNegativeFloat" use="optional"/> + <!-- priority for label placement, 0 = highest priority --> + <xs:attribute name="priority" default="0" type="xs:integer" use="optional"/> + </xs:complexType> + + <xs:complexType name="symbol"> + <xs:attribute name="cat" type="xs:string" use="optional"/> + + <xs:attribute name="id" default="0" type="xs:string" use="optional"/> + <xs:attribute name="use" default="0" type="xs:string" use="optional"/> + + <xs:attribute name="src" type="tns:src" use="optional"/> + <xs:attribute name="symbol-width" type="xs:positiveInteger" use="optional"/> + <xs:attribute name="symbol-height" type="xs:positiveInteger" use="optional"/> + <xs:attribute name="symbol-percent" type="xs:positiveInteger" use="optional"/> + + <!-- symbols on lines --> + <xs:attribute name="billboard" default="false" type="xs:boolean" use="optional"/> + <xs:attribute name="repeat" default="false" type="xs:boolean" use="optional"/> + <xs:attribute name="repeat-gap" default="200" type="xs:float" use="optional"/> + <xs:attribute name="repeat-start" default="30" type="xs:float" use="optional"/> + <xs:attribute name="rotate" default="true" type="xs:boolean" use="optional"/> + </xs:complexType> + + <xs:complexType name="extrusion"> + <xs:attribute name="cat" type="xs:string" use="optional"/> + <xs:attribute name="line-color" type="tns:color" use="optional"/> + <xs:attribute name="side-color" type="tns:color" use="required"/> + <xs:attribute name="top-color" type="tns:color" use="required"/> + <xs:attribute name="hsv-h" default="0" type="xs:double" use="optional"/> + <xs:attribute name="hsv-s" default="1" type="xs:double" use="optional"/> + <xs:attribute name="hsv-v" default="1" type="xs:double" use="optional"/> + <!-- 12m default --> + <xs:attribute name="default-height" default="12" type="xs:positiveInteger" use="optional"/> + </xs:complexType> + + <!-- match elements --> + <xs:complexType name="m"> + <xs:choice maxOccurs="unbounded" minOccurs="0"> + <!-- recursion to allow for nested m --> + <xs:element name="m" type="tns:m"/> + + <xs:element name="area" type="tns:area"/> + <xs:element name="caption" type="tns:caption"/> + <xs:element name="circle" type="tns:circle"/> + <xs:element name="line" type="tns:line"/> + <xs:element name="outline" type="tns:line"/> + <xs:element name="lineSymbol" type="tns:line"/> + <xs:element name="text" type="tns:text"/> + <xs:element name="extrusion" type="tns:extrusion"/> + <xs:element name="symbol" type="tns:symbol"/> + + <!-- outline is defined within rules to match layering --> + <xs:element name="outline-layer" type="tns:line"/> + </xs:choice> + + <xs:attribute name="select" default="any" type="tns:selectorList" use="optional"/> + <xs:attribute name="e" type="tns:elementList" use="optional"/> + <xs:attribute name="k" type="xs:string" use="optional"/> + <xs:attribute name="v" type="xs:string" use="optional"/> + <xs:attribute name="cat" type="xs:string" use="optional"/> + <xs:attribute name="closed" default="any" type="tns:closed" use="optional"/> + <xs:attribute name="zoom-min" default="0" type="xs:unsignedByte" use="optional"/> + <xs:attribute name="zoom-max" default="127" type="xs:unsignedByte" use="optional"/> + </xs:complexType> + + <xs:complexType name="atlasRect"> + <xs:attribute name="id" type="xs:string" use="required"/> + <xs:attribute name="pos" type="xs:string" use="required"/> + </xs:complexType> + + <xs:complexType name="atlas"> + <xs:choice maxOccurs="unbounded" minOccurs="0"> + <xs:element name="rect" type="tns:atlasRect"/> + </xs:choice> + <xs:attribute name="img" type="xs:string" use="required"/> + </xs:complexType> + + <!-- stylemenu element --> + <xs:complexType name="stylemenu"> + <xs:sequence maxOccurs="1" minOccurs="0"> + <xs:element name="layer" maxOccurs="unbounded" minOccurs="0" type="tns:layer"/> + </xs:sequence> + <xs:attribute name="defaultvalue" type="xs:string" use="required"/> + <xs:attribute name="defaultlang" type="xs:string" use="required"/> + <xs:attribute name="id" type="xs:string" use="required"/> + </xs:complexType> + + <!-- tag-transform element --> + <xs:complexType name="tag-transform"> + <xs:attribute name="k" type="xs:string" use="required"/> + <xs:attribute name="v" type="xs:string" use="optional"/> + <xs:attribute name="k-lib" type="xs:string" use="required"/> + <xs:attribute name="v-lib" type="xs:string" use="optional"/> + </xs:complexType> + + <!-- rendertheme element --> + <xs:complexType name="rendertheme"> + <xs:sequence maxOccurs="1" minOccurs="0"> + <xs:element name="stylemenu" maxOccurs="1" minOccurs="0" type="tns:stylemenu"/> + + <!-- tag definitions --> + <xs:choice maxOccurs="unbounded" minOccurs="0"> + <xs:element name="tag-transform" type="tns:tag-transform"/> + </xs:choice> + + <!-- style definitions --> + <xs:sequence maxOccurs="256" minOccurs="0"> + <xs:choice maxOccurs="unbounded" minOccurs="0"> + <xs:element name="style-text" type="tns:text"/> + <xs:element name="style-symbol" type="tns:symbol"/> + <xs:element name="style-area" type="tns:area"/> + <xs:element name="style-line" type="tns:line"/> + <!-- <xs:element name="style-outline" type="tns:line" /> --> + </xs:choice> + </xs:sequence> + + <xs:choice maxOccurs="1" minOccurs="0"> + <xs:element name="atlas" type="tns:atlas"/> + </xs:choice> + + <!-- matching rules --> + <xs:sequence maxOccurs="unbounded" minOccurs="0"> + <xs:element name="m" type="tns:m"/> + </xs:sequence> + </xs:sequence> + <xs:attribute name="version" type="xs:positiveInteger" use="required"/> + <xs:attribute name="map-background" default="#ffffff" type="tns:color" use="optional"/> + <xs:attribute name="base-stroke-width" default="1" type="tns:nonNegativeFloat" + use="optional"/> + <xs:attribute name="base-text-scale" default="1" type="tns:nonNegativeFloat" + use="optional"/> + </xs:complexType> + + <!-- root element --> + <xs:element name="rendertheme" type="tns:rendertheme"/> +</xs:schema>