高德地图热力图的Flutter实现
高德地图SDK是有绘制热力图的功能的,但它的Flutter插件并没有封装这一块功能。我们业务中需要这个,就基于原插件修改了一下。[*]Android部分从AMapFlutterMapPlugin这个类开始,发现它是通过AMapPlatformViewFactory这个工厂类来创建View的。
进入AMapPlatformViewFactory的create方法,里面有很多形如
if (params.containsKey("xxx")) {
builder.setInitialMarkers(params.get("yyy"));
}的代码,这是提取从Flutter侧传过来的参数。我们加入一句
if (params.containsKey("heatmapTile")){
builder.setHeatmapTile(params.get("heatmapTile"));
}heatmapTile是Flutter传过来的参数名,下面Flutter部分会提到。再到AMapOptionsBuilder中添加字段与相应的set方法
private Object heatmapTile;
@Override
public void setHeatmapTile(Object heatmapTile) {
this.heatmapTile = heatmapTile;
}AMapOptionsBuilder这个类是实现了一个接口,上面这个setHeatmapTile是在接口中定义的,所以这里有个@Override。接口定义的更改这里就不再描述了。
继续深入到AMapOptionsBuilder的build方法中,工厂类是由这个方法创建的视图,在build方法中设置地图的属性,绘制点、线等。绘制热力图当然也在这个方法中,在return之前加入
if (null != heatmapTile){
aMapPlatformView.getHeatmapController().add(heatmapTile);
}当然在AMapPlatformView中加入
private HeatmapController heatmapController;
public HeatmapController getHeatmapController() {
return heatmapController;
}绘制热力图的方法就在这个HeatmapController类中,
public class HeatmapController implements MyMethodCallHandler {
private final AMap amap;
private static final String CLASS_NAME = "HeatmapController";
public HeatmapController(AMap amap) {
this.amap = amap;
}
public void add(Object mapTileObj){
if (mapTileObj != null && amap != null){
HeatmapOptionsBuilder builder = new HeatmapOptionsBuilder();
HeatmapUtil.parseHeatmapOptions(mapTileObj,builder);
if (builder.getPointCount() > 0) {
HeatmapTileProvider provider = builder.build();
TileOverlayOptions tileOverlayOptions = new TileOverlayOptions();
tileOverlayOptions.tileProvider(provider);
amap.addTileOverlay(tileOverlayOptions);
}
}
}
@Override
public String[] getRegisterMethodIdArray() {
return Const.METHOD_ID_LIST_FOR_HEATMAP;
}
@Override
public void doMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
LogUtil.i(CLASS_NAME, "doMethodCall===>" + call.method);
switch (call.method) {
case Const.METHOD_HEATMAP_UPDATE:
Object tileData = call.argument("heatmapTile");
amap.clear();
add(tileData);
result.success(null);
break;
}
}
}View Code它的构造函数有个参数,是个地图对象,在前面AMapPlatformView的构造函数中加入下面一句,
heatmapController = new HeatmapController(amap);在HeatmapController中,除了add方法外,其他二个方法是刷新地图时用的。Const.METHOD_ID_LIST_FOR_HEATMAP中现在只有一个值,就是在doMethodCall中的 Const.METHOD_HEATMAP_UPDATE,字面值是“heatmap#update”,下面iOS及Flutter部分也会出现这个值,大家保持一致即可。然后在 AMapPlatformView的 initMyMethodCallHandlerMap中加入
methodIdArray = heatmapController.getRegisterMethodIdArray();
if (null != methodIdArray && methodIdArray.length > 0) {
for (String methodId : methodIdArray) {
myMethodCallHandlerMap.put(methodId, heatmapController);
}
}把方法名称跟执行对象的关系存起来。在AMapPlatformView的onMethodCall方法中,根据key调用相应对象的doMethodCall。
上面的HeatmapController类中还有二个辅助类,是用外部参数来构造HeatmapTileProvider用的,
public class HeatmapOptionsBuilder {
HeatmapTileProvider.Builder builder;
private int _pointCount;
public HeatmapOptionsBuilder() {
this.builder = new HeatmapTileProvider.Builder();
}
public HeatmapTileProvider build(){
return builder.build();
}
public void setData(List<LatLng> data){
builder = builder.data(data);
_pointCount = data.size();
}
public int getPointCount(){
return _pointCount;
}
public void setGradient(Gradient gradient){
builder = builder.gradient(gradient);
}
public void setRadius(int radius){
builder = builder.radius(radius);
}
public void setTransparency(double transparency){
builder = builder.transparency(transparency);
}
public void setWeightedData(List<WeightedLatLng> data){
builder = builder.weightedData(data);
}
}View Codepublic class HeatmapUtil {
public static void parseHeatmapOptions(Object o,HeatmapOptionsBuilder builder){
if (null == o) {
return;
}
final Map<?, ?> data = ConvertUtil.toMap(o);
Object radius = data.get("radius");
if (radius != null){
builder.setRadius(ConvertUtil.toInt(radius));
}
Object transparency = data.get("transparency");
if (transparency != null){
builder.setTransparency(ConvertUtil.toDouble(transparency));
}
Object noWeightData = data.get("data");
if (noWeightData != null){
builder.setData(ConvertUtil.toPoints(noWeightData));
}
}
}View Code注意一下对应关系就好。
[*]iOS部分 这部分从下往上叙述。
首先定义结构,由传入的参数构造MAHeatMapTileOverlay对象
@interface AMapHeatmap : NSObject {
/// 热力图的坐标点数组,key为@"points"
CLLocationCoordinate2D *_coords;//坐标的数组指针
NSUInteger _coordCount;//坐标的个数
}
///透明度
@property (nonatomic, assign) double transparency;
///半径
@property (nonatomic, assign) int radius;
/// 由以上数据生成的heatMapTileOverlay 对象
@property (nonatomic, strong,readonly) MAHeatMapTileOverlay *heatMapTileOverlay;
@endAMapHeatmap.h@interface AMapHeatmap ()
@property (nonatomic, strong, readwrite) MAHeatMapTileOverlay *heatMapTileOverlay;
@end
@implementation AMapHeatmap
- (instancetype)init {
self = ;
self.radius = 12;
self.transparency = 0.6;
return self;
}
- (void)postHookWith:(NSDictionary *)dict {
NSArray *points = dict[@"data"];
//如果经纬度点已经有值,需要手动释放内存
if (_coords != NULL) {
free(_coords);
_coords = NULL;
}
_coordCount = points.count;
if (_coordCount > 0){
_coords = (CLLocationCoordinate2D*)malloc(_coordCount * sizeof(CLLocationCoordinate2D));
for (NSUInteger index = 0; index < _coordCount; index ++) {
NSArray *point = points;
_coords = ;
}
}
}
- (void)dealloc {
if (_coords != NULL) {
free(_coords);
_coords = NULL;
}
}
- (MAHeatMapTileOverlay *)heatMapTileOverlay {
if (_coordCount == 0) return nil;
if (_heatMapTileOverlay == nil) {
_heatMapTileOverlay = [ init];
NSMutableArray* data = ;
for (NSUInteger index = 0; index < _coordCount; index++) {
MAHeatMapNode *node = [ init];
node.coordinate = _coords;
node.intensity = 1;//设置权重
;
}
_heatMapTileOverlay.data = data;
_heatMapTileOverlay.radius = self.radius;
_heatMapTileOverlay.opacity = self.transparency;
// MAHeatMapGradient *gradient = [ initWithColor:@[,, ] andWithStartPoints:@[@(0.2),@(0.5),@(0.9)]];
// _heatMapTileOverlay.gradient = gradient;
}
return _heatMapTileOverlay;
}
@endAMapHeatmap.m然后实现一个Controller,把热力图的Overlay加入到map中
@class AMapHeatmap;
@interface AMapHeatmapController : NSObject
- (instancetype)init:(FlutterMethodChannel*)methodChannel
mapView:(MAMapView*)mapView;
- (void)addOverlay:(NSDictionary*)nodesToAdd;
@endAMapHeatmapController.h@interface AMapHeatmapController ()
@property (nonatomic,strong) FlutterMethodChannel *methodChannel;
@property (nonatomic,strong) MAMapView *mapView;
@end
@implementation AMapHeatmapController
- (instancetype)init:(FlutterMethodChannel*)methodChannel
mapView:(MAMapView*)mapView {
self = ;
if (self){
_methodChannel = methodChannel;
_mapView = mapView;
__weak typeof(self) weakSelf = self;
[_methodChannel addMethodName:@"heatmap#update" withHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult_Nonnull result) {
id heatmapToAdd = call.arguments[@"heatmapTile"];
if (]) {
;
;
}
result(nil);
}];
}
return self;
}
- (void)addOverlay:(NSDictionary*)nodesToAdd {
AMapHeatmap *heatmapModel = ];
if (heatmapModel.heatMapTileOverlay == nil) return;
;
}
@endAMapHeatmapController.m在Controller中,除了add,同样有涉及刷新的方法。
最后改造一下AMapViewController,先在@interface中加入
@property (nonatomic,strong) AMapHeatmapController *heatmapController;在initWithFrame方法中,调用addOverlay
_heatmapController = [ init:_channel
mapView: _mapView];
id heatmapToAdd = args[@"heatmapTile"];
if (]){
;
}在rendererForOverlay方法加一个分支
else if (]) {
MATileOverlayRenderer *tileOverlayRenderer = [ initWithTileOverlay:overlay];
return tileOverlayRenderer;
}
[*]Flutter部分 先定义widget中用来传递热力图数据的结构,有三个字段,分别表示数据点的数组、半径、透明度。主要有一个把类对象转化为map的方法
class HeatmapTile extends BaseOverlay {
final List<LatLng> data;
final int? radius;
final double? transparency;
HeatmapTile({required this.data, this.radius, this.transparency});
@override
Map<String, dynamic> toMap() {
final Map<String, dynamic> json = <String, dynamic>{};
void addIfPresent(String fieldName, dynamic value) {
if (value != null) {
json = value;
}
}
json['data'] = _pointsToJson();
addIfPresent('radius', radius);
addIfPresent('transparency', transparency);
return json;
}
dynamic _pointsToJson() {
final List<dynamic> result = <dynamic>[];
for (final LatLng point in data) {
result.add(point.toJson());
}
return result;
}
}在AMapWidget这个高德地图的widget中加入
///热力图
final HeatmapTile? heatmapTile;更新一下构造函数。使用时候把热力图数据给widget的这个heatmapTile参数就行。
如果只是这样,当组件刷新的时候,热力图就没了,所以还需要在_MapState的didUpdateWidget方法中添加
_updateHeatmap();_updateHeatmap方法如下
void _updateHeatmap() async {
final AMapController controller = await _controller.future;
var tile = widget.heatmapTile;
if (tile != null){
controller._updateHeatmap(tile);
}
}当然还要在AMapController中添加
Future<void> _updateHeatmap(HeatmapTile tile){
return _methodChannel.updateHeatmap(tile, mapId: mapId);
}通过methodChannel调用原生的方法,在MethodChannelAMapFlutterMap中添加更新的方法
Future<void> updateHeatmap(
HeatmapTile tile, {
required int mapId,
}) {
return channel(mapId).invokeMethod<void>(
'heatmap#update',
{ 'heatmapTile': tile.toMap() },
);
}
这样就完成了高德地图Flutter插件的改造,
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]