How to get all places like Entertainment, food, gas station etc on google map plotted route from source to destination only on route not nearby.
To build on the link that #geocodezip gave, to work around query limits it might be a better practice to make a bounding box based on steps rather than using the Google Maps utility, and then filter locations by actual distance to a node in the step. This could be accomplished using the following code:
In initialize(), put:
var placeserv = null;
function initialize(){
//setup map
placeserv = new google.maps.PlacesService(map); //use your map variable
}
In the callback function for the Directions Service (using parameters results and status), you can include something like this:
var route = results[0];
var steps = route.legs[0].steps;
for(var i=0; i<steps.length; i++){
var lats = steps[i].path.map(function(a){return a.lat()});
var lngs = steps[i].path.map(function(a){return a.lng()});
var box = new google.maps.LatLngBounds(
new google.maps.LatLng(Math.max.apply(null, lats), Math.max.apply(null, lngs)),
new google.maps.LatLng(Math.min.apply(null, lats), Math.min.apply(null, lngs))
);
placeserv.radarSearch(yourRequest_seeDocumentation_or_geocodezipsCode,
function(r,s){callback(r,s,steps[i].path)}
);
//depending on the number of queries you need to make, you may to add in
//some setTimeouts
}
And then add a callback function for your call that checks if the route is within a specified degree. If so, it will do something with the location. (By the way, this requires the Geometry Library for Google Maps. Please look it up.)
var minimumDist = 300 //within 300 meters of a point on the route
function callback(results, status, stepPts){
if(results == 'OK'){
for(var j=0; j<results.length; j++){
for(var k=0; k<stepPts.length; k++){
var dist = google.maps.geometry.spherical.computeDistanceBetween(stepPts[k], results[j].geometry.location);
if(dist < minimumDist){
//do something with the location
}
}
}
}
}
Again, geocodezip has a very complete and excellent post in the link he gave you. This is just the implementation I would use to cut down on Places Service calls.
Related
I'm creating a ASP.NET MVC3 website using a Google Maps API V3 with a lot of markers. Each marker has an InfoWindow with some information about this place.
I wish each marker have a direct link to access my website directly on this marker, like http://www.mywebsite/1589. So the user could access the website with the map centered on marker 1589 and its InfoWindows would be open.
The markers are already on the map and their InfoWindow are already displaying informations, but I have no idea how to create a direct link to the marker... Could anybody help me ?
Thanks in advance
The key pieces are:
parse the query string
// skip the first character, we are not interested in the "?"
var query = location.search.substring(1);
// split the rest at each "&" character to give a list of "argname=value" pairs
var pairs = query.split("&");
for (var i=0; i<pairs.length; i++) {
// break each pair at the first "=" to obtain the argname and value
var pos = pairs[i].indexOf("=");
var argname = pairs[i].substring(0,pos).toLowerCase();
var value = pairs[i].substring(pos+1).toLowerCase();
// process each possible argname - use unescape() if theres any chance of spaces
if (argname == "id") {id = unescape(value);}
if (argname == "marker") {index = parseFloat(value);}
}
open the infowindow after loading the markers, if a parameter is passed
// ========= If a parameter was passed, open the info window ==========
if (id) {
if (idmarkers[id]) {
google.maps.event.trigger(idmarkers[id],"click");
} else {
alert("id "+id+" does not match any marker");
}
}
if (index > -1) {
if (index < gmarkers.length) {
google.maps.event.trigger(gmarkers[index],"click");
} else {
alert("marker "+index+" does not exist");
}
}
you might also want the "link to" functionality in this example, which sets up the query string
I have a PHP function that creates a kml file. (I validated the output and it's a valid KML file).
Then, I use Google Maps with these file, but I don't know why, no data appears on the map...
In PHP, I have this:
//some stuff here
return 'iniMap("", "", "http://my.web.com/Class/API/GMaps/Rep/'.$g->make($l, $a, $user).'.kml")';
This function is called via AJAX, so the return string will be evaluated with JS "eval()". My Google Maps functions are:
/*GOOGLE MAPS FUNCTIONS*/
function iniMap(x,y,url){
n=document.createElement('DIV');
n.id='map_canvas';
ge('con').appendChild(n);
var latlng=new google.maps.LatLng(x,y);
var map=new google.maps.Map(ge("map_canvas"),{zoom:6,center:latlng,mapTypeId:google.maps.MapTypeId.ROADMAP});
var div1=document.createElement('DIV');
var homeControl1=new makeControl(div1,'t1');
var div2=document.createElement('DIV');
var homeControl2=new makeControl(div2,'t2');
var div3=document.createElement('DIV');
var homeControl3=new makeControl(div3,'t3');
var div4=document.createElement('DIV');
var homeControl4=new makeControl(div4,'t4');
var ctaLayer=new google.maps.KmlLayer(url);
div1.index = 1;
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(div1);
div2.index = 2;
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(div2);
div3.index = 3;
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(div3);
div4.index = 4;
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(div4);
ctaLayer.setMap(map);
}
function makeControl(d,t){
d.style.padding='5px';
var controlUI=document.createElement('DIV');
controlUI.style.backgroundColor='#FEFEFE';
controlUI.style.borderStyle='solid';
controlUI.style.borderWidth='1px';
controlUI.style.cursor='pointer';
controlUI.style.textAlign='center';
controlUI.style.width='60px';
controlUI.style.height='17px';
d.appendChild(controlUI);
var controlText=document.createElement('DIV');
controlText.style.fontFamily='Arial,sans-serif';
controlText.style.fontSize='12px';
controlText.style.paddingLeft='4px';
controlText.style.paddingRight='4px';
controlText.innerHTML=t;
controlUI.appendChild(controlText);
google.maps.event.addDomListener(controlUI,'click',function(){doAction(t);});
}
function doAction(t){
if(t=='t1'){document.location.href='http://my.web.com?t=sy0'}
else if(t=='t2'){document.location.href='http://my.web.com?t=sm0'}
else if(t=='t3'){document.location.href='http://my.web.com?t=sw0'}
else if(t=='t4'){document.location.href='http://my.web.comt=sd0'}
}
What I'm doing wrong?
Thanks!
I fixed the problem...
It was a cache issue. Maybe some a file with bad data was cached, and therefore my function was always getting that bad file...
Today I changed the way to generate the file name, and the map start working...
Thanks to #geocodezip for all the answers and sorry for the time wasting...
I have a kml of points loaded into fusion table layer. I want to parse the data to une map.fitBounds on the layer extent using geoxml3, but thats not functionning. This exactly code below is working with KML polygons but not with KML points layer.
Code:
var queryText = encodeURIComponent("SELECT * FROM 1CNJWjLDYgBkJGZVslJ67Fak4DyqadEFuIabzQ60 ");
var query = new google.visualization.Query('http://www.google.com/fusiontables/gvizdata?tq=' + queryText);
query.send(zoomTo);
}
function zoomTo(response) {
if (!response) {
alert('no response');
return;
}
if (response.isError()) {
alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage());
return;
}
FTresponse = response;
//for more information on the response object, see the documentation
//http://code.google.com/apis/visualization/documentation/reference.html#QueryResponse
numRows = response.getDataTable().getNumberOfRows();
numCols = response.getDataTable().getNumberOfColumns();
var geoXml = new geoXML3.parser();
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < numCols; i++){
if (FTresponse.getDataTable().getColumnLabel(i) == 'geometry') {
var ColIndex = i;
}
}
if (!ColIndex){
alert('Geometry column "geometry" not found.')
}
for (var i = 0; i < numRows; i++){
var kml = FTresponse.getDataTable().getValue(i,ColIndex);
geoXml.parseKmlString("<Placemark>"+kml+"</Placemark>");
bounds.union(geoXml.docs[i].bounds);
}
map.fitBounds(bounds);
}
The parse method should not be used for parsing KML strings from FusionTables (the parseKmlString method is for doing that).
var kml = FTresponse.getDataTable().getValue(i,ColIndex);
geoXml.parseKmlString("<Placemark>"+kml+"</Placemark>");
Note: the KML fragments stored in FusionTables do not include the <Placemark> tags which geoxml3 looks for, that is why they are added to the string passed to geoxml3.
The GViz query response has a 500 row limit (that doesn't seem to be documented anywhere I could find, the best I could find was this reference to it, but the documentation has moved since then).
Looks like you are going to run into that limitation with your table, to overcome that use the FusionTables API v1.0, that returns GeoJSON, not KML (so you will no longer need geoxml3).
example that decodes KML "Points" from FusionTables using GViz and geoxml3 (table contains less than 500 points)
example of parsing markers from Fusion Tables using the Fusion Tables API v1.0
I've been looking at the example on:
http://code.google.com/apis/maps/documentation/javascript/examples/places-autocomplete.html
and have decided to incorporate it into my site.
Is it possible to limit the addresses to UK addresses only?
Try this:
var input = document.getElementById('searchTextField');
var options = {
types: ['(cities)'],
componentRestrictions: {country: 'tr'}//Turkey only
};
var autocomplete = new google.maps.places.Autocomplete(input,options);
You can't strictly/hard limit the locations that it finds, although there is a feature request in the system to do so, but you can set a 'bias' on the results. It's passed in as an argument to the autocomplete method as a google maps bounds object. Autocomplete will then favor locations within those boundaries. Note, however, that since this isn't a hard boundary, if there are matches for the search outside the boundaries it will return those.
From my usage it seems a bit buggy and can use some improvement - especially considering that anything outside your boundary is not tagged by proximity at all, so something one block outside the boundary is just as likely to show as something 1000 miles outside, so make sure you play around with getting the boundaries working right.
You can intercept the JSONP results that are returned by the google.maps.places.Autocomplete functionality and use them as you see fit, such as to limit by country and display the results.
Basically you redefine the appendChild method on the head element, and then monitor the javascript elements that the Google autocomplete code inserts into the DOM for JSONP. As javascript elements are added, you override the JSONP callbacks that Google defines in order to get access to the raw autocomplete data.
It's a bit of a hack, here goes (I'm using jQuery but it's not necessary for this hack to work):
//The head element, where the Google Autocomplete code will insert a tag
//for a javascript file.
var head = $('head')[0];
//The name of the method the Autocomplete code uses to insert the tag.
var method = 'appendChild';
//The method we will be overriding.
var originalMethod = head[method];
head[method] = function () {
if (arguments[0] && arguments[0].src && arguments[0].src.match(/GetPredictions/)) { //Check that the element is a javascript tag being inserted by Google.
var callbackMatchObject = (/callback=([^&]+)&|$/).exec(arguments[0].src); //Regex to extract the name of the callback method that the JSONP will call.
var searchTermMatchObject = (/\?1s([^&]+)&/).exec(arguments[0].src); //Regex to extract the search term that was entered by the user.
var searchTerm = unescape(searchTermMatchObject[1]);
if (callbackMatchObject && searchTermMatchObject) {
var names = callbackMatchObject[1].split('.'); //The JSONP callback method is in the form "abc.def" and each time has a different random name.
var originalCallback = names[0] && names[1] && window[names[0]] && window[names[0]][names[1]]; //Store the original callback method.
if (originalCallback) {
var newCallback = function () { //Define your own JSONP callback
if (arguments[0] && arguments[0][3]) {
var data = arguments[0][4]; //Your autocomplete results
//SUCCESS! - Limit results here and do something with them, such as displaying them in an autocomplete dropdown.
}
}
//Add copy all the attributes of the old callback function to the new callback function. This prevents the autocomplete functionality from throwing an error.
for (name in originalCallback) {
newCallback[name] = originalCallback[name];
}
window[names[0]][names[1]] = newCallback; //Override the JSONP callback
}
}
//Insert the element into the dom, regardless of whether it was being inserted by Google.
return originalMethod.apply(this, arguments);
};
James Alday is correct:
http://code.google.com/apis/maps/documentation/javascript/places.html#places_autocomplete
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(49.00, -13.00),
new google.maps.LatLng(60.00, 3.00));
var acOptions = {
bounds: defaultBounds,
types: ['geocode']
};
it is somewhat annoying as searching for Durham gives Durham, North Carolina as the second result, regardless of how you try to persuade it to region bias - you can set it to viewport map bounds and it'll still try to suggest NC state... The jQuery solution can be found here, but doesn't seem to give as many results as the v3 API.
http://code.google.com/p/geo-autocomplete/
The best way you would go about doing this, is to query the places api yourself and appending the queried string with your country. Or, of course, use the geo-autocomplete jQuery plugin.
Just change the google domain for the maps to your country domain and it will automatically search within your country only:
So:
http://maps.google.com/maps/api/geocode/xml?address={0}&sensor=false&language=en
To:
http://maps.google.nl/maps/api/geocode/xml?address={0}&sensor=false&language=nl
Try something like this.
// Change Bangalore, India to your cities boundary.
var bangaloreBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(12.864162, 77.438610),
new google.maps.LatLng(13.139807, 77.711895));
var autocomplete = new google.maps.places.Autocomplete(this, {
bounds: bangaloreBounds,
strictBounds: true,
});
autocomplete.addListener('place_changed', function () {
});
I find that if you set the map to roughly where you want then set bounds to it, the search finds places in that area first. You do not to physically show the map.
It works better than giving random overseas addresses first, setting to country does not work.
The code for autocomplete to get latln is:
<div id="map_canvas"></div>
<input type="text" name="location" id="location" placeholder="Type location...">
<input type="text" name="loc_latitude" id="latitude">
<input type="text" name="loc_longitude" id="longitude">
and the JS is:
$(document).ready(function () {
var mapOptions = {
center: new google.maps.LatLng(52.41041560, -1.5752999),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById('map_canvas'),
mapOptions);
var autocomplete;
autocomplete = new google.maps.places.Autocomplete((document.getElementById(searchInput)), {
types: ['geocode'],
});
autocomplete.bindTo('bounds', map);
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var near_place = autocomplete.getPlace();
document.getElementById('latitude').value = near_place.geometry.location.lat();
document.getElementById('longitude').value = near_place.geometry.location.lng();
});
});
$(document).on('change', '#'+searchInput, function () {
document.getElementById('latitude').value = '';
document.getElementById('longitude').value = '';
});
Not exactly what you asked for but it works for me.
I have two maps on a page, one is a map of the world, and the other is a closeup of the current place they picked on the map of the world. I would like to set different zoom min/max levels for each map but:
G_NORMAL_MAP.getMinimumResolution = function(){return 11};
Seems to set the same min/max for both maps, I can't set them to different levels.
I think the problem is probably elsewhere in your code - I'm not sure exactly how you're using that function.
Here is a method that will work. You can re-write it to have less duplication.
map1 = new GMap2(document.getElementById("map1"));
map1.addControl(new GLargeMapControl3D());
map1.addControl(new GMenuMapTypeControl());
var mt = map1.getMapTypes();
// Overwrite the getMinimumResolution() and getMaximumResolution() methods
for (var i=0; i<mt.length; i++) {
mt[i].getMinimumResolution = function() {return 7;}
mt[i].getMaximumResolution = function() {return 11;}
}
map1.setCenter(new GLatLng(40,-100), 8);
map2 = new GMap2(document.getElementById("map2"));
map2.addControl(new GLargeMapControl3D());
map2.addControl(new GMenuMapTypeControl());
var mt = map2.getMapTypes();
// Overwrite the getMinimumResolution() and getMaximumResolution() methods
for (var i=0; i<mt.length; i++) {
mt[i].getMinimumResolution = function() {return 2;}
mt[i].getMaximumResolution = function() {return 6;}
}
map2.setCenter(new GLatLng(40,-100), 4);
Do you need 2 different maps? You can use the Map2.showMapBlowup() function to show a subarea which will be a zoomed in section on the current map.
I'm sorry I don't know if you can actually do it with 2 different maps.
http://code.google.com/apis/maps/documentation/reference.html#GMap2.showMapBlowup
You could use a custom map type and copy the G_NORMAL_MAP members of using a library like Prototype.
var G_MY_MAP = Class.create(G_NORMAL_MAP, {
getMinimumResolution: function()
{
return 11;
}
});
Then on your second map:
secondMap.addMapType(G_MY_MAP);
secondMap.setMapType(G_MY_MAP);
No idea if this will work, just a brain storm.....