Merge branch 'master' of gitlab.navinfo.com:CollectVehicle/OneMapQS
This commit is contained in:
commit
465afbda86
@ -2,7 +2,6 @@ package com.navinfo.omqs
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import com.navinfo.omqs.db.MyRealmModule
|
||||
import com.navinfo.omqs.tools.FileManager
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import io.realm.Realm
|
||||
@ -25,7 +24,7 @@ class OMQSApplication : Application() {
|
||||
.directory(File(Constant.DATA_PATH))
|
||||
.name("OMQS.realm")
|
||||
.encryptionKey(password)
|
||||
.modules(Realm.getDefaultModule(), MyRealmModule())
|
||||
// .modules(Realm.getDefaultModule(), MyRealmModule())
|
||||
.schemaVersion(1)
|
||||
.build()
|
||||
Realm.setDefaultConfiguration(config)
|
||||
|
@ -2,6 +2,6 @@ package com.navinfo.omqs.db
|
||||
|
||||
import com.navinfo.collect.library.data.entity.QsRecordBean
|
||||
|
||||
@io.realm.annotations.RealmModule(classes = [QsRecordBean::class])
|
||||
class MyRealmModule {
|
||||
}
|
||||
//@io.realm.annotations.RealmModule(classes = [QsRecordBean::class])
|
||||
//class MyRealmModule {
|
||||
//}
|
@ -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
|
||||
|
||||
|
||||
/**
|
||||
@ -14,7 +13,7 @@ import io.realm.annotations.RealmClass
|
||||
* @Date 2016/1/12
|
||||
* @Description: ${TODO}(质检对象)
|
||||
*/
|
||||
@RealmClass
|
||||
//@RealmClass
|
||||
open class QsRecordBean @JvmOverloads constructor(
|
||||
/**
|
||||
* id 主键
|
||||
|
@ -0,0 +1,24 @@
|
||||
package com.navinfo.collect.library.map.cluster
|
||||
|
||||
import org.oscim.core.GeoPoint
|
||||
import org.oscim.layers.marker.MarkerItem
|
||||
|
||||
/*
|
||||
*com.nmp.map.cluster
|
||||
*zhjch
|
||||
*2021/12/10
|
||||
*10:51
|
||||
*说明()
|
||||
*/
|
||||
class ClusterMarkerItem(uid: Any?, title: String?, description: String?, geoPoint: GeoPoint?) :
|
||||
MarkerItem(uid, title, description, geoPoint) {
|
||||
var clusterList: List<Int> = ArrayList()
|
||||
|
||||
constructor(title: String?, description: String?, geoPoint: GeoPoint?) : this(
|
||||
null,
|
||||
title,
|
||||
description,
|
||||
geoPoint
|
||||
) {
|
||||
}
|
||||
}
|
@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2016 Izumi Kawashima
|
||||
* Copyright 2017 Longri
|
||||
* Copyright 2017-2018 devemux86
|
||||
* Copyright 2017 nebular
|
||||
* Copyright 2017 Wolfgang Schramm
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.navinfo.collect.library.map.cluster
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.text.TextPaint
|
||||
import android.util.Log
|
||||
import com.navinfo.collect.library.R
|
||||
import org.oscim.android.canvas.AndroidBitmap
|
||||
import org.oscim.backend.CanvasAdapter
|
||||
import org.oscim.backend.canvas.Bitmap
|
||||
import org.oscim.core.MercatorProjection
|
||||
import org.oscim.core.PointF
|
||||
import org.oscim.core.Tile
|
||||
import org.oscim.layers.marker.*
|
||||
import org.oscim.renderer.GLViewport
|
||||
import org.oscim.renderer.bucket.SymbolItem
|
||||
import org.oscim.utils.FastMath
|
||||
import org.oscim.utils.geom.GeometryUtils
|
||||
|
||||
/**
|
||||
* An extension to the MarkerRenderer with item clustering support.
|
||||
*/
|
||||
open class ClusterMarkerRenderer : MarkerRenderer {
|
||||
private var mContext: Context? = null
|
||||
protected var mStyleBackground = CLUSTER_COLORBACK
|
||||
protected var mStyleForeground = CLUSTER_COLORTEXT
|
||||
|
||||
/**
|
||||
* Discrete scale step, used to trigger reclustering on significant scale change
|
||||
*/
|
||||
private var mScalePow = 0
|
||||
|
||||
/**
|
||||
* Map scale to cluster the marker
|
||||
*/
|
||||
private var mClusterScale = 0.0
|
||||
|
||||
/**
|
||||
* We use a flat Sparse array to calculate the clusters. The sparse array models a 2D map where every (x,y) denotes
|
||||
* a grid slot, ie. 64x64dp. For efficiency I use a linear sparsearray with ARRindex = SLOTypos * max_x + SLOTxpos"
|
||||
*/
|
||||
// private final SparseIntArray mGridMap = new SparseIntArray(200); // initial space for 200 markers, that's not a lot of memory, and in most cases will avoid resizing the array
|
||||
private val mGridMap =
|
||||
HashMap<Int, Any?>() // initial space for 200 markers, that's not a lot of memory, and in most cases will avoid resizing the array
|
||||
|
||||
/**
|
||||
* Whether to enable clustering or disable the functionality
|
||||
*/
|
||||
private val mClusteringEnabled: Boolean
|
||||
|
||||
/**
|
||||
* Constructs a clustered marker renderer
|
||||
*
|
||||
* @param markerLayer The owner layer
|
||||
* @param defaultSymbol The default symbol
|
||||
* @param style The desired style, or NULL to disable clustering
|
||||
*/
|
||||
constructor(
|
||||
context: Context?,
|
||||
markerLayer: MarkerLayer?,
|
||||
defaultSymbol: MarkerSymbol?,
|
||||
style: ClusterStyle?
|
||||
) : super(markerLayer, defaultSymbol) {
|
||||
mContext = context
|
||||
mClusteringEnabled = style != null
|
||||
if (mClusteringEnabled) {
|
||||
setClusterStyle(style!!.foreground, style.background)
|
||||
for (k in 0..CLUSTER_MAXSIZE) {
|
||||
// cache bitmaps so render thread never creates them
|
||||
// we create CLUSTER_MAXSIZE bitmaps. Bigger clusters will show like "+"
|
||||
getClusterBitmap(k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private constructor(
|
||||
markerLayer: MarkerLayer, defaultSymbol: MarkerSymbol, style: ClusterStyle?
|
||||
) : super(markerLayer, defaultSymbol) {
|
||||
mClusteringEnabled = style != null
|
||||
if (mClusteringEnabled) {
|
||||
setClusterStyle(style!!.foreground, style.background)
|
||||
for (k in 0..CLUSTER_MAXSIZE) {
|
||||
// cache bitmaps so render thread never creates them
|
||||
// we create CLUSTER_MAXSIZE bitmaps. Bigger clusters will show like "+"
|
||||
getClusterBitmap(k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the cluster icon style. This is called by the constructor and cannot be made public because
|
||||
* we pre-cache the icons at construction time so the renderer does not have to create them while rendering
|
||||
*
|
||||
* @param backgroundColor Background color
|
||||
* @param foregroundColor text & border color
|
||||
*/
|
||||
private fun setClusterStyle(foregroundColor: Int, backgroundColor: Int) {
|
||||
mStyleBackground = backgroundColor
|
||||
mStyleForeground = foregroundColor
|
||||
}
|
||||
|
||||
protected override fun populate(size: Int) {
|
||||
repopulateCluster(size, mClusterScale)
|
||||
}
|
||||
|
||||
/**
|
||||
* Repopulates item list clustering close markers. This is triggered from update() when
|
||||
* a significant change in scale has happened.
|
||||
*
|
||||
* @param size Item list size
|
||||
* @param scale current map scale
|
||||
*/
|
||||
private fun repopulateCluster(size: Int, scale: Double) {
|
||||
/* the grid slot size in px. increase to group more aggressively. currently set to marker size */
|
||||
if (mMapPosition.zoomLevel == 15) {
|
||||
MAP_GRID_SIZE_DP = 128
|
||||
} else {
|
||||
MAP_GRID_SIZE_DP = 64
|
||||
}
|
||||
val GRIDSIZE = ClusterUtils.getPixels(MAP_GRID_SIZE_DP.toFloat())
|
||||
|
||||
/* the factor to map into Grid Coordinates (discrete squares of GRIDSIZE x GRIDSIZE) */
|
||||
val factor = scale / GRIDSIZE
|
||||
val tmp = arrayOfNulls<Clustered>(size)
|
||||
|
||||
// clear grid map to count items that share the same "grid slot"
|
||||
mGridMap.clear()
|
||||
for (i in 0 until size) {
|
||||
tmp[i] = Clustered()
|
||||
val it = tmp[i]
|
||||
it!!.item = mMarkerLayer.createItem(i)
|
||||
|
||||
/* pre-project points */MercatorProjection.project(it.item.point, mMapPoint)
|
||||
it.px = mMapPoint.x
|
||||
it.py = mMapPoint.y
|
||||
|
||||
// items can be declared non-clusterable
|
||||
if (it.item !is NonClusterable) {
|
||||
val absposx = (it.px * factor).toInt()
|
||||
// absolute item X position in the grid
|
||||
val absposy = (it.py * factor).toInt()
|
||||
// absolute item Y position
|
||||
val maxcols = factor.toInt()
|
||||
// Grid number of columns
|
||||
val itemGridIndex = absposx + absposy * maxcols // Index in the sparsearray map
|
||||
|
||||
// we store in the linear sparsearray the index of the marker,
|
||||
// ie, index = y * maxcols + x; array[index} = markerIndex
|
||||
|
||||
// Lets check if there's already an item in the grid slot
|
||||
// final int storedIndexInGridSlot = mGridMap.get(itemGridIndex, -1);
|
||||
var list: ArrayList<Int>? = null
|
||||
if (mGridMap.containsKey(itemGridIndex)) {
|
||||
list = mGridMap[itemGridIndex] as ArrayList<Int>?
|
||||
}
|
||||
if (list == null) {
|
||||
list = ArrayList()
|
||||
list.add(i)
|
||||
if (it.item is ClusterMarkerItem) {
|
||||
(it.item as ClusterMarkerItem).clusterList = list
|
||||
}
|
||||
mGridMap[itemGridIndex] = list
|
||||
// no item at that grid position. The grid slot is free so let's
|
||||
// store this item "i" (we identify every item by its InternalItem index)
|
||||
// mGridMap.put(itemGridIndex, i);
|
||||
//Log.v(TAG, "UNclustered item at " + itemGridIndex);
|
||||
} else {
|
||||
// at that grid position there's already a marker index
|
||||
// mark this item as clustered out, so it will be skipped in the update() call
|
||||
it.clusteredOut = true
|
||||
list.add(i)
|
||||
for (n in list) {
|
||||
val item: MarkerInterface = mMarkerLayer.createItem(n)
|
||||
if (item is ClusterMarkerItem) {
|
||||
(item as ClusterMarkerItem).clusterList = list
|
||||
}
|
||||
}
|
||||
// and increment the count on its "parent" that will from now on act as a cluster
|
||||
tmp[list[0]]!!.clusterSize++
|
||||
|
||||
//Log.v(TAG, "Clustered item at " + itemGridIndex + ", \'parent\' size " + (tmp[storedIndexInGridSlot].clusterSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* All ready for update. */synchronized(this) {
|
||||
mUpdate = true
|
||||
mItems = tmp
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun update(v: GLViewport) {
|
||||
val scale: Double = Tile.SIZE * v.pos.scale
|
||||
if (mClusteringEnabled) {
|
||||
/*
|
||||
Clustering check: If clustering is enabled and there's been a significant scale change
|
||||
trigger repopulation and return. After repopulation, this will be called again
|
||||
*/
|
||||
|
||||
// (int) log of scale gives us adequate steps to trigger clustering
|
||||
val scalePow = FastMath.log2(scale.toInt())
|
||||
if (scalePow != mScalePow) {
|
||||
mScalePow = scalePow
|
||||
mClusterScale = scale
|
||||
|
||||
// post repopulation to the main thread
|
||||
mMarkerLayer.map().post(Runnable { repopulateCluster(mItems.size, scale) })
|
||||
|
||||
// and get out of here
|
||||
return
|
||||
}
|
||||
}
|
||||
if (!v.changed() && !mUpdate) return
|
||||
mUpdate = false
|
||||
val mx: Double = v.pos.x
|
||||
val my: Double = v.pos.y
|
||||
|
||||
//int changesInvisible = 0;
|
||||
//int changedVisible = 0;
|
||||
var numVisible = 0
|
||||
|
||||
// Increase view to show items that are partially visible
|
||||
mMarkerLayer.map().viewport().getMapExtents(mBox, (Tile.SIZE shr 1).toFloat())
|
||||
val flip: Long = (Tile.SIZE * v.pos.scale).toLong() shr 1
|
||||
if (mItems == null) {
|
||||
if (buckets.get() != null) {
|
||||
buckets.clear()
|
||||
compile()
|
||||
}
|
||||
return
|
||||
}
|
||||
val angle = Math.toRadians(v.pos.bearing.toDouble())
|
||||
val cos = Math.cos(angle).toFloat()
|
||||
val sin = Math.sin(angle).toFloat()
|
||||
|
||||
/* check visibility */for (itm in mItems) {
|
||||
val it = itm as Clustered
|
||||
it.changes = false
|
||||
it.x = ((it.px - mx) * scale).toFloat()
|
||||
it.y = ((it.py - my) * scale).toFloat()
|
||||
if (it.x > flip) it.x -= (flip shl 1).toFloat() else if (it.x < -flip) it.x += (flip shl 1).toFloat()
|
||||
if (it.clusteredOut || !GeometryUtils.pointInPoly(it.x, it.y, mBox, 8, 0)) {
|
||||
// either properly invisible, or clustered out. Items marked as clustered out mean there's another item
|
||||
// on the same-ish position that will be promoted to cluster marker, so this particular item is considered
|
||||
// invisible
|
||||
if (it.visible && !it.clusteredOut) {
|
||||
// it was previously visible, but now it won't
|
||||
it.changes = true
|
||||
// changes to invible
|
||||
//changesInvisible++;
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// item IS definitely visible
|
||||
it.dy = sin * it.x + cos * it.y
|
||||
if (!it.visible) {
|
||||
it.visible = true
|
||||
//changedVisible++;
|
||||
}
|
||||
numVisible++
|
||||
}
|
||||
|
||||
//log.debug(numVisible + " " + changedVisible + " " + changesInvisible);
|
||||
|
||||
/* only update when zoomlevel changed, new items are visible
|
||||
* or more than 10 of the current items became invisible */
|
||||
//if ((numVisible == 0) && (changedVisible == 0 && changesInvisible < 10))
|
||||
// return;
|
||||
buckets.clear()
|
||||
if (numVisible == 0) {
|
||||
compile()
|
||||
return
|
||||
}
|
||||
/* keep position for current state */mMapPosition.copy(v.pos)
|
||||
mMapPosition.bearing = -mMapPosition.bearing
|
||||
|
||||
// why do we sort ? z-index?
|
||||
sort(mItems, 0, mItems.size)
|
||||
//log.debug(Arrays.toString(mItems));
|
||||
for (itm in mItems) {
|
||||
val it = itm as Clustered
|
||||
|
||||
// skip invisible AND clustered-out
|
||||
if (!it.visible || it.clusteredOut) continue
|
||||
if (it.changes) {
|
||||
it.visible = false
|
||||
continue
|
||||
}
|
||||
val s: SymbolItem = SymbolItem.pool.get()
|
||||
if (it.clusterSize > 0) {
|
||||
// this item will act as a cluster, just use a proper bitmap
|
||||
// depending on cluster size, instead of its marker
|
||||
val bitmap = getClusterBitmap(it.clusterSize + 1)
|
||||
s.set(it.x, it.y, bitmap, true)
|
||||
s.offset = PointF(0.5f, 0.5f)
|
||||
s.billboard = true // could be a parameter
|
||||
} else {
|
||||
// normal item, use its marker
|
||||
var symbol: MarkerSymbol? = it.item.marker
|
||||
if (symbol == null) symbol = mDefaultMarker
|
||||
symbol?.let { symbol ->
|
||||
s.set(it.x, it.y, symbol.bitmap, true)
|
||||
s.offset = symbol.hotspot
|
||||
s.billboard = symbol.isBillboard
|
||||
}
|
||||
|
||||
}
|
||||
mSymbolLayer.pushSymbol(s)
|
||||
}
|
||||
buckets.set(mSymbolLayer)
|
||||
buckets.prepare()
|
||||
compile()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a bitmap for a given cluster size
|
||||
*
|
||||
* @param size The cluster size. Can be greater than CLUSTER_MAXSIZE.
|
||||
* @return A somewhat cool bitmap to be used as the cluster marker
|
||||
*/
|
||||
open fun getClusterBitmap(size: Int): Bitmap? {
|
||||
var size = size
|
||||
val strValue: String
|
||||
if (size >= CLUSTER_MAXSIZE) {
|
||||
// restrict cluster indicator size. Bigger clusters will show as "+" instead of ie. "45"
|
||||
size = CLUSTER_MAXSIZE
|
||||
strValue = "+"
|
||||
} else {
|
||||
strValue = size.toString()
|
||||
}
|
||||
|
||||
// return cached bitmap if exists. cache hit !
|
||||
if (mClusterBitmaps[size] != null) return mClusterBitmaps[size]
|
||||
|
||||
// create and cache bitmap. This is unacceptable inside the GL thread,
|
||||
// so we'll call this routine at the beginning to pre-cache all bitmaps
|
||||
// Can customize cluster bitmap here
|
||||
//
|
||||
// Canvas canvas = new Canvas(bitmap);
|
||||
// TextPaint textPaint = new TextPaint();
|
||||
// textPaint.setColor(Color.WHITE);
|
||||
// int width = (int) Math.ceil(textPaint.measureText(strValue));
|
||||
// android.graphics.Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
|
||||
// int height = (int) Math.ceil(Math.abs(fontMetrics.bottom) + Math.abs(fontMetrics.top));
|
||||
// textPaint.setTextSize(13 * CanvasAdapter.getScale());
|
||||
// canvas.drawText(strValue, 0, Math.abs(fontMetrics.ascent), textPaint);
|
||||
val textPaint = TextPaint()
|
||||
val h: Float = 13 * CanvasAdapter.getScale()
|
||||
textPaint.textSize = h
|
||||
textPaint.strokeWidth = 2f
|
||||
textPaint.color = Color.WHITE
|
||||
val bitmap = BitmapFactory.decodeResource(
|
||||
mContext!!.resources, R.mipmap.map_icon_cluster
|
||||
).copy(android.graphics.Bitmap.Config.ARGB_8888, true)
|
||||
val canvas = Canvas(bitmap)
|
||||
val w = textPaint.getTextWidths(strValue, FloatArray(strValue.length))
|
||||
val textRect = Rect()
|
||||
textPaint.getTextBounds(strValue, 0, strValue.length, textRect)
|
||||
canvas.drawText(
|
||||
strValue,
|
||||
((bitmap.width - textRect.width()) / 2 - 4).toFloat(),
|
||||
((bitmap.height + textRect.height()) / 2).toFloat(),
|
||||
textPaint
|
||||
)
|
||||
val bitmapNew: Bitmap = AndroidBitmap(bitmap)
|
||||
// Canvas canvas = CanvasAdapter.newCanvas();
|
||||
// canvas.setBitmap(bitmapNew);
|
||||
// mSize = ClusterUtils.getPixels(sizedp);
|
||||
// int halfsize = mSize >> 1;
|
||||
// final int noneClippingRadius = halfsize - getPixels(2);
|
||||
// Paint mPaintText = CanvasAdapter.newPaint();
|
||||
// draw the number at the center
|
||||
// canvas.drawText(strValue,
|
||||
// (canvas.getWidth() - mPaintText.getTextWidth(strValue)) * 0.5f,
|
||||
// (canvas.getHeight() + mPaintText.getTextHeight(strValue)) * 0.5f,
|
||||
// mPaintText);
|
||||
// ClusterUtils.ClusterDrawable drawable = new ClusterUtils.ClusterDrawable(
|
||||
// MAP_MARKER_CLUSTER_SIZE_DP - CLUSTER_MAXSIZE + size, // make size dependent on cluster size
|
||||
// mStyleForeground,
|
||||
// mStyleBackground,
|
||||
// strValue
|
||||
// );
|
||||
mClusterBitmaps[size] = bitmapNew
|
||||
return mClusterBitmaps[size]
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to wrap the cluster icon style properties
|
||||
*/
|
||||
class ClusterStyle
|
||||
/**
|
||||
* Creates the Cluster style
|
||||
*
|
||||
* @param fore Foreground (border and text) color
|
||||
* @param back Background (circle) color
|
||||
*/(val foreground: Int, val background: Int)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Max number to display inside a cluster icon
|
||||
*/
|
||||
protected const val CLUSTER_MAXSIZE = 999
|
||||
|
||||
/**
|
||||
* default color of number inside the icon. Would be super-cool to cook this into the map theme
|
||||
*/
|
||||
private const val CLUSTER_COLORTEXT = -0x7fff40
|
||||
|
||||
/**
|
||||
* default color of circle background
|
||||
*/
|
||||
private const val CLUSTER_COLORBACK = -0x1
|
||||
|
||||
/**
|
||||
* Map Cluster Icon Size. This is the biggest size for clusters of CLUSTER_MAXSIZE elements. Smaller clusters will be slightly smaller
|
||||
*/
|
||||
protected const val MAP_MARKER_CLUSTER_SIZE_DP = 48
|
||||
|
||||
/**
|
||||
* Clustering grid square size, decrease to cluster more aggresively. Ideally this value is the typical marker size
|
||||
*/
|
||||
private var MAP_GRID_SIZE_DP = 64
|
||||
|
||||
/**
|
||||
* cached bitmaps database, we will cache cluster bitmaps from 1 to MAX_SIZE
|
||||
* and always use same bitmap for efficiency
|
||||
*/
|
||||
protected var mClusterBitmaps = arrayOfNulls<Bitmap>(CLUSTER_MAXSIZE + 1)
|
||||
|
||||
/**
|
||||
* Convenience method for instantiating this renderer via a factory, so the layer construction semantic is more pleasing to the eye
|
||||
*
|
||||
* @param defaultSymbol Default symbol to use if the Marker is not assigned a symbol
|
||||
* @param style Cluster icon style, or NULL to disable clustering functionality
|
||||
* @return A factory to be passed to the ItemizedLayer constructor in order to enable the cluster functionality
|
||||
*/
|
||||
fun factory(defaultSymbol: MarkerSymbol, style: ClusterStyle?): MarkerRendererFactory {
|
||||
return MarkerRendererFactory { markerLayer ->
|
||||
ClusterMarkerRenderer(
|
||||
markerLayer, defaultSymbol, style
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2017 nebular
|
||||
* Copyright 2017 devemux86
|
||||
* Copyright 2017 Wolfgang Schramm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.navinfo.collect.library.map.cluster
|
||||
|
||||
import org.oscim.backend.CanvasAdapter
|
||||
import org.oscim.backend.canvas.Bitmap
|
||||
import org.oscim.backend.canvas.Canvas
|
||||
import org.oscim.backend.canvas.Paint
|
||||
|
||||
/**
|
||||
* A simple utility class to make clustered markers functionality self-contained.
|
||||
* Includes a method to translate between DPs and PXs and a circular icon generator.
|
||||
*/
|
||||
object ClusterUtils {
|
||||
/**
|
||||
* Get pixels from DPs
|
||||
*
|
||||
* @param dp Value in DPs
|
||||
* @return Value in PX according to screen density
|
||||
*/
|
||||
fun getPixels(dp: Float): Int {
|
||||
return (CanvasAdapter.getScale() * dp).toInt()
|
||||
}
|
||||
|
||||
class ClusterDrawable(sizedp: Int, foregroundColor: Int, backgroundColor: Int, text: String) {
|
||||
private val mPaintText = CanvasAdapter.newPaint()
|
||||
private val mPaintCircle = CanvasAdapter.newPaint()
|
||||
private val mPaintBorder = CanvasAdapter.newPaint()
|
||||
private var mSize = 0
|
||||
private var mText: String? = null
|
||||
|
||||
/**
|
||||
* Generates a circle with a number inside
|
||||
*
|
||||
* @param sizedp Size in DPs
|
||||
* @param foregroundColor Foreground
|
||||
* @param backgroundColor Background
|
||||
* @param text Text inside. Will only work for a single character!
|
||||
*/
|
||||
init {
|
||||
setup(sizedp, foregroundColor, backgroundColor)
|
||||
setText(text)
|
||||
}
|
||||
|
||||
private fun setup(sizedp: Int, foregroundColor: Int, backgroundColor: Int) {
|
||||
mSize = getPixels(sizedp.toFloat())
|
||||
mPaintText.setTextSize(getPixels((sizedp * 0.6666666).toInt().toFloat()).toFloat())
|
||||
mPaintText.color = foregroundColor
|
||||
mPaintCircle.color = backgroundColor
|
||||
mPaintCircle.style = Paint.Style.FILL
|
||||
mPaintBorder.color = foregroundColor
|
||||
mPaintBorder.style = Paint.Style.STROKE
|
||||
mPaintBorder.strokeWidth = 2.0f * CanvasAdapter.getScale()
|
||||
}
|
||||
|
||||
private fun setText(text: String) {
|
||||
mText = text
|
||||
}
|
||||
|
||||
private fun draw(canvas: Canvas) {
|
||||
val halfsize = mSize shr 1
|
||||
val noneClippingRadius = halfsize - getPixels(2f)
|
||||
|
||||
// fill
|
||||
canvas.drawCircle(
|
||||
halfsize.toFloat(),
|
||||
halfsize.toFloat(),
|
||||
noneClippingRadius.toFloat(),
|
||||
mPaintCircle
|
||||
)
|
||||
// outline
|
||||
canvas.drawCircle(
|
||||
halfsize.toFloat(),
|
||||
halfsize.toFloat(),
|
||||
noneClippingRadius.toFloat(),
|
||||
mPaintBorder
|
||||
)
|
||||
// draw the number at the center
|
||||
canvas.drawText(
|
||||
mText,
|
||||
(canvas.width - mPaintText.getTextWidth(mText)) * 0.5f,
|
||||
(canvas.height + mPaintText.getTextHeight(mText)) * 0.5f,
|
||||
mPaintText
|
||||
)
|
||||
}
|
||||
|
||||
val bitmap: Bitmap
|
||||
get() {
|
||||
var width = mSize
|
||||
var height = mSize
|
||||
width = if (width > 0) width else 1
|
||||
height = if (height > 0) height else 1
|
||||
val bitmap = CanvasAdapter.newBitmap(width, height, 0)
|
||||
val canvas = CanvasAdapter.newCanvas()
|
||||
canvas.setBitmap(bitmap)
|
||||
draw(canvas)
|
||||
return bitmap
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2017 nebular
|
||||
* Copyright 2017 devemux86
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.navinfo.collect.library.map.cluster
|
||||
|
||||
import org.oscim.layers.marker.InternalItem
|
||||
|
||||
/**
|
||||
* Extension for clustered items.
|
||||
*/
|
||||
class Clustered : InternalItem() {
|
||||
/**
|
||||
* If this is >0, this item will be displayed as a cluster circle, with size clusterSize+1.
|
||||
*/
|
||||
var clusterSize = 0
|
||||
|
||||
/**
|
||||
* If this is true, this item is hidden (because it's represented by another InternalItem acting as cluster.
|
||||
*/
|
||||
var clusteredOut = false
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2017 nebular
|
||||
* Copyright 2017 devemux86
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.navinfo.collect.library.map.cluster
|
||||
|
||||
import org.oscim.core.GeoPoint
|
||||
import org.oscim.layers.marker.MarkerItem
|
||||
|
||||
/**
|
||||
* If a MarkerItem is created using this convenience class instead of MarkerItem,
|
||||
* this specific item will not be clusterable.
|
||||
*/
|
||||
class NonClusterable : MarkerItem {
|
||||
constructor(title: String?, description: String?, geoPoint: GeoPoint?) : super(
|
||||
null,
|
||||
title,
|
||||
description,
|
||||
geoPoint
|
||||
) {
|
||||
}
|
||||
|
||||
constructor(uid: Any?, title: String?, description: String?, geoPoint: GeoPoint?) : super(
|
||||
uid,
|
||||
title,
|
||||
description,
|
||||
geoPoint
|
||||
) {
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.navinfo.collect.library.map.handler
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.navinfo.collect.library.map.NIMapView
|
||||
import org.oscim.core.GeoPoint
|
||||
import org.oscim.core.MapPosition
|
||||
@ -8,7 +9,7 @@ import org.oscim.core.MapPosition
|
||||
/**
|
||||
* 控制地图状态相关操作
|
||||
*/
|
||||
open class AnimationHandler(context: Context, mapView: NIMapView) :
|
||||
open class AnimationHandler(context: AppCompatActivity, mapView: NIMapView) :
|
||||
BaseHandler(context, mapView) {
|
||||
|
||||
|
||||
|
@ -1,32 +1,48 @@
|
||||
package com.navinfo.collect.library.map.handler
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.navinfo.collect.library.R
|
||||
import com.navinfo.collect.library.data.entity.QsRecordBean
|
||||
import com.navinfo.collect.library.map.NIMapView
|
||||
import com.navinfo.collect.library.map.cluster.ClusterMarkerItem
|
||||
import com.navinfo.collect.library.map.cluster.ClusterMarkerRenderer
|
||||
import com.navinfo.collect.library.map.layers.MyItemizedLayer
|
||||
import com.navinfo.collect.library.map.source.NavinfoMultiMapFileTileSource
|
||||
import com.navinfo.collect.library.system.Constant
|
||||
import com.navinfo.collect.library.utils.GeometryTools
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.where
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.Cache
|
||||
import okhttp3.OkHttpClient
|
||||
import org.locationtech.jts.geom.Geometry
|
||||
import org.oscim.android.canvas.AndroidBitmap
|
||||
import org.oscim.backend.CanvasAdapter
|
||||
import org.oscim.backend.canvas.Bitmap
|
||||
import org.oscim.backend.canvas.Paint
|
||||
import org.oscim.core.GeoPoint
|
||||
import org.oscim.layers.GroupLayer
|
||||
import org.oscim.layers.marker.*
|
||||
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.tiling.source.OkHttpEngine.OkHttpFactory
|
||||
import org.oscim.tiling.source.mapfile.MapFileTileSource
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Layer 操作
|
||||
*/
|
||||
class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView) :
|
||||
open class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView) :
|
||||
BaseHandler(context, mapView) {
|
||||
private var baseGroupLayer // 用于盛放所有基础底图的图层组,便于统一管理
|
||||
: GroupLayer? = null
|
||||
@ -42,6 +58,17 @@ class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView) :
|
||||
|
||||
private lateinit var paint: Paint
|
||||
|
||||
//画布
|
||||
private lateinit var canvas: org.oscim.backend.canvas.Canvas
|
||||
private lateinit var itemizedLayer: MyItemizedLayer
|
||||
private lateinit var markerRendererFactory: MarkerRendererFactory
|
||||
private val markerItemsNames = mutableListOf<MarkerInterface>()
|
||||
|
||||
/**
|
||||
* 文字大小
|
||||
*/
|
||||
private val NUM_13 = 13
|
||||
|
||||
init {
|
||||
initMap()
|
||||
}
|
||||
@ -122,47 +149,356 @@ class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView) :
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始话质检数据图层
|
||||
*/
|
||||
private fun initQsRecordDataLayer() {
|
||||
canvas = CanvasAdapter.newCanvas()
|
||||
paint = CanvasAdapter.newPaint()
|
||||
paint.setTypeface(Paint.FontFamily.DEFAULT, Paint.FontStyle.NORMAL)
|
||||
paint.setTextSize(13 * CanvasAdapter.getScale())
|
||||
paint.setTextSize(NUM_13 * CanvasAdapter.getScale())
|
||||
paint.strokeWidth = 2 * CanvasAdapter.getScale()
|
||||
paint.color = Color.parseColor(mDefaultTextColor)
|
||||
val bitmapPoi: Bitmap = AndroidBitmap(
|
||||
BitmapFactory.decodeResource(
|
||||
mContext.resources,
|
||||
R.mipmap.map_icon_blue2
|
||||
)
|
||||
)
|
||||
val symbol = MarkerSymbol(bitmapPoi, MarkerSymbol.HotspotPlace.BOTTOM_CENTER)
|
||||
markerRendererFactory = MarkerRendererFactory { markerLayer ->
|
||||
object : ClusterMarkerRenderer(
|
||||
mContext,
|
||||
markerLayer,
|
||||
symbol,
|
||||
ClusterStyle(
|
||||
org.oscim.backend.canvas.Color.WHITE,
|
||||
org.oscim.backend.canvas.Color.BLUE
|
||||
)
|
||||
) {
|
||||
// override fun getClusterBitmap(size: Int): Bitmap? {
|
||||
// return super.getclusterbitmap(size)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
var resId = R.mipmap.map_icon_point_add
|
||||
|
||||
mContext.lifecycleScope.launch(Dispatchers.IO) {
|
||||
|
||||
|
||||
var list = mutableListOf<QsRecordBean>()
|
||||
val realm = Realm.getDefaultInstance()
|
||||
realm.executeTransaction {
|
||||
val list = realm.where<QsRecordBean>().findAll()
|
||||
paint.setColor(Color.parseColor(mDefaultTextColor))
|
||||
val objects =realm.where<QsRecordBean>().findAll()
|
||||
list = realm.copyFromRealm(objects)
|
||||
}
|
||||
realm.close()
|
||||
|
||||
itemizedLayer =
|
||||
MyItemizedLayer(
|
||||
mMapView.vtmMap,
|
||||
mutableListOf(),
|
||||
markerRendererFactory,
|
||||
object : MyItemizedLayer.OnItemGestureListener {
|
||||
override fun onItemSingleTapUp(
|
||||
layer: MyItemizedLayer?,
|
||||
list: MutableList<Int>?,
|
||||
nearest: Int
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onItemLongPress(
|
||||
layer: MyItemizedLayer?,
|
||||
list: MutableList<Int>?,
|
||||
nearest: Int
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
for (item in list) {
|
||||
val bitmap: Bitmap = createTextMarkerBitmap(mContext, item.description, resId)
|
||||
if (item.t_lifecycle != 2) {
|
||||
val geometry: Geometry? = GeometryTools.createGeometry(item.geometry)
|
||||
if (geometry != null) {
|
||||
var geoPoint: GeoPoint? = null
|
||||
if (geometry.geometryType != null) {
|
||||
when (geometry.geometryType.uppercase(Locale.getDefault())) {
|
||||
"POINT" -> geoPoint =
|
||||
GeoPoint(geometry.coordinate.y, geometry.coordinate.x)
|
||||
// "LINESTRING" -> {
|
||||
// val lineString = geometry as LineString
|
||||
// if (lineString != null && lineString.coordinates.size > 0) {
|
||||
// geoPoint = GeoPoint(
|
||||
// lineString.coordinates[0].y,
|
||||
// lineString.coordinates[0].x
|
||||
// )
|
||||
// }
|
||||
// val drawableLine: Drawable =
|
||||
// convertGeometry2Drawable(geometry, lineStyle)
|
||||
// if (drawableLine != null) {
|
||||
// dataVectorLayer.add(drawableLine)
|
||||
// }
|
||||
// }
|
||||
// "POLYGON" -> {
|
||||
// val polygon = geometry as Polygon
|
||||
// if (polygon != null && polygon.coordinates.size > 0) {
|
||||
// geoPoint = GeoPoint(
|
||||
// polygon.coordinates[0].y,
|
||||
// polygon.coordinates[0].x
|
||||
// )
|
||||
// }
|
||||
// val drawablePolygon: Drawable =
|
||||
// convertGeometry2Drawable(geometry, polygonStyle)
|
||||
// if (drawablePolygon != null) {
|
||||
// dataVectorLayer.add(drawablePolygon)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
if (geoPoint != null) {
|
||||
var geoMarkerItem: MarkerItem
|
||||
// if (item.getType() === 1) {
|
||||
geoMarkerItem = ClusterMarkerItem(
|
||||
1, item.id, item.description, geoPoint
|
||||
)
|
||||
// } else {
|
||||
// geoMarkerItem = MarkerItem(
|
||||
// ePointTemp.getType(),
|
||||
// ePointTemp.getId(),
|
||||
// ePointTemp.getStyleText(),
|
||||
// geoPoint
|
||||
// )
|
||||
// }
|
||||
markerItemsNames.add(geoMarkerItem)
|
||||
val markerSymbol =
|
||||
MarkerSymbol(bitmap, MarkerSymbol.HotspotPlace.CENTER)
|
||||
geoMarkerItem.marker = markerSymbol
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
itemizedLayer.addItems(markerItemsNames)
|
||||
addLayer(itemizedLayer, NIMapView.LAYER_GROUPS.OPERATE)
|
||||
withContext(Dispatchers.Main) {
|
||||
itemizedLayer.map().updateMap(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文字和图片拼装,文字换行
|
||||
*
|
||||
* @param context
|
||||
* @param text
|
||||
* @param resId
|
||||
* @return
|
||||
*/
|
||||
private fun createTextMarkerBitmap(context: Context, text: String, resId: Int): Bitmap {
|
||||
var text: String? = text
|
||||
return if (text == null || text.trim { it <= ' ' }.isEmpty()) {
|
||||
val drawable = ResourcesCompat.getDrawable(context.resources, resId, null)
|
||||
val originBitmap = android.graphics.Bitmap.createBitmap(
|
||||
drawable!!.intrinsicWidth,
|
||||
drawable.intrinsicHeight * 2,
|
||||
android.graphics.Bitmap.Config.ARGB_8888
|
||||
)
|
||||
val androidCanvas = Canvas(originBitmap)
|
||||
val startX = (originBitmap.width - drawable.intrinsicWidth) / 2
|
||||
drawable.setBounds(
|
||||
startX, 0, startX + drawable.intrinsicWidth, drawable.intrinsicHeight
|
||||
)
|
||||
drawable.draw(androidCanvas)
|
||||
val bitmap: Bitmap = AndroidBitmap(originBitmap)
|
||||
canvas.setBitmap(bitmap)
|
||||
bitmap
|
||||
} else {
|
||||
val drawable = ResourcesCompat.getDrawable(context.resources, resId, null)
|
||||
val textList: MutableList<String> = ArrayList()
|
||||
val fontSize: Float = NUM_13 * CanvasAdapter.getScale()
|
||||
paint.setTextSize(fontSize)
|
||||
var maxWidth = 0f
|
||||
//最多4行,一行7个
|
||||
if (text.trim { it <= ' ' }.length > 24) {
|
||||
val size = (drawable!!.intrinsicHeight / 4).toFloat()
|
||||
if (size < fontSize) paint.setTextSize(size)
|
||||
if (text.trim { it <= ' ' }.length > 28) text = text.substring(0, 26) + "..."
|
||||
val temp1 = text.substring(0, 7)
|
||||
textList.add(temp1)
|
||||
text = text.substring(7)
|
||||
maxWidth = paint.getTextWidth(temp1)
|
||||
val temp2 = text.substring(0, 7)
|
||||
textList.add(temp2)
|
||||
text = text.substring(7)
|
||||
var newWidth = paint.getTextWidth(temp2)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
val temp3 = text.substring(0, 7)
|
||||
textList.add(temp3)
|
||||
text = text.substring(7)
|
||||
newWidth = paint.getTextWidth(temp3)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
textList.add(text)
|
||||
newWidth = paint.getTextWidth(text)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
} else if (text.trim { it <= ' ' }.length > 21) {
|
||||
val size = (drawable!!.intrinsicHeight / 4).toFloat()
|
||||
if (size < fontSize) paint.setTextSize(size)
|
||||
val temp1 = text.substring(0, 6)
|
||||
textList.add(temp1)
|
||||
text = text.substring(6)
|
||||
maxWidth = paint.getTextWidth(temp1)
|
||||
val temp2 = text.substring(0, 6)
|
||||
textList.add(temp2)
|
||||
text = text.substring(6)
|
||||
var newWidth = paint.getTextWidth(temp2)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
val temp3 = text.substring(0, 6)
|
||||
textList.add(temp3)
|
||||
text = text.substring(6)
|
||||
newWidth = paint.getTextWidth(temp3)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
textList.add(text)
|
||||
newWidth = paint.getTextWidth(text)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
} else if (text.trim { it <= ' ' }.length > 18) {
|
||||
val size = (drawable!!.intrinsicHeight / 3).toFloat()
|
||||
if (size < fontSize) paint.setTextSize(size)
|
||||
val temp1 = text.substring(0, 7)
|
||||
textList.add(temp1)
|
||||
text = text.substring(7)
|
||||
maxWidth = paint.getTextWidth(temp1)
|
||||
val temp2 = text.substring(0, 7)
|
||||
textList.add(temp2)
|
||||
text = text.substring(7)
|
||||
var newWidth = paint.getTextWidth(temp2)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
textList.add(text)
|
||||
newWidth = paint.getTextWidth(text)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
} else if (text.trim { it <= ' ' }.length > 18) {
|
||||
val size = (drawable!!.intrinsicHeight / 3).toFloat()
|
||||
if (size < fontSize) paint.setTextSize(size)
|
||||
val temp1 = text.substring(0, 7)
|
||||
textList.add(temp1)
|
||||
text = text.substring(7)
|
||||
maxWidth = paint.getTextWidth(temp1)
|
||||
val temp2 = text.substring(0, 7)
|
||||
textList.add(temp2)
|
||||
text = text.substring(7)
|
||||
var newWidth = paint.getTextWidth(temp2)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
textList.add(text)
|
||||
newWidth = paint.getTextWidth(text)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
} else if (text.trim { it <= ' ' }.length > 14) {
|
||||
val size = (drawable!!.intrinsicHeight / 3).toFloat()
|
||||
if (size < fontSize) paint.setTextSize(size)
|
||||
val temp1 = text.substring(0, 6)
|
||||
textList.add(temp1)
|
||||
text = text.substring(6)
|
||||
maxWidth = paint.getTextWidth(temp1)
|
||||
val temp2 = text.substring(0, 6)
|
||||
textList.add(temp2)
|
||||
text = text.substring(6)
|
||||
var newWidth = paint.getTextWidth(temp2)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
textList.add(text)
|
||||
newWidth = paint.getTextWidth(text)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
} else if (text.trim { it <= ' ' }.length > 12) {
|
||||
val size = (drawable!!.intrinsicHeight / 2).toFloat()
|
||||
if (size < fontSize) paint.setTextSize(size)
|
||||
val temp1 = text.substring(0, 7)
|
||||
textList.add(temp1)
|
||||
text = text.substring(7)
|
||||
maxWidth = paint.getTextWidth(temp1)
|
||||
textList.add(text)
|
||||
val newWidth = paint.getTextWidth(text)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
} else if (text.trim { it <= ' ' }.length > 10) {
|
||||
val size = (drawable!!.intrinsicHeight / 2).toFloat()
|
||||
if (size < fontSize) paint.setTextSize(size)
|
||||
val temp1 = text.substring(0, 6)
|
||||
textList.add(temp1)
|
||||
text = text.substring(6)
|
||||
maxWidth = paint.getTextWidth(temp1)
|
||||
textList.add(text)
|
||||
val newWidth = paint.getTextWidth(text)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
} else if (text.trim { it <= ' ' }.length > 7) {
|
||||
val size = (drawable!!.intrinsicHeight / 2).toFloat()
|
||||
if (size < fontSize) paint.setTextSize(size)
|
||||
val temp1 = text.substring(0, 5)
|
||||
textList.add(temp1)
|
||||
text = text.substring(5)
|
||||
maxWidth = paint.getTextWidth(temp1)
|
||||
textList.add(text)
|
||||
val newWidth = paint.getTextWidth(text)
|
||||
if (newWidth > maxWidth) {
|
||||
maxWidth = newWidth
|
||||
}
|
||||
} else {
|
||||
val size = drawable!!.intrinsicHeight.toFloat()
|
||||
if (size < fontSize) paint.setTextSize(size)
|
||||
textList.add(text)
|
||||
maxWidth = paint.getTextWidth(text)
|
||||
}
|
||||
paint.color = Color.parseColor(mDefaultTextColor)
|
||||
val originBitmap = android.graphics.Bitmap.createBitmap(
|
||||
if (drawable.intrinsicWidth > maxWidth) drawable.intrinsicWidth else maxWidth.toInt(),
|
||||
drawable.intrinsicHeight * 2,
|
||||
android.graphics.Bitmap.Config.ARGB_4444
|
||||
)
|
||||
val androidCanvas = Canvas(originBitmap)
|
||||
val startX = (originBitmap.width - drawable.intrinsicWidth) / 2
|
||||
drawable.setBounds(
|
||||
startX, 0, startX + drawable.intrinsicWidth, drawable.intrinsicHeight
|
||||
)
|
||||
drawable.draw(androidCanvas)
|
||||
val bitmap: Bitmap = AndroidBitmap(originBitmap)
|
||||
canvas.setBitmap(bitmap)
|
||||
var startHeight = (drawable.intrinsicHeight + paint.getTextHeight(text)).toInt()
|
||||
for (txt in textList) {
|
||||
canvas.drawText(
|
||||
txt, (bitmap.width - paint.getTextWidth(txt)) / 2, startHeight.toFloat(), paint
|
||||
)
|
||||
startHeight += paint.getTextHeight(txt).toInt()
|
||||
}
|
||||
bitmap
|
||||
}
|
||||
}
|
||||
|
||||
// private fun getRasterTileLayer(
|
||||
// url: String?,
|
||||
// tilePath: String?,
|
||||
// useCache: Boolean
|
||||
// ): Layer {
|
||||
// val builder = OkHttpClient.Builder()
|
||||
// val mTileSource =
|
||||
// NavinfoMapRastorTileSource.builder(url).tilePath(tilePath)
|
||||
// .httpFactory(OkHttpFactory(builder)).build()
|
||||
// // 如果使用缓存
|
||||
// if (useCache) {
|
||||
// val cacheDirectory =
|
||||
// File(Constant.MAP_PATH, "cache")
|
||||
// val cacheSize = 300 * 1024 * 1024 // 300 MB
|
||||
// val cache = Cache(cacheDirectory, cacheSize.toLong())
|
||||
// builder.cache(cache)
|
||||
// }
|
||||
//
|
||||
// return BitmapTileLayer(mMapView.vtmMap, mTileSource)
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 基础
|
||||
*/
|
||||
@ -170,14 +506,10 @@ enum class BASE_MAP_TYPE(// TransportMap底图
|
||||
var title: String, var url: String, var tilePath: String
|
||||
) {
|
||||
OPEN_STREET_MAP(
|
||||
"Open Street Map",
|
||||
"http://a.tile.openstreetmap.org",
|
||||
"/{Z}}/{X}/{Y}.png"
|
||||
"Open Street Map", "http://a.tile.openstreetmap.org", "/{Z}}/{X}/{Y}.png"
|
||||
), // openStreetMap底图
|
||||
CYCLE_MAP(
|
||||
"Cycle Map",
|
||||
"http://c.tile.opencyclemap.org/cycle",
|
||||
"/{Z}}/{X}/{Y}.png"
|
||||
"Cycle Map", "http://c.tile.opencyclemap.org/cycle", "/{Z}}/{X}/{Y}.png"
|
||||
), // cyclemap底图
|
||||
S_MAP(
|
||||
"SMap",
|
||||
@ -185,9 +517,7 @@ enum class BASE_MAP_TYPE(// TransportMap底图
|
||||
"z={Z}&x={X}&y={Y}"
|
||||
), // cyclemap底图
|
||||
TRANSPORT_MAP(
|
||||
"Transport Map",
|
||||
"http://b.tile2.opencyclemap.org/transport",
|
||||
"/{Z}}/{X}/{Y}.png"
|
||||
"Transport Map", "http://b.tile2.opencyclemap.org/transport", "/{Z}}/{X}/{Y}.png"
|
||||
);
|
||||
|
||||
}
|
@ -3,6 +3,7 @@ package com.navinfo.collect.library.map.handler
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.navinfo.collect.library.R
|
||||
import com.navinfo.collect.library.map.NIMapView
|
||||
import com.navinfo.collect.library.utils.GeometryTools
|
||||
@ -21,7 +22,7 @@ import org.oscim.layers.vector.PathLayer
|
||||
import org.oscim.layers.vector.geometries.Style
|
||||
import org.oscim.map.Map
|
||||
|
||||
open class LineHandler(context: Context, mapView: NIMapView) :
|
||||
open class LineHandler(context: AppCompatActivity, mapView: NIMapView) :
|
||||
BaseHandler(context, mapView), Map.UpdateListener {
|
||||
|
||||
private var editIndex: Int = -1;
|
||||
|
@ -3,6 +3,7 @@ package com.navinfo.collect.library.map.handler
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.baidu.location.BDAbstractLocationListener
|
||||
import com.baidu.location.BDLocation
|
||||
import com.baidu.location.LocationClient
|
||||
@ -13,7 +14,7 @@ import com.navinfo.collect.library.map.NIMapView
|
||||
import org.oscim.layers.LocationLayer
|
||||
|
||||
|
||||
class LocationLayerHandler(context: Context, mapView: NIMapView) : BaseHandler(context, mapView) {
|
||||
class LocationLayerHandler(context: AppCompatActivity, mapView: NIMapView) : BaseHandler(context, mapView) {
|
||||
|
||||
private var mCurrentLocation: BDLocation? = null
|
||||
private var bFirst = true
|
||||
|
@ -2,6 +2,7 @@ package com.navinfo.collect.library.map.handler
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.navinfo.collect.library.R
|
||||
import com.navinfo.collect.library.map.GeoPoint
|
||||
import com.navinfo.collect.library.map.NIMapView
|
||||
@ -16,7 +17,7 @@ import org.oscim.layers.marker.MarkerSymbol
|
||||
/**
|
||||
* marker 操作
|
||||
*/
|
||||
class MarkHandler(context: Context, mapView: NIMapView) :
|
||||
class MarkHandler(context: AppCompatActivity, mapView: NIMapView) :
|
||||
BaseHandler(context, mapView) {
|
||||
|
||||
// //默认marker图层
|
||||
|
@ -6,6 +6,7 @@ import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.text.TextPaint
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.navinfo.collect.library.R
|
||||
import com.navinfo.collect.library.map.NIMapView
|
||||
import com.navinfo.collect.library.map.handler.BaseHandler
|
||||
@ -29,7 +30,7 @@ import org.oscim.layers.vector.geometries.Style
|
||||
import org.oscim.map.Map
|
||||
import java.math.BigDecimal
|
||||
|
||||
open class MeasureLayerHandler(context: Context, mapView: NIMapView) :
|
||||
open class MeasureLayerHandler(context: AppCompatActivity, mapView: NIMapView) :
|
||||
BaseHandler(context, mapView), Map.UpdateListener {
|
||||
|
||||
private var editIndex: Int = -1;//线修型的时候,用来表示是不是正在修型,修的第几个点
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.navinfo.collect.library.R
|
||||
import com.navinfo.collect.library.map.NIMapView
|
||||
import com.navinfo.collect.library.map.layers.NIPolygonLayer
|
||||
@ -23,7 +24,7 @@ import org.oscim.layers.vector.PathLayer
|
||||
import org.oscim.layers.vector.geometries.Style
|
||||
import org.oscim.map.Map
|
||||
|
||||
open class PolygonHandler(context: Context, mapView: NIMapView) :
|
||||
open class PolygonHandler(context: AppCompatActivity, mapView: NIMapView) :
|
||||
BaseHandler(context, mapView), Map.UpdateListener {
|
||||
|
||||
private var editIndex: Int = -1;
|
||||
|
@ -1,12 +1,13 @@
|
||||
package com.navinfo.collect.library.map.handler
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.navinfo.collect.library.map.NIMapView
|
||||
import com.navinfo.collect.library.utils.GeometryTools
|
||||
import org.oscim.core.GeoPoint
|
||||
import org.oscim.core.Point
|
||||
|
||||
open class ViewportHandler(context: Context, mapView: NIMapView) : BaseHandler(context, mapView) {
|
||||
open class ViewportHandler(context: AppCompatActivity, mapView: NIMapView) : BaseHandler(context, mapView) {
|
||||
/**
|
||||
* Set pivot horizontal / vertical relative to view center in [-1, 1].
|
||||
* e.g. pivotY 0.5 is usually preferred for navigation, moving center to 25% of view height.
|
||||
|
@ -0,0 +1,162 @@
|
||||
package com.navinfo.collect.library.map.layers;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.core.Box;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.event.Gesture;
|
||||
import org.oscim.event.MotionEvent;
|
||||
import org.oscim.layers.marker.ItemizedLayer;
|
||||
import org.oscim.layers.marker.MarkerInterface;
|
||||
import org.oscim.layers.marker.MarkerRendererFactory;
|
||||
import org.oscim.layers.marker.MarkerSymbol;
|
||||
import org.oscim.map.Map;
|
||||
import org.oscim.map.Viewport;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
*com.nmp.map.layer
|
||||
*zhjch
|
||||
*2021/12/9
|
||||
*15:32
|
||||
*说明()
|
||||
*/
|
||||
public class MyItemizedLayer extends ItemizedLayer {
|
||||
private OnItemGestureListener mOnItemGestureListener;
|
||||
private final ActiveItem mActiveItemSingleTap;
|
||||
private final ActiveItem mActiveItemLongPress;
|
||||
|
||||
public MyItemizedLayer(Map map, MarkerSymbol defaultMarker) {
|
||||
this(map, new ArrayList<>(), defaultMarker, null);
|
||||
}
|
||||
|
||||
public MyItemizedLayer(Map map, List<MarkerInterface> list, MarkerSymbol defaultMarker, OnItemGestureListener listener) {
|
||||
super(map, list, defaultMarker, null);
|
||||
mOnItemGestureListener = listener;
|
||||
this.mActiveItemSingleTap = new NamelessClass_2();
|
||||
this.mActiveItemLongPress = new NamelessClass_1();
|
||||
}
|
||||
|
||||
public MyItemizedLayer(Map map, MarkerRendererFactory markerRendererFactory) {
|
||||
this(map, new ArrayList<>(), markerRendererFactory, null);
|
||||
}
|
||||
|
||||
class NamelessClass_2 implements ActiveItem {
|
||||
NamelessClass_2() {
|
||||
}
|
||||
|
||||
public boolean run(List list1, int nearest) {
|
||||
if (mOnItemGestureListener != null) {
|
||||
return mOnItemGestureListener.onItemSingleTapUp(MyItemizedLayer.this, list1, nearest);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class NamelessClass_1 implements ActiveItem {
|
||||
NamelessClass_1() {
|
||||
}
|
||||
|
||||
public boolean run(List list1, int nearest) {
|
||||
if (mOnItemGestureListener != null) {
|
||||
return mOnItemGestureListener.onItemLongPress(MyItemizedLayer.this, list1, nearest);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public MyItemizedLayer(Map map, List<MarkerInterface> list, MarkerRendererFactory markerRendererFactory, OnItemGestureListener listener) {
|
||||
super(map, list, markerRendererFactory, null);
|
||||
mOnItemGestureListener = listener;
|
||||
this.mActiveItemSingleTap = new NamelessClass_2();
|
||||
this.mActiveItemLongPress = new NamelessClass_1();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGesture(Gesture g, MotionEvent e) {
|
||||
if (!this.isEnabled()) {
|
||||
return false;
|
||||
} else if (g instanceof Gesture.Tap) {
|
||||
return this.activateSelectedItems(e, this.mActiveItemSingleTap);
|
||||
} else {
|
||||
return g instanceof Gesture.LongPress ? this.activateSelectedItems(e, this.mActiveItemLongPress) : false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean activateSelectedItems(MotionEvent event, ActiveItem task) {
|
||||
int size = this.mItemList.size();
|
||||
|
||||
Log.e("jingo", "地图点击 size =" + size);
|
||||
if (size == 0) {
|
||||
return false;
|
||||
} else {
|
||||
int eventX = (int) event.getX() - this.mMap.getWidth() / 2;
|
||||
int eventY = (int) event.getY() - this.mMap.getHeight() / 2;
|
||||
Viewport mapPosition = this.mMap.viewport();
|
||||
Box box = mapPosition.getBBox((Box) null, Tile.SIZE / 2);
|
||||
box.map2mercator();
|
||||
box.scale(1000000.0D);
|
||||
int nearest = -1;
|
||||
int inside = -1;
|
||||
// double insideY = -1.7976931348623157E308D;
|
||||
double dist = (double) (20.0F * CanvasAdapter.getScale() * 20.0F * CanvasAdapter.getScale());
|
||||
List list = new ArrayList();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
MarkerInterface item = (MarkerInterface) this.mItemList.get(i);
|
||||
if (box.contains((double) item.getPoint().longitudeE6, (double) item.getPoint().latitudeE6)) {
|
||||
mapPosition.toScreenPoint(item.getPoint(), this.mTmpPoint);
|
||||
float dx = (float) ((double) eventX - this.mTmpPoint.x);
|
||||
float dy = (float) ((double) eventY - this.mTmpPoint.y);
|
||||
MarkerSymbol it = item.getMarker();
|
||||
if (it == null) {
|
||||
continue;
|
||||
// it = this.mMarkerRenderer.mDefaultMarker;
|
||||
}
|
||||
|
||||
if (it.isInside(dx, dy)) {// && this.mTmpPoint.y > insideY) {
|
||||
// insideY = this.mTmpPoint.y;
|
||||
inside = i;
|
||||
list.add(i);
|
||||
}
|
||||
|
||||
if (inside < 0) {
|
||||
double d = (double) (dx * dx + dy * dy);
|
||||
if (d <= dist) {
|
||||
dist = d;
|
||||
nearest = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inside >= 0) {
|
||||
nearest = inside;
|
||||
}
|
||||
|
||||
if (nearest >= 0 && task.run(list, nearest)) {
|
||||
this.mMarkerRenderer.update();
|
||||
this.mMap.render();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ActiveItem {
|
||||
boolean run(List list, int nearest);
|
||||
}
|
||||
|
||||
public void setOnItemGestureListener(OnItemGestureListener listener) {
|
||||
this.mOnItemGestureListener = listener;
|
||||
}
|
||||
|
||||
public interface OnItemGestureListener {
|
||||
boolean onItemSingleTapUp(MyItemizedLayer layer, List<Integer> list, int nearest);
|
||||
|
||||
boolean onItemLongPress(MyItemizedLayer layer, List<Integer> list, int nearest);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user