MapBox Android版开发 3 地图样式v11

前言

可以使用Mapbox Maps SDK自定义数据样式、调整地图样式、添加字体、创建数据驱动的可视化等。可以使用Mapbox StandardMapbox Standard Satellite,或通过调整地图的颜色、图标和字体来创建自定义地图样式。

有两种方法可以自定义地图的外观:

  • 使用Mapbox Studio创建自定义地图样式。
  • 使用Maps SDK在运行时动态更新地图功能。并可以动态切换语言,调整标签大小以提高可读性,根据一天中的时间调暗地图,个性化图标和地图颜色。

本文重点介绍MapBox默认的样式,样式相关的类和方法,以及如何动态更新样式并本地化语言。

MapBox样式对比

风格常量说明v9v11
Mapbox StandardSTANDARDA dynamic and performant 3D style that is the default for Mapbox maps.-standard
Mapbox Standard SatelliteSTANDARD_SATELLITECombines updated satellite imagery and vector layers to offer users improved clarity and detail.-standard-satellite
Mapbox StreetsMAPBOX_STREETSA complete base map, perfect for incorporating your own data.streets-v11streets-v12
OutdoorsOUTDOORSA general-purpose style tailored to outdoor activities.outdoors-v11outdoors-v12
LightLIGHTSubtle light backdrop for data visualizations.light-v10light-v11
DarkDARKSubtle dark backdrop for data visualizations.dark-v10dark-v11
SatelliteSATELLITEA beautiful global satellite and aerial imagery layer.satellite-v9satellite-v9
Satellite StreetsSATELLITE_STREETSGlobal satellite and aerial imagery with unobtrusive labels.satellite-streets-v11satellite-streets-v12
Traffic DayTRAFFIC_DAYColor-coded roads based on live traffic congestion data.traffic-day-v2traffic-day-v2
Traffic NightTRAFFIC_NIGHTColor-coded roads based on live traffic congestion data, designed to maximize legibility in low-light situations.traffic-night-v2traffic-night-v2

主要类和方法

Style类

Style对象是指应用程序中使用的Mapbox地图样式。

MapboxStyleManager
+StyleManager styleManager
Style

Style类

class Style internal constructor(
  styleManager: StyleManager,
  pixelRatio: Float,
  mapLoadingErrorDelegate: MapLoadingErrorDelegate,
) : MapboxStyleManager(styleManager, pixelRatio, mapLoadingErrorDelegate) {

  @Volatile
  private var isStyleValid = true
  
  /**
   * Returns the existing style layers.
   *
   * @return The list containing the ids of the existing style layers.
   */
  override val styleLayers: List<StyleObjectInfo>
    @MainThread
    get() {
      checkNativeStyle("getStyleLayers")
      return super.styleLayers
    }
    ...
}
  • MapboxStyleManager 类
open class MapboxStyleManager @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) constructor(
  /**
   * Native style manager instance.
   */
  @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
  val styleManager: StyleManager,
  /**
   * Current pixel ratio.
   */
  val pixelRatio: Float,
  /**
   * For internal usage.
   */
  @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
  val mapLoadingErrorDelegate: MapLoadingErrorDelegate,
) {
  }
  ...
}
  • StyleManager 类
public class StyleManager extends Observable {
    @MainThread
    public native boolean isStyleLoaded();

    @MainThread
    public native boolean styleLayerExists(@NonNull String layerId);

    @NonNull
    @MainThread
    public native List<StyleObjectInfo> getStyleLayers();
    ...
}

默认的MapBox样式

const val STANDARD = "mapbox://styles/mapbox/standard"
const val STANDARD_SATELLITE = "mapbox://styles/mapbox/standard-satellite"
const val MAPBOX_STREETS = "mapbox://styles/mapbox/streets-v12"
const val OUTDOORS = "mapbox://styles/mapbox/outdoors-v12"
const val LIGHT = "mapbox://styles/mapbox/light-v11"
const val DARK = "mapbox://styles/mapbox/dark-v11"
const val SATELLITE = "mapbox://styles/mapbox/satellite-v9"
const val SATELLITE_STREETS = "mapbox://styles/mapbox/satellite-streets-v12"
const val TRAFFIC_DAY = "mapbox://styles/mapbox/traffic-day-v2"
const val TRAFFIC_NIGHT = "mapbox://styles/mapbox/traffic-night-v2"

OnStyleLoaded 接口

/**
 * Callback to be invoked when a style has finished loading.
 */
fun interface OnStyleLoaded {
  /**
   * Invoked when a style has finished loading.
   *
   * @param style the style that has finished loading
   */
  fun onStyleLoaded(style: Style)
}

MapboxMap类

获取样式

var style: Style? = null
  @JvmSynthetic
  internal set
  get() {
    checkNativeMap("getStyle")
    return field
  }

设置样式

@JvmOverloads
fun loadStyle(
  style: String,
  onStyleLoaded: Style.OnStyleLoaded? = null,
) {
  checkNativeMap("loadStyle")
  initializeStyleLoad(
    onStyleLoaded,
    styleDataStyleLoadedListener = {},
  )
  applyStyle(style)
}

private fun applyStyle(style: String) {
  if (style.isValidUri()) {
    nativeMap.setStyleURI(style)
  } else {
    nativeMap.setStyleJSON(style.ifBlank { "{}" })
  }
}

本地化扩展

com.mapbox.maps.extension.localization包中实现了本地化功能,这种方法不支持standard样式。主要实现代码如下:

@JvmOverloads
fun MapboxStyleManager.localizeLabels(locale: Locale, layerIds: List<String>? = null) {
  if (styleURI == "mapbox://styles/mapbox/standard") {
    throw RuntimeException(
      "Mapbox Standard style does not support client-side runtime localization." +
        " Consider using Mapbox internationalization capability instead: https://www.mapbox.com/blog/maps-internationalization-34-languages"
    )
  }
  setMapLanguage(locale, this, layerIds)
}

internal fun setMapLanguage(locale: Locale, style: MapboxStyleManager, layerIds: List<String>?) {
  val convertedLocale = "name_${locale.language}"
  if (!isSupportedLanguage(convertedLocale)) {
    logE(TAG, "Locale: $locale is not supported.")
    return
  }

  layerIds?.forEach { id ->
    localizeTextFieldExpression(
      style = style,
      layerId = id,
      locale = locale,
      convertedLocale = convertedLocale,
      filterSymbolLayers = true,
    )
  } ?: style.styleLayers.forEach { layer ->
    if (layer.type == SYMBOL) {
      localizeTextFieldExpression(
        style = style,
        layerId = layer.id,
        locale = locale,
        convertedLocale = convertedLocale,
        filterSymbolLayers = false,
      )
    }
  }
}

private fun localizeTextFieldExpression(
  style: MapboxStyleManager,
  layerId: String,
  locale: Locale,
  convertedLocale: String,
  filterSymbolLayers: Boolean,
) {
  if (filterSymbolLayers) {
    val type = style.getStyleLayerProperty(layerId, TYPE).value.contents as? String
    if (type != SYMBOL) {
      return
    }
  }
  val textFieldProperty = style.getStyleLayerProperty(layerId, TEXT_FIELD)
  if (textFieldProperty.kind != StylePropertyValueKind.EXPRESSION) {
    return
  }
  val textField = textFieldProperty.value.toJson()
  val adaptedLocale = adaptLocaleToV8orV7IfNeeded(
    style,
    style.getStyleLayerProperty(layerId, SOURCE).value.contents as? String ?: "",
    locale
  ) ?: convertedLocale
  val getExpression = get(adaptedLocale).toJson()
  val localizedTextFieldExpressionAsJson = textField.replace(
    EXPRESSION_REGEX,
    getExpression
  ).replace(EXPRESSION_ABBR_REGEX, getExpression)
  if (BuildConfig.DEBUG) {
    logI(TAG, "Localize layer with expression: $localizedTextFieldExpressionAsJson")
  }
  val expected = Value.fromJson(localizedTextFieldExpressionAsJson)
  expected.value?.let { value ->
    style.setStyleLayerProperty(
      layerId,
      TEXT_FIELD,
      value
    )
  } ?: run {
    logE(TAG, "An error ${expected.error} occurred when converting $localizedTextFieldExpressionAsJson to a Value!")
  }
}

示例代码

地图样式类

设置地图样式,样式加载完成后重新本地化地图语言。

package com.example.mapdemo

import com.mapbox.maps.MapboxMap
import com.mapbox.maps.Style
import com.mapbox.maps.extension.localization.localizeLabels
import java.util.Locale

class MapStyle(map: MapboxMap) {
    private var map = map

    fun changeStyle(style: String) {
        map.loadStyle(style) {
            var layers = it.styleLayers
            if (style != Style.STANDARD && style != Style.STANDARD_SATELLITE) {
                it.localizeLabels(Locale.CHINESE)
            }
        }
    }
}

界面布局

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.mapbox.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/bottomView"
        app:layout_constraintTop_toTopOf="parent"
        app:mapbox_cameraBearing="0.0"
        app:mapbox_cameraPitch="0.0"
        app:mapbox_cameraTargetLat="32.2857965"
        app:mapbox_cameraTargetLng="104.293174"
        app:mapbox_cameraZoom="2" />

    <HorizontalScrollView
        android:id="@+id/bottomView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/background_dark"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@id/mapView">

        <RadioGroup
            android:id="@+id/RadioGroup"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="horizontal"
            android:paddingHorizontal="10dp">

            <RadioButton
                android:id="@+id/standard"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="false"
                android:onClick="setMapStyle"
                android:text="3D基础"
                android:textColor="@color/white"
                android:textStyle="bold" />

            <RadioButton
                android:id="@+id/standardSatellite"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="setMapStyle"
                android:text="3D影像"
                android:textColor="@color/white"
                android:textStyle="bold" />

            <RadioButton
                android:id="@+id/streets"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="true"
                android:onClick="setMapStyle"
                android:text="基础"
                android:textColor="@color/white"
                android:textStyle="bold" />

            <RadioButton
                android:id="@+id/satellite"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="setMapStyle"
                android:text="影像"
                android:textColor="@color/white"
                android:textStyle="bold" />

            <RadioButton
                android:id="@+id/satelliteStreets"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="setMapStyle"
                android:text="影像标签"
                android:textColor="@color/white"
                android:textStyle="bold" />

            <RadioButton
                android:id="@+id/outdoors"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="setMapStyle"
                android:text="户外"
                android:textColor="@color/white"
                android:textStyle="bold" />

            <RadioButton
                android:id="@+id/light"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="false"
                android:onClick="setMapStyle"
                android:text=""
                android:textColor="@color/white"
                android:textStyle="bold" />

            <RadioButton
                android:id="@+id/dark"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="false"
                android:onClick="setMapStyle"
                android:text=""
                android:textColor="@color/white"
                android:textStyle="bold" />

        </RadioGroup>
    </HorizontalScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

控件响应事件

fun setMapStyle(view: View) {
    val checked = (view as RadioButton).isChecked
    if (!checked)
        return

    val id = view.getId()
    if (id == R.id.standard) {
        mapStyle.changeStyle(Style.STANDARD)
    } else if (id == R.id.standardSatellite) {
        mapStyle.changeStyle(Style.STANDARD_SATELLITE)
    } else if (id == R.id.streets) {
        mapStyle.changeStyle(Style.MAPBOX_STREETS)
    } else if (id == R.id.satellite) {
        mapStyle.changeStyle(Style.SATELLITE)
    } else if (id == R.id.satelliteStreets) {
        mapStyle.changeStyle(Style.SATELLITE_STREETS)
    } else if (id == R.id.outdoors) {
        mapStyle.changeStyle(Style.OUTDOORS)
    } else if (id == R.id.light) {
        mapStyle.changeStyle(Style.LIGHT)
    } else if (id == R.id.dark) {
        mapStyle.changeStyle(Style.DARK)
    }
}

运行效果图

3D基础3D影像
在这里插入图片描述在这里插入图片描述
基础影像影像+标签
在这里插入图片描述在这里插入图片描述在这里插入图片描述
户外
在这里插入图片描述在这里插入图片描述在这里插入图片描述

附不同样式中的图层

查看地图样式的图层Z索引顺序和图层ID。

样式图层
Mapbox Standard-
Mapbox Standard Satellite-
Mapbox Streetsland, landcover, national-park, landuse, pitch-outline, waterway-shadow, water-shadow, waterway, water, water-depth, hillshade, land-structure-polygon, land-structure-line, aeroway-polygon, aeroway-line, building, building-underground, tunnel-minor-case, tunnel-street-case, tunnel-minor-link-case, tunnel-secondary-tertiary-case, tunnel-primary-case, tunnel-major-link-case, tunnel-motorway-trunk-case, tunnel-path, tunnel-steps, tunnel-pedestrian, tunnel-construction, tunnel-minor, tunnel-minor-link, tunnel-major-link, tunnel-street, tunnel-street-low, tunnel-secondary-tertiary, tunnel-primary, tunnel-motorway-trunk, tunnel-oneway-arrow-blue, tunnel-oneway-arrow-white, ferry, ferry-auto, road-pedestrian-polygon-fill, road-pedestrian-polygon-pattern, road-path-bg, road-steps-bg, road-pedestrian-case, road-path, road-steps, road-pedestrian, golf-hole-line, road-polygon, turning-feature-outline, road-minor-case, road-street-case, road-minor-link-case, road-secondary-tertiary-case, road-primary-case, road-major-link-case, road-motorway-trunk-case, turning-feature, road-construction, road-minor, road-minor-link, road-major-link, road-street, road-street-low, road-secondary-tertiary, road-primary, road-motorway-trunk, road-rail, road-rail-tracks, level-crossing, road-oneway-arrow-blue, road-oneway-arrow-white, crosswalks, bridge-path-bg, bridge-steps-bg, bridge-pedestrian-case, bridge-path, bridge-steps, bridge-pedestrian, bridge-minor-case, bridge-street-case, bridge-minor-link-case, bridge-secondary-tertiary-case, bridge-primary-case, bridge-major-link-case, bridge-motorway-trunk-case, bridge-construction, bridge-minor, bridge-minor-link, bridge-major-link, bridge-street, bridge-street-low, bridge-secondary-tertiary, bridge-primary, bridge-motorway-trunk, bridge-major-link-2-case, bridge-motorway-trunk-2-case, bridge-major-link-2, bridge-motorway-trunk-2, bridge-oneway-arrow-blue, bridge-oneway-arrow-white, bridge-rail, bridge-rail-tracks, aerialway, admin-1-boundary-bg, admin-0-boundary-bg, admin-1-boundary, admin-0-boundary, admin-0-boundary-disputed, building-entrance, building-number-label, block-number-label, road-label, road-intersection, road-number-shield, road-exit-shield, path-pedestrian-label, golf-hole-label, ferry-aerialway-label, waterway-label, natural-line-label, natural-point-label, water-line-label, water-point-label, poi-label, transit-label, airport-label, settlement-subdivision-label, settlement-minor-label, settlement-major-label, state-label, country-label, continent-label
Satellitebackground, satellite
Satellite Streetsbackground, satellite, tunnel-minor-case, tunnel-street-case, tunnel-minor-link-case, tunnel-secondary-tertiary-case, tunnel-primary-case, tunnel-major-link-case, tunnel-motorway-trunk-case, tunnel-path, tunnel-steps, tunnel-pedestrian, tunnel-minor, tunnel-minor-link, tunnel-major-link, tunnel-street, tunnel-street-low, tunnel-secondary-tertiary, tunnel-primary, tunnel-motorway-trunk, road-path, road-steps, road-pedestrian, road-minor-case, road-street-case, road-minor-link-case, road-secondary-tertiary-case, road-primary-case, road-major-link-case, road-motorway-trunk-case, road-minor, road-minor-link, road-major-link, road-street, road-street-low, road-secondary-tertiary, road-primary, road-motorway-trunk, bridge-path, bridge-steps, bridge-pedestrian, bridge-minor-case, bridge-street-case, bridge-minor-link-case, bridge-secondary-tertiary-case, bridge-primary-case, bridge-major-link-case, bridge-motorway-trunk-case, bridge-minor, bridge-minor-link, bridge-major-link, bridge-street, bridge-street-low, bridge-secondary-tertiary, bridge-primary, bridge-motorway-trunk, bridge-major-link-2-case, bridge-motorway-trunk-2-case, bridge-major-link-2, bridge-motorway-trunk-2, aerialway, admin-1-boundary-bg, admin-0-boundary-bg, admin-1-boundary, admin-0-boundary, admin-0-boundary-disputed, road-label, road-intersection, road-number-shield, road-exit-shield, path-pedestrian-label, ferry-aerialway-label, waterway-label, natural-line-label, natural-point-label, water-line-label, water-point-label, poi-label, transit-label, airport-label, settlement-subdivision-label, settlement-minor-label, settlement-major-label, state-label, country-label, continent-label
Outdoorsland, landcover, national-park, national-park_tint-band, landuse, pitch-outline, waterway-shadow, water-shadow, waterway, water, water-depth, wetland, wetland-pattern, hillshade, contour-line, land-structure-polygon, land-structure-line, aeroway-polygon, aeroway-line, building, building-underground, tunnel-minor-case, tunnel-street-case, tunnel-minor-link-case, tunnel-secondary-tertiary-case, tunnel-primary-case, tunnel-major-link-case, tunnel-motorway-trunk-case, tunnel-path-trail, tunnel-path-cycleway-piste, tunnel-path, tunnel-steps, tunnel-pedestrian, tunnel-construction, tunnel-minor, tunnel-minor-link, tunnel-major-link, tunnel-street, tunnel-street-low, tunnel-secondary-tertiary, tunnel-primary, tunnel-motorway-trunk, tunnel-oneway-arrow-blue, tunnel-oneway-arrow-white, cliff, ferry, ferry-auto, road-pedestrian-polygon-fill, road-pedestrian-polygon-pattern, road-path-bg, road-steps-bg, road-pedestrian-case, road-path-trail, road-path-cycleway-piste, road-path, road-steps, road-pedestrian, golf-hole-line, road-polygon, turning-feature-outline, road-minor-case, road-street-case, road-minor-link-case, road-secondary-tertiary-case, road-primary-case, road-major-link-case, road-motorway-trunk-case, turning-feature, road-construction, road-minor, road-minor-link, road-major-link, road-street, road-street-low, road-secondary-tertiary, road-primary, road-motorway-trunk, road-rail, road-rail-tracks, level-crossing, road-oneway-arrow-blue, road-oneway-arrow-white, crosswalks, gate-fence-hedge, bridge-path-bg, bridge-steps-bg, bridge-pedestrian-case, bridge-path-trail, bridge-path-cycleway-piste, bridge-path, bridge-steps, bridge-pedestrian, gate-label, bridge-minor-case, bridge-street-case, bridge-minor-link-case, bridge-secondary-tertiary-case, bridge-primary-case, bridge-major-link-case, bridge-motorway-trunk-case, bridge-construction, bridge-minor, bridge-minor-link, bridge-major-link, bridge-street, bridge-street-low, bridge-secondary-tertiary, bridge-primary, bridge-motorway-trunk, bridge-major-link-2-case, bridge-motorway-trunk-2-case, bridge-major-link-2, bridge-motorway-trunk-2, bridge-oneway-arrow-blue, bridge-oneway-arrow-white, bridge-rail, bridge-rail-tracks, aerialway, admin-1-boundary-bg, admin-0-boundary-bg, admin-1-boundary, admin-0-boundary, admin-0-boundary-disputed, contour-label, building-entrance, building-number-label, block-number-label, road-label, road-intersection, road-number-shield, road-exit-shield, path-pedestrian-label, golf-hole-label, ferry-aerialway-label, waterway-label, natural-line-label, natural-point-label, water-line-label, water-point-label, poi-label, transit-label, airport-label, settlement-subdivision-label, settlement-minor-label, settlement-major-label, state-label, country-label, continent-label
Lightland, national-park, landuse, waterway, water, land-structure-polygon, land-structure-line, aeroway-polygon, aeroway-line, building, tunnel-path-trail, tunnel-path-cycleway-piste, tunnel-path, tunnel-steps, tunnel-pedestrian, tunnel-simple, road-path-trail, road-path-cycleway-piste, road-path, road-steps, road-pedestrian, road-simple, road-rail, bridge-path-trail, bridge-path-cycleway-piste, bridge-path, bridge-steps, bridge-pedestrian, bridge-case-simple, bridge-simple, bridge-rail, admin-1-boundary-bg, admin-0-boundary-bg, admin-1-boundary, admin-0-boundary, admin-0-boundary-disputed, road-label-simple, waterway-label, natural-line-label, natural-point-label, water-line-label, water-point-label, poi-label, airport-label, settlement-subdivision-label, settlement-minor-label, settlement-major-label, state-label, country-label, continent-label
Darkland, national-park, landuse, waterway, water, land-structure-polygon, land-structure-line, aeroway-polygon, aeroway-line, building, tunnel-path-trail, tunnel-path-cycleway-piste, tunnel-path, tunnel-steps, tunnel-pedestrian, tunnel-simple, road-path-trail, road-path-cycleway-piste, road-path, road-steps, road-pedestrian, road-simple, road-rail, bridge-path-trail, bridge-path-cycleway-piste, bridge-path, bridge-steps, bridge-pedestrian, bridge-case-simple, bridge-simple, bridge-rail, admin-1-boundary-bg, admin-0-boundary-bg, admin-1-boundary, admin-0-boundary, admin-0-boundary-disputed, road-label-simple, waterway-label, natural-line-label, natural-point-label, water-line-label, water-point-label, poi-label, airport-label, settlement-subdivision-label, settlement-minor-label, settlement-major-label, state-label, country-label, continent-label
  • 26
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值