离线地图Leaflet.js开发---绘制多边形

基于leaflet.js实现的多边形绘制,附带可以判断点是否再多边形内,本着无私奉献的原则,这里直接将js组件代码贴上,大家需要可以自取,记得点赞。

先看下效果图是不是你想要的:

  1. 绘制过程中:
    在这里插入图片描述

DrawTool.js: 自己封装的画布js文件,在使用时引入即可,需要大家自己创建一个js,把以下代码copy进去

/**
 * 画布js,用于在地图中画多边形,点击以绘画,右键回退,绘制完成后右键取消,可以通过覆盖DrawTool.onDoubleClick函数来实现双击操作,
 * 通过DrawTool.iconUrl='path'来设置绘制图像的点图标
 */
var DrawTool = {};
DrawTool.points = [];// 绘制图形的点纬度和经度集合 【【】,【】】 
DrawTool.markers=[];// 标记图标
DrawTool.lines  = new L.polyline(DrawTool.points);
DrawTool.tempLines  = new L.polyline([],{dashArray: 12});
DrawTool.polygons = new L.polygon(DrawTool.points);
DrawTool.tempPolygons = new L.polygon([],{color: 'none',fillColor: 'red'});

// 双击执行的函数
DrawTool.onDoubleClick = function (e){
	alert("选定完成: \n "+ JSON.stringify(DrawTool.points));
	return false;
}

DrawTool.init = function (map,iconUrl){
	// var circldRadius = 50;
	map.off('click');
	map.on('click', onClick);    //点击地图
	map.off('contextmenu');
	map.on('contextmenu', contextmeanClick); // 右键地图

	function onClick(e) {
		var marker;
		if(iconUrl){
			marker = L.marker(e.latlng,{icon: L.icon({iconUrl: 'img/move.png', iconSize: [25,25]})});
		}else{
			marker = L.marker(e.latlng);
		}
		if(DrawTool.points.length <= 0){
			marker.on('click',clickMarker);
		}
		marker.addTo(map);
		DrawTool.points.push([e.latlng.lat, e.latlng.lng]);
		DrawTool.lines .addLatLng(e.latlng);
		map.addLayer(DrawTool.lines );
		// map.addLayer(marker);
		DrawTool.markers.push(marker);
		map.on('mousemove', onMove);//鼠标移动
	}

	function onMove(e) {
		if (DrawTool.points.length > 0) {
			ls = [DrawTool.points[DrawTool.points.length - 1], [e.latlng.lat, e.latlng.lng]];
			DrawTool.tempLines.setLatLngs(ls);
			map.addLayer(DrawTool.tempLines);
			DrawTool.tempPolygons.setLatLngs(DrawTool.points);
			DrawTool.tempPolygons.addTo(map);
		}
	}

	// 右键回退一步
	function contextmeanClick(e){
		DrawTool.points.splice(DrawTool.points.length - 1,1);
		if (DrawTool.points.length > 0) {
			ls = [DrawTool.points[DrawTool.points.length - 1], [e.latlng.lat, e.latlng.lng]];
			DrawTool.tempLines.setLatLngs(ls);
			map.addLayer(DrawTool.tempLines);
			DrawTool.tempPolygons.setLatLngs(DrawTool.points);
			DrawTool.tempPolygons.addTo(map);
			DrawTool.lines .remove();
			DrawTool.lines  = new L.polyline(DrawTool.points);
			DrawTool.lines .addTo(map);
			DrawTool.markers[DrawTool.markers.length - 1].remove();
			DrawTool.markers.splice(DrawTool.markers.length - 1,1);
		}else{
			DrawTool.tempLines.remove();
			DrawTool.lines .remove();
			if(DrawTool.markers.length > 0){
				DrawTool.markers[DrawTool.markers.length - 1].remove();
				DrawTool.markers.splice(DrawTool.markers.length - 1,1);
			}
			DrawTool.points =[];
			DrawTool.markers = [];
			DrawTool.lines  = new L.polyline(DrawTool.points);
		}
	}

	// 点击第一个节点停止绘画
	function clickMarker(e){
		// 关闭画图事件
		map.off('dblclick');
		map.off('mousemove');
		map.off('click');
		// 停止右键事件并且绑定新的
		map.off('contextmenu'); // 右键地图
		map.on('contextmenu', cancle); // 右键地图
		// 移除临时线和面
		DrawTool.tempLines.remove();
		DrawTool.tempPolygons.remove();
		DrawTool.polygons = L.polygon(DrawTool.points);
		DrawTool.polygons.addTo(map);
		DrawTool.polygons.on('dblclick', DrawTool.onDoubleClick);
		// 移动标记(微调)
		for(var marker of DrawTool.markers){
			marker.dragging.enable();
			marker.on('dragend',function(event){
				resetRegion();
			});
		}
	}

	// 调整了坐标点
	function resetRegion(){
		DrawTool.points = [];
		for(var marker of DrawTool.markers){
			DrawTool.points.push([marker.getLatLng().lat,marker.getLatLng().lng]);
		};
		DrawTool.polygons.remove();
		DrawTool.lines .remove();
		DrawTool.polygons = L.polygon(DrawTool.points);
		DrawTool.polygons.on('dblclick', DrawTool.onDoubleClick);
		DrawTool.polygons.addTo(map);
	}

	// 右键取消
	function cancle(e){
		map.on('click',onClick);
		map.off('contextmenu');
		map.on('contextmenu', contextmeanClick); // 右键地图
		for(var marker of DrawTool.markers){
			marker.remove();
		}
		DrawTool.points = [];
		DrawTool.markers=[];
		DrawTool.lines .remove();
		DrawTool.tempLines.remove();
		DrawTool.tempPolygons.remove();
		DrawTool.polygons.remove();
		DrawTool.polygons = new L.polygon(DrawTool.points);
		DrawTool.lines  = new L.polyline(DrawTool.points);
		DrawTool.tempLines = new L.polyline([],{dashArray: 12});
		DrawTool.tempPolygons = new L.polygon([],{color: 'none',fillColor: 'red'});
	}
}

赠送一个判断点是否在多边形内部的工具类GeoUtils.js:

var GeoUtils = {};

/**
 * 判断点是否在矩形内
 * @param {Point} point 点对象
 * @param {Bounds} bounds 矩形边界对象
 * @returns {Boolean} 点在矩形内返回true,否则返回false
 */
GeoUtils.isPointInRect = function (point, bounds) {
    var sw = bounds.getSouthWest(); //西南脚点
    var ne = bounds.getNorthEast(); //东北脚点
    return (point.lng >= sw.lng && point.lng <= ne.lng && point.lat >= sw.lat && point.lat <= ne.lat);
}


/**
* 判断点是否多边形内
* @param {Point} point 点对象
* @param {Polyline} polygon 多边形对象
* @returns {Boolean} 点在多边形内返回true,否则返回false
*/
GeoUtils.isPointInPolygon = function (point, polygon) {

    //首先判断点是否在多边形的外包矩形内,如果在,则进一步判断,否则返回false
    var polygonBounds = polygon.getBounds();
    if (!this.isPointInRect(point, polygonBounds)) {
        return false;
    }

    var pts = polygon.getLatLngs()[0];//获取多边形点

    //下述代码来源:http://paulbourke.net/geometry/insidepoly/,进行了部分修改
    //基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则
    //在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。
    var N = pts.length;
    var boundOrVertex = true; //如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
    var intersectCount = 0;//cross points count of x 
    var precision = 2e-10; //浮点类型计算时候与0比较时候的容差
    var p1, p2;//neighbour bound vertices
    var p = point; //测试点

    p1 = pts[0];//left vertex    
    for (var i = 1; i <= N; ++i) {//check all rays            
        if (p.equals(p1)) {
            return boundOrVertex;//p is an vertex
        }

        p2 = pts[i % N];//right vertex            
        if (p.lat < Math.min(p1.lat, p2.lat) || p.lat > Math.max(p1.lat, p2.lat)) {//ray is outside of our interests                
            p1 = p2;
            continue;//next ray left point
        }

        if (p.lat > Math.min(p1.lat, p2.lat) && p.lat < Math.max(p1.lat, p2.lat)) {//ray is crossing over by the algorithm (common part of)
            if (p.lng <= Math.max(p1.lng, p2.lng)) {//x is before of ray                    
                if (p1.lat == p2.lat && p.lng >= Math.min(p1.lng, p2.lng)) {//overlies on a horizontal ray
                    return boundOrVertex;
                }

                if (p1.lng == p2.lng) {//ray is vertical                        
                    if (p1.lng == p.lng) {//overlies on a vertical ray
                        return boundOrVertex;
                    } else {//before ray
                        ++intersectCount;
                    }
                } else {//cross point on the left side                        
                    var xinters = (p.lat - p1.lat) * (p2.lng - p1.lng) / (p2.lat - p1.lat) + p1.lng;//cross point of lng                        
                    if (Math.abs(p.lng - xinters) < precision) {//overlies on a ray
                        return boundOrVertex;
                    }

                    if (p.lng < xinters) {//before ray
                        ++intersectCount;
                    }
                }
            }
        } else {//special case when ray is crossing through the vertex                
            if (p.lat == p2.lat && p.lng <= p2.lng) {//p crossing over p2                    
                var p3 = pts[(i + 1) % N]; //next vertex                    
                if (p.lat >= Math.min(p1.lat, p3.lat) && p.lat <= Math.max(p1.lat, p3.lat)) {//p.lat lies between p1.lat & p3.lat
                    ++intersectCount;
                } else {
                    intersectCount += 2;
                }
            }
        }
        p1 = p2;//next ray left point
    }

    if (intersectCount % 2 == 0) {//偶数在多边形外
        return false;
    } else { //奇数在多边形内
        return true;
    }
}

一个简单的样例Demo(大家需要根据自己的情况适当修改):

<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<title>绘制多边形</title>
	<!--  这些是leaflet。js的依赖,必须引入 -->
	<link rel="stylesheet" type="text/css" href="../leaflet/dist/leaflet.css">
	<script type="text/javascript" src="../leaflet/dist/leaflet.js"></script>
	<script type="text/javascript" src="../lib/leaflet.motion.min.js"></script>
	<!-- 引入画布js -->
	<script type="text/javascript" src="js/DrawTool.js"></script>
	<!-- 引入判断点是否在多边形内 -->
	<script type="text/javascript" src="js/GeoUtils.js"></script>
	<style type="text/css">
		body {
			padding: 0;
			margin: 0;
		}

		html,
		body,
		#map {
			height: 100%;
		}
	</style>
</head>

<body>
	<div id='map'></div>
</body>
<script type="text/javascript">
	// 加载的地图地址,这里需要修改为你们自己的 todo
	var url = 'http://localhost:9090/img/{z}/{x}/{y}.png';
	var map = L.map('map', {
		center: [34.694, 113.587],
		zoom: 6,
		zoomControl: false
	});
	//将图层加载到地图上,并设置最大的聚焦还有map样式
	L.tileLayer(url, {
		maxZoom: 18,
		minZoom: 3
	}).addTo(map);

	// 以下代码是绘制多边形的操作
	// 初始化画布,给定地图容器,第一个参数是地图对象,第二个参数是绘制时图标的样式,第二个参数可以不写
	DrawTool.init(map,'img/move.png');

	// 通过重写双击方法来实现双击执行一系列操作,通常是获取多边形的点集合,以做其他操作
	DrawTool.onDoubleClick = function(e){
		// 打印绘制多边形的点集合
		console.log(JSON.stringify(DrawTool.points));
		// console.log(DrawTool.polygon);
		var marker = L.marker([34.694, 113.587]);
		marker.addTo(map);
		alert('我就是这么强大!啦啦啦啦啦!!!点是否在多边形内 >> '+GeoUtils.isPointInPolygon(marker.getLatLng(),DrawTool.polygon));
	}


</script>

</html>

使用说明:
在绘制时,单击鼠标右键会回退一步,单击鼠标左键进行绘制,绘制完成后可以通过拖动图标进行微调,此时单机鼠标左键会直接将绘制完成的图片清除。

希望可以帮助到您,不收您的钱,给个赞赞吧!

福利

附上一个好用的离线地图,能够进行瓦片下载和交互,支持街道图、卫星图、内置的有开发手册,亲测可用
https://download.csdn.net/download/weixin_43464964/72332736

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老司机张师傅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值