给你的地图点灯

作者: ccSundayChina | 来源:发表于2017-06-10 21:35 被阅读1215次

    曾经坐在飞机上俯瞰整个北京城,在万米高空上可以看到北京的全貌,即使是凌晨一两点,北京依然灯火通明,俨然是一座不眠之城。四通八达的交通线仿佛是这座城市的血脉,里面流淌着的,是生活在这里的人们的梦想。

    然而,今天要讲的不是北京,也不是梦想。今天要讲的是我们该如何在地图上点起一盏灯,就像夜幕下的北京城一样。

    为了方便理解,我们先看一下最终的实现效果。


    最终效果.png

    没错,就是要在夜幕下,放几盏灯,告诉别人这块区域目前有人正在里面工作。

    在讲解如何实现这种效果前,先分享一下之前的那种存在问题的思路

    之前的做法是:
    • 位于底部的阴影区域当做一个大的PolygonView,并且充满屏幕。
    • 位于上面的高亮区域当做另外的PolygonView,依次添加,并设置它们的背景色与透明度,模拟高亮。

    然而这样做会产生一个无法避免的问题,那就是无论我们怎么设置高亮区域的背景色与透明度,都不够好看:透明度低的时候,就看不到下面的地图,透明度高的时候,虽然能看到下面的地图,但是却雾蒙蒙的,仿佛那片高亮区域,被雾霾笼罩了。。

    被雾霾笼罩的效果图.png

    那么问题来了,如何让大晚上还在工作的人们,少呼吸点雾霾呢。

    思路其实很简单,只需要换个角度来思考就可以了,那就是我们不再分别绘制高亮区域和底部的全屏阴影区域,而是只需要绘制一个阴影区域就可以了,这样剩下的没有绘制的区域自然就是没有雾霾的。
    对,再说一遍,只需要绘制一个阴影区域就可以。
    先来一张图:

    绘制过程分解.png

    屏幕的四个点分别是S1、S2、S3、S4,两个高亮区域分别是区域A区域B,区域A的四个点分别是A1、A2、A3、A4,区域B的四个点分别是B1、B2、B3、B4。我们要绘制的就是除去区域A,区域B后,把剩下的那部分当做一个PolygonView来绘制。(注意:这里所说的点是指经纬度点,而不是point点)

    So ,如何得到阴影区域的Polygon

    • 首先声明一个经纬度数组CLLocationCoordinate2D * coors,用来存放区域坐标点。
    • 先从S1点开始,将其加入到coors中,下一个点是A1点,然后依次添加A2、A3、A4
    • 添加完A4后,** 一定要再将A1点加一遍**,以构成一个闭环,而不是直接去添加区域B的点,此时A1点被添加了两遍。
    • 接着添加区域B的点,先是B1点,然后依次添加B2、B3、B4
    • 添加完B4后,像区域A一样,一定要再添加一遍B1点,以构成闭环。此时B1点被添加两遍。
    • 由于此时只有两个区域,所以添加完B1点后,我们需要返回,这里需要注意的一定要原路返回。所以需要再添加一遍A1点,此时A1点被添加了3遍(注意:非屏幕边界点最多被添加三遍)。
    • 接着我们需要再次添加S1点,也就是说从S1点,出去转了一圈后,最后必须回到S1点。此时S1点被添加2遍。(注意:屏幕边界点最多也是被添加3遍)。
    • 最后再依次添加S2、S3、S4点构成闭环就可以了。

    总结一下规律,我们需要从屏幕四个角的任意一个点开始,假设是S1点,然后依次添加区域A、区域B、区域C等区域的点,区域点添加完一圈后,一定要把区域的第一个点再添加一遍,然后再去添加另一个区域的点,添加完后,依然要再加一遍该区域的第一个点,如果当前区域是最后一个区域了,那么添加完后,需要让它原路返回,也就是说除了最后一个区域的起点是填加两遍其他区域的起点都是添加三遍,添加完后,再添加一遍屏幕的S1点,最后就是按照顺时针或者逆时针添加屏幕的剩余三个点,添加完三个点后,必须再添加S1点,构成闭合(此时点被添加三遍)。

    这样我们就只需要绘制这么一个整合后的阴影区域就可以了。由此就实现了我们的去雾霾目的。

    过程有点绕,可能不太好理解,那怎么办,多看几遍然后自己试一试就可以了。demo还是之前的给你的地图模块动手术的demo。代码比较多,但是却很简单,所以文章里就不写代码了,本文介绍的关键在于这种思路,使用的时候,只需要借鉴思路,并且这种思路是通用的,移动端与web端都适用。

    为了方便大伙,下面给出对数据的程序处理。

        /*从self.locationArray中获取数据进行展示,
          里面存储的是一个个模型数组,模型是TestPolygonModel,有lng与lat属性
        */
       
        /*1、获取需要malloc的内存区域,由以下几个部分组成:
         •  6个屏幕点
         •  self.locationArray.count*2 - 1 计算多边形起点被多加的次数,
            后面的减一是因为最后一个数组的第一个点只需要多加一遍,其他的都是多加两遍,
            注意这里计算的是起点被多加的次数,而不是起点被添加的总次数。
         •  self.locationArray子数组中元素的个数和,self.locationArray存放的是经纬度模型数组。
         */
        NSInteger mallocCount = 0;//需要分配的内存大小,也就是经纬度数组中总共的点数,也就是上面提到的三部分之和
        NSInteger itemCount = 0;  //也就是self.locationArray子数组中经纬度模型的个数和
        NSInteger itemIndex = 0;  //coors下表
        for (int i = 0; i<self.locationArray.count; i++) {
            itemCount = itemCount + [self.locationArray[i] count];
        }
        mallocCount = 6 + self.locationArray.count * 2 - 1 + itemCount;
        /*2、正序计算,无论是不是最后一个数组,先依次将该数组的点加进去,加完后再加一遍该数组中的第一个点。*/
        CLLocationCoordinate2D * coors = (CLLocationCoordinate2D *)malloc(mallocCount * sizeof(CLLocationCoordinate2D));
        /*添加屏幕左下角的S1点*/
        coors[0].longitude = self.mapView.region.center.longitude-self.mapView.region.span.longitudeDelta*1.5;
        coors[0].latitude = self.mapView.region.center.latitude-self.mapView.region.span.latitudeDelta*1.5;
        /*正序添加每一个数组中的所有元素*/
        for (int i = 0; i < self.locationArray.count; i++) {
            NSArray *polygonArray = self.locationArray[i];
            for (int j = 0; j<polygonArray.count; j++) {
                TestPolygonModel *polygonModel = self.locationArray[i][j];                      //获取model
                itemIndex++;
                coors[itemIndex].longitude = [polygonModel.lng floatValue];
                coors[itemIndex].latitude = [polygonModel.lat floatValue];
            }
            //添加完每一组的元素后,需要再添加一遍该组的第一个点,
            itemIndex ++;
            TestPolygonModel *startPolygon = polygonArray[0];
            coors[itemIndex].latitude = [startPolygon.lat floatValue];
            coors[itemIndex].longitude = [startPolygon.lng floatValue];
        }
        
        /*3、倒叙添加每一组的第一个元素,但是最后一个组不需要添加,因为最后一个组总共只需要加两遍,所以过滤掉最后一组*/
        for (NSInteger i = self.locationArray.count - 1; i>0; i--) {
            itemIndex ++;
            TestPolygonModel *startPolygon = self.locationArray[i-1][0];                      //获取model
            coors[itemIndex].latitude = [startPolygon.lat floatValue];
            coors[itemIndex].longitude = [startPolygon.lng floatValue];
        }
        
        /*4、添加屏幕的边界点*/
        /*再次添加屏幕的起点*/
        itemIndex ++;
        coors[itemIndex].longitude = self.mapView.region.center.longitude-self.mapView.region.span.longitudeDelta*1.5;
        coors[itemIndex].latitude = self.mapView.region.center.latitude-self.mapView.region.span.latitudeDelta*1.5;
        /*添加屏幕右下角的S2点*/
        itemIndex ++;
        coors[itemIndex].longitude = self.mapView.region.center.longitude+self.mapView.region.span.longitudeDelta*1.5;
        coors[itemIndex].latitude = self.mapView.region.center.latitude-self.mapView.region.span.latitudeDelta*1.5;
        /*添加屏幕右上角的S3点*/
        itemIndex ++;
        coors[itemIndex].longitude = self.mapView.region.center.longitude+self.mapView.region.span.longitudeDelta*1.5;
        coors[itemIndex].latitude = self.mapView.region.center.latitude+self.mapView.region.span.latitudeDelta*1.5;
        /*添加屏幕左上角的S4点*/
        itemIndex ++;
        coors[itemIndex].longitude = self.mapView.region.center.longitude-self.mapView.region.span.longitudeDelta*1.5;
        coors[itemIndex].latitude = self.mapView.region.center.latitude+self.mapView.region.span.latitudeDelta*1.5;
        /*再次添加屏幕左下角的S1点*/
        itemIndex ++;
        coors[itemIndex].longitude = self.mapView.region.center.longitude-self.mapView.region.span.longitudeDelta*1.5;
        coors[itemIndex].latitude = self.mapView.region.center.latitude-self.mapView.region.span.latitudeDelta*1.5;
        if (mallocCount == itemIndex+1) {
            BMKPolygon *polygonModel0 = [BMKPolygon polygonWithCoordinates:coors count:mallocCount];
            [self.mapView addOverlay:polygonModel0];
        }else{
            [SVProgressHUD showErrorWithStatus:@"计算错误"];
        }
    
    

    好吧,作者比较懒,三篇文章,只留下了一个demo

    相关文章

      网友评论

      • gis杭州:作者你好 ,偶然看到 。我做过一个类似于此的功能 ,可能比较起来会简单些 欢迎指正
        https://lxqjss.github.io/test/wkt.html
        gis杭州:@ccSundayChina geojson或者wkt格式都可以构造带内部洞的面 ,上面的例子里是openlayers3加载了wkt数据(一个全世界的面出现一个余杭区的洞);我有一篇写的是arcgis加载了geojson,也是构造一个带内部洞的(https://www.jianshu.com/p/1fb030d7e864);再或者arcgis api中可以直接画一个polygon,带内部洞
        ccSundayChina:体验很不错,能问下是咋做的吗
      • 91d311374b79:最近一直在做地图项目,有个点聚合的例子研究研究呗,参考下e充网 类似效果的有个四叉数的感觉没有这个基于原生地图的流畅
        ccSundayChina:@别太放肆 恩,看了他们的软件,一方面在数据返回方面服务器的响应速度比较快。然后从A级别到B级别,比如zoomlevel从12到14,地图不会立马定位到14,这之间会有一个固定的渐变时间,关于这点,我写的另一篇文章《地图阻尼运动》可以满足这个需求,只是他的变化有一个加速的效果,而我的demo中是匀速的,这一点,通过修改level-time函数就可以。最后就是他们无论哪个级别的标注视图,都设计的很简洁,并且在出现与消失的时候做了细微的渐隐渐现的处理,给人感觉流畅,关于标注视图这方面,我可以再继续研究一下,看如何提高他的绘制效率和优化它出现与消失时候的效果。
        91d311374b79:@ccSundayChina 恩就是那个app地图页的点聚合感觉很流畅
        ccSundayChina:你好,你是说在e充网的iOS app里有你说的那个效果么
      • nickName0:在万米之上,俯瞰北京,看的难道不是雾霾吗:joy:
        ccSundayChina:@Hall_of_fame 哈哈,晚上还好,北京灯火通明的
      • a5c3e31982c2:好难哦
        ccSundayChina:@_从心_ 多看几遍
      • 880edfd621bf:需要深厚的图形学功底,妈呀,算法怎么这么难
        ccSundayChina:@每依北斗望京华 还好,多看几遍,多试试
      • 燕云倾:啧啧,情怀党
        ccSundayChina:@燕云倾 :smile:
      • ccSundayChina:不只是哪位好心人,给我赞赏了十元,你出来,我有话要跟你谈。。😀
        ebay_Happy:谢谢大神分享

      本文标题:给你的地图点灯

      本文链接:https://www.haomeiwen.com/subject/oofcqxtx.html