I've got an issue that I can't fix, it's been weeks I'm trying to do so but I just can't figure out what I'm doing wrong, or if there's something wrong in the Parse PHP SDK.
I have built a website with Parse Server as the backend, it's a listing website with location detection, and I'm working on Chrome (I've tried Firefox and Safari as well, I get the same issue).
My steps:
When you got to the home page, the browser asks you to allow location detection, I allow it.
If you click on the location button, you can open a Google map into a modal and place the pin on click to the desired location, you can also set the distance range by moving a slider. I select London, UK
I get the right console messages in Chrome:
DISTANCE: 87 Km
MARKER LAT: 51.522497992110246 - MARKER LNG: -0.12863733794108612
The issue comes here, the website shows only some of the posted Ads, those ones that I have posted through a mobile app (my own source code too, with Parse iOS and Android SDKs), it doesn't display those ads I've submitted through the website. Weird thing, if I post ads from the website and I open my mobile app, I can see them!
And if I check my database, the GeoPoint coordinates of my ads are correctly stored, whether I submitted ads through the website or the mobile apps.
Lastly, If I perform a search by keywords with the search bar on the top of the page, I can find those ads I've posted through the website. So, basically, no keywords, no ads posted through the website...
Here's my PHP code to query ads (NOTE; the variables that start with $ADS_ are simple strings declared in another file, ex. $ADS_LOCATION = "ads_location", etc):
/*--- variables ---*/
$isFollowing = $_GET['isFollowing'];
$option = $_GET['option'];
$upObjID = $_GET['upObjID'];
$latitude = $_GET['lat']; // 51.522497992110246
$longitude = $_GET['lng']; // -0.12863733794108612
$dist = $_GET['distance'];
$distance = (int)$dist; // 50
$category = $_GET['category'];
$sortBy = str_replace(' ', '', $_GET['sortBy']);
$keywords = preg_split('/\s+/', $_GET['keywords']);
// query Ads
try {
$query = new ParseQuery($ADS_CLASS_NAME);
$query->equalTo($ADS_IS_REPORTED, false);
// it's following.php
if ($isFollowing == true) {
$currentUser = ParseUser::getCurrentUser();
$cuObjIdArr = array();
array_push($cuObjIdArr, $currentUser->getObjectId());
$query->containedIn($ADS_FOLLOWED_BY, $cuObjIdArr);
// it's User profile page
} else if ($upObjID != null) {
$userPointer = new ParseUser($USER_CLASS_NAME, $upObjID);
$userPointer->fetch();
$query->equalTo($ADS_SELLER_POINTER, $userPointer);
if ($option == 'selling'){ $query->equalTo($ADS_IS_SOLD, false);
} else if ($option == 'sold'){ $query->equalTo($ADS_IS_SOLD, true);
} else if ($option == 'liked'){ $query->containedIn($ADS_LIKED_BY, array($userPointer->getObjectId())); }
// it's index.php
} else {
// nearby Ads
if ($latitude != null && $longitude != null) {
$currentLocation = new ParseGeoPoint($latitude, $longitude);
$query->withinKilometers("location", $currentLocation, $distance);
// nearby DEFAULT LOCATION COORDINATES
} else {
$defaultLocation = new ParseGeoPoint($DEFAULT_LOCATION_LATITUDE, $DEFAULT_LOCATION_LONGITUDE);
$query->withinKilometers($ADS_LOCATION, $defaultLocation, $DISTANCE_IN_KM);
}
// keywords
if (count($keywords) != 0) { $query->containedIn($ADS_KEYWORDS, $keywords); }
// category
if ($category != "All") { $query->equalTo($ADS_CATEGORY, $category); }
// sort by
switch ($sortBy) {
case 'Newest': $query->descending("createdAt");
break;
case 'Price:lowtohigh': $query->ascending($ADS_PRICE);
break;
case 'Price:hightolow': $query->descending($ADS_PRICE);
break;
case 'MostLiked': $query->descending($ADS_LIKES);
break;
default: break;
}// ./ sort by
}// ./ If
// perform query
$adsArray = $query->find();
if (count($adsArray) != 0) {
for ($i = 0; $i < count($adsArray); $i++) {
// Parse Obj
$adObj = $adsArray[$i];
// image 1
$adImg = $adObj->get($ADS_IMAGE1);
// title
$adTitle = substr ($adObj->get($ADS_TITLE), 0, 24).'...';
// currency
$adCurrency = $adObj->get($ADS_CURRENCY);
// price
$adPrice = $adObj->get($ADS_PRICE);
echo '
<!-- Ad card -->
<div class="col-lg-3 col-md-5 portfolio-item">
<div class="card">
';
// Sold badge
$isSold = $adObj->get($ADS_IS_SOLD);
if ($isSold) { echo '<div class="card-sold-badge"><img src="assets/images/sold-badge.png"></div>'; }
echo '
<img src="'.$adImg->getURL().'">
<div class="card-body">
<p class="card-title">'.$adTitle.'</p>
<p class="card-price">'.$adCurrency.' '.$adPrice.'</p>
</div>
</div>
</div>
';
}// ./ For
// no ads
} else {
echo '
<div class="col-lg-12 col-md-12">
<div class="text-center" style="margin-top: 40px; font-weight: 600">No Ads found.</div>
</div>
';
}
// error
} catch (ParseException $e){ echo $e->getMessage(); }
The above code comes from a separate file called query-ads.php and it gets displayed in a div:
<div class="row" id="adsGrid"></div>
I perform an AJAX function to call query-ads.php:
function queryAds(catName, keywords) {
// category
if (catName == null) { catName = "All"; }
document.getElementById("categoryName").innerHTML = '<h5 id="categoryName"><strong>' + catName + '</strong></h5>';
// keywords
if (keywords == null) { keywords = ''; }
// console.log('KEYWORDS: ' + keywords);
// console.log('LAT: ' + latitude + ' -- LNG: ' + longitude);
console.log('DISTANCE: ' + distance + ' Km');
// console.log('SORT BY: ' + sortBy);
$.ajax({
url:'query-ads.php',
data: 'lat=' + latitude + '&lng=' + longitude + '&distance=' + distance + '&category=' + catName + '&keywords=' + keywords + '&sortBy=' + sortBy,
type: 'GET',
success:function(data) {
document.getElementById("adsGrid").innerHTML = data;
},
// error
error: function(xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
swal(err.Message);
}});
}
And here's my JavaScript code as well:
var cityStateButton = document.getElementById("cityState");
/*--- variables --*/
// localStorage.clear();
var latitude = localStorage.getItem('latitude');
var longitude = localStorage.getItem('longitude');
var distance = localStorage.getItem('distance');
if (distance == null) { distance = 50; }
var map;
var markers = [];
var geocoder;
var sortBy = document.getElementById('sortByBtn').innerHTML;
console.log("1st LATITUDE: " + latitude + " -- 1st LONGITUDE: " + longitude + ' -- 1st DISTANCE: ' + distance + ' -- 1st SORT BY: ' + sortBy);
// Call functions
if (latitude == null) { getCurrentLocation();
} else { getAddress(); }
// ------------------------------------------------
// MARK: - GET CURRENT LOCATION
// ------------------------------------------------
function getCurrentLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getPosition, showError);
} else {
swal("Geolocation is not supported by this browser.");
// set default location coordinates
latitude = <?php echo $DEFAULT_LOCATION_LATITUDE ?>;
longitude = <?php echo $DEFAULT_LOCATION_LONGITUDE ?>;
getAddress();
}
}
function getPosition(position) {
latitude = position.coords.latitude;
longitude = position.coords.longitude;
getAddress();
}
function showError(error) {
switch(error.code) {
case error.PERMISSION_DENIED:
swal("You have denied your current Location detection.");
break;
case error.POSITION_UNAVAILABLE:
swal("Location information is unavailable.");
break;
case error.TIMEOUT:
swal("The request to get your current location timed out.");
break;
case error.UNKNOWN_ERROR:
swal("An unknown error occurred.");
break;
}
// set default location
latitude = <?php echo $DEFAULT_LOCATION_LATITUDE ?>;
longitude = <?php echo $DEFAULT_LOCATION_LONGITUDE ?>;
getAddress();
}
function getAddress () {
// geocoding API
var geocodingAPI = "https://maps.googleapis.com/maps/api/geocode/json?latlng=" + latitude + "," + longitude + "&key=<?php echo $GOOGLE_MAP_API_KEY ?>";
$.getJSON(geocodingAPI, function (json) {
if (json.status == "OK") {
var result = json.results[0];
var city = "";
var state = "";
for (var i = 0, len = result.address_components.length; i < len; i++) {
var ac = result.address_components[i];
if (ac.types.indexOf("locality") >= 0) { city = ac.short_name; }
if (ac.types.indexOf("country") >= 0) { state = ac.short_name; }
}// ./ For
// show city, state
cityStateButton.innerHTML = '<i class="fas fa-location-arrow"></i> ' + city + ', ' + state;
// call query
queryAds();
// save gps coordinates
localStorage.setItem('latitude', latitude);
localStorage.setItem('longitude', longitude);
// console.log("LAT (getAddress): " + latitude + " -- LNG (getAddress): " + longitude);
// call function
initMap();
}// ./ If
});
}
//---------------------------------
// MARK - INIT GOOGLE MAP
//---------------------------------
var mapZoom = 12;
function initMap() {
var location = new google.maps.LatLng(latitude, longitude);
map = new google.maps.Map(document.getElementById('map'), {
zoom: mapZoom,
center: location,
mapTypeId: 'roadmap',
disableDefaultUI: true,
mapTypeControl: false,
scaleControl: false,
zoomControl: false
});
// call addMarker() when the map is clicked.
map.addListener('click', function(event) {
addMarker(event.latLng);
});
// Add a marker in the center of the map.
addMarker(location);
}
function addMarker(location) {
clearMarkers();
var marker = new google.maps.Marker({
position: location,
map: map
});
markers.push(marker);
// set lat & lng based on marker's coordinates
latitude = marker.getPosition().lat();
longitude = marker.getPosition().lng();
console.log("MARKER LAT: " + latitude + " - MARKER LNG: " + longitude);
// zoom & center map based on pin
metersPerPx = 156543.03392 * Math.cos(latitude * Math.PI / 180) / Math.pow(2, mapZoom)
map.setZoom(metersPerPx/2.6);
map.setCenter(marker.getPosition());
}
function setMapOnAll(map) {
for (var i = 0; i < markers.length; i++) {
markers[i].setMap(map);
}// ./ For
}
// Removes the markers from the map, but keeps them in the array.
function clearMarkers() {
setMapOnAll(null);
markers = [];
}
//---------------------------------
// MARK - GET GPS COORDS FROM ADDRESS
//---------------------------------
function getCoordsFromAddress(address) {
geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == 'OK') {
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
markers.push(marker);
// set coordinates
latitude = results[0].geometry.location.lat();
longitude = results[0].geometry.location.lng();
// save gps coordinates
localStorage.setItem('latitude', latitude);
localStorage.setItem('longitude', longitude);
// console.log("SEARCH LOCATION LAT: " + latitude + " - SEARCH LOCATION LNG: " + longitude);
initMap();
// error
} else { swal('Geocode was not successful for the following reason: ' + status);
}});
}
I thought it was an issue with ParseGeoPoint, but it's not, because if I make a text search for keywords, I can find Ads.
Found the solution, I'm posting it just in case someone else will encounter such issue on the Parse PHP SDK, because it's there since early 2018.
In my script that saves Ads in my database, I was storing lowercase keywords based on the description and title of submitted items:
$kStr = $description. " " .$title. " " .$currentUser->getUsername();
$keywords = explode( " ", strtolower($kStr) );
Which was working fine, but since the PHP ParseQuery with a keywords filter needs at least an empty item in the array of keywords, I had to add this simple line below the above code:
array_push($keywords, "");
In this way, the database stores all proper keywords + an empty one: "", here's an example of how the keywords Arrat-type field looks like in the database:
[
"lorem",
"ad",
"ipsum",
"johndoe",
"" <-- this is the empty item that the SDK needs to properly perform a query in case of additional filters (in my case, a ParseGeopoint one)
]
This may be just a temporary trick until Parse Server's developers will fix this issue. It works, so why not ;)
Related
I'm hitting an issue that is WELL discussed in these forums, but none of the recommendations seem to be working for me so I'm looking for some full javascript that works when saved as an html file.
The issue is I keep hitting the OVER_QUERY_LIMIT error when trying to geocode > 11 locations on a Google Map using the V3 APIs called by Javascript. I understand that there is a limit to the rate at which you can call the geocoder (as well as the daily limit on total volume), so I need to introduce a pause in between each result in the array.
Any help very much appreciated.
Here is my code:
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
var geocoder;
var map;
var wait = false;
function initialize() {
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(51.32, 0.5);
var myOptions = {
zoom: 8,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
codeAddress('KT16 8LA' + ', UK');
codeAddress('LS8 2LQ' + ', UK');
codeAddress('NE13 8AF' + ', UK');
codeAddress('KT12 2BE' + ', UK');
codeAddress('W1W 8AN' + ', UK');
codeAddress('EC3N 2LS' + ', UK');
codeAddress('BS9 3BH' + ', UK');
codeAddress('KA10 6LZ' + ', UK');
codeAddress('EC1V 9BW' + ', UK');
codeAddress('WD18 8YN' + ', UK');
codeAddress('HA3 6DQ' + ', UK');
codeAddress('W1U 3PL' + ', UK');
codeAddress('W1T 7QL' + ', UK');
codeAddress('W1S 1TD' + ', UK');
codeAddress('SW1X 8NX' + ', UK');
codeAddress('LE2 8ET' + ', UK');
codeAddress('BA3 4BH' + ', UK');
codeAddress('AL3 8JP' + ', UK');
codeAddress('DE55 4QJ' + ', UK');
codeAddress('W6 0QT' + ', UK');
codeAddress('LA1 1PP' + ', UK');
codeAddress('SW16 4DH' + ', UK');
codeAddress('WC2N 6DF' + ', UK');
codeAddress('RM6 6LS' + ', UK');
codeAddress('S25 3QZ' + ', UK');
codeAddress('WC2H 7LR' + ', UK');
codeAddress('BH24 1DW' + ', UK');
codeAddress('EC2N 6AR' + ', UK');
codeAddress('W1U 2FA' + ', UK');
codeAddress('B60 3DX' + ', UK');
}
function codeAddress(vPostCode) {
if (geocoder) {
geocoder.geocode( { 'address': "'" + vPostCode + "'"}, 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);
}
});
}
}
</script>
<body style="margin:0px; padding:0px;" onload="initialize()">
<div id="map_canvas" style="width:100%; height:90%"></div>
</body>
EDIT: This is what I've tried to do to get it to pause/wait in the relevant section, but it doesn't do anything:
function codeAddress(vPostCode) {
if (geocoder) {
while (wait) { /* Just wait. */ };
geocoder.geocode( { 'address': "'" + vPostCode + "'"}, 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
});
/* When geocoding "fails", see if it was because of over quota error: */
} else if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
wait = true;
setTimeout("wait = true", 2000);
//alert("OQL: " + status);
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
}
Nothing like these two lines appears in Mike Williams' tutorial:
wait = true;
setTimeout("wait = true", 2000);
Here's a Version 3 port:
http://acleach.me.uk/gmaps/v3/plotaddresses.htm
The relevant bit of code is
// ====== Geocoding ======
function getAddress(search, next) {
geo.geocode({address:search}, function (results,status)
{
// If that was successful
if (status == google.maps.GeocoderStatus.OK) {
// Lets assume that the first marker is the one we want
var p = results[0].geometry.location;
var lat=p.lat();
var lng=p.lng();
// Output the data
var msg = 'address="' + search + '" lat=' +lat+ ' lng=' +lng+ '(delay='+delay+'ms)<br>';
document.getElementById("messages").innerHTML += msg;
// Create a marker
createMarker(search,lat,lng);
}
// ====== Decode the error status ======
else {
// === if we were sending the requests to fast, try this one again and increase the delay
if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
nextAddress--;
delay++;
} else {
var reason="Code "+status;
var msg = 'address="' + search + '" error=' +reason+ '(delay='+delay+'ms)<br>';
document.getElementById("messages").innerHTML += msg;
}
}
next();
}
);
}
The general answer to this question is:
Don't geocode known locations every time you load your page. Geocode them off-line and use the resulting coordinates to display the markers on your page.
The limits exist for a reason.
If you can't geocode the locations off-line, see this page (Part 17 Geocoding multiple addresses) from Mike Williams' v2 tutorial which describes an approach, port that to the v3 API.
Here I have loaded 2200 markers. It takes around 1 min to add 2200 locations.
https://jsfiddle.net/suchg/qm1pqunz/11/
//function to get random element from an array
(function($) {
$.rand = function(arg) {
if ($.isArray(arg)) {
return arg[$.rand(arg.length)];
} else if (typeof arg === "number") {
return Math.floor(Math.random() * arg);
} else {
return 4; // chosen by fair dice roll
}
};
})(jQuery);
//start code on document ready
$(document).ready(function () {
var map;
var elevator;
var myOptions = {
zoom: 0,
center: new google.maps.LatLng(35.392738, -100.019531),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map($('#map_canvas')[0], myOptions);
//get place from inputfile.js
var placesObject = place;
errorArray = [];
//will fire 20 ajax request at a time and other will keep in queue
var queuCounter = 0, setLimit = 20;
//keep count of added markers and update at top
totalAddedMarkers = 0;
//make an array of geocode keys to avoid the overlimit error
var geoCodKeys = [
'AIzaSyCF82XXUtT0vzMTcEPpTXvKQPr1keMNr_4',
'AIzaSyAYPw6oFHktAMhQqp34PptnkDEdmXwC3s0',
'AIzaSyAwd0OLvubYtKkEWwMe4Fe0DQpauX0pzlk',
'AIzaSyDF3F09RkYcibDuTFaINrWFBOG7ilCsVL0',
'AIzaSyC1dyD2kzPmZPmM4-oGYnIH_0x--0hVSY8'
];
//funciton to add marker
var addMarkers = function(address, queKey){
var key = jQuery.rand(geoCodKeys);
var url = 'https://maps.googleapis.com/maps/api/geocode/json?key='+key+'&address='+address+'&sensor=false';
var qyName = '';
if( queKey ) {
qyName = queKey;
} else {
qyName = 'MyQueue'+queuCounter;
}
$.ajaxq (qyName, {
url: url,
dataType: 'json'
}).done(function( data ) {
var address = getParameterByName('address', this.url);
var index = errorArray.indexOf(address);
try{
var p = data.results[0].geometry.location;
var latlng = new google.maps.LatLng(p.lat, p.lng);
new google.maps.Marker({
position: latlng,
map: map
});
totalAddedMarkers ++;
//update adde marker count
$("#totalAddedMarker").text(totalAddedMarkers);
if (index > -1) {
errorArray.splice(index, 1);
}
}catch(e){
if(data.status = 'ZERO_RESULTS')
return false;
//on error call add marker function for same address
//and keep in Error ajax queue
addMarkers( address, 'Errror' );
if (index == -1) {
errorArray.push( address );
}
}
});
//mentain ajax queue set
queuCounter++;
if( queuCounter == setLimit ){
queuCounter = 0;
}
}
//function get url parameter from url string
getParameterByName = function ( name,href )
{
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( href );
if( results == null )
return "";
else
return decodeURIComponent(results[1].replace(/\+/g, " "));
}
//call add marker function for each address mention in inputfile.js
for (var x = 0; x < placesObject.length; x++) {
var address = placesObject[x]['City'] + ', ' + placesObject[x]['State'];
addMarkers(address);
}
});
Using "setInterval" & "clearInterval" fixes the problem:
function drawMarkers(map, markers) {
var _this = this,
geocoder = new google.maps.Geocoder(),
geocode_filetrs;
_this.key = 0;
_this.interval = setInterval(function() {
_this.markerData = markers[_this.key];
geocoder.geocode({ address: _this.markerData.address }, yourCallback(_this.markerData));
_this.key++;
if ( ! markers[_this.key]) {
clearInterval(_this.interval);
}
}, 300);
}
this post was made a while ago, but it provides an answer that did not solve the problem regarding reaching the limit of requests in an iteration for me, so I publish this, to help who else has not served.
My environment happened in Ionic 3.
Instead of making a "pause" in the iteration, I ocurred the idea of iterating with a timer, this timer has the particularity of executing the code that would go in the iteration, but will run every so often until it is reached the maximum count of the "Array" in which we want to iterate.
In other words, we will consult the Google API in a certain time so that it does not exceed the limit allowed in milliseconds.
// Code to start the timer
this.count= 0;
let loading = this.loadingCtrl.create({
content: 'Buscando los mejores servicios...'
});
loading.present();
this.interval = setInterval(() => this.getDistancias(loading), 40);
// Function that runs the timer, that is, query Google API
getDistancias(loading){
if(this.count>= this.datos.length){
clearInterval(this.interval);
} else {
var sucursal = this.datos[this.count];
this.calcularDistancia(this.posicion, new LatLng(parseFloat(sucursal.position.latitude),parseFloat(sucursal.position.longitude)),sucursal.codigo).then(distancia => {
}).catch(error => {
console.log('error');
console.log(error);
});
}
this.count += 1;
}
calcularDistancia(miPosicion, markerPosicion, codigo){
return new Promise(async (resolve,reject) => {
var service = new google.maps.DistanceMatrixService;
var distance;
var duration;
service.getDistanceMatrix({
origins: [miPosicion, 'salida'],
destinations: [markerPosicion, 'llegada'],
travelMode: 'DRIVING',
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, function(response, status){
if (status == 'OK') {
var originList = response.originAddresses;
var destinationList = response.destinationAddresses;
try{
if(response != null && response != undefined){
distance = response.rows[0].elements[0].distance.value;
duration = response.rows[0].elements[0].duration.text;
resolve(distance);
}
}catch(error){
console.log("ERROR GOOGLE");
console.log(status);
}
}
});
});
}
I hope this helps!
I'm sorry for my English, I hope it's not an inconvenience, I had to use the Google translator.
Regards, Leandro.
You are using setTimeout wrong way. The (one of) function signature is setTimeout(callback, delay). So you can easily specify what code should be run after what delay.
var codeAddress = (function() {
var index = 0;
var delay = 100;
function GeocodeCallback(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
new google.maps.Marker({ map: map, position: results[0].geometry.location, animation: google.maps.Animation.DROP });
console.log(results);
}
else alert("Geocode was not successful for the following reason: " + status);
};
return function(vPostCode) {
if (geocoder) setTimeout(geocoder.geocode.bind(geocoder, { 'address': "'" + vPostCode + "'"}, GeocodeCallback), index*delay);
index++;
};
})();
This way, every codeAddress() call will result in geocoder.geocode() being called 100ms later after previous call.
I also added animation to marker so you will have a nice animation effect with markers being added to map one after another. I'm not sure what is the current google limit, so you may need to increase the value of delay variable.
Also, if you are each time geocoding the same addresses, you should instead save the results of geocode to your db and next time just use those (so you will save some traffic and your application will be a little bit quicker)
I already have a working map where it generates a valid xml and can generate a marker on my map. Now what I want to do is to upon generating of maps for each loop in the xml I can create asynchronously a check box for each name. here is the code that generates the xml
function searchNearLocations(radius){
clearLocations();
var searchUrl = './designIncludes/phpLogicIncludes/searchMarkers.php?lat=' + userLat +'&lng=' + userLng + '&radius=' + radius;
downloadUrl(searchUrl, function(data) {
var xml = parseXml(data);
var markerNodes = xml.documentElement.getElementsByTagName("marker");
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < markerNodes.length; i++) {
var name = markerNodes[i].getAttribute("name");
var address = markerNodes[i].getAttribute("address");
var info = markerNodes[i].getAttribute("info");
var tts = markerNodes[i].getAttribute("tts");
var latlng = new google.maps.LatLng(
parseFloat(markerNodes[i].getAttribute("lat")),
parseFloat(markerNodes[i].getAttribute("lng")));
createMarker(latlng, name, address,info,tts);
createCheckboxes(name);
bounds.extend(latlng);
}
map.fitBounds(bounds);
});
}
My createMarker()
function createMarker(latlng, name, address,info,tts) {
var html = "<b>" + name + "</b></br><u>" + address + "</u></br>" + info + "</br>" + "Time allowance to spend: " + tts;
var marker = new google.maps.Marker({
map: map,
position: latlng
});
google.maps.event.addListener(marker, 'click', function() {
infoWindow.setContent(html);
infoWindow.open(map, marker);
});
markers.push(marker);
}
I'm not that good in javascript but I have a marker=[] array global variable. Can I use that variable to generate a checkbox with it?by the way I also have a pre made marker before invoking the searchNearLocations function and I want to add it on the latlng bounds. Is it possible to insert it on the loop?
Okay
Everything not having to do with the checkboxes, I ignored. You'll have to put it back in your code.
I wrote a functioning example, you can copy/paste this as is.
<script src="http://maps.googleapis.com/maps/api/js?v=3.exp"></script>
<script type="text/javascript">
var markers = [];
var map;
/* I don't have that XML. I'll just insert a few locations hard coded. You can ignore this; it doesn't affect your question */
var markerNodes = [
{latlng: new google.maps.LatLng(50.896328544032805,4.4825010816688), name: 'Brussels Airport', address: 'A201, 1930 Zaventem', info: 'National airport of Brussels', tts: 'foo'},
{latlng: new google.maps.LatLng(50.8957080950929,4.334064952575659), name: 'Football stadion', address: 'Marathonlaan', info: 'Football stadion of the Red Devils', tts: 'foo'},
{latlng: new google.maps.LatLng(50.82302545625156,4.39255052014533), name: 'VUB campus', address: 'Pleinlaan 2', info: 'University of Brussels', tts: 'foo'}
];
function initialize() {
var position = new google.maps.LatLng(50.84499325563654,4.349978498661017);
var myOptions = {
zoom: 11,
center: position
};
map = new google.maps.Map(document.getElementById("map-canvas"), myOptions);
searchNearLocations(null); // I ignore the radius part. I presume this searches for locations in a DB, or something...
}
/* never mind most of the changes I made here, it's just to ignore the xml part of the code */
function searchNearLocations(radius) {
clearLocations();
var bounds = new google.maps.LatLngBounds();
for (var i=0; i<markerNodes.length; i++) {
var latlng = markerNodes[i].latlng,
name = markerNodes[i].name,
address = markerNodes[i].address,
info = markerNodes[i].info,
tts = markerNodes[i].tts;
createMarker(latlng, name, address, info, tts);
createCheckbox(name, i); // the most important thing is to pass the i; then markers[i] corresponds with checkboxes[i]
bounds.extend(latlng);
}
map.fitBounds(bounds);
}
function createMarker(latlng, name, address, info, tts) {
// var html = "<b>" + name + "</b></br><u>" + address + "</u></br>" + info + "</br>" + "Time allowance to spend: " + tts;
var marker = new google.maps.Marker({
map: map,
title: name, // You should add this
position: latlng
});
google.maps.event.addListener(marker, 'click', function() {
/* I'll ignore the infoWindow */
});
markers.push(marker);
}
function clearLocations() {
for (var i=0; i<markers.length; i++) {
markers[i].setMap(null);
}
markers=[];
}
function checkboxChanged(i, checked) {
if(checked) {
markers[i].setMap(map);
}
else {
markers[i].setMap(null);
}
}
function createCheckbox(name, i) {
document.getElementById('checkboxes').innerHTML +=
'<input type="checkbox" checked="checked" onclick="checkboxChanged(' + i + ', this.checked);"> ' + name + '<br>';
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
<style>
#map-canvas {
width: 500px;
height: 400px;
}
</style>
<div id="map-canvas"></div>
<div id="checkboxes"></div>
Can you manage to include this in your code?
EDIT:
Here is an example of locations in a DB; I bypass xml and communicate through JSON.
It gives you (kind of) a combination of your questions;
I have a different table on my DB, but that shouldn't be a problem.
I add jQuery, just for ajax.
CREATE TABLE IF NOT EXISTS stations (
id bigint(15) NOT NULL AUTO_INCREMENT,
lat decimal(12,10) DEFAULT NULL,
lng decimal(12,10) DEFAULT NULL,
name varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
INSERT INTO stations (id, lat, lng, name) VALUES
(1, '50.8456035000', '4.3568658000', 'Brussel-Centraal'),
(2, '50.8413140000', '4.3490830000', 'Brussel-Kapellekerk'),
(3, '50.8517507000', '4.3623635000', 'Brussel-Congres'),
(4, '50.8604931000', '4.3607035000', 'Brussel-Noord'),
(5, '50.8348278000', '4.3365303000', 'Brussel-Zuid');
index.php
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://maps.googleapis.com/maps/api/js?v=3.exp"></script>
<script type="text/javascript">
var markers = [];
var map;
// settings, to pass to the DB
var lat = 50.84499325563654;
var lng = 4.349978498661017;
var radius = 1.5; /* set this to more than 1.5 to see more locations */
function initialize() {
var position = new google.maps.LatLng(50.84499325563654, 4.349978498661017);
var myOptions = {
zoom: 11,
center: position
};
map = new google.maps.Map(document.getElementById("map-canvas"), myOptions);
searchNearLocations(lat, lng, radius);
}
function searchNearLocations(lat, lng, radius) {
document.getElementById('checkboxes').innerHTML = '';
// we start an Ajax call.
$.ajax({
url: 'ajax.php',
data: {lat: lat, lng: lng, radius: radius},
dataType: 'json',
success: function(data) {
// the request has returned data. Now we can proceed
clearLocations();
var bounds = new google.maps.LatLngBounds();
for (var i=0; i<data.length; i++) {
var latlng = new google.maps.LatLng(data[i].lat, data[i].lng),
name = data[i].name;
/* add what ever extra data you need */
createMarker(latlng, name, null, null, null);
createCheckbox(name, i); // the most important thing is to pass the i; then markers[i] corresponds with checkboxes[i]
bounds.extend(latlng);
}
map.fitBounds(bounds);
}
});
}
function createMarker(latlng, name, address, info, tts) {
/* var html = "<b>" + name + "</b></br><u>" + address + "</u></br>" + info + "</br>" + "Time allowance to spend: " + tts; */
var marker = new google.maps.Marker({
map: map,
title: name, // You should add this
position: latlng
});
google.maps.event.addListener(marker, 'click', function() {
/* I'll ignore the infoWindow */
});
markers.push(marker);
}
function clearLocations() {
for (var i=0; i<markers.length; i++) {
markers[i].setMap(null);
}
markers=[];
}
function checkboxChanged(i, checked) {
if(checked) {
markers[i].setMap(map);
}
else {
markers[i].setMap(null);
}
}
function createCheckbox(name, i) {
document.getElementById('checkboxes').innerHTML +=
'<input type="checkbox" checked="checked" onclick="checkboxChanged(' + i + ', this.checked);"> ' + name + '<br>';
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
<style>
#map-canvas {
width: 500px;
height: 400px;
}
</style>
<div id="map-canvas"></div>
<div id="checkboxes"></div>
ajax.php
<?php
$link = mysqli_connect('localhost', 'root', '', 'stackoverflow'); /* put your settings back */
if(!$link) {
die ('unable to connect to the database' . mysqli_connect_error());
}
//Get parameters from URL
$myLat = (isset ($_GET['lat']) ? $_GET['lat'] : 0.0); // give it some default
$myLng = (isset ($_GET['lng']) ? $_GET['lng'] : 0.0);
$calcDistance = (isset ($_GET['radius']) ? $_GET['radius'] : 1.0);
//Search the rows in the markers table
/* I have a slightly different table; I'll continue with mine; it's easy to put everything back */
// $query = sprintf("SELECT siteName,address,lat,lng,info,tts, (6371 * acos(cos(radians('%s')) * cos(radians(lat)) * cos(radians(lng) - radians ('%s')) + sin(radians('%s')) * sin(radians(lat))))AS distance FROM mapTable HAVING distance < '%s' ORDER BY distance LIMIT 0, 50",
$query = sprintf("SELECT id, lat, lng, name, (6371 * acos(cos(radians('%s')) * cos(radians(lat)) * cos(radians(lng) - radians ('%s')) + sin(radians('%s')) * sin(radians(lat)))) AS distance FROM stations HAVING distance < '%s' ORDER BY distance LIMIT 0, 50",
mysqli_real_escape_string($link, $myLat),
mysqli_real_escape_string($link, $myLng),
mysqli_real_escape_string($link,$myLat),
mysqli_real_escape_string($link, $calcDistance));
$result = mysqli_query($link, $query);
$row_cnt = mysqli_num_rows($result);
if(!$result) {
die("Invalid query: " . mysqli_error());
}
header("content-type: application/json; charset=utf-8");
$items = array();
//iterate through the rows,
while($row = mysqli_fetch_assoc($result)) {
$items[] = $row;
}
echo json_encode($items); // this can immediatly be read by javascript
exit;
?>
You can add links/buttons, whith something like
<div onclick="searchNearLocations(50.80, 4.35, 20)">click: 20 km radius from (50.80, 4.35)</div>
To add the position of the client, check out my answer here
google getLocation function not work in my script
I have this code that selects the type of a restaurant. After selecting any type the page is refreshed and after some SQL processing I get all restaurants corresponding to the selected type and show it in Google Maps.
How can I do that without refreshing the complete page, like only refreshing the <div> containing Google Maps?
<select class="mapleftS" name="type" id="type" onchange="changeType(this.value)">
<option value="0">كل الانواع</option>
<?$type = mysql_query("select * from rest_type ");
while($rod = mysql_fetch_array( $type )) {
if($rod[id] == $_REQUEST['type'])
$selll = 'selected';
else {$selll = '';
?>
<option value="<?=$rod[id]?>" <?=$selll?> ><?=$rod[name]?></option>
<? } ?>
</select>
<script>
function changeType( id ) {
parent.location = '?type=' + id;
}
$(function() {
var text_menu = $("#type option:selected").text();
$("#ddddd_").html(text_menu);
});
</script>
After selection this code is run:
if($_REQUEST['type']) {
// do some thing and refrsh map div
} else {
// do some thing and refrsh map div
}
And the following element contains Google Maps:
<div id="mppp" class="map"></div>
JS for Google Maps:
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key=SOMEAPIKEY&sensor=true"></script>
<script type="text/javascript">
var address_index = 0, map, infowindow, geocoder, bounds, mapinfo, intTimer;
$(function (){
mm();
});
mm = function() {
// Creating an object literal containing the properties you want to pass to the map
var options = {
zoom: 15,
center: new google.maps.LatLng(24.701564296830245, 46.76211117183027),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// Creating the map
map = new google.maps.Map(document.getElementById('mppp'), options);
infowindow = new google.maps.InfoWindow();
geocoder = new google.maps.Geocoder();
bounds = new google.maps.LatLngBounds();
//******* ARRAY BROUGHT OVER FROM SEARCHRESULTS.PHP **********
mapinfo = [ <?=$da?> ];
intTimer = setInterval("call_geocode();", 300);
}
function call_geocode() {
if( address_index >= mapinfo.length ) {
clearInterval(intTimer);
return;
}
geocoder.geocode({
location: new google.maps.LatLng(mapinfo[address_index][6], mapinfo[address_index][7])
}, (function(index) {
return function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
// Scale our bounds
bounds.extend(results[0].geometry.location);
var $id = mapinfo[index][0];
var $tell = mapinfo[index][3];
var $title = mapinfo[index][2];
var $img_src = mapinfo[index][3];
var img_src = mapinfo[index][1];
var $logo = mapinfo[index][4];
var $status = mapinfo[index][5];
var $sell = mapinfo[index][6];
var $city = mapinfo[index][8];
var marker = new google.maps.Marker({
position: new google.maps.LatLng(mapinfo[index][6], mapinfo[index][7]),
icon: {
url : '<? bloginfo('url'); ?>' + img_src + '',
scaledSize : new google.maps.Size(50,50)
},
map: map,
scrollwheel: false,
streetViewControl: true,
title: $title
});
google.maps.event.addListener(marker, 'click', function() {
// Setting the content of the InfoWindow
if (img_src) {
var imdd = '<img src="<? bloginfo('url'); ?>' + img_src + '" width="60" height="60" style="margin-left:4px;float:right;" />';
}
else {
var imdd = '';
}
if ($tell) {
var tell = 'رقم الهاتف : '+$tell+'<br>';
}
else {
var tell = '';
}
if ($status) {
var status = 'الحي : '+$status+'<br>';
}
else {
var status = '';
}
if ($city) {
var city = 'المدينة : '+$city+'<br>';
}
else {
var city = '';
}
var content = '<div id="info" style="direction:rtl;font:15px time new roman;font-weight:bolder;position:relative;width:210px;"><div style=" font-size:13px;font-family:Tahoma;font-weight:bolder;text-align:center;font-weight:bold">' + $title + '</div><br><div style="float:right">' + imdd + '</div><div style="float:right;text-align:right;font-family:Tahoma">' + tell + city + status + '</div><br /><a style="float:left;color:#d22f00;font-size:12px;font-family:Tahoma" href="<? bloginfo('url'); ?>/rest-det/?id=' + $id + '">المزيد+</a></div>';
infowindow.setContent(content);
infowindow.open(map, marker);
});
map.fitBounds(bounds);
if (mapinfo.length == 1) {
map.setZoom(12);
}
}
else {
// error!! alert (status);
}
}
}
)(address_index)
);
address_index++;
}
</script>
<div id="mppp" class="map"></div>
You can use an AJAX pattern to refresh part of your page.
move your SQL code into another script - e.g. query.php
return a list of results in a JSON format
when the list changes call runQuery
use the function to parse the returned data and update your map
<script>
function runQuery() {
$.ajax({
url: "query.php?type="+ $("#type").val(),
cache: false,
success: function(data){
// code to process your results list;
}
});
}
</script>
This is an AJAX concept where you are able to change only a portion of your page without having to do a full page refresh or Postback.
You will find a ton of examples on what you are trying to do but the main concept is that you will:
-Take user input
-call back to your server with values
-have the server return you information that you then use to append or overwrite a portion of the page
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.
I'm hitting an issue that is WELL discussed in these forums, but none of the recommendations seem to be working for me so I'm looking for some full javascript that works when saved as an html file.
The issue is I keep hitting the OVER_QUERY_LIMIT error when trying to geocode > 11 locations on a Google Map using the V3 APIs called by Javascript. I understand that there is a limit to the rate at which you can call the geocoder (as well as the daily limit on total volume), so I need to introduce a pause in between each result in the array.
Any help very much appreciated.
Here is my code:
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
var geocoder;
var map;
var wait = false;
function initialize() {
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(51.32, 0.5);
var myOptions = {
zoom: 8,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
codeAddress('KT16 8LA' + ', UK');
codeAddress('LS8 2LQ' + ', UK');
codeAddress('NE13 8AF' + ', UK');
codeAddress('KT12 2BE' + ', UK');
codeAddress('W1W 8AN' + ', UK');
codeAddress('EC3N 2LS' + ', UK');
codeAddress('BS9 3BH' + ', UK');
codeAddress('KA10 6LZ' + ', UK');
codeAddress('EC1V 9BW' + ', UK');
codeAddress('WD18 8YN' + ', UK');
codeAddress('HA3 6DQ' + ', UK');
codeAddress('W1U 3PL' + ', UK');
codeAddress('W1T 7QL' + ', UK');
codeAddress('W1S 1TD' + ', UK');
codeAddress('SW1X 8NX' + ', UK');
codeAddress('LE2 8ET' + ', UK');
codeAddress('BA3 4BH' + ', UK');
codeAddress('AL3 8JP' + ', UK');
codeAddress('DE55 4QJ' + ', UK');
codeAddress('W6 0QT' + ', UK');
codeAddress('LA1 1PP' + ', UK');
codeAddress('SW16 4DH' + ', UK');
codeAddress('WC2N 6DF' + ', UK');
codeAddress('RM6 6LS' + ', UK');
codeAddress('S25 3QZ' + ', UK');
codeAddress('WC2H 7LR' + ', UK');
codeAddress('BH24 1DW' + ', UK');
codeAddress('EC2N 6AR' + ', UK');
codeAddress('W1U 2FA' + ', UK');
codeAddress('B60 3DX' + ', UK');
}
function codeAddress(vPostCode) {
if (geocoder) {
geocoder.geocode( { 'address': "'" + vPostCode + "'"}, 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);
}
});
}
}
</script>
<body style="margin:0px; padding:0px;" onload="initialize()">
<div id="map_canvas" style="width:100%; height:90%"></div>
</body>
EDIT: This is what I've tried to do to get it to pause/wait in the relevant section, but it doesn't do anything:
function codeAddress(vPostCode) {
if (geocoder) {
while (wait) { /* Just wait. */ };
geocoder.geocode( { 'address': "'" + vPostCode + "'"}, 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
});
/* When geocoding "fails", see if it was because of over quota error: */
} else if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
wait = true;
setTimeout("wait = true", 2000);
//alert("OQL: " + status);
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
}
Nothing like these two lines appears in Mike Williams' tutorial:
wait = true;
setTimeout("wait = true", 2000);
Here's a Version 3 port:
http://acleach.me.uk/gmaps/v3/plotaddresses.htm
The relevant bit of code is
// ====== Geocoding ======
function getAddress(search, next) {
geo.geocode({address:search}, function (results,status)
{
// If that was successful
if (status == google.maps.GeocoderStatus.OK) {
// Lets assume that the first marker is the one we want
var p = results[0].geometry.location;
var lat=p.lat();
var lng=p.lng();
// Output the data
var msg = 'address="' + search + '" lat=' +lat+ ' lng=' +lng+ '(delay='+delay+'ms)<br>';
document.getElementById("messages").innerHTML += msg;
// Create a marker
createMarker(search,lat,lng);
}
// ====== Decode the error status ======
else {
// === if we were sending the requests to fast, try this one again and increase the delay
if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
nextAddress--;
delay++;
} else {
var reason="Code "+status;
var msg = 'address="' + search + '" error=' +reason+ '(delay='+delay+'ms)<br>';
document.getElementById("messages").innerHTML += msg;
}
}
next();
}
);
}
The general answer to this question is:
Don't geocode known locations every time you load your page. Geocode them off-line and use the resulting coordinates to display the markers on your page.
The limits exist for a reason.
If you can't geocode the locations off-line, see this page (Part 17 Geocoding multiple addresses) from Mike Williams' v2 tutorial which describes an approach, port that to the v3 API.
Here I have loaded 2200 markers. It takes around 1 min to add 2200 locations.
https://jsfiddle.net/suchg/qm1pqunz/11/
//function to get random element from an array
(function($) {
$.rand = function(arg) {
if ($.isArray(arg)) {
return arg[$.rand(arg.length)];
} else if (typeof arg === "number") {
return Math.floor(Math.random() * arg);
} else {
return 4; // chosen by fair dice roll
}
};
})(jQuery);
//start code on document ready
$(document).ready(function () {
var map;
var elevator;
var myOptions = {
zoom: 0,
center: new google.maps.LatLng(35.392738, -100.019531),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map($('#map_canvas')[0], myOptions);
//get place from inputfile.js
var placesObject = place;
errorArray = [];
//will fire 20 ajax request at a time and other will keep in queue
var queuCounter = 0, setLimit = 20;
//keep count of added markers and update at top
totalAddedMarkers = 0;
//make an array of geocode keys to avoid the overlimit error
var geoCodKeys = [
'AIzaSyCF82XXUtT0vzMTcEPpTXvKQPr1keMNr_4',
'AIzaSyAYPw6oFHktAMhQqp34PptnkDEdmXwC3s0',
'AIzaSyAwd0OLvubYtKkEWwMe4Fe0DQpauX0pzlk',
'AIzaSyDF3F09RkYcibDuTFaINrWFBOG7ilCsVL0',
'AIzaSyC1dyD2kzPmZPmM4-oGYnIH_0x--0hVSY8'
];
//funciton to add marker
var addMarkers = function(address, queKey){
var key = jQuery.rand(geoCodKeys);
var url = 'https://maps.googleapis.com/maps/api/geocode/json?key='+key+'&address='+address+'&sensor=false';
var qyName = '';
if( queKey ) {
qyName = queKey;
} else {
qyName = 'MyQueue'+queuCounter;
}
$.ajaxq (qyName, {
url: url,
dataType: 'json'
}).done(function( data ) {
var address = getParameterByName('address', this.url);
var index = errorArray.indexOf(address);
try{
var p = data.results[0].geometry.location;
var latlng = new google.maps.LatLng(p.lat, p.lng);
new google.maps.Marker({
position: latlng,
map: map
});
totalAddedMarkers ++;
//update adde marker count
$("#totalAddedMarker").text(totalAddedMarkers);
if (index > -1) {
errorArray.splice(index, 1);
}
}catch(e){
if(data.status = 'ZERO_RESULTS')
return false;
//on error call add marker function for same address
//and keep in Error ajax queue
addMarkers( address, 'Errror' );
if (index == -1) {
errorArray.push( address );
}
}
});
//mentain ajax queue set
queuCounter++;
if( queuCounter == setLimit ){
queuCounter = 0;
}
}
//function get url parameter from url string
getParameterByName = function ( name,href )
{
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( href );
if( results == null )
return "";
else
return decodeURIComponent(results[1].replace(/\+/g, " "));
}
//call add marker function for each address mention in inputfile.js
for (var x = 0; x < placesObject.length; x++) {
var address = placesObject[x]['City'] + ', ' + placesObject[x]['State'];
addMarkers(address);
}
});
Using "setInterval" & "clearInterval" fixes the problem:
function drawMarkers(map, markers) {
var _this = this,
geocoder = new google.maps.Geocoder(),
geocode_filetrs;
_this.key = 0;
_this.interval = setInterval(function() {
_this.markerData = markers[_this.key];
geocoder.geocode({ address: _this.markerData.address }, yourCallback(_this.markerData));
_this.key++;
if ( ! markers[_this.key]) {
clearInterval(_this.interval);
}
}, 300);
}
this post was made a while ago, but it provides an answer that did not solve the problem regarding reaching the limit of requests in an iteration for me, so I publish this, to help who else has not served.
My environment happened in Ionic 3.
Instead of making a "pause" in the iteration, I ocurred the idea of iterating with a timer, this timer has the particularity of executing the code that would go in the iteration, but will run every so often until it is reached the maximum count of the "Array" in which we want to iterate.
In other words, we will consult the Google API in a certain time so that it does not exceed the limit allowed in milliseconds.
// Code to start the timer
this.count= 0;
let loading = this.loadingCtrl.create({
content: 'Buscando los mejores servicios...'
});
loading.present();
this.interval = setInterval(() => this.getDistancias(loading), 40);
// Function that runs the timer, that is, query Google API
getDistancias(loading){
if(this.count>= this.datos.length){
clearInterval(this.interval);
} else {
var sucursal = this.datos[this.count];
this.calcularDistancia(this.posicion, new LatLng(parseFloat(sucursal.position.latitude),parseFloat(sucursal.position.longitude)),sucursal.codigo).then(distancia => {
}).catch(error => {
console.log('error');
console.log(error);
});
}
this.count += 1;
}
calcularDistancia(miPosicion, markerPosicion, codigo){
return new Promise(async (resolve,reject) => {
var service = new google.maps.DistanceMatrixService;
var distance;
var duration;
service.getDistanceMatrix({
origins: [miPosicion, 'salida'],
destinations: [markerPosicion, 'llegada'],
travelMode: 'DRIVING',
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, function(response, status){
if (status == 'OK') {
var originList = response.originAddresses;
var destinationList = response.destinationAddresses;
try{
if(response != null && response != undefined){
distance = response.rows[0].elements[0].distance.value;
duration = response.rows[0].elements[0].duration.text;
resolve(distance);
}
}catch(error){
console.log("ERROR GOOGLE");
console.log(status);
}
}
});
});
}
I hope this helps!
I'm sorry for my English, I hope it's not an inconvenience, I had to use the Google translator.
Regards, Leandro.
You are using setTimeout wrong way. The (one of) function signature is setTimeout(callback, delay). So you can easily specify what code should be run after what delay.
var codeAddress = (function() {
var index = 0;
var delay = 100;
function GeocodeCallback(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
new google.maps.Marker({ map: map, position: results[0].geometry.location, animation: google.maps.Animation.DROP });
console.log(results);
}
else alert("Geocode was not successful for the following reason: " + status);
};
return function(vPostCode) {
if (geocoder) setTimeout(geocoder.geocode.bind(geocoder, { 'address': "'" + vPostCode + "'"}, GeocodeCallback), index*delay);
index++;
};
})();
This way, every codeAddress() call will result in geocoder.geocode() being called 100ms later after previous call.
I also added animation to marker so you will have a nice animation effect with markers being added to map one after another. I'm not sure what is the current google limit, so you may need to increase the value of delay variable.
Also, if you are each time geocoding the same addresses, you should instead save the results of geocode to your db and next time just use those (so you will save some traffic and your application will be a little bit quicker)