Get width in kilometers of Google Maps API map - javascript

How can I get the current width of the visible area on Google map, i have read many docs but not success? I need to get real width of visible area in kilometers.

I think getBounds() is what you are looking for.
var map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
google.maps.event.addListener(map, 'idle', function(event) {
var bounds = map.getBounds();
You will receive a LatLngBounds object (for reference https://developers.google.com/maps/documentation/javascript/reference#LatLngBounds).
This contains NortEase and SouthWest LatLng points. Using those it should be easy to get the width in kilometers using this formula. You can create 2 pairs out of LatLng points to create a NorthWest and NorthEast pair and calculate the distance between those.
var R = 6371000; // metres
var φ1 = lat1.toRadians();
var φ2 = lat2.toRadians();
var Δφ = (lat2-lat1).toRadians();
var Δλ = (lon2-lon1).toRadians();
var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ/2) * Math.sin(Δλ/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
See this as an reference (http://www.movable-type.co.uk/scripts/latlong.html).

Related

Get Radius of the displayed OpenLayers Map

How do I compute the distance in mile/meters of the displayed Map? Assuming I have the coordinates of the center of the map. I would like to know the distance/radius from the center to the left/right most displayed part of the map? I already got this using Google Map but I'm new in Openlayers. Is there a way to achieve this? Right now my computation in GoogleMap is like this
var bounds = this.instance.getBounds();
var center = bounds.getCenter();
var ne = bounds.getNorthEast();
// r = radius of the earth in statute miles
var r = 3963.0;
var to_radians_divide = 57.2958;
// Convert lat or lng from decimal degrees into radians (divide by 57.2958)
var lat1 = center.lat() / to_radians_divide;
var lon1 = center.lng() / to_radians_divide;
var lat2 = ne.lat() / to_radians_divide;
var lon2 = ne.lng() / to_radians_divide;
// 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));
return dis;
Is there a way to achieve this in OpenLayers? I just want to find out the distance of the left most part of the displayed map from the center
You can achieve this in openlayers also. Here is the code for that and I am using openlayers 3.20.0 version for this example.
var size = map.getSize();
var center = map.getView().getCenter();
var sourceProj = map.getView().getProjection();
var extent = map.getView().calculateExtent(size);
extent = ol.proj.transformExtent(extent, sourceProj, 'EPSG:4326');
var posSW = [extent[0], extent[1]];
var posNE = [extent[2], extent[3]];
center = ol.proj.transform(center, sourceProj, 'EPSG:4326');
var wgs84Sphere = new ol.Sphere(6378137);
var centerToSW = wgs84Sphere.haversineDistance(center, posSW);
var centerToNE = wgs84Sphere.haversineDistance(center, posNE);
console.log("centerToSW - ",centerToSW);
console.log("centerToNE - ",centerToNE);

add padding to google maps bounds.contains()

I have a sidebar which shows the names of the markers in the current map view of a google map. The sidebar contents change as the map gets moved:
google.maps.event.addListener(map, 'bounds_changed', function() {
document.getElementById("list").innerHTML = "";
var mklen = mkrs.length,
a = 0,
bnds = map.getBounds();
for (a; a < mklen; a++) {
var themk = mkrs[a];
if (bnds.contains(themk.getPosition())) {
var myli = document.createElement("li");
myli.innerHTML = themk.title;
document.getElementById("list").appendChild(myli);
}
}
});
That's working OK, but the thing is that the bounds.contains() is very strict - if just the bottom tip of the marker is on the map (ie, you can't see 99% of it) it gets listed on the sidebar. What I'd like is to have just the markers that are completely shown pass that test.
There are a couple of approaches that I can think of and I can't believe that nobody else has come up against this problem, so I'm wondering if there is a preference out of the following:
take the bounds and recalculate them to be smaller than the actual bounds and use those new bounds for the bounds.contains() test
calculate where the edges of the marker icons are (I guess using fromDivPixelToLatLng) then check that both the ne AND sw corners are within the bounds and if so, list the item
Before you ask, I haven't tried either of those - I'm more looking for advice on which would be best or even possible, or if there is another way to do this. Here's a fiddle demonstrating the issue, in case it clarifies
In case anybody finds this later, I ended up recalculating the bounds - it seemed to be the approach that involved the least overhead. Here's the function:
function paddedBounds(npad, spad, epad, wpad) {
var SW = map.getBounds().getSouthWest();
var NE = map.getBounds().getNorthEast();
var topRight = map.getProjection().fromLatLngToPoint(NE);
var bottomLeft = map.getProjection().fromLatLngToPoint(SW);
var scale = Math.pow(2, map.getZoom());
var SWtopoint = map.getProjection().fromLatLngToPoint(SW);
var SWpoint = new google.maps.Point(((SWtopoint.x - bottomLeft.x) * scale) + wpad, ((SWtopoint.y - topRight.y) * scale) - spad);
var SWworld = new google.maps.Point(SWpoint.x / scale + bottomLeft.x, SWpoint.y / scale + topRight.y);
var pt1 = map.getProjection().fromPointToLatLng(SWworld);
var NEtopoint = map.getProjection().fromLatLngToPoint(NE);
var NEpoint = new google.maps.Point(((NEtopoint.x - bottomLeft.x) * scale) - epad, ((NEtopoint.y - topRight.y) * scale) + npad);
var NEworld = new google.maps.Point(NEpoint.x / scale + bottomLeft.x, NEpoint.y / scale + topRight.y);
var pt2 = map.getProjection().fromPointToLatLng(NEworld);
return new google.maps.LatLngBounds(pt1, pt2);
}
and you call it like this:
var padbnds = paddedBounds(50, 70, 100, 30);
specifying how much padding you want on the north, south, east and west edges of the map respectively

Parametric equation to place a leaflet marker on the circumference of a circle is not precise?

I am working on an application where I have the center of a circle and the radius and I am plotting the circle with the help of Leaflet.
I placed a marker on the north most end of the circumference and made it draggable.
var circle = L.circle(coords, radius).addTo(map);
convertRadiusToLatitude = parseInt(response.radius)/111111;
var coordsOnRadius = [parseFloat(response.lat) + convertRadiusToLatitude, parseFloat(response.long)];
var markerOnRadius = L.marker(coordsOnRadius, {draggable: true}).addTo(map);
Now, this adds the marker to the circumference and now I wanted it to be draggable only on the circumference itself for which I used the parametric equation.
Parametric equation
x = Xc + R * cos(theta)
y = Yc + R * sin(theta)
Code for dragging
markerOnRadius.on('drag', function(e){
bearing = marker.getLatLng().bearingTo(markerOnRadius.getLatLng());
var markerOnRadiusX = parseFloat(response.lat) + ((0.000009 * parseFloat(response.radius)) * Math.cos( toRad(bearing) ));
var markerOnRadiusY = parseFloat(response.long) + ((0.000009 * parseFloat(response.radius)) * Math.sin( toRad(bearing) ));
markerOnRadius.setLatLng([markerOnRadiusX, markerOnRadiusY]);
});
The bearingTo method:
L.LatLng.prototype.bearingTo = function(other) {
var d2r = L.LatLng.DEG_TO_RAD;
var r2d = L.LatLng.RAD_TO_DEG;
var lat1 = this.lat * d2r;
var lat2 = other.lat * d2r;
var dLon = (other.lng-this.lng) * d2r;
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = Math.atan2(y, x);
brng = parseInt( brng * r2d );
brng = (brng + 360) % 360;
return brng;
};
Issue
When I start dragging the marker, this code is working fine and brings it back to the circumference at the bearing at which the marker is dragged to. But there is one problem, the coords on the circumference are slightly off and in terms of longitude. When the bearing is 0 (north), the coords are perfect, but when it is 90 (east), the longitude is slightly less that it should for the marker to be at the circumference.
Again at 180 (south), coords are perfect, but at 270 (west), the longitude calculated is slightly less and the marker tends towards the radius again.
So basically if you visualize the marker being dragged, it starts perfectly on the north end and starts coming inside the circle slightly increasing with the bearing till it reacher 90 and then starts going towards the circumference again till 180 when it is perfect again.
It forms more like a ellipse if you get the gist of it.
Could anyone tell me why is longitude coming a little off and why the marker moves in an elliptical path. Has it something to do with the world coordinates and window coordinates. Or are my equations slightly off somewhere?
It does look like a projection issue. In your dragging code you are basically doing
lat = a + r cos(baring)
long = b + r sin(baring)
giving a circle in the Lat-Long coordinates. This would work fine if you were at the equator with Mercator projection. You will get more distortion as you move further towards the polls.
Assume you are using the defaults for Leaflet reference doc You have the EPSG3857 Web Mercator coordinates.
If you want to ensure you have a exact circle it will be better to work using screen coordinates. You can get these using methods on the ICRS objects. First get the coordinate system L.CRS.EPSG3857 and use the latLngToPoint and pointToLatLng methods.
var crs = L.CRS.EPSG3857;
var zoom = ...; // how you calculate your zoom factor
markerOnRadius.on('drag', function(e){
var markerLL = marker.getLatLng()
var morLL = markerOnRadius.getLatLng();
var markerP = crs.latLngToPoint(markerLL,zoom);
var morP = crs.latLngToPoint(morLL,zoom);
// get the distance between the two points
var dist = markerP.distanceTo(morP);
// Get the vector from center to point
var A = morP.subtract(markerP);
// scale so its of the desired length
var B = A. multiplyBy( factor / dist);
// Add on the center
var C = markerP.add(B);
// Convert back to LatLong
var D = crs.pointToLatLng(C,zoom);
markerOnRadius.setLatLong(D);
});

Google Maps Zoom Show All Markers

Right now I have an array of locations and one of those locations will be a marker.
The second marker will be based on the location a user searches.
Right now the code is this:
function find_closest_marker( lat1, lon1 ) {
var pi = Math.PI;
var R = 6371; //equatorial radius
var distances = [];
var closest = -1;
var markers = allMyLocations;
for( i=0;i<markers.length; i++ ) {
var lat2 = markers[i][1];
var lon2 = markers[i][2];
var chLat = lat2-lat1;
var chLon = lon2-lon1;
var dLat = chLat*(pi/180);
var dLon = chLon*(pi/180);
var rLat1 = lat1*(pi/180);
var rLat2 = lat2*(pi/180);
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(rLat1) * Math.cos(rLat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
distances[i] = d;
if ( closest == -1 || d < distances[closest] ) {
closest = i;
}
var closestMarker = markers[closest];
var newLat = markers[closest][1]
var newLon = markers[closest][2]
var b = new google.maps.LatLng(newLat, newLon);
map.setCenter(b);
map.setZoom(10);
google.maps.event.addListenerOnce(map, 'tilesloaded', function(){
ShowHideMenu();
if($('#newPos').length==0){
$('div.gmnoprint').last().parent().wrap('<div id="newPos" />');
}
//showInfoWindow(map, closest);
});
}
}
Right now this code gets the closest location from where a user searches and centers that on the page. What I'm trying to do is make it zoom so that the the location searched is centered and the location in the array is within the area of the maps. The map isn't a normal size, it is 790px by 400px.
Can someone please help me with the logic of zoom and how I could make this possible?
Have you tried the LatLngBounds function This function will construct a viewable are based upon the soutwest and north east corners. As a result the pins will be centered in the map. You can use zoom if you want but you could omit it to see if it gives you the desired result.
A sample of the code would be:
make your declaration
var mybounds = new google.maps.LatLngBounds();
then within your event listener where you create the markers add
mybounds.extend(b);
then finally outside of the event listener
map.fitBounds(mybounds)

bing maps: how to set zoom level so pinpoint is visible to users current location

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);

Categories

Resources