I have an MVC application that is using Google Maps throughout the app. To make things simple, I've created a JavaScript file that initializes the map and sets the map display.
In my application, I want to allow a front end developer to change the center of the map location with a simple JavaScript call. Unfortunately, after declaring the map and loading the application, if you try to make a JavaScript call back to the map variable, the map JS variable (and all variables) are null. This drops the instance to the map, and does not let me change the location (I believe).
In the JS Code below, I'm trying to call setLocation from the HTML page to reset the location. This is the fail part of the code. Help is appreciated.
var map;
var geocoder;
var marker;
$(document).ready(function () {
initialize();
$(function () {
$("#address").autocomplete({
//This bit uses the geocoder to fetch address values
source: function (request, response) {
geocoder.geocode({ 'address': request.term }, function (results, status) {
response($.map(results, function (item) {
return {
label: item.formatted_address,
value: item.formatted_address,
latitude: item.geometry.location.lat(),
longitude: item.geometry.location.lng()
}
}));
})
},
select: function (event, ui) {
$("#Place_Latitude").val(ui.item.latitude);
$("#Place_Longitude").val(ui.item.longitude);
var location = new google.maps.LatLng(ui.item.latitude, ui.item.longitude);
marker.setPosition(location);
map.setCenter(location);
}
});
});
});
function setLocation(lat, lon) {
var location = new google.maps.LatLng(lat, lon);
marker.setPosition(location);
map.setCenter(location);
}
function initialize() {
var location = new google.maps.LatLng(-34.397, 150.644);
var mapOptions = {
center: location, zoom: 8, mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
marker = new google.maps.Marker({
map: map,
draggable: true
});
marker.setPosition(location);
geocoder = new google.maps.Geocoder();
}
This looks bad:
return {
label: item.formatted_address,
value: item.formatted_address,
latitude: item.geometry.location.lat(),
longitude: item.geometry.location.lng()
}
The geocoder is asynchronous and can't return anything from the callback routine. you need to use the variables inside the callback.
There is an example of what you are trying to do here...
Have a look at theie placeMarker function and how they have initialized the map...
My suggestion would be to start with this example and make sure that works. If so, it should be relatively straight forward to determine what is different in your implementation.
Related
For a school project I have to make an app, using Intel XDK, jQuery Mobile and an API. I wanted to make an app in which you can make a route and display this route on a google-maps-map (like a travelapp to view your trips).
I used Intel XDK (HTML5 + Cordova and the App Designer) and got an API key from the Google Maps Javascript API.
Right now, I have used the Google Maps API and displaying a route from A to B went well. In the following code (this is in my script) I tried to add waypoints to my route. In my HTML code I have three text-inputs for the user (start, via (=waypoint), end), the map and a button to show the route. I have looked at many sample codes, but my code doesn't work and I don't know why. There is no error, but if you push the showbutton, nothing happens. What have I done wrong or what did I miss?
I hope anyone can help and thanks in advance!
var directionDisplay;
var directionsService = new google.maps.DirectionsService();
function initMap() {
var latlng = new google.maps.LatLng(41.850033, -87.6500523);
// set direction render options
//var rendererOptions = { draggable: true };
directionsDisplay = new google.maps.DirectionsRenderer({map: map});
var myOptions = {
zoom: 14,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: false
};
// add the map to the map placeholder
var map = new google.maps.Map(document.getElementById("gmap"),myOptions);
directionsDisplay.setMap(map);
calcRoute();
}
function calcRoute() {
var start = $('#start-input').val();
var via = $('#via-input').val();
var end = $('#end-input').val();
var waypts = [];
waypts.push({
location: via,
stopover: true
});
var request = {
origin: start,
destination: end,
waypoints: waypts,
optimizeWaypoints: true,
unitSystem: google.maps.UnitSystem.IMPERIAL,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
} else {
alert('Directions request failed due to ' + status);
}
});
}
function createMarker(latlng) {
var marker = new google.maps.Marker({
position: latlng,
map: map
});
}
/* button #show-btn */
$(document).on("click", "#show-btn", function(evt) {
initMap();
createMarker(start);
createMarker(via);
createMarker(end);
return false;
});
You're creating the variables start, via and end as local variables in your calcRoute function, meaning they're not available to any code outside of that function. So when you try and refer to them in these lines, they'll be undefined, and I suspect you're getting a JS error:
createMarker(start);
createMarker(via);
createMarker(end);
Make them global variables instead; define them at the same time as you do this:
var directionDisplay;
i.e. that becomes:
var directionDisplay, start, via, end;
And then remove the var keyword from where you refer to them in the calcRoute function, i.e. do:
function calcRoute() {
start = $('#start-input').val();
via = $('#via-input').val();
end = $('#end-input').val();
You also have the same problem with your map variable. You create that as a local variable in your initMap function, and then try and refer to it in the createMarker function, which won't have access to it. Make that a global variable too.
I've two diffrent .js-files: scripts.js and search.js
In script.js the Google Maps is initialized:
function initMap() {
var mapDiv = document.getElementById('market-map');
var map = new google.maps.Map(mapDiv, {
center: {lat: 12345, lng: 12345},
zoom: 8
});
}
It's working fine.
But I need this map in search.js as a variable, so I can work with this map in search.js (add Geocoder etc.)
Does anyone know a solution, how I can fix this problem?
Thanks for your help. :)
You just have to set map as a globale variable
var map = null;
function initMap() {
var mapDiv = document.getElementById('market-map');
map = new google.maps.Map(mapDiv, {
center: {lat: 12345, lng: 12345},
zoom: 8
});
}
Then, search.js will access to it.
I've been trying to geocode an address into LatLng coordinates, but I'm having a lot of difficulty doing so. I know geocode() is an asynchronous function, and I have been toying with callbacks with no avail. I've been looking at other posts like this one (How to return value from an asynchronous callback function?), but I'm having difficulty understanding the concept. If anyone can show me how to properly do that callback function, will be much appreciated. Lot of headache over this one. Was able to get the callback to do an alert() that gives the correct LatLng, but not set the position parameter. Sorry couldn't figure it out from the example cited earlier. Pretty new to the language. Any references you can point me to to understand the concept are greatly appreciated as well. Thanks!
var location;
var image;
var map;
var geocoder;
var address;
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(42.763146, -73.3776),
zoom: 6,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"),
mapOptions);
var cloudLayer = new google.maps.weather.CloudLayer();
cloudLayer.setMap(map);
addressSet(function(address) {
console.log(address);
// tried to set location = address;, location = new google.maps.LatLng(address);
// neither works
});
addMtsToMap(location,
map,
new google.maps.MarkerImage("img/mts/skier.png", null, null, null, new google.maps.Size(50,50)));
}
function addMtsToMap(location, map, image) {
var marker = new google.maps.Marker({
position: location,
map: map,
icon: image
});
var infoWindow = new google.maps.InfoWindow({content: "<h1>Powder!!!</h1>"});
google.maps.event.addListener(marker, "click", function() {
infoWindow.open(map, marker);
});
}
function addressSet(callback) {
geocoder = new google.maps.Geocoder();
geocoder.geocode({address: "Killington+VT"}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
address = results[0].geometry.location;
callback(address);
}
});
}
Sorry all. Couldn't believe what I found after messing with this for so long. I declared var map again in initialize, which made my addressSet() function pull the incorrect global map variable. Deleting the var made it work.
in my Asp.net Web Application where i am using the setTimeout to Get rid of
geocoder OVER_QUERY_LIMIT, the shorter time out is 10ms which is too longer for me, I have 800 above addresses coming from SQL SERVER which would be increased because of this setTimeout will take about 5 to 7 mints to take places of all the markers on map and that`s frustrating. I researched and saw this link setTimeout: how to get the shortest delay
but cant figure out what he want to do actually. please someone guide me....
function InitializeMap() {
// Here am calling the webService by PageMethods in which CityNames, Countries Name will take their places
PageMethods.GetCitiesbyUser_Extender(onSucess);
var myOptions =
{
zoom: 0,
center: new google.maps.LatLng(-34.397, 150.644),
mapTypeId: google.maps.MapTypeId.ROADMAP,
};
var map = new google.maps.Map(document.getElementById("map"), myOptions);
// Creating latlngbound to bound the markers on map
var bounds = new google.maps.LatLngBounds();
//// Creating an array that will contain the addresses
var places = [];
// Creating a variable that will hold the InfoWindow object
var infowindow;
// create this to add the marker Cluster on map
mc = new MarkerClusterer(map);
var popup_content = [];
var geocoder = new google.maps.Geocoder();
// image for ballon i want to change default ballon to this
var iconimage = "http://chart.apis.google.com/chart?cht=mm&chs=24x32&chco=FFFFFF,008CFF,000000&ext=.png";
var markers = [];
// Create this function for passing the values which was taken by webservice cntName is the return in webservice
function onSucess(cntName){
// loop through the cntName to pass the individual City one by one from geocode
for (i = 0; i < cntName.length; ++i) {
//for fixing the issue use closure to localize the cntName[i] variable before passing into geocode and callback function within it.
(function CreateMarkAndInfo(address) {
geocoder.geocode({ 'address': address },
function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
places[i] = results[0].geometry.location;
var marker = new google.maps.Marker({
position: places[i],
title: results[0].formatted_address,
map: map,
icon: iconimage
});
markers.push(marker);
mc.addMarker(marker);
google.maps.event.addListener(marker, 'click', function () {
if (!infowindow) {
infowindow = new google.maps.InfoWindow();
}
// Setting the content of the InfoWindow afterward
infowindow.setContent(popup_content[i]);
// Tying the InfoWindow to the marker afterward
infowindow.open(map, marker);
});
// Extending the bounds object with each LatLng
bounds.extend(places[i]);
// Adjusting the map to new bounding box
map.fitBounds(bounds);
// Zoom out after fitBound
var listener = google.maps.event.addListenerOnce(map, "idle", function () {
if (map.getZoom() < 10) map.setZoom(2);
});
}
else {
// if geocode will end the limit then make delay by timer in order to avoid the OVER_QUERY_LIMIT
if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
setTimeout(function () { CreateMarkAndInfo(address); }, (15)); // here i think i should use better approch but for now it`s ok.
}
else {
alert("Geocode was not successful for the following reason: " + status);
}
}
});
})(cntName[i]);// End closure trick
}
}
}
google.maps.event.addDomListener(window, 'load', InitializeMap);
Edit:
#just.another.programmer i cant because there is no latitute and longitude in DB, client will add cities and countries by him self thats why i had to convet city and country names by geocode and geocode doing it`s job accuretly here
How i am calling the City and country Names by web service
[System.Web.Services.WebMethod]
[System.Web.Script.Services.ScriptMethod()]
public static string[] GetCitiesbyUser_Extender()
{
System.Data.DataSet dtst = new System.Data.DataSet();
string ses = HttpContext.Current.Session["UserName"].ToString();
USTER.Dal.clsSearch clssearch = new USTER.Dal.clsSearch();
// Assinging the Stroed Procedure Method to DataSet
dtst = clssearch.GetAllCitiesByUser(ses);
string[] cntName = new string[dtst.Tables[0].Rows.Count];
int i = 0;
try
{
foreach (System.Data.DataRow rdr in dtst.Tables[0].Rows)
{
// Columns Name in SQL Server Table "CityName" and "CountryName"
cntName.SetValue(rdr["CityName"].ToString() +","+ rdr["CountryName"].ToString() , i);
i++;
}
}
catch { }
finally
{
}
return cntName;
}
Geocode your addresses one time when you first get them, then store the lat/long in your db so you don't have to geocode again. This will dramatically reduce your geocode requests and remove the need for setTimeout.
I've been cobbling together various bits of code from around the internet (including StackOverflow) and I've got a working map (almost) which geocodes postcodes from an array and creates infowindows for each one.
Two problems:
1) my info windows, which should take their text from another array, always use the last array value
2) i can't get the map to center automatically. I'm using a bit of code which has worked in other circumstances, but it doesn't in my code.
The code is fairly self-explanatory:
var geocoder = new google.maps.Geocoder();
var map = new google.maps.Map(document.getElementById('map'), {
//zoom: 10,
//center: new google.maps.LatLng(-33.92, 151.25),
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var postcodes = [
"EH14 1PR",
"KY7 4TP",
"IV6 7UP"
];
var descriptions = [
"Slateford",
"Cortachy",
"Marybank"
];
var markersArray = [];
var infowindow = new google.maps.InfoWindow();
var marker, i, address, description;
for(var i = 0; i < postcodes.length; i++) {
address = postcodes[i];
description = descriptions[i];
geocoder.geocode(
{
'address': address,
'region' : 'uk'
},
function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
marker = new google.maps.Marker({
position: results[0].geometry.location,
map: map
});
markersArray[i] = marker;
console.log(markersArray[i]);
google.maps.event.addListener(marker, 'click', (function(marker, i) {
return function() {
infowindow.setContent(description);
infowindow.open(map, marker);
}
})(marker, i));
} else {
alert("Geocode was not successful for the following reason: " + status);
return false;
}
}
);
var bounds = new google.maps.LatLngBounds();
$.each(markersArray, function (index, marker) {
bounds.extend(marker.position);
});
map.fitBounds(bounds);
console.log(bounds);
}
Any thoughts? The issue seems to be with the value of the counter i inside the geocode function.
Any help much appreciated!
1) my info windows, which should take their text from another array,
always use the last array value
2) i can't get the map to center automatically. I'm using a bit of
code which has worked in other circumstances, but it doesn't in my
code.
1) Yes this is a closure issue. This is how I get around it.
I create an object to store all of the properties I will be using. In your example I am going to use the postcode and the description.
function location(postalcode, desc){
this.PostalCode = postalcode;
this.Description = desc;
}
Now do a quick loop to add all the location objects to an array.
var locations = [];
for(var i = 0; i < postcodes.length; i++) {
locations.push(new location(postcodes[i], descriptions[i]));
}
Extract the geocode functionality into its own function with a parameter to take a location object. Then you can loop through the location object array and geocode each individually. So now both the post code and description are in scope when the request is built and sent.
function GeoCode(singleLocation){
geocoder.geocode(
{
'address': singleLocation.PostalCode,
'region' : 'uk'
},
function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
var marker = new google.maps.Marker({
position: results[0].geometry.location,
map: map
});
//quick and dirty way
bounds.extend(marker.position);
map.fitBounds(bounds);
markersArray[i] = marker;
console.log(markersArray[i]);
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(singleLocation.Description);
infowindow.open(map, this); //this refers to the marker
});
} else {
alert("Geocode was not successful for the following reason: "
+ status);
return false;
}
}
);
}
2) As you can see above the quick an dirty way to do this is extend and fit the bounds inside of the callback function for the geocode. This causes the fitBounds function to be called multiple times and isnt really a big deal if you only have a few markers, but will cause problems if you have hundreds or thousands of markers. In that case the right-way to do this is to create an async loop function. You can see an example of it on one of my previous answers.
Here is a functioning example of the code based on your example.
Two problems:
1) my info windows, which should take their text from another array,
always use the last array value
2) i can't get the map to center automatically. I'm using a bit of
code which has worked in other circumstances, but it doesn't in my
code.
Answers:
1) This is because most likely you have a closure issue here.
2) center will define the center point of your map, but in your code you have commented this
//center: new google.maps.LatLng(-33.92, 151.25),
removing the comment for this line will center to map to latitude of -33.92 and longitude of 151.25.
Use below:
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 10,
center: new google.maps.LatLng(-33.92, 151.25),
mapTypeId: google.maps.MapTypeId.ROADMAP
});