数据驱动样式设计
使用 Maps SDK 的数据驱动样式化功能来创建和显示多种类型的数据。您可以根据特定数据集中的信息实时动态地更改地图的外观和感觉。数据驱动的样式很大程度上建立在 sources 和 layers 的思想之上。
注意:Sources 和 layers 提供了灵活的选项来定制Mapbox地图的外观和地图上显示的数据。Android的Mapbox注释插件提供了一个简化的系统,用于与Mapbox映射层交互和定制Mapbox Map layers 。
1. Sources 源
源包含实际的数据和层引用源。这就是如何在Mapbox地图上显示数据。支持多种不同的源类型,选择正确的源类型取决于您的数据类型。添加源不会立即使数据出现在地图上,因为源不包含诸如颜色或宽度之类的样式细节。层引用一个源并给它一个可视化的表示。
使用一个源文件需要两个参数。源需要一个惟一的字符串ID并需要某种类型的数据。
Vector 矢量
VectorSource 块必须是 Mapbox 矢量块格式。所有使用矢量源的层都必须指定一个“源层”值。对于由 Mapbox 承载的向量块,URL值的形式应该是:Mapbox://mapid,例如:
// 添加一个矢量源层
VectorSource vectorSource = new VectorSource("vector-source", "mapbox://mapbox.mapbox-terrain-v2");
mapboxMap.getStyle().addSource(vectorSource);
Raster 栅格
如果 RasterSource 块是 TileJSON 格式的,可以将它们添加到map中。如果由Mapbox承载,URL值应该是形式:Mapbox://mapid,例如:
// 添加一个栅格源层
RasterSource rasterSource = new RasterSource("raster-source", "mapbox://mapbox.u8yyzaor");
mapboxMap.getStyle().addSource(rasterSource);
GeoJson
可以通过几种不同的方式添加 GeoJsonSource。您可以提供一个指向在线托管的 GeoJSON 原始数据的URL,提供一个指向本地托管在应用程序的assets文件夹中的 GeoJSON 文件的链接,或者您可以在代码中直接构建自己的 GeoJSON 特征集合(FeatureCollection)。下面的代码片段展示了向地图添加 GeoJSON 源的不同方法。
(1)从URL添加一个GeoJSON源
try {
URL geoJsonUrl = new URL("https://url-to-geojson-file.geojson");
GeoJsonSource geoJsonSource = new GeoJsonSource("geojson-source", geoJsonUrl);
mapboxMap.getStyle().addSource(geoJsonSource);
} catch (MalformedURLException exception) {
Log.d(TAG, exception);
}
(2)加载本地存储的GeoJSON文件。要么使用下面找到的loadJsonFromAsset()方法,要么使用自己喜欢的方式加载JSON文件
private String loadJsonFromAsset(String nameOfLocalFile) throws IOException {
InputStream is = getAssets().open(本地存储的GeoJSON文件名);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
return new String(buffer, "UTF-8");
}
GeoJsonSource source = new GeoJsonSource("geojson-source", loadJsonFromAsset("local_file.geojson"));
mapboxMap.addSource(source);
(3)创建一个GeoJSON FeatureCollection 并将其添加到地图中
// 创建一个列表来存储行坐标。
List routeCoordinates = new ArrayList<Point>();
routeCoordinates.add(Point.fromLngLat(-118.394391, 33.397676));
routeCoordinates.add(Point.fromLngLat(-118.370917, 33.391142));
// 从坐标列表中创建LineString,然后创建一个GeoJSON FeatureCollection
// 这样就可以将该line当作一个layer添加到地图中
LineString lineString = LineString.fromLngLats(routeCoordinates);
FeatureCollection featureCollection = FeatureCollection.fromFeatures(
new Feature[]{Feature.fromGeometry(lineString)});
GeoJsonSource geoJsonSource = new GeoJsonSource("geojson-source", featureCollection);
mapboxMap.getStyle().addSource(geoJsonSource);
注:将数据放在GeoJSON源中有一个好处,您可以随时在源中更新、删除或添加其他特性,从而提供了通过运行时样式化API在地图中动画数据的解决方案。例如,Android ValueAnimator可以通过更新GeoJSON数据中的坐标来移动某个feature。
Image 图片
ImageSource 允许在地图顶部显示地理相关的栅格图像。当用户缩放、倾斜和旋转地图时,地理相关的图像会缩放和旋转。由 LatLngQuad 提供的栅格图像内容的地理位置可以是非轴对齐的。
// 设置图像的四个角的经纬度坐标
LatLngQuad quad = new LatLngQuad(
new LatLng(46.437, -80.425),
new LatLng(46.437, -71.516),
new LatLng(37.936, -71.516),
new LatLng(37.936, -80.425));
mapboxMap.getStyle().addSource(new ImageSource(ID_IMAGE_SOURCE, quad, DRAWABLE_IMAGE_HERE));
// Add layer
RasterLayer layer = new RasterLayer(ID_IMAGE_LAYER, IMAGE_SOURCE_ID);
mapboxMap.getStyle().addLayer(layer);
setImage()
方法是通过传入一个 drawable 来更新 ImageSource 图像的一种方便的方法:
ImageSource imageSource = (ImageSource) mapboxMap.getSource(ID_IMAGE_SOURCE);
imageSource.setImage(DESIRED_IMAGE);
Custom geometry 自定义几何
当您拥有动态生成的数据或需要按需加载的数据时,CustomGeometrySource 非常有用。CustomGeometrySource 中可以使用任何类型和数量的 GeoJSON 几何图形的 FeatureCollection。
CustomGeometrySource source = new CustomGeometrySource(CUSTOM_SOURCE_ID, GeometryTileProvider);
mapboxMap.getStyle().addSource(source);
注:CustomGeometrySource使用的一个例子是在map的顶部创建一个黑色网格。这个例子的代码可以在Android测试应用程序的Maps SDK的GridSourceActivity中找到。
Raster DEM
RasterDemSource支持Mapbox Terrain RGB (Mapbox://map . terrine - RGB)和Mapzen Terrarium tile格式。
Mapbox地形分块栏用于将丘陵地形添加到任何Mapbox地图中。运行时样式还可以用于更改hillshade外观。示例:
RasterDemSource rasterDemSource = new RasterDemSource("source-id", "mapbox://mapbox.terrain-rgb");
mapboxMap.getStyle().addSource(rasterDemSource);
// Create hillshade layer source to map
HillshadeLayer hillshadeLayer = new HillshadeLayer("hillshade-layer-id", "source-id").withProperties(
hillshadeHighlightColor(Color.parseColor(HILLSHADE_HIGHLIGHT_COLOR)),
hillshadeShadowColor(Color.BLACK)
);
// Add hillshade layer to map
mapboxMap.getStyle().addLayer(hillshadeLayer);
2. Layers 层
当源保存数据时,层用于样式化和显示信息。根据源几何形状提供了几种层类型。除了背景类型的层之外,每个层都需要引用一个源。您可以选择过滤特性,然后定义这些特性的样式。
每一层都提供一个 setProperties API,可以用许多不同的方式对该层进行样式设置。注意,与其根据源数据中的特定情况创建不同的层,还不如使用数据驱动样式来减少地图需要呈现的层数。
Background
背景层类型是惟一的,因为它不需要源。背景层可以是纯色或图案。
BackgroundLayer backgroundLayer = new BackgroundLayer("background-layer");
backgroundLayer.setProperties(
PropertyFactory.backgroundColor(Color.BLUE)
);
Fill
填充层具有封闭的形状几何,这对于在地图上标记区域非常有用。使用带有GeoJSON多边形 或 多多边形几何图形的填充层。几何图形就像一个线层,由一系列特定顺序的坐标组成,其中第一个点和最后一个点具有相同的坐标。当坐标列表以相同的坐标开始和结束时,几何图形是“封闭的”。如果几何图形不是封闭的,填充层将呈现,但一些顶点和边可能被瓦片边界切断。
FillLayer fillLayer = new FillLayer("layer-id", "source-id");
fillLayer.setProperties(
PropertyFactory.fillColor(Color.GREEN)
);
要改变几何图形的形状,一旦你添加了它,图层可以保持不变,不需要改变,只需要更新它使用的源文件。该层将始终显示其源代码中的最新更新。
Line
一系列坐标可以组合在一起,创建一个显示在地图上的线段。在每对坐标之间,创建一个线段,该线段被绘制成直线并连接两个点。
在开始之前,您需要确保您的层将使用的源具有 LineString 作为其几何结构的一部分,您可以在GeoJSON Source部分找到一个这样的例子。一旦创建了源并将其添加到地图中,就可以启动line层,并设置属性。
LineLayer lineLayer = new LineLayer("line-layer", "line-source");
// 线条的图层属性。此处你可以使线变为虚线,设置颜色等
lineLayer.setProperties(
PropertyFactory.lineDasharray(new Float[]{0.01f, 2f}),
PropertyFactory.lineCap(Property.LINE_CAP_ROUND),
PropertyFactory.lineJoin(Property.LINE_JOIN_ROUND),
PropertyFactory.lineWidth(5f),
PropertyFactory.lineColor(Color.parseColor("#e55e5e"))
);
mapboxMap.getStyle().addLayer(lineLayer);
Symbol
符号层用图标或文本标签显示地图上的单个位置。与GL标记和标记视图一样,符号层可以表示相同的数据,并为地图显示提供最强大的功能。首先,您将向地图添加一个标记图像,然后将其显示为一个符号层,如下:
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.my_marker_icon);
mapboxMap.getStyle().addImage("my-marker-image", icon);
SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
symbolLayer.setProperties(
PropertyFactory.iconImage("my-marker-image")
);
mapboxMap.getStyle().addLayer(symbolLayer);
符号层不仅可以使用图像在地图上标记位置,还可以在地图上直接显示文本。SDK处理文本符号层的过程与上面给出的图像片段类似,只是层的属性发生了变化。
平移图标:使用PropertyFactory。如果使用标记图标作为符号层图标,则iconOffset。任何SymbolLayer图标的中间都放置在坐标上,而不是标记图标的底部。 PropertyFactory.iconOffset 将标记图标向上移动一定距离,使标记图标的底部(针点)位于坐标位置。您需要找到正确的浮点值来通过 PropertyFactory.iconOffset,因为它将取决于SymbolLayer图标的大小,示例如下:
loadedMapStyle.addLayer(new SymbolLayer("SYMBOL_LAYER_ID", "SYMBOL_LAYER_SOURCE_ID").withProperties(
iconImage("SYMBOL_LAYER_ICON_ID"),
iconOffset(new Float[] {0f, -8f})
));
Raster
栅格层通常是显示在基本地图块顶部的图像集合。虽然矢量块是首选,但卫星图像或遗留地图样式呈现为栅格层。定义方式如下:
RasterSource rasterSource = new RasterSource("source-id", "mapbox://mapbox.u8yyzaor");
mapboxMap.getStyle().addSource(rasterSource);
RasterLayer rasterLayer = new RasterLayer("layer-id", "source-id");
mapboxMap.getStyle().addLayer(rasterLayer);
栅格层的一个常见用例是向地图添加一层卫星贴图:
RasterSource satelliteRasterSource = new RasterSource("satellite-raster-source", "mapbox://mapbox.satellite",512);
mapboxMap.getStyle().addSource(satelliteRasterSource);
Circle
圆层有一个来自源数据的单中心坐标。它是在地图上精确地画出地球表面的一个圆的地理投影。提供了一些默认属性,但是可以在第一次创建层时重写这些属性。
VectorSource vectorSource = new VectorSource("source-id", "mapbox://mapbox.2opop9hr");
mapboxMap.getStyle().addSource(vectorSource);
CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
circleLayer.setSourceLayer("museum-cusco");
circleLayer.setProperties(
PropertyFactory.visibility(Property.VISIBLE),
PropertyFactory.circleRadius(8f),
PropertyFactory.circleColor(Color.argb(1, 55, 148, 179))
);
mapboxMap.getStyle().addLayer(circleLayer);
3. 移除Source和Layer
如果源仍然被任何层使用,则不能删除它。删除操作将失败,并记录控制台警告。从Maps SDK的7.0.0版本开始,SDK更改了删除方法,以返回一个boolean值,该值表示删除是否成功。
在删除源之前,必须删除使用特定源的所有层:
if (mapboxMap != null && mapboxMap.getStyle() != null) {
mapboxMap.getStyle().removeLayer("layer-id");
}
然后再删除特定的源:
if (mapboxMap != null && mapboxMap.getStyle() != null) {
mapboxMap.getStyle().removeSource("source-id");
}
4. 修改Source、Layer的属性
源和层不是不可变的,因此可以在地图呈现期间随时修改它们。例如,要更改 添加到地图后的图层的 填充颜色,可以使用地图的Style对象获取图层并设置属性。
FillLayer fillLayer = (FillLayer) mapboxMap.getStyle().getLayer("fill-layer-id");
if (fillLayer != null) {
fillLayer.setProperties(
PropertyFactory.fillColor(Color.GREEN)
);
}
在GeoJSON源代码中,您可以像这样更改、添加、删除或替换FeatureCollection :
GeoJsonSource geoJsonSource = (GeoJsonSource) mapboxMap.getStyle().getSource("geojson-source-id");
if (geoJsonSource != null) {
geoJsonSource.setGeoJson(featureCollection);
}
5. 获取单击事件
层是不可单击的,并且不公开任何事件侦听器来处理用户输入。相反,地图查询工具可以帮助检测用户何时与地图交互(例如,当填充层的多边形被选中时)。
或者,可以使用Mapbox的 Annotation 插件,它提供 onClick() 和 onLongClick() 监听。