I'm looking for a elegant solution that calculates the center between several latitude-longitude coordinates (for example, to simply center a map to the center of a google-maps polygon).
Table: locations:
id | city | latitude | longitude
-----------------------------------------------
1 | Berlin | 52.524268 | 13.406290
-----------------------------------------------
2 | London | 51.508129 | -0.1280050
-----------------------------------------------
3 | Hamburg | 53.551084 | 9.9936817
-----------------------------------------------
4 | Amsterdam | 52.370215 | 4.8951678
-----------------------------------------------
The current calculation:
function calculateCenter($array_locations) {
$minlat = false;
$minlng = false;
$maxlat = false;
$maxlng = false;
foreach ($array_locations as $geolocation) {
if ($minlat === false) { $minlat = $geolocation['lat']; } else { $minlat = ($geolocation['lat'] < $minlat) ? $geolocation['lat'] : $minlat; }
if ($maxlat === false) { $maxlat = $geolocation['lat']; } else { $maxlat = ($geolocation['lat'] > $maxlat) ? $geolocation['lat'] : $maxlat; }
if ($minlng === false) { $minlng = $geolocation['lon']; } else { $minlng = ($geolocation['lon'] < $minlng) ? $geolocation['lon'] : $minlng; }
if ($maxlng === false) { $maxlng = $geolocation['lon']; } else { $maxlng = ($geolocation['lon'] > $maxlng) ? $geolocation['lon'] : $maxlng; }
}
// Calculate the center
$lat = $maxlat - (($maxlat - $minlat) / 2);
$lon = $maxlng - (($maxlng - $minlng) / 2);
return array($lat, $lon);
}
As you are using Google Maps you can use getBounds() method and getCenter() method.
I have rearranged your coordinates to form a Convex Polygon (All the vertices point 'outwards', away from the center).The polygon is closed by having the first coordinate as the first and last value in polygonCoords array.
See jsfiddle
var map;
var polygon;
var bounds = new google.maps.LatLngBounds();
var i;
var myLatLng = new google.maps.LatLng(52.5,6.6);
var myOptions = {
zoom: 5,
center: myLatLng,
mapTypeId: google.maps.MapTypeId.TERRAIN
};
map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
var polygonCoords = [
new google.maps.LatLng(52.524268,13.406290),
new google.maps.LatLng(53.551084,9.9936817),
new google.maps.LatLng(51.508129,-0.1280050),
new google.maps.LatLng(52.370215,4.8951678),
new google.maps.LatLng(52.524268,13.406290)//Start & end point
];
polygon = new google.maps.Polygon({
paths: polygonCoords,
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 3,
fillColor: "#FF0000",
fillOpacity: 0.05
});
polygon.setMap(map);
for (i = 0; i < polygonCoords.length; i++) {
bounds.extend(polygonCoords[i]);
}
// The Center of the polygon
var latlng = bounds.getCenter();
var marker = new google.maps.Marker({
position: latlng,
map: map,
title:latlng.toString()
});
Averaging your latitudes and longitudes works in many cases, but have problems in a number of cases. Example, you have 2 cites, Tokyo (long = 140) and Seattle (long -122), your average longitude is 18, somewhere in Europe. You would expect something closer to the international date line, 180 degrees away.
The most direct, no problem method, is to average the vectors as if each originated from the earth's center.
Pseudo code, (assumes radians)
for each lat,long
// assume 1 radii from the earth's center.
// covert lat, long, and radii into x,y,z (spherical to cartesian coordinates)
r=1, theta=pi/2 - lat, phi=long
x = r*sin(theta)*cos(phi)
y = r*sin(theta)*sin(phi)
z = r*cos(theta)
N++;
// accumulate x,y,z
sum_x += x, etc.
// average x,y,z
avg_x = sum_x/N, etc.
// convert x,y,z back to spherical co-ordinates to get the lat/long center.
rho = sqrt(avg_x*avg_x + avg_y*avg_y + avg_z*avg_z)
lat = pi/2 - acos(avg_z/rho) // acos() results are 0 to pi
long = atan2(avg_y, avg_x) // 4 quadrant arctangent
[Edit Corrected spherical co-ordinates to cartesian]
Google uses a Mercator projection, treating the earth as an elongated cylinder. Thus the problem to to find the center of that projection.
For each lat/long pair, convert to map scaled x,y co-ordinates (using radians):
x = long
y = ln(tan(pi/4 + lat/2)) // Mercator projection
Then, for x & y, find the average of the minimum and maximum to get your center. Convert back to lat/long as follows
Pseudo code
center_long = average(minimum_x, maximum_x)
center_lat = (atan(exp(average(minimum_y, maximum_y))) - pi/4)*2
The calculation of the center longitude works fine were it not for the circular nature of the cylindric Earth projection. If the longitudes are in both the Eastern and Western hemispheres (some negative, some positive), than additional work may be needed.
Pseudo code
sort the longitudes into ascending order
for each longitude
difference = longitude(i-1) - longitude(i)
// for first, use longitude(0) - longitude(last)
if (difference < 0) add 2*pi (360 degrees)
Keep track of index of minimal difference
The pair with the minimal difference represents the pair that most tightly contains all longitudes.
Average this pair for the center longitude.
If this pair was index 0 & last, add pi (180 degrees)
OP 4 city result: (52.4 N, 7.0 E)
This is my second answer, for the first does not get the the crux of OP's post. Since it has some value it remains.
Related
This is for a game that I am considering.
I have a point that is noted as moving inside a (2D) circle from an arbitrary point, at an arbitrary direction and at a particular time. The point will bounce off the interior wall of the circle when it intersects.
For this example let's say the circle has a diameter of 100 kilometers with it's center at (0,0) and 10 hours ago the point was at location (20,30) with a heading of 40 degrees at a speed of 50kph .
What is the best way to determine where that point currently is and at what direction it's traveling?
I will be implementing this in PHP with the point and circle data stored in MySQL. Since it is a web page there will be no constantly running host process to keep things up to date and the data will need to be refreshed upon a page load.
I'm certainly not looking for anyone to write the code for me but am hoping someone can help me with a somewhat efficient way to approach this.
Your point-object will travel along what is called chords in geometry.
As the object hits the circle boundary, it will reflect from the circle's tangent at that point, and go along a next chord that has the same length. The next hit will be at the same angle (with the tangent at that hit point) as the previous hit, and so it will continue. At a constant speed, the time between hits will be a constant time.
Given the start time and the current time, one can calculate the number of chords that have been completed, and how much of the current chord has been completed. Calculating the position from that is easy when you know the previous and next hit positions. As these hit positions are at equal distances along the circle boundary, that is a matter of converting polar coordinates (an angle, and a distance of 1 radian) to Cartesian coordinates.
I will demonstrate this with JavaScript code. It will not be a great effort to wrap this in PHP:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var radius = canvas.height / 2 - 5;
var pixel = 1/radius;
// transform the canvas so that 0,0 is in the center of the canvas,
// and a unit circle would cover most of the height of the canvas:
context.setTransform(radius, 0, 0, radius, radius+2, radius+2);
// draw unit circle
context.beginPath();
context.arc(0, 0, 1, 0, 2 * Math.PI, false);
context.lineWidth = pixel;
context.strokeStyle = 'black';
context.stroke();
function drawPoint(point) {
// use a different color every 30 seconds:
context.fillStyle = Date.now() % 60000 > 30000 ? 'red' : 'blue';
context.fillRect(point.x-2*pixel, point.y-2*pixel, 4*pixel, 4*pixel);
}
function polarToCartesian(rad, dist) {
return {
x: Math.cos(rad) * dist,
y: Math.sin(rad) * dist
}
}
function pointBetween(a, b, fractionTravelled) {
return {
x: a.x + (b.x-a.x)*fractionTravelled,
y: a.y + (b.y-a.y)*fractionTravelled
}
}
// 4 parameters are needed:
var startRadians = 0; // distance along circle boundary from (0,1)
var hitAngle = Math.PI/2.931; // PI/2 would be head-on impact along diagonal
var speed = 0.4; // radians per second
var startTime = Date.now()/1000; // seconds
//
// Calculate some derived values which remain constant:
// - theta as used on https://en.wikipedia.org/wiki/Chord_%28geometry%29
// - chordSize formula comes from that wiki article.
var theta = 2 * hitAngle;
var chordSize = 2 * Math.sin(theta/2); // in radians
function drawCurrentPosition() {
// Note that this calculation does not look at the previous result,
// but uses the original parameters and time passed to calculate
// the objects current position.
var elapsedTime = Date.now()/1000 - startTime; // in secs
var distanceTravelled = speed * elapsedTime; // in radians
var chordsTravelled = distanceTravelled / chordSize; // in number of chords
var chordsTravelledComplete = Math.floor(chordsTravelled);
var fractionOnChord = chordsTravelled - chordsTravelledComplete; // 0<=f<1
var lastHitRadians = startRadians + chordsTravelledComplete * theta; // rad
var nextHitRadians = lastHitRadians + theta;
var lastHitPos = polarToCartesian(lastHitRadians, 1); // (x,y)
var nextHitPos = polarToCartesian(nextHitRadians, 1);
var currentPos = pointBetween(lastHitPos, nextHitPos, fractionOnChord);
drawPoint(currentPos);
}
// Demo: keep drawing the object's position every 0.1 second:
setInterval(drawCurrentPosition, 100);
<canvas id="myCanvas" width="200" height="200"></canvas>
Addendum: PHP code
Here is some code that could be useful for use in PHP. It uses the same calculations as the above JavaScript code, but does not keep running. Instead it first checks if there is a started game in the session scope, if not, it starts the "clock". At every request (reload of the page), the new position is calculated and printed on the page as an X,Y pair.
The coordinates are normalised, based on a unit circle (radius 1). The game parameters are hard-coded, but you could easily let them be passed via POST/GET parameters:
session_start(); // needed to persist game data for this user session
function getNewGame($startRadians, $hitAngle, $speed) {
$game = array();
$game["startTime"] = microtime(true);
$game["startRadians"] = $startRadians;
$game["theta"] = 2 * $hitAngle;
$game["chordSize"] = 2 * sin($hitAngle);
$game["speed"] = $speed;
return (object) $game;
}
function polarToCartesian($rad, $dist) {
return (object) array(
"x" => cos($rad) * $dist,
"y" => sin($rad) * $dist
);
}
function pointBetween($a, $b, $fractionTravelled) {
return (object) array(
"x" => $a->x + ($b->x-$a->x)*$fractionTravelled,
"y" => $a->y + ($b->y-$a->y)*$fractionTravelled
);
}
function getCurrentPosition($game) {
// Note that this calculation does not look at the previous result,
// but uses the original parameters and time passed to calculate
// the objects current position.
$elapsedTime = microtime(true) - $game->startTime; // in secs
$distanceTravelled = $game->speed * $elapsedTime; // in radians
$chordsTravelled = $distanceTravelled / $game->chordSize; //number of chords
$chordsTravelledComplete = floor($chordsTravelled);
$fractionOnChord = $chordsTravelled - $chordsTravelledComplete; // 0<=f<1
$lastHitRadians = $game->startRadians
+ $chordsTravelledComplete * $game->theta; // in radians on circle
$nextHitRadians = $lastHitRadians + $game->theta;
$lastHitPos = polarToCartesian($lastHitRadians, 1); // (x,y)
$nextHitPos = polarToCartesian($nextHitRadians, 1);
$currentPos = pointBetween($lastHitPos, $nextHitPos, $fractionOnChord);
return $currentPos;
}
// check if this is the first time the user loads this page:
if (!isset($_SESSION["game"])) {
// start game with some game parameters:
$_SESSION["game"] = getNewGame(0, pi()/2.931, 0.4);
}
// calculate the position based on game info and current time:
$pos = getCurrentPosition($_SESSION["game"]);
// print the result:
echo "Current position: {$pos->x}, {$pos->y}<br>";
Scenario
I am trying to generate 500 to 1000 random coordinates (lat,long) that lies within a circle with 1 kilometer radius where center point located at (5.418680, 100.327829). I am trying to code this in php but failed to do so as i have no idea what value should i provide for $radius.
$radius = ?;
$origin_x = 5.420525;
$origin_y = 100.319500;
$angle = deg2rad(mt_rand(0, 359));
$pointRadius = mt_rand(0, $radius);
$point[] = array(
'x' => $origin_x + ($pointRadius * cos($angle)),
'y' => $origin_y + ($pointRadius * sin($angle))
);
There is another approach came across my mind. Instead of generating point within a circle, i would like to generate point inside a boundary of square and then apply Haversine great circle distance formula to determine is the random generated point lies within the circle with 1KM radius.
Note: The generated point is fine to overlap each other.
Please advise, I need general idea of what approach should I take. Thanks in advance.
Create random points inside the bounds of the circle:
var bounds = circle.getBounds();
map.fitBounds(bounds);
var sw = bounds.getSouthWest();
var ne = bounds.getNorthEast();
for (var i = 0; i < 100; i++) {
// create a random point inside the bounds
var ptLat = Math.random() * (ne.lat() - sw.lat()) + sw.lat();
var ptLng = Math.random() * (ne.lng() - sw.lng()) + sw.lng();
var point = new google.maps.LatLng(ptLat,ptLng);
If they are within the circle itself keep them (add them to the map in this case), otherwise discard them:
if (google.maps.geometry.spherical.computeDistanceBetween(point,circle.getCenter()) < circle.getRadius()) {
createMarker(map, point,"marker "+i);
// break; if only need one point
} // else nothing.
Example using the Google Maps Javascript API v3:
var circle;
var infowindow = new google.maps.InfoWindow({});
function initialize() {
var map = new google.maps.Map(document.getElementById("map"), {
zoom: 4,
center: new google.maps.LatLng(22.7964, 79.8456),
mapTypeId: google.maps.MapTypeId.HYBRID
});
circle = new google.maps.Circle({
center: map.getCenter(),
radius: 1000, // meters
strokeColor: "#0000FF",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#0000FF",
fillOpacity: 0.26
});
circle.setMap(map);
var bounds = circle.getBounds();
map.fitBounds(bounds);
var sw = bounds.getSouthWest();
var ne = bounds.getNorthEast();
for (var i = 0; i < 100; i++) {
var ptLat = Math.random() * (ne.lat() - sw.lat()) + sw.lat();
var ptLng = Math.random() * (ne.lng() - sw.lng()) + sw.lng();
var point = new google.maps.LatLng(ptLat, ptLng);
if (google.maps.geometry.spherical.computeDistanceBetween(point, circle.getCenter()) < circle.getRadius()) {
createMarker(map, point, "marker " + i);
// break;
}
}
}
function createMarker(map, point, content) {
var marker = new google.maps.Marker({
position: point,
map: map
});
google.maps.event.addListener(marker, "click", function(evt) {
infowindow.setContent(content + "<br>" + marker.getPosition().toUrlValue(6));
infowindow.open(map, marker);
});
return marker;
}
google.maps.event.addDomListener(window, 'load', initialize);
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry"></script>
<div id="map" style="width: 530px; height: 500px">
</div>
fiddle
I'd do it like this:
Choose two independent x and y coordinate, uniformly from the interval [0,1].
If x² + y² > 1 your point lies outside the circle. Discard that sample and try again. Not transforming the square to the circle ensures equidistribution.
Turn coordinates in the circle to lat/lon on the sphere. If you want to preserve equidistribution, you'd use an area-preserving map projection here. But as the radius is way smaller than the radius of the earth, this doesn't matter much, so you might use some simpler projection instead unless you have to provide strong guarantees for equidistribution.
I guess there might be ways to avoid the discarding in step 2, but that would probably make things way more complicated, so for practical applications I'd stick with this.
i have array of lat long like :
var locationList = new Array( '23.2531803, 72.4774396', '22.808782, 70.823863', '24.3310019, 72.8516531', '22.3073095, 73.1810976', '22.3038945, 70.8021599', '23.850809, 72.114838' );
i want get nearest around 25 km 's lat long from first given array which is 23.2531803, 72.4774396
are there any calculation for nearest 25 km 's lat long from given array.
NOTE: for some reason i can not use sql query, because i get lat long from given address
Step 1: Calculate the distance between your start coordinate and every subcoordinate
Step 2: Pick the smallest distance
Step 3: Is it < 25 km? Success!
How to calculate distance between two coordinates:
function distance($lat1, $lon1, $lat2, $lon2) {
$D = 6371; // Earth Radius
$dLat = $lat2-$lat1;
$dLon = $lon2-$lon1;
$a = sin($dLat/2) * sin($dLat/2) +
sin($dLon/2) * sin($dLon/2) * cos($lat1) * cos($lat2);
$b = 2 * atan2(sqrt($a), sqrt(1-$a));
$c = 2 * atan2(sqrt($a), sqrt(1-$a));
return $D * $c;
}
This function thinks of the Earth as a perfect ball, which it is not - slight variations do apply, but are neglible at the 25km diameter you want.
First of all your data array is awfull. You need to make your data more computer readable. Then you can use Pythagorean theorem to calculate the distance from each location. You can save the first distance and index in an variable then replace it with new distance and index if it's shorter.
var closest = {id:0,dist:-1};
var myloc = [23.2531303, 72.4774398]
for(var i = 0; i < locationList.length; i++)
{
var long_lat = locationList[i].match(/([0-9.]+)+/)
long_lat[0] = parseFloat(long_lat[0]);
long_lat[1] = parseFloat(long_lat[1]);
var dist = Math.sqrt(Math.pow(long_lat[0] - myloc[0], 2)+Math.pow(long_lat[1] - myloc[1], 2)));
if((closest.dist == -1) || (closest.dist > dist))
{
closest.dist = dist;
closest.id = i;
}
}
When you like to calculate in JS you may use google.maps.geometry.spherical.computeDistanceBetween() to calculate the distance between the single points.
Store the results in an array, sort the array and when the smallest entry is <25km you got what you want.
lat/long outward distance approx 800ft on each side from center.
I have a central point of:
Latitude:38.6806353
Longitude:-96.5001029
I am trying to resolve a formula with php how to get the latitude / longitude to the NWSE corners approx 800ft outward from a center point.
So I would end up with a result similar to (but not correct):
Central:
38.6806353 -96.5001029
N: 38.6806353 -96.5001029
W: 38.6806353 -96.5001029
S: 38.6806353 -96.5001029
E: 38.6806353 -96.5001029
I've been trying to reverse engineer a few Javascripts that I found, but having absolutly no luck.
Is there a php Class available that does this math or similar that would require minor revisions? I can't find one thus far...
I found this function and I've been toying with it. I can get a nice array output like:
Array ( [0] => -112.35301079549 [1] => 36.105603064867 [2] => -112.25722008867 [3] => 36.105603064867 )
But I can't get a N W S E coordinates set to generate? Anyone know what I am doing wrong with this? I need 4 sets of values instead of two like:
N: 38.6806353 -96.5001029
W: 38.6806353 -96.5001029
S: 38.6806353 -96.5001029
E: 38.6806353 -96.5001029
<?php function getBoundingBox($lon_degrees,$lat_degrees,$distance_in_miles) {
$radius = 3963.1; // of earth in miles
// bearings
$due_north = 0;
$due_south = 180;
$due_east = 90;
$due_west = 270;
// convert latitude and longitude into radians
$lat_r = deg2rad($lat_degrees);
$lon_r = deg2rad($lon_degrees);
// find the northmost, southmost, eastmost and westmost corners $distance_in_miles away
// original formula from
// http://www.movable-type.co.uk/scripts/latlong.html
$northmost = asin(sin($lat_r) * cos($distance_in_miles/$radius) + cos($lat_r) * sin ($distance_in_miles/$radius) * cos($due_north));
$southmost = asin(sin($lat_r) * cos($distance_in_miles/$radius) + cos($lat_r) * sin ($distance_in_miles/$radius) * cos($due_south));
$eastmost = $lon_r + atan2(sin($due_east)*sin($distance_in_miles/$radius)*cos($lat_r),cos($distance_in_miles/$radius)-sin($lat_r)*sin($lat_r));
$westmost = $lon_r + atan2(sin($due_west)*sin($distance_in_miles/$radius)*cos($lat_r),cos($distance_in_miles/$radius)-sin($lat_r)*sin($lat_r));
$northmost = rad2deg($northmost);
$southmost = rad2deg($southmost);
$eastmost = rad2deg($eastmost);
$westmost = rad2deg($westmost);
// sort the lat and long so that we can use them for a between query
if ($northmost > $southmost) {
$lat1 = $southmost;
$lat2 = $northmost;
} else {
$lat1 = $northmost;
$lat2 = $southmost;
}
if ($eastmost > $westmost) {
$lon1 = $westmost;
$lon2 = $eastmost;
} else {
$lon1 = $eastmost;
$lon2 = $westmost;
}
return array($lon1,$lat1,$lon2,$lat1);
}
?>
I noticed that $due_north, $due_south, etc are in degrees but you have sin($due_east) without a conversion of $due_east to radians.
For 90 deg bearing (east), θ= pi/2 (90 deg), d/R will be 800ft / 5280 ft/mi / 3959 miles (radius of earth in miles), lat1/lon1 are your center point lat/lon in radians.
east_lat = asin(sin(lat1)*cos(d/R) + cos(lat1)*sin(d/R)*cos(θ))
east_lon = lon1 + atan2(sin(θ)*sin(d/R)*cos(lat1), cos(d/R)−sin(lat1)*sin(lat2))
Convert back to degrees then repeat for the other 3 corners.
The comment in your code references the website:
http://www.movable-type.co.uk/scripts/latlong.html
Go to the section Destination point given distance and bearing from start point. You can check your calculations with the calculator on the web page.
The bounding box must satisfy some conditions you didn't mentioned. For example Google Maps uses a z curve and 21 zoom level to subdivide the map into smaller tiles. I don't know how big is a single tile in distance but I use the script from John Brafford to convert from geo coordinate to WGS84 Datum. There is also a method to return the bounding box of a tile. You can find the script here: http://bafford.com/software/aggregate-map-tools/GlobalMapTiles.php.txt.
I have the Lat/Long value of New York City, NY; 40.7560540,-73.9869510 and a flat image of the earth, 1000px × 446px.
I would like to be able to convert, using Javascript, the Lat/Long to an X,Y coordinate where the point would reflect the location.
So the X,Y coordinate form the Top-Left corner of the image would be; 289, 111
Things to note:
don't worry about issues of what projection to use, make your own
assumption or go with what you know
might work
X,Y can be form any corner of the image
Bonus points for
the same solution in PHP (but I
really need the JS)
The projection you use is going to change everything, but this will work assuming a Mercator projection:
<html>
<head>
<script language="Javascript">
var dot_size = 3;
var longitude_shift = 55; // number of pixels your map's prime meridian is off-center.
var x_pos = 54;
var y_pos = 19;
var map_width = 430;
var map_height = 332;
var half_dot = Math.floor(dot_size / 2);
function draw_point(x, y) {
dot = '<div style="position:absolute;width:' + dot_size + 'px;height:' + dot_size + 'px;top:' + y + 'px;left:' + x + 'px;background:#00ff00"></div>';
document.body.innerHTML += dot;
}
function plot_point(lat, lng) {
// Mercator projection
// longitude: just scale and shift
x = (map_width * (180 + lng) / 360) % map_width + longitude_shift;
// latitude: using the Mercator projection
lat = lat * Math.PI / 180; // convert from degrees to radians
y = Math.log(Math.tan((lat/2) + (Math.PI/4))); // do the Mercator projection (w/ equator of 2pi units)
y = (map_height / 2) - (map_width * y / (2 * Math.PI)) + y_pos; // fit it to our map
x -= x_pos;
y -= y_pos;
draw_point(x - half_dot, y - half_dot);
}
</script>
</head>
<body onload="plot_point(40.756, -73.986)">
<!-- image found at http://www.math.ubc.ca/~israel/m103/mercator.png -->
<img src="mercator.png" style="position:absolute;top:0px;left:0px">
</body>
</html>
A basic conversion function in js would be:
MAP_WIDTH = 1000;
MAP_HEIGHT = 446;
function convert(lat, lon){
var y = ((-1 * lat) + 90) * (MAP_HEIGHT / 180);
var x = (lon + 180) * (MAP_WIDTH / 360);
return {x:x,y:y};
}
This will return the number of pixels from upper left.
This function assumes the following:
That your image is properly aligned
with the upper left corner (0,0)
aligning with 90* North by 180*
West.
That your coords are signed with N being -, S being +, W being - and E being +
If you have a picture of the whole earth, the projection does always matter. But maybe I just don't understand your question.