Code in question:
function setAllMap(map) {
console.debug(3);
for (var i = 0; i < markers.length; i++) {
markers[i].setMap(map);
}
resetZoomOnMapEvents(markers);
}
function resetZoomOnMapEvents(nuLoc) {
console.debug(1);
var marks = typeof nuLoc == 'undefined' ? nuLoc : markers;
var bounds = new google.maps.LatLngBounds();
for (var i in marks) {
bounds.extend(marks[i].position);
}
map.setCenter(bounds.getCenter());
map.fitBounds(bounds);
}
The map works correctly on return of search results. It will sometimes center to the center of the map markers but other times it will center on Spain/Africa. Direct question: why is the behavior not consistent?
Perhaps by sharing my script(s) it will help trigger something?
I have a loop, that for each position, I call a method called "renderMarker". That method assembles the marker.
I then call the "centerMap" method.
var GoogleMaps = {
'map': {},
'marker': {},
'markerArray': [],
'markerlatlngArray': [],
/**
* Renders marker(s) to the map.
* Creates infowindow for the given marker.
*
* #param {object} myobj| data for the given marker.
* #returns {void}
*
*/
renderMarker: function (myobj) {
var markerlatlng = new google.maps.LatLng(myobj.lat, myobj.lng);
GoogleMaps.marker = new google.maps.Marker({
'position': markerlatlng,
'map': GoogleMaps.map,
'title': my_title,
'icon': 'my-icon.png',
'animation': google.maps.Animation.DROP,
'data': '<div class="myobjinfowindow">'
'<p>Whatever you want goes here.</p>'
+ '</div>'
});
GoogleMaps.marker.set('propertyA', myobj.propertyA);
GoogleMaps.marker.set('propertyB', myobj.propertyB);
GoogleMaps.marker.set('propertyC', myobj.propertyC);
GoogleMaps.markerlatlngArray.push(markerlatlng);
GoogleMaps.markerArray.push(GoogleMaps.marker);
GoogleMaps.centerMap();
GoogleMaps.infowindow = new google.maps.InfoWindow();
google.maps.event.addListener(GoogleMaps.marker, 'click', function () {
GoogleMaps.infowindow.close();
GoogleMaps.infowindow.setContent(this.data);
GoogleMaps.infowindow.open(GoogleMaps.map, this);
});
}
/**
* Centers the map around the given markers.
*
* #returns {void}
*
*/
centerMap: function () {
var latlngbounds = new google.maps.LatLngBounds();
$.each(GoogleMaps.markerlatlngArray, function (i, marker) {
latlngbounds.extend(marker);
});
GoogleMaps.map.setCenter(latlngbounds.getCenter());
GoogleMaps.map.fitBounds(latlngbounds);
}
}
As given, the question asks very directly: "Why is it that sometimes the map recenters on an area between Spain and Arica?"
First reasonable analysis would suggest making sure that the code provided actually works.
After extensive testing and tracing, the code does work. Part of the difficulty in this is the fact that this is an inherited project and the methods used are called in multiple locations. The difficulty is increased by virtue of the fact that just loading the page launches a search which fires off these same methods and there is no inherited way to determine if the method is called on page load or user interaction. Therein lies the apparent inconsistency: Why would it load correctly 100% of the time on a manual search but not 100% of the time on initial page load search?
To ensure that this is a reasonable question, extensive testing of all modules and pages using the google maps api needed to be completed and observations noted. Indeed, all pages with this feature worked correctly on manual search and all pages but one worked correctly on initial page load search.
This begs the next logical question: is something happening on that one page to cause inconsistent behavior?
In the pursuit of an answer, javascript was traced, network responses were analyzed. Network times were not responsible (necessary analysis in case data did not exist by the time a call was completed thereby possibly altering results). The remaining conclusion is that, for some reason, sometimes something would recenter the map either before or after the calls noted in the original question.
After more analysis, a method was discovered that reset the map center to 35, 0 lat/long. This is, incidentally, a location between Spain and Africa. Removing the code which forced this recentering solved the issue.
To summarize, there is nothing wrong per se with the code submitted in the original question. The problem with a deeply "hidden" javascript method that was not readily visible and was not documented.
Related
I'm working on a site that will use the Google Maps / Places APIs. I have verified that my API key is working fine, so that's not an issue. I just can't even get the map to display - that's my issue.
I've copied the example exactly, and it still won't work.
Here's my code in a gist because it's a few files. It should be runnable, that's all the content.
What I've tried so far:
- Putting it all in a document ready function (jQuery).
- Taking it out of the getCurrentPosition function so it was just out in the main.js file.
- Moving the position of the script link in my HTML file, nothing changed at any position.
- If I console.log() the lat and long variables, they print my location. But they're "undefined" when I type them into Chrome Dev Tools console. However, my "map" variable prints the map div to console.
- Using static coords, rather than coords gathered from the navigator HTML5 element.
- Setting height and width for the map div with pixels, percent, and vh/vw. It's taking the screen - I know because I gave it a background color that is showing.
- Added parameters to the end of my API load: &callback=initialize&libraries=places
This is my first time using Maps, so I'm totally lost here. I followed Google's example to a tee, and still nothing.
WebStorm is underlining all of the google.maps.x classes and saying they're "unresolved variables or types". That may be my problem, but I don't know what causes that or how to fix it. And to be clear, I have looked through every article I could find on Stack Overflow related to this, and haven't found a solution.
UPDATE:
- I just tried copying and pasting this example from Google exactly as it appears (with my API key, of course), and it still didn't work. I put it in script tags in my HTML. The only way it works is if I delete the initMap() function - not the contents of it, just the declaration of it as a function. So why is the scope acting so weird here? It's treating everything inside of the function (which is in a script tag in my HTML) as if it's all undefined just because it's in a function... at the highest scope level.
You are trying to run main.js before the Google Maps SDK has loaded. Move it above main.js' inindex.html`:
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=MY_KEY&libraries=places"></script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
Then, remove the initialize function:
if ("geolocation" in navigator) {
console.log("Geolocation is enabled.");
navigator.geolocation.getCurrentPosition(function (position) {
var lat = position.coords.latitude;
var long = position.coords.longitude;
var map;
var service;
var infoWindow;
console.log("lat: " + lat);
//Initialize Google Map. Starts at user's location.
var userLocation = new google.maps.LatLng(lat, long);
map = new google.maps.Map(document.getElementById('map'), {
center: userLocation,
zoom: 15
});
var request = {
location: userLocation,
radius: '500',
types: ['store']
};
service = new google.maps.places.PlaceService(map);
service.nearbySearch(request, callback);
//Callback function for nearbySearch
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
var place = results[i];
createMarker(results[i]);
}
}
}
});
} else {
console.log("Geolocation is disabled.");
//Inform user why we use location
//Allow user to enter location manually
}
That works for me.
I have looked over the bing maps documentation trying to find an answer to my question. I would prefer to do everything using the bing maps api and not have to add a third party library if possible.
Question: How can I animate a pushpin to make a smooth transition from one set of gps coordinate(longitude/latitude) to another in order to simulate smooth movement of a Pushpin on Bing maps?
Question: can deleting an entire object out of the map.entities array waste enough resources to cause performance issues? If so how can I change the pushpin latitude and longitude properties without deleting the entire object?
Sample code of trying to change the pushpins properties without deleting the object out of the array. This code does not work… I am unsure why it is not working?
map.entities.get(theIndexOfThePushPin)._location.latitude = newLat;
map.entities.get(theIndexOfThePushPin)._location.longitude = newLon;
I create a pushpin like so - This works fine
map.entities.push(new Microsoft.Maps.Pushpin(new Microsoft.Maps.Location(lat, lon), {
text: text,
visible: true,
textOffset: new Microsoft.Maps.Point(0, 5)
}));
Pseudo code for my recursive angular $http call
function BusMoveGPSRefresh() {
$http.get(resourceURL)
.then(function (data) {
if ('if pushpins have not been created') {
//create pushpins...
}
} else {
//delete pushpins out of the array and then recreate them
//with updated lon/lat. Or just update existing objects lon/lat properties if possible?
}
}
BusMoveGPSRefresh();//after everything is done then go get more info and start again. recursion...
}, function (reason) {// if fail than do
console.log(reason);
console.log("This Is not Working!!! Dang!!");
});
}
Any insight into the problem would be greatly appreciated! Thanks!
I could not find a simple answer for adding animation. I did find a site that gave a step by step tutorial on a more in-depth answer on how to animate pushpins.
Answer: 1 -- tutorial on how to animate bing map pushpins
https://blogs.bing.com/maps/2014/08/07/bring-your-maps-to-life-creating-animations-with-bing-maps-javascript/
Answer: 2 -- this is how to update the pushpin location without deleting the pushpin object out of the entities array.
Instead of deleting the entire pushpin object find the pushpin index and then use the property "setLocation()" and then add a new location.
//check to make sure that the lat and lon have
//changed if it is still the same location do nothing. else update
if (map.entities.get(indexOfPushpin).getLocation().latitude != lat
|| map.entities.get(indexOfPushpin).getLocation().longitude != lon) {
map.entities.get(indexOfPushpin).setLocation(new Microsoft.Maps.Location(lat, lon));
}
function ZoomCenter(bounds, canvas)
{
var center = bounds.getCenter();
if (canvas.getMapTypeId() == "roadmap") canvas.fitBounds(bounds);
else
{
var maxZoomService = new google.maps.MaxZoomService();
maxZoomService.getMaxZoomAtLatLng(center, function(response)
{
if (response.status != google.maps.MaxZoomStatus.OK) alert("Error in Google MaxZoomService");
else
{
var max_zoom = (response.zoom > SATMAP_MAX_ZOOM) ? SATMAP_MAX_ZOOM : response.zoom;
canvas.setOptions( { maxZoom: max_zoom } );
canvas.fitBounds(bounds);
canvas.setOptions( { maxZoom: SATMAP_MAX_ZOOM} );
}
});
}
}
This first time this is called it zooms to 1 less than what will fit on the map. The second time and and every time after the first it will zoom to the correct level using the exact same bounds data.
Is there a bug in fitBounds? or am I missing something?
Without seeing more code and having never experienced this quirk myself, it's difficult to say. However I am always more suspicious that there is a bug in my own code than in Google's, so I would eliminate the obvious possibilities there first.
Therefore, the first thing I would do is use a JS debugger to confirm that:
bounds is identical both times
it's going through the same execution path hitting the same canvas.fitBounds(bounds) call both times. (You have it in there twice. Which one gets executed depends on your conditional involving MapTypeId. If canvas has a different MapTypeId each time through, that could be causing the quirk.)
If that doesn't turn up the problem, the next place to look might be the Google Maps API issue tracker.
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);
});
I'm trying to create map (using the Google Maps JavaScript API V3) which consists of several partially-transparent layers. By default, these layers should all be overlaid on top of one another to form a complete map, but the user should be able to turn any combination of them on or off (while preserving order) to create whatever view they prefer.
So far, I've had a great deal of luck getting this working for a single layer using map.mapTypes, but when adding all the layers via map.overlayMapTypes, I've hit a couple of snags:
The map doesn't seem to get fully initialized if map.setMapTypeId() is not called (no controls appear and the map is not correctly centered) and it cannot be called with an overlay.
It isn't clear how to toggle the visibility of an overlay without directly modifying the map.overlayMapTypes array, which complicates keeping them correctly ordered. I'd much prefer something analogous to the Traffic/Transit/Photos/etc. control available within Google Maps itself.
Here's the initialize function I'm working with. I'd post a link, but the map imagery isn't publicly available:
function initialize() {
map = new google.maps.Map(document.getElementById("map_canvas"), {
zoom: 0,
center: center
});
/* if these lines are uncommented, the single layer displays perfectly */
//map.mapTypes.set("Layer 3", layers[3]);
//map.setMapTypeId("Layer 3");
//return;
var dummy = new google.maps.ImageMapType({
name: "Dummy",
minZoom: 0,
maxZoom: 6,
tileSize: new google.maps.Size(256, 256),
getTileUrl: function() {return null; }
});
map.mapTypes.set("Dummy", dummy);
map.setMapTypeId("Dummy");
// layers is an array of ImageMapTypes
for (var i = 0; i < layers.length; i++) {
map.overlayMapTypes.push(layers[i]);
}
}
As you can see, I've tried creating a "dummy" maptype (which always returns null for tile URLs) to serve as the base map. While this does cause the controls to display, it still doesn't center correctly.
What's the best way to create a map which consists only of toggleable overlays?
Update: Turns out the dummy maptype works perfectly well if you also remember to set a projection. That's one problem solved, at least. :-)
I use ImageMapType, but I don't add it to mapTypes. I just add it to overlayMapTypes and when I need to remove it I use setAt to set the entry in overlayMapTypes to null.
You will need to add individual controls to the UI that toggle the individual layers.