Android基於Mapbox V10 繪制LineGradient軌跡
前言
當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, 在兩個類上添加擴展方法:
/** ** 通過 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{} 的源碼如下, 所以需要提供一個高階函數的數組
fun stop(block: ExpressionBuilder.() -> Unit) { [email protected](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 繪制 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- JavaScript實現經緯度轉換常用方法總結
- 微信小程序開發之實現一個跑步小程序
- vue中調用百度地圖獲取經緯度的實現
- uniapp地圖組件(map)使用與遇到的一些問題總結
- Vue+Openlayer批量設置閃爍點的實現代碼(基於postrender機制)