Race conditions in Google Maps loading - html

I've been having a problem and no amount of googling has led to an answer. I'm building a web application that uses google maps and I keep getting race condition errors after initializing the map. I'm trying to just get the bounds of the map when the page loads, so I can use those boundaries to get data from my API. The problem is that I get incorrect bounds about 20% of the time.
The main things I'm trying to figure out is:
When exactly is a map initialized so I can use it properly?
Is having a fluid map container causing problems?
Is angular.js causing problems for some reason.
The map is assigned to a div called map-canvas which is inside an absolutely positioned map-container
#map-container{
position:absolute;
background-color:#DDD;
top:0px;
bottom:0px;
left:450px;
right:0px;
}
#map-canvas{
width:100%;
height:100%;
}
I'm using jQuery's $(function(){}) method to be notified when the page is loaded. Then I call my initMap() function to initialize the map, where myLatLng is already defined. In order to detect when the map is initialized, I'm adding an 'idle' listener. The autopanning part is for detecting when a user is in the process of dragging the map.
$scope.initMap = function(){
var mapOptions = {
center: myLatLng,
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP,
panControl: false,
streetViewControl: false,
mapTypeControl: false
};
$scope.map = new google.maps.Map(document.getElementById("map-canvas"),
mapOptions);
google.maps.event.addListener($scope.map, 'idle', function() {
if($scope.autoPanning){
$scope.autoPanning = false;
}else{
$scope.getStories();
}
});
};
In my getStories() method, I get the bounds of the map so I can submit a query to my service. The problem is that latitude_1 will equal latitude_2, giving me essentially a line instead of a box as bounds. This is what I'm currently doing to catch and fix that error:
var bounds = map.getBounds();
var ne = bounds.getNorthEast();
var sw = bounds.getSouthWest();
var bounds = {
latitude_1 : ne.lat(),
longitude_1 : ne.lng(),
latitude_2 : sw.lat(),
longitude_2 : sw.lng()
};
if(bounds.latitude_1 == bounds.latitude_2){
line("---caught invalid map event---------------------");
google.maps.event.trigger($scope.map, 'resize');
setTimeout(function(){
google.maps.event.trigger($scope.map, "resize");
$scope.map.setCenter(myLatlng);
}, 1000);
return;
}
I'm catching that invalid latitudes issue about 20% of the time, and it's really frustrating. Can anyone shine light on this issue?

I finally figured out what the problem was. It was based on a misunderstanding on my part about the $(document).ready and $(window).load:
$(document).ready is fired when the DOM is ready for interaction.
$(window).load fires when all the page assets are loaded.
So window.load happens after document.ready. I was initializing my app on the document.ready event, when google maps assets weren't fully loaded. I switched my initialization code to happen on the window.load event and it works now.

Related

ionic not rendering google maps on first load

I have an app that is a welcome/sign in screen and after you sign it you are taken to a tab screen with four tabs in it, the first tab of which shows a google map.
Upon first sign in the page does not display it's self but upon refresh of the page the map show's up gracefully as it was expected to do the first time. This happens EVERY time but I only get one javascript error.
error
Uncaught Error: can't load XRegExp twice in the same frame(anonymous function) # xregexp-min.js:2
code
function initialize() {
var mapOptions = {
zoom: 16,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(
document.getElementById("map"),
mapOptions
);
var geocoder = new google.maps.Geocoder();
$scope.map = map;
$scope.centerOnMe();
}
google.maps.event.addDomListener(window, 'load', initialize);
$scope.centerOnMe = function() {
if(!$scope.map) {
return;
}
$scope.loading = $ionicLoading.show({
content: 'Getting current location...',
showBackdrop: false
});
// get user location
$cordovaGeolocation
.getCurrentPosition({
timeout:10000,
enableHighAccuracy:true
})
.then(function(position) {
$ionicLoading.hide();
// we found the position
$scope.map.setCenter(
new google.maps.LatLng(
position.coords.latitude,
position.coords.longitude
)
);
}, function(e) {
$ionicLoading.hide();
console.log(e);
alert("Uh oh, we can't find your position! Is your GPS enabled?\n\n" + e);
});
};
edit
This is my latest javascript error, after replacing the google init with the ionic init. I understand what's happening but I don't know where I should move that function declaration to. I moved it inside the init but it still didn't work. Should I set a timeout?
TypeError: $scope.loadLocations is not a function
at initialize (controllers.js:179)
at Object.ionic.Platform.ready (ionic.bundle.js:2120)
at new <anonymous> (controllers.js:182)
at invoke (ionic.bundle.js:12468)
at Object.instantiate (ionic.bundle.js:12476)
at ionic.bundle.js:16745
at IonicModule.controller.self.appendViewElement (ionic.bundle.js:47716)
at Object.IonicModule.factory.ionicViewSwitcher.create.switcher.render (ionic.bundle.js:45973)
at Object.IonicModule.factory.ionicViewSwitcher.create.switcher.init (ionic.bundle.js:45893)
at IonicModule.controller.self.render (ionic.bundle.js:47590)
You don't need $scope.centerOnMe(); in initialize. If you do then compilation is inconsistent; refer to the examples and you will understand.
Also, ionic doesn't work well with DOM in certain cases.
Load map with $scope.init inside the container instead of function initialize().
This answer explains it in detail.
Another way to do it is by replacing google.maps.event.addDomListener(window, 'load', initialize); with ionic.Platform.ready(initialize);. Example here.

Meteor: modals and google maps

I am displaying a google map in a modal and I see lots of people have had this issue and there seems to be a fix but so far nothing I've tried has worked.
Basically I am loading googles places search in Template.map.rendered - it seems like something is happening with the DIVs because when I resize the window it re-renders and displays properly.
Now, I have tried all kinds of tricks (hiding / showing various DIVs), calling google.maps.event.trigger(map, "resize"); etc.
The map template is being loaded into another template that is a form (with other fields etc).
If someone could point me in a fruitful direction that would be awesome - seems like it should be a simple fix but I'm stumped.
Thanks!
You're in luck, we just solved our issue regarding this. Assuming the template map is called within a modal:
Template.map.rendered = function() {
Tracker.autorun(function() {
Session.set('map_available', true);
var center = {
latitude: -1.8445
longitude: 52.9445
}; // end var center
var zoomLevel = 15;
if(Session.equals('map_available', true)) {
GoogleMaps.init({'sensor': true, 'key' : 'OPTIONAL_KEY' },
function() {
var mapOptions = {
zoom: zoomLevel,
minZoom: 15,
maxZoom: 20,
mapTypeId: google.maps.MapTypeId.ROADMAP,
navigationControlOptions: {
style: google.maps.NavigationControlStyle.SMALL
};
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
map.setCenter(new google.maps.LatLng( center.latitude, center.longitude ));
} // end closure param 2 of GoogleMaps init
}); // end GoogleMaps.init
} // end Session.equals 'map_available'
} //end Tracker.autorun
} // end Template.map.rendered
Template.map.destroyed = function() {
Session.set('map_available', false);
}
So the strategy here is to monitor dependency tracking independently and make sure it gets re-added after any DOM reactions. Bonus feature here is that if you include var center modification inside Tracker.autorun (e.g. var center = navigator.location) you will have a map that changes center when you move around (lat, long has changed).
Also, we used the extension mrt:googlemaps for the GoogleMaps integration. Seems to be the best one to use.
Had the same problem that stumped me for an hour. My easy fix was to wrap it in a window onload. Try it, you might like it :-)
Template.mapsAutoComplete.rendered = function() {
window.onload = function () {
var map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
...
};
};

Google map in a tab created with angularjs is not centered

I have the following problem:
I'm working in an aplication to book hotels that has tabs ("Hotel Details", "Map", "Reviews") in the overview page. the tabs are created with angularjs.
also, in the page I have the hotel address that is a link to the "Map" tab.
When I click on the "Map" tab, the google map is rendering ok. But, when I click on the hotel address, the "Map" tab opens but the map is not centered. If I refresh the page, it appears centered.
this is the code that process the map:
$scope.loadMap = function (targetID){
var latitude = $scope.latitude;
var longitude = $scope.longitude;
if(latitude && longitude && document.getElementById(targetID) != null ){
var center = new google.maps.LatLng(latitude, longitude);
var map_options = {
zoom: 13,
center: center,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// create map
var map = new google.maps.Map(document.getElementById(targetID), map_options);
// configure marker
var marker_options = {
map: map,
position: center,
title: $scope.hotelName
};
// create marker
var marker = new google.maps.Marker(marker_options);
var infoWindow = new google.maps.InfoWindow();
window.setTimeout(function(){
google.maps.event.trigger(map, 'resize');
},2000);
}
}
Can you help me, please?
I've had this very problem when I was creating maps using Leaflet.js. I was trying to initialize the map inside of my angular service and it was loading very strangely. It appears you are trying to do the same thing, but you're doing it inside of your controller, rather than a service, but the effect is the same thing.
The problem is timing. Your controller is run before the DOM finishes loading and before angular has a chance to scan through the DOM to build out your Map page partial. Because of that, the page is only partially loaded when you call $scope.loadMap, which loads the google map, as you would expect. The problem is that once Angular finishes scanning the DOM partial, it modifies the DOM and then loads it into the view. When this happens, the google map has already been initialized to its default size based on the previous state of the DOM, and doesn't resize properly when angular is finished with the DOM.
Ok, that was a long explanation, but here's how you fix it:
You should do all of your map initialization inside of a directive's linking function. You could create a directive called something like app.directive('hotelMap'), which would initialize your map using the $scope.latitude and $scope.longitude variables from your controller.
link: function(scope, elem, attrs){
var center = new google.maps.LatLng(scope.latitude, scope.longitude);
var map_options = {
zoom: 13,
center: center,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// create map
var map = new google.maps.Map(document.getElementById(targetID), map_options);
// configure marker
var marker_options = {
map: map,
position: center,
title: $scope.hotelName
};
// create marker
var marker = new google.maps.Marker(marker_options);
var infoWindow = new google.maps.InfoWindow();
}
And then in your map partial, you would have an element that looks something like <div hotel-map></div>. This fixes your problem, because your map will never be initialized until Angular has finished scanning/modifying the DOM and has finished rendering your partial. That way, the map's containing element will be at its final size/position and the map will be loaded into the element's center properly. Give it a try and let me know if that works.

Google Map not displaying on website. Controls show, but the map itself does not render

The Google map on my site no longer shows up. I can see the zoom in/out slider, map/satellite buttons, etc... but the map itself no longer shows up. I read somewhere that the map should be in a div tag labeled "map" with both height and width stated in the CSS. The map has always had height/width CSS properties listed, but the ID was labeled differently. I changed the ID to map and corresponding CSS, but the map still will not show up. I read elsewhere that the Google Analytics by Yoast plugin was also causing similar errors. I have disabled all plugins and then one at a time I have reactivated them to see if another plugin might be the issue. However, it seems that none of the plugins running have any effect on the map displaying.
I am a web designer with some HTML/CSS knowledge. I designed this site and had hired a guy to code it into a custom Wordpress theme for me. Like I said, the map had been showing just fine, but no longer does and I have no idea why. I could really use some help to figure this out. I'm trying to learn more about coding so in the future I can debug and fix things like this on my own. A little help would be greatly appreciated!
Here is a link to the page with the map: http://seegerlaw.com/contact-us/
Please let me know if you need any other information from me.
You're using undocumented fields:
function initialize() {
geocoder = new google.maps.Geocoder();
codeAddr(function(lat, lng) {
myLatlng = new google.maps.LatLng(lat,lng);
var mapOptions = {
zoom: 15,
center: myLatlng,
disableDefaultUI: false,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
var marker = new google.maps.Marker({
position: myLatlng,
map: map
});
});
}
function codeAddr(callback) {
if (geocoder) {
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
console.log(results[0].geometry);
maps_lat = results[0].geometry.location["kb"];
maps_lng = results[0].geometry.location["lb"];
callback(maps_lat, maps_lng);
}
else {
console.log("Geocoding failed: " + status);
}
});
}
}
Use the lat() and lng() properties of LatLng to get their values.
Even better, just pass in the LatLng directly from the Geocoder to the MapOptions object:
codeAddr(function(latLng) {
var mapOptions = {
center: latLng,
...
};
...
});
callback(results[0].geometry.location);
Even better – it appears that you always geocode the same address – just hardcode the LatLng into your code.

Google Map API v3 off center if reloaded (not the usual 'resize' thing)

I read all the similar questions but mine is slightly different. The first time a JQuery Mobile dialog is displayed, the map loads fine inside the usual map_canvas div, but if the dialog is reloaded (i.e. go back and click on the button to open the dialog again), it is displayed only partially, zoomed out to a 3 or 4 and centered around the div's top-left corner.
There is no change in the div size (which is explicitly set) and for good measure a google.maps.event.trigger(map, 'resize'); is called.
I also tried to initialize the map after the dialog is shown but the result is the same.
Button code:
$("#dest-map-button").click(function() {
initializeMap(job_id,"map_canvas");
}
);
Function:
function initializeMap(job_id, map_div){
var pos = arrJobs[job_id].lat_lng.split(',');
var job_pos = new google.maps.LatLng(pos[0],pos[1]);
var driverLatLng = lat_lng.split(',');
var driver_pos = new google.maps.LatLng(driverLatLng[0],driverLatLng[1]);
var myOptions = {
zoom: 18,
center: driver_pos,
mapTypeId: google.maps.MapTypeId.ROADMAP,
disableDefaultUI: true
}
map = new google.maps.Map(document.getElementById(map_div), myOptions);
var marker = new google.maps.Marker({
position: job_pos,
map: map,
title: "Job"
});
var marker2 = new google.maps.Marker({
position: driver_pos,
map: map,
title: "X",
});
google.maps.event.trigger(map, 'resize');
}
HTML:
<div data-role="page" id="dialog-destination-map" data-theme="e">
<div data-role="content">
<div id="map_canvas" style="height:300px; width:300px; position: relative; margin: 0px auto;">
map_canvas
</div>
</div>
</div>
Any ideas?
EDIT: this question seems describing exactly the same problem: JQuery Mobile & Google Maps Glitch
But the solution provided (caching) can't be used here as the map might need to change
After trying everything else, I finally stumbled upon the pageshow event. Calling initializeMap after all the page transitions are done rather than when clicking the button solved the problem:
$('#dialog-destination-map').live('pageshow',function(event){
initializeMap(job_id,"map_canvas");
}
);
I still wonder how come it was working at the first load then...
I Agree with #peter .. You do not need to create map on each pageshow event.. Instead, try this:
$(document).on("pagebeforechange", function()
{
if(mapObj){
center = mapObj.getCenter();
}
});
$("#mapPage").bind("pageshow", function()
{
google.maps.event.trigger(mapObj, "resize");
if(center)mapObj.setCenter(center);
});
var driverLatLng is being initialized with lat_lng.split(','); which hasn't been declared before.
You don't need to create a new map each time you visit the page. Create the map beforehand, e.g. on pageinit. On pageshow: google.maps.event.trigger(mapObj, "resize");