高德地图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 Code
- public 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;
- @end
复制代码 AMapHeatmap.h
- @interface AMapHeatmap ()
- @property (nonatomic, strong, readwrite) MAHeatMapTileOverlay *heatMapTileOverlay;
- @end
- @implementation AMapHeatmap
- - (instancetype)init {
- self = [super init];
- 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[index];
- _coords[index] = [AMapConvertUtil coordinateFromArray:point];
- }
- }
- }
- - (void)dealloc {
- if (_coords != NULL) {
- free(_coords);
- _coords = NULL;
- }
- }
- - (MAHeatMapTileOverlay *)heatMapTileOverlay {
- if (_coordCount == 0) return nil;
-
- if (_heatMapTileOverlay == nil) {
- _heatMapTileOverlay = [[MAHeatMapTileOverlay alloc] init];
- NSMutableArray* data = [NSMutableArray array];
- for (NSUInteger index = 0; index < _coordCount; index++) {
- MAHeatMapNode *node = [[MAHeatMapNode alloc] init];
- node.coordinate = _coords[index];
- node.intensity = 1;//设置权重
- [data addObject:node];
- }
- _heatMapTileOverlay.data = data;
- _heatMapTileOverlay.radius = self.radius;
- _heatMapTileOverlay.opacity = self.transparency;
- // MAHeatMapGradient *gradient = [[MAHeatMapGradient alloc] initWithColor:@[[UIColor blueColor],[UIColor greenColor], [UIColor redColor]] andWithStartPoints:@[@(0.2),@(0.5),@(0.9)]];
- // _heatMapTileOverlay.gradient = gradient;
- }
- return _heatMapTileOverlay;
- }
- @end
复制代码 AMapHeatmap.m然后实现一个Controller,把热力图的Overlay加入到map中
- @class AMapHeatmap;
- @interface AMapHeatmapController : NSObject
- - (instancetype)init:(FlutterMethodChannel*)methodChannel
- mapView:(MAMapView*)mapView;
- - (void)addOverlay:(NSDictionary*)nodesToAdd;
- @end
复制代码 AMapHeatmapController.h
- @interface AMapHeatmapController ()
- @property (nonatomic,strong) FlutterMethodChannel *methodChannel;
- @property (nonatomic,strong) MAMapView *mapView;
- @end
- @implementation AMapHeatmapController
- - (instancetype)init:(FlutterMethodChannel*)methodChannel
- mapView:(MAMapView*)mapView {
- self = [super init];
- 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 ([heatmapToAdd isKindOfClass:[NSDictionary class]]) {
- [weakSelf.mapView removeOverlays:weakSelf.mapView.overlays];
- [weakSelf addOverlay:heatmapToAdd];
- }
- result(nil);
- }];
- }
- return self;
- }
- - (void)addOverlay:(NSDictionary*)nodesToAdd {
- AMapHeatmap *heatmapModel = [AMapJsonUtils modelFromDict:nodesToAdd modelClass:[AMapHeatmap class]];
- if (heatmapModel.heatMapTileOverlay == nil) return;
-
- [self.mapView addOverlay:heatmapModel.heatMapTileOverlay];
-
- }
- @end
复制代码 AMapHeatmapController.m在Controller中,除了add,同样有涉及刷新的方法。
最后改造一下AMapViewController,先在@interface中加入- @property (nonatomic,strong) AMapHeatmapController *heatmapController;
复制代码 在initWithFrame方法中,调用addOverlay- _heatmapController = [[AMapHeatmapController alloc] init:_channel
- mapView: _mapView];
- id heatmapToAdd = args[@"heatmapTile"];
- if ([heatmapToAdd isKindOfClass:[NSDictionary class]]){
- [_heatmapController addOverlay:heatmapToAdd];
- }
复制代码 在rendererForOverlay方法加一个分支- else if ([overlay isKindOfClass:[MATileOverlay class]]) {
- MATileOverlayRenderer *tileOverlayRenderer = [[MATileOverlayRenderer alloc] 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[fieldName] = 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方法如下- 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插件的改造,
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |