Realization of aggregation function based on Arcgis for iOS 10.2.5

Put the code address first: https://github.com/xjf1990930/ArcGis-iOS-Cluster

Before the aggregation is implemented, the data display interface looks like this:

Before aggregation
Pre-polymerization effect

The accumulation of such a large amount of data is firstly unsightly, and secondly, it consumes display performance, so the requirement for aggregated display is put forward. After thinking, the general idea is this: first divide the screen into N equal parts, one of which corresponds to The map range in the current screen is used as the smallest aggregation range unit, and then the spatial query is performed based on this aggregation range unit. The number of query results indicates how many aggregation numbers are displayed, and can be marked on the AGSGraphicLayer with AGSSymbol.

Just write as you say, so I rolled out the following four categories, and the judges can use them immediately, with very little code intrusion:

WHGIClusterManager: Responsible for associating AGSMapView map objects and creating aggregation unit objects

WHGIClusterModel: aggregation unit object, responsible for spatial query tasks in this unit

WHGIClusterTask: Each query task (different layer ids need to perform independent AGSQueryTask)

WHGIBlockTask: a queue management class

WHGIClusterManager key code:

//聚合时监听地图缩放和拖拽变化
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mapViewDidEndZoomAction:) name:AGSMapViewDidEndZoomingNotification object:nil];//缩放触发重新聚合查询
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mapViewDidEndDragAction:) name:AGSMapViewDidEndPanningNotification object:nil];//拖拽触发继续聚合查询


//获取当前屏幕范围内的地图需要聚合查询的所有最小单元
- (NSMutableArray *)currentScreenClusterEnvlopes {
    
    NSUInteger cluster_X = 3;//取当前屏幕三分之一宽为聚合最小单元的宽
    NSUInteger cluster_Y = 4;//取当前屏幕四分之一高为聚合最小单元的高
    
    AGSEnvelope *clipTemp = [self.mapView toMapEnvelope:CGRectMake(0, 0, self.mapView.frame.size.width/cluster_X, self.mapView.frame.size.height/cluster_Y)];
    
    AGSEnvelope *currentScreenEnv = [self.mapView toMapEnvelope:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
    
    AGSPoint *startPoint = [AGSPoint pointWithX:self.mapView.maxEnvelope.xmin y:self.mapView.maxEnvelope.ymin spatialReference:self.mapView.spatialReference];
    //参照当前地图最大范围的最小x和y坐标作为原点,计算出当前屏幕地图范围所有的聚合单元
    NSUInteger env_minX = floor((currentScreenEnv.xmin - startPoint.x)/clipTemp.width);
    NSUInteger env_minY = floor((currentScreenEnv.ymin - startPoint.y)/clipTemp.height);
    
    NSMutableArray *allEnvs = [NSMutableArray new];
    AGSEnvelope *tempEnv = nil;
    for (NSUInteger i = 0; i<=cluster_X; i++) {
        for (NSUInteger j = 0; j<=cluster_Y; j++) {
            tempEnv = [[AGSEnvelope alloc] initWithXmin:startPoint.x+(i+env_minX)*clipTemp.width ymin:startPoint.y+(j+env_minY)*clipTemp.height xmax:startPoint.x+(i+1+env_minX)*clipTemp.width ymax:startPoint.y+(j+1+env_minY)*clipTemp.height spatialReference:self.mapView.spatialReference];
            BOOL contains = NO;
            for (WHGIClusterModel *clustModel in self.clusterModels) {
                if ([clustModel.referEnv isEqualToEnvelope:tempEnv]) {
                    contains = YES;
                    break;
                }
            }
            if (!contains) {
                [allEnvs addObject:tempEnv];
            }
        }
    }
    
    return allEnvs;
}

WHGIClusterModel key code:

//展示查询的结果,最小聚合单元内大于3个结果,显示聚合数字符号,否则显示原本的符号
- (void)displayClust {
    
    if (self.clusterLayer == nil) {
        return;
    }
    
    NSMutableArray *tempGraphics = [NSMutableArray new];
    
    for (WHGIClusterTask *taskModel in self.tasks) {
        if (!taskModel.marked && taskModel.clustSuccess) {//已经在底图上显示过了就不会再处理一次
            [tempGraphics addObjectsFromArray:taskModel.allResults];
            taskModel.marked = YES;
        }
    }
    
    if (tempGraphics.count<=0) {
        return;
    }
    
    NSArray<AGSGraphic *>* graphics = tempGraphics;
    NSMutableArray<AGSGraphic *>* showGraphics = [NSMutableArray new];
    if (graphics.count>0) {
        if (graphics.count>3) {
            [showGraphics addObject:[AGSGraphic graphicWithGeometry:graphics.firstObject.geometry symbol:[self textSymbol:[NSString stringWithFormat:@"%@",@(graphics.count)]] attributes:nil]];
        }else{
            //小于三个,则每个graphic单独显示
            for (AGSGraphic *singleShowGraphic in graphics) {
                
                for (WHGIClusterTask *taskModel in self.tasks) {
                    if ([taskModel.allResults containsObject:singleShowGraphic]) {
                        [showGraphics addObject:[AGSGraphic graphicWithGeometry:singleShowGraphic.geometry symbol:[self pictureSymbolWithLayerID:taskModel.layerID] attributes:nil]];
                        break;
                    }
                }
            }
        }
        [self.clusterLayer addGraphics:showGraphics];
    }
}

WHGIClusterTask key code:

//根据最小聚合单元的范围进行空间查询
- (void)clustWithEnvlope:(AGSEnvelope *)env andCall:(void(^)(BOOL success,NSArray<AGSGraphic *> *allResults))callBack {
    if (nil == self.currentOperation || self.currentOperation.finished) {
        if (self.clustSuccess == NO) {
            self.resultCall = callBack;
            self.queryTask = [[AGSQueryTask alloc] initWithURL:[NSURL URLWithString:[[NSString stringWithFormat:@"%@/%@",pWHGIClustLayerService,self.layerID] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
            AGSQuery *queryObject = [[AGSQuery alloc] init];
            queryObject.geometry = env;
            queryObject.whereClause = @"1=1";
            queryObject.outFields = @[@"*"];
            queryObject.returnGeometry = YES;
            queryObject.outSpatialReference = env.spatialReference;
            queryObject.spatialRelationship = AGSSpatialRelationshipContains;
            self.queryTask.delegate = self;
            self.currentOperation = [self.queryTask executeWithQuery:queryObject];
        }else{
            if (callBack) {
                callBack(YES,self.allResults);
            }
        }
    }else{
        if (callBack) {
            callBack(NO,nil);
        }
    }
}

WHGIBlockTask is a tool class that can queue and execute multiple asynchronous block callback tasks.

There are also some detailed optimizations, such as the need to end the last unfinished task when the user zooms in faster, etc., the details are all in the code.

The final effect is as follows:

after polymerization
after polymerization effect

 

Guess you like

Origin blog.csdn.net/qq_31672459/article/details/102947417