Displaying multiple Google Map markers and directions on one map - javascript

I would like to output multiple Google Map routes on to a single Google Map. The data is taken from an XML file which feature multiple markers. Each marker has a start point and an end point (in the form of latitude and longitude values). These markers are then added to the map and a call to the Google Maps Directions Service is made to draw a route between the start and end points of each marker.
The issue I'm having is that only one route is drawn (if I refresh the page it seems to just pick one of the markers at random and draw the directions between those two markers). The other markers aren't displayed at all.
I've console.logged the for loop to check it is running for each marker in the XML (it is). I'm guessing it is something to do with these two lines here, but that is a guess...
directionsDisplay = new google.maps.DirectionsRenderer();
directionsService = new google.maps.DirectionsService();
I've read this question - Using Google Maps 3 API to get multiple routes on a Map, but don't really understand the answer/know how relevant it is to my situation. Thanks.
jQuery(document).ready(function($) {
var map = new google.maps.Map(document.getElementById("map_canvas"), {
zoom: 13,
mapTypeId: 'roadmap'
});
// Try HTML5 geolocation
if(navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var pos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
map.setCenter(pos);
map.setZoom(13);
}, function() {
handleNoGeolocation(true);
});
} else {
// Browser doesn't support Geolocation
handleNoGeolocation(false);
}
directionsDisplay = new google.maps.DirectionsRenderer();
directionsService = new google.maps.DirectionsService();
// Change this depending on the name of your PHP file
downloadUrl("xml.php", function(data) {
var xml = data.responseXML;
var markers = xml.documentElement.getElementsByTagName("marker");
for (var i = 0; i < markers.length; i++) {
var title = markers[i].getAttribute("title");
mode = markers[i].getAttribute("mode");
startpoint = new google.maps.LatLng(
parseFloat(markers[i].getAttribute("startlat")),
parseFloat(markers[i].getAttribute("startlng")));
endpoint = new google.maps.LatLng(
parseFloat(markers[i].getAttribute("endlat")),
parseFloat(markers[i].getAttribute("endlng")));
calcRoute();
console.log('marker');
}
});
directionsDisplay.setMap(map);
});
function calcRoute() {
var request = {
origin: startpoint,
destination: endpoint,
//waypoints: waypts,
travelMode: google.maps.DirectionsTravelMode[mode]
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
}
function downloadUrl(url, callback) {
var request = window.ActiveXObject ?
new ActiveXObject('Microsoft.XMLHTTP') :
new XMLHttpRequest;
request.onreadystatechange = function() {
if (request.readyState == 4) {
request.onreadystatechange = doNothing;
callback(request, request.status);
}
};
request.open('GET', url, true);
request.send(null);
}
function doNothing() {}

I think the key point in the answer you linked to is that each route needs its own DirectionsRenderer object. And it doesn't mention it, but it seems likely that each route should also have its own DirectionsService object. You're sharing a single one of each these objects among all your routes. I suspect that these shared objects are getting overwritten for each route.
Here's an update that should fix that by creating new objects for each route:
jQuery(document).ready(function($) {
var map = new google.maps.Map(document.getElementById("map_canvas"), {
zoom: 13,
mapTypeId: 'roadmap'
});
// Try HTML5 geolocation
if(navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var pos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
map.setCenter(pos);
map.setZoom(13);
}, function() {
handleNoGeolocation(true);
});
} else {
// Browser doesn't support Geolocation
handleNoGeolocation(false);
}
// Change this depending on the name of your PHP file
$.get("xml.php", function( xml ) {
var markers = xml.documentElement.getElementsByTagName("marker");
for (var i = 0; i < markers.length; i++) {
addMarkerPair( markers[i] );
}
}, 'xml' );
});
function addMarkerPair( pair ) {
function get( name ) {
return pair.getAttribute( name );
}
var title = get("title");
var mode = get("mode");
var startpoint = new google.maps.LatLng(
+get("startlat"),
+get("startlng")
);
var endpoint = new google.maps.LatLng(
+get("endlat"),
+get("endlng")
);
calcRoute( mode, startpoint, endpoint );
console.log('marker');
}
function calcRoute( mode, startpoint, endpoint ) {
var directionsService = new google.maps.DirectionsService();
var directionsDisplay = new google.maps.DirectionsRenderer();
directionsDisplay.setMap(map);
var request = {
origin: startpoint,
destination: endpoint,
//waypoints: waypts,
travelMode: google.maps.DirectionsTravelMode[mode]
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
}
I also took the liberty of fixing and simplifying a few things:
Moved the marker loop body out to a separate addMarkerPair function for better readability.
Added a get function inside addMarkerPair as a shorthand for pair.getAttribute.
Used + instead of parseInt. Either one will do the same thing; I like + for its brevity.
Add several missing var declarations.
Passed the necessary parameters to calcRoute as function parameters instead of global variables.
Used $.get instead of downloadUrl. No need for your own function when jQuery has it already.
Also, as another alternative to the XML parsing, you could use jQuery to access the XML elements. But I stuck with code closer to how you're already doing it.

Related

Google Maps API - heatmap not defined (map displaying but cannot alter)

On 2nd version of a heatmapping system I am making using google maps api.
Initial version works fine but uses PHP and MySQL to write the points into the map, this displays fine and can then alter map (e.g. radius / opacity) using heatmap.set the only problem is you cant change heatmap displayed without reloading (points are effectively hard coded into script by PHP).
So I have altered original code to instead call a php script (using a serialized version of form) which returns query in JSON format, this works fine in as far as get a blank map to begin with, can choose some settings and it will load heatmap fine, can change these and it will do a new heatmap (which it layers on top of old one) so have gone through how to remove previous heatmap and hit a brick wall...
If I try any function calling heatmap. I get "heatmap not defined" this includes all the previous functions that worked fine in old map.
Full code is quite large so will hopefully cover everything in these snippets...
Initial map load...
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 13,
center: {lat: 51.514898, lng: -0.144432},
mapTypeId: 'roadmap'
});
}
Make a heatmap...
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var heatmapData = [];
myObj = JSON.parse(this.responseText);
for (var i = 0; i < myObj.points.length; i++) {
var latLng = new google.maps.LatLng(myObj.points[i].lat,myObj.points[i].lng);
var pushData = {
location: latLng,
weight: myObj.points[i].weight
}
heatmapData.push(pushData);
}
var heatmap = new google.maps.visualization.HeatmapLayer({
data: heatmapData,
maxIntensity: 10,
radius:30,
opacity: 0.4,
map: map
})
}
};
Which is called by form submit...
$( "form" ).on( "submit", function( event ) {
event.preventDefault();
url = "query.php?" + $( this ).serialize();
xmlhttp.open("GET", url, true);
xmlhttp.send();
});
and an example function which don't work (value set by a slider with an onchange event)...
function changeOp(val) {
heatmap.set('opacity', val);
}
The heatmap is currently local to the xmlhttp.onreadystatechange function, you can't access it outside of there. You need to declare it outside of that function (the global scope works, like the map variable), then you can access it to modify it later (or in the next call to the AJAX request).
var heatmap;
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var heatmapData = [];
myObj = JSON.parse(this.responseText);
for (var i = 0; i < myObj.points.length; i++) {
var latLng = new google.maps.LatLng(myObj.points[i].lat, myObj.points[i].lng);
var pushData = {
location: latLng,
weight: myObj.points[i].weight
}
heatmapData.push(pushData);
}
heatmap = new google.maps.visualization.HeatmapLayer({
data: heatmapData,
maxIntensity: 10,
radius: 30,
opacity: 0.4,
map: map
})
}
};

Node.js googlemaps cluster execute error

I'm now use the node.js platform. And I want to use googlemap cluster library. However I can't execute the source code.
googlemaps cluster api url : https://github.com/googlemaps/js-marker-clusterer
This library's index is below.
dosc
examples
images
src
etc..
In the directory of examples, I want to test a "simple_example.html".
My node.js source code is below.
[app.js]
var express = require("express");
var app = express();
app.set('view engine','ejs');
var path = require("path");
app.set("public", path.join(__dirname, "public"));
app.get('/', function(req, res){
res.render("simple_example", {} )
});
app.listen(3000);
console.log("Running at Port 3000");
[simple_example.ejs] (important script part)
<script src="https://maps.googleapis.com/maps/api/js"></script>
<script src="public/data.json"></script>
<script type="text/javascript" src="public/src/markerclusterer.js"></script>
<script>
function initialize() {
var center = new google.maps.LatLng(37.4419, -122.1419);
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 3,
center: center,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var markers = [];
for (var i = 0; i < 100; i++) {
var dataPhoto = data.photos[i];
var latLng = new google.maps.LatLng(dataPhoto.latitude,
dataPhoto.longitude);
var marker = new google.maps.Marker({
position: latLng
});
markers.push(marker);
}
var markerCluster = new MarkerClusterer(map, markers, {imagePath: 'public/images/m'});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
This picture is result of execute by my source code. but the picture doesn't have markered in the map. I expected picture is below.
I tried path change of src, change web platform(as python) but I can't solve the problem. Who know the solution?
Once you create a marker cluster, support it by using addMarker() method or by providing a array of markers to the constructor.
When they say add marker to constructor by providing a array of markers it's similar to doing this:
var markers = []; //create a global array where you store your markers
for (var i = 0; i < 100; i++) {
var latLng = new google.maps.LatLng(data.photos[i].latitude,
data.photos[i].longitude);
var marker = new google.maps.Marker({'position': latLng});
markers.push(marker); //push individual marker onto global array
}
var markerCluster = new MarkerClusterer(map, markers); //create clusterer and push the global array of markers into it.
To add it using addMarker() you basically add it to the cluster like the following:
var markerCluster //cluster object created on global scope
//do your marker creation and add it like this:
markerCluster.addMarker(newMarker, true); //if specify true it'll redraw the map
Here's a reference to MarkerCluster: https://googlemaps.github.io/js-marker-clusterer/docs/examples.html
Also, here's a demo app how to use markercluster: https://github.com/googlemaps/js-marker-clusterer

Using a large data set of address to create a Google API heat map

I'm trying to create a heatmap using Google's API, but running into a few issues. The main issue I have run into so far is converting a large set of about ~25000 addresses into geocoordinates for use in the API. The two approaches I have used are Google's own API, which has heavy limitations, and Python's Geopy module, which also has limitations. Since I am trying to convert ~25000, Geopy's one second delay is a pretty steep issue.
The second issue I can see coming is a size limitation. Once I have a list of ~25000 geocoordinates, putting those into an array in Javascript seems ridiculous. Assuming a user would upload a text file in a format like this:
12.3456,-12.3456
23.4567,-23.4567
...
Would there be a way to process the file in blocks, or some other work around?
In case it helps, this is my current Javascript code for processing a file of addresses. I'm planning on changing the file reading format to find a better system.
var map, heatmap, geocoder;
var MVCarr;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 36.85, lng: -75.97},
zoom: 7
});
MVCarr = new google.maps.MVCArray();
heatmap = new google.maps.visualization.HeatmapLayer({
data: MVCarr,
map: map
})
}
window.onload = function() {
var fileInput = document.getElementById('fileInput');
var fileDisplayArea = document.getElementById('fileDisplayArea');
geocoder = new google.maps.Geocoder();
fileInput.addEventListener('change', function(e) {
var file = fileInput.files[0];
var reader = new FileReader();
reader.onload = function(e) {
var mySplit = (reader.result).split("\n");
for(var i = 0, len = mySplit.length; i < len; i++) {
//console.log(mySplit[i]);
geocodeAddress(geocoder, mySplit[i]);
}
}
reader.readAsText(file);
});
}
function geocodeAddress(geocoder, address) {
geocoder.geocode({'address': address}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
MVCarr.push(results[0].geometry.location);
console.log(MVCarr.length);
} else {
console.log('Failed: ' + status);
}
});
}

Google maps geolocation.geolocate gives map, but not directions

So I've made this piece of code which turns an ordinary address into a geolocation and gives back a map and directions from your gps location (phonegap position, html5 location or whatever latlng). This function gets called on success of the phonegap or html5 location.
This code works fine in a browser but fails with a:
Error: TypeError: 'undefined' is not an object in file://bla bla at line 761
Line 761 = geocoder.geocode ( { 'address': address }, function(results, status) {
So i've checked all my variables and they are not empty just like in the online version and the return div exists. So I'm guessing there is something wrong in the geolocator link to Google.
The funny thing though, it shows the map but not the directions. And it marks the location where you need to go, but not where you are. Its as if just directions just doesnt load or something.
var directionDisplay;
var directionsService = new google.maps.DirectionsService();
function bring_me_back(call_back_map,call_back_directions,address,position) {
var latlng = new google.maps.LatLng(position.coords.latitude,position.coords.longitude); // your location
directionsDisplay = new google.maps.DirectionsRenderer();
var myOptions = {
zoom: 14,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: true
};
var map = new google.maps.Map(document.getElementById(call_back_map),myOptions);
directionsDisplay.setMap(map);
directionsDisplay.setPanel(document.getElementById(call_back_directions));
var marker = new google.maps.Marker({
position: latlng,
map: map,
title:"My location"
});
// find the address
var geocoder = new google.maps.Geocoder (map);
geocoder.geocode ( { 'address': address }, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var end = results [0].geometry.location;
map.setCenter(results [0].geometry.location);
marker.setPosition(results [0].geometry.location);
var start = latlng; // your gps location
var request = {
origin:start,
destination:end,
travelMode: google.maps.DirectionsTravelMode.WALKING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
} else alert("Directions was not successful for the following reason: " + status);
});
}
else {
$('#'+call_back).html("Geocode was not successful for the following reason: " + status);
}
});
}
The way I call google (it has plenty of time to load and "initialize" gets called way before I use this code).
function loadGoogleScript() {
if(!(navigator.network.connection.type == Connection.NONE)) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://maps.google.com/maps/api/js?v=3.6&sensor=false&callback=initialize';
document.body.appendChild(script);
return true;
}
else {
return false;
}
}
The feedback from the API is way to limited, so I can't make anything more of it.
After a hell of a search, I found out that one variable:
var directionsService = new google.maps.DirectionsService();
Was called before google script loaded. Changed it to
var directionsService;
And called:
directionsService = new google.maps.DirectionsService();
From inside the the function.
Thanks for help.

Creating multiple instances of a google API on an HTML page

I am creating a web page that lets a user enter a start location and an end location into textbox's and the distance between the two locations is output (this is using a google API). I have successfully been able to create a single instance of this that works as I want it to. However my problem is that I need multiple rows of this but as soon I duplicate the code I cannot seem to get it to work. This is my first real attempt at using javascript so my understanding of it is not that great. I realise that I need to change variables etc. but am unsure exactly as what to change. Sorry if I have not been clear enough but any help would be great, thanks.
Here is my code:
<body onload="initialize()">
<script type="text/javascript">
var directionDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
function initialize() {
directionsDisplay = new google.maps.DirectionsRenderer();
var belfast = new google.maps.LatLng(55, -5)
var myOptions = {
zoom:5,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: belfast,
}
map = new google.maps.Map(document.getElementById("map_canvas1"), myOptions);
directionsDisplay.setMap(map);
}
function calcRoute() {
var start = document.getElementById("pc1").value;
var end = document.getElementById("pc2").value;
var distanceInput = document.getElementById("distance");
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);
distanceInput.value = response.routes[0].legs[0].distance.value / 1000;
}
});
}
<td><input type="text" id="pc1" /></td>
If you're creating multiple instances of Google Maps, you need to make sure you're not mixing up global variables. These variables you're declaring on a global level:
var directionDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
So if you try to create a second map and assign the map to the map variable, which should destroy the previous map. Likewise your DirectionsService. So you need to create a variable for each of those for each instance.

Categories

Resources