Merge branch 'master' of gitlab.navinfo.com:CollectVehicle/OneMapQS

 Conflicts:
	collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt
This commit is contained in:
squallzhjch 2023-04-24 15:36:25 +08:00
commit 385e0032c2
15 changed files with 466 additions and 97 deletions

View File

@ -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": "常规点限速"
}
]
}

View File

@ -19,6 +19,11 @@ class Constant {
*/ */
lateinit var DATA_PATH: String 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_version_right_off = "1" //立即发送
const val MESSAGE_PAGE_SIZE = 30 //消息列表一页最多数量 const val MESSAGE_PAGE_SIZE = 30 //消息列表一页最多数量
lateinit var realm: Realm const val OMDB_CONFIG = "omdb.config"
} }
} }

View File

@ -22,18 +22,6 @@ class OMQSApplication : Application() {
NetUtils.getInstance().init(this) NetUtils.getInstance().init(this)
TakePhotoManager.getInstance().init(this, 1) TakePhotoManager.getInstance().init(this, 1)
FileManager.initRootDir(this) 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 { private fun getKey(inputString: String): String {
@ -42,7 +30,4 @@ class OMQSApplication : Application() {
return hashBytes.joinToString("") { "%02x".format(it) }; return hashBytes.joinToString("") { "%02x".format(it) };
} }
fun byteArrayToHexString(byteArray: ByteArray): String {
return byteArray.joinToString("") { "%02x".format(it) }
}
} }

View File

@ -11,11 +11,13 @@ import com.blankj.utilcode.util.ZipUtils
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.navinfo.collect.library.data.entity.RenderEntity import com.navinfo.collect.library.data.entity.RenderEntity
import com.navinfo.omqs.Constant
import com.navinfo.omqs.bean.ImportConfig import com.navinfo.omqs.bean.ImportConfig
import com.navinfo.omqs.hilt.ImportOMDBHiltFactory import com.navinfo.omqs.hilt.ImportOMDBHiltFactory
import com.navinfo.omqs.hilt.OMDBDataBaseHiltFactory import com.navinfo.omqs.hilt.OMDBDataBaseHiltFactory
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.realm.Realm
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
@ -28,13 +30,13 @@ import kotlin.streams.toList
/** /**
* 导入omdb数据的帮助类 * 导入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 @Inject
lateinit var omdbHiltFactory: OMDBDataBaseHiltFactory lateinit var omdbHiltFactory: OMDBDataBaseHiltFactory
@Inject @Inject
lateinit var gson: Gson lateinit var gson: Gson
private val database by lazy { omdbHiltFactory.obtainOmdbDataBaseHelper(context, omdbFile.absolutePath, 1).writableDatabase } 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的配置文件 * 读取config的配置文件
@ -99,10 +101,10 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont
* @param omdbZipFile omdb数据抽取生成的Zip文件 * @param omdbZipFile omdb数据抽取生成的Zip文件
* @param configFile 对应的配置文件 * @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 importConfig = openConfigFile()
val unZipFolder = File(omdbZipFile.parentFile, "result") val unZipFolder = File(omdbZipFile.parentFile, "result")
flow<List<Map<String, Any>>> { flow<String> {
if (unZipFolder.exists()) { if (unZipFolder.exists()) {
unZipFolder.deleteRecursively() unZipFolder.deleteRecursively()
} }
@ -110,26 +112,45 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont
// 开始解压zip文件 // 开始解压zip文件
val unZipFiles = ZipUtils.unzipFile(omdbZipFile, unZipFolder) val unZipFiles = ZipUtils.unzipFile(omdbZipFile, unZipFolder)
// 遍历解压后的文件,读取该数据返回 // 遍历解压后的文件,读取该数据返回
for (txtFile in unZipFiles) { for ((index, currentConfig) in importConfig.tables.withIndex()) {
val listResult: MutableList<Map<String, Any>> = mutableListOf() val txtFile = unZipFiles.find {
// 根据文件名称获取对应的配置 it.name == currentConfig.table
val currentConfig=importConfig.tables.find {
txtFile.name.substring(0, txtFile.name.lastIndexOf("."))==it.table
} }
val listResult: MutableList<Map<String, Any>> = mutableListOf()
currentConfig?.let {
val list = FileIOUtils.readFile2List(txtFile, "UTF-8") val list = FileIOUtils.readFile2List(txtFile, "UTF-8")
if (list!=null) {
// 将list数据转换为map // 将list数据转换为map
for (line in list) { 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<MutableMap<String, Any>>() {}.type)
.toMutableMap() .toMutableMap()
currentConfig?.let {
map["QItable"] = currentConfig.table map["QItable"] = currentConfig.table
map["QIname"] = currentConfig.name map["QIname"] = currentConfig.name
map["QIcode"] = currentConfig.code map["QIcode"] = currentConfig.code
listResult.add(map) 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流 // 1个文件发送一次flow流
emit(listResult) emit("${index+1}/${importConfig.tables.size}")
} }
} }
} }

View File

@ -8,5 +8,5 @@ import java.io.File
@AssistedFactory @AssistedFactory
interface ImportOMDBHiltFactory { 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
} }

View File

@ -10,7 +10,7 @@ import com.hjq.permissions.XXPermissions
/** /**
* 权限申请Activity * 权限申请Activity
*/ */
open class PermissionsActivity : BaseActivity() { open abstract class PermissionsActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val permissionList = mutableListOf<String>() val permissionList = mutableListOf<String>()
@ -50,10 +50,10 @@ open class PermissionsActivity : BaseActivity() {
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
) )
.show() .show()
onPermissionsGranted() onPermissionsDenied()
return return
} else { } else {
onPermissionsDenied() onPermissionsGranted()
} }
// 在SD卡创建项目目录 // 在SD卡创建项目目录
} }
@ -79,14 +79,10 @@ open class PermissionsActivity : BaseActivity() {
/** /**
* 权限全部同意 * 权限全部同意
*/ */
open fun onPermissionsGranted() { open abstract fun onPermissionsGranted()
}
/** /**
* 权限 * 权限
*/ */
open fun onPermissionsDenied() { open abstract fun onPermissionsDenied()
}
} }

View File

@ -7,14 +7,19 @@ import android.widget.Toast
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.bean.LoginUserBean
import com.navinfo.omqs.db.RoomAppDatabase import com.navinfo.omqs.db.RoomAppDatabase
import com.navinfo.omqs.http.NetResult import com.navinfo.omqs.http.NetResult
import com.navinfo.omqs.http.NetworkService import com.navinfo.omqs.http.NetworkService
import com.navinfo.omqs.tools.FileManager import com.navinfo.omqs.tools.FileManager
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import io.realm.Realm
import io.realm.RealmConfiguration
import kotlinx.coroutines.* import kotlinx.coroutines.*
import okio.IOException import okio.IOException
import java.io.File
import javax.inject.Inject import javax.inject.Inject
enum class LoginStatus { enum class LoginStatus {
@ -110,8 +115,7 @@ class LoginViewModel @Inject constructor(
//文件夹初始化 //文件夹初始化
try { try {
loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_INIT) loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_INIT)
createUserFolder(context) createUserFolder(context, "1")
// 初始化Realm
} catch (e: IOException) { } catch (e: IOException) {
loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_FAILURE) loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_FAILURE)
} }
@ -151,8 +155,28 @@ class LoginViewModel @Inject constructor(
* 创建用户目录 * 创建用户目录
*/ */
@Throws(IOException::class) @Throws(IOException::class)
private fun createUserFolder(context: Context) { private fun createUserFolder(context: Context, userId: String) {
// 在SD卡创建用户目录解压资源等 // 在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() super.onCleared()
cancelLogin() cancelLogin()
} }
private fun byteArrayToHexString(byteArray: ByteArray): String {
return byteArray.joinToString("") { "%02x".format(it) }
}
} }

View File

@ -17,6 +17,7 @@ import com.github.k1rakishou.fsaf.callback.FSAFActivityCallbacks
import com.github.k1rakishou.fsaf.callback.FileChooserCallback import com.github.k1rakishou.fsaf.callback.FileChooserCallback
import com.navinfo.collect.library.data.RealmUtils import com.navinfo.collect.library.data.RealmUtils
import com.navinfo.collect.library.data.entity.OMDBEntity import com.navinfo.collect.library.data.entity.OMDBEntity
import com.navinfo.collect.library.map.NIMapController
import com.navinfo.omqs.R import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.FragmentPersonalCenterBinding import com.navinfo.omqs.databinding.FragmentPersonalCenterBinding
import com.navinfo.omqs.db.ImportOMDBHelper import com.navinfo.omqs.db.ImportOMDBHelper
@ -31,6 +32,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.oscim.core.GeoPoint
import java.io.File import java.io.File
import java.util.UUID import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
@ -48,6 +50,8 @@ class PersonalCenterFragment : BaseFragment(), FSAFActivityCallbacks {
@Inject @Inject
lateinit var importOMDBHiltFactory: ImportOMDBHiltFactory lateinit var importOMDBHiltFactory: ImportOMDBHiltFactory
@Inject
lateinit var niMapController: NIMapController
override fun onCreateView( override fun onCreateView(
@ -73,15 +77,14 @@ class PersonalCenterFragment : BaseFragment(), FSAFActivityCallbacks {
val file = UriUtils.uri2File(uri) val file = UriUtils.uri2File(uri)
// 开始导入数据 // 开始导入数据
// 656e6372797000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 // 656e6372797000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
val job = CoroutineUtils.launchWithLoading( CoroutineUtils.launchWithLoading(
requireContext(), requireContext(),
loadingMessage = "生成数据..." loadingMessage = "生成数据..."
) { ) {
val importOMDBHelper: ImportOMDBHelper = val importOMDBHelper: ImportOMDBHelper =
importOMDBHiltFactory.obtainImportOMDBHelper( importOMDBHiltFactory.obtainImportOMDBHelper(
requireContext(), requireContext(),
file, file
File(file.parentFile, "config.json")
) )
viewModel.obtainOMDBZipData(importOMDBHelper) viewModel.obtainOMDBZipData(importOMDBHelper)
} }
@ -95,19 +98,19 @@ class PersonalCenterFragment : BaseFragment(), FSAFActivityCallbacks {
override fun onResult(uri: Uri) { override fun onResult(uri: Uri) {
val file = UriUtils.uri2File(uri) val file = UriUtils.uri2File(uri)
// 开始导入数据
CoroutineUtils.launchWithLoading(
requireContext(),
loadingMessage = "导入数据..."
) {
val importOMDBHelper: ImportOMDBHelper = val importOMDBHelper: ImportOMDBHelper =
importOMDBHiltFactory.obtainImportOMDBHelper( importOMDBHiltFactory.obtainImportOMDBHelper(
requireContext(), requireContext(),
file, file
File(file.parentFile, "config.json")
) )
viewModel.importOMDBData(importOMDBHelper) viewModel.importOMDBData(importOMDBHelper)
} // // 开始导入数据
// CoroutineUtils.launchWithLoading(
// requireContext(),
// loadingMessage = "导入数据..."
// ) {
//
// }
} }
}) })
} }
@ -125,6 +128,8 @@ class PersonalCenterFragment : BaseFragment(), FSAFActivityCallbacks {
} }
R.id.personal_center_menu_test -> { R.id.personal_center_menu_test -> {
viewModel.readRealmData() viewModel.readRealmData()
// 定位到指定位置
niMapController.mMapView.vtmMap.animator().animateTo(GeoPoint(28.608398, 115.67901))
} }
R.id.personal_center_menu_task_list -> { R.id.personal_center_menu_task_list -> {
findNavController().navigate(R.id.TaskListFragment) findNavController().navigate(R.id.TaskListFragment)

View File

@ -10,6 +10,7 @@ import com.blankj.utilcode.util.UriUtils
import com.blankj.utilcode.util.ZipUtils import com.blankj.utilcode.util.ZipUtils
import com.google.gson.Gson import com.google.gson.Gson
import com.navinfo.collect.library.data.entity.* import com.navinfo.collect.library.data.entity.*
import com.navinfo.collect.library.map.NIMapController
import com.navinfo.collect.library.utils.GeometryTools import com.navinfo.collect.library.utils.GeometryTools
import com.navinfo.omqs.bean.ScProblemTypeBean import com.navinfo.omqs.bean.ScProblemTypeBean
import com.navinfo.omqs.bean.ScRootCauseAnalysisBean import com.navinfo.omqs.bean.ScRootCauseAnalysisBean
@ -135,41 +136,17 @@ class PersonalCenterViewModel @Inject constructor(
/** /**
* 导入OMDB数据 * 导入OMDB数据
* */ * */
suspend fun importOMDBData(importOMDBHelper: ImportOMDBHelper) { fun importOMDBData(importOMDBHelper: ImportOMDBHelper) {
viewModelScope.launch(Dispatchers.IO) {
Log.d("OMQSApplication", "开始导入数据") Log.d("OMQSApplication", "开始导入数据")
// Realm.getDefaultInstance().beginTransaction()
importOMDBHelper.importOmdbZipFile(importOMDBHelper.omdbFile).collect { importOMDBHelper.importOmdbZipFile(importOMDBHelper.omdbFile).collect {
Realm.getDefaultInstance().beginTransaction() Log.d("importOMDBData", it)
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)
}
Realm.getDefaultInstance().commitTransaction()
}
// 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", "导入数据完成") Log.d("OMQSApplication", "导入数据完成")
} }
}
fun importScProblemData(uri: Uri) { fun importScProblemData(uri: Uri) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { try {

View File

@ -23,7 +23,10 @@ open class RenderEntity(): RealmObject() {
lateinit var table: String //要素表名 lateinit var table: String //要素表名
var code: Int = 0 // 要素编码 var code: Int = 0 // 要素编码
var geometry: String = "" var geometry: String = ""
get() = field get() {
wkt = GeometryTools.createGeometry(field)
return field
}
set(value) { set(value) {
field = value field = value
// 根据geometry自动计算当前要素的x-tile和y-tile // 根据geometry自动计算当前要素的x-tile和y-tile
@ -39,8 +42,8 @@ open class RenderEntity(): RealmObject() {
@Ignore @Ignore
var wkt: Geometry? = null var wkt: Geometry? = null
var properties: RealmDictionary<String?> = RealmDictionary() var properties: RealmDictionary<String?> = RealmDictionary()
val tileX: RealmSet<Int> = RealmSet() // x方向的tile编码 var tileX: RealmSet<Int> = RealmSet() // x方向的tile编码
val tileY: RealmSet<Int> = RealmSet() // y方向的tile编码 var tileY: RealmSet<Int> = RealmSet() // y方向的tile编码
constructor(name: String, properties: RealmDictionary<String?>): this() { constructor(name: String, properties: RealmDictionary<String?>): this() {
this.name = name this.name = name

View File

@ -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.layers.MyItemizedLayer
import com.navinfo.collect.library.map.source.MapLifeNiLocationTileSource import com.navinfo.collect.library.map.source.MapLifeNiLocationTileSource
import com.navinfo.collect.library.map.source.NavinfoMultiMapFileTileSource 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.system.Constant
import com.navinfo.collect.library.utils.GeometryTools import com.navinfo.collect.library.utils.GeometryTools
import io.realm.Realm 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.VectorTileLayer
import org.oscim.layers.tile.vector.labeling.LabelLayer import org.oscim.layers.tile.vector.labeling.LabelLayer
import org.oscim.layers.tile.vector.labeling.LabelTileLoaderHook import org.oscim.layers.tile.vector.labeling.LabelTileLoaderHook
import org.oscim.map.Map
import org.oscim.map.Map.UpdateListener import org.oscim.map.Map.UpdateListener
import org.oscim.tiling.source.OkHttpEngine.OkHttpFactory import org.oscim.tiling.source.OkHttpEngine.OkHttpFactory
import org.oscim.tiling.source.mapfile.MapFileTileSource import org.oscim.tiling.source.mapfile.MapFileTileSource
@ -69,6 +71,15 @@ open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView,tr
*/ */
private lateinit var labelNiLocationLayer: LabelLayer private lateinit var labelNiLocationLayer: LabelLayer
/**
* 显示待测评OMDB数据的图层
* */
private lateinit var omdbVectorTileLayer: VectorTileLayer
private lateinit var omdbLabelLayer: LabelLayer
/**
* 文字大小
*/
private val NUM_13 = 13
init { init {
initMap() initMap()
@ -81,6 +92,8 @@ open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView,tr
loadBaseMap() loadBaseMap()
initOMDBVectorTileLayer()
mapLifeNiLocationTileSource = MapLifeNiLocationTileSource(mContext, mTracePath) mapLifeNiLocationTileSource = MapLifeNiLocationTileSource(mContext, mTracePath)
vectorNiLocationTileLayer = VectorTileLayer(mMapView.vtmMap, mapLifeNiLocationTileSource) 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)
}
}
/** /**
* 切换基础底图样式 * 切换基础底图样式

View File

@ -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));
}
}

View File

@ -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();
}
}
}

View File

@ -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() {
}
}

View File

@ -31,5 +31,6 @@ public class Constant {
public static String[] HAD_LAYER_INVISIABLE_ARRAY; public static String[] HAD_LAYER_INVISIABLE_ARRAY;
public static final int OVER_ZOOM = 21; public static final int OVER_ZOOM = 21;
public static final int MAX_ZOOM = 25; public static final int MAX_ZOOM = 25;
public static final int OMDB_MIN_ZOOM = 18;
} }