EDIT:
"Does this suggest that the distances from computeOffset are wrong? ..."
No, it's not wrong, it's a projection. Projections stretch, squash, skew, ... everything
The word is projected as a rectangle, twice as large as the height. 360° wide, 180° high.
The north and south pole (both just a popint) are stretched as if they were as long as the equator.
Except ... Google Maps then squashes that map, to almost a square (depending on the zoom). So you must check the bounds that Google Maps gives you
You can read the degrees-density horizontally, then vertically, from the map bounds. When the tiles are loaded you can read this. If you want you can send this data to the server, with AJAX (not included in this code)
One thing I can't help is if the pixels on your screen aren't square.
Well, maybe you can search for the physical pixel density and factor that in as well. Maybe this helps: Mobile web: how to get physical pixel size?
As an example: 400x400 px map. A red square in the middle, half the width and height of the map.
Sorry, it's a javascript/website example, not Python/Android. But I suppose you can use the same principles.
<!DOCTYPE html>
<html>
<head>
<title>Square on Map</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
html,
body {
height: 400px;
width: 400px;
padding: 0;
}
#map {
height: 80%;
}
</style>
<script>
var map;
// return a polyline. If you give 2 points, it will simply be a line
function makePolyline(points, color, close) {
if (close) {
points.push(points[0]); // closed polyline, so duplicate the start point as the endpoint
}
return new google.maps.Polyline({
path: points,
//geodesic: false,
strokeColor: color, // example: '#FF0000',
strokeOpacity: 1.0,
strokeWeight: 2,
map: map
});
}
function initMap() {
var myLocation = {
lat: 43,
lng: -108
};
map = new google.maps.Map(document.getElementById('map'), {
center: myLocation,
zoom: 5
});
// wait until the map is loaded before calculating bounds
google.maps.event.addListenerOnce(map, 'tilesloaded', function() {
var mapWidth = document.getElementById('map').offsetWidth;
var mapHeight = document.getElementById('map').offsetWidth;
var bounds = map.getBounds();
var NE = bounds.getNorthEast();
var SW = bounds.getSouthWest();
// draw a square half the size of the map (200 X 200 px), in the midle => start at 1/4, stop at 3/4
points = [{
lat: SW.lat() + ((NE.lat() - SW.lat()) * 1/4),
lng: SW.lng() + ((NE.lng() - SW.lng()) * 1/4)
},
{
lat: SW.lat() + ((NE.lat() - SW.lat()) * 1/4),
lng: SW.lng() + ((NE.lng() - SW.lng()) * 3/4)
},
{
lat: SW.lat() + ((NE.lat() - SW.lat()) * 3/4),
lng: SW.lng() + ((NE.lng() - SW.lng()) * 3/4)
},
{
lat: SW.lat() + ((NE.lat() - SW.lat()) * 3/4),
lng: SW.lng() + ((NE.lng() - SW.lng()) * 1/4)
}];
makePolyline(points, '#ff0000', true);
// display the numbers in a div. Feel free to not do this
document.getElementById('log').innerHTML = 'map width: ' + mapWidth + 'px - map height: ' + mapHeight + 'px<br/>';
document.getElementById('log').innerHTML += 'horizontal: ' + ((NE.lng() - SW.lng() ) / mapWidth ).toFixed(4) + ' degrees/px<br/>';
document.getElementById('log').innerHTML += 'vertical: ' + ((NE.lat() - SW.lat() ) / mapHeight ).toFixed(4) + ' degrees/px<br/>';
});
}
</script>
</head>
<body>
<div id="map"></div>
<div id="log"></div>
<script src="https://maps.googleapis.com/maps/api/js?callback=initMap" async defer></script>
</body>
</html>