I have tryied to used Google Maps strictbounds parameter to restrict autocomplete results to an specific area. My current code is:
var autocomplete = new google.maps.places.Autocomplete(spaceAddress, { types: [ 'geocode' ], strictbounds: {location: "-23.544085,-46.7125434", radius: "12000"}, language: "pt-BR" });
Althoug it doesn't break my application, it also doesn't restrict the results to the selected area. Alternatively, I used the country restriction below, but the customer doesn't like it!
Here's the complete code in use by now:
document.addEventListener("DOMContentLoaded", function() {
var spaceAddress = document.getElementById('space_address');
if (spaceAddress) {
var autocomplete = new google.maps.places.Autocomplete(spaceAddress, { types: [ 'geocode' ], strictbounds: {location: "-23.544085,-46.7125434", radius: "12000"}, language: "pt-BR" });
google.maps.event.addDomListener(spaceAddress, 'keydown', function(e) {
if (e.key === "Enter") {
e.preventDefault();
const spaceAddress = document.getElementById('space_address').value;
if (spaceAddress != '' && spaceAddress != null) {
codeAddress(spaceAddress);
}
}
});
}
Could someone please enlighten me?!
You need to create a google.maps.Circle class first and then use that object to get the bounds via getBounds(). Also as mentioned already strictbounds is a boolean
https://developers.google.com/maps/documentation/javascript/reference/places-widget#Autocomplete
var circle = new google.maps.Circle({ center: new google.maps.LatLng(-23.544085, -46.7125434), radius: 12000 })
var autocomplete = new google.maps.places.Autocomplete(spaceAddress, { types: [ 'geocode' ], bounds: circle.getBounds(), strictbounds: true });
Unfortunately I can't comment yet, so a new answer. The answer from danbeggan works, but there is a tiny error in his code. The attribute is called strictBounds, here is the correct code:
var circle = new google.maps.Circle({ center: new google.maps.LatLng(-23.544085, -46.7125434), radius: 12000 })
var autocomplete = new google.maps.places.Autocomplete(spaceAddress, { types: [ 'geocode' ], bounds: circle.getBounds(), strictBounds: true });
Note: radius is in meter
Related
I am trying to build a live tracking with the route using Google Map JS version in ionic 4.
What I am trying to achieve is to give the route to the user from source to destination and update the route if the user chooses a new path other than the google provided one. The source is the user and destination is some point in the map.
I am able to draw route and update it if the user change the provided path using
startNavigation(){
this.geolocation.getCurrentPosition({ enableHighAccuracy: true })
.then((position) => {
this.userPosition = position;
this.userVehicleMarker = new google.maps.Marker({
map: this.map,
position: { lat: position.coords.latitude, lng: position.coords.longitude },
icon: this.vehicleIcon
});
this.addInfoWindow(this.userVehicleMarker, 'me')
this.watchVehicle = this.geolocation.watchPosition({ enableHighAccuracy: true })
.subscribe(async (pos) =>
{
// Calling the redraw function on every 25 meters distance travelled
this.drawRouteFromVehicleToDestination(pos.coords.latitude, pos.coords.longitude)
}
}, (err: PositionError) => {
// alert(err.message)
console.log("error : " + err.message);
});
})
)
drawRouteFromVehicleToDestination(lat, lng) {
let _self = this;
let directionsService = new google.maps.DirectionsService;
let directionsRenderer = new google.maps.DirectionsRenderer({
polylineOptions: {
strokeColor: "#428BE8",
strokeWeight: 2
},
suppressMarkers: true,
preserveViewport: true
});
directionsRenderer.addListener('directions_changed', function () {
let _data = directionsRenderer.getDirections();
let _newData = _data['routes'][0]['legs'][0]
console.log(_newData)
});
directionsService.route({
origin: { lat: lat, lng: lng},
destination: { lat: 27.673586, lng: 85.435131},
travelMode: 'DRIVING',
optimizeWaypoints: true,
provideRouteAlternatives: false,
avoidTolls: true,
}, (res, status) => {
if (status == 'OK') {
directionsRenderer.setDirections(res);
directionsRenderer.setMap(this.map);
} else {
console.warn(status);
}
});
}
But the issue is, it is sending a lot of requests to google API and which does not look like a practical approach to follow.
Is there any other approach I should follow using which I could track the route and update it as per user location change and also minimize the google ping?
Thank you in advance for your help.
I think the issue is that you call drawRouteFromVehicleToDestination() very frequently (every time the vehicle position changes, from this.geolocation.watchPosition). One way to reduce the number of calls would be to "debounce" this calls, limiting them to every X ms at most, since probably it's acceptable to update every 200/300 ms. For example, you could use the lodash _.debounce function. For an in-depth explanation, see also the article Debouncing and Throttling Explained Through Examples.
this.watchVehicle = this.geolocation.watchPosition({ enableHighAccuracy: true })
.subscribe(async (pos) => {
_.debounce(() => {
this.drawRouteFromVehicleToDestination(pos.coords.latitude, pos.coords.longitude);
}, 300, { leading: true });
}, (err: PositionError) => {
// alert(err.message)
console.log("error : " + err.message);
});
I have a map with a number of pins on it, the pins are generated from an endpoint api (json). I want to filter the pins via an input that has a v-modal - the search criteria is already set up and is pulled from the same api.
Even if someone can give some tips as to where in the vue lifecycle the filter should happen, i.e mounted, updated computed ect
Originally I used this article as a reference
https://medium.com/#limichelle21/integrating-google-maps-api-for-multiple-locations-a4329517977a
created() {
axios
.get(
`https://cors-anywhere.herokuapp.com/myEndpoint`
)
.then(response => {
// JSON responses are automatically parsed.
this.allProperties = response.data;
this.markers = this.allProperties.map(function (x) {
return {
lat: parseFloat(x.lat).toFixed(3),
lng: parseFloat(x.lng).toFixed(3),
add: x.dispAddress,
propId: x.property_id,
propPrice: x.outputAskingPrice,
propImg: x.imagePath
};
});
this.allProperties = response.data.map(x => {
x.searchIndex = `${x.sellingStatus} ${x.priceType} ${x.typeNames[0]} ${x.typeNames[1]} ${x.dispAddress}`.toLowerCase();
return x;
});
});
},
mounted: function () {
var _this = this;
function initMap() {
var center = {
lat: 53,
lng: -3
};
var map = new google.maps.Map(document.getElementById("map-canvas"), {
zoom: 10,
center: center
});
var newPin = new google.maps.Marker({
position: center,
map: map
});
}
},
updated() {
var _this = this;
var map = new google.maps.Map(document.getElementById("map-canvas"), {
zoom: 9,
center: new window.google.maps.LatLng(55.961, -3)
});
var infowindow = new google.maps.InfoWindow({});
var newPin;
var count;
for (count = 0; count < _this.markers.length; count++) {
newPin = new google.maps.Marker({
position: new google.maps.LatLng(
_this.markers[count].lat,
_this.markers[count].lng
),
map: map,
icon: "../assets/img/map-pin.png"
});
google.maps.event.addListener(
newPin,
"click",
(function (newPin, count) {
return function () {
infowindow.setContent(` ${_this.markers[count].add} <p> ${_this.markers[count].propPrice}</p><img src="${_this.markers[count].propImg}"><p>`);
infowindow.open(map, newPin);
};
})(newPin, count)
);
}
If you have v-model on an <input> field like mentioned in your question, you are binding the value of this <input> field to a variable probably defined in the data part of your Vue component. The value is always up to date in the model (reactive binding). You can watch this value and then trigger a function which updates Google Maps. Here is an example:
Vue.component('demo', {
data () {
return {
inputField: ''
};
},
created () {
console.log('Component script loaded, HTML not yet ready, load the data from your backend. Use a flag like isLoading or similar to indicate when the data is ready to enable input.');
},
mounted () {
console.log('Component mounted, HTML rendered, load Google Maps');
},
watch: {
inputField (newValue) {
console.log(`inputField changed to ${newValue}. Trigger here a method which update Google Maps. Make sure to debounce the input here, so that it does not trigger a Google Maps update too often.`);
}
},
template: `
<div>
<input type="text" v-model="inputField" placeholder="Lookup place">
</div>`
});
new Vue({ el: '#vue-demo-container' });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="vue-demo-container">
<demo></demo>
</div>
I've been trying to solve this issues with no luck. I already check other posts and no luck. I think I have an error on my code, my goal is to be able to filter the list and show/hide only the markers on that list. A sample of the code is here: https://jsfiddle.net/rp2t3gyn/2/
Here is a sample of the code that is not working for some reason:
self.filteredPlaces = ko.computed(function() {
var filter = self.filter().toLowerCase();
if (!filter) {
ko.utils.arrayForEach(self.placeList(), function (placeItem) {
placeItem.marker.setVisible(true);
});
return self.placeList();
} else {
return ko.utils.arrayFilter(self.placeList(), function(placeItem) {
// set all markers visible (false)
var result = (placeItem.city.toLowerCase().search(filter) >= 0);
placeItem.marker.setVisible(result);
return result;
});
}
}, this);
Thanks
In order to filter the markers you need to do a few things.
Your first problem is this line:
placeItem.marker.setVisible(true);
Place item doesn't have a marker object based on your constructor. So, you have to add it. I changed the Place constructor to add a marker object (see below).
var Place = function(data, map, viewmodel) {
this.city = data.city;
this.lat = data.lat;
this.lng = data.lng;
var marker = new google.maps.Marker({
map: map,
position: {lat: data.lat, lng: data.lng},
city: data.city,
icon: {
path: google.maps.SymbolPath.BACKWARD_CLOSED_ARROW,
scale: 5
},
animation: google.maps.Animation.DROP,
});
marker.addListener('click', function() {
viewmodel.clickSelection(marker, viewmodel.largeInfoWindow);
});
this.marker = marker;
};
and used that to initialize your objects. Lastly, I changed your filteredPlaces function, it needs to subscribe to the query observable so that when you type in text the markers on the map adjust accordingly .
self.query.subscribe(function() {
var filter = self.query().toLowerCase();
if (!filter) {
ko.utils.arrayForEach(self.placeList(), function (placeItem) {
placeItem.marker.setMap(map);
});
return self.placeList();
} else {
ko.utils.arrayForEach(self.placeList(), function(placeItem) {
var result = (placeItem.city.toLowerCase().search(filter) >= 0);
if(result)
placeItem.marker.setMap(map);
else
placeItem.marker.setMap(null);
});
}
});
Working fiddle here.
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,
};
}
My maps api is showing wrong places types and I can't figure out why. Se the code bellow, I've asked for:
type: ['store', 'school', 'church', 'park', 'university', 'cafe', 'gym', 'bakery']
See the entire code here:
<script>
var map;
var infowindow;
function initMap() {
var image = 'http://www.vittoriagardens.com/wp-content/uploads/2016/04/pin.png';
var myLatLng = {lat: 25.5244191, lng: -80.4063932};
map = new google.maps.Map(document.getElementById('map'), {
zoom: 15,
scrollwheel: false,
center: myLatLng
});
var principal = new google.maps.Marker({
position: myLatLng,
map: map,
title: 'Vittoria',
icon: image
});
infowindow = new google.maps.InfoWindow();
/*lojas*/
var placestore = new google.maps.places.PlacesService(map);
placestore.nearbySearch({
location: myLatLng,
radius: 2500,
type: ['store', 'school', 'church', 'park', 'university', 'cafe', 'gym', 'bakery']
}, callback);
}
function callback(results, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
createMarker(results[i]);
}
}
}
function createMarker(place) {
var placeLoc = place.geometry.location;
var iconType = {};
iconType['school'] = "http://www.vittoriagardens.com/wp-content/uploads/2016/04/categoria_school.png";
iconType['church'] = "http://www.vittoriagardens.com/wp-content/uploads/2016/04/categoria_church.png";
iconType['park'] = "http://www.vittoriagardens.com/wp-content/uploads/2016/04/categoria_park.png";
iconType['university'] = "http://www.vittoriagardens.com/wp-content/uploads/2016/04/categoria_school.png";
iconType['store'] = "http://www.vittoriagardens.com/wp-content/uploads/2016/04/categoria_shopping_mall.png";
iconType['cafe'] = "http://www.vittoriagardens.com/wp-content/uploads/2016/04/categoria_bakery.png";
iconType['bakery'] = "http://www.vittoriagardens.com/wp-content/uploads/2016/04/categoria_bakery.png";
iconType['gym'] = "http://www.vittoriagardens.com/wp-content/uploads/2016/04/categoria_gym.png";
var marker = new google.maps.Marker({
map: map,
icon: iconType[place.types[0]],
position: place.geometry.location
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(place.name);
infowindow.open(map, this);
});
}
</script>
I don't know why but on the Map that you can see here: Vittoria Gardens or in this image bellow:Print of the map on the website, for example, it's showing a Mexican Restaurant and a UPS Costumer center, and I didn't asked for this types of places.
Does anyone know why is this happening?
Thank you
From the documentation:
Warning: The implementation for types in text search requests is changing. The types parameter is deprecated as of February 16, 2016, replaced by a new type parameter which only supports one type per search request. Additionally, the establishment, place_of_worship, food, health, general_contractor and finance types will no longer be supported as search parameters (however these types may still be returned in the results of a search). Requests using the deprecated features will be supported until February 16, 2017, after which all text searches must use the new implementation.
While the text above specifically states "text search", it also applies to NearbySearch.
This won't work:
type: ['store', 'school', 'church', 'park', 'university', 'cafe', 'gym', 'bakery']