I was wondering if there is any way of getting the dimensions (in degrees or kilometers) of a google static map image, given the zoom level and the size of the map in pixels.
I have seen a formula to get the longitude in degrees:
(widthInPixels/256)*(360/pow(2,zoomLevel));
And this is pretty accurate. However the ratio between km and degrees changes depending on how close you are to the poles or the equator, so this formula won't work (even if I substitute the 360 for 180)
Has anyone got a formula or any tips for this?
You can use MKMetersBetweenMapPoints to find dimensions in km.
//Let's say region is represents the piece of map
MKMapPoint pWest = MKMapPointForCoordinate( CLLocationCoordinate2DMake(region.center.latitude, region.center.longitude-region.span.longitudeDelta/2.0));
MKMapPoint pEast = MKMapPointForCoordinate( CLLocationCoordinate2DMake(region.center.latitude, region.center.longitude+region.span.longitudeDelta/2.0));
CLLocationDistance distW = MKMetersBetweenMapPoints(pWest, pEast)/1000.0;//map width in km
MKMapPoint pNorth = MKMapPointForCoordinate( CLLocationCoordinate2DMake(region.center.latitude+region.span.latitudeDelta/2.0, region.center.longitude));
MKMapPoint pSouth = MKMapPointForCoordinate( CLLocationCoordinate2DMake(region.center.latitude-region.span.latitudeDelta/2.0, region.center.longitude));
CLLocationDistance distH = MKMetersBetweenMapPoints(pNorth, pSouth)/1000.0;;//map height in km
Related
I'm working on a project that hopes to convert Google maps static images into larger, stitched together maps.
I have created an algorithm that given a starting and ending, latitude and longitude, it will fetch the Google Maps Static image for that lat,lng pair, then increment lng by the pixel width of the fetched image, in this case 700px, using an algorithm for determining pixel to longitude ratio using a couple of formulas.
The formulas seem to be working perfectly for latitude, as you can see in my attachment, the vertically tiling images line up almost perfectly.
Yet horizontally, the longitude increment seems to be off by a factor of 2, or slightly more.
To increment latitude, I use a constant metres to latitude ratio
const metresToLatRatio = 0.001 / 111 // Get lat value from metres
const metresPerPxLat = getMetresPerPxLng(currentLat, zoom)
const latIncrement = metresToLatRatio * (metresPerPxLat * 700)
But for longitude, I replaces the metresToLngRatio constant with a variable derived from this formula
const metresToLngRatio = getMetresToLngRatio(currentLat)
const metresPerPxLng = getMetresPerPxLng(currentLat, zoom)
lngIncrement = (metresToLngRatio * (metresPerPxLng * 700))
Where getMetresToLngRatio and getMetresPerPxLng are
function getMetresPerPxLng(lat, zoom = 19, scale = 2) {
return Math.abs((156543.03392 * Math.cos(lat * Math.PI / 180) / (2 ** zoom)) / scale)
}
function getMetresToLngRatio(lat) {
return 1 / Math.abs(111111 * Math.cos(lat))
}
The getMetresPerPxLng function is derived from this post and this answer: https://groups.google.com/g/google-maps-js-api-v3/c/hDRO4oHVSeM / https://gis.stackexchange.com/questions/7430/what-ratio-scales-do-google-maps-zoom-levels-correspond-to
What I noticed is that if I change getMetresToLng Ratio to return (1 / Math.abs(111111 * Math.cos(lat))) * 2, the stitching appears more accurate, only off by a few tens of pixels, instead of almost half the image.
With * 2
Without * 2
Am I doing something wrong with my longitude equation? I know that 111111*cos(lat) is a rough estimate, so I'm wondering if there's a more accurate formula
You are using the wrong earth radius. From the answer in https://gis.stackexchange.com/questions/7430/what-ratio-scales-do-google-maps-zoom-levels-correspond-to, you need to update the equation using a radius of 6371010 instead of 6378137 meters.
Please note that stitching tiles together for some other use is likely a violation of the Terms of Service.
I have a rectangular polygon and I want to extend the boundaries by 10 km for example.
How would I do that ?
I could use extend method, but how Do I find the distance of 10 km in lat lng ?
So far I have :
bounds = new google.maps.LatLngBounds();
pt = new google.maps.LatLng(lat,lng);
bounds.extend(pt)
It depends on how exact an answer you need.
You could use the following approximation:
Latitude: 1 deg = 110.57 km; Longitude: 1 deg = 111.320 km source: http://en.wikipedia.org/wiki/Latitude
For a more exact formula, you need to check http://www.movable-type.co.uk/scripts/latlong.html . It has various formulas and also some code. You are looking for the section called 'Destination point given distance and bearing from start point'
It depends where you are looking at but a longitude is 111km and a latitude 110km:http://en.m.wikipedia.org/wiki/Latitude.
I am trying to request an image from the Google Static Maps API with the borders of the map specified by a pair of latitude and longitude coordinates. I've tried centering on the center of the two coordinates, but there doesn't seem to be any parameter for doing this with the Static API. Does anyone know how to do this?
Note: this is for a desktop application, and I am not using the Javascript API.
The thing is that you cannot base the request on the map's corners because 1. zoom levels are discrete values and 2. the amount of latitude that a pixel represents varies with latitude. So, to display 2 degrees you'll need a given map height near the equator and a different height, (greater), near the poles. Are you willing to display maps of different heights in order to fit always 2 degrees?
If so, you can use the MercatorProjection object from my other post, and use the following function to calculate the necessary map size:
<script src="MercatorProjection.js"></script>
function getMapSize(center,zoom){
var proj = new MercatorProjection();
var scale = Math.pow(2,zoom);
var centerPx = proj.fromLatLngToPoint(center);
var SW = new google.maps.LatLng(center.lat()-1,center.lng()-1);
var southzWestPx = proj.fromLatLngToPoint(SW);
var NE = new google.maps.LatLng(center.lat()+1,center.lng()+1);
var northEastPx = proj.fromLatLngToPoint(NE);
// Then you can calculate the necessary width and height of the map:
var mapWidth = Math.round((northEastPx.x - southzWestPx.x) * scale);
var mapHeight = Math.round((southzWestPx.y - northEastPx.y) * scale);
}
With center = new google.maps.LatLng(49.141404, -121.960988) and zoom = 7 you get that you need a map of (W x H) 182 x 278 pixels in order to display 2 x 2 degrees.
Ok pretty self explanatory. I'm using google maps and I'm trying to find out if a lat,long point is within a circle of radius say x (x is chosen by the user).
Bounding box will not work for this. I have already tried using the following code:
distlatLng = new google.maps.LatLng(dist.latlng[0],dist.latlng[1]);
var latLngBounds = circle.getBounds();
if(latLngBounds.contains(distlatLng)){
dropPins(distlatLng,dist.f_addr);
}
This still results in markers being places outside the circle.
I'm guess this is some simple maths requiring the calculation of the curvature or an area but I'm not sure where to begin. Any suggestions?
Unfortunately Pythagoras is no help on a sphere. Thus Stuart Beard's answer is incorrect; longitude differences don't have a fixed ratio to metres but depend on the latitude.
The correct way is to use the formula for great circle distances. A good approximation, assuming a spherical earth, is this (in C++):
/** Find the great-circle distance in metres, assuming a spherical earth, between two lat-long points in degrees. */
inline double GreatCircleDistanceInMeters(double aLong1,double aLat1,double aLong2,double aLat2)
{
aLong1 *= KDegreesToRadiansDouble;
aLat1 *= KDegreesToRadiansDouble;
aLong2 *= KDegreesToRadiansDouble;
aLat2 *= KDegreesToRadiansDouble;
double cos_angle = sin(aLat1) * sin(aLat2) + cos(aLat1) * cos(aLat2) * cos(aLong2 - aLong1);
/*
Inaccurate trig functions can cause cos_angle to be a tiny amount
greater than 1 if the two positions are very close. That in turn causes
acos to give a domain error and return the special floating point value
-1.#IND000000000000, meaning 'indefinite'. Observed on VS2008 on 64-bit Windows.
*/
if (cos_angle >= 1)
return 0;
double angle = acos(cos_angle);
return angle * KEquatorialRadiusInMetres;
}
where
const double KPiDouble = 3.141592654;
const double KDegreesToRadiansDouble = KPiDouble / 180.0;
and
/**
A constant to convert radians to metres for the Mercator and other projections.
It is the semi-major axis (equatorial radius) used by the WGS 84 datum (see http://en.wikipedia.org/wiki/WGS84).
*/
const int32 KEquatorialRadiusInMetres = 6378137;
Use Google Maps API geometry library to calculate distance between circle's center and your marker, and then compare it with your radius.
var pointIsInsideCircle = google.maps.geometry.spherical.computeDistanceBetween(circle.getCenter(), point) <= circle.getRadius();
It's very simple. You just have to calculate distance between centre and given point and compare it to radius. You can Get Help to calculate distance between two lat lang from here
The following code works for me: my marker cannot be dragged outside the circle, instead it just hangs at its edge (in any direction) and the last valid position is preserved.
The function is the eventhandler for the markers 'drag' event.
_markerDragged : function() {
var latLng = this.marker.getPosition();
var center = this.circle.getCenter();
var radius = this.circle.getRadius();
if (this.circleBounds.contains(latLng) &&
(google.maps.geometry.spherical.computeDistanceBetween(latLng, center) <= radius)) {
this.lastMarkerPos = latLng;
this._geocodePosition(latLng);
} else {
// Prevent dragging marker outside circle
// see (comments of) http://unserkaiser.com/code/google-maps-marker-check-if-in-circle/
// see http://www.mvjantzen.com/blog/?p=3190 and source code of http://mvjantzen.com/cabi/trips4q2012.html
this.marker.setPosition(this.lastMarkerPos);
}
},
Thanks to http://unserkaiser.com/code/google-maps-marker-check-if-in-circle/
and http://www.mvjantzen.com/blog/?p=3190 .
I've been a bit silly really. Thinking about it we can use Pythagorus' theorem.
We have a maximum distance away from a point (X miles), and two latitudes and two longitudes. If we form a triangle using these then we can solve for the distance from the point.
So say we know point1 with coordinates lat1,lng1 is the center of the circle and point2 with coordinates lat2,lng2 is the point we are trying to decide is in the circle or not.
We form a right angled triangle using a point determined by point1 and point2. This, point3 would have coordinates lat1,lng2 or lat2,lng1 (it doesn't matter which). We then calculate the differences (or if you prefer) distances - latDiff = lat2-lat1 and lngDiff = lng2-lng1
we then calculate the distance from the center using Pythagorus - dist=sqrt(lngDiff^2+latDiff^2).
We have to translate everything into meters so that it works correctly with google maps so miles are multiplied by 1609 (approx) and degrees of latitude/longitude by 111000 (approx). This isn't exactly accurate but it does an adequate job.
Hope that all makes sense.
I'm constructing a geolocation based application and I'm trying to figure out a way to make my application realise when a user is facing the direction of the given location (a particular long / lat co-ord). I've got the math figured, I just have the triangle to construct.
//UPDATE
So I've figured out a good bit of this...
Below is a method which takes in a long / lat value and attempts to compute a triangle finding a point 700 meters away and one to its left + right. It'd then use these to construct the triangle. It computes the correct longitude but the latitude ends up somewhere off the coast of east Africa. (I'm in Ireland!).
public void drawtri(double currlng,double currlat, double bearing){
bearing = (bearing < 0 ? -bearing : bearing);
System.out.println("RUNNING THE DRAW TRIANGLE METHOD!!!!!");
System.out.println("CURRENT LNG" + currlng);
System.out.println("CURRENT LAT" + currlat);
System.out.println("CURRENT BEARING" + bearing);
//Find point X(x,y)
double distance = 0.7; //700 meters.
double R = 6371.0; //The radius of the earth.
//Finding X's y value.
Math.toRadians(currlng);
Math.toRadians(currlat);
Math.toRadians(bearing);
distance = distance/R;
Global.Alat = Math.asin(Math.sin(currlat)*Math.cos(distance)+
Math.cos(currlat)*Math.sin(distance)*Math.cos(bearing));
System.out.println("CURRENT ALAT!!: " + Global.Alat);
//Finding X's x value.
Global.Alng = currlng + Math.atan2(Math.sin(bearing)*Math.sin(distance)
*Math.cos(currlat), Math.cos(distance)-Math.sin(currlat)*Math.sin(Global.Alat));
Math.toDegrees(Global.Alat);
Math.toDegrees(Global.Alng);
//Co-ord of Point B(x,y)
// Note: Lng = X axis, Lat = Y axis.
Global.Blat = Global.Alat+ 00.007931;
Global.Blng = Global.Alng;
//Co-ord of Point C(x,y)
Global.Clat = Global.Alat - 00.007931;
Global.Clng = Global.Alng;
}
From debugging I've determined the problem lies with the computation of the latitude done here..
Global.Alat = Math.asin(Math.sin(currlat)*Math.cos(distance)+
Math.cos(currlat)*Math.sin(distance)*Math.cos(bearing));
I have no idea why though and don't know how to fix it. I got the formula from this site..
http://www.movable-type.co.uk/scripts/latlong.html
It appears correct and I've tested multiple things...
I've tried converting to Radians then post computations back to degrees, etc. etc.
Anyone got any ideas how to fix this method so that it will map the triangle ONLY 700 meters in from my current location in the direction that I am facing?
Thanks,
for long distance: http://www.dtcenter.org/met/users/docs/write_ups/gc_simple.pdf
but for short distance You can try simple 2d math to simulate "classic" compass using: http://en.wikipedia.org/wiki/Compass#Using_a_compass. For example you can get pixel coordinates from points A and B and find angle between line connecting those points and vertical line.
also You probably should consider magnetic declination: http://www.ngdc.noaa.gov/geomagmodels/Declination.jsp
//edit:
I was trying to give intuitive solution. However calculating screen coordinates from long/lat wouldn't be easy so You probably should use formulas provided in links.
Maybe its because I don't know javascript, but don't you have to do something like
currlat = Math.toRadians(currlat);
to actually change the currlat value to be radians.
Problem was no matter what I piped in java would output in Radians, Trick was to change everything to Radians and then output came in radians, convert to degrees.