Javascript Scope & Geocode [duplicate] - javascript

This question already has an answer here:
How to get a javascript lat/lng variable out of a Google Map API function
(1 answer)
Closed 9 years ago.
I've been searching here all day for help with what is likely, just a basic noob javascript mistake.
I'm trying to get the lat/long from an address using google's geocoder glass. However, I can't seem to assign it to a global or even derive it from the object's property (which I would prefer). Basically all I need at this point to to just get the location from the geocoder and everything else should fall in place. See code:
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=true&libraries=places"></script>
<script>
var geocoder = new google.maps.Geocoder();
var ListingAddress = '1600 Pennsylvania Ave NW Washington, DC 20500';
var map;
var infowindow;
//var ListingLoc = new google.maps.LatLng(-33.8665433, 151.1956316);
var ListingLatLong;
var ListingLoc;
function initialize() {
geocoder.geocode({
address: ListingAddress
},
function(results){
ListingLatLong = results[0].geometry.location;
ListingLoc = new google.maps.LatLng(ListingLatLong);
});
map = new google.maps.Map(document.getElementById('map_canvas'), {
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: ListingLoc,
zoom: 15
});
var request = {
location: ListingLoc,
radius: 500,
types: ['school']
};
infowindow = new google.maps.InfoWindow();
var service = new google.maps.places.PlacesService(map);
service.nearbySearch(request, callback);
}
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
createMarker(results[i]);
}
}
}
function createMarker(place) {
var placeLoc = place.geometry.location;
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(place.name);
infowindow.open(map, this);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
AND the markup
<div id="map_canvas" style="width:600px;height:400px;border:none;"></div>

The second argument of geocoder.geocode is a callback function that executes asynchronously. That means that the function will run sometime after the rest of your code. Thus,ListingLatLong is not assigned a value until after you've tried to use it.
Asynchronous execution is the norm for performing network requests in JavaScript. Instead of causing your code to hang by waiting a long time for a network round-trip, you simply dispatch a request and define a listener function to fire when the request completes some time later. That's what is happening here: the function argument to geocoder.geocode is a listener function that fires once the data arrives from Google's geocoding server. geocoder.geocode doesn't run that function, exactly -- it just says, "Okay, JavaScript, here's a function you should run whenever my request completes."
To solve this problem, simply move any code that needs to use the values of results (and/or ListingLatLong) inside the callback function:
geocoder.geocode({
address: ListingAddress
},
function(results){
ListingLatLong = results[0].geometry.location;
ListingLoc = new google.maps.LatLng(ListingLatLong);
// ** note this is inside the callback now **
map = new google.maps.Map(document.getElementById('map_canvas'), {
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: ListingLoc,
zoom: 15
});
// add the rest of your code here, too
// ...
});
(Side note: you should use uppercase variable names only for constructors (e.g., var Person = function(name) { this.name = name; }) and lowercase variable names for instances (e.g., someGuy = new Person("Bob");). When I see the name ListingLatLong, I expect it to be a constructor, but it's not, so I'd suggest using listingLatLong instead.)

Related

Google maps, passing "map" to a different event

I have 2 different events, please correct me if i am doing this wrong,
//initializing the map
google.maps.event.addDomListener(window, 'load', initialize);
//initializing the markers
google.maps.event.addListener(map, 'idle', showMarkers);
The first event is to initialize the map (set it up), the second event is for querying the database for new markers when the current map bounds change (not functional yet)
Since I have defined "map" inside the initialize function, how can I access it outside the function and pass it to the second event? As you can see i have declared it before the functions as a global variable but it is still undefined as the event argument, i have been stuck on this for days
var map;
function initialize(){
//defining map options
var mapLatlng = new google.maps.LatLng(37.09024, -100.712891);
var mapOptions = {
zoom: 4,
center: mapLatlng
}
//defining the map itself
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
}
function showMarkers(){
Parse.initialize("X","X");
var query = new Parse.Query("business_and_reviews");
var results = new Parse.Object("business_and_reviews");
query.equalTo("name","McDonalds");
query.find({
success: function(results) {
console.log(results);
for (var i = 0; i < results.length; i++) {
var object = results[i];
}
var lat = (object["attributes"]["lat"]);
var lng = (object["attributes"]["lng"]);
console.log(lat);
console.log(lng);
//adding the marker from the query
var marker = new google.maps.Marker({
position: mapLatlong,
map: map,
title: 'Hello World!'
});
},
error: function(object, error) {
}
});
}
//initializing the map
google.maps.event.addDomListener(window, 'load', initialize);
//initializing the markers
google.maps.event.addListener(map, 'idle', showMarkers);
For the second event "map" is undefined
SOlved it! put the idle event listener inside the initialize function and it works like a charm! I guess initilize isn't to initialize the page initially but runs constantly

Google place search markers not showing on map

I'm coding a demo of how I want a future search function to work. I want to query google on an address or area and return the area bounds as well as nearby places. To do this, I am using places autocomplete, geocode, and places search.
So far I am successfully getting predicted search queries resulting in the bounds being drawn as a rectangle on my map. However, when I try to implement markers for the place search result no markers are appearing on my map. I know the search is working because putting an alert in the createMarker function on each location returns several lat/lng's that coincide with my area.
I suspect that maybe the map object is not being passed to my createMarker function, but I am kind of a noob when it comes to Javascript. I tried passing an additional parameter but it didn't do much.
It should be noted that I am able to create a marker within the initialize function, when I attempt to just create one static marker.
EDIT: I have removed the type parameter for the place search request, but the code doesn't work even with the parameter ['store'].
var map;
var infowindow;
function initialize() {
var view_lat = document.getElementById('view_lat').value;
var view_lng = document.getElementById('view_lng').value;
var focus = new google.maps.LatLng(view_lat,view_lng);
var swlat = document.getElementById('swlat').value;
var swlng = document.getElementById('swlng').value;
var nelat = document.getElementById('nelat').value;
var nelng = document.getElementById('nelng').value;
var map_canvas = document.getElementById('map-canvas_embed');
var map_options = {
center: new google.maps.LatLng(parseFloat(view_lat), view_lng),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var mysw = new google.maps.LatLng(swlat,swlng)
var map = new google.maps.Map(map_canvas, map_options)
var rectangle = new google.maps.Rectangle({
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35,
map: map,
bounds: new google.maps.LatLngBounds(
new google.maps.LatLng(swlat, swlng),
new google.maps.LatLng(nelat, nelng))
});
var request = {
location: focus,
radius: 500
};
var place_search = new google.maps.places.PlacesService(map);
place_search.nearbySearch(request,callback)
}
Handles the result from google places search
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
createMarker(results[i]);
}
}
}
function createMarker(place) {
var placeLoc = place.geometry.location;
var marker = new google.maps.Marker({
position: place.geometry.location
});
marker.setMap(map);
}
Autocomplete variables
var input = document.getElementById('location');
var options = {
componentRestrictions: {country: 'se'},
types: ['geocode']
}
var searchform = document.getElementById('searchform');
var place;
var autocomplete = new google.maps.places.Autocomplete(input, options);
Add listener to detect autocomplete selection
google.maps.event.addListener(autocomplete, 'place_changed', function () {
place = autocomplete.getPlace();
//console.log(place);
});
Add listener to search (This function isn't working, hence my work around in the initialize function)
searchform.addEventListener("submit", function() {
var newlatlong = new google.maps.LatLng(place.geometry.location.lat(),place.geometry.location.lng());
map.setCenter(newlatlong);
marker.setPosition(newlatlong);
map.setZoom(12);
google.maps.event.trigger(map, 'resize');
});
Reset the inpout box on click
input.addEventListener('click', function(){
input.value = "";
});
Call the initialize function
google.maps.event.addDomListener(window, 'load', initialize);
Indeed it looks like a problem of JavaScript's scope.
Consider these lines of your code:
function initialize() {
//...
var map = new google.maps.Map(map_canvas, map_options)
//...
}
Declaring map inside the initialize function makes your map variable unreachable out of your initialize function.
So when you try to reach it in your searchform, map.setCenter() will show in your console that map is undefined.
You could solve your issue in several ways.
declare map at the beginning of your script (before initialize is executed, anyways)
declare the searchform.addEventListener("submit", function(){}) inside your initialize() function and use it before initialize() ends its execution.

TypeError: map is undefined google api

I have been referencing a found tutorial and have stumbled upon an error using the google maps api. I am getting and error that telling me that a variable is not defined when in fact it is. I am not exactly sure what the problem is.. Please help.
<title></title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript"
src="http://maps.googleapis.com/maps/api/js?libraries=places&sensor=true">
</script>
<script type="text/javascript">
var map;
var service;
function handleSearchResults (results, status)
{
console.log(results);
}
function performSearch ()
{
var request = {
bounds: map.getBounds(),
name: "McDonald's" // within the bounds find the given name
}
service.nearbySearch(request, handleSearchResults);
}
function initialize (location) //initializing geolocation function
{
console.log(location);
var currentLocation = new google.maps.LatLng(location.coords.latitude, location.coords.longitude)
var mapOptions = {
center: currentLocation,
zoom: 13
};
var map = new google.maps.Map(document.getElementById("map-canvas"),
mapOptions);
// To add the marker to the map, use the 'map' property
var marker = new google.maps.Marker({
position: currentLocation,
map: map // referencing google map from variable map above
});
service = new google.maps.places.PlacesService(map);
// wait until map bounds are initialized before performing search
google.maps.event.addListenerOnce(map, 'bounds_changed', performSearch);
}
$( document ).ready(function()
{
navigator.geolocation.getCurrentPosition(initialize);
});
</script>
</html>
It looks like you're running into scoping issues with that map variable since you're calling the initialize function from within the scope of the $(document.ready() callback. I made some adjustments to it in the jsfiddle below. Does that fix the issue?
jsfiddle
function handleSearchResults (results, status)
{
console.log(results);
}
function performSearch ()
{
var request = {
bounds: map.getBounds(),
name: "McDonald's" // within the bounds find the given name
}
service.nearbySearch(request, handleSearchResults);
}
function initialize (location) //initializing geolocation function
{
console.log(location);
var currentLocation = new google.maps.LatLng(location.coords.latitude, location.coords.longitude)
var mapOptions = {
center: currentLocation,
zoom: 13
};
map = new google.maps.Map(document.getElementById("map-canvas"),
mapOptions);
// To add the marker to the map, use the 'map' property
var marker = new google.maps.Marker({
position: currentLocation,
map: map // referencing google map from variable map above
});
service = new google.maps.places.PlacesService(map);
// wait until map bounds are initialized before performing search
google.maps.event.addListenerOnce(map, 'bounds_changed', performSearch);
}
$(document).ready(function() {
var map;
var service;
navigator.geolocation.getCurrentPosition(initialize);
})

javascript variables and asynchronous calls

I have the following code, and having read this, i understand it wont work because the getJSON call is asynchronous. How do i need to change this so that the MarkerClusterer function gets triggered with a full set of markers? I've tried putting the MarkerClusterer function inside the getJSON call but with no luck...
var mcOptions = {gridSize: 50, maxZoom: 9};
var markers = [];
function parse_json(json) {
if (json.length > 0) {
for (i=0; i<json.length; i++) {
var report = json[i];
var latLng = new google.maps.LatLng(report.latitude, report.longitude);
markers[i] = new google.maps.Marker({
position: latLng,
title: report.name + ' ' + report.surf_size_ft_round,
url: "/place/"+report.slug
});
google.maps.event.addListener(markers[i], 'click', function() {
window.location.href = markers[i].url;
});
markers.push(markers[i]);
}
}
};
$.getJSON('<%= request.fullpath + ".json" %>', function(stream) {
if (stream.length > 0) {
parse_json(stream);
alert(markers[1].title); //sanity check - gives result
}
});
alert(markers[5].title); // sanity check - empty
var mc = new MarkerClusterer(map, markers, mcOptions);
Why not put this code snippet:
mc = new MarkerClusterer(map, markers, mcOptions);
inside the anonymous callback function in your $.getJSON? Just declare var mc; somewhere outside the $.getJSON scope to be able to have access to it elsewhere.
Alternatively, you can fire an event at the end of your parse_json function, listen to that event and then fire up another function that creates your MarkerClusterer object when the event has fired. Check this out: How to trigger event in JavaScript?
EDIT:
Upon inspecting your code a bit more, I can see that you set markers[i] to a new Marker instance and then push onto the markers array that same instance. You probably want to either set markers[i] to a new Marker instance or you want to create a var marker, setting it to a new Marker instance and then pushing on the markers array.
Maybe you need to put it inside the success function you give as an input to $.getJSON?
$.getJSON('<%= request.fullpath + ".json" %>', function(stream) {
if (stream.length > 0) {
parse_json(stream);
alert(markers[1].title); //sanity check - gives result
mc = new MarkerClusterer(map, markers, mcOptions);
}
});
alert(markers[5].title); // sanity check - empty

Problems calling fitbounds outside jquery.each()

I'm working on a Google Map in JavaScript(v3).
I need to show some markers from XML, for which I use jQuery.
Here's the object and function, might save me time explaining:
var VX = {
map:null,
bounds:null
}
VX.placeMarkers = function(filename) {
$.get(filename, function(xml) {
$(xml).find("marker").each(function() {
var lat = $(this).find('lat').text();
var lng = $(this).find('lng').text();
var point = new google.maps.LatLng(parseFloat(lat),parseFloat(lng));
VX.bounds.extend(point);
VX.map.fitBounds(VX.bounds); //this works
var marker = new google.maps.Marker({
position: point,
map: VX.map,
zoom: 10,
center: point
});
});
});
//VX.map.fitBounds(VX.bounds); //this shows me the ocean east of Africa
}
So basically my problem is that I can't figure out how to do fitbounds from outside of the .each function, and doing it inside the function calls it for every marker which looks bad.
I declare the bounds when I initialize the map... haven't included the entire code because its like 300 lines.
Shouldn't I be able to use a value that I passed to a global object?
Edit: ah, I was calling it from outside of the get function!
The second call doesn't work because it is firing before the ajax get() returns.
Place the fitBounds inside the get() handler, but outside the each() function. Like so:
var VX = {
map:null,
bounds:null
}
VX.placeMarkers = function(filename)
{
$.get
(
filename,
function(xml)
{
$(xml).find("marker").each
(
function()
{
var lat = $(this).find('lat').text();
var lng = $(this).find('lng').text();
var point = new google.maps.LatLng(parseFloat(lat),parseFloat(lng));
VX.bounds.extend(point);
//VX.map.fitBounds(VX.bounds); //this works
var marker = new google.maps.Marker
({
position: point,
map: VX.map,
zoom: 10,
center: point
});
}
);
VX.map.fitBounds(VX.bounds); //-- This should work.
}
);
//VX.map.fitBounds(VX.bounds); //this shows me the ocean east of africa
}

Categories