Google Maps javascript API + HTML5 geolocation quirk - javascript

I'm having a rather strange issue with the Google Maps javascript API and HTML5 geolocation.
I have the following executed onload:
var geocoder;
var map;
var latlng;
function initialize() {
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(function(position){
latlng = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
});
}else{
latlng = new google.maps.LatLng(geoip_latitude(),geoip_longitude());
}
geocoder = new google.maps.Geocoder();
var myOptions = {
zoom: 8,
center: latlng,
streetViewControl: false,
mapTypeId: google.maps.MapTypeId.HYBRID
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
google.maps.event.addListener(map, 'click', function(event) {
placeMarker(event.latLng);
var latLong = String(event.latLng).replace('(', '');
latLong = latLong.replace(')', '');
$('#latlng').attr('value', latLong);
});
function placeMarker(location) {
if(undefined != initialize.marker){
initialize.marker.setMap(null);
}
initialize.marker = new google.maps.Marker({
position: location,
map: map
});
initialize.marker.setMap(map);
map.setCenter(location);
}
}
Now, what this should do is if the browser supports geolocation, get the lat+long, create a map, and display the map centered on the lat+long.
It works just fine if I use the geoip_ functions (maxmind), but the geolocation has a strange issue:
If the code is run as is, the google maps display shows up as a gray box with no map and no controls.
If I add an alert(); immediately after
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(function(position){
latlng = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
});
}else{
latlng = new google.maps.LatLng(geoip_latitude(),geoip_longitude());
}
the map displays just fine.
What's up with that?

The W3C Geolocation API is asynchronous; the success callback function you have (which sets the variable latlng) won't be called immediately -- the browser requires some time to actually do the geolocating; in fact, if the user takes some time to read the privacy prompt and decide whether or not to give permission, this process could take many seconds. In the meantime though, your map is loaded immediately.
You're probably finding that it works with an alert after it because the alert gives the browser some time to finish the geolocation process and call the callback before getting to the code that loads the map. (Just waiting a second or two isn't a good solution though: some users and browsers will take much longer than that before revealing location.)
To fix: call the function that creates the map (you'll want to encapsulate practically everything after the geolocation call) and call it both in the success callback for getCurrentPosition and in the geoip else branch. That way you'll only try to load the map after you're guaranteed that the latlng variable has been appropriately filled.

I worked through this mess for the first time this morning (honestly I am surprised the maps API doesn't have a function to deal with this). Here is My Solution: JSFIddle

Related

How can I keep zoom on Selected Marker after a page refresh?

I'm building a tracking application that use Google Maps and as a result has multiple markers the user can click on. The page will eventually refresh every 15 minutes or so to keep up with the latest tracking data. However I want to give the the user to stick with the selected marker even after a page refresh so if needs be they can follow the selected vehicle (which the marker represents).
I've tried to achieve this using the local storage. I can get the lat and long into the storage no problem but when I come to get it out I've found that the longitude has come out with too many decimal places so the API won't recognise the position.
To go to the marker after the refresh my code is like this:
var centre = {lat: 53.806590, lng: -1.548437};
var centreRefresh = localStorage.getItem("markerPos");
// Draw the map
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 6,
center: centre
});
if (centreRefresh != null && $("#zoomRefresh").is(':checked')) {
map.setZoom(8);
map.setCenter(centreRefresh);
}
And to fill the local storage I have this:
// Add Info Window to Marker
marker.addListener('click', function() {
infowindow.open(map, marker);
if ($("#zoomRefresh").is(':checked')) {
markerPos = marker.getPosition();
localStorage.setItem("markerPos", markerPos);
}
map.setZoom(8);
map.setCenter(marker.getPosition());
});
The optput in the console from that is set in the local storage is "(55.823772, -2.8525829999999814)" and the error message is InvalidValueError: setCenter: not a LatLng or LatLngLiteral: not an Object Is there a better way to do this as this method doesn't seem to be working correctly?

Google Map API file field with Place-Id

I'm using the latest Google map API v3 and trying to accomplish the following:
Center users on the map when the page loads on a mobile device. I have this working in the code below.
The ability for users to click/touch a POI from the map and view the info window. This is also working since its a basic feature of google maps.
This is were I'm stumped. I need to be able to populate 2 fields with the name of the POI from the Info-Window in the 1st field and the 2nd field the Place-ID.
Here's what I've found so far:
https://developers.google.com/maps/documentation/javascript/examples/places-placeid-finder
This link allows searching of places and the marker that is populated produces a Info-Window with the Place-ID of the searched location. But if you click on any of the POIs outside the marker, there are no Place_IDs.
The other close solution:
Get placeId on google maps when POI is clicked
This solution allows you to click and see the Place-ID but there are 2 main issues. 1st, the same POI from the first link above produces a different Place-ID than this solution for the exact same location. Additionally it produces a Place-ID for any point on the map regardless if a POI is near.
Here's my code thus far:
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 17,
streetViewControl: false,
mapTypeControl: false
});
var infoWindow = new google.maps.InfoWindow({map: map});
// Try HTML5 geolocation.
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
infoWindow.setPosition(pos);
infoWindow.setContent('Location found.');
map.setCenter(pos);
}, function() {
handleLocationError(true, infoWindow, map.getCenter());
});
} else {
// Browser doesn't support Geolocation
handleLocationError(false, infoWindow, map.getCenter());
}
}
function handleLocationError(browserHasGeolocation, infoWindow, pos) {
infoWindow.setPosition(pos);
infoWindow.setContent(browserHasGeolocation ?
'Error: The Geolocation service failed.' :
'Error: Your browser doesn\'t support geolocation.');
}
I would greatly appreciate any light that could be shed on why the Place-IDs are always different between these methods and possible solutions on getting the true Place-ID from a click event. Thank you!
You can listen the click events on the map. If you click on the POI icon the IconMouseEvent will be raised.
According to the documentation:
google.maps.IconMouseEvent object specification
This object is sent in an event when a user clicks on an icon on the map. The place ID of this place is stored in the placeId member. To prevent the default info window from showing up, call the stop() method on this event to prevent it being propagated. Learn more about place IDs in the Places API developer guide.
This object extends MouseEvent.
Please have a look at this sample code to figure out how to use events with Place ID:
http://jsbin.com/parapex/10/edit?html,output
Hope it helps!

Getting marker from map.data class use geojson google maps

first of all I was initiate marker from geojson, and how I can get the marker if i want use marker for listener/action?
this is my script
var map;
function initMap() {
//makes map
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -6.9034482, lng: 107.6081381},
zoom: 9,
styles: [{"featureType":"water","stylers":[{"saturation":43},{"lightness":-11},{"hue":"#0088ff"}]},{"featureType":"road","elementType":"geometry.fill","stylers":[{"hue":"#ff0000"},{"saturation":-100},{"lightness":99}]},{"featureType":"road","elementType":"geometry.stroke","stylers":[{"color":"#808080"},{"lightness":54}]},{"featureType":"landscape.man_made","elementType":"geometry.fill","stylers":[{"color":"#ece2d9"}]},{"featureType":"poi.park","elementType":"geometry.fill","stylers":[{"color":"#ccdca1"}]},{"featureType":"road","elementType":"labels.text.fill","stylers":[{"color":"#767676"}]},{"featureType":"road","elementType":"labels.text.stroke","stylers":[{"color":"#ffffff"}]},{"featureType":"poi","stylers":[{"visibility":"off"}]},{"featureType":"landscape.natural","elementType":"geometry.fill","stylers":[{"visibility":"on"},{"color":"#b8cb93"}]},{"featureType":"poi.park","stylers":[{"visibility":"on"}]},{"featureType":"poi.sports_complex","stylers":[{"visibility":"on"}]},{"featureType":"poi.medical","stylers":[{"visibility":"on"}]},{"featureType":"poi.business","stylers":[{"visibility":"simplified"}]}]
});
//load marker from geojson
map.data.loadGeoJson('<?php echo base_url().'index.php/json_site/geojsongetmap'?>');
// set style marker
map.data.setStyle(function(feature){
var tit = feature.getProperty('nm_site');
return{
title: tit,
icon: '<?php echo base_url()?>assets/images/mark3.png'
};
});
//marker event
map.data.addListener(marker, 'click', function(event) {
map.setZoom(11);
map.setCenter(marker.getPosition()); // I need to get the position of the marker who i clicked
});
}
how I can make action listener if I initiate marker from geojson?
and how I can get the marker who existing in my map?
please help me, any suggest would be appreciated
thanks
Instances of the google.maps.Data.Point class are not exactly a drop-in replacement for traditional google.maps.Marker objects. For starters, they are abstract data, not tied to a particular representation. It's up to the parent google.maps.Data layer to decide how to draw them.
However, you can still capture events, with the caveat that the click happens on the Data layer, which receives a mouseEvent as argument. This argument contains the feature over which you just clicked.
This means you need to declare:
google.maps.event.addListener(map.data,'click',function(mouseEvent) {
var clickedFeature = mouseEvent.feature,
featureGeometry = clickedFeature.getGeometry(),
featurePosition = featureGeometry.get();
map.setCenter(featurePosition);
});
Please take into consideration that ingesting geoJson with the Data layer can result not just in Point geometries. If you get a mix of points, polygons and linestrings, anything different from a point won't return a latLng object when you call its get method.

Google map error: a is null

I have a program that I want to use google maps for. The problem is I get an error that says a is null where a is a var used in the google map api. Here is how I call my google map:
//Creates a new center location for the google map
var latlng = new google.maps.LatLng(centerLatitude, centerLongitude);
//The options for the google map
var myOptions = {
zoom: 7,
maxZoom: 12,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
//Creates the new map
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
And here is what my HTML tag looks like:
<div id = "map_canvas"></div>
I get the lat and lng on page load through the url. These values are passed in correctly so I know that is not the problem. I think that it has to do with the var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions); not being correct. Any suggestions?
EDIT: Here is the error message:
a is null
fromLatLngToPoint(a=null)
yg(a=null, b=Object { zoom=7, maxZoom=12, more...})
d(d=Document Default.aspx?lat=30.346317&lng=105.46313, f=[function()])
d(a=undefined)
d()
[Break On This Error] function Qf(a){a=a.f[9];return a!=i?a:...);function sg(a){a[ic]&&a[ic]Vb}
Make sure you specify the size of the element that holds the map. For example:
<div id="map_canvas" style="width: 500px; height: 500px;"></div>
Also make sure your map variable is defined in the global scope and that
you initialize the map once the DOM is loaded.
You are probably not listening for the onload event that fires when the page is completely loaded. As a result, your script is running but the div you are creating doesn't yet exist. Use jQuery to listen for this event, like so:
$(document).ready(function () {
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
});
If you don't want to use jQuery, then add an event listener to body.onload
This rather cryptic error means that the script can't find the map div.
This could happen for a couple of reasons.
1. You're using the wrong ID to refer to the map.
Check your ids (or classes) and make sure the element you're referring to actually exists.
2. You're executing the script before the DOM is ready.
Here's a jQuery example. Notice we're triggering initialise on document ready, not onDOMReady. I've taken the liberty of wrapping the script in a closure.
(function($) {
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(-34.397, 150.644),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map"),
mapOptions);
}
$(document).ready(initialize);
})(jQuery)
You could also use:
google.maps.event.addDomListener(window, 'load', initialize);
if you prefer a Google solution.
Had the exact same problem and this is have i fixed it for me.
The thing was that I had 2 google maps in my website - one in the footer and the other one on the contact page, but i called them both in one JS file like so:
var map1 = new google.maps.Map(document.getElementById("map-canvas-footer"), settings1);
var map2 = new google.maps.Map(document.getElementById("map-canvas"), settings2);
But the thing is that the object with id="map-canvas" was located only on the contact page.
So at first you have to check if that element exists on the page like so:
if ($("#map-canvas-footer").length > 0){
var map1 = new google.maps.Map(document.getElementById("map-canvas-footer"), settings1);
}
if ($("#map-canvas").length > 0){
var map2 = new google.maps.Map(document.getElementById("map-canvas"), settings2);
}
I hope this can help someone else as well ;)
This happens when the map is not yet loaded. You should build your map when the Maps API JavaScript has loaded. Executing the function to initialize your map only when the API has fully loaded passing it to the "callback" parameter in the Maps API bootstrap.
function initialize() {
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(-34.397, 150.644),
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
}
function loadScript() {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "http://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&sensor=TRUE_OR_FALSE&callback=initialize";
document.body.appendChild(script);
}
window.onload = loadScript;
This is actually in the Maps API Docs here. Hope this helps!
Make sure that your canvas div (Div associated with the map) exists.
Sometimes, if we rename the div's id attribute.
Then it creates problem as it does not get the canvas div.
I got this error once. Make sure the map script runs only on pages using the map. You can check if the map exists by using an "if". Something like this:
if ($('mapClass').length>0) { // here you run the google maps functions }
See ya
Solved, the google map type a error, make sure you get object var map = document.getElementById('map-canvas') returning properly using alert(map). Check the div container id name same as specified in getElementByid.
I have also stuck with the same type a error, fixed it by checking getElementByid('map-canvas'). Sample code enter link description here
I have fixed it removing my "style" property from the "div" tag an declaring it correctly, in a css file
My response is bit old but for those who still come here for reference, I have a similar solution. I put map initialization code as specified here https://developers.google.com/maps/documentation/javascript/adding-a-google-map
inside jQuery document ready function. Below is the code that worked in my case:
$(document).ready(function () {
var uluru = {lat: -25.344, lng: 131.036};
var map = new google.maps.Map( document.getElementById('embedmap'), {zoom: 14, center: uluru} );
var marker = new google.maps.Marker({position: uluru, map: map});
});

Google Maps V3: Only show markers in viewport - Clear markers issue

I like to create a map with Google Maps that can handle large amounts of markers (over 10.000). To not slow down the map I've created a XML-file that only outputs the markers that are inside the current viewport.
First, I use initialize() to setup the map options:
function initialize() {
var myLatlng = new google.maps.LatLng(51.25503952021694,3.27392578125);
var myOptions = {
zoom: 8,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
google.maps.event.addListener(map, 'tilesloaded', function () {
loadMapFromCurrentBounds(map);
});
}
When the event 'tilesloaded' is finished, I use loadMapFromCurrentBounds(), this functions will get the current bounds and sends a request to the XML-file to show the markers that are inside the current viewport:
function loadMapFromCurrentBounds(map) {
// First, determine the map bounds
var bounds = map.getBounds();
// Then the points
var swPoint = bounds.getSouthWest();
var nePoint = bounds.getNorthEast();
// Now, each individual coordinate
var swLat = swPoint.lat();
var swLng = swPoint.lng();
var neLat = nePoint.lat();
var neLng = nePoint.lng();
downloadUrl("mapsxml.php?swLat="+swLat+"&swLng="+swLng+"&neLat="+neLat+"&neLng="+neLng+"", function(data) {
var xml = parseXml(data);
var markers = xml.documentElement.getElementsByTagName("marker");
var infoWindow = new google.maps.InfoWindow;
for (var i = 0; i < markers.length; i++) {
var address = markers[i].getAttribute("address");
var type = markers[i].getAttribute("type");
var name = markers[i].getAttribute("name");
var point = new google.maps.LatLng(
parseFloat(markers[i].getAttribute("lat")),
parseFloat(markers[i].getAttribute("lng"))
);
var html = "<b>" + name + "</b> <br/>" + address;
var icon = customIcons[type] || {};
var marker = new google.maps.Marker({
map: map,
position: point,
icon: icon.icon,
shadow: icon.shadow});
bindInfoWindow(marker, map, infoWindow, html);
}
})
}
This is working great, however, the current code doesn't offload markers that aren't in de viewport anymore. Besides that, it loads markers again who are already loaded, that slows down the map really fast when moving the map a view times in the same area.
So when the viewport changes, I like to clear the whole map first before loading new markers. What is the best way to do this?
You need to add another Event Listener to the map:
google.maps.event.addListener(map,'bounds_changed', removeMarkers);
See here for more on removing all markers from a google map - unfortunately I dont think it can be done with one call. So you will have to write the removeMarkers or something similar which will have to iterate through all the markers on the map removing them individually like so:
markersArray[i].setMap(null);
I don't know whether it's quicker to check if the marker is in the viewport before removing by using:
map.getBounds();
Read more about Google Map API v3 events
Due to the following explanation using 'tilesloaded' or 'bounds_changed' would be very wrong and cause unwilling continuous firings. Instead you would want to use 'idle' event which will fire once the user has stopped panning/zooming.
google.maps.event.addListener(map, 'idle', loadMapFromCurrentBounds);
https://developers.google.com/maps/articles/toomanymarkers#viewportmarkermanagement
You may want to check out this thread. Daniel answered this quite nicely.
What's the most efficient way to create routes on google maps from gps files?
Also, bounds_changed is the first opportunity to call your function. tilesloaded, will be called constantly. The viewport may contain more than one tile to fill the viewport.
Alternatively, you can also do a setVisible(false).
In order to remove the marker, you may need to remove the listeners.
google.maps.event.clearInstanceListeners(marker);
marker.setMap(null);
markers.remove(marker);
delete marker;
This article goes through it pretty nicely:
Dynamically loading thousands of markers in Google Maps
dynamically load markers until we reach a threshold
keep a hashtable of markers that have already been added
after the threshold has been reached, remove markers that aren’t currently within the viewport
remove all markers from the map when the user has zoomed out, and don’t load any markers until the user zooms back to a reasonable level
Your original function seems like a lot of code. I'd do something like this:
if( map.getBounds().contains(markers[i].getPosition()) ) {
myMarkerDisplayFunction(markers[i]);
}
You might want to check out this documentation from Google. It explains what you need:
With the new list of markers you can remove the current markers
(marker.setMap(null)) that are on the map and
add the new ones (marker.setMap(map)).

Categories