I've successfully implemented google map direction service api : https://developers.google.com/maps/documentation/javascript/directions with 'draggble' option enabled. Is it possible to show all routes together if multiple routes are available between 2 locations?
The current code is similar to: https://developers.google.com/maps/documentation/javascript/examples/directions-draggable and I do have alternative routes available in response code as I've enabled provideRouteAlternatives: true.
I tried the solution provided in : How to display alternative route using google map api. But when I used that code, I found it draws multiple routes with independent markers. That is, if 4 routes are available, there will be 4 'A' locations and 4 'B' locations and while dragging - only one of them get selected. Please find the below screenshots.
Initial View:
After dragging initial locations (issue with duplicate locations)
I need to drag in such a way that, when the location A or B is dragged, there should not be any duplicates and alternate routes should be automatically shown.
My current code is as follows (API key not added here):
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Draggable directions</title>
<style>
#right-panel {
font-family: 'Roboto','sans-serif';
line-height: 30px;
padding-left: 10px;
}
#right-panel select, #right-panel input {
font-size: 15px;
}
#right-panel select {
width: 100%;
}
#right-panel i {
font-size: 12px;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
height: 100%;
float: left;
width: 63%;
height: 100%;
}
#right-panel {
float: right;
width: 34%;
height: 100%;
}
.panel {
height: 100%;
overflow: auto;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="right-panel">
<p>Total Distance: <span id="total"></span></p>
</div>
<script>
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: {lat: -24.345, lng: 134.46} // Australia.
});
var directionsService = new google.maps.DirectionsService;
var directionsDisplay = new google.maps.DirectionsRenderer({
draggable: true,
map: map,
panel: document.getElementById('right-panel')
});
directionsDisplay.addListener('directions_changed', function() {
computeTotalDistance(directionsDisplay.getDirections());
});
displayRoute('Rosedale, MD, USA', 'Savage, MD, USA', directionsService,
directionsDisplay);
}
function displayRoute(origin, destination, service, display) {
service.route({
origin: origin,
destination: destination,
travelMode: 'DRIVING',
avoidTolls: true,
provideRouteAlternatives: true,
}, function(response, status) {
if (status === 'OK') {
for (var i = 0, len = response.routes.length; i < len; i++) {
new google.maps.DirectionsRenderer({
map: map,
directions: response,
routeIndex: i,
draggable : true,
});
}
display.setDirections(response);
} else {
alert('Could not display directions due to: ' + status);
}
});
}
function computeTotalDistance(result) {
var total = 0;
var myroute = result.routes[0];
for (var i = 0; i < myroute.legs.length; i++) {
total += myroute.legs[i].distance.value;
}
total = total / 1000;
document.getElementById('total').innerHTML = total + ' km';
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=API-KEY&callback=initMap">
</script>
</body>
</html>
Please help me. Thanks in advance!
Each route you draw is editable and has a start and end marker which means that you will always be dragging one of the routes marker and not all at once. You could use the suppressMarkers option to remove markers from the alternate routes but that wouldn't change a lot to the behavior.
The problem is that as each route is separate, if you move the start or end location, you should redraw every route otherwise only one will update.
That said, if you edit a route (other than by changing its start or end location) you should not redraw your routes. If you update the start or end location you will of course then lose any route waypoint you added. Unless you do something about that... which sounds like a pain.
var directionsService = new google.maps.DirectionsService();
var map;
function initialize() {
var center = new google.maps.LatLng(0, 0);
var myOptions = {
zoom: 7,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: center
}
map = new google.maps.Map(document.getElementById("map-canvas"), myOptions);
var start = "Sadulpur, India";
var end = "New Delhi, India";
plotDirections(start, end);
}
function plotDirections(start, end) {
var method = 'DRIVING';
var request = {
origin: start,
destination: end,
travelMode: google.maps.DirectionsTravelMode[method],
provideRouteAlternatives: true
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var routes = response.routes;
var colors = ['red', 'green', 'blue', 'orange', 'yellow', 'black'];
var directionsDisplays = [];
// Reset the start and end variables to the actual coordinates
start = response.routes[0].legs[0].start_location;
end = response.routes[0].legs[0].end_location;
// Loop through each route
for (var i = 0; i < routes.length; i++) {
var directionsDisplay = new google.maps.DirectionsRenderer({
map: map,
directions: response,
routeIndex: i,
draggable: true,
polylineOptions: {
strokeColor: colors[i],
strokeWeight: 4,
strokeOpacity: .3
}
});
// Push the current renderer to an array
directionsDisplays.push(directionsDisplay);
// Listen for the directions_changed event for each route
google.maps.event.addListener(directionsDisplay, 'directions_changed', (function(directionsDisplay, i) {
return function() {
var directions = directionsDisplay.getDirections();
var new_start = directions.routes[0].legs[0].start_location;
var new_end = directions.routes[0].legs[0].end_location;
if ((new_start.toString() !== start.toString()) || (new_end.toString() !== end.toString())) {
// Remove every route from map
for (var j = 0; j < directionsDisplays.length; j++) {
directionsDisplays[j].setMap(null);
}
// Redraw routes with new start/end coordinates
plotDirections(new_start, new_end);
}
}
})(directionsDisplay, i)); // End listener
} // End route loop
}
});
}
initialize();
#map-canvas {
height: 180px;
}
<div id="map-canvas"></div>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
JSFiddle demo
Related
Currently I'm working on a direction based project and I want to push my direction to the right side when rendered.
This is what I'm getting.
I don't want to get directions behind the panel.
This is what I want to get.
Direction between two point to the right side.
My code to render directions between two points is as follows
renderDirections(): void {
var placeID = this.destinationPlaceId.getPlace();
console.log(placeID.place_id);
this.destinationPlaceId = placeID.place_id;
let directionsService = new google.maps.DirectionsService;
let directionsRenderer = new google.maps.DirectionsRenderer({
preserveViewport: false
});
initMap();
directionsRenderer.setMap(map);
placeID = this.startPlaceID.getPlace();
console.log(placeID.place_id);
this.originPlaceId = placeID.place_id;
directionsService.route(
{
origin: { 'placeId': this.originPlaceId },
destination: { 'placeId': this.destinationPlaceId },
travelMode: google.maps.TravelMode[this.travelMode]
},
function (response, status) {
if (status === 'OK') {
directionsRenderer.setDirections(response);
} else {
window.alert('Directions request failed due to ' + status);
window.location.reload();
}
});
}
There is no ready-made function to achieve this but it is feasible, as long as you know the width (and position) of your top layer.
Here is how you can do it:
Plot a route on the map and listen for the map idle event before starting with the offset process.
Check the leftmost point of the route bounds to see if it falls behind the overlay. This makes use of the fromLatLngToPoint() method to translate from lat/lng coordinates to a point on the map projection.
Check how much you can offset the route by comparing the leftmost and rightmost points of the route with the available space on the map. Offset the map until both points fit on the available canvas space.
If both points cannot fit within the map canvas, zoom out and start the same process again.
The script must be aware of the width of the overlay and you should apply some margins so that it always fits well.
Working code snippet:
var directionsDisplay;
var directionsService;
var start, end;
var map;
var routeBounds = false;
var overlayWidth = 200; // Width of the overlay DIV
var leftMargin = 30; // Grace margin to avoid too close fits on the edge of the overlay
var rightMargin = 80; // Grace margin to avoid too close fits on the right and leave space for the controls
overlayWidth += leftMargin;
function initialize() {
directionsService = new google.maps.DirectionsService();
start = new google.maps.LatLng(48.857380, 2.351717);
end = new google.maps.LatLng(50.108814, 8.672309);
var btn1 = document.getElementById('calcRoute');
btn1.addEventListener('click', calcRoute);
var btn2 = document.getElementById('offsetMap');
btn2.addEventListener('click', offsetMap);
var btn3 = document.getElementById('fitAndOffsetMap');
btn3.addEventListener('click', fitAndOffsetMap);
var btn4 = document.getElementById('fitMap');
btn4.addEventListener('click', fitMap);
directionsDisplay = new google.maps.DirectionsRenderer({
draggable: true
});
var mapOptions = {
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: start,
panControlOptions: {
position: google.maps.ControlPosition.TOP_RIGHT
},
zoomControlOptions: {
position: google.maps.ControlPosition.TOP_RIGHT
}
};
map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
directionsDisplay.setMap(map);
}
function offsetMap() {
if (routeBounds !== false) {
// Clear listener defined in directions results
google.maps.event.clearListeners(map, 'idle');
// Top right corner
var topRightCorner = new google.maps.LatLng(map.getBounds().getNorthEast().lat(), map.getBounds().getNorthEast().lng());
// Top right point
var topRightPoint = fromLatLngToPoint(topRightCorner).x;
// Get pixel position of leftmost and rightmost points
var leftCoords = routeBounds.getSouthWest();
var leftMost = fromLatLngToPoint(leftCoords).x;
var rightMost = fromLatLngToPoint(routeBounds.getNorthEast()).x;
// Calculate left and right offsets
var leftOffset = (overlayWidth - leftMost);
var rightOffset = ((topRightPoint - rightMargin) - rightMost);
// Only if left offset is needed
if (leftOffset >= 0) {
if (leftOffset < rightOffset) {
var mapOffset = Math.round((rightOffset - leftOffset) / 2);
// Pan the map by the offset calculated on the x axis
map.panBy(-mapOffset, 0);
// Get the new left point after pan
var newLeftPoint = fromLatLngToPoint(leftCoords).x;
if (newLeftPoint <= overlayWidth) {
// Leftmost point is still under the overlay
// Offset map again
offsetMap();
}
} else {
// Cannot offset map at this zoom level otherwise both leftmost and rightmost points will not fit
// Zoom out and offset map again
map.setZoom(map.getZoom() - 1);
offsetMap();
}
}
}
}
function fromLatLngToPoint(latLng) {
var scale = Math.pow(2, map.getZoom());
var nw = new google.maps.LatLng(map.getBounds().getNorthEast().lat(), map.getBounds().getSouthWest().lng());
var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw);
var worldCoordinate = map.getProjection().fromLatLngToPoint(latLng);
return new google.maps.Point(Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale), Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale));
}
function calcRoute() {
var request = {
origin: start,
destination: end,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
// Define route bounds for use in offsetMap function
routeBounds = response.routes[0].bounds;
// Write directions steps
writeDirectionsSteps(response.routes[0].legs[0].steps);
// Wait for map to be idle before calling offsetMap function
google.maps.event.addListener(map, 'idle', function () {
// Offset map
offsetMap();
});
// Listen for directions changes to update bounds and reapply offset
google.maps.event.addListener(directionsDisplay, 'directions_changed', function () {
// Get the updated route directions response
var updatedResponse = directionsDisplay.getDirections();
// Update route bounds
routeBounds = updatedResponse.routes[0].bounds;
// Fit updated bounds
map.fitBounds(routeBounds);
// Write directions steps
writeDirectionsSteps(updatedResponse.routes[0].legs[0].steps);
// Offset map
offsetMap();
});
}
});
}
function writeDirectionsSteps(steps) {
var overlayContent = document.getElementById("overlayContent");
overlayContent.innerHTML = '';
for (var i = 0; i < steps.length; i++) {
overlayContent.innerHTML += '<p>' + steps[i].instructions + '</p><small>' + steps[i].distance.text + '</small>';
}
}
function fitMap() {
if (routeBounds !== false) {
map.fitBounds(routeBounds);
}
}
function fitAndOffsetMap() {
if (routeBounds !== false) {
map.fitBounds(routeBounds);
offsetMap();
}
}
body {
margin: 0;
padding: 0;
font-family: Arial, Helvetica, sans-serif;
}
#map-canvas {
height: 160px;
}
#overlay {
position: absolute;
width: 200px;
height: 160px;
background: black;
opacity: 0.8;
top: 0;
left: 0;
overflow: auto;
}
#overlayContent {
color: white;
padding: 10px 20px;
}
#overlayContent p {
font-size: 12px;
margin: 6px 0;
}
small {
font-size: 9px;
}
#overlayContent small {
display: block;
text-align: right;
font-style: italic;
}
i {
color: lightblue;
}
h1 {
font-size: 20px;
}
h5 {
font-size: 12px;
}
button {
margin: 20px 0 0 20px;
}
<div id="map-canvas"></div>
<div id="overlay">
<div id="overlayContent">
<h1>DIV OVERLAY</h1>
<h5>Routes should not be drawn below this element.</h5>
<h5>Click the <i>Calc route</i> button to draw the directions route.</h5>
<h5><i>Map offset</i> will be applied automatically.</h5>
<h5><i>Drag the route</i> to see how it is applied.</h5>
<h5>Click the <i>Offset map</i> button to reapply the offset.</h5>
<h5>Click the <i>Fit only</i> button to only fit route bounds.</h5>
<h5>Click the <i>Fit and offset map</i> button to fit to route bounds and reapply offset.</h5>
</div>
</div>
<button id="calcRoute">Calc route</button>
<button id="offsetMap">Offset map</button>
<button id="fitMap">Fit only</button>
<button id="fitAndOffsetMap">Fit and offset map</button>
<!-- Replace the value of the key parameter with your own API key. -->
<script async defer src="//maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initialize">
</script>
Working fiddle:
You can play with it on JSFiddle as it is easier to see the behavior, resize the window, etc.
I have tried many ways and many places to enter the API key for but in all the cases, I get the error no-API-key. the request works fine in the postman, meaning the key works fine.
the places I tried:
where I mentioned in this code
at the end of snap to road request call
at the end of all the request calls
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Roads API Demo</title>
<style>
html, body, #map {
height: 100%;
margin: 0px;
padding: 0px
}
#panel {
position: absolute;
top: 5px;
left: 50%;
margin-left: -180px;
z-index: 5;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
}
#bar {
width: 240px;
background-color: rgba(255, 255, 255, 0.75);
margin: 8px;
padding: 4px;
border-radius: 4px;
}
#autoc {
width: 100%;
box-sizing: border-box;
}
</style>
<script src="/_static/js/jquery-bundle.js"></script>
<script
src="https://maps.googleapis.com/maps/api/js?libraries=drawing,places"></script>
<script>
var apiKey = 'I PUTTED THE KEY HERE IN THE LAST TRY';
var map;
var drawingManager;
var placeIdArray = [];
var polylines = [];
var snappedCoordinates = [];
function initialize() {
var mapOptions = {
zoom: 17,
center: {lat: -33.8667, lng: 151.1955}
};
map = new google.maps.Map(document.getElementById('map'), mapOptions);
// Adds a Places search box. Searching for a place will center the map on that
// location.
map.controls[google.maps.ControlPosition.RIGHT_TOP].push(
document.getElementById('bar'));
var autocomplete = new google.maps.places.Autocomplete(
document.getElementById('autoc'));
autocomplete.bindTo('bounds', map);
autocomplete.addListener('place_changed', function() {
var place = autocomplete.getPlace();
if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(17);
}
});
// Enables the polyline drawing control. Click on the map to start drawing a
// polyline. Each click will add a new vertice. Double-click to stop drawing.
drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYLINE,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [
google.maps.drawing.OverlayType.POLYLINE
]
},
polylineOptions: {
strokeColor: '#696969',
strokeWeight: 2
}
});
drawingManager.setMap(map);
// Snap-to-road when the polyline is completed.
drawingManager.addListener('polylinecomplete', function(poly) {
var path = poly.getPath();
polylines.push(poly);
placeIdArray = [];
runSnapToRoad(path);
});
// Clear button. Click to remove all polylines.
$('#clear').click(function(ev) {
for (var i = 0; i < polylines.length; ++i) {
polylines[i].setMap(null);
}
polylines = [];
ev.preventDefault();
return false;
});
}
// Snap a user-created polyline to roads and draw the snapped path
function runSnapToRoad(path) {
var pathValues = [];
for (var i = 0; i < path.getLength(); i++) {
pathValues.push(path.getAt(i).toUrlValue());
}
$.get('https://roads.googleapis.com/v1/snapToRoads', {
interpolate: true,
key: apiKey,
path: pathValues.join('|')
}, function(data) {
processSnapToRoadResponse(data);
drawSnappedPolyline();
getAndDrawSpeedLimits();
});
}
// Store snapped polyline returned by the snap-to-road service.
function processSnapToRoadResponse(data) {
snappedCoordinates = [];
placeIdArray = [];
for (var i = 0; i < data.snappedPoints.length; i++) {
var latlng = new google.maps.LatLng(
data.snappedPoints[i].location.latitude,
data.snappedPoints[i].location.longitude);
snappedCoordinates.push(latlng);
placeIdArray.push(data.snappedPoints[i].placeId);
}
}
// Draws the snapped polyline (after processing snap-to-road response).
function drawSnappedPolyline() {
var snappedPolyline = new google.maps.Polyline({
path: snappedCoordinates,
strokeColor: 'black',
strokeWeight: 3
});
snappedPolyline.setMap(map);
polylines.push(snappedPolyline);
}
// Gets speed limits (for 100 segments at a time) and draws a polyline
// color-coded by speed limit. Must be called after processing snap-to-road
// response.
function getAndDrawSpeedLimits() {
for (var i = 0; i <= placeIdArray.length / 100; i++) {
// Ensure that no query exceeds the max 100 placeID limit.
var start = i * 100;
var end = Math.min((i + 1) * 100 - 1, placeIdArray.length);
drawSpeedLimits(start, end);
}
}
// Gets speed limits for a 100-segment path and draws a polyline color-coded by
// speed limit. Must be called after processing snap-to-road response.
function drawSpeedLimits(start, end) {
var placeIdQuery = '';
for (var i = start; i < end; i++) {
placeIdQuery += '&placeId=' + placeIdArray[i];
}
$.get('https://roads.googleapis.com/v1/speedLimits',
'key=' + apiKey + placeIdQuery,
function(speedData) {
processSpeedLimitResponse(speedData, start);
}
);
}
// Draw a polyline segment (up to 100 road segments) color-coded by speed limit.
function processSpeedLimitResponse(speedData, start) {
var end = start + speedData.speedLimits.length;
for (var i = 0; i < speedData.speedLimits.length - 1; i++) {
var speedLimit = speedData.speedLimits[i].speedLimit;
var color = getColorForSpeed(speedLimit);
// Take two points for a single-segment polyline.
var coords = snappedCoordinates.slice(start + i, start + i + 2);
var snappedPolyline = new google.maps.Polyline({
path: coords,
strokeColor: color,
strokeWeight: 6
});
snappedPolyline.setMap(map);
polylines.push(snappedPolyline);
}
}
function getColorForSpeed(speed_kph) {
if (speed_kph <= 40) {
return 'purple';
}
if (speed_kph <= 50) {
return 'blue';
}
if (speed_kph <= 60) {
return 'green';
}
if (speed_kph <= 80) {
return 'yellow';
}
if (speed_kph <= 100) {
return 'orange';
}
return 'red';
}
$(window).load(initialize);
</script>
</head>
<body>
<div id="map"></div>
<div id="bar">
<p class="auto"><input type="text" id="autoc"/></p>
<p><a id="clear" href="#">Click here</a> to clear map.</p>
</div>
</body>
</html>
In the script that loads the JS api
<script src="https://maps.googleapis.com/maps/api/js?libraries=drawing,places&key=your_api_key"></script>
and for Web Services
https://roads.googleapis.com/v1/speedLimits?parameters&key=YOUR_API_KEY
I have been working in this code, and works fine in localhost, but when I try to put this in word press the map doesn't come, and I don't know how to fix case I am new in WordPress.
A really need a hand on this, thanks
var geocoder;
function initMap() {
GeolocateUser();
var enderesso, Dest_location;
var UserLocation;
var markers = [];
var origin_place_id = null;
var destination_place_id = null;
var travel_mode = google.maps.TravelMode.DRIVING;
var map = new google.maps.Map(document.getElementById('map'), {
mapTypeControl: false,
zoom: 13
});
geocoder = new google.maps.Geocoder();
var directionsService = new google.maps.DirectionsService;
var directionsDisplay = new google.maps.DirectionsRenderer({
draggable: true,
map: map,
});
directionsDisplay.setMap(map);
var origin_input = document.getElementById('origin-input');
var destination_input = document.getElementById('destination-input');
var total_distance = document.getElementById('total-distance');
var total_duration = document.getElementById('total-duration');
map.controls[google.maps.ControlPosition.TOP_LEFT].push(origin_input);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(destination_input);
var origin_autocomplete = new google.maps.places.Autocomplete(origin_input);
origin_autocomplete.bindTo('bounds', map);
var destination_autocomplete = new google.maps.places.Autocomplete(destination_input);
destination_autocomplete.bindTo('bounds', map);
// On Change route action
directionsDisplay.addListener('directions_changed', function(){
total_distance.value = getDistance(directionsDisplay.getDirections());
total_duration.value = getDuration(directionsDisplay.getDirections());
});
// On click map action
map.addListener('click',function(e){
clearMarkers();
if (!UserLocation) {
UserLocation=e.latLng;
addMarker(e.latLng);
route();
}else{
Dest_location=e.latLng;
route();
}
});
origin_autocomplete.addListener('place_changed', function() {
var place = origin_autocomplete.getPlace();
if (!place.geometry) {
window.alert("Autocomplete's returned place contains no geometry");
return;
}
clearMarkers();
expandViewportToFitPlace(map, place);
// If the place has a geometry, store its place ID and route if we have
// the other place ID
origin_place_id = place.place_id;
UserLocation = place.geometry.location;
addMarker(UserLocation);
route();
});
destination_autocomplete.addListener('place_changed', function() {
var place = destination_autocomplete.getPlace();
if (!place.geometry) {
window.alert("Autocomplete's returned place contains no geometry");
return;
}
expandViewportToFitPlace(map, place);
clearMarkers();
// If the place has a geometry, store its place ID and route if we have
// the other place ID
Dest_location = place.geometry.location;
destination_place_id = place.place_id;
route();
});
function route() {
if (!UserLocation || !Dest_location) {
return;
}
clearMarkers();
directionsService.route({
origin: UserLocation ,
destination: Dest_location,
travelMode: travel_mode
}, function(response, status) {
if (status === google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
} else {
window.alert('Directions request failed due to ' + status);
}
});
}
// Adds a marker to the map and push to the array.
function addMarker(location) {
var marker = new google.maps.Marker({
position:location,
map: map
});
markers.push(marker);
}
// Sets the map on all markers in the array.
function setMapOnAll(map) {
for (var i = 0; i < markers.length; i++) {
markers[i].setMap(map);
}
}
// Removes the markers from the map, but keeps them in the array.
function clearMarkers() {
setMapOnAll(null);
}
// Deletes all markers in the array by removing references to them.
function deleteMarkers() {
clearMarkers();
markers = [];
}
//Get Distance
function getDistance(result){
var d = 0;
var myroute = result.routes[0];
for (var i = 0; i < myroute.legs.length; i++) {
d += myroute.legs[i].distance.value;
}
d=(d/1000);
return d;
}
//Get duration
function getDuration(result){
var time = 0;
var myroute = result.routes[0];
for (var i = 0; i < myroute.legs.length; i++) {
time += myroute.legs[i].duration.value;
}
time =(time/60)+10;
time = time.toFixed(0);
return time;
}
//Get full adress
function getAddress(latLng) {
var edress;
geocoder.geocode( {'latLng': latLng},
function(results, status) {
if(status == google.maps.GeocoderStatus.OK) {
if(results[0]) {
adress = results[0].formatted_address;
}
else {
adress = "No results";
}
}
else {
adress = status;
}
});
return adress;
}
//Geolocate User
function GeolocateUser(){
UserLocation = new google.maps.LatLng(0.0,0.0);
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position){
UserLocation = new google.maps.LatLng(position.coords.latitude+0, position.coords.longitude+0);
addMarker(UserLocation);
map.setCenter(UserLocation);
});
}
}
//View Point Simple
function expandViewportToFitPlace(map, place) {
if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(11);
}
}
}
<!DOCTYPE html>
<html>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
height: 100%;
}
.controls {
margin-top: 10px;
border: 1px solid transparent;
border-radius: 2px 0 0 2px;
box-sizing: border-box;
-moz-box-sizing: border-box;
height: 32px;
outline: none;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
#origin-input,
#destination-input {
background-color: #fff;
font-family: Roboto;
font-size: 15px;
font-weight: 300;
margin-left: 12px;
padding: 0 11px 0 13px;
text-overflow: ellipsis;
width: 200px;
}
#origin-input:focus,
#destination-input:focus {
border-color: #4d90fe;
}
#mode-selector {
color: #fff;
background-color: #4d90fe;
margin-left: 12px;
padding: 5px 11px 0px 11px;
}
#mode-selector label {
font-family: Roboto;
font-size: 13px;
font-weight: 300;
}
</style>
<input id="origin-input" class="controls" type="text"
placeholder="Enter an origin location">
<input id="destination-input" class="controls" type="text"
>
<input type="text" id="total-distance" placeholder="Calculating.."/>
<input type="text" id="total-duration" placeholder="Calculating.."/>
<div id="map"></div>
<script src="main.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?signed_in=true&libraries=places&callback=initMap" async defer></script>
</html>
I found a gist that enables drawing tools and gives the ability to choose from a few colors to draw on a google map: https://gist.github.com/Hagith/5765919
I'm trying to utilize this with socket.io so that multiple people can be viewing a map, and when one person draws something onto the map, all of the people can see what is drawn.
I've got the basic idea down with markers by doing
socket.emit("marker", e.overlay.position);
When a marker is placed, however, with rectangles, polygons, and circles, it seems a bit harder. When I log out the click event on the map with any of those shapes, the data that it gives back seems way more complicated than what it gave back with marker and I can't find the coordinates for the points that I need to be able to broadcast to the other users. Does anyone know where to find these in the context of the gist above?
Edit: I've been able to find the center with e.overlay.j.center
It is not recommended to utilize those kind of properties (e.overlay.j) since they are not intended for public access and there is no guarantee that they will not change in the next version of Google Maps JavaScript API.
For google.maps.drawing.OverlayType.RECTANGLE and google.maps.drawing.OverlayType.CIRCLE types you could utilize getBounds() function to determine the lat/lng bounds of the current shape as demonstrated below:
//get lat/lng bounds of the current shape
var bounds = e.overlay.getBounds();
var start = bounds.getNorthEast();
var end = bounds.getSouthWest();
var center = bounds.getCenter();
For google.maps.drawing.OverlayType.POLYLINE and google.maps.drawing.OverlayType.POLYGON types you could utilize getPath() function:
//get lat/lng array of the current shape
var locations = e.overlay.getPath().getArray()
Modified example
var drawingManager;
var selectedShape;
var colors = ['#1E90FF', '#FF1493', '#32CD32', '#FF8C00', '#4B0082'];
var selectedColor;
var colorButtons = {};
function clearSelection() {
if (selectedShape) {
selectedShape.setEditable(false);
selectedShape = null;
}
}
function setSelection(shape) {
clearSelection();
selectedShape = shape;
shape.setEditable(true);
selectColor(shape.get('fillColor') || shape.get('strokeColor'));
}
function deleteSelectedShape() {
if (selectedShape) {
selectedShape.setMap(null);
}
}
function selectColor(color) {
selectedColor = color;
for (var i = 0; i < colors.length; ++i) {
var currColor = colors[i];
colorButtons[currColor].style.border = currColor == color ? '2px solid #789' : '2px solid #fff';
}
// Retrieves the current options from the drawing manager and replaces the
// stroke or fill color as appropriate.
var polylineOptions = drawingManager.get('polylineOptions');
polylineOptions.strokeColor = color;
drawingManager.set('polylineOptions', polylineOptions);
var rectangleOptions = drawingManager.get('rectangleOptions');
rectangleOptions.fillColor = color;
drawingManager.set('rectangleOptions', rectangleOptions);
var circleOptions = drawingManager.get('circleOptions');
circleOptions.fillColor = color;
drawingManager.set('circleOptions', circleOptions);
var polygonOptions = drawingManager.get('polygonOptions');
polygonOptions.fillColor = color;
drawingManager.set('polygonOptions', polygonOptions);
}
function setSelectedShapeColor(color) {
if (selectedShape) {
if (selectedShape.type == google.maps.drawing.OverlayType.POLYLINE) {
selectedShape.set('strokeColor', color);
} else {
selectedShape.set('fillColor', color);
}
}
}
function makeColorButton(color) {
var button = document.createElement('span');
button.className = 'color-button';
button.style.backgroundColor = color;
google.maps.event.addDomListener(button, 'click', function () {
selectColor(color);
setSelectedShapeColor(color);
});
return button;
}
function buildColorPalette() {
var colorPalette = document.getElementById('color-palette');
for (var i = 0; i < colors.length; ++i) {
var currColor = colors[i];
var colorButton = makeColorButton(currColor);
colorPalette.appendChild(colorButton);
colorButtons[currColor] = colorButton;
}
selectColor(colors[0]);
}
function initialize() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 16,
center: new google.maps.LatLng(52.25097, 20.97114),
mapTypeId: google.maps.MapTypeId.SATELLITE,
disableDefaultUI: true,
zoomControl: true
});
var polyOptions = {
strokeWeight: 0,
fillOpacity: 0.45,
editable: true,
draggable: true
};
// Creates a drawing manager attached to the map that allows the user to draw
// markers, lines, and shapes.
drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYGON,
markerOptions: {
draggable: true
},
polylineOptions: {
editable: true,
draggable: true
},
rectangleOptions: polyOptions,
circleOptions: polyOptions,
polygonOptions: polyOptions,
map: map
});
google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) {
if (e.type !== google.maps.drawing.OverlayType.MARKER) {
// Switch back to non-drawing mode after drawing a shape.
drawingManager.setDrawingMode(null);
// Add an event listener that selects the newly-drawn shape when the user
// mouses down on it.
var newShape = e.overlay;
newShape.type = e.type;
google.maps.event.addListener(newShape, 'click', function (e) {
if (e.vertex !== undefined) {
if (newShape.type === google.maps.drawing.OverlayType.POLYGON) {
var path = newShape.getPaths().getAt(e.path);
path.removeAt(e.vertex);
if (path.length < 3) {
newShape.setMap(null);
}
}
if (newShape.type === google.maps.drawing.OverlayType.POLYLINE) {
var path = newShape.getPath();
path.removeAt(e.vertex);
if (path.length < 2) {
newShape.setMap(null);
}
}
}
setSelection(newShape);
});
setSelection(newShape);
if (e.type == google.maps.drawing.OverlayType.POLYLINE || google.maps.drawing.OverlayType.POLYGON) {
var locations = e.overlay.getPath().getArray()
//console.log(bounds.toString());
document.getElementById('output').innerHTML = locations.toString();
}
else {
//get lat/lng bounds of the current shape
var bounds = e.overlay.getBounds();
var start = bounds.getNorthEast();
var end = bounds.getSouthWest();
var center = bounds.getCenter();
//console.log(bounds.toString());
document.getElementById('output').innerHTML = bounds.toString();
}
}
});
// Clear the current selection when the drawing mode is changed, or when the
// map is clicked.
google.maps.event.addListener(drawingManager, 'drawingmode_changed', clearSelection);
google.maps.event.addListener(map, 'click', clearSelection);
google.maps.event.addDomListener(document.getElementById('delete-button'), 'click', deleteSelectedShape);
buildColorPalette();
}
google.maps.event.addDomListener(window, 'load', initialize);
#map, html, body {
padding: 0;
margin: 0;
width: 960px;
height: 300px;
}
#panel {
width: 200px;
font-family: Arial, sans-serif;
font-size: 13px;
float: right;
margin: 10px;
}
#color-palette {
clear: both;
}
.color-button {
width: 14px;
height: 14px;
font-size: 0;
margin: 2px;
float: left;
cursor: pointer;
}
#delete-button {
margin-top: 5px;
}
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false&libraries=drawing"></script>
<div id="panel">
<div id="color-palette"></div>
<div>
<button id="delete-button">Delete Selected Shape</button>
</div>
</div>
<div id="map"></div>
<div id="output"></div>
Edit: I've been able to find the center with e.overlay.j.center
You want to be VERY careful using the single-letter properties you can see in the browser debug tools. They are not documented, or static, and will change without warning.
To answer the actual question - the type of e.overlay depends on what you've initialised the DrawingManager with, see the docs here. So if you're drawing polygons, e.overlay will be of type Polygon. You can then get the points that make up that Polygon using e.overlay.getPath(0).getArray(), which gives you an array of LatLng objects. (obviously loop over all the available paths, not just 0).
There's a good example here which shows switching behaviour based on what type of geometry the overlay is returned as.
getCoords(someShape){
const paths = someShape.getPath().getArray();
const coords = paths.map((a) => [a.lat(), a.lng()]);
}
Gives a list of [lat, lng]
I want to know if setting start and end location in the map while I'm retrieving coordinates and placeId through google roads API is possible or not? Because while I initialize Roads API by setting a custom coordinate like:
var mapOptions = {
zoom: 17,
center: {lat: -33.8667, lng: 151.1955}
};
its shows me a specific location in the map where the coordinate exists. But I want to initialize a map where I set a start and end location in the map and then start to use snapToRoads API to retrieve coordinates.
Ok, I have done a little bit digging and solved it by myself.
The Javascript Part:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?libraries=drawing,places"></script>
<!--Elevation JS
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true"></script>-->
<script>
//GOOGLE_API_KEY
var apiKey = 'GOOGLE_API_KEY';
alert("Provide GOOGLE API KEY");
var map;
var elevator;
var directionsDisplay;
var directionsService;
var drawingManager;
var placeIdArray = [];
var polylines = [];
var snappedCoordinates = [];
var initialLocation;
var siberia = new google.maps.LatLng(60, 105);
var newyork = new google.maps.LatLng(40.69847032728747, -73.9514422416687);
var browserSupportFlag = new Boolean();
function initialize()
{
var mapOptions = {
zoom: 17,
center: {lat: -33.8667, lng: 151.1955}
};
directionsService = new google.maps.DirectionsService();
var polylineOptionsActual = new google.maps.Polyline({
strokeColor: '#FF0000',
strokeOpacity: 0.6,
strokeWeight: 2
});
directionsDisplay = new google.maps.DirectionsRenderer({polylineOptions: polylineOptionsActual});
map = new google.maps.Map(document.getElementById('map'), mapOptions);
directionsDisplay.setMap(map);
// Create an ElevationService
elevator = new google.maps.ElevationService();
// Adds a Places search box. Searching for a place will center the map on that
// location.
map.controls[google.maps.ControlPosition.RIGHT_TOP].push(
document.getElementById('bar'));
var autocomplete = new google.maps.places.Autocomplete(document.getElementById('autocStart'));
autocomplete.bindTo('bounds', map);
autocomplete.addListener('place_changed', function() {
var placeStart = autocomplete.getPlace();
//alert(placeStart.place_id);
document.getElementById("startPlaceId").value=placeStart.place_id;
});
var autocomplete1 = new google.maps.places.Autocomplete(document.getElementById('autocEnd'));
autocomplete1.bindTo('bounds', map);
autocomplete1.addListener('place_changed', function() {
var placeEnd = autocomplete1.getPlace();
//alert(placeEnd.place_id);
document.getElementById("endPlaceId").value=placeEnd.place_id;
});
// Enables the polyline drawing control. Click on the map to start drawing a
// polyline. Each click will add a new vertice. Double-click to stop drawing.
drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYLINE,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [
google.maps.drawing.OverlayType.POLYLINE
]
},
polylineOptions: {
strokeColor: '#696969',
strokeWeight: 2
}
});
drawingManager.setMap(map);
// Snap-to-road when the polyline is completed.
drawingManager.addListener('polylinecomplete', function(poly) {
var path = poly.getPath();
polylines.push(poly);
placeIdArray = [];
runSnapToRoad(path);
});
// Clear button. Click to remove all polylines.
$('#clear').click(function(ev) {
for (var i = 0; i < polylines.length; ++i) {
polylines[i].setMap(null);
}
polylines = [];
ev.preventDefault();
return false;
});
}
// Snap a user-created polyline to roads and draw the snapped path
function runSnapToRoad(path) {
var pathValues = [];
for (var i = 0; i < path.getLength(); i++) {
pathValues.push(path.getAt(i).toUrlValue());
}
$.get('https://roads.googleapis.com/v1/snapToRoads', {
interpolate: true,
key: apiKey,
path: pathValues.join('|')
}, function(data) {
processSnapToRoadResponse(data);
drawSnappedPolyline();
//getAndDrawSpeedLimits();
});
}
// Store snapped polyline returned by the snap-to-road method.
function processSnapToRoadResponse(data)
{
snappedCoordinates = [];
placeIdArray = [];
for (var i = 0; i < data.snappedPoints.length; i++)
{
var latlng = new google.maps.LatLng(
data.snappedPoints[i].location.latitude,
data.snappedPoints[i].location.longitude);
//getElevation(latlng);
snappedCoordinates.push(latlng);
placeIdArray.push(data.snappedPoints[i].placeId);
}
//get Altitude in meters
getElevation(snappedCoordinates);
document.getElementById("snappedCoordinatesArray").value=snappedCoordinates;
document.getElementById("snappedPaceIdArray").value=placeIdArray;
}
// Draws the snapped polyline (after processing snap-to-road response).
function drawSnappedPolyline() {
var snappedPolyline = new google.maps.Polyline({
path: snappedCoordinates,
strokeColor: 'black',
strokeWeight: 3
});
snappedPolyline.setMap(map);
polylines.push(snappedPolyline);
}
// Gets speed limits (for 100 segments at a time) and draws a polyline
// color-coded by speed limit. Must be called after processing snap-to-road
// response.
function getAndDrawSpeedLimits() {
for (var i = 0; i <= placeIdArray.length / 100; i++) {
// Ensure that no query exceeds the max 100 placeID limit.
var start = i * 100;
var end = Math.min((i + 1) * 100 - 1, placeIdArray.length);
drawSpeedLimits(start, end);
}
}
// Gets speed limits for a 100-segment path and draws a polyline color-coded by
// speed limit. Must be called after processing snap-to-road response.
function drawSpeedLimits(start, end) {
var placeIdQuery = '';
for (var i = start; i < end; i++) {
placeIdQuery += '&placeId=' + placeIdArray[i];
}
$.get('https://roads.googleapis.com/v1/speedLimits',
'key=' + apiKey + placeIdQuery,
function(speedData) {
processSpeedLimitResponse(speedData, start);
}
);
}
// Draw a polyline segment (up to 100 road segments) color-coded by speed limit.
function processSpeedLimitResponse(speedData, start) {
var end = start + speedData.speedLimits.length;
for (var i = 0; i < speedData.speedLimits.length - 1; i++) {
var speedLimit = speedData.speedLimits[i].speedLimit;
var color = getColorForSpeed(speedLimit);
// Take two points for a single-segment polyline.
var coords = snappedCoordinates.slice(start + i, start + i + 2);
var snappedPolyline = new google.maps.Polyline({
path: coords,
strokeColor: color,
strokeWeight: 6
});
snappedPolyline.setMap(map);
polylines.push(snappedPolyline);
//passDataToObjC();
}
}
function getColorForSpeed(speed_kph) {
if (speed_kph <= 40) {
return 'purple';
}
if (speed_kph <= 50) {
return 'blue';
}
if (speed_kph <= 60) {
return 'green';
}
if (speed_kph <= 80) {
return 'yellow';
}
if (speed_kph <= 100) {
return 'orange';
}
return 'red';
}
function getElevation(snappedCoordinatesArr)
{
var locations = [];
// Retrieve the latlng and push it on the array
for (var i = 0; i < snappedCoordinatesArr.length; i++)
{
locations.push(snappedCoordinatesArr[i]);
}
// Create a LocationElevationRequest object using the array's one value
var positionalRequest =
{
'locations': locations
}
//alert(positionalRequest);
// Initiate the location request
elevator.getElevationForLocations(positionalRequest, function(results, status)
{
if (status == google.maps.ElevationStatus.OK)
{
// Retrieve the first result
if (results)
{
var altitudeArr=[];
for(var j=0;j<results.length;j++)
{
altitudeArr.push(results[j].elevation);
}
document.getElementById("altitudeArray").value=altitudeArr;
document.getElementById("dataDisplay").style.display="block";
//alert(altitudeArr);
}
else
{
alert('No results found');
}
}
else
{
alert('Elevation service failed due to: ' + status);
}
});
}
function calcRoute()
{
var start = document.getElementById("autocStart").value;
var end = document.getElementById('autocEnd').value;
//alert(start);
var request = {
origin:start,
destination:end,
travelMode: google.maps.TravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
}
$(window).load(initialize);
</script>
And The HTML:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Roads API</title>
<style>
html, body, #map {
height: 100%;
margin: 0px;
padding: 0px
}
#panel {
position: absolute;
top: 5px;
left: 50%;
margin-left: -180px;
z-index: 5;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
}
#bar {
width: 240px;
background-color: rgba(255, 255, 255, 0.75);
margin: 8px;
padding: 4px;
border-radius: 4px;
}
#autoc {
width: 98%;
box-sizing: border-box;
}
</style>
<body>
<div id="map"></div>
<div id="bar">
<form id="geodata-form" action="http://wayzme.sulavmart.com/map/savedata">
<div id="dataDisplay" style="display:none;">
CoordinatesArray:
<input type="text" id="snappedCoordinatesArray" />
AltitudeArray:
<input type="text" id="altitudeArray" />
PaceIdArray:
<input type="text" id="snappedPaceIdArray" />
<input type="hidden" id="startPlaceId" />
<input type="hidden" id="endPlaceId" />
</div>
<p class="auto">
<input type="text" id="autocStart" style="width:98% !important" name="start" />
</p>
<p class="auto">
<input type="text" id="autocEnd" style="width:98% !important" name="end"/>
</p>
<input type="button" value="Get Directions" onClick="calcRoute();">
</form>
<p><a id="clear" href="#">Click here</a> to clear map.</p>
</div>
</body>
</html>