地图区域大数据量 marker 坐标点高效抽稀算法

按网上的思路一般要写双层循环,第一层循环遍历点集合,时间复杂度为O(N),第二层循环遍历结果集,逐一计算距离,距离小于阈值的不加入结果集,距离大于阈值的加入结果集,时间复杂度为O(M),双层循环总时间复杂度为O(N * M)。

新的算法思路:坐标点的经纬度经过计算得到的结果作为HashMap的Key,坐标相近的点的Key相同,利用HashMap降低时间复杂度,不需要第二层循环遍历,把时间复杂度由O(N * M)降为O(N)。

该算法的优点:1、抽稀后坐标点位置均匀,2、计算效率高(时间复杂度:O(N)),3、算法逻辑简单,4、计算结果幂等(结果集确定,多次重复计算结果集相同)。

 

代码:

//抽稀
if (mapZoom >= 15 && mapZoom <= 16) {
    currentMarkerMap = new HashMap();

    var getKey = function (lng, lat, len, ratio) { //如果计算结果数量较少,就调大ratio
        var a = (10000 + lng * ratio).toString().substr(0, len);
        var b = (10000 + lat * ratio).toString().substr(0, len)
        return a + "," + b;
    };

    for (var i = 0; i < forAdd.length; i++) {
        var marker = forAdd[i];

        var key;
        if (mapZoom == 15) key = getKey(marker.geometry.x, marker.geometry.y, 9, 1);
        if (mapZoom == 16) key = getKey(marker.geometry.x, marker.geometry.y, 10, 1);

        if (!currentMarkerMap.containsKey(key)) {
            currentMarkerMap.put(key, marker);
        }
    }
}
View Code

说明:代码中 forAdd 变量是抽稀前的坐标点集合,currentMarkerMap 变量是HashMap集合(HashMap是自己实现的JS类),定义代码如下:

var forClear = [];
var currentMarkerMap = new HashMap();
View Code

效果图:

缩小:

放大:

动态效果:

新算法:

说明:上面的代码用的是SuperMap iClient Classic 8C,下面用的是Leaflet

function refreshVillageMarkers(map, deviceLayer) {
    //获取地图层级
    let mapZoom = map.getZoom();

    //点位完全显示层级
    let visibleZoom = mapType == 1 ? 17 : 17; //1:使用SuperMap地图 2:使用高德地图

    //获取地图可视区域
    let polygon = getMapBounds(map);

    //获取小区点位集合
    let villageMarkerArr = getVillageMarkerArr();

    //筛选出可视区域内的点位
    let visibleMarkerArr = [];
    for (let marker of villageMarkerArr) {
        if (!marker.options.rnd) {
            marker.options.rnd = Math.random();
        }

        let latLng = marker.getLatLng();
        let point = turf.point([latLng.lng, latLng.lat]);

        if (turf.booleanPointInPolygon(point, polygon)) {
            visibleMarkerArr.push(marker);
        }
    }

    //可视区域内的点位抽稀
    let forAdd = [];
    let visibleCount = 300;
    let ratio = 1;
    if (visibleMarkerArr.length > visibleCount) {
        ratio = visibleCount / visibleMarkerArr.length;
    }
    for (let marker of visibleMarkerArr) {
        if (mapZoom >= visibleZoom || marker.options.rnd < ratio) {
            forAdd.push(marker);
        }
    }

    //移除可视区域外的和被抽稀掉的点位
    for (let marker of currentMarkerMap.values()) {
        let latLng = marker.getLatLng();
        let point = turf.point([latLng.lng, latLng.lat]);

        if (!turf.booleanPointInPolygon(point, polygon) || (mapZoom < visibleZoom && marker.options.rnd >= ratio)) {
            deviceLayer.removeLayer(marker);
            currentMarkerMap.delete(marker.options.id);
        }
    }

    //可视区域内的点位添加到图层上
    for (let marker of forAdd) {
        deviceLayer.addLayer(marker);
        if (!currentMarkerMap.has(marker.options.id)) {
            currentMarkerMap.set(marker.options.id, marker);
        }
    }

    //打印
    console.info('实际显示点位数/可视区点位数(' + (currentMarkerMap.size == visibleMarkerArr.length ? '相等' : '不相等') + '):' + currentMarkerMap.size + '/' + visibleMarkerArr.length)
}

//获取地图可视区域
function getMapBounds(map) {
    let nw = map.getBounds().getNorthWest();
    let ne = map.getBounds().getNorthEast();
    let se = map.getBounds().getSouthEast();
    let sw = map.getBounds().getSouthWest();
    let polygon = turf.polygon([[
        [nw.lng, nw.lat],
        [ne.lng, ne.lat],
        [se.lng, se.lat],
        [sw.lng, sw.lat],
        [nw.lng, nw.lat]
    ]]);
    return polygon;
}
View Code

 

 

posted @ 2021-09-10 14:19  0611163  阅读(967)  评论(0编辑  收藏  举报