I wrote a map page that uses geolocation to detect the user's location. When the user clicks a button, this javascript is called:
if (navigator.geolocation) {
wpid = navigator.geolocation.watchPosition(function (position) {
setUserPosition(position, "Your location", pollUserPosition);
},
function() { handleNoGeolocation(true); },
{ enableHighAccuracy: false, maximumAge: 30000, timeout: 10000 }
);
}
From what I understand, watchPosition will start polling the user's location until it's told to stop. This is done by calling clearWatch and passing in the wpid from the watchPositon call. setUserPosition will create a marker on the map based upon the location in position. When I test this page in mobile safari sometimes I get two initial locations marked on the map. Does anyone know why I'd get two locations returned and how I can only return one? Is it because a location is returned using both the cell connection and the wifi connection on the iPhone?
Update:
As a test, I turned off WiFi on the phone and tested the map page again. Unfortunately, I'm still getting two initial locations when I call watchPosition.
with watchPosition, you will get updated position information on the callback if the position data changes either by device movement or if more accurate geo information arrives.
So if you want only ONE position try using the:
navigator.geolocation.getCurrentPosition
This should only return ONE position. Have you tried that already?
Related
I am loading markers from a database and then drawing a polyline between markers. I am using the polyline to calculate overall distance instead of having to calculate the distance from marker-a to marker-b to marker-c and so on.
My distance is however inaccurate because if two markers are around a curved road, the polyline just connects them instead of drawing it along the road.
I know this is possible in Google Maps API but the usage restrictions would not suit me which is why I decided to use leaflet.
My markers are not so far apart, because my GPS device sends location every 10 seconds.
I found the leaflet-routing-machine plugin and I was wondering if I can use this to make my polyline snap to the road?
This is how I am adding markers to my map:
function getlocationsfromdb(){
group.clearLayers();
latlngArray.length=0;
var deviceid = $("#selectid").val();
$.ajax({
type: "POST",
url: "functionhandlers/getlocations.php",
data: {deviceid:deviceid,start:start,end:end},
dataType: 'json',
cache: false,
})
.success(function(response) {
$('input').removeClass('error').next('.errormessage').html('');
if(!response.errors && response.result) {
$.each(response.result, function( index, value) {
var latlng = L.latLng(value[7], value[8]);
var marker = L.circleMarker(latlng,{radius:2}).addTo(group);
latlngArray.push(latlng);
});
var polyline = L.polyline(latlngArray, {color: '#605ca8'}).addTo(group);
map.fitBounds(group.getBounds());
var distancetravelled=polyline.measuredDistance();
$("#distancetravelled").html(distancetravelled);
} else {
$.each(response.errors, function( index, value) {
// add error classes
$('input[name*='+index+']').addClass('error').after('<div class="errormessage">'+value+'</div>')
});
}
});
}
Can someone please point me in the right direction?
This can be done rather easily with leaflet-routing-machine. You can just set the waypoints to your latlngArray when you initialize the routing control:
var control = L.Routing.control({
waypoints: latlngArray,
show: false,
waypointMode: 'snap',
createMarker: function() {}
}).addTo(map);
Here, show: false keeps the control from displaying on the map, and the empty createMarker function overrides the default markers that routing machine creates, instead doing nothing (though the markers would be removed when we remove the control, this just keeps them from flashing on the screen when a route is found).
You can extract all the vertices of the routing machine results by listening for the routeselected event, which will return an IRoute object that contains all the directions and geometries for the route. Placing the .route.coordinates in a new L.polyline object will keep the route around, so we can then just get rid of the routing control:
control.on('routeselected', function(e) {
L.polyline(e.route.coordinates).addTo(group);
map.removeControl(control);
});
Placing the above code blocks within your .success callback function right after you populates your latlngArray should give you the route you want. Here's a fiddle showing this at work:
http://fiddle.jshell.net/nathansnider/ygktexbj/
Also, if you're not using the routing control for anything else and want to keep it from showing up entirely (a small white control box may still appear while the route is being calculated), you can simply hide it in CSS:
.leaflet-routing-container {
display:none;
}
I realize that this solution is a bit roundabout, since it creates a control, and then half the code is just preventing that control from showing up on the map
You actually don't have to add it to the map. Furthermore you can attack inner router's route function directly.
var routingControl = L.Routing.control({
waypointMode: 'snap'
});
then
routingControl._router.route(latLngCoordinates, function(err, waypoints) {
var a = waypoints;
});
Be careful, it's raw copy/paste:
- waypoints fits internal format (inspect it)
- latLngCoordinates must be in {lat:, lng:} format
- it might require some cleaning because the url generated encapsulate some very long "hints" data.
I have a little web app and im trying to show a navigation from the users position to the university. It all work if I hard code the longitude and latitude of the two navigation points but when I replace the number values for the first waypoint I get a routing request failed message. I have alerted the values just before declaring the waypoints so I know the variables contain the correct data.
Any help is much appreciated.
Below is the code I have
`var lat;
if (navigator.geolocation)
{
navigator.geolocation.getCurrentPosition(function (position)
{
try
{
lat=position.coords.latitude;
lon=position.coords.longitude;
alert(lon+" "+lat);
//THE API IS INSERTED HERE -- WAYPOINTS SECTION OF API
// Create waypoints
alert(lon+"__"+lat);
var waypoints = new nokia.maps.routing.WaypointParameterList();
waypoints.addCoordinate(new nokia.maps.geo.Coordinate(lon, lat));
waypoints.addCoordinate(new nokia.maps.geo.Coordinate(52.51717584105763, 13.395129026281722));
}catch(err){alert(err);}
});
}else{alert("GPS not active");}
When you are creating the Coordinate, you should specify the latitude first i.e Coordinate(lat,long) not Coordinate(long,lat). Since two thirds of the surface of the globe is covered in water it is likely that your rogue coordinate is not on dry land, and hence can't successfully be used for routing. As far as I know HERE haven't released a ship navigation mode into the routing API yet.
I've found a lot of questions about GPS Coordinates but not one that confirms using the mobile hardware GPS instead of Web GPS like geoLocation and such like.
My actual method:
I'm using navigator.geolocation.getCurrentPosition(), the Lat/Long comes from the Web, here's the code:
function getGPS(funcCallBack)
{
if (navigator.geolocation)
{
var timeoutVal = getCookie("GPSTimeout");
navigator.geolocation.getCurrentPosition(sucess
,error
,{enableHighAccuracy: true
,timeout: timeoutVal
,maximumAge: 0}
);
}
else{
alert('GPS is turned off, or was not possible to find it. Now, doing the login without localization.');
window.gpsLat = 0;
window.gpsLng = 0;
window.gpsAcc = 0;
funcCallBack();
}
function sucess(position) //sucess
{
window.gpsLat = position.coords.latitude;
window.gpsLng = position.coords.longitude;
window.gpsAcc = position.coords.accuracy;
funcCallBack();
}
function error() //error
{
window.gpsLat = 0;
window.gpsLng = 0;
window.gpsAcc = 0;
funcCallBack();
}
}
My problem:
Sometimes when I do the login I am not getting the GPS Coordinates (they come 0) and sometimes I am getting coordinates with more than 2,000 Accuracy (that is not precise).
By the way, I am testing the GPS on a data internet service, when I do use a Wi-Fi connection it works perfectly with less than 100 Accuracy.
Details:
Maybe you are complaining about:
timeoutVal: it is a cookie with the number 5000 inside it.
funcCallBack: it is a function that continues the login operation.
window.gpsLat: it is a global var containing the Latitude value got from the geoLocation.
window.gpsLng: it is a global var containing the Longitude value got from the geoLocation.
window.gpsAcc: it is a global var containing the Accuracy value got from the geoLocation.
What do I want?
I want a solution in JavaScript or PHP that can get coordinates from the mobile hardware device, the Native GPS, not the geolocation, and when the Native GPS is turned off, ask the user to turn it on.
You should get the location with javascript not PHP. PHP is only capable of doing an IP lookup which is the least accurate method for determining location.
The way navigator.geolocation.getCurrentPosition() works is it uses the most accurate data currently available. In the case of a mobile device it will use GPS first if enabled, then wi-fi.
If native GPS is enabled javascript will access that data instead of the wi-fi data, but there is no way of preventing a check against the wi-fi data if the GPS data isn't available.
Your best solution is to check the accuracy field and if it's not within a range you're happy with ask the user to enable GPS.
Alternatively if you're building a hybrid app, most of the frameworks (PhoneGap .etc.) have APIs to query the device GPS directly. Use PhoneGap to Check if GPS is enabled
Geolocation API does not expose a direct way to check whether GPS is on or off, but you can catch the errors of geolocation and base on error type can draw conclusions from there.
E.g. POSITION_UNAVAILABLE (2) if the network is down or the positioning satellites can’t be contacted.
But its not sure short way you have to handle some conditions!
I will suggest use watchPostion { i agree its meant to watch and continuous to locate position} u can keep it on and if GPS throw the error u can prompt custom alert to make user turn on the GPS device/wifi/internet .. and if its come to success callback u can clear the watch.
var watch =null;
function success(position)
{
var lat = position.coords.latitude;
var lon= position.coords.longitude;
if (watch != null )
/*Need to take care .. as maybe there is no gps and user
want it off so keep attempt 3 times or some kind a way out otherwise it will
infinite loop */
{
navigator.geolocation.clearWatch(watch);
watch = null;
}
}
function getLatLon()
{
var geolocOK = ("geolocation" in navigator);
if ( geolocOK )
{
var option = {enableHighAccuracy:true, maximumAge: 0,timeout:10000 };
watch = navigator.geolocation.watchPosition(success, fails, option);
}
else {
//disable the current location?
}
}
function fails()
{
alert("please turn on the GPS !");
}
getLatLon();
I'm working on a implementing a Google map on a website with our own tiles overlays and KML elements. I've been previously requested to create code so that, for instance, when the page is loaded from a specific URL, it would initialize with one of the tile overlays already enabled. Recently, I've been requested to do the same for the buildings which are outlined by KML elements so that, arriving at the page with a specific URL, it would automatically zoom, center, and display information on the building.
However, while starting with the tile overlays work, the building KML does not. After doing some testing, I've determined that when the code which checks the URL executes, the page is still loading the KML elements and thus do not exist for the code to compare to or use:
Code for evaluating URL (placed at the end of onLoad="initialize()")
function urlClick() {
var currentURL = window.location.href; //Retrieve page URL
var URLpiece = currentURL.slice(-6); //pull the last 6 digits (for testing)
if (URLpiece === "access") { //If the resulting string is "access":
access_click(); //Display accessibility overlay
} else if (URLpiece === "middle") { //Else if the string is "middle":
facetClick('Middle College'); //Click on building "Middle College"
};
};
facetClick();
function facetClick(name) { //Convert building name to building ID.
for (var i = 0; i < active.placemarks.length; i++) {
if (active.placemarks[i].name === name) {
sideClick(i) //Click building whose id matches "Middle College"
};
};
};
Firebug Console Error
active is null
for (var i = 0; i < active.placemarks.length; i++) {
active.placemarks is which KML elements are loaded on the page, and being null, means no KML has been loaded yet. In short, I have a mistiming and I can't seem to find a suitable place to place the URL code to execute after the KMl has loaded. As noted above, I placed it at the end of onLoad="initialize()", but it would appear that, instead of waiting for the KML to completely load earlier in the function, the remainder of the function is executed:
onLoad="initialize()"
information(); //Use the buttons variables inital state to set up description
buttons(); //and button state
button_hover(0); //and button description to neutral.
//Create and arrange the Google Map.
//Create basic tile overlays.
//Set up parser to work with KML elements.
myParser = new geoXML3.parser({ //Parser: Takes KML and converts to JS.
map: map, //Applies parsed KML to the map
singleInfoWindow: true,
afterParse: useTheData //Allows us to use the parsed KML in a function
});
myParser.parse(['/maps/kml/shapes.kml','/maps/kml/shapes_hidden.kml']);
google.maps.event.addListener(map, 'maptypeid_changed', function() {
autoOverlay();
});
//Create other tile overlays to appear over KML elements.
urlClick();
I suspect one my issues lies in using the geoxml3 parser (http://code.google.com/p/geoxml3/) which converts our KML files to Javascript. While the page has completed loading all of the elements, the map on the page is still loading, including the KML elements. I have also tried placing urlClick() in the parser itself in various places which appear to execute after all the shapes have been parsed, but I've had no success there either.
While I've been intending to strip out the parser, I would like to know if there is any way of executing the "urlClick" after the parser has returned the KML shapes. Ideally, I don't want to use an arbitrary means of defining a time to wait, such as "wait 3 seconds, and go", as my various browsers all load the page at different times; rather, I'm looking for some way to say "when the parser is done, execute" or "when the Google map is completely loaded, execute" or perhaps even "hold until the parser is complete before advancing to urlClick".
Edit: Here are links to the map with the basic form of the issue found above. Since I've been developing the next update to the map on a test server, facetClick() is not part of this live version and I instead use its output function sideClick(); however the error is still the same in this arrangement:
active is null
google.maps.event.trigger(active.gpolygons[poly],'click');
Map: http://www.beloit.edu/maps/
Map w/Accessibility: http://www.beloit.edu/maps/?access
Map w/Building Click: http://www.beloit.edu/maps/?middle
EDIT: Spent most of my day working on rebuilding the functionality of the parser in Javascript and, low and behold, without the parser it works just fine. I figure that is obvious as I have to define each shape individually before the code, rather than waiting for it to be passed along by the parser. It would seem the answer is "if you want unique URLs, drop the parser". >_<
I've come across a similar problem when dealing with waiting for markers and infoWindows to load before executing a function. I found a solution here ( How can I check whether Google Maps is fully loaded? see #Veseliq's answer) that using the google maps event listener function for checking when the map is 'idle', does the trick. I assume this solution would work for KML layers as well. Essentially what you will have to do is include the following at the end of your initialize function:
google.maps.event.addListenerOnce(map, 'idle', function(){
// do something only the first time the map is loaded
});
In the API reference ( https://developers.google.com/maps/documentation/javascript/reference ) it states that the 'idle' event "is fired when the map becomes idle after panning or zooming". However, it seems to hold true that it is also fires on initial page load after everything in the map_canvas has loaded. And by using the addListenerOnce call, you ensure that it is never executed again after the initial page load (meaning it won't fire after a zoom or a pan action).
Second option:
As I mentioned you can take the callback approach, I believe this will only call your urlClick function after completing the parsing. Here's how you should probably arrange your code to make it work:
function someFunction(callback){
myParser.parse(['/maps/kml/shapes.kml','/maps/kml/shapes_hidden.kml']);
callback();
}
and then in your initialize you will have:
someFunction(function(){
urlClick();
});
You will have to make your map and myParser variables global.
Resources: This link had an excellent and detailed brief on how callback functions work in javascript, http://www.impressivewebs.com/callback-functions-javascript/
I have a google maps set up with pointers. I've set it up so whenever the zoom changes or the viewport does it loads a new set of pointers accordingly:
google.maps.event.addListener(xmap, 'zoom_changed', function(event){
tm.update();
});
google.maps.event.addListener(xmap, 'bounds_changed', function(event){
tm.update();
});
google.maps.event.addListenerOnce(xmap, 'tilesloaded', function(){
tm.update();
});
I have also set it so that clicking on any pointer would open an infobox that would be populated with data from an ajax query. The problem is that I need to set it up so that if lets say a pointer is to the far edges of the view port it should pan to the center or to a position where when the infowindow opens up it can be viewed in the entire port.
The issue arises is that at times the infowindow when it loads can be too big for the view port and causes the map to pan and that in turn triggers the update function repopulating the pointers again.
WHats the best way to handle this.
So what you could perhaps do is have something like a boolean flag to indicate whether or not to call tm.update(). In your event handler for marker clicks (to open the infowindows), set it to false. Then in your bounds_changed event handler, only call tm.update if that boolean value is true. Then perhaps at the end of bounds_changed, reset it back to true.
Another guess - not to remove and overwrite markers that is already visible (falling into map.getBounds()). When you recive response with new markers to add - verify, if it's not already there.
UPDATE:
After bounds_changed you have new bounds. Then you query your server and get marker coords and other associated data you need. When you adding markers on to the map, save references to them, say, in hash. If you have some unique id - it's perfect. In other case, invent some kind of identifier for marker. Then we need to add new markers and possibly remove some of them, that is hidden. Like this:
// somewhere initially
var markersOnMap = {}
// when got data and adding new markers
markersData = array of markers you've received from request
for (var i=0; i < markersData.length; i++)
{
var datum = markersData[i];
if (typeof(markersOnMap[datum.id]) == 'undefined')
{
// add marker to map
var marker = new google.maps.Marker({
position: ...,
etc.
});
marker.setMap(mymap);
markersOnMap[datum.id] = marker;
}
}
// you may clean up hidden markers or
// leave it for future or what you want.
// suppose we delete out of view markers
for (var id in markersOnMap)
{
if (!mymap.getBounds().contains(markersOnMap[id].getPosition()))
{
// get rid of it
markersOnMap[id].setMap(null);
delete markersOnMap[id];
}
}