Looping through Google Place API Place Details - javascript

I'm working through the Google Place API documentation and I'm trying to get a script that pulls PlaceIDs from a webpage, and replace them with output from the Google Place API.
I managed to successfully get an output from multiple Place IDs by duplicating the code and changing the variable and function names, but now I'm trying to create a loop function so that I'm not duplicating code. Below is what I have, but I'm getting an error. By looking at the console, it seems to work up till the Callback function where it beaks down.
"Uncaught TypeError: Cannot set property 'innerHTML' of null
at callback (places.html:29)"
I've tried a few things, but no luck so far. Any suggestions would be appreciated. Thanks,
<body>
<div id="MY0">ChIJaZ6Hg4iAhYARxTsHnDFJ9zE</div>
<div id="MY1">ChIJT9e323V644kRR6TiEnwcOlA</div>
<script>
var request = [];
var service = [];
var div = [];
for (i = 0; i < 2; i++) {
request[i] = {
placeId: document.getElementById("MY" + i).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
service[i] = new google.maps.places.PlacesService(document.createElement('div'));
service[i].getDetails(request[i], callback);
function callback(place, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
div[i] = document.getElementById("MY" + i);
div[i].innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
}
}
</script>
</body>

Move the callback outside of the for loop and forget about the array named div (unless you need this...if so I will rewrite). The for loop is executing before the getDetails() call returns any result, because this call is asynchronous - since you don't have much control over the Google Places callback, I would save the IDs in an array and then use them in callback, like this:
function gp_callback(place, status) {
var el = document.getElementById(window.id_set[0]); // first in first out - the for loop should populate the IDs in correct order
if (status == google.maps.places.PlacesServiceStatus.OK) {
el.innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
if (window.id_set.length > 1) {
window.id_set.splice(0, 1); // remove first element from array because has been used - now the next element is at index 0 for the next async callback
}
}
var request = [];
var service = [];
var id_set = [];
for (i = 0; i < 2; i++) {
request[i] = {
placeId: document.getElementById("MY" + i).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
id_set.push("MY" + i); // this ensures array is populated (in proper order, b/c it tracks the execution of the for loop) for use in callback before callback is called (since getDetails() is async)
service[i] = new google.maps.places.PlacesService(document.createElement('div'));
service[i].getDetails(request[i], function(place, status) {
gp_callback(place, status);
});
}
UPDATE: More scalable and elegant answer after I had a little more time to think about it.
<div id="MY0" class="gp_container">ChIJaZ6Hg4iAhYARxTsHnDFJ9zE</div>
<div id="MY1" class="gp_container">ChIJT9e323V644kRR6TiEnwcOlA</div>
.
.
.
<div id="MYN" class="gp_container">fvbfsvkjfbvkfvb</div> // the nth div
<script>
function populate_container(place, status, container_id) {
var el = document.getElementById(container_id);
if (status == google.maps.places.PlacesServiceStatus.OK) {
el.innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
}
function call_service(id_request_map) {
var i, container_id, request,
service_call = function(container_id, request) {
var service = new google.maps.places.PlacesService(document.createElement('div'));
service.getDetails(request, function(place, status) {
populate_container(place, status, container_id);
});
};
for(i in id_request_map) {
service_call(i, id_request_map[i]);
}
}
$(document).ready(function() {
var request, container_id,
id_request_map = {},
container_length = document.getElementsByClassName("gp_container").length,
i = 0;
for (; i < container_length; i++) {
container_id = "MY" + i;
request = {
placeId: document.getElementById(container_id).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
id_request_map[container_id] = request; // build the association map
}
call_service(id_request_map);
});
</script>

Related

Rest Here Api callback funtion in python insted of JS

Is there a way to use callback functions in Python with requests? This is from a here API example im trying to copy using python. This is the request and the function used at the end in the parameters is defined jsoncallback=parseResponse.
If i can replace the functionality of the callback in a other way would be fine too, is it possible to just take the response and do everything thats done in parseRespone?
"https://route.api.here.com/routing/7.2/calculateroute.json?jsonAttributes=1&waypoint0=50.7799,6.08425&waypoint1=50.77988,6.08288&waypoint2=50.78144,6.07794&representation=overview&routeattributes=sc,sm,sh,bb,lg,no,shape&legattributes=li&linkattributes=sh,nl,fc&mode=fastest;car;traffic:enabled&app_id=inCUge3uprAQEtRaruyaZ8&app_code=9Vyk_MElhgPCytA7z3iuPA&jsoncallback=parseResponse"
var parseResponse = function (resp)
{
if (resp.error != undefined)
{
alert (resp.error);
feedbackTxt.innerHTML = resp.error;
return;
}
if (resp.response == undefined)
{
alert (resp.subtype + " " + resp.details);
feedbackTxt.innerHTML = resp.error;
return;
}
//add Routing Release number if not already done
if (releaseRoutingShown == false){
releaseInfoTxt.innerHTML+="<br />HLP Routing: "+resp.response.metaInfo.moduleVersion;
routerMapRelease = resp.response.metaInfo.mapVersion;
mapReleaseTxt.innerHTML = "HLP Routing Service based on "+routerMapRelease+ " map release";
releaseRoutingShown = true;
}
var strip = new H.geo.Strip(),
shape = resp.response.route[0].shape,
i,
l = shape.length;
for(i = 0; i < l; i++)
{
strip.pushLatLngAlt.apply(strip, shape[i].split(',').map(function(item) { return parseFloat(item); }));
}
polyline = new H.map.Polyline(strip,
{
style:
{
lineWidth: 5,
strokeColor: "rgba(18, 65, 145, 0.7)",
lineJoin: "round"
}
});
group.addObject(polyline);
var links = [];
for(var i = 0; i < resp.response.route[0].leg.length; i++)
links = links.concat(resp.response.route[0].leg[i].link);
pdeManager.setLinks(links);
pdeManager.setBoundingBoxContainer(group);
pdeManager.setOnTileLoadingFinished(pdeManagerFinished);
pdeManager.start();
}
function pdeManagerFinished(finishedRequests)
{
feedbackTxt.innerHTML = "Done. Requested " + finishedRequests + " PDE tiles for " + numLinksMatched + " route links. ";
var resultHTML = '<table class="pde_table" cellpadding="2" cellspacing="0" border="1" width="90%">' +
'<thead>' +
'<tr>' +
'<th width="80%">Sign</th>' +
'<th width="20%">#</th>' +
'</tr>' +
'</thead>' +
'<tbody id="maps_table_body">';
for(var sign in signs)
{
resultHTML += "<tr>" + "<td>" + sign + "</td>" + "<td>" + signs[sign] + "</td>" + "</tr>";
}
resultHTML += "</tbody>" + "</table>";
document.getElementById("resultArea").innerHTML = resultHTML;
document.getElementById("resultArea").style.display = "block";
map.addObject(group);
map.setViewBounds(group.getBounds());
}
You don't need to use any callback (since there's nothing that would execute it anyway); elide the parameter and call response.json().
The below code does raise
Unauthorized. The request is not from an authorized source.
so you may need some additional headers or such (possibly an Origin header if the credentials are matched to a site address).
import requests
resp = requests.get(
url="https://route.api.here.com/routing/7.2/calculateroute.json",
params={
"jsonAttributes": "1",
"waypoint0": "50.7799,6.08425",
"waypoint1": "50.77988,6.08288",
"waypoint2": "50.78144,6.07794",
"representation": "overview",
"routeattributes": "sc,sm,sh,bb,lg,no,shape",
"legattributes": "li",
"linkattributes": "sh,nl,fc",
"mode": "fastest;car;traffic:enabled",
"app_id": "inCUge3uprAQEtRaruyaZ8",
"app_code": "9Vyk_MElhgPCytA7z3iuPA",
},
)
# Uncomment this to see the actual error.
# print(resp.content)
resp.raise_for_status()
print(resp.json())

OpenLayers 4 - LayerFilter on forEachFeatureAtPixel

I have 2 vector layers of which I want only 1 to be selectable for a WFS get feature info layer. OL4 docs tell me there is an opt_layerfilter for the forEachFeatuerAtPixel function.
I’m in a similar situation like this: OpenLayers 3 hasFeatureAtPixel filter for layer.
Due to my lack of JavaScript knowledge I can’t seem to make it work with the following code in OpenLayers 4:
var displayFeatureInfo = function (pixel) {
var features = [];
map.forEachFeatureAtPixel(pixel, {
layerFilter: function (layer) {
return layer.get('name') === 'isochrones';
}
}, function (feature) {
features.push(feature);
});
if (features.length > 0) {
var info = [];
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
info.push('<div id="infobox">' + '<p2>' + 'Isochroon ' + features[i].get('name') + ', locatie ' + features[i].get('facilityid') + '</p2>' + '<p>' + 'aantal lopend: ' + features[i].get('n_pedestrians') + ', fiets: ' + features[i].get('n_bike') + ', ebike: ' + features[i].get('n_ebike') + '<br>' + 'speedpedelec: ' + features[i].get('n_speedpedelec') + ', auto: ' + features[i].get('n_car') + '</p>' + '</div>');
}
document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
} else {
document.getElementById('info').innerHTML = ' ';
}
};
map.on('click', function (evt) {
displayFeatureInfo(evt.pixel);
});
The layer I want to be selectable is named ‘isochrones’.
It throws me an error “d.call is not a function” when I try to click any vector layer in the map.
Could anyone point me in the right direction?
Looks like you have your args swapped.
The params for forEachFeatureAtPixel are (pixel, callback, options)
You have (pixel, options, callback)

Sending multiple HTTP GET requests to api with a loop

I'm looking for a way to send many requests to an api using a different api url each time.
An example url for my project is:
http://api.bandsintown.com/artists/Hippo%20Campus/events.json?lapi_version=2.0&app_id=music_matcher
I'm using an HTTP request to pull the JSON info into my script and works perfectly...the first time. However, I want to be able to call it 50-100 ish times (max) in a loop with different artist names in the url (I'm using the BandsInTown API). For some reason, when I try to use a loop to call the http request multiple times, only one output appears and it is unpredictable which element in the order it will be (it's usually the output associated with the first or second element in the array). This is what my code looks like:
// HTTP GET call to BandsInTown API
function httpGetAsync(theUrl, callback) { //theURL or a path to file
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
var data = JSON.parse(httpRequest.responseText);
if (callback) {
callback(data);
}
}
else {
alert("error loading JSON doc");
}
};
httpRequest.open('GET', theUrl, true);
httpRequest.send(null);
}
//extracts data from api for each artist
function parseEvent(artist) {
var url = "http://api.bandsintown.com/artists/" + artist + "/events.json?lapi_version=2.0&app_id=music_matcher";
httpGetAsync(url, function(data) {
var numEvents = Object.keys(data).length;
//var events = [];
for (var j = 0; j < numEvents; j++) {
document.write(data[j].venue.name + "-> ");
document.write("LAT:" + data[j].venue.latitude + " " + "LNG:" + data[j].venue.longitude);
document.write("ARTIST: " + data[j].artists[0].name);
document.write("DATE: " + data[j].datetime);
document.write(" " + j + " ");
}
});
}
var artists = ["Drake", "Mac Demarco", "Hippo Campus", "STRFKR"];
for (var i = 0; i < artists.length; i++) {
parseEvent(artists[i]);
document.write(" ---NEXT ARTIST--- ");
}
So I can't tell exactly what's going on but things are acting weird with my current code. I don't have a whole lot of javascript and web development experience yet so any help is appreciated! I was preferably looking for a way to implement this with pure javascript. I have had trouble figureing out how to handle Node.js and/or JQuery in Eclipse Neon (the IDE I am using)
You have implemented closure pretty well so clearly this isn't a problem of success callback of one function overwriting response of all others.But now when you look at document.write() it all gets clear, this function first wipes your whole content clean then it writes whatever you told it to .That's why you hardly see anyone use it
`document.write('a');`
`document.write('b');`
`document.write('c');` // a and b are gone you would see only 'c'
So after loop gets over you would only see the output of the last call.Though it's mostly random as to which call would finish last it mostly biased towards some particular value due to the the way servers are tuned.
So better approach is to use some <div> or something and pour your results into it like this one
<div id="op"></div>
and
function parseEvent(artist) {
var url = "http://api.bandsintown.com/artists/" + artist + "/events.json?lapi_version=2.0&app_id=music_matcher";
httpGetAsync(url, function(data) {
var numEvents = Object.keys(data).length;
var op = document.getElementById('op');
op.innerHTML = op.innerHTML + " <br><br> <h2>---NEXT ARTIST---<h2> <br>";
//var events = [];
for (var j = 0; j < numEvents; j++) {
op.innerHTML = op.innerHTML + "<br>" + data[j].venue.name + "-> ";
op.innerHTML = op.innerHTML + "<br>" + "LAT:" + data[j].venue.latitude + " " + "LNG:" + data[j].venue.longitude ;
op.innerHTML = op.innerHTML + "<br>" +"ARTIST: " + data[j].artists[0].name;
op.innerHTML = op.innerHTML + "<br>" +"DATE: " + data[j].datetime;
op.innerHTML = op.innerHTML + "<br>" + " " + j + " <br>";
}
});
}
var artists = ["Drake", "Hippo Campus", "STRFKR","Mac Demarco"];
for (var i = 0; i < artists.length; i++) {
parseEvent(artists[i]);
}

Calling geonames to find nearby streets

I'm trying to get a list of nearby streets given a LatLng value using the geonames web service. I can get nearby wikopedia articles but am unable to get a list of street names using their findNearbyStreetsOSM method. This is what i have:
I'm blind and using a screenreader. Hopefully the code is indented correctly.
//geonames API function
//feed in the geonames method i.e. findNearbyWikipedia, findNearbyStreetsOSM + the long and lat values + the name of the div you want the results to be displayed in
function FetchDataFromGeoNames(geonamesMethod, latitude, longitude, divToOutputResultsTo) {
var geonamesAPIKey = "my_API_key);
var apiUrl = "http://api.geonames.org/";
var radius = 1;
var request = apiUrl + geonamesMethod + "JSON?lat=" + latitude + "&lng=" + longitude + "&username=" + geonamesAPIKey;
if (geonamesMethod == 'findNearbyWikipedia') {
request += "&radius=" + radius + "&maxRows=5&country=UK";
}
request += '&callback=?';
//alert(request);
//pass the request onto geonames API
$.getJSON(request, {}, function(res) {
if (res.hasOwnProperty("status")) {
$("#divToOutputResultsTo").html("Sorry, I failed to work because: " + res.status.message);
return;
}
var s = "";
//loop through the results
for (var i = 0; i < res.geonames.length; i++) {
//alert(JSON.stringify(res));
//if find wikopedia request then
if (geonamesMethod == 'findNearbyWikipedia') {
s += "<p><h2>" + res.geonames[i].title;
} else if (geonamesMethod == 'findNearbyStreetsOSM') {
s += "<p><h2>" + res.geonames[i].name;
}
if (geonamesMethod == 'findNearbyWikipedia') {
if(res.geonames[i].hasOwnProperty("thumbnailImg")) s += "<img src='"+res.geonames[i].thumbnailImg+"' align='left'>";
if (!!res.geonames[i].feature && res.geonames[i].feature != "undefined") s += '<br />Feature: ' + res.geonames[i].feature;
s += '<br />' + res.geonames[i].summary;
s += "<br clear='left'><a href='http://"+res.geonames[i].wikipediaUrl+"'>[Read More]</a></p>";
} else if (geonamesMethod == 'findNearbyStreetsOSM') {
s += '<br />Highway: ' + res.geonames[i].highway;
}
}
//concatonate the results
if (s != "") {
if (geonamesMethod == 'findNearbyWikipedia') {
s = "<h2>Nearby Wikopedia</h2>" + s;
} else if (geonamesMethod == 'findNearbyStreetsOSM') {
s = "<h2>Nearby streets (OSM)</h2>" + s;
}
//display the results on screen
$(divToOutputResultsTo).html(s);
}
});
}
Thanks,

Loading Json file into bing map API with js

I'm working on a project where I have a json file that contains a list of companies, their phone numbers, addresses, etc. Currently I have it loading a list into a table with no real order. It works to list the companies, and displays their location on a map with a pin. I have it limited to display on 10 as its almost 5000 entries - at least until I get a sort/search function working. The issue I'm having is that the only way I can get the map to load and the pins to work is if I include them as part of the $.getJSON. If I create functions to load outside of this, I'm unable to call the information out of the JSON any longer, but likewise am not able to include the functions inside as they don't work.
Here is the code I'm working with:
$.getJSON('MapDatabroke.json', function (data) {
var output = "<table class = sample>";
tableHeadings = "<thead>" +
"<tr>" +
"<th></th>" +
"<th><u>Name:</u></th>" +
"<th><u>Address:</u></th>" +
"<th><u>City:</u></th>" +
"<th><u>State:</u></th>" +
"<th><u>Phone Number:</u></th>" +
"<th><u>PO:</u></th>" +
"<th><u>Towing:</u></th>" +
"<th><u>Tires:</u></th>" +
"<th><u>MC:</u></th>" +
"<th><u>RoadSvc:</u></th>" +
"<th><u>Notes:</u></th>" +
"</tr>" +
"</thead>";
output += tableHeadings;
for (var i = 0; i < length; i++) {
var lat = data[i]["Lat"];
var lon = data[i]["Lon"];
var dist = 41.5;
if (lat < dist) {
output += "<tr>";
if (data[i]["Website"] == '---') {
output += "<td>" + ' ' + "</td>";
} else {
output += "<td>W</td>";
}
output += "<td>" + i + data[i]["Business Name"] + "</td>" + "<td>" + data[i]["Address"] + "</td>" + "<td>" + data[i]["City"] + "</td>" + "<td>" + data[i]["StateListing"] + "</td>" + "<td>" + data[i]["Phone"] + "</td>";
if (data[i]["PO"] == 'FALSE') {
output += "<td>" + data[i]["Notes"] + "</td>";
output += "</tr>";
}
}
output += "</table>";
document.getElementById("placeholder").innerHTML = output;
});
function GetMap() {
var map = null;
var pinInfobox = null;
// Initialize the map
var map = new Microsoft.Maps.Map(document.getElementById("mapDiv"), {
credentials: "AqtpRoPv2sfmrgf9VhyvDV8hCOVGPJi0-9heYhxmB-WU24OzpTIOIR0-C4fD0jc-",
center: new Microsoft.Maps.Location(45.5, -122.5),
zoom: 7
});
// Creates a collection to store multiple pins
var pins = new Microsoft.Maps.EntityCollection();
// Create Pins
for (var i = 0; i < length; i++) {
//pushpin location
var position = new Microsoft.Maps.Location(data[i]["Lat"], data[i]["Lon"]);
//Create the pin
var pin = new Microsoft.Maps.Pushpin(position);
//add pin to collection
pins.push(pin);
}
//add pins
map.entities.push(pins);
//create the info box
pinInfobox = new Microsoft.Maps.Infobox(pin.getLocation(), {
title: 'My Pushpin',
description: 'It works!',
visible: false,
offset: new Microsoft.Maps.Point(0, 15)
});
//add click event
Microsoft.Maps.Events.addHandler(pin, 'click', displayInfobox);
//hide box on map move
Microsoft.Maps.Events.addHandler(map, 'viewchange', hideInfobox);
}
function displayInfobox(e) {
pinInfobox.setOptions({
visible: true
});
}
function hideInfobox(e) {
pinInfobox.setOptions({
visible: false
});
}
The way it shows above, doesn't work. It will load the map, but will not load the information as well as give me a error of data not defined w/in the create pin. Any ideas how to assign the information so that way I can use it in functions later on?
Try asigning a variable to the $.getJSON (var JSONData = $.getJSON...). In that way you can have the data outside the query.
On the other hand, you could also use $.ajax to do the query. It's just another way of doing things, a little more "tweakable". This example have some error handlers that will help you identify the problem.
Something like this:
var JSONdata = $.ajax({
type : "GET",
url : "MapDatabroke.json",
cache : false,
dataType: "json", // Use when retrieving
success: function(data) {
// do something here after retrieving the "data".
},
error: function (xhr, ajaxOptions, thrownError) {
//Add these parameters to display the required response
console.log( 'xhr.status: ' + xhr.status );
console.log( 'thrownError: ' + thrownError );
}
});
Also, check your json data (http://jsonlint.com/). Sometimes there is a comma where should not, or a missing bracket.

Categories

Resources