diff --git a/app/src/main/assets/omdb.config b/app/src/main/assets/omdb.config new file mode 100644 index 00000000..a6737381 --- /dev/null +++ b/app/src/main/assets/omdb.config @@ -0,0 +1,35 @@ +{ + "tables" : [ + { + "table": "OMDB_RD_LINK", + "code": 2001, + "name": "道路线" + }, + { + "table": "OMDB_RD_LINK_KIND", + "code": 2008, + "name": "道路种别" + }, + { + "table": "OMDB_RD_LINK_DIRECT", + "code": 2001, + "name": "道路方向" + }, + { + "table": "OMDB_SPEEDLIMIT_COND", + "code": 2001, + "name": "条件点限速" + }, + { + "table": "OMDB_SPEEDLIMIT_VAR", + "code": 2001, + "name": "可变点限速" + }, + { + "table": "OMDB_SPEEDLIMIT", + "code": 2001, + "name": "常规点限速" + } + + ] +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/Constant.kt b/app/src/main/java/com/navinfo/omqs/Constant.kt index 4bf4f215..fe075a01 100644 --- a/app/src/main/java/com/navinfo/omqs/Constant.kt +++ b/app/src/main/java/com/navinfo/omqs/Constant.kt @@ -19,6 +19,11 @@ class Constant { */ lateinit var DATA_PATH: String + /** + * 当前用户ID + */ + lateinit var CURRENT_USER_ID: String + /** * 离线地图目录 */ @@ -41,7 +46,7 @@ class Constant { const val message_version_right_off = "1" //立即发送 const val MESSAGE_PAGE_SIZE = 30 //消息列表一页最多数量 - lateinit var realm: Realm + const val OMDB_CONFIG = "omdb.config" } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt b/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt index 1531a3d9..8937ad93 100644 --- a/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt +++ b/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt @@ -22,18 +22,6 @@ class OMQSApplication : Application() { NetUtils.getInstance().init(this) TakePhotoManager.getInstance().init(this, 1) FileManager.initRootDir(this) - Realm.init(this) - val password = "encryp".encodeToByteArray().copyInto(ByteArray(64)) - // 656e6372797000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - Log.d("OMQSApplication", "密码是: ${byteArrayToHexString(password)}") - val config = RealmConfiguration.Builder() - .directory(File(Constant.DATA_PATH)) - .name("OMQS.realm") - .encryptionKey(password) - .modules(Realm.getDefaultModule(), MyRealmModule()) - .schemaVersion(1) - .build() - Realm.setDefaultConfiguration(config) } private fun getKey(inputString: String): String { @@ -42,7 +30,4 @@ class OMQSApplication : Application() { return hashBytes.joinToString("") { "%02x".format(it) }; } - fun byteArrayToHexString(byteArray: ByteArray): String { - return byteArray.joinToString("") { "%02x".format(it) } - } } \ 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 adef0eaa..666679c0 100644 --- a/app/src/main/java/com/navinfo/omqs/db/ImportOMDBHelper.kt +++ b/app/src/main/java/com/navinfo/omqs/db/ImportOMDBHelper.kt @@ -11,11 +11,13 @@ import com.blankj.utilcode.util.ZipUtils import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.navinfo.collect.library.data.entity.RenderEntity +import com.navinfo.omqs.Constant import com.navinfo.omqs.bean.ImportConfig import com.navinfo.omqs.hilt.ImportOMDBHiltFactory import com.navinfo.omqs.hilt.OMDBDataBaseHiltFactory import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import io.realm.Realm import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -28,13 +30,13 @@ import kotlin.streams.toList /** * 导入omdb数据的帮助类 * */ -class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val context: Context,@Assisted("omdbFile") val omdbFile: File,@Assisted("configFile") val configFile: 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.DATA_PATH}/${Constant.CURRENT_USER_ID}", Constant.OMDB_CONFIG) /** * 读取config的配置文件 @@ -99,10 +101,10 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont * @param omdbZipFile omdb数据抽取生成的Zip文件 * @param configFile 对应的配置文件 * */ - suspend fun importOmdbZipFile(omdbZipFile: File): Flow<List<Map<String, Any>>> = withContext(Dispatchers.IO) { + suspend fun importOmdbZipFile(omdbZipFile: File): Flow<String> = withContext(Dispatchers.IO) { val importConfig = openConfigFile() val unZipFolder = File(omdbZipFile.parentFile, "result") - flow<List<Map<String, Any>>> { + flow<String> { if (unZipFolder.exists()) { unZipFolder.deleteRecursively() } @@ -110,26 +112,45 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont // 开始解压zip文件 val unZipFiles = ZipUtils.unzipFile(omdbZipFile, unZipFolder) // 遍历解压后的文件,读取该数据返回 - for (txtFile in unZipFiles) { - val listResult: MutableList<Map<String, Any>> = mutableListOf() - // 根据文件名称获取对应的配置 - val currentConfig=importConfig.tables.find { - txtFile.name.substring(0, txtFile.name.lastIndexOf("."))==it.table + for ((index, currentConfig) in importConfig.tables.withIndex()) { + val txtFile = unZipFiles.find { + it.name == currentConfig.table } - val list = FileIOUtils.readFile2List(txtFile, "UTF-8") - // 将list数据转换为map - for (line in list) { - val map = gson.fromJson<Map<String, Any>>(line, object : TypeToken<MutableMap<String, Any>>() {}.type) - .toMutableMap() - currentConfig?.let { - map["QItable"] = currentConfig.table - map["QIname"] = currentConfig.name - map["QIcode"] = currentConfig.code - listResult.add(map) + + val listResult: MutableList<Map<String, Any>> = mutableListOf() + currentConfig?.let { + val list = FileIOUtils.readFile2List(txtFile, "UTF-8") + if (list!=null) { + // 将list数据转换为map + for (line in list) { + val map = gson.fromJson<Map<String, Any>>(line, object : TypeToken<MutableMap<String, Any>>() {}.type) + .toMutableMap() + map["QItable"] = currentConfig.table + map["QIname"] = currentConfig.name + map["QIcode"] = 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() + // 其他数据插入到Properties中 + renderEntity.geometry = map["geometry"].toString() + for (entry in map) { + renderEntity.properties[entry.key] = entry.value.toString() + } + Realm.getDefaultInstance().insert(renderEntity) + } + Realm.getDefaultInstance().commitTransaction() // 1个文件发送一次flow流 - emit(listResult) + emit("${index+1}/${importConfig.tables.size}") } } } diff --git a/app/src/main/java/com/navinfo/omqs/hilt/ImportOMDBHiltFactory.kt b/app/src/main/java/com/navinfo/omqs/hilt/ImportOMDBHiltFactory.kt index 5f3ff4fd..a1036dc0 100644 --- a/app/src/main/java/com/navinfo/omqs/hilt/ImportOMDBHiltFactory.kt +++ b/app/src/main/java/com/navinfo/omqs/hilt/ImportOMDBHiltFactory.kt @@ -8,5 +8,5 @@ import java.io.File @AssistedFactory interface ImportOMDBHiltFactory { - fun obtainImportOMDBHelper(@Assisted("context")context: Context, @Assisted("omdbFile") omdbFile: File, @Assisted("configFile") dbVersion: File): ImportOMDBHelper + fun obtainImportOMDBHelper(@Assisted("context")context: Context, @Assisted("omdbFile") omdbFile: File): ImportOMDBHelper } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/PermissionsActivity.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/PermissionsActivity.kt index 36ec694e..cf18f7fb 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/PermissionsActivity.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/PermissionsActivity.kt @@ -10,7 +10,7 @@ import com.hjq.permissions.XXPermissions /** * 权限申请Activity */ -open class PermissionsActivity : BaseActivity() { +open abstract class PermissionsActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val permissionList = mutableListOf<String>() @@ -50,10 +50,10 @@ open class PermissionsActivity : BaseActivity() { Toast.LENGTH_SHORT ) .show() - onPermissionsGranted() + onPermissionsDenied() return } else { - onPermissionsDenied() + onPermissionsGranted() } // 在SD卡创建项目目录 } @@ -79,14 +79,10 @@ open class PermissionsActivity : BaseActivity() { /** * 权限全部同意 */ - open fun onPermissionsGranted() { - - } + open abstract fun onPermissionsGranted() /** * 权限 */ - open fun onPermissionsDenied() { - - } + open abstract fun onPermissionsDenied() } \ 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 d0bc2101..20bb6afb 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 @@ -7,14 +7,19 @@ import android.widget.Toast import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel 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.RoomAppDatabase import com.navinfo.omqs.http.NetResult import com.navinfo.omqs.http.NetworkService import com.navinfo.omqs.tools.FileManager import dagger.hilt.android.lifecycle.HiltViewModel +import io.realm.Realm +import io.realm.RealmConfiguration import kotlinx.coroutines.* import okio.IOException +import java.io.File import javax.inject.Inject enum class LoginStatus { @@ -110,8 +115,7 @@ class LoginViewModel @Inject constructor( //文件夹初始化 try { loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_INIT) - createUserFolder(context) - // 初始化Realm + createUserFolder(context, "1") } catch (e: IOException) { loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_FAILURE) } @@ -151,8 +155,28 @@ class LoginViewModel @Inject constructor( * 创建用户目录 */ @Throws(IOException::class) - private fun createUserFolder(context: Context) { + private fun createUserFolder(context: Context, userId: String) { // 在SD卡创建用户目录,解压资源等 + val userFolder = File("${Constant.DATA_PATH}/${userId}") + Constant.CURRENT_USER_ID = userId + // 初始化Realm + Realm.init(context.applicationContext) + val password = "encryp".encodeToByteArray().copyInto(ByteArray(64)) + // 656e6372797000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + Log.d("OMQSApplication", "密码是: ${byteArrayToHexString(password)}") + val config = RealmConfiguration.Builder() + .directory(userFolder) + .name("OMQS.realm") + .encryptionKey(password) +// .modules(Realm.getDefaultModule(), MyRealmModule()) + .schemaVersion(1) + .build() + Realm.setDefaultConfiguration(config) + // 拷贝配置文件到用户目录下 + val omdbConfigFile = File(userFolder.absolutePath, Constant.OMDB_CONFIG); + if (!omdbConfigFile.exists()) { + ResourceUtils.copyFileFromAssets(Constant.OMDB_CONFIG, omdbConfigFile.absolutePath) + } } /** @@ -169,4 +193,8 @@ class LoginViewModel @Inject constructor( super.onCleared() cancelLogin() } + + private fun byteArrayToHexString(byteArray: ByteArray): String { + return byteArray.joinToString("") { "%02x".format(it) } + } } \ No newline at end of file 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 77e85f60..b39d5cc0 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 @@ -17,6 +17,7 @@ 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 @@ -31,6 +32,7 @@ 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 @@ -48,6 +50,8 @@ class PersonalCenterFragment : BaseFragment(), FSAFActivityCallbacks { @Inject lateinit var importOMDBHiltFactory: ImportOMDBHiltFactory + @Inject + lateinit var niMapController: NIMapController override fun onCreateView( @@ -73,15 +77,14 @@ class PersonalCenterFragment : BaseFragment(), FSAFActivityCallbacks { val file = UriUtils.uri2File(uri) // 开始导入数据 // 656e6372797000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - val job = CoroutineUtils.launchWithLoading( + CoroutineUtils.launchWithLoading( requireContext(), loadingMessage = "生成数据..." ) { val importOMDBHelper: ImportOMDBHelper = importOMDBHiltFactory.obtainImportOMDBHelper( requireContext(), - file, - File(file.parentFile, "config.json") + file ) viewModel.obtainOMDBZipData(importOMDBHelper) } @@ -95,19 +98,19 @@ class PersonalCenterFragment : BaseFragment(), FSAFActivityCallbacks { override fun onResult(uri: Uri) { val file = UriUtils.uri2File(uri) - // 开始导入数据 - CoroutineUtils.launchWithLoading( - requireContext(), - loadingMessage = "导入数据..." - ) { - val importOMDBHelper: ImportOMDBHelper = - importOMDBHiltFactory.obtainImportOMDBHelper( - requireContext(), - file, - File(file.parentFile, "config.json") - ) + val importOMDBHelper: ImportOMDBHelper = + importOMDBHiltFactory.obtainImportOMDBHelper( + requireContext(), + file + ) viewModel.importOMDBData(importOMDBHelper) - } +// // 开始导入数据 +// CoroutineUtils.launchWithLoading( +// requireContext(), +// loadingMessage = "导入数据..." +// ) { +// +// } } }) } @@ -125,6 +128,8 @@ class PersonalCenterFragment : BaseFragment(), FSAFActivityCallbacks { } R.id.personal_center_menu_test -> { viewModel.readRealmData() + // 定位到指定位置 + niMapController.mMapView.vtmMap.animator().animateTo(GeoPoint(28.608398, 115.67901)) } R.id.personal_center_menu_task_list -> { findNavController().navigate(R.id.TaskListFragment) diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt index 24a5bdb2..19106b80 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt @@ -10,6 +10,7 @@ import com.blankj.utilcode.util.UriUtils import com.blankj.utilcode.util.ZipUtils import com.google.gson.Gson import com.navinfo.collect.library.data.entity.* +import com.navinfo.collect.library.map.NIMapController import com.navinfo.collect.library.utils.GeometryTools import com.navinfo.omqs.bean.ScProblemTypeBean import com.navinfo.omqs.bean.ScRootCauseAnalysisBean @@ -135,39 +136,15 @@ class PersonalCenterViewModel @Inject constructor( /** * 导入OMDB数据 * */ - suspend fun importOMDBData(importOMDBHelper: ImportOMDBHelper) { - Log.d("OMQSApplication", "开始导入数据") -// Realm.getDefaultInstance().beginTransaction() - importOMDBHelper.importOmdbZipFile(importOMDBHelper.omdbFile).collect { - Realm.getDefaultInstance().beginTransaction() - for (map in it) { // 每一个map就是Realm的一条数据 - val renderEntity = RenderEntity() - renderEntity.code = map["QIcode"].toString().toInt() - renderEntity.name = map["QIname"].toString() - renderEntity.table = map["QItable"].toString() - // 其他数据插入到Properties中 - renderEntity.geometry = map["GEOMETRY"].toString() - for (entry in map) { - renderEntity.properties[entry.key] = entry.value.toString() - } - Realm.getDefaultInstance().insert(renderEntity) + fun importOMDBData(importOMDBHelper: ImportOMDBHelper) { + viewModelScope.launch(Dispatchers.IO) { + Log.d("OMQSApplication", "开始导入数据") + importOMDBHelper.importOmdbZipFile(importOMDBHelper.omdbFile).collect { + Log.d("importOMDBData", it) } - Realm.getDefaultInstance().commitTransaction() + Log.d("OMQSApplication", "导入数据完成") } -// Realm.getDefaultInstance().commitTransaction() -// val gson = Gson() -// // 数据导入结束后,开始生成渲染表所需的json文件,并生成压缩包 -// for (table in importOMDBHelper.openConfigFile().tables/*listOf<String>("HAD_LINK")*/) { -// val omdbList = Realm.getDefaultInstance().where(OMDBEntity::class.java).equalTo("table", table.table).findAll() -// val outputFile = File(importOMDBHelper.omdbFile, "${table.table}.txt") -// // 将读取到的数据转换为json数据文件 -// for (omdb in omdbList) { -// FileIOUtils.writeFileFromString(outputFile, gson.toJson(omdb)) -// } -// } - - Log.d("OMQSApplication", "导入数据完成") } fun importScProblemData(uri: Uri) { diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/RenderEntity.kt b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/RenderEntity.kt index b4842cb2..3f7f0df0 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/RenderEntity.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/RenderEntity.kt @@ -23,7 +23,10 @@ open class RenderEntity(): RealmObject() { lateinit var table: String //要素表名 var code: Int = 0 // 要素编码 var geometry: String = "" - get() = field + get() { + wkt = GeometryTools.createGeometry(field) + return field + } set(value) { field = value // 根据geometry自动计算当前要素的x-tile和y-tile @@ -39,8 +42,8 @@ open class RenderEntity(): RealmObject() { @Ignore var wkt: Geometry? = null var properties: RealmDictionary<String?> = RealmDictionary() - val tileX: RealmSet<Int> = RealmSet() // x方向的tile编码 - val tileY: RealmSet<Int> = RealmSet() // y方向的tile编码 + var tileX: RealmSet<Int> = RealmSet() // x方向的tile编码 + var tileY: RealmSet<Int> = RealmSet() // y方向的tile编码 constructor(name: String, properties: RealmDictionary<String?>): this() { this.name = name 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 86fde18a..3b419ed5 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 @@ -16,6 +16,7 @@ import com.navinfo.collect.library.map.cluster.ClusterMarkerRenderer import com.navinfo.collect.library.map.layers.MyItemizedLayer import com.navinfo.collect.library.map.source.MapLifeNiLocationTileSource import com.navinfo.collect.library.map.source.NavinfoMultiMapFileTileSource +import com.navinfo.collect.library.map.source.OMDBTileSource import com.navinfo.collect.library.system.Constant import com.navinfo.collect.library.utils.GeometryTools import io.realm.Realm @@ -40,6 +41,7 @@ import org.oscim.layers.tile.buildings.BuildingLayer import org.oscim.layers.tile.vector.VectorTileLayer import org.oscim.layers.tile.vector.labeling.LabelLayer import org.oscim.layers.tile.vector.labeling.LabelTileLoaderHook +import org.oscim.map.Map import org.oscim.map.Map.UpdateListener import org.oscim.tiling.source.OkHttpEngine.OkHttpFactory import org.oscim.tiling.source.mapfile.MapFileTileSource @@ -69,6 +71,15 @@ open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView,tr */ private lateinit var labelNiLocationLayer: LabelLayer + /** + * 显示待测评OMDB数据的图层 + * */ + private lateinit var omdbVectorTileLayer: VectorTileLayer + private lateinit var omdbLabelLayer: LabelLayer + /** + * 文字大小 + */ + private val NUM_13 = 13 init { initMap() @@ -81,6 +92,8 @@ open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView,tr loadBaseMap() + initOMDBVectorTileLayer() + mapLifeNiLocationTileSource = MapLifeNiLocationTileSource(mContext, mTracePath) vectorNiLocationTileLayer = VectorTileLayer(mMapView.vtmMap, mapLifeNiLocationTileSource) @@ -105,6 +118,17 @@ open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView,tr } + private fun initOMDBVectorTileLayer() { + val omdbTileSource: OMDBTileSource = OMDBTileSource() + omdbVectorTileLayer = VectorTileLayer(mMapView.vtmMap, omdbTileSource) + omdbLabelLayer = LabelLayer(mMapView.vtmMap, omdbVectorTileLayer, LabelTileLoaderHook(), Constant.OMDB_MIN_ZOOM) + if(omdbVectorTileLayer!=null){ + addLayer(omdbVectorTileLayer,NIMapView.LAYER_GROUPS.VECTOR_TILE) + } + if(omdbLabelLayer!=null){ + addLayer(omdbLabelLayer, NIMapView.LAYER_GROUPS.VECTOR_TILE) + } + } /** * 切换基础底图样式 diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/source/OMDBDataDecoder.java b/collect-library/src/main/java/com/navinfo/collect/library/map/source/OMDBDataDecoder.java new file mode 100644 index 00000000..17111dc6 --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/source/OMDBDataDecoder.java @@ -0,0 +1,190 @@ +package com.navinfo.collect.library.map.source; + +import static org.oscim.core.MercatorProjection.latitudeToY; +import static org.oscim.core.MercatorProjection.longitudeToX; + +import android.os.Build; +import android.util.Log; + +import androidx.annotation.RequiresApi; + +import com.navinfo.collect.library.data.entity.GeometryFeatureEntity; +import com.navinfo.collect.library.data.entity.RenderEntity; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.MultiLineString; +import org.locationtech.jts.geom.MultiPoint; +import org.locationtech.jts.geom.MultiPolygon; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKBReader; +import org.oscim.core.MapElement; +import org.oscim.core.Tag; +import org.oscim.core.Tile; +import org.oscim.tiling.ITileDataSink; +import org.oscim.tiling.source.mvt.TileDecoder; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class OMDBDataDecoder extends TileDecoder { + private final String mLocale; + + private static final float REF_TILE_SIZE = 4096.0f; + + private final GeometryFactory mGeomFactory; + private final MapElement mMapElement; + private ITileDataSink mTileDataSink; + private double mTileY, mTileX, mTileScale; + + public OMDBDataDecoder() { + super(); + mLocale = ""; + mGeomFactory = new GeometryFactory(); + mMapElement = new MapElement(); + mMapElement.layer = 5; + } + + @Override + public boolean decode(Tile tile, ITileDataSink sink, InputStream is) throws IOException { + mTileDataSink = sink; + mTileScale = 1 << tile.zoomLevel; + mTileX = tile.tileX / mTileScale; + mTileY = tile.tileY / mTileScale; + mTileScale *= Tile.SIZE; + return true; + } + + @RequiresApi(api = Build.VERSION_CODES.N) + public boolean decode(Tile tile, ITileDataSink sink, List<RenderEntity> listResult) { + mTileDataSink = sink; + mTileScale = 1 << tile.zoomLevel; + mTileX = tile.tileX / mTileScale; + mTileY = tile.tileY / mTileScale; + mTileScale *= Tile.SIZE; + + listResult.stream().iterator().forEachRemaining(new Consumer<RenderEntity>() { + @Override + public void accept(RenderEntity renderEntity) { + Log.d("RealmDBTileDataSource", renderEntity.getGeometry()); + Map<String, Object> properties= new HashMap<>(renderEntity.getProperties().size()); + properties.putAll(renderEntity.getProperties()); + parseGeometry(renderEntity.getTable(), renderEntity.getWkt(), properties); + } + }); + return true; + } + + public void parseGeometry(String layerName, Geometry geometry, Map<String, Object> tags) { + mMapElement.clear(); + mMapElement.tags.clear(); + + parseTags(tags, layerName); + if (mMapElement.tags.size() == 0) { + return; + } + + boolean err = false; + if (geometry instanceof Point) { + mMapElement.startPoints(); + processCoordinateArray(geometry.getCoordinates(), false); + } else if (geometry instanceof MultiPoint) { + MultiPoint multiPoint = (MultiPoint) geometry; + for (int i = 0; i < multiPoint.getNumGeometries(); i++) { + mMapElement.startPoints(); + processCoordinateArray(multiPoint.getGeometryN(i).getCoordinates(), false); + } + } else if (geometry instanceof LineString) { + processLineString((LineString) geometry); + } else if (geometry instanceof MultiLineString) { + MultiLineString multiLineString = (MultiLineString) geometry; + for (int i = 0; i < multiLineString.getNumGeometries(); i++) { + processLineString((LineString) multiLineString.getGeometryN(i)); + } + } else if (geometry instanceof Polygon) { + Polygon polygon = (Polygon) geometry; + processPolygon(polygon); + } else if (geometry instanceof MultiPolygon) { + MultiPolygon multiPolygon = (MultiPolygon) geometry; + for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { + processPolygon((Polygon) multiPolygon.getGeometryN(i)); + } + } else { + err = true; + } + + if (!err) { + mTileDataSink.process(mMapElement); + } + } + + private void processLineString(LineString lineString) { + mMapElement.startLine(); + processCoordinateArray(lineString.getCoordinates(), false); + } + + private void processPolygon(Polygon polygon) { + mMapElement.startPolygon(); + processCoordinateArray(polygon.getExteriorRing().getCoordinates(), true); + for (int i = 0; i < polygon.getNumInteriorRing(); i++) { + mMapElement.startHole(); + processCoordinateArray(polygon.getInteriorRingN(i).getCoordinates(), true); + } + } + + private void processCoordinateArray(Coordinate[] coordinates, boolean removeLast) { + int length = removeLast ? coordinates.length - 1 : coordinates.length; + for (int i = 0; i < length; i++) { + mMapElement.addPoint((float) ((longitudeToX(coordinates[i].x) - mTileX) * mTileScale), + (float) ((latitudeToY(coordinates[i].y) - mTileY) * mTileScale)); + } + +// int length = removeLast ? coordinates.length - 1 : coordinates.length; +// // 初始化3D数据类型 +// float[] point3D = new float[coordinates.length*3]; +// for (int i = 0; i < length; i++) { +// point3D[i*3] = (float) coordinates[i].x; +// point3D[(i*3)+1] = (float) coordinates[i].y; +// point3D[(i*3)+2] = (float) coordinates[i].z; +// } +// mMapElement.points = point3D; +// mMapElement.pointNextPos = mMapElement.points.length; +// mMapElement.type = GeometryBuffer.GeometryType.TRIS; + } + + private void parseTags(Map<String, Object> map, String layerName) { + mMapElement.tags.add(new Tag("layer", layerName)); + boolean hasName = false; + String fallbackName = null; + for (Map.Entry<String, Object> entry : map.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + String val = (value instanceof String) ? (String) value : String.valueOf(value); + if (key.startsWith(Tag.KEY_NAME)) { + int len = key.length(); + if (len == 4) { + fallbackName = val; + continue; + } + if (len < 7) + continue; + if (mLocale.equals(key.substring(5))) { + hasName = true; + mMapElement.tags.add(new Tag(Tag.KEY_NAME, val, false)); + } + } else { + mMapElement.tags.add(new Tag(key, val)); + } + } + if (!hasName && fallbackName != null) + mMapElement.tags.add(new Tag(Tag.KEY_NAME, fallbackName, false)); + } +} diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/source/OMDBTileDataSource.java b/collect-library/src/main/java/com/navinfo/collect/library/map/source/OMDBTileDataSource.java new file mode 100644 index 00000000..31f2291a --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/source/OMDBTileDataSource.java @@ -0,0 +1,74 @@ +package com.navinfo.collect.library.map.source; + +import android.os.Build; +import android.util.Log; + +import androidx.annotation.RequiresApi; + +import com.navinfo.collect.library.data.RealmUtils; +import com.navinfo.collect.library.data.entity.GeometryFeatureEntity; +import com.navinfo.collect.library.data.entity.RenderEntity; +import com.navinfo.collect.library.system.Constant; + +import org.oscim.layers.tile.MapTile; +import org.oscim.tiling.ITileDataSink; +import org.oscim.tiling.ITileDataSource; +import org.oscim.tiling.QueryResult; + +import java.util.List; + +import io.realm.Realm; +import io.realm.RealmQuery; + +public class OMDBTileDataSource implements ITileDataSource { + private final ThreadLocal<OMDBDataDecoder> mThreadLocalDecoders = new ThreadLocal<OMDBDataDecoder>() { + @Override + protected OMDBDataDecoder initialValue() { + return new OMDBDataDecoder(); + } + }; + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public void query(MapTile tile, ITileDataSink mapDataSink) { + // 获取tile对应的坐标范围 + if (tile.zoomLevel>=Constant.OMDB_MIN_ZOOM&&tile.zoomLevel<=Constant.OVER_ZOOM) { + int m = Constant.OVER_ZOOM-tile.zoomLevel; + int xStart = (int)tile.tileX<<m; + int xEnd = (int)((tile.tileX+1)<<m); + int yStart = (int)tile.tileY<<m; + int yEnd = (int)((tile.tileY+1)<<m); + + RealmQuery<RenderEntity> realmQuery = Realm.getDefaultInstance().where(RenderEntity.class) + .rawPredicate("tileX>="+xStart+" and tileX<="+xEnd+" and tileY>="+yStart+" and tileY<="+yEnd); +// // 筛选不显示的数据 +// if (Constant.HAD_LAYER_INVISIABLE_ARRAY!=null&&Constant.HAD_LAYER_INVISIABLE_ARRAY.length>0) { +// realmQuery.beginGroup(); +// for (String type: Constant.HAD_LAYER_INVISIABLE_ARRAY) { +// realmQuery.notEqualTo("name", type); +// } +// realmQuery.endGroup(); +// } + List<RenderEntity> listResult = realmQuery/*.distinct("id")*/.findAll(); + if (!listResult.isEmpty()) { + mThreadLocalDecoders.get().decode(tile, mapDataSink, listResult); + } + mapDataSink.completed(QueryResult.SUCCESS); + Log.d("RealmDBTileDataSource", "tile:"+tile.getBoundingBox().toString()); + } else { + mapDataSink.completed(QueryResult.SUCCESS); + } + } + + @Override + public void dispose() { + + } + + @Override + public void cancel() { + if (Realm.getDefaultInstance().isInTransaction()) { + Realm.getDefaultInstance().cancelTransaction(); + } + } +} diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/source/OMDBTileSource.java b/collect-library/src/main/java/com/navinfo/collect/library/map/source/OMDBTileSource.java new file mode 100644 index 00000000..2ac45ac9 --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/source/OMDBTileSource.java @@ -0,0 +1,25 @@ +package com.navinfo.collect.library.map.source; + +import com.navinfo.collect.library.system.Constant; + +import org.oscim.tiling.ITileDataSource; +import org.oscim.tiling.OverzoomTileDataSource; +import org.oscim.tiling.TileSource; + +public class OMDBTileSource extends TileSource { + + @Override + public ITileDataSource getDataSource() { + return new OverzoomTileDataSource(new OMDBTileDataSource(), Constant.OVER_ZOOM); + } + + @Override + public OpenResult open() { + return OpenResult.SUCCESS; + } + + @Override + public void close() { + + } +} diff --git a/collect-library/src/main/java/com/navinfo/collect/library/system/Constant.java b/collect-library/src/main/java/com/navinfo/collect/library/system/Constant.java index ab5e683d..104b9932 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/system/Constant.java +++ b/collect-library/src/main/java/com/navinfo/collect/library/system/Constant.java @@ -31,5 +31,6 @@ public class Constant { public static String[] HAD_LAYER_INVISIABLE_ARRAY; public static final int OVER_ZOOM = 21; public static final int MAX_ZOOM = 25; + public static final int OMDB_MIN_ZOOM = 18; }