Place API - getting place photo as marker icon - google-maps

I follow this guide
https://developers.google.com/maps/documentation/javascript/places#places_photos
to create place photo as marker icon. This is my map initialization code:
var map;
function initMap() {
// Create a map centered in Pyrmont, Sydney (Australia).
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -6.920812, lng: 107.604116},
zoom: 13
});
var request = {
location: map.getCenter(),
radius: '5000',
type: ['shopping_mall']
};
var service = new google.maps.places.PlacesService(map);
service.textSearch(request, callback);
}
// Checks that the PlacesServiceStatus is OK, and adds a marker
// using the place ID and location from the PlacesService.
function callback(results, status) {
console.log(results);
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
var place = results[i];
createPhotoMarker(place);
}
}
}
google.maps.event.addDomListener(window, 'load', initMap);
this is the createPhotoMarker function
function createPhotoMarker(place) {
var photos = place.photos;
if (!photos) {
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location,
title: place.name
});
return;
}
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location,
title: place.name,
icon: photos[0].getUrl({'maxWidth': 35, 'maxHeight': 35})
});
}
the function will create regular marker if place photo is not available. But for the place with photo available, I get this error :
Failed to load resource: the server responded with a status of 404 () lh3.googleusercontent.com/w35-h35-p/AF1QipOIL6GVVmtqp_cw_hBEQxdILZSa8poMO0HAqFHd=k
And the map only shows regular marker.
What did I do wrong?
This is the fiddle http://jsfiddle.net/v90fmrhp/
==========Update 2017-07-07============
Thanks for the answers and fixes
It seems the issue solved. My fiddle is working now
https://issuetracker.google.com/issues/63298126
Marked as Fixed
Good news! We have fixed this issue. Thanks for your patience.
Happy Mapping!

Looks like the error you are seeing is caused by some issue on Google's side. It's affecting quite a few other users as well, have a look at their public issue tracker:
Thanks for reporting this issue. We verified it and we'll keep tracking it.
https://issuetracker.google.com/issues/63298126
UPDATE (2017-07-06):
A fix for this is going into our release process now and it should be out soon - probably Monday at the latest.
https://issuetracker.google.com/issues/63298126#comment13

Had the same issue and Sulyman suggested a workaround that is working but I don't know for how long when google fixes this.
Google Places Photos .GetUrl is adding width and height to url
Here is what we did.
if(place.photos != null){
for(var i = 0; i < place.photos.length; i++){
//Do a string replace to get the w-h-p out of there.
var str = place.photos[i].getUrl({"maxWidth": 100, "maxHeight": 100});
var res = str.replace("w100-h100-p", "p");
self.pacPhotos.push({
id : res
});
}
}else {
console.log("no photo");
}
}

I also ran into this with google places api. Everything was working fine then randomly it stopped. It seems likely that it is due to google making changes as they get ready for releasing a better maps api to support vector
#DKinnison saved me with his solution so I just wanted to post my ES6 solution for parsing a received place. I commented out the other properties I am personally not using in case you need to.
const PHOTO_WIDTH = 600;
const PHOTO_HEIGHT = 600;
export function parseGooglePlace(place) {
if (!place) {
return;
}
const {
// address_components,
formatted_address,
geometry,
// icon,
// id,
// international_phone_number,
name,
// rating,
// reviews,
// opening_hours,
photos: _photos = [],
place_id,
// reference,
types = [],
// url: mapsURL,
// utc_offset,
// vicinity,
website,
} = place;
const photos = _photos.map(p =>
p
.getUrl({ maxWidth: PHOTO_WIDTH, maxHeight: PHOTO_HEIGHT })
.replace(`w${PHOTO_WIDTH}-h${PHOTO_HEIGHT}-p`, 'p'),
);
return {
website,
name,
photos,
address: formatted_address,
placeId: place_id,
geometry,
types,
};
}

Related

Faster way to add multiple markers on Google Maps v3 Javascript

I'm trying to add a lot of markers into Google Map. I'm already passing the data to the server to make clusters on "busy" areas to help keep the number of markers down.
Here is my code:
markers.forEach(function(item, i){
if (item.count) {
// this is a cluster one, so add the respective icon along with the number as a label
// check we have a value for this. If we don't it means that someone has 2 markers with the exact same lat/lng, so lets ignore it!
if (item.coordinate) {
var location = new google.maps.LatLng(item.coordinate[0], item.coordinate[1]); // lat,lng of cluster
var cluster_icon;
if (item.count < 10) {
cluster_icon = icon_markers_1;
} else if (item.count < 30) {
cluster_icon = icon_markers_2; //
} else if (item.count < 50) {
cluster_icon = icon_markers_3; //
} else if (item.count < 100) {
cluster_icon = icon_markers_4; //
} else {
cluster_icon = icon_markers_5; //
}
window.VARS.markers[i] = new google.maps.Marker({
position: location,
//label: String(item.count),
title: "lat:"+item.coordinate[0]+ ",lng: " + item.coordinate[1],
label: {
text: String(item.count),
color: "#fff",
fontSize: "16px",
fontWeight: "bold"
},
map: window.VARS.Google_Map_Modal,
icon: cluster_icon
});
window.VARS.markers[i].addListener('click', function() {
//console.dir(window.VARS.markers[i].getPosition().lat());
var zoom = window.VARS.Google_Map_Modal.getZoom();
zoom++;
if (zoom <= 20) {
window.VARS.Google_Map_Modal.setZoom(zoom++)
}
window.VARS.Google_Map_Modal.setCenter(this.getPosition());
});
}
} else {
var link = window.VARS.links_stored[item.link_id];
// this is an actual marker (not cluster), so lets add it to the map
var location = new google.maps.LatLng(link.latitude, link.longitude); // lat,lng of cluster
var dataPhoto = link.image_small;
var marker = new google.maps.Marker({
position: location,
title: link.title,
the_row: i,
linkid: link.linkid,
map: window.VARS.Google_Map_Modal
});
window.VARS.markers.push(marker);
window.VARS.marker_vals.push(item);
//bounds.extend(latLng);
marker.addListener('click', function() {
// do some stuff
});
}
});
Is there a better way to do this, rather than one by one? I read that you could "batch add" to the map - but I can't seem to find any documentation to support this.
Instead of re-inventing the wheel by implementing a custom clustering logic, you can use the one provided by Google Maps.
https://developers.google.com/maps/documentation/javascript/marker-clustering
Adding markers one by one makes the map incredibly slow. MarkerClusterer avoids this issue by creating an array of markers but not adding them to the map.
The markers are added together at the end when you initialize the MarkerClusterer by passing the marker array.
var markerCluster = new MarkerClusterer(map, markers,
{imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});
This is extremely quick and efficient, allowing addition of thousands of markers without too much of a performance hit.

How to disable the know your location popup

Here I'm using this Places API in my project to find the locations of user but when I click on the field popup is opening as know your location by giving two options allow and block but client don't want that popup to be open it always allow the user to know the location. is there any option in the api to do this functionality.
Here is the documentation for your answer.
Just ask for permission as a clear response to a user gesture.
Create a button for user to allow device location, then;
var usermarkers = [];
var userMarker;
function geolocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
userLat = position.coords.latitude;
userLng = position.coords.longitude;
currentloc = { lat: userLat, lng: userLng };
var userSerial = "987654321";
for (var i = 0; i < usermarkers.length; i++) {
if (usermarkers[i].serialNumber === userSerial) {
usermarkers[i].setMap(null);
usermarkers = [];
}
}
userMarker = new google.maps.Marker({
position: currentloc,
map: map,
serialNumber: userSerial
});
userMarker.setIcon('http://maps.google.com/mapfiles/ms/icons/green-dot.png')
usermarkers.push(userMarker);
calculateDistance(ppath);
});
} else {
alert("Error: Your browser doesn\'t support geolocation.");
}
}
I also added marker remove for next calls. Hope this helps.

nearbySearch with name specified like wildcard

I am after a way to do a Google Maps API V3 "nearbySearch" by specifying both an exact location but not an exact store name (like a wildcard search).
e.g., If the request was for "Starbucks" in "San Francisco" it would return all Places called "Starbucks", whether the Place had more words in it's title or not e.g., results would contain "Starbucks Coffee", "Starbucks Smith St" or simply "Starbucks". Currently it looks like it only finds identical matches, "Starbucks".
Code (more info here):
var map;
var service;
var infowindow;
function initialize() {
// Specify lat/lng of search location
var SanFran = new google.maps.LatLng(37.777119,-122.41964);
// Create new map object
map = new google.maps.Map(document.getElementById('map'), {
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: SanFran,
zoom: 15
});
// Create request parametres
var request = {
location: SanFran
, radius: '500',
, name: "Starbucks"
};
// Call the nearby search using the parametres
service = new google.maps.places.PlacesService(map);
service.nearbySearch(request, callback);
}
// Asynchronous marker creation at returned locations
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
var place = results[i];
createMarker(results[i]);
}
}
}
I've done some reading and whilst it looks like API3 doesn't offer Wildcard search, I'm hoping there are work arounds.
Thanks in advance!
Sorry all, the answer was really quite simple. I should have been using the textSearch rather than nearbySearch:
...
// Create request parametres
var request = {
location: SanFran
, radius: '500',
, query: "Starbucks"
};
// Call the nearby search using the parametres
service = new google.maps.places.PlacesService(map);
service.textSearch(request, callback);
...
More info here https://developers.google.com/maps/documentation/javascript/places#TextSearchRequests

googlemaps info window popup doesn't loading the dynamic content

please help me with this issue. I have the following code with working address variable. I tryed to add titles to the Infowindow so when a user clicks on a marker on the map to see some content in popup. Unfortunately, for all popups I can see the same title. I tested, it is a correct js array, but shows only the first titles that comes from the array.. Please help to solve this issue.. Thank you in advance guys !
<script type="text/javascript" charset="utf-8">
var map;
var address = < ? php echo json_encode($adr); ? > ;
var titles = < ? php echo json_encode($ttl); ? > ;
var x = 0;
var nbAddresses = address.length;
var geocoder;
var mark;
var contentString = titles[x];
function init() {
var moptions = {
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map"), moptions);
geocoder = new google.maps.Geocoder();
for (var i = 0; i < nbAddresses; i++) {
geocoder.geocode({
'address': address[i]
}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
mark = new google.maps.Marker({
position: results[0].geometry.location,
map: map,
title: titles[x]
});
map.setCenter(results[0].geometry.location);
x++;
setInfoWindow();
}
});
}
function setInfoWindow() {
google.maps.event.addListener(mark, 'click', function(event) {
var iwindow = new google.maps.InfoWindow();
iwindow.setContent(contentString);
iwindow.open(map, this);
});
}
}
window.onload = init;
</script>
changing
x++;
setInfoWindow(); to
setInfoWindow();
x++;
sets the problem !
At the start of your program you set the contentString to the first element in the title array
var contentString = titles[x];
Then you use this variable inside the setInfoWindow function unchanged.
What if you changed the setInfoWindow to accept a contentString parameter like so
function setInfoWindow(contentString) {...
When you call setInfoWindow, pass in the title.
setInfoWindow(title[x]);
Make sure you increment x after you call this function, so move x++ below setInfoWindow call.
EDIT You will also notice a strange problem from time to time where some titles may appear on the wrong markers. This is because you are doing multiple geocodes at once, and you may get a response out of order.
The following modification would fix that.
for (var i = 0; i < nbAddresses; i++) {
geocoder.geocode({
'address': address[i]
}, return function(x) { // we define a function and call it straight away. The function returns another function which is our callback.
function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
mark = new google.maps.Marker({
position: results[0].geometry.location,
map: map,
title: titles[x]
});
map.setCenter(results[0].geometry.location);
setInfoWindow();
}
}
}(i)); // (i) after the function declaration calls the function and passes in the current value of i
}
So basically what we did here was define a function and run it straight away with the current value of 'i' passed in. The function would then return another function. This function will be run once the goecoding has finished. But now we have a reference to 'i' as it was when the function was defined. So you no longer need to keep a record of x outside this function as x will be equal to i which corresponds with the address passed into geocoder.
This is called a closure.
How do JavaScript closures work?

Geocoding using Google Maps API v3 - Linking the original request to the response

I have a list of schools that I want to plot on a Google Map. I'm using Google's Geocoding Service to lookup the lng/lat for a given postcode, upon successfully retrieving this information I want to drop a marker, together with adding the appropriate event listener that opens an infobox when a given marker is clicked.
When I make a request to the geocoder it's in the context of a school, when I receive a callback I lose this context. You'll see from code below that I've come up with a clunky solution to this, although it fails occasionally when the geocoder results truncate the postcode.
Should I be using something like jQuery's Deferred Object to solve this issue?
var geocoder;
var map;
var infowindow
var iterator = 0;
geosearch = new Array();
function drop() {
for (var i = 0; i < schools.length; i++) {
setTimeout(function() { // delay added to prevent being throttled
addMarker();
iterator++;
}, i * 1000);
}
}
function addMarker() {
address = schools[iterator].addresses[0].address.zip;
geosearch[address] = schools[iterator]; // this is how I'm keeping track of initial request
geocoder.geocode( { 'address': address }, function(results, status) {
var school = geosearch[results[0].address_components[0].short_name]; // loading the school associated with the initial request, which only works if the postcode completely matches up - clunky!
if (status == google.maps.GeocoderStatus.OK) {
// each school has tags, I want to set a marker if certain tags exist
if ($.inArray('D', school.tags) > 0) {
var image = 'map_markers/brown_MarkerD.png';
} else if ($.inArray('C', school.tags) > 0) {
var image = 'map_markers/red_MarkerC.png';
} else if ($.inArray('B', school.tags) > 0) {
var image = 'map_markers/yellow_MarkerB.png';
} else if ($.inArray('A', school.tags) > 0) {
var image = 'map_markers/green_MarkerA.png';
} else {
var image = 'map_markers/blue_MarkerZ.png';
}
// add the marker to the map, using result
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location,
draggable: false,
icon: image,
shadow: 'http://www.google.com/mapfiles/arrowshadow.png',
animation: google.maps.Animation.DROP
});
// adds listening on marker so that popup box appears when clicked
google.maps.event.addListener(marker, 'click', (function(marker, school) {
return function() {
infowindow.setContent(
''+school.name+''
+'<address>'
+school.addresses[0].address.street+'<br />'
+school.addresses[0].address.city+'<br />'
+school.addresses[0].address.state+'<br />'
+school.addresses[0].address.zip+'<br />'
+school.addresses[0].address.country+'<br />'
+'</address>');
infowindow.open(map, marker);
}
})(marker, school));
} else {
console.log("* NOT found: " + status);
}
});
}
function initialise() {
geocoder = new google.maps.Geocoder();
infowindow = new google.maps.InfoWindow();
var latlng = new google.maps.LatLng(54.82659788452641,-3.417279296874991);
var mapOptions = {
zoom: 6,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
drop(); // loops through schools to add marker
}
I would suggest geocoding the addresses offline and storing the coordinates in your database (or wherever you are storing the addresses). Then use the coordinates to display the markers.
I would also suggest reviewing this article on geocoding strategies from the documentation
To answer your question, I would suggest using javascript function closures to associate the address with the callback function.
The problem I was experiencing here was just a questions of scope, and in particular the way that I was referencing the school within the addMarker() function. Rather than referencing the school within the schools array using the global iterator variable, I instead pass in this school, this way the correct school is always referenced on the callback that is created within this scope.
var geocoder;
var map;
var infowindow
var iterator = 0;
function drop() {
for (var i = 0; i < schools.length; i++) {
setTimeout(function() {
addMarker(schools[iterator]); // pass in the school as an argument
iterator++;
$('#current_school').text(iterator); // taken this out of addMarker()
}, i * 1000);
}
}
function addMarker(school) {
geocoder.geocode( { 'address': school.addresses[0].address.zip }, function(results, status) {
... // the inners from here remain the same
});
}