这篇文章主要介绍了Android基于Mapbox V10 绘制LineGradient轨迹,文章通告介绍一些V10上的用法,最终讲下如何绘制渐变运动记录轨迹,感兴趣的小伙伴可以参考一下
PreV10 与 V10 的Camera 相关的animation (涉及到用到的Point,PreV10 之前有LatLn, Point 两个类,当时还觉得为啥弄两个,比较冗余,V10里面拿掉了LatLn保留Point,注意的是Point构造时 longitude为第一个prarams. )
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, 在两个类上添加扩展方法:
** 通过 southwest、northeast 两个点构建 BoundingBox 对象
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
** 扩展BoundingBox getCenter()方法。
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的方案
/**
* Defines a gradient with which to color a line feature. Can only be used with GeoJSON sources that specify `"lineMetrics": true`.
* @param expression an expression statement
* @return property wrapper around an expression statement
public static PropertyValue<Expression> lineGradient(Expression expression) {
return new PaintPropertyValue<>("line-gradient", expression);
Produces continuous, smooth results by interpolating between pairs of input and output values ("stops"). The `input` may be any numeric expression (e.g., `["get", "population"]`). Stop inputs must be numeric literals in strictly ascending order. The output type must be `number`, `array<number>`, or `color`.
Example usage:
FillLayer fillLayer = new FillLayer("layer-id", "source-id");
fillLayer.setProperties(
fillColor(
interpolate(
exponential(0.5f), zoom(),
stop(1.0f, color(Color.RED)),
stop(5.0f, color(Color.BLUE)),
stop(10.0f, color(Color.GREEN))
Params:
interpolation – type of interpolation
number – the input expression
stops – pair of input and output values
Returns:
expression
See Also:
Style specification
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{} 的源码如下, 所以需要提供一个高阶函数的数组