文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android基于Mapbox V10 绘制LineGradient轨迹

2024-04-02 19:55

关注

前言

当Mapbox升级到V10(我直接到当前的最新V10.3)版本后,就可以就此实现自己想要实现的功能。

官方文档 (docs.mapbox.com/android/map…)上的一些case就不在重复了

UISettings:

PreV10 通过MapView 拿到UISettings, 然后控制相关属性,V10 UISettings已经被移除了,不再统一管理,比较分散。

参见相关属性的控制:

mMapView.compass.visibility = false
mMapView.logo.enabled = false
mMapView.attribution.enabled = false
mMapView.gestures.updateSettings { null }//控制无法触摸

PreV10 与 V10 的Camera 相关的animation (涉及到用到的Point,PreV10 之前有LatLn, Point 两个类,当时还觉得为啥弄两个,比较冗余,V10里面拿掉了LatLn保留Point,注意的是Point构造时 longitude为第一个prarams. )

普通的 camera

fun moveCamera(
        mapView: MapView,
        originalList: List<Point>,
        paddingStart: Double,
        paddingTop: Double,
        paddingEnd: Double,
        paddingBottom: Double
    ) {
        if (originalList.isEmpty()) {
            return
        }
        val mapboxMap = mapView.getMapboxMap()
        val camera = mapboxMap.cameraForCoordinates(originalList, EdgeInsets(paddingTop, paddingStart, paddingBottom, paddingEnd))
        mapView.camera.flyTo(camera)
//        mapboxMap.setCamera(camera)
    }

camera动画之后,animationEnd 后的回调 需求时,传入animationOptions给 easeTo(),如下实现:

fun easeCamera(mapView:MapView, originalList: List<Point>,
                   margin: Double,
                   duration: Long,
                   actionAfter: (() -> Unit)? = null){
        if (originalList.isEmpty()) {
            return
        }
        val animationOptions = MapAnimationOptions.mapAnimationOptions {
            duration(duration)
//            owner(MapAnimationOwnerRegistry.GESTURES)
            animatorListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator?) {
                    actionAfter?.invoke()
                }
            })
        }
        val mapboxMap = mapView.getMapboxMap()
        val camera = mapboxMap.cameraForCoordinates(originalList, EdgeInsets(margin, margin, margin, margin))
        mapView.camera.easeTo(camera, animationOptions)
    }

LatlngBounds:

Pre10 之前的类,并且可以通过 List 创建一个 LatlngBounds, 然后 getCenter(), V10中直接拿掉了 LatLngBounds, 也就没有获取List 对应的 getCenter()了,没有仔细地去找API是否有相对应的替代,直接自己用扩展实现了一下:

@JvmStatic
fun getCenter(originalList: List<Point>): Point? {
  if (originalList.isEmpty()) {
    return null
  }
  if (originalList.size < 2) {
    return originalList[0]
  }
  val multiPoint = MultiPoint.fromLngLats(originalList)
  val boundingBox = multiPoint.createBoundingBoxFromPoints()
  return boundingBox?.getCenter()
}

涉及到两个类 BoundingBox 及 MultiPoint, 在两个类上添加扩展方法:


fun MultiPoint.createBoundingBoxFromPoints(): BoundingBox?{
    val coordinates = coordinates()
    if (coordinates.size > 1){
        var minLat: Double = MAX_LATITUDE
        var minLon: Double = MAX_LONGITUDE
        var maxLat: Double = MIN_LATITUDE
        var maxLon: Double = MIN_LONGITUDE
​
        for (gp in coordinates) {
            val latitude: Double = gp.latitude()
            val longitude: Double = gp.longitude()
            minLat = Math.min(minLat, latitude)
            minLon = Math.min(minLon, longitude)
            maxLat = Math.max(maxLat, latitude)
            maxLon = Math.max(maxLon, longitude)
        }
        val southwest = Point.fromLngLat(minLon, minLat)
        val northeast = Point.fromLngLat(maxLon, maxLat)
        return BoundingBox.fromPoints(southwest, northeast)
    }
    return null
}
​

fun BoundingBox.getCenter(): Point {
    val centerLon = (southwest().longitude() + northeast().longitude())/2.0
    val centerLat = (southwest().latitude() + northeast().latitude())/2.0
    return Point.fromLngLat(centerLon, centerLat)
}

Style设定Layer

V10 添加了DSL build 添加 source、layer,相对而言source、layer都比较集中在builder{}的 block里,实际应用中通常source、layer 的添加都是分离的,动态的,通过sourceID, layerId 找到对应的 source、layer然后修改里面的内容, 发现 addLayerBelow(layer, layId) 该方法不好使,Crash了,暂且不用它了。 SymbolLayer 相比之前的api接口,少了一个 style.addImages(imagesMap), 不再支持一次性添加多个Image,添加一个简单的扩展函数即可。

fun Style.addImages(imageMap: Map<String, Bitmap>) {
    imageMap.forEach { (t, u) ->
        addImage(t, u)
    }
}

创建SymbolLayer 及 Source (Feature)的case

 private fun createSymbolLayer(
        layerId: String,
        sourceId: String,
        isChangeStyle: Boolean,
        offsetY: Float
    ): SymbolLayer {
        return symbolLayer(layerId, sourceId){
            iconImage(PROPERTY_ICON_NAME_PATTERN)
            iconAllowOverlap(true)
            iconSize(if (isChangeStyle) 1.0 else 0.0)
            iconIgnorePlacement(true)
            iconOffset(listOf(0.0, offsetY.toDouble()))
        }
    }
// 控制iconSize 大小是方便做动画。
​
private fun createSymbolBitmap(latLng: Point, markerStr: String, markerParams: MarkerParams?) {
        val feature = Feature.fromGeometry(Point.fromLngLat(latLng.longitude(), latLng.latitude()))
        val bitmap = createMarkerBitmap(mContext, markerParams!!)
        feature.addStringProperty(PROPERTY_ICON_NAME, markerStr)
        imagesMap[markerStr] = bitmap
        markerCoordinates.add(feature)
    }

添加对应的 source, Layer

style.addSource(
                geoJsonSource(END_SOURCE_ID){
                    featureCollection(FeatureCollection.fromFeatures(markerCoordinates))
                }
            )
​
style.addLayer(endSymbolLayer)

绘制轨迹LineLayer

同样添加Layer前需要添加 source, List 构建 FeatureCollection, 如下:

mMapView.getMapboxMap().getStyle()?.addSource(
    geoJsonSource(sourceId){
        featureCollection(
            FeatureCollection.fromFeatures(
                arrayOf(
                    Feature.fromGeometry(
                        LineString.fromLngLats(points)
                    )
                )
            )
        )
        lineMetrics(true) // 注意这里,绘制LineGradient 需要添加这行代码。
    }
)

添加单色的 LineLayer

mMapView.getMapboxMap().getStyle()?.addLayer(
                lineLayer(layerId, sourceId){
                    lineDasharray(listOf(0.01, 2.0))
                    lineCap(LineCap.ROUND)
                    lineJoin(LineJoin.ROUND)
                    lineWidth(TRACE_WIDTH.toDouble())
                    lineColor(pathColor)
                }
            )

绘制LineGradient, 先聊 Pre10的方案

​ 
  public static PropertyValue<Expression> lineGradient(Expression expression) {
    return new PaintPropertyValue<>("line-gradient", expression);
  }
​
 
  public static Expression interpolate(@NonNull Interpolator interpolation,
                                       @NonNull Expression number, Stop... stops) {
    return interpolate(interpolation, number, Stop.toExpressionArray(stops));
  }

以上只需创建 Expression.Stop[] stops, 根据List 中每个Point 的配速对应的色值,传入即可,绘制LineGradient。

V10 中不再有 Expression 下 的Stop类,所以无从谈起创建Stop[] 了,从官方的demo里看 , 最后跟了不定的 stop{}, 可以看见是一个可变参数,所以打算构建一个 stop{} 的数据。

 private fun createHeatmapLayer(): HeatmapLayer {
    return heatmapLayer(
      HEATMAP_LAYER_ID,
      EARTHQUAKE_SOURCE_ID
    ) {
      maxZoom(9.0)
      sourceLayer(HEATMAP_LAYER_SOURCE)
      // Begin color ramp at 0-stop with a 0-transparancy color
      // to create a blur-like effect.
      heatmapColor(
        interpolate {
          linear()
          heatmapDensity()
          stop {
            literal(0)
            rgba(33.0, 102.0, 172.0, 0.0)
          }
          stop {
            literal(0.2)
            rgb(103.0, 169.0, 207.0)
          }
          stop {
            literal(0.4)
            rgb(209.0, 229.0, 240.0)
          }
          stop {
            literal(0.6)
            rgb(253.0, 219.0, 240.0)
          }
          stop {
            literal(0.8)
            rgb(239.0, 138.0, 98.0)
          }
          stop {
            literal(1)
            rgb(178.0, 24.0, 43.0)
          }
        }
      )
      ...
      ...
    }
 }

其实 stop{} 的源码如下, 所以需要提供一个高阶函数的数组

    fun stop(block: ExpressionBuilder.() -> Unit) {
      this@ExpressionBuilder.apply(block)
    }
​
//给Expression.InterpolatorBuilder 添加一个 stops()的扩展方法即可
fun Expression.InterpolatorBuilder.stops(stopList:Array<(Expression.ExpressionBuilder.() -> Unit)?>){
    stopList.forEach { stop ->
        stop?.let {
            apply(it)
        }
    }
}
​
//将以上的扩展方法作为参数传入 构建 Expression的最后一个参数,
var colorExpression = Expression.interpolate{
                linear()
                lineProgress()
                stops(colorStops)
            }
​
//最后将 colorExpression 应用到构建lineLayer的 lineGradient(colorExpression) 作为参数即可,大功告成
mMapView.getMapboxMap().getStyle()?.addLayer(
                lineLayer(layerId, sourceId){
                    lineCap(LineCap.ROUND)
                    lineJoin(LineJoin.ROUND)
                    lineWidth(5.0)
                  lineGradient(colorExpression)
                }
            )

到此这篇关于Android基于Mapbox V10 绘制LineGradient轨迹的文章就介绍到这了,更多相关Android Mapbox 绘制 内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯