quarta-feira, julho 07, 2010

PostGIS: conversão de coordenadas em graus decimais para graus, minutos e segundos (e vice-versa)

Recentemente tive que implementar uma solução, onde através de um formulário, o usuário informava as coordenadas em graus, minutos e segundos, logo em seguida era mostrado em um mapa, se essa coordenada caía em uma área válida da aplicação.
Acontece que por padrão, o PostGIS armazena as geometrias no formato decimal, logo eu teria que tratar as coordenadas digitadas (no código PHP ou no Banco), transformando-as em graus decimais, foi aí que eu encontrei duas funções muito úteis na lista de discussão do PostGIS. Fiz  uma pequena modificação na segunda função, que aumenta a quantidade de casas decimais dos minutos e segundos, caso do resultado seja um número inteiro nesses dois elementos.

Para utilizá-las é necessário carregá-las em um banco com o PostGIS instalado.


Create or Replace Function DMS2DD(  dDeg In Float,
                                    dMin In Float, 
                                    dSec In Float )                                
Returns Float
AS
$BODY$
Declare
   dDD Float;
BEGIN
   dDD := ABS(dDeg) + dMin / 60::float + dSec / 3600::float;
   Return SIGN(dDeg) * dDD;
End;
$BODY$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT
  COST 100;

create or Replace Function DD2DMS(  dDecDeg in Float,
                                    pDegreeSymbol in VarChar(1),
                                    pMinuteSymbol in VarChar(1),
                                    pSecondSymbol in VarChar(1) )
Returns varchar(50)
AS
$BODY$
DECLARE
   iDeg Int;
   iMin Int;
   dSec Float;
   
BEGIN

   iDeg := Trunc(dDecDeg)::Int;
   iMin := Trunc(   (Abs(dDecDeg) - Abs(iDeg)) * 60)::Int;
   dSec := Round(((((Abs(dDecDeg) - Abs(iDeg)) * 60) - iMin) * 60)::numeric, 3)::float;
   
Return trim(to_char(iDeg,'9999')) || pDegreeSymbol::text ||
       trim(to_char(iMin,'99.00')) || pMinuteSymbol::text || 
       case when dSec = 0::Float
       then '00' else replace(trim(to_char(dSec,'99.999')),'.000','') 
       end || pSecondSymbol::text;
END;
$BODY$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT
  COST 100; 

Utilizando as funções:
-- Converter as coordenadas da geometria 
-- POINT(-38.4969 -6.4026) para graus, minutos e segundos: 
 
SELECT DD2DMS(x(geometryFromText('POINT(-38.4969 -6.4026)')), '°','\'','\"' ) as Longitude,
DD2DMS(y(geometryFromText('POINT(-38.4969 -6.4026)')), '°','\'','\"' ) as Latitude;
 

Resultado:

       Longitude      |       Latitude
--------------------------+-----------------------
-38°29.00'48.840" | -6°24.00'9.360"

-- Converter as coordenadas (-34° 10' 50", -7° 33' 30")
-- para graus decimais:
 
SELECT DMS2DD(-34,10,50) as longitude,
DMS2DD(-7,33,30) as latitude;
 

Resultado:

       Longitude       |       Latitude
----------------------------+-----------------------
-34.1805555555556 | -7.55833333333333