How to detect if google map loaded successfully - javascript

I'm using google-maps version 3 in my website.
I ran through problems where the map sometimes does not load, but instead it will be shown as a grey box, and the browser log will be fulled with errors - unfortunately I can't get the log now because the map is working again.
According to some researches, the problem is because of the experimental version I'm using
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=false"></script>
Is There a way to find out if the map has been loaded successfully or crashed so I can tell the user to come back soon?
P.S. I tried the idle event, but it has been invoked even when the map crashed.

I my opinion the best or most certain way is a combination of the tilesloaded event and a delayed function to test if a "success-flag" is set.
/* flag to indicate google maps is loaded */
googleMapsLoaded = false;
/* listen to the tilesloaded event
if that is triggered, google maps is loaded successfully for sure */
google.maps.event.addListener(map, 'tilesloaded', function() {
googleMapsLoaded = true;
//clear the listener, we only need it once
google.maps.event.clearListeners(map, 'tilesloaded');
});
/* a delayed check to see if google maps was ever loaded */
setTimeout(function() {
if (!googleMapsLoaded) {
//we have waited 5 secs, google maps is not loaded yet
alert('google maps is not loaded');
}
}, 5000);

Listen for the "tilesloaded" event instead. It's the last event to fire when a map (successfully) loads and you are actually showing a map.
If there's no map (e.g. you haven't set the width/height explicitly), this event won't fire even though idle does.
You can use the Map Events example and throttle your connection from within the Network tab to do some tests. Here's the same example with width/height not set for #map.

In idle event, do following on map object:
Check following:
Use if (typeof google === 'object' && typeof google.maps === 'object') {...} to check if it loaded successfully.
And then check if(map.getCenter) {
var latlng = map.getCenter();
//check here if latlng is object
}
If any of the "IF" condition fails that means something's wrong.

Related

Leaflet featureGroup with tracks fitbounds invalid

Is there a better way???
I am adding several tracks to a map (using leaflet-gpx plugin) and want to fit the maps to the bounds of all the added tracks.
By using a featureGroup layer to contain the tracks it is possible to used the fitBounds() method using the feature group.
However the featureGroup does not have a loaded event to trigger the fitBounds() and the map also doesn't seem to have a suitable trigger.
Calling fitBounds() directly on the map after it is rendered fails with 'invalid bounds' error. However delaying the call for about a second allows it to execute correctly.
I presume what is happening is that it takes time for all of the gpx layers in the group to be loaded and until that is complete the group can't return a valid set of bounds.
So using a setTimeout to delay the fitBounds() works, but causes some annoying flashing as the map re-renders itself. Is there a better way?
This works:
// mapname has been created and tracks have already been loaded into their individual layers
var tracksLayer = L.featureGroup([track1,track2,track3]);
tracksLayer.addTo(mapname);
//this works, with more and complex tracks you might need a longer timeout
setTimeout(function(){ mapname.fitBounds(tracksLayer.getBounds()); }, 1000);
// this however does not work
// mapname.on('load', function() { mapname.fitBounds(tracksLayer.getBounds());});
//nor does this despite the fact that by this stage the map has been created and the featureGroup added
// mapname.fitBounds(tracksLayer.getBounds());

HERE Maps API event delays

When panning a map with the HERE maps API, the 'mapviewchangeend' event is triggered a short time after the animation completes. This means that is difficult to synchronise, say, a Leaflet overlay without the overlaid objects lagging behind.
var map = new H.Map(document.getElementById('mapContainer'),
defaultLayers.normal.map, ...
var lMap = L.map('mapContainer', {zoomControl: false});
...
function onMapViewChange() {
lMap.setView(map.getCenter(), map.getZoom(), {animation: false});
}
map.addEventListener('mapviewchange', function () {
onMapViewChange();
});
map.addEventListener('mapviewchangeend', function () {
onMapViewChange();
});
Is there a way to remove this delay? I have experimented with different kinetic settings for H.mapevents.Behavior but so far without success.
I think you can hook into the sync events being fired by the the view model and the viewport. I seem to recall that these events fire synchronously when the map renders...
After some digging, I found the example showing something very similar on github:
maps-api-for-javascript-examples/ground-overlay

Cannot read property '_leaflet_mousedown5' of null when attempting to enable marker dragging in Mapbox/Leaflet

I am building the functionality that allows mapbox markers to be edited from within a CMS. The functionality should open up and populate a form when a map marker is clicked, and then allow the map marker to be dragged. When the form is saved, the content is submitted via ajax and then the map is reloaded with the featureLayer.loadURL("my_geojson_endpoint").
I have added comments throughout my code below to outline how I am getting to the error.
N.B. I define a property db_id in the geojson to identify each point because when you apply a filter, _leaflet_id changes. I also have jquery included in the code.
Code:
// loop through each marker, adding a click handler
$.each(points._layers, function (item) {
var point = points._layers[item];
attachClickHandler(point);
});
function attachClickHandler(point) {
// open the edit state for the marker on click
$(point._icon).on("click", function () {
openEditState(point);
})
}
function openEditState (point) {
disableEditOthers(point);
displayContent(point);
point.dragging.enable(); // this line causes the error
$(point._icon).off("click");
}
function disableEditOthers (point) {
// hide the other markers from the map (using db_id as mentioned above)
points.setFilter(function (f) {
return f.db_id === point.feature.db_id;
})
// this functions as a callback to display the popup
// since applying the filter on click, does not show the popup
setTimeout(function () {
for (key in points._layers) {
points._layers[key].openPopup();
}
}, 0)
}
In the map creation step I have been able to call this dragging.enable() method on each of the markers and provide "draggability" to all of them, however this is undesirable from a usability point of view. I want the user to clear swap in and out of an edit state.
I discovered this issue on github, solved by the solution to this. However after swapping my mapbox.js version out to the standalone and including the latest version of leaflet (0.7.3) the same error still occured.
Am I calling the function on the wrong property of the object? dumping out the "point" variable just before the line which errors does not show that the draggable property has the enable() function defined.
Any help is much appreciated.
Ok so as a slight workaround, but still not solving the original error.
$.each(points._layers, function (item) {
points._layers[item].dragging.enable()
})
Because I have filtered out the other points, enabling dragging on all points is working around the issue.
If you can provide a fix to my original fix (avoiding the loop) I am happy to accept it.

How to get max zoom for maptype in location

On Google Maps' JS API reference page, it says that the method getMaxZoomAtLatLng:
Returns the maximum zoom level available at a particular LatLng for
the Satellite map type.
For some reason, the terrain maptype bottoms out at level 15 in Calgary (for example) but the Satellite maptype can go all the way to zoom 19. I have two map services running simulataneously, and I want the maps to be in sync, but they can't sync up if they can't reach the same zoom level.
So, basically I want to know when the terrain map (or any maptype) can't cope, and trigger some special case functions.
Is there a method, or an alternative, for getting the maximum zoom for a location for a specific map type (since the above mentioned method only works for Satellite)?
Well, one solution I've decided to go with in the meantime is to attach a zoom-end handler to my ArcGIS map (could easily do this with Leaflet as well) and a maptypeid_changed handler to the Google map.
So, for the ArcGIS map (eMap) I did this:
eMap.on('zoom-end', function (e) {
// map zooms are synced
gMap.setCenter(esri.geometry.webMercatorToGeographic(e.extent.getCenter()));
gMap.setZoom(e.level);
// if the google map couldn't do it, reset eMap
if (gMap.getZoom() !== e.level) {
eMap.setZoom(gMap.getZoom());
// runs the handler again
}
});
And if the google map was set to Satellite, then changed, it also needed to trigger the same check, so I forced it to execute another zoomEnd handler:
google.maps.event.addListener(gMap, 'maptypeid_changed', function () {
// for some reason, ESRI has issues setting zoom to current zoom
// so it has to run an initial check before calling zoomEnd
if (gMap.getZoom() !== eMap.getZoom()) {
eMap.setZoom(gMap.getZoom());
}
});
Hope this helps someone, but really, I'm looking forward to seeing a better solution than this. Seems like an awfully complicated approach to merely firing a function if Google's maptype can't reach a certain zoom level.

Google Maps ClusterManager on API V3 not working on reload / initial load

I am trying to use the Google Maps V3 API ClusterManager from http://cm.qfox.nl/ to cluster together markers on a map, but I'm hitting the same error in my code as in the original web site - an error when the page is loaded the first time, or reloaded:
Uncaught TypeError: Cannot call method 'fromLatLngToPoint' of undefined ClusterManager_v3.js:586
ClusterManager.latlngToPoint ClusterManager_v3.js:586
ClusterManager._getMarkerBounds ClusterManager_v3.js:645
ClusterManager._cacheMarkerIcon ClusterManager_v3.js:580
ClusterManager.update ClusterManager_v3.js:345
(anonymous function) ClusterManager_v3.js:91
It works fine after the initial load, so I'm fairly sure it's because of a timing issue - e.g., the map or marker isn't initialized before it being used. Unfortunately, I can't figure out a way to wait for everything to initialize because Javascript isn't my first language. Any help or pointers would be most welcome. The source from the web site is the code I'm using almost exactly.
UPDATE:
If found that changing the line:
cm._requestUpdate(50);
to
cm._requestUpdate(250);
Prevented the error. Changing it to 150 resulted in the error occurring 3/10 times. I'm not entirely sure this is a fix, but it maybe so I'm leaving this posted just in case anyone else has a better solution or wants to know mine.
For using Projection methods , it must be initialized. Map will trigger "projection_changed" event, when Projection will be created.Only after that you can use map.getProjection(). So my suggestion is, to add "projection_changed" event's listener, and initialize ClusterManager when it is called:
google.maps.event.addListenerOnce(map, 'projection_changed', function(){
var cm = window.cm = new ClusterManager(
map,
{
objClusterIcon: new google.maps.MarkerImage('markers/cluster.png', false, false, false, new google.maps.Size(20,20)),
objClusterImageSize: new google.maps.Size(20,20)
}
);
// now json contains a list of marker positions. lets add them.
for (var i = 0; i < json.length; ++i) {
.....
}
cm._requestUpdate(50);
});

Categories