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

 Conflicts:
	app/src/main/java/com/navinfo/omqs/OMQSApplication.kt
	app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt
	collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt
This commit is contained in:
squallzhjch
2023-04-23 16:41:04 +08:00
20 changed files with 212 additions and 64 deletions

View File

@@ -1530,4 +1530,23 @@
<symbol src="assets:symbols/traffic_signal.svg" />
</m>
</m>
<m k="nav_style">
<m v="symbol_object_line">
<m k="rule" zoom-min="15" zoom-max="22">
<!-- 蓝色黑色间隔线 -->
<m v="blue_link">
<line stroke="#00000000" stipple-stroke="#00000000" dasharray="20,20" fix="true" width="0.1" />
</m>
<!-- 黄色线 -->
<m v="yellow_link">
<line stroke="#f4ea2a" width="0.1" stipple-width="0.1"/>
</m>
</m>
<line stroke="#33aaaa" width="0.3" stipple-width="0.5"/>
</m>
<m v="symbol_track_point" zoom-min="10" zoom-max="25">
<symbol src="assets:symbols/dot_blue.svg" />
</m>
</m>
</rendertheme>

View File

@@ -10,7 +10,6 @@ import androidx.sqlite.db.SupportSQLiteDatabase;
import com.navinfo.collect.library.data.entity.CheckManager;
import com.navinfo.collect.library.data.entity.Element;
import com.navinfo.collect.library.data.entity.LayerManager;
import com.navinfo.collect.library.data.entity.NiLocation;
import com.navinfo.collect.library.data.entity.Project;
import com.navinfo.collect.library.data.entity.TileElement;
import com.tencent.wcdb.database.SQLiteCipherSpec;
@@ -25,7 +24,7 @@ import com.tencent.wcdb.room.db.WCDBDatabase;
import java.util.ArrayList;
import java.util.List;
@Database(entities = {Element.class, TileElement.class, LayerManager.class, Project.class, NiLocation.class, CheckManager.class},version = 1, exportSchema = false)
@Database(entities = {Element.class, TileElement.class, LayerManager.class, Project.class, CheckManager.class},version = 1, exportSchema = false)
public abstract class MapLifeDataBase extends RoomDatabase {
// marking the instance as volatile to ensure atomic access to the variable
/**
@@ -38,11 +37,6 @@ public abstract class MapLifeDataBase extends RoomDatabase {
*/
public abstract IElementDao getElementDao();
/**
* 地图坐标库类
*/
public abstract INiLocationDao getNiLocationDao();
/**
* 图层要素数据库类
*/

View File

@@ -0,0 +1,182 @@
package com.navinfo.collect.library.data.dao.impl;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteDatabase;
import com.navinfo.collect.library.data.entity.NiLocation;
import com.tencent.wcdb.database.SQLiteCipherSpec;
import com.tencent.wcdb.database.SQLiteDatabase;
import com.tencent.wcdb.repair.BackupKit;
import com.tencent.wcdb.repair.RecoverKit;
import com.tencent.wcdb.room.db.WCDBDatabase;
import com.tencent.wcdb.room.db.WCDBOpenHelperFactory;
@Database(entities = { NiLocation.class},version = 1, exportSchema = false)
public abstract class TraceDataBase extends RoomDatabase {
// marking the instance as volatile to ensure atomic access to the variable
/**
* 数据库单例对象
*/
private static volatile TraceDataBase INSTANCE;
/**
* 地图坐标库类
*/
public abstract INiLocationDao getNiLocationDao();
/**
* 数据库秘钥
*/
private final static String DB_PASSWORD = "123456";
public static TraceDataBase getDatabase(final Context context, final String name) {
if (INSTANCE == null) {
synchronized (TraceDataBase.class) {
if (INSTANCE == null) {
// [WCDB] To use Room library with WCDB, pass a WCDBOpenHelper factory object
// to the database builder with .openHelperFactory(...). In the factory object,
// you can specify passphrase and cipher options to open or create encrypted
// database, as well as optimization options like asynchronous checkpoint.
SQLiteCipherSpec cipherSpec = new SQLiteCipherSpec()
.setPageSize(1024)
.setSQLCipherVersion(3);
WCDBOpenHelperFactory factory = new WCDBOpenHelperFactory()
.passphrase(DB_PASSWORD.getBytes()) // passphrase to the database, remove this line for plain-text
.cipherSpec(cipherSpec) // cipher to use, remove for default settings
.writeAheadLoggingEnabled(true) // enable WAL mode, remove if not needed
.asyncCheckpointEnabled(true); // enable asynchronous checkpoint, remove if not needed
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
TraceDataBase.class, name)
// [WCDB] Specify open helper to use WCDB database implementation instead
// of the Android framework.
.openHelperFactory(factory)
// Wipes and rebuilds instead of migrating if no Migration object.
// Migration is not part of this codelab.
.fallbackToDestructiveMigration()
.addCallback(sRoomDatabaseCallback)
.build();
}
}
}
return INSTANCE;
}
/**
* Override the onOpen method to populate the database.
* For this sample, we clear the database every time it is created or opened.
* <p>
* If you want to populate the database only when the database is created for the 1st time,
* override RoomDatabase.Callback()#onCreate
*/
private static Callback sRoomDatabaseCallback = new Callback() {
@Override
public void onOpen(@NonNull SupportSQLiteDatabase db) {
super.onOpen(db);
// If you want to keep the data through app restarts,
// comment out the following line.
new PopulateDbAsync(INSTANCE).execute();
}
};
/**
* Populate the database in the background.
* If you want to start with more words, just add them.
*/
private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {
PopulateDbAsync(TraceDataBase db) {
}
@Override
protected Void doInBackground(final Void... params) {
// Start the app with a clean database every time.
// Not needed if you only populate on creation.
//mDao.deleteAll();
Log.e("qj", "doInBackground");
return null;
}
}
/**
* 数据恢复
*/
protected boolean recoverData(){
if(INSTANCE!=null){
SQLiteDatabase sqlite = ((WCDBDatabase) INSTANCE.getOpenHelper().getWritableDatabase()).getInnerDatabase();
RecoverKit recover = new RecoverKit(
sqlite, // 要恢复到的目标 DB
sqlite.getPath() + "-backup", // 备份文件
DB_PASSWORD.getBytes() // 加密备份文件的密钥,非 DB 密钥
);
int result = recover.run(false); // fatal 参数传 false 表示遇到错误忽略并继续,
// 若传 true 遇到错误则中止并返回 FAILED
switch (result) {
case RecoverKit.RESULT_OK:
/* 成功 */
Log.e("qj","sRoomDatabaseCallback==RecoverKit成功");
return true;
case RecoverKit.RESULT_CANCELED: /* 取消操作 */
Log.e("qj","sRoomDatabaseCallback==RecoverKit取消操作");
break;
case RecoverKit.RESULT_FAILED: /* 失败 */
Log.e("qj","sRoomDatabaseCallback==RecoverKit失败");
break;
}
recover.release();
}
return false;
}
/**
* 备份数据
*/
protected boolean backup() {
Log.e("qj", "sRoomDatabaseCallback===backup==start");
if (INSTANCE != null) {
//备份文件
SQLiteDatabase sqlite = ((WCDBDatabase) INSTANCE.getOpenHelper().getWritableDatabase()).getInnerDatabase();
BackupKit backup = new BackupKit(
sqlite, // 要备份的 DB
sqlite.getPath() + "-backup", // 备份文件
"123456".getBytes(), // 加密备份文件的密钥,非 DB 密钥
0, null);
int result = backup.run();
switch (result) {
case BackupKit.RESULT_OK:
/* 成功 */
Log.e("qj", "sRoomDatabaseCallback==成功");
return true;
case BackupKit.RESULT_CANCELED:
/* 取消操作 */
Log.e("qj", "sRoomDatabaseCallback==取消操作");
break;
case BackupKit.RESULT_FAILED:
/* 失败 */
Log.e("qj", "sRoomDatabaseCallback==失败");
break;
}
backup.release();
}
Log.e("qj", "sRoomDatabaseCallback===backup==end");
return false;
}
protected void release() {
INSTANCE = null;
}
}

View File

@@ -6,6 +6,7 @@ import com.navinfo.collect.library.utils.GeometryToolsKt
import io.realm.RealmDictionary
import io.realm.RealmObject
import io.realm.RealmSet
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import org.locationtech.jts.geom.Coordinate
import org.locationtech.jts.geom.Geometry
@@ -28,7 +29,15 @@ open class RenderEntity(): RealmObject() {
// 根据geometry自动计算当前要素的x-tile和y-tile
GeometryToolsKt.getTileXByGeometry(value, tileX)
GeometryToolsKt.getTileYByGeometry(value, tileY)
// 根据传入的geometry文本自动转换为Geometry对象
try {
wkt = GeometryTools.createGeometry(value)
} catch (e: Exception) {
}
}
@Ignore
var wkt: Geometry? = null
var properties: RealmDictionary<String?> = RealmDictionary()
val tileX: RealmSet<Int> = RealmSet() // x方向的tile编码
val tileY: RealmSet<Int> = RealmSet() // y方向的tile编码

View File

@@ -1,9 +1,9 @@
package com.navinfo.collect.library.data.handler
import android.content.Context
import com.navinfo.collect.library.data.dao.impl.MapLifeDataBase
import androidx.room.RoomDatabase
open class BaseDataHandler(context: Context, dataBase: MapLifeDataBase) {
open class BaseDataHandler(context: Context, dataBase: RoomDatabase) {
protected val mContext: Context = context;
protected val mDataBase: MapLifeDataBase = dataBase;
protected val mDataBase: RoomDatabase = dataBase;
}

View File

@@ -8,6 +8,7 @@ import android.os.Handler
import android.os.Looper
import android.util.Log
import com.navinfo.collect.library.data.dao.impl.MapLifeDataBase
import com.navinfo.collect.library.data.dao.impl.TraceDataBase
import com.navinfo.collect.library.data.entity.*
import com.navinfo.collect.library.data.entity.DataLayerItemType.*
import com.navinfo.collect.library.utils.GeometryTools
@@ -25,7 +26,7 @@ import kotlin.concurrent.thread
open class
DataNiLocationHandler(context: Context, dataBase: MapLifeDataBase) :
DataNiLocationHandler(context: Context, dataBase: TraceDataBase) :
BaseDataHandler(context, dataBase) {
/**
@@ -58,11 +59,11 @@ DataNiLocationHandler(context: Context, dataBase: MapLifeDataBase) :
}
}
val niLocationLoad = mDataBase.niLocationDao.find(niLocation.id);
val niLocationLoad = (mDataBase as TraceDataBase).niLocationDao.find(niLocation.id);
if(niLocationLoad!=null){
mDataBase.niLocationDao.update(niLocation)
(mDataBase as TraceDataBase).niLocationDao.update(niLocation)
}else{
mDataBase.niLocationDao.insert(niLocation)
(mDataBase as TraceDataBase).niLocationDao.insert(niLocation)
}
Handler(Looper.getMainLooper()).post {
callback.invoke(true, "")
@@ -89,7 +90,7 @@ DataNiLocationHandler(context: Context, dataBase: MapLifeDataBase) :
"uuid=?",
arrayOf("'${niLocation.id}'")
)
mDataBase.niLocationDao.delete(niLocation);
(mDataBase as TraceDataBase).niLocationDao.delete(niLocation);
} catch (e: Throwable) {
Handler(Looper.getMainLooper()).post {
callback.invoke(false, "${e.message}")

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.os.Handler
import android.os.Looper
import com.navinfo.collect.library.data.dao.impl.MapLifeDataBase
import com.navinfo.collect.library.data.dao.impl.TraceDataBase
import com.navinfo.collect.library.data.entity.Project
import kotlin.concurrent.thread
@@ -22,7 +23,7 @@ open class DataProjectHandler(context: Context, dataBase: MapLifeDataBase) :
) {
thread(start = true) {
try {
mDataBase.projectManagerDao.insert(project)
(mDataBase as MapLifeDataBase).projectManagerDao.insert(project)
Handler(Looper.getMainLooper()).post {
callback.invoke(true, "")
}
@@ -41,7 +42,7 @@ open class DataProjectHandler(context: Context, dataBase: MapLifeDataBase) :
*/
fun getProjectList(callback: (list: List<Project>) -> Unit) {
thread(start = true) {
val list = mDataBase.projectManagerDao.findList();
val list = (mDataBase as MapLifeDataBase).projectManagerDao.findList();
Handler(Looper.getMainLooper()).post {
callback.invoke(list)
}

View File

@@ -25,9 +25,9 @@ class NIMapController {
lateinit var viewportHandler: ViewportHandler
lateinit var measureLayerHandler: MeasureLayerHandler
fun init(context: AppCompatActivity, mapView: NIMapView, options: NIMapOptions? = null, mapPath: String) {
fun init(context: AppCompatActivity, mapView: NIMapView, options: NIMapOptions? = null, mapPath: String, tracePath: String) {
Constant.MAP_PATH = mapPath
layerManagerHandler = LayerManagerHandler(context, mapView)
layerManagerHandler = LayerManagerHandler(context, mapView, tracePath)
locationLayerHandler = LocationLayerHandler(context, mapView)
animationHandler = AnimationHandler(context, mapView)
markerHandle = MarkHandler(context, mapView)

View File

@@ -49,11 +49,10 @@ import java.util.*
/**
* Layer 操作
*/
open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView) :
BaseHandler(context, mapView) {
open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView,tracePath: String) : BaseHandler(context, mapView) {
private var baseGroupLayer // 用于盛放所有基础底图的图层组,便于统一管理
: GroupLayer? = null
protected val mTracePath:String = tracePath
/**
* 默认文字颜色
*/
@@ -104,6 +103,26 @@ open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView) :
loadBaseMap()
//初始化之间数据图层
initQsRecordDataLayer()
mapLifeNiLocationTileSource = MapLifeNiLocationTileSource(mContext, mTracePath)
vectorNiLocationTileLayer = VectorTileLayer(mMapView.vtmMap, mapLifeNiLocationTileSource)
labelNiLocationLayer = LabelLayer(mMapView.vtmMap, vectorNiLocationTileLayer, LabelTileLoaderHook(), 15)
if(vectorNiLocationTileLayer!=null){
addLayer(vectorNiLocationTileLayer,NIMapView.LAYER_GROUPS.BASE)
}
if(labelNiLocationLayer!=null){
addLayer(labelNiLocationLayer, NIMapView.LAYER_GROUPS.BASE)
}
vectorNiLocationTileLayer.isEnabled = false
labelNiLocationLayer.isEnabled = false
mMapView.switchTileVectorLayerTheme(NIMapView.MAP_THEME.DEFAULT)
mMapView.updateMap()
// initMapLifeSource()
// 设置矢量图层均在12级以上才显示
@@ -579,24 +598,15 @@ open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView) :
}
//显示轨迹图层
fun showNiLocationLayer(dbName: String?) {
if (mapLifeNiLocationTileSource == null) {
mapLifeNiLocationTileSource = MapLifeNiLocationTileSource(mContext, dbName)
}
if (vectorNiLocationTileLayer == null) {
vectorNiLocationTileLayer =
VectorTileLayer(mMapView.vtmMap, mapLifeNiLocationTileSource)
}
if (labelNiLocationLayer == null) {
labelNiLocationLayer =
LabelLayer(mMapView.vtmMap, vectorNiLocationTileLayer, LabelTileLoaderHook(), 15)
}
addLayer(labelNiLocationLayer, NIMapView.LAYER_GROUPS.VECTOR)
fun showNiLocationLayer() {
vectorNiLocationTileLayer.isEnabled = true
labelNiLocationLayer.isEnabled = true
}
//隐藏轨迹图层
fun hideNiLocationLayer() {
removeLayer(labelNiLocationLayer)
vectorNiLocationTileLayer.isEnabled = false
labelNiLocationLayer.isEnabled = false
}
}

View File

@@ -57,9 +57,7 @@ class LocationLayerHandler(context: AppCompatActivity, mapView: NIMapView) : Bas
//获取定位类型、定位错误返回码具体信息可参照类参考中BDLocation类中的说明
val errorCode = it.locType
mCurrentLocation = it
mLocationLayer.setPosition(
it.latitude, it.longitude, it.radius
)
mLocationLayer.setPosition(it.latitude, it.longitude, it.radius)
Log.e("qj","location==${it.longitude}==errorCode===$errorCode===${it.locTypeDescription}")
if(niLocationListener!=null){
getCurrentNiLocation()?.let { it1 -> niLocationListener.call(it1) }

View File

@@ -90,7 +90,7 @@ public class MapLifeNiLocationDecoder extends TileDecoder {
}
if(count==0){
properties.put("nav_style","symbol_object_line");
parseGeometry(layerName, GeometryTools.createGeometry("LINESTRING (116.245567 40.073475, 116.245855 40.072811, 116.24706 40.073034)"), properties);
parseGeometry(layerName, GeometryTools.createGeometry("LINESTRING (116.245859 40.073475, 116.245855 40.073477)"), properties);
}
// if(count%55==0){
// Log.e("qj","decode==geometry==symbol_track_point"+geometry.toString()+String.valueOf(anyNum));

View File

@@ -6,6 +6,7 @@ import android.util.Log;
import androidx.annotation.RequiresApi;
import com.navinfo.collect.library.data.dao.impl.MapLifeDataBase;
import com.navinfo.collect.library.data.dao.impl.TraceDataBase;
import com.navinfo.collect.library.data.entity.Element;
import com.navinfo.collect.library.data.entity.NiLocation;
import org.oscim.core.MapElement;
@@ -43,16 +44,16 @@ public class MapLifeNiLocationTileDataSource implements ITileDataSource {
// 获取tile对应的坐标范围
if (tile.zoomLevel >= 10 && tile.zoomLevel <= 20) {
int m = 20 - tile.zoomLevel;
int m = 21 - 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);
List<NiLocation> list = null;
if(mEndTime!=0){
list = MapLifeDataBase.getDatabase(mCon, dbName).getNiLocationDao().timeTofindList(xStart, xEnd, yStart, yEnd,mStartTime,mEndTime);
list = TraceDataBase.getDatabase(mCon, dbName).getNiLocationDao().timeTofindList(xStart, xEnd, yStart, yEnd,mStartTime,mEndTime);
}else{
list = MapLifeDataBase.getDatabase(mCon, dbName).getNiLocationDao().findList(xStart, xEnd, yStart, yEnd);
list = TraceDataBase.getDatabase(mCon, dbName).getNiLocationDao().findList(xStart, xEnd, yStart, yEnd);
}
Log.e("qj","query"+(list==null?0:list.size())+"==="+xStart+"==="+xEnd+"==="+yStart+"==="+yEnd);

View File

@@ -5,6 +5,8 @@ import android.content.Context;
import org.oscim.tiling.ITileDataSource;
import org.oscim.tiling.TileSource;
import java.util.Date;
public class MapLifeNiLocationTileSource extends TileSource {
private Context mCon;
private String dbName;

View File

@@ -1566,4 +1566,19 @@ public class GeometryTools {
return earthRadiusMeters * acos; // 最终结果
}
/**
* 将平面坐标系中的距离(以米为单位)转换为地理坐标系中的角度(以度为单位)
*
* @param distance 平面坐标系中的距离(单位:米)
* @param latitude 点的纬度(单位:度)
* @return 对应的地理坐标系中的距离(单位:度)
*/
private static final double EARTH_RADIUS = 6371000.0;
public static double convertDistanceToDegree(double distance, double latitude) {
double radianDistance = distance / EARTH_RADIUS;
double radianLatitude = Math.toRadians(latitude);
double radianDegree = 2 * Math.asin(Math.sin(radianDistance / 2) / Math.cos(radianLatitude));
return Math.toDegrees(radianDegree);
}
}