找回密码
 立即注册
首页 业界区 安全 高德地图热力图的Flutter实现

高德地图热力图的Flutter实现

幌斛者 昨天 13:08
高德地图SDK是有绘制热力图的功能的,但它的Flutter插件并没有封装这一块功能。我们业务中需要这个,就基于原插件修改了一下。

  • Android部分从AMapFlutterMapPlugin这个类开始,发现它是通过AMapPlatformViewFactory这个工厂类来创建View的。
    进入AMapPlatformViewFactory的create方法,里面有很多形如
    1. if (params.containsKey("xxx")) {
    2.                 builder.setInitialMarkers(params.get("yyy"));
    3. }
    复制代码
    的代码,这是提取从Flutter侧传过来的参数。我们加入一句
    1. if (params.containsKey("heatmapTile")){
    2.     builder.setHeatmapTile(params.get("heatmapTile"));
    3. }
    复制代码
    heatmapTile是Flutter传过来的参数名,下面Flutter部分会提到。再到AMapOptionsBuilder中添加字段与相应的set方法
    1. private Object heatmapTile;
    2. @Override
    3. public void setHeatmapTile(Object heatmapTile) {
    4.     this.heatmapTile = heatmapTile;
    5. }
    复制代码
    AMapOptionsBuilder这个类是实现了一个接口,上面这个setHeatmapTile是在接口中定义的,所以这里有个@Override。接口定义的更改这里就不再描述了。
    继续深入到AMapOptionsBuilder的build方法中,工厂类是由这个方法创建的视图,在build方法中设置地图的属性,绘制点、线等。绘制热力图当然也在这个方法中,在return之前加入
    1. if (null != heatmapTile){
    2.     aMapPlatformView.getHeatmapController().add(heatmapTile);
    3. }
    复制代码
    当然在AMapPlatformView中加入
    1. private HeatmapController heatmapController;
    2. public HeatmapController getHeatmapController() {
    3.     return heatmapController;
    4. }
    复制代码
    绘制热力图的方法就在这个HeatmapController类中,
    1.gif
    2.gif
    1. public class HeatmapController implements MyMethodCallHandler {
    2.     private final AMap amap;
    3.     private static final String CLASS_NAME = "HeatmapController";
    4.     public HeatmapController(AMap amap) {
    5.         this.amap = amap;
    6.     }
    7.     public void add(Object mapTileObj){
    8.         if (mapTileObj != null && amap != null){
    9.             HeatmapOptionsBuilder builder = new HeatmapOptionsBuilder();
    10.             HeatmapUtil.parseHeatmapOptions(mapTileObj,builder);
    11.             if (builder.getPointCount() > 0) {
    12.                 HeatmapTileProvider provider = builder.build();
    13.                 TileOverlayOptions tileOverlayOptions = new TileOverlayOptions();
    14.                 tileOverlayOptions.tileProvider(provider);
    15.                 amap.addTileOverlay(tileOverlayOptions);
    16.             }
    17.         }
    18.     }
    19.     @Override
    20.     public String[] getRegisterMethodIdArray() {
    21.         return Const.METHOD_ID_LIST_FOR_HEATMAP;
    22.     }
    23.     @Override
    24.     public void doMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
    25.         LogUtil.i(CLASS_NAME, "doMethodCall===>" + call.method);
    26.         switch (call.method) {
    27.             case Const.METHOD_HEATMAP_UPDATE:
    28.                 Object tileData = call.argument("heatmapTile");
    29.                 amap.clear();
    30.                 add(tileData);
    31.                 result.success(null);
    32.                 break;
    33.         }
    34.     }
    35. }
    复制代码
    View Code它的构造函数有个参数,是个地图对象,在前面AMapPlatformView的构造函数中加入下面一句,
    1. heatmapController = new HeatmapController(amap);
    复制代码
    在HeatmapController中,除了add方法外,其他二个方法是刷新地图时用的。Const.METHOD_ID_LIST_FOR_HEATMAP中现在只有一个值,就是在doMethodCall中的 Const.METHOD_HEATMAP_UPDATE,字面值是“heatmap#update”,下面iOS及Flutter部分也会出现这个值,大家保持一致即可。然后在 AMapPlatformView的 initMyMethodCallHandlerMap中加入
    1. methodIdArray = heatmapController.getRegisterMethodIdArray();
    2. if (null != methodIdArray && methodIdArray.length > 0) {
    3.     for (String methodId : methodIdArray) {
    4.         myMethodCallHandlerMap.put(methodId, heatmapController);
    5.     }
    6. }
    复制代码
    把方法名称跟执行对象的关系存起来。在AMapPlatformView的onMethodCall方法中,根据key调用相应对象的doMethodCall。
    上面的HeatmapController类中还有二个辅助类,是用外部参数来构造HeatmapTileProvider用的,
    3.gif
    4.gif
    1. public class HeatmapOptionsBuilder {
    2.    HeatmapTileProvider.Builder builder;
    3.    private int _pointCount;
    4.     public HeatmapOptionsBuilder() {
    5.         this.builder = new HeatmapTileProvider.Builder();
    6.     }
    7.     public HeatmapTileProvider build(){
    8.         return builder.build();
    9.     }
    10.     public void setData(List<LatLng> data){
    11.         builder = builder.data(data);
    12.         _pointCount = data.size();
    13.     }
    14.     public int getPointCount(){
    15.         return _pointCount;
    16.     }
    17.     public void setGradient(Gradient gradient){
    18.         builder = builder.gradient(gradient);
    19.     }
    20.     public void setRadius(int radius){
    21.         builder = builder.radius(radius);
    22.     }
    23.     public void setTransparency(double transparency){
    24.         builder = builder.transparency(transparency);
    25.     }
    26.     public void setWeightedData(List<WeightedLatLng> data){
    27.         builder = builder.weightedData(data);
    28.     }
    29. }
    复制代码
    View Code
    5.gif
    6.gif
    1. public class HeatmapUtil {
    2.     public static void parseHeatmapOptions(Object o,HeatmapOptionsBuilder builder){
    3.         if (null == o) {
    4.             return;
    5.         }
    6.         final Map<?, ?> data = ConvertUtil.toMap(o);
    7.         Object radius = data.get("radius");
    8.         if (radius != null){
    9.             builder.setRadius(ConvertUtil.toInt(radius));
    10.         }
    11.         Object transparency = data.get("transparency");
    12.         if (transparency != null){
    13.             builder.setTransparency(ConvertUtil.toDouble(transparency));
    14.         }
    15.         Object noWeightData = data.get("data");
    16.         if (noWeightData != null){
    17.             builder.setData(ConvertUtil.toPoints(noWeightData));
    18.         }
    19.     }
    20. }
    复制代码
    View Code注意一下对应关系就好。
     
  • iOS部分 这部分从下往上叙述。
     首先定义结构,由传入的参数构造MAHeatMapTileOverlay对象
    7.gif
    8.gif
    1. @interface AMapHeatmap : NSObject {
    2.     /// 热力图的坐标点数组,key为@"points"
    3.     CLLocationCoordinate2D *_coords;//坐标的数组指针
    4.     NSUInteger _coordCount;//坐标的个数
    5. }
    6. ///透明度
    7. @property (nonatomic, assign) double transparency;
    8. ///半径
    9. @property (nonatomic, assign) int radius;
    10. /// 由以上数据生成的heatMapTileOverlay 对象
    11. @property (nonatomic, strong,readonly) MAHeatMapTileOverlay *heatMapTileOverlay;
    12. @end
    复制代码
    AMapHeatmap.h
    9.gif
    10.gif
    1. @interface AMapHeatmap ()
    2. @property (nonatomic, strong, readwrite) MAHeatMapTileOverlay *heatMapTileOverlay;
    3. @end
    4. @implementation AMapHeatmap
    5. - (instancetype)init {
    6.     self = [super init];
    7.     self.radius = 12;
    8.     self.transparency = 0.6;
    9.     return self;
    10. }
    11. - (void)postHookWith:(NSDictionary *)dict {
    12.     NSArray *points = dict[@"data"];
    13.     //如果经纬度点已经有值,需要手动释放内存
    14.     if (_coords != NULL) {
    15.         free(_coords);
    16.         _coords = NULL;
    17.     }
    18.     _coordCount = points.count;
    19.     if (_coordCount > 0){
    20.         _coords = (CLLocationCoordinate2D*)malloc(_coordCount * sizeof(CLLocationCoordinate2D));
    21.         for (NSUInteger index = 0; index < _coordCount; index ++) {
    22.             NSArray *point = points[index];
    23.             _coords[index] = [AMapConvertUtil coordinateFromArray:point];
    24.         }
    25.     }
    26. }
    27. - (void)dealloc {
    28.     if (_coords != NULL) {
    29.         free(_coords);
    30.         _coords = NULL;
    31.     }
    32. }
    33. - (MAHeatMapTileOverlay *)heatMapTileOverlay {
    34.     if (_coordCount == 0) return nil;
    35.    
    36.     if (_heatMapTileOverlay == nil) {
    37.         _heatMapTileOverlay = [[MAHeatMapTileOverlay alloc] init];
    38.         NSMutableArray* data = [NSMutableArray array];
    39.         for (NSUInteger index = 0; index < _coordCount; index++) {
    40.             MAHeatMapNode *node = [[MAHeatMapNode alloc] init];
    41.             node.coordinate = _coords[index];        
    42.             node.intensity = 1;//设置权重
    43.             [data addObject:node];
    44.         }
    45.         _heatMapTileOverlay.data = data;
    46.         _heatMapTileOverlay.radius = self.radius;
    47.         _heatMapTileOverlay.opacity = self.transparency;
    48.         // MAHeatMapGradient *gradient = [[MAHeatMapGradient alloc] initWithColor:@[[UIColor blueColor],[UIColor greenColor], [UIColor redColor]] andWithStartPoints:@[@(0.2),@(0.5),@(0.9)]];
    49.         // _heatMapTileOverlay.gradient = gradient;
    50.     }
    51.     return _heatMapTileOverlay;
    52. }
    53. @end
    复制代码
    AMapHeatmap.m然后实现一个Controller,把热力图的Overlay加入到map中
    11.gif
    12.gif
    1. @class AMapHeatmap;
    2. @interface AMapHeatmapController : NSObject
    3. - (instancetype)init:(FlutterMethodChannel*)methodChannel
    4.             mapView:(MAMapView*)mapView;
    5. - (void)addOverlay:(NSDictionary*)nodesToAdd;
    6. @end
    复制代码
    AMapHeatmapController.h
    13.gif
    14.gif
    1. @interface AMapHeatmapController ()
    2. @property (nonatomic,strong) FlutterMethodChannel *methodChannel;
    3. @property (nonatomic,strong) MAMapView *mapView;
    4. @end
    5. @implementation AMapHeatmapController
    6. - (instancetype)init:(FlutterMethodChannel*)methodChannel
    7.              mapView:(MAMapView*)mapView {
    8.     self = [super init];
    9.     if (self){
    10.         _methodChannel = methodChannel;
    11.         _mapView = mapView;
    12.         __weak typeof(self) weakSelf = self;
    13.         [_methodChannel addMethodName:@"heatmap#update" withHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
    14.             id heatmapToAdd = call.arguments[@"heatmapTile"];
    15.             if ([heatmapToAdd isKindOfClass:[NSDictionary class]]) {
    16.                 [weakSelf.mapView removeOverlays:weakSelf.mapView.overlays];
    17.                 [weakSelf addOverlay:heatmapToAdd];
    18.             }
    19.             result(nil);
    20.         }];
    21.     }
    22.     return self;
    23. }
    24. - (void)addOverlay:(NSDictionary*)nodesToAdd {   
    25.     AMapHeatmap *heatmapModel = [AMapJsonUtils modelFromDict:nodesToAdd modelClass:[AMapHeatmap class]];
    26.     if (heatmapModel.heatMapTileOverlay == nil) return;
    27.    
    28.     [self.mapView addOverlay:heatmapModel.heatMapTileOverlay];
    29.    
    30. }
    31. @end
    复制代码
    AMapHeatmapController.m在Controller中,除了add,同样有涉及刷新的方法。
    最后改造一下AMapViewController,先在@interface中加入
    1. @property (nonatomic,strong) AMapHeatmapController *heatmapController;
    复制代码
    在initWithFrame方法中,调用addOverlay
    1. _heatmapController = [[AMapHeatmapController alloc] init:_channel
    2.                                                             mapView: _mapView];
    3. id heatmapToAdd = args[@"heatmapTile"];
    4.         if ([heatmapToAdd isKindOfClass:[NSDictionary class]]){
    5.             [_heatmapController addOverlay:heatmapToAdd];
    6.         }
    复制代码
    在rendererForOverlay方法加一个分支
    1. else if ([overlay isKindOfClass:[MATileOverlay class]]) {
    2.     MATileOverlayRenderer *tileOverlayRenderer = [[MATileOverlayRenderer alloc] initWithTileOverlay:overlay];
    3.     return tileOverlayRenderer;
    4. }
    复制代码
     
  • Flutter部分 先定义widget中用来传递热力图数据的结构,有三个字段,分别表示数据点的数组、半径、透明度。主要有一个把类对象转化为map的方法
    1. class HeatmapTile extends BaseOverlay {
    2.   final List<LatLng> data;
    3.   final int? radius;
    4.   final double? transparency;
    5.   HeatmapTile({required this.data, this.radius, this.transparency});
    6.   @override
    7.   Map<String, dynamic> toMap() {
    8.     final Map<String, dynamic> json = <String, dynamic>{};
    9.     void addIfPresent(String fieldName, dynamic value) {
    10.       if (value != null) {
    11.         json[fieldName] = value;
    12.       }
    13.     }
    14.     json['data'] = _pointsToJson();
    15.     addIfPresent('radius', radius);
    16.     addIfPresent('transparency', transparency);
    17.     return json;
    18.   }
    19.   dynamic _pointsToJson() {
    20.     final List<dynamic> result = <dynamic>[];
    21.     for (final LatLng point in data) {
    22.       result.add(point.toJson());
    23.     }
    24.     return result;
    25.   }
    26. }
    复制代码
    在AMapWidget这个高德地图的widget中加入
    1. ///热力图
    2. final HeatmapTile? heatmapTile;
    复制代码
    更新一下构造函数。使用时候把热力图数据给widget的这个heatmapTile参数就行。
    如果只是这样,当组件刷新的时候,热力图就没了,所以还需要在_MapState的didUpdateWidget方法中添加
    1. _updateHeatmap();
    复制代码
    _updateHeatmap方法如下
    1.   void _updateHeatmap() async {
    2.     final AMapController controller = await _controller.future;
    3.     var tile = widget.heatmapTile;
    4.     if (tile != null){
    5.       controller._updateHeatmap(tile);
    6.     }
    7.   }
    复制代码
    当然还要在AMapController中添加
    1.   Future<void> _updateHeatmap(HeatmapTile tile){
    2.     return _methodChannel.updateHeatmap(tile, mapId: mapId);
    3.   }
    复制代码
    通过methodChannel调用原生的方法,在MethodChannelAMapFlutterMap中添加更新的方法
    1.   Future<void> updateHeatmap(
    2.       HeatmapTile tile, {
    3.         required int mapId,
    4.       }) {
    5.     return channel(mapId).invokeMethod<void>(
    6.       'heatmap#update',
    7.       { 'heatmapTile': tile.toMap() },
    8.     );
    9.   }
    复制代码
     
     这样就完成了高德地图Flutter插件的改造,
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册