fitBounds() shows whole earth (if map is first hidden and then shown) - javascript

I have a bunch or markers, and I want to show only the area containing them. I found a long list of similar questions (see at the bottom of the post for some), but none of the solutions works for me. The LatLngBounds is built correctly, but when I call fitBounds the result will be the following:
Instead of:
Can anybody spot an evident error in my code?
var opt = {
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map"),opt);
var box = new google.maps.LatLngBounds();
for(var i=0;i<list.length;i++){
var p = new google.maps.LatLng(list[i].lat,list[i].lon);
var marker = new google.maps.Marker({
position: p,
map: map
});
box.extend(p);
}
map.fitBounds(box);
map.panToBounds(box);
Some of the posts I read and tried (list not comprehensive):
Google Maps v3 - Automating Zoom Level?
Google maps V3 custom marker images and fitBounds()
Google Maps with fitBounds don't zoom
fitbounds() in Google maps api V3 does not fit bounds
Edit: this actually happens if (as I do in my application) the map is at first hidden, and showed only later.
I hide it in this way:
$('#map').hide();
and show it:
$('#map').show(function(){
//this is necessary because otherwise
//the map will show up in the upper left corner
//until a window resize takes place
google.maps.event.trigger(map, 'resize');
});
Any clue as to why this happens and how to prevent it (apart from initialising the map when first shown)?
On a side note, if I set zoom and center when declaring the map object (i.e. I don't use fitBounds()) then the map will show correctly, even after a hide/show.
I can't set zoom and center, though, because the list of points is retrieved elsewhere and I don't know where they are beforehand.

Solved (not in a nice way, though).
What I ended up doing was initialising the LatLngBounds with the points when loading the page, but panning and zooming only when showing the map. In this way it works correctly.
E.g.
var box;
function init(){
var opt = {
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map"),opt);
box = new google.maps.LatLngBounds();
for(var i=0;i<list.length;i++){
var p = new google.maps.LatLng(list[i].lat,list[i].lon);
var marker = new google.maps.Marker({
position: p,
map: map
});
box.extend(p);
}
}
and then, later (click on a button for example)
function showMap(){
$('#map').show(function(){
google.maps.event.trigger(map, 'resize');
map.fitBounds(box);
map.panToBounds(box);
});
}
It works, but I don't like to have that global var hanging around. I implement the exact same behavior using OpenLayers, and it works correctly without the need for this hack. If anybody has a better solution, please post it and I will accept it if it works.
Thanks to #Engineer and #Matt Handy for helping me eliminate one possible source of errors.

I tried your code in a fiddle, and it works as expected.
So the reason why your code fails must be in the definition of your datapoints (as already suggested by Engineer). Compare your list definition with mine and check if they are different.

Modify to your needs
map.fitBounds(bounds);
var listener = google.maps.event.addListener(map, "idle", function() {
if (map.getZoom() > 16) map.setZoom(16);
google.maps.event.removeListener(listener);
});

same problem, found the reason is that I hide the map (make the container of the map display: none;) before calling fitbounds()

To expand a bit on #JayThakkar 's answer, this worked for me
google.maps.event.addListenerOnce(map, 'idle', function(){
map.fitBounds(bounds);
});
The addListenerOnce function removes the need to call google.maps.event.removeListener(listener);.
And calling map.fitBounds(bounds) inside the listener let us use the calculated bounds's zoom level.

Related

gmaps.js and marker clustering - same location

Using gmaps.js and having an issue with marker clustering... it works perfectly fine although in my case I may have markers with the exact same location (lat/lng).
What happens is when you zoom to the highest level the cluster disappears and there are no markers shown at all (I thought they would be layered over each other so at lease one is viewable, but that is not the case for me). I have found some solutions on here which change the lat/lng values slightly so each marker has a different location, but in reality like 100 meters difference or something. I think this would work find in my situation, but I do not know how to implement these solutions since I am using gmaps.js.
Current working code, but the problem mentioned above :
response.results in an array of my location data retrieve with ajax...
//create the map
map = new GMaps({
el: '#map_canvas_main',
lat: response.results[0].lat,
lng: response.results[0].lng,
panControl : false,
markerClusterer: function(map) {
return new MarkerClusterer(map);
}
});
//loop through array
for(var i = 0; i < response.results.length; i++)
{
//add marker
map.addMarker({
lat: response.results[i].lat,
lng: response.results[i].lng,
title: response.results[i].ip_address,
infoWindow: {
content: '<p>'+response.results[i].ip_address+'</p>'
}
});
}
EDIT :
I added a fiddle mimicking multiple markers at the same location. Strangely in the fiddle the 'cluster marker' does not disappear yet in my site it does. What happens on my site is you can zoom in on the marker any way you want... when you reach the highest zoom level and click on it... it disappears. You need to zoom out or pan around for the image to come back. This does not happen in the fiddle though.
Regardless of that though - I still need a method in which to show the information for all the markers in the same location... most of the solution I found here on SO change the lat/lng ever so slightly to avoid this...
so link
fiddle

Set Google maps coordinates as center screen

I wrote a script which allows an admin to click on a map (google maps api v3) setting up as many locations as he wants, naming them. That is, clicking on the map shows an information window with a form for naming that location. These locations are saved in a database (LatLng coordinates) and show as markers on the map.
Then, users (admin or not) may choose any of these locations from a select box (by name). Once this is selected, there's an option for showing a map with a marker on the selected location. The marker shows correctly, but I would like the map to be centered in that location. That is, the marker in the center of the screen but, instead, it shows in the extreme upper left corner. Actually, initially, the marker isn't even visible. You have to drag the map up and left a little for it to show.
This is rather strange, as I use the same coordinates for both the marker and the center screen. Here's my code (or the relevant part, at least):
var latlng = new google.maps.LatLng(parseFloat(location.lat), parseFloat(location.ltn));
var latlng2 = new google.maps.LatLng(parseFloat(location.lat) + 0.025, parseFloat(location.ltn) - 0.05);
var map = new google.maps.Map($('.modal-body', modal)[0], {
'center': latlng,
'zoom': 13,
'mapTypeId': google.maps.MapTypeId.ROADMAP,
});
var marker = new google.maps.Marker({
'position': latlng,
'map': map,
'title': location.name,
});
marker.setMap(map);
Notice how in this sample I don't use the latlng2 value. This is what I tried, but it seems to only work for a given resolution. How can I center the map in those coordinates?
Best regards
Edit:
$(map).focus();
modal.on('shown', function() {
$(window).resize(function() {
google.maps.event.trigger(map, 'resize');
});
google.maps.event.trigger(map, 'resize');
});
I'm not sure if this will work... If that window resize is cause than it might help. I don't know how it will interact with other code.
Add event listener for resize event:
...
marker.setMap(map);
google.maps.event.addListener(map, 'resize', function() {
map.setCenter(latlng);
});

Custom Map Types: repeating maps and markers. How to adding padding to map?

With the Google Maps API (v3) I've created a custom map type for a fictional game world. By default, maps, even custom map types, repeat horizontally (see image below).
Larger Image here
Is it possible to keep the map from repeating horizontally? For my map, it does not represent a planet or spherical world, so having it repeat horizontally forever doesn't make sense at all. I have figured out how to simply not load tiles for the repeated maps on the left and right like so:
Larger Image here
HOWEVER, when you create markers, the markers still show up for all the repeated maps:
Larger Image here
Is it possible to keep the markers from repeating? Or is it possible to keep the map from repeating at all? That way I don't have to deal with markers repeating?
Work Around: Limit Panning beyond the Map Bounds
I've read various work-arounds that discuss simply limiting how far the user can pan to the left or right. This won't work for me because I have to allow the user to zoom all the way out and view the entire map at once. If they zoom all the way out, repeated markers are still visible, which is unacceptable.
Is it possible to adding a bunch of padding to the map? That way there is a large amount of space between the maps:
Larger Image here
If I was able to add enough padding, then limiting the panning would work for me, because any repeated markers could be pushed far enough away by the padding that the user would never see them.
Finally my code, pretty simple:
(note: the map tile images I'm using are not available online yet)
<!DOCTYPE html>
<html style='height: 100%'>
<head>
<link rel="stylesheet" type="text/css" href="normalize.css" />
<style>
html, body { height: 100%;}
#map_canvas { height: 1000px;}
</style>
</head>
<body style='height: 100%'>
<div id="map_canvas"></div>
<script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script type='text/javascript'>
var options =
{
getTileUrl: function(coord, zoom)
{
// Don't load tiles for repeated maps
var tileRange = 1 << zoom;
if ( coord.y < 0 || coord.y >= tileRange || coord.x < 0 || coord.x >= tileRange )
return null;
// Load the tile for the requested coordinate
var file = 'images/zoom' + zoom + '/tile_' + zoom + '_' + (coord.x) + '_' + (coord.y) + '.jpg';
return file;
},
tileSize: new google.maps.Size(256, 256),
minZoom: 1,
maxZoom: 9,
radius: 1738000, // I got this from an example in the api, I have no idea what this does
name: 'Map',
};
var mapOptions =
{
center: new google.maps.LatLng(0,0),
zoom: 2,
backgroundColor: '#000',
streetViewControl: false,
mapTypeControl: false
};
var map = new google.maps.Map(document.getElementById('map_canvas'),mapOptions);
var mapType = new google.maps.ImageMapType(options);
map.mapTypes.set('map', mapType);
map.setMapTypeId('map');
var marker = new google.maps.Marker({
position: new google.maps.LatLng(0,0),
map: map,
title: "Test"
});
</script>
</body>
</html>
In answer to the question: Is it possible to keep the markers from repeating?
Yes.
From Google Maps JavaScript API V3 Reference (3.19), if you set the markerOptions property optimized to false for your marker, it does not repeat but only shows up on the center map.
See: https://developers.google.com/maps/documentation/javascript/reference#MarkerOptions
So, in your code, I would modify var marker as such (adding optimized: false):
var marker = new google.maps.Marker({
position: new google.maps.LatLng(0,0),
map: map,
title: "Test"
optimized: false
});
According to Google's docs (I've added the bolding),
Optimization renders many markers as a single static element. Optimized rendering is enabled by default. Disable optimized rendering for animated GIFs or PNGs, or when each marker must be rendered as a separate DOM element (advanced usage only).
I set optimized to false and then looked through the page to find the id (or at least class) associated with my markers. I was going to make the "extra" markers non-visible. It turns out the elements are there but have no id or class. Just as I was contemplating other ways to identify them using jQuery, I happened to look up at my "map" and realized the "extra" markers were gone! ☺
A word of caution: based on Google's docs, I suspect this behavior (the "extra" markers not showing up) may be an unintended "feature".
Cheers,
Bruce.
Looks to me like you just need to change your starting zoom and min zoom limit.
Even google runs into repeats when you are at zoom level 1, but it doesn't let you zoom out lower than that.
Just add minZoom and maxZoom properties to your options object to limit the zooming.
You can try to put a mask on the repeated area but I didn't tried it. This looks like it can solve your problem: Apply mask to Google Map.
Update: Apply the mask only when you need it, i.e. when the zoom is the lowest. I don't think resizeing the browser window will affect anything in the map. It's also has nothing to do with the question and the problem is if the mask lays on top of the marker.
Update 2: It's seems to be possible with v2 but not with v3. In v2 you can disable horizontal copies in the projection class: Google Maps API v3 with custom map image - markers repeating horizontally.
For those who has still this problem, have a look at my solution.
1- Set the maps zoom to (2) and add marker positions (lat,long) i.e
var minZoomLevel = 2;
map.setZoom(minZoomLevel);
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < result.length; i++){
var latlng = new google.maps.LatLng(result[i].Lat, result[i].Lng);
bounds.extend(latlng);
});
2- Attach a event listener on zoom changed i.e
google.maps.event.addListener(map, 'zoom_changed', function() {
if (map.getZoom() < minZoomLevel) map.setZoom(minZoomLevel);
});
3- Attach a center changed listener (This done the trick) i.e
google.maps.event.addListener(map, 'center_changed', function()
{
checkBounds(bounds);
}
function checkBounds(allowedBounds) {
if(allowedBounds.contains(map.getCenter())) {
return;
}
var mapCenter = map.getCenter();
var X = mapCenter.lng();
var Y = mapCenter.lat();
var AmaxX = allowedBounds.getNorthEast().lng();
var AmaxY = allowedBounds.getNorthEast().lat();
var AminX = allowedBounds.getSouthWest().lng();
var AminY = allowedBounds.getSouthWest().lat();
if (X < AminX) {X = AminX;}
if (X > AmaxX) {X = AmaxX;}
if (Y < AminY) {Y = AminY;}
if (Y > AmaxY) {Y = AmaxY;}
map.setCenter(new google.maps.LatLng(Y,X));
}
Every time you change the center, it will check your points and restrict map to certain area . Setting zoom will show only one world tile, and check bound will restrict the horizontal scrolling !

Google Maps API V3 fromDivPixelToLatLng not consistent

I need to place a marker at a fixed pixel location within the map's div. To instantiate a marker, you need a LatLng. I understand that fromDivPixelToLatLng() is the way to convert from pixel co-ordinates to a LatLng, but I can't get it to behave consistently.
I have posted a simple example of my problem at http://www.pinksy.co.uk/newsquare/overlaytest.html. Click on the map to place a marker at 200px/200px. Drag the map around and click again. I was expecting a marker to be placed at 200px/200px every time, but this is not the case.
First I set up the map as usual, in a 600px by 300px div:
var london = new google.maps.LatLng(51.501904,-0.130463);
var mapOptions = {
zoom: 15,
center: london,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
Then I create an overlay:
var overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(map);
To test fromDivPixelToLatLng(), I create a click event on the map, which attempts to place a marker at pixel location 200px/200px. Regardless of where you drag the map, I was expecting the marker to always be placed at 200px/200px:
google.maps.event.addListener(map, 'click', function(event) {
var pixelLatLng = overlay.getProjection().fromDivPixelToLatLng(new google.maps.Point(200,200));
var marker = new google.maps.Marker({
position: pixelLatLng,
map: map
});
});
However, drag the map around, and you will see that the marker is not always placed at 200px/200px. Any ideas?
Thanks!
After experimentation, I have found that fromContainerPixelToLatLng() is what I'm looking for. For the benefit of others, I have posted an example at http://www.pinksy.co.uk/newsquare/overlaytest2.html.
(For the record, I'm still unsure why fromDivPixelToLatLng behaves the way it does, but never mind!)
Check the demo under:
http://jsbin.com/otidih/51 for some more experiments on this.
To get the logging start the console - most things are logged there.
Detailed explanation from this groups post.
A shorter version below:
The ContainerPixel is calculated relative to your map container. If you pan the map, then the ContainerPixel of LatLngs changes.
The ContainerPixel of things that don't move with the map (float) doesn't change.
For example, the ContainerPixel of the mapCenter stays the same if you don't resize the map:
overlay.getProjection().fromLatLngToContainerPixel(map.getCenter())
The DivPixel is calculated relative to a huge Div that holds the entire tilespace for the world at the current zoom level.
overlay.getProjection().fromLatLngToDivPixel(point)
If you do not change the zoom level and move (pan) the map, then the DivPixel of anything that moves with the map will stay the same. For example the DivPixel of a given city on a map will stay the same, even if you move the map. It will only change when you change the zoom level or cross the international dateline.
Please note that the actual reference point used for calculating the DivPixel gets reset
whenever the map zooms, so the same LatLng can have different DivPixel values even when you come back to the same zoom level.
Also to be considered is the Point value returned from
map.getProjection().fromLatLngToPoint()
which is well explained in the API Reference
It translates from the LatLng cylinder to the big point plane which always stays the same (no matter which zoom level). Given LatLngs will always map to the same Point.
The (0,0) point is the (85.0511287798066,-180) LatLng - where to Google Map cuts of (if you want to know why , read about the Mercator projection)

Google Maps API - Strange Map "Offset" Behaviour

I'm adding a map next to a form on my web page. The idea is that people can sign up, and when they type in their address and click search, it place-marks their house, they must do this before they submit the form (geolocation has a large role on my site).
Unfortunately, I have added the map, but within the map window the map itself appears to be offset. See the image below to illustrate what I mean:
I can only drag the map from within the "mapped" part of the box. If I select the grey area to drag the map around, it fails.
Any ideas what could cause this?
EDIT:
Here is my map-initialising Javascript, called on page load.
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(52.428385,-3.560257);
var myOptions = {
zoom: 7,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("adminMap"), myOptions);
Here is my map CSS
#adminMap{
float:left;
width:270px;
height:370px;
margin-left:20px;
}
Here is my map HTML
<div id="adminMap">
</div>
I've seen things similar to this. Without access to any code, my best bet is that the map is initialized at a time when the container div is hidden. I've seen that cause such symptoms. Try to set up your map as you're showing it, rather than before.
To render the map fully again on changing div visibility you can use this code.
google.maps.event.trigger(map, 'resize');
This will rerender your map inside the div and will display it fully.

Categories