Radius of "viewable" region in google maps v3 - google-maps

I need to know how to retrieve the radius of the viewable zoom level in google maps API v3.
For example, if I am at zoom level 3, and depending on the users screen size (lets just say 400x400 viewable region) how do I get the "radius" circle of the viewable area.
Alternatively I'm currently using map.fitBounds() for all the points I've added to the map so really all I NEED is the radius of all the bounds. What I want is something like "20 miles" that I can feed into my database app.

The radius would equal the distance from the center of the bounds to one of the bound's corners. Using the Great Circle Distance Formula from the calculations from this page, I came up with the following:
var bounds = map.getBounds();
var center = bounds.getCenter();
var ne = bounds.getNorthEast();
// r = radius of the earth in statute miles
var r = 3963.0;
// Convert lat or lng from decimal degrees into radians (divide by 57.2958)
var lat1 = center.lat() / 57.2958;
var lon1 = center.lng() / 57.2958;
var lat2 = ne.lat() / 57.2958;
var lon2 = ne.lng() / 57.2958;
// distance = circle radius from center to Northeast corner of bounds
var dis = r * Math.acos(Math.sin(lat1) * Math.sin(lat2) +
Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1));
After playing some more with this, I've noticed that map.getBounds() will contain the full map viewport. But if your LatLngBounds is built by extending to include LatLng points, and then you issue a map.fitBounds(bounds), the api increases the map's viewport a bit so that the bounds "box" has some padding.
If you use the map's current viewport, the radius from the center to the corner of the viewport might be a longer radius than you want. Maybe the distance from the viewport center to the middle of the furthest viewport edge. (If the map isn't a perfect square)

Refactored version of Eric's answer above using google.maps.geometry.spherical namespace (make sure you loaded Geometry library to make this work).
var bounds = map.getBounds();
var center = map.getCenter();
if (bounds && center) {
var ne = bounds.getNorthEast();
// Calculate radius (in meters).
var radius = google.maps.geometry.spherical.computeDistanceBetween(center, ne);
}

I also refactored Eric's answer, mine is as a standalone function, and am returning the result in meters (as needed by the Places API search.
function getBoundsRadius(bounds){
// r = radius of the earth in km
var r = 6378.8
// degrees to radians (divide by 57.2958)
var ne_lat = bounds.getNorthEast().lat() / 57.2958
var ne_lng = bounds.getNorthEast().lng() / 57.2958
var c_lat = bounds.getCenter().lat() / 57.2958
var c_lng = bounds.getCenter().lng() / 57.2958
// distance = circle radius from center to Northeast corner of bounds
var r_km = r * Math.acos(
Math.sin(c_lat) * Math.sin(ne_lat) +
Math.cos(c_lat) * Math.cos(ne_lat) * Math.cos(ne_lng - c_lng)
)
return r_km *1000 // radius in meters
}

I believe Bounds have the getNorthEast() and getSouthWest() methods, but this would give you a rectangle (which bounds really is) and you could than calculate the distance between those two, etc.
To calculate the circle out of that rectandle might be a bit of work...
Maybe this will help: http://code.google.com/apis/maps/articles/mvcfun.html
and the example: http://code.google.com/apis/maps/articles/mvcfun/step6.html

Related

Find near by places of specific angle - consider only half circle of front not the back in round circle query

I know how to find near by locations from MySQL database using Round circle radius query and I have given answer of the same on another SO question as well here.
But I wish to do some different thing from this now. Here what happened is the query returns the result from center of the point which includes entire circle of radius. I wish to get points only of the half circle. I know this is possible and its all mathematical calculation and I am little weak in it that's why asking for experts help.
See this image, it will give very clear idea.
As you can see in the image only front part location is needed, not the back side part. Need to ignore the back side part. Also I have divided the radius in different color to make them appear as zones - like red is zone1, orange is zone 2 and yellow is zone 3. This are virtual zones to filter the data (locations).
All suggestions are welcome.
You can plot points inside a segment using Haversine/Spherical Law of Cosines for the radius. Then use pointInPolygon() to find only those within segment. You will also require function to create polygon.
polySides = number of sides in polygon
pointLatArr = Lat of point in in polygon array
pointLngArr = Lng of point in in polygon array
dat.lat = Lat from Haversine results
dat.lng = Lng from Haversine results
if (pointInPolygonpolySides,pointLatArr,pointLngArr,dat.lat,dat.lng)){
var latlng = new google.maps.LatLng(dat.lat,dat.lng);
addMarker(latlng,dat.name);
bounds.extend(latlng);
}
function pointInPolygon(polySides,polyX,polyY,x,y) {
var j = polySides-1 ;
oddNodes = 0;
for (i=0; i<polySides; i++) {
if (polyY[i]<y && polyY[j]>=y || polyY[j]<y && polyY[i]>=y) {
if (polyX[i]+(y-polyY[i])/(polyY[j]-polyY[i])*(polyX[j]-polyX[i])<x) {
oddNodes=!oddNodes;
}
}
j=i; }
return oddNodes;
}
Function for segment polygon
function drawSegment(start,end,radius) {
var d2r = Math.PI / 180;
pointLatArr = new Array();
pointLngArr = new Array();
polyLatLngs = new Array(); // latLngs of polygon
var polyLat = (radius /3963.189) / d2r; // miles
var polyLng = polyLat / Math.cos(center.lat() * d2r);
var centerLatLng = new google.maps.LatLng(center.lat(),center.lng());//Center to start
pointLatArr.push(center.lat());
pointLngArr.push(center.lng());
polyLatLngs.push(centerLatLng);
bounds.extend(centerLatLng);
// Create polygon points (extra point to close polygon)
for (var i = start; i < end; i++) {
// Convert degrees to radians
var theta = i * d2r;
var pointLat = center.lat() + (polyLat * Math.sin(theta));
var pointLng = center.lng() + (polyLng * Math.cos(theta));
var pointLatLng = new google.maps.LatLng(
parseFloat(pointLat), parseFloat(pointLng));
polyLatLngs.push(pointLatLng);
pointLatArr.push(pointLat);
pointLngArr.push(pointLng);
bounds.extend(pointLatLng);
}
var centerLatLng = new google.maps.LatLng(center.lat(),center.lng());//End to center
polyLatLngs.push(centerLatLng);
pointLatArr.push(center.lat());
pointLngArr.push(center.lng());
polySides = polyLatLngs.length;
Map using this technique
}
See Demo

Convert (x, y) pixel coordinates in google.maps.Point

I am trying to find out the LatLng based on my x,y pixel coordinates (and of course map options, such as zoom and center).
In order to do so, I posted another question and someone came up with this solution, from this post:
/**
* #param {google.maps.Map} map
* #param {google.maps.Point} point
* #param {int} z
* #return {google.maps.LatLng}
*/
var pointToLatlng = function(map, point, z){
var scale = Math.pow(2, z);
var normalizedPoint = new google.maps.Point(point.x / scale, point.y / scale);
var latlng = map.getProjection().fromPointToLatLng(normalizedPoint);
return latlng;
};
As you can notice from the code sample, the function uses as argument a google.maps.Point, therefore I need to convert my screen pixel coordinate into a google.maps.Point and I have no clue how, since their documentation of the API is not quite verbose...
Can you please help me? Or am I missing something on the way?
After some research and some fails I came up with a solution.
Following the documentation from this link I found out that the google Points are computed in the range of x:[0-256], y:[0-256] (a tile being 256x256 pixels) and the (0,0) point being the leftmost point of the map (check the link for more information).
However, my approach is as it follows:
having the x and y coordinates (which are coordinates on the screen - on the map) I computed the percentage where the x and y coordinates were placed in response to the div containing the map (in my case, the hole window)
computed the NortEast and SouthWest LatLng bounds of the (visible) map
converted the bounds in google Points
computed the new lat and lng, in google points, with the help of the boundaries and percentage of x and y
// retrieve the lat lng for the far extremities of the (visible) map
var latLngBounds = map.getBounds();
var neBound = latLngBounds.getNorthEast();
var swBound = latLngBounds.getSouthWest();
// convert the bounds in pixels
var neBoundInPx = map.getProjection().fromLatLngToPoint(neBound);
var swBoundInPx = map.getProjection().fromLatLngToPoint(swBound);
// compute the percent of x and y coordinates related to the div containing the map; in my case the screen
var procX = x/window.innerWidth;
var procY = y/window.innerHeight;
// compute new coordinates in pixels for lat and lng;
// for lng : subtract from the right edge of the container the left edge,
// multiply it by the percentage where the x coordinate was on the screen
// related to the container in which the map is placed and add back the left boundary
// you should now have the Lng coordinate in pixels
// do the same for lat
var newLngInPx = (neBoundInPx.x - swBoundInPx.x) * procX + swBoundInPx.x;
var newLatInPx = (swBoundInPx.y - neBoundInPx.y) * procY + neBoundInPx.y;
var finalResult = new google.maps.Point(newLngInPx, newLatInPx);
You could use an overlay that draws nothing in order to get at the super valuable function, fromContainerPixelToLatLng()
var overlay = new google.maps.OverlayView();
overlay.draw = function() {}; // empty function required
overlay.setMap(map);
var coordinates = overlay.getProjection().fromContainerPixelToLatLng(
new google.maps.Point(x, y)
);
console.log(coordinates.lat + ", " + coordinates.lng);

Is it possible to center a map at a fixed position (lat lng) - 100px?

I mean, I'd like to center a map to a fixed position :
map.setCenter(markerCliccato.getPosition());
and less (Y-axis) 100px down. Is it possible? Or I need the Lat/Lng of this "virtual" point?
This is because I have a InfoWindow very height, and I need to put it 100px from top of the map! Can't use "margin" on infowindow because can make troubles with other marker in the map.
Example
I have this point as Lat/Lng : https://maps.google.com/?q=54.572062,-3.038818
But I want to center the map at https://maps.google.com/?q=54.486793,-3.042046 (which is more or less 100px down of my point, in the map)
ok, i need to post pictures :)
this is before the click
and this is after
there is no gray area, the map is all rendered.
also, to see the marker you must allow the site to track your location.
function offsetCenter(latlng, offsetx, offsety) {
var scale = Math.pow(2, map.getZoom());
var nw = new google.maps.LatLng(
map.getBounds().getNorthEast().lat(), map.getBounds().getSouthWest().lng());
var worldCoordinateCenter = map.getProjection().fromLatLngToPoint(latlng);
var pixelOffset = new google.maps.Point((offsetx / scale) || 0, (offsety / scale) || 0)
var worldCoordinateNewCenter = new google.maps.Point(
worldCoordinateCenter.x - pixelOffset.x, worldCoordinateCenter.y + pixelOffset.y);
var newCenter = map.getProjection().fromPointToLatLng(worldCoordinateNewCenter);
map.setCenter(newCenter);
}
see it working here: http://jsfiddle.net/RASG/vA4eQ/
tested with FF 15, IE 9 and Chrome 21

Can I reset latlng coordinates for a custom map type?

I have made my own map using the google maps api. It is a custom map type based on a really big image.
Is there anyway I can reset the latlng coordinates so they are relative to my image and not the earth's default latlng coordinates?
I.E. Make the top left corner of the image (0,0).
Thanks.
Yes, it's possible with your own custom projection definitions. I modified this lunar surface example with a simple projection that takes the top-left corner to be (0,0) and bottom-right, (50,50). You will most likely need to change the values to work with your app.
I played around a bit with the limits and there looks to still be a hard limit of +/- 90 for latitudes and +/- 180 for longitudes. The longitude will wrap around at 180 degrees. That is, it appears to be impossible to define a custom coordinate of, say, (100, 200).
See the modified lunar surface demo here, click anywhere to display the custom latLng in an alert:
http://jsfiddle.net/yV6xv/79/
function percentProjection() {};
percentProjection.prototype.fromLatLngToPoint = function(latlng) {
// pixel size of tile at zoom 0
var max = 256;
// define bottom-right corner as (50, 50)
var x = max * latlng.lng() / 50;
var y = max * latlng.lat() / 50;
return new google.maps.Point(x, y);
};
percentProjection.prototype.fromPointToLatLng = function(pixel) {
// inverse conversion
var max = 256;
var lng = pixel.x / max * 50;
var lat = pixel.y / max * 50;
return new google.maps.LatLng(lat, lng);
};

how to get the real bounds with google maps when fully zoomed out

I have a map that shows location points based on the gbounds of the map. For example, any time the map is moved/zoomed, i find the bounds and query for locations that fall within those bounds. Unfortunately I'm unable to display all my locations when fully zoomed out. Reason being, gmaps reports the min/max long as whatever is at the edge of the map, but if you zoom out enough, you can get a longitudinal range that excludes visible locations.
For instance, if you zoom your map so that you see NorthAmerica twice, on the far left and far right. The min/max long are around: -36.5625 to 170.15625. But this almost completely excludes NorthAmerica which lies in the -180 to -60 range. Obviously this is bothersome as you can actually see the continent NorthAmerica (twice), but when I query my for locations in the range from google maps, NorthAmerica isn't returned.
My code for finding the min/max long is:
bounds = gmap.getBounds();
min_lat = bounds.getSouthWest().lat()
max_lat = bounds.getNorthEast().lat()
Has anyone encountered this and can anyone suggest a workaround? Off the top of my head I can only thing of a hack: to check the zoom level and hardcode the min/max lats to -180/180 if necessary, which is definitely unacceptable.
This isn't the most elegant solution, but I tested it and it does work. Check to see if the current bounds contains the bounds of the full map. Google doesn't actually include the north and south pole, so I had to tweak it to go almost all the way north and south.
bounds = gmap.getBounds();
var fullEarth = new GLatLngBounds(new GLatLng(-85, 180), new GLatLng(85, -180)))
var isFull = bounds.containsBounds(fullEarth);
min_lat = isFull ? -90 : bounds.getSouthWest().lat()
max_lat = isFull ? 90 : bounds.getNorthEast().lat()
min_lng = isFull ? 180 : bounds.getSouthWest().lng()
max_lng = isFull ? -180 : bounds.getNorthEast().lng()
Can not even imagine how this HUGE bug is still there !!!
isFullLng and toSpan functions are buggy and you can not rely on them.
My bug workaround for this:
var zoom = map.getZoom();
var bounds = map.getBounds();
var span = bounds.toSpan();
var isFull = span.lng() > 277 || zoom < 2;
Here is my solution, I only need isFullLng() which was apparently removed from API v3:
isFullLng: function() {
var scale = Math.pow(2, map.getZoom()),
bounds = map.getBounds(),
ne = bounds.getNorthEast(),
sw = bounds.getSouthWest(),
lat = (ne.lat() <= 0 && sw.lat() >= 0) || (ne.lat() >= 0 && sw.lat() <= 0) ? 0 : Math.min(Math.abs(ne.lat()), Math.abs(sw.lat())), // closest latitude to equator
deg1 = new google.maps.LatLng(lat, 0),
deg2 = new google.maps.LatLng(lat, 1),
coord1 = map.getProjection().fromLatLngToPoint(deg1),
coord2 = map.getProjection().fromLatLngToPoint(deg2);
// distance for one long degree in pixels for this zoom level
var pixelsPerLonDegree = (coord2.x - coord1.x) * scale;
// width of map's holder should be <= 360 (deg) * pixelsPerLonDegree if full map is displayed
var width = $this.width(); // width of map's holder div
return pixelsPerLonDegree * 360 <= width;
},
isFullLat: function() {
var bounds = map.getBounds(),
ne = bounds.getNorthEast(),
sw = bounds.getSouthWest(),
maxLat = 85; // max lat degree
return ne.lat() >= maxLat && sw.lat() <= -maxLat;
},
So isFull could then be:
isFullLng() && isFullLat()
Note that in isFullLng() the width of map's placeholder is needed, I'm using jQuery and map is rendered in div referenced by $this, so I only call
$this.width()
You should should change that to apply it to your problem.