Saturday 22 December 2018

Calculating bounding box by given center and scale in Android?


Given these conditions:



  • a scale like 1:50000

  • the center of the viewport is 100°E, 20°N

  • the size of the viewport is 400x600


How can I calculate the bounding box of the viewport?




The geographic coordinate system of the map is EPSG:4490.



We want to display them in a different projection like Mercator or latitude_longitude_projection(maybe this is the so-said un-projected).


The size of the viewport is in pixels.



Answer



Ok, with some initial issues cleared out the task is relatively simple.


Scale, prepresented as f.ex 1:50000 means that one unit on the map corresponds to 50.000 units in the real world.


For a paper map printed a scale of 1:50000 this means that 1 meter on the map corresponds to 50.000 meters in the real world, or to make it easier: 1 cm on the map corresponds to 50 meter in the real world. So far, so good.


When computer (or phone screens) enter the show it's much more difficult: the unit of measure on a screen is a pixel, which doesn't map directly to centimeters. OpenLayers are (or at least where) using the "Dots per inch", and assumed that one inch corresponds to 72 pixels (this makes some sense on 72 dpi screens, but is wrong on i.e Retina Displays. But for now, lets stick to 72 dpi (as this is what most mapping libraries does (I think, corrections welcome)).


OpenLayers has a function OpenLayers.Util.getResolutionFromScale (see source):


OpenLayers.Util.getResolutionFromScale = function (scale, units) {
var resolution;

if (scale) {
if (units == null) {
units = "degrees";
}
var normScale = OpenLayers.Util.normalizeScale(scale);
resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
* OpenLayers.DOTS_PER_INCH);
}
return resolution;
};




  • With units="degrees" (which EPSG:4490 is, from what I gather) we get inches_per unit = 4374754 (OpenLayers.INCHES_PER_UNIT["degrees"])




  • a scale of 1:50000 (which corresponds to 1/50000 = 0.00002) (this is what penLayers.Util.normalizeScale computes) gives normScale = 0.00002



  • OpenLayers.DOTS_PER_INCH = 72



We can then calculate resolution as


1 / (0.00002 * 4374754 * 72) = 0.00015873908440210453

Knowing the center point (lon=100, lat=30), the pixel size of the viewport(w=400, h=600) and the resolution we can then use the calculateBounds function from OpenLayers.Map (see source):


calculateBounds: function(center, resolution) {

var extent = null;

if (center == null) {
center = this.getCachedCenter();

}
if (resolution == null) {
resolution = this.getResolution();
}

if ((center != null) && (resolution != null)) {
var halfWDeg = (this.size.w * resolution) / 2;
var halfHDeg = (this.size.h * resolution) / 2;

extent = new OpenLayers.Bounds(center.lon - halfWDeg,

center.lat - halfHDeg,
center.lon + halfWDeg,
center.lat + halfHDeg);
}

return extent;
},

which we can reduce to:


function calculateBounds(center, resolution, size) {       

var halfWDeg = (size.w * resolution) / 2;
var halfHDeg = (size.h * resolution) / 2;
return {
"left": center.lon - halfWDeg,
"bottom": center.lat - halfHDeg,
"right": center.lon + halfWDeg,
"top": center.lat + halfHDeg
};
}


Calling this with our values gives:


calculateBounds({"lon": 100, "lat": 30}, 0.00015873908440210453, {"w": 400, "h":600});
{
left: 99.96825218311957,
bottom: 29.95237827467937,
right: 100.03174781688043,
top: 30.04762172532063
}

We can then combine all this to a function that works for degrees with scale denominator given:



function calculateBounds(center, scaleDenominator, size) {       
var resolution = 1 / ((1 / scaleDenominator) * 4374754 * 72)
var halfWDeg = (size.w * resolution) / 2;
var halfHDeg = (size.h * resolution) / 2;
return {
"left": center.lon - halfWDeg,
"bottom": center.lat - halfHDeg,
"right": center.lon + halfWDeg,
"top": center.lat + halfHDeg
};

}

No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...