I'm facing issues getting value of a variable from another function. I'm trying to get the distance of the place from current position in google maps, but using Haversine Formula of calculating distance.
My HTML:
<p>
<script>
lat = "<?php echo $atm_row_data->latitude;?>";
lng = "<?php echo $atm_row_data->longitude;?>";
dist = getDistance(lat, lng);
document.write(dist);
</script>
</p>
My JavaScript:
var curPosition;
var lat, lng;
/**** get current position ****/
function getPosition() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showCurrentPosition);
} else {
alert('Geolocation is not supported by this browser.');
}
}
function showCurrentPosition(position) {
lat = position.coords.latitude;
lng = position.coords.longitude;
curPosition = new google.maps.LatLng(lat, lng);
console.log('curPosition: '+curPosition); <--- this works
}
/******** Haversine Formula of Getting Distance ********/
var rad = function(x) {
return x * Math.PI / 180;
};
function getDistance(lt, lg) {
console.log(curPosition); <--- this shows undefined
var p1 = curPosition;
var R = 6378137; // Earth’s mean radius in meter
var dLat = rad(lt - p1.lat());
var dLong = rad(lg - p1.lng());
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(rad(p1.lat())) * Math.cos(rad(lt)) *
Math.sin(dLong / 2) * Math.sin(dLong / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
if (d >= 1000)
return Math.round( (d / 1000) * 100) / 100 + "Km";
else
return Math.round( (d * 100) / 100) + "m";
};
/******** END Haversine Formula of Getting Distance ********/
Where am I going wrong?
Any help is appreciated...
Knowing the google api, , navigator.geolocation has a callback, so showcurrentPostion is the callback function, but the thing is u never call
getPosition() <--
function getDistance(lt, lg) {
console.log(curPosition); <--- this shows undefined <---- so this will always be undefined
var p1 = curPosition;
var R = 6378137; // Earth’s mean radius in meter
var dLat = rad(lt - p1.lat());
var dLong = rad(lg - p1.lng());
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(rad(p1.lat())) * Math.cos(rad(lt)) * <--- see comment
Math.sin(dLong / 2) * Math.sin(dLong / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
if (d >= 1000)
return Math.round( (d / 1000) * 100) / 100 + "Km";
else
return Math.round( (d * 100) / 100) + "m";
};
comment: <------ p1 curposition gets an google maps object with lat and long where i know that lat() is not a function so this will fail
so even when u get the current position the function above (getDistance()) will fail
Call getPosition first() <--- u need to get the position
edit: i see that u can call console.log and you say it works, so i think its a scoping issue,
I made an example for you -> http://codepen.io/anon/pen/qOBMPj
i'm not able to render the googlemap within the pen, but u can look at the source, on how its done. hope this helps you.
so in order to do this calculation u dont really need google maps u just need the first/ and second coords, but its fun to do something with on on the google map:)
In your script tags, your only call getDistance(lat,lng), but showCurrentPosition(position) is never called! So the variable curPosition is undefined, because it is not defined yet!
You need to call showCurrentPosition(position) for the variable curPosition to hold a value.
Perhaps calling getPosition() at the beginning of your getDistance() function could solve the problem, as it seems to call showCurrentPosition.
Alternative to show the position in HTML (this is just a quick snippet, you can adapt it to whatever you like):
function getPositionHTML() {
navigator.geolocation.getCurrentPosition(function(pos) {
curPosition = new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude);
});
return curPosition;
}
It basically does the same thing as your other function, but it relies on the Single Reponsibility Principle, so once you called this function you can manipulate curPosition however you want.
Related
I'm having an issue with calculating distances.
I have installed postgis, and I'm saving Lng/Lat points in the database
CREATE TABLE geo (gid serial PRIMARY KEY, geo geography)
For inserting a record, this is what I do:
INSERT INTO geo (geo) VALUES (ST_MAKEPOINT(Lng,Lat)::geography))
When I do the following query with some Lng/Lat variables, I get the following output:
SELECT gid, ST_DISTANCE(geo, ST_MAKEPOINT(Lng2,Lat2)::geography) FROM geo;
----
OUTPUT: 1 424.02930940m
Secondly, when I do the exact same calculation with the following formula (haversine formula in javascript), I get a slightly different output.
/*
const coords1/2 = {
lat: // latitude,
lng: // longitude
};
*/
export const haversine = (coords1, coords2) => {
var R = 6371000;
var x1 = coords2.lat - coords1.lat;
var dLat = x1.toRad();
var x2 = coords2.lng - coords1.lng;
var dLon = x2.toRad();
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(coords1.lat.toRad()) * Math.cos(coords2.lat.toRad()) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return d;
};
----
OUTPUT: 422.20392010m
It's a difference of 2 meters, but I need the most accurate of the 2. It's for an app for geocaching.
I'm currently using the haversine formula in my app, but I'm thinking about switching to postgis because it would simplify my queries a lot.
Which one can I rely on the most?
I have a local .json file with lat/long coordinates for each feature, and a column Distance that is empty (randomly putting 999).
The overall goal is to display markers in a leaflet map and filter it based on geolcoation using a button.
I am trying to create one button that is able to:
Get my current location
Calculate distance for each feature in my json file (for loop)
[Blocking point] Update the column Distance / or create a new column
filter json file based on distance (for ex. show me markers where distance is under 100 km)
I got inspiration from this example
but had suffered with the "callback hell" issue.
I am right now in the 3rd step : I managed to calculate distance. but not working outside the function.
Here the code I'm using, The blockage is in the For loop: the distance column is not being updated
var allmarkers = L.markerClusterGroup();
var markersdist100 = L.markerClusterGroup();
// Load json file
var promise = $.getJSON("./data/FILE.geojson");
promise.then(function(data) {
// Update distance in json data
// Geolocation part to get current position
var userPositionPromise = new Promise(function(resolve, reject) {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(data1) {
resolve(data1);
}, function(error) {
reject(error);
});
} else {
reject({
error: 'browser doesn\'t support geolocation'
});
};
});
userPositionPromise.then(function(dataa) {
lat = dataa.coords.latitude;
lng = dataa.coords.longitude;
console.log(lng); // check ok : lng of current location
console.log(data.features.length); //check ok : json length for iteration
// For loop to calculate the new distance
for (var i = 0; i < data.features.length; i++) {
data.features[i].properties.distance = getDistanceFromLatLonInKm(lat, lng, data.features[i].geometry.coordinates[0], data.features[i].geometry.coordinates[1]);
console.log(data.features[i].properties.distance); //check ok : showing the right distance
}
})
console.log(data.features[0].properties.distance); //check BUG : showing initial distance [999]
//all data
var all = L.geoJson(data, {
pointToLayer: style_markers,
});
// data filtered by distance, see the function [filter_distance]
var distance100 = L.geoJson(data, {
pointToLayer: style_markers,
filter: filter_distance
});
// Filter distance on button click
$("#distance100").click(function() {
markersdist100.addLayer(distance100);
allmarkers.addLayer(all);
map.addLayer(markersdist100);
map.removeLayer(allmarkers);
});
});
// FUNCTIONS
function filter_distance(feature, other) {
return feature.properties.distance < 100;
};
function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2 - lat1); // deg2rad below
var dLon = deg2rad(lon2 - lon1);
var a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c; // Distance in km
return d;
};
function deg2rad(deg) {
return deg * (Math.PI / 180)
};
Do you have any recommendations on how to structure my code to access the data with the updated Distance column
Thank you
I got a solution !
I had only to put the var distance100 right after the For loop
With these functions : On button click, you are able to filter markers in a Leaflet map based on your current location (by updating distance in your json file)
Many cases are tackled here : read/access and update a Geojson local file, filter Leaflet markers onclick, use geolocation coordinates in another function, calculate distance ...
the updated code is below (loading the map and the controls is not covered):
var allmarkers = L.markerClusterGroup(); //markercluster plugin using leaflet
var markersdist100 = L.markerClusterGroup();
// Load json file
var promise = $.getJSON("./data/FILE.geojson");
promise.then(function(data) {
// Update distance in json data
// Geolocation part to get current position
var userPositionPromise = new Promise(function(resolve, reject) {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(data1) {
resolve(data1);
}, function(error) {
reject(error);
});
} else {
reject({
error: 'browser doesn\'t support geolocation'
});
};
});
userPositionPromise.then(function(dataa) {
lat = dataa.coords.latitude;
lng = dataa.coords.longitude;
console.log(lng); // check ok : lng of current location
console.log(data.features.length); //check ok : json length for iteration
// For loop to calculate the new distance
for (var i = 0; i < data.features.length; i++) {
data.features[i].properties.distance = getDistanceFromLatLonInKm(lat, lng, data.features[i].geometry.coordinates[0], data.features[i].geometry.coordinates[1]);
console.log(data.features[i].properties.distance); //check ok : showing the right distance
};
// data filtered by distance, see the function [filter_distance]
var distance100 = L.geoJson(data, {
pointToLayer: style_markers,
filter: filter_distance
});
// Filter distance on button click
$("#distance100").click(function() {
markersdist100.addLayer(distance100);
allmarkers.addLayer(all);
map.addLayer(markersdist100);
map.removeLayer(allmarkers);
});
});
//all data
var all = L.geoJson(data, {
pointToLayer: style_markers,
});
});
// FUNCTIONS
function filter_distance(feature, other) {
return feature.properties.distance < 100;
};
function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2 - lat1); // deg2rad below
var dLon = deg2rad(lon2 - lon1);
var a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c; // Distance in km
return d;
};
function deg2rad(deg) {
return deg * (Math.PI / 180)
};
A screenshot of my map
Hope this can help others in their use cases
Feel free to optimize the code and share it with us
Is there a way to calculate the travelled distance in Mapbox?
For example: if a user clicks the start button it'll record the travelled path and calculate the distance. I know it's possible to calculate a distance between two geopoints described here, however a user can go off-road and therefore won't choose to walk the fastest/most suited route between two geopoints. I would like to sum ALL walked kilometers.
Monitor position
navigator.geolocation.watchPosition(function(position) {
document.getElementById('distance').innerHTML =
calculateDistance(startPos.coords.latitude, startPos.coords.longitude,
position.coords.latitude, position.coords.longitude);
});
Calculate distance
function calculateDistance(lat1, lon1, lat2, lon2) {
var R = 6371; // km
var dLat = (lat2 - lat1).toRad();
var dLon = (lon2 - lon1).toRad();
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return d;
}
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
EDIT: one possible solution could be that every time I get a new location I calculate distance between previous geopoint and new geopoint and use that to calculate distance. If I want to see total distance I sum all the distances between previous points. I can also use that data to plot a line between all stored geopoints. Does this makes sense? Is there a more efficient way to do this since this is a pure javascript solution (maybe there is a more related to Mapbox solution?)
I am trying to work out he distance between two postcodes. One post code coordinates are generic however the other postcode is stored in the database which i need to get. This code below doesn't work out the distance between the generic postcode and the one i have inputted.
var x = getDistanceFromLatLonInKm(52.482799000000000, -2.000643000000000, 52.48463500000000, -1.980759000000000);
function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2){
console.log(lat1, lon1, lat2, lon2);
var distanceFromSpecificPoint = getDistanceFromLatLonInKm.bind(null, 52.486637, -1.890952);
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2 - lat1); // deg2rad below
var dLon = deg2rad(lon2 - lon1);
var a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c; // Distance in km
return d;
}
function deg2rad(deg) {
return deg * (Math.PI / 180)
}
document.getElementById('result').textContent = x;
console.log(x)
</script>
As per above, you can simply get the value from the database on the server side and use whatever server side web framework you wish. The example I've given here is Classic ASP, purely as an example to show you where the values are entered. Then on the server side, the database values are constants and you only enter the lat/lon for one point and get the distance from the database lat/lon (the constants LAT1/LON1 here). dbModel would be some object on the server side which is populated from the database. You can then grab the latitude/longitude values from this object for insertion into the web page via server side scripting.
function getDistanceFromLatLonInKm(lat, lon) {
const EARTH_RADIUS = 6371; // Radius of the earth in km
// Here's where you would add the value dynamically from the DB.
// I'm using classic ASP here just as an example. You'll have to
// amend for your particular server side web framework, be it
// JSP, MVC, etc.
const LAT1 = <%=dbModel.getLatitude()%>;
const LON1 = <%=dbModel.getLongitude()%>;
console.log(LAT1, LON1, lat, lon);
var dLat = deg2rad(lat - LAT1);
var dLon = deg2rad(lon - LON1);
var a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(deg2rad(LAT1)) * Math.cos(deg2rad(LON1)) * Math.pow(Math.sin(dLon / 2), 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = EARTH_RADIUS * c; // Distance in km
return d;
}
function deg2rad(deg) {
return deg * (Math.PI / 180);
}
Or, if you don't require it to be dynamic like this, just grab the static values from the database and enter them here as the values for LAT1 and LON1.
E.g.
const LAT1 = 52.482799;
const LON1 = -2.000643;
Then just call your function like this...
var distance = getDistanceFromLatLonInKm(52.48463500000000, -1.980759000000000);
To do PAF lookups:
FreeMapTools
Google Maps. Just search your postcode, then grab the latitude/longitude from the URL.
I am using bing maps ajax v7 and for simplicity's sake let's say I have 10 PinPoints placed around the world. I'm trying to have the map zoom to the lowest level so that the closest PinPoint is still visible to the current location of the user. If someone could point me in the right direction I would greatly appreciate it.
$(document).ready(function () {
var windowHeight = $(window).height();
var windowWidth = $(window).width();
map = new Microsoft.Maps.Map(document.getElementById("mapDiv"), {
credentials: "myCredentials",
backgroundColor: "#A4C4ED",
zoom: 3,
height: windowHeight,
width: windowWidth
});
Microsoft.Maps.Events.addHandler(map, 'viewchange', hideInfoBox);
Microsoft.Maps.Events.addHandler(map, 'click', hideInfoBox);
//get users location and set view bound
var geoLocationProvider = new Microsoft.Maps.GeoLocationProvider(map);
var viewRectangle = Microsoft.Maps.LocationRect(geoLocationProvider.getCurrentPosition());
map.setView({ bounds: viewRectangle });
dataLayer = new Microsoft.Maps.EntityCollection();
map.entities.push(dataLayer);
var infoboxLayer = new Microsoft.Maps.EntityCollection();
map.entities.push(infoboxLayer);
//create initial infobox
infobox = new Microsoft.Maps.Infobox(new Microsoft.Maps.Location(0, 0), {
visible: false,
offset: new Microsoft.Maps.Point(0, 20)
});
infoboxLayer.push(infobox);
Microsoft.Maps.loadModule('Microsoft.Maps.Search', { callback: searchModuleLoaded });
});
I assume that you is one pin point and you have another 10 pin points located somewere on the map.
First you need to find the pinpoint that is closest to you.
You can use this function that expect two location objects that contains latitude and longitude.
oLocation1 = {latitude:0,longitude:0};
oLocation2 = {latitude:19,longitude:23};
function calcDistHaversine (oLocation1, oLocation2) {
var dLat = (oLocation2.latitude * Math.PI / 180 - oLocation1.latitude * Math.PI / 180);//*Math.PI*180;
var dLon = (oLocation2.longitude * Math.PI / 180 - oLocation1.longitude * Math.PI / 180);//*Math.PI*180;
var lat1 = oLocation1.latitude * Math.PI / 180;//*Math.PI*180;
var lat2 = oLocation2.latitude * Math.PI / 180;//*Math.PI*180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var distance = 6371 * c;
return distance;
};
As a result you will get the distance between those two location with respect yo earth curvature.
Now you have your location and closest pinpoint location.
Lets name them as your,their.
Next you need to create array that contains those two location converted to microsoft location objects.
var yourLocation= new Microsoft.Maps.Location(your.latitude, your.longitude);
var theirLocation= new Microsoft.Maps.Location(their.latitude, their.longitude);
var arr = [];
arr.push(yourLocation);
arr.push(theirLocation);
Now you use bing maps feature that gives you best zoom and pointing according to given locations.
var bestView = Microsoft.Maps.LocationRect.fromLocations(arrLocations);
Then you set the map view according to the best view that we found.
setTimeout((function () {
map.setView({ bounds: bestView });
}).bind(this), 1000);