goog.require('bu');
/**
* Transforms a coordinate from phi/theta to pitch/yaw inside an
* equirectangular panorama 360 image.
* @param {bu.Coordinate} coordinate Coordinate as [phi, theta].
* @param {bu.street.Image} image Image object to which transform.
* @return {bu.Coordinate} Coordinate transformed to [pitch, yaw].
* @author josea.hernandez@blom.no (Jose Antonio Hernandez)
* @api
*/
bu.street.phiThetaToPitchYaw = function(coordinate, image) {
var phi = coordinate[0];
var theta = coordinate[1];
var pitch = - ( (image.equi_img_width /2) -phi - image.row_center ) *
bu.M_DEG_TO_RAD / image.pixel_angle;
var yaw = ( theta - image.column_center ) * bu.M_DEG_TO_RAD / image.pixel_angle;
return [pitch, yaw];
};
/**
* Transforms a coordinate from pitch/yaw to phi/theta inside an
* equirectangular panorama 360 image.
* @param {bu.Coordinate} coordinate Coordinate as [pitch, yaw].
* @param {bu.street.Image} image Image object to which transform.
* @return {bu.Coordinate} Coordinate transformed to [phi, theta].
* @api
*/
bu.street.pitchYawToPhiTheta = function(coordinate, image) {
var pitch = coordinate[0];
var yaw = coordinate[1];
var phi = (image.equi_img_width /2) + (pitch * bu.M_RAD_TO_DEG * image.pixel_angle) - image.row_center;
var theta = ( yaw * bu.M_RAD_TO_DEG * image.pixel_angle ) + image.column_center;
if ( theta < 0 ) theta = image.equi_img_width + theta;
if ( theta > image.equi_img_width ) theta = theta - image.equi_img_width;
return [phi, theta];
};
/**
* Transforms a coordinate from longitude/latitude/z to pitch/yaw inside an
* equirectangular panorama 360 image.
* @param {bu.Coordinate} coordinate Coordinate as longitude/latitude/z, i.e.
* an array with longitude as 1st and latitude as 2nd element, and z is
* elevation in meters. If this value is not known it can be ignored and
* use only a coordinate of two values to get a good aproximation.
* @param {bu.street.Image} image Image object to which transform.
* @return {bu.Coordinate} Coordinate transformed where first value is pitch and
* second value is yaw in radians. Yaw is [0, 2PI) value where zero is at
* initial direction and pitch is [+PI/2,-PI/2].
* @api
*/
bu.street.fromLonLatToPitchYaw = function(coordinate, image) {
var direction_yaw_rad = image.direction_yaw * Math.PI / 180.0;
var lon_rad = coordinate[0] * Math.PI / 180.0;
var lat_rad = coordinate[1] * Math.PI / 180.0;
var z = coordinate[2];
var imagelon_rad = image.xcp * Math.PI / 180.0;
var imagelat_rad = image.ycp * Math.PI / 180.0;
var imagez = image.zcp;
var h = image.camera_height;
var deltalon = (lon_rad - imagelon_rad); //Increment in longitude (radians)
//beta = Angle or azimuth between north and direction to world point (radians)
//atan2 function retrieves values between -pi and pi. Angles are taken
//clockwise starting at north, which is value zero
var beta = Math.atan2(Math.sin(deltalon) * Math.cos(lat_rad),
Math.cos(imagelat_rad) * Math.sin(lat_rad) -
Math.sin(imagelat_rad) * Math.cos(lat_rad) * Math.cos(deltalon));
//yaw = Angle or azimuth between vehicle direction and direction of line
// between image shot point and world point (radians)
var yaw = beta - direction_yaw_rad;
//Ensure yaw is in 0, 2π range
var dPI = 2 * Math.PI;
if (yaw < 0.0) yaw += dPI;
//JS does not have decimal modulus and next line do not work
//yaw = yaw % dPI;
yaw = (+yaw % (dPI = +dPI) + dPI) % dPI;
var R = bu.MEAN_EARTH_RADIUS;
//pitch = Polar angle between Z axis and direction to world point
//alpha = angle between vectors of both points respect earth center
//Some equations taken from :
// http://www.ajdesigner.com/phptriangle/scalene_triangle_area_height.php
// http://mathworld.wolfram.com/GreatCircle.html
var alpha = Math.acos(Math.cos(lat_rad) * Math.cos(imagelat_rad) * Math.cos(deltalon)
+ Math.sin(lat_rad) * Math.sin(imagelat_rad));
var b = R + imagez;
var a = b * Math.tan(alpha);
var c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
var d = c - R - z;
var f = c - d - h;
var e = Math.sqrt(Math.pow(b, 2) + Math.pow(f, 2) - 2 * b * f * Math.cos(alpha));
var pitch = Math.acos((Math.pow(a, 2) + Math.pow(e, 2) - Math.pow(d + h, 2)) / (2*a*e));
//If we consider the Earth a sphere we can approach the calculation with this equation:
// tan(PI - pitch) = (R + z2) * sin(a) / (h + z1 + R - (R + z2) * cos(a))
//var distance = bu.street.calculateDistance([image.xcp, image.ycp], coordinate);
//var a = distance / R;
//var pitch = Math.PI - Math.atan2(
// (R + z) * Math.sin(a),
// image.camera_height + image.zcp + R - (R + z) * Math.cos(a)
//);
return [pitch, yaw];
};
/**
* Transforms a coordinate from a local coordinate XYZ to phi/theta inside an
* equirectangular panorama 360 image.
* @param {bu.Coordinate} coordinate Coordinate as XYZ in the local projection
* defined for the image.
* @param {bu.street.Image} image Image object to which transform.
* @return {bu.Coordinate} Coordinate transformed where first value is phi and
* second value is theta.
* @api
*/
bu.street.fromLocalCoordToPhiTheta = function(coordinate, image) {
var phi, theta;
var r,u,v,w,ang1,vx1,vy1;
var pixel_angle= image.equi_img_width/360.0 ;
var xcp = image.local_xcp;
var ycp = image.local_ycp;
var zcp = image.zcp;
var x = coordinate[0];
var y = coordinate[1];
var z = coordinate[2];
u = image.ext0*(x-xcp) + image.ext3*(y-ycp) + image.ext6*(z-zcp) + xcp;
v = image.ext1*(x-xcp) + image.ext4*(y-ycp) + image.ext7*(z-zcp) + ycp;
w = image.ext2*(x-xcp) + image.ext5*(y-ycp) + image.ext8*(z-zcp) + zcp;
r= Math.sqrt( (u-xcp) * (u-xcp) + (v-ycp) * (v-ycp) + (w-zcp) * (w-zcp) );
vx1=u-xcp;
vy1=v-ycp;
ang1 = -Math.atan2(vy1,vx1) * bu.M_RAD_TO_DEG;
theta = ( ang1 * pixel_angle ) + image.column_center;
if(theta < 0 ) theta = image.equi_img_width + theta;
if(theta > image.equi_img_width ) theta = theta - image.equi_img_width;
phi = Math.asin((w - zcp) / r )* bu.M_RAD_TO_DEG;
phi = (image.equi_img_width /2) - ( phi * pixel_angle ) - image.row_center;
return [phi, theta];
};
/**
* Transforms an array of coordinates from a local coordinate XYZ to phi/theta inside an
* equirectangular panorama 360 image.
* @param {Array.<bu.Coordinate>} coordinates Array of coordinate as XYZ in the local projection
* defined for the image.
* @param {bu.street.Image} image Image object to which transform.
* @return {Array.<bu.Coordinate>} Array of coordinate transformed where first value is phi and
* second value is theta.
* @api
*/
bu.street.fromLocalCoordsToPhiTheta = function(coordinates, image) {
var phi, theta;
var r,u,v,w,ang1,vx1,vy1;
var pixel_angle= image.equi_img_width/360.0 ;
var xcp = image.local_xcp;
var ycp = image.local_ycp;
var zcp = image.zcp;
var result = new Array(coordinates.length,3);
for( i = 0;i<coordinates.length;i++)
{
var x = coordinates[i][0];
var y = coordinates[i][1];
var z = coordinates[i][2];
u = image.ext0*(x-xcp) + image.ext1*(y-ycp) + image.ext2*(z-zcp) + xcp;
v = image.ext3*(x-xcp) + image.ext4*(y-ycp) + image.ext5*(z-zcp) + ycp;
w = image.ext6*(x-xcp) + image.ext7*(y-ycp) + image.ext8*(z-zcp) + zcp;
r= Math.sqrt( (u-xcp) * (u-xcp) + (v-ycp) * (v-ycp) + (w-zcp) * (w-zcp) );
vx1=u-xcp;
vy1=v-ycp;
ang1 = -Math.atan2(vy1,vx1) * bu.M_RAD_TO_DEG;
theta = ( ang1 * pixel_angle ) + image.column_center;
if(theta < 0 ) theta = image.equi_img_width + theta;
if(theta > image.equi_img_width ) theta = theta - image.equi_img_width;
phi = Math.asin((w - zcp) / r )* bu.M_RAD_TO_DEG;
phi = (image.equi_img_width /2) - ( phi * pixel_angle ) - image.row_center;
result[i] = [phi, theta];
}
return result;
};
/**
* Calculates distance in meters from one point to other of Earth. Calculation is
* aproximated using mean radius spherical Earth.
* @param {bu.Coordinate} coordinate1 Coordinate as longitude and latitude, i.e.
* an array with longitude as 1st and latitude as 2nd element.
* @param {bu.Coordinate} coordinate2 Coordinate as longitude and latitude, i.e.
* an array with longitude as 1st and latitude as 2nd element.
* @return {number} Distance in meters.
* @api
*/
bu.street.calculateDistance = function(coordinate1, coordinate2)
{
var lon1_rad = coordinate1[0] * Math.PI / 180.0;
var lat1_rad = coordinate1[1] * Math.PI / 180.0;
var lon2_rad = coordinate2[0] * Math.PI / 180.0;
var lat2_rad = coordinate2[1] * Math.PI / 180.0;
var A = Math.sin(lat1_rad);
var B = Math.sin(lat2_rad);
var C = Math.cos(lat1_rad);
var D = Math.cos(lat2_rad);
var E = Math.cos(Math.abs(lon1_rad - lon2_rad));
return Math.acos(A*B + C*D*E) * bu.MEAN_EARTH_RADIUS;
};
/**
* Transforms one coordinate in geographical projection to spherical mercator.
* @param {number} longitude Longitude of the coordinate we want to transform.
* @param {number} latitude Longitude of the coordinate we want to transform.
* @return {bu.Coordinate} Coordinate as longitude and latitude.
* @api
*/
bu.street.latLon2MercatorSpheric = function (longitude, latitude){
var dblLatitude;
var dblLongitude;
var dblLambda;
var dblPhi;
dblLongitude = longitude; // 100000;
dblLatitude = latitude; // 100000
dblLambda = dblLongitude;
dblPhi = dblLatitude;
dblLongitude = bu.MEAN_EARTH_RADIUS * ((Math.PI / 180) * ((dblLambda) - 0.0));
dblLatitude = bu.MEAN_EARTH_RADIUS * Math.log(Math.tan((Math.PI / 4) + (Math.PI / 180) * dblPhi * 0.5));
return [dblLongitude, dblLatitude];
};
/**
* Transforms one coordinate in spherical mercator projection to geographical.
* @param {number} longitude Longitude of the coordinate we want to transform.
* @param {number} latitude Longitude of the coordinate we want to transform.
* @return {bu.Coordinate} Coordinate as longitude and latitude.
* @api
*/
bu.street.mercatorSpheric2LatLon = function (longitude, latitude){
var lon = longitude * Math.PI / 180;
var lat = latitude * Math.PI / 180;
var e1 = 0.081819;
var iso = Math.log(Math.tan((lat / 2.0) + (Math.PI / 4.0))) - (e1 / 2.0 * Math.log((1.0 + e1 * Math.sin(lat)) / (1.0 - e1 * Math.sin(lat))));
var longitudeResult = bu.MEAN_EARTH_RADIUS * (lon);
var latitudeResult = bu.MEAN_EARTH_RADIUS * iso;
return [longitudeResult, latitudeResult];
};