From c9ae281424534a8366b0d3a97532c3a0525233f9 Mon Sep 17 00:00:00 2001 From: squallzhjch Date: Wed, 19 Apr 2023 17:28:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=95=B0=E6=8D=AE=E6=B8=B2?= =?UTF-8?q?=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/navinfo/omqs/OMQSApplication.kt | 6 +- .../java/com/navinfo/omqs/db/MyRealmModule.kt | 6 +- .../library/data/entity/QsRecordBean.kt | 3 +- .../library/map/cluster/ClusterMarkerItem.kt | 24 + .../map/cluster/ClusterMarkerRenderer.kt | 471 ++++++++++++++++++ .../library/map/cluster/ClusterUtils.kt | 114 +++++ .../collect/library/map/cluster/Clustered.kt | 33 ++ .../library/map/cluster/NonClusterable.kt | 41 ++ .../library/map/handler/AnimationHandler.kt | 3 +- .../map/handler/LayerManagerHandler.kt | 400 +++++++++++++-- .../library/map/handler/LineHandler.kt | 3 +- .../map/handler/LocationLayerHandler.kt | 3 +- .../library/map/handler/MarkHandler.kt | 3 +- .../map/handler/MeasureLayerHandler.kt | 3 +- .../library/map/handler/PolygonHandler.kt | 3 +- .../library/map/handler/ViewportHandler.kt | 3 +- .../library/map/layers/MyItemizedLayer.java | 162 ++++++ 17 files changed, 1229 insertions(+), 52 deletions(-) create mode 100644 collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterMarkerItem.kt create mode 100644 collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterMarkerRenderer.kt create mode 100644 collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterUtils.kt create mode 100644 collect-library/src/main/java/com/navinfo/collect/library/map/cluster/Clustered.kt create mode 100644 collect-library/src/main/java/com/navinfo/collect/library/map/cluster/NonClusterable.kt create mode 100644 collect-library/src/main/java/com/navinfo/collect/library/map/layers/MyItemizedLayer.java diff --git a/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt b/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt index a021fc6b..86685c2d 100644 --- a/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt +++ b/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt @@ -1,14 +1,10 @@ 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 import io.realm.RealmConfiguration -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import java.io.File @HiltAndroidApp @@ -23,7 +19,7 @@ class OMQSApplication : Application() { val config = RealmConfiguration.Builder() .directory(File(Constant.DATA_PATH)) .name("HDData") - .modules(Realm.getDefaultModule(), MyRealmModule()) +// .modules(Realm.getDefaultModule(), MyRealmModule()) .schemaVersion(1) // .encryptionKey(password) .build() diff --git a/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt b/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt index f3d98286..0b1cb8a3 100644 --- a/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt +++ b/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt @@ -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 { -} \ No newline at end of file +//@io.realm.annotations.RealmModule(classes = [QsRecordBean::class]) +//class MyRealmModule { +//} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt index ed24f345..550239e2 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt @@ -4,7 +4,6 @@ import com.navinfo.collect.library.utils.GeometryToolsKt import io.realm.RealmObject import io.realm.RealmSet import io.realm.annotations.PrimaryKey -import io.realm.annotations.RealmClass /** @@ -14,7 +13,7 @@ import io.realm.annotations.RealmClass * @Date 2016/1/12 * @Description: ${TODO}(质检对象) */ -@RealmClass +//@RealmClass open class QsRecordBean @JvmOverloads constructor( /** * id 主键 diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterMarkerItem.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterMarkerItem.kt new file mode 100644 index 00000000..f466c4f1 --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterMarkerItem.kt @@ -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 = ArrayList() + + constructor(title: String?, description: String?, geoPoint: GeoPoint?) : this( + null, + title, + description, + geoPoint + ) { + } +} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterMarkerRenderer.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterMarkerRenderer.kt new file mode 100644 index 00000000..878482ea --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterMarkerRenderer.kt @@ -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 . + */ +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() // 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(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? = null + if (mGridMap.containsKey(itemGridIndex)) { + list = mGridMap[itemGridIndex] as ArrayList? + } + 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(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 + ) + } + } + } +} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterUtils.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterUtils.kt new file mode 100644 index 00000000..0d44204d --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/ClusterUtils.kt @@ -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 . + */ +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 + } + } +} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/Clustered.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/Clustered.kt new file mode 100644 index 00000000..9d23520c --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/Clustered.kt @@ -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 . + */ +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 +} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/NonClusterable.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/NonClusterable.kt new file mode 100644 index 00000000..885db37d --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/cluster/NonClusterable.kt @@ -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 . + */ +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 + ) { + } +} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/AnimationHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/AnimationHandler.kt index 126b0eb7..8862097d 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/AnimationHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/AnimationHandler.kt @@ -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) { diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt index aa709a97..6e6e65fb 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt @@ -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() + + /** + * 文字大小 + */ + 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() val realm = Realm.getDefaultInstance() realm.executeTransaction { - val list = realm.where().findAll() - paint.setColor(Color.parseColor(mDefaultTextColor)) + val objects =realm.where().findAll() + list = realm.copyFromRealm(objects) } realm.close() + + itemizedLayer = + MyItemizedLayer( + mMapView.vtmMap, + mutableListOf(), + markerRendererFactory, + object : MyItemizedLayer.OnItemGestureListener { + override fun onItemSingleTapUp( + layer: MyItemizedLayer?, + list: MutableList?, + nearest: Int + ): Boolean { + return true + } + + override fun onItemLongPress( + layer: MyItemizedLayer?, + list: MutableList?, + 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 = 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" ); } \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LineHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LineHandler.kt index a64311d1..ac0abb70 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LineHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LineHandler.kt @@ -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; diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt index e6558e75..dd950229 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt @@ -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 diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt index 7fbee159..6942847b 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt @@ -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图层 diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MeasureLayerHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MeasureLayerHandler.kt index b45c9633..41c0cf41 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MeasureLayerHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MeasureLayerHandler.kt @@ -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;//线修型的时候,用来表示是不是正在修型,修的第几个点 diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/PolygonHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/PolygonHandler.kt index fb3b8091..a68d1c3b 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/PolygonHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/PolygonHandler.kt @@ -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; diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt index dd3ef9d7..3f8e9e29 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt @@ -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. diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/layers/MyItemizedLayer.java b/collect-library/src/main/java/com/navinfo/collect/library/map/layers/MyItemizedLayer.java new file mode 100644 index 00000000..0452ab8c --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/layers/MyItemizedLayer.java @@ -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 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 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 list, int nearest); + + boolean onItemLongPress(MyItemizedLayer layer, List list, int nearest); + } +}