Рисуем полилинию на карте и определяем ее длину, используя API Google Maps v3

На страницах своего блога я уже рассказывал о том, как нарисовать полилинию в заметке: «API Google Maps рисуем полилинию и сохраняем ее в базе данных MySQL».

В этот раз я расскажу как примерно это же можно сделать, используя API Google Maps v3 и определить длину нарисованной полилинии.

Код для примера рисования и редактирования полилинии я взял отсюда и немного переработал.

Код файла plineedit-gmap3.html с изменениями:

<!DOCTYPE html> 
<html> 
	<head> 
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
		<title>Google Maps api v3 - Рисуем полилинию и определяем ее длину</title> 
		<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> 
		<script type="text/javascript"> 
			var map = null;
			var polyLine;
			var tmpPolyLine;
			var markers = [];
			var vmarkers = [];
			var g = google.maps;
 
			var initMap = function(mapHolder) {
				markers = [];
				vmarkers = [];
				var mapOptions = {
					zoom: 14,
					center: new g.LatLng(56.316667, 44), 
					mapTypeId: g.MapTypeId.ROADMAP,
					draggableCursor: 'auto',
					draggingCursor: 'move',
					disableDoubleClickZoom: true
				};
				map = new g.Map(document.getElementById(mapHolder), mapOptions);
				g.event.addListener(map, "click", mapLeftClick);
				mapHolder = null;
				mapOptions = null;
			};
 
			var initPolyline = function() {
				var polyOptions = {
					strokeColor: "#ff0061",
					strokeOpacity: 0.8,
					strokeWeight: 4
				};
				var tmpPolyOptions = {
					strokeColor: "#ff0061",
					strokeOpacity: 0.4,
					strokeWeight: 4
				};
				polyLine = new g.Polyline(polyOptions);
				polyLine.setMap(map);
				tmpPolyLine = new g.Polyline(tmpPolyOptions);
				tmpPolyLine.setMap(map);
			};
 
			var mapLeftClick = function(event) {
				if (event.latLng) {
					var marker = createMarker(event.latLng);
					markers.push(marker);
					if (markers.length != 1) {
						var vmarker = createVMarker(event.latLng);
						vmarkers.push(vmarker);
						vmarker = null;
					}
					var path = polyLine.getPath();
					path.push(event.latLng);
					marker = null;
				}
				event = null;
			};
 
			var createMarker = function(point) {
				var imageNormal = new g.MarkerImage(
					"ris/square.png",
					new g.Size(11, 11),
					new g.Point(0, 0),
					new g.Point(6, 6)
				);
				var imageHover = new g.MarkerImage(
					"ris/square_over.png",
					new g.Size(11, 11),
					new g.Point(0, 0),
					new g.Point(6, 6)
				);
				var marker = new g.Marker({
					position: point,
					map: map,
					icon: imageNormal,
					draggable: true
				});
				g.event.addListener(marker, "mouseover", function() {
					marker.setIcon(imageHover);
				});
				g.event.addListener(marker, "mouseout", function() {
					marker.setIcon(imageNormal);
				});
				g.event.addListener(marker, "drag", function() {
					for (var m = 0; m < markers.length; m++) {
						if (markers[m] == marker) {
							polyLine.getPath().setAt(m, marker.getPosition());
							moveVMarker(m);
							break;
						}
					}
					m = null;
				});
				g.event.addListener(marker, "click", function() {
					for (var m = 0; m < markers.length; m++) {
						if (markers[m] == marker) {
							marker.setMap(null);
							markers.splice(m, 1);
							polyLine.getPath().removeAt(m);
							removeVMarkers(m);
							break;
						}
					}
					m = null;
				});
				return marker;
			};
 
			var createVMarker = function(point) {
				var prevpoint = markers[markers.length-2].getPosition();
				var imageNormal = new g.MarkerImage(
					"ris/square_transparent.png",
					new g.Size(11, 11),
					new g.Point(0, 0),
					new g.Point(6, 6)
				);
				var imageHover = new g.MarkerImage(
					"ris/square_transparent_over.png",
					new g.Size(11, 11),
					new g.Point(0, 0),
					new g.Point(6, 6)
				);
				var marker = new g.Marker({
					position: new g.LatLng(
						point.lat() - (0.5 * (point.lat() - prevpoint.lat())),
						point.lng() - (0.5 * (point.lng() - prevpoint.lng()))
					),
					map: map,
					icon: imageNormal,
					draggable: true
				});
				g.event.addListener(marker, "mouseover", function() {
					marker.setIcon(imageHover);
				});
				g.event.addListener(marker, "mouseout", function() {
					marker.setIcon(imageNormal);
				});
				g.event.addListener(marker, "dragstart", function() {
					for (var m = 0; m < vmarkers.length; m++) {
						if (vmarkers[m] == marker) {
							var tmpPath = tmpPolyLine.getPath();
							tmpPath.push(markers[m].getPosition());
							tmpPath.push(vmarkers[m].getPosition());
							tmpPath.push(markers[m+1].getPosition());
							break;
						}
					}
					m = null;
				});
				g.event.addListener(marker, "drag", function() {
					for (var m = 0; m < vmarkers.length; m++) {
						if (vmarkers[m] == marker) {
							tmpPolyLine.getPath().setAt(1, marker.getPosition());
							break;
						}
					}
					m = null;
				});
				g.event.addListener(marker, "dragend", function() {
					for (var m = 0; m < vmarkers.length; m++) {
						if (vmarkers[m] == marker) {
							var newpos = marker.getPosition();
							var startMarkerPos = markers[m].getPosition();
							var firstVPos = new g.LatLng(
								newpos.lat() - (0.5 * (newpos.lat() - startMarkerPos.lat())),
								newpos.lng() - (0.5 * (newpos.lng() - startMarkerPos.lng()))
							);
							var endMarkerPos = markers[m+1].getPosition();
							var secondVPos = new g.LatLng(
								newpos.lat() - (0.5 * (newpos.lat() - endMarkerPos.lat())),
								newpos.lng() - (0.5 * (newpos.lng() - endMarkerPos.lng()))
							);
							var newVMarker = createVMarker(secondVPos);
							newVMarker.setPosition(secondVPos);//apply the correct position to the vmarker
							var newMarker = createMarker(newpos);
							markers.splice(m+1, 0, newMarker);
							polyLine.getPath().insertAt(m+1, newpos);
							marker.setPosition(firstVPos);
							vmarkers.splice(m+1, 0, newVMarker);
							tmpPolyLine.getPath().removeAt(2);
							tmpPolyLine.getPath().removeAt(1);
							tmpPolyLine.getPath().removeAt(0);
							newpos = null;
							startMarkerPos = null;
							firstVPos = null;
							endMarkerPos = null;
							secondVPos = null;
							newVMarker = null;
							newMarker = null;
							break;
						}
					}
				});
				return marker;
			};
 
			var moveVMarker = function(index) {
				var newpos = markers[index].getPosition();
				if (index != 0) {
					var prevpos = markers[index-1].getPosition();
					vmarkers[index-1].setPosition(new g.LatLng(
						newpos.lat() - (0.5 * (newpos.lat() - prevpos.lat())),
						newpos.lng() - (0.5 * (newpos.lng() - prevpos.lng()))
					));
					prevpos = null;
				}
				if (index != markers.length - 1) {
					var nextpos = markers[index+1].getPosition();
					vmarkers[index].setPosition(new g.LatLng(
						newpos.lat() - (0.5 * (newpos.lat() - nextpos.lat())), 
						newpos.lng() - (0.5 * (newpos.lng() - nextpos.lng()))
					));
					nextpos = null;
				}
				newpos = null;
				index = null;
			};
 
			var removeVMarkers = function(index) {
				if (markers.length > 0) {//при клике на маркере он удаляется
					if (index != markers.length) {
						vmarkers[index].setMap(null);
						vmarkers.splice(index, 1);
					} else {
						vmarkers[index-1].setMap(null);
						vmarkers.splice(index-1, 1);
					}
				}
				if (index != 0 && index != markers.length) {
					var prevpos = markers[index-1].getPosition();
					var newpos = markers[index].getPosition();
					vmarkers[index-1].setPosition(new g.LatLng(
						newpos.lat() - (0.5 * (newpos.lat() - prevpos.lat())),
						newpos.lng() - (0.5 * (newpos.lng() - prevpos.lng()))
					));
					prevpos = null;
					newpos = null;
				}
				index = null;
			};
 
			window.onload = function() {
				initMap('mapcontainer');
				initPolyline();
			};		
 
</script> 
 
<style type="text/css"> 
      html, body { height: 100%; margin: 0; padding:0; }
	  #mapcontainer {width: 800px; height: 600px; border: 1px solid black;}	  
 </style>
 
</head> 
<body> 
<div id="mapcontainer"></div>
</html>

Поясню что в этом коде реализовано.

В начале задаются глобальные переменные и параметры для нашей карты.

Так же задается стиль для отображения полилинии.

В ходе кода отслеживаются различные события на карте.

При первом клике на карте ставиться маркер начала полилинии.

При следующих кликах рисуются участки полилинии и в их центре также добавляется маркер, но уже другого вида.

Кликнув по маркеру и удерживая левую кнопку мыши, мы можем изменять расположение узла полилинии и тем самым ее форму.

Удалить узел полилинии можно два раза кликнув по нему левой кнопкой мыши (удалять можно только маркеры на концах участков, но не в середине).

Теперь давайте изменим наш код так, чтобы можно было определить длину построенной полилинии.

Координаты всех узлов полилинии хранятся в массиве markers, нам необходимо определить расстояния между узлами и сложить их.

Это будет делать функция distance().

Ее исходный код:

function distance() {
 
	var dist = 0;
 
	if (markers.length > 0) {
	for (var im = 0; im < markers.length-1; im++) {	
	var marpos1 = markers[im].getPosition();
	var marpos2 = markers[im+1].getPosition();
 
	var R = 6371000; // km (коэффициент для определения расстояния между двумя точками в километрах)
	var dLat = (marpos2.lat()-marpos1.lat()) * Math.PI / 180;
	var dLon = (marpos2.lng()-marpos1.lng()) * Math.PI / 180;
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
		Math.cos(marpos1.lat() * Math.PI / 180 ) * Math.cos(marpos2.lat() * Math.PI / 180 ) *
		Math.sin(dLon/2) * Math.sin(dLon/2);
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	var d = R * c;
	dist = dist+d;
 
	}
 
	var distans = Math.round(dist/10)/100+" км";
	document.getElementById('rast').value = distans;	
	}
}

В ней мы в цикле перебираем все пары меток концов участков полилинии, определяем расстояние между ними и суммируем.

Полученное значение в километрах мы передаем поле input с id=»rast».

Окончательный код примера:

<!DOCTYPE html> 
<html> 
	<head> 
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
		<title>Google Maps api v3 - Рисуем полилинию и определяем ее длину</title> 
		<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> 
		<script type="text/javascript"> 
			var map = null;
			var polyLine;
			var tmpPolyLine;
			var markers = [];
			var vmarkers = [];
			var g = google.maps;
 
			var initMap = function(mapHolder) {
				markers = [];
				vmarkers = [];
				var mapOptions = {
					zoom: 14,
					center: new g.LatLng(56.316667, 44), 
					mapTypeId: g.MapTypeId.ROADMAP,
					draggableCursor: 'auto',
					draggingCursor: 'move',
					disableDoubleClickZoom: true
				};
				map = new g.Map(document.getElementById(mapHolder), mapOptions);
				g.event.addListener(map, "click", mapLeftClick);
				mapHolder = null;
				mapOptions = null;
			};
 
			var initPolyline = function() {
				var polyOptions = {
					strokeColor: "#ff0061",
					strokeOpacity: 0.8,
					strokeWeight: 4
				};
				var tmpPolyOptions = {
					strokeColor: "#ff0061",
					strokeOpacity: 0.4,
					strokeWeight: 4
				};
				polyLine = new g.Polyline(polyOptions);
				polyLine.setMap(map);
				tmpPolyLine = new g.Polyline(tmpPolyOptions);
				tmpPolyLine.setMap(map);
			};
 
			var mapLeftClick = function(event) {
				if (event.latLng) {
					var marker = createMarker(event.latLng);
					markers.push(marker);
					if (markers.length != 1) {
						var vmarker = createVMarker(event.latLng);
						vmarkers.push(vmarker);
						vmarker = null;
					}
					var path = polyLine.getPath();
					path.push(event.latLng);
					marker = null;
				}
				event = null;
			};
 
			var createMarker = function(point) {
				var imageNormal = new g.MarkerImage(
					"ris/square.png",
					new g.Size(11, 11),
					new g.Point(0, 0),
					new g.Point(6, 6)
				);
				var imageHover = new g.MarkerImage(
					"ris/square_over.png",
					new g.Size(11, 11),
					new g.Point(0, 0),
					new g.Point(6, 6)
				);
				var marker = new g.Marker({
					position: point,
					map: map,
					icon: imageNormal,
					draggable: true
				});
				g.event.addListener(marker, "mouseover", function() {
					marker.setIcon(imageHover);
				});
				g.event.addListener(marker, "mouseout", function() {
					marker.setIcon(imageNormal);
				});
				g.event.addListener(marker, "drag", function() {
					for (var m = 0; m < markers.length; m++) {
						if (markers[m] == marker) {
							polyLine.getPath().setAt(m, marker.getPosition());
							moveVMarker(m);
							break;
						}
					}
					m = null;
				});
				g.event.addListener(marker, "click", function() {
					for (var m = 0; m < markers.length; m++) {
						if (markers[m] == marker) {
							marker.setMap(null);
							markers.splice(m, 1);
							polyLine.getPath().removeAt(m);
							removeVMarkers(m);
							break;
						}
					}
					m = null;
				});
				return marker;
			};
 
			var createVMarker = function(point) {
				var prevpoint = markers[markers.length-2].getPosition();
				var imageNormal = new g.MarkerImage(
					"ris/square_transparent.png",
					new g.Size(11, 11),
					new g.Point(0, 0),
					new g.Point(6, 6)
				);
				var imageHover = new g.MarkerImage(
					"ris/square_transparent_over.png",
					new g.Size(11, 11),
					new g.Point(0, 0),
					new g.Point(6, 6)
				);
				var marker = new g.Marker({
					position: new g.LatLng(
						point.lat() - (0.5 * (point.lat() - prevpoint.lat())),
						point.lng() - (0.5 * (point.lng() - prevpoint.lng()))
					),
					map: map,
					icon: imageNormal,
					draggable: true
				});
				g.event.addListener(marker, "mouseover", function() {
					marker.setIcon(imageHover);
				});
				g.event.addListener(marker, "mouseout", function() {
					marker.setIcon(imageNormal);
				});
				g.event.addListener(marker, "dragstart", function() {
					for (var m = 0; m < vmarkers.length; m++) {
						if (vmarkers[m] == marker) {
							var tmpPath = tmpPolyLine.getPath();
							tmpPath.push(markers[m].getPosition());
							tmpPath.push(vmarkers[m].getPosition());
							tmpPath.push(markers[m+1].getPosition());
							break;
						}
					}
					m = null;
				});
				g.event.addListener(marker, "drag", function() {
					for (var m = 0; m < vmarkers.length; m++) {
						if (vmarkers[m] == marker) {
							tmpPolyLine.getPath().setAt(1, marker.getPosition());
							break;
						}
					}
					m = null;
				});
				g.event.addListener(marker, "dragend", function() {
					for (var m = 0; m < vmarkers.length; m++) {
						if (vmarkers[m] == marker) {
							var newpos = marker.getPosition();
							var startMarkerPos = markers[m].getPosition();
							var firstVPos = new g.LatLng(
								newpos.lat() - (0.5 * (newpos.lat() - startMarkerPos.lat())),
								newpos.lng() - (0.5 * (newpos.lng() - startMarkerPos.lng()))
							);
							var endMarkerPos = markers[m+1].getPosition();
							var secondVPos = new g.LatLng(
								newpos.lat() - (0.5 * (newpos.lat() - endMarkerPos.lat())),
								newpos.lng() - (0.5 * (newpos.lng() - endMarkerPos.lng()))
							);
							var newVMarker = createVMarker(secondVPos);
							newVMarker.setPosition(secondVPos);//apply the correct position to the vmarker
							var newMarker = createMarker(newpos);
							markers.splice(m+1, 0, newMarker);
							polyLine.getPath().insertAt(m+1, newpos);
							marker.setPosition(firstVPos);
							vmarkers.splice(m+1, 0, newVMarker);
							tmpPolyLine.getPath().removeAt(2);
							tmpPolyLine.getPath().removeAt(1);
							tmpPolyLine.getPath().removeAt(0);
							newpos = null;
							startMarkerPos = null;
							firstVPos = null;
							endMarkerPos = null;
							secondVPos = null;
							newVMarker = null;
							newMarker = null;
							break;
						}
					}
				});
				return marker;
			};
 
			var moveVMarker = function(index) {
				var newpos = markers[index].getPosition();
				if (index != 0) {
					var prevpos = markers[index-1].getPosition();
					vmarkers[index-1].setPosition(new g.LatLng(
						newpos.lat() - (0.5 * (newpos.lat() - prevpos.lat())),
						newpos.lng() - (0.5 * (newpos.lng() - prevpos.lng()))
					));
					prevpos = null;
				}
				if (index != markers.length - 1) {
					var nextpos = markers[index+1].getPosition();
					vmarkers[index].setPosition(new g.LatLng(
						newpos.lat() - (0.5 * (newpos.lat() - nextpos.lat())), 
						newpos.lng() - (0.5 * (newpos.lng() - nextpos.lng()))
					));
					nextpos = null;
				}
				newpos = null;
				index = null;
			};
 
			var removeVMarkers = function(index) {
				if (markers.length > 0) {//при клике на маркере он удаляется
					if (index != markers.length) {
						vmarkers[index].setMap(null);
						vmarkers.splice(index, 1);
					} else {
						vmarkers[index-1].setMap(null);
						vmarkers.splice(index-1, 1);
					}
				}
				if (index != 0 && index != markers.length) {
					var prevpos = markers[index-1].getPosition();
					var newpos = markers[index].getPosition();
					vmarkers[index-1].setPosition(new g.LatLng(
						newpos.lat() - (0.5 * (newpos.lat() - prevpos.lat())),
						newpos.lng() - (0.5 * (newpos.lng() - prevpos.lng()))
					));
					prevpos = null;
					newpos = null;
				}
				index = null;
			};
 
			window.onload = function() {
				initMap('mapcontainer');
				initPolyline();
			};
 
//Функция для определения длины полилинии		
function distance() {
 
	var dist = 0;
 
	if (markers.length > 0) {
	for (var im = 0; im < markers.length-1; im++) {	
	var marpos1 = markers[im].getPosition();
	var marpos2 = markers[im+1].getPosition();
 
	var R = 6371000; // km (коэффициент для определения расстояния между двумя точками в километрах)
	var dLat = (marpos2.lat()-marpos1.lat()) * Math.PI / 180;
	var dLon = (marpos2.lng()-marpos1.lng()) * Math.PI / 180;
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
		Math.cos(marpos1.lat() * Math.PI / 180 ) * Math.cos(marpos2.lat() * Math.PI / 180 ) *
		Math.sin(dLon/2) * Math.sin(dLon/2);
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	var d = R * c;
	dist = dist+d;
 
	}
 
	var distans = Math.round(dist/10)/100+" км";
	document.getElementById('rast').value = distans;	
	}
}			
 
</script> 
 
<style type="text/css"> 
      html, body { height: 100%; margin: 0; padding:0; }
	  #mapcontainer {width: 800px; height: 600px; border: 1px solid black;}
	  #distanse { margin-top: 15px;}
	  #rast {width: 50px;}
 </style>
 
</head> 
<body> 
<div id="mapcontainer"></div>
<div id="distanse"><input type="button" onclick="distance();" value="Определить расстояние"/></div>
<p>Длина полилиниии равна: <input id="rast" type="text"></p>
	</body> 
</html>

Посмотреть работу примера в действии

Мы рисуем полилинию, последовательно задавая ее участки, для определения длины нажимаем на кнопку «Определить расстояние» в низу карты и в поле «Длина полилиниии равна:» появляется значение расстояния в километрах.

  • Гость: Неплохой пример, но по моему сильно наворочен.... Можно ли сделать пример с прямой (что-то типа измерения расстояния с одним участком, и выводом информации например в tooltip)? Он по моему будет проще в понимании...
  • Гость: Есть <a href="http://tech.cibul.net/drag-and-drop-two-markers-linked-with-a-line-on-google-maps-v3/" rel="nofollow">статья с примером</a>, правда на английском. В ней как раз делается пример с измерением расстояния между двумя точками.
  • Гость: Здравствуйте. Подскажите пожалуйста, каким образом можно добавить на Polylines "стрелочки" направления? На карту загрузил KML-маршрут, но нужны еще указатели. Спасибо.
  • Гость: Admin, ввиду актуальности и по личной просьбе прошу написать статью похожую на эту, но для другого компонента : "Заменяем карты Google Maps на Яндекс.Карты в компоненте EZ Realty - Realty Map для Joomla 1.5"
  • Гость: Хотелось бы, чтобы этот пример был дополнен кнопкой и функцией по сбросу и удалению всех точек перед созданием новой полилинии без перезагрузки всей карты.