/*jslint white:false,undef: false, rhino: true, onevar: true, evil: false */ /* Title: Cálculos Arquivo: i3geo/classesjs/classe_calculo.js Licenca: GPL2 i3Geo Interface Integrada de Ferramentas de Geoprocessamento para Internet Direitos Autorais Reservados (c) 2006 Ministério do Meio Ambiente Brasil Desenvolvedor: Edmar Moretti edmar.moretti@mma.gov.br Este programa é software livre; você pode redistribuí-lo e/ou modificá-lo sob os termos da Licença Pública Geral GNU conforme publicada pela Free Software Foundation; Este programa é distribuído na expectativa de que seja útil, porém, SEM NENHUMA GARANTIA; nem mesmo a garantia implícita de COMERCIABILIDADE OU ADEQUAÇÃO A UMA FINALIDADE ESPECÍFICA. Consulte a Licença Pública Geral do GNU para mais detalhes. Você deve ter recebido uma cópia da Licença Pública Geral do GNU junto com este programa; se não, escreva para a Free Software Foundation, Inc., no endereço 59 Temple Street, Suite 330, Boston, MA 02111-1307 USA. */ if(typeof(i3GEO) === 'undefined'){ i3GEO = []; } /* Classe: i3GEO.calculo Utilitários para cálculos. */ i3GEO.calculo = { /* Propriedade: metododistancia Método utilizado no cálculo de distâncias vicenty|vicenty Default: {vicenty} */ metododistancia: "vicenty", /* Function: dms2dd Converte coordenadas formatadas em DMS para DD Parametros: cd {Numeric} - grau (com sinal de - para sul e oeste) cm {Numeric} - minuto cs {Numeric} - segundo Return: {Numeric} - Coordenada em décimos de grau. */ dms2dd: function(cd,cm,cs){ try{ //YAHOO.log("dms2dd", "i3geo"); //converte dms em dd var sinal,spm,mpg,dd; sinal = 'positivo'; if (cd < 0){ cd = cd * -1; sinal = 'negativo'; } spm = cs / 3600; mpg = cm / 60; dd = (cd * 1) + (mpg * 1) + (spm * 1); if (sinal === 'negativo') {dd = dd * -1;} return (dd); } catch(e){return (0);} }, /* Function: dd2tela Converte coordenadas dd em coordenadas de tela. Parametros: vx {Numeric} - coordenada x. vy {Numeric} - coordenada y. docmapa - objeto DOM que contém o objeto imagem ext {String} - (opcional) extensão geográfica (espaço como separador) xmin ymin xmax ymax cellsize {Numeric} - (opcional) tamanho no terreno em DD de cada pixel da imagem Returns: {Array} - Array com o valor de x [0] e y [1] */ dd2tela: function (vx,vy,docmapa,ext,cellsize){ try{ var pos,latlng,xyn,dc,imgext,c,xy; if(i3GEO.Interface.ATUAL === "googlemaps" && docmapa.id !== "mapaReferencia"){ pos = i3GEO.util.pegaPosicaoObjeto($i(i3GEO.Interface.IDCORPO)); xyn = i3GeoMapOverlay.getProjection().fromLatLngToContainerPixel(new google.maps.LatLng(vy,vx)); xy = []; return [(xyn.x)+pos[0],(xyn.y)+pos[1]]; } if(i3GEO.Interface.ATUAL === "openlayers" && docmapa.id !== "mapaReferencia"){ pos = i3GEO.util.pegaPosicaoObjeto($i(i3GEO.Interface.IDCORPO)); xy = i3geoOL.getViewPortPxFromLonLat(new OpenLayers.LonLat(vx,vy)); return [(xy.x)+pos[0],(xy.y)+pos[1]]; } if(arguments.length === 3){ ext = i3GEO.parametros.mapexten; cellsize = i3GEO.parametros.pixelsize; } if(arguments.length === 4){ cellsize = i3GEO.parametros.pixelsize; } if(!docmapa) {docmapa = window.document;} dc = docmapa; pos = i3GEO.util.pegaPosicaoObjeto(dc); imgext = ext.split(" "); vx = (vx * 1) - (imgext[0] * 1); vy = (vy * -1) + (imgext[3] * 1); c = cellsize * 1; return [(vx / c) + pos[0],(vy / c) + pos[1]]; } catch(e){return([]);} }, /* Function: dd2dms Converte coordenadas de dd em dms. Parametros: x {Numeric} - coordenada x. y {Numeric} - coordenada y. Returns: {Array} - Array com o valor de x [0] e y [1] no formato dd mm ss */ dd2dms: function(x,y){ var restod,mx,sx,mm,restos,my,sy,s,dx,dy; dx = parseInt(x,10); if (dx > 0) {restod = x - dx;} if (dx < 0) {restod = (x * -1) - (dx * -1);} if (restod !== 0){ mm = restod * 60; mx = parseInt(restod * 60,10); restos = mm - mx; if (restos !== 0){ s = restos * 60; s = (s+"_").substring(0,5); sx = s; } } else{ mx = "00"; sx = "00.00"; } dy = parseInt(y,10); if (dy > 0) {restod = y - dy;} if (dy < 0) {restod = (y * -1) - (dy * -1);} if (restod !== 0){ mm = restod * 60; my = parseInt(restod * 60,10); restos = mm - my; if (restos !== 0){ s = restos * 60; s = (s+"_").substring(0,5); sy = s; } } else{ my = "00"; sy = "00.00"; } return [dx+" "+mx+" "+sx,dy+" "+my+" "+sy]; }, /* Function: tela2dd Converte o x,y de unidades de tela para décimo de grau. Parametros: xfign {Numeric} - x em valores de imagem. yfign {Numeric} - y em coordenadas de imagem. g_celula {Numeric} - tamanho no terreno do pixel da imagem em dd. imgext {String} - extensão geográfica do mapa. idorigem {string} - (opcional) id do objeto que originou o cálculo (é usado para identificar se o cálculo está sendo feito sobr o mapa de referência ou não) Returns: {Array} - Coordena em dd x[0] e y[1]. */ tela2dd: function(xfign,yfign,g_celula,imgext,idorigem){ try { var amext,longdd,latdd; if(i3GEO.Interface.ATUAL === "googlemaps" && arguments.length === 4){ amext = i3GeoMapOverlay.getProjection().fromContainerPixelToLatLng(new google.maps.Point(xfign,yfign)); return [amext.lng(),amext.lat()]; } if(i3GEO.Interface.ATUAL === "openlayers" && arguments.length === 4){ amext = i3geoOL.getLonLatFromPixel(new OpenLayers.Pixel(xfign,yfign)); return [amext.lon,amext.lat]; } if (navm){ xfign = xfign - 2.2; yfign = yfign - 2.7; } else{ xfign = xfign - 0.12; yfign = yfign - 1.05; } amext = imgext.split(" "); longdd = (amext[0] * 1) + (g_celula * xfign); latdd = (amext[3] * 1) - (g_celula * yfign); return [longdd,latdd]; } catch(e){return(0);} }, /* Function area Calcula a área de um polígono. Os pontos são obtidos do objeto pontos Para o cálculo da área, é feito o cálculo do número de pixel abrangido pelo polígono e multiplicado pela resolução de cada pixel. Referência - http://www.mail-archive.com/mapserver-users@lists.umn.edu/msg07052.html Parametros: pontos {Array} - array com a lista de pontos pontos.xtela corresponde a um array com os valores de x e pontos.ytela aos valores de y pixel {Numeric} - área de cada pixel no mapa Return: Type: {Numeric} */ area: function(pontos,pixel){ if(typeof(console) !== 'undefined'){console.info("i3GEO.calculo.area()");} var $polygon_area,$i,$array_length; try{ if(pontos.xpt.length > 2){ $array_length = pontos.xpt.length; pontos.xtela.push(pontos.xtela[0]); pontos.ytela.push(pontos.ytela[0]); $polygon_area = 0; for ($i=0;$i < $array_length;$i+=1) {$polygon_area += ((pontos.xtela[$i] * pontos.ytela[$i+1])-(pontos.ytela[$i] * pontos.xtela[$i+1]));} $polygon_area = Math.abs($polygon_area) / 2; } else {$polygon_area = 0;} return $polygon_area*pixel; } catch(e){return (0);} }, /* Function: distancia Calcula a distância em km entre dois pontos. O método de cálculo é definido na variável i3GEO.calculo.metododistancia Parametros: lon1 {Numeric} - x inicial. lat1 {Numeric} - y inicial lon2 {Numeric} - x final lat2 {Numeric} - y final Return: Type: {Numeric} */ distancia: function(lon1,lat1,lon2,lat2){ if(i3GEO.calculo.metododistancia === "haversine") {return i3GEO.calculo.distHaversine(lon1,lat1,lon2,lat2);} if(i3GEO.calculo.metododistancia === "vicenty") {return i3GEO.calculo.distVincenty(lon1,lat1,lon2,lat2);} }, /* Function: distHaversine Calcula a distância em km entre dois pontos (método Haversine). Baseado no site http://www.movable-type.co.uk/scripts/latlong.html (indicado por louriques@yahoo.com.br) Em versões anteriores utilizava-se o cálculo proposto em http://www.wcrl.ars.usda.gov/cec/java/lat-long.htm Parametros: lon1 {Numeric} - x inicial. lat1 {Numeric} - y inicial lon2 {Numeric} - x final lat2 {Numeric} - y final Return: Type: {Numeric} */ distHaversine: function(lon1,lat1,lon2,lat2){ if(typeof(console) !== 'undefined'){console.info("i3GEO.calculo.distancia()");} var dLat,dLon,a,c,d; dLat = ((lat2-lat1))* Math.PI / 180; dLon = ((lon2-lon1)) * Math.PI / 180; a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon/2) * Math.sin(dLon/2); c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); d = 6378.137 * c; return d; }, /* Function: distVincenty Given two objects representing points with geographic coordinates, this calculates the distance between those points on the surface of an ellipsoid. Baseado em OpenLayers.Util.distVincenty Parametros: lon1 {Numeric} - x inicial. lat1 {Numeric} - y inicial lon2 {Numeric} - x final lat2 {Numeric} - y final Return: {Numeric} - The distance (in km) between the two input points as measured on an ellipsoid. Note that the input point objects must be in geographic coordinates (decimal degrees) and the return distance is in kilometers. */ distVincenty: function(lon1,lat1,lon2,lat2) { var rad = function(x) {return x*Math.PI/180;}, ct = { a: 6378137, b: 6356752.3142, f: 1/298.257223563 }, p1 = { lat: lat1, lon: lon1 }, p2 = { lat: lat2, lon: lon2 }, a = ct.a, b = ct.b, f = ct.f, L = rad(p2.lon - p1.lon), U1 = Math.atan((1-f) * Math.tan(rad(p1.lat))), U2 = Math.atan((1-f) * Math.tan(rad(p2.lat))), sinU1 = Math.sin(U1), cosU1 = Math.cos(U1), sinU2 = Math.sin(U2), cosU2 = Math.cos(U2), lambda = L, lambdaP = 2*Math.PI, iterLimit = 20, sinLambda, cosLambda, sinSigma, cosSigma, sigma, alpha, cosSqAlpha, cos2SigmaM, C, uSq, A, B, s, d, deltaSigma; while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) { sinLambda = Math.sin(lambda); cosLambda = Math.cos(lambda); sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda)); if (sinSigma===0) { return 0; // co-incident points } cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda; sigma = Math.atan2(sinSigma, cosSigma); alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma); cosSqAlpha = Math.cos(alpha) * Math.cos(alpha); cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha; C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha)); lambdaP = lambda; lambda = L + (1-C) * f * Math.sin(alpha) * (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM))); } if (iterLimit===0) { return NaN; // formula failed to converge } uSq = cosSqAlpha * (a*a - b*b) / (b*b); A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq))); B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq))); deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)- B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM))); s = b*A*(sigma-deltaSigma); d = s.toFixed(3)/1000; // round to 1mm precision return d; }, /* Function: direcao Calcula a direção (0 a 360 graus) entre dois pontos. Baseado no site http://www.movable-type.co.uk/scripts/latlong.html (indicado por louriques@yahoo.com.br) Parametros: lon1 {Numeric} - x inicial. lat1 {Numeric} - y inicial lon2 {Numeric} - x final lat2 {Numeric} - y final Return: Ângulo em décimos de grau Type: {Numeric} */ direcao: function(lon1,lat1,lon2,lat2){ if(typeof(console) !== 'undefined'){console.info("i3GEO.calculo.direcao()");} var dLon,y,x,r; lat1 = lat1 * (Math.PI / 180); lat2 = lat2 * (Math.PI / 180); dLon = (lon2-lon1) * (Math.PI / 180); y = Math.sin(dLon) * Math.cos(lat2); x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon); r = Math.atan2(y, x); r = r * 180 / Math.PI; r = r + 360; return r % 360; }, /* Function: destinoDD Calcula as coordenadas de um novo ponto em função da posição de um ponto de origem, distância e direção O novo ponto é calculado em coordenadas geográficas em DD Baseado no site http://www.movable-type.co.uk/scripts/latlong.html (indicado por louriques@yahoo.com.br) Parametros: lon {Numeric} - longitude (x) do ponto de origem lat {Numeric} - latitude do ponto de origem d {Numeric} - distância em Km direção {Numeric} - ângulo desejado em décimos de grau (direção de 0 a 360) Return: Array com a longitude e latitude em décimos de grau ([0] = longitude, [1] = latitude Type: {Array} */ destinoDD: function(lon,lat,d,direcao){ if(typeof(console) !== 'undefined'){console.info("i3GEO.calculo.destinoDD()");} var R,lat1,lon1,brng,lat2,lon2; R = 6371; // earth's mean radius in km lat1 = lat * (Math.PI / 180); lon1 = lon * (Math.PI / 180); brng = direcao * (Math.PI / 180); lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) + Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) ); lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2)); lon2 = (lon2+Math.PI)%(2*Math.PI) - Math.PI; // normalise to -180...+180 if (isNaN(lat2) || isNaN(lon2)) {return null;} return [(lon2 * 180 / Math.PI),(lat2 * 180 / Math.PI)]; }, /* Function: rect2ext Calcula a extensão geográfica de um retângulo desenhado sobre o mapa. Parametros: idrect - id do elemento html com o retangulo mapext - extensao geografica do mapa onde está o retangulo pixel - tamanho do pixel do mapa em dd return: {Array} - extensão, xmin, ymin, xmax, ymax */ rect2ext: function(idrect,mapext,pixel){ if(typeof(console) !== 'undefined'){console.info("i3GEO.calculo.rect2ext()");} var bx,bxs,xfig,yfig,nx,ny,pix,piy,pos,amext,dx,dy,x1,y1,x2,y2, pix = parseInt(document.getElementById(idrect).style.left,10), piy = parseInt(document.getElementById(idrect).style.top,10); if($i(idrect)){ bx = $i(idrect); bxs = bx.style; } else {alert("Box nao encontrado");return;} pos = i3GEO.util.pegaPosicaoObjeto($i(i3GEO.Interface.IDCORPO)); xfig = pix + (parseInt(bxs.width,10)) - pos[0]; yfig = piy + (parseInt(bxs.height,10)) - pos[1]; amext = mapext.split(" "); dx = ((amext[0] * -1) - (amext[2] * -1)) / -1; dy = ((amext[1] * 1) - (amext[3] * 1)) / -1; if (dy < 0) {dy=dy * -1;} nx = pixel * xfig; ny = pixel * yfig; x1 = (amext[0] * 1) + nx; y1 = (amext[3] * 1) - ny; xfig = pix - pos[0]; yfig = piy - pos[1]; if (dy < 0) {dy=dy * -1;} nx = pixel * xfig; ny = pixel * yfig; x2 = (amext[0] * 1) + nx; y2 = (amext[3] * 1) - ny; return [x2+" "+y2+" "+x1+" "+y1,x1,y1,x2,y2]; }, /* Function: ext2rect Calcula o posicionamento de um retângulo com base na extensão geográfica. Parametros: idrect {String} - id do elemento html com o retangulo, pode ser vazio mapext {String} - extensao geografica do mapa onde está o retangulo boxext {String} - extensao geografica do retangulo pixel {Number} - tamanho do pixel do mapa em dd documento {Object DOM} - objeto sob o qual o retângulo será posicionado Return: {Array} - width,heigth,top,left */ ext2rect: function(idrect,mapext,boxext,pixel,documento){ if(typeof(console) !== 'undefined'){console.info("i3GEO.calculo.ext2rect()");} var rectbox,rectmap,xyMin,xyMax,w,h,tl,pos,t,l,d,box; rectbox = boxext.split(" "); rectmap = mapext.split(" "); xyMin = i3GEO.calculo.dd2tela(rectbox[0],rectbox[1],documento,boxext,pixel); xyMax = i3GEO.calculo.dd2tela(rectbox[2],rectbox[3],documento,boxext,pixel); w = xyMax[0]-xyMin[0]; h = xyMin[1]-xyMax[1]; tl = i3GEO.calculo.dd2tela(rectbox[0],rectbox[3],documento,mapext,pixel); pos = i3GEO.util.pegaPosicaoObjeto(documento); t = tl[1] - pos[1]; l = tl[0] - pos[0]; d = "block"; if($i(idrect)){ box = $i(idrect); box.style.width = w + "px"; box.style.height = h + "px"; box.style.top = t + "px"; box.style.left = l + "px"; box.style.display=d; } return [w,h,xyMax[1],xyMin[0]]; } }; //YAHOO.log("carregou classe calculo", "Classes i3geo");