I'm writing some Google Maps API v3 code, which seems to work just fine with multiple markers, but when there's only 1, it always plots the marker in the top left of the map, just beyond the visible area:
Here's my coffeescript code:
class SimpleMap
constructor: (div_id, lat = 40.783627, lng = -73.942583) ->
# L.Icon.Default.imagePath = "/assets"
#div_id = div_id
#map_options = {center: new google.maps.LatLng(lat, lng), zoom: 10, mapTypeId: google.maps.MapTypeId.ROADMAP}
#markers = []
#map = new google.maps.Map document.getElementById(div_id), #map_options
#loadMarkers() # gets them and plots on the map
#autoFit()
loadMarkers: ->
items = $(".grid-item[data-lat], .apartment[data-lat]")
for item in items
console.log "Adding #{item}"
#addMarker(item)
#autoFit()
addMarker: (item) ->
console.log "Adding marker"
lat = $(item).attr("data-lat")
lng = $(item).attr("data-lng")
console.log "#{lat}, #{lng}"
marker = new google.maps.Marker(
position: new google.maps.LatLng lat, lng
map: #map
title: "This is my marker"
)
#markers.push marker
autoFit: ->
bounds = new google.maps.LatLngBounds()
for marker in #markers
bounds.extend marker.getPosition()
#map.fitBounds bounds
# if you leave out the below, the marker appears int he same position as in the screenshot (slightly off screen) but at the max zoom level.
listener = google.maps.event.addListener(#map, "idle", =>
#map.setZoom 9 if #map.getZoom() > 8
#map.setCenter #markers[0].getPosition()
google.maps.event.removeListener listener
)
The map seems to ignore my attempts to set setCenter(#markers[0].getPosition()). Any ideas?
I believe the issue is in:
bounds = new google.maps.LatLngBounds()
for marker in #markers
bounds.extend marker.getPosition()
#map.fitBounds bounds
where you are extending the current map bounds to include all markers, but you have only one marker, the bounds will extend in a way that the marker will be in the map limit border.
Regards
Following the comments this issue occurs only when there is 1 marker.
Based on this fact I would neardown the problem to this line:
#map.fitBounds bounds
When there is only 1 marker, the NE-corner of bounds is equal to the SW-corner.
I noticed unexpected interactions when you use bounds as fitBounds()-argument in this case.
Suggestion:
only use fitBounds() when there are at least 2 markers.
Related
My code puts a marker on the map each time I click on it.
The objective is to get each time marker's lat/lon coordinates together with pixel coordinates. So far I've been successful only in getting lat/lon coord. The next step now would be taking these as input and compute the pixel coordinates.
<script>
function initMap() {41.85, -87.65
var myLatlng = {lat: 41.85, lng: -87.65};
var map = new google.maps.Map(
document.getElementById('map'), {zoom: 18,
center: myLatlng,
disableDefaultUI: false,
mapTypeId: 'satellite',
zoomControl: true,
mapTypeControl: true,
scaleControl: true,
streetViewControl: false,
rotateControl: false,
fullscreenControl: true});
map.setOptions({draggableCursor:'default'});
map.addListener('click', function(marker){
marker = new google.maps.Marker({map: map,
clickable: false,
position: marker.latLng,
})
var markerposLat = marker.getPosition().lat();
var markerposLon = marker.getPosition().lng();
function pixl(markerposLat,markerposLon){
var projection = map.getProjection();
var bounds = map.getBounds();
var topRight = projection.fromLatLngToPoint(bounds.getNorthEast());
var bottomLeft = projection.fromLatLngToPoint(bounds.getSouthWest());
var scale = Math.pow(2, map.getZoom());
var worldPoint = projection.fromLatLngToPoint(markerposLat,markerposLon);
return [Math.floor((worldPoint.x - bottomLeft.x) * scale), Math.floor((worldPoint.y - topRight.y) * scale)]
};
localStorage["pixl"] = JSON.stringify(pixl);
localStorage["markerLat"] = JSON.stringify(markerposLat);
localStorage["markerLon"] = JSON.stringify(markerposLon);
console.log(localStorage["pixl"],localStorage["markerLat"], localStorage["markerLon"]);
});
}
</script>
Function pixl is always undefined. I realize it's a question that have been asked many times. In fact I've tried to adapt many methods. My starting points are this: convert-lat-lon-to-pixels-and-back and of course this: showing pixel and tile coordinates. I can't spot the problem.
Please note that the fromLatLngToPoint method requires a google.maps.LatLng class as its parameter. From the documentation:
fromLatLngToPoint(latLng[, point])
Parameters:
latLng: LatLng
point: Point optional
Return Value: Point optional
Translates from the LatLng cylinder to the Point plane. This interface specifies a function which implements translation from given LatLng values to world coordinates on the map projection. The Maps API calls this method when it needs to plot locations on screen. Projection objects must implement this method, but may return null if the projection cannot calculate the Point.
So in your code, I would do it this way instead:
var worldPoint = projection.fromLatLngToPoint(marker.getPosition());
Another thing I (and #geocodezip) noticed is that you are not passing a parameter to your pixl function. This is why it is intended for you to get an undefined response. You should include a parameter like below instead in order to get the correct value:
localStorage["pixl"] = JSON.stringify(pixl((markerposLat,markerposLon)));
Here is the working fiddle for this.
I have a Google Maps script in this format:
function initMap()
{
var mapDiv = document.getElementById('map');
var map = new google.maps.Map(mapDiv, {center: {lat: 45.9, lng: 25.0}, zoom:7,
mapTypeId: google.maps.MapTypeId.ROADMAP, disableDoubleClickZoom:true, scrollwheel:false, ...});
var marker = new google.maps.Marker({position: {lat:46.1, lng:25.4}, map:map, icon:'../images/Marker.png});
}
I'd like to know how the var marker part of the script must look like if there were more than one points to be displayed. Note: I have seen JavaScript examples on StackExchange which use arrays etc. - one of the best is here: Google Maps Script - but I want my script to use the format above, which is very simple.
The google.maps.Marker class can only create a marker for a single point at at time, so you'll need some way to repeat that for all your points.
I'd say have an array of objects, each of which has the latitude and longitude values as separate properties. And any other properties you might want to associate with all your points, such as titles and icons. e.g.
var places = [
{
latitude: 46.1,
longitude: 25.4
title: "Place 1",
icon: "blue.png"
},
{
latitude: 54.1,
longitude: 0.0
title: "Place 2"
icon: "red.png"
},
// etc
];
Then when you want to create the markers, just loop over the array:
for (var i = 0; i < places.length; i++) {
marker = new google.maps.Marker({
position: {lat:places[i].latitude, lng: places[i].longitude},
title: places[i].title,
map: map,
icon: '../images/' + places[i].icon
});
}
Edit:
Question = "is there a way to loop through the array and check if each location (long/lat) falls within the current viewport directly" (failing that get all markers within the viewport)
Background:
I have an array of locations (lat, long, id).
I want to:
On a Google Map, use the location array to display markers.
The user can scroll/zoom the map.
Have a button underneath the map, so when the user has decided on an area, he can click the button, and the code will return the ids (from the location array) that are contained within the viewport / map bounds.
There is a .contains for Google, so I guess you could potentially use that with something like
map.getBounds().contains and somehow reference each marker.getPosition()
but I wonder if there's a way to loop through the array and check if each location (long/lat) falls within the current viewport directly
You mean something like this (not tested), map is the google.maps.Map object and needs to be in scope. markersArray is the array of markers.
for (var i=0; i< markersArray.length; i++) {
if (map.getBounds().contains(markersArray[i].getPosition())) {
// the marker is in view
} else {
// the marker is not in view
}
}
http://jsfiddle.net/UA2g2/1/
Thanks geocodezip, you gave me the idea on how to solve it via looping through the array. I don't know if this is the most efficient way, but I put together some code that seems to do what I want - if you check the jsfiddle above and view console you can see that it logs when and which points are in the viewport.
$(document).ready(function(){
var myOptions = {
center: new google.maps.LatLng(51, -2),
zoom: 9,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var storeArray = new Array(["51.38254", "-2.362804", "ID1"], ["51.235249", "-2.297804","ID2"], ["51.086126", "-2.910767","ID3"]);
google.maps.event.addListener(map, 'idle', function() {
for (i = 0; i < storeArray.length; i++) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(storeArray[i][0], storeArray[i][1]),
map: map
});
}
for (var i=0; i<storeArray.length; i++) {
if (map.getBounds().contains(new google.maps.LatLng(storeArray[i][0], storeArray[i][1]))) {
console.log("marker: " + storeArray[i][2]);
}
}
});
});
i've placed around 3 hundreds of markers on google map and i also need to highlight some regions with Cirlce on it. So i've placed 3 circles on the map. I wanna show markers only inside circle, not outside the circles. Circles can be placed any where(say fixed radius) and markers shoud be inside circles. all marking and placing circles are in javascript. I'm fresh grad and new to javascript.
Pradi
The part where i draw circle.
for (var i = 0; i < ltlg.length; i++) {
var marker = new google.maps.Marker({
map: map,
position: ltlg[i],
visible: false
});
var circle = new google.maps.Circle({
map: map,
radius: 10 * 55000,
fillcolour: '#AA0000'
});
circle.bindTo('center', marker, 'position');
}
where ltlg[i] is array containg 3 lats and longs.
and part where i mark
for (var i = 0; i < ltlg1.length; i++) {
// var detail = cityDetails[i];
var markerSnowFall = new google.maps.Marker({
map: map,
icon: Image,
position: ltlg1[i]
});
Here ltlg1[i] contains some 300 hundred lats and longs.
Problem is that i dont know how to make connection between marking and placing circles, currently both(marking use ltlg1[i] and cicle use ltlg[i]) use different arrays.
Either make a loop that for each of the markers checks if the distance to the center of the circle means that the marker is within the circle and only show the markers that fits this
or if the way you are placing the markers on a map supports it, only send markers that should be drawn (for instance in a previous project we had the position of the markers in a db that we could query for all markers within a circle)
The approach I took thus far has been:
function addMarker( query ) {
var geocoder = new google.maps.Geocoder();
var afterGeocode = $.Deferred();
// Geocode 'query' which is the address of a location.
geocoder.geocode(
{ address: query },
function( results, status ){
if( status === 'OK' ){
afterGeocode.resolve( results ); // Activate deferred.
}
}
);
afterGeocode.then( function( results ){
var mOptions = {
position: results[0].geometry.location,
map: map
}
// Create and drop in marker.
var marker = new google.maps.Marker( mOptions );
marker.setAnimation( google.maps.Animation.DROP );
var current_bounds = map.getBounds(); // Get current bounds of map
// use the extend() function of the latlngbounds object
// to incorporate the location of the marker
var new_bounds = current_bounds.extend( results[0].geometry.location );
map.fitBounds( new_bounds ); // fit the map to those bounds
});
}
The problem I'm running into is that the map inexplicably zooms out by some amount, no matter if the new marker fits within the current viewport or not.
What am I doing wrong?
ADDENDUM
I added logs and an additional variable to capture the map bounds after the transition was made (new_new_bounds)
current_bounds = // Map bounds before anything is done.
{-112.39575760000002, 33.60691883366427},
{-112.39295444655761, 33.639099}
new_bounds = // From after the extend
{-112.39295444655761, 33.60691883366427},
{-112.39575760000002, 33.639099}
new_new_bounds = // From after the fitbounds
{-112.33942438265382, 33.588697452015374},
{-112.44928766390382, 33.657309727063996}
OK, so after much wrangling, it turns out that the problem was a map's bounds are not the same as a map's bounds after fitBounds(). What happens (I presume), is Google takes the bounds you give it in the fitBounds() method, and then pads them. Every time you send the current bounds to fitBounds(), You're not going to fit bounds(x,y), you're going to fit bounds(x+m,y+m) where m = the arbitrary margin.
That said, the best approach was this:
var current_bounds = map.getBounds();
var marker_pos = marker.getPosition();
if( !current_bounds.contains( marker_pos ) ){
var new_bounds = current_bounds.extend( marker_pos );
map.fitBounds( new_bounds );
}
So, the map will only fit bounds if a marker placed falls outside the current map bounds. Hope this helps anyone else who hits this problem.
A possible explanation is that you randomly placed your new marker into the gap of the z-curve. A z-curve recursivley subdivide the map into 4 smaller tiles but that's also the reason why there are gaps between the tiles. A better way would be to use a hilbert curve or a moore curve for map applications. There is a patented search algorithm covering this issue, I think it is called multidimensional range query in quadtrees. You want to look for Nick's hilbert curce quadtree spatial index blog.