最近遇到一个需求,需要在一个园内随机打点。使用过openlayer的都知道,这个打点肯定需要知道点的经纬度。怎么去算一个园内点的经纬度呢,这个一开始也愁到我了。目前只有半径和圆心的经纬度,去网上搜也没搜到结果。然后我就换了一种思路去思考。我需要的是在圆内填充点。那我是不是可以能多个同心圆嵌套,每个圆都是虚线的。类似以下的效果
直接上代码
//根据车站数量获取车站半径
const getRadius = (count) => {
let radius = 0
if (count > 0 && count <= 500) {
//0.5公里
radius = 0.5
} else if (count <= 1000) {
//1公里
radius = 1
} else if (count <= 3000) {
//1.5公里
radius = 1.5
} else {
//2公里
radius = 2
}
return radius * 4
}
data是圆心点的坐标集合,是个数组。
const pointFeatures = data.reduce((prev, item) => {
//圆心点的经纬度
const coordinate = [Number(item.lon), Number(item.lat)]
//后端返回的数量
const count = item.count
if (count == 0) {
return
}
//这个根据数量,算出每个圆的半径
const radius1 = getRadius(count)
//每个圆要嵌套同心圆的个数(层级)
let features = Array(Math.round(radius / 5) + 1).fill(1)
features = features.map((item, index) => {
//radius为圆的半径,如果是第一个圆,就默认很小的半径。第一圆就是一个点。后面的都是根据当前的索引算出半径, 随着索引半径依次递增。
const radius =
index == 0
? (0.1 * 250) /
ol.proj.getPointResolution('EPSG:4326', 1, coordinate, 'm')
: ((index + 1) * 500) /
ol.proj.getPointResolution('EPSG:4326', 1, coordinate, 'm')
const geometry = new ol.Feature({
geometry: new ol.geom.Circle(coordinate, radius)
})
//最后一个圆加上背景
if (index == features.length - 1) {
geometry.setStyle(
new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'rgb(54,225,7)',//边框颜色
width: 5,//这是边框的宽度
lineDash: [1, 45],//第一个传每个虚线的长度,第二个传每个虚线直接的距离.这个可以控制每个同心圆点的个数.距离越大,圆上点的个数越少.
lineJoin: 'round',
lineCap: 'round'
}),
//圆的填充颜色
fill: new ol.style.Fill({
color: 'rgba(0, 0, 255, 0.1)'
})
})
)
} else {
geometry.setStyle(
new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'rgb(54,225,7)',
width: 5,
lineDash: [1, 45],
lineJoin: 'round',
lineCap: 'round'
})
})
)
}
return geometry
})
return prev.concat(features)
}, [])
//创建地图图层
let pointsStationLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: features,
wrapX: true
})
})
map.addLayer(pointsStationLayer)
这样就实现了在地图上创建多个圆,每个圆内均匀的打上点.
好处是这样打的点上,页面不会卡.也不需要算每个圆内点的经纬度.
缺点是这个点在慢慢放大的过程中,这个圆会以一个风扇的形式展开,不太好看.并且这个点也不是随机的.
客户想要的是随机打点的效果,上面这种方案点太均匀了.于是不得不换一种方法.
这次得根据圆心和半径求出园内任意点的坐标.这次难道我了.这个确实不好算.然后我就突然想了个办法,把这个点看作是一个(x,y)的坐标点.
可以知道这个园内点的坐标主要和角度,圆心点,半径有关.那我们就可以随机0-360的角度,以及0-r的半径.
把这个经度看在y轴上,纬度在x轴上
//r为半径,lon为经度,lat为纬度
const getPointCoordinate = (r, lon, lat) => {
let angle = Math.floor(Math.random() * 360)
//半径
let nd = Math.random(0,1)*r
//经度
lon = lon + Math.sin(angle) * nd
//纬度
lat = lat + Math.cos(angle) * nd
return {
lon,
lat
}
}
这种方式打出的点会比较多,建议不用使用矢量图层.我是用的mapV,webgl格式的图层,有几十万个点也不卡.
const addPointStationLayer3 = (data) => {
const pointFeatures = data.reduce((prev, item) => {
const coordinate = [Number(item.lon), Number(item.lat)]
const counts = item.count
if (counts == 0) {
return
}
const miter = ol.proj.getPointResolution('EPSG:4326', 1, coordinate, 'm')
//5,10,15,20,0.5公里-500米,1公里-10000米,1.5公里-1500米,2公里-2000米
const { r, count } = getRadius2(counts,miter)
let features = Array(count).fill(1)
features = features.map((item, index) => {
const coordinates = getPointCoordinate(r, coordinate[0], coordinate[1])
return {
geometry: {
type: 'Point',
coordinates: [coordinates.lon, coordinates.lat]
}
}
})
return prev.concat(features)
}, [])
let dataset = new mapv.DataSet(pointFeatures)
let mapvOptions = {
fillStyle: 'rgb(54,225,7)',
globalCompositeOperation: 'lighter',
size: 3,
context: 'webgl',
updateImmediate: true,
draw: 'simple'
}
let options = { map: map, dataSet: dataset, mapvOptions: mapvOptions }
let source = new ol.source.Mapv(options)
pointsStationLayer = new ol.layer.Image({
source: source
})
map.addLayer(pointsStationLayer)
}
//根据车站数量获取车站半径
const getRadius2 = (count,miter) => {
let radius = 0
if (count > 0 && count <= 500) {
//0.5公里
radius = 0.5
} else if (count <= 1000) {
//1公里
radius = 1
} else if (count <= 3000) {
//1.5公里
radius = 1.5
} else {
//2公里
radius = 2
}
return { r: (radius / miter)*1000, count: radius * 500 }
}
最后实现效果图如下: