sábado, 22 de marzo de 2014

Geolocalizador usando HTML5

Sabías que...







Cada vez HTML5 va tomando mas importancia en nuestros nuevos desarrollos WEB, ya que incorpora una serie de mejoras muy interesantesç frente al predecesor HTML4. Esas mejoras nos permiten hacer muchas tareas que antes dependíamos de software de terceros como Flash, JAVA, etc...

Una de las funcionalidades interesantes es la posibilidad de localizarnos geográficamente en cualquier parte del mundo usando varios métodos de localización que son totalmente transparentes para el usuario, cada uno con sus ventajas y desventajas.

Si usamos el navegador de un PC de sobremesa/portátil lo mas normal es que use nuestra dirección IP para dar una aproximación de dónde podemos estar geográficamente. Un método muy rápido pero poco fiable en según qué circunstancias.

Si usamos el navegador en un teléfono móvil se añade la posibilidad de usar las antenas de telefonía y el módulo GPS en caso de estar disponible, el cual nos da una información mucho mas detallada y fiable añadiendo información adicional como la altitud y la velocidad a la que nos desplazamos en ese preciso instante.


He desarrollado un pequeño programa para ver como funciona esta funcionalidad en HTML5, compatible con ordenadores de sobremesa y móviles permitiendo usar el GPS (activando y desactivando la opción):

Demostración


       Usar GPS 

Código


    function localizame() {
     

//Objeto donde se definen la configuración del geocoder:
var options = {
     enableHighAccuracy: document.getElementById("checkGPS").checked,  //Habilita el GPS
     timeout: 60000,  //Tiempo de espera en caso de no recibir ninguna posición.
     maximumAge: 0   //Definir un tiempo de caducidad respecto la ultima posición recibida, si está a 0 siempre obtendremos una posición actualizada, nunca una almacenada en caché.
 };

 function success(pos) {
            //El objeto "pos" contiene toda la información geografica obtenida.
     console.log(pos);
     alert("Tu posicion es: \n"+
           "Latitud: "+pos.coords.latitude+"\n"+
    "Longitud: "+pos.coords.longitude+"\n"+
                  "Altitud: "+pos.coords.altitude+"\n"+
                  "Velocidad: "+pos.coords.speed+"\n");
 };
 function error(err) {
            //En caso de error nos mostrará una alerta con su código asociado.
     alert('ERROR(' + err.code + '): ' + err.message);
 };
 
        //La función es asincrona, hay que definir una función que se ejecutará cuando encuentre una posición, una en caso de error y finalmente los parametros de configuración.
        navigator.geolocation.getCurrentPosition(success, error, options);
    }

domingo, 21 de abril de 2013

JSON (JavaScript Object Notation)

En JavaScript, habitualmente llamamos JSON a un objeto creado mediante la sintaxis:

var json = new Object()    o bien     var json = {}

Estos objetos son muy útiles para almacenar una estructura de datos como la que hice unos meses atrás en el que se guardaban los datos de un formulario en un array de dichos objetos.

Un JSON tiene la capacidad de crear atributos dentro de él e incluso crear estructuras dentro de sus atributos, creciendo de forma exponencial sus nodos, pero no lleva asociado ningún método para facilitar al programador algunos de los problemas más típicos que podemos encontrarnos.

Vamos a ver algunas técnicas útiles para dichos objetos que pueden facilitar la vida a muchos programadores:

Recorrer un JSON:

¿Cuantos atributos tiene mi JSON?
¿Qué nombres tienen los atributos?
¿Cómo puedo recorrerlo?

function recorrerJSON(json) {
    var i = 0;
    for (var attr in json) {
         console.log(attr);
         i++;
    }
    return "Hay "+ i + " atributos.";
}

Eliminar atributos del JSON

Para eliminar un atributo de un objeto JSON podría bastar en igualar el atributo a NULL. Pero el problema que tenemos es que el atributo sigue existiendo dentro del JSON, tomando su valor como NULL. Podemos solucionarlo creando otro JSON con la siguiente función:


function eliminarAttr(json,attr) {
  var temp = {};
  json[attr] = null;
for (var atr in json) {
if ((json[atr] != null)||(atr != attr)) {
temp[atr] = json[atr]
}
  }
return temp;
}

Fusionar varios JSON

Puede darse el caso que tengamos dos o más JSON en diferentes variables y queramos unificar toda la información en un único JSON. Vamos a crear una función para fusionar estos JSON. En caso de que exista el atributo en ambos JSON se machacará la información por la del segundo JSON.

function fusionarJSON(json1,json2) {
for (var attr in json2) {
json1[attr] = json2[attr];
}
return json1;
}

Acceder dinámicamente a los atributos del JSON (evitar uso del eval() )

Un JSON puede ser accedido a sus atributos principalmente de dos formas diferentes:

1) json.nombreDelAtributo
2) json["nombreDelAtributo"]

La forma más aconsejable es la segunda, accedes a los atributos del JSON como si de un Array se tratara. La ventaja de este método es que dentro de los corchetes le pasamos un String, que puede ser perfectamente una variable que con el primer método no podemos hacerlo.

Ejemplo:

Dentro de un bucle for, con un iterador " i " (variable) correspondiente a los atributos del json , accedemos a los campos de la siguiente manera:

1)
     eval("json."+i) --> Devuelve el valor del atributo "i" del JSON.

Esta práctica debe ser evitada, el uso de eval ha de evitarse tanto como se pueda ya que permite inyectar código a la página, abriendo puertas a posibles vulnerabilidades. Además de sobrecargar más el código y complicarlo más.

2)
json[i] --> Devuelve el valor del atributo "i" del JSON.

Sin hacer uso de ningún eval y simplificando notablemente el código obtenemos los mismos resultados que con el primer método.


Guardar/Recuperar JSON en localStorage de HTML5

HTML5 ofrece la posibilidad de guardar información de forma "permanente" al ordenador del usuario. Como si de una cookie se tratara pero con la ventaja de poder almacenar mucha más información que una cookie.
Una de las formas es usando el objeto localStorage que se puede acceder de la misma forma que un JSON:

Ejemplo:
 
   localStorage["ola"] = "5".
   Ahora si cerramos y abrimos el navegador y escribimos localStorage["ola"] nos devolvera nuestro 5.

La principal desventaja es que los valores de los atributos de la localStorage deben ser Strings. No podemos asignar-le un JSON entero. Debemos pasarlo a String previamente. podemos usar estas funciones para guardar y recuperar información:


function guardarJSON(json,nombre) { 
localStorage[nombre] = JSON.stringify(json); 
} 
function recuperarJSON(nombre) { 
return JSON.parse(localStorage[nombre]); 
}

Espero que estos pequeños Snippets sean de ayuda a alguien. Sería de agradecer algún comentario por si quedan dudas o mejoraríais las funciones, que de bien seguro se pueden mejorar. Pretendo que sean lo más simples posible para que cada uno se la haga a medida.

Un saludo.

martes, 6 de noviembre de 2012

Antena para Radio FM en Smartphones

Planteamiento

A menudo nos surge la necesidad de escuchar la radio sin auriculares desde nuestro Smartphone, por ejemplo, antes de ir a dormir puede que apetezca escuchar la radio usando el altavoz del móvil.

Si habéis probado de iniciar la aplicación de la Radio FM (Android) sin tener conectados unos auriculares os dará un mensaje como el siguiente:

"Conecte los auriculares o manos libres. Funcionan como antena de radio"

Solución

La solución que propongo va orientada a aquellos dispositivos que el puerto de los auriculares sea un minijack 3.5.

Material

  • Cable con conector minijack 3.5 (sirve el de unos auriculares rotos)
  • Tijeras
  • Smartphone con Radio FM
Procedimiento
  • Cortar el cable, usando las tijeras, dejando mínimo 2 dedos de cable contando desde el minijack. Cuanto más largo el cable mejor cobertura.
  • Asegurarnos que los hilos que componen el cable no se toquen entre sí. Suele haber 1 cable con recubrimiento de plástico que pasa por el centro, y una malla de cobre que envuelve el cable. En mi caso he enroscado esa malla alrededor del cable grande (ver foto).
Resultado

Después de este largo y laborioso bricolaje, debemos conectar el minijack al Smartphone como si tuviéramos auriculares normales y acceder a la Radio FM.
Sorpresa! el mensaje no aparece, pero tampoco se escucha nada... Hay que habilitar la salida por el altavoz del Smartphone, ya que sino él reproduce la musica por los auriculares inexistentes. 

La ventaja de ésto frente a dejar unos auriculares enteros es la comodidad de no tener unos auriculares colgando del Smartphone. Puede parecer una tontería, pero más lo es tirar a la basura unos auriculares rotos sin darle un segundo uso.

                         Mi antena casera para XPERIA NEO

viernes, 3 de agosto de 2012

Reparar nVIDIA 8800GTS con artifacts

Buenas! Hoy voy a hacer una entrada enfocada a Hardware en vez de la programación, para romper un poco el hielo. Hablaré desde mi propia experiencia y por supuesto no me hago responsable de ningún daño que pueda ocasionar lo que expondré más abajo.

Cómo reparar una nVIDIA 8800 GTS:

Síntomas:

  • Rallas blancas verticales justo al arranque del PC, cuando está con el fondo negro y letras blancas. (http://www.youtube.com/watch?v=hRo9c21u0WU)
  • Rallas rojas verticales cargando un gestor de arranque como ahora GRUB con fondo azul.
  • Bloqueo del PC al iniciar juegos
  • Letras ilegibles y gráficos corruptos
Posible explicación del problema:

       Parece que es un fallo bastante común en estos chips de nVIDIA, el G80 y similares ensamblados en las mismas condiciones. Parece ser que sufre problemas de malas soldaduras y esto hace que no circule bien la información a través de sus conexiones y provoca los síntomas detallados anteriormente.
       La solución buena sería reemplazar esas soldaduras que están mal hechas por unas de mejor calidad, algo que no está al alcance de nosotros ya que son soldaduras sumamente pequeñas y hay demasiadas como para hacerlo a mano.
       La solución que propongo es calentar esas soldaduras para lograr que se asienten bien y lograr que vuelva a ser la que era; como no podemos discriminar las soldaduras del resto de la gráfica a la hora de calentarlas, tendremos que calentar la gráfica entera.

Solución:

     Como si de una receta de cocina se tratara... 

  1. Precalentamos el horno  (el de la cocina, sisi) a 200 ºC, sin gratinador.
  2. Hacemos 4 bolitas de papel de plata lo mas iguales posible, y se colocan encima de la bandeja       que pondremos al horno. Las ponemos de forma que queden en las 4 esquinas de la gráfica y ésta no toque directamente la bandeja.
  3. Desconectamos el ventilador de la gráfica y desmontamos el disipador. (En mi modelo, XFX 8800GTS 320MB, solamente hay que sacar los tornillos que llevan como un muelle y los 2 tornillos que van en la parte delantera, donde van las conexiones DVI/VGA al monitor). Una vez sacados los tornillos puede que parezca que siga pegado, hay que hacer un poco de fuerza.
  4. Colocamos la gráfica encima la bandeja con las bolitas
  5. Colocamos la bandeja en el centro del horno, ni muy arriba ni muy abajo.
  6. Esperamos 8 minutos y sacamos la bandeja con mucho cuidado. Las soldaduras pueden estar débiles y caerse algún condensador. Difícil que pase, pero mejor ir prevenidos.
  7. La dejamos enfriar unos 20 minutos y volvemos a montarla entera, no olviden conectar el ventilador de nuevo.
  8. Colocarla al ordenador y a disfrutar de ella una temporada mas.
Puede que os funcione bien para siempre ya, o puede que a la semana os vuelva a fallar... En mi caso, aproximadamente cada 4 o 5 meses debo hacerle una horneada... Hasta que diga basta.


Antes que dar la gráfica por perdida... por qué no intentarlo??

                                                nVIDIA 9800GT recién sacada del horno.

Un saludo!

domingo, 8 de abril de 2012

Array de objetos en JavaScript

Array de objetos en JavaScript



Objetos en nuestra Array: (Abre la consola de JavaScript para ver el objeto directamente)







Buenas tardes!

Hoy toca explicar como hacer una array de objetos usando JavaScript. Es posible que normalmente estemos acostumbrados a trabajar con un Array de numeros como este:

var array = [0,1,2,3,4,5,6]

Donde cada posición es un numero (integer) o cadenas de caracteres. Pero en ocasiones, los numeros o cadenas de caracteres se nos quedan cortos y debemos crear un objeto en cada posicion. Por lo tanto, en algun momento de la implementacion de nuestro programa nos puede interesar hacer un Array de objetos como los del ejemplo anterior (arriba del todo).

Probad de añadir elementos rellenando los campos de 'Nombre', 'Tipo', 'Texto' con lo que queráis. Una vez rellenados apretad al botón 'Añadir Objeto'.
Podremos ver nuestra Array con sus objetos dentro.

Para eliminar objetos basta con rellenar el campo valor y seleccionar el atributo que queremos usar para decidir qué elemento eliminar. Por ejemplo:
Si en 'valor' especificamos: 'aa'  (sin las comillas)   y en el select dejamos seleccionado 'Nombre' nos eliminará todos los objetos de nuestra Array que contenga en el atributo 'nombre' el valor 'aa'.

El código en si es bien simple, hay algunos elementos secundarios (básicamente estilos de CSS) que no forman parte de la estructura básica del objetivo del snipet.

El código fuente es el siguiente:


<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">

<style type="text/css">
.muestra-objeto {
display: inline;
float: left;
overflow: auto;
width: 180px;
height: 180px;
background-color: aliceBlue;
border-color: red;
}
</style>

<script>
//Inicializamos el array i el objeto.
var array = new Array();
var objeto = {"nombre": null,"tipo": null, "timestamp": null, "texto": null};


function afegirObjecte(divs) {

console.log("Objeto directamente del formulario:");
console.log(divs);
//Extraemos lo que nos interesa de los divs y definimos el timestamp:
var nombre = divs.childNodes[1].value;
var tipo = divs.childNodes[3].value;
var texto = divs.childNodes[5].value
var timestamp = new Date().getTime();

//Insertamos objeto en Array:
array.push({"nombre": nombre,"tipo": tipo, "timestamp": timestamp, "texto": texto});
console.log("Array despues de añadir este objeto: ");
console.log(array);
mostrarObjetosArray()
}

//Para eliminar un objeto debemos especificar cuál queremos eliminar. Para ello definimos dos parametros en la función
//filtro: sera uno de los atributos del objeto: nombre, tipo, texto o timestamp
//valor: aquel objeto que en el filtro que hemos definido contenga el valor aquí indicado será el elegido para eliminar.

function eliminarObjeto(divs) {

console.log("Objeto directamente del formulario:");
console.log(divs);
var valor = divs.childNodes[1].value;
var filtro = (divs.childNodes[3].value).toLowerCase(); //Recordemos que es sensible a mayusculas y minusculas (lo pasamos a minusculas).
var arrayTemp = new Array();
for (var i = 0; i < array.length; i++) {

if (array[i][filtro] != valor) {
arrayTemp.push(array[i]);
}
}
if (array.length == arrayTemp.length) {
alert("No se ha eliminado nada. \nRecuerda que 'valor' y filtro deben coincidir i existir en la array principal.");
}
array = arrayTemp;
console.log("Array despues de eliminar el objeto: ");
console.log(array);
mostrarObjetosArray()
}

function mostrarObjetosArray() {

var content = document.getElementById("mostrar-array");
while (content.hasChildNodes() == true) { //Eliminamos todo lo que hay en pantalla, del objeto y lo generaremos de nuevo.
content.removeChild(content.childNodes[0])
}
for (var i = 0; i < array.length; i++) { //Para cada elemento existente en la array
var div = document.createElement("fieldset"); //Creamos un div (cuadrado azulado en la pantalla);
div.className = "muestra-objeto"
var divNombre = document.createElement("div");
divNombre.textContent = "-Nombre: "+array[i].nombre;
var divTipo = document.createElement("div");
divTipo.textContent = "-Tipo: "+array[i].tipo;
var divTimestamp = document.createElement("div");
divTimestamp.textContent = "-Dia/Hora: "+new Date(array[i].timestamp).toUTCString();
var divTexto = document.createElement("div");
divTexto.textContent = "-Texto: "+array[i].texto;
div.appendChild(divNombre);
div.appendChild(document.createElement("hr"));
div.appendChild(divTipo);
div.appendChild(document.createElement("hr"));
div.appendChild(divTimestamp);
div.appendChild(document.createElement("hr"));
div.appendChild(divTexto);
content.appendChild(div);
}
}
</script>
</head>
<body>
<div style="width: 100%">
<fieldset style="display: inline;float:left;">
<div id="formulario">
<input type="text" placeholder="Nombre" id="nombre" />
<input type="text" placeholder="Tipo" id="tipo" />
<input type="text" placeholder="Texto" id="texto" />
<input type="button" value="Añadir objeto" onclick="afegirObjecte(document.getElementById('formulario'))" />
</div>
</fieldset>
<fieldset style="display: inline;float:left;">
<div id="formulario-eliminar">
<input type="text" placeholder="Valor" id="valor" />
<select>
<option>Nombre</option>
<option>Tipo</option>
<option>Texto</option>
</select>
<input type="button" value="Eliminar Objetos" onclick="eliminarObjeto(document.getElementById('formulario-eliminar'))" />
</div>
</fieldset>
</div>
<br />
<fieldset style="float:left;display: block;width:100%;">
<div>
<p>Objetos en nuestra Array: (Abre la consola de JavaScript para ver el objeto directamente) </p>
<div id="mostrar-array"></div>
</div>
</fieldset>
</body>
</html>

sábado, 7 de abril de 2012

"Capicua" o "Palindromo" en JavaScript

"Capicua" o "Palindromo" en JavaScript


Buenas tardes a todos!

Después de un tiempo sin redactar nada.. vuelvo a la carga con un pequeño snipet que puede ayudar a los mas nobeles en programación.
Como comprobar si una palabra es capicua (palindromo) usando una función de JavaScript.

La forma humana de resolver este problema es intentar leer la palabra al revés y fijarnos si el orden de las letras es el mismo que si se lee normal. Pues eso mismo se puede implementar usando la programación.

Para ello declaramos el método .reverse para las cadenas de caracteres (palabras), ya que por defecto JavaScript no trae esta función. Para ello se usa la propiedad prototype que nos permite construir métodos para un tipo de dato.

Llegados a este punto, lo demás es muy sencillo, simplemente comprobar si la palabra al revés es igual que la palabra normal.

Codigo fuente:




<html>
<head>
<script>

     //Definimos un metodo para el tipo de datos "string", que lo unico que hace es devolver nuestra palabra en orden inverso.
String.prototype.reverse=function(){return this.split("").reverse().join("");}

function testCapCua(word) {


//Una vez definido, ya lo tenemos practicamente hecho.
var inverseWord = word.reverse();
if (word == inverseWord) { //Si la palabra es igual a la palabra girada.
alert("La palabra '"+word+"' SI es cap i cua");
return(true); //Es capicua/palindromo.
}
else {
alert("La palabra '"+word+"' NO es cap i cua: '"+inverseWord+"'");
return(false); //NO es capicua/palindromo.
}
}
</script>
</head>

<body>
<input type="text" placeholder="Introducir palabra." id="input-word" />
<input type="button" onclick="testCapCua(document.getElementById('input-word').value)" value="Comprobar"/>
</body>
</html>

lunes, 26 de diciembre de 2011

Evitar cache del navegador en Google FT

Seguimos hablando de Google Fusion Tables.

Presentación del problema

A veces podemos encontrarnos con la necesidad de querer actualizar los marcadores de un mapa usando la información actualizada de una tabla de Google Fusion Tables.

El problema que hay, es que si lanzamos una query a la base de datos, si la sentencia es idéntica que en otro caso anterior, el navegador en vez de hacer la llamada al servidor, se pasea por la caché de nuestro navegador en busca del resultado obtenido anteriormente con esa misma sentencia.

Eso puede darnos muchos dolores de cabeza, ya que si estamos haciendo un seguimiento en 'tiempo real' de algún transmisor GPS que nos envía la información en FT, la query puede ser siempre la misma, pero el resultado no debería ser el mismo.

Resumen del problema:
"Hacemos una query a Google Fusion Tables y nos devuelve siempre el mismo resultado aunque la tabla haya sido modificada y sabemos que este resultado no es correcto."

Nuestra query por ahora será algo así:

SELECT lat,lng FROM 2455909 ORDER BY timestamp DESC LIMIT 1
La traducción de esta query a lenguaje humano sería: Seleccionar las columnas Latitud y Longitud de la tabla 2455909 y ordenarla en función de la columna 'timestamp' en decreciente y limita la respuesta a 1 sola fila.

Ahora deberemos añadir alguna fila en nuestra tabla, para ello usaremos la interfaz de Google Fusion Tables y añadiremos manualmente la fila. Tener en cuenta que si usáis mi tabla de prueba, la columna 'timestamp' debéis poner un numero SIEMPRE mayor a cualquier otro. El 'timestamp' es lo que identifica que esa fila añadida sea la última, y si queremos obtener la fila más actualizada, deberemos hacer un SELECT a la fila con un 'timestamp' más grande.

Realizamos otra vez la Query anterior y... Qué observamos? El resultado sigue siendo el mismo que antes de añadir la fila.
Si probamos de borrar la cache del navegador y volver a hacer la query veremos que funciona correctamente.

Solución


Para evitar esto, podemos añadir una condición a la query que SIEMPRE cambie un poco y sobretodo que estemos 100% seguros de que no se repita nunca en cualquiera de los casos.
Para ello podemos jugar con el 'timestamp', en JavaScript podemos usar la funcion Date():
Creamos una función en nuestro JS:
function update() {
    var tempsAct = new Date();
    var milisec = tempsAct.getTime();
    return (milisec);
}

Y en nuestra Query hacemos referencia a nuestra función, para que la llame siempre que hagamos la query:

var query = "SELECT lat,lng FROM 2455909 WHERE lat NOT EQUAL TO "+update()+" ORDER BY timestamp DESC LIMIT 1";

Como veis, hemos añadido la función 'update()' dentro de la query. Esto hará que nuestra Query sea SIEMPRE distinta y de esa forma el navegador nunca podrá usar la caché.

Hay que decir que la variable milisec tendremos que actualizarla cada vez que querramos

Por cierto, 'timestamp' son los milisegundos transcurridos desde el 01/01/1970, con lo cual, como no podemos hacer mas de 1 query por ms en Google FT, esta solución es perfectamente válida.

Espero que os haya servido de ayuda, si tenéis alguna duda poneros en contacto conmigo.

Saludos!