Google Places and ZERO_RESULTS - javascript

I've been working on this very simple Google Places search and I cannot get anything but a ZERO_RESULTS. It makes no sense to me at this point as my map is working and displays markers from my database within a separate AJAX function. I've logged my objects and variables and all seem to be just fine.
Why does the success callback go right to my else statement with ZERO_RESULTS?
$( "#submit3" ).click(function(e) {
e.preventDefault();
findPlaces();
$('#results').text("Triggah3!");
});
function findPlaces() {
var lat = document.getElementById("latitude").value;
var lng = document.getElementById("longitude").value;
var cur_location = new google.maps.LatLng(lat, lng);
// prepare request to Places
var request = {
location: cur_location,
radius: 50000,
types: 'bank'
};
// send request
service = new google.maps.places.PlacesService(map);
service.search(request, createMarkers);
}
// create markers (from 'findPlaces' function)
function createMarkers(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) { //ZERO_RESULTS
// if we have found something - clear map (overlays)
clearOverlays();
// and create new markers by search result
for (var i = 0; i < results.length; i++) {
createMarker(results[i]);
}
} else if (status == google.maps.places.PlacesServiceStatus.ZERO_RESULTS) {
alert('Sorry, nothing is found');
}
}
// create single marker function
function createMarker(obj) {
// prepare new Marker object
var mark = new google.maps.Marker({
position: obj.geometry.location,
map: map,
title: obj.name
});
markers.push(mark);
// prepare info window
var infowindow = new google.maps.InfoWindow({
content: '<img src="' + obj.icon + '" /><font style="color:#000;">' + obj.name +
'<br />Rating: ' + obj.rating + '<br />Vicinity: ' + obj.vicinity + '</font>'
});
// add event handler to current marker
google.maps.event.addListener(mark, 'click', function() {
clearInfos();
infowindow.open(map,mark);
});
infos.push(infowindow);
}

types is expected to be an array.
Use:
types: ['bank']

Related

Grabs addresses from table and displays markers on map using Google Maps API but marker's hover title or infoWindow (onclick) doesn't work

This script can be used as a standalone javascript or greasemonkey script. What I am trying to fix is the hover title and on-click's info-window (it should display the address). here is a jsFiddle
// ==UserScript==
// #name mapMarkers
// #namespace mapMarkers
// #include https://www.example.com/*
// #description map markers of addresses in table
// #version 1
// #grant none
// ==/UserScript==
// find the table and loop through each rows to get the 11th, 12th, 13th cell's content (street address, city and zip respectively
// convert to lat/lon and show markers on map
if (document.getElementById('main_report') !== null) {
API_js_callback = 'https://maps.googleapis.com/maps/api/js?v=3.exp&callback=initialize';
var script = document.createElement('script');
script.src = API_js_callback;
var head = document.getElementsByTagName("head")[0];
(head || document.body).appendChild(script);
var Table_1 = document.getElementById('main_report');
var DIVmap = document.createElement('div');
DIVmap.id = 'DIVmap';
DIVmap.style.border = '2px coral solid';
DIVmap.style.cursor = 'pointer';
DIVmap.style.display = '';
DIVmap.style.height = '35%';
DIVmap.style.margin = '1';
DIVmap.style.position = 'fixed';
DIVmap.style.padding = '1';
DIVmap.style.right = '1%';
DIVmap.style.bottom = '1%';
DIVmap.style.width = '35%';
DIVmap.style.zIndex = '99';
var DIVinternal = document.createElement('div');
DIVinternal.id = 'DIVinternal';
DIVinternal.style.height = '100%';
DIVinternal.style.width = '100%';
DIVinternal.style.zIndex = '999';
document.body.appendChild(DIVmap);
DIVmap.appendChild(DIVinternal);
//Adds a button which allows the user to re-run calcRoute
var reloadMapButton = document.createElement("button");
reloadMapButton.setAttribute("type", "button");
reloadMapButton.setAttribute("href", "#");
reloadMapButton.textContent="Reload map";
reloadMapButton.id="calcRoute";
reloadMapButton.style.zIndex = '1000';
document.getElementById('Content_Title').appendChild(reloadMapButton);
window.initialize = setTimeout(function () {
var myWindow;
try{
myWindow = unsafeWindow;
}catch(e){
myWindow = window;
}
google = myWindow.google;
directionsService = new google.maps.DirectionsService();
directionsDisplay = new google.maps.DirectionsRenderer();
var myLoc = new google.maps.LatLng(28.882193,-81.317936);
var myOptions = {
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: myLoc
}
map = new google.maps.Map(document.getElementById("DIVinternal"), myOptions);
var infoWindow1 = new google.maps.InfoWindow();
//var bounds = new google.maps.LatLngBounds();
var geocoder = new google.maps.Geocoder();
function codeAddress(address,i) {
setTimeout( function () { // timer to avoid google geocode limits
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
//map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}, i * 350);
}
function calcRoute() {
if (Table_1.rows.length > 1) { // table has 1 empty row if no search results are returned and first row is always empty
var newPoint;
for (var i = 1, row; row = Table_1.rows[i]; i++) {
newPoint = codeAddress(row.cells[10].title + ', ' + row.cells[11].innerHTML + ', ' + row.cells[12].innerHTML, i);
// bounds.extend(newPoint);
marker = new google.maps.Marker({
position: newPoint,
map: map,
title: row.cells[10].title + ', ' + row.cells[11].innerHTML + ', ' + row.cells[12].innerHTML
});
google.maps.event.addListener(marker, 'click', function () {
infoWindow1.setContent(row.cells[10].title + ', ' + row.cells[11].innerHTML + ', ' + row.cells[12].innerHTML);
infoWindow1.open(map, this);
});
// Automatically center the map fitting all markers on the screen
// map.fitBounds(bounds);
}
}
}
//reloadMapButton.addEventListener('click', calcRoute);
document.getElementById("calcRoute").onclick = calcRoute;
calcRoute();
}, 1000);
} // if (document.getElementById('main_report') !== null)
sample data
Answer copied from the Reddit post:
If you carefully think through the code step by step, you can see why the infowindow isn't assigning itself to the markers. Starting from the calcRoute function:
if the table is more than one row
create newPoint variable
for each row in the table starting with the second one:
call the codeAddress function and assign it to newPoint
Let me cut in here. This is where your confusion is starting. codeAddress has no return statement (and even if it did, it would be asynchronous and wouldn't matter [AJAX]), so it isn't actually doing anything to newPoint. The whole marker is being created inside the codeAddress function, rendering the lines below this useless -- there is no position stored in newPoint so no marker is created, and thus no infoWindow is created.
Instead, you have to create the infoWindow inside the geocoding callback, right after you create the marker. You also have to assign that infoWindow to the marker using a click event. Also, if you want the hover title, just add it as a property of the marker called title.
The final code is here: http://pastebin.com/3rpWEnrp
You'll notice I cleaned it up a bit to make it more readable and clear:
The document's head can be accessed using document.head -- no need for getting it by the tag name
There is a shorthand for assigning two variables the same value: x = y = 3
No need for the z-index on the internal div; the external div already has it. (think of this like hold a piece of cardboard over something--anything on the cardboard is also lifted)
No need for the unsafeWindow; this is literally unsafe
Shorthand for definining multiple variables at once is var x = 3, y = 4;
You don't need the codeAddress function because you only call it once, so the whole contents of this just gets put into the calcRoute function.
It's less intrusive to use the console instead of alert, unless you really want the user to know, and in that case you need a simpler message
Why check if the table is longer than one row when the if statement won't fire in that case because it starts on row 2?
Change your calcRoute() function to call another function for each loop iteration. This will capture that function's parameters and local variables in a closure, so they will remain valid in the asynchronous event handler nested inside it.
You have a lengthy string expression repeated three times. Pull that out into a common variable so you only have to do it once.
Use var on your local variables. You're missing one on marker.
Be careful of your indentation. It is not consistent in your code: the marker click listener is not indented the same as the rest of the function it is nested inside. This makes it hard to see what is nested inside what.
Do not use this loop style:
for( var i = 1, row; row = Table_1.rows[i]; i++ ) { ... }
I used to advocate this type of loop, but it turned out to not be such a good idea. In an optimizing JavaScript environment, it is likely to force the array to become de-optimized. That may or may not happen in your particular code, and the array may be short enough that it just doesn't matter, but it's not a good habit these days. You're better off with a conventional loop with an i < xxx.length test.
So putting those tips together, we have this for your calcRoute():
function calcRoute() {
// table has 1 empty row if no search results are returned,
// and first row is always empty
if( Table_1.rows.length < 2 )
return;
for( var i = 1; i < Table_1.rows.length; i++ ) {
addMarker( i );
}
}
function addMarker( i ) {
var row = Table_1.rows[i];
var title =
row.cells[10].title + ', ' +
row.cells[11].innerHTML + ', ' +
row.cells[12].innerHTML;
var newPoint = codeAddress( title, i );
var marker = new google.maps.Marker({
position: newPoint,
map: map,
title: title
});
google.maps.event.addListener( marker, 'click', function () {
infoWindow1.setContent( title );
infoWindow1.open( map, this );
});
}
There are other problems here. You're calling this function:
var newPoint = codeAddress( ... );
But codeAddress() does not return a value! Nor could it return any useful value, because the function issues an asynchronous geocoder request. If you want to use the location provided by the geocoder, you will need to call a function from within the geocoder callback. I don't understand what you're trying to well enough to make a more specific suggestion here.
Thanks mostly to IanSan5653 on Reddit, I used mostly his code but had to change it a little. Here is working version on jsFiddle and the code below:
// ==UserScript==
// #name mapMarkers
// #namespace mapMarkers
// #include https://www.volusia.realforeclose.com/*
// #description map markers of addresses in table
// #version 1
// #grant none
// ==/UserScript==
// find the table and loop through each rows to get the 11th, 12th, 13th cell's content (street address, city and zip respectively
// convert to lat/lon and show markers on map
if (document.getElementById('main_report') != null) {
var script = document.createElement('script');
script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&callback=initialize';
(document.head || document.body).appendChild(script);
var Table_1 = document.getElementById('main_report');
var DIVmap = document.createElement('div');
DIVmap.id = 'DIVmap';
DIVmap.style.border = '2px coral solid';
DIVmap.style.height = DIVmap.style.width = '35%';
DIVmap.style.margin = DIVmap.style.padding = '1';
DIVmap.style.position = 'fixed';
DIVmap.style.right = DIVmap.style.bottom = '1%';
DIVmap.style.zIndex = '999';
var DIVinternal = document.createElement('div');
DIVinternal.id = 'DIVinternal';
DIVinternal.style.height = DIVinternal.style.width = '100%';
document.body.appendChild(DIVmap);
DIVmap.appendChild(DIVinternal);
//Adds a button which allows the user to re-run calcRoute
var reloadMapButton = document.createElement("button");
reloadMapButton.setAttribute("type", "button");
reloadMapButton.textContent="Reload map";
reloadMapButton.id="calcRoute";
reloadMapButton.style.zIndex = '1000';
document.getElementById('Content_Title').appendChild(reloadMapButton);
// reloadMapButton.onclick = calcRoute;
window.initialize = function () {
var google = window.google,
directionsService = new google.maps.DirectionsService(),
directionsDisplay = new google.maps.DirectionsRenderer(),
myLoc = new google.maps.LatLng(28.882193,-81.317936),
myOptions = {
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: myLoc
},
infoWindow1 = new google.maps.InfoWindow(),
map = new google.maps.Map(document.getElementById("DIVinternal"), myOptions),
geocoder = new google.maps.Geocoder();
function calcRoute() {
for (var i = 1, row; row = Table_1.rows[i]; i++) {
console.log("processing row " + i);
address = row.cells[10].title + ', ' + row.cells[11].innerHTML + ', ' + row.cells[12].innerHTML,
setTimeout( function(addr) { // timer to avoid google geocode limits
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location,
title: addr
});
google.maps.event.addListener(marker, 'click', function() {
infoWindow1.setContent(addr);
infoWindow1.open(map,this);
});
} else {
console.error("Geocode was not successful for the following reason: " + status);
}
});
}(address), i * 400);
}
}
document.getElementById("calcRoute").onclick = calcRoute;
calcRoute();
}
}

Google Places not returning complete results with RouteBoxer

EDIT: It seems that I'm hitting the query limit, but I'm not being returned a full 200 results. So upon further research it looks like the Google API will let me query 10 boxes, return those results, and then smacks me with an OVER_QUERY_LIMIT status for the rest. So I figure I now have two options: slow my queries, or broaden my distance to create fewer boxes along the route.
I'm currently fooling around building a little web app that provides a details about places along a route (like gas stations and coffee on a road trip). I'm using the Google Maps API with the Places Library and RouteBoxer. I'm generating all the appropriate boxes with RouteBoxer, but when the boxes are passed to the Places Library I'm only getting back some of the places. Usually I'll get the first half of the route (on shorter routes) or a few random chunks (for longer routes). San Francisco to Seattle returns me gas stations around Seattle and around Medford, OR only.
Initially I thought maybe I was hitting the results cap of 200, but it's making a separate request for each box, and my total results often aren't hitting 200. Results returned are generally pretty consistent from what I can see. When looking at the details of my network requests and responses, it seems that the script is moving through the boxes making requests with the Places library, and suddenly it stops part way through.
The live app where you can see results and boxes is on Heroku.
My JavaScript isn't the strongest by any means. That's part of why I wanted to work with this API, so please pardon my ignorance if I'm making a trivial mistake. The full script is below. Any direction is tremendously appreciated!
var infowindow = new google.maps.InfoWindow();
var map;
var routeBoxer;
var service;
function initialize() {
directionsDisplay = new google.maps.DirectionsRenderer();
var mapOptions = {
zoom: 4,
center: new google.maps.LatLng(39, -98),
mapTypeId: google.maps.MapTypeId.ROADMAP,
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
service = new google.maps.places.PlacesService(map);
routeBoxer = new RouteBoxer();
directionService = new google.maps.DirectionsService();
directionsRenderer = new google.maps.DirectionsRenderer({ map: map })
directionsDisplay.setMap(map);
directionsDisplay.setPanel(document.getElementById('directions-panel'));
}
function calcRoute() {
var start = document.getElementById('start').value;
var end = document.getElementById('end').value;
var waypt1 = document.getElementById('waypoint1').value;
var waypt2 = document.getElementById('waypoint2').value;
var waypts = []
if (waypt1) {
waypts.push({
location:waypt1,
stopover:true});
}
if (waypt2) {
waypts.push({
location:waypt2,
stopover:true});
}
var request = {
origin: start,
destination: end,
waypoints: waypts,
optimizeWaypoints: true,
travelMode: google.maps.TravelMode.DRIVING
};
directionService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
// Build boxes around route
var path = response.routes[0].overview_path;
var boxes = routeBoxer.box(path, 2); // distance in km from route
drawBoxes(boxes);
for (var i=0; i < boxes.length; i++) {
var bounds = boxes[i];
findPlaces(bounds);
findPlacesByText(bounds);
}
} else {
alert("Directions query failed: " + status);
}
});
}
function findPlaces(bounds) {
var selectedTypes = [];
var inputElements = document.getElementsByClassName('placeOption');
for (var i=0; inputElements[i]; i++) {
if (inputElements[i].checked) {
selectedTypes.push(inputElements[i].value)
}
}
var request = {
bounds: bounds,
types: selectedTypes
};
if (selectedTypes.length > 0) {
service.radarSearch(request, callback);
}
}
function findPlacesByText(bounds) {
var selectedTypes = '';
var inputElements = document.getElementsByClassName('textOption');
for (var i=0; inputElements[i]; i++) {
if (inputElements[i].checked) {
selectedTypes += inputElements[i].value + ', '
}
}
var request = {
bounds: bounds,
query: selectedTypes
};
if (selectedTypes.length > 0) {
service.textSearch(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 marker = new google.maps.Marker({
map: map,
position: place.geometry.location
});
var request = {
reference: place.reference
};
google.maps.event.addListener(marker,'click',function(){
service.getDetails(request, function(place, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
var contentStr = '<h5>' + place.name + '</h5><p>' + place.formatted_address;
if (!!place.formatted_phone_number) contentStr += '<br />' + place.formatted_phone_number;
if (!!place.website) contentStr += '<br /><a target="_blank" href="' + place.website + '">' + place.website + '</a>';
contentStr += '<br />' + place.types + '</p>';
infowindow.setContent(contentStr);
infowindow.open(map,marker);
} else {
var contentStr = "<h5>No Result, status=" + status + "</h5>";
infowindow.setContent(contentStr);
infowindow.open(map,marker);
}
});
});
}
google.maps.event.addDomListener(window, 'load', initialize);
After much experimentation and further research, I decided to try to slow my queries. The way I handled that was to write a new function that calls my query function, and then recursively calls itself with a delay for the next route box. If an OVER_QUERY_LIMIT status is returned, it recalls that box with an increased delay. So far it seems to be working great, but it quickly increases the delay to nearly a half second (or more) between calls, which can take a while if you have a long route with many boxes. My new function that seems to have solves the problem is below. It'll take some more fine-tuning to really get it right, but it's close!
var delay = 100;
...
function queryPlaces(boxes, searchIndex) {
// delay calls to Places API to prevent going over query limit (10/sec)
var bounds = boxes[searchIndex];
findPlaces(bounds);
findPlacesByText(bounds);
if (searchIndex > 0) {
searchIndex--;
setTimeout(queryPlaces, delay, boxes, searchIndex);
}
}

Json Result MVC Controller to Google Map via Jquery

I have a problem because i want to use this Json Result that returns Json List but my problem is how should i call the json result that i will be using to geocode and add marker to my Google Maps ? I used getJson and its not functioning but i dont tried yet the .ajax function
Here is my sets of codes:
<script type="text/javascript">
var geocoder;
var map;
function initialize() {
var minZoomLevel = 4;
var zooms = 7;
geocoder = new google.maps.Geocoder();
map = new google.maps.Map(document.getElementById('map'), {
zoom: minZoomLevel,
center: new google.maps.LatLng(38.50, -90.50),
mapTypeId: google.maps.MapTypeId.ROADMAP
});
// Bounds for North America
var strictBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(15.70, -160.50),
new google.maps.LatLng(68.85, -55.90)
);
// Listen for the dragend event
google.maps.event.addListener(map, 'dragend', function () {
if (strictBounds.contains(map.getCenter())) return;
// We're out of bounds - Move the map back within the bounds
var c = map.getCenter(),
x = c.lng(),
y = c.lat(),
maxX = strictBounds.getNorthEast().lng(),
maxY = strictBounds.getNorthEast().lat(),
minX = strictBounds.getSouthWest().lng(),
minY = strictBounds.getSouthWest().lat();
if (x < minX) x = minX;
if (x > maxX) x = maxX;
if (y < minY) y = minY;
if (y > maxY) y = maxY;
map.setCenter(new google.maps.LatLng(y, x));
});
// Limit the zoom level
google.maps.event.addListener(map, 'zoom_changed', function () {
if (map.getZoom() < minZoomLevel) map.setZoom(minZoomLevel);
});
}
var iconBase = 'https://maps.google.com/mapfiles/kml/shapes/';
function codeAddress() {
var infowindow = new google.maps.InfoWindow();
$.getJson("Dashboard/DashboardIndex",null , function(address) {
$.each(address, function () {
var currVal = $(this).val();
address.each(function () {
geocoder.geocode({ 'address': currVal }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
icon: iconBase + 'man.png',
position: results[0].geometry.location,
title: currVal
})
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
infowindow.setContent(currVal);
infowindow.open(map, marker);
}
})(marker, currVal));
address.push(marker);
}
else if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
setTimeout(codeAddress, 2000);
}
else {
alert("Geocode was not successful for the following reason: " + status);
}
});
});
});
});
return false;
}
window.onload = function () {
initialize();
codeAddress();
}
</script>
And my JsonResult at my Controller
public JsonResult LoadWorkerList()
{
var workerList = new List<Worker_Address>();
// check if search string has value
// retrieve list of workers filtered by search criteria
var list = (from a in db.Worker_Address
where a.LogicalDelete == false
select a).ToList();
List<WorkerAddressInfo> wlist = new List<WorkerAddressInfo>();
foreach (var row in list)
{
WorkerAddressInfo ci = new WorkerAddressInfo
{
ID = row.ID,
Worker_ID = row.WorkerID,
AddressLine1 = row.Address_Line1 + " " + row.Address_Line2+ " " +row.City + " "+ GetLookupDisplayValById(row.State_LookID),
LogicalDelete = row.LogicalDelete
};
wlist.Add(ci);
}
return Json(wlist.ToList().OrderBy(p => p.AddressLine1), JsonRequestBehavior.AllowGet);
}
Im thanking some who could help me in Advance :)
It's hard to guess where it goes wrong since you didn't post the JSON format and are getting errors (toLowerCase) of code you haven't posted. I think it's in the following area:
function codeAddress() {
var infowindow = new google.maps.InfoWindow();
$.getJson("Dashboard/DashboardIndex",null , function(address) {
console.log("full json object",address);//<--should show an array of objects
$.each(address, function () {
console.log(this);//<--here you can see what the JSON object is
var currVal = this["AddressLine1"];//<--guess from what your C# code looks like
//next each doesn't make much sense unless you have an array of arrays
// but the C# code makes json for a list (not a list of lists)
You can use IE for your console output but don't bother posting the output here because I can already tell you it's going to be [Object, object]. To get useful info you're going to have to use firefox with firebug or Chrome. To see the console you can press F12
The line:
setTimeout(codeAddress, 2000);
Could be optimized since now when you are making too many requests you'll fetch the entire address list again and start from the beginning instead of "waiting" for 2 seconds and continue where you were.
The following code:
map.setCenter(results[0].geometry.location);
Why set the center of the map within the loop? It'll just end up having the center of the last found address so you may as well do it outside the loop to set the center to last found address.

Placing Geolocated Coordinates in a jQuery File

I am having difficulty inserting the geolocated coordinates (latitude and longitude) of the current user's location into a PHP/MySQL generated xml file. It requires the user's geolocation to correctly generate the 20 closest businesses within a 30-mile radius. I am currently using a jQuery-powered store locator script to generate the map. The script works fine with a static URL as the xmlLocation, but when I try to use variables in the URL it just outputs an undefined alert message. My aim is to get javascript to place the latitude and longitude values of the user's location into the PHP GET variables so that the XML generator can generate correct output. It looks like this:
LocationGlobal = 'data/gen_default_map.php?lat=' + lat + '&lng=' + lon + '&radius=30';
And should ouput something like this:
data/gen_default_map.php?lat=34.383747&lng=-82.364574&radius=30
I have made modifications to the script and placed comments accordingly. You probably only need to concern yourself with the first 42 lines of the code, but just in case here is the script in it's entirety:
/* Get the User's Current Location and place it in the URL */
/*--------------------------------------------------*/
var LocationGlobal;
if(navigator.geolocation)
{
navigator.geolocation.getCurrentPosition(function(position)
{
var lat = position.coords.latitude;
var lon = position.coords.longitude;
LocationGlobal = 'data/gen_default_map.php?lat=' + lat + '&lng=' + lon + '&radius=30';
alert(LocationGlobal); // Sets correctly here
return LocationGlobal;
});
} else {
console.log('Error getting coordinates.');
}
alert(LocationGlobal); // Undefined here
/*--------------------------------------------------*/
(function ($) {
$.fn.storeLocator = function (options) {
var settings = $.extend({
'mapDiv': 'map',
'listDiv': 'list',
'formID': 'user-location',
'pinColor': 'fe7569',
'startPinColor': '66bd4a',
'pinTextColor': '000000',
'storeLimit': 10,
'distanceAlert': 60,
'xmlLocation': LocationGlobal, //'data/gen_default_map.php?lat=34&lng=-82&radius=30', <--- the commented static URL works but variable doesn't
'addressErrorMsg': 'Please enter valid address address or postcode',
'googleDistanceMatrixDestinationLimit': 25,
'defaultLat': 34.8483680,
'defaultLng': -82.400440,
'defaultLocationName': 'Greenville, South Carolina'
}, options);
return this.each(function () {
var $this = $(this);
// global array of shop objects
var _locationset = new Array();
var geocoder;
// Calculate distances from passed in origin to all locations in the [_locationset] array
// using Google Maps Distance Matrix Service https://developers.google.com/maps/documentation/javascript/reference#DistanceMatrixService
var GeoCodeCalc = {};
GeoCodeCalc.CalcDistanceGoogle = function (origin, callback) {
var destCoordArr = new Array();
var subFunctionTokens = [];
$.each(_locationset, function (ix, loc) {
destCoordArr.push(loc.LatLng);
});
for (var i = 0; i < destCoordArr.length; i = i + settings.googleDistanceMatrixDestinationLimit) { // Google Distance Matrix allows up to 25 destinations to be passed in
var tempArr = destCoordArr.slice(i, Math.min(i + settings.googleDistanceMatrixDestinationLimit));
subFunctionTokens.push(this.CallGoogleDistanceMatrix(i, origin, tempArr));
}
$.when.apply($, subFunctionTokens)
.then(function () {
callback(true);
});
};
GeoCodeCalc.CallGoogleDistanceMatrix = function (startIndex, origin, destinations) {
var token = $.Deferred();
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origins: [origin],
destinations: destinations,
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.IMPERIAL
}, function (response, status) {
if (response && response.rows.length) {
var results = response.rows[0].elements;
$.each(results, function (j, val) {
if (results[j].status != "ZERO_RESULTS") {
_locationset[startIndex + j].Distance = GoogleMapDistanceTextToNumber(results[j].distance.text);
}
});
token.resolve();
}
});
return token.promise();
};
// Converts "123.45 mi" into 123.45
function GoogleMapDistanceTextToNumber(str) {
return Number(str.replace(/[^0-9.]/g, ""));
}
// removes Google Maps URL unfriendly chars from a string
function formatGoogleMapUrlString(str) {
return str.replace("&", "%26").replace(" ", "+");
}
//Geocode function for the origin location
geocoder = new google.maps.Geocoder();
function GoogleGeocode() {
this.geocode = function (address, callbackFunction) {
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var result = {};
result.latitude = results[0].geometry.location.lat();
result.longitude = results[0].geometry.location.lng();
result.formatted_address = results[0].formatted_address;
result.address_components = results[0].address_components;
callbackFunction(result);
} else {
handleError("Geocode was not successful for the following reason: " + status);
callbackFunction(null);
}
});
};
this.geocodeLatLng = function (LatLng, callbackFunction) {
geocoder.geocode({ 'location': LatLng }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK && results.length) {
callbackFunction(results[0]);
} else {
handleError("Geocode was not successful for the following reason: " + status);
callbackFunction(null);
}
});
};
}
//Process form input
$(function () {
$(document).on('submit', '#' + settings.formID, function (e) {
$("#lblError").html("");
//Stop the form submission
e.preventDefault();
//Get the user input and use it
var userinput = $('form').serialize();
userinput = userinput.replace("address=", "");
if (userinput == "") {
handleError(settings.addressErrorMsg);
}
var g = new GoogleGeocode();
var address = userinput;
g.geocode(address, function (data) {
if (data != null) {
showAddress(data);
mapping(data.latitude, data.longitude);
} else {
//Unable to geocode
handleError(settings.addressErrorMsg);
}
});
//Replace spaces in user input
userinput = formatGoogleMapUrlString(userinput);
});
});
$(document).ready(function () {
// Try HTML5 geolocation
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
//map.setCenter(pos);
var g = new GoogleGeocode();
var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
g.geocodeLatLng(latlng, function (address) {
if (address) {
showAddress(address);
} else {
//Unable to geocode
handleNoGeolocation('Error: Unable to geocode address');
}
});
// do the mapping stuff
mapping(position.coords.latitude, position.coords.longitude);
}, function () {
handleNoGeolocation("Tracking of location was not allowed.");
});
} else {
// Browser doesn't support Geolocation
handleNoGeolocation(false);
}
});
function showAddress(address) {
$("#lblAddress").html(address.formatted_address);
// find a postcode and show it in the address textbox
$.each(address.address_components, function (i, val) {
if (val.types[0] == "postal_code") {
$("#address").val(val.short_name);
return false; // breaks the each() loop
}
});
}
function handleNoGeolocation(error) {
if (error) {
var content = error;
} else {
var content = 'Error: Your browser doesn\'t support geolocation.';
}
handleError(content + " Using default location.");
mapping(settings.defaultLat, settings.defaultLng);
$("#lblAddress").html(settings.defaultLocationName);
}
function handleError(error) {
$("#lblError").html(error);
}
//Now all the mapping stuff
function mapping(orig_lat, orig_lng) {
$(function () {
//Parse xml with jQuery
$.ajax({
type: "GET",
url: settings.xmlLocation,
dataType: "xml",
success: function (xml) {
_locationset = new Array();
$(xml).find('Placemark').each(function (i) {
var shop = {
Name: $(this).find('name').text(),
//Take the lat lng from the user, geocoded above
LatLng: new google.maps.LatLng(
$(this).find('coordinates').text().split(",")[1],
$(this).find('coordinates').text().split(",")[0]),
Description: $(this).find('description').text(),
Marker: null,
Distance: null
};
_locationset.push(shop);
});
// Calc Distances from user's location
GeoCodeCalc.CalcDistanceGoogle(new google.maps.LatLng(orig_lat, orig_lng), function (success) {
if (!success) { //something went wrong
handleError("Unable to calculate distances at this time");
}
else {
//Sort the multi-dimensional array numerically
_locationset.sort(function (a, b) {
return ((a.Distance < b.Distance) ? -1 : ((a.Distance > b.Distance) ? 1 : 0));
});
// take "N" closest shops
_locationset = _locationset.slice(0, settings.storeLimit);
//Check the closest marker
if (_locationset[0].Distance > settings.distanceAlert) {
handleError("Unfortunately, our closest location is more than " + settings.distanceAlert + " miles away.");
}
//Create the map with jQuery
$(function () {
var orig_LatLng = new google.maps.LatLng(orig_lat, orig_lng);
//Google maps settings
var myOptions = {
center: orig_LatLng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById(settings.mapDiv), myOptions);
//Create one infowindow to fill later
var infowindow = new google.maps.InfoWindow();
//Add user location marker
var marker = createUserMarker(orig_LatLng, "0", settings.startPinColor);
marker.setAnimation(google.maps.Animation.DROP);
var bounds = new google.maps.LatLngBounds();
bounds.extend(orig_LatLng);
$("#" + settings.listDiv).empty();
$(_locationset).each(function (i, location) {
bounds.extend(location.LatLng);
letter = String.fromCharCode("A".charCodeAt(0) + i);
location.Marker = createMarker(location.LatLng, letter, settings.pinColor);
create_infowindow(location);
listClick(letter, location);
});
// zoom in/out to show all markers
map.fitBounds(bounds);
function listClick(letter, shop) {
$('<li />').html("<div class=\"list-details\">"
+ "<div class=\"list-label\">" + letter + "<\/div><div class=\"list-content\">"
+ "<div class=\"loc-name\">" + shop.Name + "<\/div> <div class=\"loc-addr\">" + shop.Description + "<\/div>"
+ (shop.Distance ? "<div class=\"loc-addr2\"><i>approx. " + shop.Distance + " " + ((shop.Distance == 1) ? "mile" : "miles" ) + "</i><\/div>" : "")
+ "<div class=\"loc-web\"><a href=\"http://maps.google.co.uk/maps?saddr="
+ formatGoogleMapUrlString($("#address").val()) + "+%40" + orig_lat + "," + orig_lng
+ "&daddr=" + formatGoogleMapUrlString(shop.Name) + "+%40" + shop.LatLng.lat() + "," + shop.LatLng.lng()
+ "&hl=en" + "\" target=\"_blank\">ยป Get directions</a><\/div><\/div><\/div>")
.click(function () {
create_infowindow(shop, "left");
}).appendTo("#" + settings.listDiv);
};
//Custom marker function - aplhabetical
function createMarker(point, letter, pinColor) {
//Set up pin icon with the Google Charts API for all of our markers
var pinImage = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=" + letter + "|" + pinColor + "|" + settings.pinTextColor,
new google.maps.Size(21, 34),
new google.maps.Point(0, 0),
new google.maps.Point(10, 34));
var pinShadow = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_shadow",
new google.maps.Size(40, 37),
new google.maps.Point(0, 0),
new google.maps.Point(12, 35));
//Create the markers
return new google.maps.Marker({
position: point,
map: map,
icon: pinImage,
shadow: pinShadow,
draggable: false
});
};
//Custom marker function - aplhabetical
function createUserMarker(point, letter, pinColor) {
//Set up pin icon with the Google Charts API for all of our markers
var pinImage = new google.maps.MarkerImage("images/green_pin.png");
//Create the markers
return new google.maps.Marker({
position: point,
map: map,
title: "Your Location",
icon: pinImage,
draggable: false
});
};
//Infowindows
function create_infowindow(shop, listLocation) {
//Is the distance more than one mile?
if (shop.Distance == 1) {
var mi_s = "mile";
} else {
var mi_s = "miles";
}
var formattedAddress = "<div class=\"infoWindow\"><b>" + shop.Name + "<\/b>"
+ "<div>" + shop.Description + "<\/div>"
+ (shop.Distance ? "<div><i>" + shop.Distance + " " + mi_s + "<\/i><\/div><\/div>" : "<\/div>");
//Opens the infowindow when list item is clicked
if (listLocation == "left") {
infowindow.setContent(formattedAddress);
infowindow.open(shop.Marker.get(settings.mapDiv), shop.Marker);
}
//Opens the infowindow when the marker is clicked
else {
google.maps.event.addListener(shop.Marker, 'click', function () {
infowindow.setContent(formattedAddress);
infowindow.open(shop.Marker.get(settings.mapDiv), shop.Marker);
})
}
};
});
}
});
}
});
});
}
});
};
})(jQuery);
Under var settings I need xmlLocation to be the dynamically geolocated URL. It seems the variable is not being set correctly. I get an undefined error message when I try to call LocationGlobal. I have speculated on this issue and have hit a dead end with it. Any help is greatly appreciated. Thanks.
The alert shows undefined because of the callback nature of the getCurrentLocation() operation. You have to call the LocationGlobal dependent function from inside the callback function of the getCurrentLocation() operation. probably like,
navigator.geolocation.getCurrentPosition(function(position)
{
var lat = position.coords.latitude;
var lon = position.coords.longitude;
LocationGlobal = 'data/gen_default_map.php?lat=' + lat + '&lng=' + lon + '&radius=30';
alert(LocationGlobal);
$('#map').storeLocaor({ mapDiv: 'map', xmlLocation: LocationGlobal }); //Just an example
});
hope this helps.

Javascript array showing 0 length when populated

I'm sure this is really simple but I haven't had much luck figuring out what's wrong. I'm creating an empty array (locations), filling it with location objects in the getPartnerLocations function and then trying to plot the locations on the map with the drop function. The problem I'm having is that inside the drop function the locations array which has stuff in it is returning a length of zero so the loop in the isn't working. Any tips or ideas about what's going on here would be greatly appreciated.
var markers = [];
var locations = [];
var iterator = 0;
var map;
var geocoder;
var newYork = new google.maps.LatLng(40.7143528, -74.0059731);
function initialize() {
var mapOptions = {
zoom: 12,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: newYork
};
map = new google.maps.Map(document.getElementById("map_canvas"),mapOptions);
}
function getPartnerLocations() {
geocoder = new google.maps.Geocoder();
$('.partner').each(function(index){
var street = $('.partner-streetaddress',this).text();
var city = $('.partner-city',this).text();
var state = $('.partner-state',this).text();
var country = $('.partner-country',this).text();
var address = street + ', ' + city + ', ' + state + ', ' + country;
geocoder.geocode( { 'address': address}, function(results, status)
{
if (status == google.maps.GeocoderStatus.OK)
{
locations.push( results[0].geometry.location );
console.log(locations[index]);
}
else
{
console.log('failed to geocode address: ' + address);
}
});
});
initialize();
drop();
}
function addMarker() {
console.log('add marker function');
markers.push(new google.maps.Marker({
position: locations[iterator],
map: map,
draggable: false,
animation: google.maps.Animation.DROP
}));
iterator++;
}
function drop()
{
console.log(locations.length);
for (var i = 0; i < locations.length; i++) {
setTimeout(function() {
addMarker();
}, i * 200);
}
}
getPartnerLocations();
geocode is an asynchronous function.
The callback doesn't execute until some time after you call drop.
Therefore, when you call drop, the array is still empty.
You need to call initialize and drop after the last AJAX call replies, in the geocode callback.

Categories