El blog de Juan Palómez

17 junio 2008

Calcular distancias entre dos puntos dados en grados

Filed under: Uncategorized — Etiquetas: , , , , — thisisoneball @ 16:20

Las coordenadas latitud/longitud vienen en grados, con lo cual no se pueden medir distancias. En cambio el sistema UTM sirve también para localizar un punto concreto en la superficie de la Tierra, y viene expresado en metros. Con lo cual solo hace falta pasarlas de un sistema a otro.
Este código (Perl) pasa las coordenadas a UTM con el módulo Geo-Coordinates-UTM y luego calcula la distancia con el teorema de Pitágoras. Previamente tienes que tener las coordenadas de un punto en $latitud1 y $longitud1 y las del otro en $latitud2 y $longitud2:


use Geo::Coordinates::UTM;...

($zona, $este1, $norte1) = latlon_to_utm ("WGS-84", $latitud1, $longitud1);
($zona, $este2, $norte2) = latlon_to_utm ("WGS-84", $latitud2, $longitud2);
$distancia = sqrt((($este2 - $este1)**2) + (($norte2 - $norte1)**2));

De forma manual se puede usar simplemente la regla de Google Earth. El código anterior es útil si tienes que procesar series grandes de coordenadas.
Se puede configurar Google Earth para que muestre las coordenadas en UTM en vez de en grados: Herramientas > Opciones > Vista 3D > Universal Transversal de Mercator.

Creo que se puede calcular sin pasar a UTM, con las fórmulas de geometría de la esfera. Tendría cierto error ya que la Tierra no es esférica, pero para distancias pequeñas no debería importar.

Problema: El sistema UTM va por cuadrantes. Este código sólo sirve entre dos coordenadas que estén en el mismo cuadrante.

Anuncios

13 junio 2008

Sincronizar hora por HTTP

Filed under: Uncategorized — Etiquetas: , , , , — thisisoneball @ 01:00

Esto sirve para casos en los que no se pueda usar NTP. En mi caso los dispositivos que tengo que sincronizar no dejan conectar al puerto de NTP (123), solo puede conectar a los puertos de HTTP y FTP, así que lo hago a través de HTTP.
Los servidores web devuelven la fecha en las cabeceras HTTP, por ejemplo:

HTTP/1.1 200 OK
Date: Thu, 12 Jun 2008 22:27:47 GMT
Server: Oracle-Application-Server-10g/10.1.2.2.0 Oracle-HTTP-Server
Last-Modified: Mon, 31 Mar 2008 17:00:37 GMT
ETag: “6555ee-168-47f118b5”
Accept-Ranges: bytes
Content-Length: 360
Connection: close
Content-Type: text/html

Así que se puede coger la fecha de ahí. Esto lo hace un programa llamado Htpdate:
http://www.clevervest.com/twiki/bin/view/HTP/WebHome
El código que pongo aquí es para Windows Mobile, ya que Htpdate no tiene versión para este sistema. Es bastante más simple que dicho programa, pero funciona.


SYSTEMTIME HTTPGetTime(LPTSTR server)
{
HINTERNET session, connection, request;
SYSTEMTIME time; DWORD size;

session = InternetOpen (L"HTTPGetTime", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
connection = InternetConnect (session, server, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, NULL);
request = HttpOpenRequest (connection, L"GET", L"/", NULL, NULL, NULL, NULL, NULL);
HttpSendRequest (request, NULL, 0, NULL, 0);
size = sizeof(time);
HttpQueryInfo (peticion, HTTP_QUERY_DATE | HTTP_QUERY_FLAG_SYSTEMTIME, &time, &size, NULL);
InternetCloseHandle(request);
InternetCloseHandle(connection);
InternetCloseHandle(session);

return time;
}

El único parámetro que toma la función es el hostname de un servidor web. Sirve cualquiera (que esté en hora), pero es mejor usar uno lo más cercano posible.
La mecánica es bastante simple, conecta al servidor, hace una petición GET, recibe los datos y coge la fecha de las cabeceras HTTP. Las funciones de WinInet hacen casi todo el trabajo.
Esto está probado en Windows Mobile 5 pero debería funcionar en cualquier Windows.

La función devuelve la hora en GMT. Para sincronizar la hora del sistema sería por ejemplo así:


SYSTEMTIME time;

time = HTTPGetTime(L"www.rediris.es");
SetSystemTime(&time);

No es realmente exacto por el retardo que hay desde que el servidor devuelve la hora hasta que se cambia la hora del dispositivo, pero es bastante exacto. Como dice el autor de Htpdate, si puedes hazlo por NTP y si no por HTTP.

Posibles mejoras:

  • comprobación de los códigos de retorno de las funciones
  • usar HEAD en vez de GET: no hace falta bajar toda la pagina solo para leer las cabeceras

9 junio 2008

Redirector TCP chapucero con netcat

Filed under: Uncategorized — Etiquetas: , , — thisisoneball @ 00:16

En un equipo uno.dominio.com (probado bajo Linux, deberia funcionar en Windows con Cygwin)


mkfifo tuberia
nc -l -p 8080 < tuberia | nc http://www.google.es 80 > tuberia

En otro equipo dos.dominio.com en el navegador pones http://uno.dominio.com:8080

Utilidad: poner un equipo como “proxy” si no tienes permiso de administrador para poder instalar un proxy normal

6 junio 2008

Comparar tablas

Filed under: Uncategorized — Etiquetas: , , — thisisoneball @ 10:40

Para saber si dos tablas a y b son exactamente iguales:


select * from a except select * from b;
select * from b except select * from a;

  • Si las dos consultas salen vacías es que a y b son iguales.
  • Si hay una fila que está en una y en la otra no, saldrá en una de las dos consultas
  • Si hay una fila que está en las dos tablas pero con algún valor cambiado, saldrá en las dos consultas, con distintos valores en cada una.
  • Si el número o el tipo de datos de los campos es distinto, fallarán las consultas (igual que pasa con UNION y con INTERSECT)

Vistas materializadas en PostgreSQL

Filed under: Uncategorized — Etiquetas: , , , , — thisisoneball @ 10:26

Si has estado buscando como materializar vistas en Postgres habrás visto que de momento no trae esa posibilidad, pero se puede conseguir algo parecido por medio de tablas temporales (CREATE TEMPORARY TABLE). Haciendo por ejemplo:

create temporary table v1 as select * from v1;

el sistema calcularía la vista v1 y guardaría las filas resultantes en una tabla temporal llamada también v1. Esto puede servir para una vista que tarda mucho en ejecutarse. Despues de hacer esto, cada vez que una consulta acceda a v1 accederá a los datos ya calculados y devolverá las filas casi instantáneamente. Además como se puede ver el nombre de la tabla temporal puede ser el de una tabla o vista ya existente. Esa tabla o vista no se borra, simplemente no sería accesible hasta que no se destruya la tabla temporal (DROP TABLE) o se cierre la sesión.
Esta es otra característica de las tablas temporales, son visibles solo en la sesión (conexión a la base de datos) en la que se han creado, por lo que otros usuarios no pueden verla y además se destruye al cerrar la sesión. No hay problema en hacer DROP TABLE v1; se borrará la tabla temporal. En el caso de que no estuviera creada la tabla temporal no te cargarás la vista, ya que eso es con DROP VIEW, y el DROP TABLE fallaría.
Lo de usar el mismo nombre para la vista y para la tabla sirve para no tener que modificar las consultas que ya estuvieran hechas: lo que antes accedía a v1 sigue accediendo a v1 pero más rápido.
El principal problema, y por esto no se le puede llamar realmente vista materializada, es que en la tabla temporal queda una “foto” de lo que devolvía v1 en el momento de hacer el CREATE TEMPORARY TABLE; no se verían los cambios que hayan podido ocurrir desde entonces en las tablas de las cuales la vista v1 tomaba los datos. Esto puede ser conveniente o no. Por ejempo: un usuario tiene que estudiar unas gráficas provenientes de los datos de v1, y pide una gráfica diferente cada 10 minutos. Si hay otros usuarios metiendo datos en la BD a la vez, las gráficas no serían coherentes unas con otras. Con la tabla temporal las gráficas siempre leerían del mismo conjunto de datos.
En otros casos no sería conveniente, ya que normalente se quiere tener los datos actualizados al momento. En ese caso lo que se puede hacer es cada cierto tiempo destruir la tabla temporal y volverla a crear. Todo depende de lo lenta que sea la consulta y de lo actualizados que tengan que estar los datos.

Dependencias

Este sistema puede dar problemas a la hora de materializar varias vistas, si dependen unas de otras, debido al sistema que tiene Postgres para registrar las dependencias entre objetos. En el CREATE TEMPORARY TABLE anterior, la vista v1 es un objeto de la base de datos, y la nueva tabla temporal v1 será otro (el nombre real interno de la tabla temporal sería algo como “pg_temp_1.v1″. Debido a esto y dependiendo de como accedas a v1 puede que estés accediento a la vista, con lo cual iría lento de nuevo.

En mi caso tengo varias vistas que tardan mucho en ejecutarse, y que dependen unas de otras. Por ejemplo si tengo v1, v2 y v3 que no dependen de ninguna vista sino de tablas, luego v4 que depende de v1 y v2; y v5 que depende de v3 y v4. Si hago esto:

create temporary table v1 as select * from v1;
create temporary table v2 as select * from v2;
create temporary table v3 as select * from v3;
create temporary table v4 as select * from v4;
create temporary table v5 as select * from v5;

Las tres primeras sentencias harían lo esperado. La cuarta en cambio no: accedería a las vistas reales v1 y v2 y no a las tablas temporales, a pesar de que justo antes las acabo de crear. Esto es porque cuando se creó la vista v4 (CREATE VIEW) no estaban estas tablas temporales, se hizo contra las vistas v1 y v2, y Postgres internamente tiene registrado que se lea de ahí. Por lo tanto el cuarto CREATE TEMPORARY TABLE funcionará pero muy lento, no se beneficiará de que v1 y v2 ya están materializadas. Dependerá de cada vista si esto es importante o no:
Por ejemplo si en el SELECT de v4 aparecen v1 y v2 en un JOIN, tardará probablemente la suma de lo que tardan las dos y un poco más; sin embargo si es un SELECT con v1 en el FROM y con una subconsulta en el SELECT o en el WHERE en la que aparece v2, se multiplicará el tiempo que necesita (lo que tarde v1 + el número de filas de v1 x lo que tarde v2).

Una solución a esto es crear las tablas temporales en una función de plperl. Este lenguaje, a diferencia de plpgsql, no tiene en cuenta las dependencias entre objetos. En este caso v4 y v5 usarán las tablas temporales creadas anteriormente.
Esta función serviría para “materializar” las vistas que pongáis en la lista @vistas:

CREATE OR REPLACE FUNCTION materializar() RETURNS integer AS
$BODY$

@vistas = ("v1", "v2", "v3", "v4", "v5");
@consultas = ();
foreach $vista (@vistas) {
	$sql = “SELECT definition FROM pg_views WHERE viewname=’$vista’”;
	$rv = spi_exec_query($sql);
	$row = $rv->{rows}[0];
	push @consultas, “CREATE TEMPORARY TABLE \”$vista\” AS $row->{definition}”;
}
foreach $consulta (@consultas) {
	$rv = spi_exec_query($consulta);
}
return 0;

$BODY$
LANGUAGE ‘plperl’ VOLATILE; 

Esta función crea las tablas temporales v1 … v5 en ese orden, leyendo siempre los datos de las tablas temporales recién creadas, por lo que lo hace de la forma más rápida posible. La lista @vistas tiene que ir en orden de dependencia, una vista que dependa de otra tiene que ir especificada después de ella.

Crea un blog o un sitio web gratuitos con WordPress.com.