最終效果:
1.參考官網地址:https://openlayers.org/en/latest/examples/jsts.html 官網默認坐標系是:3857
2.版本:
2.1 openlayer 版本:6.3.1 //js地圖操作必要
2.2 jsts 版本:https://unpkg.com/jsts@2.0.2/dist/jsts.min.js //生成閉環區域需要使用
2.3 rbush.js //畫線需要使用
3.建立坐標系4326 的地圖服務
var mapServer;
//地圖中心點位
var gcenter = [114.025705,22.68988];
//初始化地圖圖層
var gzoom = 13;
//地圖最小縮小圖層
var gminZoom = 1;
//地圖最大放大圖層
var gmaxZoom = 16;
//地圖圖片加載路徑
var gispath="http://127.0.0.1:8082/map/{z}/{n}/{x}_{y}.png";
//地圖坐標生成對應圖片路徑
function tileUrlFunction(tileCoord, pixelRatio, projection) {
var z = tileCoord[0];
var x = tileCoord[1];
var y = tileCoord[2] - 1;
var height = (1 << z);
var num2 = (height * y) + x;
var num3 = parseInt(num2 / 10000);
var url = gispath.replace('{z}', z).replace('{y}', y).replace('{n}', num3).replace('{x}', x);
return url;
}
//地圖layer
var Thai = new ol.layer.Tile({
source: new ol.source.XYZ({
tileUrlFunction: tileUrlFunction,
wrapX: false
})
});
function mapServerBuild(){
mapServer = new ol.Map({
layers: [Thai],
view: new ol.View({
center:gcenter,
projection: 'EPSG:4326',
zoom: gzoom,
minZoom: gminZoom,
maxZoom: gmaxZoom
//extent:extentPoint 地圖可查看邊界
}),
logo : false,
controls : new ol.control.defaults({
zoom : false
// 隱藏縮放按鈕
}),
target: document.getElementById('sitemap')
});
}
4.創建點擊事件並進行畫線
var clickPoints = [];//保留地圖點擊點位
mapServer.on('click', function(event){
var currentPoint={} ;
currentPoint.x = event.coordinate[0];
currentPoint.y = event.coordinate[1];
console.log("x:"+currentPoint.x+" "+"y:"+currentPoint.y);
clickPoints.push(currentPoint);
//限制,最多畫5條線
if(clickPoints.length > 1 && clickPoints.length <=6){
drawLines(clickPoints);
}
}
})
function drawLines(pointArray){
var arrayLine = [];
for(var i=0;i<pointArray.length;i++){
var point = pointArray[i];
//新增如果坐標無法識別,則過濾掉
if(isNaN(point.x)){ continue;}
if(isNaN(point.y)){ continue;}
// 新增坐標默認0的進行過濾
if(point.x == 0 && point.y == 0){continue;}
arrayLine.push([point.x,point.y]);
}
if(arrayLine.length!=0){
//地圖划線
if(arrayLine.length>1){
for(var i =0 ;i<arrayLine.length-1;i++){
var arrayLines = [];
arrayLines.push(arrayLine[i]);
arrayLines.push(arrayLine[i+1]);
lineString(arrayLines);
}
}else{
lineString(arrayLine);
}
}
}
/**
* eg:
var arrayLines = [];
arrayLines.push([x,y]);
arrayLines.push([x1,y1]);
lineString(arrayLines);
* 畫線
* @param zb
*/
function lineString(zb){
var vectorLine = new ol.source.Vector({}); //線
var lineLayer = new ol.layer.Vector({
style : function(feature){
return styleFunction(feature,0.000072);
}
});
mapServer.addLayer(lineLayer);
if(zb.length>1){
var line = new ol.Feature({
geometry : new ol.geom.LineString(zb)
});
vectorLine.addFeature(line);
lineLayer.setSource(vectorLine);
}
}
//軌跡線樣式
var styleFunction = function(feature,res){
//軌跡線圖形
var trackLine= feature.getGeometry();
var styles = [
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#fc0101',
width: 2
})
})
];
//對segments建立btree索引
let tree= rbush();//路段數
trackLine.forEachSegment(function(start, end) {
var dx = end[0] - start[0];
var dy = end[1] - start[1];
//計算每個segment的方向,即箭頭旋轉方向
let rotation = Math.atan2(dy, dx);
let geom=new ol.geom.LineString([start,end]);
let extent=geom.getExtent();
var item = {
minX: extent[0],
minY: extent[1],
maxX: extent[2],
maxY: extent[3],
geom: geom,
rotation:rotation
};
tree.insert(item);
});
//軌跡地理長度
let length=trackLine.getLength();
//像素間隔步長
let stpes=50;//像素步長間隔
//將像素步長轉實際地理距離步長
let geo_steps=stpes*res;
//箭頭總數
let arrowsNum=parseInt(length/geo_steps);
for(let i=1;i<arrowsNum;i++){
let arraw_coor=trackLine.getCoordinateAt(i*1.0/arrowsNum);
let tol=0.0001;//查詢設置的點的容差,測試地圖單位是米。如果是4326坐標系單位為度的話,改成0.0001.
let arraw_coor_buffer=[arraw_coor[0]-tol,arraw_coor[1]-tol,arraw_coor[0]+tol,arraw_coor[1]+tol];
//進行btree查詢
var treeSearch = tree.search({
minX: arraw_coor_buffer[0],
minY: arraw_coor_buffer[1],
maxX: arraw_coor_buffer[2],
maxY: arraw_coor_buffer[3]
});
let arrow_rotation;
//只查詢一個,那么肯定是它了,直接返回
if(treeSearch.length==1)
arrow_rotation=treeSearch[0].rotation;
else if(treeSearch.length>1){
let results=treeSearch.filter(function(item){
//箭頭點與segment相交,返回結果。該方法實測不是很准,可能是計算中間結果
//保存到小數精度導致查詢有點問題
// if(item.geom.intersectsCoordinate(arraw_coor))
// return true;
//換一種方案,設置一個稍小的容差,消除精度問題
let _tol=1;//消除精度誤差的容差
if(item.geom.intersectsExtent([arraw_coor[0]-_tol,arraw_coor[1]-_tol,arraw_coor[0]+_tol,arraw_coor[1]+_tol]))
return true;
})
if(results.length>0)
arrow_rotation=results[0].rotation;
}
}
return styles;
}
5.根據所畫線,生成閉環區域
var vectorCloseCycleSource = new ol.source.Vector({}); //閉環線資源
var vectorCloseCycleLayer = new ol.layer.Vector({
style : new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#fc0101',
width: 1
})
})
});//比環線layer
/*
* 畫閉環
* @param pointArray 坐標點位數組
* @param drawCloseCycle 閉環寬度設置 默認80
*/
function drawCloseCycle(pointArray){
if(pointArray.length <=1)return;
console.log(pointArray);
var json = constructFeatureJson(pointArray);
var format = new ol.format.GeoJSON();
var features = format.readFeatures(json, {featureProjection: 'EPSG:4326'});
var parser = new jsts.io.OL3Parser();
parser.inject(
ol.geom.Point,
ol.geom.LineString,
ol.geom.LinearRing,
ol.geom.Polygon,
ol.geom.MultiPoint,
ol.geom.MultiLineString,
ol.geom.MultiPolygon
);
for (var i = 0; i < features.length; i++) {
var feature = features[i];
// convert the OpenLayers geometry to a JSTS geometry
var jstsGeom = parser.read(feature.getGeometry());
// create a buffer of 40 meters around each line
var buffered = jstsGeom.buffer(metertodegree(40,jstsGeom._points._coordinates[0].y));
// convert back from JSTS and replace the geometry on the feature
feature.setGeometry(parser.write(buffered));
console.log("feature:");
console.log(feature.getGeometry());
}
vectorCloseCycleLayer.setSource(vectorCloseCycleSource);
mapServer.addLayer(vectorCloseCycleLayer);
vectorCloseCycleSource.addFeatures(features);
console.log("------------");
console.log(features[0].getGeometry().flatCoordinates);
console.log("------------");
}
/*
* 構建閉環json
*/
function constructFeatureJson(pointArray){
var featuresJson = '{';
featuresJson += '"features": [{';
featuresJson += '"geometry": {';
featuresJson += '"coordinates": [';
var point;
for(var i=0;i<pointArray.length;i++){
featuresJson += '[';
//坐標系轉換,默認地圖的3857 要轉為 4326坐標系。
//point = [pointArray[i].x,pointArray[i].y];
//point = ol.proj.transform(point,"EPSG:3857","EPSG:4326");
//featuresJson += point[0]+',';
//featuresJson += point[1];
featuresJson += pointArray[i].x+',';
featuresJson += pointArray[i].y;
featuresJson += ']';
if(i + 1 < pointArray.length){
featuresJson += ',';
}
}
featuresJson += '],';
featuresJson += '"type": "LineString"';
featuresJson += '},';
featuresJson += '"id": "way/33803251",';
featuresJson += '"type": "Feature"';
featuresJson += '}],';
featuresJson += '"type": "FeatureCollection"';
featuresJson += '}';
return featuresJson;
}
var radiusEarth = 6378137
/*米數 轉換 度數*/
function metertodegree (distance, lat) {
return 180 * distance / (Math.PI * radiusEarth * Math.cos(lat * Math.PI / 180))
}