生成指定辖区内随机点

在 web 地图开发过程中可能需要根据某一个区域生成一些随机点用来做测试。

之前在某个网站有这个功能是借助百度地图相关 api 来实现的,那我们不借助这些地图厂商的 api 可以实现吗?答案是可以的。

在线体验地址: leaflet-gaode.netlify.app/

仓库地址: github.com/leafletjsEx...

要实现什么?

实现的内容如下图:选择辖区、输入生成点位数量后点击"生成点位" 会移动到辖区中心点并在辖区范围内生成一定数量的随机点(因为需要过滤掉不在辖区内的的点位实际加载的点位数量会小于输入框的数量)。

分解下任务

  1. 获取辖区数据即省市区三级联动数据
  2. 获取区域的中心点、区域边界框
  3. 判断点位是否在指定区域内
  4. 生成随机点
  5. 加载到地图

获取辖区数据即省市区三级联动数据

这个数据是从 datav 这个网站获取的。

获取的数据是一维数据手动处理成了省市区三级联动数据点击获取。

获取区域的中心点、边界框

上边通过 datav 获取的数据中已经包含了区域边界框、中心点。

你可能会好奇如果没有怎么计算呢?

使用 turfjs 执行 pnpm i @turf/turf 安装依赖

Turf.js 是一个用于地理空间分析和处理的 JavaScript 库。它提供了一系列功能强大且易于使用的工具,用于处理地理空间数据、执行地理空间分析以及生成地理空间几何对象等操作。

根据范围获取边界框

bbox方法接收一个 geojson 类型的参数

js 复制代码
import {bbox as turfBbox} from '@turf/turf'

const getBbox = async () => {  
// 获取 datav 区域边界 geojson
const data = await fetch('https://geo.datav.aliyun.com/areas_v3/bound/371500.json').then(res => res.json())  

// 输出四个顶点坐标数组 [115.271447, 35.779921,116.549533,37.032217]
console.log(turfBbox(data));   
}

线上 github、netlify 等部署后无法访问 datav geojson 数据(403)使用 aircode写接口代理一层。

js 复制代码
// @see https://docs.aircode.io/guide/functions/
const aircode = require('aircode');

module.exports = async function (params, context) {
  const areaCode = params.areaCode
  if (!areaCode) {
    return {
      msg: 'areaCode 不能为空'
    }
  }
  
  const url = `https://geo.datav.aliyun.com/areas_v3/bound/${areaCode}.json`

  return await fetch(url).then(res => res.json())
};

// 访问如下接口传递 区域 code 获取数据
const areaData = await fetch(`https://36dvjmmx39.us.aircode.run/index?areaCode=${code}`).then(res => res.json())

非 geojson 类型的数据使用 polygon 方法转换为 gepjson 在进行上边的操作

js 复制代码
import { polygon , bbox} from '@turf/turf'

// 创建一个表示区域范围的多边形
const polygon = polygon([[
  [0, 0],
  [0, 10],
  [10, 10],
  [10, 0],
  [0, 0]
]]);

// 计算多边形的边界框
const bbox = bbox(polygon);

console.log(bbox); // 返回一个表示边界框的数组 [minX, minY, maxX, maxY]

生成随机点

随机点是根据边界框来计算生成逻辑比较简单

js 复制代码
const generateRandomPointInBoundingBox = (bbox) => {  
    const [minX, minY, maxX, maxY] = bbox;  
    const randomX = Math.random() * (maxX - minX) + minX;  
    const randomY = Math.random() * (maxY - minY) + minY;  
    
    // 坐标点
    return [randomX, randomY];  
}

判断点位是否在指定区域内

这个需要使用 turfjs 的 booleanPointInPolygon 方法用于判断点是否在多边形内部。接收一个 geojson<Polygon | MultiPolygon> 类型的数据。

区域边界数据从 datav 来获取https://geo.datav.aliyun.com/areas_v3/bound/371500.json 将链接中的 371500 替换成对应的辖区 code 就可以获取到对应的辖区数据(上边获取的省市区数据里包含区域 code)。

js 复制代码
// todo 这里重命名 只是为了防止名称重复
import {  
point as turfPoint,  
multiPolygon as turfMultiPolygon,  
booleanPointInPolygon  
} from '@turf/turf'

// 判断点是否在 geoJson 内  
const isPointInsideFeatureCollection = (featureCollection, pointCoordinates) => {  
  
    // 将点坐标转换为 Turf.js 对象  
    const point = turfPoint(pointCoordinates);  

    // 遍历 FeatureCollection 中的每个 Feature  
    for (const feature of featureCollection.features) {  
    const multiPolygon = turfMultiPolygon(feature.geometry.coordinates);  
    const isPointInsidePolygon = booleanPointInPolygon(point, multiPolygon);  

    if (isPointInsidePolygon) {  
    return true; // 如果点在任何一个 Feature 内,返回 true  
    }  
    }  

    return false; // 如果点不在任何一个 Feature 内,返回 false  
}

生成随机点

这个逻辑也比较简单根据输入的生成数量执行 for 循环生成随机点然后判断是否在区域范围内,在的话就添加到数组。

js 复制代码
const pointArr = ref([]);
  
// num 由参数传入
for (let i = 0; i < num; i++) {  
    // 生成随机点
    const point = generateRandomPointInBoundingBox(areaInfo.bbox);  

    // 判断点是否在区域范围内
    const isInside = isPointInsideFeatureCollection(areaData, point);  

    // 添加到数组中
    if (isInside) {  
        pointArr.value.push(point)  
    }  
}

将点位添加到地图

作者使用的是 leafletjs 所以就以此为例,创建地图的过程就省略了。

js 复制代码
const pointArr = ref([]);  
const markerList = ref([]);  
  
const clearAllMarker = () => {  
markerList.value.map((item) => {  
    item.remove();  
    return item;  
    });  
    markerList.value = [];  
};  
  
// 获取 marker icon  
const getMarkerIcon = () => {  
    return L.icon({  
        iconUrl: point_icon,  
        iconSize: [40, 40],  
    });  
};  
  
const markersLayerGroup = ref();  

const addMarkerToMap = () => {  
    clearAllMarker()  

    // 图曾存在先清除  
    if (markersLayerGroup.value) {  
    mapObj.value.removeLayer(markersLayerGroup.value);  
    }  

    // 创建图层组 聚合图层 leaflet.markercluster 插件  
    markersLayerGroup.value = L.markerClusterGroup({  
        chunkedLoading: true,  
        showCoverageOnHover: false,  
    });  

    // 向图层添加数据  
    pointArr.value.map((item) => {  
        markersLayerGroup.value.addLayer(  
            L.marker(item.reverse(), {icon: getMarkerIcon()}), 
        );  
    });  

    // 将图层组加载到地图  
    mapObj.value.addLayer(markersLayerGroup.value);  
};

最终效果

总结

本文介绍了如何根据辖区范围生成随机点及 turfjs 的简单使用。

完整示例请查看 git 仓库: github.com/leafletjsEx...

往期文章

相关推荐
getaxiosluo9 分钟前
vue3使用element-plus,树组件el-tree增加引导线
前端·javascript·vue.js·elementui·css3·element-plus
hiddenSharp42914 分钟前
【Python】使用 爬虫/GitHub Actions 自动更新 CSDN 个人主页浏览量统计
爬虫·python·github
YUJIANYUE17 分钟前
6KBhtm+js实现提交名单随机抽取功能适用活动或课堂随机点名
前端·javascript·css
可缺不可滥1 小时前
前端 性能优化 (图片与样式篇)
前端·性能优化
Bug从此不上门1 小时前
Nuxt3之使用lighthouse性能测试及性能优化实操
前端·javascript·性能优化·vue·fetch·nuxt3
拼图2091 小时前
Vue.js开发基础——数据绑定/响应式数据绑定
前端·javascript·vue.js
刘志辉1 小时前
vue反向代理配置及宝塔配置
前端·javascript·vue.js
星叔2 小时前
ARXML汽车可扩展标记性语言规范讲解
java·前端·汽车
编程老船长2 小时前
第18章 从零开始:春节门联网页设计,用DIV+CSS打造传统与现代的完美融合
前端·css·html
sky.fly2 小时前
HTML5+css3(浮动,浮动的相关属性,float,解决浮动的塌陷问题,clear,overflow,给父亲盒子加高度,伪元素)
前端·css·html