公司接到一个需求是实现在离线地图上做点聚合,并且点上有连线,还需要对特定的点做纬度偏移,然后我就选择了openlayers来实现离线地图开发
第一步 刚开始实现的时候我是按照官网的案例先把地图画出来
javascript
import Map from 'ol/Map';
import { Tile as TileLayer } from 'ol/layer';
import { ScaleLine } from 'ol/control';
import XYZ from 'ol/source/XYZ'
this.mapObj = new Map({
target: 'map', // 地图容器
view: new View({
center: [0,0], // 地图中心点
zoom: 9, // 缩放
maxZoom: 21, 最大缩放等级
projection: 'EPSG:4326', // 坐标系
})
})
this.mapObj.addControl(new ScaleLine()) // 设置地图比例尺
// 添加一个使用离线瓦片地图的层
const offlineMapLayer = new TileLayer({
source: new XYZ({
url: 'http://192.168.10.170:30808/courseware/tiles/10/124/5.jpg'// 设置本地离线瓦片所在路径
})
})
// 将图层添加到地图
this.mapObj.addLayer(offlineMapLayer)
我这边使用的是地图瓦片加载,瓦片下载地址可以在下载望远网,其实也是可以openlayers上面的SOM,它是OpenStreetMap 切片服务器的图层源。
javascript
import OSM from 'ol/source/OSM';
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
const TileLayer = new TileLayer({
source: new OSM(),
})
this.mapObj.addLayer(TileLayer)
现在地图终于展示出来了😄,然后就是开始画点了,点数据是从接口获取的并且包含经纬度值,这里我就模拟数据。
第二步 创建点元素并且添加到地图上
javascript
import VectorSource from 'ol/source/Vector'; //提供矢量图层的特征源。该源提供的矢量特征适合编辑
import { Vector } from 'ol/layer'; // 矢量数据在客户端呈现为矢量。即使在动画期间,此图层类型也能提供最准确的渲染。
import { Cluster } from 'ol/source'; // 对矢量数据进行聚类的图层源。开箱即用,具有点几何形状。对于其他几何类型,或者如果并非所有几何都应考虑进行聚类,则`geometryFunction`可以定义自定义。
import Feature from 'ol/Feature'; //具有几何和其他属性属性的地理要素的矢量对象
import Point from 'ol/geom/Point'; //点几何类型
const pointData = [{
id: 1,
lon: 118.72016345937209,
lat: 25.763332248147847
},
{
id: 2,
lon: 119.03663979738225,
lat: 25.885031687253477
},
{
id: 3,
lon: 118.62016345937209,
lat: 25.763332248147847
},
{
id: 4,
lon: 119.03663979738225,
lat: 26.085031687253477
},
{
id: 5,
lon: 118.72016345937209,
lat: 26.085031687253477
}];
const pointSource = new VectorSource();
pointData.forEach(point => {
const feature = new Feature({
geometry: new Point(([point.lon, point.lat])),
// 单独设置一个字段存储数据,方便后续使用
data: point,
});
pointSource.addFeature(feature);
});
// 把点数据的矢量对象添加到聚合图层上
const clusterSource = new Cluster({
distance: 80, // 要素将聚集在一起的距离(以像素为单位)。
source: pointSource, // 数据源
});
// 然后把聚合图层添加到矢量层上
const clusterLayer = new Vector({
source: clusterSource,
style: baseStyle // 该函数可以设置点的基础样式,具体参考官网API
})
// 最后把矢量层添加到地图上
this.map.addLayer(clusterLayer);
然后地图上就可以展示出点了。
现在就是开始画连线了,终于完成一大半了,可以早点下班了😏 画线其实和画点方法很类似,只不过我们需要使用线的对象,这边我们还是使用模拟数据渲染。
第三步 创建线元素并且添加到地图上
arduino
import LineString from 'ol/geom/LineString'; //线几何类型
const lineData = [
{
id: 1,
startX: 118.72016345937209, // 起点经度
startY: 25.763332248147847, // 起点纬度
endX: 118.62016345937209, // 终点经度
endY: 25.763332248147847 // 终点纬度
},
{
id: 2,
startX: 118.72016345937209,
startY: 25.763332248147847,
endX: 119.03663979738225,
endY: 25.885031687253477
}
]
// 同样需要创建矢量源
const lineSource = new VectorSource();
lineData.forEach(line => {
const feature = new Feature({
geometry: new LineString([([line.startX, line.startY]), ([line.endX,line.endY])]),
data: line,
});
lineSource.addFeature(feature);
});
// 创建矢量层把线数据源添加进去
const lineLayer = new Vector({
source: lineSource
})
// 最后把线的矢量层添加到地图上
this.map.addLayer(lineLayer);
现在的效果就出来了。
但是当我缩小地图时,咦,怎么连线展示不对了😲,突然想到线连的还是实际的经纬度,但是当点聚合时,聚合点经纬度就是两点之间的中心点了。
然后就是问下产品这种情况下需要怎么展示,本来想说服产品就按这种效果就可以,因为我也不想改🤦♂️。最终还是需要改,那就没办法动手改吧!
产品想要的效果时当某几个点参与聚合时,这几个点上的所有连线都要隐藏,否则就是展示。
第四步 对线要素做聚合时隐藏
刚刚写到画线的把连线的数据源添加到矢量层时,里面有个style属性可以处理线的对象属性
javascript
// 创建矢量层把线数据源添加进去
const lineLayer = new Vector({
source: lineSource,
style: (feature) => { //feature就是当前连线的对象
console.log(feature.getGeometry())
}
})
然后我们就可以处理这个连线属性
scss
// 创建矢量层把线数据源添加进去
const lineLayer = new Vector({
source: lineSource,
style: (feature) => { //feature就是当前连线的对象
const resolution = this.map.getView().getResolution(); // 获取当前地图的分辨率,它表示地图上一个像素对应的地理距离。
const currLineString = feature.getGeometry(); // 获取当前对象的几何类型
const pixelsLength = currLineString.getLength()/resolution; // 然后,获取当前要处理的线要素(feature)的几何信息,并计算出线的长度(pixelsLength)
// 获取聚合点的几何对象
const clusterFeatures = clusterSource.getFeatures();
// 从当前线要素的几何信息中获取起点和终点的坐标(startCoordinate 和 endCoordinate)
const [startCoordinate, endCoordinate] = currLineString.getCoordinates();
// 如果线的起点终点都在聚合点上
const hasStart = clusterFeatures.some((it) =>equals(it.getGeometry()?.getCoordinates(), startCoordinate))
const hasEnd = clusterFeatures.some((it) =>equals(it.getGeometry()?.getCoordinates(), endCoordinate))
// 判断当前线的长度如果大于聚合层设置的间距值,并且连线的起点和终点都在聚合点上,才画线。
// Stroke就是设置矢量特征的描边样式。
if (pixelsLength > distance && hasStart && hasEnd) {
return new Style({
stroke: new Stroke({
color: "rgba(255,0,255,0.8)",
width: 5,
}),
})
}
}
})
最终效果就是如下:
希望帅哥美女们可以给点个赞👍。