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

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
})
}
};

Related

OpenStreetMap OSMXML not rendering

I am trying to offline-render a part of OpenStreetMap, previously loaded as map.osm (OSM XML). I'm starting a localhost and loading the xml from disk.
I tried to use similar example (https://openlayers.org/en/latest/examples/vector-osm.html) changing only the source to my file, yet this didn't work out. Data loaded is pretty much similar to data I see in the example.
I tried a bunch of various approaches, and I still can't render the map. I get no errors, and I can't get what I'm missing.
var vectorSource = new VectorSource({
format: new OSMXML()
});
var xml = await fetch('map.osm').then(res => res.text());
var features = (new OSMXML()).readFeatures(xml);
// Features are parsed OK
vectorSource.addFeatures(features);
var vector = new VectorLayer({
source: vectorSource,
// Using styles, I don't post them here (a lot of code)
style: function (feature) {
for (var key in styles) {
var value = feature.get(key);
if (value !== undefined) {
for (var regexp in styles[key]) {
if (new RegExp(regexp).test(value)) {
return styles[key][regexp];
}
}
}
}
return null;
}
});
map = new Map({
layers: [vector],
target: document.getElementById('map'),
view: new View({
center: [0, 0],
maxZoom: 0,
zoom: 0
})
});

Using a file to hold array items for Google Maps

I have create a Google Map using however, it has a lot of markers and it has become unwieldy to have them in the the same html file. To use the markers I first create a array as follows;
var markers = [
['Balham Leisure Centre',51.441234,-0.152297,'leisure_center.png'],
['Putney Leisure Centre',51.463955,-0.228771,'leisure_center.png'],
['Latchmere Leisure Centre',51.470967,-0.163805,'leisure_center.png']
]
then utilise them as follows;
for( i = 0; i < markers.length; i++ ) {
var position = new google.maps.LatLng(markers[i][1], markers[i][2]);
marker = new google.maps.Marker({
position: position,
map: map,
title: markers[i][0],
icon: iconBase + markers[i][3],
});
}
I would like to store the markers array in a separate text file like;
['Balham Leisure Centre',51.441234,-0.152297,'leisure_center.png'],
['Putney Leisure Centre',51.463955,-0.228771,'leisure_center.png'],
['Latchmere Leisure Centre',51.470967,-0.163805,'leisure_center.png']
then have the main page read that file and put it into the markers array so that everything still works the same.
Since you don't use any frameworks, in native JS you want to use XMLHttpRequest object. Save your data in a separate file (you might want to use JSON for example) and then follow the code below.
Basic usage:
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (req.readyState == XMLHttpRequest.DONE) {
console.log(req.responseText);
}
}
req.open('GET', url);
req.send();
Readmore: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

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);
}
});
}

Displaying multiple Google Map markers and directions on one map

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.

Load external js from an object, then use the same object's functions after loaded

Making a script which must to be one-lined to attach Google Maps everywhere in our site.
Sometimes 10 maps same time, so I was needed a function to build an object which can build maps. The problem is when I'm loading the maps.js even if it does not need it (case count maps = 0).
So I started to make this:
document.addEventListener("DOMContentLoaded", function(){ var map = new initmap(0, 'country_tinymap_canvas', {'longitude': {LONG}, 'latitude': {LAT}} ) }, false);
this binds on domready to load my maps. {LONG} and {LAT} are smarty-alike PHP variables, imagine those are numbers.
Also made a tiny callback handler for the Google Maps API callback.
window.___gmapsready = function(){
console.log("o/ - map here!");
$(window).trigger('gmapsready');
}
when I'm making the new map it constructs an initmap object
function initmap(type,container,params) {
var self = this;
this.container = container;
this.type = type;
this.params = params;
this.makemap = function()
{
console.log("doin map.");
var mapc = document.getElementById(this.container);
if (mapc != null)
{
var map = new google.maps.Map(mapc, window.___gmaps);
var marker;
var marker = new google.maps.Marker({
position: new google.maps.LatLng(this.params.longitude,this.params.latitude),
image: image,
draggable: false,
map: map
});
map.setCenter(new google.maps.LatLng(this.params.longitude,this.params.latitude));
}
console.log("map done, unset vars etc.");
}
this.checkload = function()
{
console.log("houston houston got map?");
if (typeof google === 'object' && typeof google.maps === 'object')
{
console.log("thanks bro.");
self.makemap();
}
else
{
console.log("nop. but i'm gonna load it.");
var s = document.createElement("script");
s.type = "text/javascript";
s.src = "http://maps.google.com/maps/api/js?v=3&sensor=true&callback=___gmapsready";
$("head").append(s);
document.addEventListener("gmapsready", self.makemap);
}
}
this.checkload();
}
The problem is that I can't add the function makemap to be called by the gmapsready event. Could you help me please how to do this?
Everything is fine but the document.addEventListener("gmapsready", self.makemap); call.
Aye also tried with document.addEventListener("gmapsready", function(event){self.makemap(event)}, false); gave the same result.
You can't mix custom jQuery events with native browser addEventListener. Try this instead:
$(window).on('gmapsready',function() { self.makemap();});

Categories

Resources