Ok, con algunos problemas iniciales resueltos, la tarea es relativamente simple.
Escala, representada como f.ex 1: 50000 significa que una unidad en el mapa corresponde a 50,000 unidades en el mundo real.
Para un mapa en papel impreso en una escala de 1: 50000, esto significa que 1 metro en el mapa corresponde a 50,000 metros en el mundo real, o para hacerlo más fácil: 1 cm en el mapa corresponde a 50 metros en el mundo real. Hasta aquí todo bien.
Cuando la computadora (o las pantallas del teléfono) entran al programa, es mucho más difícil: la unidad de medida en una pantalla es un píxel, que no se asigna directamente a centímetros. Los OpenLayers están (o al menos donde) usando los "Puntos por pulgada", y se supone que una pulgada corresponde a 72 píxeles (esto tiene sentido en pantallas de 72 ppp, pero es incorrecto en pantallas Retina. Pero por ahora, sigamos con 72 ppp (ya que esto es lo que hace la mayoría de las bibliotecas de mapeo (creo que las correcciones son bienvenidas)).
OpenLayers tiene una función OpenLayers.Util.getResolutionFromScale (ver fuente ):
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;
};
Con unidades = "grados" (que es EPSG: 4490, por lo que deduzco) obtenemos pulgadas_por unidad = 4374754 (OpenLayers.INCHES_PER_UNIT ["grados"])
una escala de 1: 50000 (que corresponde a 1/50000 = 0.00002) (esto es lo que calcula penLayers.Util.normalizeScale) da normScale = 0.00002
- OpenLayers.DOTS_PER_INCH = 72
Entonces podemos calcular la resolución como
1 / (0.00002 * 4374754 * 72) = 0.00015873908440210453
Conociendo el punto central (lon = 100, lat = 30), el tamaño de píxel de la ventana gráfica (w = 400, h = 600) y la resolución, podemos usar la función CalculateBounds de OpenLayers.Map (ver fuente ):
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;
},
que podemos reducir a:
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
};
}
Llamar a esto con nuestros valores da:
calculateBounds({"lon": 100, "lat": 30}, 0.00015873908440210453, {"w": 400, "h":600});
{
left: 99.96825218311957,
bottom: 29.95237827467937,
right: 100.03174781688043,
top: 30.04762172532063
}
Luego podemos combinar todo esto en una función que funcione para grados con el denominador de escala dado:
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
};
}